@mj-biz-apps/sales-core-entities-server 5.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (106) hide show
  1. package/LICENSE +183 -0
  2. package/dist/CloseDealOperation.d.ts +176 -0
  3. package/dist/CloseDealOperation.d.ts.map +1 -0
  4. package/dist/CloseDealOperation.js +1107 -0
  5. package/dist/CloseDealOperation.js.map +1 -0
  6. package/dist/CloseWonTaskService.d.ts +249 -0
  7. package/dist/CloseWonTaskService.d.ts.map +1 -0
  8. package/dist/CloseWonTaskService.js +460 -0
  9. package/dist/CloseWonTaskService.js.map +1 -0
  10. package/dist/DealEntityServer.d.ts +625 -0
  11. package/dist/DealEntityServer.d.ts.map +1 -0
  12. package/dist/DealEntityServer.js +1529 -0
  13. package/dist/DealEntityServer.js.map +1 -0
  14. package/dist/LiveContractsSeam.d.ts +212 -0
  15. package/dist/LiveContractsSeam.d.ts.map +1 -0
  16. package/dist/LiveContractsSeam.js +362 -0
  17. package/dist/LiveContractsSeam.js.map +1 -0
  18. package/dist/LiveOrdersSeam.d.ts +68 -0
  19. package/dist/LiveOrdersSeam.d.ts.map +1 -0
  20. package/dist/LiveOrdersSeam.js +80 -0
  21. package/dist/LiveOrdersSeam.js.map +1 -0
  22. package/dist/SaveDealOperation.d.ts +45 -0
  23. package/dist/SaveDealOperation.d.ts.map +1 -0
  24. package/dist/SaveDealOperation.js +268 -0
  25. package/dist/SaveDealOperation.js.map +1 -0
  26. package/dist/SequenceService.d.ts +39 -0
  27. package/dist/SequenceService.d.ts.map +1 -0
  28. package/dist/SequenceService.js +44 -0
  29. package/dist/SequenceService.js.map +1 -0
  30. package/dist/activities/ActivityIngestService.d.ts +111 -0
  31. package/dist/activities/ActivityIngestService.d.ts.map +1 -0
  32. package/dist/activities/ActivityIngestService.js +485 -0
  33. package/dist/activities/ActivityIngestService.js.map +1 -0
  34. package/dist/activities/ActivityReader.d.ts +68 -0
  35. package/dist/activities/ActivityReader.d.ts.map +1 -0
  36. package/dist/activities/ActivityReader.js +147 -0
  37. package/dist/activities/ActivityReader.js.map +1 -0
  38. package/dist/activities/ActivitySource.d.ts +142 -0
  39. package/dist/activities/ActivitySource.d.ts.map +1 -0
  40. package/dist/activities/ActivitySource.js +2 -0
  41. package/dist/activities/ActivitySource.js.map +1 -0
  42. package/dist/activities/ActivitySyncJob.d.ts +72 -0
  43. package/dist/activities/ActivitySyncJob.d.ts.map +1 -0
  44. package/dist/activities/ActivitySyncJob.js +146 -0
  45. package/dist/activities/ActivitySyncJob.js.map +1 -0
  46. package/dist/activities/ActivityWriterService.d.ts +119 -0
  47. package/dist/activities/ActivityWriterService.d.ts.map +1 -0
  48. package/dist/activities/ActivityWriterService.js +270 -0
  49. package/dist/activities/ActivityWriterService.js.map +1 -0
  50. package/dist/activities/DealMatcher.d.ts +58 -0
  51. package/dist/activities/DealMatcher.d.ts.map +1 -0
  52. package/dist/activities/DealMatcher.js +109 -0
  53. package/dist/activities/DealMatcher.js.map +1 -0
  54. package/dist/activities/FixtureActivitySource.d.ts +37 -0
  55. package/dist/activities/FixtureActivitySource.d.ts.map +1 -0
  56. package/dist/activities/FixtureActivitySource.js +58 -0
  57. package/dist/activities/FixtureActivitySource.js.map +1 -0
  58. package/dist/activities/GraphMessageMapper.d.ts +47 -0
  59. package/dist/activities/GraphMessageMapper.d.ts.map +1 -0
  60. package/dist/activities/GraphMessageMapper.js +141 -0
  61. package/dist/activities/GraphMessageMapper.js.map +1 -0
  62. package/dist/activities/ImportedGraphActivitySource.d.ts +16 -0
  63. package/dist/activities/ImportedGraphActivitySource.d.ts.map +1 -0
  64. package/dist/activities/ImportedGraphActivitySource.js +84 -0
  65. package/dist/activities/ImportedGraphActivitySource.js.map +1 -0
  66. package/dist/activities/MSGraphActivitySource.d.ts +67 -0
  67. package/dist/activities/MSGraphActivitySource.d.ts.map +1 -0
  68. package/dist/activities/MSGraphActivitySource.js +186 -0
  69. package/dist/activities/MSGraphActivitySource.js.map +1 -0
  70. package/dist/activities/MSGraphCalendarSource.d.ts +72 -0
  71. package/dist/activities/MSGraphCalendarSource.d.ts.map +1 -0
  72. package/dist/activities/MSGraphCalendarSource.js +225 -0
  73. package/dist/activities/MSGraphCalendarSource.js.map +1 -0
  74. package/dist/activities/RelevanceFilter.d.ts +101 -0
  75. package/dist/activities/RelevanceFilter.d.ts.map +1 -0
  76. package/dist/activities/RelevanceFilter.js +148 -0
  77. package/dist/activities/RelevanceFilter.js.map +1 -0
  78. package/dist/activities/activity-vocabulary.d.ts +103 -0
  79. package/dist/activities/activity-vocabulary.d.ts.map +1 -0
  80. package/dist/activities/activity-vocabulary.js +79 -0
  81. package/dist/activities/activity-vocabulary.js.map +1 -0
  82. package/dist/forecast/FixtureForecastSource.d.ts +22 -0
  83. package/dist/forecast/FixtureForecastSource.d.ts.map +1 -0
  84. package/dist/forecast/FixtureForecastSource.js +16 -0
  85. package/dist/forecast/FixtureForecastSource.js.map +1 -0
  86. package/dist/forecast/ForecastSnapshotJob.d.ts +78 -0
  87. package/dist/forecast/ForecastSnapshotJob.d.ts.map +1 -0
  88. package/dist/forecast/ForecastSnapshotJob.js +225 -0
  89. package/dist/forecast/ForecastSnapshotJob.js.map +1 -0
  90. package/dist/forecast/ForecastSource.d.ts +74 -0
  91. package/dist/forecast/ForecastSource.d.ts.map +1 -0
  92. package/dist/forecast/ForecastSource.js +25 -0
  93. package/dist/forecast/ForecastSource.js.map +1 -0
  94. package/dist/forecast/QueryForecastSource.d.ts +76 -0
  95. package/dist/forecast/QueryForecastSource.d.ts.map +1 -0
  96. package/dist/forecast/QueryForecastSource.js +164 -0
  97. package/dist/forecast/QueryForecastSource.js.map +1 -0
  98. package/dist/index.d.ts +117 -0
  99. package/dist/index.d.ts.map +1 -0
  100. package/dist/index.js +125 -0
  101. package/dist/index.js.map +1 -0
  102. package/dist/orders-availability.d.ts +15 -0
  103. package/dist/orders-availability.d.ts.map +1 -0
  104. package/dist/orders-availability.js +47 -0
  105. package/dist/orders-availability.js.map +1 -0
  106. package/package.json +43 -0
