feat(web): folio y RUT en los formularios de facturas, notas de crédito, entregas y recepciones; RUT en todos los de socio
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
49408de082
commit
c734dcbd0a
6 files changed
+186
-8
No files matched your search
@@ -56,7 +56,7 @@ Cada menú abre primero una **lista de los últimos documentos** (barra de filtr
|
||||
Hecho (commits en `main`): monorepo · API (config, capa SAP con sesión compartida y cola, login, permisos SQLite, proxy `/api/sl`) · web (temas, login, layout con menú por permisos, `sbo-value-help`, campos y filas compuestas, listas con filtros, ventana/toolbar/pestañas/menús, grilla de líneas, `/dev/componentes`).
|
||||
|
||||
Pendiente, en orden:
|
||||
1. **Paso 9 — formulario de documentos:** hecho para ventas y compras (`document-form.page`, `document-model.ts`: cargar, crear, actualizar con `If-Match`/412, navegación, totales con `SalesTaxCodes` (también en compras), solo lectura), probado con SAP simulado y contra `SBODemoCL` (leer, crear, actualizar, líneas, 412). Un código escrito a mano (socio, artículo, almacén, impuesto) se resuelve al salir del campo con `SlLookupApi` (mismo recurso y filtro base que la lista "…"); si no existe se avisa. "Copiar de/a" hecho (`document-copy.ts`: flujos estándar y `BaseType/BaseEntry/BaseLine`; probado: orden de compra 2883 → recepción 299, orden de venta 315 → entrega 580, ambas órdenes quedaron cerradas). Formularios sin socio ni totales **hechos** (`document-layouts.ts`, configuración declarativa: cabecera de 2 columnas, columnas de línea, colección `DocumentLines` o `StockTransferLines`): entradas y salidas de inventario, transferencias de stock, solicitudes de traslado y solicitudes de compra. Entradas, salidas y transferencias contabilizadas son de solo lectura (SAP no las modifica); solicitudes de traslado y de compra se crean y modifican. Probado contra `SBODemoCL`: solicitud de compra 2 (la 1 es una prueba por API), solicitud de traslado 472 (cantidad modificada) y entrada de inventario 155 (1 unidad de `3MG30080714 M 10.5` en el almacén 8803). Limitaciones conocidas: los artículos por **lote o serie** no se pueden contabilizar (SAP pide `BatchNumbers`/`SerialNumbers`); las líneas de compra/inventario llevan `UoMEntry` solo si el artículo tiene grupo de unidades (`uomEntryOf`; sin él SAP responde "specify a UoM code"); los formularios de ventas y compras (`marketing`) aún no lo envían, y los de compra fallan con artículos sin unidad de compra por defecto. Falta: botón PDF, fila del flete y toolbar de ancho completo.
|
||||
1. **Paso 9 — formulario de documentos:** hecho para ventas y compras (`document-form.page`, `document-model.ts`: cargar, crear, actualizar con `If-Match`/412, navegación, totales con `SalesTaxCodes` (también en compras), solo lectura), probado con SAP simulado y contra `SBODemoCL` (leer, crear, actualizar, líneas, 412). Un código escrito a mano (socio, artículo, almacén, impuesto) se resuelve al salir del campo con `SlLookupApi` (mismo recurso y filtro base que la lista "…"); si no existe se avisa. "Copiar de/a" hecho (`document-copy.ts`: flujos estándar y `BaseType/BaseEntry/BaseLine`; probado: orden de compra 2883 → recepción 299, orden de venta 315 → entrega 580, ambas órdenes quedaron cerradas). Formularios sin socio ni totales **hechos** (`document-layouts.ts`, configuración declarativa: cabecera de 2 columnas, columnas de línea, colección `DocumentLines` o `StockTransferLines`): entradas y salidas de inventario, transferencias de stock, solicitudes de traslado y solicitudes de compra. Entradas, salidas y transferencias contabilizadas son de solo lectura (SAP no las modifica); solicitudes de traslado y de compra se crean y modifican. Probado contra `SBODemoCL`: solicitud de compra 2 (la 1 es una prueba por API), solicitud de traslado 472 (cantidad modificada) y entrada de inventario 155 (1 unidad de `3MG30080714 M 10.5` en el almacén 8803). Limitaciones conocidas: los artículos por **lote o serie** no se pueden contabilizar (SAP pide `BatchNumbers`/`SerialNumbers`); las líneas de compra/inventario llevan `UoMEntry` solo si el artículo tiene grupo de unidades (`uomEntryOf`; sin él SAP responde "specify a UoM code"); los formularios de ventas y compras (`marketing`) aún no lo envían, y los de compra fallan con artículos sin unidad de compra por defecto. Los formularios con socio muestran `NumAtCard` y el RUT (`FederalTaxID`, solo lectura: lo completa SAP desde el socio); facturas, notas de crédito, entregas y recepciones (`FOLIO_PATHS` en `document-screens.ts`) añaden el folio tributario, que en el Service Layer es `FolioPrefixString` + `FolioNumber` (se edita como texto y se envía como número); las listas de facturas también los muestran. Falta: botón PDF, fila del flete y toolbar de ancho completo.
|
||||
2. Pantallas de maestros (`Items`, `BusinessPartners`: Clientes/Proveedores, Artículos), hoy "no disponible".
|
||||
3. ~~Pantalla web de permisos~~ — hecha (`/admin/permisos`).
|
||||
4. Auditoría (`audit_log` en SQLite; hoy solo líneas `AUDIT` en el log) y `nestjs-cls` para el usuario en los logs.
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
import { provideHttpClient } from '@angular/common/http';
|
||||
import { HttpTestingController, provideHttpClientTesting } from '@angular/common/http/testing';
|
||||
import { type ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { ActivatedRoute, Router, convertToParamMap, provideRouter } from '@angular/router';
|
||||
import { of } from 'rxjs';
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import { DocumentFormPage } from './document-form.page';
|
||||
import { DOCUMENT_SCREENS } from './document-screens';
|
||||
|
||||
const invoice = (over: Record<string, unknown> = {}) => ({
|
||||
'odata.etag': 'W/"5"',
|
||||
DocEntry: 8,
|
||||
DocNum: 108,
|
||||
CardCode: 'P1',
|
||||
CardName: 'Proveedor Uno',
|
||||
FederalTaxID: '76543210-K',
|
||||
NumAtCard: 'REF-1',
|
||||
FolioPrefixString: 'F001',
|
||||
FolioNumber: 198837,
|
||||
DocDate: '2026-09-11T00:00:00Z',
|
||||
DocDueDate: '2026-09-20T00:00:00Z',
|
||||
DiscountPercent: 0,
|
||||
Comments: '',
|
||||
DocumentStatus: 'bost_Open',
|
||||
Cancelled: 'tNO',
|
||||
DocumentLines: [{ LineNum: 0, ItemCode: 'A1', ItemDescription: 'Pieza', Quantity: 1, Price: 10, DiscountPercent: 0, TaxCode: 'IVA', LineTotal: 10, WarehouseCode: '01' }],
|
||||
...over,
|
||||
});
|
||||
|
||||
describe('folio y RUT en los formularios', () => {
|
||||
let fixture: ComponentFixture<DocumentFormPage>;
|
||||
let ctrl: HttpTestingController;
|
||||
const el = () => fixture.nativeElement as HTMLElement;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const page = () => fixture.componentInstance as any;
|
||||
const tick = async () => {
|
||||
fixture.detectChanges();
|
||||
await new Promise((r) => setTimeout(r));
|
||||
fixture.detectChanges();
|
||||
};
|
||||
const labels = () => [...el().querySelectorAll('label')].map((l) => l.textContent?.trim());
|
||||
|
||||
async function open(resource: string, doc: Record<string, unknown> | null) {
|
||||
const screen = DOCUMENT_SCREENS.find((s) => s.resource === resource)!;
|
||||
const params = doc ? { docEntry: '8' } : {};
|
||||
const route = {
|
||||
snapshot: { data: { screen, title: 'Documento' }, paramMap: convertToParamMap(params) },
|
||||
paramMap: of(convertToParamMap(params)),
|
||||
};
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [DocumentFormPage],
|
||||
providers: [provideRouter([]), provideHttpClient(), provideHttpClientTesting(), { provide: ActivatedRoute, useValue: route }],
|
||||
}).compileComponents();
|
||||
ctrl = TestBed.inject(HttpTestingController);
|
||||
vi.spyOn(TestBed.inject(Router), 'navigate').mockResolvedValue(true);
|
||||
fixture = TestBed.createComponent(DocumentFormPage);
|
||||
await tick();
|
||||
ctrl.expectOne((r) => r.url === '/api/sl/SalesTaxCodes').flush({ value: [{ Code: 'IVA', Rate: 19 }] });
|
||||
if (doc) ctrl.expectOne(`/api/sl/${resource}/8`).flush(doc);
|
||||
await tick();
|
||||
}
|
||||
|
||||
beforeEach(() => TestBed.resetTestingModule());
|
||||
|
||||
it('lo llevan facturas, notas de crédito, entregas y recepciones (ventas y compras)', () => {
|
||||
const withFolio = DOCUMENT_SCREENS.filter((s) => s.folio).map((s) => s.resource);
|
||||
expect(withFolio.sort()).toEqual(
|
||||
['CreditNotes', 'DeliveryNotes', 'Invoices', 'PurchaseCreditNotes', 'PurchaseDeliveryNotes', 'PurchaseInvoices'].sort(),
|
||||
);
|
||||
});
|
||||
|
||||
it('una factura muestra folio, número de folio, referencia y RUT', async () => {
|
||||
await open('PurchaseInvoices', invoice());
|
||||
expect(labels()).toEqual(expect.arrayContaining(['Folio', 'Nº referencia', 'RUT']));
|
||||
const inputs = [...el().querySelectorAll<HTMLInputElement>('input')].map((i) => i.value);
|
||||
expect(inputs).toEqual(expect.arrayContaining(['F001', '198837', 'REF-1', '76543210-K']));
|
||||
});
|
||||
|
||||
it('una orden muestra referencia y RUT pero no el folio', async () => {
|
||||
await open('Orders', invoice());
|
||||
expect(labels()).toEqual(expect.arrayContaining(['Nº referencia', 'RUT']));
|
||||
expect(labels()).not.toContain('Folio');
|
||||
});
|
||||
|
||||
it('cambiar el folio envía el número como número', async () => {
|
||||
await open('Invoices', invoice());
|
||||
page().setHeader('FolioPrefixString', 'F002');
|
||||
page().setHeader('FolioNumber', '200100');
|
||||
await tick();
|
||||
(el().querySelector('.actions .btn.main') as HTMLElement).click();
|
||||
await tick();
|
||||
const req = ctrl.expectOne((r) => r.method === 'PATCH');
|
||||
expect(req.request.body).toEqual({ FolioPrefixString: 'F002', FolioNumber: 200100 });
|
||||
req.flush(null);
|
||||
await tick();
|
||||
ctrl.expectOne('/api/sl/Invoices/8').flush(invoice());
|
||||
});
|
||||
|
||||
it('un folio que no es numérico no se envía', async () => {
|
||||
await open('Invoices', invoice());
|
||||
page().setHeader('FolioNumber', '12ab');
|
||||
await tick();
|
||||
(el().querySelector('.actions .btn.main') as HTMLElement).click();
|
||||
await tick();
|
||||
ctrl.expectNone((r) => r.method === 'PATCH');
|
||||
expect(el().querySelector('.status')?.textContent).toContain('folio debe ser numérico');
|
||||
});
|
||||
|
||||
it('un documento nuevo sin folio no lo envía', async () => {
|
||||
await open('Invoices', null);
|
||||
page().setHeader('CardCode', 'C1');
|
||||
page().setLines([{ ItemCode: 'A1', Quantity: 1, Price: 50, LineTotal: 50 }]);
|
||||
await tick();
|
||||
(el().querySelector('.actions .btn.main') as HTMLElement).click();
|
||||
await tick();
|
||||
const body = ctrl.expectOne((r) => r.method === 'POST').request.body as Record<string, unknown>;
|
||||
expect(body).not.toHaveProperty('FolioNumber');
|
||||
expect(body).not.toHaveProperty('FolioPrefixString');
|
||||
});
|
||||
});
|
||||
@@ -23,6 +23,7 @@
|
||||
(picked)="onPartner($event)"
|
||||
/>
|
||||
<sbo-text-field label="Nombre" readonly [value]="partnerName()" />
|
||||
<sbo-text-field label="RUT" readonly [value]="partnerTaxId()" />
|
||||
<sbo-text-field
|
||||
label="Nº referencia"
|
||||
[readonly]="readOnly()"
|
||||
@@ -45,6 +46,23 @@
|
||||
[value]="str(header()['DocDueDate'])"
|
||||
(valueChange)="setHeader('DocDueDate', $event)"
|
||||
/>
|
||||
@if (folio) {
|
||||
<sbo-field label="Folio">
|
||||
<sbo-input
|
||||
width="90px"
|
||||
[maxlength]="10"
|
||||
[readonly]="readOnly()"
|
||||
[value]="str(header()['FolioPrefixString'])"
|
||||
(valueChange)="setHeader('FolioPrefixString', $event)"
|
||||
/>
|
||||
<sbo-input
|
||||
ariaLabel="Número de folio"
|
||||
[readonly]="readOnly()"
|
||||
[value]="str(header()['FolioNumber'])"
|
||||
(valueChange)="setHeader('FolioNumber', $event)"
|
||||
/>
|
||||
</sbo-field>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import { map } from 'rxjs';
|
||||
import { API_URL, apiErrorMessage } from '../../core/api';
|
||||
import { formatNumber } from '../../core/format';
|
||||
import { SlDocumentApi } from '../../core/sl/sl-document';
|
||||
import { DateField, LookupField, NumberField, TextField, TextareaField } from '../../shared/form';
|
||||
import { DateField, LookupField, NumberField, SboField, TextField, TextInput, TextareaField } from '../../shared/form';
|
||||
import type { GridLine, GridMultiAdd } from '../../shared/grid/grid.types';
|
||||
import { LinesGrid } from '../../shared/grid/lines-grid';
|
||||
import type { VhLookup } from '../../shared/value-help/value-help.types';
|
||||
@@ -35,6 +35,8 @@ import { ITEMS_MULTI_ADD, lineColumnsFor, newDocumentLine, recalcLine } from './
|
||||
|
||||
/** Campos de cabecera que se editan en el formulario (el resto lo calcula SAP). */
|
||||
const HEADER_KEYS = ['CardCode', 'NumAtCard', 'DocDate', 'DocDueDate', 'DiscountPercent', 'Comments'] as const;
|
||||
/** Folio tributario de los documentos que lo llevan. */
|
||||
const FOLIO_KEYS = ['FolioPrefixString', 'FolioNumber'] as const;
|
||||
|
||||
const today = (): string => new Date().toISOString().slice(0, 10);
|
||||
const DATE_KEYS = new Set(['DocDate', 'DocDueDate', 'RequriedDate']);
|
||||
@@ -43,10 +45,19 @@ const dateOnly = (v: unknown): string => (typeof v === 'string' ? v.slice(0, 10)
|
||||
const pickHeader = (doc: DocRecord | undefined, keys: readonly string[], blank: DocRecord): DocRecord => {
|
||||
if (!doc) return blank;
|
||||
return Object.fromEntries(
|
||||
keys.map((k) => [k, DATE_KEYS.has(k) ? dateOnly(doc[k]) : (doc[k] ?? (k === 'DiscountPercent' ? 0 : ''))]),
|
||||
keys.map((k) => [
|
||||
k,
|
||||
DATE_KEYS.has(k) ? dateOnly(doc[k]) : k === 'FolioNumber' ? text(doc[k]) : (doc[k] ?? (k === 'DiscountPercent' ? 0 : '')),
|
||||
]),
|
||||
);
|
||||
};
|
||||
const text = (v: unknown): string => (v === null || v === undefined ? '' : String(v));
|
||||
/** El folio se edita como texto; SAP lo espera como número. */
|
||||
const folioAsNumber = (h: DocRecord): DocRecord => {
|
||||
const folio = h['FolioNumber'];
|
||||
if (typeof folio !== 'string') return h;
|
||||
return { ...h, FolioNumber: folio.trim() === '' ? null : Number(folio) };
|
||||
};
|
||||
|
||||
/**
|
||||
* Formulario de un documento de ventas o compras: `nuevo` (vacío) o `:docEntry` (ver / editar).
|
||||
@@ -56,7 +67,7 @@ const text = (v: unknown): string => (v === null || v === undefined ? '' : Strin
|
||||
@Component({
|
||||
selector: 'sbo-document-form-page',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
imports: [DateField, Icon, LinesGrid, LookupField, MenuButton, NgTemplateOutlet, NumberField, RouterLink, SboWindow, TextField, TextareaField, Toolbar, ValueHelp],
|
||||
imports: [DateField, Icon, LinesGrid, LookupField, MenuButton, NgTemplateOutlet, NumberField, RouterLink, SboField, SboWindow, TextField, TextInput, TextareaField, Toolbar, ValueHelp],
|
||||
templateUrl: './document-form.page.html',
|
||||
styles: `
|
||||
h1 { margin: 0 0 6px; font-size: 18px; font-weight: 600; }
|
||||
@@ -76,7 +87,12 @@ export class DocumentFormPage {
|
||||
/** Formulario de inventario o de solicitud de compra (sin socio ni totales). */
|
||||
protected readonly layout = this.screen.layout;
|
||||
private readonly lineSpec = this.layout?.lines ?? MARKETING_LINES;
|
||||
private readonly headerKeys: readonly string[] = this.layout ? headerKeysOf(this.layout) : HEADER_KEYS;
|
||||
private readonly headerKeys: readonly string[] = this.layout
|
||||
? headerKeysOf(this.layout)
|
||||
: this.screen.folio
|
||||
? [...HEADER_KEYS, ...FOLIO_KEYS]
|
||||
: HEADER_KEYS;
|
||||
protected readonly folio = !!this.screen.folio;
|
||||
private readonly blankHeader: DocRecord = this.layout
|
||||
? this.layout.newHeader()
|
||||
: { DocDate: today(), DocDueDate: today(), DiscountPercent: 0 };
|
||||
@@ -115,6 +131,8 @@ export class DocumentFormPage {
|
||||
protected readonly header = linkedSignal<DocRecord>(() => this.pick(this.doc()));
|
||||
protected readonly lines = linkedSignal<readonly GridLine[]>(() => this.docLines(this.doc()).map((l) => ({ ...l })));
|
||||
protected readonly partnerName = linkedSignal(() => text(this.doc()?.['CardName']));
|
||||
/** RUT del socio: lo completa SAP desde el maestro; aquí solo se muestra. */
|
||||
protected readonly partnerTaxId = linkedSignal(() => text(this.doc()?.['FederalTaxID']));
|
||||
|
||||
// --- estado --------------------------------------------------------------------------------
|
||||
|
||||
@@ -233,6 +251,7 @@ export class DocumentFormPage {
|
||||
if (!card) {
|
||||
this.header.update((h) => ({ ...h, ...copied.header }));
|
||||
this.partnerName.set(text(doc['CardName']));
|
||||
this.partnerTaxId.set(text(doc['FederalTaxID']));
|
||||
}
|
||||
this.lines.update((ls) => [...ls.filter((l) => !isBlankLine(l)), ...copied.lines]);
|
||||
this.message.set({
|
||||
@@ -262,6 +281,7 @@ export class DocumentFormPage {
|
||||
{ key: 'CardCode', title: 'Código', width: '130px', mono: true },
|
||||
{ key: 'CardName', title: 'Nombre' },
|
||||
],
|
||||
extraFields: ['FederalTaxID'],
|
||||
};
|
||||
|
||||
protected str = text;
|
||||
@@ -280,6 +300,7 @@ export class DocumentFormPage {
|
||||
if (code === text(this.header()['CardCode'])) return;
|
||||
this.setHeader('CardCode', code);
|
||||
this.partnerName.set('');
|
||||
this.partnerTaxId.set('');
|
||||
}
|
||||
|
||||
protected notify(textMessage: string): void {
|
||||
@@ -288,6 +309,7 @@ export class DocumentFormPage {
|
||||
|
||||
protected onPartner(row: Record<string, unknown>): void {
|
||||
this.partnerName.set(text(row['CardName']));
|
||||
this.partnerTaxId.set(text(row['FederalTaxID']));
|
||||
}
|
||||
|
||||
protected setLines(lines: readonly GridLine[]): void {
|
||||
@@ -321,6 +343,7 @@ export class DocumentFormPage {
|
||||
this.layout.requiredLine,
|
||||
)
|
||||
: validateDocument(this.header(), this.lines(), this.partner?.label ?? 'Socio de negocio');
|
||||
if (this.folio && !/^\d*$/.test(text(this.header()['FolioNumber']).trim())) errors.push('El número de folio debe ser numérico.');
|
||||
if (errors.length) {
|
||||
this.message.set({ kind: 'err', text: errors[0] });
|
||||
return;
|
||||
@@ -330,7 +353,7 @@ export class DocumentFormPage {
|
||||
try {
|
||||
const d = this.doc();
|
||||
if (!d) {
|
||||
const header = this.layout?.beforeCreate?.(this.header()) ?? this.header();
|
||||
const header = folioAsNumber(this.layout?.beforeCreate?.(this.header()) ?? this.header());
|
||||
const created = await this.api.create(this.screen.resource, createPayload(header, this.lines(), this.lineSpec));
|
||||
await this.router.navigate(['..', created['DocEntry']], {
|
||||
relativeTo: this.route,
|
||||
@@ -338,7 +361,9 @@ export class DocumentFormPage {
|
||||
});
|
||||
return;
|
||||
}
|
||||
const { body, replaceLines } = updatePayload(this.pick(d), this.header(), this.docLines(d), this.lines(), this.lineSpec);
|
||||
const update = updatePayload(this.pick(d), this.header(), this.docLines(d), this.lines(), this.lineSpec);
|
||||
const body = folioAsNumber(update.body);
|
||||
const replaceLines = update.replaceLines;
|
||||
if (!Object.keys(body).length) {
|
||||
this.message.set({ kind: 'info', text: 'No hay cambios que guardar.' });
|
||||
return;
|
||||
|
||||
@@ -24,6 +24,8 @@ export interface DocumentScreen {
|
||||
/** Maestro de impuestos (el Service Layer no tiene uno propio de compras: en Chile ambos usan `SalesTaxCodes`). */
|
||||
taxResource: string;
|
||||
};
|
||||
/** El formulario muestra el folio tributario (`FolioPrefixString`, `FolioNumber`) y el RUT del socio. */
|
||||
folio?: boolean;
|
||||
/** Formulario de los documentos sin socio ni totales (inventario y solicitudes de compra). */
|
||||
layout?: DocumentLayout;
|
||||
}
|
||||
@@ -165,7 +167,17 @@ const inventory = (
|
||||
filters: withStatus ? [statusFilter, dateFilter] : [dateFilter],
|
||||
});
|
||||
|
||||
export const DOCUMENT_SCREENS: readonly DocumentScreen[] = [
|
||||
/** Documentos tributarios: llevan folio y RUT. */
|
||||
const FOLIO_PATHS: ReadonlySet<string> = new Set([
|
||||
'/ventas/entrega',
|
||||
'/ventas/facturas',
|
||||
'/ventas/notas-credito',
|
||||
'/compras/recepcion',
|
||||
'/compras/facturas',
|
||||
'/compras/notas-credito',
|
||||
]);
|
||||
|
||||
const SCREENS: readonly DocumentScreen[] = [
|
||||
// Ventas
|
||||
marketing('/ventas/ofertas', 'Quotations', 'Nueva oferta', 'Cliente'),
|
||||
marketing('/ventas/ordenes', 'Orders', 'Nueva orden', 'Cliente'),
|
||||
@@ -202,3 +214,5 @@ export const DOCUMENT_SCREENS: readonly DocumentScreen[] = [
|
||||
inventory('/inventario/transferencias', 'StockTransfers', 'Nueva transferencia', false, LAYOUTS.transfer),
|
||||
inventory('/inventario/solicitudes-traslado', 'InventoryTransferRequests', 'Nueva solicitud', true, LAYOUTS.transferRequest),
|
||||
];
|
||||
|
||||
export const DOCUMENT_SCREENS: readonly DocumentScreen[] = SCREENS.map((s) => (FOLIO_PATHS.has(s.path) ? { ...s, folio: true } : s));
|
||||
@@ -62,6 +62,7 @@ import { SboField } from './sbo-field';
|
||||
[columns]="lookup().columns"
|
||||
[baseFilter]="lookup().baseFilter"
|
||||
[orderby]="lookup().orderby"
|
||||
[extraFields]="lookup().extraFields ?? []"
|
||||
(selected)="onPicked($event)"
|
||||
(closed)="closeList()"
|
||||
/>
|
||||
|
||||
Reference in new issue
Block a user