sin-titulo

Pasted Nov 8, 2026, 6:23 AM1 file827 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/zzgsa1bqrga1kx2t
sin-titulo

package com.negocio.desinfectantes.data

import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import kotlinx.coroutines.flow.Flow
import androidx.room.Transaction
import java.util.UUID

@Dao
interface ProductoDao {

    // PRODUCTOS

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun guardarProducto(producto: ProductoEntity)
    @Query("""
    UPDATE productos
    SET nombre = :nombre,
        tipo = :tipo
    WHERE id = :productoId
    """)
    suspend fun actualizarProducto(
        productoId: String,
        nombre: String,
        tipo: String
    )

    @Query("SELECT * FROM productos WHERE activo = 1 ORDER BY nombre ASC")
    fun obtenerProductos(): Flow<List<ProductoEntity>>

    @Query("SELECT * FROM productos ORDER BY nombre ASC")
    fun obtenerTodosProductos(): Flow<List<ProductoEntity>>

    @Query("SELECT * FROM productos WHERE activo = 0 ORDER BY nombre ASC")
    fun obtenerProductosInactivos(): Flow<List<ProductoEntity>>

    @Query("SELECT * FROM productos WHERE id = :id LIMIT 1")
    suspend fun buscarProducto(id: String): ProductoEntity?

    @Query("UPDATE productos SET activo = 0 WHERE id = :productoId")
    suspend fun desactivarProducto(productoId: String)

    @Query("UPDATE productos SET activo = 1 WHERE id = :productoId")
    suspend fun reactivarProducto(productoId: String)


    // AROMAS

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun guardarAroma(aroma: AromaEntity)

    @Query("SELECT * FROM aromas WHERE productoId = :productoId AND activo = 1 ORDER BY nombre ASC")
    fun obtenerAromas(productoId: String): Flow<List<AromaEntity>>

    @Query("SELECT * FROM aromas ORDER BY nombre ASC")
    fun obtenerTodosAromas(): Flow<List<AromaEntity>>

    @Query("SELECT * FROM aromas WHERE productoId = :productoId AND activo = 0 ORDER BY nombre ASC")
    fun obtenerAromasInactivos(productoId: String): Flow<List<AromaEntity>>

    @Query("UPDATE aromas SET activo = 1 WHERE id = :aromaId")
    suspend fun reactivarAroma(aromaId: String)

    @Query("SELECT * FROM aromas WHERE id = :aromaId LIMIT 1")
    suspend fun buscarAroma(aromaId: String): AromaEntity?

    @Query("SELECT * FROM aromas WHERE activo = 0 ORDER BY nombre ASC")
    fun obtenerTodosAromasInactivos(): Flow<List<AromaEntity>>

    @Query("SELECT COUNT(*) FROM presentaciones WHERE aromaId = :aromaId")
    suspend fun contarPresentacionesDeAroma(aromaId: String): Int

    @Query("UPDATE aromas SET activo = 0 WHERE id = :aromaId")
    suspend fun eliminarAroma(aromaId: String)
    @Query("""
    UPDATE aromas
    SET nombre = :nuevoNombre
    WHERE id = :aromaId
    """)
    suspend fun actualizarAroma(
        aromaId: String,
        nuevoNombre: String
    )


    // PRESENTACIONES

    @Query("SELECT * FROM presentaciones WHERE activo = 1 ORDER BY nombre ASC")
    fun obtenerTodasPresentacionesActivas(): Flow<List<PresentacionEntity>>

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun guardarPresentacion(presentacion: PresentacionEntity)

    @Query("SELECT * FROM presentaciones WHERE aromaId = :aromaId AND activo = 1")
    fun obtenerPresentaciones(aromaId: String): Flow<List<PresentacionEntity>>

    @Query("SELECT * FROM presentaciones WHERE aromaId = :aromaId AND activo = 0")
    fun obtenerPresentacionesInactivas(aromaId: String): Flow<List<PresentacionEntity>>

    @Query("UPDATE presentaciones SET activo = 1 WHERE id = :presentacionId")
    suspend fun reactivarPresentacion(presentacionId: String)

