@aglyn/plugins-commerce 1.0.0-beta.289 → 1.0.0-beta.291
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/README.md +4 -2
- package/package.json +13 -13
- package/src/lib/components/commerce-console-page.js +2 -1
- package/src/lib/components/commerce-console-page.js.map +1 -1
- package/src/lib/components/console/commerce-pdf-download.d.ts +30 -0
- package/src/lib/components/console/commerce-pdf-download.js +53 -0
- package/src/lib/components/console/commerce-pdf-download.js.map +1 -0
- package/src/lib/components/console/host-orders-card.component.d.ts +6 -0
- package/src/lib/components/console/host-orders-card.component.js +3 -2
- package/src/lib/components/console/host-orders-card.component.js.map +1 -1
- package/src/lib/components/console/host-quotes-card.component.js +20 -4
- package/src/lib/components/console/host-quotes-card.component.js.map +1 -1
- package/src/lib/components/console/invoice-send.component.d.ts +44 -0
- package/src/lib/components/console/invoice-send.component.js +237 -0
- package/src/lib/components/console/invoice-send.component.js.map +1 -0
- package/src/lib/components/console/order-detail-dialog.component.d.ts +5 -0
- package/src/lib/components/console/order-detail-dialog.component.js +9 -2
- package/src/lib/components/console/order-detail-dialog.component.js.map +1 -1
- package/src/lib/components/console/order-invoice.component.d.ts +15 -4
- package/src/lib/components/console/order-invoice.component.js +93 -12
- package/src/lib/components/console/order-invoice.component.js.map +1 -1
- package/src/lib/components/console/quote-editor-dialog.component.d.ts +29 -0
- package/src/lib/components/console/quote-editor-dialog.component.js +154 -5
- package/src/lib/components/console/quote-editor-dialog.component.js.map +1 -1
- package/src/lib/components/console/quote-proofs.component.d.ts +48 -0
- package/src/lib/components/console/quote-proofs.component.js +694 -0
- package/src/lib/components/console/quote-proofs.component.js.map +1 -0
- package/src/lib/components/console/quote-send.component.d.ts +42 -0
- package/src/lib/components/console/quote-send.component.js +307 -0
- package/src/lib/components/console/quote-send.component.js.map +1 -0
- package/src/lib/components/console/quote-start.component.d.ts +45 -0
- package/src/lib/components/console/quote-start.component.js +182 -0
- package/src/lib/components/console/quote-start.component.js.map +1 -0
- package/src/lib/components/product-options-field-body.d.ts +44 -0
- package/src/lib/components/product-options-field-body.js +260 -0
- package/src/lib/components/product-options-field-body.js.map +1 -0
- package/src/lib/components/product-options-field.d.ts +32 -0
- package/src/lib/components/product-options-field.js +128 -0
- package/src/lib/components/product-options-field.js.map +1 -0
- package/src/lib/components/quote-page-body.js +67 -20
- package/src/lib/components/quote-page-body.js.map +1 -1
- package/src/lib/components/quote-proof-panel.d.ts +42 -0
- package/src/lib/components/quote-proof-panel.js +436 -0
- package/src/lib/components/quote-proof-panel.js.map +1 -0
- package/src/lib/model/commerce-invoices.d.ts +132 -0
- package/src/lib/model/commerce-invoices.js +332 -0
- package/src/lib/model/commerce-invoices.js.map +1 -0
- package/src/lib/model/commerce-orders.d.ts +29 -2
- package/src/lib/model/commerce-orders.js.map +1 -1
- package/src/lib/model/commerce-product-field.d.ts +23 -0
- package/src/lib/model/commerce-product-field.js +26 -0
- package/src/lib/model/commerce-product-field.js.map +1 -0
- package/src/lib/model/commerce-quote-email.d.ts +67 -0
- package/src/lib/model/commerce-quote-email.js +154 -0
- package/src/lib/model/commerce-quote-email.js.map +1 -0
- package/src/lib/model/commerce-quote-pay.d.ts +76 -6
- package/src/lib/model/commerce-quote-pay.js +105 -39
- package/src/lib/model/commerce-quote-pay.js.map +1 -1
- package/src/lib/model/commerce-quote-proofs.d.ts +159 -0
- package/src/lib/model/commerce-quote-proofs.js +348 -0
- package/src/lib/model/commerce-quote-proofs.js.map +1 -0
- package/src/lib/model/commerce-quote-request.d.ts +93 -0
- package/src/lib/model/commerce-quote-request.js +207 -0
- package/src/lib/model/commerce-quote-request.js.map +1 -0
- package/src/lib/model/commerce-quotes.d.ts +140 -3
- package/src/lib/model/commerce-quotes.js.map +1 -1
- package/src/lib/model/index.d.ts +4 -0
- package/src/lib/model/index.js +4 -0
- package/src/lib/model/index.js.map +1 -1
- package/src/lib/plugin.js +19 -0
- package/src/lib/plugin.js.map +1 -1
- package/src/lib/server/billing-webhook.js +115 -70
- package/src/lib/server/billing-webhook.js.map +1 -1
- package/src/lib/server/commerce-pdf.d.ts +104 -0
- package/src/lib/server/commerce-pdf.js +310 -0
- package/src/lib/server/commerce-pdf.js.map +1 -0
- package/src/lib/server/draft-order.js +23 -3
- package/src/lib/server/draft-order.js.map +1 -1
- package/src/lib/server/invoice-email.d.ts +76 -0
- package/src/lib/server/invoice-email.js +183 -0
- package/src/lib/server/invoice-email.js.map +1 -0
- package/src/lib/server/order-invoice-send.d.ts +43 -0
- package/src/lib/server/order-invoice-send.js +218 -0
- package/src/lib/server/order-invoice-send.js.map +1 -0
- package/src/lib/server/order-print.d.ts +6 -0
- package/src/lib/server/order-print.js +22 -5
- package/src/lib/server/order-print.js.map +1 -1
- package/src/lib/server/order-status.d.ts +11 -0
- package/src/lib/server/order-status.js +44 -8
- package/src/lib/server/order-status.js.map +1 -1
- package/src/lib/server/pdf-response.d.ts +31 -0
- package/src/lib/server/pdf-response.js +30 -0
- package/src/lib/server/pdf-response.js.map +1 -0
- package/src/lib/server/person-eraser.d.ts +3 -2
- package/src/lib/server/person-eraser.js +25 -6
- package/src/lib/server/person-eraser.js.map +1 -1
- package/src/lib/server/pos-reader-splash.d.ts +2 -18
- package/src/lib/server/pos-reader-splash.js +8 -93
- package/src/lib/server/pos-reader-splash.js.map +1 -1
- package/src/lib/server/quote-email.d.ts +85 -0
- package/src/lib/server/quote-email.js +211 -0
- package/src/lib/server/quote-email.js.map +1 -0
- package/src/lib/server/quote-link-token.d.ts +11 -0
- package/src/lib/server/quote-link-token.js +21 -0
- package/src/lib/server/quote-link-token.js.map +1 -1
- package/src/lib/server/quote-pay.d.ts +38 -6
- package/src/lib/server/quote-pay.js +115 -53
- package/src/lib/server/quote-pay.js.map +1 -1
- package/src/lib/server/quote-proofs.d.ts +51 -0
- package/src/lib/server/quote-proofs.js +500 -0
- package/src/lib/server/quote-proofs.js.map +1 -0
- package/src/lib/server/quote-uploads.d.ts +33 -0
- package/src/lib/server/quote-uploads.js +29 -0
- package/src/lib/server/quote-uploads.js.map +1 -0
- package/src/lib/server/quote.d.ts +25 -0
- package/src/lib/server/quote.js +638 -8
- package/src/lib/server/quote.js.map +1 -1
- package/src/lib/server/site-image-source.d.ts +38 -0
- package/src/lib/server/site-image-source.js +130 -0
- package/src/lib/server/site-image-source.js.map +1 -0
- package/src/lib/server/site-page-resolver.js +2 -1
- package/src/lib/server/site-page-resolver.js.map +1 -1
- package/src/lib/server.js +24 -1
- package/src/lib/server.js.map +1 -1
- package/src/lib/site.js +8 -0
- package/src/lib/site.js.map +1 -1
- package/src/lib/tenant-emails.js +243 -0
- package/src/lib/tenant-emails.js.map +1 -1
- package/src/lib/utils/commerce-document-pdf.d.ts +82 -0
- package/src/lib/utils/commerce-document-pdf.js +613 -0
- package/src/lib/utils/commerce-document-pdf.js.map +1 -0
- package/src/lib/utils/invoice-email-content.d.ts +45 -0
- package/src/lib/utils/invoice-email-content.js +126 -0
- package/src/lib/utils/invoice-email-content.js.map +1 -0
- package/src/lib/utils/order-invoice.d.ts +52 -3
- package/src/lib/utils/order-invoice.js +124 -64
- package/src/lib/utils/order-invoice.js.map +1 -1
- package/src/lib/utils/quote-email-content.d.ts +62 -0
- package/src/lib/utils/quote-email-content.js +154 -0
- package/src/lib/utils/quote-email-content.js.map +1 -0
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
import { type HostOrder } from './commerce-orders';
|
|
18
|
+
import { type OrderQuote, type QuoteEmailThread, type QuoteSendRecord } from './commerce-quotes';
|
|
19
|
+
/** The most sends a quote keeps on record; the oldest go first. */
|
|
20
|
+
export declare const QUOTE_MAX_SENDS = 50;
|
|
21
|
+
/** The longest message a send carries. */
|
|
22
|
+
export declare const QUOTE_MAX_SEND_MESSAGE = 5000;
|
|
23
|
+
/** The longest subject a send carries. */
|
|
24
|
+
export declare const QUOTE_MAX_SEND_SUBJECT = 200;
|
|
25
|
+
/** The quote's sends on record, oldest first. */
|
|
26
|
+
export declare function quoteSends(quote: Pick<OrderQuote, 'sends'> | null | undefined): QuoteSendRecord[];
|
|
27
|
+
/** The last time the quote was emailed, or null. */
|
|
28
|
+
export declare function lastQuoteSend(quote: Pick<OrderQuote, 'sends'> | null | undefined): QuoteSendRecord | null;
|
|
29
|
+
/**
|
|
30
|
+
* Whether emailing the quote now gives the customer a new pay link: they were
|
|
31
|
+
* emailed an earlier revision, and the link has not been replaced since.
|
|
32
|
+
*/
|
|
33
|
+
export declare function quoteSendReplacesLink(quote: Pick<OrderQuote, 'sends' | 'revision' | 'linkVersion'> | null | undefined): boolean;
|
|
34
|
+
/** What the console's Send button says: `Send quote`, `Send again`, or `Send revision 2`. */
|
|
35
|
+
export declare function quoteSendLabel(quote: Pick<OrderQuote, 'sends' | 'revision'> | null | undefined): string;
|
|
36
|
+
/** Where the quote goes: what was typed in the dialog, else the customer's email. */
|
|
37
|
+
export declare function quoteSendRecipient(order: Pick<HostOrder, 'quote'> | null | undefined, typed: string | null | undefined): string;
|
|
38
|
+
export type QuoteSendRefusal = 'not-a-quote' | 'paid' | 'no-address' | 'bad-address';
|
|
39
|
+
/** Why the quote cannot be emailed to `to` now, if it cannot. */
|
|
40
|
+
export declare function quoteSendRefusal(order: Pick<HostOrder, 'channel' | 'status' | 'quote'> | null | undefined, to: string): QuoteSendRefusal | null;
|
|
41
|
+
export declare function quoteSendRefusalMessage(reason: QuoteSendRefusal): string;
|
|
42
|
+
/**
|
|
43
|
+
* The subject a send defaults to: the conversation's, so it stays one
|
|
44
|
+
* conversation, else `Quote #1042 from Acme Signs`.
|
|
45
|
+
*/
|
|
46
|
+
export declare function quoteEmailDefaultSubject(order: Pick<HostOrder, 'number' | 'quote'>, businessName: string, quoteId?: string): string;
|
|
47
|
+
/** What the console sends: where, under what subject, and the business's message. */
|
|
48
|
+
export interface QuoteSendInput {
|
|
49
|
+
to: string;
|
|
50
|
+
subject: string;
|
|
51
|
+
message: string;
|
|
52
|
+
}
|
|
53
|
+
/** A send as the route received it, trimmed, or what to fix. `to` and `subject` may be empty: the defaults fill them. */
|
|
54
|
+
export declare function parseQuoteSendInput(raw: unknown): {
|
|
55
|
+
ok: true;
|
|
56
|
+
input: QuoteSendInput;
|
|
57
|
+
} | {
|
|
58
|
+
ok: false;
|
|
59
|
+
error: string;
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* The quote once an email went out: the send on record, the conversation
|
|
63
|
+
* moved on, a line on the timeline, and the quote at Sent, unless the
|
|
64
|
+
* customer has already answered this revision (approved it, or asked for
|
|
65
|
+
* changes), which stays what it says.
|
|
66
|
+
*/
|
|
67
|
+
export declare function quoteAfterSend(order: HostOrder, send: QuoteSendRecord, thread: QuoteEmailThread): HostOrder;
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
2
|
+
/**
|
|
3
|
+
* @license
|
|
4
|
+
* Copyright 2026 Aglyn LLC
|
|
5
|
+
*
|
|
6
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
* you may not use this file except in compliance with the License.
|
|
8
|
+
* You may obtain a copy of the License at
|
|
9
|
+
*
|
|
10
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
*
|
|
12
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
* See the License for the specific language governing permissions and
|
|
16
|
+
* limitations under the License.
|
|
17
|
+
*/ import { appendOrderEvent } from "./commerce-orders.js";
|
|
18
|
+
import { quoteLinkVersion } from "./commerce-quote-pay.js";
|
|
19
|
+
import { latestQuoteResponse } from "./commerce-quote-proofs.js";
|
|
20
|
+
import { formatQuoteNumber, isQuote, isUnpaidQuote } from "./commerce-quotes.js";
|
|
21
|
+
/*==========================================
|
|
22
|
+
* EMAILING A QUOTE TO THE CUSTOMER (AGL-4097).
|
|
23
|
+
*
|
|
24
|
+
* The business sends the quote from the console: one email in the site's
|
|
25
|
+
* look with its message, the proof, the PDF and the pay button, in the
|
|
26
|
+
* customer's existing conversation when there is one. Each send is recorded
|
|
27
|
+
* on the quote (`quote.sends`) with the revision it carried and the pay link
|
|
28
|
+
* version it carried, and the conversation is kept as `quote.emailThread`.
|
|
29
|
+
*
|
|
30
|
+
* THE PAY LINK ON A NEW REVISION. When the customer was emailed an earlier
|
|
31
|
+
* revision, the first email of a new revision gives them a new pay link and
|
|
32
|
+
* retires the old one (slice 2's Replace link), so the link in the earlier
|
|
33
|
+
* email opens a page saying the quote was updated. A resend of the same
|
|
34
|
+
* revision keeps the link. "Was the link already replaced since that email"
|
|
35
|
+
* is read off the record (the link version it carried), which is also what
|
|
36
|
+
* keeps a send that failed after replacing the link from replacing it again
|
|
37
|
+
* when it is retried.
|
|
38
|
+
*
|
|
39
|
+
* Pure: the console's Send button and the server's send action read the
|
|
40
|
+
* same answers.
|
|
41
|
+
*=========================================*/ /** The most sends a quote keeps on record; the oldest go first. */ export const QUOTE_MAX_SENDS = 50;
|
|
42
|
+
/** The longest message a send carries. */ export const QUOTE_MAX_SEND_MESSAGE = 5000;
|
|
43
|
+
/** The longest subject a send carries. */ export const QUOTE_MAX_SEND_SUBJECT = 200;
|
|
44
|
+
const EMAIL = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
45
|
+
/** The quote's sends on record, oldest first. */ export function quoteSends(quote) {
|
|
46
|
+
return Array.isArray(quote == null ? void 0 : quote.sends) ? quote.sends.filter((send)=>send && typeof send === 'object') : [];
|
|
47
|
+
}
|
|
48
|
+
/** The last time the quote was emailed, or null. */ export function lastQuoteSend(quote) {
|
|
49
|
+
const sends = quoteSends(quote);
|
|
50
|
+
return sends.length ? sends[sends.length - 1] : null;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Whether emailing the quote now gives the customer a new pay link: they were
|
|
54
|
+
* emailed an earlier revision, and the link has not been replaced since.
|
|
55
|
+
*/ export function quoteSendReplacesLink(quote) {
|
|
56
|
+
var _ref;
|
|
57
|
+
const last = lastQuoteSend(quote);
|
|
58
|
+
if (!last) return false;
|
|
59
|
+
if (!(Number(last.revision) < Number((_ref = quote == null ? void 0 : quote.revision) != null ? _ref : 1))) return false;
|
|
60
|
+
return Number(last.linkVersion) === quoteLinkVersion(quote);
|
|
61
|
+
}
|
|
62
|
+
/** What the console's Send button says: `Send quote`, `Send again`, or `Send revision 2`. */ export function quoteSendLabel(quote) {
|
|
63
|
+
var _ref;
|
|
64
|
+
const last = lastQuoteSend(quote);
|
|
65
|
+
if (!last) return 'Send quote';
|
|
66
|
+
const revision = Number((_ref = quote == null ? void 0 : quote.revision) != null ? _ref : 1);
|
|
67
|
+
return Number(last.revision) < revision ? `Send revision ${revision}` : 'Send again';
|
|
68
|
+
}
|
|
69
|
+
/** Where the quote goes: what was typed in the dialog, else the customer's email. */ export function quoteSendRecipient(order, typed) {
|
|
70
|
+
var _ref;
|
|
71
|
+
var _order_quote_customer, _order_quote;
|
|
72
|
+
return String(typed != null ? typed : '').trim() || String((_ref = order == null ? void 0 : (_order_quote = order.quote) == null ? void 0 : (_order_quote_customer = _order_quote.customer) == null ? void 0 : _order_quote_customer.email) != null ? _ref : '').trim();
|
|
73
|
+
}
|
|
74
|
+
/** Why the quote cannot be emailed to `to` now, if it cannot. */ export function quoteSendRefusal(order, to) {
|
|
75
|
+
if (!order || !isQuote(order) || !order.quote) return 'not-a-quote';
|
|
76
|
+
if (!isUnpaidQuote(order)) return 'paid';
|
|
77
|
+
if (!to) return 'no-address';
|
|
78
|
+
if (to.length > 320 || !EMAIL.test(to)) return 'bad-address';
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
export function quoteSendRefusalMessage(reason) {
|
|
82
|
+
switch(reason){
|
|
83
|
+
case 'not-a-quote':
|
|
84
|
+
return 'Unknown quote';
|
|
85
|
+
case 'paid':
|
|
86
|
+
return 'This quote has been paid, so it is an order now and is not sent again.';
|
|
87
|
+
case 'no-address':
|
|
88
|
+
return 'This quote has no email for the customer. Add one to the quote, or type where to send it.';
|
|
89
|
+
case 'bad-address':
|
|
90
|
+
return 'Enter a valid email address to send the quote to.';
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* The subject a send defaults to: the conversation's, so it stays one
|
|
95
|
+
* conversation, else `Quote #1042 from Acme Signs`.
|
|
96
|
+
*/ export function quoteEmailDefaultSubject(order, businessName, quoteId) {
|
|
97
|
+
var _ref;
|
|
98
|
+
var _order_quote_emailThread, _order_quote;
|
|
99
|
+
const thread = String((_ref = (_order_quote = order.quote) == null ? void 0 : (_order_quote_emailThread = _order_quote.emailThread) == null ? void 0 : _order_quote_emailThread.subject) != null ? _ref : '').trim();
|
|
100
|
+
if (thread) return thread;
|
|
101
|
+
const name = String(businessName != null ? businessName : '').trim();
|
|
102
|
+
const number = formatQuoteNumber(order, quoteId);
|
|
103
|
+
return name ? `${number} from ${name}` : number;
|
|
104
|
+
}
|
|
105
|
+
/** A send as the route received it, trimmed, or what to fix. `to` and `subject` may be empty: the defaults fill them. */ export function parseQuoteSendInput(raw) {
|
|
106
|
+
const source = raw && typeof raw === 'object' ? raw : {};
|
|
107
|
+
const text = (value)=>typeof value === 'string' ? value : '';
|
|
108
|
+
const to = text(source['to']).trim();
|
|
109
|
+
// A subject is one header line.
|
|
110
|
+
const subject = text(source['subject']).replace(/[\r\n]+/g, ' ').trim();
|
|
111
|
+
const message = text(source['message']).replace(/\r\n?/g, '\n').trim();
|
|
112
|
+
if (subject.length > QUOTE_MAX_SEND_SUBJECT) {
|
|
113
|
+
return {
|
|
114
|
+
ok: false,
|
|
115
|
+
error: `Keep the subject under ${QUOTE_MAX_SEND_SUBJECT} characters.`
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
if (message.length > QUOTE_MAX_SEND_MESSAGE) {
|
|
119
|
+
return {
|
|
120
|
+
ok: false,
|
|
121
|
+
error: `Keep the message under ${QUOTE_MAX_SEND_MESSAGE.toLocaleString('en-US')} characters.`
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
return {
|
|
125
|
+
ok: true,
|
|
126
|
+
input: {
|
|
127
|
+
to,
|
|
128
|
+
subject,
|
|
129
|
+
message
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* The quote once an email went out: the send on record, the conversation
|
|
135
|
+
* moved on, a line on the timeline, and the quote at Sent, unless the
|
|
136
|
+
* customer has already answered this revision (approved it, or asked for
|
|
137
|
+
* changes), which stays what it says.
|
|
138
|
+
*/ export function quoteAfterSend(order, send, thread) {
|
|
139
|
+
const quote = order.quote;
|
|
140
|
+
const answered = latestQuoteResponse(quote) !== null;
|
|
141
|
+
return _extends({}, order, {
|
|
142
|
+
timeline: appendOrderEvent(order, 'quote-sent', `Revision ${send.revision} emailed to ${send.to} by ${send.by}`, send.atMs),
|
|
143
|
+
quote: _extends({}, quote, {
|
|
144
|
+
stage: answered ? quote.stage : 'sent',
|
|
145
|
+
sends: [
|
|
146
|
+
...quoteSends(quote),
|
|
147
|
+
send
|
|
148
|
+
].slice(-QUOTE_MAX_SENDS),
|
|
149
|
+
emailThread: thread
|
|
150
|
+
})
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
//# sourceMappingURL=commerce-quote-email.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/model/commerce-quote-email.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { appendOrderEvent, type HostOrder } from './commerce-orders'\nimport { quoteLinkVersion } from './commerce-quote-pay'\nimport { latestQuoteResponse } from './commerce-quote-proofs'\nimport {\n formatQuoteNumber,\n isQuote,\n isUnpaidQuote,\n type OrderQuote,\n type QuoteEmailThread,\n type QuoteSendRecord,\n} from './commerce-quotes'\n\n/*==========================================\n * EMAILING A QUOTE TO THE CUSTOMER (AGL-4097).\n *\n * The business sends the quote from the console: one email in the site's\n * look with its message, the proof, the PDF and the pay button, in the\n * customer's existing conversation when there is one. Each send is recorded\n * on the quote (`quote.sends`) with the revision it carried and the pay link\n * version it carried, and the conversation is kept as `quote.emailThread`.\n *\n * THE PAY LINK ON A NEW REVISION. When the customer was emailed an earlier\n * revision, the first email of a new revision gives them a new pay link and\n * retires the old one (slice 2's Replace link), so the link in the earlier\n * email opens a page saying the quote was updated. A resend of the same\n * revision keeps the link. \"Was the link already replaced since that email\"\n * is read off the record (the link version it carried), which is also what\n * keeps a send that failed after replacing the link from replacing it again\n * when it is retried.\n *\n * Pure: the console's Send button and the server's send action read the\n * same answers.\n *=========================================*/\n\n/** The most sends a quote keeps on record; the oldest go first. */\nexport const QUOTE_MAX_SENDS = 50\n\n/** The longest message a send carries. */\nexport const QUOTE_MAX_SEND_MESSAGE = 5_000\n\n/** The longest subject a send carries. */\nexport const QUOTE_MAX_SEND_SUBJECT = 200\n\nconst EMAIL = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/\n\n/** The quote's sends on record, oldest first. */\nexport function quoteSends(quote: Pick<OrderQuote, 'sends'> | null | undefined): QuoteSendRecord[] {\n return Array.isArray(quote?.sends) ? quote.sends.filter((send) => send && typeof send === 'object') : []\n}\n\n/** The last time the quote was emailed, or null. */\nexport function lastQuoteSend(quote: Pick<OrderQuote, 'sends'> | null | undefined): QuoteSendRecord | null {\n const sends = quoteSends(quote)\n return sends.length ? sends[sends.length - 1] : null\n}\n\n/**\n * Whether emailing the quote now gives the customer a new pay link: they were\n * emailed an earlier revision, and the link has not been replaced since.\n */\nexport function quoteSendReplacesLink(\n quote: Pick<OrderQuote, 'sends' | 'revision' | 'linkVersion'> | null | undefined,\n): boolean {\n const last = lastQuoteSend(quote)\n if (!last) return false\n if (!(Number(last.revision) < Number(quote?.revision ?? 1))) return false\n return Number(last.linkVersion) === quoteLinkVersion(quote)\n}\n\n/** What the console's Send button says: `Send quote`, `Send again`, or `Send revision 2`. */\nexport function quoteSendLabel(quote: Pick<OrderQuote, 'sends' | 'revision'> | null | undefined): string {\n const last = lastQuoteSend(quote)\n if (!last) return 'Send quote'\n const revision = Number(quote?.revision ?? 1)\n return Number(last.revision) < revision ? `Send revision ${revision}` : 'Send again'\n}\n\n/** Where the quote goes: what was typed in the dialog, else the customer's email. */\nexport function quoteSendRecipient(\n order: Pick<HostOrder, 'quote'> | null | undefined,\n typed: string | null | undefined,\n): string {\n return String(typed ?? '').trim() || String(order?.quote?.customer?.email ?? '').trim()\n}\n\nexport type QuoteSendRefusal = 'not-a-quote' | 'paid' | 'no-address' | 'bad-address'\n\n/** Why the quote cannot be emailed to `to` now, if it cannot. */\nexport function quoteSendRefusal(\n order: Pick<HostOrder, 'channel' | 'status' | 'quote'> | null | undefined,\n to: string,\n): QuoteSendRefusal | null {\n if (!order || !isQuote(order) || !order.quote) return 'not-a-quote'\n if (!isUnpaidQuote(order)) return 'paid'\n if (!to) return 'no-address'\n if (to.length > 320 || !EMAIL.test(to)) return 'bad-address'\n return null\n}\n\nexport function quoteSendRefusalMessage(reason: QuoteSendRefusal): string {\n switch (reason) {\n case 'not-a-quote':\n return 'Unknown quote'\n case 'paid':\n return 'This quote has been paid, so it is an order now and is not sent again.'\n case 'no-address':\n return 'This quote has no email for the customer. Add one to the quote, or type where to send it.'\n case 'bad-address':\n return 'Enter a valid email address to send the quote to.'\n }\n}\n\n/**\n * The subject a send defaults to: the conversation's, so it stays one\n * conversation, else `Quote #1042 from Acme Signs`.\n */\nexport function quoteEmailDefaultSubject(\n order: Pick<HostOrder, 'number' | 'quote'>,\n businessName: string,\n quoteId?: string,\n): string {\n const thread = String(order.quote?.emailThread?.subject ?? '').trim()\n if (thread) return thread\n const name = String(businessName ?? '').trim()\n const number = formatQuoteNumber(order, quoteId)\n return name ? `${number} from ${name}` : number\n}\n\n/** What the console sends: where, under what subject, and the business's message. */\nexport interface QuoteSendInput {\n to: string\n subject: string\n message: string\n}\n\n/** A send as the route received it, trimmed, or what to fix. `to` and `subject` may be empty: the defaults fill them. */\nexport function parseQuoteSendInput(raw: unknown): { ok: true; input: QuoteSendInput } | { ok: false; error: string } {\n const source = (raw && typeof raw === 'object' ? raw : {}) as Record<string, unknown>\n const text = (value: unknown) => (typeof value === 'string' ? value : '')\n const to = text(source['to']).trim()\n // A subject is one header line.\n const subject = text(source['subject']).replace(/[\\r\\n]+/g, ' ').trim()\n const message = text(source['message']).replace(/\\r\\n?/g, '\\n').trim()\n if (subject.length > QUOTE_MAX_SEND_SUBJECT) {\n return { ok: false, error: `Keep the subject under ${QUOTE_MAX_SEND_SUBJECT} characters.` }\n }\n if (message.length > QUOTE_MAX_SEND_MESSAGE) {\n return { ok: false, error: `Keep the message under ${QUOTE_MAX_SEND_MESSAGE.toLocaleString('en-US')} characters.` }\n }\n return { ok: true, input: { to, subject, message } }\n}\n\n/**\n * The quote once an email went out: the send on record, the conversation\n * moved on, a line on the timeline, and the quote at Sent, unless the\n * customer has already answered this revision (approved it, or asked for\n * changes), which stays what it says.\n */\nexport function quoteAfterSend(\n order: HostOrder,\n send: QuoteSendRecord,\n thread: QuoteEmailThread,\n): HostOrder {\n const quote = order.quote as OrderQuote\n const answered = latestQuoteResponse(quote) !== null\n return {\n ...order,\n timeline: appendOrderEvent(\n order,\n 'quote-sent',\n `Revision ${send.revision} emailed to ${send.to} by ${send.by}`,\n send.atMs,\n ),\n quote: {\n ...quote,\n stage: answered ? quote.stage : 'sent',\n sends: [...quoteSends(quote), send].slice(-QUOTE_MAX_SENDS),\n emailThread: thread,\n },\n }\n}\n"],"names":["appendOrderEvent","quoteLinkVersion","latestQuoteResponse","formatQuoteNumber","isQuote","isUnpaidQuote","QUOTE_MAX_SENDS","QUOTE_MAX_SEND_MESSAGE","QUOTE_MAX_SEND_SUBJECT","EMAIL","quoteSends","quote","Array","isArray","sends","filter","send","lastQuoteSend","length","quoteSendReplacesLink","last","Number","revision","linkVersion","quoteSendLabel","quoteSendRecipient","order","typed","String","trim","customer","email","quoteSendRefusal","to","test","quoteSendRefusalMessage","reason","quoteEmailDefaultSubject","businessName","quoteId","thread","emailThread","subject","name","number","parseQuoteSendInput","raw","source","text","value","replace","message","ok","error","toLocaleString","input","quoteAfterSend","answered","timeline","by","atMs","stage","slice"],"mappings":"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"}
|
|
@@ -15,14 +15,23 @@
|
|
|
15
15
|
* limitations under the License.
|
|
16
16
|
*/
|
|
17
17
|
import type { ProductType } from './commerce';
|
|
18
|
-
import { type HostOrder, type OrderAddress } from './commerce-orders';
|
|
19
|
-
import { type OrderQuote, type QuoteUnmatchedPayment } from './commerce-quotes';
|
|
18
|
+
import { type HostOrder, type OrderAddress, type OrderLineItem, type OrderStatus } from './commerce-orders';
|
|
19
|
+
import { type OrderQuote, type QuoteDiscount, type QuoteTaxBasis, type QuoteUnmatchedPayment } from './commerce-quotes';
|
|
20
20
|
import type { StorefrontTaxMode } from './commerce-tax-decision';
|
|
21
21
|
import { type OrderListFields } from './order-list-fields';
|
|
22
22
|
/** Stripe's smallest card charge; a quote that totals less cannot be paid by card. */
|
|
23
23
|
export declare const QUOTE_MIN_CHARGE_CENTS = 50;
|
|
24
24
|
/** The pay link version a quote is on; absent reads as the first. */
|
|
25
25
|
export declare function quoteLinkVersion(quote: Pick<OrderQuote, 'linkVersion'> | null | undefined): number;
|
|
26
|
+
/**
|
|
27
|
+
* The pay link version any order is on (AGL-4099). A quote keeps its version
|
|
28
|
+
* where slice 2 put it, `quote.linkVersion`, so every link already sent keeps
|
|
29
|
+
* verifying; any other order keeps it as `payLink.version`. Absent reads as
|
|
30
|
+
* the first either way.
|
|
31
|
+
*/
|
|
32
|
+
export declare function orderPayLinkVersion(order: Partial<Pick<HostOrder, 'quote' | 'payLink'>> | null | undefined): number;
|
|
33
|
+
/** Payments the order's pay link could not take: a quote's on the quote, any other order's on `payLink`. */
|
|
34
|
+
export declare function orderPayLinkUnmatched(order: Partial<Pick<HostOrder, 'quote' | 'payLink'>> | null | undefined): QuoteUnmatchedPayment[];
|
|
26
35
|
/** An address as the quote page prints it: no phone. */
|
|
27
36
|
export interface QuotePageAddress {
|
|
28
37
|
name: string | null;
|
|
@@ -48,6 +57,25 @@ export interface QuotePageLine {
|
|
|
48
57
|
}
|
|
49
58
|
/** Where the quote stands, as its customer sees it. */
|
|
50
59
|
export type QuotePageStatus = 'unpaid' | 'paid' | 'canceled' | 'refunded';
|
|
60
|
+
/** A proof as the quote page lists it: no storage path, nothing about who attached it. */
|
|
61
|
+
export interface QuotePageProof {
|
|
62
|
+
id: string;
|
|
63
|
+
name: string;
|
|
64
|
+
contentType: string;
|
|
65
|
+
size: number;
|
|
66
|
+
/** Shown in the page as a picture; everything else is a download. */
|
|
67
|
+
inline: boolean;
|
|
68
|
+
}
|
|
69
|
+
/** The customer's latest answer to the revision the page shows. */
|
|
70
|
+
export interface QuotePageResponse {
|
|
71
|
+
action: 'approve' | 'changes';
|
|
72
|
+
atMs: number;
|
|
73
|
+
/** With an approval: the name it was given under. */
|
|
74
|
+
name: string | null;
|
|
75
|
+
comment: string | null;
|
|
76
|
+
/** How many files came with it. */
|
|
77
|
+
files: number;
|
|
78
|
+
}
|
|
51
79
|
/**
|
|
52
80
|
* What the quote page shows. Allow-listed, never spread: the order document
|
|
53
81
|
* carries the customer's email and phone, Stripe ids, the business's own
|
|
@@ -56,8 +84,14 @@ export type QuotePageStatus = 'unpaid' | 'paid' | 'canceled' | 'refunded';
|
|
|
56
84
|
*/
|
|
57
85
|
export interface QuotePageView {
|
|
58
86
|
state: 'quote';
|
|
87
|
+
/**
|
|
88
|
+
* What the page shows (AGL-4099): a quote, or an invoice for an order the
|
|
89
|
+
* business sent one for. An invoice has no proofs and no approval, and the
|
|
90
|
+
* page says "invoice" where a quote's says "quote".
|
|
91
|
+
*/
|
|
92
|
+
kind: 'quote' | 'invoice';
|
|
59
93
|
storeName: string;
|
|
60
|
-
/** `Quote #1042`. */
|
|
94
|
+
/** `Quote #1042`, or `Invoice #1042`. */
|
|
61
95
|
number: string;
|
|
62
96
|
/** When the quote was last written, which is the date it carries. */
|
|
63
97
|
dateMs: number | null;
|
|
@@ -85,6 +119,12 @@ export interface QuotePageView {
|
|
|
85
119
|
taxLabel: string;
|
|
86
120
|
/** Stripe Tax works the tax out when the customer pays; `taxCents` is 0 until then. */
|
|
87
121
|
taxAtPayment: boolean;
|
|
122
|
+
/**
|
|
123
|
+
* The customer picks a shipping rate when they pay (an invoiced draft
|
|
124
|
+
* order, AGL-4099); `shippingCents` is 0 until then. A quote's shipping is
|
|
125
|
+
* priced on it, so this is false for every quote.
|
|
126
|
+
*/
|
|
127
|
+
shippingAtPayment: boolean;
|
|
88
128
|
notes: string | null;
|
|
89
129
|
terms: string | null;
|
|
90
130
|
status: QuotePageStatus;
|
|
@@ -96,12 +136,27 @@ export interface QuotePageView {
|
|
|
96
136
|
approvalRequired: boolean;
|
|
97
137
|
/** A Pay button belongs on the page. */
|
|
98
138
|
payable: boolean;
|
|
139
|
+
/** The revision the page shows (AGL-4096). */
|
|
140
|
+
revision: number;
|
|
141
|
+
/** That revision's proofs. */
|
|
142
|
+
proofs: QuotePageProof[];
|
|
143
|
+
/** The customer's latest answer to it, if any. */
|
|
144
|
+
response: QuotePageResponse | null;
|
|
145
|
+
/** Approve and Request changes belong on the page. */
|
|
146
|
+
respondable: boolean;
|
|
99
147
|
}
|
|
100
148
|
/** What a signed link for an older version of the quote opens: nothing of the quote. */
|
|
101
149
|
export interface QuotePageReplacedView {
|
|
102
150
|
state: 'replaced';
|
|
103
151
|
storeName: string;
|
|
104
152
|
}
|
|
153
|
+
/** An address as the pay link's page prints it, or null when it says nothing. */
|
|
154
|
+
export declare function quotePageAddress(address: OrderAddress | null | undefined): QuotePageAddress | null;
|
|
155
|
+
/** Where an order stands as the pay link's page says it. */
|
|
156
|
+
export declare function quotePageStatus(status: OrderStatus | string | undefined): {
|
|
157
|
+
status: QuotePageStatus;
|
|
158
|
+
label: string;
|
|
159
|
+
};
|
|
105
160
|
/**
|
|
106
161
|
* The quote page's view of an order document on the `quote` channel. Totals
|
|
107
162
|
* are the stored lines' through {@link computeQuoteTotals} until the quote is
|
|
@@ -115,6 +170,8 @@ export declare function buildQuotePageView(input: {
|
|
|
115
170
|
/** The order's status page, for a paid quote. */
|
|
116
171
|
receiptUrl?: string | null;
|
|
117
172
|
}): QuotePageView;
|
|
173
|
+
/** Order lines as the pay link's page lists them: what each charges, nothing about stock or cost. */
|
|
174
|
+
export declare function quotePageLines(lines: readonly OrderLineItem[]): QuotePageLine[];
|
|
118
175
|
/** One line of a quote's Checkout Session. */
|
|
119
176
|
export interface QuoteCheckoutLine {
|
|
120
177
|
name: string;
|
|
@@ -160,6 +217,19 @@ export interface QuoteCheckoutCharge {
|
|
|
160
217
|
* line it amounts to. Included lines charge nothing and are not sent.
|
|
161
218
|
*/
|
|
162
219
|
export declare function quoteCheckoutCharge(order: Pick<HostOrder, 'number' | 'lineItems' | 'quote'>, orderId: string): QuoteCheckoutCharge;
|
|
220
|
+
/**
|
|
221
|
+
* What a pay link's Checkout Session charges for stored lines, a discount,
|
|
222
|
+
* fixed shipping and a tax basis, under a label (`Quote #1042`,
|
|
223
|
+
* `Invoice #1042`): {@link quoteCheckoutCharge}'s pricing, shared with an
|
|
224
|
+
* invoiced order's (AGL-4099), so both are charged the same way.
|
|
225
|
+
*/
|
|
226
|
+
export declare function payLinkCheckoutCharge(input: {
|
|
227
|
+
lineItems: readonly OrderLineItem[];
|
|
228
|
+
tax: QuoteTaxBasis;
|
|
229
|
+
discount: QuoteDiscount | null;
|
|
230
|
+
shippingCents: number;
|
|
231
|
+
label: string;
|
|
232
|
+
}): QuoteCheckoutCharge;
|
|
163
233
|
export type QuotePayRefusal = 'paid' | 'closed' | 'approval-required' | 'tax-undecided' | 'nothing-to-pay';
|
|
164
234
|
/** Why a quote cannot be paid now, or null when it can. */
|
|
165
235
|
export declare function quotePayRefusal(order: Pick<HostOrder, 'number' | 'status' | 'lineItems' | 'quote'>, orderId: string): QuotePayRefusal | null;
|
|
@@ -202,11 +272,11 @@ export interface QuotePaymentFacts {
|
|
|
202
272
|
* the business is told, so a payment is never fulfilled twice and never lost.
|
|
203
273
|
*/
|
|
204
274
|
export type QuotePaymentDisposition = 'pay' | 'superseded' | 'already-paid' | 'redelivery';
|
|
205
|
-
export declare function quotePaymentDisposition(order: Pick<HostOrder, 'status' | 'quote' | 'checkoutSessionId' | 'paymentIntentId'
|
|
275
|
+
export declare function quotePaymentDisposition(order: Pick<HostOrder, 'status' | 'quote' | 'checkoutSessionId' | 'paymentIntentId'> & Partial<Pick<HostOrder, 'payLink'>>, payment: Pick<QuotePaymentFacts, 'sessionId' | 'paymentIntentId' | 'linkVersion' | 'quoteUpdatedAtMs'>): QuotePaymentDisposition;
|
|
206
276
|
/** A payment the quote could not take, as {@link QuoteUnmatchedPayment} records it. */
|
|
207
277
|
export declare function quoteUnmatchedPayment(facts: Pick<QuotePaymentFacts, 'sessionId' | 'paymentIntentId' | 'amountTotalCents' | 'paidAtMs'>, reason: QuoteUnmatchedPayment['reason']): QuoteUnmatchedPayment;
|
|
208
|
-
/** The timeline line for a payment the quote could not take. */
|
|
209
|
-
export declare function quoteUnmatchedPaymentDetail(entry: QuoteUnmatchedPayment): string;
|
|
278
|
+
/** The timeline line for a payment the quote (or an order's invoice, AGL-4099) could not take. */
|
|
279
|
+
export declare function quoteUnmatchedPaymentDetail(entry: QuoteUnmatchedPayment, noun?: 'quote' | 'invoice'): string;
|
|
210
280
|
/**
|
|
211
281
|
* The order a paid quote becomes, the whole document as it will stand. A
|
|
212
282
|
* quote carries none of the fields order lists read (`commerce-quotes.ts`);
|