Skip to main content

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 pisan

Un 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

SPRol
sp_accept_delivery_noteAceptació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_v2Valida 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_v2El dispatcher del cierre. Bifurca por DELIVERYNOTETYPE
sp_close_delivery · sp_close_visit · sp_close_retirementLos tres sub-SPs. Ver Cierre de remito
sp_transfer_supply_to_entityLa primitiva atómica de movimiento de stock. Ver Stock y transferencias
sp_reclassify_generic_suppliesCorrige 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_reportReportes 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);
Por qué la traducción va en el backend y no en el cliente
  • 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 EntityStatus que el SP busca, con ese CODE y ese ENTITYCODE, en ese ambiente?
  • ¿El ELEMENTS.ELEMENTTYPE está bien cargado (descartable / inventariable / reutilizable)?
  • ¿El producto tiene SKU y supplyType correcto?
  • ¿Hay PS huérfanos (SUPPLYID en NULL) que expliquen el desajuste?
  • ¿El changeType de 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.