sin-titulo

Pasted Nov 5, 2026, 5:45 AM1 file871 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/zzgscav525nxv3wp
sin-titulo

package com.negocio.desinfectantes

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
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.*
import androidx.compose.ui.Alignment
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 androidx.compose.ui.unit.sp
import com.negocio.desinfectantes.ui.theme.DesinfectantesAppTheme
import com.negocio.desinfectantes.ui.productos.ProductosScreen
import com.negocio.desinfectantes.ui.productos.AgregarProductoScreen
import com.negocio.desinfectantes.ui.productos.AromasScreen
import com.negocio.desinfectantes.ui.inactivos.RegistrosInactivosScreen
import androidx.compose.ui.platform.LocalContext
import com.negocio.desinfectantes.data.DatabaseProvider
import com.negocio.desinfectantes.data.ProductoEntity
import com.negocio.desinfectantes.data.AromaEntity
import com.negocio.desinfectantes.data.PresentacionEntity
import kotlinx.coroutines.launch
import java.util.UUID
import androidx.compose.runtime.collectAsState
import com.negocio.desinfectantes.ui.productos.AgregarAromaScreen
import com.negocio.desinfectantes.ui.productos.PresentacionesScreen
import com.negocio.desinfectantes.ui.productos.AgregarPresentacionScreen
import com.negocio.desinfectantes.ui.productos.EditarPresentacionScreen
import com.negocio.desinfectantes.ui.produccion.ProduccionScreen
import com.negocio.desinfectantes.data.ProduccionEntity
import com.negocio.desinfectantes.data.DistribucionProduccionEntity
import com.negocio.desinfectantes.data.InventarioEntity
import com.negocio.desinfectantes.data.MovimientoInventarioEntity
private val Navy = Color(0xFF101D35)
private val CardBlue = Color(0xFF1B2D4A)
private val AccentBlue = Color(0xFF2585F5)
private val SoftBlue = Color(0xFFA9BEDB)
private val White = Color(0xFFFFFFFF)

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()

        setContent {
            DesinfectantesAppTheme {
                PrestigeCleanApp()
            }
        }
    }
}

