@aglyn/plugins-commerce 1.0.0-beta.290 → 1.0.0-beta.292
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/cart-fulfillment.d.ts +3 -0
- package/src/lib/components/cart-fulfillment.js.map +1 -1
- package/src/lib/components/cart.js +14 -4
- package/src/lib/components/cart.js.map +1 -1
- 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/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/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 +9 -0
- package/src/lib/components/console/order-invoice.component.js +27 -2
- package/src/lib/components/console/order-invoice.component.js.map +1 -1
- package/src/lib/components/console/pos/pos-item-dialog.component.js +3 -2
- package/src/lib/components/console/pos/pos-item-dialog.component.js.map +1 -1
- package/src/lib/components/console/pos-kiosk-page.component.js +2 -1
- package/src/lib/components/console/pos-kiosk-page.component.js.map +1 -1
- package/src/lib/components/console/pos-page.component.js +1 -1
- package/src/lib/components/console/pos-page.component.js.map +1 -1
- package/src/lib/components/console/product-add-ons-fields.component.d.ts +41 -0
- package/src/lib/components/console/product-add-ons-fields.component.js +356 -0
- package/src/lib/components/console/product-add-ons-fields.component.js.map +1 -0
- package/src/lib/components/console/product-editor-dialog.component.js +31 -5
- package/src/lib/components/console/product-editor-dialog.component.js.map +1 -1
- package/src/lib/components/console/product-register-fields.component.d.ts +4 -5
- package/src/lib/components/console/product-register-fields.component.js +8 -197
- package/src/lib/components/console/product-register-fields.component.js.map +1 -1
- package/src/lib/components/console/products-hub-zone.component.js +6 -0
- package/src/lib/components/console/products-hub-zone.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 +102 -4
- package/src/lib/components/console/quote-editor-dialog.component.js.map +1 -1
- package/src/lib/components/console/quote-pay-link.component.d.ts +8 -1
- package/src/lib/components/console/quote-pay-link.component.js +29 -3
- package/src/lib/components/console/quote-pay-link.component.js.map +1 -1
- package/src/lib/components/console/quote-proofs.component.d.ts +11 -0
- package/src/lib/components/console/quote-proofs.component.js +68 -0
- package/src/lib/components/console/quote-proofs.component.js.map +1 -1
- package/src/lib/components/console/quote-send.component.d.ts +42 -0
- package/src/lib/components/console/quote-send.component.js +304 -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-add-ons.d.ts +28 -0
- package/src/lib/components/product-add-ons.js +136 -0
- package/src/lib/components/product-add-ons.js.map +1 -0
- package/src/lib/components/product-detail.d.ts +16 -0
- package/src/lib/components/product-detail.js +112 -19
- package/src/lib/components/product-detail.js.map +1 -1
- package/src/lib/components/product-grid.d.ts +8 -2
- package/src/lib/components/product-grid.js +6 -1
- package/src/lib/components/product-grid.js.map +1 -1
- 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 +39 -20
- package/src/lib/components/quote-page-body.js.map +1 -1
- package/src/lib/model/commerce-cart.d.ts +15 -3
- package/src/lib/model/commerce-cart.js +10 -3
- package/src/lib/model/commerce-cart.js.map +1 -1
- package/src/lib/model/commerce-invoices.d.ts +139 -0
- package/src/lib/model/commerce-invoices.js +343 -0
- package/src/lib/model/commerce-invoices.js.map +1 -0
- package/src/lib/model/commerce-orders.d.ts +35 -5
- package/src/lib/model/commerce-orders.js +3 -0
- package/src/lib/model/commerce-orders.js.map +1 -1
- package/src/lib/model/commerce-pos-kiosk.d.ts +1 -1
- package/src/lib/model/commerce-pos-kiosk.js +3 -2
- package/src/lib/model/commerce-pos-kiosk.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 +81 -0
- package/src/lib/model/commerce-quote-email.js +170 -0
- package/src/lib/model/commerce-quote-email.js.map +1 -0
- package/src/lib/model/commerce-quote-pay.d.ts +49 -6
- package/src/lib/model/commerce-quote-pay.js +69 -36
- package/src/lib/model/commerce-quote-pay.js.map +1 -1
- 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 +81 -0
- package/src/lib/model/commerce-quotes.js +1 -1
- package/src/lib/model/commerce-quotes.js.map +1 -1
- package/src/lib/model/commerce.d.ts +18 -0
- package/src/lib/model/commerce.js +26 -0
- package/src/lib/model/commerce.js.map +1 -1
- package/src/lib/model/index.d.ts +3 -0
- package/src/lib/model/index.js +3 -0
- package/src/lib/model/index.js.map +1 -1
- package/src/lib/model/product-modifiers.d.ts +74 -4
- package/src/lib/model/product-modifiers.js +131 -10
- package/src/lib/model/product-modifiers.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 +164 -98
- package/src/lib/server/billing-webhook.js.map +1 -1
- package/src/lib/server/cart-checkout.js +14 -2
- package/src/lib/server/cart-checkout.js.map +1 -1
- package/src/lib/server/cart.d.ts +5 -0
- package/src/lib/server/cart.js +41 -4
- package/src/lib/server/cart.js.map +1 -1
- package/src/lib/server/catalog.d.ts +6 -0
- package/src/lib/server/catalog.js +2 -0
- package/src/lib/server/catalog.js.map +1 -1
- package/src/lib/server/checkout-extras.js +3 -1
- package/src/lib/server/checkout-extras.js.map +1 -1
- package/src/lib/server/checkout.js +28 -3
- package/src/lib/server/checkout.js.map +1 -1
- package/src/lib/server/commerce-pdf.d.ts +5 -1
- package/src/lib/server/commerce-pdf.js +37 -10
- package/src/lib/server/commerce-pdf.js.map +1 -1
- 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/local-fulfillment.js +5 -1
- package/src/lib/server/local-fulfillment.js.map +1 -1
- 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-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/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-offline-sync.js +1 -1
- package/src/lib/server/pos-offline-sync.js.map +1 -1
- package/src/lib/server/pos-order.js +1 -1
- package/src/lib/server/pos-order.js.map +1 -1
- package/src/lib/server/product.d.ts +26 -0
- package/src/lib/server/product.js +18 -0
- package/src/lib/server/product.js.map +1 -1
- package/src/lib/server/products-write.js +5 -2
- package/src/lib/server/products-write.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 +8 -3
- package/src/lib/server/quote-pay.js +105 -43
- package/src/lib/server/quote-pay.js.map +1 -1
- package/src/lib/server/quote.d.ts +11 -0
- package/src/lib/server/quote.js +309 -12
- package/src/lib/server/quote.js.map +1 -1
- 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 +5 -0
- 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/add-to-cart.d.ts +8 -0
- package/src/lib/utils/add-to-cart.js +10 -2
- package/src/lib/utils/add-to-cart.js.map +1 -1
- package/src/lib/utils/commerce-document-pdf.d.ts +5 -0
- package/src/lib/utils/commerce-document-pdf.js +6 -1
- package/src/lib/utils/commerce-document-pdf.js.map +1 -1
- 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 +1 -1
- package/src/lib/utils/order-invoice.js +14 -6
- package/src/lib/utils/order-invoice.js.map +1 -1
- package/src/lib/utils/quote-email-content.d.ts +63 -0
- package/src/lib/utils/quote-email-content.js +157 -0
- package/src/lib/utils/quote-email-content.js.map +1 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/utils/commerce-document-pdf.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 { buildEmailPalette, emailAccent, resolveEmailColor, type EmailPalette } from '@aglyn/shared-util-email'\nimport { relativeLuminanceOfRgb, rgbChannelsOfHex } from '@aglyn/shared-util-tools/contrast'\nimport type { PdfJpegImage } from '@aglyn/shared-util-tools/pdf/pdf-file'\nimport { PdfWriter, pdfRgbFromHex, type PdfRgb, type PdfTableCell } from '@aglyn/shared-util-tools/pdf/pdf-writer'\nimport type { QuotePageAddress, QuotePageView } from '../model/commerce-quote-pay'\nimport { buildOrderInvoiceData, invoiceAddressLines, invoiceMoney, type InvoiceOrder, type OrderInvoiceStore } from './order-invoice'\n\n/*==========================================\n * A QUOTE AND AN INVOICE AS BRANDED PDFs (AGL-4095).\n *\n * renderQuotePdf({ brand, view, revision, payUrl }) → Uint8Array\n * renderOrderInvoicePdf({ brand, order, orderId, store }) → Uint8Array\n *\n * Both are one layout: the business's logo, name and contact at the top left\n * under a band of the site's primary color, the document's name, number,\n * date and standing at the top right, who it is billed and shipped to, the\n * lines in a table whose header wears the primary color, the totals, then\n * notes, terms and (for a quote still to be paid) the pay link as a button\n * and as text, both clickable. Every page ends with the document's name and\n * its page number.\n *\n * Pure over its input: the server reads the site, its theme and its logo\n * (`server/commerce-pdf-brand.ts`) and the quote or order, and these turn\n * them into bytes, so the console's download, the customer's download and\n * the emails that attach a quote or an invoice all make the same file.\n *\n * A quote is drawn from `buildQuotePageView`, the customer-safe projection\n * the quote page shows, so the PDF never says more than the page does. An\n * invoice is drawn from `buildOrderInvoiceData`, the content the printed\n * invoice shows.\n *=========================================*/\n\n/** The colors a document is drawn in, as `#rrggbb`. */\nexport interface CommercePdfColors {\n /** The band, the table's header and the pay button: the site's primary color. */\n accent: string\n /** Text set on the accent. */\n onAccent: string\n /** The accent as text on white: the document's name, links. */\n accentText: string\n /** A wash of the accent, behind the total. */\n tint: string\n ink: string\n muted: string\n rule: string\n}\n\n/** Who the document is from. */\nexport interface CommercePdfBrand {\n businessName: string\n /** Address lines, email, phone and web address, in that order. */\n contactLines: readonly string[]\n /** The site's logo as a JPEG; the business name stands alone without one. */\n logo?: PdfJpegImage | null\n /** The site theme's colors; any left out are the neutral ones. */\n colors?: Partial<CommercePdfColors> | null\n /** The site's time zone, which dates are given in. */\n timeZone?: string | null\n}\n\nconst NEUTRAL_PALETTE = buildEmailPalette({ base: 'tenant' })\nconst neutral = (path: string) => resolveEmailColor(path, NEUTRAL_PALETTE) ?? ''\n\n/** The colors a document wears where its site's theme gives none: the default site theme's greys. */\nexport const COMMERCE_PDF_NEUTRAL_COLORS: Readonly<CommercePdfColors> = Object.freeze({\n accent: neutral('grey.800'),\n onAccent: neutral('background.paper'),\n accentText: neutral('grey.800'),\n tint: neutral('grey.100'),\n ink: neutral('text.primary'),\n muted: neutral('text.secondary'),\n rule: neutral('divider'),\n})\n\n/** WCAG contrast between two `#rrggbb` colors; 1 when either cannot be read. */\nfunction contrast(one: string, other: string): number {\n const a = rgbChannelsOfHex(one)\n const b = rgbChannelsOfHex(other)\n if (!a || !b) return 1\n const la = relativeLuminanceOfRgb(...a)\n const lb = relativeLuminanceOfRgb(...b)\n return (Math.max(la, lb) + 0.05) / (Math.min(la, lb) + 0.05)\n}\n\n/**\n * A document's colors from a site's resolved palette: the primary color for\n * the accent, the theme's own text and divider colors, and text on the\n * accent that reads at 4.5:1 (the theme's contrast text when it does, else\n * the theme's dark ink or white, whichever reads better).\n */\nexport function commercePdfColorsFromPalette(palette: EmailPalette): CommercePdfColors {\n const accent = emailAccent(palette)\n const read = (path: string, fallback: string) => resolveEmailColor(path, palette) ?? fallback\n const ink = read('text.primary', COMMERCE_PDF_NEUTRAL_COLORS.ink)\n const paper = read('background.paper', COMMERCE_PDF_NEUTRAL_COLORS.onAccent)\n const onAccent =\n contrast(accent.label, accent.fill) >= 4.5\n ? accent.label\n : contrast(ink, accent.fill) >= contrast(paper, accent.fill)\n ? ink\n : paper\n return {\n accent: accent.fill,\n onAccent,\n accentText: contrast(accent.link, paper) >= 4.5 ? accent.link : ink,\n tint: read('tint.primary', COMMERCE_PDF_NEUTRAL_COLORS.tint),\n ink,\n muted: read('text.secondary', COMMERCE_PDF_NEUTRAL_COLORS.muted),\n rule: read('divider', COMMERCE_PDF_NEUTRAL_COLORS.rule),\n }\n}\n\n/*==========================================\n * WHAT A DOCUMENT SAYS\n *=========================================*/\n\ninterface DocumentLine {\n name: string\n details: string[]\n quantity: number\n unit: string\n amount: string\n}\n\ninterface SummaryRow {\n label: string\n value: string\n emphasis?: boolean\n /** A note in place of a figure, such as \"Tax is added at payment\". */\n note?: boolean\n}\n\ninterface CommerceDocument {\n /** QUOTE or INVOICE, across the top. */\n kind: string\n /** `Quote #1042`. */\n title: string\n facts: Array<[string, string]>\n /** A short standing, set in a pill under the facts: `Paid October 30, 2026`. */\n stamp: string | null\n parties: Array<{ label: string; lines: string[] }>\n lines: DocumentLine[]\n summary: SummaryRow[]\n sections: Array<{ heading: string; text: string; link?: string }>\n pay: { url: string; label: string; intro: string } | null\n}\n\n/** A date the American way, in the site's time zone: `October 28, 2026`. */\nexport function formatPdfDate(ms: number | null | undefined, timeZone?: string | null): string {\n if (!ms || !Number.isFinite(ms)) return ''\n const options: Intl.DateTimeFormatOptions = { year: 'numeric', month: 'long', day: 'numeric' }\n try {\n return new Intl.DateTimeFormat('en-US', { ...options, ...(timeZone ? { timeZone } : {}) }).format(new Date(ms))\n } catch {\n return new Intl.DateTimeFormat('en-US', { ...options, timeZone: 'UTC' }).format(new Date(ms))\n }\n}\n\nconst minus = (text: string) => `-${text}`\n\n/*==========================================\n * THE QUOTE\n *=========================================*/\n\nexport interface QuotePdfInput {\n brand: CommercePdfBrand\n /** The quote as its customer may see it: `buildQuotePageView`. */\n view: QuotePageView\n /** The quote's revision, printed beside its date. */\n revision?: number | null\n /** The pay link, printed on a quote still to be paid; left off when the reader may not have it. */\n payUrl?: string | null\n}\n\nfunction quoteAddressLines(address: QuotePageAddress | null): string[] {\n return invoiceAddressLines(address as never, { phone: false })\n}\n\nfunction quoteDocument(input: QuotePdfInput): CommerceDocument {\n const { view, brand } = input\n const money = (cents: number) => invoiceMoney(cents, view.currency)\n const date = (ms: number | null) => formatPdfDate(ms, brand.timeZone)\n const customer = [view.customerName ?? '', view.company ?? ''].filter(Boolean)\n const billTo = quoteAddressLines(view.billTo)\n const parties: CommerceDocument['parties'] = []\n if (billTo.length) {\n // The bill-to's own name leads; the customer's name and company are added when it does not carry them.\n const named = customer.filter((part) => !billTo.includes(part))\n parties.push({ label: 'Bill to', lines: [...named, ...billTo] })\n } else if (customer.length) {\n parties.push({ label: 'Prepared for', lines: customer })\n }\n const shipTo = quoteAddressLines(view.shipTo)\n if (shipTo.length) parties.push({ label: 'Ship to', lines: shipTo })\n\n const summary: SummaryRow[] = [{ label: 'Subtotal', value: money(view.totals.itemsCents) }]\n if (view.totals.discountCents > 0) {\n summary.push({\n label: view.discountLabel ? `Discount (${view.discountLabel})` : 'Discount',\n value: minus(money(view.totals.discountCents)),\n })\n }\n if (view.totals.shippingCents > 0) summary.push({ label: 'Shipping', value: money(view.totals.shippingCents) })\n if (view.taxAtPayment) summary.push({ label: 'Tax is added at payment', value: '', note: true })\n else if (view.totals.taxCents > 0) summary.push({ label: view.taxLabel || 'Tax', value: money(view.totals.taxCents) })\n else if (view.taxLabel && view.taxLabel !== 'Tax') summary.push({ label: view.taxLabel, value: '', note: true })\n summary.push({ label: view.taxAtPayment ? 'Total before tax' : 'Total', value: money(view.totals.totalCents), emphasis: true })\n\n const stamp =\n view.status === 'paid'\n ? `Paid${view.paidAtMs ? ` ${date(view.paidAtMs)}` : ''}`\n : view.status === 'canceled'\n ? 'Canceled'\n : view.status === 'refunded'\n ? 'Refunded'\n : null\n const facts: Array<[string, string]> = []\n if (view.dateMs) facts.push(['Date', date(view.dateMs)])\n if (input.revision && input.revision > 0) facts.push(['Revision', String(input.revision)])\n\n const sections: CommerceDocument['sections'] = []\n if (view.notes) sections.push({ heading: 'Notes', text: view.notes })\n if (view.terms) sections.push({ heading: 'Terms', text: view.terms })\n\n const payUrl = view.status === 'unpaid' && input.payUrl ? input.payUrl : null\n return {\n kind: 'QUOTE',\n title: view.number,\n facts,\n stamp,\n parties,\n lines: view.lines.map((line) => ({\n name: line.name,\n details: [line.variantLabel ?? '', line.description ?? '', line.sku ? `SKU ${line.sku}` : ''].filter(Boolean),\n quantity: line.quantity,\n unit: line.included ? '' : money(line.unitAmountCents),\n amount: line.included ? 'Included' : money(line.amountCents),\n })),\n summary,\n sections,\n pay: payUrl\n ? {\n url: payUrl,\n label: 'Pay this quote',\n intro: view.approvalRequired\n ? `${view.storeName || 'We'} will ask you to approve the proof first, at this link:`\n : 'Open the quote on our site to pay it securely by card:',\n }\n : null,\n }\n}\n\n/** The quote as a PDF. */\nexport function renderQuotePdf(input: QuotePdfInput): Uint8Array {\n return renderCommerceDocument(input.brand, quoteDocument(input))\n}\n\n/*==========================================\n * THE INVOICE\n *=========================================*/\n\nexport interface OrderInvoicePdfInput {\n brand: CommercePdfBrand\n order: InvoiceOrder\n orderId: string\n /** The store's currency, receipt footer and terms address (`invoiceStoreFrom`). */\n store: OrderInvoiceStore\n}\n\nfunction invoiceDocument(input: OrderInvoicePdfInput): CommerceDocument {\n const data = buildOrderInvoiceData(input)\n const money = (cents: number) => invoiceMoney(cents, data.currency)\n const parties: CommerceDocument['parties'] = []\n if (data.billTo.length) parties.push({ label: 'Bill to', lines: data.billTo })\n if (data.shipTo.length) parties.push({ label: 'Ship to', lines: data.shipTo })\n const facts: Array<[string, string]> = []\n if (data.dateMs) facts.push(['Order date', formatPdfDate(data.dateMs, input.brand.timeZone)])\n const sections: CommerceDocument['sections'] = []\n if (data.footer) sections.push({ heading: '', text: data.footer })\n if (data.termsUrl) sections.push({ heading: 'Terms', text: data.termsUrl, link: data.termsUrl })\n return {\n kind: 'INVOICE',\n title: `Invoice ${data.number}`,\n facts,\n stamp: data.paymentStatus,\n parties,\n lines: data.lines.map((line) => ({\n name: line.name,\n details: [line.detail, line.description].filter(Boolean),\n quantity: line.quantity,\n unit: line.included ? '' : money(line.unitCents),\n amount: line.included ? 'Included' : money(line.amountCents),\n })),\n summary: data.summary.map((row) => ({\n label: row.label,\n value: row.minus ? minus(money(row.cents)) : money(row.cents),\n emphasis: row.emphasis,\n })),\n sections,\n pay: null,\n }\n}\n\n/** Any order's invoice as a PDF, a paid quote's included. */\nexport function renderOrderInvoicePdf(input: OrderInvoicePdfInput): Uint8Array {\n return renderCommerceDocument(input.brand, invoiceDocument(input))\n}\n\n/*==========================================\n * THE LAYOUT\n *=========================================*/\n\nconst PAGE_MARGIN = { top: 48, right: 48, bottom: 60, left: 48 }\nconst BAND_HEIGHT = 10\nconst LOGO_BOX = { width: 180, height: 50 }\n\nfunction renderCommerceDocument(brand: CommercePdfBrand, document: CommerceDocument): Uint8Array {\n const palette = { ...COMMERCE_PDF_NEUTRAL_COLORS, ...(brand.colors ?? {}) }\n const color = (hex: string, fallback: string): PdfRgb =>\n pdfRgbFromHex(hex) ?? pdfRgbFromHex(fallback) ?? [0, 0, 0]\n const colors = {\n accent: color(palette.accent, COMMERCE_PDF_NEUTRAL_COLORS.accent),\n onAccent: color(palette.onAccent, COMMERCE_PDF_NEUTRAL_COLORS.onAccent),\n accentText: color(palette.accentText, COMMERCE_PDF_NEUTRAL_COLORS.accentText),\n tint: color(palette.tint, COMMERCE_PDF_NEUTRAL_COLORS.tint),\n ink: color(palette.ink, COMMERCE_PDF_NEUTRAL_COLORS.ink),\n muted: color(palette.muted, COMMERCE_PDF_NEUTRAL_COLORS.muted),\n rule: color(palette.rule, COMMERCE_PDF_NEUTRAL_COLORS.rule),\n }\n const businessName = brand.businessName.trim()\n const documentTitle = businessName ? `${document.title} - ${businessName}` : document.title\n\n const writer: PdfWriter = new PdfWriter({\n title: documentTitle,\n margin: PAGE_MARGIN,\n onNewPage: (page) => {\n page.fillRect(0, page.pageHeight - BAND_HEIGHT / 2, page.pageWidth, BAND_HEIGHT / 2, colors.accent)\n const baseline = page.top + 14\n if (businessName) page.textIn(page.left, page.contentWidth, baseline, businessName, { size: 8.5, font: 'bold', color: colors.muted })\n page.textIn(page.left, page.contentWidth, baseline, `${document.title} (continued)`, { size: 8.5, color: colors.muted }, 'right')\n },\n })\n const { left, right } = writer\n\n // The band across the top of the first page.\n writer.fillRect(0, writer.pageHeight - BAND_HEIGHT, writer.pageWidth, BAND_HEIGHT, colors.accent)\n\n // Left: logo, business name, contact.\n const leftWidth = writer.contentWidth * 0.55\n let leftY = writer.top\n if (brand.logo) {\n const drawn = writer.image(brand.logo, { x: left, y: leftY - LOGO_BOX.height, width: LOGO_BOX.width, height: LOGO_BOX.height })\n leftY = drawn.y - 12\n }\n writer.y = leftY\n if (businessName) writer.paragraph(businessName, { width: leftWidth, style: { font: 'bold', size: 14, color: colors.ink }, leading: 1.25, after: 2 })\n for (const line of brand.contactLines) {\n writer.paragraph(line, { width: leftWidth, style: { size: 9, color: colors.muted }, leading: 1.3 })\n }\n leftY = writer.y\n\n // Right: the document's name, number, facts and standing.\n const factsWidth = 210\n const factsX = right - factsWidth\n let rightY = writer.top - 22\n writer.textIn(factsX, factsWidth, rightY, document.kind, { font: 'bold', size: 24, color: colors.accentText }, 'right')\n rightY -= 20\n writer.textIn(factsX, factsWidth, rightY, document.title, { font: 'bold', size: 11, color: colors.ink }, 'right')\n for (const [label, value] of document.facts) {\n rightY -= 14\n const valueWidth = writer.measure(value, { size: 9.5 })\n writer.textIn(factsX, factsWidth, rightY, value, { size: 9.5, color: colors.ink }, 'right')\n writer.textIn(factsX, factsWidth - valueWidth - 8, rightY, label, { size: 9.5, color: colors.muted }, 'right')\n }\n if (document.stamp) {\n rightY -= 24\n const style = { font: 'bold' as const, size: 8.5, color: colors.onAccent }\n const width = writer.measure(document.stamp, style) + 16\n writer.fillRect(right - width, rightY - 5, width, 17, colors.accent)\n writer.textIn(right - width, width, rightY, document.stamp, style, 'center')\n rightY -= 5\n }\n\n writer.y = Math.min(leftY, rightY) - 22\n\n // Who it is for.\n if (document.parties.length) {\n const columnWidth = Math.min(220, writer.contentWidth / document.parties.length)\n const top = writer.y\n let lowest = top\n document.parties.forEach((party, index) => {\n const x = left + index * (columnWidth + 16)\n writer.y = top\n writer.paragraph(party.label.toUpperCase(), { x, width: columnWidth, style: { font: 'bold', size: 8, color: colors.muted }, after: 2 })\n party.lines.forEach((line, lineIndex) => {\n writer.paragraph(line, {\n x,\n width: columnWidth,\n style: { size: 9.5, color: colors.ink, font: lineIndex === 0 ? 'bold' : 'regular' },\n })\n })\n lowest = Math.min(lowest, writer.y)\n })\n writer.y = lowest - 18\n }\n\n // The lines.\n const rows: PdfTableCell[][] = document.lines.map((line) => [\n [\n { text: line.name, style: { font: 'bold', size: 9.5, color: colors.ink } },\n ...line.details.map((detail) => ({ text: detail, style: { size: 8.5, color: colors.muted } })),\n ],\n String(line.quantity),\n line.unit,\n line.amount,\n ])\n writer.table({\n columns: [\n { header: 'Item', width: 0 },\n { header: 'Qty', width: 48, align: 'right' },\n { header: 'Unit price', width: 86, align: 'right' },\n { header: 'Amount', width: 90, align: 'right' },\n ],\n rows,\n headerFill: colors.accent,\n headerStyle: { font: 'bold', size: 8.5, color: colors.onAccent },\n bodyStyle: { size: 9.5, color: colors.ink },\n padding: { x: 8, y: 6 },\n ruleColor: colors.rule,\n })\n\n // The totals on the right; beside them on the left, paying it.\n const summaryWidth = 250\n const summaryX = right - summaryWidth\n const payWidth = writer.contentWidth - summaryWidth - 28\n const rowHeight = 17\n const summaryHeight = document.summary.reduce((sum, row) => sum + (row.emphasis ? rowHeight + 8 : rowHeight), 0)\n const pay = document.pay\n const buttonStyle = { font: 'bold' as const, size: 10.5, color: colors.onAccent }\n const buttonHeight = 26\n const payParts = pay\n ? {\n intro: writer.wrap(pay.intro, payWidth, { size: 9 }),\n url: writer.wrap(pay.url, payWidth, { size: 8 }),\n }\n : null\n const payHeight = payParts ? 18 + payParts.intro.length * 12 + 6 + buttonHeight + 8 + payParts.url.length * 11 : 0\n writer.down(12)\n writer.ensure(Math.max(summaryHeight, payHeight))\n const blockTop = writer.y\n for (const row of document.summary) {\n const height = row.emphasis ? rowHeight + 8 : rowHeight\n if (row.emphasis) writer.fillRect(summaryX, writer.y - height, summaryWidth, height, colors.tint)\n const size = row.emphasis ? 11.5 : 9.5\n const baseline = writer.y - height / 2 - size * 0.35\n const style = row.emphasis\n ? { font: 'bold' as const, size, color: colors.ink }\n : { size, color: row.note ? colors.muted : colors.ink, ...(row.note ? { font: 'italic' as const } : {}) }\n writer.textIn(summaryX + 8, summaryWidth - 16, baseline, row.label, style, 'left')\n if (row.value) writer.textIn(summaryX + 8, summaryWidth - 16, baseline, row.value, style, 'right')\n writer.down(height)\n }\n const summaryBottom = writer.y\n if (pay && payParts) {\n let y = blockTop - 12\n writer.text(left, y, 'Pay online', { font: 'bold', size: 11, color: colors.ink })\n y -= 6\n for (const line of payParts.intro) {\n y -= 12\n writer.text(left, y + 2, line, { size: 9, color: colors.muted })\n }\n y -= 6\n const buttonWidth = writer.measure(pay.label, buttonStyle) + 36\n writer.fillRect(left, y - buttonHeight, buttonWidth, buttonHeight, colors.accent)\n writer.textIn(left, buttonWidth, y - buttonHeight / 2 - 10.5 * 0.35, pay.label, buttonStyle, 'center')\n writer.link(left, y - buttonHeight, buttonWidth, buttonHeight, pay.url)\n y -= buttonHeight + 8\n for (const line of payParts.url) {\n y -= 11\n writer.text(left, y + 2, line, { size: 8, color: colors.accentText })\n writer.link(left, y, writer.measure(line, { size: 8 }), 11, pay.url)\n }\n writer.y = Math.min(summaryBottom, y)\n }\n writer.down(22)\n\n // Notes, terms, the store's footer.\n for (const section of document.sections) {\n if (section.heading) {\n writer.ensure(36)\n writer.paragraph(section.heading, { style: { font: 'bold', size: 10, color: colors.ink }, after: 1 })\n }\n writer.paragraph(section.text, {\n style: { size: 9, color: section.link ? colors.accentText : colors.muted },\n ...(section.link ? { link: section.link } : {}),\n after: 12,\n })\n }\n\n return writer.finish({\n footer: (page, pages) => ({\n left: businessName ? `${businessName} · ${document.title}` : document.title,\n right: `Page ${page} of ${pages}`,\n }),\n footerStyle: { size: 8, color: colors.muted },\n footerRule: colors.rule,\n })\n}\n"],"names":["buildEmailPalette","emailAccent","resolveEmailColor","relativeLuminanceOfRgb","rgbChannelsOfHex","PdfWriter","pdfRgbFromHex","buildOrderInvoiceData","invoiceAddressLines","invoiceMoney","NEUTRAL_PALETTE","base","neutral","path","COMMERCE_PDF_NEUTRAL_COLORS","Object","freeze","accent","onAccent","accentText","tint","ink","muted","rule","contrast","one","other","a","b","la","lb","Math","max","min","commercePdfColorsFromPalette","palette","read","fallback","paper","label","fill","link","formatPdfDate","ms","timeZone","Number","isFinite","options","year","month","day","Intl","DateTimeFormat","format","Date","minus","text","quoteAddressLines","address","phone","quoteDocument","input","view","brand","money","cents","currency","date","customer","customerName","company","filter","Boolean","billTo","parties","length","named","part","includes","push","lines","shipTo","summary","value","totals","itemsCents","discountCents","discountLabel","shippingCents","taxAtPayment","note","taxCents","taxLabel","totalCents","emphasis","stamp","status","paidAtMs","facts","dateMs","revision","String","sections","notes","heading","terms","payUrl","kind","title","number","map","line","name","details","variantLabel","description","sku","quantity","unit","included","unitAmountCents","amount","amountCents","pay","url","intro","approvalRequired","storeName","renderQuotePdf","renderCommerceDocument","invoiceDocument","data","footer","termsUrl","paymentStatus","detail","unitCents","row","renderOrderInvoicePdf","PAGE_MARGIN","top","right","bottom","left","BAND_HEIGHT","LOGO_BOX","width","height","document","colors","color","hex","businessName","trim","documentTitle","writer","margin","onNewPage","page","fillRect","pageHeight","pageWidth","baseline","textIn","contentWidth","size","font","leftWidth","leftY","logo","drawn","image","x","y","paragraph","style","leading","after","contactLines","factsWidth","factsX","rightY","valueWidth","measure","columnWidth","lowest","forEach","party","index","toUpperCase","lineIndex","rows","table","columns","header","align","headerFill","headerStyle","bodyStyle","padding","ruleColor","summaryWidth","summaryX","payWidth","rowHeight","summaryHeight","reduce","sum","buttonStyle","buttonHeight","payParts","wrap","payHeight","down","ensure","blockTop","summaryBottom","buttonWidth","section","finish","pages","footerStyle","footerRule"],"mappings":";AAAA;;;;;;;;;;;;;;;CAeC,GAED,SAASA,iBAAiB,EAAEC,WAAW,EAAEC,iBAAiB,QAA2B,2BAA0B;AAC/G,SAASC,sBAAsB,EAAEC,gBAAgB,QAAQ,oCAAmC;AAE5F,SAASC,SAAS,EAAEC,aAAa,QAAwC,0CAAyC;AAElH,SAASC,qBAAqB,EAAEC,mBAAmB,EAAEC,YAAY,QAAmD,qBAAiB;AAuDrI,MAAMC,kBAAkBV,kBAAkB;IAAEW,MAAM;AAAS;AAC3D,MAAMC,UAAU,CAACC;QAAiBX;YAAAA,qBAAAA,kBAAkBW,MAAMH,4BAAxBR,qBAA4C;;AAE9E,mGAAmG,GACnG,OAAO,MAAMY,8BAA2DC,OAAOC,MAAM,CAAC;IACpFC,QAAQL,QAAQ;IAChBM,UAAUN,QAAQ;IAClBO,YAAYP,QAAQ;IACpBQ,MAAMR,QAAQ;IACdS,KAAKT,QAAQ;IACbU,OAAOV,QAAQ;IACfW,MAAMX,QAAQ;AAChB,GAAE;AAEF,8EAA8E,GAC9E,SAASY,SAASC,GAAW,EAAEC,KAAa;IAC1C,MAAMC,IAAIvB,iBAAiBqB;IAC3B,MAAMG,IAAIxB,iBAAiBsB;IAC3B,IAAI,CAACC,KAAK,CAACC,GAAG,OAAO;IACrB,MAAMC,KAAK1B,0BAA0BwB;IACrC,MAAMG,KAAK3B,0BAA0ByB;IACrC,OAAO,AAACG,CAAAA,KAAKC,GAAG,CAACH,IAAIC,MAAM,IAAG,IAAMC,CAAAA,KAAKE,GAAG,CAACJ,IAAIC,MAAM,IAAG;AAC5D;AAEA;;;;;CAKC,GACD,OAAO,SAASI,6BAA6BC,OAAqB;IAChE,MAAMlB,SAAShB,YAAYkC;IAC3B,MAAMC,OAAO,CAACvB,MAAcwB;YAAqBnC;gBAAAA,qBAAAA,kBAAkBW,MAAMsB,oBAAxBjC,qBAAoCmC;;IACrF,MAAMhB,MAAMe,KAAK,gBAAgBtB,4BAA4BO,GAAG;IAChE,MAAMiB,QAAQF,KAAK,oBAAoBtB,4BAA4BI,QAAQ;IAC3E,MAAMA,WACJM,SAASP,OAAOsB,KAAK,EAAEtB,OAAOuB,IAAI,KAAK,MACnCvB,OAAOsB,KAAK,GACZf,SAASH,KAAKJ,OAAOuB,IAAI,KAAKhB,SAASc,OAAOrB,OAAOuB,IAAI,IACvDnB,MACAiB;IACR,OAAO;QACLrB,QAAQA,OAAOuB,IAAI;QACnBtB;QACAC,YAAYK,SAASP,OAAOwB,IAAI,EAAEH,UAAU,MAAMrB,OAAOwB,IAAI,GAAGpB;QAChED,MAAMgB,KAAK,gBAAgBtB,4BAA4BM,IAAI;QAC3DC;QACAC,OAAOc,KAAK,kBAAkBtB,4BAA4BQ,KAAK;QAC/DC,MAAMa,KAAK,WAAWtB,4BAA4BS,IAAI;IACxD;AACF;AAqCA,0EAA0E,GAC1E,OAAO,SAASmB,cAAcC,EAA6B,EAAEC,QAAwB;IACnF,IAAI,CAACD,MAAM,CAACE,OAAOC,QAAQ,CAACH,KAAK,OAAO;IACxC,MAAMI,UAAsC;QAAEC,MAAM;QAAWC,OAAO;QAAQC,KAAK;IAAU;IAC7F,IAAI;QACF,OAAO,IAAIC,KAAKC,cAAc,CAAC,SAAS,aAAKL,SAAaH,WAAW;YAAEA;QAAS,IAAI,CAAC,IAAMS,MAAM,CAAC,IAAIC,KAAKX;IAC7G,EAAE,eAAM;QACN,OAAO,IAAIQ,KAAKC,cAAc,CAAC,SAAS,aAAKL;YAASH,UAAU;YAASS,MAAM,CAAC,IAAIC,KAAKX;IAC3F;AACF;AAEA,MAAMY,QAAQ,CAACC,OAAiB,CAAC,CAAC,EAAEA,MAAM;AAgB1C,SAASC,kBAAkBC,OAAgC;IACzD,OAAOlD,oBAAoBkD,SAAkB;QAAEC,OAAO;IAAM;AAC9D;AAEA,SAASC,cAAcC,KAAoB;QAIvBC,oBAAyBA;IAH3C,MAAM,EAAEA,IAAI,EAAEC,KAAK,EAAE,GAAGF;IACxB,MAAMG,QAAQ,CAACC,QAAkBxD,aAAawD,OAAOH,KAAKI,QAAQ;IAClE,MAAMC,OAAO,CAACxB,KAAsBD,cAAcC,IAAIoB,MAAMnB,QAAQ;IACpE,MAAMwB,WAAW;SAACN,qBAAAA,KAAKO,YAAY,YAAjBP,qBAAqB;SAAIA,gBAAAA,KAAKQ,OAAO,YAAZR,gBAAgB;KAAG,CAACS,MAAM,CAACC;IACtE,MAAMC,SAAShB,kBAAkBK,KAAKW,MAAM;IAC5C,MAAMC,UAAuC,EAAE;IAC/C,IAAID,OAAOE,MAAM,EAAE;QACjB,uGAAuG;QACvG,MAAMC,QAAQR,SAASG,MAAM,CAAC,CAACM,OAAS,CAACJ,OAAOK,QAAQ,CAACD;QACzDH,QAAQK,IAAI,CAAC;YAAExC,OAAO;YAAWyC,OAAO;mBAAIJ;mBAAUH;aAAO;QAAC;IAChE,OAAO,IAAIL,SAASO,MAAM,EAAE;QAC1BD,QAAQK,IAAI,CAAC;YAAExC,OAAO;YAAgByC,OAAOZ;QAAS;IACxD;IACA,MAAMa,SAASxB,kBAAkBK,KAAKmB,MAAM;IAC5C,IAAIA,OAAON,MAAM,EAAED,QAAQK,IAAI,CAAC;QAAExC,OAAO;QAAWyC,OAAOC;IAAO;IAElE,MAAMC,UAAwB;QAAC;YAAE3C,OAAO;YAAY4C,OAAOnB,MAAMF,KAAKsB,MAAM,CAACC,UAAU;QAAE;KAAE;IAC3F,IAAIvB,KAAKsB,MAAM,CAACE,aAAa,GAAG,GAAG;QACjCJ,QAAQH,IAAI,CAAC;YACXxC,OAAOuB,KAAKyB,aAAa,GAAG,CAAC,UAAU,EAAEzB,KAAKyB,aAAa,CAAC,CAAC,CAAC,GAAG;YACjEJ,OAAO5B,MAAMS,MAAMF,KAAKsB,MAAM,CAACE,aAAa;QAC9C;IACF;IACA,IAAIxB,KAAKsB,MAAM,CAACI,aAAa,GAAG,GAAGN,QAAQH,IAAI,CAAC;QAAExC,OAAO;QAAY4C,OAAOnB,MAAMF,KAAKsB,MAAM,CAACI,aAAa;IAAE;IAC7G,IAAI1B,KAAK2B,YAAY,EAAEP,QAAQH,IAAI,CAAC;QAAExC,OAAO;QAA2B4C,OAAO;QAAIO,MAAM;IAAK;SACzF,IAAI5B,KAAKsB,MAAM,CAACO,QAAQ,GAAG,GAAGT,QAAQH,IAAI,CAAC;QAAExC,OAAOuB,KAAK8B,QAAQ,IAAI;QAAOT,OAAOnB,MAAMF,KAAKsB,MAAM,CAACO,QAAQ;IAAE;SAC/G,IAAI7B,KAAK8B,QAAQ,IAAI9B,KAAK8B,QAAQ,KAAK,OAAOV,QAAQH,IAAI,CAAC;QAAExC,OAAOuB,KAAK8B,QAAQ;QAAET,OAAO;QAAIO,MAAM;IAAK;IAC9GR,QAAQH,IAAI,CAAC;QAAExC,OAAOuB,KAAK2B,YAAY,GAAG,qBAAqB;QAASN,OAAOnB,MAAMF,KAAKsB,MAAM,CAACS,UAAU;QAAGC,UAAU;IAAK;IAE7H,MAAMC,QACJjC,KAAKkC,MAAM,KAAK,SACZ,CAAC,IAAI,EAAElC,KAAKmC,QAAQ,GAAG,CAAC,CAAC,EAAE9B,KAAKL,KAAKmC,QAAQ,GAAG,GAAG,IAAI,GACvDnC,KAAKkC,MAAM,KAAK,aACd,aACAlC,KAAKkC,MAAM,KAAK,aACd,aACA;IACV,MAAME,QAAiC,EAAE;IACzC,IAAIpC,KAAKqC,MAAM,EAAED,MAAMnB,IAAI,CAAC;QAAC;QAAQZ,KAAKL,KAAKqC,MAAM;KAAE;IACvD,IAAItC,MAAMuC,QAAQ,IAAIvC,MAAMuC,QAAQ,GAAG,GAAGF,MAAMnB,IAAI,CAAC;QAAC;QAAYsB,OAAOxC,MAAMuC,QAAQ;KAAE;IAEzF,MAAME,WAAyC,EAAE;IACjD,IAAIxC,KAAKyC,KAAK,EAAED,SAASvB,IAAI,CAAC;QAAEyB,SAAS;QAAShD,MAAMM,KAAKyC,KAAK;IAAC;IACnE,IAAIzC,KAAK2C,KAAK,EAAEH,SAASvB,IAAI,CAAC;QAAEyB,SAAS;QAAShD,MAAMM,KAAK2C,KAAK;IAAC;IAEnE,MAAMC,SAAS5C,KAAKkC,MAAM,KAAK,YAAYnC,MAAM6C,MAAM,GAAG7C,MAAM6C,MAAM,GAAG;IACzE,OAAO;QACLC,MAAM;QACNC,OAAO9C,KAAK+C,MAAM;QAClBX;QACAH;QACArB;QACAM,OAAOlB,KAAKkB,KAAK,CAAC8B,GAAG,CAAC,CAACC;gBAEXA,oBAAyBA;mBAFJ;gBAC/BC,MAAMD,KAAKC,IAAI;gBACfC,SAAS;qBAACF,qBAAAA,KAAKG,YAAY,YAAjBH,qBAAqB;qBAAIA,oBAAAA,KAAKI,WAAW,YAAhBJ,oBAAoB;oBAAIA,KAAKK,GAAG,GAAG,CAAC,IAAI,EAAEL,KAAKK,GAAG,EAAE,GAAG;iBAAG,CAAC7C,MAAM,CAACC;gBACrG6C,UAAUN,KAAKM,QAAQ;gBACvBC,MAAMP,KAAKQ,QAAQ,GAAG,KAAKvD,MAAM+C,KAAKS,eAAe;gBACrDC,QAAQV,KAAKQ,QAAQ,GAAG,aAAavD,MAAM+C,KAAKW,WAAW;YAC7D;;QACAxC;QACAoB;QACAqB,KAAKjB,SACD;YACEkB,KAAKlB;YACLnE,OAAO;YACPsF,OAAO/D,KAAKgE,gBAAgB,GACxB,GAAGhE,KAAKiE,SAAS,IAAI,KAAK,uDAAuD,CAAC,GAClF;QACN,IACA;IACN;AACF;AAEA,wBAAwB,GACxB,OAAO,SAASC,eAAenE,KAAoB;IACjD,OAAOoE,uBAAuBpE,MAAME,KAAK,EAAEH,cAAcC;AAC3D;AAcA,SAASqE,gBAAgBrE,KAA2B;IAClD,MAAMsE,OAAO5H,sBAAsBsD;IACnC,MAAMG,QAAQ,CAACC,QAAkBxD,aAAawD,OAAOkE,KAAKjE,QAAQ;IAClE,MAAMQ,UAAuC,EAAE;IAC/C,IAAIyD,KAAK1D,MAAM,CAACE,MAAM,EAAED,QAAQK,IAAI,CAAC;QAAExC,OAAO;QAAWyC,OAAOmD,KAAK1D,MAAM;IAAC;IAC5E,IAAI0D,KAAKlD,MAAM,CAACN,MAAM,EAAED,QAAQK,IAAI,CAAC;QAAExC,OAAO;QAAWyC,OAAOmD,KAAKlD,MAAM;IAAC;IAC5E,MAAMiB,QAAiC,EAAE;IACzC,IAAIiC,KAAKhC,MAAM,EAAED,MAAMnB,IAAI,CAAC;QAAC;QAAcrC,cAAcyF,KAAKhC,MAAM,EAAEtC,MAAME,KAAK,CAACnB,QAAQ;KAAE;IAC5F,MAAM0D,WAAyC,EAAE;IACjD,IAAI6B,KAAKC,MAAM,EAAE9B,SAASvB,IAAI,CAAC;QAAEyB,SAAS;QAAIhD,MAAM2E,KAAKC,MAAM;IAAC;IAChE,IAAID,KAAKE,QAAQ,EAAE/B,SAASvB,IAAI,CAAC;QAAEyB,SAAS;QAAShD,MAAM2E,KAAKE,QAAQ;QAAE5F,MAAM0F,KAAKE,QAAQ;IAAC;IAC9F,OAAO;QACL1B,MAAM;QACNC,OAAO,CAAC,QAAQ,EAAEuB,KAAKtB,MAAM,EAAE;QAC/BX;QACAH,OAAOoC,KAAKG,aAAa;QACzB5D;QACAM,OAAOmD,KAAKnD,KAAK,CAAC8B,GAAG,CAAC,CAACC,OAAU,CAAA;gBAC/BC,MAAMD,KAAKC,IAAI;gBACfC,SAAS;oBAACF,KAAKwB,MAAM;oBAAExB,KAAKI,WAAW;iBAAC,CAAC5C,MAAM,CAACC;gBAChD6C,UAAUN,KAAKM,QAAQ;gBACvBC,MAAMP,KAAKQ,QAAQ,GAAG,KAAKvD,MAAM+C,KAAKyB,SAAS;gBAC/Cf,QAAQV,KAAKQ,QAAQ,GAAG,aAAavD,MAAM+C,KAAKW,WAAW;YAC7D,CAAA;QACAxC,SAASiD,KAAKjD,OAAO,CAAC4B,GAAG,CAAC,CAAC2B,MAAS,CAAA;gBAClClG,OAAOkG,IAAIlG,KAAK;gBAChB4C,OAAOsD,IAAIlF,KAAK,GAAGA,MAAMS,MAAMyE,IAAIxE,KAAK,KAAKD,MAAMyE,IAAIxE,KAAK;gBAC5D6B,UAAU2C,IAAI3C,QAAQ;YACxB,CAAA;QACAQ;QACAqB,KAAK;IACP;AACF;AAEA,2DAA2D,GAC3D,OAAO,SAASe,sBAAsB7E,KAA2B;IAC/D,OAAOoE,uBAAuBpE,MAAME,KAAK,EAAEmE,gBAAgBrE;AAC7D;AAEA;;2CAE2C,GAE3C,MAAM8E,cAAc;IAAEC,KAAK;IAAIC,OAAO;IAAIC,QAAQ;IAAIC,MAAM;AAAG;AAC/D,MAAMC,cAAc;AACpB,MAAMC,WAAW;IAAEC,OAAO;IAAKC,QAAQ;AAAG;AAE1C,SAASlB,uBAAuBlE,KAAuB,EAAEqF,QAA0B;QAC3BrF;IAAtD,MAAM5B,UAAU,aAAKrB,8BAAiCiD,gBAAAA,MAAMsF,MAAM,YAAZtF,gBAAgB,CAAC;IACvE,MAAMuF,QAAQ,CAACC,KAAalH;YAC1B/B,MAAAA;gBAAAA,QAAAA,iBAAAA,cAAciJ,gBAAdjJ,iBAAsBA,cAAc+B,qBAApC/B,OAAiD;YAAC;YAAG;YAAG;SAAE;;IAC5D,MAAM+I,SAAS;QACbpI,QAAQqI,MAAMnH,QAAQlB,MAAM,EAAEH,4BAA4BG,MAAM;QAChEC,UAAUoI,MAAMnH,QAAQjB,QAAQ,EAAEJ,4BAA4BI,QAAQ;QACtEC,YAAYmI,MAAMnH,QAAQhB,UAAU,EAAEL,4BAA4BK,UAAU;QAC5EC,MAAMkI,MAAMnH,QAAQf,IAAI,EAAEN,4BAA4BM,IAAI;QAC1DC,KAAKiI,MAAMnH,QAAQd,GAAG,EAAEP,4BAA4BO,GAAG;QACvDC,OAAOgI,MAAMnH,QAAQb,KAAK,EAAER,4BAA4BQ,KAAK;QAC7DC,MAAM+H,MAAMnH,QAAQZ,IAAI,EAAET,4BAA4BS,IAAI;IAC5D;IACA,MAAMiI,eAAezF,MAAMyF,YAAY,CAACC,IAAI;IAC5C,MAAMC,gBAAgBF,eAAe,GAAGJ,SAASxC,KAAK,CAAC,GAAG,EAAE4C,cAAc,GAAGJ,SAASxC,KAAK;IAE3F,MAAM+C,SAAoB,IAAItJ,UAAU;QACtCuG,OAAO8C;QACPE,QAAQjB;QACRkB,WAAW,CAACC;YACVA,KAAKC,QAAQ,CAAC,GAAGD,KAAKE,UAAU,GAAGhB,cAAc,GAAGc,KAAKG,SAAS,EAAEjB,cAAc,GAAGK,OAAOpI,MAAM;YAClG,MAAMiJ,WAAWJ,KAAKlB,GAAG,GAAG;YAC5B,IAAIY,cAAcM,KAAKK,MAAM,CAACL,KAAKf,IAAI,EAAEe,KAAKM,YAAY,EAAEF,UAAUV,cAAc;gBAAEa,MAAM;gBAAKC,MAAM;gBAAQhB,OAAOD,OAAO/H,KAAK;YAAC;YACnIwI,KAAKK,MAAM,CAACL,KAAKf,IAAI,EAAEe,KAAKM,YAAY,EAAEF,UAAU,GAAGd,SAASxC,KAAK,CAAC,YAAY,CAAC,EAAE;gBAAEyD,MAAM;gBAAKf,OAAOD,OAAO/H,KAAK;YAAC,GAAG;QAC3H;IACF;IACA,MAAM,EAAEyH,IAAI,EAAEF,KAAK,EAAE,GAAGc;IAExB,6CAA6C;IAC7CA,OAAOI,QAAQ,CAAC,GAAGJ,OAAOK,UAAU,GAAGhB,aAAaW,OAAOM,SAAS,EAAEjB,aAAaK,OAAOpI,MAAM;IAEhG,sCAAsC;IACtC,MAAMsJ,YAAYZ,OAAOS,YAAY,GAAG;IACxC,IAAII,QAAQb,OAAOf,GAAG;IACtB,IAAI7E,MAAM0G,IAAI,EAAE;QACd,MAAMC,QAAQf,OAAOgB,KAAK,CAAC5G,MAAM0G,IAAI,EAAE;YAAEG,GAAG7B;YAAM8B,GAAGL,QAAQvB,SAASE,MAAM;YAAED,OAAOD,SAASC,KAAK;YAAEC,QAAQF,SAASE,MAAM;QAAC;QAC7HqB,QAAQE,MAAMG,CAAC,GAAG;IACpB;IACAlB,OAAOkB,CAAC,GAAGL;IACX,IAAIhB,cAAcG,OAAOmB,SAAS,CAACtB,cAAc;QAAEN,OAAOqB;QAAWQ,OAAO;YAAET,MAAM;YAAQD,MAAM;YAAIf,OAAOD,OAAOhI,GAAG;QAAC;QAAG2J,SAAS;QAAMC,OAAO;IAAE;IACnJ,KAAK,MAAMlE,QAAQhD,MAAMmH,YAAY,CAAE;QACrCvB,OAAOmB,SAAS,CAAC/D,MAAM;YAAEmC,OAAOqB;YAAWQ,OAAO;gBAAEV,MAAM;gBAAGf,OAAOD,OAAO/H,KAAK;YAAC;YAAG0J,SAAS;QAAI;IACnG;IACAR,QAAQb,OAAOkB,CAAC;IAEhB,0DAA0D;IAC1D,MAAMM,aAAa;IACnB,MAAMC,SAASvC,QAAQsC;IACvB,IAAIE,SAAS1B,OAAOf,GAAG,GAAG;IAC1Be,OAAOQ,MAAM,CAACiB,QAAQD,YAAYE,QAAQjC,SAASzC,IAAI,EAAE;QAAE2D,MAAM;QAAQD,MAAM;QAAIf,OAAOD,OAAOlI,UAAU;IAAC,GAAG;IAC/GkK,UAAU;IACV1B,OAAOQ,MAAM,CAACiB,QAAQD,YAAYE,QAAQjC,SAASxC,KAAK,EAAE;QAAE0D,MAAM;QAAQD,MAAM;QAAIf,OAAOD,OAAOhI,GAAG;IAAC,GAAG;IACzG,KAAK,MAAM,CAACkB,OAAO4C,MAAM,IAAIiE,SAASlD,KAAK,CAAE;QAC3CmF,UAAU;QACV,MAAMC,aAAa3B,OAAO4B,OAAO,CAACpG,OAAO;YAAEkF,MAAM;QAAI;QACrDV,OAAOQ,MAAM,CAACiB,QAAQD,YAAYE,QAAQlG,OAAO;YAAEkF,MAAM;YAAKf,OAAOD,OAAOhI,GAAG;QAAC,GAAG;QACnFsI,OAAOQ,MAAM,CAACiB,QAAQD,aAAaG,aAAa,GAAGD,QAAQ9I,OAAO;YAAE8H,MAAM;YAAKf,OAAOD,OAAO/H,KAAK;QAAC,GAAG;IACxG;IACA,IAAI8H,SAASrD,KAAK,EAAE;QAClBsF,UAAU;QACV,MAAMN,QAAQ;YAAET,MAAM;YAAiBD,MAAM;YAAKf,OAAOD,OAAOnI,QAAQ;QAAC;QACzE,MAAMgI,QAAQS,OAAO4B,OAAO,CAACnC,SAASrD,KAAK,EAAEgF,SAAS;QACtDpB,OAAOI,QAAQ,CAAClB,QAAQK,OAAOmC,SAAS,GAAGnC,OAAO,IAAIG,OAAOpI,MAAM;QACnE0I,OAAOQ,MAAM,CAACtB,QAAQK,OAAOA,OAAOmC,QAAQjC,SAASrD,KAAK,EAAEgF,OAAO;QACnEM,UAAU;IACZ;IAEA1B,OAAOkB,CAAC,GAAG9I,KAAKE,GAAG,CAACuI,OAAOa,UAAU;IAErC,iBAAiB;IACjB,IAAIjC,SAAS1E,OAAO,CAACC,MAAM,EAAE;QAC3B,MAAM6G,cAAczJ,KAAKE,GAAG,CAAC,KAAK0H,OAAOS,YAAY,GAAGhB,SAAS1E,OAAO,CAACC,MAAM;QAC/E,MAAMiE,MAAMe,OAAOkB,CAAC;QACpB,IAAIY,SAAS7C;QACbQ,SAAS1E,OAAO,CAACgH,OAAO,CAAC,CAACC,OAAOC;YAC/B,MAAMhB,IAAI7B,OAAO6C,QAASJ,CAAAA,cAAc,EAAC;YACzC7B,OAAOkB,CAAC,GAAGjC;YACXe,OAAOmB,SAAS,CAACa,MAAMpJ,KAAK,CAACsJ,WAAW,IAAI;gBAAEjB;gBAAG1B,OAAOsC;gBAAaT,OAAO;oBAAET,MAAM;oBAAQD,MAAM;oBAAGf,OAAOD,OAAO/H,KAAK;gBAAC;gBAAG2J,OAAO;YAAE;YACrIU,MAAM3G,KAAK,CAAC0G,OAAO,CAAC,CAAC3E,MAAM+E;gBACzBnC,OAAOmB,SAAS,CAAC/D,MAAM;oBACrB6D;oBACA1B,OAAOsC;oBACPT,OAAO;wBAAEV,MAAM;wBAAKf,OAAOD,OAAOhI,GAAG;wBAAEiJ,MAAMwB,cAAc,IAAI,SAAS;oBAAU;gBACpF;YACF;YACAL,SAAS1J,KAAKE,GAAG,CAACwJ,QAAQ9B,OAAOkB,CAAC;QACpC;QACAlB,OAAOkB,CAAC,GAAGY,SAAS;IACtB;IAEA,aAAa;IACb,MAAMM,OAAyB3C,SAASpE,KAAK,CAAC8B,GAAG,CAAC,CAACC,OAAS;YAC1D;gBACE;oBAAEvD,MAAMuD,KAAKC,IAAI;oBAAE+D,OAAO;wBAAET,MAAM;wBAAQD,MAAM;wBAAKf,OAAOD,OAAOhI,GAAG;oBAAC;gBAAE;mBACtE0F,KAAKE,OAAO,CAACH,GAAG,CAAC,CAACyB,SAAY,CAAA;wBAAE/E,MAAM+E;wBAAQwC,OAAO;4BAAEV,MAAM;4BAAKf,OAAOD,OAAO/H,KAAK;wBAAC;oBAAE,CAAA;aAC5F;YACD+E,OAAOU,KAAKM,QAAQ;YACpBN,KAAKO,IAAI;YACTP,KAAKU,MAAM;SACZ;IACDkC,OAAOqC,KAAK,CAAC;QACXC,SAAS;YACP;gBAAEC,QAAQ;gBAAQhD,OAAO;YAAE;YAC3B;gBAAEgD,QAAQ;gBAAOhD,OAAO;gBAAIiD,OAAO;YAAQ;YAC3C;gBAAED,QAAQ;gBAAchD,OAAO;gBAAIiD,OAAO;YAAQ;YAClD;gBAAED,QAAQ;gBAAUhD,OAAO;gBAAIiD,OAAO;YAAQ;SAC/C;QACDJ;QACAK,YAAY/C,OAAOpI,MAAM;QACzBoL,aAAa;YAAE/B,MAAM;YAAQD,MAAM;YAAKf,OAAOD,OAAOnI,QAAQ;QAAC;QAC/DoL,WAAW;YAAEjC,MAAM;YAAKf,OAAOD,OAAOhI,GAAG;QAAC;QAC1CkL,SAAS;YAAE3B,GAAG;YAAGC,GAAG;QAAE;QACtB2B,WAAWnD,OAAO9H,IAAI;IACxB;IAEA,+DAA+D;IAC/D,MAAMkL,eAAe;IACrB,MAAMC,WAAW7D,QAAQ4D;IACzB,MAAME,WAAWhD,OAAOS,YAAY,GAAGqC,eAAe;IACtD,MAAMG,YAAY;IAClB,MAAMC,gBAAgBzD,SAASlE,OAAO,CAAC4H,MAAM,CAAC,CAACC,KAAKtE,MAAQsE,MAAOtE,CAAAA,IAAI3C,QAAQ,GAAG8G,YAAY,IAAIA,SAAQ,GAAI;IAC9G,MAAMjF,MAAMyB,SAASzB,GAAG;IACxB,MAAMqF,cAAc;QAAE1C,MAAM;QAAiBD,MAAM;QAAMf,OAAOD,OAAOnI,QAAQ;IAAC;IAChF,MAAM+L,eAAe;IACrB,MAAMC,WAAWvF,MACb;QACEE,OAAO8B,OAAOwD,IAAI,CAACxF,IAAIE,KAAK,EAAE8E,UAAU;YAAEtC,MAAM;QAAE;QAClDzC,KAAK+B,OAAOwD,IAAI,CAACxF,IAAIC,GAAG,EAAE+E,UAAU;YAAEtC,MAAM;QAAE;IAChD,IACA;IACJ,MAAM+C,YAAYF,WAAW,KAAKA,SAASrF,KAAK,CAAClD,MAAM,GAAG,KAAK,IAAIsI,eAAe,IAAIC,SAAStF,GAAG,CAACjD,MAAM,GAAG,KAAK;IACjHgF,OAAO0D,IAAI,CAAC;IACZ1D,OAAO2D,MAAM,CAACvL,KAAKC,GAAG,CAAC6K,eAAeO;IACtC,MAAMG,WAAW5D,OAAOkB,CAAC;IACzB,KAAK,MAAMpC,OAAOW,SAASlE,OAAO,CAAE;QAClC,MAAMiE,SAASV,IAAI3C,QAAQ,GAAG8G,YAAY,IAAIA;QAC9C,IAAInE,IAAI3C,QAAQ,EAAE6D,OAAOI,QAAQ,CAAC2C,UAAU/C,OAAOkB,CAAC,GAAG1B,QAAQsD,cAActD,QAAQE,OAAOjI,IAAI;QAChG,MAAMiJ,OAAO5B,IAAI3C,QAAQ,GAAG,OAAO;QACnC,MAAMoE,WAAWP,OAAOkB,CAAC,GAAG1B,SAAS,IAAIkB,OAAO;QAChD,MAAMU,QAAQtC,IAAI3C,QAAQ,GACtB;YAAEwE,MAAM;YAAiBD;YAAMf,OAAOD,OAAOhI,GAAG;QAAC,IACjD;YAAEgJ;YAAMf,OAAOb,IAAI/C,IAAI,GAAG2D,OAAO/H,KAAK,GAAG+H,OAAOhI,GAAG;WAAMoH,IAAI/C,IAAI,GAAG;YAAE4E,MAAM;QAAkB,IAAI,CAAC;QACvGX,OAAOQ,MAAM,CAACuC,WAAW,GAAGD,eAAe,IAAIvC,UAAUzB,IAAIlG,KAAK,EAAEwI,OAAO;QAC3E,IAAItC,IAAItD,KAAK,EAAEwE,OAAOQ,MAAM,CAACuC,WAAW,GAAGD,eAAe,IAAIvC,UAAUzB,IAAItD,KAAK,EAAE4F,OAAO;QAC1FpB,OAAO0D,IAAI,CAAClE;IACd;IACA,MAAMqE,gBAAgB7D,OAAOkB,CAAC;IAC9B,IAAIlD,OAAOuF,UAAU;QACnB,IAAIrC,IAAI0C,WAAW;QACnB5D,OAAOnG,IAAI,CAACuF,MAAM8B,GAAG,cAAc;YAAEP,MAAM;YAAQD,MAAM;YAAIf,OAAOD,OAAOhI,GAAG;QAAC;QAC/EwJ,KAAK;QACL,KAAK,MAAM9D,QAAQmG,SAASrF,KAAK,CAAE;YACjCgD,KAAK;YACLlB,OAAOnG,IAAI,CAACuF,MAAM8B,IAAI,GAAG9D,MAAM;gBAAEsD,MAAM;gBAAGf,OAAOD,OAAO/H,KAAK;YAAC;QAChE;QACAuJ,KAAK;QACL,MAAM4C,cAAc9D,OAAO4B,OAAO,CAAC5D,IAAIpF,KAAK,EAAEyK,eAAe;QAC7DrD,OAAOI,QAAQ,CAAChB,MAAM8B,IAAIoC,cAAcQ,aAAaR,cAAc5D,OAAOpI,MAAM;QAChF0I,OAAOQ,MAAM,CAACpB,MAAM0E,aAAa5C,IAAIoC,eAAe,IAAI,OAAO,MAAMtF,IAAIpF,KAAK,EAAEyK,aAAa;QAC7FrD,OAAOlH,IAAI,CAACsG,MAAM8B,IAAIoC,cAAcQ,aAAaR,cAActF,IAAIC,GAAG;QACtEiD,KAAKoC,eAAe;QACpB,KAAK,MAAMlG,QAAQmG,SAAStF,GAAG,CAAE;YAC/BiD,KAAK;YACLlB,OAAOnG,IAAI,CAACuF,MAAM8B,IAAI,GAAG9D,MAAM;gBAAEsD,MAAM;gBAAGf,OAAOD,OAAOlI,UAAU;YAAC;YACnEwI,OAAOlH,IAAI,CAACsG,MAAM8B,GAAGlB,OAAO4B,OAAO,CAACxE,MAAM;gBAAEsD,MAAM;YAAE,IAAI,IAAI1C,IAAIC,GAAG;QACrE;QACA+B,OAAOkB,CAAC,GAAG9I,KAAKE,GAAG,CAACuL,eAAe3C;IACrC;IACAlB,OAAO0D,IAAI,CAAC;IAEZ,oCAAoC;IACpC,KAAK,MAAMK,WAAWtE,SAAS9C,QAAQ,CAAE;QACvC,IAAIoH,QAAQlH,OAAO,EAAE;YACnBmD,OAAO2D,MAAM,CAAC;YACd3D,OAAOmB,SAAS,CAAC4C,QAAQlH,OAAO,EAAE;gBAAEuE,OAAO;oBAAET,MAAM;oBAAQD,MAAM;oBAAIf,OAAOD,OAAOhI,GAAG;gBAAC;gBAAG4J,OAAO;YAAE;QACrG;QACAtB,OAAOmB,SAAS,CAAC4C,QAAQlK,IAAI,EAAE;YAC7BuH,OAAO;gBAAEV,MAAM;gBAAGf,OAAOoE,QAAQjL,IAAI,GAAG4G,OAAOlI,UAAU,GAAGkI,OAAO/H,KAAK;YAAC;WACrEoM,QAAQjL,IAAI,GAAG;YAAEA,MAAMiL,QAAQjL,IAAI;QAAC,IAAI,CAAC;YAC7CwI,OAAO;;IAEX;IAEA,OAAOtB,OAAOgE,MAAM,CAAC;QACnBvF,QAAQ,CAAC0B,MAAM8D,QAAW,CAAA;gBACxB7E,MAAMS,eAAe,GAAGA,aAAa,GAAG,EAAEJ,SAASxC,KAAK,EAAE,GAAGwC,SAASxC,KAAK;gBAC3EiC,OAAO,CAAC,KAAK,EAAEiB,KAAK,IAAI,EAAE8D,OAAO;YACnC,CAAA;QACAC,aAAa;YAAExD,MAAM;YAAGf,OAAOD,OAAO/H,KAAK;QAAC;QAC5CwM,YAAYzE,OAAO9H,IAAI;IACzB;AACF"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/utils/commerce-document-pdf.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 { buildEmailPalette, emailAccent, resolveEmailColor, type EmailPalette } from '@aglyn/shared-util-email'\nimport { relativeLuminanceOfRgb, rgbChannelsOfHex } from '@aglyn/shared-util-tools/contrast'\nimport type { PdfJpegImage } from '@aglyn/shared-util-tools/pdf/pdf-file'\nimport { PdfWriter, pdfRgbFromHex, type PdfRgb, type PdfTableCell } from '@aglyn/shared-util-tools/pdf/pdf-writer'\nimport type { QuotePageAddress, QuotePageView } from '../model/commerce-quote-pay'\nimport { buildOrderInvoiceData, invoiceAddressLines, invoiceMoney, type InvoiceOrder, type OrderInvoiceStore } from './order-invoice'\n\n/*==========================================\n * A QUOTE AND AN INVOICE AS BRANDED PDFs (AGL-4095).\n *\n * renderQuotePdf({ brand, view, revision, payUrl }) → Uint8Array\n * renderOrderInvoicePdf({ brand, order, orderId, store }) → Uint8Array\n *\n * Both are one layout: the business's logo, name and contact at the top left\n * under a band of the site's primary color, the document's name, number,\n * date and standing at the top right, who it is billed and shipped to, the\n * lines in a table whose header wears the primary color, the totals, then\n * notes, terms and (for a quote still to be paid) the pay link as a button\n * and as text, both clickable. Every page ends with the document's name and\n * its page number.\n *\n * Pure over its input: the server reads the site, its theme and its logo\n * (`server/commerce-pdf-brand.ts`) and the quote or order, and these turn\n * them into bytes, so the console's download, the customer's download and\n * the emails that attach a quote or an invoice all make the same file.\n *\n * A quote is drawn from `buildQuotePageView`, the customer-safe projection\n * the quote page shows, so the PDF never says more than the page does. An\n * invoice is drawn from `buildOrderInvoiceData`, the content the printed\n * invoice shows.\n *=========================================*/\n\n/** The colors a document is drawn in, as `#rrggbb`. */\nexport interface CommercePdfColors {\n /** The band, the table's header and the pay button: the site's primary color. */\n accent: string\n /** Text set on the accent. */\n onAccent: string\n /** The accent as text on white: the document's name, links. */\n accentText: string\n /** A wash of the accent, behind the total. */\n tint: string\n ink: string\n muted: string\n rule: string\n}\n\n/** Who the document is from. */\nexport interface CommercePdfBrand {\n businessName: string\n /** Address lines, email, phone and web address, in that order. */\n contactLines: readonly string[]\n /** The site's logo as a JPEG; the business name stands alone without one. */\n logo?: PdfJpegImage | null\n /** The site theme's colors; any left out are the neutral ones. */\n colors?: Partial<CommercePdfColors> | null\n /** The site's time zone, which dates are given in. */\n timeZone?: string | null\n}\n\nconst NEUTRAL_PALETTE = buildEmailPalette({ base: 'tenant' })\nconst neutral = (path: string) => resolveEmailColor(path, NEUTRAL_PALETTE) ?? ''\n\n/** The colors a document wears where its site's theme gives none: the default site theme's greys. */\nexport const COMMERCE_PDF_NEUTRAL_COLORS: Readonly<CommercePdfColors> = Object.freeze({\n accent: neutral('grey.800'),\n onAccent: neutral('background.paper'),\n accentText: neutral('grey.800'),\n tint: neutral('grey.100'),\n ink: neutral('text.primary'),\n muted: neutral('text.secondary'),\n rule: neutral('divider'),\n})\n\n/** WCAG contrast between two `#rrggbb` colors; 1 when either cannot be read. */\nfunction contrast(one: string, other: string): number {\n const a = rgbChannelsOfHex(one)\n const b = rgbChannelsOfHex(other)\n if (!a || !b) return 1\n const la = relativeLuminanceOfRgb(...a)\n const lb = relativeLuminanceOfRgb(...b)\n return (Math.max(la, lb) + 0.05) / (Math.min(la, lb) + 0.05)\n}\n\n/**\n * A document's colors from a site's resolved palette: the primary color for\n * the accent, the theme's own text and divider colors, and text on the\n * accent that reads at 4.5:1 (the theme's contrast text when it does, else\n * the theme's dark ink or white, whichever reads better).\n */\nexport function commercePdfColorsFromPalette(palette: EmailPalette): CommercePdfColors {\n const accent = emailAccent(palette)\n const read = (path: string, fallback: string) => resolveEmailColor(path, palette) ?? fallback\n const ink = read('text.primary', COMMERCE_PDF_NEUTRAL_COLORS.ink)\n const paper = read('background.paper', COMMERCE_PDF_NEUTRAL_COLORS.onAccent)\n const onAccent =\n contrast(accent.label, accent.fill) >= 4.5\n ? accent.label\n : contrast(ink, accent.fill) >= contrast(paper, accent.fill)\n ? ink\n : paper\n return {\n accent: accent.fill,\n onAccent,\n accentText: contrast(accent.link, paper) >= 4.5 ? accent.link : ink,\n tint: read('tint.primary', COMMERCE_PDF_NEUTRAL_COLORS.tint),\n ink,\n muted: read('text.secondary', COMMERCE_PDF_NEUTRAL_COLORS.muted),\n rule: read('divider', COMMERCE_PDF_NEUTRAL_COLORS.rule),\n }\n}\n\n/*==========================================\n * WHAT A DOCUMENT SAYS\n *=========================================*/\n\ninterface DocumentLine {\n name: string\n details: string[]\n quantity: number\n unit: string\n amount: string\n}\n\ninterface SummaryRow {\n label: string\n value: string\n emphasis?: boolean\n /** A note in place of a figure, such as \"Tax is added at payment\". */\n note?: boolean\n}\n\ninterface CommerceDocument {\n /** QUOTE or INVOICE, across the top. */\n kind: string\n /** `Quote #1042`. */\n title: string\n facts: Array<[string, string]>\n /** A short standing, set in a pill under the facts: `Paid October 30, 2026`. */\n stamp: string | null\n parties: Array<{ label: string; lines: string[] }>\n lines: DocumentLine[]\n summary: SummaryRow[]\n sections: Array<{ heading: string; text: string; link?: string }>\n pay: { url: string; label: string; intro: string } | null\n}\n\n/** A date the American way, in the site's time zone: `October 28, 2026`. */\nexport function formatPdfDate(ms: number | null | undefined, timeZone?: string | null): string {\n if (!ms || !Number.isFinite(ms)) return ''\n const options: Intl.DateTimeFormatOptions = { year: 'numeric', month: 'long', day: 'numeric' }\n try {\n return new Intl.DateTimeFormat('en-US', { ...options, ...(timeZone ? { timeZone } : {}) }).format(new Date(ms))\n } catch {\n return new Intl.DateTimeFormat('en-US', { ...options, timeZone: 'UTC' }).format(new Date(ms))\n }\n}\n\nconst minus = (text: string) => `-${text}`\n\n/*==========================================\n * THE QUOTE\n *=========================================*/\n\nexport interface QuotePdfInput {\n brand: CommercePdfBrand\n /** The quote as its customer may see it: `buildQuotePageView`. */\n view: QuotePageView\n /** The quote's revision, printed beside its date. */\n revision?: number | null\n /** The pay link, printed on a quote still to be paid; left off when the reader may not have it. */\n payUrl?: string | null\n}\n\nfunction quoteAddressLines(address: QuotePageAddress | null): string[] {\n return invoiceAddressLines(address as never, { phone: false })\n}\n\nfunction quoteDocument(input: QuotePdfInput): CommerceDocument {\n const { view, brand } = input\n const money = (cents: number) => invoiceMoney(cents, view.currency)\n const date = (ms: number | null) => formatPdfDate(ms, brand.timeZone)\n const customer = [view.customerName ?? '', view.company ?? ''].filter(Boolean)\n const billTo = quoteAddressLines(view.billTo)\n const parties: CommerceDocument['parties'] = []\n if (billTo.length) {\n // The bill-to's own name leads; the customer's name and company are added when it does not carry them.\n const named = customer.filter((part) => !billTo.includes(part))\n parties.push({ label: 'Bill to', lines: [...named, ...billTo] })\n } else if (customer.length) {\n parties.push({ label: 'Prepared for', lines: customer })\n }\n const shipTo = quoteAddressLines(view.shipTo)\n if (shipTo.length) parties.push({ label: 'Ship to', lines: shipTo })\n\n const summary: SummaryRow[] = [{ label: 'Subtotal', value: money(view.totals.itemsCents) }]\n if (view.totals.discountCents > 0) {\n summary.push({\n label: view.discountLabel ? `Discount (${view.discountLabel})` : 'Discount',\n value: minus(money(view.totals.discountCents)),\n })\n }\n if (view.totals.shippingCents > 0) summary.push({ label: 'Shipping', value: money(view.totals.shippingCents) })\n if (view.taxAtPayment) summary.push({ label: 'Tax is added at payment', value: '', note: true })\n else if (view.totals.taxCents > 0) summary.push({ label: view.taxLabel || 'Tax', value: money(view.totals.taxCents) })\n else if (view.taxLabel && view.taxLabel !== 'Tax') summary.push({ label: view.taxLabel, value: '', note: true })\n summary.push({ label: view.taxAtPayment ? 'Total before tax' : 'Total', value: money(view.totals.totalCents), emphasis: true })\n\n const stamp =\n view.status === 'paid'\n ? `Paid${view.paidAtMs ? ` ${date(view.paidAtMs)}` : ''}`\n : view.status === 'canceled'\n ? 'Canceled'\n : view.status === 'refunded'\n ? 'Refunded'\n : null\n const facts: Array<[string, string]> = []\n if (view.dateMs) facts.push(['Date', date(view.dateMs)])\n if (input.revision && input.revision > 0) facts.push(['Revision', String(input.revision)])\n\n const sections: CommerceDocument['sections'] = []\n if (view.notes) sections.push({ heading: 'Notes', text: view.notes })\n if (view.terms) sections.push({ heading: 'Terms', text: view.terms })\n\n const payUrl = view.status === 'unpaid' && input.payUrl ? input.payUrl : null\n return {\n kind: 'QUOTE',\n title: view.number,\n facts,\n stamp,\n parties,\n lines: view.lines.map((line) => ({\n name: line.name,\n details: [line.variantLabel ?? '', line.description ?? '', line.sku ? `SKU ${line.sku}` : ''].filter(Boolean),\n quantity: line.quantity,\n unit: line.included ? '' : money(line.unitAmountCents),\n amount: line.included ? 'Included' : money(line.amountCents),\n })),\n summary,\n sections,\n pay: payUrl\n ? {\n url: payUrl,\n label: 'Pay this quote',\n intro: view.approvalRequired\n ? `${view.storeName || 'We'} will ask you to approve the proof first, at this link:`\n : 'Open the quote on our site to pay it securely by card:',\n }\n : null,\n }\n}\n\n/** The quote as a PDF. */\nexport function renderQuotePdf(input: QuotePdfInput): Uint8Array {\n return renderCommerceDocument(input.brand, quoteDocument(input))\n}\n\n/*==========================================\n * THE INVOICE\n *=========================================*/\n\nexport interface OrderInvoicePdfInput {\n brand: CommercePdfBrand\n order: InvoiceOrder\n orderId: string\n /** The store's currency, receipt footer and terms address (`invoiceStoreFrom`). */\n store: OrderInvoiceStore\n /**\n * The order's pay link (AGL-4099), printed on an invoice still to be paid\n * as Pay online with a button and the address; left off when absent.\n */\n payUrl?: string | null\n}\n\nfunction invoiceDocument(input: OrderInvoicePdfInput): CommerceDocument {\n const data = buildOrderInvoiceData(input)\n const money = (cents: number) => invoiceMoney(cents, data.currency)\n const parties: CommerceDocument['parties'] = []\n if (data.billTo.length) parties.push({ label: 'Bill to', lines: data.billTo })\n if (data.shipTo.length) parties.push({ label: 'Ship to', lines: data.shipTo })\n const facts: Array<[string, string]> = []\n if (data.dateMs) facts.push(['Order date', formatPdfDate(data.dateMs, input.brand.timeZone)])\n const sections: CommerceDocument['sections'] = []\n if (data.footer) sections.push({ heading: '', text: data.footer })\n if (data.termsUrl) sections.push({ heading: 'Terms', text: data.termsUrl, link: data.termsUrl })\n const payUrl = input.order.status === 'pending' && input.payUrl ? input.payUrl : null\n return {\n kind: 'INVOICE',\n title: `Invoice ${data.number}`,\n facts,\n stamp: data.paymentStatus,\n parties,\n lines: data.lines.map((line) => ({\n name: line.name,\n details: [line.detail, line.description].filter(Boolean),\n quantity: line.quantity,\n unit: line.included ? '' : money(line.unitCents),\n amount: line.included ? 'Included' : money(line.amountCents),\n })),\n summary: data.summary.map((row) => ({\n label: row.label,\n value: row.minus ? minus(money(row.cents)) : money(row.cents),\n emphasis: row.emphasis,\n })),\n sections,\n pay: payUrl\n ? { url: payUrl, label: 'Pay this invoice', intro: 'Open the invoice on our site to pay it securely by card:' }\n : null,\n }\n}\n\n/** Any order's invoice as a PDF, a paid quote's included. */\nexport function renderOrderInvoicePdf(input: OrderInvoicePdfInput): Uint8Array {\n return renderCommerceDocument(input.brand, invoiceDocument(input))\n}\n\n/*==========================================\n * THE LAYOUT\n *=========================================*/\n\nconst PAGE_MARGIN = { top: 48, right: 48, bottom: 60, left: 48 }\nconst BAND_HEIGHT = 10\nconst LOGO_BOX = { width: 180, height: 50 }\n\nfunction renderCommerceDocument(brand: CommercePdfBrand, document: CommerceDocument): Uint8Array {\n const palette = { ...COMMERCE_PDF_NEUTRAL_COLORS, ...(brand.colors ?? {}) }\n const color = (hex: string, fallback: string): PdfRgb =>\n pdfRgbFromHex(hex) ?? pdfRgbFromHex(fallback) ?? [0, 0, 0]\n const colors = {\n accent: color(palette.accent, COMMERCE_PDF_NEUTRAL_COLORS.accent),\n onAccent: color(palette.onAccent, COMMERCE_PDF_NEUTRAL_COLORS.onAccent),\n accentText: color(palette.accentText, COMMERCE_PDF_NEUTRAL_COLORS.accentText),\n tint: color(palette.tint, COMMERCE_PDF_NEUTRAL_COLORS.tint),\n ink: color(palette.ink, COMMERCE_PDF_NEUTRAL_COLORS.ink),\n muted: color(palette.muted, COMMERCE_PDF_NEUTRAL_COLORS.muted),\n rule: color(palette.rule, COMMERCE_PDF_NEUTRAL_COLORS.rule),\n }\n const businessName = brand.businessName.trim()\n const documentTitle = businessName ? `${document.title} - ${businessName}` : document.title\n\n const writer: PdfWriter = new PdfWriter({\n title: documentTitle,\n margin: PAGE_MARGIN,\n onNewPage: (page) => {\n page.fillRect(0, page.pageHeight - BAND_HEIGHT / 2, page.pageWidth, BAND_HEIGHT / 2, colors.accent)\n const baseline = page.top + 14\n if (businessName) page.textIn(page.left, page.contentWidth, baseline, businessName, { size: 8.5, font: 'bold', color: colors.muted })\n page.textIn(page.left, page.contentWidth, baseline, `${document.title} (continued)`, { size: 8.5, color: colors.muted }, 'right')\n },\n })\n const { left, right } = writer\n\n // The band across the top of the first page.\n writer.fillRect(0, writer.pageHeight - BAND_HEIGHT, writer.pageWidth, BAND_HEIGHT, colors.accent)\n\n // Left: logo, business name, contact.\n const leftWidth = writer.contentWidth * 0.55\n let leftY = writer.top\n if (brand.logo) {\n const drawn = writer.image(brand.logo, { x: left, y: leftY - LOGO_BOX.height, width: LOGO_BOX.width, height: LOGO_BOX.height })\n leftY = drawn.y - 12\n }\n writer.y = leftY\n if (businessName) writer.paragraph(businessName, { width: leftWidth, style: { font: 'bold', size: 14, color: colors.ink }, leading: 1.25, after: 2 })\n for (const line of brand.contactLines) {\n writer.paragraph(line, { width: leftWidth, style: { size: 9, color: colors.muted }, leading: 1.3 })\n }\n leftY = writer.y\n\n // Right: the document's name, number, facts and standing.\n const factsWidth = 210\n const factsX = right - factsWidth\n let rightY = writer.top - 22\n writer.textIn(factsX, factsWidth, rightY, document.kind, { font: 'bold', size: 24, color: colors.accentText }, 'right')\n rightY -= 20\n writer.textIn(factsX, factsWidth, rightY, document.title, { font: 'bold', size: 11, color: colors.ink }, 'right')\n for (const [label, value] of document.facts) {\n rightY -= 14\n const valueWidth = writer.measure(value, { size: 9.5 })\n writer.textIn(factsX, factsWidth, rightY, value, { size: 9.5, color: colors.ink }, 'right')\n writer.textIn(factsX, factsWidth - valueWidth - 8, rightY, label, { size: 9.5, color: colors.muted }, 'right')\n }\n if (document.stamp) {\n rightY -= 24\n const style = { font: 'bold' as const, size: 8.5, color: colors.onAccent }\n const width = writer.measure(document.stamp, style) + 16\n writer.fillRect(right - width, rightY - 5, width, 17, colors.accent)\n writer.textIn(right - width, width, rightY, document.stamp, style, 'center')\n rightY -= 5\n }\n\n writer.y = Math.min(leftY, rightY) - 22\n\n // Who it is for.\n if (document.parties.length) {\n const columnWidth = Math.min(220, writer.contentWidth / document.parties.length)\n const top = writer.y\n let lowest = top\n document.parties.forEach((party, index) => {\n const x = left + index * (columnWidth + 16)\n writer.y = top\n writer.paragraph(party.label.toUpperCase(), { x, width: columnWidth, style: { font: 'bold', size: 8, color: colors.muted }, after: 2 })\n party.lines.forEach((line, lineIndex) => {\n writer.paragraph(line, {\n x,\n width: columnWidth,\n style: { size: 9.5, color: colors.ink, font: lineIndex === 0 ? 'bold' : 'regular' },\n })\n })\n lowest = Math.min(lowest, writer.y)\n })\n writer.y = lowest - 18\n }\n\n // The lines.\n const rows: PdfTableCell[][] = document.lines.map((line) => [\n [\n { text: line.name, style: { font: 'bold', size: 9.5, color: colors.ink } },\n ...line.details.map((detail) => ({ text: detail, style: { size: 8.5, color: colors.muted } })),\n ],\n String(line.quantity),\n line.unit,\n line.amount,\n ])\n writer.table({\n columns: [\n { header: 'Item', width: 0 },\n { header: 'Qty', width: 48, align: 'right' },\n { header: 'Unit price', width: 86, align: 'right' },\n { header: 'Amount', width: 90, align: 'right' },\n ],\n rows,\n headerFill: colors.accent,\n headerStyle: { font: 'bold', size: 8.5, color: colors.onAccent },\n bodyStyle: { size: 9.5, color: colors.ink },\n padding: { x: 8, y: 6 },\n ruleColor: colors.rule,\n })\n\n // The totals on the right; beside them on the left, paying it.\n const summaryWidth = 250\n const summaryX = right - summaryWidth\n const payWidth = writer.contentWidth - summaryWidth - 28\n const rowHeight = 17\n const summaryHeight = document.summary.reduce((sum, row) => sum + (row.emphasis ? rowHeight + 8 : rowHeight), 0)\n const pay = document.pay\n const buttonStyle = { font: 'bold' as const, size: 10.5, color: colors.onAccent }\n const buttonHeight = 26\n const payParts = pay\n ? {\n intro: writer.wrap(pay.intro, payWidth, { size: 9 }),\n url: writer.wrap(pay.url, payWidth, { size: 8 }),\n }\n : null\n const payHeight = payParts ? 18 + payParts.intro.length * 12 + 6 + buttonHeight + 8 + payParts.url.length * 11 : 0\n writer.down(12)\n writer.ensure(Math.max(summaryHeight, payHeight))\n const blockTop = writer.y\n for (const row of document.summary) {\n const height = row.emphasis ? rowHeight + 8 : rowHeight\n if (row.emphasis) writer.fillRect(summaryX, writer.y - height, summaryWidth, height, colors.tint)\n const size = row.emphasis ? 11.5 : 9.5\n const baseline = writer.y - height / 2 - size * 0.35\n const style = row.emphasis\n ? { font: 'bold' as const, size, color: colors.ink }\n : { size, color: row.note ? colors.muted : colors.ink, ...(row.note ? { font: 'italic' as const } : {}) }\n writer.textIn(summaryX + 8, summaryWidth - 16, baseline, row.label, style, 'left')\n if (row.value) writer.textIn(summaryX + 8, summaryWidth - 16, baseline, row.value, style, 'right')\n writer.down(height)\n }\n const summaryBottom = writer.y\n if (pay && payParts) {\n let y = blockTop - 12\n writer.text(left, y, 'Pay online', { font: 'bold', size: 11, color: colors.ink })\n y -= 6\n for (const line of payParts.intro) {\n y -= 12\n writer.text(left, y + 2, line, { size: 9, color: colors.muted })\n }\n y -= 6\n const buttonWidth = writer.measure(pay.label, buttonStyle) + 36\n writer.fillRect(left, y - buttonHeight, buttonWidth, buttonHeight, colors.accent)\n writer.textIn(left, buttonWidth, y - buttonHeight / 2 - 10.5 * 0.35, pay.label, buttonStyle, 'center')\n writer.link(left, y - buttonHeight, buttonWidth, buttonHeight, pay.url)\n y -= buttonHeight + 8\n for (const line of payParts.url) {\n y -= 11\n writer.text(left, y + 2, line, { size: 8, color: colors.accentText })\n writer.link(left, y, writer.measure(line, { size: 8 }), 11, pay.url)\n }\n writer.y = Math.min(summaryBottom, y)\n }\n writer.down(22)\n\n // Notes, terms, the store's footer.\n for (const section of document.sections) {\n if (section.heading) {\n writer.ensure(36)\n writer.paragraph(section.heading, { style: { font: 'bold', size: 10, color: colors.ink }, after: 1 })\n }\n writer.paragraph(section.text, {\n style: { size: 9, color: section.link ? colors.accentText : colors.muted },\n ...(section.link ? { link: section.link } : {}),\n after: 12,\n })\n }\n\n return writer.finish({\n footer: (page, pages) => ({\n left: businessName ? `${businessName} · ${document.title}` : document.title,\n right: `Page ${page} of ${pages}`,\n }),\n footerStyle: { size: 8, color: colors.muted },\n footerRule: colors.rule,\n })\n}\n"],"names":["buildEmailPalette","emailAccent","resolveEmailColor","relativeLuminanceOfRgb","rgbChannelsOfHex","PdfWriter","pdfRgbFromHex","buildOrderInvoiceData","invoiceAddressLines","invoiceMoney","NEUTRAL_PALETTE","base","neutral","path","COMMERCE_PDF_NEUTRAL_COLORS","Object","freeze","accent","onAccent","accentText","tint","ink","muted","rule","contrast","one","other","a","b","la","lb","Math","max","min","commercePdfColorsFromPalette","palette","read","fallback","paper","label","fill","link","formatPdfDate","ms","timeZone","Number","isFinite","options","year","month","day","Intl","DateTimeFormat","format","Date","minus","text","quoteAddressLines","address","phone","quoteDocument","input","view","brand","money","cents","currency","date","customer","customerName","company","filter","Boolean","billTo","parties","length","named","part","includes","push","lines","shipTo","summary","value","totals","itemsCents","discountCents","discountLabel","shippingCents","taxAtPayment","note","taxCents","taxLabel","totalCents","emphasis","stamp","status","paidAtMs","facts","dateMs","revision","String","sections","notes","heading","terms","payUrl","kind","title","number","map","line","name","details","variantLabel","description","sku","quantity","unit","included","unitAmountCents","amount","amountCents","pay","url","intro","approvalRequired","storeName","renderQuotePdf","renderCommerceDocument","invoiceDocument","data","footer","termsUrl","order","paymentStatus","detail","unitCents","row","renderOrderInvoicePdf","PAGE_MARGIN","top","right","bottom","left","BAND_HEIGHT","LOGO_BOX","width","height","document","colors","color","hex","businessName","trim","documentTitle","writer","margin","onNewPage","page","fillRect","pageHeight","pageWidth","baseline","textIn","contentWidth","size","font","leftWidth","leftY","logo","drawn","image","x","y","paragraph","style","leading","after","contactLines","factsWidth","factsX","rightY","valueWidth","measure","columnWidth","lowest","forEach","party","index","toUpperCase","lineIndex","rows","table","columns","header","align","headerFill","headerStyle","bodyStyle","padding","ruleColor","summaryWidth","summaryX","payWidth","rowHeight","summaryHeight","reduce","sum","buttonStyle","buttonHeight","payParts","wrap","payHeight","down","ensure","blockTop","summaryBottom","buttonWidth","section","finish","pages","footerStyle","footerRule"],"mappings":"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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
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 '../model';
|
|
18
|
+
/** The catalog key the invoice email is sent and designed under. */
|
|
19
|
+
export declare const INVOICE_EMAIL_KEY = "order-invoice";
|
|
20
|
+
export interface InvoiceEmailContentInput {
|
|
21
|
+
order: HostOrder;
|
|
22
|
+
orderId: string;
|
|
23
|
+
businessName: string;
|
|
24
|
+
currency: string;
|
|
25
|
+
/** The subject the email goes under. */
|
|
26
|
+
subject: string;
|
|
27
|
+
/** The business's message, as typed; may be empty. */
|
|
28
|
+
message: string;
|
|
29
|
+
/** The order's pay link, for an invoice still to be paid; null for a paid one. */
|
|
30
|
+
payUrl: string | null;
|
|
31
|
+
/** The site's time zone, for the dates. */
|
|
32
|
+
timeZone?: string | null;
|
|
33
|
+
}
|
|
34
|
+
export interface InvoiceEmailContent {
|
|
35
|
+
/** The catalog email's merge tokens. */
|
|
36
|
+
tokens: Record<string, string>;
|
|
37
|
+
/** The same email as plain text, for a send whose design cannot be rendered. */
|
|
38
|
+
text: string;
|
|
39
|
+
/** The invoice says it is paid: no button. */
|
|
40
|
+
paid: boolean;
|
|
41
|
+
/** The total it shows. */
|
|
42
|
+
totalCents: number;
|
|
43
|
+
}
|
|
44
|
+
/** The invoice email's tokens and its plain-text twin. */
|
|
45
|
+
export declare function invoiceEmailContent(input: InvoiceEmailContentInput): InvoiceEmailContent;
|
|
@@ -0,0 +1,126 @@
|
|
|
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 { formatOrderMoney, formatOrderNumber, invoiceCheckoutCharge, invoiceIsPaid, orderCreatedAtMs, orderPaidAtMs } from "../model/index.js";
|
|
18
|
+
/*==========================================
|
|
19
|
+
* WHAT THE INVOICE EMAIL SAYS (AGL-4099).
|
|
20
|
+
*
|
|
21
|
+
* The `order-invoice` catalog email's tokens, composed per send, curated for
|
|
22
|
+
* the two things an invoice can be:
|
|
23
|
+
*
|
|
24
|
+
* - UNPAID: "Invoice #1042 from Acme Signs", what it is for, the business's
|
|
25
|
+
* own message, the number, date, total and what is due, how to pay, and
|
|
26
|
+
* one button, "Pay $1,250.00", to the order's lasting pay link on the
|
|
27
|
+
* business's site.
|
|
28
|
+
* - PAID: the same heading and details with the date it was paid, "This
|
|
29
|
+
* invoice is paid; keep it for your records", and no button.
|
|
30
|
+
*
|
|
31
|
+
* The invoice itself is attached as a PDF by the sender. The figures are the
|
|
32
|
+
* ones the pay link charges (`invoiceCheckoutCharge`) while it is unpaid, and
|
|
33
|
+
* the order's own once paid, so the email, the page and the PDF agree.
|
|
34
|
+
*
|
|
35
|
+
* Pure: the send route and the sample renders call the same function.
|
|
36
|
+
*=========================================*/ /** The catalog key the invoice email is sent and designed under. */ export const INVOICE_EMAIL_KEY = 'order-invoice';
|
|
37
|
+
/** A date the American way, in the site's time zone: `October 28, 2026`. */ function longDate(ms, timeZone) {
|
|
38
|
+
if (!ms || !Number.isFinite(ms)) return '';
|
|
39
|
+
const options = {
|
|
40
|
+
year: 'numeric',
|
|
41
|
+
month: 'long',
|
|
42
|
+
day: 'numeric'
|
|
43
|
+
};
|
|
44
|
+
try {
|
|
45
|
+
return new Intl.DateTimeFormat('en-US', _extends({}, options, timeZone ? {
|
|
46
|
+
timeZone
|
|
47
|
+
} : {})).format(new Date(ms));
|
|
48
|
+
} catch (unused) {
|
|
49
|
+
return new Intl.DateTimeFormat('en-US', _extends({}, options, {
|
|
50
|
+
timeZone: 'UTC'
|
|
51
|
+
})).format(new Date(ms));
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
function capitalize(value) {
|
|
55
|
+
return value ? value[0].toUpperCase() + value.slice(1) : value;
|
|
56
|
+
}
|
|
57
|
+
/** `before tax`, `before shipping`, `before shipping and tax`, or nothing. */ function laterParts(shipping, tax) {
|
|
58
|
+
if (shipping && tax) return 'shipping and tax';
|
|
59
|
+
if (shipping) return 'shipping';
|
|
60
|
+
if (tax) return 'tax';
|
|
61
|
+
return '';
|
|
62
|
+
}
|
|
63
|
+
/** The invoice email's tokens and its plain-text twin. */ export function invoiceEmailContent(input) {
|
|
64
|
+
var _input_payUrl, _ref, _ref1, _order_refundedCents;
|
|
65
|
+
var _order_totals;
|
|
66
|
+
const { order, orderId, timeZone } = input;
|
|
67
|
+
const name = input.businessName.trim();
|
|
68
|
+
const business = name || 'the business';
|
|
69
|
+
const number = formatOrderNumber(order, orderId);
|
|
70
|
+
const paid = invoiceIsPaid(order);
|
|
71
|
+
const payUrl = paid ? '' : String((_input_payUrl = input.payUrl) != null ? _input_payUrl : '').trim();
|
|
72
|
+
const forWhat = order.quote ? 'your quote' : 'your order';
|
|
73
|
+
const quoteSection = order.quote;
|
|
74
|
+
const dateMs = orderCreatedAtMs(order) || Number((_ref = quoteSection == null ? void 0 : quoteSection.updatedAtMs) != null ? _ref : quoteSection == null ? void 0 : quoteSection.createdAtMs) || 0;
|
|
75
|
+
const date = longDate(dateMs, timeZone);
|
|
76
|
+
// What it shows: what the link charges while it is unpaid, the order's own figures once paid.
|
|
77
|
+
const charge = paid ? null : invoiceCheckoutCharge(order, orderId);
|
|
78
|
+
const totalCents = charge ? charge.totalCents : Math.max(0, Math.round(Number((_ref1 = (_order_totals = order.totals) == null ? void 0 : _order_totals.totalCents) != null ? _ref1 : 0)));
|
|
79
|
+
const money = formatOrderMoney(totalCents, input.currency);
|
|
80
|
+
const later = charge ? laterParts(charge.shippingOptions.length > 0, charge.taxAtPayment) : '';
|
|
81
|
+
const total = later ? `${money} before ${later}` : money;
|
|
82
|
+
const refundedCents = Math.max(0, Math.round(Number((_order_refundedCents = order.refundedCents) != null ? _order_refundedCents : 0)));
|
|
83
|
+
const paidOn = longDate(orderPaidAtMs(order), timeZone);
|
|
84
|
+
const status = paid ? `${paidOn ? `Paid on ${paidOn}` : 'Paid'}` + (refundedCents > 0 ? `, ${formatOrderMoney(refundedCents, input.currency)} refunded` : '') : `Unpaid, ${money}${later ? ` plus ${later}` : ''} due`;
|
|
85
|
+
const heading = name ? `Invoice ${number} from ${name}` : `Invoice ${number}`;
|
|
86
|
+
const intro = paid ? `Here is the invoice for ${forWhat} with ${business}` + (paidOn ? `, paid on ${paidOn}` : ', which is paid') + '. It is attached as a PDF for your records.' : `${capitalize(business)} sent you an invoice for ${forWhat}. The details are below, and the invoice is attached as a PDF.`;
|
|
87
|
+
const nextStep = paid ? 'This invoice is paid; keep it for your records.' : payUrl ? 'Use the button below to pay it securely by card.' + (later ? ` ${capitalize(later)} ${later === 'shipping and tax' ? 'are' : 'is'} added when you pay.` : '') + ' The button keeps working until the invoice is paid.' : `Reply to this email to arrange payment with ${business}.`;
|
|
88
|
+
const buttonLabel = payUrl ? `Pay ${money}${later ? ` plus ${later}` : ''}` : '';
|
|
89
|
+
const message = input.message.trim();
|
|
90
|
+
const details = [
|
|
91
|
+
`Invoice: ${number}`,
|
|
92
|
+
date ? `Date: ${date}` : '',
|
|
93
|
+
`Total: ${total}`,
|
|
94
|
+
`Status: ${status}`
|
|
95
|
+
].filter(Boolean).join('\n');
|
|
96
|
+
const tokens = {
|
|
97
|
+
'invoice.subject': input.subject,
|
|
98
|
+
'invoice.number': number,
|
|
99
|
+
'invoice.date': date,
|
|
100
|
+
'invoice.total': total,
|
|
101
|
+
'invoice.status': status,
|
|
102
|
+
'invoice.heading': heading,
|
|
103
|
+
'invoice.intro': intro,
|
|
104
|
+
'invoice.message': message,
|
|
105
|
+
'invoice.nextStep': nextStep,
|
|
106
|
+
'invoice.buttonLabel': buttonLabel,
|
|
107
|
+
'invoice.url': payUrl
|
|
108
|
+
};
|
|
109
|
+
const text = [
|
|
110
|
+
heading,
|
|
111
|
+
intro,
|
|
112
|
+
message,
|
|
113
|
+
details,
|
|
114
|
+
nextStep,
|
|
115
|
+
buttonLabel ? `${buttonLabel}: ${payUrl}` : '',
|
|
116
|
+
`Reply to this email to reach ${business}.`
|
|
117
|
+
].filter(Boolean).join('\n\n');
|
|
118
|
+
return {
|
|
119
|
+
tokens,
|
|
120
|
+
text,
|
|
121
|
+
paid,
|
|
122
|
+
totalCents
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
//# sourceMappingURL=invoice-email-content.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/utils/invoice-email-content.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 {\n formatOrderMoney,\n formatOrderNumber,\n invoiceCheckoutCharge,\n invoiceIsPaid,\n orderCreatedAtMs,\n orderPaidAtMs,\n type HostOrder,\n} from '../model'\n\n/*==========================================\n * WHAT THE INVOICE EMAIL SAYS (AGL-4099).\n *\n * The `order-invoice` catalog email's tokens, composed per send, curated for\n * the two things an invoice can be:\n *\n * - UNPAID: \"Invoice #1042 from Acme Signs\", what it is for, the business's\n * own message, the number, date, total and what is due, how to pay, and\n * one button, \"Pay $1,250.00\", to the order's lasting pay link on the\n * business's site.\n * - PAID: the same heading and details with the date it was paid, \"This\n * invoice is paid; keep it for your records\", and no button.\n *\n * The invoice itself is attached as a PDF by the sender. The figures are the\n * ones the pay link charges (`invoiceCheckoutCharge`) while it is unpaid, and\n * the order's own once paid, so the email, the page and the PDF agree.\n *\n * Pure: the send route and the sample renders call the same function.\n *=========================================*/\n\n/** The catalog key the invoice email is sent and designed under. */\nexport const INVOICE_EMAIL_KEY = 'order-invoice'\n\nexport interface InvoiceEmailContentInput {\n order: HostOrder\n orderId: string\n businessName: string\n currency: string\n /** The subject the email goes under. */\n subject: string\n /** The business's message, as typed; may be empty. */\n message: string\n /** The order's pay link, for an invoice still to be paid; null for a paid one. */\n payUrl: string | null\n /** The site's time zone, for the dates. */\n timeZone?: string | null\n}\n\nexport interface InvoiceEmailContent {\n /** The catalog email's merge tokens. */\n tokens: Record<string, string>\n /** The same email as plain text, for a send whose design cannot be rendered. */\n text: string\n /** The invoice says it is paid: no button. */\n paid: boolean\n /** The total it shows. */\n totalCents: number\n}\n\n/** A date the American way, in the site's time zone: `October 28, 2026`. */\nfunction longDate(ms: number | null | undefined, timeZone?: string | null): string {\n if (!ms || !Number.isFinite(ms)) return ''\n const options: Intl.DateTimeFormatOptions = { year: 'numeric', month: 'long', day: 'numeric' }\n try {\n return new Intl.DateTimeFormat('en-US', { ...options, ...(timeZone ? { timeZone } : {}) }).format(new Date(ms))\n } catch {\n return new Intl.DateTimeFormat('en-US', { ...options, timeZone: 'UTC' }).format(new Date(ms))\n }\n}\n\nfunction capitalize(value: string): string {\n return value ? value[0].toUpperCase() + value.slice(1) : value\n}\n\n/** `before tax`, `before shipping`, `before shipping and tax`, or nothing. */\nfunction laterParts(shipping: boolean, tax: boolean): string {\n if (shipping && tax) return 'shipping and tax'\n if (shipping) return 'shipping'\n if (tax) return 'tax'\n return ''\n}\n\n/** The invoice email's tokens and its plain-text twin. */\nexport function invoiceEmailContent(input: InvoiceEmailContentInput): InvoiceEmailContent {\n const { order, orderId, timeZone } = input\n const name = input.businessName.trim()\n const business = name || 'the business'\n const number = formatOrderNumber(order, orderId)\n const paid = invoiceIsPaid(order)\n const payUrl = paid ? '' : String(input.payUrl ?? '').trim()\n const forWhat = order.quote ? 'your quote' : 'your order'\n const quoteSection = order.quote\n const dateMs =\n orderCreatedAtMs(order as never) || Number(quoteSection?.updatedAtMs ?? quoteSection?.createdAtMs) || 0\n const date = longDate(dateMs, timeZone)\n\n // What it shows: what the link charges while it is unpaid, the order's own figures once paid.\n const charge = paid ? null : invoiceCheckoutCharge(order, orderId)\n const totalCents = charge ? charge.totalCents : Math.max(0, Math.round(Number(order.totals?.totalCents ?? 0)))\n const money = formatOrderMoney(totalCents, input.currency)\n const later = charge ? laterParts(charge.shippingOptions.length > 0, charge.taxAtPayment) : ''\n const total = later ? `${money} before ${later}` : money\n const refundedCents = Math.max(0, Math.round(Number(order.refundedCents ?? 0)))\n const paidOn = longDate(orderPaidAtMs(order), timeZone)\n const status = paid\n ? `${paidOn ? `Paid on ${paidOn}` : 'Paid'}` +\n (refundedCents > 0 ? `, ${formatOrderMoney(refundedCents, input.currency)} refunded` : '')\n : `Unpaid, ${money}${later ? ` plus ${later}` : ''} due`\n\n const heading = name ? `Invoice ${number} from ${name}` : `Invoice ${number}`\n const intro = paid\n ? `Here is the invoice for ${forWhat} with ${business}` +\n (paidOn ? `, paid on ${paidOn}` : ', which is paid') +\n '. It is attached as a PDF for your records.'\n : `${capitalize(business)} sent you an invoice for ${forWhat}. The details are below, and the invoice is attached as a PDF.`\n const nextStep = paid\n ? 'This invoice is paid; keep it for your records.'\n : payUrl\n ? 'Use the button below to pay it securely by card.' +\n (later ? ` ${capitalize(later)} ${later === 'shipping and tax' ? 'are' : 'is'} added when you pay.` : '') +\n ' The button keeps working until the invoice is paid.'\n : `Reply to this email to arrange payment with ${business}.`\n const buttonLabel = payUrl ? `Pay ${money}${later ? ` plus ${later}` : ''}` : ''\n const message = input.message.trim()\n const details = [`Invoice: ${number}`, date ? `Date: ${date}` : '', `Total: ${total}`, `Status: ${status}`]\n .filter(Boolean)\n .join('\\n')\n\n const tokens: Record<string, string> = {\n 'invoice.subject': input.subject,\n 'invoice.number': number,\n 'invoice.date': date,\n 'invoice.total': total,\n 'invoice.status': status,\n 'invoice.heading': heading,\n 'invoice.intro': intro,\n 'invoice.message': message,\n 'invoice.nextStep': nextStep,\n 'invoice.buttonLabel': buttonLabel,\n 'invoice.url': payUrl,\n }\n\n const text = [\n heading,\n intro,\n message,\n details,\n nextStep,\n buttonLabel ? `${buttonLabel}: ${payUrl}` : '',\n `Reply to this email to reach ${business}.`,\n ]\n .filter(Boolean)\n .join('\\n\\n')\n\n return { tokens, text, paid, totalCents }\n}\n"],"names":["formatOrderMoney","formatOrderNumber","invoiceCheckoutCharge","invoiceIsPaid","orderCreatedAtMs","orderPaidAtMs","INVOICE_EMAIL_KEY","longDate","ms","timeZone","Number","isFinite","options","year","month","day","Intl","DateTimeFormat","format","Date","capitalize","value","toUpperCase","slice","laterParts","shipping","tax","invoiceEmailContent","input","order","orderId","name","businessName","trim","business","number","paid","payUrl","String","forWhat","quote","quoteSection","dateMs","updatedAtMs","createdAtMs","date","charge","totalCents","Math","max","round","totals","money","currency","later","shippingOptions","length","taxAtPayment","total","refundedCents","paidOn","status","heading","intro","nextStep","buttonLabel","message","details","filter","Boolean","join","tokens","subject","text"],"mappings":"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"}
|
|
@@ -42,7 +42,7 @@ export interface OrderInvoiceStore {
|
|
|
42
42
|
* the order dialog's Invoice button and `commerce/order-print` read them.
|
|
43
43
|
*/
|
|
44
44
|
export declare function invoiceStoreFrom(host: Record<string, unknown> | undefined | null, store: Record<string, unknown> | undefined | null): OrderInvoiceStore;
|
|
45
|
-
export type InvoiceOrder = Pick<HostOrder, 'number' | 'status' | 'lineItems' | 'totals' | 'refundedCents' | 'customerName' | 'customerEmail' | 'shippingAddress' | 'billingAddress'> & {
|
|
45
|
+
export type InvoiceOrder = Pick<HostOrder, 'number' | 'status' | 'lineItems' | 'totals' | 'refundedCents' | 'customerName' | 'customerEmail' | 'shippingAddress' | 'billingAddress'> & Partial<Pick<HostOrder, 'quote'>> & {
|
|
46
46
|
createdAtMs?: number;
|
|
47
47
|
currency?: string;
|
|
48
48
|
amountCents?: number;
|
|
@@ -78,7 +78,8 @@ const addressLines = (address)=>invoiceAddressLines(address);
|
|
|
78
78
|
return 'Paid';
|
|
79
79
|
}
|
|
80
80
|
/** What an invoice says, from the order and its store. */ export function buildOrderInvoiceData(input) {
|
|
81
|
-
var _ref, _order_lineItems, _ref1, _ref2, _order_refundedCents, _order_lineItems1, _ref3, _ref4, _ref5, _ref6, _order_customerName, _order_customerEmail, _store_footer, _store_termsUrl;
|
|
81
|
+
var _ref, _order_lineItems, _ref1, _ref2, _order_refundedCents, _order_lineItems1, _ref3, _ref4, _ref5, _ref6, _ref7, _order_billingAddress, _ref8, _ref9, _order_customerName, _ref10, _order_customerEmail, _ref11, _ref12, _order_shippingAddress, _store_footer, _store_termsUrl;
|
|
82
|
+
var _quote_customer, _quote_customer1, _quote_customer2;
|
|
82
83
|
const { order, orderId, store } = input;
|
|
83
84
|
const currency = String(order.currency || store.currency || 'USD').toUpperCase();
|
|
84
85
|
const totals = order.totals;
|
|
@@ -142,18 +143,25 @@ const addressLines = (address)=>invoiceAddressLines(address);
|
|
|
142
143
|
emphasis: true
|
|
143
144
|
});
|
|
144
145
|
}
|
|
146
|
+
// An unpaid quote keeps its customer, its addresses and its date in its
|
|
147
|
+
// quote section until it is paid (AGL-4093); its invoice (AGL-4099) reads
|
|
148
|
+
// them there.
|
|
149
|
+
const quote = order.quote;
|
|
150
|
+
const billingAddress = (_ref7 = (_order_billingAddress = order.billingAddress) != null ? _order_billingAddress : quote == null ? void 0 : quote.billTo) != null ? _ref7 : null;
|
|
151
|
+
const customerName = (_ref8 = (_ref9 = (_order_customerName = order.customerName) != null ? _order_customerName : quote == null ? void 0 : (_quote_customer = quote.customer) == null ? void 0 : _quote_customer.name) != null ? _ref9 : quote == null ? void 0 : (_quote_customer1 = quote.customer) == null ? void 0 : _quote_customer1.company) != null ? _ref8 : '';
|
|
152
|
+
const customerEmail = (_ref10 = (_order_customerEmail = order.customerEmail) != null ? _order_customerEmail : quote == null ? void 0 : (_quote_customer2 = quote.customer) == null ? void 0 : _quote_customer2.email) != null ? _ref10 : '';
|
|
145
153
|
return {
|
|
146
154
|
number: formatOrderNumber(order, orderId),
|
|
147
|
-
dateMs: orderCreatedAtMs(order),
|
|
155
|
+
dateMs: orderCreatedAtMs(order) || Number((_ref11 = quote == null ? void 0 : quote.updatedAtMs) != null ? _ref11 : quote == null ? void 0 : quote.createdAtMs) || 0,
|
|
148
156
|
currency,
|
|
149
157
|
paymentStatus: invoicePaymentStatus(order),
|
|
150
158
|
billTo: [
|
|
151
|
-
...
|
|
152
|
-
|
|
159
|
+
...billingAddress ? addressLines(billingAddress) : [
|
|
160
|
+
customerName
|
|
153
161
|
].filter(Boolean),
|
|
154
|
-
|
|
162
|
+
customerEmail
|
|
155
163
|
].filter(Boolean),
|
|
156
|
-
shipTo: addressLines(order.shippingAddress),
|
|
164
|
+
shipTo: addressLines((_ref12 = (_order_shippingAddress = order.shippingAddress) != null ? _order_shippingAddress : quote == null ? void 0 : quote.shipTo) != null ? _ref12 : null),
|
|
157
165
|
lines,
|
|
158
166
|
summary,
|
|
159
167
|
footer: String((_store_footer = store.footer) != null ? _store_footer : '').trim(),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/utils/order-invoice.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 {\n formatOrderNumber,\n orderCreatedAtMs,\n type HostOrder,\n type OrderAddress,\n} from '../model/commerce-orders'\nimport { escapeHtml } from './escape-html'\n\n/**\n * The printable invoice (AGL-3611), beside the packing slip: the order as a\n * bill — who sold it, to whom, each line with its unit price, the totals,\n * what was refunded and what was paid in the end.\n *\n * A string of markup, because it is written into a print popup the way the\n * packing slip is, and the browser's print dialog saves it as a PDF. Every\n * value is escaped: the addresses are typed by the shopper, and the popup\n * inherits the console's origin (AGL-2283). No `<style>` and no `style`\n * attributes, because the popup inherits the console's content security\n * policy; the layout is the table attributes HTML still honors.\n */\n\nexport interface OrderInvoiceStore {\n /** The business's name, as its emails sign. */\n name: string\n /** The store's currency, for an order that does not carry its own. */\n currency: string\n /** The receipt footer the store set, printed at the foot. */\n footer?: string | null\n /** The store's terms address, printed at the foot. */\n termsUrl?: string | null\n}\n\n/**\n * The invoice's store from the site's document and its store settings, as\n * the order dialog's Invoice button and `commerce/order-print` read them.\n */\nexport function invoiceStoreFrom(\n host: Record<string, unknown> | undefined | null,\n store: Record<string, unknown> | undefined | null,\n): OrderInvoiceStore {\n const hostData = host ?? {}\n const storeData = store ?? {}\n return {\n name: String(hostData['businessName'] ?? hostData['displayName'] ?? hostData['name'] ?? ''),\n currency: String(storeData['currency'] ?? 'USD'),\n footer: (storeData['receiptFooter'] as string | undefined) ?? null,\n termsUrl: (storeData['termsUrl'] as string | undefined) ?? null,\n }\n}\n\nexport type InvoiceOrder = Pick<\n HostOrder,\n | 'number'\n | 'status'\n | 'lineItems'\n | 'totals'\n | 'refundedCents'\n | 'customerName'\n | 'customerEmail'\n | 'shippingAddress'\n | 'billingAddress'\n> & { createdAtMs?: number; currency?: string; amountCents?: number }\n\n/** Money in the order's currency, integer cents in. */\nexport function invoiceMoney(cents: number, currency: string): string {\n const amount = (Number(cents) || 0) / 100\n try {\n return new Intl.NumberFormat('en-US', { style: 'currency', currency: currency || 'USD' }).format(amount)\n } catch {\n return `${amount.toFixed(2)} ${String(currency || '').toUpperCase()}`.trim()\n }\n}\n\n/** The United States, however an address names it: left off, as a US envelope does. */\nconst US_COUNTRY = /^(us|usa|u\\.s\\.a?\\.?|united states( of america)?)$/i\n\n/**\n * An address as lines, the American way: name, street, `City, ST 12345`,\n * the country unless it is the United States, then the phone when the\n * address carries one and it is wanted.\n */\nexport function invoiceAddressLines(\n address: Partial<OrderAddress> | undefined | null,\n options: { phone?: boolean } = {},\n): string[] {\n if (!address) return []\n const clean = (part: unknown) => String(part ?? '').trim()\n const region = [clean(address.state), clean(address.postalCode)].filter(Boolean).join(' ')\n return [\n clean(address.name),\n clean(address.line1),\n clean(address.line2),\n [clean(address.city), region].filter(Boolean).join(', '),\n US_COUNTRY.test(clean(address.country)) ? '' : clean(address.country),\n options.phone === false ? '' : clean(address.phone),\n ].filter(Boolean)\n}\n\nconst addressLines = (address: OrderAddress | undefined | null) => invoiceAddressLines(address)\n\n/** What the payment line says. */\nexport function invoicePaymentStatus(order: Pick<InvoiceOrder, 'status' | 'refundedCents' | 'totals' | 'amountCents'>): string {\n const total = Number(order.totals?.totalCents ?? order.amountCents ?? 0)\n const refunded = Math.max(0, Number(order.refundedCents ?? 0))\n const status = String(order.status ?? '')\n if (status === 'pending' || status === 'draft') return 'Awaiting payment'\n if (status === 'cancelled') return 'Canceled'\n if (refunded > 0 && refunded >= total) return 'Refunded'\n if (refunded > 0) return 'Paid, partially refunded'\n return 'Paid'\n}\n\n/** One line of an invoice, as both the printed page and the PDF set it. */\nexport interface InvoiceLine {\n name: string\n /** The option chosen and the SKU, as one line under the name. */\n detail: string\n /** A typed line's own description. */\n description: string\n quantity: number\n unitCents: number\n amountCents: number\n /** Included at no charge. */\n included: boolean\n}\n\n/** One row of an invoice's summary: a figure, or a deduction printed with a minus. */\nexport interface InvoiceSummaryRow {\n label: string\n cents: number\n minus?: boolean\n /** The row the summary leads to: the total, or what was paid after refunds. */\n emphasis?: boolean\n}\n\n/** An invoice's content, worked out once for the printed page and the PDF. */\nexport interface OrderInvoiceData {\n /** `#1042`. */\n number: string\n /** When the order was placed; 0 when it does not say. */\n dateMs: number\n currency: string\n paymentStatus: string\n billTo: string[]\n shipTo: string[]\n lines: InvoiceLine[]\n summary: InvoiceSummaryRow[]\n footer: string\n termsUrl: string\n}\n\n/** What an invoice says, from the order and its store. */\nexport function buildOrderInvoiceData(input: {\n order: InvoiceOrder\n orderId: string\n store: OrderInvoiceStore\n}): OrderInvoiceData {\n const { order, orderId, store } = input\n const currency = String(order.currency || store.currency || 'USD').toUpperCase()\n const totals = order.totals\n const itemsCents =\n totals?.itemsCents ??\n (order.lineItems ?? []).reduce((sum, line) => sum + Number(line.unitAmountCents || 0) * Number(line.quantity || 0), 0)\n const totalCents = Number(totals?.totalCents ?? order.amountCents ?? itemsCents)\n const refundedCents = Math.max(0, Number(order.refundedCents ?? 0))\n\n const lines = (order.lineItems ?? []).map((line): InvoiceLine => {\n const quantity = Number(line.quantity) || 0\n const unit = Number(line.unitAmountCents) || 0\n return {\n name: String(line.name ?? ''),\n detail: [line.variantLabel ? String(line.variantLabel) : '', line.sku ? `SKU ${line.sku}` : ''].filter(Boolean).join(' · '),\n description: String(line.description ?? '').trim(),\n quantity,\n unitCents: unit,\n amountCents: unit * quantity,\n included: line.included === true,\n }\n })\n\n const summary: InvoiceSummaryRow[] = [{ label: 'Subtotal', cents: itemsCents }]\n if (Number(totals?.discountCents ?? 0) > 0) summary.push({ label: 'Discount', cents: Number(totals?.discountCents), minus: true })\n if (Number(totals?.shippingCents ?? 0) > 0) summary.push({ label: 'Shipping', cents: Number(totals?.shippingCents) })\n if (Number(totals?.taxCents ?? 0) > 0) summary.push({ label: 'Tax', cents: Number(totals?.taxCents) })\n summary.push({ label: 'Total', cents: totalCents, emphasis: true })\n if (Number(totals?.tipCents ?? 0) > 0) summary.push({ label: 'Tip', cents: Number(totals?.tipCents) })\n if (refundedCents > 0) {\n summary.push({ label: 'Refunded', cents: refundedCents, minus: true })\n summary.push({ label: 'Net paid', cents: Math.max(0, totalCents - refundedCents), emphasis: true })\n }\n\n return {\n number: formatOrderNumber(order, orderId),\n dateMs: orderCreatedAtMs(order as never),\n currency,\n paymentStatus: invoicePaymentStatus(order),\n billTo: [\n ...(order.billingAddress ? addressLines(order.billingAddress) : [order.customerName ?? ''].filter(Boolean)),\n order.customerEmail ?? '',\n ].filter(Boolean),\n shipTo: addressLines(order.shippingAddress),\n lines,\n summary,\n footer: String(store.footer ?? '').trim(),\n termsUrl: String(store.termsUrl ?? '').trim(),\n }\n}\n\n/** The invoice's markup: a full document body, every value escaped. */\nexport function buildOrderInvoiceHtml(input: {\n order: InvoiceOrder\n orderId: string\n store: OrderInvoiceStore\n}): string {\n const data = buildOrderInvoiceData(input)\n const { store } = input\n const money = (cents: number) => escapeHtml(invoiceMoney(cents, data.currency))\n const number = data.number\n\n const rows = data.lines\n .map((line) => {\n const detail = [line.detail, line.description].filter(Boolean).map((part) => escapeHtml(part)).join('<br/>')\n return (\n `<tr><td>${escapeHtml(line.name)}${detail ? `<br/><small>${detail}</small>` : ''}</td>` +\n `<td align=\"right\">${escapeHtml(line.quantity)}</td>` +\n `<td align=\"right\">${line.included ? '' : money(line.unitCents)}</td>` +\n `<td align=\"right\">${line.included ? 'Included' : money(line.amountCents)}</td></tr>`\n )\n })\n .join('')\n\n const summary = data.summary.map(({ label, cents, minus, emphasis }): [string, string] => {\n const figure = `${minus ? '−' : ''}${money(cents)}`\n return emphasis ? [`<b>${label}</b>`, `<b>${figure}</b>`] : [label, figure]\n })\n\n const block = (title: string, lines: string[]) =>\n lines.length ? `<td valign=\"top\"><b>${title}</b><br/>${lines.map((line) => escapeHtml(line)).join('<br/>')}</td>` : ''\n\n return (\n `<title>${escapeHtml(`Invoice ${number}`)}</title>` +\n `<h2>${escapeHtml(store.name || 'Invoice')}</h2>` +\n `<h3>${escapeHtml(`Invoice ${number}`)}</h3>` +\n `<p>${data.dateMs ? `Date: ${escapeHtml(new Date(data.dateMs).toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' }))}<br/>` : ''}` +\n `Payment: ${escapeHtml(data.paymentStatus)}<br/>Currency: ${escapeHtml(data.currency)}</p>` +\n `<table width=\"100%\" cellpadding=\"6\"><tr>${block('Bill to', data.billTo)}${block('Ship to', data.shipTo)}</tr></table>` +\n `<table width=\"100%\" cellpadding=\"6\" border=\"1\" cellspacing=\"0\">` +\n `<tr><th align=\"left\">Item</th><th align=\"right\">Qty</th><th align=\"right\">Unit price</th><th align=\"right\">Amount</th></tr>` +\n rows +\n `</table>` +\n `<table width=\"100%\" cellpadding=\"4\">${summary\n .map(([label, value]) => `<tr><td align=\"right\" width=\"80%\">${label}</td><td align=\"right\">${value}</td></tr>`)\n .join('')}</table>` +\n (store.footer ? `<p>${escapeHtml(store.footer)}</p>` : '') +\n (store.termsUrl ? `<p><small>Terms: ${escapeHtml(store.termsUrl)}</small></p>` : '')\n )\n}\n"],"names":["formatOrderNumber","orderCreatedAtMs","escapeHtml","invoiceStoreFrom","host","store","hostData","storeData","name","String","currency","footer","termsUrl","invoiceMoney","cents","amount","Number","Intl","NumberFormat","style","format","toFixed","toUpperCase","trim","US_COUNTRY","invoiceAddressLines","address","options","clean","part","region","state","postalCode","filter","Boolean","join","line1","line2","city","test","country","phone","addressLines","invoicePaymentStatus","order","total","totals","totalCents","amountCents","refunded","Math","max","refundedCents","status","buildOrderInvoiceData","input","orderId","itemsCents","lineItems","reduce","sum","line","unitAmountCents","quantity","lines","map","unit","detail","variantLabel","sku","description","unitCents","included","summary","label","discountCents","push","minus","shippingCents","taxCents","emphasis","tipCents","number","dateMs","paymentStatus","billTo","billingAddress","customerName","customerEmail","shipTo","shippingAddress","buildOrderInvoiceHtml","data","money","rows","figure","block","title","length","Date","toLocaleDateString","year","month","day","value"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/utils/order-invoice.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 {\n formatOrderNumber,\n orderCreatedAtMs,\n type HostOrder,\n type OrderAddress,\n} from '../model/commerce-orders'\nimport { escapeHtml } from './escape-html'\n\n/**\n * The printable invoice (AGL-3611), beside the packing slip: the order as a\n * bill — who sold it, to whom, each line with its unit price, the totals,\n * what was refunded and what was paid in the end.\n *\n * A string of markup, because it is written into a print popup the way the\n * packing slip is, and the browser's print dialog saves it as a PDF. Every\n * value is escaped: the addresses are typed by the shopper, and the popup\n * inherits the console's origin (AGL-2283). No `<style>` and no `style`\n * attributes, because the popup inherits the console's content security\n * policy; the layout is the table attributes HTML still honors.\n */\n\nexport interface OrderInvoiceStore {\n /** The business's name, as its emails sign. */\n name: string\n /** The store's currency, for an order that does not carry its own. */\n currency: string\n /** The receipt footer the store set, printed at the foot. */\n footer?: string | null\n /** The store's terms address, printed at the foot. */\n termsUrl?: string | null\n}\n\n/**\n * The invoice's store from the site's document and its store settings, as\n * the order dialog's Invoice button and `commerce/order-print` read them.\n */\nexport function invoiceStoreFrom(\n host: Record<string, unknown> | undefined | null,\n store: Record<string, unknown> | undefined | null,\n): OrderInvoiceStore {\n const hostData = host ?? {}\n const storeData = store ?? {}\n return {\n name: String(hostData['businessName'] ?? hostData['displayName'] ?? hostData['name'] ?? ''),\n currency: String(storeData['currency'] ?? 'USD'),\n footer: (storeData['receiptFooter'] as string | undefined) ?? null,\n termsUrl: (storeData['termsUrl'] as string | undefined) ?? null,\n }\n}\n\nexport type InvoiceOrder = Pick<\n HostOrder,\n | 'number'\n | 'status'\n | 'lineItems'\n | 'totals'\n | 'refundedCents'\n | 'customerName'\n | 'customerEmail'\n | 'shippingAddress'\n | 'billingAddress'\n> &\n Partial<Pick<HostOrder, 'quote'>> & { createdAtMs?: number; currency?: string; amountCents?: number }\n\n/** Money in the order's currency, integer cents in. */\nexport function invoiceMoney(cents: number, currency: string): string {\n const amount = (Number(cents) || 0) / 100\n try {\n return new Intl.NumberFormat('en-US', { style: 'currency', currency: currency || 'USD' }).format(amount)\n } catch {\n return `${amount.toFixed(2)} ${String(currency || '').toUpperCase()}`.trim()\n }\n}\n\n/** The United States, however an address names it: left off, as a US envelope does. */\nconst US_COUNTRY = /^(us|usa|u\\.s\\.a?\\.?|united states( of america)?)$/i\n\n/**\n * An address as lines, the American way: name, street, `City, ST 12345`,\n * the country unless it is the United States, then the phone when the\n * address carries one and it is wanted.\n */\nexport function invoiceAddressLines(\n address: Partial<OrderAddress> | undefined | null,\n options: { phone?: boolean } = {},\n): string[] {\n if (!address) return []\n const clean = (part: unknown) => String(part ?? '').trim()\n const region = [clean(address.state), clean(address.postalCode)].filter(Boolean).join(' ')\n return [\n clean(address.name),\n clean(address.line1),\n clean(address.line2),\n [clean(address.city), region].filter(Boolean).join(', '),\n US_COUNTRY.test(clean(address.country)) ? '' : clean(address.country),\n options.phone === false ? '' : clean(address.phone),\n ].filter(Boolean)\n}\n\nconst addressLines = (address: OrderAddress | undefined | null) => invoiceAddressLines(address)\n\n/** What the payment line says. */\nexport function invoicePaymentStatus(order: Pick<InvoiceOrder, 'status' | 'refundedCents' | 'totals' | 'amountCents'>): string {\n const total = Number(order.totals?.totalCents ?? order.amountCents ?? 0)\n const refunded = Math.max(0, Number(order.refundedCents ?? 0))\n const status = String(order.status ?? '')\n if (status === 'pending' || status === 'draft') return 'Awaiting payment'\n if (status === 'cancelled') return 'Canceled'\n if (refunded > 0 && refunded >= total) return 'Refunded'\n if (refunded > 0) return 'Paid, partially refunded'\n return 'Paid'\n}\n\n/** One line of an invoice, as both the printed page and the PDF set it. */\nexport interface InvoiceLine {\n name: string\n /** The option chosen and the SKU, as one line under the name. */\n detail: string\n /** A typed line's own description. */\n description: string\n quantity: number\n unitCents: number\n amountCents: number\n /** Included at no charge. */\n included: boolean\n}\n\n/** One row of an invoice's summary: a figure, or a deduction printed with a minus. */\nexport interface InvoiceSummaryRow {\n label: string\n cents: number\n minus?: boolean\n /** The row the summary leads to: the total, or what was paid after refunds. */\n emphasis?: boolean\n}\n\n/** An invoice's content, worked out once for the printed page and the PDF. */\nexport interface OrderInvoiceData {\n /** `#1042`. */\n number: string\n /** When the order was placed; 0 when it does not say. */\n dateMs: number\n currency: string\n paymentStatus: string\n billTo: string[]\n shipTo: string[]\n lines: InvoiceLine[]\n summary: InvoiceSummaryRow[]\n footer: string\n termsUrl: string\n}\n\n/** What an invoice says, from the order and its store. */\nexport function buildOrderInvoiceData(input: {\n order: InvoiceOrder\n orderId: string\n store: OrderInvoiceStore\n}): OrderInvoiceData {\n const { order, orderId, store } = input\n const currency = String(order.currency || store.currency || 'USD').toUpperCase()\n const totals = order.totals\n const itemsCents =\n totals?.itemsCents ??\n (order.lineItems ?? []).reduce((sum, line) => sum + Number(line.unitAmountCents || 0) * Number(line.quantity || 0), 0)\n const totalCents = Number(totals?.totalCents ?? order.amountCents ?? itemsCents)\n const refundedCents = Math.max(0, Number(order.refundedCents ?? 0))\n\n const lines = (order.lineItems ?? []).map((line): InvoiceLine => {\n const quantity = Number(line.quantity) || 0\n const unit = Number(line.unitAmountCents) || 0\n return {\n name: String(line.name ?? ''),\n detail: [line.variantLabel ? String(line.variantLabel) : '', line.sku ? `SKU ${line.sku}` : ''].filter(Boolean).join(' · '),\n description: String(line.description ?? '').trim(),\n quantity,\n unitCents: unit,\n amountCents: unit * quantity,\n included: line.included === true,\n }\n })\n\n const summary: InvoiceSummaryRow[] = [{ label: 'Subtotal', cents: itemsCents }]\n if (Number(totals?.discountCents ?? 0) > 0) summary.push({ label: 'Discount', cents: Number(totals?.discountCents), minus: true })\n if (Number(totals?.shippingCents ?? 0) > 0) summary.push({ label: 'Shipping', cents: Number(totals?.shippingCents) })\n if (Number(totals?.taxCents ?? 0) > 0) summary.push({ label: 'Tax', cents: Number(totals?.taxCents) })\n summary.push({ label: 'Total', cents: totalCents, emphasis: true })\n if (Number(totals?.tipCents ?? 0) > 0) summary.push({ label: 'Tip', cents: Number(totals?.tipCents) })\n if (refundedCents > 0) {\n summary.push({ label: 'Refunded', cents: refundedCents, minus: true })\n summary.push({ label: 'Net paid', cents: Math.max(0, totalCents - refundedCents), emphasis: true })\n }\n\n // An unpaid quote keeps its customer, its addresses and its date in its\n // quote section until it is paid (AGL-4093); its invoice (AGL-4099) reads\n // them there.\n const quote = order.quote\n const billingAddress = order.billingAddress ?? quote?.billTo ?? null\n const customerName = order.customerName ?? quote?.customer?.name ?? quote?.customer?.company ?? ''\n const customerEmail = order.customerEmail ?? quote?.customer?.email ?? ''\n return {\n number: formatOrderNumber(order, orderId),\n dateMs: orderCreatedAtMs(order as never) || Number(quote?.updatedAtMs ?? quote?.createdAtMs) || 0,\n currency,\n paymentStatus: invoicePaymentStatus(order),\n billTo: [\n ...(billingAddress ? addressLines(billingAddress) : [customerName].filter(Boolean)),\n customerEmail,\n ].filter(Boolean),\n shipTo: addressLines(order.shippingAddress ?? quote?.shipTo ?? null),\n lines,\n summary,\n footer: String(store.footer ?? '').trim(),\n termsUrl: String(store.termsUrl ?? '').trim(),\n }\n}\n\n/** The invoice's markup: a full document body, every value escaped. */\nexport function buildOrderInvoiceHtml(input: {\n order: InvoiceOrder\n orderId: string\n store: OrderInvoiceStore\n}): string {\n const data = buildOrderInvoiceData(input)\n const { store } = input\n const money = (cents: number) => escapeHtml(invoiceMoney(cents, data.currency))\n const number = data.number\n\n const rows = data.lines\n .map((line) => {\n const detail = [line.detail, line.description].filter(Boolean).map((part) => escapeHtml(part)).join('<br/>')\n return (\n `<tr><td>${escapeHtml(line.name)}${detail ? `<br/><small>${detail}</small>` : ''}</td>` +\n `<td align=\"right\">${escapeHtml(line.quantity)}</td>` +\n `<td align=\"right\">${line.included ? '' : money(line.unitCents)}</td>` +\n `<td align=\"right\">${line.included ? 'Included' : money(line.amountCents)}</td></tr>`\n )\n })\n .join('')\n\n const summary = data.summary.map(({ label, cents, minus, emphasis }): [string, string] => {\n const figure = `${minus ? '−' : ''}${money(cents)}`\n return emphasis ? [`<b>${label}</b>`, `<b>${figure}</b>`] : [label, figure]\n })\n\n const block = (title: string, lines: string[]) =>\n lines.length ? `<td valign=\"top\"><b>${title}</b><br/>${lines.map((line) => escapeHtml(line)).join('<br/>')}</td>` : ''\n\n return (\n `<title>${escapeHtml(`Invoice ${number}`)}</title>` +\n `<h2>${escapeHtml(store.name || 'Invoice')}</h2>` +\n `<h3>${escapeHtml(`Invoice ${number}`)}</h3>` +\n `<p>${data.dateMs ? `Date: ${escapeHtml(new Date(data.dateMs).toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' }))}<br/>` : ''}` +\n `Payment: ${escapeHtml(data.paymentStatus)}<br/>Currency: ${escapeHtml(data.currency)}</p>` +\n `<table width=\"100%\" cellpadding=\"6\"><tr>${block('Bill to', data.billTo)}${block('Ship to', data.shipTo)}</tr></table>` +\n `<table width=\"100%\" cellpadding=\"6\" border=\"1\" cellspacing=\"0\">` +\n `<tr><th align=\"left\">Item</th><th align=\"right\">Qty</th><th align=\"right\">Unit price</th><th align=\"right\">Amount</th></tr>` +\n rows +\n `</table>` +\n `<table width=\"100%\" cellpadding=\"4\">${summary\n .map(([label, value]) => `<tr><td align=\"right\" width=\"80%\">${label}</td><td align=\"right\">${value}</td></tr>`)\n .join('')}</table>` +\n (store.footer ? `<p>${escapeHtml(store.footer)}</p>` : '') +\n (store.termsUrl ? `<p><small>Terms: ${escapeHtml(store.termsUrl)}</small></p>` : '')\n )\n}\n"],"names":["formatOrderNumber","orderCreatedAtMs","escapeHtml","invoiceStoreFrom","host","store","hostData","storeData","name","String","currency","footer","termsUrl","invoiceMoney","cents","amount","Number","Intl","NumberFormat","style","format","toFixed","toUpperCase","trim","US_COUNTRY","invoiceAddressLines","address","options","clean","part","region","state","postalCode","filter","Boolean","join","line1","line2","city","test","country","phone","addressLines","invoicePaymentStatus","order","total","totals","totalCents","amountCents","refunded","Math","max","refundedCents","status","buildOrderInvoiceData","input","quote","orderId","itemsCents","lineItems","reduce","sum","line","unitAmountCents","quantity","lines","map","unit","detail","variantLabel","sku","description","unitCents","included","summary","label","discountCents","push","minus","shippingCents","taxCents","emphasis","tipCents","billingAddress","billTo","customerName","customer","company","customerEmail","email","number","dateMs","updatedAtMs","createdAtMs","paymentStatus","shipTo","shippingAddress","buildOrderInvoiceHtml","data","money","rows","figure","block","title","length","Date","toLocaleDateString","year","month","day","value"],"mappings":"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"}
|