pict-section-recordset 1.17.0 → 1.19.0
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/package.json
CHANGED
|
@@ -38,6 +38,31 @@ class PictRecordSetAssociationManager extends libPictProvider
|
|
|
38
38
|
|
|
39
39
|
/** @type {Record<string, any>} - Lazily-created EntityProviders scoped to a non-default URL prefix. */
|
|
40
40
|
this._scopedEntityProviders = {};
|
|
41
|
+
|
|
42
|
+
/** @type {string} - EntityProvider cache scope for join-list reads; cleared on every join write so
|
|
43
|
+
* the editor and pickers never show a stale (cached) association list after an add or remove. */
|
|
44
|
+
this._cacheScope = 'RecordSetAssociation';
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Invalidate the cached join-list reads after a write so the next list reflects it immediately
|
|
49
|
+
* (pict's EntityProvider caches getEntitySet by filter for ~10s; clearScope no-ops on the default
|
|
50
|
+
* empty scope, which is why join reads run under a dedicated scope).
|
|
51
|
+
* @param {Record<string, any>} pEntityProvider - the (scoped) EntityProvider used for the join.
|
|
52
|
+
*/
|
|
53
|
+
_clearAssociationCache(pEntityProvider)
|
|
54
|
+
{
|
|
55
|
+
try
|
|
56
|
+
{
|
|
57
|
+
if (pEntityProvider && (typeof pEntityProvider.clearScope === 'function'))
|
|
58
|
+
{
|
|
59
|
+
pEntityProvider.clearScope(this._cacheScope);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
catch (pError)
|
|
63
|
+
{
|
|
64
|
+
this.pict.log.warn(`AssociationManager: association cache clear failed: ${pError.message || pError}`);
|
|
65
|
+
}
|
|
41
66
|
}
|
|
42
67
|
|
|
43
68
|
/**
|
|
@@ -53,10 +78,20 @@ class PictRecordSetAssociationManager extends libPictProvider
|
|
|
53
78
|
const tmpSide = pSide || {};
|
|
54
79
|
const tmpEntity = tmpSide.Entity || tmpSide.RecordSet;
|
|
55
80
|
const tmpDisplayField = tmpSide.DisplayField || 'Name';
|
|
81
|
+
const tmpIDField = tmpSide.IDField || `ID${tmpEntity}`;
|
|
56
82
|
return {
|
|
57
83
|
RecordSet: tmpSide.RecordSet || tmpEntity,
|
|
58
84
|
Entity: tmpEntity,
|
|
59
|
-
|
|
85
|
+
// The field on THIS SIDE's record that identifies it for association purposes — the picker
|
|
86
|
+
// value, the display lookup key, and (by default) the value stored in the join. Usually the
|
|
87
|
+
// primary key (`ID<Entity>`), but can be any unique field (e.g. ObservationManifest is keyed
|
|
88
|
+
// by 'Name' on its join, so its side is { IDField: 'Name' }).
|
|
89
|
+
IDField: tmpIDField,
|
|
90
|
+
// The column on the JOIN ENTITY that references this side. Defaults to IDField — the common
|
|
91
|
+
// case where the join column has the same name as the side's id field. Set it when the join
|
|
92
|
+
// names this side differently: ProjectObservationManifestJoin stores the manifest by
|
|
93
|
+
// 'ObservationManifestName', so the manifest side is { IDField: 'Name', JoinField: 'ObservationManifestName' }.
|
|
94
|
+
JoinField: tmpSide.JoinField || tmpIDField,
|
|
60
95
|
DisplayField: tmpDisplayField,
|
|
61
96
|
SearchFields: (Array.isArray(tmpSide.SearchFields) && tmpSide.SearchFields.length > 0) ? tmpSide.SearchFields : [ tmpDisplayField ],
|
|
62
97
|
// No default sort: alphabetical-by-display sorts empty values first (blank rows). The picker's
|
|
@@ -110,6 +145,17 @@ class PictRecordSetAssociationManager extends libPictProvider
|
|
|
110
145
|
return `FOP~0~(~0~${tmpInner}~FCP~0~)~0`;
|
|
111
146
|
}
|
|
112
147
|
|
|
148
|
+
/**
|
|
149
|
+
* URL-encode each value and comma-join, for `INN`/`NIN` filter lists. Numeric ids pass through; this
|
|
150
|
+
* keeps string keys (e.g. manifest names with spaces) intact when a side is keyed by a name column.
|
|
151
|
+
* @param {Array<string|number>} pValues
|
|
152
|
+
* @return {string}
|
|
153
|
+
*/
|
|
154
|
+
_encodeList(pValues)
|
|
155
|
+
{
|
|
156
|
+
return (Array.isArray(pValues) ? pValues : []).map((pValue) => encodeURIComponent(pValue)).join(',');
|
|
157
|
+
}
|
|
158
|
+
|
|
113
159
|
/**
|
|
114
160
|
* Fetch one page of a side's records for the matrix table (search across its SearchFields + optional
|
|
115
161
|
* Sort, offset/limit paging). Returns the raw records + a `hasMore` flag.
|
|
@@ -161,6 +207,17 @@ class PictRecordSetAssociationManager extends libPictProvider
|
|
|
161
207
|
JoinEntity: pDefinition.JoinEntity,
|
|
162
208
|
JoinURLPrefix: pDefinition.JoinURLPrefix || '',
|
|
163
209
|
DefaultJoinValues: pDefinition.DefaultJoinValues || {},
|
|
210
|
+
// Per-join config columns the editor renders as inline-editable on each association row — for
|
|
211
|
+
// "rich" joins that carry settings (e.g. Journal/Spreadsheet/Ordinal). Each entry:
|
|
212
|
+
// { Field, Label?, Type? ('checkbox'|'number'|'text'|'select'), Options?, Width? }.
|
|
213
|
+
JoinEditableFields: Array.isArray(pDefinition.JoinEditableFields) ? pDefinition.JoinEditableFields : [],
|
|
214
|
+
// The defaults lifecycle hook — the overridable seam the host implements. An async
|
|
215
|
+
// `(context) => Array<{ value, joinValues? }>` returning the OTHER side's key values (+ optional
|
|
216
|
+
// per-join config) to seed for an anchor record. PSRS owns APPLYING them (dedupe + createJoin),
|
|
217
|
+
// so every association synthesizes defaults the same way; the host only decides what they are.
|
|
218
|
+
SynthesizeDefaults: (typeof pDefinition.SynthesizeDefaults === 'function') ? pDefinition.SynthesizeDefaults : false,
|
|
219
|
+
// Opt-in: auto-run synthesizeDefaults the first time the editor opens an anchor with zero joins.
|
|
220
|
+
AutoSynthesizeWhenEmpty: (pDefinition.AutoSynthesizeWhenEmpty === true),
|
|
164
221
|
SideA: this._normalizeSide(pDefinition.SideA),
|
|
165
222
|
SideB: this._normalizeSide(pDefinition.SideB),
|
|
166
223
|
};
|
|
@@ -283,7 +340,7 @@ class PictRecordSetAssociationManager extends libPictProvider
|
|
|
283
340
|
return Promise.resolve([]);
|
|
284
341
|
}
|
|
285
342
|
const tmpEntityProvider = this._entityProvider(tmpSides.association.JoinURLPrefix);
|
|
286
|
-
const tmpFilter = `FBV~${tmpSides.thisSide.
|
|
343
|
+
const tmpFilter = `FBV~${tmpSides.thisSide.JoinField}~EQ~${encodeURIComponent(pThisID)}`;
|
|
287
344
|
return new Promise((resolve) =>
|
|
288
345
|
{
|
|
289
346
|
tmpEntityProvider.getEntitySet(tmpSides.association.JoinEntity, tmpFilter, (pError, pRecords) =>
|
|
@@ -294,7 +351,7 @@ class PictRecordSetAssociationManager extends libPictProvider
|
|
|
294
351
|
return resolve([]);
|
|
295
352
|
}
|
|
296
353
|
return resolve(Array.isArray(pRecords) ? pRecords : []);
|
|
297
|
-
});
|
|
354
|
+
}, '', { Scope: this._cacheScope, NoCount: true });
|
|
298
355
|
});
|
|
299
356
|
}
|
|
300
357
|
|
|
@@ -315,7 +372,7 @@ class PictRecordSetAssociationManager extends libPictProvider
|
|
|
315
372
|
return Promise.resolve([]);
|
|
316
373
|
}
|
|
317
374
|
const tmpEntityProvider = this._entityProvider(tmpSides.association.JoinURLPrefix);
|
|
318
|
-
const tmpFilter = `FBL~${tmpSides.thisSide.
|
|
375
|
+
const tmpFilter = `FBL~${tmpSides.thisSide.JoinField}~INN~${this._encodeList(pThisIDs)}`;
|
|
319
376
|
return new Promise((resolve) =>
|
|
320
377
|
{
|
|
321
378
|
tmpEntityProvider.getEntitySet(tmpSides.association.JoinEntity, tmpFilter, (pError, pRecords) =>
|
|
@@ -326,7 +383,7 @@ class PictRecordSetAssociationManager extends libPictProvider
|
|
|
326
383
|
return resolve([]);
|
|
327
384
|
}
|
|
328
385
|
return resolve(Array.isArray(pRecords) ? pRecords : []);
|
|
329
|
-
});
|
|
386
|
+
}, '', { Scope: this._cacheScope, NoCount: true });
|
|
330
387
|
});
|
|
331
388
|
}
|
|
332
389
|
|
|
@@ -347,7 +404,7 @@ class PictRecordSetAssociationManager extends libPictProvider
|
|
|
347
404
|
}
|
|
348
405
|
const tmpJoins = await this.listJoinRecords(pAssociationHash, pThisRecordSetName, pThisID);
|
|
349
406
|
return tmpJoins
|
|
350
|
-
.map((pJoin) => pJoin[tmpSides.otherSide.
|
|
407
|
+
.map((pJoin) => pJoin[tmpSides.otherSide.JoinField])
|
|
351
408
|
.filter((pValue) => (pValue !== undefined && pValue !== null && pValue !== ''));
|
|
352
409
|
}
|
|
353
410
|
|
|
@@ -372,14 +429,14 @@ class PictRecordSetAssociationManager extends libPictProvider
|
|
|
372
429
|
const tmpJoinIDField = this.getJoinIDField(tmpSides.association);
|
|
373
430
|
const tmpJoins = await this.listJoinRecords(pAssociationHash, pThisRecordSetName, pThisID);
|
|
374
431
|
const tmpOtherIDs = tmpJoins
|
|
375
|
-
.map((pJoin) => pJoin[tmpSides.otherSide.
|
|
432
|
+
.map((pJoin) => pJoin[tmpSides.otherSide.JoinField])
|
|
376
433
|
.filter((pValue) => (pValue !== undefined && pValue !== null && pValue !== ''));
|
|
377
434
|
|
|
378
435
|
let tmpByID = {};
|
|
379
436
|
if (tmpOtherIDs.length > 0)
|
|
380
437
|
{
|
|
381
438
|
const tmpEntityProvider = this._entityProvider(tmpSides.otherSide.URLPrefix);
|
|
382
|
-
const tmpFilter = `FBL~${tmpSides.otherSide.IDField}~INN~${
|
|
439
|
+
const tmpFilter = `FBL~${tmpSides.otherSide.IDField}~INN~${this._encodeList(tmpOtherIDs)}`;
|
|
383
440
|
const tmpOthers = await new Promise((resolve) =>
|
|
384
441
|
{
|
|
385
442
|
tmpEntityProvider.getEntitySet(tmpSides.otherSide.Entity, tmpFilter, (pError, pRecords) =>
|
|
@@ -399,10 +456,10 @@ class PictRecordSetAssociationManager extends libPictProvider
|
|
|
399
456
|
}
|
|
400
457
|
|
|
401
458
|
return tmpJoins
|
|
402
|
-
.filter((pJoin) => (pJoin[tmpSides.otherSide.
|
|
459
|
+
.filter((pJoin) => (pJoin[tmpSides.otherSide.JoinField] !== undefined && pJoin[tmpSides.otherSide.JoinField] !== null && pJoin[tmpSides.otherSide.JoinField] !== ''))
|
|
403
460
|
.map((pJoin) =>
|
|
404
461
|
{
|
|
405
|
-
const tmpOtherID = pJoin[tmpSides.otherSide.
|
|
462
|
+
const tmpOtherID = pJoin[tmpSides.otherSide.JoinField];
|
|
406
463
|
const tmpOtherRecord = tmpByID[tmpOtherID] || {};
|
|
407
464
|
const tmpDisplay = (tmpOtherRecord[tmpSides.otherSide.DisplayField] !== undefined && tmpOtherRecord[tmpSides.otherSide.DisplayField] !== null && tmpOtherRecord[tmpSides.otherSide.DisplayField] !== '')
|
|
408
465
|
? tmpOtherRecord[tmpSides.otherSide.DisplayField]
|
|
@@ -426,18 +483,20 @@ class PictRecordSetAssociationManager extends libPictProvider
|
|
|
426
483
|
* @param {string} pThisRecordSetName
|
|
427
484
|
* @param {string|number} pThisID
|
|
428
485
|
* @param {string|number} pOtherID
|
|
486
|
+
* @param {Record<string, any>} [pJoinValues] - Optional extra columns to stamp on the join row (e.g.
|
|
487
|
+
* per-association config like { Journal: 1, Ordinal: 3 } from a synthesized default).
|
|
429
488
|
* @return {Promise<Record<string, any>>}
|
|
430
489
|
*/
|
|
431
|
-
createJoin(pAssociationHash, pThisRecordSetName, pThisID, pOtherID)
|
|
490
|
+
createJoin(pAssociationHash, pThisRecordSetName, pThisID, pOtherID, pJoinValues)
|
|
432
491
|
{
|
|
433
492
|
const tmpSides = this.resolveSides(pAssociationHash, pThisRecordSetName);
|
|
434
493
|
if (!tmpSides)
|
|
435
494
|
{
|
|
436
495
|
return Promise.reject(new Error(`AssociationManager: cannot create join for [${pAssociationHash}] from [${pThisRecordSetName}].`));
|
|
437
496
|
}
|
|
438
|
-
const tmpRecord = Object.assign({}, tmpSides.association.DefaultJoinValues);
|
|
439
|
-
tmpRecord[tmpSides.thisSide.
|
|
440
|
-
tmpRecord[tmpSides.otherSide.
|
|
497
|
+
const tmpRecord = Object.assign({}, tmpSides.association.DefaultJoinValues, pJoinValues || {});
|
|
498
|
+
tmpRecord[tmpSides.thisSide.JoinField] = pThisID;
|
|
499
|
+
tmpRecord[tmpSides.otherSide.JoinField] = pOtherID;
|
|
441
500
|
const tmpEntityProvider = this._entityProvider(tmpSides.association.JoinURLPrefix);
|
|
442
501
|
return new Promise((resolve, reject) =>
|
|
443
502
|
{
|
|
@@ -447,6 +506,7 @@ class PictRecordSetAssociationManager extends libPictProvider
|
|
|
447
506
|
{
|
|
448
507
|
return reject(pError);
|
|
449
508
|
}
|
|
509
|
+
this._clearAssociationCache(tmpEntityProvider);
|
|
450
510
|
return resolve(pBody);
|
|
451
511
|
});
|
|
452
512
|
});
|
|
@@ -476,11 +536,143 @@ class PictRecordSetAssociationManager extends libPictProvider
|
|
|
476
536
|
{
|
|
477
537
|
return reject(pError);
|
|
478
538
|
}
|
|
539
|
+
this._clearAssociationCache(tmpEntityProvider);
|
|
479
540
|
return resolve(pBody);
|
|
480
541
|
});
|
|
481
542
|
});
|
|
482
543
|
}
|
|
483
544
|
|
|
545
|
+
/**
|
|
546
|
+
* Update the config columns of an existing join row (a "rich" join's per-association settings, e.g.
|
|
547
|
+
* Journal / Spreadsheet / Ordinal). Merges pValues over the join record and PUTs it.
|
|
548
|
+
*
|
|
549
|
+
* @param {string} pAssociationHash
|
|
550
|
+
* @param {Record<string, any>} pJoinRecord - the full join row (as carried by listAssociatedRecords).
|
|
551
|
+
* @param {Record<string, any>} pValues - the columns to change.
|
|
552
|
+
* @return {Promise<Record<string, any>>}
|
|
553
|
+
*/
|
|
554
|
+
updateJoin(pAssociationHash, pJoinRecord, pValues)
|
|
555
|
+
{
|
|
556
|
+
const tmpAssociation = this.associations[pAssociationHash];
|
|
557
|
+
if (!tmpAssociation || !pJoinRecord)
|
|
558
|
+
{
|
|
559
|
+
return Promise.reject(new Error(`AssociationManager: cannot update join for [${pAssociationHash}].`));
|
|
560
|
+
}
|
|
561
|
+
const tmpRecord = Object.assign({}, pJoinRecord, pValues || {});
|
|
562
|
+
const tmpEntityProvider = this._entityProvider(tmpAssociation.JoinURLPrefix);
|
|
563
|
+
return new Promise((resolve, reject) =>
|
|
564
|
+
{
|
|
565
|
+
tmpEntityProvider.updateEntity(tmpAssociation.JoinEntity, tmpRecord, (pError, pBody) =>
|
|
566
|
+
{
|
|
567
|
+
if (pError)
|
|
568
|
+
{
|
|
569
|
+
return reject(pError);
|
|
570
|
+
}
|
|
571
|
+
this._clearAssociationCache(tmpEntityProvider);
|
|
572
|
+
return resolve(pBody);
|
|
573
|
+
});
|
|
574
|
+
});
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
/**
|
|
578
|
+
* The per-join config columns an association exposes as inline-editable (empty for a plain join).
|
|
579
|
+
* @param {string} pAssociationHash
|
|
580
|
+
* @return {Array<Record<string, any>>}
|
|
581
|
+
*/
|
|
582
|
+
getJoinEditableFields(pAssociationHash)
|
|
583
|
+
{
|
|
584
|
+
const tmpAssociation = this.associations[pAssociationHash];
|
|
585
|
+
return (tmpAssociation && Array.isArray(tmpAssociation.JoinEditableFields)) ? tmpAssociation.JoinEditableFields : [];
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
/**
|
|
589
|
+
* Whether an association has a host-provided defaults synthesizer registered.
|
|
590
|
+
* @param {string} pAssociationHash
|
|
591
|
+
* @return {boolean}
|
|
592
|
+
*/
|
|
593
|
+
hasDefaultSynthesizer(pAssociationHash)
|
|
594
|
+
{
|
|
595
|
+
const tmpAssociation = this.associations[pAssociationHash];
|
|
596
|
+
return !!(tmpAssociation && (typeof tmpAssociation.SynthesizeDefaults === 'function'));
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
/**
|
|
600
|
+
* Synthesize default associations for one anchor record — THE consistent defaults lifecycle function.
|
|
601
|
+
* PSRS owns the "apply" half (dedupe against existing joins, then createJoin each new one with its
|
|
602
|
+
* config); the host owns only WHAT the defaults are, via the association's overridable
|
|
603
|
+
* `SynthesizeDefaults(context)` hook. No-op when no hook is registered.
|
|
604
|
+
*
|
|
605
|
+
* The hook receives `{ association, thisSide, otherSide, thisID, existingJoins, manager, pict }` and
|
|
606
|
+
* returns `Array<{ value, joinValues? }>` — `value` is the OTHER side's key value (what goes in the
|
|
607
|
+
* join's other JoinField), `joinValues` is optional extra config to stamp on that join row.
|
|
608
|
+
*
|
|
609
|
+
* @param {string} pAssociationHash
|
|
610
|
+
* @param {string} pThisRecordSetName
|
|
611
|
+
* @param {string|number} pThisID
|
|
612
|
+
* @return {Promise<{ created: number, skipped: number }>}
|
|
613
|
+
*/
|
|
614
|
+
async synthesizeDefaults(pAssociationHash, pThisRecordSetName, pThisID)
|
|
615
|
+
{
|
|
616
|
+
const tmpSides = this.resolveSides(pAssociationHash, pThisRecordSetName);
|
|
617
|
+
if (!tmpSides || !this.hasDefaultSynthesizer(pAssociationHash) || pThisID === undefined || pThisID === null || pThisID === '')
|
|
618
|
+
{
|
|
619
|
+
return { created: 0, skipped: 0 };
|
|
620
|
+
}
|
|
621
|
+
const tmpExistingJoins = await this.listJoinRecords(pAssociationHash, pThisRecordSetName, pThisID);
|
|
622
|
+
const tmpExisting = {};
|
|
623
|
+
tmpExistingJoins.forEach((pJoin) =>
|
|
624
|
+
{
|
|
625
|
+
const tmpValue = pJoin[tmpSides.otherSide.JoinField];
|
|
626
|
+
if (tmpValue !== undefined && tmpValue !== null && tmpValue !== '') { tmpExisting[`${tmpValue}`] = true; }
|
|
627
|
+
});
|
|
628
|
+
|
|
629
|
+
let tmpDefaults = [];
|
|
630
|
+
try
|
|
631
|
+
{
|
|
632
|
+
tmpDefaults = await tmpSides.association.SynthesizeDefaults(
|
|
633
|
+
{
|
|
634
|
+
association: tmpSides.association,
|
|
635
|
+
thisSide: tmpSides.thisSide,
|
|
636
|
+
otherSide: tmpSides.otherSide,
|
|
637
|
+
thisID: pThisID,
|
|
638
|
+
existingJoins: tmpExistingJoins,
|
|
639
|
+
manager: this,
|
|
640
|
+
pict: this.pict,
|
|
641
|
+
});
|
|
642
|
+
}
|
|
643
|
+
catch (pError)
|
|
644
|
+
{
|
|
645
|
+
this.pict.log.error(`AssociationManager: SynthesizeDefaults hook for [${pAssociationHash}] threw: ${pError.message || pError}`);
|
|
646
|
+
return { created: 0, skipped: 0 };
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
let tmpCreated = 0;
|
|
650
|
+
let tmpSkipped = 0;
|
|
651
|
+
const tmpList = Array.isArray(tmpDefaults) ? tmpDefaults : [];
|
|
652
|
+
for (let i = 0; i < tmpList.length; i++)
|
|
653
|
+
{
|
|
654
|
+
const tmpDefault = tmpList[i] || {};
|
|
655
|
+
const tmpValue = (tmpDefault.value !== undefined) ? tmpDefault.value : tmpDefault.Value;
|
|
656
|
+
if (tmpValue === undefined || tmpValue === null || tmpValue === '' || tmpExisting[`${tmpValue}`])
|
|
657
|
+
{
|
|
658
|
+
tmpSkipped++;
|
|
659
|
+
continue;
|
|
660
|
+
}
|
|
661
|
+
try
|
|
662
|
+
{
|
|
663
|
+
await this.createJoin(pAssociationHash, pThisRecordSetName, pThisID, tmpValue, tmpDefault.joinValues || tmpDefault.JoinValues || {});
|
|
664
|
+
tmpExisting[`${tmpValue}`] = true;
|
|
665
|
+
tmpCreated++;
|
|
666
|
+
}
|
|
667
|
+
catch (pError)
|
|
668
|
+
{
|
|
669
|
+
this.pict.log.warn(`AssociationManager: synthesize createJoin failed for [${pAssociationHash}] value [${tmpValue}]: ${pError.message || pError}`);
|
|
670
|
+
tmpSkipped++;
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
return { created: tmpCreated, skipped: tmpSkipped };
|
|
674
|
+
}
|
|
675
|
+
|
|
484
676
|
/**
|
|
485
677
|
* Build a `createEntityPicker` config for one side, optionally culling a live set of ids (a function
|
|
486
678
|
* so the cull re-evaluates on every search as associations change).
|
|
@@ -511,7 +703,7 @@ class PictRecordSetAssociationManager extends libPictProvider
|
|
|
511
703
|
tmpConfig.BaseFilter = () =>
|
|
512
704
|
{
|
|
513
705
|
const tmpIDs = pGetExcludedIDsFn();
|
|
514
|
-
return (Array.isArray(tmpIDs) && tmpIDs.length > 0) ? `FBL~${pSide.IDField}~NIN~${
|
|
706
|
+
return (Array.isArray(tmpIDs) && tmpIDs.length > 0) ? `FBL~${pSide.IDField}~NIN~${this._encodeList(tmpIDs)}` : '';
|
|
515
707
|
};
|
|
516
708
|
}
|
|
517
709
|
return Object.assign(tmpConfig, pOverrides || {});
|