@mj-biz-apps/contracts-entities 0.4.2 → 0.6.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/ContractEntity.d.ts +44 -0
- package/dist/ContractEntity.d.ts.map +1 -1
- package/dist/ContractEntity.js +91 -0
- package/dist/ContractEntity.js.map +1 -1
- package/dist/default-template.d.ts +128 -0
- package/dist/default-template.d.ts.map +1 -0
- package/dist/default-template.js +155 -0
- package/dist/default-template.js.map +1 -0
- package/dist/generated/entities/__mj_BizAppsContracts.d.ts +1345 -0
- package/dist/generated/entities/__mj_BizAppsContracts.d.ts.map +1 -0
- package/dist/generated/entities/__mj_BizAppsContracts.js +1973 -0
- package/dist/generated/entities/__mj_BizAppsContracts.js.map +1 -0
- package/dist/generated/entity_subclasses.d.ts +1 -1233
- package/dist/generated/entity_subclasses.d.ts.map +1 -1
- package/dist/generated/entity_subclasses.js +2 -1814
- package/dist/generated/entity_subclasses.js.map +1 -1
- package/dist/generated/remote_operations.d.ts +1 -1144
- package/dist/generated/remote_operations.d.ts.map +1 -1
- package/dist/generated/remote_operations.js +1 -425
- package/dist/generated/remote_operations.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -1
- package/dist/renewal-defaults.d.ts +99 -0
- package/dist/renewal-defaults.d.ts.map +1 -0
- package/dist/renewal-defaults.js +123 -0
- package/dist/renewal-defaults.js.map +1 -0
- package/package.json +1 -1
package/dist/ContractEntity.d.ts
CHANGED
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
*/
|
|
22
22
|
import { ValidationResult } from '@memberjunction/core';
|
|
23
23
|
import { mjBizAppsContractsContractEntity } from './generated/entity_subclasses.js';
|
|
24
|
+
import { type ContractRenewalField } from './renewal-defaults.js';
|
|
24
25
|
/**
|
|
25
26
|
* Do two contracts sit at the SAME LEVEL of the tree — i.e. may one supersede the other?
|
|
26
27
|
*
|
|
@@ -207,6 +208,49 @@ export declare class ContractEntity extends mjBizAppsContractsContractEntity {
|
|
|
207
208
|
* neither does. Mirrors orders' `Confirm()`, which is likewise an intent-setter, not a persister.
|
|
208
209
|
*/
|
|
209
210
|
Supersede(successor: ContractEntity): void;
|
|
211
|
+
/**
|
|
212
|
+
* Record that the USER set one of the four renewal fields, so seeding will never overwrite it.
|
|
213
|
+
*
|
|
214
|
+
* Called by the form panel that owns those fields, on every write it makes. It has to be an
|
|
215
|
+
* explicit signal rather than something derived from the entity's dirty state: `AutoRenew` starts
|
|
216
|
+
* at No on a new contract, so a user who deliberately answers No leaves the field holding the
|
|
217
|
+
* value it already had, and no amount of comparing against `OldValue` can tell that apart from a
|
|
218
|
+
* field nobody has looked at.
|
|
219
|
+
*/
|
|
220
|
+
MarkRenewalFieldEdited(field: ContractRenewalField): void;
|
|
221
|
+
/**
|
|
222
|
+
* Copy the selected Contract Type's renewal defaults onto this contract.
|
|
223
|
+
*
|
|
224
|
+
* UNSAVED RECORDS ONLY, and this is the guard that makes the feature safe rather than merely
|
|
225
|
+
* convenient. On a saved contract every renewal value was typed by somebody reading the paper —
|
|
226
|
+
* including the blanks, which mean "the agreement says nothing" — so a type change on an existing
|
|
227
|
+
* record must not touch them. The story is about filling in a new contract, and the blast radius
|
|
228
|
+
* is kept to exactly that.
|
|
229
|
+
*
|
|
230
|
+
* Returns whether anything was written, so a caller can skip a redundant change-detection pass.
|
|
231
|
+
* Safe to call repeatedly with the same type: the computed seed is empty when the fields already
|
|
232
|
+
* hold their targets.
|
|
233
|
+
*/
|
|
234
|
+
SeedRenewalDefaultsFromType(): Promise<boolean>;
|
|
235
|
+
/** The four fields as they stand right now. */
|
|
236
|
+
private currentRenewalValues;
|
|
237
|
+
/**
|
|
238
|
+
* The selected type's four default columns, or null when no type is selected — which restores the
|
|
239
|
+
* pristine values rather than leaving the departing type's numbers behind.
|
|
240
|
+
*
|
|
241
|
+
* One `RunView` rather than an entity load: four scalars are wanted and none of the type's rules
|
|
242
|
+
* are, and this runs on a keystroke-adjacent path (picking from a dropdown). The UUID is shape-
|
|
243
|
+
* checked before it reaches `ExtraFilter` for the same reason `ContractEntityServer` checks the
|
|
244
|
+
* one it walks the lineage with — this string is concatenated into SQL.
|
|
245
|
+
*/
|
|
246
|
+
private loadRenewalDefaults;
|
|
247
|
+
/**
|
|
248
|
+
* Per-instance and deliberately NOT persisted: both of these describe an editing session, not the
|
|
249
|
+
* contract. A fresh instance of a saved record starts with neither, which is right — seeding does
|
|
250
|
+
* not run on saved records at all.
|
|
251
|
+
*/
|
|
252
|
+
private readonly renewalFieldsUserEdited;
|
|
253
|
+
private renewalFieldsPristine;
|
|
210
254
|
}
|
|
211
255
|
/**
|
|
212
256
|
* Anti-tree-shake anchor. Importing this module is what fires `@RegisterClass`; a production build
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ContractEntity.d.ts","sourceRoot":"","sources":["../src/ContractEntity.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,OAAO,EAAwD,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAE9G,OAAO,EAAE,gCAAgC,EAAE,MAAM,+BAA+B,CAAC;
|
|
1
|
+
{"version":3,"file":"ContractEntity.d.ts","sourceRoot":"","sources":["../src/ContractEntity.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,OAAO,EAAwD,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAE9G,OAAO,EAAE,gCAAgC,EAAE,MAAM,+BAA+B,CAAC;AACjF,OAAO,EAGH,KAAK,oBAAoB,EAG5B,MAAM,oBAAoB,CAAC;AAS5B;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAAE,aAAa,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAM5H;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,yBAAyB,CAAC,YAAY,EAAE,SAAS,OAAO,EAAE,GAAG,MAAM,EAAE,CAWpF;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,oBAAoB,CAAC,aAAa,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAUtF;AAED,qBACa,cAAe,SAAQ,gCAAgC;IAChE;;;;;;;;;;;;;OAaG;IACa,QAAQ,IAAI,gBAAgB;IAwB5C;;;;;;;;;;;;;;OAcG;IACH,OAAO,CAAC,wBAAwB;IAchC;;;;;;;;;;;;;;;OAeG;IACH,OAAO,CAAC,kCAAkC;IAkB1C;;;;;;;;;;;;;;;;;;;OAmBG;IACH;;;;;;;;;;;;;;;;;;;;;;;;;;OA0BG;IACH,OAAO,CAAC,4BAA4B;IAmBpC;;;;OAIG;IACH,SAAS,CAAC,uBAAuB,UAAS;IAE1C;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,yBAAyB;IAIjC;;;;;;;;;;;OAWG;IACI,SAAS,CAAC,SAAS,EAAE,cAAc,GAAG,IAAI;IAqBjD;;;;;;;;OAQG;IACI,sBAAsB,CAAC,KAAK,EAAE,oBAAoB,GAAG,IAAI;IAIhE;;;;;;;;;;;;OAYG;IACU,2BAA2B,IAAI,OAAO,CAAC,OAAO,CAAC;IAsB5D,+CAA+C;IAC/C,OAAO,CAAC,oBAAoB;IAQ5B;;;;;;;;OAQG;YACW,mBAAmB;IAgBjC;;;;OAIG;IACH,OAAO,CAAC,QAAQ,CAAC,uBAAuB,CAAmC;IAC3E,OAAO,CAAC,qBAAqB,CAA4B;CAE5D;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,IAAI,IAAI,CAEzC"}
|
package/dist/ContractEntity.js
CHANGED
|
@@ -28,6 +28,13 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
28
28
|
import { BaseEntity, ValidationErrorInfo, ValidationErrorType } from '@memberjunction/core';
|
|
29
29
|
import { RegisterClass } from '@memberjunction/global';
|
|
30
30
|
import { mjBizAppsContractsContractEntity } from './generated/entity_subclasses.js';
|
|
31
|
+
import { ComputeRenewalSeed, RENEWAL_SEED_FIELDS, } from './renewal-defaults.js';
|
|
32
|
+
/**
|
|
33
|
+
* Guards an ID before it is interpolated into a filter. Mirrors `ContractEntityServer`'s own guard;
|
|
34
|
+
* both exist because MJ has no parameter binding on `ExtraFilter`, so the shape check IS the
|
|
35
|
+
* protection.
|
|
36
|
+
*/
|
|
37
|
+
const UUID_SHAPE = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
|
|
31
38
|
/**
|
|
32
39
|
* Do two contracts sit at the SAME LEVEL of the tree — i.e. may one supersede the other?
|
|
33
40
|
*
|
|
@@ -126,6 +133,13 @@ let ContractEntity = class ContractEntity extends mjBizAppsContractsContractEnti
|
|
|
126
133
|
* the entity has any business claiming a number was system-assigned.
|
|
127
134
|
*/
|
|
128
135
|
this.NumberWasSystemAssigned = false;
|
|
136
|
+
/**
|
|
137
|
+
* Per-instance and deliberately NOT persisted: both of these describe an editing session, not the
|
|
138
|
+
* contract. A fresh instance of a saved record starts with neither, which is right — seeding does
|
|
139
|
+
* not run on saved records at all.
|
|
140
|
+
*/
|
|
141
|
+
this.renewalFieldsUserEdited = new Set();
|
|
142
|
+
this.renewalFieldsPristine = null;
|
|
129
143
|
}
|
|
130
144
|
/**
|
|
131
145
|
* `HasModifications` is MONOTONIC: it must be true when modification rows exist, and it is never
|
|
@@ -299,6 +313,83 @@ let ContractEntity = class ContractEntity extends mjBizAppsContractsContractEnti
|
|
|
299
313
|
}
|
|
300
314
|
this.SupersededByContractID = successor.ID;
|
|
301
315
|
}
|
|
316
|
+
/* ────────────────────────────────────────────────────────────────────────
|
|
317
|
+
* Renewal-obligation defaults (golive #217 / C-US1). The decision is in
|
|
318
|
+
* `renewal-defaults.ts`; this is the half that reads the type row and writes.
|
|
319
|
+
* ──────────────────────────────────────────────────────────────────────── */
|
|
320
|
+
/**
|
|
321
|
+
* Record that the USER set one of the four renewal fields, so seeding will never overwrite it.
|
|
322
|
+
*
|
|
323
|
+
* Called by the form panel that owns those fields, on every write it makes. It has to be an
|
|
324
|
+
* explicit signal rather than something derived from the entity's dirty state: `AutoRenew` starts
|
|
325
|
+
* at No on a new contract, so a user who deliberately answers No leaves the field holding the
|
|
326
|
+
* value it already had, and no amount of comparing against `OldValue` can tell that apart from a
|
|
327
|
+
* field nobody has looked at.
|
|
328
|
+
*/
|
|
329
|
+
MarkRenewalFieldEdited(field) {
|
|
330
|
+
this.renewalFieldsUserEdited.add(field);
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* Copy the selected Contract Type's renewal defaults onto this contract.
|
|
334
|
+
*
|
|
335
|
+
* UNSAVED RECORDS ONLY, and this is the guard that makes the feature safe rather than merely
|
|
336
|
+
* convenient. On a saved contract every renewal value was typed by somebody reading the paper —
|
|
337
|
+
* including the blanks, which mean "the agreement says nothing" — so a type change on an existing
|
|
338
|
+
* record must not touch them. The story is about filling in a new contract, and the blast radius
|
|
339
|
+
* is kept to exactly that.
|
|
340
|
+
*
|
|
341
|
+
* Returns whether anything was written, so a caller can skip a redundant change-detection pass.
|
|
342
|
+
* Safe to call repeatedly with the same type: the computed seed is empty when the fields already
|
|
343
|
+
* hold their targets.
|
|
344
|
+
*/
|
|
345
|
+
async SeedRenewalDefaultsFromType() {
|
|
346
|
+
if (this.IsSaved)
|
|
347
|
+
return false;
|
|
348
|
+
// Captured ONCE, before the first seed ever writes: the values this record had of its own
|
|
349
|
+
// accord. Restored later for any field the newly-chosen type has no opinion about, so a
|
|
350
|
+
// default never outlives the type that supplied it.
|
|
351
|
+
this.renewalFieldsPristine ??= this.currentRenewalValues();
|
|
352
|
+
const seed = ComputeRenewalSeed({
|
|
353
|
+
defaults: await this.loadRenewalDefaults(),
|
|
354
|
+
current: this.currentRenewalValues(),
|
|
355
|
+
pristine: this.renewalFieldsPristine,
|
|
356
|
+
userEdited: this.renewalFieldsUserEdited,
|
|
357
|
+
});
|
|
358
|
+
const fields = Object.keys(seed);
|
|
359
|
+
for (const field of fields) {
|
|
360
|
+
this.Set(field, seed[field]);
|
|
361
|
+
}
|
|
362
|
+
return fields.length > 0;
|
|
363
|
+
}
|
|
364
|
+
/** The four fields as they stand right now. */
|
|
365
|
+
currentRenewalValues() {
|
|
366
|
+
const values = {};
|
|
367
|
+
for (const field of RENEWAL_SEED_FIELDS) {
|
|
368
|
+
values[field.ContractField] = this.Get(field.ContractField);
|
|
369
|
+
}
|
|
370
|
+
return values;
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* The selected type's four default columns, or null when no type is selected — which restores the
|
|
374
|
+
* pristine values rather than leaving the departing type's numbers behind.
|
|
375
|
+
*
|
|
376
|
+
* One `RunView` rather than an entity load: four scalars are wanted and none of the type's rules
|
|
377
|
+
* are, and this runs on a keystroke-adjacent path (picking from a dropdown). The UUID is shape-
|
|
378
|
+
* checked before it reaches `ExtraFilter` for the same reason `ContractEntityServer` checks the
|
|
379
|
+
* one it walks the lineage with — this string is concatenated into SQL.
|
|
380
|
+
*/
|
|
381
|
+
async loadRenewalDefaults() {
|
|
382
|
+
const id = String(this.ContractTypeID ?? '').trim();
|
|
383
|
+
if (!UUID_SHAPE.test(id))
|
|
384
|
+
return null;
|
|
385
|
+
const result = await this.RunViewProviderToUse.RunView({
|
|
386
|
+
EntityName: 'MJ_BizApps_Contracts: Contract Types',
|
|
387
|
+
ExtraFilter: `ID = '${id}'`,
|
|
388
|
+
Fields: RENEWAL_SEED_FIELDS.map((f) => f.TypeField),
|
|
389
|
+
ResultType: 'simple',
|
|
390
|
+
}, this.ContextCurrentUser);
|
|
391
|
+
return result?.Results?.[0] ?? null;
|
|
392
|
+
}
|
|
302
393
|
};
|
|
303
394
|
ContractEntity = __decorate([
|
|
304
395
|
RegisterClass(BaseEntity, 'MJ_BizApps_Contracts: Contracts')
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ContractEntity.js","sourceRoot":"","sources":["../src/ContractEntity.ts"],"names":[],"mappings":";;;;;;AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,OAAO,EAAE,UAAU,EAAE,mBAAmB,EAAE,mBAAmB,EAAoB,MAAM,sBAAsB,CAAC;AAC9G,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EAAE,gCAAgC,EAAE,MAAM,+BAA+B,CAAC;
|
|
1
|
+
{"version":3,"file":"ContractEntity.js","sourceRoot":"","sources":["../src/ContractEntity.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Which template a NEW contract starts with, and when we are allowed to put it there.
|
|
3
|
+
*
|
|
4
|
+
* golive #218. Story C-US1 says `ContractTemplateID` defaults to the current template and the user
|
|
5
|
+
* corrects it if the executed document cites an older version. Nothing defaulted it, so every new
|
|
6
|
+
* Order Form, Statement of Work and Payment Link opened with the field empty — and all three carry
|
|
7
|
+
* `TemplateRequired = 1`, so the save was refused until the user found the picker themselves.
|
|
8
|
+
*
|
|
9
|
+
* FOUR DECISIONS LIVE HERE, AND THE PANEL OWNS THE QUERY — the same split as
|
|
10
|
+
* `supersede-candidates.ts`, and for the same reason: anything declared inside an Angular component
|
|
11
|
+
* can only be asserted by standing up Angular's DI, so a rule stated in a panel is a rule no unit
|
|
12
|
+
* test reaches.
|
|
13
|
+
*
|
|
14
|
+
* · {@link CurrentTemplateFilter} — WHICH templates may be defaulted at all
|
|
15
|
+
* · {@link CurrentTemplateOrderBy} — which of them is "current"
|
|
16
|
+
* · {@link ShouldDefaultTemplate} — whether we may write over what is in the field right now
|
|
17
|
+
* · {@link ShouldWithdrawDefaultTemplate} — whether we must TAKE BACK what we wrote, once the
|
|
18
|
+
* contract type stops wanting a template at all
|
|
19
|
+
*
|
|
20
|
+
* SCOPING, THE ISSUE'S ONE OPEN DECISION: the newest published usable template wins, REGARDLESS of
|
|
21
|
+
* its template type. Not a simplification — `ContractType` carries no `ContractTemplateTypeID`, so
|
|
22
|
+
* there is nothing in the data to scope by, and of the two seeded template types only Master
|
|
23
|
+
* Agreement has templates behind it (Statement of Work is seeded deliberately unused, because the
|
|
24
|
+
* business does not version its SOW language). Scoping by type would need a schema change and would
|
|
25
|
+
* today narrow a one-element set to itself.
|
|
26
|
+
*
|
|
27
|
+
* @module @mj-biz-apps/contracts-entities
|
|
28
|
+
*/
|
|
29
|
+
/**
|
|
30
|
+
* The templates a contract may be started on — an `ExtraFilter`, not a preference.
|
|
31
|
+
*
|
|
32
|
+
* Both halves restate what `ContractEntityServer.refuseUnusableTemplate()` enforces on save, and
|
|
33
|
+
* that duplication is the point: a default that offers something the server will refuse turns a
|
|
34
|
+
* convenience into a save the user cannot complete and did not cause. The server stays the floor.
|
|
35
|
+
*
|
|
36
|
+
* · `Status = 'Published'` — a draft's provisions can still be added to, edited and removed, so a
|
|
37
|
+
* contract pointing at one would have its standard terms change underneath it.
|
|
38
|
+
* · `IsUsable = 1` — derived in `vwContractTemplates`: the template records a `SourceURL` or has a
|
|
39
|
+
* file attached. A contract cannot incorporate terms nobody can open.
|
|
40
|
+
*/
|
|
41
|
+
export declare const CurrentTemplateFilter = "Status = 'Published' AND IsUsable = 1";
|
|
42
|
+
/**
|
|
43
|
+
* Which published template is the CURRENT one: the most recently introduced.
|
|
44
|
+
*
|
|
45
|
+
* `IntroducedDate` is when a version started being offered — deliberately not named `EffectiveDate`,
|
|
46
|
+
* because a template becomes effective for a customer when THAT customer signs it. It is the column
|
|
47
|
+
* the business versions by, so it leads.
|
|
48
|
+
*
|
|
49
|
+
* IT IS NULLABLE, AND THE DIRECTION MATTERS. T-SQL sorts NULLs FIRST ascending, therefore LAST
|
|
50
|
+
* descending — so a template whose introduction date was never recorded loses to every template that
|
|
51
|
+
* has one, and only wins when it is the only candidate. That is the outcome we want and it is the
|
|
52
|
+
* one a switch to `ASC` silently inverts, which is why it is pinned by a test rather than by this
|
|
53
|
+
* sentence.
|
|
54
|
+
*
|
|
55
|
+
* `__mj_CreatedAt` breaks a same-date tie by which row was registered last. Two templates introduced
|
|
56
|
+
* on one day is a correction, not a schedule, and the later row is the correction.
|
|
57
|
+
*/
|
|
58
|
+
export declare const CurrentTemplateOrderBy = "IntroducedDate DESC, __mj_CreatedAt DESC";
|
|
59
|
+
/** What the defaulting rule needs to know about the contract in front of it. */
|
|
60
|
+
export interface TemplateDefaultState {
|
|
61
|
+
/** `BaseEntity.IsSaved` — false only for a contract that has never been written. */
|
|
62
|
+
isSaved: boolean;
|
|
63
|
+
/** The value in the field right now. */
|
|
64
|
+
currentTemplateID: string | null | undefined;
|
|
65
|
+
/** The value THIS session last defaulted, or null if it has defaulted nothing yet. */
|
|
66
|
+
lastDefaultedID: string | null | undefined;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* May this panel write the template field at all — the half of every rule that needs no database?
|
|
70
|
+
*
|
|
71
|
+
* Exported separately so the caller can ask it BEFORE reading the contract type, the only input the
|
|
72
|
+
* rules below need that costs a query. It is false for every saved contract, which is what keeps an
|
|
73
|
+
* ordinary form open free of one.
|
|
74
|
+
*
|
|
75
|
+
* NAMED FOR PERMISSION RATHER THAN FOR DEFAULTING, because it now gates two opposite writes — the
|
|
76
|
+
* default and its withdrawal — and both are allowed in exactly the same circumstances: the contract
|
|
77
|
+
* is unsaved, and the field is either empty or still holds our own default. It was
|
|
78
|
+
* `RecordWantsDefaultTemplate` while defaulting was the only thing it guarded.
|
|
79
|
+
*
|
|
80
|
+
* · **a SAVED contract is never touched.** Its template is a fact about an agreement that exists,
|
|
81
|
+
* and re-deciding it when someone opens the form would rewrite history on a record the user only
|
|
82
|
+
* came to read.
|
|
83
|
+
* · **an empty field is fair game** — the ordinary case the issue is about.
|
|
84
|
+
* · **a HAND-PICKED template is never touched.** A user who deliberately chose last year's version
|
|
85
|
+
* and then corrected the type must not silently get this year's back, and must not have theirs
|
|
86
|
+
* taken away either. We may only overwrite what we ourselves put there.
|
|
87
|
+
*/
|
|
88
|
+
export declare function MayWriteDefaultTemplate(state: TemplateDefaultState): boolean;
|
|
89
|
+
/**
|
|
90
|
+
* Take back a template we defaulted, once the type stops wanting one (review of PR #51).
|
|
91
|
+
*
|
|
92
|
+
* THE BUG THIS EXISTS TO CLOSE. Picking Order Form fills the field in; changing the type to Change
|
|
93
|
+
* Order used to leave it filled, because the defaulting rule saw a type that needs no template and
|
|
94
|
+
* simply stopped — treating "there was never anything to do here" and "what I put here no longer
|
|
95
|
+
* belongs" as the same case. Nothing downstream caught it: `ContractEntityServer` refuses a MISSING
|
|
96
|
+
* template on a type that requires one and says nothing about a present one on a type that does not.
|
|
97
|
+
* The contract saved, claiming to incorporate a full set of standard terms nobody chose.
|
|
98
|
+
*
|
|
99
|
+
* A default leaves with the type that supplied it — the same principle the renewal-field defaults
|
|
100
|
+
* follow.
|
|
101
|
+
*
|
|
102
|
+
* ONLY OUR OWN DEFAULT IS WITHDRAWN. A template the user picked by hand stays, exactly as it stays
|
|
103
|
+
* through a default; `holdsOurDefault` is the single comparison both directions read.
|
|
104
|
+
*
|
|
105
|
+
* Clearing the type entirely withdraws it too, and that is deliberate: the caller reports a type it
|
|
106
|
+
* genuinely could not find as `templateRequired = false`, and a contract with no type has nothing
|
|
107
|
+
* asserting that those standard terms apply. A type it could not READ is a different answer, and the
|
|
108
|
+
* caller must not translate that into `false` — see the panel's `typeRequiresTemplate`.
|
|
109
|
+
*/
|
|
110
|
+
export declare function ShouldWithdrawDefaultTemplate(state: TemplateDefaultState & {
|
|
111
|
+
templateRequired: boolean;
|
|
112
|
+
}): boolean;
|
|
113
|
+
/**
|
|
114
|
+
* The whole rule in the other direction: may we default this contract's template right now?
|
|
115
|
+
*
|
|
116
|
+
* `templateRequired` is `ContractType.TemplateRequired`, read from the type the contract currently
|
|
117
|
+
* names. It gates the default because the flag is what says a type's terms live in a template at
|
|
118
|
+
* all: Change Order is the one seeded type carrying `0`, and it carries no template of its own —
|
|
119
|
+
* a modification recorded on a change order cites a provision of a template at or above it in the
|
|
120
|
+
* tree. Defaulting one onto it would assert an incorporation nobody made.
|
|
121
|
+
*
|
|
122
|
+
* A contract with NO type yet cannot be defaulted either, and falls out of this naturally: the
|
|
123
|
+
* caller has no type to read `TemplateRequired` from, so it passes `false`.
|
|
124
|
+
*/
|
|
125
|
+
export declare function ShouldDefaultTemplate(state: TemplateDefaultState & {
|
|
126
|
+
templateRequired: boolean;
|
|
127
|
+
}): boolean;
|
|
128
|
+
//# sourceMappingURL=default-template.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"default-template.d.ts","sourceRoot":"","sources":["../src/default-template.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,qBAAqB,0CAA0C,CAAC;AAE7E;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,sBAAsB,6CAA6C,CAAC;AAEjF,gFAAgF;AAChF,MAAM,WAAW,oBAAoB;IACjC,oFAAoF;IACpF,OAAO,EAAE,OAAO,CAAC;IACjB,wCAAwC;IACxC,iBAAiB,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAC7C,sFAAsF;IACtF,eAAe,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;CAC9C;AA2BD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,oBAAoB,GAAG,OAAO,CAK5E;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,oBAAoB,GAAG;IAAE,gBAAgB,EAAE,OAAO,CAAA;CAAE,GAAG,OAAO,CAIlH;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,oBAAoB,GAAG;IAAE,gBAAgB,EAAE,OAAO,CAAA;CAAE,GAAG,OAAO,CAE1G"}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Which template a NEW contract starts with, and when we are allowed to put it there.
|
|
3
|
+
*
|
|
4
|
+
* golive #218. Story C-US1 says `ContractTemplateID` defaults to the current template and the user
|
|
5
|
+
* corrects it if the executed document cites an older version. Nothing defaulted it, so every new
|
|
6
|
+
* Order Form, Statement of Work and Payment Link opened with the field empty — and all three carry
|
|
7
|
+
* `TemplateRequired = 1`, so the save was refused until the user found the picker themselves.
|
|
8
|
+
*
|
|
9
|
+
* FOUR DECISIONS LIVE HERE, AND THE PANEL OWNS THE QUERY — the same split as
|
|
10
|
+
* `supersede-candidates.ts`, and for the same reason: anything declared inside an Angular component
|
|
11
|
+
* can only be asserted by standing up Angular's DI, so a rule stated in a panel is a rule no unit
|
|
12
|
+
* test reaches.
|
|
13
|
+
*
|
|
14
|
+
* · {@link CurrentTemplateFilter} — WHICH templates may be defaulted at all
|
|
15
|
+
* · {@link CurrentTemplateOrderBy} — which of them is "current"
|
|
16
|
+
* · {@link ShouldDefaultTemplate} — whether we may write over what is in the field right now
|
|
17
|
+
* · {@link ShouldWithdrawDefaultTemplate} — whether we must TAKE BACK what we wrote, once the
|
|
18
|
+
* contract type stops wanting a template at all
|
|
19
|
+
*
|
|
20
|
+
* SCOPING, THE ISSUE'S ONE OPEN DECISION: the newest published usable template wins, REGARDLESS of
|
|
21
|
+
* its template type. Not a simplification — `ContractType` carries no `ContractTemplateTypeID`, so
|
|
22
|
+
* there is nothing in the data to scope by, and of the two seeded template types only Master
|
|
23
|
+
* Agreement has templates behind it (Statement of Work is seeded deliberately unused, because the
|
|
24
|
+
* business does not version its SOW language). Scoping by type would need a schema change and would
|
|
25
|
+
* today narrow a one-element set to itself.
|
|
26
|
+
*
|
|
27
|
+
* @module @mj-biz-apps/contracts-entities
|
|
28
|
+
*/
|
|
29
|
+
/**
|
|
30
|
+
* The templates a contract may be started on — an `ExtraFilter`, not a preference.
|
|
31
|
+
*
|
|
32
|
+
* Both halves restate what `ContractEntityServer.refuseUnusableTemplate()` enforces on save, and
|
|
33
|
+
* that duplication is the point: a default that offers something the server will refuse turns a
|
|
34
|
+
* convenience into a save the user cannot complete and did not cause. The server stays the floor.
|
|
35
|
+
*
|
|
36
|
+
* · `Status = 'Published'` — a draft's provisions can still be added to, edited and removed, so a
|
|
37
|
+
* contract pointing at one would have its standard terms change underneath it.
|
|
38
|
+
* · `IsUsable = 1` — derived in `vwContractTemplates`: the template records a `SourceURL` or has a
|
|
39
|
+
* file attached. A contract cannot incorporate terms nobody can open.
|
|
40
|
+
*/
|
|
41
|
+
export const CurrentTemplateFilter = `Status = 'Published' AND IsUsable = 1`;
|
|
42
|
+
/**
|
|
43
|
+
* Which published template is the CURRENT one: the most recently introduced.
|
|
44
|
+
*
|
|
45
|
+
* `IntroducedDate` is when a version started being offered — deliberately not named `EffectiveDate`,
|
|
46
|
+
* because a template becomes effective for a customer when THAT customer signs it. It is the column
|
|
47
|
+
* the business versions by, so it leads.
|
|
48
|
+
*
|
|
49
|
+
* IT IS NULLABLE, AND THE DIRECTION MATTERS. T-SQL sorts NULLs FIRST ascending, therefore LAST
|
|
50
|
+
* descending — so a template whose introduction date was never recorded loses to every template that
|
|
51
|
+
* has one, and only wins when it is the only candidate. That is the outcome we want and it is the
|
|
52
|
+
* one a switch to `ASC` silently inverts, which is why it is pinned by a test rather than by this
|
|
53
|
+
* sentence.
|
|
54
|
+
*
|
|
55
|
+
* `__mj_CreatedAt` breaks a same-date tie by which row was registered last. Two templates introduced
|
|
56
|
+
* on one day is a correction, not a schedule, and the later row is the correction.
|
|
57
|
+
*/
|
|
58
|
+
export const CurrentTemplateOrderBy = `IntroducedDate DESC, __mj_CreatedAt DESC`;
|
|
59
|
+
/**
|
|
60
|
+
* Does the field hold exactly the value THIS panel put there?
|
|
61
|
+
*
|
|
62
|
+
* The shared notion underneath every rule in this file, and the reason it is factored out rather
|
|
63
|
+
* than written three times: "ours" is what separates a default we may revisit from a choice a person
|
|
64
|
+
* made, and a copy of that comparison that drifts turns one of those into the other.
|
|
65
|
+
*
|
|
66
|
+
* An EMPTY field is not ours — it is nobody's. That distinction is what keeps
|
|
67
|
+
* {@link ShouldWithdrawDefaultTemplate} from dirtying a record to write null over null.
|
|
68
|
+
*
|
|
69
|
+
* The comparison is case-insensitive because MJ hands UUIDs back in either casing depending on how
|
|
70
|
+
* the record was loaded (MJ's UUID_COMPARISON_GUIDE); a `===` here would read our own default as the
|
|
71
|
+
* user's choice. Compared by hand rather than through `UUIDsEqual` for the same reason
|
|
72
|
+
* `IsSameContractLevel` does: these free functions stay importable by a test with no MJ runtime
|
|
73
|
+
* standing behind them.
|
|
74
|
+
*/
|
|
75
|
+
function holdsOurDefault(state) {
|
|
76
|
+
const current = (state.currentTemplateID ?? '').trim();
|
|
77
|
+
if (!current)
|
|
78
|
+
return false;
|
|
79
|
+
const defaulted = (state.lastDefaultedID ?? '').trim();
|
|
80
|
+
if (!defaulted)
|
|
81
|
+
return false;
|
|
82
|
+
return current.toLowerCase() === defaulted.toLowerCase();
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* May this panel write the template field at all — the half of every rule that needs no database?
|
|
86
|
+
*
|
|
87
|
+
* Exported separately so the caller can ask it BEFORE reading the contract type, the only input the
|
|
88
|
+
* rules below need that costs a query. It is false for every saved contract, which is what keeps an
|
|
89
|
+
* ordinary form open free of one.
|
|
90
|
+
*
|
|
91
|
+
* NAMED FOR PERMISSION RATHER THAN FOR DEFAULTING, because it now gates two opposite writes — the
|
|
92
|
+
* default and its withdrawal — and both are allowed in exactly the same circumstances: the contract
|
|
93
|
+
* is unsaved, and the field is either empty or still holds our own default. It was
|
|
94
|
+
* `RecordWantsDefaultTemplate` while defaulting was the only thing it guarded.
|
|
95
|
+
*
|
|
96
|
+
* · **a SAVED contract is never touched.** Its template is a fact about an agreement that exists,
|
|
97
|
+
* and re-deciding it when someone opens the form would rewrite history on a record the user only
|
|
98
|
+
* came to read.
|
|
99
|
+
* · **an empty field is fair game** — the ordinary case the issue is about.
|
|
100
|
+
* · **a HAND-PICKED template is never touched.** A user who deliberately chose last year's version
|
|
101
|
+
* and then corrected the type must not silently get this year's back, and must not have theirs
|
|
102
|
+
* taken away either. We may only overwrite what we ourselves put there.
|
|
103
|
+
*/
|
|
104
|
+
export function MayWriteDefaultTemplate(state) {
|
|
105
|
+
if (state.isSaved)
|
|
106
|
+
return false;
|
|
107
|
+
const current = (state.currentTemplateID ?? '').trim();
|
|
108
|
+
if (!current)
|
|
109
|
+
return true;
|
|
110
|
+
return holdsOurDefault(state);
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Take back a template we defaulted, once the type stops wanting one (review of PR #51).
|
|
114
|
+
*
|
|
115
|
+
* THE BUG THIS EXISTS TO CLOSE. Picking Order Form fills the field in; changing the type to Change
|
|
116
|
+
* Order used to leave it filled, because the defaulting rule saw a type that needs no template and
|
|
117
|
+
* simply stopped — treating "there was never anything to do here" and "what I put here no longer
|
|
118
|
+
* belongs" as the same case. Nothing downstream caught it: `ContractEntityServer` refuses a MISSING
|
|
119
|
+
* template on a type that requires one and says nothing about a present one on a type that does not.
|
|
120
|
+
* The contract saved, claiming to incorporate a full set of standard terms nobody chose.
|
|
121
|
+
*
|
|
122
|
+
* A default leaves with the type that supplied it — the same principle the renewal-field defaults
|
|
123
|
+
* follow.
|
|
124
|
+
*
|
|
125
|
+
* ONLY OUR OWN DEFAULT IS WITHDRAWN. A template the user picked by hand stays, exactly as it stays
|
|
126
|
+
* through a default; `holdsOurDefault` is the single comparison both directions read.
|
|
127
|
+
*
|
|
128
|
+
* Clearing the type entirely withdraws it too, and that is deliberate: the caller reports a type it
|
|
129
|
+
* genuinely could not find as `templateRequired = false`, and a contract with no type has nothing
|
|
130
|
+
* asserting that those standard terms apply. A type it could not READ is a different answer, and the
|
|
131
|
+
* caller must not translate that into `false` — see the panel's `typeRequiresTemplate`.
|
|
132
|
+
*/
|
|
133
|
+
export function ShouldWithdrawDefaultTemplate(state) {
|
|
134
|
+
if (state.templateRequired)
|
|
135
|
+
return false;
|
|
136
|
+
if (state.isSaved)
|
|
137
|
+
return false;
|
|
138
|
+
return holdsOurDefault(state);
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* The whole rule in the other direction: may we default this contract's template right now?
|
|
142
|
+
*
|
|
143
|
+
* `templateRequired` is `ContractType.TemplateRequired`, read from the type the contract currently
|
|
144
|
+
* names. It gates the default because the flag is what says a type's terms live in a template at
|
|
145
|
+
* all: Change Order is the one seeded type carrying `0`, and it carries no template of its own —
|
|
146
|
+
* a modification recorded on a change order cites a provision of a template at or above it in the
|
|
147
|
+
* tree. Defaulting one onto it would assert an incorporation nobody made.
|
|
148
|
+
*
|
|
149
|
+
* A contract with NO type yet cannot be defaulted either, and falls out of this naturally: the
|
|
150
|
+
* caller has no type to read `TemplateRequired` from, so it passes `false`.
|
|
151
|
+
*/
|
|
152
|
+
export function ShouldDefaultTemplate(state) {
|
|
153
|
+
return state.templateRequired && MayWriteDefaultTemplate(state);
|
|
154
|
+
}
|
|
155
|
+
//# sourceMappingURL=default-template.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"default-template.js","sourceRoot":"","sources":["../src/default-template.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,uCAAuC,CAAC;AAE7E;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,0CAA0C,CAAC;AAYjF;;;;;;;;;;;;;;;GAeG;AACH,SAAS,eAAe,CAAC,KAA2B;IAChD,MAAM,OAAO,GAAG,CAAC,KAAK,CAAC,iBAAiB,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IACvD,IAAI,CAAC,OAAO;QAAE,OAAO,KAAK,CAAC;IAE3B,MAAM,SAAS,GAAG,CAAC,KAAK,CAAC,eAAe,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IACvD,IAAI,CAAC,SAAS;QAAE,OAAO,KAAK,CAAC;IAC7B,OAAO,OAAO,CAAC,WAAW,EAAE,KAAK,SAAS,CAAC,WAAW,EAAE,CAAC;AAC7D,CAAC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,uBAAuB,CAAC,KAA2B;IAC/D,IAAI,KAAK,CAAC,OAAO;QAAE,OAAO,KAAK,CAAC;IAChC,MAAM,OAAO,GAAG,CAAC,KAAK,CAAC,iBAAiB,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IACvD,IAAI,CAAC,OAAO;QAAE,OAAO,IAAI,CAAC;IAC1B,OAAO,eAAe,CAAC,KAAK,CAAC,CAAC;AAClC,CAAC;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,UAAU,6BAA6B,CAAC,KAA2D;IACrG,IAAI,KAAK,CAAC,gBAAgB;QAAE,OAAO,KAAK,CAAC;IACzC,IAAI,KAAK,CAAC,OAAO;QAAE,OAAO,KAAK,CAAC;IAChC,OAAO,eAAe,CAAC,KAAK,CAAC,CAAC;AAClC,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,qBAAqB,CAAC,KAA2D;IAC7F,OAAO,KAAK,CAAC,gBAAgB,IAAI,uBAAuB,CAAC,KAAK,CAAC,CAAC;AACpE,CAAC"}
|