@sixtyninecommerce/admin 0.11.0 → 0.13.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.
Files changed (153) hide show
  1. package/README.md +1509 -28
  2. package/dist/auth/index.d.ts +7 -1
  3. package/dist/auth/index.js +8 -1
  4. package/dist/catalog/documents.d.ts +142 -0
  5. package/dist/catalog/documents.js +114 -0
  6. package/dist/catalog/index.d.ts +48 -7
  7. package/dist/catalog/index.js +6 -1
  8. package/dist/catalog/metadata.d.ts +244 -0
  9. package/dist/catalog/metadata.js +220 -0
  10. package/dist/catalog/properties.d.ts +38 -0
  11. package/dist/catalog/properties.js +52 -1
  12. package/dist/catalog/recommendations.d.ts +33 -6
  13. package/dist/catalog/recommendations.js +94 -2
  14. package/dist/catalog/reviews.d.ts +17 -4
  15. package/dist/catalog/reviews.js +7 -0
  16. package/dist/catalog/variants.d.ts +60 -0
  17. package/dist/catalog/variants.js +39 -0
  18. package/dist/catalog/visibility.d.ts +18 -2
  19. package/dist/client.d.ts +29 -3
  20. package/dist/client.js +19 -8
  21. package/dist/content/assets.d.ts +3 -4
  22. package/dist/content/forms.d.ts +332 -1
  23. package/dist/content/forms.js +140 -4
  24. package/dist/content/items.d.ts +31 -8
  25. package/dist/content/items.js +1 -1
  26. package/dist/content/menus.d.ts +254 -0
  27. package/dist/content/menus.js +70 -0
  28. package/dist/content/strings.d.ts +137 -0
  29. package/dist/content/strings.js +82 -0
  30. package/dist/customers/delivery-terms.d.ts +44 -0
  31. package/dist/customers/delivery-terms.js +32 -0
  32. package/dist/customers/groups.d.ts +16 -5
  33. package/dist/customers/groups.js +6 -2
  34. package/dist/customers/index.d.ts +15 -0
  35. package/dist/customers/index.js +4 -0
  36. package/dist/documents/emails.d.ts +154 -0
  37. package/dist/documents/emails.js +220 -0
  38. package/dist/documents/index.d.ts +13 -0
  39. package/dist/documents/index.js +2 -0
  40. package/dist/fulfillments/create.d.ts +1 -1
  41. package/dist/fulfillments/create.js +5 -2
  42. package/dist/fulfillments/index.d.ts +119 -1
  43. package/dist/fulfillments/index.js +124 -4
  44. package/dist/fulfillments/warehouse.d.ts +29 -17
  45. package/dist/fulfillments/warehouse.js +32 -1
  46. package/dist/index.d.ts +43 -23
  47. package/dist/index.js +11 -5
  48. package/dist/integrations/exact-online-types.d.ts +542 -0
  49. package/dist/integrations/exact-online.d.ts +541 -5
  50. package/dist/integrations/exact-online.js +714 -1
  51. package/dist/integrations/index.d.ts +23 -8
  52. package/dist/integrations/index.js +9 -4
  53. package/dist/integrations/pipeline-discoveries.d.ts +65 -0
  54. package/dist/integrations/pipeline-discoveries.js +65 -0
  55. package/dist/integrations/pipeline-runs.d.ts +119 -0
  56. package/dist/integrations/pipeline-runs.js +87 -0
  57. package/dist/integrations/pipelines.d.ts +73 -2
  58. package/dist/integrations/pipelines.js +36 -1
  59. package/dist/integrations/price-import.d.ts +223 -0
  60. package/dist/integrations/price-import.js +124 -0
  61. package/dist/integrations/types.d.ts +221 -3
  62. package/dist/integrations/types.js +4 -3
  63. package/dist/meta/index.d.ts +10 -0
  64. package/dist/metafields/index.d.ts +8 -1
  65. package/dist/metafields/index.js +10 -0
  66. package/dist/orders/cancel-with-money.d.ts +10 -0
  67. package/dist/orders/cancel-with-money.js +2 -0
  68. package/dist/orders/commit-edit.d.ts +16 -0
  69. package/dist/orders/commit-edit.js +14 -1
  70. package/dist/orders/deposit-policy.d.ts +3 -1
  71. package/dist/orders/index.d.ts +22 -11
  72. package/dist/orders/index.js +5 -2
  73. package/dist/orders/list.d.ts +26 -5
  74. package/dist/orders/list.js +123 -13
  75. package/dist/orders/manual-discounts.d.ts +13 -5
  76. package/dist/orders/money-view.d.ts +149 -0
  77. package/dist/orders/money-view.js +99 -0
  78. package/dist/orders/plan-edit.d.ts +34 -2
  79. package/dist/orders/plan-edit.js +110 -12
  80. package/dist/orders/surcharges.d.ts +12 -0
  81. package/dist/orders/surcharges.js +34 -9
  82. package/dist/payments/account-credit.d.ts +17 -1
  83. package/dist/payments/account-credit.js +25 -2
  84. package/dist/payments/credit-noted-fields.d.ts +8 -1
  85. package/dist/payments/credit-noted-fields.js +34 -2
  86. package/dist/payments/customer-consent.d.ts +30 -0
  87. package/dist/payments/customer-consent.js +49 -0
  88. package/dist/payments/index.d.ts +89 -14
  89. package/dist/payments/index.js +22 -2
  90. package/dist/payments/requests.d.ts +6 -3
  91. package/dist/payments/requests.js +1 -0
  92. package/dist/pickups/index.d.ts +5 -3
  93. package/dist/pickups/index.js +5 -2
  94. package/dist/platform/email-gateways.d.ts +165 -0
  95. package/dist/platform/email-gateways.js +78 -0
  96. package/dist/platform/tax.d.ts +11 -3
  97. package/dist/platform/tax.js +30 -0
  98. package/dist/pos/exchanges.d.ts +6 -1
  99. package/dist/pos/index.d.ts +229 -10
  100. package/dist/pos/index.js +113 -4
  101. package/dist/pos/offline-sales.d.ts +44 -3
  102. package/dist/pos/offline-sales.js +57 -3
  103. package/dist/pos/returns.d.ts +28 -1
  104. package/dist/pos/returns.js +36 -6
  105. package/dist/pricing/price-lists.d.ts +3 -1
  106. package/dist/purchasing/alerts.d.ts +19 -4
  107. package/dist/purchasing/alerts.js +29 -3
  108. package/dist/purchasing/backorders.d.ts +61 -0
  109. package/dist/purchasing/backorders.js +92 -0
  110. package/dist/purchasing/index.d.ts +21 -3
  111. package/dist/purchasing/index.js +8 -0
  112. package/dist/purchasing/links.d.ts +263 -0
  113. package/dist/purchasing/links.js +192 -0
  114. package/dist/purchasing/purchase-orders.d.ts +4 -1
  115. package/dist/purchasing/receipts.d.ts +396 -0
  116. package/dist/purchasing/receipts.js +287 -0
  117. package/dist/purchasing/returns.d.ts +273 -0
  118. package/dist/purchasing/returns.js +229 -0
  119. package/dist/quotes/index.d.ts +20 -3
  120. package/dist/returns/index.d.ts +62 -1
  121. package/dist/returns/index.js +61 -0
  122. package/dist/seo/config.d.ts +128 -0
  123. package/dist/seo/config.js +95 -0
  124. package/dist/seo/redirects.d.ts +125 -0
  125. package/dist/seo/redirects.js +60 -1
  126. package/dist/seo/settings.d.ts +147 -0
  127. package/dist/seo/settings.js +209 -0
  128. package/dist/shipping/index.d.ts +6 -0
  129. package/dist/shipping/index.js +2 -0
  130. package/dist/shipping/methods.d.ts +8 -1
  131. package/dist/shipping/myparcel.d.ts +415 -1
  132. package/dist/shipping/myparcel.js +291 -3
  133. package/dist/shipping/rate-tables.d.ts +149 -4
  134. package/dist/shipping/rate-tables.js +229 -23
  135. package/dist/shipping/ship-to-countries.d.ts +75 -0
  136. package/dist/shipping/ship-to-countries.js +47 -0
  137. package/dist/stock/by-location.d.ts +86 -0
  138. package/dist/stock/by-location.js +94 -0
  139. package/dist/stock/index.d.ts +8 -1
  140. package/dist/stock/index.js +4 -0
  141. package/dist/stock/settings.d.ts +106 -0
  142. package/dist/stock/settings.js +103 -0
  143. package/dist/stock/transfers.d.ts +171 -10
  144. package/dist/stock/transfers.js +82 -4
  145. package/dist/stored-value/index.d.ts +98 -7
  146. package/dist/stored-value/index.js +71 -4
  147. package/dist/tax/display-policy.d.ts +105 -0
  148. package/dist/tax/display-policy.js +84 -0
  149. package/dist/tax/index.d.ts +3 -0
  150. package/dist/tax/index.js +2 -0
  151. package/dist/tenant/email-templates.d.ts +222 -1
  152. package/dist/tenant/email-templates.js +227 -10
  153. package/package.json +2 -2