    @Query("SELECT * FROM presentaciones WHERE activo = 0 ORDER BY nombre ASC")
    fun obtenerTodasPresentacionesInactivas(): Flow<List<PresentacionEntity>>

    @Query("UPDATE presentaciones SET activo = 0 WHERE id = :presentacionId")
    suspend fun eliminarPresentacion(presentacionId: String)


    // INVENTARIO

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun guardarInventario(inventario: InventarioEntity)

    @Query("SELECT COUNT(*) FROM aromas WHERE productoId = :productoId AND activo = 1")
    suspend fun contarAromasDeProducto(productoId: String): Int

    @Query("""
        SELECT COALESCE(SUM(cantidadDisponible + cantidadReservada), 0)
        FROM inventario
        WHERE presentacionId = :presentacionId
    """)
    suspend fun contarExistenciasPresentacion(presentacionId: String): Int

    @Query("SELECT * FROM inventario WHERE presentacionId = :presentacionId")
    fun obtenerInventario(presentacionId: String): Flow<List<InventarioEntity>>

    @Query("SELECT * FROM inventario ORDER BY sede ASC")
    fun obtenerTodoInventario(): Flow<List<InventarioEntity>>

    @Query("""
        SELECT * FROM inventario
        WHERE presentacionId = :presentacionId AND sede = :sede
        LIMIT 1
    """)
    suspend fun buscarInventario(
        presentacionId: String,
        sede: String
    ): InventarioEntity?


    // PRODUCCIONES

    @Insert
    suspend fun guardarProduccion(produccion: ProduccionEntity)

    @Query("SELECT * FROM producciones ORDER BY fechaCreacion DESC")
    fun obtenerProducciones(): Flow<List<ProduccionEntity>>

    @Query("SELECT * FROM producciones WHERE id = :produccionId LIMIT 1")
    suspend fun buscarProduccion(produccionId: String): ProduccionEntity?


    // DISTRIBUCIONES DE PRODUCCION

    @Insert
    suspend fun guardarDistribucion(
        distribucion: DistribucionProduccionEntity
    )

    @Query("""
        SELECT * FROM distribuciones_produccion
        WHERE produccionId = :produccionId
        ORDER BY sede ASC
    """)
    fun obtenerDistribuciones(
        produccionId: String
    ): Flow<List<DistribucionProduccionEntity>>


    // MOVIMIENTOS DE INVENTARIO

    @Insert
    suspend fun guardarMovimiento(
        movimiento: MovimientoInventarioEntity
    )

    @Query("""
        SELECT * FROM movimientos_inventario
        ORDER BY fechaCreacion DESC
    """)
    fun obtenerTodosMovimientos(): Flow<List<MovimientoInventarioEntity>>

    @Query("""
        SELECT * FROM movimientos_inventario
        WHERE presentacionId = :presentacionId
        ORDER BY fechaCreacion DESC
    """)
    fun obtenerMovimientosPresentacion(
        presentacionId: String
    ): Flow<List<MovimientoInventarioEntity>>

    @Query("""
        SELECT * FROM movimientos_inventario
        WHERE sede = :sede
        ORDER BY fechaCreacion DESC
    """)
    fun obtenerMovimientosSede(
        sede: String
    ): Flow<List<MovimientoInventarioEntity>>

    // GUARDADO COMPLETO DE PRODUCCION

    @Insert
    suspend fun guardarDistribuciones(
        distribuciones: List<DistribucionProduccionEntity>
    )

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun guardarInventarios(
        inventarios: List<InventarioEntity>
    )

    @Insert
    suspend fun guardarMovimientos(
        movimientos: List<MovimientoInventarioEntity>
    )

    @Transaction
    suspend fun registrarProduccionCompleta(
        produccion: ProduccionEntity,
        distribuciones: List<DistribucionProduccionEntity>,
        inventarios: List<InventarioEntity>,
        movimientos: List<MovimientoInventarioEntity>
    ) {
        guardarProduccion(produccion)
        guardarDistribuciones(distribuciones)
        guardarInventarios(inventarios)
        guardarMovimientos(movimientos)
    }

    // CLIENTES

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun guardarCliente(cliente: ClienteEntity)

