feat(web): código de cuenta con separadores de segmento, ramas vacías sin flecha y Cuenta de mayor con formato
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
456b0f4ac0
commit
a10de1ff29
11 files changed
+108
-6
No files matched your search
@@ -1,3 +1,4 @@
|
||||
import { formatAccountCode } from '../../core/sl/account-segments';
|
||||
import type { VhLookup } from '../../shared/value-help/value-help.types';
|
||||
|
||||
/** Dimensión de costos (centro de costo, `CostingCode` 1 a 5): reglas de distribución activas de esa dimensión. */
|
||||
@@ -45,6 +46,6 @@ export const ACCOUNT_LOOKUP: VhLookup = {
|
||||
{ key: 'Code', title: 'Código', width: '160px', mono: true },
|
||||
{ key: 'Name', title: 'Nombre' },
|
||||
// en un plan de cuentas segmentado, `Code` es interno (`_SYS…`): se muestra también el código con formato
|
||||
{ key: 'FormatCode', title: 'Cuenta', width: '140px', mono: true },
|
||||
{ key: 'FormatCode', title: 'Cuenta', width: '140px', mono: true, format: (r) => formatAccountCode(String(r['FormatCode'] ?? '')) },
|
||||
],
|
||||
};
|
||||
@@ -40,6 +40,10 @@ describe('árbol del plan de cuentas', () => {
|
||||
expect(flattenBranch(root, new Map(), new Set(['1']))[0]).toMatchObject({ expandable: true, expanded: false });
|
||||
});
|
||||
|
||||
it('una cuenta de título sin hijas no se puede desplegar', () => {
|
||||
expect(flattenBranch(root, new Map([['1', []]]), new Set(['1']))[0]).toMatchObject({ expandable: false, expanded: false });
|
||||
});
|
||||
|
||||
it('el nivel máximo oculta las cuentas más profundas', () => {
|
||||
expect(flattenBranch(root, children, new Set(['1', '1-1']), 2).map((r) => r.account.Code)).toEqual(['1', '1-1', '1-2']);
|
||||
});
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { formatAccountCode } from '../../../core/sl/account-segments';
|
||||
|
||||
/** Campos de cada cuenta que pide el árbol. */
|
||||
export const TREE_FIELDS = 'Code,FormatCode,Name,AccountLevel,FatherAccountKey,ActiveAccount,AccountType,Balance,PrimaryAccount';
|
||||
|
||||
@@ -19,7 +21,7 @@ export interface TreeAccount {
|
||||
* Código que ve el usuario: el `FormatCode` y, si la cuenta no lo tiene (las de título de un plan segmentado,
|
||||
* o cualquiera en uno sin segmentar), su `Code`. La clave que se envía a SAP es siempre `Code`.
|
||||
*/
|
||||
export const displayCode = (a: { Code: string; FormatCode?: string | null }): string => (a.FormatCode && a.FormatCode.trim() !== '' ? a.FormatCode : a.Code);
|
||||
export const displayCode = (a: { Code: string; FormatCode?: string | null }): string => (a.FormatCode && a.FormatCode.trim() !== '' ? formatAccountCode(a.FormatCode) : a.Code);
|
||||
|
||||
export interface TreeRow {
|
||||
account: TreeAccount;
|
||||
@@ -37,7 +39,8 @@ export interface TreeRow {
|
||||
export function flattenBranch(root: TreeAccount, children: ReadonlyMap<string, readonly TreeAccount[]>, expanded: ReadonlySet<string>, maxLevel = Infinity): TreeRow[] {
|
||||
const out: TreeRow[] = [];
|
||||
const walk = (a: TreeAccount, depth: number): void => {
|
||||
const expandable = a.ActiveAccount !== 'tYES';
|
||||
// una cuenta de título cuyas hijas ya se pidieron y no hay ninguna se muestra como hoja (sin flecha)
|
||||
const expandable = a.ActiveAccount !== 'tYES' && children.get(a.Code)?.length !== 0;
|
||||
const open = expandable && expanded.has(a.Code) && children.has(a.Code);
|
||||
out.push({ account: a, depth, expandable, expanded: open });
|
||||
if (!open) return;
|
||||
|
||||
@@ -52,6 +52,9 @@
|
||||
} @empty {
|
||||
<div class="none">{{ error() || (loading() ? 'Cargando el plan de cuentas…' : 'Sin cuentas.') }}</div>
|
||||
}
|
||||
@if (rows().length === 1 && !rows()[0].expandable && !loading()) {
|
||||
<div class="none">Este grupo no tiene cuentas.</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
@@ -81,7 +81,9 @@ export class ChartTreePanel {
|
||||
|
||||
private async loadGroups(): Promise<void> {
|
||||
try {
|
||||
this.groups.set(await this.list('AccountLevel eq 1'));
|
||||
const groups = await this.list('AccountLevel eq 1');
|
||||
this.groups.set(groups);
|
||||
if (groups[0] && !this.selected()) void this.openRoot(groups[0].Code);
|
||||
} catch (e) {
|
||||
this.error.set(apiErrorMessage(e));
|
||||
} finally {
|
||||
@@ -136,6 +138,18 @@ export class ChartTreePanel {
|
||||
|
||||
protected pickGroup(code: string): void {
|
||||
this.picked.set(code);
|
||||
void this.openRoot(code);
|
||||
}
|
||||
|
||||
/** Al ver un grupo se muestran sus cuentas de primer nivel sin que el usuario tenga que desplegarlo. */
|
||||
private async openRoot(code: string): Promise<void> {
|
||||
if (this.expanded().has(code)) return;
|
||||
try {
|
||||
await this.load(code);
|
||||
this.expanded.update((s) => new Set(s).add(code));
|
||||
} catch (e) {
|
||||
this.error.set(apiErrorMessage(e));
|
||||
}
|
||||
}
|
||||
protected setLevel(value: string): void {
|
||||
this.level.set(Number(value) || 10);
|
||||
|
||||
@@ -9,6 +9,7 @@ import { RowsTable } from '../../shared/grid/rows-table';
|
||||
import { Icon, SboWindow, Tab, Tabs, Toolbar, type StatusMessage, type WindowBadge } from '../../shared/window';
|
||||
import { updatePayload, type DocRecord } from '../documents/document-model';
|
||||
import type { HeaderField } from '../documents/document-layouts';
|
||||
import { displayCode } from './finanzas/chart-tree.model';
|
||||
import { ChartTreePanel } from './finanzas/chart-tree.panel';
|
||||
import { MasterField } from './master-field';
|
||||
import {
|
||||
@@ -131,7 +132,7 @@ export class MasterFormPage {
|
||||
});
|
||||
protected readonly windowTitle = computed(() => {
|
||||
const d = this.doc();
|
||||
const code = (this.screen.displayField && text(d?.[this.screen.displayField]).trim()) || d?.[this.screen.keyField];
|
||||
const code = this.screen.displayField && d ? displayCode({ Code: text(d[this.screen.keyField]), FormatCode: text(d[this.screen.displayField]) }) : d?.[this.screen.keyField];
|
||||
return code === undefined ? this.screen.singular : `${this.screen.singular} · ${text(code)}`;
|
||||
});
|
||||
|
||||
@@ -178,6 +179,11 @@ export class MasterFormPage {
|
||||
}
|
||||
|
||||
protected value(key: string): unknown {
|
||||
// el código de una cuenta ya creada se ve con su formato (el `_SYS…` interno no es lo que el usuario conoce)
|
||||
if (this.screen.displayField && key === this.screen.keyField && !this.isNew()) {
|
||||
const d = this.doc();
|
||||
return displayCode({ Code: text(d?.[this.screen.keyField]), FormatCode: text(d?.[this.screen.displayField]) });
|
||||
}
|
||||
return this.header()[key];
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user