Visión general
El núcleo transaccional de Oxitesa está escrito en MySQL, no en TypeScript. Aceptar un remito,
cerrarlo, mover un suministro entre entidades y validar stock son stored procedures. TypeScript arma
el JSON de líneas, hace el CALL, y traduce el errorCode que vuelve.
Es la decisión más contraintuitiva del sistema y la que más confunde a quien llega nuevo: buscás la lógica en un service y no está ahí.
Por qué en SQL
Un cierre de remito toca SUPPLIES, SUPPLYMOVEMENT, SUPPLYMOVEMENTDETAIL,
SUPPLYMOVEMENTHISTORY, SUPPLIESRESERVATION, PATIENTSUPPLIES y DELIVERYNOTESDETAIL en una sola
operación que tiene que ser atómica. Resolverlo en un solo procedimiento dentro del motor evita
docenas de roundtrips y deja la consistencia garantizada por la transacción de MySQL.
Dónde vive el código
oxitesa-backend/migrations/finances/
├── store/ ← los SPs vigentes (+ store/deprecated/)
├── views/ ← views de stock (+ views/deprecated/)
├── fix/ ← parches incrementales, uno por bug, con fecha en el nombre
└── test/ ← tests de integración SQL (transacción + ROLLBACK)
store/ y fix/ no se pisanUn archivo de fix/ no reemplaza al de store/: los dos se mantienen. El de store/ es el
estado completo actual del SP; el de fix/ es el ALTER/CREATE OR REPLACE que se aplicó a un
ambiente concreto y queda como registro histórico. Cuando redeployás, aplicás el de store/.
El mapa
| SP | Rol |
|---|---|
sp_accept_delivery_note | Aceptación en depósito: ajusta líneas, computa needsReservation, crea reservas. Archivo store/sp_accept_delivery_note_v2.sql, pero el SP se llama sin el _v2 |
sp_validate_stocks_v2 | Valida disponibilidad leyendo la view view_stock_detail_by_origin (no la tabla SUPPLIES), con balance acumulado dentro de la misma corrida. Sin OUT params: deja la temp tmp_stock_validation |
sp_close_delivery_note_v2 | El dispatcher del cierre. Bifurca por DELIVERYNOTETYPE |
sp_close_delivery · sp_close_visit · sp_close_retirement | Los tres sub-SPs. Ver Cierre de remito |
sp_transfer_supply_to_entity | La primitiva atómica de movimiento de stock. Ver Stock y transferencias |
sp_reclassify_generic_supplies | Corrige el producto real de un supply genérico seriado usando el prefijo del número de serie |
sp_report_deliverys · sp_view_stocks_report · sp_view_stocks_serial_report | Reportes Excel. Ver Reportes |
Quién los llama
src/services/v3/DeliveryNoteServiceV3.ts. Ahí están los CALL y ahí se traducen los códigos de
error.
Errores: el contrato
Los SPs devuelven un errorCode textual. Ese código se traduce en el backend, con una key de
i18n por código (sp_error_<codigo>), en src/utils/SpErrorUtils.ts.
const message = translateSpErrorCode(result.errorCode, "No se pudo cerrar el remito.");
response.errorMessage(message);
- Una sola fuente de verdad para BullStock y la consola web.
- Cada código tiene su propio texto. Si el front mapeara varios códigos a un mensaje genérico se perdería el diagnóstico: el mensaje tiene que seguir diciendo qué falló.
- Un código nuevo o sin traducir se devuelve tal cual: nunca se pierde información.
Antes de esto los operarios veían alerts que decían literalmente SUPPLY_ERROR_QUANTITY.
El cliente solo elige el título del alert, que es puro UI y el backend no puede saberlo.
Códigos actuales: SUPPLY_ERROR_QUANTITY, PATIENT_SUPPLY_ERROR_QUANTITY,
DUPLICATE_DELIVERY_SERIAL, DUPLICATE_RETIRE_SERIAL, DUPLICATE_ONFLY_RETIRE_SERIAL,
DELIVERY_NOTE_DETAIL_MISMATCH, DELIVERY_NOTE_DETAIL_ERROR, DELIVERY_NOTE_NOT_FOUND,
INSUFFICIENT_STOCK, AMOUNT_ZERO_EXISTING_LINE, SUPPLY_NOT_FOUND_FOR_DELIVERY,
SUPPLY_NOT_FOUND_FOR_RETIRE, VISIT_SUPPLY_NOT_FOUND_FOR_DELIVERY,
VISIT_SUPPLY_NOT_FOUND_FOR_RETIRE, VISIT_DELIVERY_TRANSFER_ERROR,
VISIT_RETIRE_TRANSFER_ERROR, UNKNOWN_DELIVERY_NOTE_TYPE.
Agregar un código = agregar su key en es/translation.json. Hay un test que lo verifica.
Antes de asumir que hay un bug
Casi todos los "bugs" de cierre reportados resultaron ser parametrización. Recorré esto primero:
- ¿Existe el
EntityStatusque el SP busca, con eseCODEy eseENTITYCODE, en ese ambiente? - ¿El
ELEMENTS.ELEMENTTYPEestá bien cargado (descartable / inventariable / reutilizable)? - ¿El producto tiene SKU y
supplyTypecorrecto? - ¿Hay PS huérfanos (
SUPPLYIDenNULL) que expliquen el desajuste? - ¿El
changeTypede la línea es el que se solicitó originalmente?
migrations/finances/test/SP_BUGS_FOUND.md lleva la lista de bugs ya encontrados y su fix.
Cómo se testea
migrations/finances/test/transaction_close_delivery_note_edge_cases.sql es el catálogo grande.
Patrón: abrir transacción → crear entidades frescas → assertions → ROLLBACK, con naming
T<sección>.<n> y constantes resueltas por CODE, nunca por id hardcodeado.
Corre contra una base real (PRE), no contra los mocks de Jest. Ver Testing.