@mj-biz-apps/contracts-entities 0.1.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/LICENSE +183 -0
- package/dist/ContractEntity.d.ts +181 -0
- package/dist/ContractEntity.d.ts.map +1 -0
- package/dist/ContractEntity.js +261 -0
- package/dist/ContractEntity.js.map +1 -0
- package/dist/ContractTemplateEntity.d.ts +50 -0
- package/dist/ContractTemplateEntity.d.ts.map +1 -0
- package/dist/ContractTemplateEntity.js +85 -0
- package/dist/ContractTemplateEntity.js.map +1 -0
- package/dist/ContractTemplateModificationEntity.d.ts +46 -0
- package/dist/ContractTemplateModificationEntity.d.ts.map +1 -0
- package/dist/ContractTemplateModificationEntity.js +72 -0
- package/dist/ContractTemplateModificationEntity.js.map +1 -0
- package/dist/ContractTemplateProvisionEntity.d.ts +45 -0
- package/dist/ContractTemplateProvisionEntity.d.ts.map +1 -0
- package/dist/ContractTemplateProvisionEntity.js +73 -0
- package/dist/ContractTemplateProvisionEntity.js.map +1 -0
- package/dist/ContractTypeEntity.d.ts +55 -0
- package/dist/ContractTypeEntity.d.ts.map +1 -0
- package/dist/ContractTypeEntity.js +82 -0
- package/dist/ContractTypeEntity.js.map +1 -0
- package/dist/contract-state.d.ts +49 -0
- package/dist/contract-state.d.ts.map +1 -0
- package/dist/contract-state.js +48 -0
- package/dist/contract-state.js.map +1 -0
- package/dist/generated/entity_subclasses.d.ts +1235 -0
- package/dist/generated/entity_subclasses.d.ts.map +1 -0
- package/dist/generated/entity_subclasses.js +1817 -0
- package/dist/generated/entity_subclasses.js.map +1 -0
- package/dist/generated/remote_operations.d.ts +1145 -0
- package/dist/generated/remote_operations.d.ts.map +1 -0
- package/dist/generated/remote_operations.js +426 -0
- package/dist/generated/remote_operations.js.map +1 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +49 -0
- package/dist/index.js.map +1 -0
- package/dist/required-field-prose.d.ts +48 -0
- package/dist/required-field-prose.d.ts.map +1 -0
- package/dist/required-field-prose.js +55 -0
- package/dist/required-field-prose.js.map +1 -0
- package/dist/value-list-validation.d.ts +69 -0
- package/dist/value-list-validation.d.ts.map +1 -0
- package/dist/value-list-validation.js +88 -0
- package/dist/value-list-validation.js.map +1 -0
- package/package.json +33 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview `ContractTemplateEntity` — the publication lifecycle, and the one rule that makes it
|
|
3
|
+
* mean anything.
|
|
4
|
+
*
|
|
5
|
+
* A template version is either a **Draft** (freely editable, not referenceable) or **Published**
|
|
6
|
+
* (frozen, referenceable). The freeze itself is enforced by
|
|
7
|
+
* `trg_ContractTemplateProvision_Immutability` against INSERT, UPDATE and DELETE on its provisions —
|
|
8
|
+
* a trigger, because rewriting signed terms is the case where a bypass is silent corruption rather
|
|
9
|
+
* than a bad edit in our own UI.
|
|
10
|
+
*
|
|
11
|
+
* WHAT LIVES HERE IS THE TRANSITION RULE, which the trigger cannot see: **publishing is one-way.**
|
|
12
|
+
* Without it the freeze is decorative — anyone wanting to edit a published version would flip
|
|
13
|
+
* `Status` back to Draft, edit, and flip it forward again, and every guarantee above evaporates. To
|
|
14
|
+
* change published terms you publish a NEW VERSION, which is what `VersionLabel` and `IntroducedDate`
|
|
15
|
+
* exist for.
|
|
16
|
+
*
|
|
17
|
+
* WHY NOT A CHECK CONSTRAINT: a CHECK sees one row's final state, not the transition. "Was Published,
|
|
18
|
+
* is now Draft" is a comparison against `OldValue`, which only the entity layer has.
|
|
19
|
+
*
|
|
20
|
+
* WHY PROVISION-LEVEL DEACTIVATION WAS REJECTED (Marcelo, 2026-08-20), recorded so it is not
|
|
21
|
+
* re-proposed: soft-deleting provisions would build a second, finer-grained versioning system beside
|
|
22
|
+
* the one that already exists, and "what did this customer sign" would degrade from "template T" into
|
|
23
|
+
* "the provisions of T that were active on date X" — a temporal query, which is exactly what
|
|
24
|
+
* versioning avoids. The lifecycle belongs to the VERSION, not to the clause.
|
|
25
|
+
*
|
|
26
|
+
* @module @mj-biz-apps/contracts-entities
|
|
27
|
+
*/
|
|
28
|
+
import { ValidationResult } from '@memberjunction/core';
|
|
29
|
+
import { mjBizAppsContractsContractTemplateEntity } from './generated/entity_subclasses.js';
|
|
30
|
+
/** The published state — the string the `IN (...)` CHECK constrains `Status` to. */
|
|
31
|
+
export declare const TEMPLATE_PUBLISHED = "Published";
|
|
32
|
+
/** The editable state, and the default for a new version. */
|
|
33
|
+
export declare const TEMPLATE_DRAFT = "Draft";
|
|
34
|
+
export declare class ContractTemplateEntity extends mjBizAppsContractsContractTemplateEntity {
|
|
35
|
+
Validate(): ValidationResult;
|
|
36
|
+
/**
|
|
37
|
+
* Publishing is one-way: `Published` never returns to `Draft`.
|
|
38
|
+
*
|
|
39
|
+
* Compared against `OldValue` rather than probing anything, so it costs nothing and works on a
|
|
40
|
+
* record composed in the browser. Only fires on a SAVED record: a new version is legitimately
|
|
41
|
+
* created as a Draft, and there is no previous state to have been published.
|
|
42
|
+
*/
|
|
43
|
+
private refuseUnpublishing;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Anti-tree-shake anchor. Importing this module is what fires `@RegisterClass`; a dropped registration
|
|
47
|
+
* would silently make publishing reversible, with nothing failing.
|
|
48
|
+
*/
|
|
49
|
+
export declare function LoadContractTemplateEntity(): void;
|
|
50
|
+
//# sourceMappingURL=ContractTemplateEntity.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ContractTemplateEntity.d.ts","sourceRoot":"","sources":["../src/ContractTemplateEntity.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,OAAO,EAAwD,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAE9G,OAAO,EAAE,wCAAwC,EAAE,MAAM,+BAA+B,CAAC;AAGzF,oFAAoF;AACpF,eAAO,MAAM,kBAAkB,cAAc,CAAC;AAC9C,6DAA6D;AAC7D,eAAO,MAAM,cAAc,UAAU,CAAC;AAEtC,qBACa,sBAAuB,SAAQ,wCAAwC;IAChE,QAAQ,IAAI,gBAAgB;IAS5C;;;;;;OAMG;IACH,OAAO,CAAC,kBAAkB;CAoB7B;AAED;;;GAGG;AACH,wBAAgB,0BAA0B,IAAI,IAAI,CAEjD"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* @fileoverview `ContractTemplateEntity` — the publication lifecycle, and the one rule that makes it
|
|
9
|
+
* mean anything.
|
|
10
|
+
*
|
|
11
|
+
* A template version is either a **Draft** (freely editable, not referenceable) or **Published**
|
|
12
|
+
* (frozen, referenceable). The freeze itself is enforced by
|
|
13
|
+
* `trg_ContractTemplateProvision_Immutability` against INSERT, UPDATE and DELETE on its provisions —
|
|
14
|
+
* a trigger, because rewriting signed terms is the case where a bypass is silent corruption rather
|
|
15
|
+
* than a bad edit in our own UI.
|
|
16
|
+
*
|
|
17
|
+
* WHAT LIVES HERE IS THE TRANSITION RULE, which the trigger cannot see: **publishing is one-way.**
|
|
18
|
+
* Without it the freeze is decorative — anyone wanting to edit a published version would flip
|
|
19
|
+
* `Status` back to Draft, edit, and flip it forward again, and every guarantee above evaporates. To
|
|
20
|
+
* change published terms you publish a NEW VERSION, which is what `VersionLabel` and `IntroducedDate`
|
|
21
|
+
* exist for.
|
|
22
|
+
*
|
|
23
|
+
* WHY NOT A CHECK CONSTRAINT: a CHECK sees one row's final state, not the transition. "Was Published,
|
|
24
|
+
* is now Draft" is a comparison against `OldValue`, which only the entity layer has.
|
|
25
|
+
*
|
|
26
|
+
* WHY PROVISION-LEVEL DEACTIVATION WAS REJECTED (Marcelo, 2026-08-20), recorded so it is not
|
|
27
|
+
* re-proposed: soft-deleting provisions would build a second, finer-grained versioning system beside
|
|
28
|
+
* the one that already exists, and "what did this customer sign" would degrade from "template T" into
|
|
29
|
+
* "the provisions of T that were active on date X" — a temporal query, which is exactly what
|
|
30
|
+
* versioning avoids. The lifecycle belongs to the VERSION, not to the clause.
|
|
31
|
+
*
|
|
32
|
+
* @module @mj-biz-apps/contracts-entities
|
|
33
|
+
*/
|
|
34
|
+
import { BaseEntity, ValidationErrorInfo, ValidationErrorType } from '@memberjunction/core';
|
|
35
|
+
import { RegisterClass } from '@memberjunction/global';
|
|
36
|
+
import { mjBizAppsContractsContractTemplateEntity } from './generated/entity_subclasses.js';
|
|
37
|
+
import { ValidateValueLists } from './value-list-validation.js';
|
|
38
|
+
/** The published state — the string the `IN (...)` CHECK constrains `Status` to. */
|
|
39
|
+
export const TEMPLATE_PUBLISHED = 'Published';
|
|
40
|
+
/** The editable state, and the default for a new version. */
|
|
41
|
+
export const TEMPLATE_DRAFT = 'Draft';
|
|
42
|
+
let ContractTemplateEntity = class ContractTemplateEntity extends mjBizAppsContractsContractTemplateEntity {
|
|
43
|
+
Validate() {
|
|
44
|
+
const result = super.Validate();
|
|
45
|
+
// Status is an exhaustive value list with no generated validator — MJ never checks list
|
|
46
|
+
// membership (MJ#3969), so the generic stopgap covers it like the other lookup columns.
|
|
47
|
+
ValidateValueLists(this, result);
|
|
48
|
+
this.refuseUnpublishing(result);
|
|
49
|
+
return result;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Publishing is one-way: `Published` never returns to `Draft`.
|
|
53
|
+
*
|
|
54
|
+
* Compared against `OldValue` rather than probing anything, so it costs nothing and works on a
|
|
55
|
+
* record composed in the browser. Only fires on a SAVED record: a new version is legitimately
|
|
56
|
+
* created as a Draft, and there is no previous state to have been published.
|
|
57
|
+
*/
|
|
58
|
+
refuseUnpublishing(result) {
|
|
59
|
+
if (!this.IsSaved)
|
|
60
|
+
return;
|
|
61
|
+
const field = this.GetFieldByName('Status');
|
|
62
|
+
if (!field)
|
|
63
|
+
return;
|
|
64
|
+
const was = String(field.OldValue ?? '');
|
|
65
|
+
const now = String(field.Value ?? '');
|
|
66
|
+
if (was !== TEMPLATE_PUBLISHED || now === TEMPLATE_PUBLISHED)
|
|
67
|
+
return;
|
|
68
|
+
result.Success = false;
|
|
69
|
+
result.Errors.push(new ValidationErrorInfo('Status', `"${this.Name ?? this.ID}" is published and cannot be returned to draft. Contracts may already ` +
|
|
70
|
+
`incorporate its terms, and un-publishing would let those terms be edited underneath them. ` +
|
|
71
|
+
`Create a new version of this agreement instead — that is what versioning is for.`, now, ValidationErrorType.Failure));
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
ContractTemplateEntity = __decorate([
|
|
75
|
+
RegisterClass(BaseEntity, 'MJ_BizApps_Contracts: Contract Templates')
|
|
76
|
+
], ContractTemplateEntity);
|
|
77
|
+
export { ContractTemplateEntity };
|
|
78
|
+
/**
|
|
79
|
+
* Anti-tree-shake anchor. Importing this module is what fires `@RegisterClass`; a dropped registration
|
|
80
|
+
* would silently make publishing reversible, with nothing failing.
|
|
81
|
+
*/
|
|
82
|
+
export function LoadContractTemplateEntity() {
|
|
83
|
+
void ContractTemplateEntity;
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=ContractTemplateEntity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ContractTemplateEntity.js","sourceRoot":"","sources":["../src/ContractTemplateEntity.ts"],"names":[],"mappings":";;;;;;AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,OAAO,EAAE,UAAU,EAAE,mBAAmB,EAAE,mBAAmB,EAAoB,MAAM,sBAAsB,CAAC;AAC9G,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EAAE,wCAAwC,EAAE,MAAM,+BAA+B,CAAC;AACzF,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAE7D,oFAAoF;AACpF,MAAM,CAAC,MAAM,kBAAkB,GAAG,WAAW,CAAC;AAC9C,6DAA6D;AAC7D,MAAM,CAAC,MAAM,cAAc,GAAG,OAAO,CAAC;AAG/B,IAAM,sBAAsB,GAA5B,MAAM,sBAAuB,SAAQ,wCAAwC;IAChE,QAAQ;QACpB,MAAM,MAAM,GAAG,KAAK,CAAC,QAAQ,EAAE,CAAC;QAChC,wFAAwF;QACxF,wFAAwF;QACxF,kBAAkB,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QACjC,IAAI,CAAC,kBAAkB,CAAC,MAAM,CAAC,CAAC;QAChC,OAAO,MAAM,CAAC;IAClB,CAAC;IAED;;;;;;OAMG;IACK,kBAAkB,CAAC,MAAwB;QAC/C,IAAI,CAAC,IAAI,CAAC,OAAO;YAAE,OAAO;QAC1B,MAAM,KAAK,GAAG,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC;QAC5C,IAAI,CAAC,KAAK;YAAE,OAAO;QACnB,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC;QACzC,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC;QACtC,IAAI,GAAG,KAAK,kBAAkB,IAAI,GAAG,KAAK,kBAAkB;YAAE,OAAO;QAErE,MAAM,CAAC,OAAO,GAAG,KAAK,CAAC;QACvB,MAAM,CAAC,MAAM,CAAC,IAAI,CACd,IAAI,mBAAmB,CACnB,QAAQ,EACR,IAAI,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,EAAE,wEAAwE;YAC5F,4FAA4F;YAC5F,kFAAkF,EACtF,GAAG,EACH,mBAAmB,CAAC,OAAO,CAC9B,CACJ,CAAC;IACN,CAAC;CACJ,CAAA;AArCY,sBAAsB;IADlC,aAAa,CAAC,UAAU,EAAE,0CAA0C,CAAC;GACzD,sBAAsB,CAqClC;;AAED;;;GAGG;AACH,MAAM,UAAU,0BAA0B;IACtC,KAAK,sBAAsB,CAAC;AAChC,CAAC"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview `ContractTemplateModificationEntity` — the SHARED subclass, and it exists for exactly
|
|
3
|
+
* one reason: the three fields a modification cannot do without refuse in prose nobody can act on.
|
|
4
|
+
*
|
|
5
|
+
* IT ADDS NO RULE. That distinction is the whole design, and getting it wrong here is a known trap
|
|
6
|
+
* (R-2 of `plans/backend-requirements.md`, withdrawn because implementing it would have produced a
|
|
7
|
+
* second copy of four working generated rules). `ContractID`, `ContractTemplateProvisionID` and — as
|
|
8
|
+
* of V202608192340 — `ModificationText` are all NOT NULL in the database, so MJ's own
|
|
9
|
+
* `EntityField.Validate()` already refuses an empty one and already names the field. The rule is the
|
|
10
|
+
* metadata; restating it in TypeScript would give one fact two owners and let them drift the next
|
|
11
|
+
* time a column's nullability changes.
|
|
12
|
+
*
|
|
13
|
+
* What MJ cannot know is what the user should DO about it. Its message is
|
|
14
|
+
* `` `${DisplayNameOrName} cannot be null` `` (`baseEntity.ts:320`) — correct, field-named, and mute
|
|
15
|
+
* on the only question the person in front of the form is asking. "Contract Template Provision
|
|
16
|
+
* cannot be null" does not tell anyone to pick a clause. So this class REPLACES those three messages
|
|
17
|
+
* and adds none.
|
|
18
|
+
*
|
|
19
|
+
* The mechanism — replace rather than append, collapse the duplicate absence errors, match on
|
|
20
|
+
* emptiness rather than on MJ's wording — lives in `required-field-prose.ts`, shared with provisions.
|
|
21
|
+
*
|
|
22
|
+
* @module @mj-biz-apps/contracts-entities
|
|
23
|
+
*/
|
|
24
|
+
import { ValidationResult } from '@memberjunction/core';
|
|
25
|
+
import { mjBizAppsContractsContractTemplateModificationEntity } from './generated/entity_subclasses.js';
|
|
26
|
+
/**
|
|
27
|
+
* The prose each required field gets instead of "<Display Name> cannot be null".
|
|
28
|
+
*
|
|
29
|
+
* Each one says what to do, not what is wrong. `ContractID` is the odd member and is included
|
|
30
|
+
* deliberately: a user never sets it — MJ stamps it from the parent before validating
|
|
31
|
+
* (`relatedRecordCollection.ts`, `stampParentKey()` ahead of the child's `Validate()`), so inside the
|
|
32
|
+
* modification editor it cannot be empty. It can only be empty on the standalone form or an API
|
|
33
|
+
* call, which is precisely where "Contract cannot be null" is least useful, because the caller has to
|
|
34
|
+
* be told that a modification is meaningless without the contract it belongs to.
|
|
35
|
+
*/
|
|
36
|
+
export declare const MODIFICATION_REQUIRED_FIELD_PROSE: Readonly<Record<string, string>>;
|
|
37
|
+
export declare class ContractTemplateModificationEntity extends mjBizAppsContractsContractTemplateModificationEntity {
|
|
38
|
+
Validate(): ValidationResult;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Anti-tree-shake anchor. Importing this module is what fires `@RegisterClass`; a production build can
|
|
42
|
+
* drop an import whose class is never referenced, and a dropped registration would silently restore
|
|
43
|
+
* MJ's flat messages with nothing failing — the same reason `LoadContractEntity` exists.
|
|
44
|
+
*/
|
|
45
|
+
export declare function LoadContractTemplateModificationEntity(): void;
|
|
46
|
+
//# sourceMappingURL=ContractTemplateModificationEntity.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ContractTemplateModificationEntity.d.ts","sourceRoot":"","sources":["../src/ContractTemplateModificationEntity.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,OAAO,EAAc,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAEpE,OAAO,EAAE,oDAAoD,EAAE,MAAM,+BAA+B,CAAC;AAGrG;;;;;;;;;GASG;AACH,eAAO,MAAM,iCAAiC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAW9E,CAAC;AAEF,qBACa,kCAAmC,SAAQ,oDAAoD;IACxF,QAAQ,IAAI,gBAAgB;CAK/C;AAED;;;;GAIG;AACH,wBAAgB,sCAAsC,IAAI,IAAI,CAE7D"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* @fileoverview `ContractTemplateModificationEntity` — the SHARED subclass, and it exists for exactly
|
|
9
|
+
* one reason: the three fields a modification cannot do without refuse in prose nobody can act on.
|
|
10
|
+
*
|
|
11
|
+
* IT ADDS NO RULE. That distinction is the whole design, and getting it wrong here is a known trap
|
|
12
|
+
* (R-2 of `plans/backend-requirements.md`, withdrawn because implementing it would have produced a
|
|
13
|
+
* second copy of four working generated rules). `ContractID`, `ContractTemplateProvisionID` and — as
|
|
14
|
+
* of V202608192340 — `ModificationText` are all NOT NULL in the database, so MJ's own
|
|
15
|
+
* `EntityField.Validate()` already refuses an empty one and already names the field. The rule is the
|
|
16
|
+
* metadata; restating it in TypeScript would give one fact two owners and let them drift the next
|
|
17
|
+
* time a column's nullability changes.
|
|
18
|
+
*
|
|
19
|
+
* What MJ cannot know is what the user should DO about it. Its message is
|
|
20
|
+
* `` `${DisplayNameOrName} cannot be null` `` (`baseEntity.ts:320`) — correct, field-named, and mute
|
|
21
|
+
* on the only question the person in front of the form is asking. "Contract Template Provision
|
|
22
|
+
* cannot be null" does not tell anyone to pick a clause. So this class REPLACES those three messages
|
|
23
|
+
* and adds none.
|
|
24
|
+
*
|
|
25
|
+
* The mechanism — replace rather than append, collapse the duplicate absence errors, match on
|
|
26
|
+
* emptiness rather than on MJ's wording — lives in `required-field-prose.ts`, shared with provisions.
|
|
27
|
+
*
|
|
28
|
+
* @module @mj-biz-apps/contracts-entities
|
|
29
|
+
*/
|
|
30
|
+
import { BaseEntity } from '@memberjunction/core';
|
|
31
|
+
import { RegisterClass } from '@memberjunction/global';
|
|
32
|
+
import { mjBizAppsContractsContractTemplateModificationEntity } from './generated/entity_subclasses.js';
|
|
33
|
+
import { ExplainMissingRequiredFields } from './required-field-prose.js';
|
|
34
|
+
/**
|
|
35
|
+
* The prose each required field gets instead of "<Display Name> cannot be null".
|
|
36
|
+
*
|
|
37
|
+
* Each one says what to do, not what is wrong. `ContractID` is the odd member and is included
|
|
38
|
+
* deliberately: a user never sets it — MJ stamps it from the parent before validating
|
|
39
|
+
* (`relatedRecordCollection.ts`, `stampParentKey()` ahead of the child's `Validate()`), so inside the
|
|
40
|
+
* modification editor it cannot be empty. It can only be empty on the standalone form or an API
|
|
41
|
+
* call, which is precisely where "Contract cannot be null" is least useful, because the caller has to
|
|
42
|
+
* be told that a modification is meaningless without the contract it belongs to.
|
|
43
|
+
*/
|
|
44
|
+
export const MODIFICATION_REQUIRED_FIELD_PROSE = {
|
|
45
|
+
ContractID: 'A modification records what one specific contract negotiated, so it cannot exist on its own — ' +
|
|
46
|
+
'choose the contract this deviation belongs to.',
|
|
47
|
+
ContractTemplateProvisionID: 'Choose the standard provision this modification changes. The negotiated wording is only ' +
|
|
48
|
+
'meaningful beside the clause it replaces.',
|
|
49
|
+
ModificationText: 'Record what this contract says instead of the standard clause. A modification that does not ' +
|
|
50
|
+
'say what was agreed asserts that the paper differs without saying how, which is the one ' +
|
|
51
|
+
'state this record cannot usefully be in.',
|
|
52
|
+
};
|
|
53
|
+
let ContractTemplateModificationEntity = class ContractTemplateModificationEntity extends mjBizAppsContractsContractTemplateModificationEntity {
|
|
54
|
+
Validate() {
|
|
55
|
+
const result = super.Validate();
|
|
56
|
+
ExplainMissingRequiredFields(this, result, MODIFICATION_REQUIRED_FIELD_PROSE);
|
|
57
|
+
return result;
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
ContractTemplateModificationEntity = __decorate([
|
|
61
|
+
RegisterClass(BaseEntity, 'MJ_BizApps_Contracts: Contract Template Modifications')
|
|
62
|
+
], ContractTemplateModificationEntity);
|
|
63
|
+
export { ContractTemplateModificationEntity };
|
|
64
|
+
/**
|
|
65
|
+
* Anti-tree-shake anchor. Importing this module is what fires `@RegisterClass`; a production build can
|
|
66
|
+
* drop an import whose class is never referenced, and a dropped registration would silently restore
|
|
67
|
+
* MJ's flat messages with nothing failing — the same reason `LoadContractEntity` exists.
|
|
68
|
+
*/
|
|
69
|
+
export function LoadContractTemplateModificationEntity() {
|
|
70
|
+
void ContractTemplateModificationEntity;
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=ContractTemplateModificationEntity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ContractTemplateModificationEntity.js","sourceRoot":"","sources":["../src/ContractTemplateModificationEntity.ts"],"names":[],"mappings":";;;;;;AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,OAAO,EAAE,UAAU,EAAoB,MAAM,sBAAsB,CAAC;AACpE,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EAAE,oDAAoD,EAAE,MAAM,+BAA+B,CAAC;AACrG,OAAO,EAAE,4BAA4B,EAAE,MAAM,wBAAwB,CAAC;AAEtE;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,iCAAiC,GAAqC;IAC/E,UAAU,EACN,gGAAgG;QAChG,gDAAgD;IACpD,2BAA2B,EACvB,0FAA0F;QAC1F,2CAA2C;IAC/C,gBAAgB,EACZ,8FAA8F;QAC9F,0FAA0F;QAC1F,0CAA0C;CACjD,CAAC;AAGK,IAAM,kCAAkC,GAAxC,MAAM,kCAAmC,SAAQ,oDAAoD;IACxF,QAAQ;QACpB,MAAM,MAAM,GAAG,KAAK,CAAC,QAAQ,EAAE,CAAC;QAChC,4BAA4B,CAAC,IAAI,EAAE,MAAM,EAAE,iCAAiC,CAAC,CAAC;QAC9E,OAAO,MAAM,CAAC;IAClB,CAAC;CACJ,CAAA;AANY,kCAAkC;IAD9C,aAAa,CAAC,UAAU,EAAE,uDAAuD,CAAC;GACtE,kCAAkC,CAM9C;;AAED;;;;GAIG;AACH,MAAM,UAAU,sCAAsC;IAClD,KAAK,kCAAkC,CAAC;AAC5C,CAAC"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview `ContractTemplateProvisionEntity` — the SHARED subclass, and like the modification's
|
|
3
|
+
* it exists for one reason: the fields a provision cannot do without refuse in prose nobody can act on.
|
|
4
|
+
*
|
|
5
|
+
* IT ADDS NO RULE. `ContractTemplateID`, `ProvisionNumber`, `Title` and — as of V202608200800 —
|
|
6
|
+
* `ProvisionText` are all required at the database, the last two of them via
|
|
7
|
+
* `CK_ContractTemplateProvision_TextNotBlank` on top of `NOT NULL` (because `NOT NULL` accepts `''`;
|
|
8
|
+
* see the migration). MJ and CodeGen already refuse an empty one and already name the field. Only the
|
|
9
|
+
* sentence changes here.
|
|
10
|
+
*
|
|
11
|
+
* WHY `ProvisionText` MATTERS MORE THAN IT LOOKS. A provision is the STANDARD wording of a clause, and
|
|
12
|
+
* a `ContractTemplateModification` is only meaningful read as a PAIR against it — a dispute needs the
|
|
13
|
+
* comparison, not either half. A provision with no text is therefore worse than a modification with
|
|
14
|
+
* no text: every modification pointing at it inherits the gap, and the contract still renders.
|
|
15
|
+
*
|
|
16
|
+
* SERVER RULES ARE NOT HERE. Provision immutability once a contract references the template (R-1) and
|
|
17
|
+
* the delete guards (R-8) need cross-entity reads, so they live on
|
|
18
|
+
* `ContractTemplateProvisionEntityServer`. What is here is what a browser can preflight.
|
|
19
|
+
*
|
|
20
|
+
* @module @mj-biz-apps/contracts-entities
|
|
21
|
+
*/
|
|
22
|
+
import { ValidationResult } from '@memberjunction/core';
|
|
23
|
+
import { mjBizAppsContractsContractTemplateProvisionEntity } from './generated/entity_subclasses.js';
|
|
24
|
+
/**
|
|
25
|
+
* The prose each required field gets instead of "<Display Name> cannot be null".
|
|
26
|
+
*
|
|
27
|
+
* `ContractTemplateID` is included for the same reason `ContractID` is on the modification: a user
|
|
28
|
+
* never sets it inside the template form — MJ stamps it from the parent before validating — so it can
|
|
29
|
+
* only be empty on the standalone form or an API call, which is exactly where the flat message helps
|
|
30
|
+
* least.
|
|
31
|
+
*
|
|
32
|
+
* `ProvisionSortKey` is deliberately ABSENT. It is a persisted computed column and read-only, so it
|
|
33
|
+
* cannot be missing and cannot be set; naming it here would describe a failure that cannot happen.
|
|
34
|
+
*/
|
|
35
|
+
export declare const PROVISION_REQUIRED_FIELD_PROSE: Readonly<Record<string, string>>;
|
|
36
|
+
export declare class ContractTemplateProvisionEntity extends mjBizAppsContractsContractTemplateProvisionEntity {
|
|
37
|
+
Validate(): ValidationResult;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Anti-tree-shake anchor. Importing this module is what fires `@RegisterClass`; a production build can
|
|
41
|
+
* drop an import whose class is never referenced, and a dropped registration would silently restore
|
|
42
|
+
* MJ's flat messages with nothing failing.
|
|
43
|
+
*/
|
|
44
|
+
export declare function LoadContractTemplateProvisionEntity(): void;
|
|
45
|
+
//# sourceMappingURL=ContractTemplateProvisionEntity.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ContractTemplateProvisionEntity.d.ts","sourceRoot":"","sources":["../src/ContractTemplateProvisionEntity.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,OAAO,EAAc,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAEpE,OAAO,EAAE,iDAAiD,EAAE,MAAM,+BAA+B,CAAC;AAGlG;;;;;;;;;;GAUG;AACH,eAAO,MAAM,8BAA8B,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAc3E,CAAC;AAEF,qBACa,+BAAgC,SAAQ,iDAAiD;IAClF,QAAQ,IAAI,gBAAgB;CAK/C;AAED;;;;GAIG;AACH,wBAAgB,mCAAmC,IAAI,IAAI,CAE1D"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* @fileoverview `ContractTemplateProvisionEntity` — the SHARED subclass, and like the modification's
|
|
9
|
+
* it exists for one reason: the fields a provision cannot do without refuse in prose nobody can act on.
|
|
10
|
+
*
|
|
11
|
+
* IT ADDS NO RULE. `ContractTemplateID`, `ProvisionNumber`, `Title` and — as of V202608200800 —
|
|
12
|
+
* `ProvisionText` are all required at the database, the last two of them via
|
|
13
|
+
* `CK_ContractTemplateProvision_TextNotBlank` on top of `NOT NULL` (because `NOT NULL` accepts `''`;
|
|
14
|
+
* see the migration). MJ and CodeGen already refuse an empty one and already name the field. Only the
|
|
15
|
+
* sentence changes here.
|
|
16
|
+
*
|
|
17
|
+
* WHY `ProvisionText` MATTERS MORE THAN IT LOOKS. A provision is the STANDARD wording of a clause, and
|
|
18
|
+
* a `ContractTemplateModification` is only meaningful read as a PAIR against it — a dispute needs the
|
|
19
|
+
* comparison, not either half. A provision with no text is therefore worse than a modification with
|
|
20
|
+
* no text: every modification pointing at it inherits the gap, and the contract still renders.
|
|
21
|
+
*
|
|
22
|
+
* SERVER RULES ARE NOT HERE. Provision immutability once a contract references the template (R-1) and
|
|
23
|
+
* the delete guards (R-8) need cross-entity reads, so they live on
|
|
24
|
+
* `ContractTemplateProvisionEntityServer`. What is here is what a browser can preflight.
|
|
25
|
+
*
|
|
26
|
+
* @module @mj-biz-apps/contracts-entities
|
|
27
|
+
*/
|
|
28
|
+
import { BaseEntity } from '@memberjunction/core';
|
|
29
|
+
import { RegisterClass } from '@memberjunction/global';
|
|
30
|
+
import { mjBizAppsContractsContractTemplateProvisionEntity } from './generated/entity_subclasses.js';
|
|
31
|
+
import { ExplainMissingRequiredFields } from './required-field-prose.js';
|
|
32
|
+
/**
|
|
33
|
+
* The prose each required field gets instead of "<Display Name> cannot be null".
|
|
34
|
+
*
|
|
35
|
+
* `ContractTemplateID` is included for the same reason `ContractID` is on the modification: a user
|
|
36
|
+
* never sets it inside the template form — MJ stamps it from the parent before validating — so it can
|
|
37
|
+
* only be empty on the standalone form or an API call, which is exactly where the flat message helps
|
|
38
|
+
* least.
|
|
39
|
+
*
|
|
40
|
+
* `ProvisionSortKey` is deliberately ABSENT. It is a persisted computed column and read-only, so it
|
|
41
|
+
* cannot be missing and cannot be set; naming it here would describe a failure that cannot happen.
|
|
42
|
+
*/
|
|
43
|
+
export const PROVISION_REQUIRED_FIELD_PROSE = {
|
|
44
|
+
ContractTemplateID: 'A provision is a clause OF an agreement version, so it cannot stand alone — choose the ' +
|
|
45
|
+
'template this clause belongs to.',
|
|
46
|
+
ProvisionNumber: 'Give the clause its number as the document states it (for example 1.1, or 2.1A). The number is ' +
|
|
47
|
+
'also what orders the provisions, so a clause without one sorts to the top until it has one.',
|
|
48
|
+
Title: 'Give the clause a heading, as the document states it. It is what the contract screens show ' +
|
|
49
|
+
'when the full text is collapsed.',
|
|
50
|
+
ProvisionText: 'Record the standard wording of this clause. A provision with no text leaves every modification ' +
|
|
51
|
+
'that negotiates it comparing against nothing, and the pair — standard beside negotiated — is ' +
|
|
52
|
+
'what a dispute actually needs.',
|
|
53
|
+
};
|
|
54
|
+
let ContractTemplateProvisionEntity = class ContractTemplateProvisionEntity extends mjBizAppsContractsContractTemplateProvisionEntity {
|
|
55
|
+
Validate() {
|
|
56
|
+
const result = super.Validate();
|
|
57
|
+
ExplainMissingRequiredFields(this, result, PROVISION_REQUIRED_FIELD_PROSE);
|
|
58
|
+
return result;
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
ContractTemplateProvisionEntity = __decorate([
|
|
62
|
+
RegisterClass(BaseEntity, 'MJ_BizApps_Contracts: Contract Template Provisions')
|
|
63
|
+
], ContractTemplateProvisionEntity);
|
|
64
|
+
export { ContractTemplateProvisionEntity };
|
|
65
|
+
/**
|
|
66
|
+
* Anti-tree-shake anchor. Importing this module is what fires `@RegisterClass`; a production build can
|
|
67
|
+
* drop an import whose class is never referenced, and a dropped registration would silently restore
|
|
68
|
+
* MJ's flat messages with nothing failing.
|
|
69
|
+
*/
|
|
70
|
+
export function LoadContractTemplateProvisionEntity() {
|
|
71
|
+
void ContractTemplateProvisionEntity;
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=ContractTemplateProvisionEntity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ContractTemplateProvisionEntity.js","sourceRoot":"","sources":["../src/ContractTemplateProvisionEntity.ts"],"names":[],"mappings":";;;;;;AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,OAAO,EAAE,UAAU,EAAoB,MAAM,sBAAsB,CAAC;AACpE,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EAAE,iDAAiD,EAAE,MAAM,+BAA+B,CAAC;AAClG,OAAO,EAAE,4BAA4B,EAAE,MAAM,wBAAwB,CAAC;AAEtE;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,8BAA8B,GAAqC;IAC5E,kBAAkB,EACd,yFAAyF;QACzF,kCAAkC;IACtC,eAAe,EACX,iGAAiG;QACjG,6FAA6F;IACjG,KAAK,EACD,6FAA6F;QAC7F,kCAAkC;IACtC,aAAa,EACT,iGAAiG;QACjG,+FAA+F;QAC/F,gCAAgC;CACvC,CAAC;AAGK,IAAM,+BAA+B,GAArC,MAAM,+BAAgC,SAAQ,iDAAiD;IAClF,QAAQ;QACpB,MAAM,MAAM,GAAG,KAAK,CAAC,QAAQ,EAAE,CAAC;QAChC,4BAA4B,CAAC,IAAI,EAAE,MAAM,EAAE,8BAA8B,CAAC,CAAC;QAC3E,OAAO,MAAM,CAAC;IAClB,CAAC;CACJ,CAAA;AANY,+BAA+B;IAD3C,aAAa,CAAC,UAAU,EAAE,oDAAoD,CAAC;GACnE,+BAA+B,CAM3C;;AAED;;;;GAIG;AACH,MAAM,UAAU,mCAAmC;IAC/C,KAAK,+BAA+B,CAAC;AACzC,CAAC"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview `ContractTypeEntity` and `ContractTemplateTypeEntity` — the two lookup tables, which
|
|
3
|
+
* exist as subclasses for exactly one reason: their value-list columns are validated NOWHERE.
|
|
4
|
+
*
|
|
5
|
+
* Both carry an `nvarchar` column constrained by `CHECK (… IN (…))` — `ContractType.Status` and
|
|
6
|
+
* `ContractTemplateType.Status`. (A third, `ContractType.ParentStatusRequirement`, was the original
|
|
7
|
+
* motivating case; R-4 replaced it with two booleans, which need no value list at all — a `bit` has
|
|
8
|
+
* only two possible values. The guard stays generic, so losing a field does not change it.) CodeGen
|
|
9
|
+
* renders that kind of
|
|
10
|
+
* constraint as value-list METADATA rather than as a generated `Validate()` method, and `BaseEntity`
|
|
11
|
+
* never reads value-list metadata, so the constraint has no TypeScript representation at all. Full
|
|
12
|
+
* reasoning, and the deletion plan, in `value-list-validation.ts`; filed as MJ#3969.
|
|
13
|
+
*
|
|
14
|
+
* These are the app's only entities with value-list fields today. `ValidateValueLists` is generic
|
|
15
|
+
* rather than field-specific, so a future one is covered by adding the call, not by writing a check.
|
|
16
|
+
*
|
|
17
|
+
* WHY THESE TWO AND NOT ALL FIVE UNSUBCLASSED ENTITIES. `ContractTemplate` and
|
|
18
|
+
* `ContractTemplateProvision` also lack subclasses, and both will need one — for provision
|
|
19
|
+
* immutability (R-1) and the `ProvisionNumber` sort key (R-11). Neither rule is decided yet, and a
|
|
20
|
+
* subclass created ahead of a rule is a file that says nothing while looking like it says something.
|
|
21
|
+
* They arrive with their rules. `ContractSequence` is a separate question (R-7): whether it should be
|
|
22
|
+
* an API-writable entity at all.
|
|
23
|
+
*
|
|
24
|
+
* @module @mj-biz-apps/contracts-entities
|
|
25
|
+
*/
|
|
26
|
+
import { ValidationResult } from '@memberjunction/core';
|
|
27
|
+
import { mjBizAppsContractsContractTemplateTypeEntity, mjBizAppsContractsContractTypeEntity } from './generated/entity_subclasses.js';
|
|
28
|
+
/**
|
|
29
|
+
* `Status` (`Active` / `Inactive`) — the only value-list column left on this table.
|
|
30
|
+
*
|
|
31
|
+
* It is load-bearing rather than cosmetic: R-5 refuses a contract that NEWLY selects a type whose
|
|
32
|
+
* `Status` is `Inactive`, and R-8 refuses deleting a type in use while telling the user to retire it
|
|
33
|
+
* instead. So "Active" / "Inactive" is the vocabulary two rules read, and the database's `CHECK` is
|
|
34
|
+
* what keeps a third string out of the column — but it refuses at the very end, as a constraint
|
|
35
|
+
* violation naming no field. This makes the refusal arrive on the field, in the browser, before the
|
|
36
|
+
* round trip.
|
|
37
|
+
*
|
|
38
|
+
* The type's three RULE columns (`MustBeRoot`, `MustBeChild`, `TemplateRequired`) are `bit`s and need
|
|
39
|
+
* nothing here — a boolean cannot hold an out-of-list value, which is half the argument R-4 made for
|
|
40
|
+
* replacing the three-state string.
|
|
41
|
+
*/
|
|
42
|
+
export declare class ContractTypeEntity extends mjBizAppsContractsContractTypeEntity {
|
|
43
|
+
Validate(): ValidationResult;
|
|
44
|
+
}
|
|
45
|
+
/** `Status` (`Active` / `Inactive`) — see the note above. */
|
|
46
|
+
export declare class ContractTemplateTypeEntity extends mjBizAppsContractsContractTemplateTypeEntity {
|
|
47
|
+
Validate(): ValidationResult;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Anti-tree-shake anchor. Importing this module is what fires `@RegisterClass`; a production build can
|
|
51
|
+
* drop an import whose class is never referenced, and a dropped registration means the rules above
|
|
52
|
+
* silently stop existing — the same reason `LoadContractEntity` exists.
|
|
53
|
+
*/
|
|
54
|
+
export declare function LoadContractTypeEntities(): void;
|
|
55
|
+
//# sourceMappingURL=ContractTypeEntity.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ContractTypeEntity.d.ts","sourceRoot":"","sources":["../src/ContractTypeEntity.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,OAAO,EAAc,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAEpE,OAAO,EACH,4CAA4C,EAC5C,oCAAoC,EACvC,MAAM,+BAA+B,CAAC;AAGvC;;;;;;;;;;;;;GAaG;AACH,qBACa,kBAAmB,SAAQ,oCAAoC;IACxD,QAAQ,IAAI,gBAAgB;CAK/C;AAED,6DAA6D;AAC7D,qBACa,0BAA2B,SAAQ,4CAA4C;IACxE,QAAQ,IAAI,gBAAgB;CAK/C;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,IAAI,IAAI,CAG/C"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* @fileoverview `ContractTypeEntity` and `ContractTemplateTypeEntity` — the two lookup tables, which
|
|
9
|
+
* exist as subclasses for exactly one reason: their value-list columns are validated NOWHERE.
|
|
10
|
+
*
|
|
11
|
+
* Both carry an `nvarchar` column constrained by `CHECK (… IN (…))` — `ContractType.Status` and
|
|
12
|
+
* `ContractTemplateType.Status`. (A third, `ContractType.ParentStatusRequirement`, was the original
|
|
13
|
+
* motivating case; R-4 replaced it with two booleans, which need no value list at all — a `bit` has
|
|
14
|
+
* only two possible values. The guard stays generic, so losing a field does not change it.) CodeGen
|
|
15
|
+
* renders that kind of
|
|
16
|
+
* constraint as value-list METADATA rather than as a generated `Validate()` method, and `BaseEntity`
|
|
17
|
+
* never reads value-list metadata, so the constraint has no TypeScript representation at all. Full
|
|
18
|
+
* reasoning, and the deletion plan, in `value-list-validation.ts`; filed as MJ#3969.
|
|
19
|
+
*
|
|
20
|
+
* These are the app's only entities with value-list fields today. `ValidateValueLists` is generic
|
|
21
|
+
* rather than field-specific, so a future one is covered by adding the call, not by writing a check.
|
|
22
|
+
*
|
|
23
|
+
* WHY THESE TWO AND NOT ALL FIVE UNSUBCLASSED ENTITIES. `ContractTemplate` and
|
|
24
|
+
* `ContractTemplateProvision` also lack subclasses, and both will need one — for provision
|
|
25
|
+
* immutability (R-1) and the `ProvisionNumber` sort key (R-11). Neither rule is decided yet, and a
|
|
26
|
+
* subclass created ahead of a rule is a file that says nothing while looking like it says something.
|
|
27
|
+
* They arrive with their rules. `ContractSequence` is a separate question (R-7): whether it should be
|
|
28
|
+
* an API-writable entity at all.
|
|
29
|
+
*
|
|
30
|
+
* @module @mj-biz-apps/contracts-entities
|
|
31
|
+
*/
|
|
32
|
+
import { BaseEntity } from '@memberjunction/core';
|
|
33
|
+
import { RegisterClass } from '@memberjunction/global';
|
|
34
|
+
import { mjBizAppsContractsContractTemplateTypeEntity, mjBizAppsContractsContractTypeEntity, } from './generated/entity_subclasses.js';
|
|
35
|
+
import { ValidateValueLists } from './value-list-validation.js';
|
|
36
|
+
/**
|
|
37
|
+
* `Status` (`Active` / `Inactive`) — the only value-list column left on this table.
|
|
38
|
+
*
|
|
39
|
+
* It is load-bearing rather than cosmetic: R-5 refuses a contract that NEWLY selects a type whose
|
|
40
|
+
* `Status` is `Inactive`, and R-8 refuses deleting a type in use while telling the user to retire it
|
|
41
|
+
* instead. So "Active" / "Inactive" is the vocabulary two rules read, and the database's `CHECK` is
|
|
42
|
+
* what keeps a third string out of the column — but it refuses at the very end, as a constraint
|
|
43
|
+
* violation naming no field. This makes the refusal arrive on the field, in the browser, before the
|
|
44
|
+
* round trip.
|
|
45
|
+
*
|
|
46
|
+
* The type's three RULE columns (`MustBeRoot`, `MustBeChild`, `TemplateRequired`) are `bit`s and need
|
|
47
|
+
* nothing here — a boolean cannot hold an out-of-list value, which is half the argument R-4 made for
|
|
48
|
+
* replacing the three-state string.
|
|
49
|
+
*/
|
|
50
|
+
let ContractTypeEntity = class ContractTypeEntity extends mjBizAppsContractsContractTypeEntity {
|
|
51
|
+
Validate() {
|
|
52
|
+
const result = super.Validate();
|
|
53
|
+
ValidateValueLists(this, result);
|
|
54
|
+
return result;
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
ContractTypeEntity = __decorate([
|
|
58
|
+
RegisterClass(BaseEntity, 'MJ_BizApps_Contracts: Contract Types')
|
|
59
|
+
], ContractTypeEntity);
|
|
60
|
+
export { ContractTypeEntity };
|
|
61
|
+
/** `Status` (`Active` / `Inactive`) — see the note above. */
|
|
62
|
+
let ContractTemplateTypeEntity = class ContractTemplateTypeEntity extends mjBizAppsContractsContractTemplateTypeEntity {
|
|
63
|
+
Validate() {
|
|
64
|
+
const result = super.Validate();
|
|
65
|
+
ValidateValueLists(this, result);
|
|
66
|
+
return result;
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
ContractTemplateTypeEntity = __decorate([
|
|
70
|
+
RegisterClass(BaseEntity, 'MJ_BizApps_Contracts: Contract Template Types')
|
|
71
|
+
], ContractTemplateTypeEntity);
|
|
72
|
+
export { ContractTemplateTypeEntity };
|
|
73
|
+
/**
|
|
74
|
+
* Anti-tree-shake anchor. Importing this module is what fires `@RegisterClass`; a production build can
|
|
75
|
+
* drop an import whose class is never referenced, and a dropped registration means the rules above
|
|
76
|
+
* silently stop existing — the same reason `LoadContractEntity` exists.
|
|
77
|
+
*/
|
|
78
|
+
export function LoadContractTypeEntities() {
|
|
79
|
+
void ContractTypeEntity;
|
|
80
|
+
void ContractTemplateTypeEntity;
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=ContractTypeEntity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ContractTypeEntity.js","sourceRoot":"","sources":["../src/ContractTypeEntity.ts"],"names":[],"mappings":";;;;;;AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,OAAO,EAAE,UAAU,EAAoB,MAAM,sBAAsB,CAAC;AACpE,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EACH,4CAA4C,EAC5C,oCAAoC,GACvC,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAE7D;;;;;;;;;;;;;GAaG;AAEI,IAAM,kBAAkB,GAAxB,MAAM,kBAAmB,SAAQ,oCAAoC;IACxD,QAAQ;QACpB,MAAM,MAAM,GAAG,KAAK,CAAC,QAAQ,EAAE,CAAC;QAChC,kBAAkB,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QACjC,OAAO,MAAM,CAAC;IAClB,CAAC;CACJ,CAAA;AANY,kBAAkB;IAD9B,aAAa,CAAC,UAAU,EAAE,sCAAsC,CAAC;GACrD,kBAAkB,CAM9B;;AAED,6DAA6D;AAEtD,IAAM,0BAA0B,GAAhC,MAAM,0BAA2B,SAAQ,4CAA4C;IACxE,QAAQ;QACpB,MAAM,MAAM,GAAG,KAAK,CAAC,QAAQ,EAAE,CAAC;QAChC,kBAAkB,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QACjC,OAAO,MAAM,CAAC;IAClB,CAAC;CACJ,CAAA;AANY,0BAA0B;IADtC,aAAa,CAAC,UAAU,EAAE,+CAA+C,CAAC;GAC9D,0BAA0B,CAMtC;;AAED;;;;GAIG;AACH,MAAM,UAAU,wBAAwB;IACpC,KAAK,kBAAkB,CAAC;IACxB,KAAK,0BAA0B,CAAC;AACpC,CAAC"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview The contract lifecycle — the VALUES only. The rule itself lives in SQL.
|
|
3
|
+
*
|
|
4
|
+
* `State` is derived, not stored (D-19 / ERD R-18): four of the old `Status` column's five values
|
|
5
|
+
* were projections of the dates and the two self-FKs, and a stored copy of a projection can only
|
|
6
|
+
* agree or lie. It is derived in exactly ONE place — the `CASE` in the app-owned layered base view
|
|
7
|
+
* (`V202608182001`) — and application code READS `contract.State`. It does not re-derive it.
|
|
8
|
+
*
|
|
9
|
+
* WHY THIS FILE NO LONGER CARRIES THE RULE (Marcelo, 2026-08-19). It used to hold
|
|
10
|
+
* `DeriveContractState()` for TypeScript and `StateSQL()` to render the same rule as T-SQL, on the
|
|
11
|
+
* reasoning that one module rendering both copies could not drift. That reasoning was wrong, and it
|
|
12
|
+
* failed in the most direct way available: the TypeScript branch for termination was tightened to
|
|
13
|
+
* "terminated before today", the view was not, and for several hours the same contract read `Active`
|
|
14
|
+
* in the browser and `Terminated` from every view-backed query. The guard did not fire because it
|
|
15
|
+
* compared the two renderings as TEXT, and text cannot see a semantic split.
|
|
16
|
+
*
|
|
17
|
+
* The lesson is not "write a better guard" — a guard that has to be right about two implementations
|
|
18
|
+
* is a third thing to get wrong. It is: **do not mirror a rule.** One definition, in one language.
|
|
19
|
+
* SQL wins here because the rule is time-dependent (`Expired` and `Active` turn over at midnight with
|
|
20
|
+
* no write to trigger them), so it must be evaluated at READ time, which is what a view does and what
|
|
21
|
+
* no amount of TypeScript can do for a row nobody has loaded.
|
|
22
|
+
*
|
|
23
|
+
* WHAT THAT COSTS, stated plainly: the rule can no longer be tested without a database, so
|
|
24
|
+
* `npx vitest run` cannot cover its semantics. Two things stand in for it. `contract-state.test.ts`
|
|
25
|
+
* asserts the committed migration's `CASE` matches the branches and precedence THIS TEST declares —
|
|
26
|
+
* an expectation owned by the test, checked against the single implementation, so it is not two
|
|
27
|
+
* implementations agreeing with each other. And `test-harnesses/state-equivalence.mjs` runs fixtures
|
|
28
|
+
* covering every state and the termination boundary through the deployed view and asserts the answers
|
|
29
|
+
* a person wrote down.
|
|
30
|
+
*
|
|
31
|
+
* A contract being EDITED therefore shows its last-saved state, not a live projection of unsaved
|
|
32
|
+
* dates. That is deliberate: a chip reacting to an unsaved date asserts something no query would
|
|
33
|
+
* agree with.
|
|
34
|
+
*
|
|
35
|
+
* @module @mj-biz-apps/contracts-entities
|
|
36
|
+
*/
|
|
37
|
+
/**
|
|
38
|
+
* The six lifecycle states, in precedence order — first match wins (ERD §4.5).
|
|
39
|
+
*
|
|
40
|
+
* Kept in TypeScript because the CLIENT needs the vocabulary (chip colours, a filter pill, a typed
|
|
41
|
+
* `State` accessor) even though it does not own the rule. This is a value list, not a second
|
|
42
|
+
* implementation: nothing here decides which state a contract is in.
|
|
43
|
+
*
|
|
44
|
+
* The order is load-bearing documentation of the view's precedence, and
|
|
45
|
+
* `contract-state.test.ts` asserts the migration's `CASE` still matches it.
|
|
46
|
+
*/
|
|
47
|
+
export declare const CONTRACT_STATES: readonly ["Terminated", "Superseded", "Expired", "Active", "Executed", "Draft"];
|
|
48
|
+
export type ContractState = (typeof CONTRACT_STATES)[number];
|
|
49
|
+
//# sourceMappingURL=contract-state.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contract-state.d.ts","sourceRoot":"","sources":["../src/contract-state.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAEH;;;;;;;;;GASG;AACH,eAAO,MAAM,eAAe,iFAAkF,CAAC;AAE/G,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,CAAC,CAAC"}
|