@mj-biz-apps/orders-entities 0.0.1 → 5.2.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 (115) hide show
  1. package/LICENSE +183 -0
  2. package/README.md +50 -2
  3. package/dist/CustomerPaymentMethodEntity.d.ts +4 -0
  4. package/dist/CustomerPaymentMethodEntity.d.ts.map +1 -0
  5. package/dist/CustomerPaymentMethodEntity.js +26 -0
  6. package/dist/CustomerPaymentMethodEntity.js.map +1 -0
  7. package/dist/InitialPaymentIntentCompanion.d.ts +29 -0
  8. package/dist/InitialPaymentIntentCompanion.d.ts.map +1 -0
  9. package/dist/InitialPaymentIntentCompanion.js +40 -0
  10. package/dist/InitialPaymentIntentCompanion.js.map +1 -0
  11. package/dist/OrderHeaderEntity.d.ts +224 -0
  12. package/dist/OrderHeaderEntity.d.ts.map +1 -0
  13. package/dist/OrderHeaderEntity.js +509 -0
  14. package/dist/OrderHeaderEntity.js.map +1 -0
  15. package/dist/OrderLineEntity.d.ts +25 -0
  16. package/dist/OrderLineEntity.d.ts.map +1 -0
  17. package/dist/OrderLineEntity.js +54 -0
  18. package/dist/OrderLineEntity.js.map +1 -0
  19. package/dist/OrderLineExtensionCompanion.d.ts +54 -0
  20. package/dist/OrderLineExtensionCompanion.d.ts.map +1 -0
  21. package/dist/OrderLineExtensionCompanion.js +167 -0
  22. package/dist/OrderLineExtensionCompanion.js.map +1 -0
  23. package/dist/OrderStatusBehavior.d.ts +86 -0
  24. package/dist/OrderStatusBehavior.d.ts.map +1 -0
  25. package/dist/OrderStatusBehavior.js +160 -0
  26. package/dist/OrderStatusBehavior.js.map +1 -0
  27. package/dist/PartyAffiliationBehavior.d.ts +20 -0
  28. package/dist/PartyAffiliationBehavior.d.ts.map +1 -0
  29. package/dist/PartyAffiliationBehavior.js +52 -0
  30. package/dist/PartyAffiliationBehavior.js.map +1 -0
  31. package/dist/PaymentHeaderEntity.d.ts +18 -0
  32. package/dist/PaymentHeaderEntity.d.ts.map +1 -0
  33. package/dist/PaymentHeaderEntity.js +80 -0
  34. package/dist/PaymentHeaderEntity.js.map +1 -0
  35. package/dist/PricingDriverResolver.d.ts +70 -0
  36. package/dist/PricingDriverResolver.d.ts.map +1 -0
  37. package/dist/PricingDriverResolver.js +145 -0
  38. package/dist/PricingDriverResolver.js.map +1 -0
  39. package/dist/PromotionCodesCompanion.d.ts +77 -0
  40. package/dist/PromotionCodesCompanion.d.ts.map +1 -0
  41. package/dist/PromotionCodesCompanion.js +111 -0
  42. package/dist/PromotionCodesCompanion.js.map +1 -0
  43. package/dist/booked-money.d.ts +27 -0
  44. package/dist/booked-money.d.ts.map +1 -0
  45. package/dist/booked-money.js +67 -0
  46. package/dist/booked-money.js.map +1 -0
  47. package/dist/configuration-types.d.ts +157 -0
  48. package/dist/configuration-types.d.ts.map +1 -0
  49. package/dist/configuration-types.js +10 -0
  50. package/dist/configuration-types.js.map +1 -0
  51. package/dist/date-cell.d.ts +109 -0
  52. package/dist/date-cell.d.ts.map +1 -0
  53. package/dist/date-cell.js +131 -0
  54. package/dist/date-cell.js.map +1 -0
  55. package/dist/generated/entity_subclasses.d.ts +12055 -0
  56. package/dist/generated/entity_subclasses.d.ts.map +1 -0
  57. package/dist/generated/entity_subclasses.js +17800 -0
  58. package/dist/generated/entity_subclasses.js.map +1 -0
  59. package/dist/generated/remote_operations.d.ts +2365 -0
  60. package/dist/generated/remote_operations.d.ts.map +1 -0
  61. package/dist/generated/remote_operations.js +768 -0
  62. package/dist/generated/remote_operations.js.map +1 -0
  63. package/dist/index.d.ts +102 -0
  64. package/dist/index.d.ts.map +1 -0
  65. package/dist/index.js +103 -0
  66. package/dist/index.js.map +1 -0
  67. package/dist/line-quantity.d.ts +6 -0
  68. package/dist/line-quantity.d.ts.map +1 -0
  69. package/dist/line-quantity.js +11 -0
  70. package/dist/line-quantity.js.map +1 -0
  71. package/dist/overdue.d.ts +77 -0
  72. package/dist/overdue.d.ts.map +1 -0
  73. package/dist/overdue.js +90 -0
  74. package/dist/overdue.js.map +1 -0
  75. package/dist/pricing/ChargeBehavior.d.ts +64 -0
  76. package/dist/pricing/ChargeBehavior.d.ts.map +1 -0
  77. package/dist/pricing/ChargeBehavior.js +124 -0
  78. package/dist/pricing/ChargeBehavior.js.map +1 -0
  79. package/dist/pricing/ChargeEngine.d.ts +54 -0
  80. package/dist/pricing/ChargeEngine.d.ts.map +1 -0
  81. package/dist/pricing/ChargeEngine.js +154 -0
  82. package/dist/pricing/ChargeEngine.js.map +1 -0
  83. package/dist/pricing/OrderPricingService.d.ts +244 -0
  84. package/dist/pricing/OrderPricingService.d.ts.map +1 -0
  85. package/dist/pricing/OrderPricingService.js +516 -0
  86. package/dist/pricing/OrderPricingService.js.map +1 -0
  87. package/dist/pricing/OrdersEngine.d.ts +100 -0
  88. package/dist/pricing/OrdersEngine.d.ts.map +1 -0
  89. package/dist/pricing/OrdersEngine.js +156 -0
  90. package/dist/pricing/OrdersEngine.js.map +1 -0
  91. package/dist/pricing/PriceResolver.d.ts +127 -0
  92. package/dist/pricing/PriceResolver.d.ts.map +1 -0
  93. package/dist/pricing/PriceResolver.js +345 -0
  94. package/dist/pricing/PriceResolver.js.map +1 -0
  95. package/dist/pricing/PricingBehavior.d.ts +161 -0
  96. package/dist/pricing/PricingBehavior.d.ts.map +1 -0
  97. package/dist/pricing/PricingBehavior.js +351 -0
  98. package/dist/pricing/PricingBehavior.js.map +1 -0
  99. package/dist/pricing/PromotionBehavior.d.ts +73 -0
  100. package/dist/pricing/PromotionBehavior.d.ts.map +1 -0
  101. package/dist/pricing/PromotionBehavior.js +166 -0
  102. package/dist/pricing/PromotionBehavior.js.map +1 -0
  103. package/dist/pricing/PromotionEngine.d.ts +131 -0
  104. package/dist/pricing/PromotionEngine.d.ts.map +1 -0
  105. package/dist/pricing/PromotionEngine.js +505 -0
  106. package/dist/pricing/PromotionEngine.js.map +1 -0
  107. package/dist/pricing/TaxResolver.d.ts +162 -0
  108. package/dist/pricing/TaxResolver.d.ts.map +1 -0
  109. package/dist/pricing/TaxResolver.js +361 -0
  110. package/dist/pricing/TaxResolver.js.map +1 -0
  111. package/dist/save-populated-fields.d.ts +17 -0
  112. package/dist/save-populated-fields.d.ts.map +1 -0
  113. package/dist/save-populated-fields.js +25 -0
  114. package/dist/save-populated-fields.js.map +1 -0
  115. package/package.json +34 -5
