116 lines
4.0 KiB
TypeScript
116 lines
4.0 KiB
TypeScript
import type { GridColumn, GridLine, GridMultiAdd } from '../../shared/grid/grid.types';
|
||
|
||
const round2 = (n: number): number => Math.round((n + Number.EPSILON) * 100) / 100;
|
||
const num = (v: unknown): number => {
|
||
const n = Number(v ?? 0);
|
||
return Number.isFinite(n) ? n : 0;
|
||
};
|
||
|
||
/** Total de la línea antes de impuestos: cantidad × precio × (1 − descuento %). SAP lo recalcula al guardar. */
|
||
export function recalcLine(line: GridLine): GridLine {
|
||
const total = num(line['Quantity']) * num(line['Price']) * (1 - num(line['DiscountPercent']) / 100);
|
||
return { ...line, LineTotal: round2(total) };
|
||
}
|
||
|
||
/**
|
||
* Línea nueva con los valores por defecto de un documento. El precio queda vacío (no 0): si el
|
||
* usuario no lo escribe no se envía y SAP aplica la lista de precios del socio de negocio.
|
||
*/
|
||
export const newDocumentLine = (): GridLine => ({
|
||
ItemCode: '',
|
||
ItemDescription: '',
|
||
Quantity: 1,
|
||
WarehouseCode: '',
|
||
Price: null,
|
||
DiscountPercent: 0,
|
||
TaxCode: '',
|
||
LineTotal: 0,
|
||
});
|
||
|
||
/**
|
||
* Columnas estándar de una línea de documento de SAP B1 (artículo, descripción, cantidad, almacén,
|
||
* precio, % descuento, código de impuesto y total). El impuesto es `TaxCode` (maestro `SalesTaxCodes`);
|
||
* un cliente que use `VatGroup` cambia esa columna en su rama.
|
||
*/
|
||
export const DOCUMENT_LINE_COLUMNS: readonly GridColumn[] = [
|
||
{
|
||
key: 'ItemCode',
|
||
title: 'Nº de artículo',
|
||
width: '140px',
|
||
mono: true,
|
||
required: true,
|
||
lookup: {
|
||
config: {
|
||
title: 'Artículos',
|
||
resource: 'Items',
|
||
orderby: 'ItemCode',
|
||
columns: [
|
||
{ key: 'ItemCode', title: 'Código', width: '150px', mono: true },
|
||
{ key: 'ItemName', title: 'Descripción' },
|
||
],
|
||
},
|
||
multiple: true,
|
||
apply: (item, line) => ({ ...line, ItemCode: item['ItemCode'], ItemDescription: item['ItemName'] ?? line['ItemDescription'] }),
|
||
},
|
||
},
|
||
{ key: 'ItemDescription', title: 'Descripción' },
|
||
{ key: 'Quantity', title: 'Cantidad', type: 'number', width: '85px', required: true },
|
||
{
|
||
key: 'WarehouseCode',
|
||
title: 'Almacén',
|
||
width: '90px',
|
||
mono: true,
|
||
lookup: {
|
||
config: {
|
||
title: 'Almacenes',
|
||
resource: 'Warehouses',
|
||
orderby: 'WarehouseCode',
|
||
columns: [
|
||
{ key: 'WarehouseCode', title: 'Código', width: '110px', mono: true },
|
||
{ key: 'WarehouseName', title: 'Nombre' },
|
||
],
|
||
},
|
||
apply: (wh, line) => ({ ...line, WarehouseCode: wh['WarehouseCode'] }),
|
||
},
|
||
},
|
||
{ key: 'Price', title: 'Precio', type: 'number', decimals: 2, width: '100px' },
|
||
{ key: 'DiscountPercent', title: '% Dto.', type: 'number', decimals: 2, width: '70px' },
|
||
{
|
||
key: 'TaxCode',
|
||
title: 'Impuesto',
|
||
width: '90px',
|
||
mono: true,
|
||
lookup: {
|
||
config: {
|
||
title: 'Códigos de impuesto',
|
||
resource: 'SalesTaxCodes',
|
||
orderby: 'Code',
|
||
columns: [
|
||
{ key: 'Code', title: 'Código', width: '110px', mono: true },
|
||
{ key: 'Name', title: 'Nombre' },
|
||
{ key: 'Rate', title: 'Tasa %', type: 'number', width: '80px' },
|
||
],
|
||
},
|
||
apply: (tax, line) => ({ ...line, TaxCode: tax['Code'] }),
|
||
},
|
||
},
|
||
{ key: 'LineTotal', title: 'Total', type: 'number', decimals: 2, width: '110px', readonly: true },
|
||
];
|
||
|
||
/** Columnas de línea con el maestro de impuestos que corresponde (por defecto `SalesTaxCodes`). */
|
||
export function lineColumnsFor(taxResource: string): readonly GridColumn[] {
|
||
if (taxResource === 'SalesTaxCodes') return DOCUMENT_LINE_COLUMNS;
|
||
return DOCUMENT_LINE_COLUMNS.map((c) =>
|
||
c.key === 'TaxCode' && c.lookup
|
||
? { ...c, lookup: { ...c.lookup, config: { ...c.lookup.config, resource: taxResource } } }
|
||
: c,
|
||
);
|
||
}
|
||
|
||
/** "Catálogo completo": agrega de una vez varios artículos como líneas nuevas. */
|
||
export const ITEMS_MULTI_ADD: GridMultiAdd = {
|
||
label: 'Elegir artículos…',
|
||
lookup: DOCUMENT_LINE_COLUMNS[0].lookup!.config,
|
||
toLine: (item) => ({ ...newDocumentLine(), ItemCode: item['ItemCode'], ItemDescription: item['ItemName'] ?? '' }),
|
||
};
|