@Composable
fun PrestigeCleanApp() {
    var selectedTab by remember { mutableStateOf("Inicio") }
    var mostrarFormularioProducto by remember { mutableStateOf(false) }
    var mostrarRegistrosInactivos by remember {
        mutableStateOf(false)
    }
    var productoSeleccionado by remember {
        mutableStateOf<ProductoEntity?>(null)
    }
    var mostrarFormularioAroma by remember {
        mutableStateOf(false)
    }
    var aromaSeleccionado by remember {
        mutableStateOf<AromaEntity?>(null)
    }
    var mostrarFormularioPresentacion by remember {
        mutableStateOf(false)
    }
    var mensajeAroma by remember {
        mutableStateOf<String?>(null)
    }
    var presentacionSeleccionada by remember {
        mutableStateOf<PresentacionEntity?>(null)
    }
    val context = LocalContext.current

    val scope = rememberCoroutineScope()

    val productoDao = remember {
        DatabaseProvider.obtenerBaseDatos(context).productoDao()
    }
    val productos by productoDao.obtenerProductos()
        .collectAsState(initial = emptyList())
    val productosInactivos by productoDao.obtenerProductosInactivos()
        .collectAsState(initial = emptyList())
    val aromasInactivos by productoDao.obtenerTodosAromasInactivos()
        .collectAsState(initial = emptyList())

    val presentacionesInactivas by productoDao.obtenerTodasPresentacionesInactivas()
        .collectAsState(initial = emptyList())
    val todosProductos by productoDao.obtenerTodosProductos()
        .collectAsState(initial = emptyList())

    val todosAromas by productoDao.obtenerTodosAromas()
        .collectAsState(initial = emptyList())
    val aromasFlow = remember(productoSeleccionado?.id) {
        productoSeleccionado?.let {
            productoDao.obtenerAromas(it.id)
        } ?: kotlinx.coroutines.flow.flowOf(emptyList())
    }

    val aromas by aromasFlow.collectAsState(initial = emptyList())
    val presentacionesFlow = remember(aromaSeleccionado?.id) {
        aromaSeleccionado?.let {
            productoDao.obtenerPresentaciones(it.id)
        } ?: kotlinx.coroutines.flow.flowOf(emptyList())
    }

    val presentaciones by presentacionesFlow.collectAsState(initial = emptyList())

    val todasPresentacionesActivas by productoDao
        .obtenerTodasPresentacionesActivas()
        .collectAsState(initial = emptyList())

    Scaffold(
        containerColor = Navy,
        bottomBar = {
            NavigationBar(
                containerColor = CardBlue,
                contentColor = White
            ) {
                NavigationBarItem(
                    selected = selectedTab == "Inicio",
                    onClick = { selectedTab = "Inicio" },
                    icon = { Text("⌂", fontSize = 22.sp) },
                    label = { Text("Inicio") },
                    colors = navColors()
                )

                NavigationBarItem(
                    selected = selectedTab == "Ventas",
                    onClick = { selectedTab = "Ventas" },
                    icon = { Text("$", fontSize = 22.sp) },
                    label = { Text("Ventas") },
                    colors = navColors()
                )

                NavigationBarItem(
                    selected = selectedTab == "Productos",
                    onClick = { selectedTab = "Productos" },
                    icon = { Text("▣", fontSize = 22.sp) },
                    label = { Text("Productos") },
                    colors = navColors()
                )

                NavigationBarItem(
                    selected = selectedTab == "Clientes",
                    onClick = { selectedTab = "Clientes" },
                    icon = { Text("♙", fontSize = 22.sp) },
                    label = { Text("Clientes") },
                    colors = navColors()
                )

                NavigationBarItem(
                    selected = selectedTab == "Reportes",
                    onClick = { selectedTab = "Reportes" },
                    icon = { Text("▤", fontSize = 22.sp) },
                    label = { Text("Reportes") },
                    colors = navColors()
                )
            }
        }
    ) { innerPadding ->

        when (selectedTab) {
            "Inicio" -> DashboardScreen(
                modifier = Modifier.padding(innerPadding),
                onNavigate = { selectedTab = it }
            )

            "Ventas" -> SectionScreen(
                "Ventas",
                "Aquí podremos registrar ventas de contado y a crédito.",
                "+",
                Modifier.padding(innerPadding)
            )

            "Produccion" -> ProduccionScreen(
                modifier = Modifier.padding(innerPadding),
                productos = productos,
                aromas = todosAromas.filter { it.activo },
                presentaciones = todasPresentacionesActivas,
                onVolver = {
                    selectedTab = "Inicio"
                },
                onGuardar = { presentacion, producidas, barrancas, valledupar ->

                    scope.launch {

                        val fecha = System.currentTimeMillis()
                        val produccionId = UUID.randomUUID().toString()

                        // Crear el registro del lote fabricado.
                        val nuevaProduccion = ProduccionEntity(
                            id = produccionId,
                            presentacionId = presentacion.id,
                            cantidadProducida = producidas,
                            fechaCreacion = fecha
                        )

                        val distribuciones = mutableListOf<DistribucionProduccionEntity>()
                        val inventarios = mutableListOf<InventarioEntity>()
                        val movimientos = mutableListOf<MovimientoInventarioEntity>()

                        // Preparar la distribución de las dos sedes.
                        val sedes = listOf(
                            "Barrancas" to barrancas,
                            "Valledupar" to valledupar
                        )

                        sedes.forEach { (sede, cantidad) ->

                            if (cantidad > 0) {

                                // Registrar la cantidad enviada a esta sede.
                                distribuciones.add(
                                    DistribucionProduccionEntity(
                                        id = UUID.randomUUID().toString(),
                                        produccionId = produccionId,
                                        sede = sede,
                                        cantidad = cantidad,
                                        fechaCreacion = fecha
                                    )
                                )

                                // Consultar si ya existe inventario para esa sede.
                                val inventarioActual = productoDao.buscarInventario(
                                    presentacionId = presentacion.id,
                                    sede = sede
                                )

                                // Sumar las nuevas unidades a las existentes.
                                val inventarioActualizado = if (inventarioActual != null) {

                                    inventarioActual.copy(
                                        cantidadDisponible =
                                            inventarioActual.cantidadDisponible + cantidad,
                                        fechaActualizacion = fecha
                                    )

                                } else {

                                    InventarioEntity(
                                        id = UUID.randomUUID().toString(),
                                        presentacionId = presentacion.id,
                                        sede = sede,
                                        cantidadDisponible = cantidad,
                                        cantidadReservada = 0,
                                        fechaActualizacion = fecha
                                    )
                                }

                                inventarios.add(inventarioActualizado)

                                // Registrar el movimiento de entrada.
                                movimientos.add(
                                    MovimientoInventarioEntity(
                                        id = UUID.randomUUID().toString(),
                                        presentacionId = presentacion.id,
                                        sede = sede,
                                        tipoMovimiento = "PRODUCCION",
                                        cantidad = cantidad,
                                        referenciaId = produccionId,
                                        observacion = "Entrada por producción",
                                        fechaCreacion = fecha
                                    )
                                )
                            }
                        }

                        // Guardar todo en una sola transacción.
                        productoDao.registrarProduccionCompleta(
                            produccion = nuevaProduccion,
                            distribuciones = distribuciones,
                            inventarios = inventarios,
                            movimientos = movimientos
                        )

                        selectedTab = "Inicio"
                    }
                }
            )

            "Productos" -> {
                if (mostrarRegistrosInactivos) {

                    RegistrosInactivosScreen(
                        productos = productosInactivos,
                        aromas = aromasInactivos,
                        presentaciones = presentacionesInactivas,
                        todosProductos = todosProductos,
                        todosAromas = todosAromas,
                        onVolver = {
                            mostrarRegistrosInactivos = false
                        },
                        onReactivarProducto = { producto ->
                            scope.launch {
                                productoDao.reactivarProducto(producto.id)
                            }
                        },
                        onReactivarAroma = { aroma ->
                            scope.launch {
                                productoDao.reactivarAroma(aroma.id)
                            }
                        },
                        onReactivarPresentacion = { presentacion ->
                            scope.launch {
                                productoDao.reactivarPresentacion(presentacion.id)
                            }
                        }
                    )
                } else if (mostrarFormularioProducto) {

                    AgregarProductoScreen(
                        onVolver = {
                            mostrarFormularioProducto = false
                        },
                        onGuardar = { nombre, tipo, aroma ->

                            scope.launch {
                                val productoId = UUID.randomUUID().toString()

                                val producto = ProductoEntity(
                                    id = productoId,
                                    nombre = nombre,
                                    tipo = tipo
                                )

                                val nuevoAroma = AromaEntity(
                                    id = UUID.randomUUID().toString(),
                                    productoId = productoId,
                                    nombre = aroma
                                )

                                productoDao.guardarProducto(producto)
                                productoDao.guardarAroma(nuevoAroma)

                                mostrarFormularioProducto = false
                            }
                        }
                    )

                } else if (productoSeleccionado != null) {

                    if (mostrarFormularioAroma) {

                        AgregarAromaScreen(
                            nombreProducto = productoSeleccionado!!.nombre,

                            onVolver = {
                                mostrarFormularioAroma = false
                            },

                            onGuardar = { nombreAroma ->
                                scope.launch {
                                    val nuevoAroma = AromaEntity(
                                        id = UUID.randomUUID().toString(),
                                        productoId = productoSeleccionado!!.id,
                                        nombre = nombreAroma
                                    )

                                    productoDao.guardarAroma(nuevoAroma)

                                    mostrarFormularioAroma = false
                                }
                            }
                        )

                    } else if (aromaSeleccionado != null) {
                        if (presentacionSeleccionada != null) {
                            EditarPresentacionScreen(
                                nombreProducto = productoSeleccionado!!.nombre,
                                nombreAroma = aromaSeleccionado!!.nombre,
                                presentacion = presentacionSeleccionada!!,
                                onVolver = {
                                    presentacionSeleccionada = null
                                },
                                onGuardar = { nombre, contenido, precio ->
                                    scope.launch {
                                        val presentacionActualizada = presentacionSeleccionada!!.copy(
                                            nombre = nombre,
                                            contenidoMl = contenido,
                                            precio = precio
                                        )

                                        productoDao.guardarPresentacion(presentacionActualizada)

                                        presentacionSeleccionada = null
                                    }
                                },
                                onEliminar = {
                                    scope.launch {
                                        val idPresentacion = presentacionSeleccionada!!.id

                                        val existencias =
                                            productoDao.contarExistenciasPresentacion(idPresentacion)

                                        if (existencias > 0) {
                                            mensajeAroma =
                                                "No se puede eliminar esta presentación porque todavía tiene $existencias unidades en inventario."
                                        } else {
                                            productoDao.eliminarPresentacion(idPresentacion)
                                        }

                                        presentacionSeleccionada = null
                                    }
                                }
                            )
                        } else if (mostrarFormularioPresentacion) {

                            AgregarPresentacionScreen(
                                nombreProducto = productoSeleccionado!!.nombre,
                                nombreAroma = aromaSeleccionado!!.nombre,

                                onVolver = {
                                    mostrarFormularioPresentacion = false
                                },

                                onGuardar = { nombre, contenido, precio ->
                                    scope.launch {
                                        val nuevaPresentacion = PresentacionEntity(
                                            id = UUID.randomUUID().toString(),
                                            aromaId = aromaSeleccionado!!.id,
                                            nombre = nombre,
                                            contenidoMl = contenido,
                                            precio = precio
                                        )

                                        productoDao.guardarPresentacion(nuevaPresentacion)

                                        mostrarFormularioPresentacion = false
                                    }
                                }
                            )

                        } else {
                            PresentacionesScreen(
                                nombreProducto = productoSeleccionado!!.nombre,
                                nombreAroma = aromaSeleccionado!!.nombre,
                                presentaciones = presentaciones,
                                onVolver = {
                                    aromaSeleccionado = null
                                },
                                onAgregarPresentacion = {
                                    mostrarFormularioPresentacion = true
                                },
                                onSeleccionarPresentacion = { presentacion ->
                                    presentacionSeleccionada = presentacion
                                }
                            )
                        }
                    } else {

                        AromasScreen(
                            nombreProducto = productoSeleccionado!!.nombre,
                            aromas = aromas,

                            onVolver = {
                                productoSeleccionado = null
                            },

                            onAgregarAroma = {
                                mostrarFormularioAroma = true
                            },

                            onSeleccionarAroma = { aroma ->
                                aromaSeleccionado = aroma
                            },

                            onEliminarAroma = { aroma ->
                                scope.launch {
                                    val cantidad = productoDao.contarPresentacionesDeAroma(aroma.id)

                                    if (cantidad == 0) {
                                        productoDao.eliminarAroma(aroma.id)
                                    } else {
                                        mensajeAroma =
                                            "No se puede eliminar este aroma porque tiene $cantidad presentación(es) registradas."
                                    }
                                }
                            }
                        )
                    }
                } else {

                    ProductosScreen(
                        modifier = Modifier.padding(innerPadding),
                        productos = productos,
                        onAgregarProducto = {
                            mostrarFormularioProducto = true
                        },
                        onVerInactivos = {
                            mostrarRegistrosInactivos = true
                        },
                        onSeleccionarProducto = { producto ->
                            productoSeleccionado = producto
                        },
                        onDesactivarProducto = { producto ->
                            scope.launch {
                                val cantidadAromas =
                                    productoDao.contarAromasDeProducto(producto.id)

                                if (cantidadAromas > 0) {
                                    mensajeAroma =
                                        "No se puede desactivar este producto porque tiene $cantidadAromas aroma(s) registrados."
                                } else {
                                    productoDao.desactivarProducto(producto.id)
                                }
                            }
                        }
                    )
                }
            }
            "Produccion" -> ProduccionScreen(
                productos = productos,
                aromas = todosAromas.filter { it.activo },
                presentaciones = todasPresentacionesActivas,
                onVolver = {
                    selectedTab = "Inicio"
                },
                onGuardar = { presentacion, producidas, barrancas, valledupar ->
                    // Aquí conectaremos el guardado real de la producción.
                }
            )

            "Clientes" -> SectionScreen(
                "Clientes",
                "Aquí guardaremos los datos de tus clientes.",
                "♙",
                Modifier.padding(innerPadding)
            )

            "Reportes" -> SectionScreen(
                "Reportes",
                "Aquí consultaremos las ventas, ganancias y movimientos.",
                "▤",
                Modifier.padding(innerPadding)
            )
        }
    }

    if (mensajeAroma != null) {
        AlertDialog(
            onDismissRequest = {
                mensajeAroma = null
            },
            title = {
                Text("No se puede eliminar")
            },
            text = {
                Text(mensajeAroma!!)
            },
            confirmButton = {
                TextButton(
                    onClick = {
                        mensajeAroma = null
                    }
                ) {
                    Text("Entendido")
                }
            }
        )
    }
}

