fix(web): la columna de lotes y series va junto a la cantidad, a la vista
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
fe8c080471
commit
08977741de
3 files changed
+45
-5
No files matched your search
@@ -14,12 +14,12 @@
|
|||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th class="rn">#</th>
|
<th class="rn">#</th>
|
||||||
@for (c of columns(); track c.key) {
|
@for (c of columns(); track c.key; let j = $index) {
|
||||||
<th sboColResize [style.width]="c.width" [style.text-align]="alignOf(c)" [class.req]="c.required">{{ c.title }}</th>
|
<th sboColResize [style.width]="c.width" [style.text-align]="alignOf(c)" [class.req]="c.required">{{ c.title }}</th>
|
||||||
}
|
@if (hasDetails() && j === detailAt()) {
|
||||||
@if (hasDetails()) {
|
|
||||||
<th class="det">Lotes / Series</th>
|
<th class="det">Lotes / Series</th>
|
||||||
}
|
}
|
||||||
|
}
|
||||||
@if (!readonly()) {
|
@if (!readonly()) {
|
||||||
<th class="del"><span class="sr">Quitar</span></th>
|
<th class="del"><span class="sr">Quitar</span></th>
|
||||||
}
|
}
|
||||||
@@ -69,14 +69,14 @@
|
|||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
</td>
|
</td>
|
||||||
}
|
@if (hasDetails() && j === detailAt()) {
|
||||||
@if (hasDetails()) {
|
|
||||||
<td class="det">
|
<td class="det">
|
||||||
@for (d of detailsOf(line); track d.key) {
|
@for (d of detailsOf(line); track d.key) {
|
||||||
<button type="button" class="detbtn" [attr.aria-label]="d.title + ' de la línea ' + (i + 1)" (click)="openDetail(i, d)">{{ d.summary(line) }}</button>
|
<button type="button" class="detbtn" [attr.aria-label]="d.title + ' de la línea ' + (i + 1)" (click)="openDetail(i, d)">{{ d.summary(line) }}</button>
|
||||||
}
|
}
|
||||||
</td>
|
</td>
|
||||||
}
|
}
|
||||||
|
}
|
||||||
@if (!readonly()) {
|
@if (!readonly()) {
|
||||||
<td class="del">
|
<td class="del">
|
||||||
<button type="button" [attr.aria-label]="'Quitar la línea ' + (i + 1)" title="Quitar línea" (click)="remove(i)">×</button>
|
<button type="button" [attr.aria-label]="'Quitar la línea ' + (i + 1)" title="Quitar línea" (click)="remove(i)">×</button>
|
||||||
|
|||||||
@@ -580,3 +580,37 @@ describe('LinesGrid: detalles de línea (lotes)', () => {
|
|||||||
expect(el.querySelector('sbo-line-detail-dialog footer .btn.main')).toBeNull();
|
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: `<sbo-lines-grid [columns]="columns" [(lines)]="lines" [newLine]="newLine" [details]="details" />`,
|
||||||
|
})
|
||||||
|
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<readonly GridLine[]>([{ 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();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -68,6 +68,12 @@ export class LinesGrid {
|
|||||||
protected readonly multiOpen = signal(false);
|
protected readonly multiOpen = signal(false);
|
||||||
protected readonly lookup = computed(() => this.lookupTarget()?.column.lookup?.config);
|
protected readonly lookup = computed(() => this.lookupTarget()?.column.lookup?.config);
|
||||||
protected readonly hasDetails = computed(() => this.details().length > 0);
|
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). */
|
/** Filas con que se abre la ventana (estable mientras no cambie la línea: la ventana edita una copia). */
|
||||||
protected readonly detailInitial = computed<readonly Record<string, unknown>[]>(() => {
|
protected readonly detailInitial = computed<readonly Record<string, unknown>[]>(() => {
|
||||||
const t = this.detailTarget();
|
const t = this.detailTarget();
|
||||||
|
|||||||
Reference in new issue
Block a user