feat(web): Indicador, prefijo y número del folio juntos en una fila (documentos de facturación y asiento) y las tres fechas de la cabecera en orden
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
1bb8efa77e
commit
a74c19a139
11 files changed
+170
-19
No files matched your search
@@ -6,6 +6,14 @@ import { INDICATOR_LOOKUP, PROJECT_LOOKUP } from './dimension-lookups';
|
||||
import { SERIES_SUBTYPE } from './document-objects';
|
||||
import { ANALYTIC_COLUMNS, DOCUMENT_LINE_COLUMNS, lineColumnsFor, lineFieldsOf, newDocumentLine, newServiceLine, recalcLine, serviceLineColumns, uomEntryOf } from './line-columns';
|
||||
|
||||
/** Una celda de un grupo de campos: un texto (con ancho y largo máximo) o un código que se elige de una lista. */
|
||||
export type GroupCell =
|
||||
| { kind: 'text'; key: string; label: string; width?: string; maxlength?: number }
|
||||
| { kind: 'lookup'; key: string; label: string; lookup: VhLookup; width?: string };
|
||||
|
||||
/** Fila con varios campos juntos, para la cabecera de un documento. */
|
||||
export const fieldGroup = (label: string, cells: readonly GroupCell[]): HeaderField => ({ type: 'group', label, cells });
|
||||
|
||||
/** Valores calculados o del socio que una fila de solo lectura puede mostrar. */
|
||||
export type ComputedSource = 'docNum' | 'partnerName' | 'partnerTaxId' | 'subtotal' | 'expenses' | 'tax' | 'total' | 'downPayment' | 'applied' | 'balance';
|
||||
|
||||
@@ -39,6 +47,11 @@ export type HeaderField =
|
||||
| { type: 'partner'; key: string; label: string; required?: boolean; missing?: string }
|
||||
/** Prefijo y número del folio tributario en una misma fila (`FolioPrefixString` y `FolioNumber`). */
|
||||
| { type: 'folio'; label: string; prefixKey: string; numberKey: string }
|
||||
/**
|
||||
* Varios campos juntos en una sola fila, en el orden dado (el indicador, el prefijo y el número del folio). Una empresa
|
||||
* que los sustituye por campos de usuario arma su propio grupo con `fieldGroup(...)` y oculta el del base.
|
||||
*/
|
||||
| { type: 'group'; label: string; cells: readonly GroupCell[] }
|
||||
/** Serie de numeración y número del documento en una misma fila (`Series` y `DocNum`). */
|
||||
| { type: 'series'; key: string; label: string }
|
||||
/** Flete: código del gasto adicional y su importe (`DocumentAdditionalExpenses`). */
|
||||
@@ -139,6 +152,16 @@ export const FIELDS = {
|
||||
requester: { type: 'text', key: 'Requester', label: 'Solicitante (usuario)' },
|
||||
} as const satisfies Record<string, HeaderField>;
|
||||
|
||||
/**
|
||||
* Indicador y folio tributario juntos y en ese orden (en el Service Layer `Indicator`, `FolioPrefixString` y
|
||||
* `FolioNumber`). El número se edita como texto y se envía como número.
|
||||
*/
|
||||
export const IDENTITY_GROUP: HeaderField = fieldGroup('Indicador / Folio', [
|
||||
{ kind: 'lookup', key: 'Indicator', label: 'Indicador', lookup: INDICATOR_LOOKUP, width: '80px' },
|
||||
{ kind: 'text', key: 'FolioPrefixString', label: 'Prefijo del folio', width: '90px', maxlength: 10 },
|
||||
{ kind: 'text', key: 'FolioNumber', label: 'Número de folio' },
|
||||
]);
|
||||
|
||||
export const commentsOnly: FieldColumns = { left: [FIELDS.comments], right: [] };
|
||||
|
||||
// --- columnas de línea compartidas ----------------------------------------------------------
|
||||
@@ -237,6 +260,8 @@ export function marketingLayout(opts: {
|
||||
uom: 'sales' | 'purchase';
|
||||
/** Fila del flete en el pie (por defecto sí). */
|
||||
expenses?: boolean;
|
||||
/** Indicador y folio juntos en una fila de la derecha (facturas y notas de crédito) en lugar del indicador suelto a la izquierda y el folio. */
|
||||
identity?: boolean;
|
||||
/** Facturas y notas de crédito: el pie suma el anticipo total, el importe aplicado y el saldo pendiente. */
|
||||
billing?: boolean;
|
||||
/** Valores que se envían siempre al crear (ej. `DocumentSubType: 'bod_Bill'` en las boletas). */
|
||||
@@ -254,11 +279,12 @@ export function marketingLayout(opts: {
|
||||
{ type: 'computed', label: 'Nombre', source: 'partnerName' },
|
||||
{ type: 'computed', label: 'RUT', source: 'partnerTaxId' },
|
||||
FIELDS.numAtCard,
|
||||
FIELDS.indicator,
|
||||
...(opts.identity ? [] : [FIELDS.indicator]),
|
||||
FIELDS.project,
|
||||
...(opts.left ?? []),
|
||||
],
|
||||
right: [FIELDS.docNumber, FIELDS.docDate, FIELDS.docDueDate, ...(opts.right ?? [])],
|
||||
// las tres fechas juntas, una debajo de la otra: contabilización, vencimiento y del documento
|
||||
right: [FIELDS.docNumber, FIELDS.docDate, FIELDS.docDueDate, FIELDS.taxDate, ...(opts.identity ? [IDENTITY_GROUP] : []), ...(opts.right ?? [])],
|
||||
},
|
||||
footer: {
|
||||
left: [...(opts.expenses === false ? [] : [{ type: 'expenses', label: 'Gastos adicionales' } as const]), FIELDS.comments],
|
||||
@@ -277,7 +303,7 @@ export function marketingLayout(opts: {
|
||||
],
|
||||
},
|
||||
columns,
|
||||
newHeader: () => ({ DocType: ITEMS_DOC_TYPE, DocDate: today(), DocDueDate: today(), DiscountPercent: 0, Rounding: 'tNO', RoundingDiffAmount: 0 }),
|
||||
newHeader: () => ({ DocType: ITEMS_DOC_TYPE, DocDate: today(), DocDueDate: today(), TaxDate: today(), DiscountPercent: 0, Rounding: 'tNO', RoundingDiffAmount: 0 }),
|
||||
newLine: newDocumentLine,
|
||||
recalc: recalcLine,
|
||||
service: {
|
||||
@@ -309,6 +335,8 @@ export const keysOfFields = (fields: readonly HeaderField[]): string[] =>
|
||||
return [];
|
||||
case 'folio':
|
||||
return [f.prefixKey, f.numberKey];
|
||||
case 'group':
|
||||
return f.cells.map((c) => c.key);
|
||||
case 'expenses':
|
||||
return [];
|
||||
case 'rounding':
|
||||
@@ -337,4 +365,4 @@ export const hasSeries = (layout: DocumentLayout): boolean => allFields(layout).
|
||||
|
||||
export const hasExpenses = (layout: DocumentLayout): boolean => allFields(layout).some((f) => f.type === 'expenses');
|
||||
|
||||
export const hasFolio = (layout: DocumentLayout): boolean => allFields(layout).some((f) => f.type === 'folio');
|
||||
export const hasFolio = (layout: DocumentLayout): boolean => allFields(layout).some((f) => f.type === 'folio' || (f.type === 'group' && f.cells.some((c) => c.key === 'FolioNumber')));
|
||||
Reference in new issue
Block a user