feat(web): serie, indicador y proyecto en la cabecera; dimensiones, proyecto, cuenta, referencia base y precio con IVA en el detalle
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
9f003ee4bf
commit
f66ef108cf
22 files changed
+698
-100
No files matched your search
@@ -0,0 +1,35 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { SL_RESOURCE_MAP, SL_RESOURCES } from './sl-resources.js';
|
||||
|
||||
describe('recursos del Service Layer', () => {
|
||||
it('no incluye jamás EmployeesInfo (tiene U_WEB_PASS)', () => {
|
||||
expect(SL_RESOURCE_MAP.has('EmployeesInfo')).toBe(false);
|
||||
});
|
||||
|
||||
it('los nombres no se repiten', () => {
|
||||
expect(new Set(SL_RESOURCES.map((r) => r.name)).size).toBe(SL_RESOURCES.length);
|
||||
});
|
||||
|
||||
it('las series de un documento se piden con una acción de solo consulta, para cualquier sesión', () => {
|
||||
const r = SL_RESOURCE_MAP.get('SeriesService_GetDocumentSeries')!;
|
||||
expect(r.methods).toEqual(['POST']);
|
||||
expect(r.write).toBeNull();
|
||||
expect(r.read).toBeNull();
|
||||
});
|
||||
|
||||
it('dimensiones, proyectos y cuentas son catálogos de solo lectura', () => {
|
||||
for (const name of ['DistributionRules', 'Projects', 'ChartOfAccounts', 'AdditionalExpenses', 'ItemGroups', 'BusinessPartnerGroups']) {
|
||||
const r = SL_RESOURCE_MAP.get(name)!;
|
||||
expect(r.methods, name).toEqual(['GET']);
|
||||
expect(r.read, name).toBeNull();
|
||||
}
|
||||
});
|
||||
|
||||
it('las cuatro pantallas de facturación de venta comparten Invoices con los permisos de todas', () => {
|
||||
const r = SL_RESOURCE_MAP.get('Invoices')!;
|
||||
for (const p of ['/ventas/facturas', '/ventas/boletas', '/ventas/facturas-reserva', '/ventas/boletas-reserva']) {
|
||||
expect(r.read as string[], p).toContain(p);
|
||||
expect(r.write as string[], p).toContain(p);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -11,8 +11,8 @@ export interface SlResource {
|
||||
methods: SlMethod[];
|
||||
/** Ruta de menú requerida para leer; `null` = cualquier sesión (catálogos de consulta). */
|
||||
read: RequiredPath | null;
|
||||
/** Ruta de menú requerida para crear/modificar. */
|
||||
write: RequiredPath;
|
||||
/** Ruta de menú requerida para crear/modificar; `null` = cualquier sesión (acciones de solo consulta). */
|
||||
write: RequiredPath | null;
|
||||
/** Colecciones permitidas en `$expand` (por defecto ninguna). */
|
||||
expand?: string[];
|
||||
}
|
||||
@@ -33,6 +33,12 @@ const catalog = (name: string, key: 'number' | 'string', writePath?: RequiredPat
|
||||
write: writePath ?? [],
|
||||
});
|
||||
|
||||
/**
|
||||
* Acción del Service Layer que solo consulta (se invoca con POST y no escribe nada): cualquier sesión.
|
||||
* El nombre es el de la acción (`SeriesService_GetDocumentSeries`), no el de una entidad.
|
||||
*/
|
||||
const queryAction = (name: string): SlResource => ({ name, key: 'string', methods: ['POST'], read: null, write: null });
|
||||
|
||||
/**
|
||||
* Único lugar donde se declara qué puede tocar el frontend. Lo que no esté aquí
|
||||
* no se puede consultar (404). Nunca registrar `EmployeesInfo`: contiene U_WEB_PASS.
|
||||
@@ -78,6 +84,12 @@ export const SL_RESOURCES: readonly SlResource[] = [
|
||||
// Grupos de los maestros (para elegirlos y filtrar las listas de clientes, proveedores y artículos).
|
||||
catalog('BusinessPartnerGroups', 'number'),
|
||||
catalog('ItemGroups', 'number'),
|
||||
// Dimensiones, proyectos y cuentas de los documentos (para elegirlos en cabecera y líneas).
|
||||
catalog('DistributionRules', 'string'),
|
||||
catalog('Projects', 'string'),
|
||||
catalog('ChartOfAccounts', 'string'),
|
||||
// Series de numeración válidas de un tipo de documento.
|
||||
queryAction('SeriesService_GetDocumentSeries'),
|
||||
// Gastos adicionales de un documento (el flete): se leen para elegir el código de gasto.
|
||||
catalog('AdditionalExpenses', 'number'),
|
||||
];
|
||||
|
||||
Reference in new issue
Block a user