feat(web): la «Cuenta / socio» del asiento se elige de cuentas, clientes o proveedores y completa la cuenta de control del socio
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
5dfe096c5d
commit
c567bc6836
3 files changed
+129
-16
No files matched your search
@@ -1,9 +1,9 @@
|
||||
import { HttpErrorResponse, httpResource } from '@angular/common/http';
|
||||
import { HttpClient, HttpErrorResponse, HttpParams, httpResource } from '@angular/common/http';
|
||||
import { ChangeDetectionStrategy, Component, computed, inject, linkedSignal, signal } from '@angular/core';
|
||||
import { toSignal } from '@angular/core/rxjs-interop';
|
||||
import { ActivatedRoute, Router, RouterLink } from '@angular/router';
|
||||
import type { SlListResponse } from '@basic-erp/sap-types';
|
||||
import { map } from 'rxjs';
|
||||
import { firstValueFrom, map } from 'rxjs';
|
||||
import { API_URL, apiErrorMessage } from '../../core/api';
|
||||
import { formatNumber } from '../../core/format';
|
||||
import { SlDocumentApi } from '../../core/sl/sl-document';
|
||||
@@ -11,12 +11,29 @@ import { DateField, LookupField, SboField, SelectField, TextField, TextInput } f
|
||||
import type { DetailColumn } from '../../shared/grid/grid.types';
|
||||
import { RowsTable } from '../../shared/grid/rows-table';
|
||||
import { Icon, SboWindow, Toolbar, type StatusMessage, type WindowBadge } from '../../shared/window';
|
||||
import { ACCOUNT_CODES } from '../../core/sl/account-codes';
|
||||
import type { VhLookup } from '../../shared/value-help/value-help.types';
|
||||
import { ACCOUNT_LOOKUP, INDICATOR_LOOKUP, PROJECT_LOOKUP, dimensionLookup } from '../documents/dimension-lookups';
|
||||
import type { NavTarget } from '../documents/document-model';
|
||||
import { isManualJournal, journalOrigin, journalPayload, journalTotals, validateJournal } from './journal-model';
|
||||
|
||||
type Row = Record<string, unknown>;
|
||||
|
||||
/** Clientes o proveedores para la «Cuenta / socio» de una línea; piden su cuenta de control para completar la «Cuenta». */
|
||||
const partnerLookup = (type: 'cCustomer' | 'cSupplier', title: string): VhLookup => ({
|
||||
title,
|
||||
resource: 'BusinessPartners',
|
||||
baseFilter: `CardType eq '${type}'`,
|
||||
orderby: 'CardCode',
|
||||
columns: [
|
||||
{ key: 'CardCode', title: 'Código', width: '130px', mono: true },
|
||||
{ key: 'CardName', title: 'Nombre' },
|
||||
],
|
||||
});
|
||||
|
||||
/** Una línea de socio: el código del socio (su cuenta de control se pide aparte: SAP falla al listarla junto con los socios). */
|
||||
const partnerLine = (partner: Record<string, unknown>): Record<string, unknown> => ({ ShortName: partner['CardCode'] });
|
||||
|
||||
/** Tipo de objeto de SAP del asiento contable (para pedir sus series de numeración). */
|
||||
const OBJECT_TYPE = 30;
|
||||
|
||||
@@ -40,6 +57,7 @@ export class JournalFormPage {
|
||||
private readonly route = inject(ActivatedRoute);
|
||||
private readonly router = inject(Router);
|
||||
private readonly api = inject(SlDocumentApi);
|
||||
private readonly http = inject(HttpClient);
|
||||
|
||||
protected readonly title = this.route.snapshot.data['title'] as string;
|
||||
protected readonly jdtNum = toSignal(this.route.paramMap.pipe(map((p) => (p.has('jdtNum') ? Number(p.get('jdtNum')) : null))), {
|
||||
@@ -105,9 +123,29 @@ export class JournalFormPage {
|
||||
|
||||
// --- vista -----------------------------------------------------------------------------------
|
||||
|
||||
/** La cuenta de control del socio, para una línea que todavía no tiene cuenta. */
|
||||
private async controlAccount(partner: Record<string, unknown>, row: Record<string, unknown>): Promise<Record<string, unknown>> {
|
||||
if (row['AccountCode']) return {};
|
||||
const res = await firstValueFrom(
|
||||
this.http.get<{ DebitorAccount?: string }>(`${API_URL}/sl/BusinessPartners/${encodeURIComponent(String(partner['CardCode']))}`, { params: new HttpParams().set('$select', 'DebitorAccount') }),
|
||||
);
|
||||
return res.DebitorAccount ? { AccountCode: res.DebitorAccount } : {};
|
||||
}
|
||||
|
||||
protected readonly columns: readonly DetailColumn[] = [
|
||||
{ key: 'AccountCode', title: 'Cuenta', type: 'lookup', required: true, lookup: ACCOUNT_LOOKUP, width: '170px' },
|
||||
{ key: 'ShortName', title: 'Cuenta / socio', type: 'lookup', lookup: ACCOUNT_LOOKUP, width: '170px' },
|
||||
{
|
||||
key: 'ShortName',
|
||||
title: 'Cuenta / socio',
|
||||
type: 'lookup',
|
||||
width: '170px',
|
||||
codes: ACCOUNT_CODES,
|
||||
lookups: [
|
||||
{ label: 'Cuentas', lookup: ACCOUNT_LOOKUP, apply: (a) => ({ ShortName: a['Code'], AccountCode: a['Code'] }) },
|
||||
{ label: 'Clientes', lookup: partnerLookup('cCustomer', 'Clientes'), apply: partnerLine, complete: (p, row) => this.controlAccount(p, row) },
|
||||
{ label: 'Proveedores', lookup: partnerLookup('cSupplier', 'Proveedores'), apply: partnerLine, complete: (p, row) => this.controlAccount(p, row) },
|
||||
],
|
||||
},
|
||||
{ key: 'Debit', title: 'Debe', type: 'number', decimals: 2, width: '120px' },
|
||||
{ key: 'Credit', title: 'Haber', type: 'number', decimals: 2, width: '120px' },
|
||||
{ key: 'DueDate', title: 'Vencimiento', type: 'date', width: '120px' },
|
||||
|
||||
Reference in new issue
Block a user