@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,348 @@
|
|
|
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 { formatQuoteNumber, isQuote, isUnpaidQuote } from "./commerce-quotes.js";
|
|
19
|
+
/*==========================================
|
|
20
|
+
* PROOFS, REVISIONS AND THE CUSTOMER'S ANSWER (AGL-4096).
|
|
21
|
+
*
|
|
22
|
+
* A quote's REVISION is the version the customer is looking at. The business
|
|
23
|
+
* attaches proofs to the current revision; the customer approves them under
|
|
24
|
+
* a typed name or asks for changes; the business then starts the next
|
|
25
|
+
* revision, which keeps the one before exactly as it stood in
|
|
26
|
+
* `hosts/{hostId}/orders/{id}/revisions/{n}` and starts the new one with no
|
|
27
|
+
* proofs and no answer. Every revision is kept.
|
|
28
|
+
*
|
|
29
|
+
* Whether paying waits on an approval is the business's control
|
|
30
|
+
* (`requireProofApproval`, AGL-4093) and `quotePaymentBlockedByApproval`
|
|
31
|
+
* answers it; nothing here makes paying wait when it is off.
|
|
32
|
+
*
|
|
33
|
+
* Pure: the console route, the customer's route and (slice 5) the send that
|
|
34
|
+
* starts a revision all call these, and write what they answer.
|
|
35
|
+
*=========================================*/ /** The most proofs one revision holds. */ export const QUOTE_MAX_PROOFS = 10;
|
|
36
|
+
/** The most answers the customer gives one revision; plenty for a change of mind. */ export const QUOTE_MAX_RESPONSES = 20;
|
|
37
|
+
/** The longest comment a customer may leave. */ export const QUOTE_MAX_COMMENT = 5000;
|
|
38
|
+
/** The longest name an approval is signed with. */ export const QUOTE_MAX_APPROVER_NAME = 120;
|
|
39
|
+
/** What the visitor uploads are called for a quote: its proofs, and the customer's artwork. */ export const QUOTE_PROOF_PURPOSE = 'quote-proof';
|
|
40
|
+
export const QUOTE_ARTWORK_PURPOSE = 'quote-artwork';
|
|
41
|
+
/** The current revision's proofs. */ export function currentQuoteProofs(quote) {
|
|
42
|
+
var _ref, _ref1;
|
|
43
|
+
const revision = Number((_ref = quote == null ? void 0 : quote.revision) != null ? _ref : 1);
|
|
44
|
+
return ((_ref1 = quote == null ? void 0 : quote.proofs) != null ? _ref1 : []).filter((proof)=>Number(proof == null ? void 0 : proof.revision) === revision);
|
|
45
|
+
}
|
|
46
|
+
/** The customer's latest answer to the current revision, if they gave one. */ export function latestQuoteResponse(quote) {
|
|
47
|
+
var _ref, _ref1;
|
|
48
|
+
const revision = Number((_ref = quote == null ? void 0 : quote.revision) != null ? _ref : 1);
|
|
49
|
+
const answers = ((_ref1 = quote == null ? void 0 : quote.responses) != null ? _ref1 : []).filter((response)=>Number(response == null ? void 0 : response.revision) === revision);
|
|
50
|
+
return answers.length ? answers[answers.length - 1] : null;
|
|
51
|
+
}
|
|
52
|
+
/** Whether the customer has answered the current revision. */ export function quoteRevisionAnswered(quote) {
|
|
53
|
+
return latestQuoteResponse(quote) !== null;
|
|
54
|
+
}
|
|
55
|
+
/** Why the current revision's proofs cannot be changed now, if they cannot. */ export function quoteProofChangeRefusal(order, change) {
|
|
56
|
+
if (!order || !isQuote(order) || !order.quote) return 'not-a-quote';
|
|
57
|
+
if (!isUnpaidQuote(order)) return 'paid';
|
|
58
|
+
if (quoteRevisionAnswered(order.quote)) return 'answered';
|
|
59
|
+
if (change === 'add' && currentQuoteProofs(order.quote).length >= QUOTE_MAX_PROOFS) return 'full';
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
export function quoteProofRefusalMessage(reason) {
|
|
63
|
+
switch(reason){
|
|
64
|
+
case 'not-a-quote':
|
|
65
|
+
return 'Unknown quote';
|
|
66
|
+
case 'paid':
|
|
67
|
+
return 'This quote has been paid, so its proofs can no longer be changed.';
|
|
68
|
+
case 'answered':
|
|
69
|
+
return 'The customer has answered this revision. Start a new revision to send different proofs.';
|
|
70
|
+
case 'full':
|
|
71
|
+
return `A revision holds up to ${QUOTE_MAX_PROOFS} proofs. Remove one first.`;
|
|
72
|
+
case 'unknown-proof':
|
|
73
|
+
return 'That proof is no longer on this quote.';
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/** The quote with a proof added to its current revision, edited by `by` at `atMs`. */ export function withQuoteProofAdded(order, proof, options) {
|
|
77
|
+
var _quote_revision;
|
|
78
|
+
const refused = quoteProofChangeRefusal(order, 'add');
|
|
79
|
+
if (refused) return {
|
|
80
|
+
ok: false,
|
|
81
|
+
reason: refused
|
|
82
|
+
};
|
|
83
|
+
const quote = order.quote;
|
|
84
|
+
const added = {
|
|
85
|
+
id: proof.id,
|
|
86
|
+
revision: Number((_quote_revision = quote.revision) != null ? _quote_revision : 1),
|
|
87
|
+
name: proof.name,
|
|
88
|
+
contentType: proof.contentType,
|
|
89
|
+
size: proof.size,
|
|
90
|
+
path: proof.path,
|
|
91
|
+
addedAtMs: options.atMs,
|
|
92
|
+
addedBy: options.by
|
|
93
|
+
};
|
|
94
|
+
return {
|
|
95
|
+
ok: true,
|
|
96
|
+
proof: added,
|
|
97
|
+
order: _extends({}, order, {
|
|
98
|
+
timeline: appendOrderEvent(order, 'quote-proof-added', `Proof ${added.name} added to revision ${added.revision} by ${options.by}`, options.atMs),
|
|
99
|
+
quote: _extends({}, quote, {
|
|
100
|
+
proofs: [
|
|
101
|
+
...currentQuoteProofs(quote).filter((entry)=>entry.id !== added.id),
|
|
102
|
+
added
|
|
103
|
+
],
|
|
104
|
+
updatedAtMs: options.atMs,
|
|
105
|
+
updatedBy: options.by
|
|
106
|
+
})
|
|
107
|
+
})
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
/** The quote with one of the current revision's proofs taken off. */ export function withQuoteProofRemoved(order, proofId, options) {
|
|
111
|
+
const refused = quoteProofChangeRefusal(order, 'remove');
|
|
112
|
+
if (refused) return {
|
|
113
|
+
ok: false,
|
|
114
|
+
reason: refused
|
|
115
|
+
};
|
|
116
|
+
const quote = order.quote;
|
|
117
|
+
const proofs = currentQuoteProofs(quote);
|
|
118
|
+
const proof = proofs.find((entry)=>entry.id === proofId);
|
|
119
|
+
if (!proof) return {
|
|
120
|
+
ok: false,
|
|
121
|
+
reason: 'unknown-proof'
|
|
122
|
+
};
|
|
123
|
+
return {
|
|
124
|
+
ok: true,
|
|
125
|
+
proof,
|
|
126
|
+
order: _extends({}, order, {
|
|
127
|
+
timeline: appendOrderEvent(order, 'quote-proof-removed', `Proof ${proof.name} removed from revision ${proof.revision} by ${options.by}`, options.atMs),
|
|
128
|
+
quote: _extends({}, quote, {
|
|
129
|
+
proofs: proofs.filter((entry)=>entry.id !== proofId),
|
|
130
|
+
updatedAtMs: options.atMs,
|
|
131
|
+
updatedBy: options.by
|
|
132
|
+
})
|
|
133
|
+
})
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Starts the quote's next revision. The current one is SNAPSHOTTED as it
|
|
138
|
+
* stands (its lines, totals, notes, terms, proofs and the customer's
|
|
139
|
+
* answers); the quote moves to revision N+1 with no proofs and no answer,
|
|
140
|
+
* back at Draft, edited by `by` at `atMs`. A paid quote starts none.
|
|
141
|
+
*
|
|
142
|
+
* The caller writes `snapshot` to `hosts/{hostId}/orders/{id}/revisions/{revision}`
|
|
143
|
+
* (`String(snapshot.revision)`, created, never overwritten) and `order` over
|
|
144
|
+
* the quote, in one transaction. The pay link's version is not touched: the
|
|
145
|
+
* send that hands the customer the new revision decides that.
|
|
146
|
+
*/ export function startQuoteRevision(order, options) {
|
|
147
|
+
var _quote_revision, _quote_responses, _quote_stage, _order_lineItems, _order_totals, _order_taxMode, _quote_tax, _quote_discount, _quote_shipping, _quote_notes, _quote_terms, _quote_customer, _quote_billTo, _quote_shipTo;
|
|
148
|
+
if (!isQuote(order) || !order.quote) return {
|
|
149
|
+
ok: false,
|
|
150
|
+
reason: 'not-a-quote',
|
|
151
|
+
error: 'Unknown quote'
|
|
152
|
+
};
|
|
153
|
+
if (!isUnpaidQuote(order)) {
|
|
154
|
+
return {
|
|
155
|
+
ok: false,
|
|
156
|
+
reason: 'paid',
|
|
157
|
+
error: 'This quote has been paid, so it takes no new revision.'
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
const quote = order.quote;
|
|
161
|
+
const current = Math.max(1, Math.floor(Number((_quote_revision = quote.revision) != null ? _quote_revision : 1)) || 1);
|
|
162
|
+
const responses = ((_quote_responses = quote.responses) != null ? _quote_responses : []).filter((response)=>Number(response == null ? void 0 : response.revision) === current);
|
|
163
|
+
const approval = quote.approval && Number(quote.approval.revision) === current ? quote.approval : null;
|
|
164
|
+
const snapshot = {
|
|
165
|
+
revision: current,
|
|
166
|
+
stage: (_quote_stage = quote.stage) != null ? _quote_stage : 'draft',
|
|
167
|
+
lineItems: (_order_lineItems = order.lineItems) != null ? _order_lineItems : [],
|
|
168
|
+
totals: (_order_totals = order.totals) != null ? _order_totals : null,
|
|
169
|
+
taxMode: (_order_taxMode = order.taxMode) != null ? _order_taxMode : null,
|
|
170
|
+
tax: (_quote_tax = quote.tax) != null ? _quote_tax : null,
|
|
171
|
+
discount: (_quote_discount = quote.discount) != null ? _quote_discount : null,
|
|
172
|
+
shipping: (_quote_shipping = quote.shipping) != null ? _quote_shipping : null,
|
|
173
|
+
notes: (_quote_notes = quote.notes) != null ? _quote_notes : '',
|
|
174
|
+
terms: (_quote_terms = quote.terms) != null ? _quote_terms : '',
|
|
175
|
+
customer: (_quote_customer = quote.customer) != null ? _quote_customer : null,
|
|
176
|
+
billTo: (_quote_billTo = quote.billTo) != null ? _quote_billTo : null,
|
|
177
|
+
shipTo: (_quote_shipTo = quote.shipTo) != null ? _quote_shipTo : null,
|
|
178
|
+
proofs: currentQuoteProofs(quote),
|
|
179
|
+
responses,
|
|
180
|
+
approval,
|
|
181
|
+
updatedAtMs: Number(quote.updatedAtMs) || 0,
|
|
182
|
+
closedAtMs: options.atMs,
|
|
183
|
+
closedBy: options.by
|
|
184
|
+
};
|
|
185
|
+
const next = current + 1;
|
|
186
|
+
return {
|
|
187
|
+
ok: true,
|
|
188
|
+
revision: current,
|
|
189
|
+
snapshot,
|
|
190
|
+
order: _extends({}, order, {
|
|
191
|
+
timeline: appendOrderEvent(order, 'quote-revision-started', `Revision ${next} started by ${options.by}; revision ${current} is kept with its proofs and answers`, options.atMs),
|
|
192
|
+
quote: _extends({}, quote, {
|
|
193
|
+
revision: next,
|
|
194
|
+
stage: 'draft',
|
|
195
|
+
proofs: [],
|
|
196
|
+
responses: [],
|
|
197
|
+
approval: null,
|
|
198
|
+
updatedAtMs: options.atMs,
|
|
199
|
+
updatedBy: options.by
|
|
200
|
+
})
|
|
201
|
+
})
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
/** Why the customer cannot answer the current revision now, if they cannot. */ export function quoteRespondRefusal(order) {
|
|
205
|
+
var _order_quote_revision, _order_quote_responses;
|
|
206
|
+
if (!order || !isQuote(order) || !order.quote) return 'not-a-quote';
|
|
207
|
+
if (!isUnpaidQuote(order)) return 'paid';
|
|
208
|
+
if (currentQuoteProofs(order.quote).length === 0) return 'no-proof';
|
|
209
|
+
const revision = Number((_order_quote_revision = order.quote.revision) != null ? _order_quote_revision : 1);
|
|
210
|
+
const given = ((_order_quote_responses = order.quote.responses) != null ? _order_quote_responses : []).filter((response)=>Number(response == null ? void 0 : response.revision) === revision);
|
|
211
|
+
if (given.length >= QUOTE_MAX_RESPONSES) return 'too-many';
|
|
212
|
+
return null;
|
|
213
|
+
}
|
|
214
|
+
export function quoteRespondRefusalMessage(reason, storeName) {
|
|
215
|
+
const business = storeName || 'the business';
|
|
216
|
+
switch(reason){
|
|
217
|
+
case 'not-a-quote':
|
|
218
|
+
return 'We could not find that quote. Check the link you were sent.';
|
|
219
|
+
case 'paid':
|
|
220
|
+
return 'This quote has been paid, so it takes no more answers.';
|
|
221
|
+
case 'no-proof':
|
|
222
|
+
return `There is no proof on this quote to answer yet. ${storeName || 'The business'} will send one.`;
|
|
223
|
+
case 'too-many':
|
|
224
|
+
return `This proof has had a lot of answers already. Contact ${business} directly.`;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
/** The customer's answer as the route received it, checked and trimmed, or what to fix. */ export function parseQuoteResponseInput(raw) {
|
|
228
|
+
const source = raw && typeof raw === 'object' ? raw : {};
|
|
229
|
+
if (source['action'] === 'approve') {
|
|
230
|
+
const name = typeof source['name'] === 'string' ? source['name'].replace(/\s+/g, ' ').trim() : '';
|
|
231
|
+
if (name.length < 2) return {
|
|
232
|
+
ok: false,
|
|
233
|
+
error: 'Type your name to approve the proof.'
|
|
234
|
+
};
|
|
235
|
+
return {
|
|
236
|
+
ok: true,
|
|
237
|
+
input: {
|
|
238
|
+
action: 'approve',
|
|
239
|
+
name: name.slice(0, QUOTE_MAX_APPROVER_NAME)
|
|
240
|
+
}
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
if (source['action'] === 'changes') {
|
|
244
|
+
const comment = typeof source['comment'] === 'string' ? source['comment'].trim() : '';
|
|
245
|
+
if (!comment) return {
|
|
246
|
+
ok: false,
|
|
247
|
+
error: 'Tell us what to change.'
|
|
248
|
+
};
|
|
249
|
+
const raws = Array.isArray(source['fileIds']) ? source['fileIds'] : [];
|
|
250
|
+
const named = raws.map((value)=>String(value != null ? value : '')).filter((value)=>/^[A-Za-z0-9_-]{1,128}$/.test(value));
|
|
251
|
+
if (named.length !== raws.length) return {
|
|
252
|
+
ok: false,
|
|
253
|
+
error: 'One of the files could not be attached. Send it again.'
|
|
254
|
+
};
|
|
255
|
+
const fileIds = [
|
|
256
|
+
...new Set(named)
|
|
257
|
+
];
|
|
258
|
+
if (fileIds.length > QUOTE_MAX_PROOFS) return {
|
|
259
|
+
ok: false,
|
|
260
|
+
error: `Send up to ${QUOTE_MAX_PROOFS} files at a time.`
|
|
261
|
+
};
|
|
262
|
+
return {
|
|
263
|
+
ok: true,
|
|
264
|
+
input: {
|
|
265
|
+
action: 'changes',
|
|
266
|
+
comment: comment.slice(0, QUOTE_MAX_COMMENT),
|
|
267
|
+
fileIds
|
|
268
|
+
}
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
return {
|
|
272
|
+
ok: false,
|
|
273
|
+
error: 'Approve the proof or ask for changes.'
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* The quote with the customer's answer recorded on its current revision.
|
|
278
|
+
* Approving names the revision approved and moves the quote to Approved;
|
|
279
|
+
* asking for changes moves it to Changes requested and withdraws an approval
|
|
280
|
+
* of the same revision. The latest answer is the one that counts. The
|
|
281
|
+
* business's edit stamp (`updatedAtMs`) is not touched: the quote's content
|
|
282
|
+
* did not change, and the page that sent the answer can still pay it.
|
|
283
|
+
*/ export function applyQuoteResponse(order, input, context) {
|
|
284
|
+
var _quote_revision, _quote_responses, _response_name, _quote_approvedRevision;
|
|
285
|
+
var _input_files, _response_files;
|
|
286
|
+
const refused = quoteRespondRefusal(order);
|
|
287
|
+
if (refused) return {
|
|
288
|
+
ok: false,
|
|
289
|
+
reason: refused
|
|
290
|
+
};
|
|
291
|
+
const quote = order.quote;
|
|
292
|
+
const revision = Number((_quote_revision = quote.revision) != null ? _quote_revision : 1);
|
|
293
|
+
const ip = context.ip ? context.ip.slice(0, 64) : null;
|
|
294
|
+
const userAgent = context.userAgent ? context.userAgent.slice(0, 400) : null;
|
|
295
|
+
const response = input.action === 'approve' ? {
|
|
296
|
+
action: 'approve',
|
|
297
|
+
revision,
|
|
298
|
+
atMs: context.atMs,
|
|
299
|
+
name: input.name,
|
|
300
|
+
ip,
|
|
301
|
+
userAgent
|
|
302
|
+
} : _extends({
|
|
303
|
+
action: 'changes',
|
|
304
|
+
revision,
|
|
305
|
+
atMs: context.atMs,
|
|
306
|
+
comment: input.comment
|
|
307
|
+
}, ((_input_files = input.files) == null ? void 0 : _input_files.length) ? {
|
|
308
|
+
files: input.files
|
|
309
|
+
} : {}, {
|
|
310
|
+
ip,
|
|
311
|
+
userAgent
|
|
312
|
+
});
|
|
313
|
+
const responses = [
|
|
314
|
+
...((_quote_responses = quote.responses) != null ? _quote_responses : []).filter((entry)=>Number(entry == null ? void 0 : entry.revision) === revision),
|
|
315
|
+
response
|
|
316
|
+
];
|
|
317
|
+
const number = formatQuoteNumber(order);
|
|
318
|
+
const next = response.action === 'approve' ? _extends({}, quote, {
|
|
319
|
+
responses,
|
|
320
|
+
stage: 'approved',
|
|
321
|
+
approvedRevision: revision,
|
|
322
|
+
approval: {
|
|
323
|
+
revision,
|
|
324
|
+
name: (_response_name = response.name) != null ? _response_name : '',
|
|
325
|
+
atMs: context.atMs,
|
|
326
|
+
ip,
|
|
327
|
+
userAgent
|
|
328
|
+
}
|
|
329
|
+
}) : _extends({}, quote, {
|
|
330
|
+
responses,
|
|
331
|
+
stage: 'changes_requested',
|
|
332
|
+
approvedRevision: Number(quote.approvedRevision) === revision ? null : (_quote_approvedRevision = quote.approvedRevision) != null ? _quote_approvedRevision : null,
|
|
333
|
+
approval: null
|
|
334
|
+
});
|
|
335
|
+
return {
|
|
336
|
+
ok: true,
|
|
337
|
+
response,
|
|
338
|
+
order: _extends({}, order, {
|
|
339
|
+
timeline: appendOrderEvent(order, response.action === 'approve' ? 'quote-approved' : 'quote-changes-requested', response.action === 'approve' ? `${number} revision ${revision} approved by the customer as ${response.name}` : `The customer asked for changes to ${number} revision ${revision}` + (((_response_files = response.files) == null ? void 0 : _response_files.length) ? ` and sent ${response.files.length} file${response.files.length === 1 ? '' : 's'}` : ''), context.atMs),
|
|
340
|
+
quote: next
|
|
341
|
+
})
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
/** One line on what the customer last said: `Approved by Jordan Lee`, `Changes requested`. */ export function describeQuoteResponse(response) {
|
|
345
|
+
return response.action === 'approve' ? `Approved by ${response.name || 'the customer'}` : 'Changes requested';
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
//# sourceMappingURL=commerce-quote-proofs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/model/commerce-quote-proofs.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, type OrderAddress, type OrderLineItem, type OrderTotals } from './commerce-orders'\nimport {\n formatQuoteNumber,\n isQuote,\n isUnpaidQuote,\n type OrderQuote,\n type QuoteApproval,\n type QuoteCustomer,\n type QuoteCustomerResponse,\n type QuoteDiscount,\n type QuoteProof,\n type QuoteResponseFile,\n type QuoteShipping,\n type QuoteStage,\n type QuoteTaxBasis,\n} from './commerce-quotes'\nimport type { StorefrontTaxMode } from './commerce-tax-decision'\n\n/*==========================================\n * PROOFS, REVISIONS AND THE CUSTOMER'S ANSWER (AGL-4096).\n *\n * A quote's REVISION is the version the customer is looking at. The business\n * attaches proofs to the current revision; the customer approves them under\n * a typed name or asks for changes; the business then starts the next\n * revision, which keeps the one before exactly as it stood in\n * `hosts/{hostId}/orders/{id}/revisions/{n}` and starts the new one with no\n * proofs and no answer. Every revision is kept.\n *\n * Whether paying waits on an approval is the business's control\n * (`requireProofApproval`, AGL-4093) and `quotePaymentBlockedByApproval`\n * answers it; nothing here makes paying wait when it is off.\n *\n * Pure: the console route, the customer's route and (slice 5) the send that\n * starts a revision all call these, and write what they answer.\n *=========================================*/\n\n/** The most proofs one revision holds. */\nexport const QUOTE_MAX_PROOFS = 10\n\n/** The most answers the customer gives one revision; plenty for a change of mind. */\nexport const QUOTE_MAX_RESPONSES = 20\n\n/** The longest comment a customer may leave. */\nexport const QUOTE_MAX_COMMENT = 5_000\n\n/** The longest name an approval is signed with. */\nexport const QUOTE_MAX_APPROVER_NAME = 120\n\n/** What the visitor uploads are called for a quote: its proofs, and the customer's artwork. */\nexport const QUOTE_PROOF_PURPOSE = 'quote-proof'\nexport const QUOTE_ARTWORK_PURPOSE = 'quote-artwork'\n\n/** A revision as it stood when the next one started. */\nexport interface QuoteRevisionSnapshot {\n revision: number\n /** Where it stood with the business when it closed. */\n stage: QuoteStage\n lineItems: OrderLineItem[]\n totals: OrderTotals | null\n taxMode: StorefrontTaxMode | null\n tax: QuoteTaxBasis | null\n discount: QuoteDiscount | null\n shipping: QuoteShipping | null\n notes: string\n terms: string\n customer: QuoteCustomer | null\n billTo: OrderAddress | null\n shipTo: OrderAddress | null\n proofs: QuoteProof[]\n responses: QuoteCustomerResponse[]\n approval: QuoteApproval | null\n /** When the revision was last written by the business. */\n updatedAtMs: number\n /** When the next revision started, and who started it. */\n closedAtMs: number\n closedBy: string\n}\n\n/** The current revision's proofs. */\nexport function currentQuoteProofs(quote: Pick<OrderQuote, 'proofs' | 'revision'> | null | undefined): QuoteProof[] {\n const revision = Number(quote?.revision ?? 1)\n return (quote?.proofs ?? []).filter((proof) => Number(proof?.revision) === revision)\n}\n\n/** The customer's latest answer to the current revision, if they gave one. */\nexport function latestQuoteResponse(\n quote: Pick<OrderQuote, 'responses' | 'revision'> | null | undefined,\n): QuoteCustomerResponse | null {\n const revision = Number(quote?.revision ?? 1)\n const answers = (quote?.responses ?? []).filter((response) => Number(response?.revision) === revision)\n return answers.length ? answers[answers.length - 1] : null\n}\n\n/** Whether the customer has answered the current revision. */\nexport function quoteRevisionAnswered(quote: Pick<OrderQuote, 'responses' | 'revision'> | null | undefined): boolean {\n return latestQuoteResponse(quote) !== null\n}\n\n/*------------------------------------------\n * The business's proofs\n *-----------------------------------------*/\n\nexport type QuoteProofRefusal = 'not-a-quote' | 'paid' | 'answered' | 'full' | 'unknown-proof'\n\n/** Why the current revision's proofs cannot be changed now, if they cannot. */\nexport function quoteProofChangeRefusal(\n order: Pick<HostOrder, 'channel' | 'status' | 'quote'> | null | undefined,\n change: 'add' | 'remove',\n): QuoteProofRefusal | null {\n if (!order || !isQuote(order) || !order.quote) return 'not-a-quote'\n if (!isUnpaidQuote(order)) return 'paid'\n if (quoteRevisionAnswered(order.quote)) return 'answered'\n if (change === 'add' && currentQuoteProofs(order.quote).length >= QUOTE_MAX_PROOFS) return 'full'\n return null\n}\n\nexport function quoteProofRefusalMessage(reason: QuoteProofRefusal): string {\n switch (reason) {\n case 'not-a-quote':\n return 'Unknown quote'\n case 'paid':\n return 'This quote has been paid, so its proofs can no longer be changed.'\n case 'answered':\n return 'The customer has answered this revision. Start a new revision to send different proofs.'\n case 'full':\n return `A revision holds up to ${QUOTE_MAX_PROOFS} proofs. Remove one first.`\n case 'unknown-proof':\n return 'That proof is no longer on this quote.'\n }\n}\n\n/** The quote with a proof added to its current revision, edited by `by` at `atMs`. */\nexport function withQuoteProofAdded(\n order: HostOrder,\n proof: Omit<QuoteProof, 'revision' | 'addedAtMs' | 'addedBy'>,\n options: { atMs: number; by: string },\n): { ok: true; order: HostOrder; proof: QuoteProof } | { ok: false; reason: QuoteProofRefusal } {\n const refused = quoteProofChangeRefusal(order, 'add')\n if (refused) return { ok: false, reason: refused }\n const quote = order.quote as OrderQuote\n const added: QuoteProof = {\n id: proof.id,\n revision: Number(quote.revision ?? 1),\n name: proof.name,\n contentType: proof.contentType,\n size: proof.size,\n path: proof.path,\n addedAtMs: options.atMs,\n addedBy: options.by,\n }\n return {\n ok: true,\n proof: added,\n order: {\n ...order,\n timeline: appendOrderEvent(\n order,\n 'quote-proof-added',\n `Proof ${added.name} added to revision ${added.revision} by ${options.by}`,\n options.atMs,\n ),\n quote: {\n ...quote,\n proofs: [...currentQuoteProofs(quote).filter((entry) => entry.id !== added.id), added],\n updatedAtMs: options.atMs,\n updatedBy: options.by,\n },\n },\n }\n}\n\n/** The quote with one of the current revision's proofs taken off. */\nexport function withQuoteProofRemoved(\n order: HostOrder,\n proofId: string,\n options: { atMs: number; by: string },\n): { ok: true; order: HostOrder; proof: QuoteProof } | { ok: false; reason: QuoteProofRefusal } {\n const refused = quoteProofChangeRefusal(order, 'remove')\n if (refused) return { ok: false, reason: refused }\n const quote = order.quote as OrderQuote\n const proofs = currentQuoteProofs(quote)\n const proof = proofs.find((entry) => entry.id === proofId)\n if (!proof) return { ok: false, reason: 'unknown-proof' }\n return {\n ok: true,\n proof,\n order: {\n ...order,\n timeline: appendOrderEvent(\n order,\n 'quote-proof-removed',\n `Proof ${proof.name} removed from revision ${proof.revision} by ${options.by}`,\n options.atMs,\n ),\n quote: {\n ...quote,\n proofs: proofs.filter((entry) => entry.id !== proofId),\n updatedAtMs: options.atMs,\n updatedBy: options.by,\n },\n },\n }\n}\n\n/*------------------------------------------\n * Revisions\n *-----------------------------------------*/\n\nexport type QuoteRevisionRefusal = 'not-a-quote' | 'paid'\n\n/**\n * Starts the quote's next revision. The current one is SNAPSHOTTED as it\n * stands (its lines, totals, notes, terms, proofs and the customer's\n * answers); the quote moves to revision N+1 with no proofs and no answer,\n * back at Draft, edited by `by` at `atMs`. A paid quote starts none.\n *\n * The caller writes `snapshot` to `hosts/{hostId}/orders/{id}/revisions/{revision}`\n * (`String(snapshot.revision)`, created, never overwritten) and `order` over\n * the quote, in one transaction. The pay link's version is not touched: the\n * send that hands the customer the new revision decides that.\n */\nexport function startQuoteRevision(\n order: HostOrder,\n options: { atMs: number; by: string },\n):\n | { ok: true; snapshot: QuoteRevisionSnapshot; order: HostOrder; revision: number }\n | { ok: false; reason: QuoteRevisionRefusal; error: string } {\n if (!isQuote(order) || !order.quote) return { ok: false, reason: 'not-a-quote', error: 'Unknown quote' }\n if (!isUnpaidQuote(order)) {\n return { ok: false, reason: 'paid', error: 'This quote has been paid, so it takes no new revision.' }\n }\n const quote = order.quote\n const current = Math.max(1, Math.floor(Number(quote.revision ?? 1)) || 1)\n const responses = (quote.responses ?? []).filter((response) => Number(response?.revision) === current)\n const approval = quote.approval && Number(quote.approval.revision) === current ? quote.approval : null\n const snapshot: QuoteRevisionSnapshot = {\n revision: current,\n stage: quote.stage ?? 'draft',\n lineItems: order.lineItems ?? [],\n totals: order.totals ?? null,\n taxMode: order.taxMode ?? null,\n tax: quote.tax ?? null,\n discount: quote.discount ?? null,\n shipping: quote.shipping ?? null,\n notes: quote.notes ?? '',\n terms: quote.terms ?? '',\n customer: quote.customer ?? null,\n billTo: quote.billTo ?? null,\n shipTo: quote.shipTo ?? null,\n proofs: currentQuoteProofs(quote),\n responses,\n approval,\n updatedAtMs: Number(quote.updatedAtMs) || 0,\n closedAtMs: options.atMs,\n closedBy: options.by,\n }\n const next = current + 1\n return {\n ok: true,\n revision: current,\n snapshot,\n order: {\n ...order,\n timeline: appendOrderEvent(\n order,\n 'quote-revision-started',\n `Revision ${next} started by ${options.by}; revision ${current} is kept with its proofs and answers`,\n options.atMs,\n ),\n quote: {\n ...quote,\n revision: next,\n stage: 'draft',\n proofs: [],\n responses: [],\n approval: null,\n updatedAtMs: options.atMs,\n updatedBy: options.by,\n },\n },\n }\n}\n\n/*------------------------------------------\n * The customer's answer\n *-----------------------------------------*/\n\nexport type QuoteRespondRefusal = 'not-a-quote' | 'paid' | 'no-proof' | 'too-many'\n\n/** Why the customer cannot answer the current revision now, if they cannot. */\nexport function quoteRespondRefusal(\n order: Pick<HostOrder, 'channel' | 'status' | 'quote'> | null | undefined,\n): QuoteRespondRefusal | null {\n if (!order || !isQuote(order) || !order.quote) return 'not-a-quote'\n if (!isUnpaidQuote(order)) return 'paid'\n if (currentQuoteProofs(order.quote).length === 0) return 'no-proof'\n const revision = Number(order.quote.revision ?? 1)\n const given = (order.quote.responses ?? []).filter((response) => Number(response?.revision) === revision)\n if (given.length >= QUOTE_MAX_RESPONSES) return 'too-many'\n return null\n}\n\nexport function quoteRespondRefusalMessage(reason: QuoteRespondRefusal, storeName: string): string {\n const business = storeName || 'the business'\n switch (reason) {\n case 'not-a-quote':\n return 'We could not find that quote. Check the link you were sent.'\n case 'paid':\n return 'This quote has been paid, so it takes no more answers.'\n case 'no-proof':\n return `There is no proof on this quote to answer yet. ${storeName || 'The business'} will send one.`\n case 'too-many':\n return `This proof has had a lot of answers already. Contact ${business} directly.`\n }\n}\n\n/** What the customer sends: approve under a name, or changes with a comment and any files. */\nexport type QuoteResponseInput =\n | { action: 'approve'; name: string }\n | { action: 'changes'; comment: string; fileIds: string[] }\n\n/** The customer's answer as the route received it, checked and trimmed, or what to fix. */\nexport function parseQuoteResponseInput(raw: unknown): { ok: true; input: QuoteResponseInput } | { ok: false; error: string } {\n const source = (raw && typeof raw === 'object' ? raw : {}) as Record<string, unknown>\n if (source['action'] === 'approve') {\n const name = typeof source['name'] === 'string' ? source['name'].replace(/\\s+/g, ' ').trim() : ''\n if (name.length < 2) return { ok: false, error: 'Type your name to approve the proof.' }\n return { ok: true, input: { action: 'approve', name: name.slice(0, QUOTE_MAX_APPROVER_NAME) } }\n }\n if (source['action'] === 'changes') {\n const comment = typeof source['comment'] === 'string' ? source['comment'].trim() : ''\n if (!comment) return { ok: false, error: 'Tell us what to change.' }\n const raws = Array.isArray(source['fileIds']) ? (source['fileIds'] as unknown[]) : []\n const named = raws.map((value) => String(value ?? '')).filter((value) => /^[A-Za-z0-9_-]{1,128}$/.test(value))\n if (named.length !== raws.length) return { ok: false, error: 'One of the files could not be attached. Send it again.' }\n const fileIds = [...new Set(named)]\n if (fileIds.length > QUOTE_MAX_PROOFS) return { ok: false, error: `Send up to ${QUOTE_MAX_PROOFS} files at a time.` }\n return { ok: true, input: { action: 'changes', comment: comment.slice(0, QUOTE_MAX_COMMENT), fileIds } }\n }\n return { ok: false, error: 'Approve the proof or ask for changes.' }\n}\n\n/**\n * The quote with the customer's answer recorded on its current revision.\n * Approving names the revision approved and moves the quote to Approved;\n * asking for changes moves it to Changes requested and withdraws an approval\n * of the same revision. The latest answer is the one that counts. The\n * business's edit stamp (`updatedAtMs`) is not touched: the quote's content\n * did not change, and the page that sent the answer can still pay it.\n */\nexport function applyQuoteResponse(\n order: HostOrder,\n input: QuoteResponseInput & { files?: QuoteResponseFile[] },\n context: { atMs: number; ip: string | null; userAgent: string | null },\n): { ok: true; order: HostOrder; response: QuoteCustomerResponse } | { ok: false; reason: QuoteRespondRefusal } {\n const refused = quoteRespondRefusal(order)\n if (refused) return { ok: false, reason: refused }\n const quote = order.quote as OrderQuote\n const revision = Number(quote.revision ?? 1)\n const ip = context.ip ? context.ip.slice(0, 64) : null\n const userAgent = context.userAgent ? context.userAgent.slice(0, 400) : null\n const response: QuoteCustomerResponse =\n input.action === 'approve'\n ? { action: 'approve', revision, atMs: context.atMs, name: input.name, ip, userAgent }\n : {\n action: 'changes',\n revision,\n atMs: context.atMs,\n comment: input.comment,\n ...(input.files?.length ? { files: input.files } : {}),\n ip,\n userAgent,\n }\n const responses = [...(quote.responses ?? []).filter((entry) => Number(entry?.revision) === revision), response]\n const number = formatQuoteNumber(order)\n const next: OrderQuote =\n response.action === 'approve'\n ? {\n ...quote,\n responses,\n stage: 'approved',\n approvedRevision: revision,\n approval: { revision, name: response.name ?? '', atMs: context.atMs, ip, userAgent },\n }\n : {\n ...quote,\n responses,\n stage: 'changes_requested',\n approvedRevision: Number(quote.approvedRevision) === revision ? null : (quote.approvedRevision ?? null),\n approval: null,\n }\n return {\n ok: true,\n response,\n order: {\n ...order,\n timeline: appendOrderEvent(\n order,\n response.action === 'approve' ? 'quote-approved' : 'quote-changes-requested',\n response.action === 'approve'\n ? `${number} revision ${revision} approved by the customer as ${response.name}`\n : `The customer asked for changes to ${number} revision ${revision}` +\n (response.files?.length ? ` and sent ${response.files.length} file${response.files.length === 1 ? '' : 's'}` : ''),\n context.atMs,\n ),\n quote: next,\n },\n }\n}\n\n/** One line on what the customer last said: `Approved by Jordan Lee`, `Changes requested`. */\nexport function describeQuoteResponse(response: Pick<QuoteCustomerResponse, 'action' | 'name'>): string {\n return response.action === 'approve' ? `Approved by ${response.name || 'the customer'}` : 'Changes requested'\n}\n"],"names":["appendOrderEvent","formatQuoteNumber","isQuote","isUnpaidQuote","QUOTE_MAX_PROOFS","QUOTE_MAX_RESPONSES","QUOTE_MAX_COMMENT","QUOTE_MAX_APPROVER_NAME","QUOTE_PROOF_PURPOSE","QUOTE_ARTWORK_PURPOSE","currentQuoteProofs","quote","revision","Number","proofs","filter","proof","latestQuoteResponse","answers","responses","response","length","quoteRevisionAnswered","quoteProofChangeRefusal","order","change","quoteProofRefusalMessage","reason","withQuoteProofAdded","options","refused","ok","added","id","name","contentType","size","path","addedAtMs","atMs","addedBy","by","timeline","entry","updatedAtMs","updatedBy","withQuoteProofRemoved","proofId","find","startQuoteRevision","error","current","Math","max","floor","approval","snapshot","stage","lineItems","totals","taxMode","tax","discount","shipping","notes","terms","customer","billTo","shipTo","closedAtMs","closedBy","next","quoteRespondRefusal","given","quoteRespondRefusalMessage","storeName","business","parseQuoteResponseInput","raw","source","replace","trim","input","action","slice","comment","raws","Array","isArray","named","map","value","String","test","fileIds","Set","applyQuoteResponse","context","ip","userAgent","files","number","approvedRevision","describeQuoteResponse"],"mappings":"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"}
|
|
@@ -0,0 +1,93 @@
|
|
|
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 { QuoteCatalogLineRequest, QuoteCustomer, QuoteEmailThread, QuoteResponseFile } from './commerce-quotes';
|
|
18
|
+
/** The resource the forms plugin reads a submission's facts under, on the core's seam. */
|
|
19
|
+
export declare const QUOTE_REQUEST_FACTS_RESOURCE = "forms.submission";
|
|
20
|
+
/** The request a quote was started from. */
|
|
21
|
+
export interface QuoteRequestRef {
|
|
22
|
+
submissionId: string;
|
|
23
|
+
formName: string;
|
|
24
|
+
/** When the request arrived, epoch ms; null when the submission did not say. */
|
|
25
|
+
receivedAtMs: number | null;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* What the Product and options field posts beside its readable line, under
|
|
29
|
+
* `{name}__data`: the product, the variant the options make, and the options
|
|
30
|
+
* as picked. Add-ons are not taken from the storefront yet; when they are,
|
|
31
|
+
* they ride here as `modifiers` and the quote line prices them as the editor
|
|
32
|
+
* does.
|
|
33
|
+
*/
|
|
34
|
+
export interface ProductFieldValue {
|
|
35
|
+
productId: string;
|
|
36
|
+
variantId?: string;
|
|
37
|
+
options?: Record<string, string>;
|
|
38
|
+
modifiers?: Array<{
|
|
39
|
+
groupId: string;
|
|
40
|
+
optionId: string;
|
|
41
|
+
}>;
|
|
42
|
+
}
|
|
43
|
+
/** The compact value the field posts. */
|
|
44
|
+
export declare function encodeProductFieldValue(value: ProductFieldValue): string;
|
|
45
|
+
/**
|
|
46
|
+
* The field's posted value, read back, or null for anything that is not one:
|
|
47
|
+
* malformed JSON, no product id, ids that cannot be document ids. The options
|
|
48
|
+
* keep at most twenty names and values of up to 200 characters.
|
|
49
|
+
*/
|
|
50
|
+
export declare function parseProductFieldValue(raw: unknown): ProductFieldValue | null;
|
|
51
|
+
/** The readable line the field posts under its name: the product, then its options. */
|
|
52
|
+
export declare function productFieldLabel(productName: string, options: Record<string, string> | undefined): string;
|
|
53
|
+
/** What this plugin reads of a request's facts (the forms plugin's reader documents them). */
|
|
54
|
+
export interface QuoteRequestFacts {
|
|
55
|
+
hostId: string;
|
|
56
|
+
formName: string;
|
|
57
|
+
createdAtMs: number | null;
|
|
58
|
+
requestKind: string | null;
|
|
59
|
+
person: {
|
|
60
|
+
name: string;
|
|
61
|
+
email: string;
|
|
62
|
+
phone: string;
|
|
63
|
+
company: string;
|
|
64
|
+
};
|
|
65
|
+
/** The lead or contact the request was filed as, which the quote's customer links to. */
|
|
66
|
+
capturedRecord: {
|
|
67
|
+
kind: string;
|
|
68
|
+
id: string;
|
|
69
|
+
} | null;
|
|
70
|
+
message: string;
|
|
71
|
+
fieldData: Record<string, string>;
|
|
72
|
+
files: Array<QuoteResponseFile>;
|
|
73
|
+
emailThread: QuoteEmailThread | null;
|
|
74
|
+
}
|
|
75
|
+
/** A request's facts as the seam answered them, read defensively. */
|
|
76
|
+
export declare function readQuoteRequestFacts(raw: Readonly<Record<string, unknown>> | null | undefined): QuoteRequestFacts | null;
|
|
77
|
+
/** What the quote editor opens with when a quote is started from a request. */
|
|
78
|
+
export interface QuoteStartPrefill {
|
|
79
|
+
customer: Pick<QuoteCustomer, 'record' | 'name' | 'email' | 'phone' | 'company'>;
|
|
80
|
+
lines: QuoteCatalogLineRequest[];
|
|
81
|
+
notes: string;
|
|
82
|
+
}
|
|
83
|
+
/** The product lines a request's Product and options fields ask for, one per field. */
|
|
84
|
+
export declare function quoteLinesFromFieldData(fieldData: Readonly<Record<string, string>>): QuoteCatalogLineRequest[];
|
|
85
|
+
/** A quote's start from a request: the editor's prefill, and what the server stamps on the quote. */
|
|
86
|
+
export declare function quoteStartFromRequest(submissionId: string, facts: QuoteRequestFacts): {
|
|
87
|
+
prefill: QuoteStartPrefill;
|
|
88
|
+
request: QuoteRequestRef;
|
|
89
|
+
files: QuoteResponseFile[];
|
|
90
|
+
emailThread: QuoteEmailThread | null;
|
|
91
|
+
};
|
|
92
|
+
/** A request's files as the console may see them: no storage path. */
|
|
93
|
+
export declare function quoteRequestFileViews(files: readonly QuoteResponseFile[] | null | undefined): Array<Omit<QuoteResponseFile, 'path'>>;
|