feat(web): el almacén y el impuesto elegidos en la primera línea se copian a las de abajo
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
155d18fce4
commit
43cba0cc3e
4 files changed
+53
-1
No files matched your search
@@ -75,6 +75,7 @@ export const DOCUMENT_LINE_COLUMNS: readonly GridColumn[] = [
|
||||
title: 'Almacén',
|
||||
width: '90px',
|
||||
mono: true,
|
||||
propagate: true,
|
||||
lookup: {
|
||||
config: {
|
||||
title: 'Almacenes',
|
||||
@@ -95,6 +96,7 @@ export const DOCUMENT_LINE_COLUMNS: readonly GridColumn[] = [
|
||||
title: 'Impuesto',
|
||||
width: '90px',
|
||||
mono: true,
|
||||
propagate: true,
|
||||
lookup: {
|
||||
config: {
|
||||
title: 'Códigos de impuesto',
|
||||
|
||||
@@ -24,6 +24,8 @@ export interface GridColumn {
|
||||
/** No editable (siempre, o según la línea). */
|
||||
readonly?: boolean | ((line: GridLine) => boolean);
|
||||
required?: boolean;
|
||||
/** Lo elegido en la primera fila se copia a las filas de abajo (almacén, impuesto); desde la 2.ª fila no se replica. */
|
||||
propagate?: boolean;
|
||||
/** Celda con botón "…" / F4 que abre una lista de valores. */
|
||||
lookup?: GridLookup;
|
||||
/** Texto calculado a mostrar (columnas de solo lectura). */
|
||||
|
||||
@@ -246,6 +246,46 @@ describe('LinesGrid', () => {
|
||||
expect(lines()[0]).toMatchObject({ Quantity: 2, Price: 50 });
|
||||
});
|
||||
|
||||
async function pickFromList(buttonIndex: number, url: string, row: Record<string, unknown>) {
|
||||
(qa('.pick button')[buttonIndex] as HTMLButtonElement).click();
|
||||
await tick();
|
||||
ctrl.expectOne((r) => r.url === url).flush({ value: [row], 'odata.count': '1' });
|
||||
await tick();
|
||||
(q('.dlg tbody tr') as HTMLElement).click();
|
||||
await tick();
|
||||
}
|
||||
|
||||
describe('almacén e impuesto: lo elegido en la 1.ª fila se copia hacia abajo', () => {
|
||||
beforeEach(async () => {
|
||||
fixture.componentInstance.lines.set([line('A'), line('B'), line('C')]);
|
||||
await tick();
|
||||
});
|
||||
|
||||
it('el almacén elegido en la primera fila llega a todas las de abajo', async () => {
|
||||
await pickFromList(1, '/api/sl/Warehouses', { WarehouseCode: '01', WarehouseName: 'Central' });
|
||||
expect(lines().map((l) => l['WarehouseCode'])).toEqual(['01', '01', '01']);
|
||||
expect(lines()[2]).toMatchObject({ ItemCode: 'C', Quantity: 1 });
|
||||
});
|
||||
|
||||
it('el impuesto elegido en la primera fila llega a todas las de abajo y recalcula', async () => {
|
||||
await pickFromList(2, '/api/sl/SalesTaxCodes', { Code: 'IVA', Name: 'IVA 19%', Rate: 19 });
|
||||
expect(lines().map((l) => l['TaxCode'])).toEqual(['IVA', 'IVA', 'IVA']);
|
||||
expect(lines()[1]).toMatchObject({ LineTotal: 100 });
|
||||
});
|
||||
|
||||
it('elegir el almacén en la segunda fila no se replica', async () => {
|
||||
await pickFromList(4, '/api/sl/Warehouses', { WarehouseCode: '02', WarehouseName: 'Norte' });
|
||||
expect(lines().map((l) => l['WarehouseCode'])).toEqual(['', '02', '']);
|
||||
});
|
||||
|
||||
it('lo que ya tenían las filas de abajo se sustituye al elegir en la primera', async () => {
|
||||
fixture.componentInstance.lines.set([line('A'), { ...line('B'), WarehouseCode: '09' }]);
|
||||
await tick();
|
||||
await pickFromList(1, '/api/sl/Warehouses', { WarehouseCode: '01', WarehouseName: 'Central' });
|
||||
expect(lines().map((l) => l['WarehouseCode'])).toEqual(['01', '01']);
|
||||
});
|
||||
});
|
||||
|
||||
it('F4 abre la lista de la celda; una celda sin lista no', async () => {
|
||||
await key(0, 3, 'F4'); // almacén
|
||||
ctrl.expectOne((r) => r.url === '/api/sl/Warehouses').flush({ value: [], 'odata.count': '0' });
|
||||
|
||||
@@ -141,7 +141,15 @@ export class LinesGrid {
|
||||
|
||||
private replace(row: number, line: GridLine, key: string): void {
|
||||
const next = this.recalc()?.(line, key) ?? line;
|
||||
this.lines.update((ls) => ls.map((l, i) => (i === row ? next : l)));
|
||||
const spread = row === 0 && this.columns().some((c) => c.key === key && c.propagate) && next[key] !== this.lines()[0]?.[key];
|
||||
this.lines.update((ls) =>
|
||||
ls.map((l, i) => {
|
||||
if (i === row) return next;
|
||||
if (!spread || i === 0) return l;
|
||||
const copy = { ...l, [key]: next[key] };
|
||||
return this.recalc()?.(copy, key) ?? copy;
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
// --- filas -----------------------------------------------------------------------------
|
||||
|
||||
Reference in new issue
Block a user