    @Query("SELECT * FROM clientes WHERE activo = 1 ORDER BY nombre ASC")
    fun obtenerClientes(): Flow<List<ClienteEntity>>

    @Query("SELECT * FROM clientes ORDER BY nombre ASC")
    fun obtenerTodosClientes(): Flow<List<ClienteEntity>>

    @Query("SELECT * FROM clientes WHERE id = :clienteId LIMIT 1")
    suspend fun buscarCliente(clienteId: String): ClienteEntity?

    @Query("UPDATE clientes SET activo = 0 WHERE id = :clienteId")
    suspend fun desactivarCliente(clienteId: String)

    @Query("UPDATE clientes SET activo = 1 WHERE id = :clienteId")
    suspend fun reactivarCliente(clienteId: String)

    // VENTAS

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun guardarVenta(venta: VentaEntity)

    @Query("SELECT * FROM ventas ORDER BY fechaCreacion DESC")
    fun obtenerVentas(): Flow<List<VentaEntity>>

    @Query("SELECT * FROM ventas WHERE id = :ventaId LIMIT 1")
    suspend fun buscarVenta(ventaId: String): VentaEntity?

    // DETALLES DE VENTA

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun guardarDetalleVenta(detalle: DetalleVentaEntity)

    @Query("SELECT * FROM detalles_venta WHERE ventaId = :ventaId")
    fun obtenerDetallesVenta(ventaId: String): Flow<List<DetalleVentaEntity>>

    // PAGOS Y ABONOS

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun guardarPagoVenta(pago: PagoVentaEntity)

    @Query("SELECT * FROM pagos_venta WHERE ventaId = :ventaId ORDER BY fechaCreacion ASC")
    fun obtenerPagosVenta(ventaId: String): Flow<List<PagoVentaEntity>>

    @Query("SELECT * FROM pagos_venta ORDER BY fechaCreacion DESC")
    fun obtenerTodosLosPagos(): Flow<List<PagoVentaEntity>>


    // REGISTRO COMPLETO DE VENTAS

    @Insert
    suspend fun guardarDetallesVenta(
        detalles: List<DetalleVentaEntity>
    )

    @Insert
    suspend fun guardarPagosVenta(
        pagos: List<PagoVentaEntity>
    )

    @Insert
    suspend fun guardarMovimientosVenta(
        movimientos: List<MovimientoInventarioEntity>
    )

    // DESCONTAR EXISTENCIAS DE UNA SEDE

    @Query("""
        UPDATE inventario
        SET cantidadDisponible = cantidadDisponible - :cantidad,
            fechaActualizacion = :fecha
        WHERE presentacionId = :presentacionId
        AND sede = :sede
        AND cantidadDisponible >= :cantidad
    """)
    suspend fun descontarExistencias(
        presentacionId: String,
        sede: String,
        cantidad: Int,
        fecha: Long
    ): Int

    // GUARDAR VENTA Y SUS MOVIMIENTOS EN UNA SOLA TRANSACCION

    @Transaction
    suspend fun registrarVentaCompleta(
        venta: VentaEntity,
        detalles: List<DetalleVentaEntity>,
        pagos: List<PagoVentaEntity>,
        movimientos: List<MovimientoInventarioEntity>,
        descontarInventario: Boolean
    ) {
        check(venta.estadoEntrega != "PENDIENTE_ENTREGA") {
            "Un pedido debe pasar por el proceso de reserva antes de quedar pendiente de entrega."
        }

        check(descontarInventario == (venta.estadoEntrega == "ENTREGADA")) {
            "El estado de entrega no coincide con la operación de inventario."
        }
        if (descontarInventario) {
            for (detalle in detalles) {
                val resultado = descontarExistencias(
                    presentacionId = detalle.presentacionId,
                    sede = venta.sede,
                    cantidad = detalle.cantidad,
                    fecha = System.currentTimeMillis()
                )

                check(resultado == 1) {
                    "No hay existencias suficientes para completar la venta."
                }
            }
        }

        guardarVenta(venta)
        guardarDetallesVenta(detalles)

        if (pagos.isNotEmpty()) {
            guardarPagosVenta(pagos)
        }

        if (descontarInventario && movimientos.isNotEmpty()) {
            guardarMovimientosVenta(movimientos)
        }
    }

