feat(tagumedica): el botón «Campos» funciona en las pantallas del flujo de lotes (campos y cabeceras de tablas)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
GeorgeandClaude Sonnet 5.5 committed 2026-10-20 15:55:51 -05:00
1 parent 2d25b3b880
commit 51fbae34f5
3 files changed
+28 -15

No files matched your search

@@ -1,5 +1,6 @@
import { ChangeDetectionStrategy, Component, computed, inject, resource, signal } from '@angular/core';
import { apiErrorMessage } from '../../../core/api';
import { FieldNames } from '../../../core/field-names';
import { formatDate } from '../../../core/format';
import type { DetailColumn } from '../../../shared/grid/grid.types';
import { RowsTable } from '../../../shared/grid/rows-table';
@@ -27,7 +28,7 @@ const text = (v: unknown): string => (v === null || v === undefined ? '' : Strin
<sbo-window title="Confirmar liberación de lote" [status]="status()">
<p class="hint">Solicitudes de traslado de liberación abiertas. Elija una para indicar a qué almacenes vuelve cada lote.</p>
<table class="grid">
<thead><tr><th>Solicitud</th><th>Fecha</th><th>Motivo del estado</th><th>Comentario</th><th>Acción a tomar</th></tr></thead>
<thead><tr><th [attr.title]="'DocNum'">Solicitud@if (names.show()) {<span class="db">DocNum</span>}</th><th [attr.title]="'DocDate'">Fecha@if (names.show()) {<span class="db">DocDate</span>}</th><th [attr.title]="'U_SIS_MOTEST'">Motivo del estado@if (names.show()) {<span class="db">U_SIS_MOTEST</span>}</th><th [attr.title]="'U_SIS_COMEST'">Comentario@if (names.show()) {<span class="db">U_SIS_COMEST</span>}</th><th [attr.title]="'U_SIS_ACCTOM'">Acción a tomar@if (names.show()) {<span class="db">U_SIS_ACCTOM</span>}</th></tr></thead>
<tbody>
@for (r of pending.value() ?? []; track r['DocEntry']) {
<tr tabindex="0" [class.sel]="r['DocEntry'] === docEntry()" (click)="select(+r['DocEntry']!)" (keydown.enter)="select(+r['DocEntry']!)">
@@ -72,6 +73,7 @@ const text = (v: unknown): string => (v === null || v === undefined ? '' : Strin
h3 { margin: 14px 0 4px; font-size: 13px; font-weight: 600; }
.hint { margin: 0 0 8px; color: var(--quiet); }
.grid { width: 100%; border-collapse: collapse; }
.grid th .db { display: block; font: 10.5px/1.2 var(--mono, monospace); font-weight: 400; color: var(--quiet); }
.grid th { padding: 4px 8px; background: var(--grid-head); border: 1px solid var(--line); font-size: 12.5px; text-align: left; }
.grid td { padding: 3px 8px; border: 1px solid var(--line-soft); }
.grid tbody tr { cursor: pointer; }
@@ -88,6 +90,7 @@ const text = (v: unknown): string => (v === null || v === undefined ? '' : Strin
})
export class LoteReleaseConfirmPage {
private readonly api = inject(LotesFlujoApi);
protected readonly names = inject(FieldNames);
protected readonly pending = resource({ loader: () => this.api.pending(PENDING_LIBERATION) });
protected readonly docEntry = signal<number | null>(null);
@@ -132,6 +132,13 @@ describe('LoteTransferPage', () => {
expect(el().querySelector('.status')?.textContent).toContain('Stock insuficiente');
});
it('cada campo de la solicitud lleva el nombre de su campo de SAP (botón «Campos»)', async () => {
await search('2090456');
const titles = [...el().querySelectorAll('label')].map((l) => l.getAttribute('title'));
expect(titles).toEqual(expect.arrayContaining(['BatchNumber', 'DocDate', 'Series', 'U_SIS_MOVINV', 'U_EXX_TIPOOPER', 'U_SIS_TIPDESP', 'U_SIS_MOTALM', 'U_SIS_MOTEST', 'U_SIS_COMEST', 'U_SIS_ACCTOM']));
expect([...el().querySelectorAll('.stock th')].map((t) => t.getAttribute('title'))).toEqual(expect.arrayContaining(['ItemCode', 'WarehouseCode', 'Quantity']));
});
it('un lote sin stock se avisa', async () => {
await search('NOEXISTE', []);
expect(el().querySelector('.status')?.textContent).toContain('No se encontró stock para el lote «NOEXISTE»');
@@ -1,6 +1,7 @@
import { ChangeDetectionStrategy, Component, computed, inject, resource, signal } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { apiErrorMessage } from '../../../core/api';
import { FieldNames } from '../../../core/field-names';
import { todayIso } from '../../../core/format';
import { DateField, LookupField, SelectField, TextField, TextareaField } from '../../../shared/form';
import type { VhLookup } from '../../../shared/value-help/value-help.types';
@@ -36,11 +37,11 @@ const WAREHOUSE: VhLookup = { ...WAREHOUSE_LOOKUP, nameKey: 'WarehouseName' };
<div class="sbo-hdr">
<div class="sbo-col">
<sbo-text-field label="Número de lote" [value]="lot()" (valueChange)="lot.set($event)" (keydown.enter)="search()" />
<sbo-text-field label="Número de lote" dbName="BatchNumber" [value]="lot()" (valueChange)="lot.set($event)" (keydown.enter)="search()" />
</div>
<div class="sbo-col">
@if (config.pickFrom) {
<sbo-lookup-field label="Almacén de origen" [lookup]="warehouse" [value]="fromWarehouse()" (valueChange)="fromWarehouse.set($event)" resolveOnLeave />
<sbo-lookup-field label="Almacén de origen" dbName="FromWarehouseCode" [lookup]="warehouse" [value]="fromWarehouse()" (valueChange)="fromWarehouse.set($event)" resolveOnLeave />
}
</div>
</div>
@@ -52,8 +53,8 @@ const WAREHOUSE: VhLookup = { ...WAREHOUSE_LOOKUP, nameKey: 'WarehouseName' };
<table class="stock">
<thead>
<tr>
<th class="chk"></th><th>Artículo</th><th>Descripción</th><th>Almacén</th><th>Nombre</th>
<th class="num">En stock</th><th class="num">Comprometido</th><th class="num">Cantidad a mover</th>
<th class="chk"></th><th [attr.title]="'ItemCode'">Artículo@if (names.show()) {<span class="db">ItemCode</span>}</th><th [attr.title]="'ItemDescription'">Descripción@if (names.show()) {<span class="db">ItemDescription</span>}</th><th [attr.title]="'WarehouseCode'">Almacén@if (names.show()) {<span class="db">WarehouseCode</span>}</th><th [attr.title]="'WarehouseName'">Nombre@if (names.show()) {<span class="db">WarehouseName</span>}</th>
<th class="num" [attr.title]="'Quantity'">En stock@if (names.show()) {<span class="db">Quantity</span>}</th><th class="num" [attr.title]="'CommitQty'">Comprometido@if (names.show()) {<span class="db">CommitQty</span>}</th><th class="num">Cantidad a mover</th>
</tr>
</thead>
<tbody>
@@ -87,20 +88,20 @@ const WAREHOUSE: VhLookup = { ...WAREHOUSE_LOOKUP, nameKey: 'WarehouseName' };
<h3>Solicitud de traslado</h3>
<div class="sbo-hdr">
<div class="sbo-col">
<sbo-date-field label="Fecha" required [value]="form().docDate" (valueChange)="set({ docDate: $event })" />
<sbo-select-field label="Serie" [options]="series.value() ?? []" placeholder="(por defecto)" [value]="form().series" (valueChange)="set({ series: $event === null || $event === '' ? null : +$event })" />
<sbo-lookup-field label="Almacén destino" required [lookup]="warehouse" [value]="form().toWarehouse" (valueChange)="set({ toWarehouse: $event })" resolveOnLeave />
<sbo-date-field label="Fecha" dbName="DocDate" required [value]="form().docDate" (valueChange)="set({ docDate: $event })" />
<sbo-select-field label="Serie" dbName="Series" [options]="series.value() ?? []" placeholder="(por defecto)" [value]="form().series" (valueChange)="set({ series: $event === null || $event === '' ? null : +$event })" />
<sbo-lookup-field label="Almacén destino" dbName="WarehouseCode" required [lookup]="warehouse" [value]="form().toWarehouse" (valueChange)="set({ toWarehouse: $event })" resolveOnLeave />
</div>
<div class="sbo-col">
<sbo-select-field label="Movimiento de inventario" required [options]="options().movimientoInventario" placeholder="" [value]="form().movimientoInventario" (valueChange)="set({ movimientoInventario: '' + ($event ?? '') })" />
<sbo-select-field label="Tipo de operación" required [options]="options().tipoOperacion" placeholder="" [value]="form().tipoOperacion" (valueChange)="set({ tipoOperacion: '' + ($event ?? '') })" />
<sbo-select-field label="Tipo de despacho" required [options]="options().tipoDespacho" placeholder="" [value]="form().tipoDespacho" (valueChange)="set({ tipoDespacho: '' + ($event ?? '') })" />
<sbo-select-field label="Motivo de almacén" required [options]="options().movimientoAlmacen" placeholder="" [value]="form().movimientoAlmacen" (valueChange)="set({ movimientoAlmacen: '' + ($event ?? '') })" />
<sbo-select-field label="Movimiento de inventario" dbName="U_SIS_MOVINV" required [options]="options().movimientoInventario" placeholder="" [value]="form().movimientoInventario" (valueChange)="set({ movimientoInventario: '' + ($event ?? '') })" />
<sbo-select-field label="Tipo de operación" dbName="U_EXX_TIPOOPER" required [options]="options().tipoOperacion" placeholder="" [value]="form().tipoOperacion" (valueChange)="set({ tipoOperacion: '' + ($event ?? '') })" />
<sbo-select-field label="Tipo de despacho" dbName="U_SIS_TIPDESP" required [options]="options().tipoDespacho" placeholder="" [value]="form().tipoDespacho" (valueChange)="set({ tipoDespacho: '' + ($event ?? '') })" />
<sbo-select-field label="Motivo de almacén" dbName="U_SIS_MOTALM" required [options]="options().movimientoAlmacen" placeholder="" [value]="form().movimientoAlmacen" (valueChange)="set({ movimientoAlmacen: '' + ($event ?? '') })" />
</div>
</div>
<sbo-textarea-field label="Motivo del estado" [rows]="3" required [value]="form().motivo" (valueChange)="set({ motivo: $event })" [error]="over(form().motivo, motivoMax)" />
<sbo-textarea-field label="Comentario del estado" [rows]="3" [required]="config.requireDetail" [value]="form().comentario" (valueChange)="set({ comentario: $event })" [error]="over(form().comentario, textMax)" />
<sbo-textarea-field label="Acción a tomar" [rows]="3" [required]="config.requireDetail" [value]="form().accion" (valueChange)="set({ accion: $event })" [error]="over(form().accion, textMax)" />
<sbo-textarea-field label="Motivo del estado" dbName="U_SIS_MOTEST" [rows]="3" required [value]="form().motivo" (valueChange)="set({ motivo: $event })" [error]="over(form().motivo, motivoMax)" />
<sbo-textarea-field label="Comentario del estado" dbName="U_SIS_COMEST" [rows]="3" [required]="config.requireDetail" [value]="form().comentario" (valueChange)="set({ comentario: $event })" [error]="over(form().comentario, textMax)" />
<sbo-textarea-field label="Acción a tomar" dbName="U_SIS_ACCTOM" [rows]="3" [required]="config.requireDetail" [value]="form().accion" (valueChange)="set({ accion: $event })" [error]="over(form().accion, textMax)" />
@if (confirming()) {
<p class="ask" role="alert">
@@ -122,6 +123,7 @@ const WAREHOUSE: VhLookup = { ...WAREHOUSE_LOOKUP, nameKey: 'WarehouseName' };
.row { margin: 4px 0 0; }
.wrap { overflow-x: auto; }
.stock { width: 100%; border-collapse: collapse; }
.stock th .db { display: block; font: 10.5px/1.2 var(--mono, monospace); font-weight: 400; color: var(--quiet); }
.stock th { padding: 4px 8px; background: var(--grid-head); border: 1px solid var(--line); font-size: 12.5px; text-align: left; white-space: nowrap; }
.stock td { padding: 3px 8px; border: 1px solid var(--line-soft); }
.stock .num { text-align: right; }
@@ -137,6 +139,7 @@ const WAREHOUSE: VhLookup = { ...WAREHOUSE_LOOKUP, nameKey: 'WarehouseName' };
})
export class LoteTransferPage {
private readonly api = inject(LotesFlujoApi);
protected readonly names = inject(FieldNames);
private readonly flow = inject(ActivatedRoute).snapshot.data['flow'] as LotFlow;
protected readonly config = LOT_FLOWS[this.flow];