@mj-biz-apps/sales-entities 6.3.3 → 6.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.
@@ -0,0 +1,69 @@
1
+ /**
2
+ * @fileoverview Whether `Deal.Amount` still describes the order it was cached from.
3
+ *
4
+ * `Deal.Amount` is a CACHED ANSWER from Orders, stamped with `AmountIsComputed` / `AmountComputedAt` /
5
+ * `AmountSourceHash`. Sales never recomputes it. The only question a sales surface may ask is whether
6
+ * the cached number still matches the order — and that is a COMPARISON of two stored figures, not
7
+ * arithmetic. Nothing here multiplies, discounts, prorates, sums or rounds.
8
+ *
9
+ * ── WHY THIS IS NOT A TIMESTAMP COMPARE ANY MORE (bc-aidp-next-golive#230) ──────────────────────
10
+ *
11
+ * Both surfaces used to read the newest `__mj_UpdatedAt` across the order's lines and call the amount
12
+ * stale if it was later than `AmountComputedAt`. That asks "was a line TOUCHED", and the answer to that
13
+ * is yes for reasons that have nothing to do with the price: closing a deal books the order, which moves
14
+ * the lines' status and stamps `__mj_UpdatedAt` without a figure changing. So every Won deal warned,
15
+ * permanently, that its frozen amount needed repricing.
16
+ *
17
+ * It is the failure shape CLAUDE.md rule 8 names: a claim that was true when it was written -- a touched
18
+ * line usually DID mean a moved price -- and stayed asserted after the close flow started touching lines
19
+ * for its own reasons. The guard was keyed on a PROXY (the timestamp) rather than on the thing it
20
+ * actually cared about (the number). A proxy can be outgrown; the number cannot.
21
+ *
22
+ * So the test is now the one the question deserves: does the cached amount equal the order's current
23
+ * total? That is also EXACTLY the test `DealEntityServer.refreshAmountFromOrder()` uses to decide the
24
+ * cache is already current, so this surface and the server agree by construction rather than by
25
+ * coincidence — if the server would rewrite the cache, this says stale; if it would no-op, this says
26
+ * fresh.
27
+ *
28
+ * @module @mj-biz-apps/sales-entities
29
+ */
30
+ import { type UserInfo } from '@memberjunction/core';
31
+ /**
32
+ * The notice a surface shows when the cached amount no longer matches its order.
33
+ *
34
+ * golive#230: the previous wording — "A line has changed since this amount was last priced. Reprice the
35
+ * order to update the total." — named an action that DOES NOT EXIST. There is no reprice control
36
+ * anywhere in this codebase, so the sentence asked the reader to do something impossible.
37
+ *
38
+ * Saving the deal genuinely is the fix, and that is verifiable rather than hopeful:
39
+ * `DealEntityServer.Save()` sets `amountMayHaveMoved` when `AmountIsComputed === true`, which is
40
+ * precisely the state this notice appears in, and then re-reads `OrderHeader.TotalGross` into the cache.
41
+ */
42
+ export declare const DEAL_AMOUNT_STALE_NOTICE = "The products on this deal changed after the amount was calculated. Save the deal to update it.";
43
+ /** The answer, plus the notice to show. `Notice` is null whenever `IsStale` is false. */
44
+ export interface DealAmountFreshness {
45
+ IsStale: boolean;
46
+ Notice: string | null;
47
+ }
48
+ /** What the caller must already know about the deal. Both surfaces hold all four. */
49
+ export interface DealAmountFreshnessInput {
50
+ /**
51
+ * Whether the PERSISTED status locks the deal, as `ResolveDealLockState` reports it.
52
+ *
53
+ * Required rather than optional on purpose. A caller that forgot to pass it would get the old
54
+ * always-warns behaviour back, silently, on exactly the screen the bug was reported against.
55
+ */
56
+ IsLocked: boolean;
57
+ AmountIsComputed: boolean | null | undefined;
58
+ Amount: number | string | null | undefined;
59
+ OrderID: string | null | undefined;
60
+ }
61
+ /**
62
+ * Whether the deal's cached amount still matches its order's total.
63
+ *
64
+ * Returns FRESH — never warns — in every case where the question is meaningless: a locked deal (the
65
+ * amount is frozen and no edit could change it), a deal whose amount was typed rather than computed,
66
+ * a deal with no order, and an order with no usable total.
67
+ */
68
+ export declare function ResolveDealAmountFreshness(input: DealAmountFreshnessInput, contextUser?: UserInfo): Promise<DealAmountFreshness>;
69
+ //# sourceMappingURL=amount-freshness.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"amount-freshness.d.ts","sourceRoot":"","sources":["../src/amount-freshness.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,OAAO,EAAW,KAAK,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAI9D;;;;;;;;;;GAUG;AACH,eAAO,MAAM,wBAAwB,mGAC+D,CAAC;AAErG,yFAAyF;AACzF,MAAM,WAAW,mBAAmB;IAChC,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED,qFAAqF;AACrF,MAAM,WAAW,wBAAwB;IACrC;;;;;OAKG;IACH,QAAQ,EAAE,OAAO,CAAC;IAClB,gBAAgB,EAAE,OAAO,GAAG,IAAI,GAAG,SAAS,CAAC;IAC7C,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAC3C,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;CACtC;AAKD;;;;;;GAMG;AACH,wBAAsB,0BAA0B,CAC5C,KAAK,EAAE,wBAAwB,EAC/B,WAAW,CAAC,EAAE,QAAQ,GACvB,OAAO,CAAC,mBAAmB,CAAC,CAsD9B"}
@@ -0,0 +1,100 @@
1
+ /**
2
+ * @fileoverview Whether `Deal.Amount` still describes the order it was cached from.
3
+ *
4
+ * `Deal.Amount` is a CACHED ANSWER from Orders, stamped with `AmountIsComputed` / `AmountComputedAt` /
5
+ * `AmountSourceHash`. Sales never recomputes it. The only question a sales surface may ask is whether
6
+ * the cached number still matches the order — and that is a COMPARISON of two stored figures, not
7
+ * arithmetic. Nothing here multiplies, discounts, prorates, sums or rounds.
8
+ *
9
+ * ── WHY THIS IS NOT A TIMESTAMP COMPARE ANY MORE (bc-aidp-next-golive#230) ──────────────────────
10
+ *
11
+ * Both surfaces used to read the newest `__mj_UpdatedAt` across the order's lines and call the amount
12
+ * stale if it was later than `AmountComputedAt`. That asks "was a line TOUCHED", and the answer to that
13
+ * is yes for reasons that have nothing to do with the price: closing a deal books the order, which moves
14
+ * the lines' status and stamps `__mj_UpdatedAt` without a figure changing. So every Won deal warned,
15
+ * permanently, that its frozen amount needed repricing.
16
+ *
17
+ * It is the failure shape CLAUDE.md rule 8 names: a claim that was true when it was written -- a touched
18
+ * line usually DID mean a moved price -- and stayed asserted after the close flow started touching lines
19
+ * for its own reasons. The guard was keyed on a PROXY (the timestamp) rather than on the thing it
20
+ * actually cared about (the number). A proxy can be outgrown; the number cannot.
21
+ *
22
+ * So the test is now the one the question deserves: does the cached amount equal the order's current
23
+ * total? That is also EXACTLY the test `DealEntityServer.refreshAmountFromOrder()` uses to decide the
24
+ * cache is already current, so this surface and the server agree by construction rather than by
25
+ * coincidence — if the server would rewrite the cache, this says stale; if it would no-op, this says
26
+ * fresh.
27
+ *
28
+ * @module @mj-biz-apps/sales-entities
29
+ */
30
+ import { RunView } from '@memberjunction/core';
31
+ const E_ORDER_HEADER = 'MJ_BizApps_Orders: Order Headers';
32
+ /**
33
+ * The notice a surface shows when the cached amount no longer matches its order.
34
+ *
35
+ * golive#230: the previous wording — "A line has changed since this amount was last priced. Reprice the
36
+ * order to update the total." — named an action that DOES NOT EXIST. There is no reprice control
37
+ * anywhere in this codebase, so the sentence asked the reader to do something impossible.
38
+ *
39
+ * Saving the deal genuinely is the fix, and that is verifiable rather than hopeful:
40
+ * `DealEntityServer.Save()` sets `amountMayHaveMoved` when `AmountIsComputed === true`, which is
41
+ * precisely the state this notice appears in, and then re-reads `OrderHeader.TotalGross` into the cache.
42
+ */
43
+ export const DEAL_AMOUNT_STALE_NOTICE = 'The products on this deal changed after the amount was calculated. Save the deal to update it.';
44
+ const FRESH = Object.freeze({ IsStale: false, Notice: null });
45
+ const STALE = Object.freeze({ IsStale: true, Notice: DEAL_AMOUNT_STALE_NOTICE });
46
+ /**
47
+ * Whether the deal's cached amount still matches its order's total.
48
+ *
49
+ * Returns FRESH — never warns — in every case where the question is meaningless: a locked deal (the
50
+ * amount is frozen and no edit could change it), a deal whose amount was typed rather than computed,
51
+ * a deal with no order, and an order with no usable total.
52
+ */
53
+ export async function ResolveDealAmountFreshness(input, contextUser) {
54
+ /**
55
+ * A CLOSED DEAL NEVER WARNS, and this is the first test rather than one of several.
56
+ *
57
+ * The amount is frozen by the close lock, so there is no edit that could resolve the notice and no
58
+ * reason to read it. golive#230 is precisely this case: the close books the order, the order's
59
+ * lines move, and the deal is simultaneously forbidden from doing anything about it. A warning
60
+ * nobody can act on is worse than silence — it trains the reader to ignore the flag that matters.
61
+ */
62
+ if (input.IsLocked) {
63
+ return FRESH;
64
+ }
65
+ // A typed amount claims nothing about the order, so it cannot disagree with one.
66
+ if (!input.AmountIsComputed || !input.OrderID) {
67
+ return FRESH;
68
+ }
69
+ const result = await new RunView().RunView({
70
+ EntityName: E_ORDER_HEADER,
71
+ ExtraFilter: `ID = '${String(input.OrderID).replace(/'/g, "''")}'`,
72
+ ResultType: 'simple',
73
+ Fields: ['TotalGross'],
74
+ }, contextUser);
75
+ if (!result?.Success) {
76
+ // A failed read is not evidence of staleness. Saying nothing is the honest answer.
77
+ return FRESH;
78
+ }
79
+ const raw = (result.Results ?? [])[0]?.TotalGross;
80
+ const total = raw === null || raw === undefined ? null : Number(raw);
81
+ if (total === null || !Number.isFinite(total)) {
82
+ /**
83
+ * `OrderHeader.TotalGross` is NULL for an order with no lines — `SUM` over no rows is NULL —
84
+ * so this is the empty-order case, not an error. There is no total to disagree with.
85
+ */
86
+ return FRESH;
87
+ }
88
+ const cachedRaw = input.Amount;
89
+ const cached = cachedRaw === null || cachedRaw === undefined ? null : Number(cachedRaw);
90
+ if (cached === null || !Number.isFinite(cached)) {
91
+ /**
92
+ * A deal claiming a COMPUTED amount that is not a number is the corrupt state `SD23` was written
93
+ * for. Reporting it stale is both true and useful: the notice's own instruction — save the deal
94
+ * — is exactly what repairs it, because the save re-reads the total into the cache.
95
+ */
96
+ return STALE;
97
+ }
98
+ return cached === total ? FRESH : STALE;
99
+ }
100
+ //# sourceMappingURL=amount-freshness.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"amount-freshness.js","sourceRoot":"","sources":["../src/amount-freshness.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,OAAO,EAAE,OAAO,EAAiB,MAAM,sBAAsB,CAAC;AAE9D,MAAM,cAAc,GAAG,kCAAkC,CAAC;AAE1D;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,wBAAwB,GACjC,gGAAgG,CAAC;AAsBrG,MAAM,KAAK,GAAwB,MAAM,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;AACnF,MAAM,KAAK,GAAwB,MAAM,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,wBAAwB,EAAE,CAAC,CAAC;AAEtG;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,0BAA0B,CAC5C,KAA+B,EAC/B,WAAsB;IAEtB;;;;;;;OAOG;IACH,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;QACjB,OAAO,KAAK,CAAC;IACjB,CAAC;IAED,iFAAiF;IACjF,IAAI,CAAC,KAAK,CAAC,gBAAgB,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;QAC5C,OAAO,KAAK,CAAC;IACjB,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,IAAI,OAAO,EAAE,CAAC,OAAO,CACtC;QACI,UAAU,EAAE,cAAc;QAC1B,WAAW,EAAE,SAAS,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG;QAClE,UAAU,EAAE,QAAQ;QACpB,MAAM,EAAE,CAAC,YAAY,CAAC;KACzB,EACD,WAAW,CACd,CAAC;IACF,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC;QACnB,mFAAmF;QACnF,OAAO,KAAK,CAAC;IACjB,CAAC;IAED,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,UAAU,CAAC;IAClD,MAAM,KAAK,GAAG,GAAG,KAAK,IAAI,IAAI,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IACrE,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QAC5C;;;WAGG;QACH,OAAO,KAAK,CAAC;IACjB,CAAC;IAED,MAAM,SAAS,GAAG,KAAK,CAAC,MAAM,CAAC;IAC/B,MAAM,MAAM,GAAG,SAAS,KAAK,IAAI,IAAI,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IACxF,IAAI,MAAM,KAAK,IAAI,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;QAC9C;;;;WAIG;QACH,OAAO,KAAK,CAAC;IACjB,CAAC;IAED,OAAO,MAAM,KAAK,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC;AAC5C,CAAC"}
@@ -28,25 +28,60 @@
28
28
  */