    // CONSULTAR DETALLES DE UN PEDIDO

    @Query("""
        SELECT * FROM detalles_venta
        WHERE ventaId = :ventaId
    """)
    suspend fun buscarDetallesVenta(
        ventaId: String
    ): List<DetalleVentaEntity>


    // CAMBIAR ESTADO DE UN PEDIDO

    @Query("""
        UPDATE ventas
        SET estadoEntrega = :nuevoEstado
        WHERE id = :ventaId
        AND estadoEntrega = :estadoActual
    """)
    suspend fun actualizarEstadoEntrega(
        ventaId: String,
        estadoActual: String,
        nuevoEstado: String
    ): Int


    // RESERVAR EXISTENCIAS

    @Query("""
        UPDATE inventario
        SET cantidadDisponible = cantidadDisponible - :cantidad,
            cantidadReservada = cantidadReservada + :cantidad,
            fechaActualizacion = :fecha
        WHERE presentacionId = :presentacionId
        AND sede = :sede
        AND cantidadDisponible >= :cantidad
    """)
    suspend fun reservarExistencias(
        presentacionId: String,
        sede: String,
        cantidad: Int,
        fecha: Long
    ): Int


    // CONSUMIR EXISTENCIAS RESERVADAS AL ENTREGAR

    @Query("""
        UPDATE inventario
        SET cantidadReservada = cantidadReservada - :cantidad,
            fechaActualizacion = :fecha
        WHERE presentacionId = :presentacionId
        AND sede = :sede
        AND cantidadReservada >= :cantidad
    """)
    suspend fun consumirExistenciasReservadas(
        presentacionId: String,
        sede: String,
        cantidad: Int,
        fecha: Long
    ): Int


    // PASAR PEDIDO A PENDIENTE DE ENTREGA

    @Transaction
    suspend fun reservarPedidoParaEntrega(
        ventaId: String
    ) {
        val venta = buscarVenta(ventaId)
            ?: error("No se encontró el pedido.")

        check(venta.estadoEntrega == "PENDIENTE_FABRICACION") {
            "El pedido no está pendiente de fabricación."
        }

        val detalles = buscarDetallesVenta(ventaId)

        check(detalles.isNotEmpty()) {
            "El pedido no tiene productos registrados."
        }

        val fecha = System.currentTimeMillis()

        val movimientos = mutableListOf<MovimientoInventarioEntity>()

        val detallesAgrupados = detalles.groupBy { it.presentacionId }

        for ((presentacionId, lineas) in detallesAgrupados) {

            val cantidad = lineas.sumOf { it.cantidad }

            val resultado = reservarExistencias(
                presentacionId = presentacionId,
                sede = venta.sede,
                cantidad = cantidad,
                fecha = fecha
            )

            check(resultado == 1) {
                "No hay existencias suficientes para reservar el pedido."
            }

            movimientos.add(
                MovimientoInventarioEntity(
                    id = UUID.randomUUID().toString(),
                    presentacionId = presentacionId,
                    sede = venta.sede,
                    tipoMovimiento = "RESERVA_PEDIDO",
                    cantidad = cantidad,
                    referenciaId = ventaId,
                    observacion = "Reserva de unidades para pedido"
                )
            )
        }

        guardarMovimientosVenta(movimientos)

        val actualizado = actualizarEstadoEntrega(
            ventaId = ventaId,
            estadoActual = "PENDIENTE_FABRICACION",
            nuevoEstado = "PENDIENTE_ENTREGA"
        )

        check(actualizado == 1) {
            "No se pudo actualizar el estado del pedido."
        }
    }


    // CONFIRMAR ENTREGA DE UN PEDIDO RESERVADO