@@ -0,0 +1,509 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var OrderHeaderEntity_1;
8
+ /**
9
+ * @fileoverview `OrderHeaderEntity` — the order rules that hold on BOTH tiers.
10
+ *
11
+ * WHY THIS LAYER EXISTS
12
+ *
13
+ * Every order rule used to live in `OrderEntityServer`, in a server-only package. That meant the
14
+ * browser could compose an order that the server would refuse, and only find out after a round
15
+ * trip — and it meant a rule enforced "in the order screen" was enforced nowhere at all the moment
16
+ * anything else saved an order. This codebase has found that shape three times.
17
+ *
18
+ * So the rules split by what they NEED, not by where they were written:
19
+ *
20
+ * · Here — anything decidable from the record and its lines alone. No database, no engine, no
21
+ * provider. Runs in the browser before a round trip and again on the server, because the server
22
+ * subclass extends this one and `super.Validate()` still fires.
23
+ * · `OrderEntityServer` — anything that must read or write the database: pricing, promotions,
24
+ * charges and tax, journal entries, subscriptions, sequence numbers.
25
+ *
26
+ * `ClassFactory` priority auto-increments by load order, so the server subclass — registered later
27
+ * because it is loaded later — wins server-side with no configuration, while the browser resolves
28
+ * to this one and keeps the `Lines` collection the generated class declares.
29
+ *
30
+ * WHAT THIS DELIBERATELY DOES NOT DO
31
+ *
32
+ * It does not override `Save()`. A shared class that persisted would have to work on a provider
33
+ * that cannot open a transaction, which is exactly the split that made composite saves server-only
34
+ * before MJ 6.1. Persistence stays with the server subclass; the browser ships the whole graph in
35
+ * one `MJ.SaveEntityGraph` call and the server runs the same executor.
36
+ *
37
+ * @module @mj-biz-apps/orders-entities
38
+ */
39
+ import { BaseEntity, ValidationErrorInfo, ValidationErrorType } from '@memberjunction/core';
40
+ import { RegisterClass } from '@memberjunction/global';
41
+ import { mjBizAppsOrdersOrderHeaderEntity } from './generated/entity_subclasses.js';
42
+ import { CanOfferConfirm, CanTransition, IsBooked } from './OrderStatusBehavior.js';
43
+ import { ResolveActiveEmployerOrganization } from './PartyAffiliationBehavior.js';
44
+ import { PromotionCodesCompanion } from './PromotionCodesCompanion.js';
45
+ import { InitialPaymentIntentCompanion } from './InitialPaymentIntentCompanion.js';
46
+ import { IsSavePopulatedFieldError } from './save-populated-fields.js';
47
+ import { BookedMoneyEditMessage, ORDER_HEADER_MONEY_FIELDS, ORDER_LINE_MONEY_FIELDS, } from './booked-money.js';
48
+ /** Statuses that mean the order has been booked to the ledger (plan D8). */
49
+ const BOOKED_STATUSES = new Set(['Confirmed']);
50
+ let OrderHeaderEntity = OrderHeaderEntity_1 = class OrderHeaderEntity extends mjBizAppsOrdersOrderHeaderEntity {
51
+ constructor() {
52
+ super(...arguments);
53
+ /**
54
+ * Promotion codes the customer presented, riding with the order across the wire.
55
+ *
56
+ * Registered HERE rather than on the server subclass so a browser has it: the whole point is
57
+ * that a code typed on screen reaches the engine. See `PromotionCodesCompanion` for why this is
58
+ * a companion and not a related-record collection — in short, a code has no child row, and only
59
+ * the engine can turn one into an `OrderAdjustment`.
60
+ */
61
+ this.PromotionCodes = this.RegisterCompanion(new PromotionCodesCompanion(this));
62
+ /**
63
+ * Check / wire / transfer number typed at entry. Not a column — it rides as a companion
64
+ * and `OrderEntityServer.createInitialPayment` turns it into a `PaymentDetail`.
65
+ */
66
+ this.InitialPaymentIntent = this.RegisterCompanion(new InitialPaymentIntentCompanion(this));
67
+ /**
68
+ * True while THIS save is the booking save, and it stays true across `ConfirmedAt` being set.
69
+ *
70
+ * `willBookOnThisSave()` answers "would a save starting now book?", which is the right question
71
+ * everywhere except inside the booking save itself. The server subclass stamps `ConfirmedAt`
72
+ * before it calls `super.Save()`, and `ConfirmedAt` is exactly what makes `willBookOnThisSave()`
73
+ * return false — so validation running inside that `super.Save()` was asking a question whose
74
+ * answer had already been flipped by its own caller, three lines earlier. Every rule gated on it
75
+ * was therefore skipped on the one save it existed to guard.
76
+ *
77
+ * That is why a confirm with zero lines (ORD-000030) and a confirm with no payer (ORD-000028,
78
+ * which posted Dr A/R 99 / Cr Sales 99) both went through. Two separate defects had to coincide
79
+ * — this one and a skipped `ValidateAsync` — and fixing either alone changes nothing, which is
80
+ * why the block looked correct for as long as it did.
81
+ *
82
+ * `protected` because the server subclass sets it around its booking walk.
83
+ */
84
+ this.bookingInFlight = false;
85
+ }
86
+ ClearInitialPaymentDetail() {
87
+ this.InitialPaymentIntent.Reference = null;
88
+ this.GetCompanion('InitialPaymentDetailID_Object')?.Clear();
89
+ }
90
+ ClearBillToAddress() {
91
+ this.GetCompanion('BillToAddressID_Object')?.Clear();
92
+ }
93
+ ClearShipToAddress() {
94
+ this.GetCompanion('ShipToAddressID_Object')?.Clear();
95
+ }
96
+ get InitialPaymentReference() {
97
+ return (this.InitialPaymentDetailID_Object?.ReferenceNumber ??
98
+ this.InitialPaymentIntent.Reference ??
99
+ null);
100
+ }
101
+ set InitialPaymentReference(value) {
102
+ const trimmed = (value ?? '').trim();
103
+ const ref = trimmed.length ? trimmed : null;
104
+ this.InitialPaymentIntent.Reference = ref;
105
+ if (ref) {
106
+ const detail = this.InitialPaymentDetailID_EnsureObject();
107
+ detail.ReferenceNumber = ref;
108
+ if (this.CompanyID && !detail.CompanyID) {
109
+ detail.CompanyID = this.CompanyID;
110
+ }
111
+ if (this.InitialPaymentTypeID && !detail.PaymentTypeID) {
112
+ detail.PaymentTypeID = this.InitialPaymentTypeID;
113
+ }
114
+ }
115
+ else if (this.InitialPaymentDetailID_Object) {
116
+ this.InitialPaymentDetailID_Object.ReferenceNumber = null;
117
+ if (this.isInitialPaymentDetailEmpty(this.InitialPaymentDetailID_Object)) {
118
+ this.ClearInitialPaymentDetail();
119
+ }
120
+ }
121
+ }
122
+ isInitialPaymentDetailEmpty(detail) {
123
+ return (!detail.ReferenceNumber &&
124
+ !detail.Last4 &&
125
+ !detail.BankName &&
126
+ !detail.RoutingLast4 &&
127
+ !detail.AccountLast4 &&
128
+ !detail.ProviderCustomerRef &&
129
+ !detail.ProviderInstrumentRef &&
130
+ !detail.HolderName);
131
+ }
132
+ /** True when this save is the first transition into a booked status (plan D8). */
133
+ willBookOnThisSave() {
134
+ if (this.bookingInFlight)
135
+ return true;
136
+ if (!BOOKED_STATUSES.has(this.Status))
137
+ return false;
138
+ if (this.ConfirmedAt)
139
+ return false; // already booked — never re-book
140
+ return true;
141
+ }
142
+ /**
143
+ * Whether the status change on this save is a legal move.
144
+ *
145
+ * The PERSISTED status is the `from`: `OldValue` is what is on disk, so re-saving an unchanged
146
+ * row is a no-op transition and a genuine move is measured against what was really there rather
147
+ * than against whatever this object was last set to.
148
+ *
149
+ * `CK_OrderHeader_Status` enforces the legal SET of statuses and nothing enforced the legal
150
+ * MOVES, which is how `Voided → Confirmed` used to save: a voided order came back to life
151
+ * keeping the journal entries its reversal had already unwound.
152
+ */
153
+ /**
154
+ * The lifecycle verdict on this save's status change, with no side effects.
155
+ *
156
+ * Separate from `Validate()` because the server subclass has to ask this question EARLY —
157
+ * before it prices lines, mints an order number or posts anything — while the rest of
158
+ * `Validate()` cannot be asked that early: it includes the generated NOT NULL field checks, and
159
+ * `OrderNumber`, `Company` and each line's `UnitPrice` are all populated by the save itself.
160
+ * Running the whole of `Validate()` up front therefore fails on fields the save was about to
161
+ * fill in.
162
+ */
163
+ statusTransitionVerdict() {
164
+ const from = this.IsSaved ? this.GetFieldByName('Status')?.OldValue : null;
165
+ return CanTransition(from ?? null, this.Status);
166
+ }
167
+ /** The refusal message for an illegal move, shared by `Validate()` and the server's early gate. */
168
+ statusTransitionRefusal(verdict) {
169
+ return `${verdict.Reason} (order ${this.OrderNumber ?? 'not yet numbered'}).`;
170
+ }
171
+ /**
172
+ * Rules decidable without touching the database.
173
+ *
174
+ * `super.Validate()` fans out to every declared related-record collection, so each line's own
175
+ * `Validate()` runs here too and its failures arrive attributed by position (`Lines[3].Quantity`).
176
+ * That replaces a hand-written loop that only existed on the server.
177
+ */
178
+ NewRecord(newValues) {
179
+ const created = super.NewRecord(newValues);
180
+ if (created) {
181
+ if (this.OrderDate == null) {
182
+ this.OrderDate = new Date();
183
+ }
184
+ if (!this.Status) {
185
+ this.Status = 'Draft';
186
+ }
187
+ }
188
+ return created;
189
+ }
190
+ Validate() {
191
+ if (this.InitialPaymentDetailID_Object) {
192
+ if (this.isInitialPaymentDetailEmpty(this.InitialPaymentDetailID_Object)) {
193
+ this.ClearInitialPaymentDetail();
194
+ }
195
+ else {
196
+ if (this.CompanyID && !this.InitialPaymentDetailID_Object.CompanyID) {
197
+ this.InitialPaymentDetailID_Object.CompanyID = this.CompanyID;
198
+ }
199
+ if (this.InitialPaymentTypeID && !this.InitialPaymentDetailID_Object.PaymentTypeID) {
200
+ this.InitialPaymentDetailID_Object.PaymentTypeID = this.InitialPaymentTypeID;
201
+ }
202
+ }
203
+ }
204
+ const result = super.Validate();
205
+ this.dropSavePopulatedFieldErrors(result);
206
+ this.refuseBookedMoneyEdits(result);
207
+ const verdict = this.statusTransitionVerdict();
208
+ if (!verdict.Allowed) {
209
+ result.Success = false;
210
+ result.Errors.push(new ValidationErrorInfo('Status', this.statusTransitionRefusal(verdict), this.Status, ValidationErrorType.Failure));
211
+ }
212
+ if (this.willBookOnThisSave()) {
213
+ // A CONFIRMED ORDER MUST NAME SOMEONE TO BILL. A confirmed order IS the receivable in
214
+ // this app — there is no separate invoice record — so a booked order with neither a
215
+ // bill-to person nor a bill-to organization is a receivable owed by nobody. It debits
216
+ // Accounts Receivable, appears in the balance, and can never be aged, chased or
217
+ // collected, because every collections surface groups by the payer key that is null on
218
+ // it. Draft and Quoted are deliberately exempt: you take an order before you know who is
219
+ // paying, and forcing the payer up front would break order entry.
220
+ if (!this.BillToPersonID && !this.BillToOrganizationID) {
221
+ result.Success = false;
222
+ result.Errors.push(new ValidationErrorInfo('BillToOrganizationID', `Order ${this.OrderNumber ?? ''} cannot be confirmed without a customer — set a ` +
223
+ `bill-to person or a bill-to organization. A confirmed order is the receivable, ` +
224
+ `so one with no payer could never be collected.`, this.BillToOrganizationID, ValidationErrorType.Failure));
225
+ }
226
+ // AND IT MUST HAVE SOMETHING TO BOOK — but only where that can be known for CERTAIN
227
+ // without the database.
228
+ //
229
+ // An empty collection means "no lines" only when there cannot be any on disk: a record
230
+ // that was never saved, or one whose collection was actually loaded. On a saved order
231
+ // with an unloaded collection, empty means "unknown" — and treating it as zero would
232
+ // refuse a perfectly good confirm of an order whose lines are sitting in the table.
233
+ // `OrderEntityServer.ValidateAsync` settles that case against the database.
234
+ //
235
+ // Note `IsLoaded` alone is not the test: `Add()` does not mark a collection loaded, so
236
+ // a new order composed in the browser has lines and `IsLoaded === false`.
237
+ if (this.Lines.Count === 0 && (!this.IsSaved || this.Lines.IsLoaded)) {
238
+ result.Success = false;
239
+ result.Errors.push(new ValidationErrorInfo('Status', `Order ${this.OrderNumber ?? ''} cannot be confirmed with no lines — there would ` +
240
+ `be nothing to book.`, this.Status, ValidationErrorType.Failure));
241
+ }
242
+ }
243
+ return result;
244
+ }
245
+ /**
246
+ * `super.Validate()` includes generated NOT NULL checks for fields this save
247
+ * is about to fill in (`OrderNumber`, each new line's `UnitPrice` / `CompanyID`
248
+ * / `LineNumber`). Fast Entry and the editor gate confirm on `Validate()`, so
249
+ * those checks disabled the button on every complete unsaved order.
250
+ *
251
+ * Same reason `OrderEntityServer.Save()` refuses to run the full `Validate()`
252
+ * before it mints the number. After the first save the values exist, so an
253
+ * empty one is a real error and is kept.
254
+ */
255
+ dropSavePopulatedFieldErrors(result) {
256
+ const kept = result.Errors.filter((error) => !IsSavePopulatedFieldError(error.Source ?? '', this.IsSaved, (index) => this.Lines.Items[index]?.IsSaved === true));
257
+ if (kept.length === result.Errors.length)
258
+ return;
259
+ result.Errors = kept;
260
+ result.Success = kept.every((error) => error.Type !== ValidationErrorType.Failure);
261
+ }
262
+ /** Whether this order is booked to the ledger right now. */
263
+ get IsBookedOrder() {
264
+ return IsBooked(this.Status);
265
+ }
266
+ /**
267
+ * True when money composition is frozen: the order was already booked
268
+ * *before* this save. The booking save itself (Draft/Quoted → Confirmed)
269
+ * must still write the figures it just computed. A brand-new unsaved row
270
+ * is never locked, including back-office create-as-Confirmed.
271
+ */
272
+ get MoneyLocked() {
273
+ if (!this.IsSaved || this.bookingInFlight)
274
+ return false;
275
+ const from = this.GetFieldByName('Status')?.OldValue;
276
+ return IsBooked(from ?? '');
277
+ }
278
+ /**
279
+ * Refuse adds / removes / reprices / tender restatements after booking.
280
+ * The booking save itself still has to write the figures it just computed.
281
+ */
282
+ refuseBookedMoneyEdits(result) {
283
+ if (!this.MoneyLocked)
284
+ return;
285
+ const dirtyLineMoney = [];
286
+ let newLineCount = 0;
287
+ for (const line of this.Lines.Items) {
288
+ if (!line.IsSaved) {
289
+ newLineCount += 1;
290
+ continue;
291
+ }
292
+ for (const name of ORDER_LINE_MONEY_FIELDS) {
293
+ if (line.GetFieldByName(name)?.Dirty) {
294
+ dirtyLineMoney.push(name);
295
+ }
296
+ }
297
+ }
298
+ const dirtyHeaderMoney = [];
299
+ for (const name of ORDER_HEADER_MONEY_FIELDS) {
300
+ if (this.GetFieldByName(name)?.Dirty) {
301
+ dirtyHeaderMoney.push(name);
302
+ }
303
+ }
304
+ const message = BookedMoneyEditMessage({
305
+ NewLineCount: newLineCount,
306
+ RemovedLineCount: this.Lines.Removed.length,
307
+ DirtyLineMoneyFields: dirtyLineMoney,
308
+ ChargesChanged: this.Charges.IsLoaded && this.Charges.Dirty,
309
+ AdjustmentsChanged: this.Adjustments.IsLoaded && this.Adjustments.Dirty,
310
+ DirtyHeaderMoneyFields: dirtyHeaderMoney,
311
+ });
312
+ if (!message)
313
+ return;
314
+ result.Success = false;
315
+ result.Errors.push(new ValidationErrorInfo('Status', message, this.Status, ValidationErrorType.Failure));
316
+ }
317
+ /**
318
+ * Which editing SECTION each validation failure belongs to.
319
+ *
320
+ * The order editor shows errors against the section that owns the field — an unreachable payer
321
+ * lights up "parties", a bad quantity lights up "lines" — so the user is stopped at the field
322
+ * they can fix rather than at a rejection after the fact.
323
+ *
324
+ * This lives on the shared subclass because it is metadata-only: it reads the `Source` a
325
+ * `ValidationErrorInfo` already carries and maps it to a section. No database, no provider, so
326
+ * the browser gets it for free and the server does not need it at all.
327
+ *
328
+ * It replaces `OrderDraft.SectionsWithErrors`, which computed the same thing from a parallel
329
+ * model of the order that had to be kept in step with the entity by hand.
330
+ */
331
+ static SectionForField(source) {
332
+ const field = (source ?? '').trim();
333
+ if (!field)
334
+ return 'header';
335
+ // Companion failures arrive positionally attributed — `Lines[3].Quantity`.
336
+ if (/^Lines\[/i.test(field))
337
+ return 'lines';
338
+ switch (field) {
339
+ case 'BillToPersonID':
340
+ case 'BillToOrganizationID':
341
+ case 'BillToAddressID':
342
+ case 'ShipToPersonID':
343
+ case 'ShipToOrganizationID':
344
+ case 'ShipToAddressID':
345
+ return 'parties';
346
+ case 'InitialPaymentTypeID':
347
+ case 'InitialPaymentAmount':
348
+ case 'InitialPaymentReference':
349
+ case 'InitialPaymentDetailID':
350
+ return 'payment';
351
+ default:
352
+ return 'header';
353
+ }
354
+ }
355
+ /**
356
+ * The sections currently holding at least one error, for the editor's section chrome.
357
+ *
358
+ * Runs the real `Validate()` — the same rules the server enforces — so a section cannot light up
359
+ * for a reason the save would not also refuse, and cannot stay quiet for one it would.
360
+ */
361
+ SectionsWithErrors() {
362
+ const result = this.Validate();
363
+ if (result.Success)
364
+ return [];
365
+ const sections = new Set();
366
+ for (const e of result.Errors) {
367
+ sections.add(OrderHeaderEntity_1.SectionForField(e.Source));
368
+ }
369
+ return [...sections];
370
+ }
371
+ /**
372
+ * Save, or throw with the reason the engine gave.
373
+ *
374
+ * WHY THIS IS ON THE ENTITY. It was a method on an Angular service, which
375
+ * `docs/ui-architecture.md` names as the wrong place: "if a method on it loads, saves, validates
376
+ * or maps entity data, it is in the wrong place." The reason it existed at all is the boolean
377
+ * return — `Save()` answers true/false and leaves the reason on `LatestResult`, so every caller
378
+ * wrote the same three lines to turn that into something a person could read. Writing them once
379
+ * is right; writing them in a service is not.
380
+ *
381
+ * A non-Angular host gets this too, which is the test the guidelines actually apply.
382
+ *
383
+ * @throws The engine's own message — never a generic one. "Order ORD-000123 cannot be confirmed
384
+ * without a customer" is actionable; "the order could not be saved" is not.
385
+ */
386
+ async SaveOrThrow() {
387
+ if (!(await this.Save())) {
388
+ throw new Error(this.LatestResult?.CompleteMessage?.trim() || 'The order could not be saved.');
389
+ }
390
+ }
391
+ /**
392
+ * Whether the Confirm verb should be offered, and why not if it shouldn't.
393
+ *
394
+ * Status-only half is {@link CanOfferConfirm}. This adds the two facts the
395
+ * screen already knows: a payer, and at least one line. The UI stays dumb —
396
+ * it asks this, then calls {@link Confirm}.
397
+ */
398
+ ConfirmEligibility() {
399
+ const from = this.IsSaved
400
+ ? (this.GetFieldByName('Status')?.OldValue ?? this.Status)
401
+ : this.Status || 'Draft';
402
+ const status = CanOfferConfirm(from);
403
+ if (!status.Allowed)
404
+ return status;
405
+ if (!this.BillToPersonID && !this.BillToOrganizationID) {
406
+ return { Allowed: false, Reason: 'Need a bill-to party.' };
407
+ }
408
+ if (this.Lines.Count === 0 && (!this.IsSaved || this.Lines.IsLoaded)) {
409
+ return { Allowed: false, Reason: 'Need a line.' };
410
+ }
411
+ return { Allowed: true };
412
+ }
413
+ /** True when {@link Confirm} is a legal next move from what is on this object. */
414
+ get CanConfirm() {
415
+ return this.ConfirmEligibility().Allowed;
416
+ }
417
+ /**
418
+ * Confirm — the irreversible step.
419
+ *
420
+ * Setting the status and saving IS the confirm: the server subclass sees the transition into a
421
+ * booked state and books — journal entries, subscriptions, entitlements, the initial payment —
422
+ * in one transaction, or refuses with a reason and writes nothing.
423
+ *
424
+ * There is no dry run in front of it. Every rule is enforced by the engine itself, and a browser
425
+ * has already run the tier-independent ones through `Validate()`, so the user is told about a
426
+ * missing payer without a round trip.
427
+ */
428
+ async Confirm() {
429
+ if (this.IsSaved && !this.Lines.IsLoaded) {
430
+ await this.Lines.Load();
431
+ }
432
+ this.Status = 'Confirmed';
433
+ if (!(await this.Save())) {
434
+ throw new Error(this.LatestResult?.CompleteMessage?.trim() || 'The order could not be confirmed.');
435
+ }
436
+ }
437
+ /**
438
+ * Load this order and its lines together — the state an editor needs.
439
+ *
440
+ * Two calls and no mapping layer: the object bound to the screen is the object that will be
441
+ * saved. `Lines` is `Load: 'explicit'`, so it has to be asked for; asking here means no caller
442
+ * can forget and then wonder why an order with lines renders as empty.
443
+ *
444
+ * @returns False when the order does not exist, leaving this entity unloaded.
445
+ */
446
+ async LoadWithLines(orderHeaderID) {
447
+ if (!(await this.Load(orderHeaderID)))
448
+ return false;
449
+ await this.Lines.Load();
450
+ return true;
451
+ }
452
+ /**
453
+ * Person-level party defaults after a bill-to / ship-to person is set.
454
+ *
455
+ * 1. Copy person bill-to ↔ ship-to when the other side's person is null.
456
+ * 2. For each side that has a person and no org, stamp the longest-lasting
457
+ * active Employee relationship's organization.
458
+ *
459
+ * Does not overwrite an org the user already chose. Skips booked/voided orders.
460
+ */
461
+ async ApplyPersonPartyDefaults(changed = 'Both') {
462
+ if (this.Status === 'Voided' || this.IsBookedOrder)
463
+ return;
464
+ if (changed === 'BillTo' || changed === 'Both') {
465
+ if (this.BillToPersonID && !this.ShipToPersonID) {
466
+ this.ShipToPersonID = this.BillToPersonID;
467
+ }
468
+ }
469
+ if (changed === 'ShipTo' || changed === 'Both') {
470
+ if (this.ShipToPersonID && !this.BillToPersonID) {
471
+ this.BillToPersonID = this.ShipToPersonID;
472
+ }
473
+ }
474
+ await this.AutoPopulateEmployerOrganization('BillTo');
475
+ await this.AutoPopulateEmployerOrganization('ShipTo');
476
+ }
477
+ /**
478
+ * Stamp BillToOrganizationID or ShipToOrganizationID from the person's longest
479
+ * active Employee relationship when that side's org is still empty.
480
+ */
481
+ async AutoPopulateEmployerOrganization(partyRole = 'ShipTo', personID) {
482
+ const targetPersonID = personID ?? (partyRole === 'ShipTo' ? this.ShipToPersonID : this.BillToPersonID);
483
+ if (!targetPersonID)
484
+ return null;
485
+ const currentOrgID = partyRole === 'ShipTo' ? this.ShipToOrganizationID : this.BillToOrganizationID;
486
+ if (currentOrgID)
487
+ return currentOrgID;
488
+ const provider = this.ProviderToUse;
489
+ if (!provider)
490
+ return null;
491
+ const asOf = this.OrderDate ?? new Date();
492
+ const orgId = await ResolveActiveEmployerOrganization(provider, targetPersonID, asOf, this.ContextCurrentUser);
493
+ if (!orgId)
494
+ return null;
495
+ if (partyRole === 'ShipTo') {
496
+ if (!this.ShipToOrganizationID)
497
+ this.ShipToOrganizationID = orgId;
498
+ }
499
+ else if (!this.BillToOrganizationID) {
500
+ this.BillToOrganizationID = orgId;
501
+ }
502
+ return orgId;
503
+ }
504
+ };
505
+ OrderHeaderEntity = OrderHeaderEntity_1 = __decorate([
506
+ RegisterClass(BaseEntity, 'MJ_BizApps_Orders: Order Headers')
507
+ ], OrderHeaderEntity);
508
+ export { OrderHeaderEntity };
509
+ //# sourceMappingURL=OrderHeaderEntity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OrderHeaderEntity.js","sourceRoot":"","sources":["../src/OrderHeaderEntity.ts"],"names":[],"mappings":";;;;;;;AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,OAAO,EAAE,UAAU,EAAkB,mBAAmB,EAAE,mBAAmB,EAA+E,MAAM,sBAAsB,CAAC;AACzL,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAEvD,OAAO,EAAE,gCAAgC,EAAsC,MAAM,+BAA+B,CAAC;AACrH,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,QAAQ,EAA0B,MAAM,uBAAuB,CAAC;AACzG,OAAO,EAAE,iCAAiC,EAAE,MAAM,4BAA4B,CAAC;AAC/E,OAAO,EAAE,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AACpE,OAAO,EAAE,6BAA6B,EAAE,MAAM,iCAAiC,CAAC;AAChF,OAAO,EAAE,yBAAyB,EAAE,MAAM,yBAAyB,CAAC;AACpE,OAAO,EACH,sBAAsB,EACtB,yBAAyB,EACzB,uBAAuB,GAC1B,MAAM,gBAAgB,CAAC;AAKxB,4EAA4E;AAC5E,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC;AAGxC,IAAM,iBAAiB,yBAAvB,MAAM,iBAAkB,SAAQ,gCAAgC;IAAhE;;QACH;;;;;;;WAOG;QACa,mBAAc,GAAG,IAAI,CAAC,iBAAiB,CAAC,IAAI,uBAAuB,CAAC,IAAI,CAAC,CAAC,CAAC;QAE3F;;;WAGG;QACa,yBAAoB,GAAG,IAAI,CAAC,iBAAiB,CAAC,IAAI,6BAA6B,CAAC,IAAI,CAAC,CAAC,CAAC;QAyDvG;;;;;;;;;;;;;;;;WAgBG;QACO,oBAAe,GAAG,KAAK,CAAC;IAyatC,CAAC;IAjfU,yBAAyB;QAC5B,IAAI,CAAC,oBAAoB,CAAC,SAAS,GAAG,IAAI,CAAC;QAC3C,IAAI,CAAC,YAAY,CAAiB,+BAA+B,CAAC,EAAE,KAAK,EAAE,CAAC;IAChF,CAAC;IAEM,kBAAkB;QACrB,IAAI,CAAC,YAAY,CAAiB,wBAAwB,CAAC,EAAE,KAAK,EAAE,CAAC;IACzE,CAAC;IAEM,kBAAkB;QACrB,IAAI,CAAC,YAAY,CAAiB,wBAAwB,CAAC,EAAE,KAAK,EAAE,CAAC;IACzE,CAAC;IAED,IAAW,uBAAuB;QAC9B,OAAO,CACH,IAAI,CAAC,6BAA6B,EAAE,eAAe;YACnD,IAAI,CAAC,oBAAoB,CAAC,SAAS;YACnC,IAAI,CACP,CAAC;IACN,CAAC;IAED,IAAW,uBAAuB,CAAC,KAAoB;QACnD,MAAM,OAAO,GAAG,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;QACrC,MAAM,GAAG,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;QAC5C,IAAI,CAAC,oBAAoB,CAAC,SAAS,GAAG,GAAG,CAAC;QAC1C,IAAI,GAAG,EAAE,CAAC;YACN,MAAM,MAAM,GAAG,IAAI,CAAC,mCAAmC,EAAE,CAAC;YAC1D,MAAM,CAAC,eAAe,GAAG,GAAG,CAAC;YAC7B,IAAI,IAAI,CAAC,SAAS,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC;gBACtC,MAAM,CAAC,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC;YACtC,CAAC;YACD,IAAI,IAAI,CAAC,oBAAoB,IAAI,CAAC,MAAM,CAAC,aAAa,EAAE,CAAC;gBACrD,MAAM,CAAC,aAAa,GAAG,IAAI,CAAC,oBAAoB,CAAC;YACrD,CAAC;QACL,CAAC;aAAM,IAAI,IAAI,CAAC,6BAA6B,EAAE,CAAC;YAC5C,IAAI,CAAC,6BAA6B,CAAC,eAAe,GAAG,IAAI,CAAC;YAC1D,IAAI,IAAI,CAAC,2BAA2B,CAAC,IAAI,CAAC,6BAA6B,CAAC,EAAE,CAAC;gBACvE,IAAI,CAAC,yBAAyB,EAAE,CAAC;YACrC,CAAC;QACL,CAAC;IACL,CAAC;IAEO,2BAA2B,CAAC,MAA0C;QAC1E,OAAO,CACH,CAAC,MAAM,CAAC,eAAe;YACvB,CAAC,MAAM,CAAC,KAAK;YACb,CAAC,MAAM,CAAC,QAAQ;YAChB,CAAC,MAAM,CAAC,YAAY;YACpB,CAAC,MAAM,CAAC,YAAY;YACpB,CAAC,MAAM,CAAC,mBAAmB;YAC3B,CAAC,MAAM,CAAC,qBAAqB;YAC7B,CAAC,MAAM,CAAC,UAAU,CACrB,CAAC;IACN,CAAC;IAqBD,kFAAkF;IACxE,kBAAkB;QACxB,IAAI,IAAI,CAAC,eAAe;YAAE,OAAO,IAAI,CAAC;QACtC,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC;YAAE,OAAO,KAAK,CAAC;QACpD,IAAI,IAAI,CAAC,WAAW;YAAE,OAAO,KAAK,CAAC,CAAC,iCAAiC;QACrE,OAAO,IAAI,CAAC;IAChB,CAAC;IAED;;;;;;;;;;OAUG;IACH;;;;;;;;;OASG;IACO,uBAAuB;QAC7B,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC,CAAE,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,EAAE,QAA+B,CAAC,CAAC,CAAC,IAAI,CAAC;QACnG,OAAO,aAAa,CAAC,IAAI,IAAI,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;IACpD,CAAC;IAED,mGAAmG;IACzF,uBAAuB,CAAC,OAA0B;QACxD,OAAO,GAAG,OAAO,CAAC,MAAM,WAAW,IAAI,CAAC,WAAW,IAAI,kBAAkB,IAAI,CAAC;IAClF,CAAC;IAED;;;;;;OAMG;IACa,SAAS,CAAC,SAAgC;QACtD,MAAM,OAAO,GAAG,KAAK,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;QAC3C,IAAI,OAAO,EAAE,CAAC;YACV,IAAI,IAAI,CAAC,SAAS,IAAI,IAAI,EAAE,CAAC;gBACzB,IAAI,CAAC,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC;YAChC,CAAC;YACD,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;gBACf,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC;YAC1B,CAAC;QACL,CAAC;QACD,OAAO,OAAO,CAAC;IACnB,CAAC;IAEe,QAAQ;QACpB,IAAI,IAAI,CAAC,6BAA6B,EAAE,CAAC;YACrC,IAAI,IAAI,CAAC,2BAA2B,CAAC,IAAI,CAAC,6BAA6B,CAAC,EAAE,CAAC;gBACvE,IAAI,CAAC,yBAAyB,EAAE,CAAC;YACrC,CAAC;iBAAM,CAAC;gBACJ,IAAI,IAAI,CAAC,SAAS,IAAI,CAAC,IAAI,CAAC,6BAA6B,CAAC,SAAS,EAAE,CAAC;oBAClE,IAAI,CAAC,6BAA6B,CAAC,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC;gBAClE,CAAC;gBACD,IAAI,IAAI,CAAC,oBAAoB,IAAI,CAAC,IAAI,CAAC,6BAA6B,CAAC,aAAa,EAAE,CAAC;oBACjF,IAAI,CAAC,6BAA6B,CAAC,aAAa,GAAG,IAAI,CAAC,oBAAoB,CAAC;gBACjF,CAAC;YACL,CAAC;QACL,CAAC;QAED,MAAM,MAAM,GAAG,KAAK,CAAC,QAAQ,EAAE,CAAC;QAChC,IAAI,CAAC,4BAA4B,CAAC,MAAM,CAAC,CAAC;QAC1C,IAAI,CAAC,sBAAsB,CAAC,MAAM,CAAC,CAAC;QAEpC,MAAM,OAAO,GAAG,IAAI,CAAC,uBAAuB,EAAE,CAAC;QAC/C,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;YACnB,MAAM,CAAC,OAAO,GAAG,KAAK,CAAC;YACvB,MAAM,CAAC,MAAM,CAAC,IAAI,CACd,IAAI,mBAAmB,CACnB,QAAQ,EACR,IAAI,CAAC,uBAAuB,CAAC,OAAO,CAAC,EACrC,IAAI,CAAC,MAAM,EACX,mBAAmB,CAAC,OAAO,CAC9B,CACJ,CAAC;QACN,CAAC;QAED,IAAI,IAAI,CAAC,kBAAkB,EAAE,EAAE,CAAC;YAC5B,sFAAsF;YACtF,oFAAoF;YACpF,sFAAsF;YACtF,gFAAgF;YAChF,uFAAuF;YACvF,yFAAyF;YACzF,kEAAkE;YAClE,IAAI,CAAC,IAAI,CAAC,cAAc,IAAI,CAAC,IAAI,CAAC,oBAAoB,EAAE,CAAC;gBACrD,MAAM,CAAC,OAAO,GAAG,KAAK,CAAC;gBACvB,MAAM,CAAC,MAAM,CAAC,IAAI,CACd,IAAI,mBAAmB,CACnB,sBAAsB,EACtB,SAAS,IAAI,CAAC,WAAW,IAAI,EAAE,kDAAkD;oBAC7E,iFAAiF;oBACjF,gDAAgD,EACpD,IAAI,CAAC,oBAAoB,EACzB,mBAAmB,CAAC,OAAO,CAC9B,CACJ,CAAC;YACN,CAAC;YAED,oFAAoF;YACpF,wBAAwB;YACxB,EAAE;YACF,uFAAuF;YACvF,sFAAsF;YACtF,qFAAqF;YACrF,oFAAoF;YACpF,4EAA4E;YAC5E,EAAE;YACF,uFAAuF;YACvF,0EAA0E;YAC1E,IAAI,IAAI,CAAC,KAAK,CAAC,KAAK,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC;gBACnE,MAAM,CAAC,OAAO,GAAG,KAAK,CAAC;gBACvB,MAAM,CAAC,MAAM,CAAC,IAAI,CACd,IAAI,mBAAmB,CACnB,QAAQ,EACR,SAAS,IAAI,CAAC,WAAW,IAAI,EAAE,mDAAmD;oBAC9E,qBAAqB,EACzB,IAAI,CAAC,MAAM,EACX,mBAAmB,CAAC,OAAO,CAC9B,CACJ,CAAC;YACN,CAAC;QACL,CAAC;QAED,OAAO,MAAM,CAAC;IAClB,CAAC;IAED;;;;;;;;;OASG;IACK,4BAA4B,CAAC,MAAwB;QACzD,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAC7B,CAAC,KAAK,EAAE,EAAE,CACN,CAAC,yBAAyB,CACtB,KAAK,CAAC,MAAM,IAAI,EAAE,EAClB,IAAI,CAAC,OAAO,EACZ,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,OAAO,KAAK,IAAI,CACvD,CACR,CAAC;QACF,IAAI,IAAI,CAAC,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,MAAM;YAAE,OAAO;QACjD,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC;QACrB,MAAM,CAAC,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,KAAK,mBAAmB,CAAC,OAAO,CAAC,CAAC;IACvF,CAAC;IAED,4DAA4D;IAC5D,IAAW,aAAa;QACpB,OAAO,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACjC,CAAC;IAED;;;;;OAKG;IACH,IAAW,WAAW;QAClB,IAAI,CAAC,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,eAAe;YAAE,OAAO,KAAK,CAAC;QACxD,MAAM,IAAI,GAAG,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,EAAE,QAA8B,CAAC;QAC3E,OAAO,QAAQ,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;IAChC,CAAC;IAED;;;OAGG;IACK,sBAAsB,CAAC,MAAwB;QACnD,IAAI,CAAC,IAAI,CAAC,WAAW;YAAE,OAAO;QAE9B,MAAM,cAAc,GAAa,EAAE,CAAC;QACpC,IAAI,YAAY,GAAG,CAAC,CAAC;QACrB,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;YAClC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;gBAChB,YAAY,IAAI,CAAC,CAAC;gBAClB,SAAS;YACb,CAAC;YACD,KAAK,MAAM,IAAI,IAAI,uBAAuB,EAAE,CAAC;gBACzC,IAAI,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,CAAC;oBACnC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBAC9B,CAAC;YACL,CAAC;QACL,CAAC;QAED,MAAM,gBAAgB,GAAa,EAAE,CAAC;QACtC,KAAK,MAAM,IAAI,IAAI,yBAAyB,EAAE,CAAC;YAC3C,IAAI,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,CAAC;gBACnC,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAChC,CAAC;QACL,CAAC;QAED,MAAM,OAAO,GAAG,sBAAsB,CAAC;YACnC,YAAY,EAAE,YAAY;YAC1B,gBAAgB,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM;YAC3C,oBAAoB,EAAE,cAAc;YACpC,cAAc,EAAE,IAAI,CAAC,OAAO,CAAC,QAAQ,IAAI,IAAI,CAAC,OAAO,CAAC,KAAK;YAC3D,kBAAkB,EAAE,IAAI,CAAC,WAAW,CAAC,QAAQ,IAAI,IAAI,CAAC,WAAW,CAAC,KAAK;YACvE,sBAAsB,EAAE,gBAAgB;SAC3C,CAAC,CAAC;QACH,IAAI,CAAC,OAAO;YAAE,OAAO;QAErB,MAAM,CAAC,OAAO,GAAG,KAAK,CAAC;QACvB,MAAM,CAAC,MAAM,CAAC,IAAI,CACd,IAAI,mBAAmB,CACnB,QAAQ,EACR,OAAO,EACP,IAAI,CAAC,MAAM,EACX,mBAAmB,CAAC,OAAO,CAC9B,CACJ,CAAC;IACN,CAAC;IAED;;;;;;;;;;;;;OAaG;IACI,MAAM,CAAC,eAAe,CAAC,MAAiC;QAC3D,MAAM,KAAK,GAAG,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;QACpC,IAAI,CAAC,KAAK;YAAE,OAAO,QAAQ,CAAC;QAC5B,2EAA2E;QAC3E,IAAI,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC;YAAE,OAAO,OAAO,CAAC;QAC5C,QAAQ,KAAK,EAAE,CAAC;YACZ,KAAK,gBAAgB,CAAC;YACtB,KAAK,sBAAsB,CAAC;YAC5B,KAAK,iBAAiB,CAAC;YACvB,KAAK,gBAAgB,CAAC;YACtB,KAAK,sBAAsB,CAAC;YAC5B,KAAK,iBAAiB;gBAClB,OAAO,SAAS,CAAC;YACrB,KAAK,sBAAsB,CAAC;YAC5B,KAAK,sBAAsB,CAAC;YAC5B,KAAK,yBAAyB,CAAC;YAC/B,KAAK,wBAAwB;gBACzB,OAAO,SAAS,CAAC;YACrB;gBACI,OAAO,QAAQ,CAAC;QACxB,CAAC;IACL,CAAC;IAED;;;;;OAKG;IACI,kBAAkB;QACrB,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC;QAC/B,IAAI,MAAM,CAAC,OAAO;YAAE,OAAO,EAAE,CAAC;QAC9B,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAsB,CAAC;QAC/C,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;YAC5B,QAAQ,CAAC,GAAG,CAAC,mBAAiB,CAAC,eAAe,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;QAC9D,CAAC;QACD,OAAO,CAAC,GAAG,QAAQ,CAAC,CAAC;IACzB,CAAC;IAED;;;;;;;;;;;;;;OAcG;IACI,KAAK,CAAC,WAAW;QACpB,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC;YACvB,MAAM,IAAI,KAAK,CAAC,IAAI,CAAC,YAAY,EAAE,eAAe,EAAE,IAAI,EAAE,IAAI,+BAA+B,CAAC,CAAC;QACnG,CAAC;IACL,CAAC;IAED;;;;;;OAMG;IACI,kBAAkB;QACrB,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO;YACrB,CAAC,CAAC,CAAE,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,EAAE,QAA+B,IAAI,IAAI,CAAC,MAAM,CAAC;YAClF,CAAC,CAAC,IAAI,CAAC,MAAM,IAAI,OAAO,CAAC;QAC7B,MAAM,MAAM,GAAG,eAAe,CAAC,IAAI,CAAC,CAAC;QACrC,IAAI,CAAC,MAAM,CAAC,OAAO;YAAE,OAAO,MAAM,CAAC;QACnC,IAAI,CAAC,IAAI,CAAC,cAAc,IAAI,CAAC,IAAI,CAAC,oBAAoB,EAAE,CAAC;YACrD,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,uBAAuB,EAAE,CAAC;QAC/D,CAAC;QACD,IAAI,IAAI,CAAC,KAAK,CAAC,KAAK,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC;YACnE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,cAAc,EAAE,CAAC;QACtD,CAAC;QACD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IAC7B,CAAC;IAED,kFAAkF;IAClF,IAAW,UAAU;QACjB,OAAO,IAAI,CAAC,kBAAkB,EAAE,CAAC,OAAO,CAAC;IAC7C,CAAC;IAED;;;;;;;;;;OAUG;IACI,KAAK,CAAC,OAAO;QAChB,IAAI,IAAI,CAAC,OAAO,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC;YACvC,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;QAC5B,CAAC;QACD,IAAI,CAAC,MAAM,GAAG,WAAW,CAAC;QAC1B,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC;YACvB,MAAM,IAAI,KAAK,CAAC,IAAI,CAAC,YAAY,EAAE,eAAe,EAAE,IAAI,EAAE,IAAI,mCAAmC,CAAC,CAAC;QACvG,CAAC;IACL,CAAC;IAED;;;;;;;;OAQG;IACI,KAAK,CAAC,aAAa,CAAC,aAAqB;QAC5C,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;YAAE,OAAO,KAAK,CAAC;QACpD,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;QACxB,OAAO,IAAI,CAAC;IAChB,CAAC;IAGD;;;;;;;;OAQG;IACI,KAAK,CAAC,wBAAwB,CAAC,UAAwC,MAAM;QAChF,IAAI,IAAI,CAAC,MAAM,KAAK,QAAQ,IAAI,IAAI,CAAC,aAAa;YAAE,OAAO;QAE3D,IAAI,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,MAAM,EAAE,CAAC;YAC7C,IAAI,IAAI,CAAC,cAAc,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,CAAC;gBAC9C,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC,cAAc,CAAC;YAC9C,CAAC;QACL,CAAC;QACD,IAAI,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,MAAM,EAAE,CAAC;YAC7C,IAAI,IAAI,CAAC,cAAc,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,CAAC;gBAC9C,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC,cAAc,CAAC;YAC9C,CAAC;QACL,CAAC;QAED,MAAM,IAAI,CAAC,gCAAgC,CAAC,QAAQ,CAAC,CAAC;QACtD,MAAM,IAAI,CAAC,gCAAgC,CAAC,QAAQ,CAAC,CAAC;IAC1D,CAAC;IAED;;;OAGG;IACI,KAAK,CAAC,gCAAgC,CACzC,YAAiC,QAAQ,EACzC,QAAwB;QAExB,MAAM,cAAc,GAAG,QAAQ,IAAI,CAAC,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QACxG,IAAI,CAAC,cAAc;YAAE,OAAO,IAAI,CAAC;QAEjC,MAAM,YAAY,GAAG,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC,CAAC,IAAI,CAAC,oBAAoB,CAAC;QACpG,IAAI,YAAY;YAAE,OAAO,YAAY,CAAC;QAEtC,MAAM,QAAQ,GAAG,IAAI,CAAC,aAA4C,CAAC;QACnE,IAAI,CAAC,QAAQ;YAAE,OAAO,IAAI,CAAC;QAE3B,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,IAAI,IAAI,IAAI,EAAE,CAAC;QAC1C,MAAM,KAAK,GAAG,MAAM,iCAAiC,CAAC,QAAQ,EAAE,cAAc,EAAE,IAAI,EAAE,IAAI,CAAC,kBAAkB,CAAC,CAAC;QAC/G,IAAI,CAAC,KAAK;YAAE,OAAO,IAAI,CAAC;QAExB,IAAI,SAAS,KAAK,QAAQ,EAAE,CAAC;YACzB,IAAI,CAAC,IAAI,CAAC,oBAAoB;gBAAE,IAAI,CAAC,oBAAoB,GAAG,KAAK,CAAC;QACtE,CAAC;aAAM,IAAI,CAAC,IAAI,CAAC,oBAAoB,EAAE,CAAC;YACpC,IAAI,CAAC,oBAAoB,GAAG,KAAK,CAAC;QACtC,CAAC;QACD,OAAO,KAAK,CAAC;IACjB,CAAC;CACJ,CAAA;AAlgBY,iBAAiB;IAD7B,aAAa,CAAC,UAAU,EAAE,kCAAkC,CAAC;GACjD,iBAAiB,CAkgB7B"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * @fileoverview `OrderLineEntity` — shared entity subclass for Order Lines.
3
+ *
4
+ * @module @mj-biz-apps/orders-entities
5
+ */
6
+ import { ValidationResult } from '@memberjunction/core';
7
+ import { mjBizAppsOrdersOrderLineEntity } from './generated/entity_subclasses.js';
8
+ import { OrderLineExtensionCompanion } from './OrderLineExtensionCompanion.js';
9
+ export declare class OrderLineEntity extends mjBizAppsOrdersOrderLineEntity {
10
+ /**
11
+ * Extension entity (e.g. `EventOrderLine`) companion riding with this line.
12
+ */
13
+ readonly Extension: OrderLineExtensionCompanion;
14
+ /**
15
+ * Runs validation on this line and fans out to the extension companion.
16
+ */
17
+ Validate(): ValidationResult;
18
+ /**
19
+ * A line that already carries its booking journal cannot change quantity or
20
+ * price. New lines on a booked order are refused on the header (graph save)
21
+ * and again in the server subclass (standalone save).
22
+ */
23
+ private refuseBookedMoneyEdits;
24
+ }
25
+ //# sourceMappingURL=OrderLineEntity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OrderLineEntity.d.ts","sourceRoot":"","sources":["../src/OrderLineEntity.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAwD,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAE9G,OAAO,EAAE,8BAA8B,EAAE,MAAM,+BAA+B,CAAC;AAC/E,OAAO,EAAE,2BAA2B,EAAE,MAAM,+BAA+B,CAAC;AAG5E,qBACa,eAAgB,SAAQ,8BAA8B;IAC/D;;OAEG;IACH,SAAgB,SAAS,8BAAiE;IAE1F;;OAEG;IACa,QAAQ,IAAI,gBAAgB;IAO5C;;;;OAIG;IACH,OAAO,CAAC,sBAAsB;CAejC"}
@@ -0,0 +1,54 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ /**
8
+ * @fileoverview `OrderLineEntity` — shared entity subclass for Order Lines.
9
+ *
10
+ * @module @mj-biz-apps/orders-entities
11
+ */
12
+ import { BaseEntity, ValidationErrorInfo, ValidationErrorType } from '@memberjunction/core';
13
+ import { RegisterClass } from '@memberjunction/global';
14
+ import { mjBizAppsOrdersOrderLineEntity } from './generated/entity_subclasses.js';
15
+ import { OrderLineExtensionCompanion } from './OrderLineExtensionCompanion.js';
16
+ import { ORDER_LINE_MONEY_FIELDS } from './booked-money.js';
17
+ let OrderLineEntity = class OrderLineEntity extends mjBizAppsOrdersOrderLineEntity {
18
+ constructor() {
19
+ super(...arguments);
20
+ /**
21
+ * Extension entity (e.g. `EventOrderLine`) companion riding with this line.
22
+ */
23
+ this.Extension = this.RegisterCompanion(new OrderLineExtensionCompanion(this));
24
+ }
25
+ /**
26
+ * Runs validation on this line and fans out to the extension companion.
27
+ */
28
+ Validate() {
29
+ const result = super.Validate();
30
+ this.Extension.Validate(result);
31
+ this.refuseBookedMoneyEdits(result);
32
+ return result;
33
+ }
34
+ /**
35
+ * A line that already carries its booking journal cannot change quantity or
36
+ * price. New lines on a booked order are refused on the header (graph save)
37
+ * and again in the server subclass (standalone save).
38
+ */
39
+ refuseBookedMoneyEdits(result) {
40
+ if (!this.JournalEntryID)
41
+ return;
42
+ const dirty = ORDER_LINE_MONEY_FIELDS.filter((name) => this.GetFieldByName(name)?.Dirty);
43
+ if (dirty.length === 0)
44
+ return;
45
+ result.Success = false;
46
+ result.Errors.push(new ValidationErrorInfo(dirty[0], `This line is booked — it cannot change ${dirty.join(', ')}. ` +
47
+ `Voiding the order is how booked money is undone.`, this.GetFieldByName(dirty[0])?.Value, ValidationErrorType.Failure));
48
+ }
49
+ };
50
+ OrderLineEntity = __decorate([
51
+ RegisterClass(BaseEntity, 'MJ_BizApps_Orders: Order Lines')
52
+ ], OrderLineEntity);
53
+ export { OrderLineEntity };
54
+ //# sourceMappingURL=OrderLineEntity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OrderLineEntity.js","sourceRoot":"","sources":["../src/OrderLineEntity.ts"],"names":[],"mappings":";;;;;;AAAA;;;;GAIG;AACH,OAAO,EAAE,UAAU,EAAE,mBAAmB,EAAE,mBAAmB,EAAoB,MAAM,sBAAsB,CAAC;AAC9G,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EAAE,8BAA8B,EAAE,MAAM,+BAA+B,CAAC;AAC/E,OAAO,EAAE,2BAA2B,EAAE,MAAM,+BAA+B,CAAC;AAC5E,OAAO,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAC;AAGlD,IAAM,eAAe,GAArB,MAAM,eAAgB,SAAQ,8BAA8B;IAA5D;;QACH;;WAEG;QACa,cAAS,GAAG,IAAI,CAAC,iBAAiB,CAAC,IAAI,2BAA2B,CAAC,IAAI,CAAC,CAAC,CAAC;IAgC9F,CAAC;IA9BG;;OAEG;IACa,QAAQ;QACpB,MAAM,MAAM,GAAG,KAAK,CAAC,QAAQ,EAAE,CAAC;QAChC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QAChC,IAAI,CAAC,sBAAsB,CAAC,MAAM,CAAC,CAAC;QACpC,OAAO,MAAM,CAAC;IAClB,CAAC;IAED;;;;OAIG;IACK,sBAAsB,CAAC,MAAwB;QACnD,IAAI,CAAC,IAAI,CAAC,cAAc;YAAE,OAAO;QACjC,MAAM,KAAK,GAAG,uBAAuB,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,EAAE,KAAK,CAAC,CAAC;QACzF,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QAC/B,MAAM,CAAC,OAAO,GAAG,KAAK,CAAC;QACvB,MAAM,CAAC,MAAM,CAAC,IAAI,CACd,IAAI,mBAAmB,CACnB,KAAK,CAAC,CAAC,CAAC,EACR,0CAA0C,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI;YAC1D,kDAAkD,EACtD,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,EACpC,mBAAmB,CAAC,OAAO,CAC9B,CACJ,CAAC;IACN,CAAC;CACJ,CAAA;AApCY,eAAe;IAD3B,aAAa,CAAC,UAAU,EAAE,gCAAgC,CAAC;GAC/C,eAAe,CAoC3B"}