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
@@ -2,7 +2,9 @@ import type { GridColumn, GridLine } from '../../shared/grid/grid.types';
|
||||
import type { ListColumn } from '../../shared/list/list.types';
|
||||
import type { VhLookup } from '../../shared/value-help/value-help.types';
|
||||
import type { DocRecord, LinesSpec } from './document-model';
|
||||
import { DOCUMENT_LINE_COLUMNS, lineColumnsFor, lineFieldsOf, newDocumentLine, recalcLine, uomEntryOf } from './line-columns';
|
||||
import { PROJECT_LOOKUP } from './dimension-lookups';
|
||||
import { SERIES_SUBTYPE } from './document-objects';
|
||||
import { ANALYTIC_COLUMNS, DOCUMENT_LINE_COLUMNS, lineColumnsFor, lineFieldsOf, newDocumentLine, recalcLine, uomEntryOf } from './line-columns';
|
||||
|
||||
/** Valores calculados o del socio que una fila de solo lectura puede mostrar. */
|
||||
export type ComputedSource = 'docNum' | 'partnerName' | 'partnerTaxId' | 'subtotal' | 'tax' | 'total';
|
||||
@@ -37,6 +39,8 @@ 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 }
|
||||
/** 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`). */
|
||||
| { type: 'freight'; label: string }
|
||||
| { type: 'computed'; label: string; source: ComputedSource };
|
||||
@@ -67,8 +71,10 @@ export interface DocumentLayout {
|
||||
newHeader: () => DocRecord;
|
||||
/** Línea nueva; las transferencias toman los almacenes de la cabecera. */
|
||||
newLine: (header: DocRecord) => GridLine;
|
||||
/** Recalcula una línea tras un cambio (total = cantidad × precio…). */
|
||||
recalc?: (line: GridLine, changedKey: string) => GridLine;
|
||||
/** Recalcula una línea tras un cambio (total = cantidad × precio…); recibe la tasa de cada código de impuesto. */
|
||||
recalc?: (line: GridLine, changedKey: string, rates?: ReadonlyMap<string, number>) => GridLine;
|
||||
/** Subtipo con que SAP etiqueta las series de este documento (`--` = ninguno, `IB` = boleta…). */
|
||||
seriesSubType?: string;
|
||||
/** Columnas de las listas de valores de este documento; las que falten usan las del base. */
|
||||
lookups?: LookupColumns;
|
||||
/** Una vez contabilizado ya no se modifica (SAP no permite cambiar entradas, salidas ni transferencias). */
|
||||
@@ -85,6 +91,10 @@ export const today = (): string => new Date().toISOString().slice(0, 10);
|
||||
/** Catálogo de campos: cada pantalla elige cuáles lleva. */
|
||||
export const FIELDS = {
|
||||
docNum: { type: 'computed', label: 'Número', source: 'docNum' },
|
||||
/** Número del documento con su serie. */
|
||||
docNumber: { type: 'series', key: 'Series', label: 'Número' },
|
||||
indicator: { type: 'text', key: 'Indicator', label: 'Indicador' },
|
||||
project: { type: 'lookup', key: 'Project', label: 'Proyecto', lookup: PROJECT_LOOKUP },
|
||||
docDate: { type: 'date', key: 'DocDate', label: 'Fecha de contabilización', required: true, missing: 'la fecha de contabilización' },
|
||||
docDueDate: { type: 'date', key: 'DocDueDate', label: 'Fecha de vencimiento' },
|
||||
requiredDate: { type: 'date', key: 'RequriedDate', label: 'Fecha requerida', required: true, missing: 'la fecha requerida' },
|
||||
@@ -201,9 +211,11 @@ export function marketingLayout(opts: {
|
||||
{ type: 'computed', label: 'Nombre', source: 'partnerName' },
|
||||
{ type: 'computed', label: 'RUT', source: 'partnerTaxId' },
|
||||
FIELDS.numAtCard,
|
||||
FIELDS.indicator,
|
||||
FIELDS.project,
|
||||
...(opts.left ?? []),
|
||||
],
|
||||
right: [FIELDS.docNum, FIELDS.docDate, FIELDS.docDueDate, ...(opts.right ?? [])],
|
||||
right: [FIELDS.docNumber, FIELDS.docDate, FIELDS.docDueDate, ...(opts.right ?? [])],
|
||||
},
|
||||
footer: {
|
||||
left: [FIELDS.comments],
|
||||
@@ -219,6 +231,7 @@ export function marketingLayout(opts: {
|
||||
newHeader: () => ({ DocDate: today(), DocDueDate: today(), DiscountPercent: 0 }),
|
||||
newLine: newDocumentLine,
|
||||
recalc: recalcLine,
|
||||
seriesSubType: SERIES_SUBTYPE[String(opts.fixed?.['DocumentSubType'] ?? 'bod_None')] ?? '--',
|
||||
...(opts.fixed ? { beforeCreate: (h: DocRecord) => ({ ...h, ...opts.fixed }) } : {}),
|
||||
requiredLine: [],
|
||||
};
|
||||
@@ -264,6 +277,8 @@ export function isRequired(f: HeaderField): f is HeaderField & { key: string; re
|
||||
return 'required' in f && f.required === true && 'key' in f;
|
||||
}
|
||||
|
||||
export const hasSeries = (layout: DocumentLayout): boolean => allFields(layout).some((f) => f.type === 'series');
|
||||
|
||||
export const hasFreight = (layout: DocumentLayout): boolean => allFields(layout).some((f) => f.type === 'freight');
|
||||
|
||||
export const hasFolio = (layout: DocumentLayout): boolean => allFields(layout).some((f) => f.type === 'folio');
|
||||
Reference in new issue
Block a user