@mj-biz-apps/contracts-ng 0.4.1 → 0.5.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/dist/lib/data/entity-names.d.ts +38 -0
- package/dist/lib/data/entity-names.d.ts.map +1 -1
- package/dist/lib/data/entity-names.js +38 -0
- package/dist/lib/data/entity-names.js.map +1 -1
- package/dist/lib/data/task-filters.d.ts +66 -0
- package/dist/lib/data/task-filters.d.ts.map +1 -0
- package/dist/lib/data/task-filters.js +124 -0
- package/dist/lib/data/task-filters.js.map +1 -0
- package/dist/lib/form-panels/contract-form.panels.d.ts +217 -20
- package/dist/lib/form-panels/contract-form.panels.d.ts.map +1 -1
- package/dist/lib/form-panels/contract-form.panels.js +533 -38
- package/dist/lib/form-panels/contract-form.panels.js.map +1 -1
- package/dist/lib/form-panels/contract.panels.d.ts +67 -3
- package/dist/lib/form-panels/contract.panels.d.ts.map +1 -1
- package/dist/lib/form-panels/contract.panels.js +55 -2
- package/dist/lib/form-panels/contract.panels.js.map +1 -1
- package/dist/lib/generated/Entities/mjBizAppsContractsContractType/mjbizappscontractscontracttype.form.component.d.ts.map +1 -1
- package/dist/lib/generated/Entities/mjBizAppsContractsContractType/mjbizappscontractscontracttype.form.component.js +25 -11
- package/dist/lib/generated/Entities/mjBizAppsContractsContractType/mjbizappscontractscontracttype.form.component.js.map +1 -1
- package/dist/lib/pages/contract-grid.page.d.ts +67 -0
- package/dist/lib/pages/contract-grid.page.d.ts.map +1 -1
- package/dist/lib/pages/contract-grid.page.js +147 -5
- package/dist/lib/pages/contract-grid.page.js.map +1 -1
- package/dist/lib/pages/contracts-dashboard.page.d.ts +132 -52
- package/dist/lib/pages/contracts-dashboard.page.d.ts.map +1 -1
- package/dist/lib/pages/contracts-dashboard.page.js +418 -425
- package/dist/lib/pages/contracts-dashboard.page.js.map +1 -1
- package/dist/lib/sections/contracts-sections.component.d.ts +25 -3
- package/dist/lib/sections/contracts-sections.component.d.ts.map +1 -1
- package/dist/lib/sections/contracts-sections.component.js +56 -9
- package/dist/lib/sections/contracts-sections.component.js.map +1 -1
- package/package.json +3 -2
|
@@ -29,5 +29,43 @@ export declare const MJC_FOREIGN_ENTITIES: {
|
|
|
29
29
|
readonly File: "MJ: Files";
|
|
30
30
|
readonly FileCategory: "MJ: File Categories";
|
|
31
31
|
readonly FileEntityRecordLink: "MJ: File Entity Record Links";
|
|
32
|
+
/**
|
|
33
|
+
* bizapps-SALES, and read SOFTLY — no manifest dependency, deliberately (golive #219).
|
|
34
|
+
*
|
|
35
|
+
* Sales depends on THIS app: its Close-Won seam is what creates contracts. That is precisely why
|
|
36
|
+
* a contract's provenance is the polymorphic `CreatingEntityID` / `CreatingRecordID` pair rather
|
|
37
|
+
* than a `DealID` column — a hard reference upward would invert the dependency graph, and the
|
|
38
|
+
* Contract entity's own description says so in as many words.
|
|
39
|
+
*
|
|
40
|
+
* So this is a NAME resolved from provider metadata at runtime and nothing more. No import, no
|
|
41
|
+
* entry in `mj-app.json`. An installation without sales finds no such entity, and the Source-record
|
|
42
|
+
* picker does not render — a supported state, not a failure.
|
|
43
|
+
*/
|
|
44
|
+
readonly Deal: "MJ_BizApps_Sales: Deals";
|
|
45
|
+
/**
|
|
46
|
+
* bizapps-tasks. Read for the "To process" tile and the "Has open task" pill — the finance flow
|
|
47
|
+
* (S-US2 / C-US2) is task-driven, which is why `mj-app.json` declares tasks a real dependency
|
|
48
|
+
* rather than an optional read.
|
|
49
|
+
*
|
|
50
|
+
* ⚠ These belong in `@mj-biz-apps/tasks-entities` and are here only because that package exports
|
|
51
|
+
* generated entity subclasses and nothing else — the names live as private consts scattered
|
|
52
|
+
* across four repos today. When tasks exports them, delete these three and import instead.
|
|
53
|
+
*/
|
|
54
|
+
readonly Task: "MJ_BizApps_Tasks: Tasks";
|
|
55
|
+
readonly TaskLink: "MJ_BizApps_Tasks: Task Links";
|
|
56
|
+
readonly TaskType: "MJ_BizApps_Tasks: Task Types";
|
|
32
57
|
};
|
|
58
|
+
/**
|
|
59
|
+
* Stored queries this app owns, addressed by name the way entities are.
|
|
60
|
+
*
|
|
61
|
+
* `RunQuery` resolves by NAME plus category path, so these strings carry the same typo hazard the
|
|
62
|
+
* entity names do — wrong by one character and the tile renders an em dash at runtime with nothing
|
|
63
|
+
* failing to compile. The rows live in `metadata/queries/`; the category in `metadata/query-categories/`.
|
|
64
|
+
*/
|
|
65
|
+
export declare const MJC_QUERIES: {
|
|
66
|
+
/** Distinct customers with an in-force modified agreement, and how many contracts that spans. */
|
|
67
|
+
readonly SpecialTerms: "Contracts Special Terms";
|
|
68
|
+
};
|
|
69
|
+
/** The category every query above hangs under — `RunQuery`'s `CategoryPath`. */
|
|
70
|
+
export declare const MJC_QUERY_CATEGORY = "Contracts";
|
|
33
71
|
//# sourceMappingURL=entity-names.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entity-names.d.ts","sourceRoot":"","sources":["../../../src/lib/data/entity-names.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,kCAAkC;AAClC,eAAO,MAAM,YAAY;;;;;;;CAOf,CAAC;AAEX,iGAAiG;AACjG,eAAO,MAAM,oBAAoB;;;IAG7B,sFAAsF;;IAEtF,4GAA4G;;;;
|
|
1
|
+
{"version":3,"file":"entity-names.d.ts","sourceRoot":"","sources":["../../../src/lib/data/entity-names.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,kCAAkC;AAClC,eAAO,MAAM,YAAY;;;;;;;CAOf,CAAC;AAEX,iGAAiG;AACjG,eAAO,MAAM,oBAAoB;;;IAG7B,sFAAsF;;IAEtF,4GAA4G;;;;IAI5G;;;;;;;;;;;OAWG;;IAEH;;;;;;;;OAQG;;;;CAIG,CAAC;AAEX;;;;;;GAMG;AACH,eAAO,MAAM,WAAW;IACpB,iGAAiG;;CAE3F,CAAC;AAEX,gFAAgF;AAChF,eAAO,MAAM,kBAAkB,cAAc,CAAC"}
|
|
@@ -29,5 +29,43 @@ export const MJC_FOREIGN_ENTITIES = {
|
|
|
29
29
|
File: 'MJ: Files',
|
|
30
30
|
FileCategory: 'MJ: File Categories',
|
|
31
31
|
FileEntityRecordLink: 'MJ: File Entity Record Links',
|
|
32
|
+
/**
|
|
33
|
+
* bizapps-SALES, and read SOFTLY — no manifest dependency, deliberately (golive #219).
|
|
34
|
+
*
|
|
35
|
+
* Sales depends on THIS app: its Close-Won seam is what creates contracts. That is precisely why
|
|
36
|
+
* a contract's provenance is the polymorphic `CreatingEntityID` / `CreatingRecordID` pair rather
|
|
37
|
+
* than a `DealID` column — a hard reference upward would invert the dependency graph, and the
|
|
38
|
+
* Contract entity's own description says so in as many words.
|
|
39
|
+
*
|
|
40
|
+
* So this is a NAME resolved from provider metadata at runtime and nothing more. No import, no
|
|
41
|
+
* entry in `mj-app.json`. An installation without sales finds no such entity, and the Source-record
|
|
42
|
+
* picker does not render — a supported state, not a failure.
|
|
43
|
+
*/
|
|
44
|
+
Deal: 'MJ_BizApps_Sales: Deals',
|
|
45
|
+
/**
|
|
46
|
+
* bizapps-tasks. Read for the "To process" tile and the "Has open task" pill — the finance flow
|
|
47
|
+
* (S-US2 / C-US2) is task-driven, which is why `mj-app.json` declares tasks a real dependency
|
|
48
|
+
* rather than an optional read.
|
|
49
|
+
*
|
|
50
|
+
* ⚠ These belong in `@mj-biz-apps/tasks-entities` and are here only because that package exports
|
|
51
|
+
* generated entity subclasses and nothing else — the names live as private consts scattered
|
|
52
|
+
* across four repos today. When tasks exports them, delete these three and import instead.
|
|
53
|
+
*/
|
|
54
|
+
Task: 'MJ_BizApps_Tasks: Tasks',
|
|
55
|
+
TaskLink: 'MJ_BizApps_Tasks: Task Links',
|
|
56
|
+
TaskType: 'MJ_BizApps_Tasks: Task Types',
|
|
32
57
|
};
|
|
58
|
+
/**
|
|
59
|
+
* Stored queries this app owns, addressed by name the way entities are.
|
|
60
|
+
*
|
|
61
|
+
* `RunQuery` resolves by NAME plus category path, so these strings carry the same typo hazard the
|
|
62
|
+
* entity names do — wrong by one character and the tile renders an em dash at runtime with nothing
|
|
63
|
+
* failing to compile. The rows live in `metadata/queries/`; the category in `metadata/query-categories/`.
|
|
64
|
+
*/
|
|
65
|
+
export const MJC_QUERIES = {
|
|
66
|
+
/** Distinct customers with an in-force modified agreement, and how many contracts that spans. */
|
|
67
|
+
SpecialTerms: 'Contracts Special Terms',
|
|
68
|
+
};
|
|
69
|
+
/** The category every query above hangs under — `RunQuery`'s `CategoryPath`. */
|
|
70
|
+
export const MJC_QUERY_CATEGORY = 'Contracts';
|
|
33
71
|
//# sourceMappingURL=entity-names.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entity-names.js","sourceRoot":"","sources":["../../../src/lib/data/entity-names.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,kCAAkC;AAClC,MAAM,CAAC,MAAM,YAAY,GAAG;IACxB,QAAQ,EAAE,iCAAiC;IAC3C,YAAY,EAAE,sCAAsC;IACpD,gBAAgB,EAAE,0CAA0C;IAC5D,oBAAoB,EAAE,+CAA+C;IACrE,yBAAyB,EAAE,oDAAoD;IAC/E,4BAA4B,EAAE,uDAAuD;CAC/E,CAAC;AAEX,iGAAiG;AACjG,MAAM,CAAC,MAAM,oBAAoB,GAAG;IAChC,YAAY,EAAE,kCAAkC;IAChD,MAAM,EAAE,2BAA2B;IACnC,sFAAsF;IACtF,OAAO,EAAE,eAAe;IACxB,4GAA4G;IAC5G,IAAI,EAAE,WAAW;IACjB,YAAY,EAAE,qBAAqB;IACnC,oBAAoB,EAAE,8BAA8B;
|
|
1
|
+
{"version":3,"file":"entity-names.js","sourceRoot":"","sources":["../../../src/lib/data/entity-names.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,kCAAkC;AAClC,MAAM,CAAC,MAAM,YAAY,GAAG;IACxB,QAAQ,EAAE,iCAAiC;IAC3C,YAAY,EAAE,sCAAsC;IACpD,gBAAgB,EAAE,0CAA0C;IAC5D,oBAAoB,EAAE,+CAA+C;IACrE,yBAAyB,EAAE,oDAAoD;IAC/E,4BAA4B,EAAE,uDAAuD;CAC/E,CAAC;AAEX,iGAAiG;AACjG,MAAM,CAAC,MAAM,oBAAoB,GAAG;IAChC,YAAY,EAAE,kCAAkC;IAChD,MAAM,EAAE,2BAA2B;IACnC,sFAAsF;IACtF,OAAO,EAAE,eAAe;IACxB,4GAA4G;IAC5G,IAAI,EAAE,WAAW;IACjB,YAAY,EAAE,qBAAqB;IACnC,oBAAoB,EAAE,8BAA8B;IACpD;;;;;;;;;;;OAWG;IACH,IAAI,EAAE,yBAAyB;IAC/B;;;;;;;;OAQG;IACH,IAAI,EAAE,yBAAyB;IAC/B,QAAQ,EAAE,8BAA8B;IACxC,QAAQ,EAAE,8BAA8B;CAClC,CAAC;AAEX;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG;IACvB,iGAAiG;IACjG,YAAY,EAAE,yBAAyB;CACjC,CAAC;AAEX,gFAAgF;AAChF,MAAM,CAAC,MAAM,kBAAkB,GAAG,WAAW,CAAC"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview The one definition of "this contract has an open processing task".
|
|
3
|
+
*
|
|
4
|
+
* WHY THIS IS A FILE AND NOT TWO INLINE STRINGS. The dashboard's "To process" tile and the All
|
|
5
|
+
* Contracts "Has open task" pill must count the same rows — the tile's entire promise is that
|
|
6
|
+
* clicking it lands on the list it just counted. Written twice they agree until one is edited, and
|
|
7
|
+
* the failure is silent: a tile saying 7 over a list showing 5 reads as a stale page rather than a
|
|
8
|
+
* bug. One exported builder, two callers.
|
|
9
|
+
*
|
|
10
|
+
* ## Why a correlated subquery rather than three reads
|
|
11
|
+
*
|
|
12
|
+
* Tasks live in ANOTHER APP's schema, and `Task Links` carries `EntityID` / `RecordID` while the
|
|
13
|
+
* status and due date live on `Tasks`. There is no single view to filter, so the alternatives are a
|
|
14
|
+
* SQL `EXISTS` inside `ExtraFilter`, or reading task ids into memory and passing `ID IN (…)`. The
|
|
15
|
+
* second is not a worklist filter — an `IN` list has to be capped, and a capped filter quietly hides
|
|
16
|
+
* work, which is the one thing a work queue must not do.
|
|
17
|
+
*
|
|
18
|
+
* `ExtraFilter` is raw SQL appended to the base view's WHERE, so the subquery is legal. MJ screens
|
|
19
|
+
* it with a keyword denylist (`DatabaseProviderBase.ValidateUserProvidedSQLClause`) covering
|
|
20
|
+
* `insert / update / delete / exec / drop / -- / union / xp_ / ; / waitfor` — `SELECT`, `EXISTS` and
|
|
21
|
+
* `JOIN` are not on it. (The stricter validator that DOES block `EXISTS` governs aggregate
|
|
22
|
+
* expressions, not filters.) The base view is selected without an alias, so `vwContracts.ID` is a
|
|
23
|
+
* legal correlated reference.
|
|
24
|
+
*
|
|
25
|
+
* ## ⚠ Two consequences worth knowing before this pattern spreads
|
|
26
|
+
*
|
|
27
|
+
* **It bypasses MJ's permission and RLS model for Tasks.** MJ applies entity permissions and row
|
|
28
|
+
* filters to the entity being *read* — here, Contracts. Nothing checks the reader against Tasks, so
|
|
29
|
+
* a user with no access to Tasks still gets a count shaped by them. That is an acceptable trade for
|
|
30
|
+
* a COUNT of the reader's own contracts, and it is the reason not to extend this to reading task
|
|
31
|
+
* rows: the moment task content reaches the screen, it must go through a Tasks read.
|
|
32
|
+
*
|
|
33
|
+
* **Nothing here is hardcoded except the type code.** Schema and view names come from MJ's metadata
|
|
34
|
+
* rather than string literals, so an app that is not installed produces `null` (and a dashboard tile
|
|
35
|
+
* that renders "—") instead of SQL naming a schema that does not exist.
|
|
36
|
+
*
|
|
37
|
+
* @module @mj-biz-apps/contracts-ng
|
|
38
|
+
*/
|
|
39
|
+
import { type IMetadataProvider } from '@memberjunction/core';
|
|
40
|
+
/**
|
|
41
|
+
* The task type sales raises on Close-Won for finance to work through.
|
|
42
|
+
*
|
|
43
|
+
* Addressed by CODE, not by name or id: the row is seeded by bizapps-sales
|
|
44
|
+
* (`metadata/task-types/.task-types.json`) and `CloseWonTaskService` writes this exact code, so the
|
|
45
|
+
* code is the contract between the two apps. A display name is content and can be edited; an id
|
|
46
|
+
* differs per database.
|
|
47
|
+
*/
|
|
48
|
+
export declare const MJC_TASK_CODE_CONTRACT_PROCESSING = "CONTRACT_PROCESSING";
|
|
49
|
+
/** The two filter fragments the dashboard and the pill share. */
|
|
50
|
+
export interface MJCOpenTaskFilters {
|
|
51
|
+
/** Contracts with at least one open Contract Processing task. */
|
|
52
|
+
HasOpenTask: string;
|
|
53
|
+
/** The same, narrowed to tasks whose due date has passed. */
|
|
54
|
+
HasOverdueTask: string;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Build the shared fragments, or `null` when they cannot be built.
|
|
58
|
+
*
|
|
59
|
+
* `null` is returned — rather than a filter that matches nothing — when bizapps-tasks is not
|
|
60
|
+
* installed, or when the CONTRACT_PROCESSING type row is missing. The distinction matters at the
|
|
61
|
+
* call site: a filter matching nothing renders a confident `0`, while `null` lets the caller render
|
|
62
|
+
* "—". "We cannot tell" and "there is no work" are different answers, and only one of them is safe
|
|
63
|
+
* to show a person whose job is working the queue.
|
|
64
|
+
*/
|
|
65
|
+
export declare function BuildOpenTaskFilters(provider?: IMetadataProvider | null): Promise<MJCOpenTaskFilters | null>;
|
|
66
|
+
//# sourceMappingURL=task-filters.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"task-filters.d.ts","sourceRoot":"","sources":["../../../src/lib/data/task-filters.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,OAAO,EAAY,KAAK,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAIxE;;;;;;;GAOG;AACH,eAAO,MAAM,iCAAiC,wBAAwB,CAAC;AAKvE,iEAAiE;AACjE,MAAM,WAAW,kBAAkB;IAC/B,iEAAiE;IACjE,WAAW,EAAE,MAAM,CAAC;IACpB,6DAA6D;IAC7D,cAAc,EAAE,MAAM,CAAC;CAC1B;AAED;;;;;;;;GAQG;AACH,wBAAsB,oBAAoB,CACtC,QAAQ,CAAC,EAAE,iBAAiB,GAAG,IAAI,GACpC,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAqCpC"}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview The one definition of "this contract has an open processing task".
|
|
3
|
+
*
|
|
4
|
+
* WHY THIS IS A FILE AND NOT TWO INLINE STRINGS. The dashboard's "To process" tile and the All
|
|
5
|
+
* Contracts "Has open task" pill must count the same rows — the tile's entire promise is that
|
|
6
|
+
* clicking it lands on the list it just counted. Written twice they agree until one is edited, and
|
|
7
|
+
* the failure is silent: a tile saying 7 over a list showing 5 reads as a stale page rather than a
|
|
8
|
+
* bug. One exported builder, two callers.
|
|
9
|
+
*
|
|
10
|
+
* ## Why a correlated subquery rather than three reads
|
|
11
|
+
*
|
|
12
|
+
* Tasks live in ANOTHER APP's schema, and `Task Links` carries `EntityID` / `RecordID` while the
|
|
13
|
+
* status and due date live on `Tasks`. There is no single view to filter, so the alternatives are a
|
|
14
|
+
* SQL `EXISTS` inside `ExtraFilter`, or reading task ids into memory and passing `ID IN (…)`. The
|
|
15
|
+
* second is not a worklist filter — an `IN` list has to be capped, and a capped filter quietly hides
|
|
16
|
+
* work, which is the one thing a work queue must not do.
|
|
17
|
+
*
|
|
18
|
+
* `ExtraFilter` is raw SQL appended to the base view's WHERE, so the subquery is legal. MJ screens
|
|
19
|
+
* it with a keyword denylist (`DatabaseProviderBase.ValidateUserProvidedSQLClause`) covering
|
|
20
|
+
* `insert / update / delete / exec / drop / -- / union / xp_ / ; / waitfor` — `SELECT`, `EXISTS` and
|
|
21
|
+
* `JOIN` are not on it. (The stricter validator that DOES block `EXISTS` governs aggregate
|
|
22
|
+
* expressions, not filters.) The base view is selected without an alias, so `vwContracts.ID` is a
|
|
23
|
+
* legal correlated reference.
|
|
24
|
+
*
|
|
25
|
+
* ## ⚠ Two consequences worth knowing before this pattern spreads
|
|
26
|
+
*
|
|
27
|
+
* **It bypasses MJ's permission and RLS model for Tasks.** MJ applies entity permissions and row
|
|
28
|
+
* filters to the entity being *read* — here, Contracts. Nothing checks the reader against Tasks, so
|
|
29
|
+
* a user with no access to Tasks still gets a count shaped by them. That is an acceptable trade for
|
|
30
|
+
* a COUNT of the reader's own contracts, and it is the reason not to extend this to reading task
|
|
31
|
+
* rows: the moment task content reaches the screen, it must go through a Tasks read.
|
|
32
|
+
*
|
|
33
|
+
* **Nothing here is hardcoded except the type code.** Schema and view names come from MJ's metadata
|
|
34
|
+
* rather than string literals, so an app that is not installed produces `null` (and a dashboard tile
|
|
35
|
+
* that renders "—") instead of SQL naming a schema that does not exist.
|
|
36
|
+
*
|
|
37
|
+
* @module @mj-biz-apps/contracts-ng
|
|
38
|
+
*/
|
|
39
|
+
import { Metadata } from '@memberjunction/core';
|
|
40
|
+
import { MJC_ENTITIES, MJC_FOREIGN_ENTITIES } from './entity-names';
|
|
41
|
+
import { ScopedRunView } from './provider';
|
|
42
|
+
/**
|
|
43
|
+
* The task type sales raises on Close-Won for finance to work through.
|
|
44
|
+
*
|
|
45
|
+
* Addressed by CODE, not by name or id: the row is seeded by bizapps-sales
|
|
46
|
+
* (`metadata/task-types/.task-types.json`) and `CloseWonTaskService` writes this exact code, so the
|
|
47
|
+
* code is the contract between the two apps. A display name is content and can be edited; an id
|
|
48
|
+
* differs per database.
|
|
49
|
+
*/
|
|
50
|
+
export const MJC_TASK_CODE_CONTRACT_PROCESSING = 'CONTRACT_PROCESSING';
|
|
51
|
+
/** Statuses that mean the task is done with, in the Tasks app's own vocabulary. */
|
|
52
|
+
const CLOSED_TASK_STATUSES = ['Completed', 'Cancelled'];
|
|
53
|
+
/**
|
|
54
|
+
* Build the shared fragments, or `null` when they cannot be built.
|
|
55
|
+
*
|
|
56
|
+
* `null` is returned — rather than a filter that matches nothing — when bizapps-tasks is not
|
|
57
|
+
* installed, or when the CONTRACT_PROCESSING type row is missing. The distinction matters at the
|
|
58
|
+
* call site: a filter matching nothing renders a confident `0`, while `null` lets the caller render
|
|
59
|
+
* "—". "We cannot tell" and "there is no work" are different answers, and only one of them is safe
|
|
60
|
+
* to show a person whose job is working the queue.
|
|
61
|
+
*/
|
|
62
|
+
export async function BuildOpenTaskFilters(provider) {
|
|
63
|
+
const entities = (provider ?? Metadata.Provider)?.Entities ?? [];
|
|
64
|
+
const contracts = entities.find((e) => e.Name === MJC_ENTITIES.Contract);
|
|
65
|
+
const taskLinks = entities.find((e) => e.Name === MJC_FOREIGN_ENTITIES.TaskLink);
|
|
66
|
+
const tasks = entities.find((e) => e.Name === MJC_FOREIGN_ENTITIES.Task);
|
|
67
|
+
// Any of these missing means the Tasks app is not on this host. Not an error — contracts is
|
|
68
|
+
// installable without it, and the tile simply has nothing to say.
|
|
69
|
+
if (!contracts?.ID || !taskLinks?.BaseView || !tasks?.BaseView)
|
|
70
|
+
return null;
|
|
71
|
+
const typeID = await resolveContractProcessingTypeID(provider);
|
|
72
|
+
if (!typeID)
|
|
73
|
+
return null;
|
|
74
|
+
const linkView = qualified(taskLinks.SchemaName, taskLinks.BaseView);
|
|
75
|
+
const taskView = qualified(tasks.SchemaName, tasks.BaseView);
|
|
76
|
+
const contractView = qualified(contracts.SchemaName, contracts.BaseView);
|
|
77
|
+
const closed = CLOSED_TASK_STATUSES.map((s) => `'${s}'`).join(',');
|
|
78
|
+
/**
|
|
79
|
+
* `RecordID` is nvarchar — it holds a primary key of any shape, since a link can point at any
|
|
80
|
+
* entity — so the contract's uniqueidentifier is CAST rather than compared directly. Without the
|
|
81
|
+
* cast SQL Server applies its own conversion and the comparison becomes non-sargable at best.
|
|
82
|
+
*/
|
|
83
|
+
const exists = (extra) => `EXISTS (SELECT 1 FROM ${linkView} tl ` +
|
|
84
|
+
`INNER JOIN ${taskView} t ON t.ID = tl.TaskID ` +
|
|
85
|
+
`WHERE tl.RecordID = CAST(${contractView}.ID AS nvarchar(255)) ` +
|
|
86
|
+
`AND tl.EntityID = '${contracts.ID}' ` +
|
|
87
|
+
`AND t.TypeID = '${typeID}' ` +
|
|
88
|
+
`AND t.Status NOT IN (${closed})${extra})`;
|
|
89
|
+
return {
|
|
90
|
+
HasOpenTask: exists(''),
|
|
91
|
+
// Date, not datetime: "overdue" is a whole-day judgement, and comparing against the current
|
|
92
|
+
// instant would call a task due later today overdue all morning.
|
|
93
|
+
HasOverdueTask: exists(' AND t.DueAt < CAST(GETUTCDATE() AS date)'),
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Code → id, once.
|
|
98
|
+
*
|
|
99
|
+
* The Tasks base view exposes the type's NAME but not its CODE, so the id cannot be reached by
|
|
100
|
+
* filtering Tasks directly — it takes a read against Task Types. Comparing on the name instead would
|
|
101
|
+
* make an editable label load-bearing.
|
|
102
|
+
*/
|
|
103
|
+
async function resolveContractProcessingTypeID(provider) {
|
|
104
|
+
try {
|
|
105
|
+
const result = await ScopedRunView(provider).RunView({
|
|
106
|
+
EntityName: MJC_FOREIGN_ENTITIES.TaskType,
|
|
107
|
+
ExtraFilter: `Code = '${MJC_TASK_CODE_CONTRACT_PROCESSING}'`,
|
|
108
|
+
ResultType: 'simple',
|
|
109
|
+
Fields: ['ID'],
|
|
110
|
+
MaxRows: 1,
|
|
111
|
+
});
|
|
112
|
+
return result?.Success ? (result.Results?.[0]?.ID ?? null) : null;
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
// The entity exists but could not be read — same answer as "not installed" from here, and the
|
|
116
|
+
// caller renders "—" either way.
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
/** `[schema].[view]`, with the schema omitted when metadata does not carry one. */
|
|
121
|
+
function qualified(schema, view) {
|
|
122
|
+
return schema ? `[${schema}].[${view}]` : `[${view}]`;
|
|
123
|
+
}
|
|
124
|
+
//# sourceMappingURL=task-filters.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"task-filters.js","sourceRoot":"","sources":["../../../src/lib/data/task-filters.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,7 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Contract form body — Overview plus organized rail sections.
|
|
3
|
+
*
|
|
4
|
+
* Replaces leftover generated field dumps (Contract Details, Stakeholders, Provenance,
|
|
5
|
+
* Lifecycle, Notes) with:
|
|
6
|
+
* Overview (exec briefing) · Agreement · Parties · Dates · Renewal · Notes
|
|
7
|
+
* Modifications · Lineage · Re-papering · Provenance
|
|
8
|
+
*
|
|
9
|
+
* Dates / Renewal / Lineage / Re-papering already live in sibling files.
|
|
10
|
+
* Files on a contract are MJ's stock attachments (form toolbar), not a custom Documents section.
|
|
11
|
+
* `contributionKey` equals each panel's `SectionKey`. Overview is the only
|
|
12
|
+
* `inclusion: 'Primary'` so it leads; the rest sort into the related band by sortKey.
|
|
13
|
+
*
|
|
14
|
+
* @module @mj-biz-apps/contracts-ng
|
|
15
|
+
*/
|
|
16
|
+
import { type OnInit } from '@angular/core';
|
|
1
17
|
import { BaseFormPanel } from '@memberjunction/ng-base-forms';
|
|
2
18
|
import type { AfterDataLoadEventArgs } from '@memberjunction/ng-entity-viewer';
|
|
3
|
-
import { ContractEntity, type ContractState } from '@mj-biz-apps/contracts-entities';
|
|
19
|
+
import { ContractEntity, type ContractState, type DealOption } from '@mj-biz-apps/contracts-entities';
|
|
4
20
|
import * as i0 from "@angular/core";
|
|
21
|
+
/**
|
|
22
|
+
* A deal option with its rendered label attached (golive #219).
|
|
23
|
+
*
|
|
24
|
+
* The label is computed ONCE per row, on load, rather than from a template call: `mj-combobox` reads
|
|
25
|
+
* `TextField` on every change-detection pass and for every option, so a function there re-renders the
|
|
26
|
+
* whole list on each keystroke.
|
|
27
|
+
*/
|
|
28
|
+
interface DealChoice extends DealOption {
|
|
29
|
+
Label: string;
|
|
30
|
+
}
|
|
5
31
|
type ContractFieldType = 'textbox' | 'textarea' | 'number' | 'datepicker' | 'checkbox' | 'select' | 'autocomplete' | 'code' | 'dropdownlist' | 'numerictextbox';
|
|
6
32
|
interface ContractFieldSpec {
|
|
7
33
|
name: string;
|
|
@@ -47,8 +73,71 @@ export declare class MJCContractOverviewPanel extends BaseFormPanel<ContractEnti
|
|
|
47
73
|
static ɵfac: i0.ɵɵFactoryDeclaration<MJCContractOverviewPanel, never>;
|
|
48
74
|
static ɵcmp: i0.ɵɵComponentDeclaration<MJCContractOverviewPanel, "mjc-contract-overview-panel", never, {}, {}, never, never, true, never>;
|
|
49
75
|
}
|
|
50
|
-
export declare class MJCContractAgreementPanel extends BaseFormPanel<ContractEntity> {
|
|
76
|
+
export declare class MJCContractAgreementPanel extends BaseFormPanel<ContractEntity> implements OnInit {
|
|
77
|
+
private readonly cdr;
|
|
51
78
|
readonly Fields: ContractFieldSpec[];
|
|
79
|
+
/**
|
|
80
|
+
* The template ID this panel last put in the field — how it tells its own default from a choice.
|
|
81
|
+
*
|
|
82
|
+
* Session-local on purpose: it is never read from the record, because the record cannot say who
|
|
83
|
+
* wrote a value into it. See `MayWriteDefaultTemplate`, which owns what it means.
|
|
84
|
+
*/
|
|
85
|
+
private lastDefaultedID;
|
|
86
|
+
/**
|
|
87
|
+
* A contract that arrives with its type already set — Explorer's `NewRecordValues`, or a New
|
|
88
|
+
* clicked from a type-scoped list — gets its template here, before the form is ever drawn.
|
|
89
|
+
*
|
|
90
|
+
* The commoner path is a person choosing the type by hand, which never reaches `ngOnInit` and is
|
|
91
|
+
* why `OnFieldValueChange` exists too. Both call the same method; neither is sufficient alone.
|
|
92
|
+
*/
|
|
93
|
+
ngOnInit(): Promise<void>;
|
|
94
|
+
/**
|
|
95
|
+
* Re-decide the template whenever the CONTRACT TYPE changes (golive #218).
|
|
96
|
+
*
|
|
97
|
+
* `mj-form-field` emits this from its value setter, so it fires for a person's edit and NOT for
|
|
98
|
+
* the programmatic assignment below — writing `Record.ContractTemplateID` directly bypasses the
|
|
99
|
+
* setter. That asymmetry is what keeps this from re-entering itself.
|
|
100
|
+
*/
|
|
101
|
+
OnFieldValueChange(event: {
|
|
102
|
+
FieldName: string;
|
|
103
|
+
}): Promise<void>;
|
|
104
|
+
/**
|
|
105
|
+
* Put the current standard-terms version in the field, if we are allowed to.
|
|
106
|
+
*
|
|
107
|
+
* THE CHEAP HALF OF THE RULE RUNS FIRST. `RecordWantsDefaultTemplate` needs no database, and it
|
|
108
|
+
* is false for every saved contract — so opening an existing agreement costs no extra query at
|
|
109
|
+
* all, and the full rule is still evaluated in one place, on `ShouldDefaultTemplate`, before
|
|
110
|
+
* anything is written.
|
|
111
|
+
*/
|
|
112
|
+
private defaultTemplate;
|
|
113
|
+
/**
|
|
114
|
+
* Does the contract's current type say its terms live in a template — `TemplateRequired`?
|
|
115
|
+
*
|
|
116
|
+
* READ FROM THE FLAG, NEVER FROM THE TYPE'S NAME. The columns on `ContractType` exist precisely
|
|
117
|
+
* so no code branches on a name someone can rename; `ContractEntityServer` reads the same flag
|
|
118
|
+
* to decide whether to REQUIRE the template this defaults.
|
|
119
|
+
*
|
|
120
|
+
* THREE-WAY, AND THE THIRD CASE IS LOAD-BEARING. `false` now means "this type does not want a
|
|
121
|
+
* template", which WITHDRAWS one this panel defaulted — so a read that failed must not be folded
|
|
122
|
+
* into it. It was `boolean` while defaulting was the only outcome, where swallowing a failure as
|
|
123
|
+
* `false` merely declined to act; under the withdrawal it would quietly take away a default on
|
|
124
|
+
* the strength of a query that never answered.
|
|
125
|
+
*
|
|
126
|
+
* · `true` — the type requires a template
|
|
127
|
+
* · `false` — the type does not, or there is no type at all: both known answers
|
|
128
|
+
* · `null` — could not tell, and the caller does nothing in either direction
|
|
129
|
+
*/
|
|
130
|
+
private typeRequiresTemplate;
|
|
131
|
+
/**
|
|
132
|
+
* The newest Published, usable template — the one a new contract should start on.
|
|
133
|
+
*
|
|
134
|
+
* A FAILED OR EMPTY READ IS SILENT, and deliberately so. This is a convenience: the field stays
|
|
135
|
+
* empty, the picker still works, and the server still refuses a save with no template when the
|
|
136
|
+
* type demands one — which is the message the user needs and the one they would already get.
|
|
137
|
+
* Announcing "could not default a template" on a form nobody has filled in yet reports our own
|
|
138
|
+
* plumbing as the user's problem.
|
|
139
|
+
*/
|
|
140
|
+
private currentTemplateID;
|
|
52
141
|
static ɵfac: i0.ɵɵFactoryDeclaration<MJCContractAgreementPanel, never>;
|
|
53
142
|
static ɵcmp: i0.ɵɵComponentDeclaration<MJCContractAgreementPanel, "mjc-contract-agreement-panel", never, {}, {}, never, never, true, never>;
|
|
54
143
|
}
|
|
@@ -69,30 +158,138 @@ export declare class MJCContractNotesPanel extends BaseFormPanel<ContractEntity>
|
|
|
69
158
|
static ɵcmp: i0.ɵɵComponentDeclaration<MJCContractNotesPanel, "mjc-contract-notes-panel", never, {}, {}, never, never, true, never>;
|
|
70
159
|
}
|
|
71
160
|
export declare class MJCContractProvenanceFieldsPanel extends BaseFormPanel<ContractEntity> {
|
|
161
|
+
private readonly cdr;
|
|
72
162
|
/**
|
|
73
|
-
* READ-ONLY
|
|
163
|
+
* THE RAW IDS STAY, AND STAY READ-ONLY. The lookup above is what WRITES them (golive #219).
|
|
164
|
+
*
|
|
165
|
+
* ── WHY THEY WERE READ-ONLY, WHICH HAS NOT CHANGED ────────────────────────────────────────
|
|
74
166
|
*
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
* today would remove the only visible provenance for a stat that does not appear.
|
|
167
|
+
* `CreatingEntityID` is a real FK to `__mj.Entity` and `CreatingRecordID` is the row it names, under
|
|
168
|
+
* `CK_Contract_CreatingPairBothOrNeither` — both or neither. Rendered as two ordinary inputs
|
|
169
|
+
* (contracts#28 item 18) they produced exactly the two failures the shape invites: editing one
|
|
170
|
+
* alone is a save the constraint refuses, and editing both silently re-points a contract's
|
|
171
|
+
* provenance at an unrelated record. `CTR-000026` still carries the evidence — a hand-typed pair
|
|
172
|
+
* naming `MJ: Explorer Navigation Items` with a record id that is not a UUID.
|
|
82
173
|
*
|
|
83
|
-
* WHAT
|
|
84
|
-
* ordinary inputs a person could type into, which is how that junk pair got there.
|
|
85
|
-
* `CreatingEntityID` is a real FK to `__mj.Entity` and `CreatingRecordID` is the row it names, and
|
|
86
|
-
* `CK_Contract_CreatingPairBothOrNeither` requires both or neither — so editing one field alone
|
|
87
|
-
* produces a save the constraint refuses, and editing both silently re-points a contract's
|
|
88
|
-
* provenance at an unrelated record, which is then what the hero's link displays. The server sets
|
|
89
|
-
* this pair (`LiveContractsSeam.setProvenance`, on Close-Won) and nothing else should.
|
|
174
|
+
* ── WHAT #219 ADDS, AND WHY IT IS NOT A REVERSAL ──────────────────────────────────────────
|
|
90
175
|
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
176
|
+
* Story C-US1 says finance may attach a MANUALLY created contract to an existing deal. Item 18's
|
|
177
|
+
* read-only made that impossible: the Close Won seam became not just the preferred writer but the
|
|
178
|
+
* only one, and a contract typed in by hand could never show its deal. The answer is not to reopen
|
|
179
|
+
* the fields — it is to write them through a control that cannot produce a half pair. The lookup
|
|
180
|
+
* sets both halves from one chosen deal and clears both together; these two remain what they became
|
|
181
|
+
* in item 18, a read-only audit of what the pair actually holds, sitting under a human-readable name.
|
|
182
|
+
*
|
|
183
|
+
* The SECTION is likewise kept rather than hidden (item 18 asked for it gone, on the premise that
|
|
184
|
+
* the hero's Source Deal stat replaces it). With #219 that premise becomes true for deals — but the
|
|
185
|
+
* pair is polymorphic and the stat renders only what it can resolve, so the ids stay as the one
|
|
186
|
+
* place a reader can see the provenance verbatim, which is item 3 of #219.
|
|
94
187
|
*/
|
|
95
188
|
readonly Fields: ContractFieldSpec[];
|
|
189
|
+
/** Options for the picker. Never read directly — go through the `Deals` getter, which loads. */
|
|
190
|
+
private _deals;
|
|
191
|
+
/** Bound to the combobox. Mirrors `CreatingRecordID`, and is '' whenever the source is not a deal. */
|
|
192
|
+
PickedDealID: string;
|
|
193
|
+
Loading: boolean;
|
|
194
|
+
/** Why the list is empty, when the reason is a failure rather than genuinely nothing. */
|
|
195
|
+
LoadError: string;
|
|
196
|
+
/** The pair names a record of some OTHER entity — surfaced rather than silently shown as blank. */
|
|
197
|
+
ForeignSource: boolean;
|
|
198
|
+
/** This contract arrived with provenance already recorded, so re-pointing takes a second click. */
|
|
199
|
+
Locked: boolean;
|
|
200
|
+
/** Label of the deal currently linked, for the locked line. */
|
|
201
|
+
SelectedLabel: string;
|
|
202
|
+
/**
|
|
203
|
+
* Which contract the picker was loaded for — not merely whether it was.
|
|
204
|
+
*
|
|
205
|
+
* The form reuses one component instance as it navigates, so a boolean leaves the PREVIOUS
|
|
206
|
+
* contract's candidates in the dropdown: a list scoped to the previous customer, which is a wrong
|
|
207
|
+
* list rather than a stale one. Same defect, same fix, as the Supersedes picker (contracts#28
|
|
208
|
+
* item 23).
|
|
209
|
+
*/
|
|
210
|
+
private loadedFor;
|
|
211
|
+
private searchTimer;
|
|
212
|
+
/**
|
|
213
|
+
* Is there a Deals entity this user can see?
|
|
214
|
+
*
|
|
215
|
+
* Resolved from METADATA BY NAME, never imported: sales depends on contracts, so this app may not
|
|
216
|
+
* depend on sales (see `MJC_FOREIGN_ENTITIES.Deal`). A host without sales installed, or a user
|
|
217
|
+
* without read permission, simply gets no picker — the read-only ids and the hint below say so
|
|
218
|
+
* rather than rendering a control that can find nothing.
|
|
219
|
+
*/
|
|
220
|
+
get DealLookupAvailable(): boolean;
|
|
221
|
+
/**
|
|
222
|
+
* Whether to render the lookup — AND the panel's single load trigger.
|
|
223
|
+
*
|
|
224
|
+
* `BaseFormPanel` has no lifecycle hook and the slot host sets `Record` before view init, so the
|
|
225
|
+
* first template read is the earliest reliable moment to do per-record work. This is the FIRST
|
|
226
|
+
* thing the template reads, which is the whole reason the trigger lives here rather than on
|
|
227
|
+
* `Deals`: the options are read inside the unlocked branch only, so a contract that arrives with
|
|
228
|
+
* provenance would never read them, `syncFromRecord()` would never run, and `Locked` would never
|
|
229
|
+
* become true — the guard silently absent on exactly the records it exists to protect.
|
|
230
|
+
*
|
|
231
|
+
* Keyed on the record so navigating the form to another contract re-syncs and reloads.
|
|
232
|
+
*/
|
|
233
|
+
get ShowLookup(): boolean;
|
|
234
|
+
/** The candidate deals. Loaded by {@link ShowLookup}; this is a plain read. */
|
|
235
|
+
get Deals(): DealChoice[];
|
|
236
|
+
/**
|
|
237
|
+
* What the locked line says about the existing link.
|
|
238
|
+
*
|
|
239
|
+
* Deliberately does NOT claim the Close Won automation set it: nothing on the row records who
|
|
240
|
+
* did, and a contract linked by hand, saved and reopened arrives locked the same way. The panel
|
|
241
|
+
* says what it can actually see — the deal's name when the list has it, that the source is not a
|
|
242
|
+
* deal when the entity half says so, and otherwise nothing more than that something is linked.
|
|
243
|
+
* The raw ids sit directly below either way.
|
|
244
|
+
*/
|
|
245
|
+
get LockedSummary(): string;
|
|
246
|
+
/** Release the re-point guard. Deliberate, and only ever by a click. */
|
|
247
|
+
Unlock(): void;
|
|
248
|
+
/**
|
|
249
|
+
* Take a chosen deal — and write BOTH halves of the pair, always.
|
|
250
|
+
*
|
|
251
|
+
* This is the whole reason the control exists. There is no path here that sets one column: a
|
|
252
|
+
* chosen deal writes the Deals entity id together with the deal's own id, and an empty selection
|
|
253
|
+
* goes through {@link ClearSource}, which empties both. The database's both-or-neither constraint
|
|
254
|
+
* (and the validator CodeGen derives from it) can therefore never be the thing that reports a
|
|
255
|
+
* mistake this form allowed a person to make.
|
|
256
|
+
*/
|
|
257
|
+
PickDeal(dealID: string | null | undefined): void;
|
|
258
|
+
/** Empty BOTH halves. The constraint's other legal state, and #219's third acceptance criterion. */
|
|
259
|
+
ClearSource(): void;
|
|
260
|
+
/**
|
|
261
|
+
* Re-query as the user types, debounced.
|
|
262
|
+
*
|
|
263
|
+
* SERVER-SIDE, with the combobox's own `Filterable` off. Client filtering would need every deal in
|
|
264
|
+
* the database loaded to be correct, and it filters on the option LABEL — so a search that matched
|
|
265
|
+
* a customer whose name the label abbreviates would hide rows the server just returned. One
|
|
266
|
+
* authority for what matches, and it is the query.
|
|
267
|
+
*/
|
|
268
|
+
Search(text: string): void;
|
|
269
|
+
/** The Deals entity's id, or null when this installation has no such entity. */
|
|
270
|
+
private dealEntityID;
|
|
271
|
+
/**
|
|
272
|
+
* Read the record's pair into the control's own state.
|
|
273
|
+
*
|
|
274
|
+
* The pair is POLYMORPHIC, so "there is a `CreatingRecordID`" does not mean "a deal is selected".
|
|
275
|
+
* Pre-selecting on the record id alone would show an unrelated record's id as the chosen deal; the
|
|
276
|
+
* entity half has to agree first, and when it does not, `ForeignSource` says so out loud.
|
|
277
|
+
*
|
|
278
|
+
* `Locked` is captured HERE — from what the record ARRIVED with — and never recomputed live. A
|
|
279
|
+
* live "already set, so lock it" would re-lock the instant a user picked a deal, which is the
|
|
280
|
+
* guard eating the very edit it was there to confirm.
|
|
281
|
+
*/
|
|
282
|
+
private syncFromRecord;
|
|
283
|
+
/**
|
|
284
|
+
* Load the options.
|
|
285
|
+
*
|
|
286
|
+
* With nothing typed this is the contract's customer's deals — #219 item 2's "prefer deals for the
|
|
287
|
+
* contract's customer organization" — ORed with the deal already linked, so a contract pointed at
|
|
288
|
+
* a deal outside its customer still shows what it is pointed AT rather than a blank box. Typing
|
|
289
|
+
* widens the search to every deal by name, number or customer. The clauses themselves live in
|
|
290
|
+
* `@mj-biz-apps/contracts-entities`, where they can be unit-tested without Angular.
|
|
291
|
+
*/
|
|
292
|
+
private load;
|
|
96
293
|
static ɵfac: i0.ɵɵFactoryDeclaration<MJCContractProvenanceFieldsPanel, never>;
|
|
97
294
|
static ɵcmp: i0.ɵɵComponentDeclaration<MJCContractProvenanceFieldsPanel, "mjc-contract-provenance-panel", never, {}, {}, never, never, true, never>;
|
|
98
295
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contract-form.panels.d.ts","sourceRoot":"","sources":["../../../src/lib/form-panels/contract-form.panels.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"contract-form.panels.d.ts","sourceRoot":"","sources":["../../../src/lib/form-panels/contract-form.panels.ts"],"names":[],"mappings":"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"}
|