JM Contabilidad — Sistema central de empleados, nóminas y gastos Joyerías Meza
Contexto
Sistema central de administración de Joyerías Meza (gastos, nóminas, comisiones, cobranza, empleados, sucursales). Hosteado en gastos.joyeriameza.com. Es el sistema más activo de la cadena JM y se conecta a las DBs de los otros proyectos JM como satélites.
Sergio lo clonó al hub el 2026-05-14 cuando analizaba el bug de Lidia en jm-checador — necesitaba mapear quién administra qué para proponer un fix permanente sensato.
Tareas pendientes
(Ninguna activa derivada de este análisis — ver jm-checador para acciones concretas.)
Bug conocido: pivot cobranzas_cortes_cobradores no se limpia al borrar un corte
El modelo CorteCobrador::boot() cascadea deleted a movimiento_caja y gasto, pero no toca el pivot. La migración 2018_09_17_133633_create_cobranzas_cortes_cobradores.php define corte_cobrador_id como unsignedInteger con ->index(), sin FK ni CASCADE. Al borrar un corte por UI, las filas del pivot quedan huérfanas (referencian un corte que ya no existe).
Fix permanente posible (no implementado, fuera de scope):
- Opción A: agregar FK con
ON DELETE CASCADEen migración nueva. - Opción B: en el event
deleteddel modelo, agregar$corteCobrador->cobranzas_cobradores()->detach()antes de manipular movimiento_caja/gasto.
Por ahora, cualquier borrado manual desde tinker debe limpiar el pivot a mano (ver bitácora 2026-05-15).
En progreso
(Nada activo aquí.)
Notas técnicas
Stack
- Framework: Laravel (con Vue.js inline templates + Bootstrap).
- PHP: 7.4 en producción.
- Hosting:
gastos.joyeriameza.com(Plesk, userjmeza). - Bare repo de deploy:
~/git/jm_contabilidad.
Remotes locales
origin git@github.com:sevaor/jm-contabilidad.git
production jmeza@gastos.joyeriameza.com:~/git/jm_contabilidad
Múltiples conexiones de DB (config/database.php)
mysql(default) — DB propia de contabilidad (empleados, nominas, gastos, sucursales, comisiones, …).mysql_admin— DB deadmin.joyeriameza.com, sistema legacy. ModeloEmpleadoAdminapunta aquí. Solo se usó para migrar empleados (one-shot viaJMMigrarEmplados).checador(DBjmeza_checadorenchecador.joyeriameza.com) — accede ahoras_checadas,checadas,horarios_empleadas,horarios_nomina,variables,usuarios. Modelos:ChecadaChecador,HoraChecadaChecador.sistema_ventas— DB del POS (tienda en linea). Modelo: muchos*SistemaVentas. Se lee para comisiones.tienda_en_linea, otras — para el sitio público.
Relación con jm-checador (clave para el bug de Lidia)
La tabla empleados es ÚNICA y vive aquí (en la DB de contabilidad). jm-checador no tiene tabla propia de empleados — se conecta a esta vía una conexión nombrada admin (su EmpleadosTable.php declara defaultConnectionName() { return 'admin'; }). Lo confirma también que jm-checador no tiene migración CreateEmpleados y que su modelo respeta deleted_at IS null (soft delete estilo Laravel).
| Aspecto | jm-checador (CakePHP) | jm-contabilidad (Laravel) |
|---|---|---|
¿Tiene tabla empleados propia? | No — usa conexión admin → esta DB | Sí, es la dueña |
empleados.descanso | Lee y escribe vía connection admin | Tabla propia |
Form que edita descanso | Solo a superadmin (forma_empleados.ctp:15 lo envuelve en if($usuario->admin)) | No lo edita a nadie (empleados/forma.blade.php no lo incluye) |
| Otras tablas (checadas, horas_checadas, horarios_empleadas, …) | En jmeza_checador | jm-contabilidad las lee vía connection checador |
Conclusión: la fuente de verdad de descanso es esta DB (jm-contabilidad), pero los dos sistemas la editan/usan diferente:
- jm-checador la lee/escribe pero solo expone el campo a superadmin global.
- jm-contabilidad no la expone en su propio form de empleados.
El fix permanente puede vivir en cualquiera de los dos proyectos (o ambos). El fix corto (UPDATE) se hace contra esta DB.
Modelos que cruzan DBs
App\Empleado::horasTrabajadasSemanaNomina()→ leeDB::connection('checador')->table('horas_checadas'). Es la única integración activa con el checador.App\ChecadaChecadoryApp\HoraChecadaChecadordeclaranprotected $connection = 'checador'. Solo lectura para reportes/nómina.
ANTIGRAVITY.md
No tiene. Proyecto Laravel pre-Boost; convenciones de boilerplate no aplican.
Bitácora
2026-05-14
- Pidió Sergio: clonar y analizar antes de proponer fix permanente para el bug de Lidia en jm-checador.
- Hice:
- Cloné
git@github.com:sevaor/jm-contabilidad.giten~/code/jm-contabilidad. - Agregué remote
productionajmeza@gastos.joyeriameza.com:~/git/jm_contabilidad. - Mapeé conexiones de DB y la relación con el checador.
- Confirmé que el formulario de empleados aquí NO edita
descansoni horarios semanales, y que las dos tablasempleadosestán duplicadas sin sync entre BDs.
- Cloné
- Conclusión que llevo a jm-checador: el fix permanente debe vivir en jm-checador (donde está la fuente de verdad de
empleados.descansopara el bloqueo), no en jm-contabilidad, porque ahí ni siquiera se edita ese campo y no hay sync.
2026-05-15
- Pidió Sergio (reenvío de admin Joyerías Meza): borrar corte de cobrador folio 707 del 13-mayo (no puede ella sola por UI).
- Diagnóstico:
- “Folio” en UI =
cortes_cobradores.id(resources/views/cortes_cobradores/show.blade.php:21). 707 = id 707. - Corte 707: 2026-05-13 09:08, cobrador “Jorge de la Rosa” (id 3), creado por cajera (usuario id 4), comisiones $310. Asociados: gasto id 15801 ($310 categoría Cobranza), sin movimiento_caja, 6 filas en pivot
cobranzas_cortes_cobradores. - Policy
CorteCobradorPolicy::eliminarexige tres condiciones:permiso_cobranza,usuario_idmatch, ser el último corte del cobrador. La admin tenía 1/3 (permiso) pero fallaba en las otras dos: no lo creó ella, y existe el corte 708 posterior (09:20 mismo día, mismo cobrador). - Lectura del incidente: la cajera creó 707, vio que metió la cobranza_cobrador #8 en vez de la #7, creó 708 para corregir. La cobranza_cobrador #10 quedó duplicada en ambos cortes → $310 contado dos veces como gasto. Borrar 707 deshace el duplicado y libera la #8.
- “Folio” en UI =
- Hice (tinker prod, transacción atómica):
- Snapshot JSON del corte + pivot + gasto en
jmeza:/tmp/corte_707_backup_20260515_152134.json. DELETE FROM cobranzas_cortes_cobradores WHERE corte_cobrador_id=707(6 filas — el eventdeleteddel modelo no limpia el pivot, ver “Bug conocido” arriba).CorteCobrador::find(707)->delete()→ cascadea y borra gasto 15801.
- Snapshot JSON del corte + pivot + gasto en
- Verificación: corte 707 ya no existe, gasto 15801 ya no existe, 0 filas restantes en pivot para corte 707, sin movimiento_caja huérfano. Cortes recientes del cobrador 3 ahora arrancan en 708 correctamente.