29
29
  import { type UserInfo } from '@memberjunction/core';
30
30
  /**
31
- * The deal fields that stay editable while the deal is locked.
31
+ * Every field a locked deal still accepts, given its outcome.
32
32
  *
33
- * Pinned by integration check CD14, which closes a deal and then proves each of these is genuinely
34
- * accepted and that a field outside the set is genuinely refused — so this constant cannot quietly
35
- * stop describing what the server does.
33
+ * Takes the flag rather than exposing a flat set, so a caller cannot read the list and forget the
34
+ * condition. That is not hypothetical: four call sites used to read the flat constant directly.
35
+ *
36
+ * @param isLost - whether the deal's PERSISTED status carries `IsLost`. When it cannot be determined,
37
+ * pass `false`: refusing an edit to a closed deal is the cheaper mistake.
38
+ */
39
+ export declare function DealFieldsEditableWhileLocked(isLost: boolean): ReadonlySet<string>;
40
+ /**
41
+ * Whether `fieldName` may still be edited on a locked deal with this outcome.
42
+ *
43
+ * Callers should prefer this to reaching into the sets, so the membership test stays in one place.
44
+ */
45
+ export declare function IsDealFieldEditableWhileLocked(fieldName: string, isLost: boolean): boolean;
46
+ /** The on-screen label for a deal field: the override if there is one, else the field name split into words. */
47
+ export declare function DealFieldLabel(fieldName: string): string;
48
+ /**
49
+ * What the lock notice lists as still editable.
50
+ *
51
+ * DEAL STATUS IS IN HERE AND NOT IN `DealFieldsEditableWhileLocked`, which looks like a
52
+ * contradiction and is not. That set is what the SERVER accepts in a bare save, and golive#205 asks
53
+ * for a bare status write to be refused on every path — the status moves through `Sales.CloseDeal`
54
+ * and `Sales.ReopenDeal`, which the form's status control routes to. So the field IS editable to a
55
+ * person and IS NOT writable by a raw save, and one set cannot say both.
56
+ *
57
+ * The notice describes what a PERSON can do, so it is the one that carries Deal Status.
58
+ *
59
+ * It takes `isLost` because the set it wraps does: golive#206 keeps Loss Notes editable on a lost
60
+ * deal and frozen on a won one, so the notice must name it on one and not the other.
36
61
  */
37
- export declare const DEAL_FIELDS_EDITABLE_WHILE_LOCKED: ReadonlySet<string>;
62
+ export declare function DealFieldsListedAsEditable(isLost: boolean): readonly string[];
38
63
  /**
39
- * Whether `fieldName` may still be edited on a locked deal.
64
+ * Join labels the way a sentence does: "A, B and C".
40
65
  *
41
- * Callers should prefer this to reaching into the set, so the membership test stays in one place if the
42
- * rule ever grows a condition beyond simple membership.
66
+ * Oxford-comma-free and with "and" before the last, because this lands mid-sentence in prose a tester
67
+ * wrote, not in a bulleted list.
43
68
  */
44
- export declare function IsDealFieldEditableWhileLocked(fieldName: string): boolean;
69
+ export declare function JoinLabels(labels: readonly string[]): string;
45
70
  /** What a surface needs to know to render the lock: whether it is on, and what to say about it. */
46
71
  export interface DealLockState {
47
72
  IsLocked: boolean;
48
73
  /** The status' display name, for the notice. Null when not locked. */
49
74
  StatusName: string | null;
75
+ /**
76
+ * Whether the locking status carries `IsLost`, which decides one field: golive#206 keeps Loss
77
+ * Notes editable on a lost deal and frozen on a won one.
78
+ *
79
+ * Resolved HERE rather than by each surface, for the same reason `IsLocked` is: it is read off the
80
+ * status ROW by flag, from the PERSISTED status, and a second implementation gets one of those
81
+ * quietly wrong. `false` on an open deal and on a status that cannot be read — refusing an edit to
82
+ * a closed deal is the cheaper mistake.
83
+ */
84
+ IsLost: boolean;
50
85
  /** A ready-to-render explanation, or null when the deal is open. */
51
86
  Notice: string | null;
52
87
  }
