@mj-biz-apps/orders-server 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.
- package/LICENSE +183 -0
- package/README.md +47 -2
- package/dist/CheckoutServerExtension.d.ts +96 -0
- package/dist/CheckoutServerExtension.d.ts.map +1 -0
- package/dist/CheckoutServerExtension.js +554 -0
- package/dist/CheckoutServerExtension.js.map +1 -0
- package/dist/PaymentWebhookExtension.d.ts +17 -0
- package/dist/PaymentWebhookExtension.d.ts.map +1 -0
- package/dist/PaymentWebhookExtension.js +151 -0
- package/dist/PaymentWebhookExtension.js.map +1 -0
- package/dist/__tests__/CheckoutServerExtension.test.d.ts +2 -0
- package/dist/__tests__/CheckoutServerExtension.test.d.ts.map +1 -0
- package/dist/__tests__/CheckoutServerExtension.test.js +257 -0
- package/dist/__tests__/CheckoutServerExtension.test.js.map +1 -0
- package/dist/__tests__/checkout-edge-policy.test.d.ts +2 -0
- package/dist/__tests__/checkout-edge-policy.test.d.ts.map +1 -0
- package/dist/__tests__/checkout-edge-policy.test.js +70 -0
- package/dist/__tests__/checkout-edge-policy.test.js.map +1 -0
- package/dist/__tests__/checkout-host-page.test.d.ts +2 -0
- package/dist/__tests__/checkout-host-page.test.d.ts.map +1 -0
- package/dist/__tests__/checkout-host-page.test.js +86 -0
- package/dist/__tests__/checkout-host-page.test.js.map +1 -0
- package/dist/__tests__/server-extensions-manifest.test.d.ts +2 -0
- package/dist/__tests__/server-extensions-manifest.test.d.ts.map +1 -0
- package/dist/__tests__/server-extensions-manifest.test.js +29 -0
- package/dist/__tests__/server-extensions-manifest.test.js.map +1 -0
- package/dist/checkout-edge-policy.d.ts +44 -0
- package/dist/checkout-edge-policy.d.ts.map +1 -0
- package/dist/checkout-edge-policy.js +84 -0
- package/dist/checkout-edge-policy.js.map +1 -0
- package/dist/checkout-host-page.d.ts +43 -0
- package/dist/checkout-host-page.d.ts.map +1 -0
- package/dist/checkout-host-page.js +445 -0
- package/dist/checkout-host-page.js.map +1 -0
- package/dist/config.d.ts +6 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +6 -0
- package/dist/config.js.map +1 -0
- package/dist/custom/generate-invoice.action.d.ts +59 -0
- package/dist/custom/generate-invoice.action.d.ts.map +1 -0
- package/dist/custom/generate-invoice.action.js +208 -0
- package/dist/custom/generate-invoice.action.js.map +1 -0
- package/dist/custom/open-payment-intent.action.d.ts +39 -0
- package/dist/custom/open-payment-intent.action.d.ts.map +1 -0
- package/dist/custom/open-payment-intent.action.js +169 -0
- package/dist/custom/open-payment-intent.action.js.map +1 -0
- package/dist/custom/send-document.action.d.ts +78 -0
- package/dist/custom/send-document.action.d.ts.map +1 -0
- package/dist/custom/send-document.action.js +277 -0
- package/dist/custom/send-document.action.js.map +1 -0
- package/dist/generated/class-registrations-manifest.d.ts +7 -0
- package/dist/generated/class-registrations-manifest.d.ts.map +1 -0
- package/dist/generated/class-registrations-manifest.js +7 -0
- package/dist/generated/class-registrations-manifest.js.map +1 -0
- package/dist/generated/generated.d.ts +3536 -0
- package/dist/generated/generated.d.ts.map +1 -0
- package/dist/generated/generated.js +18371 -0
- package/dist/generated/generated.js.map +1 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +105 -0
- package/dist/index.js.map +1 -0
- package/dist/server-extensions-manifest.d.ts +14 -0
- package/dist/server-extensions-manifest.d.ts.map +1 -0
- package/dist/server-extensions-manifest.js +15 -0
- package/dist/server-extensions-manifest.js.map +1 -0
- package/dist/services/invoice-renderer.d.ts +80 -0
- package/dist/services/invoice-renderer.d.ts.map +1 -0
- package/dist/services/invoice-renderer.js +103 -0
- package/dist/services/invoice-renderer.js.map +1 -0
- package/package.json +65 -5
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview `Orders: Generate Invoice` — hand it any order, get back the document you send the
|
|
3
|
+
* customer.
|
|
4
|
+
*
|
|
5
|
+
* WHY AN ACTION AND NOT A REMOTE OPERATION. The other order verbs — confirm, capture, fulfil — are
|
|
6
|
+
* operations because they are the API a browser calls to change something. This one changes
|
|
7
|
+
* nothing, and its callers are different in kind: a workflow that emails a bill on confirmation, an
|
|
8
|
+
* agent asked to "send Contoso their invoice", a scheduled job producing a month's PDFs, a person
|
|
9
|
+
* clicking Print. Actions are the surface all four already speak, and they are discoverable in
|
|
10
|
+
* metadata rather than only from code.
|
|
11
|
+
*
|
|
12
|
+
* WHAT IT COMPOSES. This class does no arithmetic, no formatting and no markup. It resolves inputs,
|
|
13
|
+
* calls the reader, the display layer and the template engine in that order, and hands back what
|
|
14
|
+
* they produced:
|
|
15
|
+
*
|
|
16
|
+
* `InvoiceBuilder` → the document, per selling company, derived from the order
|
|
17
|
+
* `InvoiceDisplay` → the strings, so the template can print without computing
|
|
18
|
+
* MJ Templates → the HTML, editable in the database with no deploy
|
|
19
|
+
*
|
|
20
|
+
* IT RETURNS THE DATA ALONGSIDE THE HTML. A caller that wants a PDF renders the HTML; a caller
|
|
21
|
+
* doing a reconciliation check wants the numbers, and making it scrape them back out of markup
|
|
22
|
+
* would be a guarantee that the check and the document eventually disagree.
|
|
23
|
+
*
|
|
24
|
+
* IT NEVER WRITES. There is no invoice record — the confirmed order IS the receivable — so
|
|
25
|
+
* rendering the same order twice produces two identical documents. See the header of
|
|
26
|
+
* `InvoiceBehavior` for why storing one would be a mistake, and why statements are the opposite
|
|
27
|
+
* case.
|
|
28
|
+
*
|
|
29
|
+
* @module @mj-biz-apps/orders-actions
|
|
30
|
+
*/
|
|
31
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
32
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
33
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
34
|
+
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;
|
|
35
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
36
|
+
};
|
|
37
|
+
import { BaseAction } from '@memberjunction/actions';
|
|
38
|
+
import { Metadata } from '@memberjunction/core';
|
|
39
|
+
import { RegisterClass } from '@memberjunction/global';
|
|
40
|
+
import { DEFAULT_INVOICE_TEMPLATE, RenderInvoiceDocuments, } from '../services/invoice-renderer.js';
|
|
41
|
+
// Re-exported for the callers that imported it from here before the render orchestration moved into
|
|
42
|
+
// the shared service. The constant itself now lives with the code that uses it.
|
|
43
|
+
export { DEFAULT_INVOICE_TEMPLATE } from '../services/invoice-renderer.js';
|
|
44
|
+
const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
45
|
+
/** Read a parameter case-insensitively — action callers are not consistent about casing. */
|
|
46
|
+
function param(params, name) {
|
|
47
|
+
return params.Params?.find((p) => p.Name?.toLowerCase() === name.toLowerCase())?.Value;
|
|
48
|
+
}
|
|
49
|
+
function boolParam(params, name, fallback) {
|
|
50
|
+
const raw = param(params, name);
|
|
51
|
+
if (raw == null || raw === '')
|
|
52
|
+
return fallback;
|
|
53
|
+
if (typeof raw === 'boolean')
|
|
54
|
+
return raw;
|
|
55
|
+
return ['true', '1', 'yes'].includes(String(raw).trim().toLowerCase());
|
|
56
|
+
}
|
|
57
|
+
function strParam(params, name) {
|
|
58
|
+
const raw = param(params, name);
|
|
59
|
+
if (raw == null)
|
|
60
|
+
return null;
|
|
61
|
+
const value = String(raw).trim();
|
|
62
|
+
return value.length ? value : null;
|
|
63
|
+
}
|
|
64
|
+
/** Attach an output value, replacing any placeholder the engine already put there. */
|
|
65
|
+
function setOutput(params, name, value) {
|
|
66
|
+
const existing = params.Params?.find((p) => p.Name?.toLowerCase() === name.toLowerCase());
|
|
67
|
+
if (existing) {
|
|
68
|
+
existing.Value = value;
|
|
69
|
+
existing.Type = 'Output';
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
params.Params = params.Params ?? [];
|
|
73
|
+
params.Params.push({ Name: name, Value: value, Type: 'Output' });
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Render an order as an invoice, quote or credit memo.
|
|
77
|
+
*
|
|
78
|
+
* Inputs: `OrderID` (required), `CompanyID`, `AsOfDate`, `TemplateName`, `Locale`, `CurrencyCode`,
|
|
79
|
+
* `Format`, `ShowDiagnostics`. Outputs: `Invoices`, `HTML`, `DocumentCount`, `Notes`.
|
|
80
|
+
*/
|
|
81
|
+
let GenerateInvoiceAction = class GenerateInvoiceAction extends BaseAction {
|
|
82
|
+
/**
|
|
83
|
+
* AN ACTION MUST NOT THROW AT ITS CALLER. The layers below guard their inputs by throwing —
|
|
84
|
+
* `RequireUUID` is what keeps an id out of a SQL string — and a workflow, an agent or a print
|
|
85
|
+
* button calling this gets an exception rather than a result it can branch on. So the whole body
|
|
86
|
+
* runs behind this, and every failure comes back as a Success=false with a code.
|
|
87
|
+
*/
|
|
88
|
+
async InternalRunAction(params) {
|
|
89
|
+
try {
|
|
90
|
+
return await this.render(params);
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
return {
|
|
94
|
+
Success: false,
|
|
95
|
+
ResultCode: 'ERROR',
|
|
96
|
+
Message: `Could not produce the invoice: ${error instanceof Error ? error.message : String(error)}`,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
async render(params) {
|
|
101
|
+
const orderID = strParam(params, 'OrderID');
|
|
102
|
+
if (!orderID) {
|
|
103
|
+
return {
|
|
104
|
+
Success: false,
|
|
105
|
+
ResultCode: 'MISSING_ORDER_ID',
|
|
106
|
+
Message: 'OrderID is required — this action renders an existing order and has nothing to render without one.',
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
if (!UUID.test(orderID)) {
|
|
110
|
+
// Refused HERE rather than let the reader's SQL guard throw, so a caller passing a
|
|
111
|
+
// record number, a blank string or an injection attempt gets an answer it can branch on.
|
|
112
|
+
return {
|
|
113
|
+
Success: false,
|
|
114
|
+
ResultCode: 'INVALID_ORDER_ID',
|
|
115
|
+
Message: `'${orderID}' is not an order ID. This action takes the OrderHeader's ID, not its order number.`,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
const companyID = strParam(params, 'CompanyID');
|
|
119
|
+
if (companyID && !UUID.test(companyID)) {
|
|
120
|
+
return {
|
|
121
|
+
Success: false,
|
|
122
|
+
ResultCode: 'INVALID_COMPANY_ID',
|
|
123
|
+
Message: `'${companyID}' is not a company ID.`,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
const user = params.ContextUser;
|
|
127
|
+
if (!user) {
|
|
128
|
+
return {
|
|
129
|
+
Success: false,
|
|
130
|
+
ResultCode: 'MISSING_USER',
|
|
131
|
+
Message: 'ContextUser is required: the order is read through the metadata layer, which resolves permissions per user.',
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
// Honour a caller's provider when one is threaded through — an invoice rendered inside a
|
|
135
|
+
// transaction must read the same connection that owns it, or it reports the order as it was
|
|
136
|
+
// before the work it is meant to document.
|
|
137
|
+
//
|
|
138
|
+
// The fallback is `Metadata.Provider`, NOT `new Metadata()`. A `Metadata` instance is a
|
|
139
|
+
// facade over the provider and does not implement `IMetadataProvider`; handing one to
|
|
140
|
+
// `RunView.FromMetadataProvider` type-checks only through a cast and fails at the first
|
|
141
|
+
// read with "RunView is not a function".
|
|
142
|
+
const provider = params.Provider ?? Metadata.Provider;
|
|
143
|
+
if (!provider) {
|
|
144
|
+
return {
|
|
145
|
+
Success: false,
|
|
146
|
+
ResultCode: 'NO_PROVIDER',
|
|
147
|
+
Message: 'No metadata provider is configured — the data layer has not been set up in this process.',
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
const format = (strParam(params, 'Format') ?? 'HTML').toUpperCase();
|
|
151
|
+
if (!['HTML', 'DATA'].includes(format)) {
|
|
152
|
+
return {
|
|
153
|
+
Success: false,
|
|
154
|
+
ResultCode: 'UNSUPPORTED_FORMAT',
|
|
155
|
+
Message: `Format '${format}' is not supported. Use 'HTML' for a rendered document or 'DATA' for the figures alone. PDF is produced by rendering this HTML — see the operations notes.`,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
const rendered = await RenderInvoiceDocuments(orderID, provider, user, {
|
|
159
|
+
CompanyID: companyID,
|
|
160
|
+
AsOfDate: strParam(params, 'AsOfDate'),
|
|
161
|
+
TemplateName: strParam(params, 'TemplateName') ?? DEFAULT_INVOICE_TEMPLATE,
|
|
162
|
+
Locale: strParam(params, 'Locale') ?? 'en-US',
|
|
163
|
+
CurrencyCode: strParam(params, 'CurrencyCode'),
|
|
164
|
+
Format: format,
|
|
165
|
+
ShowDiagnostics: boolParam(params, 'ShowDiagnostics', false),
|
|
166
|
+
});
|
|
167
|
+
if (!rendered.Success) {
|
|
168
|
+
return {
|
|
169
|
+
Success: false,
|
|
170
|
+
ResultCode: rendered.Code ?? 'RENDER_FAILED',
|
|
171
|
+
Message: rendered.Message ?? 'The order could not be rendered.',
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
const invoices = rendered.Documents;
|
|
175
|
+
setOutput(params, 'Invoices', invoices);
|
|
176
|
+
// The scalar HTML is a convenience for the overwhelmingly common single-company order. It is
|
|
177
|
+
// deliberately null when an order split, rather than silently handing back the first of two
|
|
178
|
+
// documents as though it were the whole bill.
|
|
179
|
+
setOutput(params, 'HTML', invoices.length === 1 ? invoices[0].HTML : null);
|
|
180
|
+
setOutput(params, 'DocumentCount', invoices.length);
|
|
181
|
+
const notes = invoices.flatMap((i) => i.Notes.map((n) => `${i.DocumentNumber}: ${n}`));
|
|
182
|
+
setOutput(params, 'Notes', notes);
|
|
183
|
+
const kind = invoices[0]?.Kind ?? 'Invoice';
|
|
184
|
+
return {
|
|
185
|
+
Success: true,
|
|
186
|
+
ResultCode: invoices.length > 1 ? 'SPLIT_BY_COMPANY' : 'SUCCESS',
|
|
187
|
+
Params: params.Params,
|
|
188
|
+
Message: invoices.length > 1
|
|
189
|
+
? `This order is sold by ${invoices.length} companies, so it produces ${invoices.length} documents: ${invoices.map((i) => i.DocumentNumber).join(', ')}. Each one bills only what its company is owed.`
|
|
190
|
+
: `${kind} ${invoices[0]?.DocumentNumber ?? ''} rendered.`,
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
GenerateInvoiceAction = __decorate([
|
|
195
|
+
RegisterClass(BaseAction, 'Orders.GenerateInvoice')
|
|
196
|
+
], GenerateInvoiceAction);
|
|
197
|
+
export { GenerateInvoiceAction };
|
|
198
|
+
/**
|
|
199
|
+
* Registers {@link GenerateInvoiceAction}.
|
|
200
|
+
*
|
|
201
|
+
* WITHOUT THIS ANCHOR the class is tree-shaken out of the bundle, the `@RegisterClass` decorator
|
|
202
|
+
* never runs, and `ActionEngine` falls back to no implementation — which surfaces as an action that
|
|
203
|
+
* exists in metadata and does nothing.
|
|
204
|
+
*/
|
|
205
|
+
export function LoadGenerateInvoiceAction() {
|
|
206
|
+
void GenerateInvoiceAction;
|
|
207
|
+
}
|
|
208
|
+
//# sourceMappingURL=generate-invoice.action.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate-invoice.action.js","sourceRoot":"","sources":["../../src/custom/generate-invoice.action.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;;;;;;;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAErD,OAAO,EAAE,QAAQ,EAA0B,MAAM,sBAAsB,CAAC;AACxE,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EACH,wBAAwB,EACxB,sBAAsB,GAEzB,MAAM,iCAAiC,CAAC;AAEzC,oGAAoG;AACpG,gFAAgF;AAChF,OAAO,EAAE,wBAAwB,EAAE,MAAM,iCAAiC,CAAC;AAG3E,MAAM,IAAI,GAAG,iEAAiE,CAAC;AAE/E,4FAA4F;AAC5F,SAAS,KAAK,CAAC,MAAuB,EAAE,IAAY;IAChD,OAAO,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,WAAW,EAAE,KAAK,IAAI,CAAC,WAAW,EAAE,CAAC,EAAE,KAAK,CAAC;AAC3F,CAAC;AAED,SAAS,SAAS,CAAC,MAAuB,EAAE,IAAY,EAAE,QAAiB;IACvE,MAAM,GAAG,GAAG,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAChC,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,KAAK,EAAE;QAAE,OAAO,QAAQ,CAAC;IAC/C,IAAI,OAAO,GAAG,KAAK,SAAS;QAAE,OAAO,GAAG,CAAC;IACzC,OAAO,CAAC,MAAM,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC;AAC3E,CAAC;AAED,SAAS,QAAQ,CAAC,MAAuB,EAAE,IAAY;IACnD,MAAM,GAAG,GAAG,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAChC,IAAI,GAAG,IAAI,IAAI;QAAE,OAAO,IAAI,CAAC;IAC7B,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;IACjC,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;AACvC,CAAC;AAED,sFAAsF;AACtF,SAAS,SAAS,CAAC,MAAuB,EAAE,IAAY,EAAE,KAAc;IACpE,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,WAAW,EAAE,KAAK,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC;IAC1F,IAAI,QAAQ,EAAE,CAAC;QACX,QAAQ,CAAC,KAAK,GAAG,KAAK,CAAC;QACvB,QAAQ,CAAC,IAAI,GAAG,QAAQ,CAAC;QACzB,OAAO;IACX,CAAC;IACD,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,IAAI,EAAE,CAAC;IACpC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAiB,CAAC,CAAC;AACpF,CAAC;AAED;;;;;GAKG;AAEI,IAAM,qBAAqB,GAA3B,MAAM,qBAAsB,SAAQ,UAAU;IACjD;;;;;OAKG;IACO,KAAK,CAAC,iBAAiB,CAAC,MAAuB;QACrD,IAAI,CAAC;YACD,OAAO,MAAM,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QACrC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,OAAO;gBACH,OAAO,EAAE,KAAK;gBACd,UAAU,EAAE,OAAO;gBACnB,OAAO,EAAE,kCAAkC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE;aACtG,CAAC;QACN,CAAC;IACL,CAAC;IAEO,KAAK,CAAC,MAAM,CAAC,MAAuB;QACxC,MAAM,OAAO,GAAG,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;QAC5C,IAAI,CAAC,OAAO,EAAE,CAAC;YACX,OAAO;gBACH,OAAO,EAAE,KAAK;gBACd,UAAU,EAAE,kBAAkB;gBAC9B,OAAO,EAAE,oGAAoG;aAChH,CAAC;QACN,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;YACtB,mFAAmF;YACnF,yFAAyF;YACzF,OAAO;gBACH,OAAO,EAAE,KAAK;gBACd,UAAU,EAAE,kBAAkB;gBAC9B,OAAO,EAAE,IAAI,OAAO,qFAAqF;aAC5G,CAAC;QACN,CAAC;QAED,MAAM,SAAS,GAAG,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;QAChD,IAAI,SAAS,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;YACrC,OAAO;gBACH,OAAO,EAAE,KAAK;gBACd,UAAU,EAAE,oBAAoB;gBAChC,OAAO,EAAE,IAAI,SAAS,wBAAwB;aACjD,CAAC;QACN,CAAC;QAED,MAAM,IAAI,GAAG,MAAM,CAAC,WAAW,CAAC;QAChC,IAAI,CAAC,IAAI,EAAE,CAAC;YACR,OAAO;gBACH,OAAO,EAAE,KAAK;gBACd,UAAU,EAAE,cAAc;gBAC1B,OAAO,EAAE,6GAA6G;aACzH,CAAC;QACN,CAAC;QAED,yFAAyF;QACzF,4FAA4F;QAC5F,2CAA2C;QAC3C,EAAE;QACF,wFAAwF;QACxF,sFAAsF;QACtF,wFAAwF;QACxF,yCAAyC;QACzC,MAAM,QAAQ,GAAsB,MAAM,CAAC,QAAQ,IAAI,QAAQ,CAAC,QAAQ,CAAC;QACzE,IAAI,CAAC,QAAQ,EAAE,CAAC;YACZ,OAAO;gBACH,OAAO,EAAE,KAAK;gBACd,UAAU,EAAE,aAAa;gBACzB,OAAO,EAAE,0FAA0F;aACtG,CAAC;QACN,CAAC;QAED,MAAM,MAAM,GAAG,CAAC,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,IAAI,MAAM,CAAC,CAAC,WAAW,EAAE,CAAC;QACpE,IAAI,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;YACrC,OAAO;gBACH,OAAO,EAAE,KAAK;gBACd,UAAU,EAAE,oBAAoB;gBAChC,OAAO,EAAE,WAAW,MAAM,4JAA4J;aACzL,CAAC;QACN,CAAC;QAED,MAAM,QAAQ,GAAG,MAAM,sBAAsB,CAAC,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE;YACnE,SAAS,EAAE,SAAS;YACpB,QAAQ,EAAE,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;YACtC,YAAY,EAAE,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC,IAAI,wBAAwB;YAC1E,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,IAAI,OAAO;YAC7C,YAAY,EAAE,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC;YAC9C,MAAM,EAAE,MAAyB;YACjC,eAAe,EAAE,SAAS,CAAC,MAAM,EAAE,iBAAiB,EAAE,KAAK,CAAC;SAC/D,CAAC,CAAC;QAEH,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC;YACpB,OAAO;gBACH,OAAO,EAAE,KAAK;gBACd,UAAU,EAAE,QAAQ,CAAC,IAAI,IAAI,eAAe;gBAC5C,OAAO,EAAE,QAAQ,CAAC,OAAO,IAAI,kCAAkC;aAClE,CAAC;QACN,CAAC;QAED,MAAM,QAAQ,GAAsB,QAAQ,CAAC,SAAS,CAAC;QAEvD,SAAS,CAAC,MAAM,EAAE,UAAU,EAAE,QAAQ,CAAC,CAAC;QACxC,6FAA6F;QAC7F,4FAA4F;QAC5F,8CAA8C;QAC9C,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAC3E,SAAS,CAAC,MAAM,EAAE,eAAe,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;QAEpD,MAAM,KAAK,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,cAAc,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;QACvF,SAAS,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC;QAElC,MAAM,IAAI,GAAG,QAAQ,CAAC,CAAC,CAAC,EAAE,IAAI,IAAI,SAAS,CAAC;QAC5C,OAAO;YACH,OAAO,EAAE,IAAI;YACb,UAAU,EAAE,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,SAAS;YAChE,MAAM,EAAE,MAAM,CAAC,MAAM;YACrB,OAAO,EACH,QAAQ,CAAC,MAAM,GAAG,CAAC;gBACf,CAAC,CAAC,yBAAyB,QAAQ,CAAC,MAAM,8BAA8B,QAAQ,CAAC,MAAM,eAAe,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,iDAAiD;gBACvM,CAAC,CAAC,GAAG,IAAI,IAAI,QAAQ,CAAC,CAAC,CAAC,EAAE,cAAc,IAAI,EAAE,YAAY;SACrE,CAAC;IACN,CAAC;CACJ,CAAA;AA3HY,qBAAqB;IADjC,aAAa,CAAC,UAAU,EAAE,wBAAwB,CAAC;GACvC,qBAAqB,CA2HjC;;AAED;;;;;;GAMG;AACH,MAAM,UAAU,yBAAyB;IACrC,KAAK,qBAAqB,CAAC;AAC/B,CAAC"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview `Orders: Open Payment Intent` — ask a gateway to stand ready to take money.
|
|
3
|
+
*
|
|
4
|
+
* WHY AN ACTION. Three callers want this and they are different in kind: the customer portal opening
|
|
5
|
+
* a checkout, a renewal job charging a saved mandate on schedule, and a person in the back office
|
|
6
|
+
* sending a pay link. Actions are the surface all three already speak, and they are discoverable in
|
|
7
|
+
* metadata rather than only from code.
|
|
8
|
+
*
|
|
9
|
+
* IT MOVES NO MONEY AND WRITES NO PAYMENT. Opening an intent is the gateway agreeing to stand ready;
|
|
10
|
+
* nothing is owed differently because of it, and an intent nobody completes simply expires. The
|
|
11
|
+
* payment is written later by `Orders.CapturePayment`, which takes the `PaymentIntentID` this returns.
|
|
12
|
+
*
|
|
13
|
+
* THE ORDER OF THE TWO CALLS IS THE WHOLE POINT. Until now `CreateIntent` existed on every driver and
|
|
14
|
+
* was called by nothing but unit tests, so a provider-backed capture always failed with "there is
|
|
15
|
+
* nothing for the gateway to capture". This action is the missing first step.
|
|
16
|
+
*
|
|
17
|
+
* THE CLIENT SECRET IS AN OUTPUT AND IS NEVER PERSISTED. It authorises a browser to confirm this one
|
|
18
|
+
* intent. Callers should hand it straight to Stripe.js and not log it.
|
|
19
|
+
*
|
|
20
|
+
* @module @mj-biz-apps/orders-actions
|
|
21
|
+
*/
|
|
22
|
+
import { BaseAction } from '@memberjunction/actions';
|
|
23
|
+
import type { ActionResultSimple, RunActionParams } from '@memberjunction/actions-base';
|
|
24
|
+
/**
|
|
25
|
+
* Open a gateway payment intent.
|
|
26
|
+
*
|
|
27
|
+
* Inputs: `PaymentProviderID` (required), `Amount` (required), `CurrencyCode`, `OrderHeaderID`,
|
|
28
|
+
* `BillToPersonID`, `BillToOrganizationID`, `ProviderCustomerRef`, `ProviderInstrumentRef`,
|
|
29
|
+
* `IdempotencyKey`.
|
|
30
|
+
* Outputs: `PaymentIntentID`, `ProviderIntentID`, `Status`, `ClientSecret`, `HostedUrl`, `WasExisting`.
|
|
31
|
+
*/
|
|
32
|
+
export declare class OpenPaymentIntentAction extends BaseAction {
|
|
33
|
+
/** An action must not throw at its caller — same reasoning as the other hand-authored actions. */
|
|
34
|
+
protected InternalRunAction(params: RunActionParams): Promise<ActionResultSimple>;
|
|
35
|
+
private open;
|
|
36
|
+
}
|
|
37
|
+
/** Tree-shaking anchor — without it the decorator never runs and the action has nothing behind it. */
|
|
38
|
+
export declare function LoadOpenPaymentIntentAction(): void;
|
|
39
|
+
//# sourceMappingURL=open-payment-intent.action.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"open-payment-intent.action.d.ts","sourceRoot":"","sources":["../../src/custom/open-payment-intent.action.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACrD,OAAO,KAAK,EAAe,kBAAkB,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAoCrG;;;;;;;GAOG;AACH,qBACa,uBAAwB,SAAQ,UAAU;IACnD,kGAAkG;cAClF,iBAAiB,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,kBAAkB,CAAC;YAYzE,IAAI;CA0FrB;AAED,sGAAsG;AACtG,wBAAgB,2BAA2B,IAAI,IAAI,CAElD"}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview `Orders: Open Payment Intent` — ask a gateway to stand ready to take money.
|
|
3
|
+
*
|
|
4
|
+
* WHY AN ACTION. Three callers want this and they are different in kind: the customer portal opening
|
|
5
|
+
* a checkout, a renewal job charging a saved mandate on schedule, and a person in the back office
|
|
6
|
+
* sending a pay link. Actions are the surface all three already speak, and they are discoverable in
|
|
7
|
+
* metadata rather than only from code.
|
|
8
|
+
*
|
|
9
|
+
* IT MOVES NO MONEY AND WRITES NO PAYMENT. Opening an intent is the gateway agreeing to stand ready;
|
|
10
|
+
* nothing is owed differently because of it, and an intent nobody completes simply expires. The
|
|
11
|
+
* payment is written later by `Orders.CapturePayment`, which takes the `PaymentIntentID` this returns.
|
|
12
|
+
*
|
|
13
|
+
* THE ORDER OF THE TWO CALLS IS THE WHOLE POINT. Until now `CreateIntent` existed on every driver and
|
|
14
|
+
* was called by nothing but unit tests, so a provider-backed capture always failed with "there is
|
|
15
|
+
* nothing for the gateway to capture". This action is the missing first step.
|
|
16
|
+
*
|
|
17
|
+
* THE CLIENT SECRET IS AN OUTPUT AND IS NEVER PERSISTED. It authorises a browser to confirm this one
|
|
18
|
+
* intent. Callers should hand it straight to Stripe.js and not log it.
|
|
19
|
+
*
|
|
20
|
+
* @module @mj-biz-apps/orders-actions
|
|
21
|
+
*/
|
|
22
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
23
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
24
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
25
|
+
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;
|
|
26
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
27
|
+
};
|
|
28
|
+
import { BaseAction } from '@memberjunction/actions';
|
|
29
|
+
import { Metadata } from '@memberjunction/core';
|
|
30
|
+
import { RegisterClass } from '@memberjunction/global';
|
|
31
|
+
import { OpenPaymentIntent } from '@mj-biz-apps/orders-core-entities-server';
|
|
32
|
+
const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
33
|
+
function param(params, name) {
|
|
34
|
+
return params.Params?.find((p) => p.Name?.toLowerCase() === name.toLowerCase())?.Value;
|
|
35
|
+
}
|
|
36
|
+
function strParam(params, name) {
|
|
37
|
+
const raw = param(params, name);
|
|
38
|
+
if (raw == null)
|
|
39
|
+
return null;
|
|
40
|
+
const value = String(raw).trim();
|
|
41
|
+
return value.length ? value : null;
|
|
42
|
+
}
|
|
43
|
+
function numParam(params, name) {
|
|
44
|
+
const raw = param(params, name);
|
|
45
|
+
if (raw == null || raw === '')
|
|
46
|
+
return null;
|
|
47
|
+
const value = Number(raw);
|
|
48
|
+
return Number.isFinite(value) ? value : null;
|
|
49
|
+
}
|
|
50
|
+
function setOutput(params, name, value) {
|
|
51
|
+
const existing = params.Params?.find((p) => p.Name?.toLowerCase() === name.toLowerCase());
|
|
52
|
+
if (existing) {
|
|
53
|
+
existing.Value = value;
|
|
54
|
+
existing.Type = 'Output';
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
params.Params = params.Params ?? [];
|
|
58
|
+
params.Params.push({ Name: name, Value: value, Type: 'Output' });
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Open a gateway payment intent.
|
|
62
|
+
*
|
|
63
|
+
* Inputs: `PaymentProviderID` (required), `Amount` (required), `CurrencyCode`, `OrderHeaderID`,
|
|
64
|
+
* `BillToPersonID`, `BillToOrganizationID`, `ProviderCustomerRef`, `ProviderInstrumentRef`,
|
|
65
|
+
* `IdempotencyKey`.
|
|
66
|
+
* Outputs: `PaymentIntentID`, `ProviderIntentID`, `Status`, `ClientSecret`, `HostedUrl`, `WasExisting`.
|
|
67
|
+
*/
|
|
68
|
+
let OpenPaymentIntentAction = class OpenPaymentIntentAction extends BaseAction {
|
|
69
|
+
/** An action must not throw at its caller — same reasoning as the other hand-authored actions. */
|
|
70
|
+
async InternalRunAction(params) {
|
|
71
|
+
try {
|
|
72
|
+
return await this.open(params);
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
return {
|
|
76
|
+
Success: false,
|
|
77
|
+
ResultCode: 'ERROR',
|
|
78
|
+
Message: `Could not open a payment intent: ${error instanceof Error ? error.message : String(error)}`,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
async open(params) {
|
|
83
|
+
const providerID = strParam(params, 'PaymentProviderID');
|
|
84
|
+
if (!providerID) {
|
|
85
|
+
return {
|
|
86
|
+
Success: false,
|
|
87
|
+
ResultCode: 'MISSING_PROVIDER_ID',
|
|
88
|
+
Message: 'PaymentProviderID is required — it decides which gateway account to ask and which credentials to use.',
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
if (!UUID.test(providerID)) {
|
|
92
|
+
return {
|
|
93
|
+
Success: false,
|
|
94
|
+
ResultCode: 'INVALID_PROVIDER_ID',
|
|
95
|
+
Message: `'${providerID}' is not a payment provider ID. This takes the PaymentProvider row's ID, not the type code.`,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
for (const optional of ['OrderHeaderID', 'BillToPersonID', 'BillToOrganizationID']) {
|
|
99
|
+
const value = strParam(params, optional);
|
|
100
|
+
if (value && !UUID.test(value)) {
|
|
101
|
+
return { Success: false, ResultCode: 'INVALID_ID', Message: `'${value}' is not a valid ${optional}.` };
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
const amount = numParam(params, 'Amount');
|
|
105
|
+
if (amount == null || amount <= 0) {
|
|
106
|
+
return {
|
|
107
|
+
Success: false,
|
|
108
|
+
ResultCode: 'INVALID_AMOUNT',
|
|
109
|
+
Message: `Amount must be a positive number; received '${param(params, 'Amount')}'.`,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
const user = params.ContextUser;
|
|
113
|
+
if (!user) {
|
|
114
|
+
return {
|
|
115
|
+
Success: false,
|
|
116
|
+
ResultCode: 'MISSING_USER',
|
|
117
|
+
Message: 'ContextUser is required: the provider and its credentials are resolved through the metadata layer.',
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
const provider = params.Provider ?? Metadata.Provider;
|
|
121
|
+
if (!provider) {
|
|
122
|
+
return { Success: false, ResultCode: 'NO_PROVIDER', Message: 'No metadata provider is configured.' };
|
|
123
|
+
}
|
|
124
|
+
const result = await OpenPaymentIntent({
|
|
125
|
+
PaymentProviderID: providerID,
|
|
126
|
+
Amount: amount,
|
|
127
|
+
CurrencyCode: strParam(params, 'CurrencyCode') ?? 'USD',
|
|
128
|
+
OrderHeaderID: strParam(params, 'OrderHeaderID'),
|
|
129
|
+
BillToPersonID: strParam(params, 'BillToPersonID'),
|
|
130
|
+
BillToOrganizationID: strParam(params, 'BillToOrganizationID'),
|
|
131
|
+
ProviderCustomerRef: strParam(params, 'ProviderCustomerRef'),
|
|
132
|
+
ProviderInstrumentRef: strParam(params, 'ProviderInstrumentRef'),
|
|
133
|
+
IdempotencyKey: strParam(params, 'IdempotencyKey'),
|
|
134
|
+
}, provider, user);
|
|
135
|
+
if (!result.Success) {
|
|
136
|
+
// A gateway refusal, not a fault. Reported with the gateway's own words so the person
|
|
137
|
+
// retrying knows whether to change something or just try again.
|
|
138
|
+
return {
|
|
139
|
+
Success: false,
|
|
140
|
+
ResultCode: 'GATEWAY_REFUSED',
|
|
141
|
+
Params: params.Params,
|
|
142
|
+
Message: result.Reason ?? 'The gateway did not open a payment intent.',
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
setOutput(params, 'PaymentIntentID', result.PaymentIntentID);
|
|
146
|
+
setOutput(params, 'ProviderIntentID', result.ProviderIntentID);
|
|
147
|
+
setOutput(params, 'Status', result.Status);
|
|
148
|
+
setOutput(params, 'ClientSecret', result.ClientSecret);
|
|
149
|
+
setOutput(params, 'HostedUrl', result.HostedUrl);
|
|
150
|
+
setOutput(params, 'WasExisting', result.WasExisting === true);
|
|
151
|
+
return {
|
|
152
|
+
Success: true,
|
|
153
|
+
ResultCode: result.WasExisting ? 'ALREADY_OPEN' : 'OPENED',
|
|
154
|
+
Params: params.Params,
|
|
155
|
+
Message: result.WasExisting
|
|
156
|
+
? `Payment intent ${result.ProviderIntentID} was already open; reusing it.`
|
|
157
|
+
: `Opened payment intent ${result.ProviderIntentID} (${result.Status}). Pass PaymentIntentID to Orders.CapturePayment when the money is confirmed.`,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
OpenPaymentIntentAction = __decorate([
|
|
162
|
+
RegisterClass(BaseAction, 'Orders.OpenPaymentIntent')
|
|
163
|
+
], OpenPaymentIntentAction);
|
|
164
|
+
export { OpenPaymentIntentAction };
|
|
165
|
+
/** Tree-shaking anchor — without it the decorator never runs and the action has nothing behind it. */
|
|
166
|
+
export function LoadOpenPaymentIntentAction() {
|
|
167
|
+
void OpenPaymentIntentAction;
|
|
168
|
+
}
|
|
169
|
+
//# sourceMappingURL=open-payment-intent.action.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"open-payment-intent.action.js","sourceRoot":"","sources":["../../src/custom/open-payment-intent.action.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview `Orders: Send Document` — render a customer-facing document and actually send it.
|
|
3
|
+
*
|
|
4
|
+
* WHY THIS IS SEPARATE FROM `Orders: Generate Invoice`. Generating writes nothing and can be called a
|
|
5
|
+
* hundred times safely — a print button, a reconciliation check, a PDF job. Sending is IRREVERSIBLE and
|
|
6
|
+
* reaches a person outside the company. Folding a `Send: true` flag into the render action would put
|
|
7
|
+
* the single most consequential side effect in this app behind a boolean that is easy to set by
|
|
8
|
+
* accident, in an action whose whole documented contract is that it never writes.
|
|
9
|
+
*
|
|
10
|
+
* WHAT IT COMPOSES, and the order is the design:
|
|
11
|
+
*
|
|
12
|
+
* 1. RENDER `RenderInvoiceDocuments` — the same service `Orders.GenerateInvoice` calls
|
|
13
|
+
* 2. RESOLVE who the order says it is billed to, and their recorded addresses
|
|
14
|
+
* 3. DECIDE `DecideDelivery` — is this a bill at all, and is there anybody to send it to
|
|
15
|
+
* 4. DELIVER the registered channel for the requested code
|
|
16
|
+
*
|
|
17
|
+
* Every step is somebody else's code. This action extracts parameters, sequences four calls, and maps
|
|
18
|
+
* the answers onto result codes — which is what an action is supposed to be.
|
|
19
|
+
*
|
|
20
|
+
* ONE DOCUMENT PER SELLING COMPANY, ONE SEND EACH. An order sold by two companies is two receivables
|
|
21
|
+
* owed to two legal entities, and each has its own remit-to details. They are sent as two messages,
|
|
22
|
+
* and if the second fails the first is still reported as sent — the alternative, reporting the whole
|
|
23
|
+
* thing as failed, invites a retry that sends the first one twice.
|
|
24
|
+
*
|
|
25
|
+
* IT IS NOT IDEMPOTENT, AND SAYS SO. Calling it twice sends the document twice. `DeliveryIdempotencyKey`
|
|
26
|
+
* exists so a caller that needs at-most-once can build it, but this action cannot make that judgement:
|
|
27
|
+
* re-sending because the customer asked again is ordinary, and re-sending because a workflow retried is
|
|
28
|
+
* not, and only the caller knows which happened.
|
|
29
|
+
*
|
|
30
|
+
* @module @mj-biz-apps/orders-actions
|
|
31
|
+
*/
|
|
32
|
+
import { BaseAction } from '@memberjunction/actions';
|
|
33
|
+
import type { ActionResultSimple, RunActionParams } from '@memberjunction/actions-base';
|
|
34
|
+
/** What happened to one document. */
|
|
35
|
+
export interface DocumentDeliveryOutcome {
|
|
36
|
+
DocumentNumber: string;
|
|
37
|
+
Kind: string;
|
|
38
|
+
CompanyID: string;
|
|
39
|
+
Sent: boolean;
|
|
40
|
+
/** Addresses the channel accepted. */
|
|
41
|
+
Recipients: string[];
|
|
42
|
+
/** Why not, when not. */
|
|
43
|
+
Reason?: string;
|
|
44
|
+
/** A stable key for "this document, this channel, this address" — for a caller doing its own dedup. */
|
|
45
|
+
IdempotencyKeys: string[];
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Send an order's document to the customer.
|
|
49
|
+
*
|
|
50
|
+
* Inputs: `OrderID` (required), `CompanyID`, `Channel`, `TemplateName`, `Locale`, `CurrencyCode`,
|
|
51
|
+
* `ToAddress`, `CC`, `BCC`, `From`, `FromName`, `PreviewOnly`.
|
|
52
|
+
* Outputs: `Deliveries`, `SentCount`, `FailedCount`.
|
|
53
|
+
*/
|
|
54
|
+
export declare class SendDocumentAction extends BaseAction {
|
|
55
|
+
/**
|
|
56
|
+
* AN ACTION MUST NOT THROW AT ITS CALLER — same reasoning as `Orders.GenerateInvoice`. The layers
|
|
57
|
+
* below guard by throwing, and a workflow or agent needs a result it can branch on.
|
|
58
|
+
*/
|
|
59
|
+
protected InternalRunAction(params: RunActionParams): Promise<ActionResultSimple>;
|
|
60
|
+
private send;
|
|
61
|
+
/**
|
|
62
|
+
* The rendered document, reduced to what the delivery decision needs.
|
|
63
|
+
*
|
|
64
|
+
* The ORDER's status is carried through rather than the document's kind alone, because "may this
|
|
65
|
+
* be sent" is a question about the order — a draft renders perfectly well and must not be mailed.
|
|
66
|
+
*/
|
|
67
|
+
private toFacts;
|
|
68
|
+
/** A comma- or semicolon-separated list, or an array, from one parameter. */
|
|
69
|
+
private addressList;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Registers {@link SendDocumentAction}.
|
|
73
|
+
*
|
|
74
|
+
* WITHOUT THIS ANCHOR the class is tree-shaken out, the `@RegisterClass` decorator never runs, and
|
|
75
|
+
* `ActionEngine` finds an action row with no implementation behind it.
|
|
76
|
+
*/
|
|
77
|
+
export declare function LoadSendDocumentAction(): void;
|
|
78
|
+
//# sourceMappingURL=send-document.action.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"send-document.action.d.ts","sourceRoot":"","sources":["../../src/custom/send-document.action.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACrD,OAAO,KAAK,EAAe,kBAAkB,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAsBrG,qCAAqC;AACrC,MAAM,WAAW,uBAAuB;IACpC,cAAc,EAAE,MAAM,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,OAAO,CAAC;IACd,sCAAsC;IACtC,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,yBAAyB;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,uGAAuG;IACvG,eAAe,EAAE,MAAM,EAAE,CAAC;CAC7B;AA+BD;;;;;;GAMG;AACH,qBACa,kBAAmB,SAAQ,UAAU;IAC9C;;;OAGG;cACa,iBAAiB,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,kBAAkB,CAAC;YAYzE,IAAI;IAoKlB;;;;;OAKG;IACH,OAAO,CAAC,OAAO;IAcf,6EAA6E;IAC7E,OAAO,CAAC,WAAW;CAMtB;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,IAAI,IAAI,CAE7C"}
|