@@ -0,0 +1,460 @@
1
+ /**
2
+ * @fileoverview The finance tasks a won deal raises — S-US2 (#34) and S-US3 (#35).
3
+ *
4
+ * On Closed Won both pipelines raise an **order-review** task against the deal's embedded order, and a
5
+ * B2B deal additionally raises a **contract-processing** task. Finance reviews the order, corrects it and
6
+ * advances it; that advance is what triggers invoicing, so the task is the control on money leaving the
7
+ * building — not a reminder.
8
+ *
9
+ * ── WHAT THIS SERVICE DOES NOT DECIDE ───────────────────────────────────────────────────────────────
10
+ *
11
+ * It does not decide WHICH task type, WHICH role, or WHO finance is. Every one of those is a row in
12
+ * somebody's table, and this app's second rule is that vocabulary is data: the engine branches on flags,
13
+ * never on names. `TaskType` and `TaskRole` carry **no `Code` and no behaviour flag** — only `Name` — so
14
+ * there is no vocabulary-safe way to look up "the order-review type" from in here. Asking the caller for
15
+ * the IDs is the honest resolution: it keeps the name comparison out of the engine entirely rather than
16
+ * hiding one behind a helper.
17
+ *
18
+ * The one thing it does decide is whether the contract task is raised, and that comes from the pipeline's
19
+ * `CloseWonPolicy.CreateContract` **flag** — the same policy the close operation already reads. A B2B
20
+ * pipeline is not "the one called B2B"; it is the one whose policy says it creates a contract.
21
+ *
22
+ * ── IT IS NOT WIRED INTO THE CLOSE ──────────────────────────────────────────────────────────────────
23
+ *
24
+ * Deliberately. `Sales.CloseDeal` is mid-rework for the embedded-order redesign, and wiring a new call
25
+ * into a file being restructured is how two changes become one debugging session. The caller passes IDs;
26
+ * this service creates records. Wiring lands after that rework.
27
+ *
28
+ * @module @mj-biz-apps/sales-core-entities-server
29
+ */
30
+ import { LogError, LogStatus, RunView, } from '@memberjunction/core';
31
+ import { TaskAssignmentService, TaskOrchestrationService } from '@mj-biz-apps/tasks-core';
32
+ const E_PIPELINE = 'MJ_BizApps_Sales: Pipelines';
33
+ const E_TASK_LINK = 'MJ_BizApps_Tasks: Task Links';
34
+ const E_CONTRACT = 'MJ_BizApps_Contracts: Contracts';
35
+ const E_DEAL = 'MJ_BizApps_Sales: Deals';
36
+ const E_TASK_TYPE = 'MJ_BizApps_Tasks: Task Types';
37
+ /**
38
+ * The two task types sales owns, addressed by CODE.
39
+ *
40
+ * These are not configuration and no deployment varies them. The rows live in this repo, under
41
+ * `metadata/task-types/`, on Amith's ruling that tasks owns the SHAPE while the app that raises a
42
+ * kind of work owns the ROW -- so a code is simply this service naming its own vocabulary.
43
+ *
44
+ * -- WHY A CODE AND NOT AN ID --
45
+ *
46
+ * These used to arrive as UUIDs on `Pipeline.CloseWonPolicy`, which was the best available handle
47
+ * before bizapps-tasks PR #42 added `TaskType.Code` (NOT NULL, UNIQUE) on 2026-08-20. It made every
48
+ * deployment restate a fact identical everywhere, and routed a fixed UUID through configuration to
49
+ * reach code that only ever wanted the order-review one. Worse, a database missing the seed and a
50
+ * policy missing the key were indistinguishable at this end.
51
+ *
52
+ * -- AND WHY THIS IS NOT A VOCABULARY COMPARISON --
53
+ *
54
+ * Nothing branches on these. They are looked up, they yield an ID, and no behaviour anywhere asks
55
+ * which type a task has -- the contract task is still decided by `CloseWonPolicy.CreateContract`, a
56
+ * flag. A code is a stable identifier that survives renaming the display Name, which is the property
57
+ * a NAME comparison lacks and the whole reason the column exists.
58
+ */
59
+ const CODE_ORDER_REVIEW = 'ORDER_REVIEW';
60
+ const CODE_CONTRACT_PROCESSING = 'CONTRACT_PROCESSING';
61
+ /**
62
+ * The display Names paired with those codes, used ONLY by the pre-PR-#42 fallback in
63
+ * `resolveTaskType`. Deliberately a lookup beside the codes rather than a string inline at the call
64
+ * site: when the fallback is deleted, this goes with it and nothing is left holding a name.
65
+ */
66
+ const TASK_TYPE_NAMES = {
67
+ [CODE_ORDER_REVIEW]: 'Order Review',
68
+ [CODE_CONTRACT_PROCESSING]: 'Contract Processing',
69
+ };
70
+ /**
71
+ * Reads the task configuration out of a `CloseWonPolicy`, tolerating every absent shape.
72
+ *
73
+ * Exported so the close operation can map policy to service input in one line rather than reaching
74
+ * into the JSON itself — the shape belongs with the service that consumes it.
75
+ */
76
+ /**
77
+ * WHICH COLUMN THE TASK-TYPE LOOKUP WILL USE ON THIS HOST.
78
+ *
79
+ * Exported so the choice can be ASSERTED rather than inferred. The fallback announces itself with a
80
+ * `LogStatus`, and a log line is not an observable a check can fail on -- the same gap that let the
81
+ * D-19 collation check stay green while the fix it guarded was reverted.
82
+ *
83
+ * Reads ENTITY METADATA, not the physical column: `RunView` builds its select from `EntityField` rows,
84
+ * so a database that has the column while metadata does not would still fail a `Code` filter. Asking the
85
+ * same source the query is built from is the only probe that cannot disagree with it.
86
+ */
87
+ export function ResolveTaskTypeColumn(provider) {
88
+ const info = provider.Entities.find((e) => e.Name === E_TASK_TYPE);
89
+ return info?.Fields?.some((f) => f.Name === 'Code') === true ? 'Code' : 'Name';
90
+ }
91
+ /**
92
+ * How long a close-won task gets by default.
93
+ *
94
+ * Five days rather than one or thirty: the order review gates the invoice, so it cannot sit for a month,
95
+ * and finance is not on call, so it cannot be due in an hour. It is a default precisely because it is
96
+ * arguable — `DueInDays` in the policy is how a deployment disagrees.
97
+ */
98
+ export const DEFAULT_DUE_IN_DAYS = 5;
99
+ /**
100
+ * The due date for work created by a close, in UTC.
101
+ *
102
+ * ── THE ARITHMETIC IS DELIBERATELY DONE THIS WAY, AND THERE IS A CAUTIONARY TALE ────────────────
103
+ *
104
+ * `Date.UTC` with an out-of-range day is the whole trick: `Date.UTC(2026, 0, 30 + 5)` is 4 February, and
105
+ * month-end, month-length and year rollover are the platform's problem rather than this function's. The
106
+ * alternative — adding milliseconds, or building a string — is where day arithmetic goes wrong.
107
+ *
108
+ * It goes wrong quietly. Orders' own `GetOverdueWorklistOperation` carried broken day arithmetic for
109
+ * MONTHS: it reported an order one month overdue as **46,264 days** overdue, and nobody noticed, because
110
+ * `DueDate` was NULL on every order in the system so the code path never ran on real data. That is the
111
+ * same shape as the bug being fixed here — a due-date feature made inert by a null — and it is the reason
112
+ * this returns a date at all rather than leaving the column empty and the feature untested.
113
+ *
114
+ * Truncated to UTC midnight, like `Deal.ActualCloseDate`: a due DATE is a day, and stamping 23:47 makes
115
+ * "due today" depend on the reader's timezone.
116
+ */
117
+ export function CloseWonTaskDueAt(closedAt, dueInDays) {
118
+ const days = Number.isFinite(dueInDays) ? Number(dueInDays) : DEFAULT_DUE_IN_DAYS;
119
+ return new Date(Date.UTC(closedAt.getUTCFullYear(), closedAt.getUTCMonth(), closedAt.getUTCDate() + days));
120
+ }
121
+ export function ReadCloseWonTaskConfig(policy) {
122
+ const holder = policy;
123
+ return holder?.CloseWonTasks ?? {};
124
+ }
125
+ export class CloseWonTaskService {
126
+ constructor() {
127
+ this.orchestration = new TaskOrchestrationService();
128
+ this.assignments = new TaskAssignmentService();
129
+ }
130
+ /**
131
+ * Raises the tasks a won deal owes, and reports what it raised.
132
+ *
133
+ * Order-review always; contract-processing when the pipeline's policy says so. Never throws for a
134
+ * business reason — a caller mid-close wants a report it can fold into its own, not an exception that
135
+ * loses the task it did manage to create.
136
+ */
137
+ async CreateCloseWonTasks(input, provider, contextUser) {
138
+ const result = { Success: true, Tasks: [], Issues: [] };
139
+ /**
140
+ * NO ORDER, NO ORDER-REVIEW TASK. The order is embedded on the deal from creation, so an empty
141
+ * OrderID means something upstream did not happen — and a task pointing at nothing is a work
142
+ * item finance opens to find an empty page. Report it and create nothing.
143
+ */
144
+ if (!input.OrderID) {
145
+ result.Issues.push('The deal has no order, so no order-review task was created. The order is created with the '
146
+ + 'deal, so this means the deal predates that or its order was never written.');
147
+ result.Success = false;
148
+ }
149
+ const orderReviewTypeID = input.OrderID
150
+ ? await this.resolveTaskType(CODE_ORDER_REVIEW, provider, contextUser, result)
151
+ : null;
152
+ const orderReview = (input.OrderID && orderReviewTypeID) ? await this.raise('OrderReview', {
153
+ Name: `Review order for ${input.DealName}`,
154
+ TypeID: orderReviewTypeID,
155
+ Description: 'Review the order for accuracy, correct it if needed, then advance it. Advancing is '
156
+ + 'the confirm that locks the order, books the journal entries and triggers invoicing.',
157
+ },
158
+ // The order is what this task is ABOUT; the deal is how a rep finds it.
159
+ [
160
+ { EntityName: 'MJ_BizApps_Orders: Order Headers', RecordID: input.OrderID },
161
+ { EntityName: E_DEAL, RecordID: input.DealID },
162
+ ], input, provider, contextUser, result) : null;
163
+ if (orderReview) {
164
+ result.Tasks.push(orderReview);
165
+ }
166
+ if (await this.policyRaisesContractTask(input.PipelineID, contextUser, result)) {
167
+ const contractTypeID = await this.resolveTaskType(CODE_CONTRACT_PROCESSING, provider, contextUser, result);
168
+ if (contractTypeID) {
169
+ /**
170
+ * THE FALLBACK IS DELIBERATE, not defensive padding, and it has TWO triggers.
171
+ *
172
+ * Contract creation is a separate story and may not have produced a row — and on a host
173
+ * without bizapps-contracts installed there is no contracts entity to point a link at,
174
+ * whatever ID the caller holds. Either way, linking to the deal keeps the task reachable
175
+ * from the thing that caused it. An UNLINKED task is the outcome worth avoiding: finance
176
+ * gets a work item with nothing to open, which is how a task becomes noise.
177
+ *
178
+ * The result object reports the target it actually used, and the second trigger also
179
+ * records an issue — a silent relink would look identical to a correct one.
180
+ */
181
+ const contractsAvailable = !!provider.Entities.find((e) => e.Name === E_CONTRACT);
182
+ let target = { EntityName: E_DEAL, RecordID: input.DealID };
183
+ if (input.ContractID) {
184
+ if (contractsAvailable) {
185
+ target = { EntityName: E_CONTRACT, RecordID: input.ContractID };
186
+ }
187
+ else {
188
+ result.Issues.push('A contract was supplied but bizapps-contracts is not installed on this host, so '
189
+ + 'the contract task was linked to the deal instead.');
190
+ }
191
+ }
192
+ const contractTask = await this.raise('ContractProcessing', {
193
+ Name: `Process contract for ${input.DealName}`,
194
+ TypeID: contractTypeID,
195
+ Description: 'Attach the executed PDF, confirm the agreement version against the signed '
196
+ + 'document, record the executed/effective/end dates, validate whether the '
197
+ + 'template was modified and capture any deviations, then move the contract to '
198
+ + 'its active status.',
199
+ },
200
+ /**
201
+ * The contract (or the deal, per the fallback above) is what this task is ABOUT, and the
202
+ * deal is always appended so the work is reachable from it. When the fallback already
203
+ * chose the deal these collapse to one link -- `raise` de-duplicates rather than
204
+ * showing finance the same record twice.
205
+ */
206
+ [target, { EntityName: E_DEAL, RecordID: input.DealID }], input, provider, contextUser, result);
207
+ if (contractTask) {
208
+ result.Tasks.push(contractTask);
209
+ }
210
+ }
211
+ }
212
+ return result;
213
+ }
214
+ /* ── internals ───────────────────────────────────────────────────────────── */
215
+ /** Creates one task, links it and routes it. Returns null when the task itself could not be created. */
216
+ /**
217
+ * The `TaskType` row for a code, or null with an issue explaining why not.
218
+ *
219
+ * -- THE PROBE, AND WHY IT IS NOT PADDING --
220
+ *
221
+ * `TaskType.Code` arrived in bizapps-tasks PR #42, merged 2026-08-20. A database migrated before
222
+ * that does not have the column, and a filter naming a column that does not exist does not return
223
+ * nothing -- it fails the whole query. Verified: neither local host has the migration applied.
224
+ *
225
+ * So this asks the metadata which identifier tasks actually offers. With `Code` present it matches
226
+ * on `Code`, which is the point of the exercise. Without it, it matches on `Name` -- safe here in a
227
+ * way it would not be in general, because these two rows are seeded by THIS repo under fixed UUIDs,
228
+ * so their names are ours and change only when we change them.
229
+ *
230
+ * When every host has the column the `else` goes, and nothing else in this file moves.
231
+ */
232
+ async resolveTaskType(code, provider, contextUser, result) {
233
+ const info = provider.Entities.find((e) => e.Name === E_TASK_TYPE);
234
+ if (!info) {
235
+ result.Issues.push(`bizapps-tasks is not installed on this host, so the ${code} task could not be created.`);
236
+ result.Success = false;
237
+ return null;
238
+ }
239
+ const column = ResolveTaskTypeColumn(provider);
240
+ const hasCode = column === 'Code';
241
+ const value = hasCode ? code : TASK_TYPE_NAMES[code];
242
+ if (!hasCode) {
243
+ /**
244
+ * LOGGED, NOT RAISED AS AN ISSUE -- and the distinction matters more than it looks.
245
+ *
246
+ * This was an Issue first, and WT10 caught what that actually meant: every close on an
247
+ * unmigrated host reports two warnings about a pending migration to the person closing a
248
+ * deal, who can do nothing about it. An issue list that is never empty is an issue list
249
+ * nobody reads, and the close would have been training people to ignore it.
250
+ *
251
+ * It is a property of the HOST, not of this deal, so it goes where host facts go. The
252
+ * fallback stays observable where it should be: WT1 and WT6 run against a database with no
253
+ * Code column and assert the right type is still found.
254
+ */
255
+ LogStatus(`CloseWonTaskService: this host predates bizapps-tasks PR #42, so TaskType has no Code `
256
+ + `column and ${code} was matched on its Name. Apply the tasks migration.`);
257
+ }
258
+ const r = await new RunView().RunView({
259
+ EntityName: E_TASK_TYPE,
260
+ ExtraFilter: `${column} = '${String(value).replace(/'/g, "''")}'`,
261
+ ResultType: 'simple',
262
+ Fields: ['ID'],
263
+ }, contextUser);
264
+ if (!r?.Success) {
265
+ result.Issues.push(`Could not read task types: ${r?.ErrorMessage ?? 'unknown error'}.`);
266
+ result.Success = false;
267
+ return null;
268
+ }
269
+ const id = (r.Results ?? [])[0]?.ID ?? null;
270
+ if (!id) {
271
+ /**
272
+ * A MISSING SEED, NAMED AS SUCH. The old code could only say the configuration key was
273
+ * unset, which was the same message whether a deployment had forgotten a policy field or
274
+ * had never pushed the metadata at all. This says which, and where the row comes from.
275
+ */
276
+ result.Issues.push(`No task type with ${column} '${value}' exists, so that task was not created. The row is `
277
+ + `seeded from this repo: run \`mj sync push --dir metadata\` against this database.`);
278
+ result.Success = false;
279
+ }
280
+ return id;
281
+ }
282
+ async raise(kind, task,
283
+ /**
284
+ * Every record this task should be reachable from, PRIMARY FIRST.
285
+ *
286
+ * It used to be a single target, which is how both tasks ended up unreachable from the deal that
287
+ * caused them: one pointed at the order header, the other at the contract, and neither at the
288
+ * deal. From a deal you could not find the work its own close had raised — which is the first
289
+ * navigation a rep tries.
290
+ *
291
+ * The primary is still what the result reports, so `LinkedEntityName` / `LinkedRecordID` keep
292
+ * their meaning: the record this task is fundamentally ABOUT. The deal is an additional way in,
293
+ * not a reclassification.
294
+ */
295
+ targets, input, provider, contextUser, result) {
296
+ let taskID;
297
+ try {
298
+ // Through tasks' OWN orchestration service rather than a bare entity save: it is that app's
299
+ // supported entry point, and going around it would mean re-deciding defaults it already owns.
300
+ const created = await this.orchestration.CreateTask({
301
+ Name: task.Name,
302
+ TypeID: task.TypeID,
303
+ Description: task.Description,
304
+ DueAt: input.DueAt,
305
+ CreatedByPersonID: input.AssignedByPersonID,
306
+ }, contextUser);
307
+ taskID = created.ID;
308
+ }
309
+ catch (err) {
310
+ result.Issues.push(`The ${kind} task could not be created: ${String(err)}`);
311
+ result.Success = false;
312
+ return null;
313
+ }
314
+ /**
315
+ * DE-DUPLICATED, because the contract task's own fallback already targets the deal when there is no
316
+ * contract to point at. Linking the same record twice would put two identical rows in front of
317
+ * finance and make the task look like two pieces of work.
318
+ */
319
+ const unique = targets.filter((t, i) => targets.findIndex((o) => o.EntityName === t.EntityName
320
+ && o.RecordID.toLowerCase() === t.RecordID.toLowerCase()) === i);
321
+ const primary = unique[0];
322
+ /**
323
+ * The PRIMARY link decides whether the task is usable at all; a secondary that fails leaves the
324
+ * task reachable and is reported rather than fatal. `link` already pushes an issue for each
325
+ * failure, so a partial outcome is visible without inventing a second channel for it.
326
+ */
327
+ let linked = false;
328
+ for (const [index, t] of unique.entries()) {
329
+ const ok = await this.link(taskID, t, provider, contextUser, result, kind);
330
+ if (index === 0) {
331
+ linked = ok;
332
+ }
333
+ }
334
+ if (!linked) {
335
+ result.Success = false;
336
+ }
337
+ const assignmentID = await this.route(taskID, input, provider, contextUser, result, kind);
338
+ if (!assignmentID) {
339
+ result.Success = false;
340
+ }
341
+ return {
342
+ Kind: kind,
343
+ TaskID: taskID,
344
+ LinkedEntityName: primary.EntityName,
345
+ LinkedRecordID: primary.RecordID,
346
+ AssignmentID: assignmentID,
347
+ };
348
+ }
349
+ /**
350
+ * Hangs the task off its record.
351
+ *
352
+ * `TaskLink` is polymorphic — an `EntityID`/`RecordID` pair — so the entity has to be resolved from
353
+ * metadata. An unresolvable name means the sibling app is not installed on this host, and that is
354
+ * reported rather than thrown: the task exists and is still useful without the link.
355
+ */
356
+ async link(taskID, target, provider, contextUser, result, kind) {
357
+ const entity = provider.Entities.find((e) => e.Name === target.EntityName);
358
+ if (!entity) {
359
+ result.Issues.push(`The ${kind} task was created but could not be linked: entity '${target.EntityName}' is `
360
+ + 'not registered on this host.');
361
+ return false;
362
+ }
363
+ /**
364
+ * THE INJECTED PROVIDER, NOT `new Metadata()` — and this was a real hole in the transaction
365
+ * proof, not a style point.
366
+ *
367
+ * `WT9` shows task writes joining the caller's transaction, and it holds only where
368
+ * `Metadata.Provider` IS the injected object. `fixture.ts` documents the counter-case: under
369
+ * `mj test` the CLI installs its own instrumented cache first, so the global and the injected
370
+ * provider are different objects. On such a host this insert landed on a different connection
371
+ * and SURVIVED the close's rollback — a TaskLink left pointing at a deal that was never closed,
372
+ * and WT9 would still have been green because it reads through the same global.
373
+ *
374
+ * `DealEntityServer` uses `this.ProviderToUse` throughout for exactly this reason; the pattern
375
+ * was already in the repo.
376
+ */
377
+ const link = await provider.GetEntityObject(E_TASK_LINK, contextUser);
378
+ link.NewRecord();
379
+ link.TaskID = taskID;
380
+ link.EntityID = entity.ID;
381
+ link.RecordID = target.RecordID;
382
+ if (!(await link.Save())) {
383
+ const why = link.LatestResult?.CompleteMessage ?? 'unknown error';
384
+ LogError(`CloseWonTaskService: link failed for task ${taskID}: ${why}`);
385
+ result.Issues.push(`The ${kind} task was created but could not be linked: ${why}`);
386
+ return false;
387
+ }
388
+ return true;
389
+ }
390
+ /** Routes the task to its assignee, through tasks' own service so the activity log gets its entry. */
391
+ async route(taskID, input, provider, contextUser, result, kind) {
392
+ if (!input.Assignee?.RecordID || !input.Assignee?.EntityName) {
393
+ result.Issues.push(`The ${kind} task was created but NOT routed: no finance assignee is configured on the `
394
+ + 'pipeline\'s CloseWonPolicy (CloseWonTasks.AssigneeRecordID).');
395
+ return null;
396
+ }
397
+ /**
398
+ * Also the injected provider. A read rather than a write, so the stakes are lower — but an
399
+ * instrumented cache can carry a different entity registry, and resolving an EntityID from one
400
+ * provider to write it through another is how an assignment ends up pointing at the wrong
401
+ * entity on the one host where it matters.
402
+ */
403
+ const assigneeEntity = provider.Entities.find((e) => e.Name === input.Assignee.EntityName);
404
+ if (!assigneeEntity) {
405
+ result.Issues.push(`The ${kind} task was created but could not be routed: entity `
406
+ + `'${input.Assignee.EntityName}' is not registered on this host.`);
407
+ return null;
408
+ }
409
+ try {
410
+ const assignment = await this.assignments.assignToTask({
411
+ taskID,
412
+ assigneeEntityID: assigneeEntity.ID,
413
+ assigneeRecordID: input.Assignee.RecordID,
414
+ roleID: input.Assignee.RoleID,
415
+ assignedByPersonID: input.AssignedByPersonID,
416
+ }, contextUser);
417
+ return assignment.Get('ID');
418
+ }
419
+ catch (err) {
420
+ result.Issues.push(`The ${kind} task was created but could not be routed: ${String(err)}`);
421
+ return null;
422
+ }
423
+ }
424
+ /**
425
+ * Whether this pipeline's policy raises a contract task.
426
+ *
427
+ * `CloseWonPolicy.CreateContract` is a FLAG on a JSON policy column — the same one the close operation
428
+ * branches on. Reading the flag rather than the pipeline's name is what lets a deployment rename its
429
+ * pipelines, or add a third, without touching this file.
430
+ */
431
+ async policyRaisesContractTask(pipelineID, contextUser, result) {
432
+ const rv = new RunView();
433
+ const view = await rv.RunView({
434
+ EntityName: E_PIPELINE,
435
+ ExtraFilter: `ID = '${pipelineID}'`,
436
+ ResultType: 'simple',
437
+ Fields: ['CloseWonPolicy'],
438
+ }, contextUser);
439
+ if (!view.Success) {
440
+ result.Issues.push(`The pipeline's policy could not be read: ${view.ErrorMessage}`);
441
+ result.Success = false;
442
+ return false;
443
+ }
444
+ const raw = (view.Results ?? [])[0]?.CloseWonPolicy;
445
+ if (!raw) {
446
+ // No policy is a legitimate state — it means the pipeline raises no contract, not an error.
447
+ return false;
448
+ }
449
+ try {
450
+ const policy = JSON.parse(raw);
451
+ return policy.CreateContract === true;
452
+ }
453
+ catch (err) {
454
+ result.Issues.push(`The pipeline's CloseWonPolicy is not valid JSON: ${String(err)}`);
455
+ result.Success = false;
456
+ return false;
457
+ }
458
+ }
459
+ }
460
+ //# sourceMappingURL=CloseWonTaskService.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CloseWonTaskService.js","sourceRoot":"","sources":["../src/CloseWonTaskService.ts"],"names":[],"mappings":"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"}