Files
basic-erp/apps/web/src/app/features/documents/line-columns.ts
T
GeorgeandClaude Sonnet 5.5 6978fed6fb fix(web): fechas de SAP con hora, enlaces como botones y un solo maestro de impuestos
Probado contra el SAP real (solo lectura): órdenes de venta y facturas de proveedor.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-14 09:13:53 -05:00

112 lines
3.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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. */
export const newDocumentLine = (): GridLine => ({
ItemCode: '',
ItemDescription: '',
Quantity: 1,
WarehouseCode: '',
Price: 0,
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' },
],
},
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'] ?? '' }),
};