untitled

Pasted Nov 11, 2026, 3:33 AM1 file731 lines

Scan to open on your phone

Point a camera at the QR code - it opens this exact paste, no app needed.

https://codepastes.com/zzgs7wv52kjft60e
untitled
package com.negocio.desinfectantes.ui.ventas

import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import com.negocio.desinfectantes.data.*
import java.text.NumberFormat
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.launch

private val FondoHistorial = Color(0xFF101D35)
private val TarjetaHistorial = Color(0xFF1B2D4A)
private val AzulHistorial = Color(0xFF2585F5)
private val TextoHistorial = Color.White
private val TextoSecundario = Color(0xFFA9BEDB)

private fun formatoDinero(valor: Double): String {
    return NumberFormat.getCurrencyInstance(
        Locale("es", "CO")
    ).format(valor)
}

private fun formatoFecha(fecha: Long): String {
    return SimpleDateFormat(
        "dd/MM/yyyy - hh:mm a",
        Locale("es", "CO")
    ).format(Date(fecha))
}

@Composable
fun HistorialVentasScreen(
    modifier: Modifier = Modifier,
    ventas: List<VentaEntity>,
    clientes: List<ClienteEntity>,
    productos: List<ProductoEntity>,
    aromas: List<AromaEntity>,
    presentaciones: List<PresentacionEntity>,
    productoDao: ProductoDao,
    onVolver: () -> Unit,
    ventaInicialId: String? = null
) {
    var ventaSeleccionada by remember {
        mutableStateOf<VentaEntity?>(null)
    }
    LaunchedEffect(ventaInicialId, ventas) {
        if (ventaInicialId != null) {
            ventaSeleccionada = ventas.find {
                it.id == ventaInicialId
            }
        }
    }
    LaunchedEffect(ventas, ventaSeleccionada?.id) {
        val idActual = ventaSeleccionada?.id

        if (idActual != null) {
            ventaSeleccionada = ventas.find {
                it.id == idActual
            }
        }
    }
    val scope = rememberCoroutineScope()

    var montoAbonoTexto by remember { mutableStateOf("") }
    var metodoAbono by remember { mutableStateOf("Efectivo") }
    var mensajeAbono by remember { mutableStateOf<String?>(null) }
    var guardandoAbono by remember { mutableStateOf(false) }

    var procesandoPedido by remember {
        mutableStateOf(false)
    }

    var mensajePedido by remember {
        mutableStateOf<String?>(null)
    }

    var errorPedido by remember {
        mutableStateOf(false)
    }

    var mostrarDialogoCancelar by remember {
        mutableStateOf(false)
    }

    val detallesFlow = remember(ventaSeleccionada?.id) {
        ventaSeleccionada?.let {
            productoDao.obtenerDetallesVenta(it.id)
        } ?: flowOf(emptyList<DetalleVentaEntity>())
    }

    val pagosFlow = remember(ventaSeleccionada?.id) {
        ventaSeleccionada?.let {
            productoDao.obtenerPagosVenta(it.id)
        } ?: flowOf(emptyList<PagoVentaEntity>())
    }

    val detalles by detallesFlow.collectAsState(
        initial = emptyList()
    )

    val pagos by pagosFlow.collectAsState(
        initial = emptyList()
    )

    Column(
        modifier = modifier
            .fillMaxSize()
            .verticalScroll(rememberScrollState())
            .padding(16.dp),
        verticalArrangement = Arrangement.spacedBy(12.dp)
    ) {

        if (ventaSeleccionada == null) {

            Text(
                text = "Historial de ventas",
                color = TextoHistorial,
                style = MaterialTheme.typography.headlineSmall,
                fontWeight = FontWeight.Bold
            )

            Text(
                text = "${ventas.size} venta(s) registrada(s)",
                color = TextoSecundario
            )

            if (ventas.isEmpty()) {

                Card(
                    colors = CardDefaults.cardColors(
                        containerColor = TarjetaHistorial
                    ),
                    modifier = Modifier.fillMaxWidth()
                ) {
                    Text(
                        text = "Todavía no hay ventas registradas.",
                        color = TextoSecundario,
                        modifier = Modifier.padding(20.dp)
                    )
                }

            } else {

                ventas.forEach { venta ->

                    val cliente = clientes.find {
                        it.id == venta.clienteId
                    }

                    Card(
                        modifier = Modifier
                            .fillMaxWidth()
                            .clickable {
                                ventaSeleccionada = venta
                            },
                        colors = CardDefaults.cardColors(
                            containerColor = TarjetaHistorial
                        )
                    ) {
                        Column(
                            modifier = Modifier.padding(16.dp),
                            verticalArrangement = Arrangement.spacedBy(7.dp)
                        ) {

                            Text(
                                text = cliente?.nombre
                                    ?: "Cliente no disponible",
                                color = TextoHistorial,
                                fontWeight = FontWeight.Bold
                            )

                            Text(
                                text = formatoFecha(venta.fechaCreacion),
                                color = TextoSecundario
                            )

                            Text(
                                text = "Sede: ${venta.sede}",
                                color = TextoSecundario
                            )

                            Text(
                                text = "Tipo: ${venta.tipoVenta.replace("_", " ")}",
                                color = TextoSecundario
                            )

                            Text(
                                text = "Entrega: ${venta.estadoEntrega.replace("_", " ")}",
                                color = TextoSecundario
                            )

                            Text(
                                text = formatoDinero(venta.total),
                                color = Color(0xFF81C784),
                                fontWeight = FontWeight.Bold,
                                style = MaterialTheme.typography.titleMedium
                            )

                            Text(
                                text = "Toca para ver los detalles →",
                                color = AzulHistorial
                            )
                        }
                    }
                }
            }

        } else {

            val venta = ventaSeleccionada!!

            val cliente = clientes.find {
                it.id == venta.clienteId
            }

            val totalPagado = pagos.sumOf { it.monto }

            val saldoPendiente = (
                    venta.total - totalPagado
                    ).coerceAtLeast(0.0)

            Text(
                text = "Detalle de la venta",
                color = TextoHistorial,
                style = MaterialTheme.typography.headlineSmall,
                fontWeight = FontWeight.Bold
            )

            OutlinedButton(
                onClick = {
                    ventaSeleccionada = null
                }
            ) {
                Text("← Volver al historial")
            }

            Card(
                colors = CardDefaults.cardColors(
                    containerColor = TarjetaHistorial
                ),
                modifier = Modifier.fillMaxWidth()
            ) {
                Column(
                    modifier = Modifier.padding(16.dp),
                    verticalArrangement = Arrangement.spacedBy(8.dp)
                ) {

                    Text(
                        text = cliente?.nombre
                            ?: "Cliente no disponible",
                        color = TextoHistorial,
                        fontWeight = FontWeight.Bold
                    )

                    Text(
                        text = formatoFecha(venta.fechaCreacion),
                        color = TextoSecundario
                    )

                    Text(
                        text = "Sede: ${venta.sede}",
                        color = TextoSecundario
                    )

                    Text(
                        text = "Tipo: ${venta.tipoVenta.replace("_", " ")}",
                        color = TextoSecundario
                    )

                    Text(
                        text = "Estado: ${venta.estadoEntrega.replace("_", " ")}",
                        color = TextoSecundario
                    )
                }
            }

            Text(
                text = "Productos vendidos",
                color = TextoHistorial,
                fontWeight = FontWeight.Bold,
                style = MaterialTheme.typography.titleMedium
            )

            detalles.forEach { detalle ->

                val presentacion = presentaciones.find {
                    it.id == detalle.presentacionId
                }

                val aroma = presentacion?.let { p ->
                    aromas.find { it.id == p.aromaId }
                }

                val producto = aroma?.let { a ->
                    productos.find { it.id == a.productoId }
                }

                Card(
                    colors = CardDefaults.cardColors(
                        containerColor = TarjetaHistorial
                    ),
                    modifier = Modifier.fillMaxWidth()
                ) {
                    Column(
                        modifier = Modifier.padding(14.dp),
                        verticalArrangement = Arrangement.spacedBy(5.dp)
                    ) {

                        Text(
                            text = producto?.nombre
                                ?: "Producto no disponible",
                            color = TextoHistorial,
                            fontWeight = FontWeight.Bold
                        )

                        Text(
                            text = "Aroma: ${aroma?.nombre ?: "No disponible"}",
                            color = TextoSecundario
                        )

                        Text(
                            text = "Presentación: ${presentacion?.nombre ?: "No disponible"}",
                            color = TextoSecundario
                        )

                        Text(
                            text = "Cantidad: ${detalle.cantidad}",
                            color = TextoSecundario
                        )

                        Text(
                            text = "Precio unitario: ${formatoDinero(detalle.precioUnitario)}",
                            color = TextoSecundario
                        )

                        Text(
                            text = "Subtotal: ${formatoDinero(detalle.subtotal)}",
                            color = Color(0xFF81C784),
                            fontWeight = FontWeight.Bold
                        )
                    }
                }
            }
            // CONTROL DE ESTADOS DE PEDIDOS

            if (venta.estadoEntrega == "PENDIENTE_FABRICACION") {

                Text(
                    text = "Gestión del pedido",
                    color = TextoHistorial,
                    fontWeight = FontWeight.Bold,
                    style = MaterialTheme.typography.titleMedium
                )

                Text(
                    text = "Cuando tengas los productos fabricados y distribuidos, puedes reservar las unidades para este cliente.",
                    color = TextoSecundario
                )

                Button(
                    onClick = {
                        procesandoPedido = true
                        mensajePedido = null
                        errorPedido = false

                        scope.launch {
                            try {
                                productoDao.reservarPedidoParaEntrega(venta.id)

                                mensajePedido =
                                    "¡Pedido listo! Las unidades quedaron reservadas."
                            } catch (e: Exception) {
                                mensajePedido = e.message
                                    ?: "No fue posible reservar las unidades."
                                errorPedido = true
                            } finally {
                                procesandoPedido = false
                            }
                        }
                    },
                    enabled = !procesandoPedido,
                    modifier = Modifier.fillMaxWidth(),
                    colors = ButtonDefaults.buttonColors(
                        containerColor = AzulHistorial
                    )
                ) {
                    Text(
                        if (procesandoPedido)
                            "Reservando unidades..."
                        else
                            "Marcar como listo para entregar"
                    )
                }

            } else if (venta.estadoEntrega == "PENDIENTE_ENTREGA") {

                Text(
                    text = "Pedido listo para entregar",
                    color = Color(0xFF81C784),
                    fontWeight = FontWeight.Bold,
                    style = MaterialTheme.typography.titleMedium
                )

                Text(
                    text = "Las unidades están reservadas para este cliente. Confirma cuando hayas realizado la entrega.",
                    color = TextoSecundario
                )

                Button(
                    onClick = {
                        procesandoPedido = true
                        mensajePedido = null
                        errorPedido = false

                        scope.launch {
                            try {
                                productoDao.entregarPedidoReservado(venta.id)

                                mensajePedido =
                                    "¡Entrega registrada correctamente!"
                            } catch (e: Exception) {
                                mensajePedido = e.message
                                    ?: "No fue posible registrar la entrega."
                                errorPedido = true
                            } finally {
                                procesandoPedido = false
                            }
                        }
                    },
                    enabled = !procesandoPedido,
                    modifier = Modifier.fillMaxWidth(),
                    colors = ButtonDefaults.buttonColors(
                        containerColor = Color(0xFF2E9D65)
                    )
                ) {
                    Text(
                        if (procesandoPedido)
                            "Registrando entrega..."
                        else
                            "Confirmar entrega"
                    )
                }
            }

            // Botón para cancelar pedidos que aún no han sido entregados.
            if (
                venta.estadoEntrega == "PENDIENTE_FABRICACION" ||
                venta.estadoEntrega == "PENDIENTE_ENTREGA"
            ) {
                OutlinedButton(
                    onClick = {
                        mostrarDialogoCancelar = true
                        mensajePedido = null
                        errorPedido = false
                    },
                    enabled = !procesandoPedido,
                    modifier = Modifier.fillMaxWidth(),
                    colors = ButtonDefaults.outlinedButtonColors(
                        contentColor = Color(0xFFFF6B6B)
                    )
                ) {
                    Text("Cancelar pedido")
                }
            }

            if (mostrarDialogoCancelar) {
                AlertDialog(
                    onDismissRequest = {
                        if (!procesandoPedido) {
                            mostrarDialogoCancelar = false
                        }
                    },
                    title = {
                        Text("Cancelar pedido")
                    },
                    text = {
                        Text(
                            if (venta.estadoEntrega == "PENDIENTE_ENTREGA") {
                                "¿Seguro que deseas cancelar este pedido? " +
                                        "Las unidades reservadas volverán a estar disponibles."
                            } else {
                                "¿Seguro que deseas cancelar este pedido? " +
                                        "Como aún no está listo para entregar, no se modificará el inventario."
                            }
                        )
                    },
                    confirmButton = {
                        TextButton(
                            onClick = {
                                mostrarDialogoCancelar = false
                                procesandoPedido = true
                                mensajePedido = null
                                errorPedido = false

                                scope.launch {
                                    try {
                                        productoDao.cancelarPedido(venta.id)
                                        mensajePedido =
                                            "Pedido cancelado correctamente."
                                    } catch (e: Exception) {
                                        mensajePedido = e.message
                                            ?: "No fue posible cancelar el pedido."
                                        errorPedido = true
                                    } finally {
                                        procesandoPedido = false
                                    }
                                }
                            },
                            enabled = !procesandoPedido
                        ) {
                            Text(
                                "Sí, cancelar",
                                color = Color(0xFFFF6B6B)
                            )
                        }
                    },
                    dismissButton = {
                        TextButton(
                            onClick = {
                                mostrarDialogoCancelar = false
                            },
                            enabled = !procesandoPedido
                        ) {
                            Text("No, volver")
                        }
                    }
                )
            }

            if (mensajePedido != null) {
                Text(
                    text = mensajePedido!!,
                    color = if (errorPedido)
                        Color(0xFFFF6B6B)
                    else
                        Color(0xFF81C784),
                    fontWeight = FontWeight.Bold
                )
            }
            Text(
                text = "Pagos registrados",
                color = TextoHistorial,
                fontWeight = FontWeight.Bold,
                style = MaterialTheme.typography.titleMedium
            )

            if (pagos.isEmpty()) {

                Text(
                    text = "Esta venta todavía no tiene pagos registrados.",
                    color = TextoSecundario
                )

            } else {

                pagos.forEach { pago ->

                    Card(
                        colors = CardDefaults.cardColors(
                            containerColor = TarjetaHistorial
                        ),
                        modifier = Modifier.fillMaxWidth()
                    ) {
                        Column(
                            modifier = Modifier.padding(14.dp),
                            verticalArrangement = Arrangement.spacedBy(5.dp)
                        ) {

                            Text(
                                text = formatoDinero(pago.monto),
                                color = Color(0xFF81C784),
                                fontWeight = FontWeight.Bold
                            )

                            Text(
                                text = "Método: ${pago.metodoPago}",
                                color = TextoSecundario
                            )

                            Text(
                                text = formatoFecha(pago.fechaCreacion),
                                color = TextoSecundario
                            )
                        }
                    }
                }
            }

            Card(
                colors = CardDefaults.cardColors(
                    containerColor = TarjetaHistorial
                ),
                modifier = Modifier.fillMaxWidth()
            ) {
                Column(
                    modifier = Modifier.padding(16.dp),
                    verticalArrangement = Arrangement.spacedBy(8.dp)
                ) {

                    Text(
                        text = "Resumen de la venta",
                        color = TextoHistorial,
                        fontWeight = FontWeight.Bold
                    )

                    Text(
                        text = "Total: ${formatoDinero(venta.total)}",
                        color = TextoHistorial
                    )

                    Text(
                        text = "Total pagado: ${formatoDinero(totalPagado)}",
                        color = Color(0xFF81C784)
                    )

                    Text(
                        text = "Saldo pendiente: ${formatoDinero(saldoPendiente)}",
                        color = if (saldoPendiente > 0)
                            Color(0xFFFFB74D)
                        else
                            Color(0xFF81C784),
                        fontWeight = FontWeight.Bold
                    )
                }
            }
            if (saldoPendiente > 0) {

                Text(
                    text = "Registrar un abono",
                    color = TextoHistorial,
                    fontWeight = FontWeight.Bold,
                    style = MaterialTheme.typography.titleMedium
                )

                OutlinedTextField(
                    value = montoAbonoTexto,
                    onValueChange = { montoAbonoTexto = it },
                    label = { Text("Valor del abono") },
                    placeholder = { Text("Ej. 50000") },
                    modifier = Modifier.fillMaxWidth(),
                    singleLine = true
                )

                OutlinedTextField(
                    value = metodoAbono,
                    onValueChange = { metodoAbono = it },
                    label = { Text("Método de pago") },
                    placeholder = { Text("Ej. Efectivo, Nequi, Bancolombia") },
                    modifier = Modifier.fillMaxWidth(),
                    singleLine = true
                )

                if (mensajeAbono != null) {
                    Text(
                        text = mensajeAbono!!,
                        color = Color(0xFFFF6B6B),
                        fontWeight = FontWeight.Bold
                    )
                }

                Button(
                    onClick = {
                        val monto = montoAbonoTexto
                            .replace(",", ".")
                            .toDoubleOrNull()

                        when {
                            monto == null || monto <= 0 -> {
                                mensajeAbono = "Ingresa un valor de abono válido."
                            }

                            monto > saldoPendiente -> {
                                mensajeAbono = "El abono no puede superar el saldo pendiente."
                            }

                            metodoAbono.isBlank() -> {
                                mensajeAbono = "Especifica el método de pago."
                            }

                            else -> {
                                mensajeAbono = null
                                guardandoAbono = true

                                scope.launch {
                                    try {
                                        productoDao.guardarPagoVenta(
                                            PagoVentaEntity(
                                                id = java.util.UUID.randomUUID().toString(),
                                                ventaId = venta.id,
                                                monto = monto,
                                                metodoPago = metodoAbono.trim(),
                                                fechaCreacion = System.currentTimeMillis()
                                            )
                                        )

                                        montoAbonoTexto = ""
                                        mensajeAbono = "¡Abono registrado correctamente!"

                                    } catch (e: Exception) {
                                        mensajeAbono = e.message
                                            ?: "No fue posible registrar el abono."
                                    } finally {
                                        guardandoAbono = false
                                    }
                                }
                            }
                        }
                    },
                    enabled = !guardandoAbono,
                    modifier = Modifier.fillMaxWidth(),
                    colors = ButtonDefaults.buttonColors(
                        containerColor = AzulHistorial
                    )
                ) {
                    Text(
                        if (guardandoAbono) "Guardando abono..." else "Guardar abono"
                    )
                }
            }
        }
    }
}

26,316 chars · codepastes.com