@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,600 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Price lists (headless spec §9.2 `pricing`: "price lists"; W10-14): a store's B2B price lists,
|
|
3
|
+
* their entries (what each target costs), who they are assigned to, the order rules they carry,
|
|
4
|
+
* and enabling, disabling or deleting many at once.
|
|
5
|
+
*
|
|
6
|
+
* - **Money** is integer MINOR UNITS of the store's DEFAULT currency, unless an entry's
|
|
7
|
+
* `currencyCode` names another; an entry with no `currencyCode` applies in the default
|
|
8
|
+
* currency only. Its basis differs per figure:
|
|
9
|
+
* - a `fixed_price` / `fixed_amount` entry: the store's price basis (gross on a tax-inclusive
|
|
10
|
+
* store, net on a tax-exclusive one);
|
|
11
|
+
* - a list's order minimum (`minOrderAmount`): its own `minOrderAmountBasis`, `net` or
|
|
12
|
+
* `gross`, and `gross` when omitted, whatever the store's basis;
|
|
13
|
+
* - an order rule's money (a `min_order_total` threshold, a `fixed_amount` discount): the
|
|
14
|
+
* store's price basis, as the dashboard labels it (USER DECISION N7, 2026-10-13).
|
|
15
|
+
* Percentages are basis points (`1000` = 10 %).
|
|
16
|
+
* - **Order rules apply** (USER DECISION N1). A customer a list reaches gets the single best rule
|
|
17
|
+
* their order meets (the most money off), as one line-level discount named "Order discount" in
|
|
18
|
+
* the order's language. Only the most specific level of lists with rules decides (contract →
|
|
19
|
+
* customer → group → store-wide); lists at that level are compared together. A total threshold
|
|
20
|
+
* reads the goods at the customer's own prices before any other discount, in the store's price
|
|
21
|
+
* basis, leaving out shipping, fees, gift cards and stated-price lines (imports, accepted
|
|
22
|
+
* quotes, till manager prices); a quantity threshold counts the units of those same lines.
|
|
23
|
+
* Money converts into the order's currency at the order's rate (no rate: no discount). Drafts,
|
|
24
|
+
* storefront carts and till sales with a customer get it; quote, import and subscription
|
|
25
|
+
* orders never do. A placed order keeps the rule SET it was placed with: an edit takes the
|
|
26
|
+
* discount off below the threshold and gives it above it, and later rule changes never reach
|
|
27
|
+
* it. The order minimum is checked before this discount. `orderRulesCombineWithPromotions`
|
|
28
|
+
* (off by default): on, the discount and the store's coupons and promotions add up; off, a
|
|
29
|
+
* coupon or promotion on the order wins and the list's rules step aside for that order.
|
|
30
|
+
* - **Times** are ISO-8601 instants in UTC (`startsAt`, `endsAt`).
|
|
31
|
+
* - **A list locked to an order** (an order's one-time discount, read as `Order.priceList`) is
|
|
32
|
+
* never answered here: `get` answers `null`, and every write refuses it
|
|
33
|
+
* `PRICE_LIST_LOCKED_TO_ORDER` (`BusinessRuleError`, 422). Change it from its order
|
|
34
|
+
* (`orders.setOneTimeDiscounts`).
|
|
35
|
+
* - **Organizations:** a price list is the store's own row; a workspace's lists price only the
|
|
36
|
+
* workspace.
|
|
37
|
+
*
|
|
38
|
+
* Permissions: reads need `ReadCatalog`, writes `UpdateCatalog` (deleting too, as the engine's
|
|
39
|
+
* single door asks). The names behind assignments also need `ReadCustomer` (a customer) or
|
|
40
|
+
* `ReadCustomer`/`ReadCustomerGroup` (a group); without it the name is `null` and the id stays.
|
|
41
|
+
*/
|
|
42
|
+
import { pageFromVendureList, readOffsetCursor, ValidationError, } from '@sixtyninecommerce/core';
|
|
43
|
+
import { assertDeleted, DELETION_FIELDS, fieldOf, } from '../catalog/types.js';
|
|
44
|
+
import { redactionWatcher } from '../customer-data-redaction.js';
|
|
45
|
+
/**
|
|
46
|
+
* What a list search matches the term against:
|
|
47
|
+
* - `all` (the default): every facet below;
|
|
48
|
+
* - `name`: the list's own name and description;
|
|
49
|
+
* - `sku`: lists pricing a variant whose SKU STARTS WITH the term;
|
|
50
|
+
* - `product`: lists pricing a product or variant whose name contains the term;
|
|
51
|
+
* - `customer`: lists assigned to a matching customer, directly or through a group holding them
|
|
52
|
+
* (or a group whose name matches).
|
|
53
|
+
* `sku` and `product` reach a list through variant, product, collection and facet-value entries
|
|
54
|
+
* alike. A list that prices everything (a default discount, or assigned to every customer) is
|
|
55
|
+
* never matched: it would answer every search.
|
|
56
|
+
*/
|
|
57
|
+
export const PRICE_LIST_SEARCH_MODES = ['all', 'name', 'sku', 'product', 'customer'];
|
|
58
|
+
/** What an entries search matches: the entry's own label, or (`sku`) every entry pricing the SKU. */
|
|
59
|
+
export const PRICE_LIST_ENTRY_SEARCH_MODES = ['all', 'sku'];
|
|
60
|
+
/** The engine's `adminListQueryLimit`: a larger page of lists is refused. */
|
|
61
|
+
export const MAX_PRICE_LIST_LIST_TAKE = 1000;
|
|
62
|
+
/** The largest page of entries the engine answers. */
|
|
63
|
+
export const MAX_PRICE_LIST_ENTRY_TAKE = 200;
|
|
64
|
+
/** How many entries `entries.export` answers at most; `total` still counts every match. */
|
|
65
|
+
export const MAX_PRICE_LIST_ENTRY_EXPORT = 10_000;
|
|
66
|
+
/** How many lists one `bulk` call may name. */
|
|
67
|
+
export const MAX_BULK_PRICE_LISTS = 100;
|
|
68
|
+
/** A search term shorter than this is ignored by the engine: the whole list answers. */
|
|
69
|
+
export const PRICE_LIST_SEARCH_MIN_TERM_LENGTH = 2;
|
|
70
|
+
/** How many entries `forCustomer` answers per list at most (`entriesTake`). */
|
|
71
|
+
export const MAX_CUSTOMER_PRICE_LIST_ENTRIES = 100;
|
|
72
|
+
export const PRICE_LIST_BULK_ACTIONS = [
|
|
73
|
+
'ENABLE',
|
|
74
|
+
'DISABLE',
|
|
75
|
+
'DELETE',
|
|
76
|
+
];
|
|
77
|
+
const FIELDS = /* GraphQL */ `
|
|
78
|
+
id
|
|
79
|
+
name
|
|
80
|
+
description
|
|
81
|
+
isContract
|
|
82
|
+
priority
|
|
83
|
+
defaultDiscountBps
|
|
84
|
+
startsAt
|
|
85
|
+
endsAt
|
|
86
|
+
enabled
|
|
87
|
+
appliesToAllCustomers
|
|
88
|
+
paymentTermsDays
|
|
89
|
+
paymentTermsCode
|
|
90
|
+
minOrderAmount
|
|
91
|
+
minOrderAmountBasis
|
|
92
|
+
minOrderQuantity
|
|
93
|
+
orderRulesCombineWithPromotions
|
|
94
|
+
createdAt
|
|
95
|
+
updatedAt
|
|
96
|
+
`;
|
|
97
|
+
const ASSIGNMENT_FIELDS = /* GraphQL */ `
|
|
98
|
+
id
|
|
99
|
+
priceListId
|
|
100
|
+
customerGroupId
|
|
101
|
+
customerId
|
|
102
|
+
customerGroup {
|
|
103
|
+
id
|
|
104
|
+
name
|
|
105
|
+
}
|
|
106
|
+
customer {
|
|
107
|
+
id
|
|
108
|
+
firstName
|
|
109
|
+
lastName
|
|
110
|
+
emailAddress
|
|
111
|
+
company
|
|
112
|
+
}
|
|
113
|
+
createdAt
|
|
114
|
+
updatedAt
|
|
115
|
+
`;
|
|
116
|
+
/** A list as `get`, `create` and `update` read it: with its assignments' names. */
|
|
117
|
+
export const PRICE_LIST_FIELDS = /* GraphQL */ `
|
|
118
|
+
${FIELDS}
|
|
119
|
+
assignments { ${ASSIGNMENT_FIELDS} }
|
|
120
|
+
`;
|
|
121
|
+
export const PRICE_LISTS_QUERY = /* GraphQL */ `
|
|
122
|
+
query PriceLists($options: PriceListListOptions) {
|
|
123
|
+
priceLists(options: $options) {
|
|
124
|
+
items {
|
|
125
|
+
${FIELDS}
|
|
126
|
+
assignments {
|
|
127
|
+
id
|
|
128
|
+
priceListId
|
|
129
|
+
customerGroupId
|
|
130
|
+
customerId
|
|
131
|
+
}
|
|
132
|
+
searchMatches {
|
|
133
|
+
kind
|
|
134
|
+
label
|
|
135
|
+
}
|
|
136
|
+
searchMatchCount
|
|
137
|
+
}
|
|
138
|
+
totalItems
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
`;
|
|
142
|
+
export const PRICE_LIST_QUERY = /* GraphQL */ `
|
|
143
|
+
query PriceList($id: ID!) {
|
|
144
|
+
priceList(id: $id) { ${PRICE_LIST_FIELDS} }
|
|
145
|
+
}
|
|
146
|
+
`;
|
|
147
|
+
export const CREATE_PRICE_LIST_MUTATION = /* GraphQL */ `
|
|
148
|
+
mutation CreatePriceList($input: CreatePriceListInput!) {
|
|
149
|
+
createPriceList(input: $input) { ${PRICE_LIST_FIELDS} }
|
|
150
|
+
}
|
|
151
|
+
`;
|
|
152
|
+
export const UPDATE_PRICE_LIST_MUTATION = /* GraphQL */ `
|
|
153
|
+
mutation UpdatePriceList($id: ID!, $input: UpdatePriceListInput!) {
|
|
154
|
+
updatePriceList(id: $id, input: $input) { ${PRICE_LIST_FIELDS} }
|
|
155
|
+
}
|
|
156
|
+
`;
|
|
157
|
+
export const DELETE_PRICE_LIST_MUTATION = /* GraphQL */ `
|
|
158
|
+
mutation DeletePriceList($id: ID!) {
|
|
159
|
+
deletePriceList(id: $id) { ${DELETION_FIELDS} }
|
|
160
|
+
}
|
|
161
|
+
`;
|
|
162
|
+
export const ASSIGN_PRICE_LIST_MUTATION = /* GraphQL */ `
|
|
163
|
+
mutation AssignPriceList($priceListId: ID!, $input: AssignPriceListInput!) {
|
|
164
|
+
assignPriceList(priceListId: $priceListId, input: $input) { ${ASSIGNMENT_FIELDS} }
|
|
165
|
+
}
|
|
166
|
+
`;
|
|
167
|
+
export const UNASSIGN_PRICE_LIST_MUTATION = /* GraphQL */ `
|
|
168
|
+
mutation UnassignPriceList($assignmentId: ID!) {
|
|
169
|
+
unassignPriceList(assignmentId: $assignmentId) { ${DELETION_FIELDS} }
|
|
170
|
+
}
|
|
171
|
+
`;
|
|
172
|
+
export const BULK_PRICE_LIST_ACTION_MUTATION = /* GraphQL */ `
|
|
173
|
+
mutation BulkPriceListAction($input: BulkPriceListActionInput!) {
|
|
174
|
+
bulkPriceListAction(input: $input) {
|
|
175
|
+
items {
|
|
176
|
+
id
|
|
177
|
+
status
|
|
178
|
+
code
|
|
179
|
+
message
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
`;
|
|
184
|
+
const ENTRY_WRITE_FIELDS = /* GraphQL */ `
|
|
185
|
+
id
|
|
186
|
+
priceListId
|
|
187
|
+
discountType
|
|
188
|
+
targetType
|
|
189
|
+
variantId
|
|
190
|
+
productId
|
|
191
|
+
collectionId
|
|
192
|
+
facetValueId
|
|
193
|
+
discountValue
|
|
194
|
+
chainValues
|
|
195
|
+
minQuantity
|
|
196
|
+
currencyCode
|
|
197
|
+
createdAt
|
|
198
|
+
updatedAt
|
|
199
|
+
`;
|
|
200
|
+
const ENTRY_FIELDS = /* GraphQL */ `
|
|
201
|
+
${ENTRY_WRITE_FIELDS}
|
|
202
|
+
variantName
|
|
203
|
+
variantSku
|
|
204
|
+
targetName
|
|
205
|
+
`;
|
|
206
|
+
export const PRICE_LIST_ENTRIES_QUERY = /* GraphQL */ `
|
|
207
|
+
query PriceListEntries($priceListId: ID!, $options: PriceListEntryListOptions) {
|
|
208
|
+
priceListEntries(priceListId: $priceListId, options: $options) {
|
|
209
|
+
items { ${ENTRY_FIELDS} }
|
|
210
|
+
totalItems
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
`;
|
|
214
|
+
const FACET_COUNT_FIELDS = 'id label count';
|
|
215
|
+
export const PRICE_LIST_ENTRY_FACETS_QUERY = /* GraphQL */ `
|
|
216
|
+
query PriceListEntryFacets($priceListId: ID!, $search: String, $searchMode: String) {
|
|
217
|
+
priceListEntryFacets(priceListId: $priceListId, search: $search, searchMode: $searchMode) {
|
|
218
|
+
targetTypes { ${FACET_COUNT_FIELDS} }
|
|
219
|
+
collections { ${FACET_COUNT_FIELDS} }
|
|
220
|
+
facetValues { ${FACET_COUNT_FIELDS} }
|
|
221
|
+
total
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
`;
|
|
225
|
+
export const SET_PRICE_LIST_ENTRY_MUTATION = /* GraphQL */ `
|
|
226
|
+
mutation SetPriceListEntry($priceListId: ID!, $input: SetPriceListEntryInput!) {
|
|
227
|
+
setPriceListEntry(priceListId: $priceListId, input: $input) { ${ENTRY_FIELDS} }
|
|
228
|
+
}
|
|
229
|
+
`;
|
|
230
|
+
export const BULK_SET_PRICE_LIST_ENTRIES_MUTATION = /* GraphQL */ `
|
|
231
|
+
mutation BulkSetPriceListEntries($priceListId: ID!, $entries: [SetPriceListEntryInput!]!) {
|
|
232
|
+
bulkSetPriceListEntries(priceListId: $priceListId, entries: $entries) {
|
|
233
|
+
${ENTRY_WRITE_FIELDS}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
`;
|
|
237
|
+
export const REMOVE_PRICE_LIST_ENTRY_MUTATION = /* GraphQL */ `
|
|
238
|
+
mutation RemovePriceListEntry($id: ID!) {
|
|
239
|
+
removePriceListEntry(id: $id) { ${DELETION_FIELDS} }
|
|
240
|
+
}
|
|
241
|
+
`;
|
|
242
|
+
const ORDER_RULE_FIELDS = /* GraphQL */ `
|
|
243
|
+
id
|
|
244
|
+
priceListId
|
|
245
|
+
discountType
|
|
246
|
+
conditionType
|
|
247
|
+
conditionValue
|
|
248
|
+
discountValue
|
|
249
|
+
createdAt
|
|
250
|
+
updatedAt
|
|
251
|
+
`;
|
|
252
|
+
export const PRICE_LIST_ORDER_RULES_QUERY = /* GraphQL */ `
|
|
253
|
+
query PriceListOrderRules($priceListId: ID!) {
|
|
254
|
+
priceListOrderRules(priceListId: $priceListId) { ${ORDER_RULE_FIELDS} }
|
|
255
|
+
}
|
|
256
|
+
`;
|
|
257
|
+
export const SET_PRICE_LIST_ORDER_RULE_MUTATION = /* GraphQL */ `
|
|
258
|
+
mutation SetPriceListOrderRule($priceListId: ID!, $input: SetPriceListOrderRuleInput!) {
|
|
259
|
+
setPriceListOrderRule(priceListId: $priceListId, input: $input) { ${ORDER_RULE_FIELDS} }
|
|
260
|
+
}
|
|
261
|
+
`;
|
|
262
|
+
export const REMOVE_PRICE_LIST_ORDER_RULE_MUTATION = /* GraphQL */ `
|
|
263
|
+
mutation RemovePriceListOrderRule($id: ID!) {
|
|
264
|
+
removePriceListOrderRule(id: $id) { ${DELETION_FIELDS} }
|
|
265
|
+
}
|
|
266
|
+
`;
|
|
267
|
+
export const CUSTOMER_PRICE_LISTS_QUERY = /* GraphQL */ `
|
|
268
|
+
query CustomerPriceLists($customerId: ID!, $options: CustomerPriceListsOptions) {
|
|
269
|
+
customerPriceLists(customerId: $customerId, options: $options) {
|
|
270
|
+
priceList { ${FIELDS} }
|
|
271
|
+
assignmentType
|
|
272
|
+
entries {
|
|
273
|
+
items { ${ENTRY_FIELDS} }
|
|
274
|
+
totalItems
|
|
275
|
+
}
|
|
276
|
+
orderRules { ${ORDER_RULE_FIELDS} }
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
`;
|
|
280
|
+
const DEFAULT_LIST_TAKE = 25;
|
|
281
|
+
const DEFAULT_ENTRY_TAKE = 50;
|
|
282
|
+
function invalid(code, message) {
|
|
283
|
+
return new ValidationError({ code, message, status: 400, service: 'vendure' });
|
|
284
|
+
}
|
|
285
|
+
const invalidListOptions = (message) => invalid('INVALID_PRICE_LIST_LIST_OPTIONS', message);
|
|
286
|
+
const isObject = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
287
|
+
const isIdList = (value) => Array.isArray(value) && value.every((id) => typeof id === 'string');
|
|
288
|
+
const isWhole = (value, min, max) => typeof value === 'number' && Number.isInteger(value) && value >= min && value <= max;
|
|
289
|
+
/** The page of one list call: `skip` or `after`, and `take` (else `limit`) within `max`. */
|
|
290
|
+
function pageOf(options, defaultTake, max) {
|
|
291
|
+
if (options?.after !== undefined && options.skip !== undefined) {
|
|
292
|
+
throw invalidListOptions('Page with after or with skip, not both.');
|
|
293
|
+
}
|
|
294
|
+
const take = options?.take ?? options?.limit ?? defaultTake;
|
|
295
|
+
const skip = options?.skip ?? (options?.after === undefined ? 0 : readOffsetCursor(options.after));
|
|
296
|
+
if (!isWhole(take, 1, max)) {
|
|
297
|
+
throw invalidListOptions(`take must be a whole number from 1 to ${String(max)}.`);
|
|
298
|
+
}
|
|
299
|
+
if (!isWhole(skip, 0, Number.MAX_SAFE_INTEGER)) {
|
|
300
|
+
throw invalidListOptions('skip must be a whole number of 0 or more.');
|
|
301
|
+
}
|
|
302
|
+
return { skip, take };
|
|
303
|
+
}
|
|
304
|
+
/** A list search as the engine reads it; a term it would ignore is not sent. */
|
|
305
|
+
function searchOf(options) {
|
|
306
|
+
const mode = options?.searchMode;
|
|
307
|
+
if (mode !== undefined && !PRICE_LIST_SEARCH_MODES.includes(mode)) {
|
|
308
|
+
throw invalidListOptions(`searchMode must be one of ${PRICE_LIST_SEARCH_MODES.join(', ')}.`);
|
|
309
|
+
}
|
|
310
|
+
const search = options?.search;
|
|
311
|
+
if (search === undefined)
|
|
312
|
+
return {};
|
|
313
|
+
if (typeof search !== 'string')
|
|
314
|
+
throw invalidListOptions('search must be a string.');
|
|
315
|
+
const term = search.trim();
|
|
316
|
+
if (term.length < PRICE_LIST_SEARCH_MIN_TERM_LENGTH)
|
|
317
|
+
return {};
|
|
318
|
+
return { search: term, ...(mode === undefined ? {} : { searchMode: mode }) };
|
|
319
|
+
}
|
|
320
|
+
/** An `assignedTo` filter as the engine reads it, or nothing; exactly one target, else refused. */
|
|
321
|
+
function assignedToOf(options) {
|
|
322
|
+
const assignedTo = options?.assignedTo;
|
|
323
|
+
if (assignedTo === undefined)
|
|
324
|
+
return {};
|
|
325
|
+
const refused = () => invalidListOptions('assignedTo names exactly one of customerId and customerGroupId.');
|
|
326
|
+
if (!isObject(assignedTo))
|
|
327
|
+
throw refused();
|
|
328
|
+
const { customerId, customerGroupId, includeChannelWide } = assignedTo;
|
|
329
|
+
const hasCustomer = customerId !== undefined && customerId !== null;
|
|
330
|
+
const hasGroup = customerGroupId !== undefined && customerGroupId !== null;
|
|
331
|
+
if (hasCustomer === hasGroup)
|
|
332
|
+
throw refused();
|
|
333
|
+
const id = hasCustomer ? customerId : customerGroupId;
|
|
334
|
+
if (typeof id !== 'string' || id === '')
|
|
335
|
+
throw refused();
|
|
336
|
+
if (includeChannelWide !== undefined && typeof includeChannelWide !== 'boolean') {
|
|
337
|
+
throw invalidListOptions('assignedTo.includeChannelWide must be true or false.');
|
|
338
|
+
}
|
|
339
|
+
return {
|
|
340
|
+
filter: {
|
|
341
|
+
assignedTo: {
|
|
342
|
+
...(hasCustomer ? { customerId: id } : { customerGroupId: id }),
|
|
343
|
+
...(includeChannelWide === undefined ? {} : { includeChannelWide }),
|
|
344
|
+
},
|
|
345
|
+
},
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
/** An entries filter as the engine reads it; anything else is refused before it is sent. */
|
|
349
|
+
function entryFilterOf(filter) {
|
|
350
|
+
if (filter === undefined)
|
|
351
|
+
return {};
|
|
352
|
+
const out = {};
|
|
353
|
+
const { targetType, search, searchMode, collectionIds, facetValueIds } = filter;
|
|
354
|
+
if (targetType !== undefined) {
|
|
355
|
+
if (!['variant', 'product', 'collection', 'facet_value'].includes(targetType)) {
|
|
356
|
+
throw invalidListOptions('targetType must be variant, product, collection or facet_value.');
|
|
357
|
+
}
|
|
358
|
+
out.targetType = targetType;
|
|
359
|
+
}
|
|
360
|
+
if (searchMode !== undefined) {
|
|
361
|
+
if (!PRICE_LIST_ENTRY_SEARCH_MODES.includes(searchMode)) {
|
|
362
|
+
throw invalidListOptions(`searchMode must be one of ${PRICE_LIST_ENTRY_SEARCH_MODES.join(', ')}.`);
|
|
363
|
+
}
|
|
364
|
+
out.searchMode = searchMode;
|
|
365
|
+
}
|
|
366
|
+
if (search !== undefined) {
|
|
367
|
+
if (typeof search !== 'string')
|
|
368
|
+
throw invalidListOptions('search must be a string.');
|
|
369
|
+
if (search.trim() !== '')
|
|
370
|
+
out.search = search.trim();
|
|
371
|
+
}
|
|
372
|
+
for (const [key, ids] of [
|
|
373
|
+
['collectionIds', collectionIds],
|
|
374
|
+
['facetValueIds', facetValueIds],
|
|
375
|
+
]) {
|
|
376
|
+
if (ids === undefined)
|
|
377
|
+
continue;
|
|
378
|
+
if (!isIdList(ids))
|
|
379
|
+
throw invalidListOptions(`${key} must be a list of strings.`);
|
|
380
|
+
if (ids.length > 0)
|
|
381
|
+
out[key] = [...ids];
|
|
382
|
+
}
|
|
383
|
+
return out;
|
|
384
|
+
}
|
|
385
|
+
async function listPage(transport, skip, take, search, options) {
|
|
386
|
+
const redaction = redactionWatcher();
|
|
387
|
+
const data = await transport.graphql({
|
|
388
|
+
api: 'admin',
|
|
389
|
+
query: PRICE_LISTS_QUERY,
|
|
390
|
+
variables: { options: { skip, take, ...search } },
|
|
391
|
+
onExtensions: redaction.onExtensions,
|
|
392
|
+
}, options);
|
|
393
|
+
return pageFromVendureList({
|
|
394
|
+
items: data.priceLists.items.map((item) => redaction.mark({ ...item })),
|
|
395
|
+
totalItems: data.priceLists.totalItems,
|
|
396
|
+
}, skip, take,
|
|
397
|
+
// Each following page re-sends the same search and per-call options.
|
|
398
|
+
(cursor) => listPage(transport, readOffsetCursor(cursor), take, search, options));
|
|
399
|
+
}
|
|
400
|
+
async function entriesPage(transport, priceListId, skip, take, filter, options) {
|
|
401
|
+
const data = await fieldOf(transport, PRICE_LIST_ENTRIES_QUERY, 'priceListEntries', { priceListId, options: { skip, take, ...filter } }, options);
|
|
402
|
+
return pageFromVendureList(data, skip, take, (cursor) => entriesPage(transport, priceListId, readOffsetCursor(cursor), take, filter, options));
|
|
403
|
+
}
|
|
404
|
+
/** A list read with its customers' names: each named customer is marked when withheld. */
|
|
405
|
+
async function readList(transport, query, field, variables, options) {
|
|
406
|
+
const redaction = redactionWatcher();
|
|
407
|
+
const data = await transport.graphql({ api: 'admin', query, variables, onExtensions: redaction.onExtensions }, options);
|
|
408
|
+
const list = data[field] ?? null;
|
|
409
|
+
if (list === null)
|
|
410
|
+
return null;
|
|
411
|
+
return { ...list, assignments: list.assignments.map((row) => markCustomer(row, redaction)) };
|
|
412
|
+
}
|
|
413
|
+
function markCustomer(assignment, redaction) {
|
|
414
|
+
return assignment.customer === null
|
|
415
|
+
? assignment
|
|
416
|
+
: {
|
|
417
|
+
...assignment,
|
|
418
|
+
customer: redaction.mark({ ...assignment.customer }),
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
/** The largest value GraphQL's `Int` carries; the smallest is its negation minus one. */
|
|
422
|
+
const GRAPHQL_INT_MAX = 2_147_483_647;
|
|
423
|
+
/**
|
|
424
|
+
* An order rule's two values as the wire can carry them. `SetPriceListOrderRuleInput` types them
|
|
425
|
+
* `Int!`, so a fraction or a value outside 32 bits is answered by GraphQL's variable coercion with
|
|
426
|
+
* an uncoded `BAD_USER_INPUT` before the engine sees the rule, and the engine's own refusal (which
|
|
427
|
+
* names the field) never comes. The SDK refuses exactly those shapes the way the engine would,
|
|
428
|
+
* `PRICE_LIST_ORDER_RULE_INVALID` with a detail per field, before anything is sent. Every other
|
|
429
|
+
* rule (types, bounds, a discount below 1) stays the engine's.
|
|
430
|
+
*/
|
|
431
|
+
function assertRuleValuesFitTheWire(input) {
|
|
432
|
+
const problems = [];
|
|
433
|
+
for (const field of ['conditionValue', 'discountValue']) {
|
|
434
|
+
const value = isObject(input) ? input[field] : undefined;
|
|
435
|
+
if (typeof value !== 'number' || !Number.isInteger(value)) {
|
|
436
|
+
problems.push({
|
|
437
|
+
field,
|
|
438
|
+
code: 'VALUE_NOT_AN_INTEGER',
|
|
439
|
+
message: `${field} must be a whole number`,
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
else if (value > GRAPHQL_INT_MAX) {
|
|
443
|
+
problems.push({
|
|
444
|
+
field,
|
|
445
|
+
code: 'VALUE_TOO_LARGE',
|
|
446
|
+
message: `${field} must not exceed ${String(GRAPHQL_INT_MAX)}`,
|
|
447
|
+
});
|
|
448
|
+
}
|
|
449
|
+
else if (value < -GRAPHQL_INT_MAX - 1) {
|
|
450
|
+
problems.push(field === 'conditionValue'
|
|
451
|
+
? { field, code: 'VALUE_NEGATIVE', message: 'conditionValue must not be negative' }
|
|
452
|
+
: {
|
|
453
|
+
field,
|
|
454
|
+
code: 'DISCOUNT_NOT_POSITIVE',
|
|
455
|
+
message: 'discountValue must be at least 1: a rule must take something off',
|
|
456
|
+
});
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
const first = problems[0];
|
|
460
|
+
if (first === undefined)
|
|
461
|
+
return;
|
|
462
|
+
throw new ValidationError({
|
|
463
|
+
code: 'PRICE_LIST_ORDER_RULE_INVALID',
|
|
464
|
+
message: first.message,
|
|
465
|
+
status: 400,
|
|
466
|
+
service: 'vendure',
|
|
467
|
+
details: problems,
|
|
468
|
+
});
|
|
469
|
+
}
|
|
470
|
+
/** The selection of a bulk call, checked as the engine checks it, before anything is sent. */
|
|
471
|
+
function bulkVariables(input) {
|
|
472
|
+
const refused = (message) => invalid('BULK_ACTION_INPUT_INVALID', message);
|
|
473
|
+
if (!isObject(input))
|
|
474
|
+
throw refused('A bulk action needs an action and priceListIds.');
|
|
475
|
+
if (!PRICE_LIST_BULK_ACTIONS.includes(input.action)) {
|
|
476
|
+
throw refused(`action must be one of ${PRICE_LIST_BULK_ACTIONS.join(', ')}.`);
|
|
477
|
+
}
|
|
478
|
+
if (!isIdList(input.priceListIds))
|
|
479
|
+
throw refused('priceListIds must be a list of strings.');
|
|
480
|
+
const ids = input.priceListIds;
|
|
481
|
+
if (ids.length === 0)
|
|
482
|
+
throw refused('Name at least one price list.');
|
|
483
|
+
if (ids.length > MAX_BULK_PRICE_LISTS) {
|
|
484
|
+
throw refused(`A bulk action takes at most ${String(MAX_BULK_PRICE_LISTS)} price lists.`);
|
|
485
|
+
}
|
|
486
|
+
if (new Set(ids.map((id) => id.toLowerCase())).size !== ids.length) {
|
|
487
|
+
throw refused('Name each price list once.');
|
|
488
|
+
}
|
|
489
|
+
return { input: { action: input.action, priceListIds: [...ids] } };
|
|
490
|
+
}
|
|
491
|
+
export function createPricingPriceListsModule(transport) {
|
|
492
|
+
const entries = {
|
|
493
|
+
// `async`: a refused option rejects the promise, as an engine refusal does.
|
|
494
|
+
async list(priceListId, options) {
|
|
495
|
+
const { skip, take } = pageOf(options, DEFAULT_ENTRY_TAKE, MAX_PRICE_LIST_ENTRY_TAKE);
|
|
496
|
+
return entriesPage(transport, priceListId, skip, take, entryFilterOf(options), options);
|
|
497
|
+
},
|
|
498
|
+
async export(priceListId, filter, options) {
|
|
499
|
+
const data = await fieldOf(transport, PRICE_LIST_ENTRIES_QUERY, 'priceListEntries', { priceListId, options: { ...entryFilterOf(filter), export: true } }, options);
|
|
500
|
+
return { items: data.items, total: data.totalItems };
|
|
501
|
+
},
|
|
502
|
+
async facets(priceListId, search, options) {
|
|
503
|
+
const filter = entryFilterOf({ search: search?.search, searchMode: search?.searchMode });
|
|
504
|
+
return fieldOf(transport, PRICE_LIST_ENTRY_FACETS_QUERY, 'priceListEntryFacets', { priceListId, search: filter.search ?? null, searchMode: filter.searchMode ?? null }, options);
|
|
505
|
+
},
|
|
506
|
+
set(priceListId, input, options) {
|
|
507
|
+
return fieldOf(transport, SET_PRICE_LIST_ENTRY_MUTATION, 'setPriceListEntry', { priceListId, input }, options);
|
|
508
|
+
},
|
|
509
|
+
bulkSet(priceListId, inputs, options) {
|
|
510
|
+
return fieldOf(transport, BULK_SET_PRICE_LIST_ENTRIES_MUTATION, 'bulkSetPriceListEntries', { priceListId, entries: [...inputs] }, options);
|
|
511
|
+
},
|
|
512
|
+
async remove(entryId, options) {
|
|
513
|
+
const response = await fieldOf(transport, REMOVE_PRICE_LIST_ENTRY_MUTATION, 'removePriceListEntry', { id: entryId }, options);
|
|
514
|
+
assertDeleted('PRICE_LIST_ENTRY_NOT_DELETED', [response]);
|
|
515
|
+
},
|
|
516
|
+
};
|
|
517
|
+
const orderRules = {
|
|
518
|
+
list(priceListId, options) {
|
|
519
|
+
return fieldOf(transport, PRICE_LIST_ORDER_RULES_QUERY, 'priceListOrderRules', { priceListId }, options);
|
|
520
|
+
},
|
|
521
|
+
async set(priceListId, input, options) {
|
|
522
|
+
assertRuleValuesFitTheWire(input);
|
|
523
|
+
return fieldOf(transport, SET_PRICE_LIST_ORDER_RULE_MUTATION, 'setPriceListOrderRule', { priceListId, input }, options);
|
|
524
|
+
},
|
|
525
|
+
async remove(ruleId, options) {
|
|
526
|
+
const response = await fieldOf(transport, REMOVE_PRICE_LIST_ORDER_RULE_MUTATION, 'removePriceListOrderRule', { id: ruleId }, options);
|
|
527
|
+
assertDeleted('PRICE_LIST_ORDER_RULE_NOT_DELETED', [response]);
|
|
528
|
+
},
|
|
529
|
+
};
|
|
530
|
+
return {
|
|
531
|
+
async list(options) {
|
|
532
|
+
const { skip, take } = pageOf(options, DEFAULT_LIST_TAKE, MAX_PRICE_LIST_LIST_TAKE);
|
|
533
|
+
// The filter is sent only when asked for: an engine without it refuses the whole document.
|
|
534
|
+
const narrowed = { ...searchOf(options), ...assignedToOf(options) };
|
|
535
|
+
return listPage(transport, skip, take, narrowed, options);
|
|
536
|
+
},
|
|
537
|
+
async forCustomer(customerId, options) {
|
|
538
|
+
const { entriesTake, ...call } = options ?? {};
|
|
539
|
+
if (entriesTake !== undefined && !isWhole(entriesTake, 1, MAX_CUSTOMER_PRICE_LIST_ENTRIES)) {
|
|
540
|
+
throw invalidListOptions(`entriesTake must be a whole number from 1 to ${String(MAX_CUSTOMER_PRICE_LIST_ENTRIES)}.`);
|
|
541
|
+
}
|
|
542
|
+
const rows = await fieldOf(transport, CUSTOMER_PRICE_LISTS_QUERY, 'customerPriceLists', { customerId, options: entriesTake === undefined ? null : { entriesTake } }, call);
|
|
543
|
+
return rows.map((row) => ({
|
|
544
|
+
priceList: row.priceList,
|
|
545
|
+
assignmentType: row.assignmentType,
|
|
546
|
+
entries: { items: row.entries.items, total: row.entries.totalItems },
|
|
547
|
+
orderRules: row.orderRules,
|
|
548
|
+
}));
|
|
549
|
+
},
|
|
550
|
+
get(id, options) {
|
|
551
|
+
return readList(transport, PRICE_LIST_QUERY, 'priceList', { id }, options);
|
|
552
|
+
},
|
|
553
|
+
async create(input, options) {
|
|
554
|
+
if (!isObject(input) || typeof input.name !== 'string') {
|
|
555
|
+
throw invalid('INVALID_PRICE_LIST_INPUT', 'A price list needs a name.');
|
|
556
|
+
}
|
|
557
|
+
const list = await readList(transport, CREATE_PRICE_LIST_MUTATION, 'createPriceList', { input }, options);
|
|
558
|
+
return list;
|
|
559
|
+
},
|
|
560
|
+
async update(id, input, options) {
|
|
561
|
+
if (!isObject(input))
|
|
562
|
+
throw invalid('INVALID_PRICE_LIST_INPUT', 'input must be an object.');
|
|
563
|
+
const list = await readList(transport, UPDATE_PRICE_LIST_MUTATION, 'updatePriceList', { id, input }, options);
|
|
564
|
+
return list;
|
|
565
|
+
},
|
|
566
|
+
async delete(id, options) {
|
|
567
|
+
const response = await fieldOf(transport, DELETE_PRICE_LIST_MUTATION, 'deletePriceList', { id }, options);
|
|
568
|
+
assertDeleted('PRICE_LIST_NOT_DELETED', [response]);
|
|
569
|
+
},
|
|
570
|
+
async assign(priceListId, target, options) {
|
|
571
|
+
const named = isObject(target) ? target : {};
|
|
572
|
+
const groupId = named.customerGroupId;
|
|
573
|
+
const customerId = named.customerId;
|
|
574
|
+
if ((typeof groupId === 'string') === (typeof customerId === 'string')) {
|
|
575
|
+
throw invalid('INVALID_PRICE_LIST_INPUT', 'Assign a price list to exactly one customerGroupId or customerId.');
|
|
576
|
+
}
|
|
577
|
+
const redaction = redactionWatcher();
|
|
578
|
+
const data = await transport.graphql({
|
|
579
|
+
api: 'admin',
|
|
580
|
+
query: ASSIGN_PRICE_LIST_MUTATION,
|
|
581
|
+
variables: {
|
|
582
|
+
priceListId,
|
|
583
|
+
input: typeof groupId === 'string' ? { customerGroupId: groupId } : { customerId },
|
|
584
|
+
},
|
|
585
|
+
onExtensions: redaction.onExtensions,
|
|
586
|
+
}, options);
|
|
587
|
+
return markCustomer(data.assignPriceList, redaction);
|
|
588
|
+
},
|
|
589
|
+
async unassign(assignmentId, options) {
|
|
590
|
+
const response = await fieldOf(transport, UNASSIGN_PRICE_LIST_MUTATION, 'unassignPriceList', { assignmentId }, options);
|
|
591
|
+
assertDeleted('PRICE_LIST_ASSIGNMENT_NOT_DELETED', [response]);
|
|
592
|
+
},
|
|
593
|
+
async bulk(input, options) {
|
|
594
|
+
const data = await fieldOf(transport, BULK_PRICE_LIST_ACTION_MUTATION, 'bulkPriceListAction', bulkVariables(input), options);
|
|
595
|
+
return data.items;
|
|
596
|
+
},
|
|
597
|
+
entries,
|
|
598
|
+
orderRules,
|
|
599
|
+
};
|
|
600
|
+
}
|