diff --git a/apps/web/src/app/shared/grid/lines-grid.html b/apps/web/src/app/shared/grid/lines-grid.html index 05bda82..e2e6ad5 100644 --- a/apps/web/src/app/shared/grid/lines-grid.html +++ b/apps/web/src/app/shared/grid/lines-grid.html @@ -14,11 +14,11 @@ # - @for (c of columns(); track c.key) { + @for (c of columns(); track c.key; let j = $index) { {{ c.title }} - } - @if (hasDetails()) { - Lotes / Series + @if (hasDetails() && j === detailAt()) { + Lotes / Series + } } @if (!readonly()) { Quitar @@ -69,13 +69,13 @@ /> } - } - @if (hasDetails()) { - - @for (d of detailsOf(line); track d.key) { - - } - + @if (hasDetails() && j === detailAt()) { + + @for (d of detailsOf(line); track d.key) { + + } + + } } @if (!readonly()) { diff --git a/apps/web/src/app/shared/grid/lines-grid.spec.ts b/apps/web/src/app/shared/grid/lines-grid.spec.ts index d9862fd..364f169 100644 --- a/apps/web/src/app/shared/grid/lines-grid.spec.ts +++ b/apps/web/src/app/shared/grid/lines-grid.spec.ts @@ -580,3 +580,37 @@ describe('LinesGrid: detalles de línea (lotes)', () => { expect(el.querySelector('sbo-line-detail-dialog footer .btn.main')).toBeNull(); }); }); + +describe('LinesGrid: dónde va la columna de lotes y series', () => { + @Component({ + imports: [LinesGrid], + template: ``, + }) + class PlaceHost { + columns = [ + { key: 'ItemCode', title: 'Artículo' }, + { key: 'Quantity', title: 'Cantidad', type: 'number' as const }, + { key: 'Price', title: 'Precio', type: 'number' as const }, + { key: 'CostingCode', title: 'Dim. 1' }, + ]; + lines = signal([{ ItemCode: 'SU01', Quantity: 1, _manageBatches: true }]); + newLine = () => ({}); + details = [{ key: 'BatchNumbers', title: 'Lotes', applies: () => true, columns: [], summary: () => 'Indicar lotes' }]; + } + + it('va justo después de la cantidad, no al final tras todas las demás columnas', async () => { + await TestBed.configureTestingModule({ imports: [PlaceHost], providers: [provideHttpClient(), provideHttpClientTesting()] }).compileComponents(); + const fixture = TestBed.createComponent(PlaceHost); + fixture.detectChanges(); + await fixture.whenStable(); + fixture.detectChanges(); + const el = fixture.nativeElement as HTMLElement; + const heads = [...el.querySelectorAll('thead th')].map((t) => t.textContent?.trim()); + expect(heads.slice(1, 5)).toEqual(['Artículo', 'Cantidad', 'Lotes / Series', 'Precio']); + const cells = [...el.querySelectorAll('tbody tr:first-child td')]; + expect(cells[3].classList.contains('det')).toBe(true); + expect(cells[3].textContent?.trim()).toBe('Indicar lotes'); + // los índices de columna (teclado) no cuentan la columna de lotes + expect(el.querySelector('input[data-r="0"][data-c="2"]')).toBeTruthy(); + }); +}); diff --git a/apps/web/src/app/shared/grid/lines-grid.ts b/apps/web/src/app/shared/grid/lines-grid.ts index 29ed822..3a8a779 100644 --- a/apps/web/src/app/shared/grid/lines-grid.ts +++ b/apps/web/src/app/shared/grid/lines-grid.ts @@ -68,6 +68,12 @@ export class LinesGrid { protected readonly multiOpen = signal(false); protected readonly lookup = computed(() => this.lookupTarget()?.column.lookup?.config); protected readonly hasDetails = computed(() => this.details().length > 0); + /** Posición de la columna «Lotes / Series»: justo después de la cantidad (si no hay, al final). */ + protected readonly detailAt = computed(() => { + const cols = this.columns(); + const q = cols.findIndex((c) => c.key === 'Quantity'); + return q >= 0 ? q : cols.length - 1; + }); /** Filas con que se abre la ventana (estable mientras no cambie la línea: la ventana edita una copia). */ protected readonly detailInitial = computed[]>(() => { const t = this.detailTarget();