    @Transaction
    suspend fun entregarPedidoReservado(
        ventaId: String
    ) {
        val venta = buscarVenta(ventaId)
            ?: error("No se encontró el pedido.")

        check(venta.estadoEntrega == "PENDIENTE_ENTREGA") {
            "El pedido no está pendiente de entrega."
        }

        val detalles = buscarDetallesVenta(ventaId)

        check(detalles.isNotEmpty()) {
            "El pedido no tiene productos registrados."
        }

        val fecha = System.currentTimeMillis()

        val movimientos = mutableListOf<MovimientoInventarioEntity>()

        val detallesAgrupados = detalles.groupBy { it.presentacionId }

        for ((presentacionId, lineas) in detallesAgrupados) {

            val cantidad = lineas.sumOf { it.cantidad }

            val resultado = consumirExistenciasReservadas(
                presentacionId = presentacionId,
                sede = venta.sede,
                cantidad = cantidad,
                fecha = fecha
            )

            check(resultado == 1) {
                "No se encontraron todas las unidades reservadas."
            }

            movimientos.add(
                MovimientoInventarioEntity(
                    id = UUID.randomUUID().toString(),
                    presentacionId = presentacionId,
                    sede = venta.sede,
                    tipoMovimiento = "VENTA",
                    cantidad = cantidad,
                    referenciaId = ventaId,
                    observacion = "Salida por entrega de pedido"
                )
            )
        }

        guardarMovimientosVenta(movimientos)

        val actualizado = actualizarEstadoEntrega(
            ventaId = ventaId,
            estadoActual = "PENDIENTE_ENTREGA",
            nuevoEstado = "ENTREGADA"
        )

        check(actualizado == 1) {
            "No se pudo confirmar la entrega del pedido."
        }
    }


    @Query("""
    UPDATE inventario
    SET cantidadDisponible = cantidadDisponible + :cantidad,
        cantidadReservada = cantidadReservada - :cantidad,
        fechaActualizacion = :fecha
    WHERE presentacionId = :presentacionId
    AND sede = :sede
    AND cantidadReservada >= :cantidad
""")
    suspend fun liberarExistenciasReservadas(
        presentacionId: String,
        sede: String,
        cantidad: Int,
        fecha: Long
    ): Int

    @Transaction
    suspend fun cancelarPedido(ventaId: String) {
        val venta = buscarVenta(ventaId)
            ?: error("No se encontró el pedido.")

        check(
            venta.estadoEntrega == "PENDIENTE_FABRICACION" ||
                    venta.estadoEntrega == "PENDIENTE_ENTREGA"
        ) {
            "Este pedido no se puede cancelar."
        }

        val fecha = System.currentTimeMillis()
        val movimientos = mutableListOf<MovimientoInventarioEntity>()

        // Si ya estaba listo para entregar, liberamos las reservas.
        if (venta.estadoEntrega == "PENDIENTE_ENTREGA") {
            val detalles = buscarDetallesVenta(ventaId)

            check(detalles.isNotEmpty()) {
                "El pedido no tiene productos registrados."
            }

            val detallesAgrupados = detalles.groupBy { it.presentacionId }

            for ((presentacionId, lineas) in detallesAgrupados) {
                val cantidad = lineas.sumOf { it.cantidad }

                val resultado = liberarExistenciasReservadas(
                    presentacionId,
                    venta.sede,
                    cantidad,
                    fecha
                )

                check(resultado == 1) {
                    "No se pudieron liberar todas las unidades reservadas."
                }

                movimientos.add(
                    MovimientoInventarioEntity(
                        id = UUID.randomUUID().toString(),
                        presentacionId = presentacionId,
                        sede = venta.sede,
                        tipoMovimiento = "LIBERACION_RESERVA",
                        cantidad = cantidad,
                        referenciaId = ventaId,
                        observacion = "Liberación de existencias por cancelación del pedido"
                    )
                )
            }

            if (movimientos.isNotEmpty()) {
                guardarMovimientosVenta(movimientos)
            }
        }

        val actualizado = actualizarEstadoEntrega(
            ventaId,
            venta.estadoEntrega,
            "CANCELADA"
        )

        check(actualizado == 1) {
            "No se pudo cancelar el pedido."
        }
    }

    // ESTADISTICAS DEL INICIO

    // Total vendido durante el día
    @Query("""
        SELECT COALESCE(SUM(total), 0)
        FROM ventas
        WHERE fechaCreacion >= :inicioDia
        AND fechaCreacion < :finDia
        AND estadoEntrega != 'CANCELADA'
    """)
    fun obtenerVentasDelDia(
        inicioDia: Long,
        finDia: Long
    ): Flow<Double>