@Composable
fun navColors() = NavigationBarItemDefaults.colors(
    selectedIconColor = AccentBlue,
    selectedTextColor = AccentBlue,
    indicatorColor = Color(0xFF263F62),
    unselectedIconColor = SoftBlue,
    unselectedTextColor = SoftBlue
)

@Composable
fun DashboardScreen(
    modifier: Modifier = Modifier,
    onNavigate: (String) -> Unit
) {
    Column(
        modifier = modifier
            .fillMaxSize()
            .background(Navy)
            .verticalScroll(rememberScrollState())
            .padding(20.dp),
        verticalArrangement = Arrangement.spacedBy(20.dp)
    ) {

        Row(
            verticalAlignment = Alignment.CenterVertically,
            horizontalArrangement = Arrangement.spacedBy(12.dp)
        ) {
            Box(
                modifier = Modifier
                    .size(48.dp)
                    .background(AccentBlue, RoundedCornerShape(14.dp)),
                contentAlignment = Alignment.Center
            ) {
                Text(
                    "PC",
                    color = White,
                    fontSize = 20.sp,
                    fontWeight = FontWeight.Bold
                )
            }

            Column(modifier = Modifier.weight(1f)) {
                Text(
                    "PRESTIGE CLEAN",
                    color = White,
                    fontSize = 17.sp,
                    fontWeight = FontWeight.Bold
                )
                Text(
                    "Panel de administración",
                    color = SoftBlue,
                    fontSize = 12.sp
                )
            }

            Text("●", color = Color(0xFF4ADE80), fontSize = 13.sp)
        }

        Column(verticalArrangement = Arrangement.spacedBy(4.dp)) {
            Text(
                "¡Bienvenido!",
                color = White,
                fontSize = 25.sp,
                fontWeight = FontWeight.Bold
            )
            Text(
                "Este es el resumen de tu negocio.",
                color = SoftBlue,
                fontSize = 14.sp
            )
        }

        Column(verticalArrangement = Arrangement.spacedBy(12.dp)) {
            Text(
                "Resumen del día",
                color = White,
                fontSize = 18.sp,
                fontWeight = FontWeight.Bold
            )

            Row(horizontalArrangement = Arrangement.spacedBy(12.dp)) {
                MetricCard(
                    "Ventas de hoy",
                    "$0",
                    "Operaciones registradas",
                    Modifier.weight(1f)
                )
                MetricCard(
                    "Dinero recibido",
                    "$0",
                    "Pagos recibidos",
                    Modifier.weight(1f)
                )
            }

            Row(horizontalArrangement = Arrangement.spacedBy(12.dp)) {
                MetricCard(
                    "Por cobrar",
                    "$0",
                    "Saldos pendientes",
                    Modifier.weight(1f)
                )
                MetricCard(
                    "Inventario",
                    "0",
                    "Unidades disponibles",
                    Modifier.weight(1f)
                )
            }
        }

        Column(verticalArrangement = Arrangement.spacedBy(12.dp)) {
            Text(
                "Accesos rápidos",
                color = White,
                fontSize = 18.sp,
                fontWeight = FontWeight.Bold
            )

            Row(horizontalArrangement = Arrangement.spacedBy(12.dp)) {
                ActionCard(
                    "+",
                    "Nueva venta",
                    Modifier.weight(1f)
                ) { onNavigate("Ventas") }

                ActionCard(
                    "▣",
                    "Productos",
                    Modifier.weight(1f)
                ) { onNavigate("Productos") }
            }

            Row(horizontalArrangement = Arrangement.spacedBy(12.dp)) {
                ActionCard(
                    "♙",
                    "Clientes",
                    Modifier.weight(1f)
                ) { onNavigate("Clientes") }

                ActionCard(
                    "◷",
                    "Historial",
                    Modifier.weight(1f)
                ) { onNavigate("Reportes") }
            }
            Row(horizontalArrangement = Arrangement.spacedBy(12.dp)) {
                ActionCard(
                    "⚙",
                    "Producción",
                    Modifier.weight(1f)
                ) { onNavigate("Produccion") }

                Spacer(modifier = Modifier.weight(1f))
            }
        }

        Text(
            "Prestige Clean  •  Gestión inteligente de tu negocio",
            color = SoftBlue,
            fontSize = 11.sp,
            modifier = Modifier
                .fillMaxWidth()
                .padding(top = 4.dp),
        )
    }
}

