@sixtyninecommerce/admin 0.10.0 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5772 -118
- package/dist/auth/index.d.ts +7 -1
- package/dist/auth/index.js +8 -1
- package/dist/catalog/assets.d.ts +120 -8
- package/dist/catalog/assets.js +98 -12
- package/dist/catalog/collections.d.ts +279 -14
- package/dist/catalog/collections.js +246 -15
- package/dist/catalog/documents.d.ts +142 -0
- package/dist/catalog/documents.js +114 -0
- package/dist/catalog/index.d.ts +54 -6
- package/dist/catalog/index.js +10 -1
- package/dist/catalog/metadata.d.ts +244 -0
- package/dist/catalog/metadata.js +220 -0
- package/dist/catalog/products.d.ts +159 -19
- package/dist/catalog/products.js +88 -13
- package/dist/catalog/properties.d.ts +140 -14
- package/dist/catalog/properties.js +65 -1
- package/dist/catalog/recommendations.d.ts +288 -0
- package/dist/catalog/recommendations.js +237 -0
- package/dist/catalog/reviews.d.ts +381 -0
- package/dist/catalog/reviews.js +325 -0
- package/dist/catalog/types.d.ts +17 -1
- package/dist/catalog/types.js +10 -0
- package/dist/catalog/variant-find.d.ts +136 -0
- package/dist/catalog/variant-find.js +239 -0
- package/dist/catalog/variants.d.ts +293 -26
- package/dist/catalog/variants.js +164 -7
- package/dist/catalog/visibility.d.ts +38 -7
- package/dist/catalog/visibility.js +19 -1
- package/dist/client.d.ts +121 -9
- package/dist/client.js +53 -14
- package/dist/content/assets.d.ts +285 -0
- package/dist/content/assets.js +181 -0
- package/dist/content/forms.d.ts +332 -1
- package/dist/content/forms.js +140 -4
- package/dist/content/items.d.ts +292 -0
- package/dist/content/items.js +170 -0
- package/dist/content/menus.d.ts +254 -0
- package/dist/content/menus.js +70 -0
- package/dist/content/strings.d.ts +137 -0
- package/dist/content/strings.js +82 -0
- package/dist/content/types.d.ts +282 -0
- package/dist/content/types.js +100 -0
- package/dist/customers/age-verification.d.ts +66 -0
- package/dist/customers/age-verification.js +79 -0
- package/dist/customers/credit.d.ts +263 -0
- package/dist/customers/credit.js +244 -0
- package/dist/customers/delivery-preferences.d.ts +120 -0
- package/dist/customers/delivery-preferences.js +97 -0
- package/dist/customers/delivery-terms.d.ts +44 -0
- package/dist/customers/delivery-terms.js +32 -0
- package/dist/customers/groups.d.ts +207 -0
- package/dist/customers/groups.js +438 -0
- package/dist/customers/index.d.ts +462 -37
- package/dist/customers/index.js +317 -40
- package/dist/customers/invitation-status.d.ts +57 -0
- package/dist/customers/invitation-status.js +21 -0
- package/dist/customers/notes.d.ts +67 -0
- package/dist/customers/notes.js +51 -0
- package/dist/customers/notification-recipients.d.ts +97 -0
- package/dist/customers/notification-recipients.js +84 -0
- package/dist/customers/saved-lists.d.ts +42 -0
- package/dist/customers/saved-lists.js +61 -0
- package/dist/customers/vat.d.ts +83 -0
- package/dist/customers/vat.js +46 -0
- package/dist/customers/wire.d.ts +20 -0
- package/dist/customers/wire.js +35 -0
- package/dist/documents/ar.d.ts +307 -0
- package/dist/documents/ar.js +254 -0
- package/dist/documents/emails.d.ts +150 -0
- package/dist/documents/emails.js +220 -0
- package/dist/documents/index.d.ts +744 -21
- package/dist/documents/index.js +438 -24
- package/dist/documents/pdf.d.ts +2 -0
- package/dist/documents/pdf.js +11 -0
- package/dist/fulfillments/create.d.ts +167 -0
- package/dist/fulfillments/create.js +216 -0
- package/dist/fulfillments/index.d.ts +163 -11
- package/dist/fulfillments/index.js +135 -6
- package/dist/fulfillments/payment-policy.d.ts +64 -0
- package/dist/fulfillments/payment-policy.js +140 -0
- package/dist/fulfillments/warehouse.d.ts +724 -0
- package/dist/fulfillments/warehouse.js +914 -0
- package/dist/geo/index.d.ts +119 -0
- package/dist/geo/index.js +107 -0
- package/dist/index.d.ts +147 -39
- package/dist/index.js +51 -12
- package/dist/integrations/afas-actions.d.ts +234 -0
- package/dist/integrations/afas-actions.js +310 -0
- package/dist/integrations/afas-types.d.ts +483 -0
- package/dist/integrations/afas-types.js +5 -0
- package/dist/integrations/afas.d.ts +234 -0
- package/dist/integrations/afas.js +290 -0
- package/dist/integrations/connections.d.ts +103 -0
- package/dist/integrations/connections.js +55 -0
- package/dist/integrations/esp.d.ts +313 -0
- package/dist/integrations/esp.js +169 -0
- package/dist/integrations/exact-online-types.d.ts +965 -0
- package/dist/integrations/exact-online-types.js +6 -0
- package/dist/integrations/exact-online.d.ts +784 -0
- package/dist/integrations/exact-online.js +1089 -0
- package/dist/integrations/index.d.ts +44 -3
- package/dist/integrations/index.js +23 -3
- package/dist/integrations/pipeline-discoveries.d.ts +65 -0
- package/dist/integrations/pipeline-discoveries.js +65 -0
- package/dist/integrations/pipeline-keys.d.ts +13 -0
- package/dist/integrations/pipeline-keys.js +22 -0
- package/dist/integrations/pipeline-runs.d.ts +119 -0
- package/dist/integrations/pipeline-runs.js +87 -0
- package/dist/integrations/pipelines.d.ts +87 -15
- package/dist/integrations/pipelines.js +41 -22
- package/dist/integrations/price-import.d.ts +223 -0
- package/dist/integrations/price-import.js +124 -0
- package/dist/integrations/types.d.ts +367 -3
- package/dist/integrations/types.js +4 -3
- package/dist/locations/index.d.ts +66 -0
- package/dist/locations/index.js +31 -0
- package/dist/meta/index.d.ts +172 -0
- package/dist/metafields/index.d.ts +8 -1
- package/dist/metafields/index.js +10 -0
- package/dist/orders/cancel-with-money.d.ts +237 -0
- package/dist/orders/cancel-with-money.js +155 -0
- package/dist/orders/commit-edit.d.ts +23 -1
- package/dist/orders/commit-edit.js +19 -2
- package/dist/orders/deposit-policy.d.ts +3 -1
- package/dist/orders/drafts.d.ts +800 -0
- package/dist/orders/drafts.js +771 -0
- package/dist/orders/fields.d.ts +245 -0
- package/dist/orders/fields.js +188 -0
- package/dist/orders/get.d.ts +29 -19
- package/dist/orders/get.js +17 -8
- package/dist/orders/index.d.ts +66 -15
- package/dist/orders/index.js +25 -2
- package/dist/orders/lifecycle.d.ts +128 -0
- package/dist/orders/lifecycle.js +158 -0
- package/dist/orders/line-price-preview.d.ts +7 -2
- package/dist/orders/list.d.ts +243 -0
- package/dist/orders/list.js +487 -0
- package/dist/orders/manual-discounts.d.ts +13 -5
- package/dist/orders/manual-pricing.d.ts +5 -0
- package/dist/orders/money-view.d.ts +149 -0
- package/dist/orders/money-view.js +99 -0
- package/dist/orders/order-entry.d.ts +20 -7
- package/dist/orders/plan-edit.d.ts +34 -2
- package/dist/orders/plan-edit.js +112 -11
- package/dist/orders/settings.d.ts +201 -0
- package/dist/orders/settings.js +325 -0
- package/dist/orders/status.d.ts +159 -13
- package/dist/orders/status.js +21 -0
- package/dist/orders/surcharges.d.ts +20 -1
- package/dist/orders/surcharges.js +37 -11
- package/dist/payments/account-credit.d.ts +46 -0
- package/dist/payments/account-credit.js +42 -0
- package/dist/payments/ar.d.ts +292 -0
- package/dist/payments/ar.js +223 -0
- package/dist/payments/connected-account.d.ts +47 -0
- package/dist/payments/connected-account.js +61 -0
- package/dist/payments/credit-noted-fields.d.ts +19 -0
- package/dist/payments/credit-noted-fields.js +69 -0
- package/dist/payments/customer-consent.d.ts +30 -0
- package/dist/payments/customer-consent.js +49 -0
- package/dist/payments/index.d.ts +340 -19
- package/dist/payments/index.js +151 -1
- package/dist/payments/methods.d.ts +303 -8
- package/dist/payments/methods.js +337 -7
- package/dist/payments/mollie-methods.d.ts +159 -0
- package/dist/payments/mollie-methods.js +141 -0
- package/dist/payments/payment-links.d.ts +180 -0
- package/dist/payments/payment-links.js +97 -0
- package/dist/payments/provider-keys.d.ts +150 -0
- package/dist/payments/provider-keys.js +118 -0
- package/dist/payments/providers.d.ts +86 -11
- package/dist/payments/providers.js +29 -0
- package/dist/payments/requests.d.ts +147 -0
- package/dist/payments/requests.js +93 -0
- package/dist/payments/store-credit-fields.d.ts +15 -0
- package/dist/payments/store-credit-fields.js +51 -0
- package/dist/pickups/index.d.ts +106 -0
- package/dist/pickups/index.js +108 -0
- package/dist/platform/channels.d.ts +132 -0
- package/dist/platform/channels.js +128 -0
- package/dist/platform/email-gateways.d.ts +165 -0
- package/dist/platform/email-gateways.js +78 -0
- package/dist/platform/geo.d.ts +123 -0
- package/dist/platform/geo.js +144 -0
- package/dist/platform/tax.d.ts +162 -0
- package/dist/platform/tax.js +169 -0
- package/dist/pos/anomalies.d.ts +158 -0
- package/dist/pos/anomalies.js +64 -0
- package/dist/pos/assisted-selling.d.ts +79 -0
- package/dist/pos/assisted-selling.js +38 -0
- package/dist/pos/exchanges.d.ts +108 -0
- package/dist/pos/exchanges.js +22 -0
- package/dist/pos/gift-cards.d.ts +135 -0
- package/dist/pos/gift-cards.js +47 -0
- package/dist/pos/index.d.ts +2370 -58
- package/dist/pos/index.js +854 -7
- package/dist/pos/no-receipt.d.ts +94 -0
- package/dist/pos/no-receipt.js +25 -0
- package/dist/pos/offline-sales.d.ts +228 -0
- package/dist/pos/offline-sales.js +170 -0
- package/dist/pos/pay-by-link.d.ts +143 -0
- package/dist/pos/pay-by-link.js +57 -0
- package/dist/pos/quick-keys.d.ts +103 -0
- package/dist/pos/quick-keys.js +45 -0
- package/dist/pos/reports.d.ts +360 -0
- package/dist/pos/reports.js +62 -0
- package/dist/pos/returns.d.ts +244 -0
- package/dist/pos/returns.js +83 -0
- package/dist/pricing/bulk-coupons.d.ts +139 -0
- package/dist/pricing/bulk-coupons.js +112 -0
- package/dist/pricing/index.d.ts +174 -4
- package/dist/pricing/index.js +174 -1
- package/dist/pricing/price-lists.d.ts +606 -0
- package/dist/pricing/price-lists.js +600 -0
- package/dist/purchasing/alerts.d.ts +104 -0
- package/dist/purchasing/alerts.js +88 -0
- package/dist/purchasing/backorders.d.ts +61 -0
- package/dist/purchasing/backorders.js +92 -0
- package/dist/purchasing/cost.d.ts +110 -0
- package/dist/purchasing/cost.js +86 -0
- package/dist/purchasing/index.d.ts +54 -0
- package/dist/purchasing/index.js +24 -0
- package/dist/purchasing/links.d.ts +263 -0
- package/dist/purchasing/links.js +192 -0
- package/dist/purchasing/purchase-orders.d.ts +551 -0
- package/dist/purchasing/purchase-orders.js +491 -0
- package/dist/purchasing/receipts.d.ts +396 -0
- package/dist/purchasing/receipts.js +287 -0
- package/dist/purchasing/replenishment.d.ts +281 -0
- package/dist/purchasing/replenishment.js +240 -0
- package/dist/purchasing/returns.d.ts +273 -0
- package/dist/purchasing/returns.js +229 -0
- package/dist/purchasing/settings.d.ts +152 -0
- package/dist/purchasing/settings.js +181 -0
- package/dist/purchasing/suppliers.d.ts +476 -0
- package/dist/purchasing/suppliers.js +438 -0
- package/dist/quotes/index.d.ts +518 -0
- package/dist/quotes/index.js +439 -0
- package/dist/reports/index.d.ts +71 -8
- package/dist/reports/index.js +74 -11
- package/dist/returns/index.d.ts +675 -21
- package/dist/returns/index.js +756 -11
- package/dist/search/index.d.ts +76 -1
- package/dist/search/index.js +63 -0
- package/dist/seo/config.d.ts +128 -0
- package/dist/seo/config.js +95 -0
- package/dist/seo/redirects.d.ts +125 -0
- package/dist/seo/redirects.js +60 -1
- package/dist/seo/settings.d.ts +147 -0
- package/dist/seo/settings.js +209 -0
- package/dist/shipping/definitions.d.ts +29 -0
- package/dist/shipping/definitions.js +47 -0
- package/dist/shipping/fulfillment-profiles.d.ts +174 -0
- package/dist/shipping/fulfillment-profiles.js +104 -0
- package/dist/shipping/hendriks.d.ts +97 -0
- package/dist/shipping/hendriks.js +50 -0
- package/dist/shipping/index.d.ts +54 -0
- package/dist/shipping/index.js +27 -0
- package/dist/shipping/methods.d.ts +205 -0
- package/dist/shipping/methods.js +240 -0
- package/dist/shipping/myparcel.d.ts +668 -0
- package/dist/shipping/myparcel.js +466 -0
- package/dist/shipping/parcel-count-suggestion.d.ts +2 -0
- package/dist/shipping/parcel-rule.d.ts +6 -5
- package/dist/shipping/parcel-rule.js +21 -23
- package/dist/shipping/rate-tables.d.ts +324 -0
- package/dist/shipping/rate-tables.js +390 -0
- package/dist/shipping/ship-to-countries.d.ts +75 -0
- package/dist/shipping/ship-to-countries.js +47 -0
- package/dist/stock/by-location.d.ts +86 -0
- package/dist/stock/by-location.js +94 -0
- package/dist/stock/index.d.ts +18 -4
- package/dist/stock/index.js +7 -0
- package/dist/stock/locations.d.ts +144 -0
- package/dist/stock/locations.js +168 -0
- package/dist/stock/settings.d.ts +106 -0
- package/dist/stock/settings.js +103 -0
- package/dist/stock/transfers.d.ts +386 -52
- package/dist/stock/transfers.js +222 -17
- package/dist/stored-value/index.d.ts +312 -11
- package/dist/stored-value/index.js +223 -4
- package/dist/subscriptions/index.d.ts +682 -0
- package/dist/subscriptions/index.js +815 -0
- package/dist/tax/display-policy.d.ts +105 -0
- package/dist/tax/display-policy.js +84 -0
- package/dist/tax/index.d.ts +79 -1
- package/dist/tax/index.js +52 -0
- package/dist/tenant/email-style.d.ts +102 -0
- package/dist/tenant/email-style.js +72 -0
- package/dist/tenant/email-templates.d.ts +287 -0
- package/dist/tenant/email-templates.js +271 -0
- package/dist/tenant/notifications.d.ts +23 -17
- package/dist/tenant/settings-sources.d.ts +1 -1
- package/dist/tenant/time-zone.d.ts +17 -0
- package/dist/tenant/time-zone.js +11 -0
- package/dist/types.d.ts +50 -3
- package/dist/users/index.d.ts +3 -0
- package/dist/users/index.js +2 -0
- package/dist/users/warehouses.d.ts +50 -0
- package/dist/users/warehouses.js +51 -0
- package/package.json +2 -2
|
@@ -0,0 +1,439 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `admin.quotes`: B2B quotes, from the draft a sales rep builds to the order it converts into
|
|
3
|
+
* (headless spec §9.2 `quotes`; plan W10-35a).
|
|
4
|
+
*
|
|
5
|
+
* A quote is a store's offer to one customer, in one currency (`currencyCode`, one of the store's
|
|
6
|
+
* currencies). Staff build it (`createDraft`, `lines.*`, `setPricing`), move it through its
|
|
7
|
+
* statuses (`submit`, `approve`, `decline`, `cancel`) and turn an approved one into an order
|
|
8
|
+
* awaiting payment (`convertToOrder`). A customer can also request one from the storefront; it
|
|
9
|
+
* arrives `requested`.
|
|
10
|
+
*
|
|
11
|
+
* **The engine answers what can be done.** `Quote.editable` says whether the lines and prices may
|
|
12
|
+
* still change (every line and pricing method refuses `QUOTE_NOT_EDITABLE` exactly when it is
|
|
13
|
+
* false), and `Quote.convertible` whether `convertToOrder` would create the order now. Show the
|
|
14
|
+
* buttons from them; never re-derive either from the status.
|
|
15
|
+
*
|
|
16
|
+
* **Money** is an integer in minor units of the QUOTE's `currencyCode`. A line's `unitPrice`, its
|
|
17
|
+
* fixed discount, `discountAmount` and `effectiveUnitPrice` are in the store's primary tax mode
|
|
18
|
+
* (gross on a store whose prices include tax, net otherwise); `lineTotal` / `lineTotalWithTax`
|
|
19
|
+
* are the explicit net and gross halves. A percentage discount is in basis points (1000 = 10%).
|
|
20
|
+
* Every figure is the engine's own maths: the quote PDF and the converted order use the same.
|
|
21
|
+
*
|
|
22
|
+
* **Refusals** (`PlatformError`, by `code`): `QUOTE_NOT_FOUND` (404) for a quote or line the store
|
|
23
|
+
* does not have, another store's included; `QUOTE_LINE_INVALID` (400, `details[0].field` names the
|
|
24
|
+
* input) for a quantity, unit price, variant or pricing line it refuses; `QUOTE_DISCOUNT_INVALID`
|
|
25
|
+
* (400); `QUOTE_NOT_EDITABLE` (422); `QUOTE_CONVERT_STATE_INVALID` (422); `QUOTE_CURRENCY_MISMATCH`,
|
|
26
|
+
* `CURRENCY_NOT_AVAILABLE`, `ORDER_LINE_VARIANT_UNPRICED` (a line priced from the catalogue for a
|
|
27
|
+
* variant the store has no price for) and `HUB_HAS_NO_ORDERS` / `ORGANIZATION_WORKSPACE_NOT_A_STORE`
|
|
28
|
+
* (an organization workspace keeps no quotes). Each passes through as the engine sent it.
|
|
29
|
+
*
|
|
30
|
+
* Every value set below grows over time: treat a status this version does not name as one with
|
|
31
|
+
* no action (the engine's `editable` / `convertible` stay authoritative).
|
|
32
|
+
*/
|
|
33
|
+
import { idempotencyKey as mintIdempotencyKey, UpstreamError, } from '@sixtyninecommerce/core';
|
|
34
|
+
import { fieldOf } from '../catalog/types.js';
|
|
35
|
+
import { redactionWatcher } from '../customer-data-redaction.js';
|
|
36
|
+
export const QUOTE_STATUSES = [
|
|
37
|
+
'draft',
|
|
38
|
+
'requested',
|
|
39
|
+
'in_review',
|
|
40
|
+
'approved',
|
|
41
|
+
'declined',
|
|
42
|
+
'expired',
|
|
43
|
+
'converted_to_order',
|
|
44
|
+
'cancelled',
|
|
45
|
+
];
|
|
46
|
+
const LINE_FIELDS = /* GraphQL */ `
|
|
47
|
+
id
|
|
48
|
+
productVariantId
|
|
49
|
+
quantity
|
|
50
|
+
unitPrice
|
|
51
|
+
discountType
|
|
52
|
+
discountValue
|
|
53
|
+
discountLabel
|
|
54
|
+
position
|
|
55
|
+
discountAmount
|
|
56
|
+
effectiveUnitPrice
|
|
57
|
+
lineTotal
|
|
58
|
+
lineTotalWithTax
|
|
59
|
+
taxAmount
|
|
60
|
+
taxRate
|
|
61
|
+
createdAt
|
|
62
|
+
updatedAt
|
|
63
|
+
productVariant {
|
|
64
|
+
id
|
|
65
|
+
name
|
|
66
|
+
sku
|
|
67
|
+
storePrice {
|
|
68
|
+
price
|
|
69
|
+
priceWithTax
|
|
70
|
+
}
|
|
71
|
+
product {
|
|
72
|
+
id
|
|
73
|
+
name
|
|
74
|
+
featuredAsset {
|
|
75
|
+
id
|
|
76
|
+
preview
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
`;
|
|
81
|
+
const DOCUMENT_FIELDS = /* GraphQL */ `
|
|
82
|
+
id
|
|
83
|
+
documentNumber
|
|
84
|
+
fileSize
|
|
85
|
+
generatedAt
|
|
86
|
+
lastSentAt
|
|
87
|
+
`;
|
|
88
|
+
export const QUOTE_FIELDS = /* GraphQL */ `
|
|
89
|
+
id
|
|
90
|
+
code
|
|
91
|
+
customerId
|
|
92
|
+
currencyCode
|
|
93
|
+
status
|
|
94
|
+
editable
|
|
95
|
+
convertible
|
|
96
|
+
expiresAt
|
|
97
|
+
validUntil
|
|
98
|
+
salesRepUserId
|
|
99
|
+
customerNotes
|
|
100
|
+
internalNotes
|
|
101
|
+
convertedOrderId
|
|
102
|
+
createdAt
|
|
103
|
+
updatedAt
|
|
104
|
+
customer {
|
|
105
|
+
id
|
|
106
|
+
firstName
|
|
107
|
+
lastName
|
|
108
|
+
emailAddress
|
|
109
|
+
addresses {
|
|
110
|
+
company
|
|
111
|
+
defaultBillingAddress
|
|
112
|
+
defaultShippingAddress
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
salesRep {
|
|
116
|
+
id
|
|
117
|
+
userId
|
|
118
|
+
firstName
|
|
119
|
+
lastName
|
|
120
|
+
emailAddress
|
|
121
|
+
}
|
|
122
|
+
lines {
|
|
123
|
+
${LINE_FIELDS}
|
|
124
|
+
}
|
|
125
|
+
document {
|
|
126
|
+
${DOCUMENT_FIELDS}
|
|
127
|
+
}
|
|
128
|
+
`;
|
|
129
|
+
export const QUOTES_LIST_QUERY = /* GraphQL */ `
|
|
130
|
+
query QuotesList($options: AdminQuoteListOptions) {
|
|
131
|
+
quotes(options: $options) {
|
|
132
|
+
items {
|
|
133
|
+
${QUOTE_FIELDS}
|
|
134
|
+
}
|
|
135
|
+
totalItems
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
`;
|
|
139
|
+
/**
|
|
140
|
+
* The detail read adds what costs per read: the signed PDF link (a storage round trip) and each
|
|
141
|
+
* line's price-list suggestion (the price-list strategy per line). The list never selects them.
|
|
142
|
+
*/
|
|
143
|
+
export const QUOTE_GET_QUERY = /* GraphQL */ `
|
|
144
|
+
query QuotesGet($id: ID!) {
|
|
145
|
+
quote(id: $id) {
|
|
146
|
+
${QUOTE_FIELDS}
|
|
147
|
+
document {
|
|
148
|
+
downloadUrl
|
|
149
|
+
}
|
|
150
|
+
lines {
|
|
151
|
+
id
|
|
152
|
+
suggestedUnitPrice
|
|
153
|
+
}
|
|
154
|
+
statusLogs {
|
|
155
|
+
id
|
|
156
|
+
status
|
|
157
|
+
createdAt
|
|
158
|
+
actorUserId
|
|
159
|
+
metadata
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
`;
|
|
164
|
+
export const QUOTE_CREATE_DRAFT_MUTATION = /* GraphQL */ `
|
|
165
|
+
mutation QuotesCreateDraft($input: CreateDraftQuoteInput!) {
|
|
166
|
+
createDraftQuote(input: $input) {
|
|
167
|
+
${QUOTE_FIELDS}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
`;
|
|
171
|
+
export const QUOTE_ADD_LINE_MUTATION = /* GraphQL */ `
|
|
172
|
+
mutation QuotesAddLine($quoteId: ID!, $input: AddQuoteLineInput!) {
|
|
173
|
+
addQuoteLine(quoteId: $quoteId, input: $input) {
|
|
174
|
+
${QUOTE_FIELDS}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
`;
|
|
178
|
+
export const QUOTE_UPDATE_LINE_MUTATION = /* GraphQL */ `
|
|
179
|
+
mutation QuotesUpdateLine($lineId: ID!, $input: UpdateQuoteLineInput!) {
|
|
180
|
+
updateQuoteLine(lineId: $lineId, input: $input) {
|
|
181
|
+
${QUOTE_FIELDS}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
`;
|
|
185
|
+
export const QUOTE_REMOVE_LINE_MUTATION = /* GraphQL */ `
|
|
186
|
+
mutation QuotesRemoveLine($lineId: ID!) {
|
|
187
|
+
removeQuoteLine(lineId: $lineId) {
|
|
188
|
+
${QUOTE_FIELDS}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
`;
|
|
192
|
+
export const QUOTE_SET_PRICING_MUTATION = /* GraphQL */ `
|
|
193
|
+
mutation QuotesSetPricing($quoteId: ID!, $input: SetQuotePricingInput!) {
|
|
194
|
+
setQuotePricing(quoteId: $quoteId, input: $input) {
|
|
195
|
+
${QUOTE_FIELDS}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
`;
|
|
199
|
+
export const QUOTE_EXTEND_VALIDITY_MUTATION = /* GraphQL */ `
|
|
200
|
+
mutation QuotesExtendValidity($quoteId: ID!, $input: ExtendQuoteValidityInput!) {
|
|
201
|
+
extendQuoteValidity(quoteId: $quoteId, input: $input) {
|
|
202
|
+
${QUOTE_FIELDS}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
`;
|
|
206
|
+
export const QUOTE_ASSIGN_SALES_REP_MUTATION = /* GraphQL */ `
|
|
207
|
+
mutation QuotesAssignSalesRep($quoteId: ID!, $salesRepUserId: ID) {
|
|
208
|
+
assignQuoteSalesRep(quoteId: $quoteId, salesRepUserId: $salesRepUserId) {
|
|
209
|
+
${QUOTE_FIELDS}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
`;
|
|
213
|
+
export const QUOTE_SALES_REPRESENTATIVES_QUERY = /* GraphQL */ `
|
|
214
|
+
query QuotesSalesRepresentatives($search: String) {
|
|
215
|
+
quoteSalesRepresentatives(search: $search) {
|
|
216
|
+
id
|
|
217
|
+
userId
|
|
218
|
+
firstName
|
|
219
|
+
lastName
|
|
220
|
+
emailAddress
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
`;
|
|
224
|
+
export const QUOTE_SUBMIT_MUTATION = /* GraphQL */ `
|
|
225
|
+
mutation QuotesSubmit($quoteId: ID!) {
|
|
226
|
+
submitQuoteForReview(quoteId: $quoteId) {
|
|
227
|
+
${QUOTE_FIELDS}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
`;
|
|
231
|
+
export const QUOTE_APPROVE_MUTATION = /* GraphQL */ `
|
|
232
|
+
mutation QuotesApprove($quoteId: ID!) {
|
|
233
|
+
approveQuote(quoteId: $quoteId) {
|
|
234
|
+
${QUOTE_FIELDS}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
`;
|
|
238
|
+
export const QUOTE_DECLINE_MUTATION = /* GraphQL */ `
|
|
239
|
+
mutation QuotesDecline($quoteId: ID!, $reason: String!) {
|
|
240
|
+
declineQuote(quoteId: $quoteId, reason: $reason) {
|
|
241
|
+
${QUOTE_FIELDS}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
`;
|
|
245
|
+
export const QUOTE_CANCEL_MUTATION = /* GraphQL */ `
|
|
246
|
+
mutation QuotesCancel($quoteId: ID!, $reason: String) {
|
|
247
|
+
cancelQuote(quoteId: $quoteId, reason: $reason) {
|
|
248
|
+
${QUOTE_FIELDS}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
`;
|
|
252
|
+
export const QUOTE_CONVERT_MUTATION = /* GraphQL */ `
|
|
253
|
+
mutation QuotesConvertToOrder($quoteId: ID!, $idempotencyKey: String) {
|
|
254
|
+
convertQuoteToOrder(quoteId: $quoteId, idempotencyKey: $idempotencyKey) {
|
|
255
|
+
id
|
|
256
|
+
code
|
|
257
|
+
state
|
|
258
|
+
currencyCode
|
|
259
|
+
totalWithTax
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
`;
|
|
263
|
+
export const QUOTE_GENERATE_DOCUMENT_MUTATION = /* GraphQL */ `
|
|
264
|
+
mutation QuotesGenerateDocument($quoteId: ID!) {
|
|
265
|
+
generateQuoteDocument(quoteId: $quoteId) {
|
|
266
|
+
${DOCUMENT_FIELDS}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
`;
|
|
270
|
+
export const QUOTE_SEND_TO_CUSTOMER_MUTATION = /* GraphQL */ `
|
|
271
|
+
mutation QuotesSendToCustomer($quoteId: ID!) {
|
|
272
|
+
sendQuoteToCustomer(quoteId: $quoteId)
|
|
273
|
+
}
|
|
274
|
+
`;
|
|
275
|
+
const QUOTE_CONFIG_FIELDS = /* GraphQL */ `
|
|
276
|
+
id
|
|
277
|
+
channelId
|
|
278
|
+
numberPrefix
|
|
279
|
+
numberSuffix
|
|
280
|
+
numberDigits
|
|
281
|
+
yearFormat
|
|
282
|
+
yearReset
|
|
283
|
+
lastResetYear
|
|
284
|
+
documentTitle
|
|
285
|
+
footerText
|
|
286
|
+
validityNoticeTemplate
|
|
287
|
+
columnProduct
|
|
288
|
+
columnSku
|
|
289
|
+
columnQuantity
|
|
290
|
+
columnUnitPriceExcl
|
|
291
|
+
columnUnitPriceDiscount
|
|
292
|
+
columnUnitPriceIncl
|
|
293
|
+
columnUnitPriceAfterDiscount
|
|
294
|
+
columnLineTotal
|
|
295
|
+
columnLineDiscount
|
|
296
|
+
columnTaxRate
|
|
297
|
+
columnTaxAmount
|
|
298
|
+
columnTotal
|
|
299
|
+
showLogo
|
|
300
|
+
defaultLanguage
|
|
301
|
+
termsTitle
|
|
302
|
+
termsBody
|
|
303
|
+
acceptanceInstructions
|
|
304
|
+
`;
|
|
305
|
+
export const QUOTE_CONFIG_QUERY = /* GraphQL */ `
|
|
306
|
+
query QuotesConfig {
|
|
307
|
+
quoteConfig {
|
|
308
|
+
${QUOTE_CONFIG_FIELDS}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
`;
|
|
312
|
+
export const QUOTE_UPDATE_CONFIG_MUTATION = /* GraphQL */ `
|
|
313
|
+
mutation QuotesUpdateConfig($input: UpdateQuoteConfigInput!) {
|
|
314
|
+
updateQuoteConfig(input: $input) {
|
|
315
|
+
${QUOTE_CONFIG_FIELDS}
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
`;
|
|
319
|
+
export const QUOTE_SETTINGS_QUERY = /* GraphQL */ `
|
|
320
|
+
query QuotesSettings {
|
|
321
|
+
activeChannel {
|
|
322
|
+
id
|
|
323
|
+
defaultCurrencyCode
|
|
324
|
+
availableCurrencyCodes
|
|
325
|
+
customFields {
|
|
326
|
+
quotesEnabled
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
`;
|
|
331
|
+
function unexpected(field) {
|
|
332
|
+
return new UpstreamError({
|
|
333
|
+
code: 'UNEXPECTED_RESPONSE',
|
|
334
|
+
message: `The engine answered without "${field}".`,
|
|
335
|
+
status: 502,
|
|
336
|
+
service: 'vendure',
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
/** The key a caller retries under, on an error of a call whose key it did not choose. */
|
|
340
|
+
function withAttemptKey(error, key) {
|
|
341
|
+
if (error !== null && typeof error === 'object' && !('idempotencyKey' in error)) {
|
|
342
|
+
Object.defineProperty(error, 'idempotencyKey', { value: key, enumerable: true });
|
|
343
|
+
}
|
|
344
|
+
return error;
|
|
345
|
+
}
|
|
346
|
+
export function createQuotesModule(transport) {
|
|
347
|
+
/** One quote-answering call: the redaction stamp, and the field required. */
|
|
348
|
+
async function quoteCall(document, field, variables, options) {
|
|
349
|
+
const redaction = redactionWatcher();
|
|
350
|
+
const data = await transport.graphql({ api: 'admin', query: document, variables, onExtensions: redaction.onExtensions }, options);
|
|
351
|
+
const quote = data?.[field];
|
|
352
|
+
if (quote == null)
|
|
353
|
+
throw unexpected(field);
|
|
354
|
+
return redaction.mark(quote);
|
|
355
|
+
}
|
|
356
|
+
const lines = {
|
|
357
|
+
add: (quoteId, input, options) => quoteCall(QUOTE_ADD_LINE_MUTATION, 'addQuoteLine', { quoteId, input }, options),
|
|
358
|
+
update: (lineId, input, options) => quoteCall(QUOTE_UPDATE_LINE_MUTATION, 'updateQuoteLine', { lineId, input }, options),
|
|
359
|
+
remove: (lineId, options) => quoteCall(QUOTE_REMOVE_LINE_MUTATION, 'removeQuoteLine', { lineId }, options),
|
|
360
|
+
};
|
|
361
|
+
const config = {
|
|
362
|
+
get: (options) => fieldOf(transport, QUOTE_CONFIG_QUERY, 'quoteConfig', {}, options),
|
|
363
|
+
update: (input, options) => fieldOf(transport, QUOTE_UPDATE_CONFIG_MUTATION, 'updateQuoteConfig', { input }, options),
|
|
364
|
+
};
|
|
365
|
+
const settings = {
|
|
366
|
+
async get(options) {
|
|
367
|
+
const channel = await fieldOf(transport, QUOTE_SETTINGS_QUERY, 'activeChannel', {}, options);
|
|
368
|
+
if (!channel)
|
|
369
|
+
throw unexpected('activeChannel');
|
|
370
|
+
return {
|
|
371
|
+
quotesEnabled: channel.customFields?.quotesEnabled === true,
|
|
372
|
+
defaultCurrencyCode: channel.defaultCurrencyCode,
|
|
373
|
+
availableCurrencyCodes: channel.availableCurrencyCodes ?? [channel.defaultCurrencyCode],
|
|
374
|
+
};
|
|
375
|
+
},
|
|
376
|
+
};
|
|
377
|
+
return {
|
|
378
|
+
async list(options) {
|
|
379
|
+
const { take, skip, status, customerId, code, ...call } = options ?? {};
|
|
380
|
+
const redaction = redactionWatcher();
|
|
381
|
+
const data = await transport.graphql({
|
|
382
|
+
api: 'admin',
|
|
383
|
+
query: QUOTES_LIST_QUERY,
|
|
384
|
+
variables: { options: { take, skip, status, customerId, code } },
|
|
385
|
+
onExtensions: redaction.onExtensions,
|
|
386
|
+
}, call);
|
|
387
|
+
if (data?.quotes == null)
|
|
388
|
+
throw unexpected('quotes');
|
|
389
|
+
return {
|
|
390
|
+
items: data.quotes.items.map((quote) => redaction.mark(quote)),
|
|
391
|
+
totalItems: data.quotes.totalItems,
|
|
392
|
+
};
|
|
393
|
+
},
|
|
394
|
+
async get(quoteId, options) {
|
|
395
|
+
const redaction = redactionWatcher();
|
|
396
|
+
const data = await transport.graphql({
|
|
397
|
+
api: 'admin',
|
|
398
|
+
query: QUOTE_GET_QUERY,
|
|
399
|
+
variables: { id: quoteId },
|
|
400
|
+
onExtensions: redaction.onExtensions,
|
|
401
|
+
}, options);
|
|
402
|
+
const quote = data?.quote ?? null;
|
|
403
|
+
// `document` and `lines` are selected twice; GraphQL merges each into one object.
|
|
404
|
+
return quote ? redaction.mark(quote) : null;
|
|
405
|
+
},
|
|
406
|
+
createDraft: (input, options) => quoteCall(QUOTE_CREATE_DRAFT_MUTATION, 'createDraftQuote', { input }, options),
|
|
407
|
+
lines,
|
|
408
|
+
setPricing: (quoteId, input, options) => quoteCall(QUOTE_SET_PRICING_MUTATION, 'setQuotePricing', { quoteId, input }, options),
|
|
409
|
+
extendValidity: (quoteId, input, options) => quoteCall(QUOTE_EXTEND_VALIDITY_MUTATION, 'extendQuoteValidity', { quoteId, input }, options),
|
|
410
|
+
assignSalesRep: (quoteId, salesRepUserId, options) => quoteCall(QUOTE_ASSIGN_SALES_REP_MUTATION, 'assignQuoteSalesRep', { quoteId, salesRepUserId }, options),
|
|
411
|
+
salesRepresentatives: (term, options) => fieldOf(transport, QUOTE_SALES_REPRESENTATIVES_QUERY, 'quoteSalesRepresentatives', { search: term?.trim() ? term.trim() : null }, options),
|
|
412
|
+
submit: (quoteId, options) => quoteCall(QUOTE_SUBMIT_MUTATION, 'submitQuoteForReview', { quoteId }, options),
|
|
413
|
+
approve: (quoteId, options) => quoteCall(QUOTE_APPROVE_MUTATION, 'approveQuote', { quoteId }, options),
|
|
414
|
+
decline: (quoteId, reason, options) => quoteCall(QUOTE_DECLINE_MUTATION, 'declineQuote', { quoteId, reason }, options),
|
|
415
|
+
cancel: (quoteId, reason, options) => quoteCall(QUOTE_CANCEL_MUTATION, 'cancelQuote', { quoteId, reason: reason ?? null }, options),
|
|
416
|
+
async convertToOrder(quoteId, options) {
|
|
417
|
+
const key = options?.idempotencyKey ?? mintIdempotencyKey();
|
|
418
|
+
try {
|
|
419
|
+
const data = await transport.graphql({
|
|
420
|
+
api: 'admin',
|
|
421
|
+
query: QUOTE_CONVERT_MUTATION,
|
|
422
|
+
variables: { quoteId, idempotencyKey: key },
|
|
423
|
+
}, { ...options, idempotencyKey: key });
|
|
424
|
+
if (data?.convertQuoteToOrder == null)
|
|
425
|
+
throw unexpected('convertQuoteToOrder');
|
|
426
|
+
return { ...data.convertQuoteToOrder, idempotencyKey: key };
|
|
427
|
+
}
|
|
428
|
+
catch (error) {
|
|
429
|
+
throw withAttemptKey(error, key);
|
|
430
|
+
}
|
|
431
|
+
},
|
|
432
|
+
generateDocument: (quoteId, options) => fieldOf(transport, QUOTE_GENERATE_DOCUMENT_MUTATION, 'generateQuoteDocument', { quoteId }, options),
|
|
433
|
+
async sendToCustomer(quoteId, options) {
|
|
434
|
+
await fieldOf(transport, QUOTE_SEND_TO_CUSTOMER_MUTATION, 'sendQuoteToCustomer', { quoteId }, options);
|
|
435
|
+
},
|
|
436
|
+
config,
|
|
437
|
+
settings,
|
|
438
|
+
};
|
|
439
|
+
}
|
package/dist/reports/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type OrderScope, type Page, type Transport } from '@sixtyninecommerce/core';
|
|
2
2
|
import type { BulkModule, BulkOperation } from '../bulk/index.js';
|
|
3
|
+
import { type FoundVariant } from '../catalog/variant-find.js';
|
|
3
4
|
import type { JobHandle, JobsModule } from '../jobs/index.js';
|
|
4
5
|
import type { Call, ListOptions } from '../types.js';
|
|
5
6
|
/**
|
|
@@ -19,8 +20,22 @@ export interface ReportPeriod {
|
|
|
19
20
|
from: string;
|
|
20
21
|
to: string;
|
|
21
22
|
}
|
|
23
|
+
/**
|
|
24
|
+
* The last `lastDays` days of the STORE's calendar, ending on the store's today:
|
|
25
|
+
* the day it is now in the store's time zone (`Channel.customFields.timezone`,
|
|
26
|
+
* else Europe/Amsterdam), never yours, never UTC. `{ lastDays: 1 }` is the
|
|
27
|
+
* store's today so far. Whole local days, so a DST day is 23 or 25 hours long;
|
|
28
|
+
* compared against the `lastDays` days before. A whole number from 1 to 366.
|
|
29
|
+
*
|
|
30
|
+
* Needs an engine that knows `lastDays` (vendure-server deployed with it); an
|
|
31
|
+
* older one refuses the request as a GraphQL validation error.
|
|
32
|
+
*/
|
|
33
|
+
export interface ReportLastDays {
|
|
34
|
+
lastDays: number;
|
|
35
|
+
}
|
|
22
36
|
export interface ReportOverviewInput {
|
|
23
|
-
|
|
37
|
+
/** A `from`/`to` range, or the store's last N days. */
|
|
38
|
+
period: ReportPeriod | ReportLastDays;
|
|
24
39
|
/**
|
|
25
40
|
* One stock location of this store (`stockLocationId`). Keeps the orders whose
|
|
26
41
|
* stock was allocated or sold there, or that stated it at entry; an order
|
|
@@ -100,11 +115,12 @@ export interface PopularVariantsInput {
|
|
|
100
115
|
/** 1–3650, default 90. */
|
|
101
116
|
sinceDays?: number;
|
|
102
117
|
}
|
|
103
|
-
|
|
104
|
-
|
|
118
|
+
/**
|
|
119
|
+
* A best-selling variant, as a picker shows it: `catalog.variants.find`'s variant (store price,
|
|
120
|
+
* stock, product), plus its `productId`.
|
|
121
|
+
*/
|
|
122
|
+
export interface PopularVariant extends FoundVariant {
|
|
105
123
|
productId: string;
|
|
106
|
-
name: string;
|
|
107
|
-
sku: string;
|
|
108
124
|
}
|
|
109
125
|
export interface LowStockInput extends ListOptions {
|
|
110
126
|
/** Rows whose `available` is at or below this; a whole number, 0–1,000,000. */
|
|
@@ -166,12 +182,27 @@ export type ReportExport = Omit<BulkOperation, 'entity'> & {
|
|
|
166
182
|
* `admin.platform.reports.fleet` (SuperAdmin session only).
|
|
167
183
|
*/
|
|
168
184
|
export interface ReportsModule {
|
|
169
|
-
/**
|
|
185
|
+
/**
|
|
186
|
+
* Orders, revenue and customers of a period, by the store's own days. Needs `ReadOrder`.
|
|
187
|
+
*
|
|
188
|
+
* Refused before anything is asked, `INVALID_REPORT_PERIOD` (`ValidationError`, 400):
|
|
189
|
+
* `lastDays` that is not a whole number from 1 to 366, or a period carrying both `lastDays`
|
|
190
|
+
* and `from`/`to`. The engine refuses the same, and a malformed `from`/`to`.
|
|
191
|
+
*
|
|
192
|
+
* @effect read
|
|
193
|
+
*/
|
|
170
194
|
overview(input: ReportOverviewInput, options?: Call): Promise<ReportOverview>;
|
|
171
195
|
/**
|
|
172
196
|
* Variants ranked by units sold in placed orders of the last `sinceDays`
|
|
173
197
|
* days, most first. A `Page` because every list is one; it is always a
|
|
174
198
|
* single, final page.
|
|
199
|
+
*
|
|
200
|
+
* Each variant carries what a picker shows: its store price (null when the store has none,
|
|
201
|
+
* priced in the call's `tenant.currency` when one is named), its stock and its product. A
|
|
202
|
+
* hidden carrier variant is never ranked, and a variant the store no longer holds is left out,
|
|
203
|
+
* so the list can be shorter than `take`. Needs `ReadOrder`.
|
|
204
|
+
*
|
|
205
|
+
* @effect read
|
|
175
206
|
*/
|
|
176
207
|
popularVariants(input?: PopularVariantsInput, options?: Call): Promise<Page<PopularVariant>>;
|
|
177
208
|
/**
|
|
@@ -189,10 +220,42 @@ export interface ReportsModule {
|
|
|
189
220
|
* restriction withholds.
|
|
190
221
|
*/
|
|
191
222
|
export(kind: ReportExportKind, filter: ReportExportFilter, options?: Call): Promise<JobHandle<ReportExport>>;
|
|
223
|
+
/**
|
|
224
|
+
* Accounts-receivable aging: every customer who owes on an AR invoice, their outstanding split
|
|
225
|
+
* into buckets by days past due (against the store's today), one row per customer and
|
|
226
|
+
* currency, never summed across currencies. An ERP-linked customer's figures come from the
|
|
227
|
+
* ERP's mirrored invoices (`erpLinked`). Needs `ReadOrder`.
|
|
228
|
+
*
|
|
229
|
+
* @effect read
|
|
230
|
+
* @permission ReadOrder
|
|
231
|
+
*/
|
|
232
|
+
arAging(options?: Call): Promise<ArAgingRow[]>;
|
|
192
233
|
}
|
|
234
|
+
/** One customer's outstanding AR, in one currency, by days past due. Minor units. */
|
|
235
|
+
export interface ArAgingRow {
|
|
236
|
+
customerId: string;
|
|
237
|
+
customerName: string;
|
|
238
|
+
/** Not yet due. */
|
|
239
|
+
current: number;
|
|
240
|
+
d1_30: number;
|
|
241
|
+
d31_60: number;
|
|
242
|
+
d61_90: number;
|
|
243
|
+
d90_plus: number;
|
|
244
|
+
/** The sum of the buckets. */
|
|
245
|
+
total: number;
|
|
246
|
+
currencyCode: string;
|
|
247
|
+
/** True when the figures are the ERP's. */
|
|
248
|
+
erpLinked: boolean;
|
|
249
|
+
}
|
|
250
|
+
export declare const AR_AGING_QUERY = "\n query ArAgingReport {\n b2bArAgingReport {\n customerId\n customerName\n current\n d1_30\n d31_60\n d61_90\n d90_plus\n total\n currencyCode\n erpLinked\n }\n }\n";
|
|
193
251
|
/** Only the §9.3 generation is selected — never the deprecated Float/String fields. */
|
|
194
|
-
export declare const REPORT_OVERVIEW_QUERY = "\n query ReportOverview($range: ReportPeriodInput!, $locationId: ID, $orderScope: OrderScope) {\n commerceOverviewMetrics(range: $range, locationId: $locationId, orderScope: $orderScope) {\n currencyCode\n timeZone\n from\n to\n previousFrom\n previousTo\n current { \n orderCount\n cancelledCount\n revenueMinor\n revenueNetMinor\n averageOrderValueMinor\n averageOrderValueNetMinor\n ordersInOtherCurrencies\n }\n previous { \n orderCount\n cancelledCount\n revenueMinor\n revenueNetMinor\n averageOrderValueMinor\n averageOrderValueNetMinor\n ordersInOtherCurrencies\n }\n revenueSeries { startsAt valueMinor valueNetMinor }\n orderSeries { startsAt value }\n newCustomers\n previousNewCustomers\n awaitingFulfillment\n statusBreakdown { state count }\n topProducts { productVariantId productId name sku unitsSold }\n }\n }\n";
|
|
195
|
-
|
|
252
|
+
export declare const REPORT_OVERVIEW_QUERY = "\n query ReportOverview($range: ReportPeriodInput!, $locationId: ID, $orderScope: OrderScope) {\n commerceOverviewMetrics(range: $range, locationId: $locationId, orderScope: $orderScope) {\n \n currencyCode\n timeZone\n from\n to\n previousFrom\n previousTo\n current { \n orderCount\n cancelledCount\n revenueMinor\n revenueNetMinor\n averageOrderValueMinor\n averageOrderValueNetMinor\n ordersInOtherCurrencies\n }\n previous { \n orderCount\n cancelledCount\n revenueMinor\n revenueNetMinor\n averageOrderValueMinor\n averageOrderValueNetMinor\n ordersInOtherCurrencies\n }\n revenueSeries { startsAt valueMinor valueNetMinor }\n orderSeries { startsAt value }\n newCustomers\n previousNewCustomers\n awaitingFulfillment\n statusBreakdown { state count }\n topProducts { productVariantId productId name sku unitsSold }\n\n }\n }\n";
|
|
253
|
+
/**
|
|
254
|
+
* `lastDays` has a document of its own, so a `from`/`to` caller never sends an
|
|
255
|
+
* argument an engine without `lastDays` would refuse (each service deploys alone).
|
|
256
|
+
*/
|
|
257
|
+
export declare const REPORT_OVERVIEW_LAST_DAYS_QUERY = "\n query ReportOverviewLastDays($lastDays: Int!, $locationId: ID, $orderScope: OrderScope) {\n commerceOverviewMetrics(lastDays: $lastDays, locationId: $locationId, orderScope: $orderScope) {\n \n currencyCode\n timeZone\n from\n to\n previousFrom\n previousTo\n current { \n orderCount\n cancelledCount\n revenueMinor\n revenueNetMinor\n averageOrderValueMinor\n averageOrderValueNetMinor\n ordersInOtherCurrencies\n }\n previous { \n orderCount\n cancelledCount\n revenueMinor\n revenueNetMinor\n averageOrderValueMinor\n averageOrderValueNetMinor\n ordersInOtherCurrencies\n }\n revenueSeries { startsAt valueMinor valueNetMinor }\n orderSeries { startsAt value }\n newCustomers\n previousNewCustomers\n awaitingFulfillment\n statusBreakdown { state count }\n topProducts { productVariantId productId name sku unitsSold }\n\n }\n }\n";
|
|
258
|
+
export declare const POPULAR_VARIANTS_QUERY = "\n query ReportPopularVariants($take: Int, $sinceDays: Int) {\n popularProductVariantsInChannel(take: $take, sinceDays: $sinceDays) {\n productId\n \n \n id\n name\n sku\n enabled\n storePrice {\n price\n priceWithTax\n currencyCode\n }\n trackInventory\n outOfStockThreshold\n useGlobalOutOfStockThreshold\n stockOnHand\n stockAllocated\n stockLevels {\n stockLocationId\n stockOnHand\n stockAllocated\n stockLocation {\n id\n name\n }\n }\n\n product {\n \n id\n name\n slug\n featuredAsset {\n id\n preview\n }\n\n }\n\n }\n }\n";
|
|
196
259
|
export declare const LOW_STOCK_QUERY = "\n query ReportLowStock($threshold: Int!, $locationId: ID, $options: LowStockVariantListOptions) {\n lowStockVariants(threshold: $threshold, locationId: $locationId, options: $options) {\n items {\n productVariantId\n productId\n sku\n name\n stockLocationId\n stockLocationName\n stockOnHand\n stockAllocated\n reserved\n available\n }\n totalItems\n }\n }\n";
|
|
197
260
|
export declare function createReportsModule(transport: Transport, bulk: BulkModule, jobs: JobsModule,
|
|
198
261
|
/** The client's `tenant.location`, read per call (a derived client has its own). */
|
package/dist/reports/index.js
CHANGED
|
@@ -1,4 +1,23 @@
|
|
|
1
|
-
import { createPage, pageFromVendureList, readOffsetCursor, } from '@sixtyninecommerce/core';
|
|
1
|
+
import { createPage, pageFromVendureList, readOffsetCursor, ValidationError, } from '@sixtyninecommerce/core';
|
|
2
|
+
import { FOUND_VARIANT_WITH_PRODUCT_FIELDS, toFoundVariant, } from '../catalog/variant-find.js';
|
|
3
|
+
/** The longest period a report covers, in days (the engine's bound). */
|
|
4
|
+
const MAX_REPORT_DAYS = 366;
|
|
5
|
+
export const AR_AGING_QUERY = /* GraphQL */ `
|
|
6
|
+
query ArAgingReport {
|
|
7
|
+
b2bArAgingReport {
|
|
8
|
+
customerId
|
|
9
|
+
customerName
|
|
10
|
+
current
|
|
11
|
+
d1_30
|
|
12
|
+
d31_60
|
|
13
|
+
d61_90
|
|
14
|
+
d90_plus
|
|
15
|
+
total
|
|
16
|
+
currencyCode
|
|
17
|
+
erpLinked
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
`;
|
|
2
21
|
const TOTALS_FIELDS = /* GraphQL */ `
|
|
3
22
|
orderCount
|
|
4
23
|
cancelledCount
|
|
@@ -8,10 +27,7 @@ const TOTALS_FIELDS = /* GraphQL */ `
|
|
|
8
27
|
averageOrderValueNetMinor
|
|
9
28
|
ordersInOtherCurrencies
|
|
10
29
|
`;
|
|
11
|
-
|
|
12
|
-
export const REPORT_OVERVIEW_QUERY = /* GraphQL */ `
|
|
13
|
-
query ReportOverview($range: ReportPeriodInput!, $locationId: ID, $orderScope: OrderScope) {
|
|
14
|
-
commerceOverviewMetrics(range: $range, locationId: $locationId, orderScope: $orderScope) {
|
|
30
|
+
const OVERVIEW_FIELDS = /* GraphQL */ `
|
|
15
31
|
currencyCode
|
|
16
32
|
timeZone
|
|
17
33
|
from
|
|
@@ -27,16 +43,31 @@ export const REPORT_OVERVIEW_QUERY = /* GraphQL */ `
|
|
|
27
43
|
awaitingFulfillment
|
|
28
44
|
statusBreakdown { state count }
|
|
29
45
|
topProducts { productVariantId productId name sku unitsSold }
|
|
46
|
+
`;
|
|
47
|
+
/** Only the §9.3 generation is selected — never the deprecated Float/String fields. */
|
|
48
|
+
export const REPORT_OVERVIEW_QUERY = /* GraphQL */ `
|
|
49
|
+
query ReportOverview($range: ReportPeriodInput!, $locationId: ID, $orderScope: OrderScope) {
|
|
50
|
+
commerceOverviewMetrics(range: $range, locationId: $locationId, orderScope: $orderScope) {
|
|
51
|
+
${OVERVIEW_FIELDS}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
`;
|
|
55
|
+
/**
|
|
56
|
+
* `lastDays` has a document of its own, so a `from`/`to` caller never sends an
|
|
57
|
+
* argument an engine without `lastDays` would refuse (each service deploys alone).
|
|
58
|
+
*/
|
|
59
|
+
export const REPORT_OVERVIEW_LAST_DAYS_QUERY = /* GraphQL */ `
|
|
60
|
+
query ReportOverviewLastDays($lastDays: Int!, $locationId: ID, $orderScope: OrderScope) {
|
|
61
|
+
commerceOverviewMetrics(lastDays: $lastDays, locationId: $locationId, orderScope: $orderScope) {
|
|
62
|
+
${OVERVIEW_FIELDS}
|
|
30
63
|
}
|
|
31
64
|
}
|
|
32
65
|
`;
|
|
33
66
|
export const POPULAR_VARIANTS_QUERY = /* GraphQL */ `
|
|
34
67
|
query ReportPopularVariants($take: Int, $sinceDays: Int) {
|
|
35
68
|
popularProductVariantsInChannel(take: $take, sinceDays: $sinceDays) {
|
|
36
|
-
id
|
|
37
69
|
productId
|
|
38
|
-
|
|
39
|
-
sku
|
|
70
|
+
${FOUND_VARIANT_WITH_PRODUCT_FIELDS}
|
|
40
71
|
}
|
|
41
72
|
}
|
|
42
73
|
`;
|
|
@@ -60,6 +91,32 @@ export const LOW_STOCK_QUERY = /* GraphQL */ `
|
|
|
60
91
|
}
|
|
61
92
|
`;
|
|
62
93
|
const DEFAULT_LOW_STOCK_PAGE = 50;
|
|
94
|
+
function invalidPeriod(message) {
|
|
95
|
+
return new ValidationError({
|
|
96
|
+
code: 'INVALID_REPORT_PERIOD',
|
|
97
|
+
message,
|
|
98
|
+
status: 400,
|
|
99
|
+
service: 'vendure',
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
/** The overview's request for its period form, refused at the boundary like the engine would. */
|
|
103
|
+
function overviewRequest(period) {
|
|
104
|
+
const given = (period ?? {});
|
|
105
|
+
if (given.lastDays === undefined) {
|
|
106
|
+
return {
|
|
107
|
+
query: REPORT_OVERVIEW_QUERY,
|
|
108
|
+
variables: { range: { from: given.from, to: given.to } },
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
if (given.from !== undefined || given.to !== undefined) {
|
|
112
|
+
throw invalidPeriod('Give either `lastDays` or `from`/`to`, not both: each one names the whole period.');
|
|
113
|
+
}
|
|
114
|
+
const { lastDays } = given;
|
|
115
|
+
if (!Number.isInteger(lastDays) || lastDays < 1 || lastDays > MAX_REPORT_DAYS) {
|
|
116
|
+
throw invalidPeriod(`\`lastDays\` must be a whole number from 1 to ${MAX_REPORT_DAYS}.`);
|
|
117
|
+
}
|
|
118
|
+
return { query: REPORT_OVERVIEW_LAST_DAYS_QUERY, variables: { lastDays } };
|
|
119
|
+
}
|
|
63
120
|
/** The bulk entity a report kind is exported as. */
|
|
64
121
|
const EXPORT_ENTITY = { overview: 'report_overview' };
|
|
65
122
|
/** Filter keys travel as the service spells them — see `bulk/index.ts`'s `FILTER_PASSTHROUGH`. */
|
|
@@ -79,13 +136,19 @@ clientLocation = () => undefined) {
|
|
|
79
136
|
}, input);
|
|
80
137
|
return pageFromVendureList(data.lowStockVariants, skip, take, (cursor) => lowStockPage(input, readOffsetCursor(cursor), take));
|
|
81
138
|
};
|
|
139
|
+
const arAging = async (options) => {
|
|
140
|
+
const data = await transport.graphql({ api: 'admin', query: AR_AGING_QUERY }, options);
|
|
141
|
+
return data.b2bArAgingReport;
|
|
142
|
+
};
|
|
82
143
|
return {
|
|
144
|
+
arAging,
|
|
83
145
|
async overview(input, options) {
|
|
146
|
+
const request = overviewRequest(input.period);
|
|
84
147
|
const data = await transport.graphql({
|
|
85
148
|
api: 'admin',
|
|
86
|
-
query:
|
|
149
|
+
query: request.query,
|
|
87
150
|
variables: {
|
|
88
|
-
|
|
151
|
+
...request.variables,
|
|
89
152
|
locationId: input.locationId ?? null,
|
|
90
153
|
orderScope: input.orderScope ?? null,
|
|
91
154
|
},
|
|
@@ -111,7 +174,7 @@ clientLocation = () => undefined) {
|
|
|
111
174
|
query: POPULAR_VARIANTS_QUERY,
|
|
112
175
|
variables: { take: input?.take ?? null, sinceDays: input?.sinceDays ?? null },
|
|
113
176
|
}, options);
|
|
114
|
-
const items = data.popularProductVariantsInChannel;
|
|
177
|
+
const items = data.popularProductVariantsInChannel.map((row) => ({ ...toFoundVariant(row), productId: row.productId }));
|
|
115
178
|
return createPage(items, { nextCursor: null, hasNext: false, total: items.length }, () => {
|
|
116
179
|
// Unreachable while `hasNext` is false — `Page.next()` short-circuits.
|
|
117
180
|
throw new Error('popularVariants returns one ranked list; there is no next page');
|