Scan to open on your phone
Point a camera at the QR code - it opens this exact paste, no app needed.
https://codepastes.com/zzgs810qgrd11ya3untitledPlain Text
@Transaction
suspend fun editarLoteCompleto(
loteId: String,
detallesNuevos: List<LoteDetalleEntity>,
inventariosLoteNuevos: List<LoteInventarioEntity>
) {
check(puedeEditarProduccionLote(loteId)) {
"No se puede editar este lote porque tiene movimientos, ventas o reservas asociados."
}
val loteOriginal = obtenerLotePorId(loteId)
?: error("No se encontró el lote de producción.")
for (detalle in detallesNuevos) {
val presentacion = obtenerPresentacionPorId(
detalle.presentacionId
) ?: error("No se encontró una de las presentaciones.")
val aroma = obtenerAromaPorId(presentacion.aromaId)
?: error("No se encontró el aroma de la presentación.")
check(aroma.productoId == loteOriginal.productoId) {
"No puedes agregar presentaciones de otro producto."
}
}
val detallesActuales = obtenerDetallesLote(loteId)
val inventariosActuales = obtenerInventariosDeLote(loteId)
check(detallesActuales.isNotEmpty()) {
"No se encontraron detalles de producción para este lote."
}
check(detallesNuevos.isNotEmpty()) {
"El lote debe conservar al menos una presentación."
}
check(detallesNuevos.all {
it.loteId == loteId && it.cantidadFabricada > 0
}) {
"Hay cantidades de producción incorrectas."
}
check(detallesNuevos.map { it.presentacionId }.distinct().size ==
detallesNuevos.size) {
"No puedes repetir una presentación."
}
check(inventariosLoteNuevos.all {
it.loteId == loteId &&
it.cantidadDisponible > 0 &&
it.cantidadReservada == 0 &&
it.sede.isNotBlank()
}) {
"Hay una distribución incorrecta."
}
val clavesNuevas = inventariosLoteNuevos.map {
"${it.presentacionId}|${it.sede}"
}
check(clavesNuevas.distinct().size == clavesNuevas.size) {
"Hay distribuciones duplicadas."
}
val clavesActuales = inventariosActuales.map {
"${it.presentacionId}|${it.sede}"
}
check(clavesActuales.distinct().size == clavesActuales.size) {
"El inventario actual del lote contiene distribuciones duplicadas."
}
check(inventariosActuales.all {
it.cantidadDisponible >= 0 && it.cantidadReservada == 0
}) {
"El inventario actual contiene cantidades reservadas o incorrectas."
}
val presentacionesNuevas =
detallesNuevos.map { it.presentacionId }.toSet()
val presentacionesDistribuidas =
inventariosLoteNuevos.map { it.presentacionId }.toSet()
check(presentacionesNuevas == presentacionesDistribuidas) {
"Las presentaciones fabricadas no coinciden con las distribuidas."
}
for (detalle in detallesNuevos) {
val cantidadDistribuida = inventariosLoteNuevos
.filter { it.presentacionId == detalle.presentacionId }
.sumOf { it.cantidadDisponible }
check(cantidadDistribuida == detalle.cantidadFabricada) {
"La distribución no coincide con la cantidad fabricada."
}
}
val presentacionesActuales =
detallesActuales.map { it.presentacionId }.toSet()
check(inventariosActuales.all {
it.presentacionId in presentacionesActuales
}) {
"El inventario actual contiene presentaciones que no pertenecen al lote."
}
for (detalle in detallesActuales) {
val cantidadActual = inventariosActuales
.filter { it.presentacionId == detalle.presentacionId }
.sumOf { it.cantidadDisponible + it.cantidadReservada }
check(cantidadActual == detalle.cantidadFabricada) {
"El inventario actual no coincide con la producción registrada."
}
}
val actualesPorClave = inventariosActuales.associateBy {
"${it.presentacionId}|${it.sede}"
}
val nuevasPorClave = inventariosLoteNuevos.associateBy {
"${it.presentacionId}|${it.sede}"
}
val todasLasClaves =
(actualesPorClave.keys + nuevasPorClave.keys).toSet()
val fecha = System.currentTimeMillis()
val movimientos = mutableListOf<MovimientoInventarioEntity>()
// Ajustar inventario general e inventario FIFO.
for (clave in todasLasClaves) {
val inventarioActual = actualesPorClave[clave]
val inventarioNuevo = nuevasPorClave[clave]
val presentacionId =
inventarioNuevo?.presentacionId
?: inventarioActual!!.presentacionId
val sede =
inventarioNuevo?.sede
?: inventarioActual!!.sede
val cantidadAnterior =
inventarioActual?.cantidadDisponible ?: 0
val cantidadNueva =
inventarioNuevo?.cantidadDisponible ?: 0
val diferencia = cantidadNueva - cantidadAnterior
if (diferencia == 0) continue
// Actualizar inventario general.
val inventarioGeneral = buscarInventario(
presentacionId = presentacionId,
sede = sede
)
if (inventarioGeneral == null) {
check(diferencia > 0) {
"No existe inventario general para retirar unidades."
}
guardarInventario(
InventarioEntity(
id = UUID.randomUUID().toString(),
presentacionId = presentacionId,
sede = sede,
cantidadDisponible = diferencia,
cantidadReservada = 0,
fechaActualizacion = fecha
)
)
} else {
val actualizado = ajustarInventarioDisponible(
presentacionId = presentacionId,
sede = sede,
diferencia = diferencia,
fecha = fecha
)
check(actualizado == 1) {
"No se pudo ajustar el inventario general. Puede que no haya existencias suficientes."
}
}
// Actualizar inventario individual del lote.
if (inventarioActual != null) {
val actualizado = ajustarInventarioLote(
inventarioLoteId = inventarioActual.id,
diferencia = diferencia
)
check(actualizado == 1) {
"No se pudo ajustar el inventario FIFO del lote."
}
} else if (cantidadNueva > 0) {
guardarInventariosLote(
listOf(
inventarioNuevo!!.copy(
id = UUID.randomUUID().toString(),
loteId = loteId,
cantidadReservada = 0
)
)
)
}
// Registrar el ajuste sin borrar el historial anterior.
movimientos.add(
MovimientoInventarioEntity(
id = UUID.randomUUID().toString(),
presentacionId = presentacionId,
sede = sede,
tipoMovimiento = if (diferencia > 0)
"AJUSTE_PRODUCCION_ENTRADA"
else
"AJUSTE_PRODUCCION_SALIDA",
cantidad = kotlin.math.abs(diferencia),
referenciaId = loteId,
observacion = "Ajuste de producción del lote"
)
)
}
// Actualizar cantidades de presentaciones existentes.
val detallesActualesPorPresentacion =
detallesActuales.associateBy { it.presentacionId }
for (detalleNuevo in detallesNuevos) {
val detalleActual =
detallesActualesPorPresentacion[detalleNuevo.presentacionId]
if (detalleActual != null) {
val actualizado = actualizarCantidadDetalleLote(
detalleId = detalleActual.id,
loteId = loteId,
cantidad = detalleNuevo.cantidadFabricada
)
check(actualizado == 1) {
"No se pudo actualizar una presentación del lote."
}
} else {
// Agregar una presentación nueva.
guardarDetallesLote(
listOf(
detalleNuevo.copy(
id = UUID.randomUUID().toString(),
loteId = loteId
)
)
)
}
}
// Retirar presentaciones que ya no pertenecen al lote.
for (detalleActual in detallesActuales) {
if (detalleActual.presentacionId !in presentacionesNuevas) {
val eliminado = eliminarDetalleLote(
detalleId = detalleActual.id,
loteId = loteId
)
check(eliminado == 1) {
"No se pudo retirar una presentación del lote."
}
}
}
if (movimientos.isNotEmpty()) {
guardarMovimientos(movimientos)
}
}9,842 chars · codepastes.com