feat(web): fila del flete (gastos adicionales) y toolbar de ancho completo; la configuración de pantallas se carga en diferido
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
d3ab97cd2e
commit
d6c127627f
14 files changed
+360
-27
No files matched your search
@@ -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, SboField, SelectField, TextField, TextInput, TextareaField } from '../../shared/form';
|
||||
import { DateField, LookupField, NumberField, NumberInput, SboField, SelectField, SelectInput, 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';
|
||||
@@ -19,6 +19,8 @@ import {
|
||||
type DocRecord,
|
||||
type NavTarget,
|
||||
computeTotals,
|
||||
freightOf,
|
||||
freightPayload,
|
||||
createPayload,
|
||||
isBlankLine,
|
||||
isDocumentReadOnly,
|
||||
@@ -26,7 +28,7 @@ import {
|
||||
updatePayload,
|
||||
validateLayout,
|
||||
} from './document-model';
|
||||
import { type ComputedSource, PARTNER_COLUMNS, WAREHOUSE_LOOKUP, hasFolio, headerKeysOf, requiredHeaderOf } from './document-layouts';
|
||||
import { type ComputedSource, PARTNER_COLUMNS, WAREHOUSE_LOOKUP, hasFolio, hasFreight, headerKeysOf, requiredHeaderOf } from './document-layouts';
|
||||
import { type CopyOrigin, copyFromDocument, copySources, copyTargets } from './document-copy';
|
||||
import { DOCUMENT_SCREENS, type DocumentScreen, docNum, documentStatus, menuLabel } from './document-screens';
|
||||
import { ITEMS_MULTI_ADD } from './line-columns';
|
||||
@@ -60,7 +62,7 @@ const folioAsNumber = (h: DocRecord): DocRecord => {
|
||||
@Component({
|
||||
selector: 'sbo-document-form-page',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
imports: [DateField, Icon, LinesGrid, LookupField, MenuButton, NgTemplateOutlet, NumberField, RouterLink, SboField, SelectField, SboWindow, TextField, TextInput, TextareaField, Toolbar, ValueHelp],
|
||||
imports: [DateField, Icon, LinesGrid, LookupField, MenuButton, NgTemplateOutlet, NumberField, NumberInput, RouterLink, SboField, SelectField, SelectInput, SboWindow, TextField, TextInput, TextareaField, Toolbar, ValueHelp],
|
||||
templateUrl: './document-form.page.html',
|
||||
styles: `
|
||||
h1 { margin: 0 0 6px; font-size: 18px; font-weight: 600; }
|
||||
@@ -83,8 +85,12 @@ export class DocumentFormPage {
|
||||
private readonly headerKeys: readonly string[] = headerKeysOf(this.layout);
|
||||
private readonly hasFolio = hasFolio(this.layout);
|
||||
private readonly blankHeader: DocRecord = this.layout.newHeader();
|
||||
private readonly hasFreight = hasFreight(this.layout);
|
||||
private pick(doc: DocRecord | undefined): DocRecord {
|
||||
return pickHeader(doc, this.headerKeys, this.blankHeader);
|
||||
const header = pickHeader(doc, this.headerKeys, this.blankHeader);
|
||||
if (!doc || !this.hasFreight) return header;
|
||||
const freight = freightOf(doc);
|
||||
return { ...header, FreightExpense: freight.expense, FreightAmount: freight.amount };
|
||||
}
|
||||
private docLines(doc: DocRecord | undefined): GridLine[] {
|
||||
return (doc?.[this.lineSpec.key] as GridLine[] | undefined) ?? [];
|
||||
@@ -107,6 +113,23 @@ export class DocumentFormPage {
|
||||
: undefined,
|
||||
);
|
||||
|
||||
/** Gastos adicionales de SAP (el flete se registra con uno de ellos). */
|
||||
private readonly expensesResource = httpResource<SlListResponse<DocRecord>>(() =>
|
||||
this.hasFreight ? { url: `${API_URL}/sl/AdditionalExpenses`, params: { $select: 'ExpensCode,Name', $top: 100 } } : undefined,
|
||||
);
|
||||
protected readonly expenseOptions = computed(() =>
|
||||
(this.expensesResource.hasValue() ? this.expensesResource.value().value : []).map((e) => ({
|
||||
value: String(e['ExpensCode']),
|
||||
label: text(e['Name']) || String(e['ExpensCode']),
|
||||
})),
|
||||
);
|
||||
/** Gasto que se usa si el usuario no elige: el que se llame "flete" o, si no hay, el primero. */
|
||||
protected readonly defaultExpense = computed(() => {
|
||||
const options = this.expenseOptions();
|
||||
return (options.find((o) => /flete|freight/i.test(o.label)) ?? options[0])?.value ?? '';
|
||||
});
|
||||
protected readonly freightEditable = computed(() => freightOf(this.doc()).editable);
|
||||
|
||||
protected readonly doc = computed(() => (this.docResource.hasValue() ? this.docResource.value() : undefined));
|
||||
protected readonly loading = computed(() => this.docResource.isLoading());
|
||||
protected readonly loadError = computed(() => {
|
||||
@@ -170,7 +193,7 @@ export class DocumentFormPage {
|
||||
taxRateMap(this.taxResource.hasValue() ? this.taxResource.value().value : []),
|
||||
);
|
||||
protected readonly totals = computed(() =>
|
||||
computeTotals(this.lines(), this.rates(), Number(this.header()['DiscountPercent'] ?? 0)),
|
||||
computeTotals(this.lines(), this.rates(), Number(this.header()['DiscountPercent'] ?? 0), Number(this.header()['FreightAmount'] ?? 0)),
|
||||
);
|
||||
protected readonly fmt = (n: number): string => formatNumber(n, 2);
|
||||
|
||||
@@ -355,7 +378,10 @@ export class DocumentFormPage {
|
||||
const d = this.doc();
|
||||
if (!d) {
|
||||
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));
|
||||
const body = this.hasFreight
|
||||
? freightPayload(createPayload(header, this.lines(), this.lineSpec), this.header(), this.defaultExpense(), 'create').body
|
||||
: createPayload(header, this.lines(), this.lineSpec);
|
||||
const created = await this.api.create(this.screen.resource, body);
|
||||
await this.router.navigate(['..', created['DocEntry']], {
|
||||
relativeTo: this.route,
|
||||
state: { created: text(created['DocNum']) },
|
||||
@@ -363,8 +389,13 @@ export class DocumentFormPage {
|
||||
return;
|
||||
}
|
||||
const update = updatePayload(this.pick(d), this.header(), this.docLines(d), this.lines(), this.lineSpec);
|
||||
const body = folioAsNumber(update.body);
|
||||
const replaceLines = update.replaceLines;
|
||||
let body = folioAsNumber(update.body);
|
||||
let replaceLines = update.replaceLines;
|
||||
if (this.hasFreight) {
|
||||
const freight = freightPayload(body, this.header(), this.defaultExpense(), 'update', freightOf(d).amount > 0);
|
||||
body = freight.body;
|
||||
replaceLines = replaceLines || freight.replace;
|
||||
}
|
||||
if (!Object.keys(body).length) {
|
||||
this.message.set({ kind: 'info', text: 'No hay cambios que guardar.' });
|
||||
return;
|
||||
|
||||
Reference in new issue
Block a user