tin-spa 20.14.29 → 20.14.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/tin-spa.mjs +44 -6
- package/fesm2022/tin-spa.mjs.map +1 -1
- package/index.d.ts +2 -0
- package/package.json +1 -1
package/fesm2022/tin-spa.mjs
CHANGED
|
@@ -375,7 +375,22 @@ class Core {
|
|
|
375
375
|
return false;
|
|
376
376
|
}
|
|
377
377
|
static getInitialValue(field) {
|
|
378
|
-
if (field.defaultValue)
|
|
378
|
+
// Changed (E21): was `if (field.defaultValue)` — a TRUTHINESS test, so a deliberately falsy default
|
|
379
|
+
// (0, false, '') was silently ignored and the field fell through to the type default below. For most
|
|
380
|
+
// types that is harmless, because the fallthrough happens to land on the same value anyway (number -> 0,
|
|
381
|
+
// checkbox -> false, text -> ''). For a SELECT it is not: the fallthrough is `return null`, and a null
|
|
382
|
+
// posted into a non-nullable backend enum is rejected at model binding with an HTTP 400 that the user
|
|
383
|
+
// sees as the generic "Something went wrong ... a technical problem in the app" panel
|
|
384
|
+
// (api-error.service.ts:138-144). Nothing is logged server-side, because a binding failure is not an
|
|
385
|
+
// exception — which is exactly why this stayed hidden through a 124k-line API log.
|
|
386
|
+
//
|
|
387
|
+
// Measured before changing it: across ng-space and all four consumer apps exactly 11 fields declare a
|
|
388
|
+
// falsy defaultValue. Seven are number/money/checkbox and are unaffected, their fallthrough already
|
|
389
|
+
// producing the same value. The other four are selects that asked for 0 and were silently getting null:
|
|
390
|
+
// accounting.service.ts:152 itemType, :574 kind, assets.service.ts:25 defaultDepreciationMethod and
|
|
391
|
+
// :115 disposalType — three of them required, so users were being made to re-pick a value the config had
|
|
392
|
+
// already chosen for them. This is therefore behaviour-neutral everywhere except where it repairs a bug.
|
|
393
|
+
if (field.defaultValue !== undefined && field.defaultValue !== null) {
|
|
379
394
|
if ((field.type == 'date' || field.type == 'datetime') && field.defaultValue == 'now')
|
|
380
395
|
return this.nowDate(true);
|
|
381
396
|
return field.defaultValue;
|
|
@@ -6766,7 +6781,13 @@ class AccountingService {
|
|
|
6766
6781
|
{ name: 'code', type: 'text', alias: 'Account Code', section: 'classification', infoMessage: 'Chart of accounts code (1xxx assets, 2xxx liabilities, 3xxx equity, 4xxx revenue, 5xxx expenses)' }, // Changed: COA code (B1); moved into the collapsed section
|
|
6767
6782
|
{ name: 'includeInCashTotal', type: 'checkbox', alias: 'Include in Cash Total', section: 'classification', hidden: (data) => data.type !== 0 }, // Changed: Only visible for Asset accounts (type 0)
|
|
6768
6783
|
{ name: 'includeInBankTotal', type: 'checkbox', alias: 'Include in Bank Total', section: 'classification', hidden: (data) => data.type !== 0 }, // Changed: Only visible for Asset accounts (type 0)
|
|
6769
|
-
|
|
6784
|
+
// Changed (E21): defaultValue added. An untouched select initialises to null (TinCore.getInitialValue
|
|
6785
|
+
// `case 'select': return null`), and Account.CashFlow is a NON-NULLABLE CashFlowCategory, so creating an
|
|
6786
|
+
// account without opening this collapsed section posted cashFlow:null and was rejected at model binding
|
|
6787
|
+
// with a 400 — surfaced to the user as "Something went wrong ... a technical problem in the app" and
|
|
6788
|
+
// logged nowhere. 0 is Operating, which is the model's own C# default (Account.cs:69), so this makes the
|
|
6789
|
+
// form agree with the entity rather than inventing a value.
|
|
6790
|
+
{ name: 'cashFlow', type: 'select', alias: 'Cash Flow Section', section: 'classification', defaultValue: 0, infoMessage: 'IAS 7 cash flow statement classification', // Changed: C5 cash flow category
|
|
6770
6791
|
options: [
|
|
6771
6792
|
{ name: 'Operating', value: 0 },
|
|
6772
6793
|
{ name: 'Investing', value: 1 },
|
|
@@ -17362,6 +17383,7 @@ class TableComponent {
|
|
|
17362
17383
|
this.apiErrorService = apiErrorService;
|
|
17363
17384
|
this.runtimeConfig = runtimeConfig;
|
|
17364
17385
|
this.subs = []; // TS-8: breakpoint + parent-owned reload subscriptions leaked on table teardown; stacked reload subs also caused duplicate loadData fetches
|
|
17386
|
+
this.initialized = false; // Added: guards ensureInitialized — initialization must happen exactly once, whichever hook reaches it first
|
|
17365
17387
|
this.elevation = "mat-elevation-z5";
|
|
17366
17388
|
this.actionsWidth = "50px";
|
|
17367
17389
|
// Added: collapsible flat section header (sectionConfig) — Day Book sections
|
|
@@ -17465,6 +17487,17 @@ class TableComponent {
|
|
|
17465
17487
|
}));
|
|
17466
17488
|
}
|
|
17467
17489
|
ngOnInit() {
|
|
17490
|
+
this.ensureInitialized(); // Changed: the body moved to ensureInitialized so ngOnChanges can pull it forward when it has to start a load first
|
|
17491
|
+
}
|
|
17492
|
+
// Added: Angular runs ngOnChanges BEFORE ngOnInit on the first pass. A lazy tab that is already active on the
|
|
17493
|
+
// first binding (a details dialog's tableConfigs[0]) therefore started its load from ngOnChanges while
|
|
17494
|
+
// setupPagination() had not yet run, so pagedMode was still false and the rows landed on the legacy path;
|
|
17495
|
+
// ngOnInit then flipped pagedMode on and the next updateSlice() drew from the empty loadedRows window and
|
|
17496
|
+
// replaced the good rows with nothing. Initialization is now idempotent so the load site can order it first.
|
|
17497
|
+
ensureInitialized() {
|
|
17498
|
+
if (this.initialized)
|
|
17499
|
+
return;
|
|
17500
|
+
this.initialized = true;
|
|
17468
17501
|
this.sectionCollapsed = !!this.config?.sectionConfig?.collapsed; // Added: seed section collapse from config — state lives on the component, never written back to the shared config
|
|
17469
17502
|
if (this.config?.formConfig) {
|
|
17470
17503
|
this.hasFormAccess = Core.hasFormAccess(this.config.formConfig, this.authService.currentRoleSource.value);
|
|
@@ -17487,6 +17520,7 @@ class TableComponent {
|
|
|
17487
17520
|
}
|
|
17488
17521
|
if (this.inTab && changes['activeTab']) {
|
|
17489
17522
|
if (!this.hasBeenActivated && this.activeTab && this.config?.lazyLoad && this.config.loadAction) {
|
|
17523
|
+
this.ensureInitialized(); // Changed: this is the ONLY load ngOnChanges can start, and it must not run ahead of setupPagination() — see ensureInitialized
|
|
17490
17524
|
this.loadData(this.config.loadAction, "");
|
|
17491
17525
|
this.hasBeenActivated = true;
|
|
17492
17526
|
}
|
|
@@ -18548,13 +18582,17 @@ class TableComponent {
|
|
|
18548
18582
|
return "mat-elevation-z5";
|
|
18549
18583
|
}
|
|
18550
18584
|
}
|
|
18551
|
-
// Changed:
|
|
18585
|
+
// Changed: this now ALWAYS refreshes. The old body refreshed only when realTime was OFF or SignalR was DOWN,
|
|
18586
|
+
// so a healthy real-time table did nothing after a write and simply waited for a broadcast. But the SignalR
|
|
18587
|
+
// streams below are filtered on this table's own entityName, and an action that writes a DIFFERENT entity
|
|
18588
|
+
// (invoicing a rental writes an Invoice) never emits a matching one — grip's rentals grid kept rendering
|
|
18589
|
+
// "Returned" for a row already Invoiced in the database. Nothing correlates a broadcast back to the action
|
|
18590
|
+
// just performed, so "refresh unless a broadcast arrives" is not implementable without new machinery.
|
|
18591
|
+
// A same-URL re-read after a user-initiated write is idempotent; a broadcast that does arrive patches the same rows.
|
|
18552
18592
|
// In paged mode this is NOT a full reload — refreshClicked routes through loadDataPaged's same-URL branch,
|
|
18553
18593
|
// which re-fetches only the loaded window (skip=0, take=loadedCount), never the whole dataset.
|
|
18554
18594
|
realTimeRefreshOrFallback() {
|
|
18555
|
-
if (!this.effRealTime || !this.isSignalRConnected)
|
|
18556
|
-
this.refreshClicked();
|
|
18557
|
-
}
|
|
18595
|
+
this.refreshClicked(); // Changed: unconditional — was `if (!this.effRealTime || !this.isSignalRConnected)`, which made the healthy real-time case a silent no-op
|
|
18558
18596
|
}
|
|
18559
18597
|
//---------------- TinSync offline support ----------------
|
|
18560
18598
|
// Registers the table's URLs with the offline service, starts the sync engine, and wires live overlay updates
|