untitled

Pasted Nov 10, 2026, 5:08 AM1 file665 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/zzgs8j0eydd1cahg
untitled
package com.negocio.desinfectantes.ui.produccion

import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
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

private val LoteNavy = Color(0xFF101D35)
private val LoteCardBlue = Color(0xFF1B2D4A)
private val LoteAccentBlue = Color(0xFF2585F5)
private val LoteSoftBlue = Color(0xFFA9BEDB)
private val LoteWhite = Color(0xFFFFFFFF)

@Composable
fun HistorialLotesScreen(
    modifier: Modifier = Modifier,
    lotes: List<LoteProduccionEntity>,
    detalles: List<LoteDetalleEntity>,
    gastos: List<LoteGastoEntity>,
    inventariosLote: List<LoteInventarioEntity>,
    ingresosPorLoteYSede: List<IngresoLoteSedeResultado> = emptyList(),
    productos: List<ProductoEntity>,
    aromas: List<AromaEntity>,
    presentaciones: List<PresentacionEntity>,
    onEditarGasto: (
        gastoId: String,
        loteId: String,
        concepto: String,
        monto: Double,
        observacion: String?
    ) -> Unit,
    onAgregarGasto: (LoteGastoEntity) -> Unit,
    onEliminarGasto: (
        gastoId: String,
        loteId: String
    ) -> Unit,
    onEditarProduccion: (
        loteId: String,
        detalles: List<LoteDetalleEntity>,
        inventarios: List<LoteInventarioEntity>
    ) -> Unit,
    onVolver: () -> Unit
) {
    var gastoEnEdicion by remember {
        mutableStateOf<LoteGastoEntity?>(null)
    }

    var gastoAEliminar by remember {
        mutableStateOf<LoteGastoEntity?>(null)
    }

    var conceptoEditado by remember {
        mutableStateOf("")
    }

    var montoEditado by remember {
        mutableStateOf("")
    }

    var observacionEditada by remember {
        mutableStateOf("")
    }
    var loteParaNuevoGasto by remember {
        mutableStateOf<LoteProduccionEntity?>(null)
    }

    var nuevoConcepto by remember {
        mutableStateOf("")
    }

    var nuevoMonto by remember {
        mutableStateOf("")
    }

    var nuevaObservacion by remember {
        mutableStateOf("")
    }
    val formatoFecha = SimpleDateFormat(
        "dd/MM/yyyy • hh:mm a",
        Locale("es", "CO")
    )

    val formatoPesos = NumberFormat.getCurrencyInstance(
        Locale("es", "CO")
    ).apply {
        maximumFractionDigits = 0
        minimumFractionDigits = 0
    }

    Column(
        modifier = modifier
            .fillMaxSize()
            .background(LoteNavy)
            .verticalScroll(rememberScrollState())
            .padding(20.dp),
        verticalArrangement = Arrangement.spacedBy(16.dp)
    ) {
        TextButton(onClick = onVolver) {
            Text("← Volver", color = LoteSoftBlue)
        }

        Text(
            "Historial de lotes",
            color = LoteWhite,
            style = MaterialTheme.typography.headlineMedium,
            fontWeight = FontWeight.Bold
        )

        Text(
            "Consulta las producciones y sus gastos registrados.",
            color = LoteSoftBlue
        )

        if (lotes.isEmpty()) {
            Card(
                modifier = Modifier.fillMaxWidth(),
                colors = CardDefaults.cardColors(
                    containerColor = LoteCardBlue
                ),
                shape = RoundedCornerShape(16.dp)
            ) {
                Column(
                    modifier = Modifier.padding(20.dp),
                    verticalArrangement = Arrangement.spacedBy(8.dp)
                ) {
                    Text(
                        "Todavía no hay lotes registrados",
                        color = LoteWhite,
                        fontWeight = FontWeight.Bold
                    )
                    Text(
                        "Cuando registres una producción, aparecerá aquí.",
                        color = LoteSoftBlue
                    )
                }
            }
        }

        lotes.forEach { lote ->
            val producto = productos.firstOrNull {
                it.id == lote.productoId
            }

            val aroma = aromas.firstOrNull {
                it.id == lote.aromaId
            }

            val detallesLote = detalles.filter {
                it.loteId == lote.id
            }

            val gastosLote = gastos.filter {
                it.loteId == lote.id
            }

            val inventarios = inventariosLote.filter {
                it.loteId == lote.id
            }

            val totalUnidades = detallesLote.sumOf {
                it.cantidadFabricada
            }

            val totalGastos = gastosLote.sumOf {
                it.monto
            }

            val ingresosLote = ingresosPorLoteYSede.filter {
                it.loteId == lote.id
            }

            val ingresosBarrancas = ingresosLote
                .filter { it.sede.equals("Barrancas", ignoreCase = true) }
                .sumOf { it.ingresos }

            val ingresosValledupar = ingresosLote
                .filter { it.sede.equals("Valledupar", ignoreCase = true) }
                .sumOf { it.ingresos }

            val ingresosTotales = ingresosBarrancas + ingresosValledupar

            val gananciaLote = ingresosTotales - totalGastos

            Card(
                modifier = Modifier.fillMaxWidth(),
                colors = CardDefaults.cardColors(
                    containerColor = LoteCardBlue
                ),
                shape = RoundedCornerShape(18.dp)
            ) {
                Column(
                    modifier = Modifier.padding(16.dp),
                    verticalArrangement = Arrangement.spacedBy(12.dp)
                ) {
                    Text(
                        lote.numeroLote,
                        color = LoteAccentBlue,
                        style = MaterialTheme.typography.titleMedium,
                        fontWeight = FontWeight.Bold
                    )
                    Text(
                        producto?.nombre ?: "Producto no encontrado",
                        color = LoteWhite,
                        style = MaterialTheme.typography.titleLarge,
                        fontWeight = FontWeight.Bold
                    )

                    Text(
                        "Aroma: ${aroma?.nombre ?: "No encontrado"}",
                        color = LoteSoftBlue
                    )

                    Text(
                        "Fecha: ${formatoFecha.format(Date(lote.fechaCreacion))}",
                        color = LoteSoftBlue
                    )

                    HorizontalDivider(color = Color(0xFF40536F))

                    Text(
                        "Producción",
                        color = LoteAccentBlue,
                        fontWeight = FontWeight.Bold
                    )

                    detallesLote.forEach { detalle ->
                        val presentacion = presentaciones.firstOrNull {
                            it.id == detalle.presentacionId
                        }

                        val distribuciones = inventarios.filter {
                            it.presentacionId == detalle.presentacionId
                        }

                        Column(
                            verticalArrangement = Arrangement.spacedBy(5.dp)
                        ) {
                            Text(
                                presentacion?.nombre
                                    ?: "Presentación no encontrada",
                                color = LoteWhite,
                                fontWeight = FontWeight.SemiBold
                            )

                            Text(
                                "${detalle.cantidadFabricada} unidades fabricadas",
                                color = LoteSoftBlue
                            )


                            distribuciones.forEach { distribucion ->
                                Text(
                                    "• ${distribucion.sede}: ${distribucion.cantidadDisponible} unidades disponibles",
                                    color = LoteSoftBlue
                                )

                                if (distribucion.cantidadReservada > 0) {
                                    Text(
                                        "   ${distribucion.cantidadReservada} unidades reservadas",
                                        color = LoteAccentBlue
                                    )
                                }
                            }

                        }
                    }



                    Text(
                        "Gastos de producción",
                        color = LoteAccentBlue,
                        fontWeight = FontWeight.Bold
                    )

                    if (gastosLote.isEmpty()) {
                        Text(
                            "No se registraron gastos.",
                            color = LoteSoftBlue
                        )
                    } else {
                        gastosLote.forEach { gasto ->
                            Column(
                                verticalArrangement = Arrangement.spacedBy(6.dp)
                            ) {
                                Row(
                                    modifier = Modifier.fillMaxWidth(),
                                    horizontalArrangement = Arrangement.SpaceBetween
                                ) {
                                    Text(
                                        gasto.concepto,
                                        color = LoteWhite,
                                        modifier = Modifier.weight(1f)
                                    )

                                    Text(
                                        formatoPesos.format(gasto.monto),
                                        color = LoteWhite,
                                        fontWeight = FontWeight.SemiBold
                                    )
                                }

                                if (!gasto.observacion.isNullOrBlank()) {
                                    Text(
                                        gasto.observacion,
                                        color = LoteSoftBlue,
                                        style = MaterialTheme.typography.bodySmall
                                    )
                                }

                                Row(
                                    horizontalArrangement = Arrangement.spacedBy(12.dp)
                                ) {
                                    TextButton(
                                        onClick = {
                                            gastoEnEdicion = gasto
                                            conceptoEditado = gasto.concepto
                                            montoEditado = gasto.monto.toString()
                                            observacionEditada = gasto.observacion ?: ""
                                        }
                                    ) {
                                        Text("Editar", color = LoteAccentBlue)
                                    }

                                    TextButton(
                                        onClick = {
                                            gastoAEliminar = gasto
                                        }
                                    ) {
                                        Text("Eliminar", color = Color(0xFFFF6B6B))
                                    }
                                }
                            }
                        }
                    }
                    TextButton(
                        onClick = {
                            loteParaNuevoGasto = lote
                            nuevoConcepto = ""
                            nuevoMonto = ""
                            nuevaObservacion = ""
                        }
                    ) {
                        Text(
                            "+ Añadir gasto",
                            color = LoteAccentBlue,
                            fontWeight = FontWeight.Bold
                        )
                    }

                    HorizontalDivider(color = Color(0xFF40536F))

                    Row(
                        modifier = Modifier.fillMaxWidth(),
                        horizontalArrangement = Arrangement.SpaceBetween
                    ) {
                        Text(
                            "Total de gastos",
                            color = LoteSoftBlue
                        )
                        Text(
                            formatoPesos.format(totalGastos),
                            color = LoteWhite,
                            fontWeight = FontWeight.Bold
                        )
                    }
                    HorizontalDivider(color = Color(0xFF40536F))

                    Text(
                        "Rentabilidad del lote",
                        color = LoteAccentBlue,
                        fontWeight = FontWeight.Bold
                    )

                    Row(
                        modifier = Modifier.fillMaxWidth(),
                        horizontalArrangement = Arrangement.SpaceBetween
                    ) {
                        Text("Ingresos Barrancas", color = LoteSoftBlue)
                        Text(
                            formatoPesos.format(ingresosBarrancas),
                            color = LoteWhite
                        )
                    }

                    Row(
                        modifier = Modifier.fillMaxWidth(),
                        horizontalArrangement = Arrangement.SpaceBetween
                    ) {
                        Text("Ingresos Valledupar", color = LoteSoftBlue)
                        Text(
                            formatoPesos.format(ingresosValledupar),
                            color = LoteWhite
                        )
                    }

                    HorizontalDivider(color = Color(0xFF40536F))

                    Row(
                        modifier = Modifier.fillMaxWidth(),
                        horizontalArrangement = Arrangement.SpaceBetween
                    ) {
                        Text(
                            "Ingresos totales",
                            color = LoteWhite,
                            fontWeight = FontWeight.Bold
                        )
                        Text(
                            formatoPesos.format(ingresosTotales),
                            color = LoteWhite,
                            fontWeight = FontWeight.Bold
                        )
                    }

                    Row(
                        modifier = Modifier.fillMaxWidth(),
                        horizontalArrangement = Arrangement.SpaceBetween
                    ) {
                        Text(
                            if (gananciaLote >= 0) "Ganancia del lote" else "Pérdida del lote",
                            color = LoteWhite,
                            fontWeight = FontWeight.Bold
                        )
                        Text(
                            formatoPesos.format(gananciaLote),
                            color = if (gananciaLote >= 0) Color(0xFF4ADE80)
                            else Color(0xFFFF6B6B),
                            fontWeight = FontWeight.Bold
                        )
                    }

                    HorizontalDivider(color = Color(0xFF40536F))



                }
            }
        }

        Spacer(modifier = Modifier.height(20.dp))
    }
    gastoEnEdicion?.let { gasto ->
        AlertDialog(
            onDismissRequest = {
                gastoEnEdicion = null
            },
            title = {
                Text("Editar gasto")
            },
            text = {
                Column(
                    verticalArrangement = Arrangement.spacedBy(12.dp)
                ) {
                    OutlinedTextField(
                        value = conceptoEditado,
                        onValueChange = {
                            conceptoEditado = it
                        },
                        label = { Text("Concepto") },
                        singleLine = true
                    )

                    OutlinedTextField(
                        value = montoEditado,
                        onValueChange = {
                            montoEditado = it
                        },
                        label = { Text("Monto") },
                        singleLine = true
                    )

                    OutlinedTextField(
                        value = observacionEditada,
                        onValueChange = {
                            observacionEditada = it
                        },
                        label = { Text("Observación (opcional)") }
                    )
                }
            },
            confirmButton = {
                TextButton(
                    onClick = {
                        val monto = montoEditado
                            .replace(",", ".")
                            .toDoubleOrNull()

                        if (
                            conceptoEditado.isNotBlank() &&
                            monto != null &&
                            monto >= 0
                        ) {
                            onEditarGasto(
                                gasto.id,
                                gasto.loteId,
                                conceptoEditado.trim(),
                                monto,
                                observacionEditada.trim().ifBlank { null }
                            )

                            gastoEnEdicion = null
                        }
                    }
                ) {
                    Text("Guardar")
                }
            },
            dismissButton = {
                TextButton(
                    onClick = {
                        gastoEnEdicion = null
                    }
                ) {
                    Text("Cancelar")
                }
            },
            containerColor = LoteCardBlue,
            titleContentColor = LoteWhite,
            textContentColor = LoteWhite
        )
    }
    gastoAEliminar?.let { gasto ->
        AlertDialog(
            onDismissRequest = {
                gastoAEliminar = null
            },
            title = {
                Text("Eliminar gasto")
            },
            text = {
                Text(
                    "¿Estás seguro de que deseas eliminar el gasto \"${gasto.concepto}\" por ${formatoPesos.format(gasto.monto)}? Esta acción no se puede deshacer."
                )
            },
            confirmButton = {
                TextButton(
                    onClick = {
                        onEliminarGasto(
                            gasto.id,
                            gasto.loteId
                        )
                        gastoAEliminar = null
                    }
                ) {
                    Text(
                        "Eliminar",
                        color = Color(0xFFFF6B6B)
                    )
                }
            },
            dismissButton = {
                TextButton(
                    onClick = {
                        gastoAEliminar = null
                    }
                ) {
                    Text("Cancelar")
                }
            },
            containerColor = LoteCardBlue,
            titleContentColor = LoteWhite,
            textContentColor = LoteWhite
        )
    }
    loteParaNuevoGasto?.let { lote ->
        AlertDialog(
            onDismissRequest = {
                loteParaNuevoGasto = null
            },
            title = {
                Text("Añadir gasto a ${lote.numeroLote}")
            },
            text = {
                Column(
                    verticalArrangement = Arrangement.spacedBy(12.dp)
                ) {
                    OutlinedTextField(
                        value = nuevoConcepto,
                        onValueChange = {
                            nuevoConcepto = it
                        },
                        label = { Text("Concepto") },
                        singleLine = true
                    )

                    OutlinedTextField(
                        value = nuevoMonto,
                        onValueChange = {
                            nuevoMonto = it
                        },
                        label = { Text("Monto") },
                        singleLine = true
                    )

                    OutlinedTextField(
                        value = nuevaObservacion,
                        onValueChange = {
                            nuevaObservacion = it
                        },
                        label = { Text("Observación (opcional)") }
                    )
                }
            },
            confirmButton = {
                TextButton(
                    onClick = {
                        val monto = nuevoMonto
                            .replace(",", ".")
                            .toDoubleOrNull()

                        if (
                            nuevoConcepto.isNotBlank() &&
                            monto != null &&
                            monto >= 0
                        ) {
                            onAgregarGasto(
                                LoteGastoEntity(
                                    id = java.util.UUID.randomUUID().toString(),
                                    loteId = lote.id,
                                    concepto = nuevoConcepto.trim(),
                                    monto = monto,
                                    observacion = nuevaObservacion
                                        .trim()
                                        .ifBlank { null }
                                )
                            )

                            loteParaNuevoGasto = null
                        }
                    }
                ) {
                    Text("Guardar")
                }
            },
            dismissButton = {
                TextButton(
                    onClick = {
                        loteParaNuevoGasto = null
                    }
                ) {
                    Text("Cancelar")
                }
            },
            containerColor = LoteCardBlue,
            titleContentColor = LoteWhite,
            textContentColor = LoteWhite
        )
    }
}


23,090 chars · codepastes.com