@Composable
fun MetricCard(
    title: String,
    value: String,
    description: String,
    modifier: Modifier = Modifier
) {
    Card(
        modifier = modifier,
        shape = RoundedCornerShape(18.dp),
        colors = CardDefaults.cardColors(
            containerColor = CardBlue
        )
    ) {
        Column(
            modifier = Modifier.padding(14.dp),
            verticalArrangement = Arrangement.spacedBy(7.dp)
        ) {
            Text(
                title,
                color = SoftBlue,
                fontSize = 12.sp
            )

            Text(
                value,
                color = White,
                fontSize = 23.sp,
                fontWeight = FontWeight.Bold
            )

            Text(
                description,
                color = SoftBlue,
                fontSize = 10.sp
            )
        }
    }
}

@Composable
fun ActionCard(
    symbol: String,
    title: String,
    modifier: Modifier = Modifier,
    onClick: () -> Unit
) {
    Card(
        modifier = modifier.clickable { onClick() },
        shape = RoundedCornerShape(18.dp),
        colors = CardDefaults.cardColors(
            containerColor = CardBlue
        )
    ) {
        Column(
            modifier = Modifier
                .fillMaxWidth()
                .padding(16.dp),
            verticalArrangement = Arrangement.spacedBy(12.dp)
        ) {
            Text(
                symbol,
                color = AccentBlue,
                fontSize = 27.sp,
                fontWeight = FontWeight.Bold
            )

            Text(
                title,
                color = White,
                fontSize = 14.sp,
                fontWeight = FontWeight.SemiBold
            )
        }
    }
}

@Composable
fun SectionScreen(
    title: String,
    description: String,
    symbol: String,
    modifier: Modifier = Modifier
) {
    Column(
        modifier = modifier
            .fillMaxSize()
            .background(Navy)
            .padding(24.dp),
        verticalArrangement = Arrangement.spacedBy(16.dp)
    ) {
        Text(
            title,
            color = White,
            fontSize = 26.sp,
            fontWeight = FontWeight.Bold
        )

        Box(
            modifier = Modifier
                .size(70.dp)
                .background(CardBlue, RoundedCornerShape(20.dp)),
            contentAlignment = Alignment.Center
        ) {
            Text(
                symbol,
                color = AccentBlue,
                fontSize = 32.sp
            )
        }

        Text(
            description,
            color = SoftBlue,
            fontSize = 15.sp
        )

        Text(
            "Estamos preparando esta sección.",
            color = White,
            fontSize = 13.sp
        )
    }
}

32,059 chars · codepastes.com