@@ -69,41 +104,31 @@ export interface DealLockState {
69
104
  * @param contextUser - Server callers must pass one; the browser omits it.
70
105
  */
71
106
  export declare function ResolveDealLockState(persistedStatusID: string | null | undefined, contextUser?: UserInfo): Promise<DealLockState>;
72
- /**
73
- * What a save knows about a status change, before deciding whether it is a bare close.
74
- *
75
- * Resolved by the caller because two of these need a database read; the decision itself does not, and
76
- * that is the point of separating them.
77
- */
78
- export interface StatusTransitionFacts {
79
- /** False on creation — a deal born closed has no transition to have run. */
80
- IsSaved: boolean;
81
- /** True when `Sales.CloseDeal` (or a reopen) announced itself via `DeclareTransition`. */
82
- HasDeclaredTransition: boolean;
83
- /** True when the caller set `DealStatusTypeID` on this save. */
84
- StatusIsDirty: boolean;
85
- /** Whether the status being moved INTO carries `LocksDeal`. */
86
- TargetLocks: boolean;
87
- /** Whether the status being moved OUT OF carries it. Null when there was none. */
88
- PriorLocks: boolean | null;
107
+ /** One row of the deal-status list, with the flag that decides whether it may be picked. */
108
+ export interface DealStatusOption {
109
+ ID: string;
110
+ Name: string;
111
+ /** Entering this status closes and freezes the deal. Enforced server-side. */
112
+ LocksDeal: boolean;
113
+ /**
114
+ * The OUTCOME flags, carried so a close action can find the status to close INTO by flag rather
115
+ * than by name. A deployment may call its winning status "Signed"; nothing may match on the word.
116
+ */
117
+ IsWon: boolean;
118
+ IsLost: boolean;
89
119
  }
90
120
  /**
91
- * Is this save about to lock a deal without the close having run? (bc-aidp-next-golive#205)
92
- *
93
- * THE DEFECT IT NAMES. The close lock reads the PERSISTED status, so Open -> Won is a save on an
94
- * unlocked deal and passes straight through it. The deal ends up locked with none of the close having
95
- * happened — no stage event, no contract, no finance tasks, no loss reason, an order still live — and
96
- * the lock then refuses `DealStatusTypeID` on every later save, so it cannot be undone either.
97
- *
98
- * WHAT MAKES A CLOSE LEGITIMATE is the declared transition. `Sales.CloseDeal` calls `stampClose`
99
- * immediately before saving, and that declares one; a form writing a field has no way to. So this
100
- * needs no new flag on the entity, and it cannot be spoofed by a caller that does not know about it.
121
+ * Every active deal status, with its lock flag, for a surface that has to offer a choice.
101
122
  *
102
- * LEAVING a locking status is deliberately NOT this function's business. That is a reopen, and the
103
- * close lock already refuses a bare one with a message that names `Sales.ReopenDeal` — two refusals
104
- * for the same edit would be one too many, and the other one is better worded for it.
123
+ * RETURNS ALL OF THEM AND LETS THE CALLER FILTER, deliberately. A closed deal still has to SHOW the
124
+ * status it is in — a control that simply dropped the locking ones would render a won deal as blank
125
+ * or "— choose —", which reads as data loss. The workspace already solves it this way: it offers the
126
+ * non-locking ones and adds the deal's own status back as a display-only option.
105
127
  *
106
- * Pure, so the decision can be pinned without a deal, a status table or a save.
128
+ * `LocksDeal` rather than `IsWon || IsLost`, because that is the flag the server's refusal reads.
129
+ * They coincide on today's data — Won, Lost and Abandoned carry both — but a surface filtering on a
130
+ * different flag would eventually offer a status the server then refuses, which is the drift this
131
+ * module exists to prevent.
107
132
  */
108
- export declare function IsBareCloseWrite(facts: StatusTransitionFacts): boolean;
133
+ export declare function LoadDealStatusOptions(contextUser?: UserInfo): Promise<DealStatusOption[]>;
109
134
  //# sourceMappingURL=close-lock.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"close-lock.d.ts","sourceRoot":"","sources":["../src/close-lock.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,OAAO,EAAW,KAAK,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAE9D;;;;;;GAMG;AACH,eAAO,MAAM,iCAAiC,EAAE,WAAW,CAAC,MAAM,CAGhE,CAAC;AAEH;;;;;GAKG;AACH,wBAAgB,8BAA8B,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAEzE;AAKD,mGAAmG;AACnG,MAAM,WAAW,aAAa;IAC1B,QAAQ,EAAE,OAAO,CAAC;IAClB,sEAAsE;IACtE,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,oEAAoE;IACpE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,oBAAoB,CACtC,iBAAiB,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAC5C,WAAW,CAAC,EAAE,QAAQ,GACvB,OAAO,CAAC,aAAa,CAAC,CA6BxB;AAED;;;;;GAKG;AACH,MAAM,WAAW,qBAAqB;IAClC,4EAA4E;IAC5E,OAAO,EAAE,OAAO,CAAC;IACjB,0FAA0F;IAC1F,qBAAqB,EAAE,OAAO,CAAC;IAC/B,gEAAgE;IAChE,aAAa,EAAE,OAAO,CAAC;IACvB,+DAA+D;IAC/D,WAAW,EAAE,OAAO,CAAC;IACrB,kFAAkF;IAClF,UAAU,EAAE,OAAO,GAAG,IAAI,CAAC;CAC9B;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,qBAAqB,GAAG,OAAO,CAQtE"}
1
+ {"version":3,"file":"close-lock.d.ts","sourceRoot":"","sources":["../src/close-lock.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,OAAO,EAAW,KAAK,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AA0C9D;;;;;;;;GAQG;AACH,wBAAgB,6BAA6B,CAAC,MAAM,EAAE,OAAO,GAAG,WAAW,CAAC,MAAM,CAAC,CAKlF;AAED;;;;GAIG;AACH,wBAAgB,8BAA8B,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,OAAO,CAE1F;AAkDD,gHAAgH;AAChH,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAExD;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,OAAO,GAAG,SAAS,MAAM,EAAE,CAE7E;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAI5D;AAKD,mGAAmG;AACnG,MAAM,WAAW,aAAa;IAC1B,QAAQ,EAAE,OAAO,CAAC;IAClB,sEAAsE;IACtE,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B;;;;;;;;OAQG;IACH,MAAM,EAAE,OAAO,CAAC;IAChB,oEAAoE;IACpE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,oBAAoB,CACtC,iBAAiB,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAC5C,WAAW,CAAC,EAAE,QAAQ,GACvB,OAAO,CAAC,aAAa,CAAC,CA0CxB;AAED,4FAA4F;AAC5F,MAAM,WAAW,gBAAgB;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,8EAA8E;IAC9E,SAAS,EAAE,OAAO,CAAC;IACnB;;;OAGG;IACH,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,OAAO,CAAC;CACnB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,qBAAqB,CAAC,WAAW,CAAC,EAAE,QAAQ,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAiC/F"}
@@ -28,24 +28,145 @@
28
28
  */
29
29
  import { RunView } from '@memberjunction/core';
30
30
  /**
31
- * The deal fields that stay editable while the deal is locked.
31
+ * The deal fields that stay editable while the deal is locked, on ANY locked deal.
32
32
  *
33
33
  * Pinned by integration check CD14, which closes a deal and then proves each of these is genuinely
34
- * accepted and that a field outside the set is genuinely refused — so this constant cannot quietly
35
- * stop describing what the server does.
34
+ * accepted and that a field outside the set is genuinely refused — so this cannot quietly stop
35
+ * describing what the server does.
36
36
  */
37
- export const DEAL_FIELDS_EDITABLE_WHILE_LOCKED = new Set([
37
+ const EDITABLE_ON_ANY_LOCKED_DEAL = new Set([
38
+ // Commentary. A closed deal still gets notes, and forcing a reopen to add one would corrupt the
39
+ // reopen record with administrative noise.
38
40
  'Description',
41
+ // Follow-up. NextStepDate is here because leaving one of the pair open and the other frozen makes
42
+ // no sense -- a next step nobody may date is half a field.
39
43
  'NextStep',
44
+ 'NextStepDate',
45
+ // Attribution and bookkeeping. Nothing downstream reads these, and they are routinely corrected
46
+ // after the fact: the contract takes the PRIMARY contact, not the billing one, and Lead Source and
47
+ // Campaign exist for reporting.
48
+ 'BillingContactID',
49
+ 'LeadSourceTypeID',
50
+ 'CampaignID',
40
51
  ]);
41
52
  /**
42
- * Whether `fieldName` may still be edited on a locked deal.
53
+ * The fields that stay editable only on a LOST deal.
43
54
  *
44
- * Callers should prefer this to reaching into the set, so the membership test stays in one place if the
45
- * rule ever grows a condition beyond simple membership.
55
+ * golive#206's field table says "Loss Notes (Lost deals only)", and item 3 asks that the server's
56
+ * list "match this list exactly". An earlier version of this module put `LossNotes` in the flat set
57
+ * above and said so out loud: a conditional member would need the form and the server to evaluate the
58
+ * same condition, which is the drift this module exists to prevent.
59
+ *
60
+ * That reasoning was weaker than it read. THIS module is exactly where such a condition belongs --
61
+ * the same place that already owns "how you decide a deal is locked at all". Both sides now pass a
62
+ * flag they already hold, from the same resolver, and the condition itself lives here once.
63
+ *
64
+ * `LossReasonID` stays frozen on every deal, lost included: the close event records which reason was
65
+ * chosen, and rewriting it would make that event dishonest. Notes are the channel for corrections.
66
+ */
67
+ const EDITABLE_ON_A_LOST_DEAL = new Set(['LossNotes']);
68
+ /**
69
+ * Every field a locked deal still accepts, given its outcome.
70
+ *
71
+ * Takes the flag rather than exposing a flat set, so a caller cannot read the list and forget the
72
+ * condition. That is not hypothetical: four call sites used to read the flat constant directly.
73
+ *
74
+ * @param isLost - whether the deal's PERSISTED status carries `IsLost`. When it cannot be determined,
75
+ * pass `false`: refusing an edit to a closed deal is the cheaper mistake.
76
+ */
77
+ export function DealFieldsEditableWhileLocked(isLost) {
78
+ if (!isLost) {
79
+ return EDITABLE_ON_ANY_LOCKED_DEAL;
80
+ }
81
+ return new Set([...EDITABLE_ON_ANY_LOCKED_DEAL, ...EDITABLE_ON_A_LOST_DEAL]);
82
+ }
83
+ /**
84
+ * Whether `fieldName` may still be edited on a locked deal with this outcome.
85
+ *
86
+ * Callers should prefer this to reaching into the sets, so the membership test stays in one place.
87
+ */
88
+ export function IsDealFieldEditableWhileLocked(fieldName, isLost) {
89
+ return EDITABLE_ON_ANY_LOCKED_DEAL.has(fieldName) || (isLost && EDITABLE_ON_A_LOST_DEAL.has(fieldName));
90
+ }
91
+ /**
92
+ * What each editable-while-locked field is CALLED on screen.
93
+ *
94
+ * The lock notice used to interpolate the raw field names, so a user read "only Description and
95
+ * NextStep can still be changed" — `NextStep` being a column name that appears nowhere on the form.
96
+ * golive#207 asks for the labels a salesperson sees.
97
+ *
98
+ * Falling back to the field name is deliberate rather than throwing: a field added to the set without
99
+ * a label here should read slightly wrong, not take the whole notice down. `DealFieldLabel` is
100
+ * exported so a test can prove every member of the set has one.
101
+ */
102
+ const DEAL_FIELD_LABELS = {
103
+ Description: 'Description',
104
+ NextStep: 'Next Step',
105
+ NextStepDate: 'Next Step Date',
106
+ BillingContactID: 'Billing Contact',
107
+ LeadSourceTypeID: 'Lead Source',
108
+ CampaignID: 'Campaign',
109
+ LossNotes: 'Loss Notes',
110
+ DealStatusTypeID: 'Deal Status',
111
+ };
112
+ /**
113
+ * Splits a column name into words and drops a trailing `ID`: `ExpectedCloseDate` -> `Expected Close
114
+ * Date`, `BillingContactID` -> `Billing Contact`.
115
+ *
116
+ * THIS EXISTS BECAUSE THE MAP ABOVE COVERS THE WRONG HALF OF THE RULE. It holds the fields the lock
117
+ * leaves EDITABLE -- what row 16 lists. Row 18 names the FROZEN fields, and there are far more of
118
+ * those, none of them in the map: `Amount`, `ExpectedCloseDate`, `AnnualIncreasePctOverride` and every
119
+ * other column a form or an importer can set. Falling back to the raw field name meant row 18 read
120
+ * "ExpectedCloseDate, AnnualIncreasePctOverride cannot be changed" -- the exact "NextStep being a
121
+ * column name that appears nowhere on the form" complaint the map was added to answer, on the message
122
+ * a user is far more likely to see.
123
+ *
124
+ * Deriving rather than hand-listing the frozen set, because a hand list would have to be extended
125
+ * every time a column is added and would read correctly right up until somebody forgot -- the same
126
+ * shape as the gap it is replacing. The map stays for the two labels a split cannot produce
127
+ * (`LeadSourceTypeID` -> "Lead Source", not "Lead Source Type") and is now an override rather than
128
+ * the only source of a label.
129
+ */
130
+ function SplitFieldName(fieldName) {
131
+ return fieldName
132
+ .replace(/ID$/, '')
133
+ .replace(/([a-z0-9])([A-Z])/g, '$1 $2')
134
+ .replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2')
135
+ .trim();
136
+ }
137
+ /** The on-screen label for a deal field: the override if there is one, else the field name split into words. */
138
+ export function DealFieldLabel(fieldName) {
139
+ return DEAL_FIELD_LABELS[fieldName] ?? SplitFieldName(fieldName);
140
+ }
141
+ /**
142
+ * What the lock notice lists as still editable.
143
+ *
144
+ * DEAL STATUS IS IN HERE AND NOT IN `DealFieldsEditableWhileLocked`, which looks like a
145
+ * contradiction and is not. That set is what the SERVER accepts in a bare save, and golive#205 asks
146
+ * for a bare status write to be refused on every path — the status moves through `Sales.CloseDeal`
147
+ * and `Sales.ReopenDeal`, which the form's status control routes to. So the field IS editable to a
148
+ * person and IS NOT writable by a raw save, and one set cannot say both.
149
+ *
150
+ * The notice describes what a PERSON can do, so it is the one that carries Deal Status.
151
+ *
152
+ * It takes `isLost` because the set it wraps does: golive#206 keeps Loss Notes editable on a lost
153
+ * deal and frozen on a won one, so the notice must name it on one and not the other.
46
154
  */
47
- export function IsDealFieldEditableWhileLocked(fieldName) {
48
- return DEAL_FIELDS_EDITABLE_WHILE_LOCKED.has(fieldName);
155
+ export function DealFieldsListedAsEditable(isLost) {
156
+ return ['DealStatusTypeID', ...DealFieldsEditableWhileLocked(isLost)];
157
+ }
158
+ /**
159
+ * Join labels the way a sentence does: "A, B and C".
160
+ *
161
+ * Oxford-comma-free and with "and" before the last, because this lands mid-sentence in prose a tester
162
+ * wrote, not in a bulleted list.
163
+ */
164
+ export function JoinLabels(labels) {
165
+ if (labels.length === 0)
166
+ return '';
167
+ if (labels.length === 1)
168
+ return labels[0];
169
+ return `${labels.slice(0, -1).join(', ')} and ${labels[labels.length - 1]}`;
49
170
  }
50
171
  /** Sales' deal-status type table. Named here so the lock lookup below has one spelling of it. */
51
172
  const E_DEAL_STATUS_TYPE = 'MJ_BizApps_Sales: Deal Status Types';
@@ -68,7 +189,7 @@ const E_DEAL_STATUS_TYPE = 'MJ_BizApps_Sales: Deal Status Types';
68
189
  * @param contextUser - Server callers must pass one; the browser omits it.
69
190
  */
70
191
  export async function ResolveDealLockState(persistedStatusID, contextUser) {
71
- const open = { IsLocked: false, StatusName: null, Notice: null };
192
+ const open = { IsLocked: false, StatusName: null, IsLost: false, Notice: null };
72
193
  if (!persistedStatusID) {
73
194
  return open;
74
195
  }
@@ -76,46 +197,70 @@ export async function ResolveDealLockState(persistedStatusID, contextUser) {
76
197
  EntityName: E_DEAL_STATUS_TYPE,
77
198
  ExtraFilter: `ID = '${String(persistedStatusID).replace(/'/g, "''")}'`,
78
199
  ResultType: 'simple',
79
- Fields: ['LocksDeal', 'Name'],
200
+ Fields: ['LocksDeal', 'Name', 'IsLost'],
80
201
  }, contextUser);
81
202
  const row = result?.Success ? (result.Results ?? [])[0] : undefined;
82
203
  if (!row?.LocksDeal) {
83
204
  return open;
84
205
  }
85
- const editable = [...DEAL_FIELDS_EDITABLE_WHILE_LOCKED].join(' and ');
206
+ /**
207
+ * golive#207 row 16, in the tester's words: "This deal is closed (Won). Only Deal Status,
208
+ * Description and Next Step can be edited. To change anything else, set the status back to Open."
209
+ *
210
+ * DERIVED rather than hardcoded to those three. The tester wrote that list when the editable set
211
+ * held two fields; golive#206 item 3 expands it. A hardcoded sentence would have started lying the
212
+ * moment that landed, and the lie would be invisible — it reads perfectly either way.
213
+ *
214
+ * What went, and why it is no loss: the old notice explained WHY the deal is frozen ("a contract or
215
+ * an order was derived from it"). A person who has just been stopped wants to know what they can do,
216
+ * not the provenance argument, and the reason is one click away in the close history.
217
+ */
218
+ const isLost = row.IsLost === true;
219
+ const editable = JoinLabels(DealFieldsListedAsEditable(isLost).map(DealFieldLabel));
86
220
  return {
87
221
  IsLocked: true,
88
222
  StatusName: row.Name,
89
- Notice: `This deal is closed (${row.Name}) and locked. A contract or an order was derived from it, so ` +
90
- `its terms are frozen — only ${editable} can still be changed. To change anything else, reopen ` +
91
- 'the deal, which records a reason.',
223
+ IsLost: isLost,
224
+ Notice: `This deal is closed (${row.Name}). Only ${editable} can be edited. ` +
225
+ 'To change anything else, set the status back to Open.',
92
226
  };
93
227
  }
94
228
  /**
95
- * Is this save about to lock a deal without the close having run? (bc-aidp-next-golive#205)
96
- *
97
- * THE DEFECT IT NAMES. The close lock reads the PERSISTED status, so Open -> Won is a save on an
98
- * unlocked deal and passes straight through it. The deal ends up locked with none of the close having
99
- * happened — no stage event, no contract, no finance tasks, no loss reason, an order still live — and
100
- * the lock then refuses `DealStatusTypeID` on every later save, so it cannot be undone either.
229
+ * Every active deal status, with its lock flag, for a surface that has to offer a choice.
101
230
  *
102
- * WHAT MAKES A CLOSE LEGITIMATE is the declared transition. `Sales.CloseDeal` calls `stampClose`
103
- * immediately before saving, and that declares one; a form writing a field has no way to. So this
104
- * needs no new flag on the entity, and it cannot be spoofed by a caller that does not know about it.
231
+ * RETURNS ALL OF THEM AND LETS THE CALLER FILTER, deliberately. A closed deal still has to SHOW the
232
+ * status it is in — a control that simply dropped the locking ones would render a won deal as blank
233
+ * or "— choose —", which reads as data loss. The workspace already solves it this way: it offers the
234
+ * non-locking ones and adds the deal's own status back as a display-only option.
105
235
  *
106
- * LEAVING a locking status is deliberately NOT this function's business. That is a reopen, and the
107
- * close lock already refuses a bare one with a message that names `Sales.ReopenDeal` — two refusals
108
- * for the same edit would be one too many, and the other one is better worded for it.
109
- *
110
- * Pure, so the decision can be pinned without a deal, a status table or a save.
236
+ * `LocksDeal` rather than `IsWon || IsLost`, because that is the flag the server's refusal reads.
237
+ * They coincide on today's data — Won, Lost and Abandoned carry both — but a surface filtering on a
238
+ * different flag would eventually offer a status the server then refuses, which is the drift this
239
+ * module exists to prevent.
111
240
  */
112
- export function IsBareCloseWrite(facts) {
113
- if (!facts.IsSaved || facts.HasDeclaredTransition || !facts.StatusIsDirty) {
114
- return false;
115
- }
116
- if (facts.PriorLocks === true) {
117
- return false; // a reopen attempt; the close lock owns that refusal
241
+ export async function LoadDealStatusOptions(contextUser) {
242
+ const result = await new RunView().RunView({
243
+ EntityName: E_DEAL_STATUS_TYPE,
244
+ ExtraFilter: 'IsActive = 1',
245
+ OrderBy: 'DisplayRank',
246
+ ResultType: 'simple',
247
+ // Every field read below must be listed here. A field declared on the row type and left
248
+ // out of this list arrives `undefined`, and a flag check against it quietly never fires --
249
+ // which is how ActivitySyncProviderType.IsActive did nothing for a release.
250
+ Fields: ['ID', 'Name', 'LocksDeal', 'IsWon', 'IsLost'],
251
+ }, contextUser);
252
+ if (!result?.Success) {
253
+ // An empty list leaves the control with nothing to offer, which is visibly wrong and therefore
254
+ // reportable. Inventing a list from somewhere else would hide a failed lookup behind a control
255
+ // that looks like it is working.
256
+ return [];
118
257
  }
119
- return facts.TargetLocks;
258
+ return (result.Results ?? []).map((r) => ({
259
+ ID: String(r.ID),
260
+ Name: String(r.Name),
261
+ LocksDeal: r.LocksDeal === true,
262
+ IsWon: r.IsWon === true,
263
+ IsLost: r.IsLost === true,
264
+ }));
120
265
  }
121
266
  //# sourceMappingURL=close-lock.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"close-lock.js","sourceRoot":"","sources":["../src/close-lock.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,OAAO,EAAE,OAAO,EAAiB,MAAM,sBAAsB,CAAC;AAE9D;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,iCAAiC,GAAwB,IAAI,GAAG,CAAS;IAClF,aAAa;IACb,UAAU;CACb,CAAC,CAAC;AAEH;;;;;GAKG;AACH,MAAM,UAAU,8BAA8B,CAAC,SAAiB;IAC5D,OAAO,iCAAiC,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;AAC5D,CAAC;AAED,iGAAiG;AACjG,MAAM,kBAAkB,GAAG,qCAAqC,CAAC;AAWjE;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACtC,iBAA4C,EAC5C,WAAsB;IAEtB,MAAM,IAAI,GAAkB,EAAE,QAAQ,EAAE,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IAChF,IAAI,CAAC,iBAAiB,EAAE,CAAC;QACrB,OAAO,IAAI,CAAC;IAChB,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,IAAI,OAAO,EAAE,CAAC,OAAO,CACtC;QACI,UAAU,EAAE,kBAAkB;QAC9B,WAAW,EAAE,SAAS,MAAM,CAAC,iBAAiB,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG;QACtE,UAAU,EAAE,QAAQ;QACpB,MAAM,EAAE,CAAC,WAAW,EAAE,MAAM,CAAC;KAChC,EACD,WAAW,CACd,CAAC;IACF,MAAM,GAAG,GAAG,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACpE,IAAI,CAAC,GAAG,EAAE,SAAS,EAAE,CAAC;QAClB,OAAO,IAAI,CAAC;IAChB,CAAC;IAED,MAAM,QAAQ,GAAG,CAAC,GAAG,iCAAiC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACtE,OAAO;QACH,QAAQ,EAAE,IAAI;QACd,UAAU,EAAE,GAAG,CAAC,IAAI;QACpB,MAAM,EACF,wBAAwB,GAAG,CAAC,IAAI,+DAA+D;YAC/F,+BAA+B,QAAQ,yDAAyD;YAChG,mCAAmC;KAC1C,CAAC;AACN,CAAC;AAqBD;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAA4B;IACzD,IAAI,CAAC,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,qBAAqB,IAAI,CAAC,KAAK,CAAC,aAAa,EAAE,CAAC;QACxE,OAAO,KAAK,CAAC;IACjB,CAAC;IACD,IAAI,KAAK,CAAC,UAAU,KAAK,IAAI,EAAE,CAAC;QAC5B,OAAO,KAAK,CAAC,CAAC,qDAAqD;IACvE,CAAC;IACD,OAAO,KAAK,CAAC,WAAW,CAAC;AAC7B,CAAC"}
1
+ {"version":3,"file":"close-lock.js","sourceRoot":"","sources":["../src/close-lock.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,OAAO,EAAE,OAAO,EAAiB,MAAM,sBAAsB,CAAC;AAE9D;;;;;;GAMG;AACH,MAAM,2BAA2B,GAAwB,IAAI,GAAG,CAAS;IACrE,gGAAgG;IAChG,2CAA2C;IAC3C,aAAa;IACb,kGAAkG;IAClG,2DAA2D;IAC3D,UAAU;IACV,cAAc;IACd,gGAAgG;IAChG,mGAAmG;IACnG,gCAAgC;IAChC,kBAAkB;IAClB,kBAAkB;IAClB,YAAY;CACf,CAAC,CAAC;AAEH;;;;;;;;;;;;;;GAcG;AACH,MAAM,uBAAuB,GAAwB,IAAI,GAAG,CAAS,CAAC,WAAW,CAAC,CAAC,CAAC;AAEpF;;;;;;;;GAQG;AACH,MAAM,UAAU,6BAA6B,CAAC,MAAe;IACzD,IAAI,CAAC,MAAM,EAAE,CAAC;QACV,OAAO,2BAA2B,CAAC;IACvC,CAAC;IACD,OAAO,IAAI,GAAG,CAAS,CAAC,GAAG,2BAA2B,EAAE,GAAG,uBAAuB,CAAC,CAAC,CAAC;AACzF,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,8BAA8B,CAAC,SAAiB,EAAE,MAAe;IAC7E,OAAO,2BAA2B,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,MAAM,IAAI,uBAAuB,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC;AAC5G,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,iBAAiB,GAAqC;IACxD,WAAW,EAAE,aAAa;IAC1B,QAAQ,EAAE,WAAW;IACrB,YAAY,EAAE,gBAAgB;IAC9B,gBAAgB,EAAE,iBAAiB;IACnC,gBAAgB,EAAE,aAAa;IAC/B,UAAU,EAAE,UAAU;IACtB,SAAS,EAAE,YAAY;IACvB,gBAAgB,EAAE,aAAa;CAClC,CAAC;AAEF;;;;;;;;;;;;;;;;;GAiBG;AACH,SAAS,cAAc,CAAC,SAAiB;IACrC,OAAO,SAAS;SACX,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC;SAClB,OAAO,CAAC,oBAAoB,EAAE,OAAO,CAAC;SACtC,OAAO,CAAC,uBAAuB,EAAE,OAAO,CAAC;SACzC,IAAI,EAAE,CAAC;AAChB,CAAC;AAED,gHAAgH;AAChH,MAAM,UAAU,cAAc,CAAC,SAAiB;IAC5C,OAAO,iBAAiB,CAAC,SAAS,CAAC,IAAI,cAAc,CAAC,SAAS,CAAC,CAAC;AACrE,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,0BAA0B,CAAC,MAAe;IACtD,OAAO,CAAC,kBAAkB,EAAE,GAAG,6BAA6B,CAAC,MAAM,CAAC,CAAC,CAAC;AAC1E,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,UAAU,CAAC,MAAyB;IAChD,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IACnC,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,MAAM,CAAC,CAAC,CAAC,CAAC;IAC1C,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,EAAE,CAAC;AAChF,CAAC;AAED,iGAAiG;AACjG,MAAM,kBAAkB,GAAG,qCAAqC,CAAC;AAqBjE;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACtC,iBAA4C,EAC5C,WAAsB;IAEtB,MAAM,IAAI,GAAkB,EAAE,QAAQ,EAAE,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IAC/F,IAAI,CAAC,iBAAiB,EAAE,CAAC;QACrB,OAAO,IAAI,CAAC;IAChB,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,IAAI,OAAO,EAAE,CAAC,OAAO,CACtC;QACI,UAAU,EAAE,kBAAkB;QAC9B,WAAW,EAAE,SAAS,MAAM,CAAC,iBAAiB,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG;QACtE,UAAU,EAAE,QAAQ;QACpB,MAAM,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,CAAC;KAC1C,EACD,WAAW,CACd,CAAC;IACF,MAAM,GAAG,GAAG,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACpE,IAAI,CAAC,GAAG,EAAE,SAAS,EAAE,CAAC;QAClB,OAAO,IAAI,CAAC;IAChB,CAAC;IAED;;;;;;;;;;;OAWG;IACH,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,KAAK,IAAI,CAAC;IACnC,MAAM,QAAQ,GAAG,UAAU,CAAC,0BAA0B,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC,CAAC;IACpF,OAAO;QACH,QAAQ,EAAE,IAAI;QACd,UAAU,EAAE,GAAG,CAAC,IAAI;QACpB,MAAM,EAAE,MAAM;QACd,MAAM,EACF,wBAAwB,GAAG,CAAC,IAAI,WAAW,QAAQ,kBAAkB;YACrE,uDAAuD;KAC9D,CAAC;AACN,CAAC;AAgBD;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAAC,WAAsB;IAC9D,MAAM,MAAM,GAAG,MAAM,IAAI,OAAO,EAAE,CAAC,OAAO,CAOtC;QACI,UAAU,EAAE,kBAAkB;QAC9B,WAAW,EAAE,cAAc;QAC3B,OAAO,EAAE,aAAa;QACtB,UAAU,EAAE,QAAQ;QACpB,wFAAwF;QACxF,2FAA2F;QAC3F,4EAA4E;QAC5E,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,QAAQ,CAAC;KACzD,EACD,WAAW,CACd,CAAC;IACF,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC;QACnB,+FAA+F;QAC/F,+FAA+F;QAC/F,iCAAiC;QACjC,OAAO,EAAE,CAAC;IACd,CAAC;IACD,OAAO,CAAC,MAAM,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACtC,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;QAChB,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC;QACpB,SAAS,EAAE,CAAC,CAAC,SAAS,KAAK,IAAI;QAC/B,KAAK,EAAE,CAAC,CAAC,KAAK,KAAK,IAAI;QACvB,MAAM,EAAE,CAAC,CAAC,MAAM,KAAK,IAAI;KAC5B,CAAC,CAAC,CAAC;AACR,CAAC"}
@@ -19,24 +19,24 @@ export declare const mjBizAppsSalesAccountTypeSchema: z.ZodObject<{
19
19
  }, "strip", z.ZodTypeAny, {
20
20
  Description?: string;
21
21
  Name?: string;
22
+ DisplayRank?: number;
22
23
  ID?: string;
23
24
  Code?: string;
24
25
  IsCustomer?: boolean;
25
26
  IsProspect?: boolean;
26
27
  IsPartner?: boolean;
27
- DisplayRank?: number;
28
28
  IsActive?: boolean;
29
29
  __mj_CreatedAt?: Date;
30
30
  __mj_UpdatedAt?: Date;
31
31
  }, {
32
32
  Description?: string;
33
33
  Name?: string;
34
+ DisplayRank?: number;
34
35
  ID?: string;
35
36
  Code?: string;
36
37
  IsCustomer?: boolean;
37
38
  IsProspect?: boolean;
38
39
  IsPartner?: boolean;
39
- DisplayRank?: number;
40
40
  IsActive?: boolean;
41
41
  __mj_CreatedAt?: Date;
42
42
  __mj_UpdatedAt?: Date;
@@ -60,9 +60,9 @@ export declare const mjBizAppsSalesBuyingRoleTypeSchema: z.ZodObject<{
60
60
  }, "strip", z.ZodTypeAny, {
61
61
  Description?: string;
62
62
  Name?: string;
63
+ DisplayRank?: number;
63
64
  ID?: string;
64
65
  Code?: string;
65
- DisplayRank?: number;
66
66
  IsActive?: boolean;
67
67
  __mj_CreatedAt?: Date;
68
68
  __mj_UpdatedAt?: Date;
@@ -72,9 +72,9 @@ export declare const mjBizAppsSalesBuyingRoleTypeSchema: z.ZodObject<{
72
72
  }, {
73
73
  Description?: string;
74
74
  Name?: string;
75
+ DisplayRank?: number;
75
76
  ID?: string;
76
77
  Code?: string;
77
- DisplayRank?: number;
78
78
  IsActive?: boolean;
79
79
  __mj_CreatedAt?: Date;
80
80
  __mj_UpdatedAt?: Date;
@@ -175,9 +175,9 @@ export declare const mjBizAppsSalesDealRoleSchema: z.ZodObject<{
175
175
  }, "strip", z.ZodTypeAny, {
176
176
  Description?: string;
177
177
  Name?: string;
178
+ DisplayRank?: number;
178
179
  ID?: string;
179
180
  Code?: string;
180
- DisplayRank?: number;
181
181
  IsActive?: boolean;
182
182
  __mj_CreatedAt?: Date;
183
183
  __mj_UpdatedAt?: Date;
@@ -188,9 +188,9 @@ export declare const mjBizAppsSalesDealRoleSchema: z.ZodObject<{
188
188
  }, {
189
189
  Description?: string;
190
190
  Name?: string;
191
+ DisplayRank?: number;
191
192
  ID?: string;
192
193
  Code?: string;
193
- DisplayRank?: number;
194
194
  IsActive?: boolean;
195
195
  __mj_CreatedAt?: Date;
196
196
  __mj_UpdatedAt?: Date;
@@ -309,30 +309,30 @@ export declare const mjBizAppsSalesDealStatusTypeSchema: z.ZodObject<{
309
309
  Description?: string;
310
310
  LocksDeal?: boolean;
311
311
  Name?: string;
312
+ IsLost?: boolean;
313
+ DisplayRank?: number;
312
314
  ID?: string;
315
+ IsWon?: boolean;
313
316
  Code?: string;
314
- DisplayRank?: number;
315
317
  IsActive?: boolean;
316
318
  __mj_CreatedAt?: Date;
317
319
  __mj_UpdatedAt?: Date;
318
320
  IsOpen?: boolean;
319
321
  IsClosed?: boolean;
320
- IsWon?: boolean;
321
- IsLost?: boolean;
322
322
  }, {
323
323
  Description?: string;
324
324
  LocksDeal?: boolean;
325
325
  Name?: string;
326
+ IsLost?: boolean;
327
+ DisplayRank?: number;
326
328
  ID?: string;
329
+ IsWon?: boolean;
327
330
  Code?: string;
328
- DisplayRank?: number;
329
331
  IsActive?: boolean;
330
332
  __mj_CreatedAt?: Date;
331
333
  __mj_UpdatedAt?: Date;
332
334
  IsOpen?: boolean;
333
335
  IsClosed?: boolean;
334
- IsWon?: boolean;
335
- IsLost?: boolean;
336
336
  }>;
337
337
  export type mjBizAppsSalesDealStatusTypeEntityType = z.infer<typeof mjBizAppsSalesDealStatusTypeSchema>;
338
338
  /**
@@ -410,9 +410,9 @@ export declare const mjBizAppsSalesDealTypeSchema: z.ZodObject<{
410
410
  }, "strip", z.ZodTypeAny, {
411
411
  Description?: string;
412
412
  Name?: string;
413
+ DisplayRank?: number;
413
414
  ID?: string;
414
415
  Code?: string;
415
- DisplayRank?: number;
416
416
  IsActive?: boolean;
417
417
  __mj_CreatedAt?: Date;
418
418
  __mj_UpdatedAt?: Date;
@@ -423,9 +423,9 @@ export declare const mjBizAppsSalesDealTypeSchema: z.ZodObject<{
423
423
  }, {
424
424
  Description?: string;
425
425
  Name?: string;
426
+ DisplayRank?: number;
426
427
  ID?: string;
427
428
  Code?: string;
428
- DisplayRank?: number;
429
429
  IsActive?: boolean;
430
430
  __mj_CreatedAt?: Date;
431
431
  __mj_UpdatedAt?: Date;
@@ -501,6 +501,12 @@ export declare const mjBizAppsSalesDealSchema: z.ZodObject<{
501
501
  }, "strip", z.ZodTypeAny, {
502
502
  Description?: string;
503
503
  NextStep?: string;
504
+ NextStepDate?: Date;
505
+ BillingContactID?: string;
506
+ LeadSourceTypeID?: string;
507
+ CampaignID?: string;
508
+ LossNotes?: string;
509
+ DealStatusTypeID?: string;
504
510
  Name?: string;
505
511
  ID?: string;
506
512
  __mj_CreatedAt?: Date;
@@ -511,10 +517,8 @@ export declare const mjBizAppsSalesDealSchema: z.ZodObject<{
511
517
  PipelineID?: string;
512
518
  PipelineStageID?: string;
513
519
  DealTypeID?: string;
514
- DealStatusTypeID?: string;
515
520
  AccountID?: string;
516
521
  PrimaryContactID?: string;
517
- BillingContactID?: string;
518
522
  CompanyID?: string;
519
523
  OwnerEmployeeID?: string;
520
524
  AmountIsComputed?: boolean;
@@ -531,9 +535,6 @@ export declare const mjBizAppsSalesDealSchema: z.ZodObject<{
531
535
  Probability?: number;
532
536
  ForecastCategoryTypeID?: string;
533
537
  LossReasonID?: string;
534
- LossNotes?: string;
535
- LeadSourceTypeID?: string;
536
- CampaignID?: string;
537
538
  ContractID?: string;
538
539
  RenewsContractID?: string;
539
540
  AutoRenew?: boolean;
@@ -541,7 +542,6 @@ export declare const mjBizAppsSalesDealSchema: z.ZodObject<{
541
542
  CancellationNoticeDaysOverride?: number;
542
543
  PaymentMethod?: string;
543
544
  ContractVariances?: string;
544
- NextStepDate?: Date;
545
545
  ClosedAt?: Date;
546
546
  ClosedByUserID?: string;
547
547
  OrderID?: string;
@@ -561,6 +561,12 @@ export declare const mjBizAppsSalesDealSchema: z.ZodObject<{
561
561
  }, {
562
562
  Description?: string;
563
563
  NextStep?: string;
564
+ NextStepDate?: Date;
565
+ BillingContactID?: string;
566
+ LeadSourceTypeID?: string;
567
+ CampaignID?: string;
568
+ LossNotes?: string;
569
+ DealStatusTypeID?: string;
564
570
  Name?: string;
565
571
  ID?: string;
566
572
  __mj_CreatedAt?: Date;
@@ -571,10 +577,8 @@ export declare const mjBizAppsSalesDealSchema: z.ZodObject<{
571
577
  PipelineID?: string;
572
578
  PipelineStageID?: string;
573
579
  DealTypeID?: string;
574
- DealStatusTypeID?: string;
575
580
  AccountID?: string;
576
581
  PrimaryContactID?: string;
577
- BillingContactID?: string;
578
582
  CompanyID?: string;
579
583
  OwnerEmployeeID?: string;
580
584
  AmountIsComputed?: boolean;
@@ -591,9 +595,6 @@ export declare const mjBizAppsSalesDealSchema: z.ZodObject<{
591
595
  Probability?: number;
592
596
  ForecastCategoryTypeID?: string;
593
597
  LossReasonID?: string;
594
- LossNotes?: string;
595
- LeadSourceTypeID?: string;
596
- CampaignID?: string;
597
598
  ContractID?: string;
598
599
  RenewsContractID?: string;
599
600
  AutoRenew?: boolean;
@@ -601,7 +602,6 @@ export declare const mjBizAppsSalesDealSchema: z.ZodObject<{
601
602
  CancellationNoticeDaysOverride?: number;
602
603
  PaymentMethod?: string;
603
604
  ContractVariances?: string;
604
- NextStepDate?: Date;
605
605
  ClosedAt?: Date;
606
606
  ClosedByUserID?: string;
607
607
  OrderID?: string;
@@ -638,9 +638,9 @@ export declare const mjBizAppsSalesForecastCategoryTypeSchema: z.ZodObject<{
638
638
  }, "strip", z.ZodTypeAny, {
639
639
  Description?: string;
640
640
  Name?: string;
641
+ DisplayRank?: number;
641
642
  ID?: string;
642
643
  Code?: string;
643
- DisplayRank?: number;
644
644
  IsActive?: boolean;
645
645
  __mj_CreatedAt?: Date;
646
646
  __mj_UpdatedAt?: Date;
@@ -650,9 +650,9 @@ export declare const mjBizAppsSalesForecastCategoryTypeSchema: z.ZodObject<{
650
650
  }, {
651
651
  Description?: string;
652
652
  Name?: string;
653
+ DisplayRank?: number;
653
654
  ID?: string;
654
655
  Code?: string;
655
- DisplayRank?: number;
656
656
  IsActive?: boolean;
657
657
  __mj_CreatedAt?: Date;
658
658
  __mj_UpdatedAt?: Date;
@@ -738,9 +738,9 @@ export declare const mjBizAppsSalesLeadSourceTypeSchema: z.ZodObject<{
738
738
  }, "strip", z.ZodTypeAny, {
739
739
  Description?: string;
740
740
  Name?: string;
741
+ DisplayRank?: number;
741
742
  ID?: string;
742
743
  Code?: string;
743
- DisplayRank?: number;
744
744
  IsActive?: boolean;
745
745
  __mj_CreatedAt?: Date;
746
746
  __mj_UpdatedAt?: Date;
@@ -750,9 +750,9 @@ export declare const mjBizAppsSalesLeadSourceTypeSchema: z.ZodObject<{
750
750
  }, {
751
751
  Description?: string;
752
752
  Name?: string;
753
+ DisplayRank?: number;
753
754
  ID?: string;
754
755
  Code?: string;
755
- DisplayRank?: number;
756
756
  IsActive?: boolean;
757
757
  __mj_CreatedAt?: Date;
758
758
  __mj_UpdatedAt?: Date;
@@ -779,10 +779,10 @@ export declare const mjBizAppsSalesLifecycleStageTypeSchema: z.ZodObject<{
779
779
  }, "strip", z.ZodTypeAny, {
780
780
  Description?: string;
781
781
  Name?: string;
782
+ DisplayRank?: number;
782
783
  ID?: string;
783
784
  Code?: string;
784
785
  IsCustomer?: boolean;
785
- DisplayRank?: number;
786
786
  IsActive?: boolean;
787
787
  __mj_CreatedAt?: Date;
788
788
  __mj_UpdatedAt?: Date;
@@ -791,10 +791,10 @@ export declare const mjBizAppsSalesLifecycleStageTypeSchema: z.ZodObject<{
791
791
  }, {
792
792
  Description?: string;
793
793
  Name?: string;
794
+ DisplayRank?: number;
794
795
  ID?: string;
795
796
  Code?: string;
796
797
  IsCustomer?: boolean;
797
- DisplayRank?: number;
798
798
  IsActive?: boolean;
799
799
  __mj_CreatedAt?: Date;
800
800
  __mj_UpdatedAt?: Date;
@@ -820,9 +820,9 @@ export declare const mjBizAppsSalesLossReasonSchema: z.ZodObject<{
820
820
  }, "strip", z.ZodTypeAny, {
821
821
  Description?: string;
822
822
  Name?: string;
823
+ DisplayRank?: number;
823
824
  ID?: string;
824
825
  Code?: string;
825
- DisplayRank?: number;
826
826
  IsActive?: boolean;
827
827
  __mj_CreatedAt?: Date;
828
828
  __mj_UpdatedAt?: Date;
@@ -832,9 +832,9 @@ export declare const mjBizAppsSalesLossReasonSchema: z.ZodObject<{
832
832
  }, {
833
833
  Description?: string;
834
834
  Name?: string;
835
+ DisplayRank?: number;
835
836
  ID?: string;
836
837
  Code?: string;
837
- DisplayRank?: number;
838
838
  IsActive?: boolean;
839
839
  __mj_CreatedAt?: Date;
840
840
  __mj_UpdatedAt?: Date;
@@ -868,6 +868,7 @@ export declare const mjBizAppsSalesPipelineStageSchema: z.ZodObject<{
868
868
  ForecastCategoryType: z.ZodNullable<z.ZodString>;
869
869
  DealStatusType: z.ZodNullable<z.ZodString>;
870
870
  }, "strip", z.ZodTypeAny, {
871
+ DealStatusTypeID?: string;
871
872
  Name?: string;
872
873
  ID?: string;
873
874
  Code?: string;
@@ -876,7 +877,6 @@ export declare const mjBizAppsSalesPipelineStageSchema: z.ZodObject<{
876
877
  __mj_UpdatedAt?: Date;
877
878
  DisplayOrder?: number;
878
879
  PipelineID?: string;
879
- DealStatusTypeID?: string;
880
880
  Probability?: number;
881
881
  ForecastCategoryTypeID?: string;
882
882
  Pipeline?: string;
@@ -889,6 +889,7 @@ export declare const mjBizAppsSalesPipelineStageSchema: z.ZodObject<{
889
889
  GuidanceMarkdown?: string;
890
890
  OrderStatusOnEntry?: "Confirmed" | "Draft" | "Quoted" | "Voided";
891
891
  }, {
892
+ DealStatusTypeID?: string;
892
893
  Name?: string;
893
894
  ID?: string;
894
895
  Code?: string;
@@ -897,7 +898,6 @@ export declare const mjBizAppsSalesPipelineStageSchema: z.ZodObject<{
897
898
  __mj_UpdatedAt?: Date;
898
899
  DisplayOrder?: number;
899
900
  PipelineID?: string;
900
- DealStatusTypeID?: string;
901
901
  Probability?: number;
902
902
  ForecastCategoryTypeID?: string;
903
903
  Pipeline?: string;
@@ -935,9 +935,9 @@ export declare const mjBizAppsSalesPipelineSchema: z.ZodObject<{
935
935
  }, "strip", z.ZodTypeAny, {
936
936
  Description?: string;
937
937
  Name?: string;
938
+ DisplayRank?: number;
938
939
  ID?: string;
939
940
  Code?: string;
940
- DisplayRank?: number;
941
941
  IsActive?: boolean;
942
942
  __mj_CreatedAt?: Date;
943
943
  __mj_UpdatedAt?: Date;
@@ -953,9 +953,9 @@ export declare const mjBizAppsSalesPipelineSchema: z.ZodObject<{
953
953
  }, {
954
954
  Description?: string;
955
955
  Name?: string;
956
+ DisplayRank?: number;
956
957
  ID?: string;
957
958
  Code?: string;
958
- DisplayRank?: number;
959
959
  IsActive?: boolean;
960
960
  __mj_CreatedAt?: Date;
961
961
  __mj_UpdatedAt?: Date;
@@ -1008,13 +1008,13 @@ export declare const mjBizAppsSalesSalesAccountSchema: z.ZodObject<{
1008
1008
  LeadSourceType: z.ZodNullable<z.ZodString>;
1009
1009
  }, "strip", z.ZodTypeAny, {
1010
1010
  Description?: string;
1011
+ LeadSourceTypeID?: string;
1011
1012
  Name?: string;
1012
1013
  ID?: string;
1013
1014
  IsActive?: boolean;
1014
1015
  __mj_CreatedAt?: Date;
1015
1016
  __mj_UpdatedAt?: Date;
1016
1017
  OwnerEmployeeID?: string;
1017
- LeadSourceTypeID?: string;
1018
1018
  OwnerEmployee?: string;
1019
1019
  LeadSourceType?: string;
1020
1020
  AccountTypeID?: string;
@@ -1041,13 +1041,13 @@ export declare const mjBizAppsSalesSalesAccountSchema: z.ZodObject<{
1041
1041
  LifecycleStageType?: string;
1042
1042
  }, {
1043
1043
  Description?: string;
1044
+ LeadSourceTypeID?: string;
1044
1045
  Name?: string;
1045
1046
  ID?: string;
1046
1047
  IsActive?: boolean;
1047
1048
  __mj_CreatedAt?: Date;
1048
1049
  __mj_UpdatedAt?: Date;
1049
1050
  OwnerEmployeeID?: string;
1050
- LeadSourceTypeID?: string;
1051
1051
  OwnerEmployee?: string;
1052
1052
  LeadSourceType?: string;
1053
1053
  AccountTypeID?: string;
@@ -1109,13 +1109,13 @@ export declare const mjBizAppsSalesSalesContactSchema: z.ZodObject<{
1109
1109
  BuyingRoleType: z.ZodNullable<z.ZodString>;
1110
1110
  LeadSourceType: z.ZodNullable<z.ZodString>;
1111
1111
  }, "strip", z.ZodTypeAny, {
1112
+ LeadSourceTypeID?: string;
1112
1113
  ID?: string;
1113
1114
  __mj_CreatedAt?: Date;
1114
1115
  __mj_UpdatedAt?: Date;
1115
1116
  BuyingRoleTypeID?: string;
1116
1117
  BuyingRoleType?: string;
1117
1118
  OwnerEmployeeID?: string;
1118
- LeadSourceTypeID?: string;
1119
1119
  OwnerEmployee?: string;
1120
1120
  LeadSourceType?: string;
1121
1121
  LifecycleStageTypeID?: string;
@@ -1140,13 +1140,13 @@ export declare const mjBizAppsSalesSalesContactSchema: z.ZodObject<{
1140
1140
  Bio?: string;
1141
1141
  LinkedUserID?: string;
1142
1142
  }, {
1143
+ LeadSourceTypeID?: string;
1143
1144
  ID?: string;
1144
1145
  __mj_CreatedAt?: Date;
1145
1146
  __mj_UpdatedAt?: Date;
1146
1147
  BuyingRoleTypeID?: string;
1147
1148
  BuyingRoleType?: string;
1148
1149
  OwnerEmployeeID?: string;
1149
- LeadSourceTypeID?: string;
1150
1150
  OwnerEmployee?: string;
1151
1151
  LeadSourceType?: string;
1152
1152
  LifecycleStageTypeID?: string;
package/dist/index.d.ts CHANGED
@@ -34,6 +34,12 @@ export * from './term-start.js';
34
34
  * form cannot offer a field the server will refuse. Pinned by integration check CD14.
35
35
  */
36
36
  export * from './close-lock.js';
37
+ /**
38
+ * Whether `Deal.Amount` still matches the order it was cached from — shared by the Deal form and the
39
+ * deal hero so the two surfaces cannot answer the same question differently. A comparison of two
40
+ * stored figures, never a computation. See the file header for why it is no longer a timestamp test.
41
+ */
42
+ export * from './amount-freshness.js';
37
43
  /**
38
44
  * Forces the generated entity subclasses to be loaded. Without an explicit
39
45
  * import + call, tree-shaking drops the generated entities because they are not
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,+BAA+B,CAAC;AAE9C;;;;GAIG;AACH,cAAc,+BAA+B,CAAC;AAE9C;;;;;;;;;GASG;AACH,cAAc,eAAe,CAAC;AAE9B;;;;GAIG;AACH,cAAc,oBAAoB,CAAC;AAEnC;;;;GAIG;AACH,cAAc,kBAAkB,CAAC;AACjC,cAAc,cAAc,CAAC;AAE7B;;;GAGG;AACH,cAAc,cAAc,CAAC;AAE7B;;;;;GAKG;AACH,wBAAgB,qBAAqB,IAAI,IAAI,CAC5C;AACD,cAAc,0BAA0B,CAAC;AAEzC,wFAAwF;AACxF,cAAc,qCAAqC,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,+BAA+B,CAAC;AAE9C;;;;GAIG;AACH,cAAc,+BAA+B,CAAC;AAE9C;;;;;;;;;GASG;AACH,cAAc,eAAe,CAAC;AAE9B;;;;GAIG;AACH,cAAc,oBAAoB,CAAC;AAEnC;;;;GAIG;AACH,cAAc,kBAAkB,CAAC;AACjC,cAAc,cAAc,CAAC;AAE7B;;;GAGG;AACH,cAAc,cAAc,CAAC;AAE7B;;;;GAIG;AACH,cAAc,oBAAoB,CAAC;AAEnC;;;;;GAKG;AACH,wBAAgB,qBAAqB,IAAI,IAAI,CAC5C;AACD,cAAc,0BAA0B,CAAC;AAEzC,wFAAwF;AACxF,cAAc,qCAAqC,CAAC"}
package/dist/index.js CHANGED
@@ -34,6 +34,12 @@ export * from './term-start.js';
34
34
  * form cannot offer a field the server will refuse. Pinned by integration check CD14.
35
35
  */
36
36
  export * from './close-lock.js';
37
+ /**
38
+ * Whether `Deal.Amount` still matches the order it was cached from — shared by the Deal form and the
39
+ * deal hero so the two surfaces cannot answer the same question differently. A comparison of two
40
+ * stored figures, never a computation. See the file header for why it is no longer a timestamp test.
41
+ */
42
+ export * from './amount-freshness.js';
37
43
  /**
38
44
  * Forces the generated entity subclasses to be loaded. Without an explicit
39
45
  * import + call, tree-shaking drops the generated entities because they are not
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,+BAA+B,CAAC;AAE9C;;;;GAIG;AACH,cAAc,+BAA+B,CAAC;AAE9C;;;;;;;;;GASG;AACH,cAAc,eAAe,CAAC;AAE9B;;;;GAIG;AACH,cAAc,oBAAoB,CAAC;AAEnC;;;;GAIG;AACH,cAAc,kBAAkB,CAAC;AACjC,cAAc,cAAc,CAAC;AAE7B;;;GAGG;AACH,cAAc,cAAc,CAAC;AAE7B;;;;;GAKG;AACH,MAAM,UAAU,qBAAqB;AACrC,CAAC;AACD,cAAc,0BAA0B,CAAC;AAEzC,wFAAwF;AACxF,cAAc,qCAAqC,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,+BAA+B,CAAC;AAE9C;;;;GAIG;AACH,cAAc,+BAA+B,CAAC;AAE9C;;;;;;;;;GASG;AACH,cAAc,eAAe,CAAC;AAE9B;;;;GAIG;AACH,cAAc,oBAAoB,CAAC;AAEnC;;;;GAIG;AACH,cAAc,kBAAkB,CAAC;AACjC,cAAc,cAAc,CAAC;AAE7B;;;GAGG;AACH,cAAc,cAAc,CAAC;AAE7B;;;;GAIG;AACH,cAAc,oBAAoB,CAAC;AAEnC;;;;;GAKG;AACH,MAAM,UAAU,qBAAqB;AACrC,CAAC;AACD,cAAc,0BAA0B,CAAC;AAEzC,wFAAwF;AACxF,cAAc,qCAAqC,CAAC"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@mj-biz-apps/sales-entities",
3
3
  "type": "module",
4
- "version": "6.3.3",
4
+ "version": "6.5.0",
5
5
  "description": "MJ BizApps Sales Entity Subclasses",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",