    // Dinero recibido durante el día
    @Query("""
        SELECT COALESCE(SUM(monto), 0)
        FROM pagos_venta
        WHERE fechaCreacion >= :inicioDia
        AND fechaCreacion < :finDia
        AND ventaId IN (
            SELECT id FROM ventas
            WHERE estadoEntrega != 'CANCELADA'
        )
    """)
    fun obtenerDineroRecibidoDelDia(
        inicioDia: Long,
        finDia: Long
    ): Flow<Double>


    // Saldo pendiente de cobro de todas las ventas no canceladas
    @Query("""
        SELECT COALESCE(
            SUM(MAX(
                v.total - COALESCE(
                    (SELECT SUM(p.monto)
                     FROM pagos_venta p
                     WHERE p.ventaId = v.id),
                    0
                ),
                0
            )),
            0
        )
        FROM ventas v
        WHERE v.estadoEntrega != 'CANCELADA'
    """)
    fun obtenerTotalPorCobrar(): Flow<Double>


    // Unidades disponibles en todas las sedes
    @Query("""
        SELECT COALESCE(SUM(cantidadDisponible), 0)
        FROM inventario
    """)
    fun obtenerInventarioDisponible(): Flow<Int>

}


package com.negocio.desinfectantes.data

import androidx.room.Entity
import androidx.room.PrimaryKey

@Entity(tableName = "producciones")
data class ProduccionEntity(
    @PrimaryKey
    val id: String,
    val presentacionId: String,
    val cantidadProducida: Int,
    val fechaCreacion: Long = System.currentTimeMillis()
)

package com.negocio.desinfectantes.data

import androidx.room.Entity
import androidx.room.PrimaryKey

@Entity(tableName = "distribuciones_produccion")
data class DistribucionProduccionEntity(
    @PrimaryKey
    val id: String,
    val produccionId: String,
    val sede: String,
    val cantidad: Int,
    val fechaCreacion: Long = System.currentTimeMillis()
)


package com.negocio.desinfectantes.data

import androidx.room.Entity
import androidx.room.PrimaryKey

@Entity(tableName = "inventario")
data class InventarioEntity(
    @PrimaryKey
    val id: String,

    val presentacionId: String,

    val sede: String,

    val cantidadDisponible: Int = 0,

    val cantidadReservada: Int = 0,

    val fechaActualizacion: Long = System.currentTimeMillis()
)

package com.negocio.desinfectantes.data

import androidx.room.Entity
import androidx.room.PrimaryKey

@Entity(tableName = "movimientos_inventario")
data class MovimientoInventarioEntity(
    @PrimaryKey
    val id: String,
    val presentacionId: String,
    val sede: String,
    val tipoMovimiento: String,
    val cantidad: Int,
    val referenciaId: String? = null,
    val observacion: String? = null,
    val fechaCreacion: Long = System.currentTimeMillis()
)


package com.negocio.desinfectantes.data

import androidx.room.Entity
import androidx.room.PrimaryKey

@Entity(tableName = "presentaciones")
data class PresentacionEntity(
    @PrimaryKey
    val id: String,

    val aromaId: String,

    val nombre: String,

    val contenidoMl: Int,

    val precio: Double,

    val activo: Boolean = true,

    val fechaCreacion: Long = System.currentTimeMillis()
)


package com.negocio.desinfectantes.data

import androidx.room.Database
import androidx.room.RoomDatabase

@Database(
    entities = [
        ProductoEntity::class,
        AromaEntity::class,
        PresentacionEntity::class,
        InventarioEntity::class,
        ProduccionEntity::class,
        DistribucionProduccionEntity::class,
        MovimientoInventarioEntity::class,
        ClienteEntity::class,
        VentaEntity::class,
        DetalleVentaEntity::class,
        PagoVentaEntity::class
    ],
    version = 5,
    exportSchema = false
)
abstract class PrestigeDatabase : RoomDatabase() {
    abstract fun productoDao(): ProductoDao
}

23,764 chars · codepastes.com