@@ -0,0 +1,165 @@
1
+ import type { Transport } from '@sixtyninecommerce/core';
2
+ import type { AdminClientConfig } from '../client.js';
3
+ import type { Call } from '../types.js';
4
+ /** How a gateway sends: an SMTP server, or the sixtynine.email HTTP API. Fixed at creation. */
5
+ export type EmailGatewayType = 'smtp' | 'sixtynine_email';
6
+ /**
7
+ * A store's email gateway: the way its transactional email leaves the platform. One per store at
8
+ * most; a store without one sends through the platform's default sender.
9
+ *
10
+ * Credentials (the SMTP password, the sixtynine.email API key) are write-only and never read back.
11
+ */
12
+ export interface EmailGateway {
13
+ id: string;
14
+ /** The store (channel id) it sends for. */
15
+ channelId: string;
16
+ type: EmailGatewayType;
17
+ name: string;
18
+ /** SMTP only; `null` on a sixtynine.email gateway. */
19
+ host: string | null;
20
+ /** SMTP only; `null` on a sixtynine.email gateway. */
21
+ port: number | null;
22
+ /** SMTP only; `null` for an unauthenticated relay or a sixtynine.email gateway. */
23
+ username: string | null;
24
+ fromAddress: string;
25
+ fromName: string;
26
+ /**
27
+ * SMTP only: `tls` (TLS from the first byte, usually port 465), `starttls` (a plain connection
28
+ * that must upgrade to TLS, usually port 587) or `none`. `null` on a sixtynine.email gateway.
29
+ */
30
+ encryption: string | null;
31
+ /** sixtynine.email only: the API's address, such as `https://sixtynine.email`. */
32
+ apiUrl: string | null;
33
+ /** A disabled gateway is skipped: its store sends through the platform's default sender. */
34
+ enabled: boolean;
35
+ /** ISO-8601 UTC. */
36
+ createdAt: string;
37
+ /** ISO-8601 UTC. */
38
+ updatedAt: string;
39
+ }
40
+ /**
41
+ * A new gateway. `type` decides which fields apply: an SMTP gateway (the default) needs `host` and
42
+ * `password`; a sixtynine.email gateway needs `apiKey`. The other type's fields are ignored.
43
+ */
44
+ export interface CreateEmailGatewayInput {
45
+ channelId: string;
46
+ /** Default `smtp`. */
47
+ type?: EmailGatewayType;
48
+ name: string;
49
+ host?: string;
50
+ /** 1–65535; default 587. */
51
+ port?: number;
52
+ username?: string;
53
+ /** Write-only. */
54
+ password?: string;
55
+ /**
56
+ * `tls`, `starttls` or `none`. Left out: `tls` on port 465, `starttls` on any other. Any other
57
+ * letter case of `TLS`/`STARTTLS` is stored by the port (465 → `tls`, else `starttls`).
58
+ */
59
+ encryption?: string;
60
+ /**
61
+ * An https:// address on one of the platform's sixtynine.email hosts (such as
62
+ * `https://sixtynine.email`), with no user name, query or fragment: the API key is sent there.
63
+ * Left out, none is stored and the gateway sends nothing until one is set.
64
+ */
65
+ apiUrl?: string;
66
+ /** Write-only. */
67
+ apiKey?: string;
68
+ fromAddress: string;
69
+ fromName: string;
70
+ }
71
+ /**
72
+ * A change to a gateway. A field left out keeps what the gateway has; `password` and `apiKey`
73
+ * left out or empty keep the stored credential. The type cannot change: the SMTP fields are
74
+ * ignored on a sixtynine.email gateway, and `apiUrl`/`apiKey` on an SMTP one.
75
+ */
76
+ export interface UpdateEmailGatewayInput {
77
+ /** Moves the gateway to another store. */
78
+ channelId?: string;
79
+ name?: string;
80
+ host?: string;
81
+ port?: number;
82
+ /** `null` clears it (an unauthenticated relay). */
83
+ username?: string | null;
84
+ password?: string;
85
+ encryption?: string;
86
+ /** `null` clears it: the gateway then sends nothing. */
87
+ apiUrl?: string | null;
88
+ apiKey?: string;
89
+ fromAddress?: string;
90
+ fromName?: string;
91
+ enabled?: boolean;
92
+ }
93
+ /**
94
+ * `admin.platform.emailGateways` — the stores' email gateways, for platform staff (spec §9.2
95
+ * `platform`). Gateways of every store, so no tenant is needed on the client.
96
+ *
97
+ * Every method needs a human SuperAdmin session: an API token or an app credential is refused
98
+ * with `PLATFORM_REQUIRES_SESSION` (`PermissionDeniedError`, 403) BEFORE anything is sent, and a
99
+ * session without SuperAdmin is refused by the engine (`FORBIDDEN`).
100
+ *
101
+ * **Coded refusals** (GraphQL errors mapped by `core`):
102
+ * - `EMAIL_GATEWAY_INPUT_INVALID` → `ValidationError` (400): a value the gateway cannot use. Every
103
+ * problem of one save comes back at once in `details[]` (`field` names the input property, e.g.
104
+ * `apiUrl`), and no refusal repeats a value it was sent (a password put in the wrong field
105
+ * never comes back).
106
+ * - `EMAIL_GATEWAY_NOT_FOUND` → `NotFoundError` (404): no gateway has that id.
107
+ * - `EMAIL_GATEWAY_CHANNEL_TAKEN` → `ConflictError` (409): the store already has a gateway.
108
+ * - `ORGANIZATION_WORKSPACE_NOT_A_STORE` → `BusinessRuleError` (422): a workspace's channel is
109
+ * not a store; moving a gateway there, enabling one there or storing a credential on it is
110
+ * refused.
111
+ * - `EMAIL_GATEWAY_TEST_FAILED` → `BusinessRuleError` (422): see {@link test}.
112
+ * - `EMAIL_GATEWAY_TEST_NOT_SUPPORTED` → `BusinessRuleError` (422): see {@link test}.
113
+ */
114
+ export interface PlatformEmailGatewaysModule {
115
+ /**
116
+ * Every store's gateway, newest first.
117
+ *
118
+ * @effect read
119
+ */
120
+ list(options?: Call): Promise<EmailGateway[]>;
121
+ /**
122
+ * One gateway, or `null` when no gateway has that id.
123
+ *
124
+ * @effect read
125
+ */
126
+ get(id: string, options?: Call): Promise<EmailGateway | null>;
127
+ /**
128
+ * Create a store's gateway, enabled, and answer it as read.
129
+ *
130
+ * @effect write
131
+ */
132
+ create(input: CreateEmailGatewayInput, options?: Call): Promise<EmailGateway>;
133
+ /**
134
+ * Change a gateway, and answer it as read.
135
+ *
136
+ * @effect write
137
+ */
138
+ update(id: string, patch: UpdateEmailGatewayInput, options?: Call): Promise<EmailGateway>;
139
+ /**
140
+ * Delete a gateway. Its store then sends through the platform's default sender.
141
+ *
142
+ * @effect destructive
143
+ */
144
+ delete(id: string, options?: Call): Promise<void>;
145
+ /**
146
+ * Check that an SMTP gateway's server takes a connection with its stored settings and
147
+ * credentials. No email is sent. Resolves when the check passed.
148
+ *
149
+ * - `EMAIL_GATEWAY_TEST_FAILED`: the server refused or could not be reached. The message names
150
+ * the step that failed (TLS handshake, user name or password, host name, timeout,
151
+ * connection), never the server's own reply.
152
+ * - `EMAIL_GATEWAY_TEST_NOT_SUPPORTED`: a sixtynine.email gateway; the check is SMTP-only.
153
+ * - `EMAIL_GATEWAY_NOT_FOUND`: no gateway has that id.
154
+ *
155
+ * @effect read
156
+ */
157
+ test(id: string, options?: Call): Promise<void>;
158
+ }
159
+ export declare const EMAIL_GATEWAYS_QUERY = "\n query PlatformEmailGateways {\n emailGateways { \n id\n channelId\n type\n name\n host\n port\n username\n fromAddress\n fromName\n encryption\n apiUrl\n enabled\n createdAt\n updatedAt\n }\n }\n";
160
+ export declare const EMAIL_GATEWAY_QUERY = "\n query PlatformEmailGateway($id: ID!) {\n emailGateway(id: $id) { \n id\n channelId\n type\n name\n host\n port\n username\n fromAddress\n fromName\n encryption\n apiUrl\n enabled\n createdAt\n updatedAt\n }\n }\n";
161
+ export declare const CREATE_EMAIL_GATEWAY_MUTATION = "\n mutation PlatformCreateEmailGateway($input: CreateEmailGatewayInput!) {\n createEmailGateway(input: $input) { \n id\n channelId\n type\n name\n host\n port\n username\n fromAddress\n fromName\n encryption\n apiUrl\n enabled\n createdAt\n updatedAt\n }\n }\n";
162
+ export declare const UPDATE_EMAIL_GATEWAY_MUTATION = "\n mutation PlatformUpdateEmailGateway($id: ID!, $input: UpdateEmailGatewayInput!) {\n updateEmailGateway(id: $id, input: $input) { \n id\n channelId\n type\n name\n host\n port\n username\n fromAddress\n fromName\n encryption\n apiUrl\n enabled\n createdAt\n updatedAt\n }\n }\n";
163
+ export declare const DELETE_EMAIL_GATEWAY_MUTATION = "\n mutation PlatformDeleteEmailGateway($id: ID!) {\n deleteEmailGateway(id: $id) { result }\n }\n";
164
+ export declare const TEST_EMAIL_GATEWAY_MUTATION = "\n mutation PlatformTestEmailGateway($id: ID!) {\n testEmailGateway(id: $id)\n }\n";
165
+ export declare function createPlatformEmailGatewaysModule(transport: Transport, config: AdminClientConfig): PlatformEmailGatewaysModule;
@@ -0,0 +1,78 @@
1
+ import { fieldOf } from '../catalog/types.js';
2
+ import { requireHumanSession } from './guard.js';
3
+ const GATEWAY_FIELDS = /* GraphQL */ `
4
+ id
5
+ channelId
6
+ type
7
+ name
8
+ host
9
+ port
10
+ username
11
+ fromAddress
12
+ fromName
13
+ encryption
14
+ apiUrl
15
+ enabled
16
+ createdAt
17
+ updatedAt
18
+ `;
19
+ export const EMAIL_GATEWAYS_QUERY = /* GraphQL */ `
20
+ query PlatformEmailGateways {
21
+ emailGateways { ${GATEWAY_FIELDS} }
22
+ }
23
+ `;
24
+ export const EMAIL_GATEWAY_QUERY = /* GraphQL */ `
25
+ query PlatformEmailGateway($id: ID!) {
26
+ emailGateway(id: $id) { ${GATEWAY_FIELDS} }
27
+ }
28
+ `;
29
+ export const CREATE_EMAIL_GATEWAY_MUTATION = /* GraphQL */ `
30
+ mutation PlatformCreateEmailGateway($input: CreateEmailGatewayInput!) {
31
+ createEmailGateway(input: $input) { ${GATEWAY_FIELDS} }
32
+ }
33
+ `;
34
+ export const UPDATE_EMAIL_GATEWAY_MUTATION = /* GraphQL */ `
35
+ mutation PlatformUpdateEmailGateway($id: ID!, $input: UpdateEmailGatewayInput!) {
36
+ updateEmailGateway(id: $id, input: $input) { ${GATEWAY_FIELDS} }
37
+ }
38
+ `;
39
+ export const DELETE_EMAIL_GATEWAY_MUTATION = /* GraphQL */ `
40
+ mutation PlatformDeleteEmailGateway($id: ID!) {
41
+ deleteEmailGateway(id: $id) { result }
42
+ }
43
+ `;
44
+ export const TEST_EMAIL_GATEWAY_MUTATION = /* GraphQL */ `
45
+ mutation PlatformTestEmailGateway($id: ID!) {
46
+ testEmailGateway(id: $id)
47
+ }
48
+ `;
49
+ export function createPlatformEmailGatewaysModule(transport, config) {
50
+ // Gateways of every store: no store is involved, so no tenant is needed (as `platform.tax`).
51
+ const unscoped = (call) => ({ ...call, allowUnscoped: true });
52
+ return {
53
+ async list(options) {
54
+ requireHumanSession(config);
55
+ return fieldOf(transport, EMAIL_GATEWAYS_QUERY, 'emailGateways', {}, unscoped(options));
56
+ },
57
+ async get(id, options) {
58
+ requireHumanSession(config);
59
+ return fieldOf(transport, EMAIL_GATEWAY_QUERY, 'emailGateway', { id }, unscoped(options));
60
+ },
61
+ async create(input, options) {
62
+ requireHumanSession(config);
63
+ return fieldOf(transport, CREATE_EMAIL_GATEWAY_MUTATION, 'createEmailGateway', { input }, unscoped(options));
64
+ },
65
+ async update(id, patch, options) {
66
+ requireHumanSession(config);
67
+ return fieldOf(transport, UPDATE_EMAIL_GATEWAY_MUTATION, 'updateEmailGateway', { id, input: patch }, unscoped(options));
68
+ },
69
+ async delete(id, options) {
70
+ requireHumanSession(config);
71
+ await fieldOf(transport, DELETE_EMAIL_GATEWAY_MUTATION, 'deleteEmailGateway', { id }, unscoped(options));
72
+ },
73
+ async test(id, options) {
74
+ requireHumanSession(config);
75
+ await fieldOf(transport, TEST_EMAIL_GATEWAY_MUTATION, 'testEmailGateway', { id }, unscoped(options));
76
+ },
77
+ };
78
+ }
@@ -21,8 +21,10 @@ export interface UpdateTaxCategoryInput {
21
21
  export interface CreateTaxRateInput {
22
22
  name: string;
23
23
  /**
24
- * A percentage: `21` is 21 %. Not money. The engine keeps two decimals (`5.5`, `20.75`), so
25
- * a third one is rounded when it is stored; the answer carries the stored value.
24
+ * A percentage from 0 to 100, both included: `21` is 21 %. Not money. The engine keeps two
25
+ * decimals (`5.5`, `20.75`), so a third one is rounded when it is stored; the answer carries
26
+ * the stored value. A value outside 0–100 is refused as sent, before any rounding
27
+ * (`TAX_RATE_VALUE_INVALID`).
26
28
  */
27
29
  value: number;
28
30
  /** A disabled rate is never applied. Required: a rate is applied from the moment it exists. */
@@ -113,6 +115,10 @@ export interface PlatformTaxRatesModule {
113
115
  *
114
116
  * - `INVALID_TAX_RATE_INPUT` (`ValidationError`, 400): a missing name, category, zone or
115
117
  * `enabled`, or a value that is not a finite number, refused here before anything is sent.
118
+ * - `TAX_RATE_VALUE_INVALID` (`ValidationError`, 400): a value below 0 or above 100. Refused
119
+ * here before anything is sent, and by the engine for any other caller, with the same code
120
+ * and `details[0].field` `/input/value` (`code` `minimum` or `maximum`), so a form can put
121
+ * the sentence on the rate field whichever side refused it.
116
122
  * - `ENTITY_NOT_FOUND` (`NotFoundError`, 404): the category, zone or customer group does not
117
123
  * exist.
118
124
  *
@@ -125,7 +131,9 @@ export interface PlatformTaxRatesModule {
125
131
  /**
126
132
  * Change a tax rate, and answer it as read. Refused as {@link create} is, plus
127
133
  * `INVALID_TAX_RATE_INPUT` for `customerGroupId: null` (see {@link UpdateTaxRateInput}) and
128
- * `ENTITY_NOT_FOUND` for an unknown rate.
134
+ * `ENTITY_NOT_FOUND` for an unknown rate. Only a value the patch SENDS is held to 0–100: a
135
+ * rate stored outside it before the bound existed can still be renamed or switched off
136
+ * without one.
129
137
  *
130
138
  * @effect money
131
139
  */
@@ -36,6 +36,33 @@ const isObject = (value) => typeof value === 'object' && value !== null && !Arra
36
36
  const refusal = (code) => (message) => new ValidationError({ code, message, status: 400, service: 'vendure' });
37
37
  const categoryRefusal = refusal('INVALID_TAX_CATEGORY_INPUT');
38
38
  const rateRefusal = refusal('INVALID_TAX_RATE_INPUT');
39
+ /** The bounds of a rate's percentage; the engine holds every caller to the same ones. */
40
+ const TAX_RATE_MIN_PERCENT = 0;
41
+ const TAX_RATE_MAX_PERCENT = 100;
42
+ /**
43
+ * The engine's own refusal of a value outside 0–100 % (`TAX_RATE_VALUE_INVALID`), word for word,
44
+ * with the detail on `/input/value` the engine answers, so a caller cannot tell which side
45
+ * refused it.
46
+ */
47
+ function rateValueOutOfRange(value) {
48
+ const below = value < TAX_RATE_MIN_PERCENT;
49
+ const range = `from ${String(TAX_RATE_MIN_PERCENT)} to ${String(TAX_RATE_MAX_PERCENT)}`;
50
+ return new ValidationError({
51
+ code: 'TAX_RATE_VALUE_INVALID',
52
+ message: below
53
+ ? `A tax rate cannot be below ${String(TAX_RATE_MIN_PERCENT)} %: ${String(value)} % was sent. Enter a percentage ${range}.`
54
+ : `A tax rate cannot be above ${String(TAX_RATE_MAX_PERCENT)} %: ${String(value)} % was sent. Enter a percentage ${range}.`,
55
+ status: 400,
56
+ service: 'vendure',
57
+ details: [
58
+ {
59
+ field: '/input/value',
60
+ code: below ? 'minimum' : 'maximum',
61
+ message: `Enter a rate ${range}.`,
62
+ },
63
+ ],
64
+ });
65
+ }
39
66
  function requireId(id, what, refuse) {
40
67
  if (typeof id !== 'string' || id.trim() === '')
41
68
  throw refuse(`${what} must be an id.`);
@@ -72,6 +99,9 @@ function rateInput(input, creating) {
72
99
  if (typeof input.value !== 'number' || !Number.isFinite(input.value)) {
73
100
  throw rateRefusal('value must be a finite number: the percentage, 21 for 21 %.');
74
101
  }
102
+ if (input.value < TAX_RATE_MIN_PERCENT || input.value > TAX_RATE_MAX_PERCENT) {
103
+ throw rateValueOutOfRange(input.value);
104
+ }
75
105
  out.value = input.value;
76
106
  }
77
107
  if (input.enabled !== undefined || creating) {
@@ -86,7 +86,12 @@ export interface PosExchangeResult {
86
86
  carries: PosExchangeCarry[];
87
87
  refunds: PosReturnRefund[];
88
88
  cashPaidOut: number;
89
- /** What the cart still owes: 0. Complete it with `sale.complete`. */
89
+ /**
90
+ * What the cart still owes after the credit: 0, unless a card is being charged on it or a
91
+ * payment link is open, which the exchange holds back (it never carries credit over money in
92
+ * flight). Complete the sale with `sale.complete` once that money lands; a completion while
93
+ * money is still owed is refused `POS_SALE_NOT_COVERED`.
94
+ */
90
95
  remaining: number;
91
96
  complete: boolean;
92
97
  }