refactor(web): pantallas y formularios separados por módulo (ventas, compras, inventario)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
GeorgeandClaude Sonnet 5.5 committed 2026-10-14 15:12:01 -05:00
1 parent 8b0b8afc87
commit c5368c7ad0
10 files changed
+358 -311

No files matched your search

@@ -52,7 +52,7 @@ export interface DocumentLayout {
requiredLine: readonly { key: string; label: string }[];
}
const today = (): string => new Date().toISOString().slice(0, 10);
export const today = (): string => new Date().toISOString().slice(0, 10);
// --- catálogo de campos ---------------------------------------------------------------------
@@ -70,14 +70,15 @@ export const FIELDS = {
requester: { type: 'text', key: 'Requester', label: 'Solicitante (usuario)' },
} as const satisfies Record<string, HeaderField>;
const commentsOnly: FieldColumns = { left: [FIELDS.comments], right: [] };
export const commentsOnly: FieldColumns = { left: [FIELDS.comments], right: [] };
// --- columnas de línea compartidas ----------------------------------------------------------
const [baseItemColumn, descriptionColumn, quantityColumn, warehouseColumn, priceColumn] = DOCUMENT_LINE_COLUMNS;
export { descriptionColumn, priceColumn, quantityColumn, warehouseColumn };
/** Artículo que además completa la unidad de medida de la línea (`UoMEntry`). */
const itemColumn: GridColumn = {
export const itemColumn: GridColumn = {
...baseItemColumn,
lookup: {
...baseItemColumn.lookup!,
@@ -88,7 +89,7 @@ const itemColumn: GridColumn = {
/** Lista de almacenes de la cabecera (la misma de las líneas). */
export const WAREHOUSE_LOOKUP: VhLookup = warehouseColumn.lookup!.config;
const blankLine = (): GridLine => ({ ItemCode: '', ItemDescription: '', Quantity: 1, WarehouseCode: '' });
export const blankLine = (): GridLine => ({ ItemCode: '', ItemDescription: '', Quantity: 1, WarehouseCode: '' });
// --- documentos con socio de negocio (ventas y compras) -------------------------------------
@@ -132,95 +133,6 @@ export function marketingLayout(opts: {
};
}
// --- documentos sin socio ni totales (inventario y solicitudes de compra) -------------------
/** Entradas y salidas de mercancía: artículo, cantidad, almacén y precio (opcional: si falta, SAP usa el costo). */
const goods: DocumentLayout = {
lines: { key: 'DocumentLines', fields: ['ItemCode', 'ItemDescription', 'Quantity', 'UoMEntry', 'WarehouseCode', 'Price'], base: false },
header: { left: [FIELDS.docDate], right: [FIELDS.docNum] },
footer: commentsOnly,
columns: [itemColumn, descriptionColumn, quantityColumn, { ...warehouseColumn, required: true }, priceColumn],
newHeader: () => ({ DocDate: today() }),
newLine: () => ({ ...blankLine(), Price: null }),
immutable: true,
requiredLine: [{ key: 'WarehouseCode', label: 'el almacén' }],
};
/** Transferencias de stock y solicitudes de traslado: almacén origen y destino en la cabecera y en cada línea. */
const transfer = (immutable: boolean): DocumentLayout => ({
lines: { key: 'StockTransferLines', fields: ['ItemCode', 'ItemDescription', 'Quantity', 'FromWarehouseCode', 'WarehouseCode'], base: false },
header: { left: [FIELDS.fromWarehouse, FIELDS.toWarehouse], right: [FIELDS.docNum, FIELDS.docDate] },
footer: commentsOnly,
columns: [
itemColumn,
descriptionColumn,
quantityColumn,
{
...warehouseColumn,
key: 'FromWarehouseCode',
title: 'Almacén origen',
required: true,
lookup: { ...warehouseColumn.lookup!, apply: (wh, line) => ({ ...line, FromWarehouseCode: wh['WarehouseCode'] }) },
},
{ ...warehouseColumn, title: 'Almacén destino', required: true },
],
newHeader: () => ({ DocDate: today() }),
newLine: (header) => ({
...blankLine(),
FromWarehouseCode: header['FromWarehouse'] ?? '',
WarehouseCode: header['ToWarehouse'] ?? '',
}),
immutable,
requiredLine: [
{ key: 'FromWarehouseCode', label: 'el almacén origen' },
{ key: 'WarehouseCode', label: 'el almacén destino' },
],
});
/** Solicitud de compra: solicitante (usuario de SAP), fecha requerida y, por línea, proveedor sugerido. */
const purchaseRequest: DocumentLayout = {
lines: { key: 'DocumentLines', fields: ['ItemCode', 'ItemDescription', 'Quantity', 'UoMEntry', 'WarehouseCode', 'LineVendor'], base: false },
header: { left: [FIELDS.requester], right: [FIELDS.docNum, FIELDS.docDate, FIELDS.requiredDate] },
footer: commentsOnly,
columns: [
itemColumn,
descriptionColumn,
quantityColumn,
warehouseColumn,
{
key: 'LineVendor',
title: 'Proveedor',
width: '130px',
mono: true,
lookup: {
config: {
title: 'Proveedores',
resource: 'BusinessPartners',
baseFilter: `CardType eq 'cSupplier'`,
orderby: 'CardCode',
columns: [
{ key: 'CardCode', title: 'Código', width: '130px', mono: true },
{ key: 'CardName', title: 'Nombre' },
],
},
apply: (bp, line) => ({ ...line, LineVendor: bp['CardCode'] }),
},
},
],
newHeader: () => ({ DocDate: today(), RequriedDate: today() }),
newLine: blankLine,
// El solicitante es un usuario de SAP (`ReqType` 12).
beforeCreate: (h) => (h['Requester'] ? { ...h, ReqType: 12 } : h),
requiredLine: [],
};
export const LAYOUTS = {
goods,
transfer: transfer(true),
transferRequest: transfer(false),
purchaseRequest,
} as const;
// --- consultas sobre un layout --------------------------------------------------------------
const allFields = (layout: DocumentLayout): HeaderField[] => [