Skip to content

Commit ead7bfa

Browse files
feat(core): Merge custom fields on updating order lines (#3673)
1 parent fb8b3c2 commit ead7bfa

File tree

5 files changed

+482
-59
lines changed

5 files changed

+482
-59
lines changed

docs/docs/guides/developer-guide/custom-fields/index.md

Lines changed: 89 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ Some use-cases for custom fields include:
1818
* Adding a longitude and latitude to the `StockLocation` for use in selecting the closest location to a customer.
1919

2020
:::note
21-
Custom fields are not solely restricted to Vendure's native entities though, it's also possible to add support for custom fields to your own custom entities. See: [Supporting custom fields](/guides/developer-guide/database-entity/#supporting-custom-fields)
21+
Custom fields are not solely restricted to Vendure's native entities though, it's also possible to add support for custom fields to your own custom entities. See: [Supporting custom fields](/guides/developer-guide/database-entity/#supporting-custom-fields)
2222
:::
2323

2424
## Defining custom fields
@@ -61,15 +61,15 @@ mutation {
6161
id: 1
6262
// highlight-start
6363
customFields: {
64-
infoUrl: "https://some-url.com",
65-
downloadable: true,
64+
infoUrl: "https://some-url.com",
65+
downloadable: true,
6666
}
6767
// highlight-end
6868
translations: [
69-
// highlight-next-line
70-
{ languageCode: en, customFields: { shortName: "foo" } }
69+
// highlight-next-line
70+
{ languageCode: en, customFields: { shortName: "foo" } }
7171
]
72-
}) {
72+
}) {
7373
id
7474
name
7575
// highlight-start
@@ -88,17 +88,17 @@ mutation {
8888

8989
```json
9090
{
91-
"data": {
92-
"product": {
93-
"id": "1",
94-
"name": "Laptop",
95-
"customFields": {
96-
"infoUrl": "https://some-url.com",
97-
"downloadable": true,
98-
"shortName": "foo"
99-
}
91+
"data": {
92+
"product": {
93+
"id": "1",
94+
"name": "Laptop",
95+
"customFields": {
96+
"infoUrl": "https://some-url.com",
97+
"downloadable": true,
98+
"shortName": "foo"
99+
}
100+
}
100101
}
101-
}
102102
}
103103
```
104104

@@ -111,15 +111,15 @@ The custom fields will also extend the filter and sort options available to the
111111
query {
112112
products(options: {
113113
// highlight-start
114-
filter: {
115-
infoUrl: { contains: "new" },
116-
downloadable: { eq: true }
114+
filter: {
115+
infoUrl: { contains: "new" },
116+
downloadable: { eq: true }
117117
},
118118
sort: {
119119
infoUrl: ASC
120120
}
121121
// highlight-end
122-
}) {
122+
}) {
123123
items {
124124
id
125125
name
@@ -135,7 +135,6 @@ query {
135135
}
136136
```
137137

138-
139138
## Available custom field types
140139

141140
The following types are available for custom fields:
@@ -353,9 +352,9 @@ const config = {
353352
type: 'string',
354353
// highlight-start
355354
label: [
356-
{languageCode: LanguageCode.en, value: 'Info URL'},
357-
{languageCode: LanguageCode.de, value: 'Info-URL'},
358-
{languageCode: LanguageCode.es, value: 'URL de información'},
355+
{ languageCode: LanguageCode.en, value: 'Info URL' },
356+
{ languageCode: LanguageCode.de, value: 'Info-URL' },
357+
{ languageCode: LanguageCode.es, value: 'URL de información' },
359358
],
360359
// highlight-end
361360
},
@@ -382,9 +381,9 @@ const config = {
382381
type: 'string',
383382
// highlight-start
384383
description: [
385-
{languageCode: LanguageCode.en, value: 'A URL to more information about the product'},
386-
{languageCode: LanguageCode.de, value: 'Eine URL zu weiteren Informationen über das Produkt'},
387-
{languageCode: LanguageCode.es, value: 'Una URL con más información sobre el producto'},
384+
{ languageCode: LanguageCode.en, value: 'A URL to more information about the product' },
385+
{ languageCode: LanguageCode.de, value: 'Eine URL zu weiteren Informationen über das Produkt' },
386+
{ languageCode: LanguageCode.es, value: 'Una URL con más información sobre el producto' },
388387
],
389388
// highlight-end
390389
},
@@ -558,9 +557,9 @@ const config = {
558557

559558
// If a localized error message is required, return an array of LocalizedString objects.
560559
return [
561-
{languageCode: LanguageCode.en, value: 'The URL must start with "http"'},
562-
{languageCode: LanguageCode.de, value: 'Die URL muss mit "http" beginnen'},
563-
{languageCode: LanguageCode.es, value: 'La URL debe comenzar con "http"'},
560+
{ languageCode: LanguageCode.en, value: 'The URL must start with "http"' },
561+
{ languageCode: LanguageCode.de, value: 'Die URL muss mit "http" beginnen' },
562+
{ languageCode: LanguageCode.es, value: 'La URL debe comenzar con "http"' },
564563
];
565564
}
566565
},
@@ -606,7 +605,7 @@ to view or update the field.
606605

607606
In the Admin UI, the custom field will not be displayed if the current administrator lacks the required permission.
608607

609-
In the GraphQL API, if the current user does not have the required permission, then the field will always return `null`.
608+
In the GraphQL API, if the current user does not have the required permission, then the field will always return `null`.
610609
Attempting to set the value of a field for which the user does not have the required permission will cause the mutation to fail
611610
with an error.
612611

@@ -632,7 +631,7 @@ const config = {
632631
// and the user must have at least one of the permissions
633632
// to access the field.
634633
requiresPermission: [
635-
Permission.SuperAdmin,
634+
Permission.SuperAdmin,
636635
Permission.ReadShippingMethod,
637636
],
638637
// highlight-end
@@ -700,8 +699,8 @@ const config = {
700699
type: 'string',
701700
// highlight-start
702701
options: [
703-
{value: 'new', label: [{languageCode: LanguageCode.en, value: 'New'}]},
704-
{value: 'used', label: [{languageCode: LanguageCode.en, value: 'Used'}]},
702+
{ value: 'new', label: [{ languageCode: LanguageCode.en, value: 'New' }] },
703+
{ value: 'used', label: [{ languageCode: LanguageCode.en, value: 'Used' }] },
705704
],
706705
// highlight-end
707706
},
@@ -940,7 +939,7 @@ query {
940939
}
941940
```
942941

943-
Struct fields support many of the same properties as other custom fields, such as `list`, `label`, `description`, `validate`, `ui` and
942+
Struct fields support many of the same properties as other custom fields, such as `list`, `label`, `description`, `validate`, `ui` and
944943
type-specific properties such as `options` and `pattern` for string types.
945944

946945
:::note
@@ -963,8 +962,8 @@ const config = {
963962
type: 'string',
964963
// highlight-start
965964
options: [
966-
{value: 'red', label: [{languageCode: LanguageCode.en, value: 'Red'}]},
967-
{value: 'blue', label: [{languageCode: LanguageCode.en, value: 'Blue'}]},
965+
{ value: 'red', label: [{ languageCode: LanguageCode.en, value: 'Red' }] },
966+
{ value: 'blue', label: [{ languageCode: LanguageCode.en, value: 'Blue' }] },
968967
],
969968
// highlight-end
970969
},
@@ -1192,20 +1191,20 @@ const config: VendureConfig = {
11921191
customFields: {
11931192
Product: [
11941193
// Rich text editor
1195-
{name: 'additionalInfo', type: 'text', ui: {component: 'rich-text-form-input'}},
1194+
{ name: 'additionalInfo', type: 'text', ui: { component: 'rich-text-form-input' } },
11961195
// JSON editor
1197-
{name: 'specs', type: 'text', ui: {component: 'json-editor-form-input'}},
1196+
{ name: 'specs', type: 'text', ui: { component: 'json-editor-form-input' } },
11981197
// Numeric with suffix
11991198
{
12001199
name: 'weight',
12011200
type: 'int',
1202-
ui: {component: 'number-form-input', suffix: 'g'},
1201+
ui: { component: 'number-form-input', suffix: 'g' },
12031202
},
12041203
// Currency input
12051204
{
12061205
name: 'RRP',
12071206
type: 'int',
1208-
ui: {component: 'currency-form-input'},
1207+
ui: { component: 'currency-form-input' },
12091208
},
12101209
// Select with options
12111210
{
@@ -1214,8 +1213,8 @@ const config: VendureConfig = {
12141213
ui: {
12151214
component: 'select-form-input',
12161215
options: [
1217-
{value: 'static', label: [{languageCode: LanguageCode.en, value: 'Static'}]},
1218-
{value: 'dynamic', label: [{languageCode: LanguageCode.en, value: 'Dynamic'}]},
1216+
{ value: 'static', label: [{ languageCode: LanguageCode.en, value: 'Static' }] },
1217+
{ value: 'dynamic', label: [{ languageCode: LanguageCode.en, value: 'Dynamic' }] },
12191218
],
12201219
},
12211220
},
@@ -1247,7 +1246,6 @@ The various configuration options for each of the built-in form input (e.g. `su
12471246

12481247
If none of the built-in form input components are suitable, you can create your own. This is a more advanced topic which is covered in detail in the [Custom Form Input Components](/guides/extending-the-admin-ui/custom-form-inputs/) guide.
12491248

1250-
12511249
## Tabbed custom fields
12521250

12531251
With a large, complex project, it's common for lots of custom fields to be required. This can get visually noisy in the UI, so Vendure supports tabbed custom fields. Just specify the tab name in the `ui` object, and those fields with the same tab name will be grouped in the UI! The tab name can also be a translation token if you need to support multiple languages.
@@ -1263,19 +1261,17 @@ const config = {
12631261
// ...
12641262
customFields: {
12651263
Product: [
1266-
{ name: 'additionalInfo', type: 'text', ui: {component: 'rich-text-form-input'} },
1267-
{ name: 'specs', type: 'text', ui: {component: 'json-editor-form-input'} },
1268-
{ name: 'width', type: 'int', ui: {tab: 'Shipping'} },
1269-
{ name: 'height', type: 'int', ui: {tab: 'Shipping'} },
1270-
{ name: 'depth', type: 'int', ui: {tab: 'Shipping'} },
1271-
{ name: 'weight', type: 'int', ui: {tab: 'Shipping'} },
1264+
{ name: 'additionalInfo', type: 'text', ui: { component: 'rich-text-form-input' } },
1265+
{ name: 'specs', type: 'text', ui: { component: 'json-editor-form-input' } },
1266+
{ name: 'width', type: 'int', ui: { tab: 'Shipping' } },
1267+
{ name: 'height', type: 'int', ui: { tab: 'Shipping' } },
1268+
{ name: 'depth', type: 'int', ui: { tab: 'Shipping' } },
1269+
{ name: 'weight', type: 'int', ui: { tab: 'Shipping' } },
12721270
],
12731271
},
12741272
}
12751273
```
12761274

1277-
1278-
12791275
## TypeScript Typings
12801276

12811277
Because custom fields are generated at run-time, TypeScript has no way of knowing about them based on your
@@ -1343,3 +1339,44 @@ One way to ensure that your custom field typings always get imported first is to
13431339
For a working example of this setup, see the [real-world-vendure repo](https://github.com/vendure-ecommerce/real-world-vendure/blob/master/src/plugins/reviews/types.ts)
13441340
:::
13451341

1342+
## Special cases
1343+
1344+
Beyond adding custom fields to the corresponding GraphQL types, and updating paginated list sort & filter options, there are a few special cases where adding custom fields to certain entities will result in further API changes.
1345+
1346+
### OrderLine custom fields
1347+
1348+
When you define custom fields on the `OrderLine` entity, the following API changes are also automatically provided by Vendure:
1349+
1350+
- Shop API: [addItemToOrder](/reference/graphql-api/shop/mutations#additemtoorder) will have a 3rd input argument, `customFields`, which allows custom field values to be set when adding an item to the order.
1351+
- Shop API: [adjustOrderLine](/reference/graphql-api/shop/mutations#additemtoorder) will have a 3rd input argument, `customFields`, which allows custom field values to be updated.
1352+
- Admin API: the equivalent mutations for manipulating draft orders and for modifying and order will also have inputs to allow custom field values to be set.
1353+
1354+
:::info
1355+
To see an example of this in practice, see the [Configurable Product guide](/guides/how-to/configurable-products/)
1356+
:::
1357+
1358+
### Order custom fields
1359+
1360+
When you define custom fields on the `Order` entity, the following API changes are also automatically provided by Vendure:
1361+
1362+
- Admin API: [modifyOrder](/reference/graphql-api/admin/mutations#modifyorder) will have a `customFields` field on the input object.
1363+
1364+
### ShippingMethod custom fields
1365+
1366+
When you define custom fields on the `ShippingMethod` entity, the following API changes are also automatically provided by Vendure:
1367+
1368+
- Shop API: [eligibleShippingMethods](/reference/graphql-api/shop/queries#eligibleshippingmethods) will have public custom fields available on the result.
1369+
1370+
### PaymentMethod custom fields
1371+
1372+
When you define custom fields on the `PaymentMethod` entity, the following API changes are also automatically provided by Vendure:
1373+
1374+
- Shop API: [eligiblePaymentMethods](/reference/graphql-api/shop/queries#eligiblepaymentmethods) will have public custom fields available on the result.
1375+
1376+
### Customer custom fields
1377+
1378+
When you define custom fields on the `Customer` entity, the following API changes are also automatically provided by Vendure:
1379+
1380+
- Shop API: [registerCustomerAccount](/reference/graphql-api/shop/mutations#registercustomeraccount) will have have a `customFields` field on the input
1381+
object.
1382+

packages/core/e2e/custom-fields.e2e-spec.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,10 @@ import { createTestEnvironment } from '@vendure/testing';
44
import { fail } from 'assert';
55
import gql from 'graphql-tag';
66
import path from 'path';
7-
import { vi } from 'vitest';
8-
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
7+
import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest';
98

109
import { initialData } from '../../../e2e-common/e2e-initial-data';
11-
import { testConfig, TEST_SETUP_TIMEOUT_MS } from '../../../e2e-common/test-config';
10+
import { TEST_SETUP_TIMEOUT_MS, testConfig } from '../../../e2e-common/test-config';
1211

1312
import { assertThrowsWithMessage } from './utils/assert-throws-with-message';
1413
import { fixPostgresTimezone } from './utils/fix-pg-timezone';
@@ -192,8 +191,8 @@ const customConfig = mergeConfig(testConfig(), {
192191
{
193192
name: 'costPrice',
194193
type: 'int',
195-
}
196-
],
194+
},
195+
],
197196
// Single readonly Address custom field to test
198197
// https://github.com/vendure-ecommerce/vendure/issues/3326
199198
Address: [

0 commit comments

Comments
 (0)