ソースを参照

feat(core): Implement Regions & support for Provinces

Relates to #76

BREAKING CHANGE: A new `Region` entity has been introduced, which is a base class for `Country` and
the new `Province` entity. The `Zone.members` property is now an array of `Region` rather than
`Country`, since Zones may now be composed of both countries and provinces. If you have defined
any custom fields on `Country`, you'll need to change it to `Region` in your custom fields config.
Michael Bromley 2 年 前
コミット
7b8f5bf6a4
41 ファイル変更1855 行追加659 行削除
  1. 159 24
      packages/admin-ui/src/lib/core/src/common/generated-types.ts
  2. 235 312
      packages/admin-ui/src/lib/core/src/common/introspection-result.ts
  3. 3 3
      packages/admin-ui/src/lib/core/src/data/definitions/settings-definitions.ts
  4. 1 1
      packages/admin-ui/src/lib/settings/src/components/country-detail/country-detail.component.ts
  5. 151 21
      packages/asset-server-plugin/e2e/graphql/generated-e2e-asset-server-plugin-types.ts
  6. 65 22
      packages/common/src/generated-shop-types.ts
  7. 148 13
      packages/common/src/generated-types.ts
  8. 212 64
      packages/core/e2e/graphql/generated-e2e-admin-types.ts
  9. 61 20
      packages/core/e2e/graphql/generated-e2e-shop-types.ts
  10. 17 0
      packages/core/src/api/config/generate-resolvers.ts
  11. 15 1
      packages/core/src/api/config/graphql-custom-fields.ts
  12. 1 1
      packages/core/src/api/resolvers/admin/country.resolver.ts
  13. 1 1
      packages/core/src/api/resolvers/entity/country-entity.resolver.ts
  14. 1 1
      packages/core/src/api/resolvers/entity/zone-entity.resolver.ts
  15. 37 0
      packages/core/src/api/schema/admin-api/province.api.graphql
  16. 0 23
      packages/core/src/api/schema/common/country.type.graphql
  17. 59 0
      packages/core/src/api/schema/common/region.type.graphql
  18. 2 1
      packages/core/src/api/schema/common/zone.type.graphql
  19. 1 1
      packages/core/src/config/custom-field/custom-field-types.ts
  20. 1 1
      packages/core/src/config/default-config.ts
  21. 1 1
      packages/core/src/entity/address/address.entity.ts
  22. 0 36
      packages/core/src/entity/country/country.entity.ts
  23. 2 2
      packages/core/src/entity/custom-entity-fields.ts
  24. 7 3
      packages/core/src/entity/entities.ts
  25. 1 2
      packages/core/src/entity/index.ts
  26. 21 0
      packages/core/src/entity/region/country.entity.ts
  27. 20 0
      packages/core/src/entity/region/province.entity.ts
  28. 8 8
      packages/core/src/entity/region/region-translation.entity.ts
  29. 51 0
      packages/core/src/entity/region/region.entity.ts
  30. 4 4
      packages/core/src/entity/register-custom-entity-fields.ts
  31. 4 3
      packages/core/src/entity/zone/zone.entity.ts
  32. 27 0
      packages/core/src/event-bus/events/province-event.ts
  33. 6 5
      packages/core/src/service/services/country.service.ts
  34. 102 0
      packages/core/src/service/services/province.service.ts
  35. 1 1
      packages/core/src/service/services/zone.service.ts
  36. 151 21
      packages/elasticsearch-plugin/e2e/graphql/generated-e2e-elasticsearch-plugin-types.ts
  37. 153 21
      packages/payments-plugin/e2e/graphql/generated-admin-types.ts
  38. 61 20
      packages/payments-plugin/e2e/graphql/generated-shop-types.ts
  39. 65 22
      packages/payments-plugin/src/mollie/graphql/generated-shop-types.ts
  40. 0 0
      schema-admin.json
  41. 0 0
      schema-shop.json

ファイルの差分が大きいため隠しています
+ 159 - 24
packages/admin-ui/src/lib/core/src/common/generated-types.ts


+ 235 - 312
packages/admin-ui/src/lib/core/src/common/introspection-result.ts

@@ -1,315 +1,238 @@
 /* eslint-disable */
 
-      export interface PossibleTypesResultData {
-        possibleTypes: {
-          [key: string]: string[]
-        }
-      }
-      const result: PossibleTypesResultData = {
-  "possibleTypes": {
-    "AddFulfillmentToOrderResult": [
-      "CreateFulfillmentError",
-      "EmptyOrderLineSelectionError",
-      "Fulfillment",
-      "FulfillmentStateTransitionError",
-      "InsufficientStockOnHandError",
-      "InvalidFulfillmentHandlerError",
-      "ItemsAlreadyFulfilledError"
-    ],
-    "AddManualPaymentToOrderResult": [
-      "ManualPaymentStateError",
-      "Order"
-    ],
-    "ApplyCouponCodeResult": [
-      "CouponCodeExpiredError",
-      "CouponCodeInvalidError",
-      "CouponCodeLimitError",
-      "Order"
-    ],
-    "AuthenticationResult": [
-      "CurrentUser",
-      "InvalidCredentialsError"
-    ],
-    "CancelOrderResult": [
-      "CancelActiveOrderError",
-      "EmptyOrderLineSelectionError",
-      "MultipleOrderError",
-      "Order",
-      "OrderStateTransitionError",
-      "QuantityTooGreatError"
-    ],
-    "CancelPaymentResult": [
-      "CancelPaymentError",
-      "Payment",
-      "PaymentStateTransitionError"
-    ],
-    "CreateAssetResult": [
-      "Asset",
-      "MimeTypeError"
-    ],
-    "CreateChannelResult": [
-      "Channel",
-      "LanguageNotAvailableError"
-    ],
-    "CreateCustomerResult": [
-      "Customer",
-      "EmailAddressConflictError"
-    ],
-    "CreatePromotionResult": [
-      "MissingConditionsError",
-      "Promotion"
-    ],
-    "CustomField": [
-      "BooleanCustomFieldConfig",
-      "DateTimeCustomFieldConfig",
-      "FloatCustomFieldConfig",
-      "IntCustomFieldConfig",
-      "LocaleStringCustomFieldConfig",
-      "LocaleTextCustomFieldConfig",
-      "RelationCustomFieldConfig",
-      "StringCustomFieldConfig",
-      "TextCustomFieldConfig"
-    ],
-    "CustomFieldConfig": [
-      "BooleanCustomFieldConfig",
-      "DateTimeCustomFieldConfig",
-      "FloatCustomFieldConfig",
-      "IntCustomFieldConfig",
-      "LocaleStringCustomFieldConfig",
-      "LocaleTextCustomFieldConfig",
-      "RelationCustomFieldConfig",
-      "StringCustomFieldConfig",
-      "TextCustomFieldConfig"
-    ],
-    "ErrorResult": [
-      "AlreadyRefundedError",
-      "CancelActiveOrderError",
-      "CancelPaymentError",
-      "ChannelDefaultLanguageError",
-      "CouponCodeExpiredError",
-      "CouponCodeInvalidError",
-      "CouponCodeLimitError",
-      "CreateFulfillmentError",
-      "EmailAddressConflictError",
-      "EmptyOrderLineSelectionError",
-      "FacetInUseError",
-      "FulfillmentStateTransitionError",
-      "GuestCheckoutError",
-      "IneligibleShippingMethodError",
-      "InsufficientStockError",
-      "InsufficientStockOnHandError",
-      "InvalidCredentialsError",
-      "InvalidFulfillmentHandlerError",
-      "ItemsAlreadyFulfilledError",
-      "LanguageNotAvailableError",
-      "ManualPaymentStateError",
-      "MimeTypeError",
-      "MissingConditionsError",
-      "MultipleOrderError",
-      "NativeAuthStrategyError",
-      "NegativeQuantityError",
-      "NoActiveOrderError",
-      "NoChangesSpecifiedError",
-      "NothingToRefundError",
-      "OrderLimitError",
-      "OrderModificationError",
-      "OrderModificationStateError",
-      "OrderStateTransitionError",
-      "PaymentMethodMissingError",
-      "PaymentOrderMismatchError",
-      "PaymentStateTransitionError",
-      "ProductOptionInUseError",
-      "QuantityTooGreatError",
-      "RefundOrderStateError",
-      "RefundPaymentIdMissingError",
-      "RefundStateTransitionError",
-      "SettlePaymentError"
-    ],
-    "ModifyOrderResult": [
-      "CouponCodeExpiredError",
-      "CouponCodeInvalidError",
-      "CouponCodeLimitError",
-      "InsufficientStockError",
-      "NegativeQuantityError",
-      "NoChangesSpecifiedError",
-      "Order",
-      "OrderLimitError",
-      "OrderModificationStateError",
-      "PaymentMethodMissingError",
-      "RefundPaymentIdMissingError"
-    ],
-    "NativeAuthenticationResult": [
-      "CurrentUser",
-      "InvalidCredentialsError",
-      "NativeAuthStrategyError"
-    ],
-    "Node": [
-      "Address",
-      "Administrator",
-      "Allocation",
-      "Asset",
-      "AuthenticationMethod",
-      "Cancellation",
-      "Channel",
-      "Collection",
-      "Country",
-      "Customer",
-      "CustomerGroup",
-      "Facet",
-      "FacetValue",
-      "Fulfillment",
-      "HistoryEntry",
-      "Job",
-      "Order",
-      "OrderItem",
-      "OrderLine",
-      "OrderModification",
-      "Payment",
-      "PaymentMethod",
-      "Product",
-      "ProductOption",
-      "ProductOptionGroup",
-      "ProductVariant",
-      "Promotion",
-      "Refund",
-      "Release",
-      "Return",
-      "Role",
-      "Sale",
-      "Seller",
-      "ShippingMethod",
-      "StockAdjustment",
-      "StockLevel",
-      "StockLocation",
-      "Surcharge",
-      "Tag",
-      "TaxCategory",
-      "TaxRate",
-      "User",
-      "Zone"
-    ],
-    "PaginatedList": [
-      "AdministratorList",
-      "AssetList",
-      "CollectionList",
-      "CountryList",
-      "CustomerGroupList",
-      "CustomerList",
-      "FacetList",
-      "FacetValueList",
-      "HistoryEntryList",
-      "JobList",
-      "OrderList",
-      "PaymentMethodList",
-      "ProductList",
-      "ProductVariantList",
-      "PromotionList",
-      "RoleList",
-      "SellerList",
-      "ShippingMethodList",
-      "StockLocationList",
-      "TagList",
-      "TaxRateList"
-    ],
-    "RefundOrderResult": [
-      "AlreadyRefundedError",
-      "MultipleOrderError",
-      "NothingToRefundError",
-      "OrderStateTransitionError",
-      "PaymentOrderMismatchError",
-      "QuantityTooGreatError",
-      "Refund",
-      "RefundOrderStateError",
-      "RefundStateTransitionError"
-    ],
-    "RemoveFacetFromChannelResult": [
-      "Facet",
-      "FacetInUseError"
-    ],
-    "RemoveOptionGroupFromProductResult": [
-      "Product",
-      "ProductOptionInUseError"
-    ],
-    "RemoveOrderItemsResult": [
-      "Order",
-      "OrderModificationError"
-    ],
-    "SearchResultPrice": [
-      "PriceRange",
-      "SinglePrice"
-    ],
-    "SetCustomerForDraftOrderResult": [
-      "EmailAddressConflictError",
-      "Order"
-    ],
-    "SetOrderShippingMethodResult": [
-      "IneligibleShippingMethodError",
-      "NoActiveOrderError",
-      "Order",
-      "OrderModificationError"
-    ],
-    "SettlePaymentResult": [
-      "OrderStateTransitionError",
-      "Payment",
-      "PaymentStateTransitionError",
-      "SettlePaymentError"
-    ],
-    "SettleRefundResult": [
-      "Refund",
-      "RefundStateTransitionError"
-    ],
-    "StockMovement": [
-      "Allocation",
-      "Cancellation",
-      "Release",
-      "Return",
-      "Sale",
-      "StockAdjustment"
-    ],
-    "StockMovementItem": [
-      "Allocation",
-      "Cancellation",
-      "Release",
-      "Return",
-      "Sale",
-      "StockAdjustment"
-    ],
-    "TransitionFulfillmentToStateResult": [
-      "Fulfillment",
-      "FulfillmentStateTransitionError"
-    ],
-    "TransitionOrderToStateResult": [
-      "Order",
-      "OrderStateTransitionError"
-    ],
-    "TransitionPaymentToStateResult": [
-      "Payment",
-      "PaymentStateTransitionError"
-    ],
-    "UpdateChannelResult": [
-      "Channel",
-      "LanguageNotAvailableError"
-    ],
-    "UpdateCustomerResult": [
-      "Customer",
-      "EmailAddressConflictError"
-    ],
-    "UpdateGlobalSettingsResult": [
-      "ChannelDefaultLanguageError",
-      "GlobalSettings"
-    ],
-    "UpdateOrderItemsResult": [
-      "InsufficientStockError",
-      "NegativeQuantityError",
-      "Order",
-      "OrderLimitError",
-      "OrderModificationError"
-    ],
-    "UpdatePromotionResult": [
-      "MissingConditionsError",
-      "Promotion"
-    ]
-  }
+export interface PossibleTypesResultData {
+    possibleTypes: {
+        [key: string]: string[];
+    };
+}
+const result: PossibleTypesResultData = {
+    possibleTypes: {
+        AddFulfillmentToOrderResult: [
+            'CreateFulfillmentError',
+            'EmptyOrderLineSelectionError',
+            'Fulfillment',
+            'FulfillmentStateTransitionError',
+            'InsufficientStockOnHandError',
+            'InvalidFulfillmentHandlerError',
+            'ItemsAlreadyFulfilledError',
+        ],
+        AddManualPaymentToOrderResult: ['ManualPaymentStateError', 'Order'],
+        ApplyCouponCodeResult: [
+            'CouponCodeExpiredError',
+            'CouponCodeInvalidError',
+            'CouponCodeLimitError',
+            'Order',
+        ],
+        AuthenticationResult: ['CurrentUser', 'InvalidCredentialsError'],
+        CancelOrderResult: [
+            'CancelActiveOrderError',
+            'EmptyOrderLineSelectionError',
+            'MultipleOrderError',
+            'Order',
+            'OrderStateTransitionError',
+            'QuantityTooGreatError',
+        ],
+        CancelPaymentResult: ['CancelPaymentError', 'Payment', 'PaymentStateTransitionError'],
+        CreateAssetResult: ['Asset', 'MimeTypeError'],
+        CreateChannelResult: ['Channel', 'LanguageNotAvailableError'],
+        CreateCustomerResult: ['Customer', 'EmailAddressConflictError'],
+        CreatePromotionResult: ['MissingConditionsError', 'Promotion'],
+        CustomField: [
+            'BooleanCustomFieldConfig',
+            'DateTimeCustomFieldConfig',
+            'FloatCustomFieldConfig',
+            'IntCustomFieldConfig',
+            'LocaleStringCustomFieldConfig',
+            'LocaleTextCustomFieldConfig',
+            'RelationCustomFieldConfig',
+            'StringCustomFieldConfig',
+            'TextCustomFieldConfig',
+        ],
+        CustomFieldConfig: [
+            'BooleanCustomFieldConfig',
+            'DateTimeCustomFieldConfig',
+            'FloatCustomFieldConfig',
+            'IntCustomFieldConfig',
+            'LocaleStringCustomFieldConfig',
+            'LocaleTextCustomFieldConfig',
+            'RelationCustomFieldConfig',
+            'StringCustomFieldConfig',
+            'TextCustomFieldConfig',
+        ],
+        ErrorResult: [
+            'AlreadyRefundedError',
+            'CancelActiveOrderError',
+            'CancelPaymentError',
+            'ChannelDefaultLanguageError',
+            'CouponCodeExpiredError',
+            'CouponCodeInvalidError',
+            'CouponCodeLimitError',
+            'CreateFulfillmentError',
+            'EmailAddressConflictError',
+            'EmptyOrderLineSelectionError',
+            'FacetInUseError',
+            'FulfillmentStateTransitionError',
+            'GuestCheckoutError',
+            'IneligibleShippingMethodError',
+            'InsufficientStockError',
+            'InsufficientStockOnHandError',
+            'InvalidCredentialsError',
+            'InvalidFulfillmentHandlerError',
+            'ItemsAlreadyFulfilledError',
+            'LanguageNotAvailableError',
+            'ManualPaymentStateError',
+            'MimeTypeError',
+            'MissingConditionsError',
+            'MultipleOrderError',
+            'NativeAuthStrategyError',
+            'NegativeQuantityError',
+            'NoActiveOrderError',
+            'NoChangesSpecifiedError',
+            'NothingToRefundError',
+            'OrderLimitError',
+            'OrderModificationError',
+            'OrderModificationStateError',
+            'OrderStateTransitionError',
+            'PaymentMethodMissingError',
+            'PaymentOrderMismatchError',
+            'PaymentStateTransitionError',
+            'ProductOptionInUseError',
+            'QuantityTooGreatError',
+            'RefundOrderStateError',
+            'RefundPaymentIdMissingError',
+            'RefundStateTransitionError',
+            'SettlePaymentError',
+        ],
+        ModifyOrderResult: [
+            'CouponCodeExpiredError',
+            'CouponCodeInvalidError',
+            'CouponCodeLimitError',
+            'InsufficientStockError',
+            'NegativeQuantityError',
+            'NoChangesSpecifiedError',
+            'Order',
+            'OrderLimitError',
+            'OrderModificationStateError',
+            'PaymentMethodMissingError',
+            'RefundPaymentIdMissingError',
+        ],
+        NativeAuthenticationResult: ['CurrentUser', 'InvalidCredentialsError', 'NativeAuthStrategyError'],
+        Node: [
+            'Address',
+            'Administrator',
+            'Allocation',
+            'Asset',
+            'AuthenticationMethod',
+            'Cancellation',
+            'Channel',
+            'Collection',
+            'Country',
+            'Customer',
+            'CustomerGroup',
+            'Facet',
+            'FacetValue',
+            'Fulfillment',
+            'HistoryEntry',
+            'Job',
+            'Order',
+            'OrderItem',
+            'OrderLine',
+            'OrderModification',
+            'Payment',
+            'PaymentMethod',
+            'Product',
+            'ProductOption',
+            'ProductOptionGroup',
+            'ProductVariant',
+            'Promotion',
+            'Province',
+            'Refund',
+            'Release',
+            'Return',
+            'Role',
+            'Sale',
+            'Seller',
+            'ShippingMethod',
+            'StockAdjustment',
+            'StockLevel',
+            'StockLocation',
+            'Surcharge',
+            'Tag',
+            'TaxCategory',
+            'TaxRate',
+            'User',
+            'Zone',
+        ],
+        PaginatedList: [
+            'AdministratorList',
+            'AssetList',
+            'CollectionList',
+            'CountryList',
+            'CustomerGroupList',
+            'CustomerList',
+            'FacetList',
+            'FacetValueList',
+            'HistoryEntryList',
+            'JobList',
+            'OrderList',
+            'PaymentMethodList',
+            'ProductList',
+            'ProductVariantList',
+            'PromotionList',
+            'ProvinceList',
+            'RoleList',
+            'SellerList',
+            'ShippingMethodList',
+            'StockLocationList',
+            'TagList',
+            'TaxRateList',
+        ],
+        RefundOrderResult: [
+            'AlreadyRefundedError',
+            'MultipleOrderError',
+            'NothingToRefundError',
+            'OrderStateTransitionError',
+            'PaymentOrderMismatchError',
+            'QuantityTooGreatError',
+            'Refund',
+            'RefundOrderStateError',
+            'RefundStateTransitionError',
+        ],
+        Region: ['Country', 'Province'],
+        RemoveFacetFromChannelResult: ['Facet', 'FacetInUseError'],
+        RemoveOptionGroupFromProductResult: ['Product', 'ProductOptionInUseError'],
+        RemoveOrderItemsResult: ['Order', 'OrderModificationError'],
+        SearchResultPrice: ['PriceRange', 'SinglePrice'],
+        SetCustomerForDraftOrderResult: ['EmailAddressConflictError', 'Order'],
+        SetOrderShippingMethodResult: [
+            'IneligibleShippingMethodError',
+            'NoActiveOrderError',
+            'Order',
+            'OrderModificationError',
+        ],
+        SettlePaymentResult: [
+            'OrderStateTransitionError',
+            'Payment',
+            'PaymentStateTransitionError',
+            'SettlePaymentError',
+        ],
+        SettleRefundResult: ['Refund', 'RefundStateTransitionError'],
+        StockMovement: ['Allocation', 'Cancellation', 'Release', 'Return', 'Sale', 'StockAdjustment'],
+        StockMovementItem: ['Allocation', 'Cancellation', 'Release', 'Return', 'Sale', 'StockAdjustment'],
+        TransitionFulfillmentToStateResult: ['Fulfillment', 'FulfillmentStateTransitionError'],
+        TransitionOrderToStateResult: ['Order', 'OrderStateTransitionError'],
+        TransitionPaymentToStateResult: ['Payment', 'PaymentStateTransitionError'],
+        UpdateChannelResult: ['Channel', 'LanguageNotAvailableError'],
+        UpdateCustomerResult: ['Customer', 'EmailAddressConflictError'],
+        UpdateGlobalSettingsResult: ['ChannelDefaultLanguageError', 'GlobalSettings'],
+        UpdateOrderItemsResult: [
+            'InsufficientStockError',
+            'NegativeQuantityError',
+            'Order',
+            'OrderLimitError',
+            'OrderModificationError',
+        ],
+        UpdatePromotionResult: ['MissingConditionsError', 'Promotion'],
+    },
 };
-      export default result;
-    
+export default result;

+ 3 - 3
packages/admin-ui/src/lib/core/src/data/definitions/settings-definitions.ts

@@ -750,9 +750,6 @@ export const GET_SERVER_CONFIG = gql`
                     Collection {
                         ...CustomFields
                     }
-                    Country {
-                        ...CustomFields
-                    }
                     Customer {
                         ...CustomFields
                     }
@@ -795,6 +792,9 @@ export const GET_SERVER_CONFIG = gql`
                     Promotion {
                         ...CustomFields
                     }
+                    Region {
+                        ...CustomFields
+                    }
                     Seller {
                         ...CustomFields
                     }

+ 1 - 1
packages/admin-ui/src/lib/settings/src/components/country-detail/country-detail.component.ts

@@ -44,7 +44,7 @@ export class CountryDetailComponent
         private notificationService: NotificationService,
     ) {
         super(route, router, serverConfigService, dataService);
-        this.customFields = this.getCustomFieldConfig('Country');
+        this.customFields = this.getCustomFieldConfig('Region');
         this.detailForm = this.formBuilder.group({
             code: ['', Validators.required],
             name: ['', Validators.required],

+ 151 - 21
packages/asset-server-plugin/e2e/graphql/generated-e2e-asset-server-plugin-types.ts

@@ -354,8 +354,10 @@ export type Cancellation = Node &
 export type Channel = Node & {
     code: Scalars['String'];
     createdAt: Scalars['DateTime'];
+    /** @deprecated Use defaultCurrencyCode instead */
     currencyCode: CurrencyCode;
     customFields?: Maybe<Scalars['JSON']>;
+    defaultCurrencyCode: CurrencyCode;
     defaultLanguageCode: LanguageCode;
     defaultShippingZone?: Maybe<Zone>;
     defaultTaxZone?: Maybe<Zone>;
@@ -517,17 +519,21 @@ export type CoordinateInput = {
     y: Scalars['Float'];
 };
 
-export type Country = Node & {
-    code: Scalars['String'];
-    createdAt: Scalars['DateTime'];
-    customFields?: Maybe<Scalars['JSON']>;
-    enabled: Scalars['Boolean'];
-    id: Scalars['ID'];
-    languageCode: LanguageCode;
-    name: Scalars['String'];
-    translations: Array<CountryTranslation>;
-    updatedAt: Scalars['DateTime'];
-};
+export type Country = Node &
+    Region & {
+        code: Scalars['String'];
+        createdAt: Scalars['DateTime'];
+        customFields?: Maybe<Scalars['JSON']>;
+        enabled: Scalars['Boolean'];
+        id: Scalars['ID'];
+        languageCode: LanguageCode;
+        name: Scalars['String'];
+        parent?: Maybe<Region>;
+        parentId?: Maybe<Scalars['ID']>;
+        translations: Array<RegionTranslation>;
+        type: Scalars['String'];
+        updatedAt: Scalars['DateTime'];
+    };
 
 export type CountryFilterParameter = {
     code?: InputMaybe<StringOperators>;
@@ -536,6 +542,8 @@ export type CountryFilterParameter = {
     id?: InputMaybe<IdOperators>;
     languageCode?: InputMaybe<StringOperators>;
     name?: InputMaybe<StringOperators>;
+    parentId?: InputMaybe<IdOperators>;
+    type?: InputMaybe<StringOperators>;
     updatedAt?: InputMaybe<DateOperators>;
 };
 
@@ -562,17 +570,11 @@ export type CountrySortParameter = {
     createdAt?: InputMaybe<SortOrder>;
     id?: InputMaybe<SortOrder>;
     name?: InputMaybe<SortOrder>;
+    parentId?: InputMaybe<SortOrder>;
+    type?: InputMaybe<SortOrder>;
     updatedAt?: InputMaybe<SortOrder>;
 };
 
-export type CountryTranslation = {
-    createdAt: Scalars['DateTime'];
-    id: Scalars['ID'];
-    languageCode: LanguageCode;
-    name: Scalars['String'];
-    updatedAt: Scalars['DateTime'];
-};
-
 export type CountryTranslationInput = {
     customFields?: InputMaybe<Scalars['JSON']>;
     id?: InputMaybe<Scalars['ID']>;
@@ -793,6 +795,13 @@ export type CreatePromotionInput = {
 
 export type CreatePromotionResult = MissingConditionsError | Promotion;
 
+export type CreateProvinceInput = {
+    code: Scalars['String'];
+    customFields?: InputMaybe<Scalars['JSON']>;
+    enabled: Scalars['Boolean'];
+    translations: Array<ProvinceTranslationInput>;
+};
+
 export type CreateRoleInput = {
     channelIds?: InputMaybe<Array<Scalars['ID']>>;
     code: Scalars['String'];
@@ -1211,7 +1220,6 @@ export type CustomFields = {
     Asset: Array<CustomFieldConfig>;
     Channel: Array<CustomFieldConfig>;
     Collection: Array<CustomFieldConfig>;
-    Country: Array<CustomFieldConfig>;
     Customer: Array<CustomFieldConfig>;
     CustomerGroup: Array<CustomFieldConfig>;
     Facet: Array<CustomFieldConfig>;
@@ -1226,6 +1234,7 @@ export type CustomFields = {
     ProductOptionGroup: Array<CustomFieldConfig>;
     ProductVariant: Array<CustomFieldConfig>;
     Promotion: Array<CustomFieldConfig>;
+    Region: Array<CustomFieldConfig>;
     Seller: Array<CustomFieldConfig>;
     ShippingMethod: Array<CustomFieldConfig>;
     StockLocation: Array<CustomFieldConfig>;
@@ -2470,6 +2479,8 @@ export type Mutation = {
     /** Create a set of ProductVariants based on the OptionGroups assigned to the given Product */
     createProductVariants: Array<Maybe<ProductVariant>>;
     createPromotion: CreatePromotionResult;
+    /** Create a new Province */
+    createProvince: Province;
     /** Create a new Role */
     createRole: Role;
     /** Create a new Seller */
@@ -2528,6 +2539,8 @@ export type Mutation = {
     /** Delete multiple Products */
     deleteProducts: Array<DeletionResponse>;
     deletePromotion: DeletionResponse;
+    /** Delete a Province */
+    deleteProvince: DeletionResponse;
     /** Delete an existing Role */
     deleteRole: DeletionResponse;
     /** Delete a Seller */
@@ -2633,6 +2646,8 @@ export type Mutation = {
     /** Update multiple existing Products */
     updateProducts: Array<Product>;
     updatePromotion: UpdatePromotionResult;
+    /** Update an existing Province */
+    updateProvince: Province;
     /** Update an existing Role */
     updateRole: Role;
     /** Update an existing Seller */
@@ -2808,6 +2823,10 @@ export type MutationCreatePromotionArgs = {
     input: CreatePromotionInput;
 };
 
+export type MutationCreateProvinceArgs = {
+    input: CreateProvinceInput;
+};
+
 export type MutationCreateRoleArgs = {
     input: CreateRoleInput;
 };
@@ -2936,6 +2955,10 @@ export type MutationDeletePromotionArgs = {
     id: Scalars['ID'];
 };
 
+export type MutationDeleteProvinceArgs = {
+    id: Scalars['ID'];
+};
+
 export type MutationDeleteRoleArgs = {
     id: Scalars['ID'];
 };
@@ -3181,6 +3204,10 @@ export type MutationUpdatePromotionArgs = {
     input: UpdatePromotionInput;
 };
 
+export type MutationUpdateProvinceArgs = {
+    input: UpdateProvinceInput;
+};
+
 export type MutationUpdateRoleArgs = {
     input: UpdateRoleInput;
 };
@@ -4277,6 +4304,69 @@ export type PromotionTranslationInput = {
     name?: InputMaybe<Scalars['String']>;
 };
 
+export type Province = Node &
+    Region & {
+        code: Scalars['String'];
+        createdAt: Scalars['DateTime'];
+        customFields?: Maybe<Scalars['JSON']>;
+        enabled: Scalars['Boolean'];
+        id: Scalars['ID'];
+        languageCode: LanguageCode;
+        name: Scalars['String'];
+        parent?: Maybe<Region>;
+        parentId?: Maybe<Scalars['ID']>;
+        translations: Array<RegionTranslation>;
+        type: Scalars['String'];
+        updatedAt: Scalars['DateTime'];
+    };
+
+export type ProvinceFilterParameter = {
+    code?: InputMaybe<StringOperators>;
+    createdAt?: InputMaybe<DateOperators>;
+    enabled?: InputMaybe<BooleanOperators>;
+    id?: InputMaybe<IdOperators>;
+    languageCode?: InputMaybe<StringOperators>;
+    name?: InputMaybe<StringOperators>;
+    parentId?: InputMaybe<IdOperators>;
+    type?: InputMaybe<StringOperators>;
+    updatedAt?: InputMaybe<DateOperators>;
+};
+
+export type ProvinceList = PaginatedList & {
+    items: Array<Province>;
+    totalItems: Scalars['Int'];
+};
+
+export type ProvinceListOptions = {
+    /** Allows the results to be filtered */
+    filter?: InputMaybe<ProvinceFilterParameter>;
+    /** Specifies whether multiple "filter" arguments should be combines with a logical AND or OR operation. Defaults to AND. */
+    filterOperator?: InputMaybe<LogicalOperator>;
+    /** Skips the first n results, for use in pagination */
+    skip?: InputMaybe<Scalars['Int']>;
+    /** Specifies which properties to sort the results by */
+    sort?: InputMaybe<ProvinceSortParameter>;
+    /** Takes n results, for use in pagination */
+    take?: InputMaybe<Scalars['Int']>;
+};
+
+export type ProvinceSortParameter = {
+    code?: InputMaybe<SortOrder>;
+    createdAt?: InputMaybe<SortOrder>;
+    id?: InputMaybe<SortOrder>;
+    name?: InputMaybe<SortOrder>;
+    parentId?: InputMaybe<SortOrder>;
+    type?: InputMaybe<SortOrder>;
+    updatedAt?: InputMaybe<SortOrder>;
+};
+
+export type ProvinceTranslationInput = {
+    customFields?: InputMaybe<Scalars['JSON']>;
+    id?: InputMaybe<Scalars['ID']>;
+    languageCode: LanguageCode;
+    name?: InputMaybe<Scalars['String']>;
+};
+
 /** Returned if the specified quantity of an OrderLine is greater than the number of items in that line */
 export type QuantityTooGreatError = ErrorResult & {
     errorCode: ErrorCode;
@@ -4340,6 +4430,8 @@ export type Query = {
     promotionActions: Array<ConfigurableOperationDefinition>;
     promotionConditions: Array<ConfigurableOperationDefinition>;
     promotions: PromotionList;
+    province?: Maybe<Province>;
+    provinces: ProvinceList;
     role?: Maybe<Role>;
     roles: RoleList;
     search: SearchResponse;
@@ -4503,6 +4595,14 @@ export type QueryPromotionsArgs = {
     options?: InputMaybe<PromotionListOptions>;
 };
 
+export type QueryProvinceArgs = {
+    id: Scalars['ID'];
+};
+
+export type QueryProvincesArgs = {
+    options?: InputMaybe<ProvinceListOptions>;
+};
+
 export type QueryRoleArgs = {
     id: Scalars['ID'];
 };
@@ -4640,6 +4740,28 @@ export type RefundStateTransitionError = ErrorResult & {
     transitionError: Scalars['String'];
 };
 
+export type Region = {
+    code: Scalars['String'];
+    createdAt: Scalars['DateTime'];
+    enabled: Scalars['Boolean'];
+    id: Scalars['ID'];
+    languageCode: LanguageCode;
+    name: Scalars['String'];
+    parent?: Maybe<Region>;
+    parentId?: Maybe<Scalars['ID']>;
+    translations: Array<RegionTranslation>;
+    type: Scalars['String'];
+    updatedAt: Scalars['DateTime'];
+};
+
+export type RegionTranslation = {
+    createdAt: Scalars['DateTime'];
+    id: Scalars['ID'];
+    languageCode: LanguageCode;
+    name: Scalars['String'];
+    updatedAt: Scalars['DateTime'];
+};
+
 export type RelationCustomFieldConfig = CustomField & {
     description?: Maybe<Array<LocalizedString>>;
     entity: Scalars['String'];
@@ -5544,6 +5666,14 @@ export type UpdatePromotionInput = {
 
 export type UpdatePromotionResult = MissingConditionsError | Promotion;
 
+export type UpdateProvinceInput = {
+    code?: InputMaybe<Scalars['String']>;
+    customFields?: InputMaybe<Scalars['JSON']>;
+    enabled?: InputMaybe<Scalars['Boolean']>;
+    id: Scalars['ID'];
+    translations?: InputMaybe<Array<ProvinceTranslationInput>>;
+};
+
 export type UpdateRoleInput = {
     channelIds?: InputMaybe<Array<Scalars['ID']>>;
     code?: InputMaybe<Scalars['String']>;
@@ -5620,7 +5750,7 @@ export type Zone = Node & {
     createdAt: Scalars['DateTime'];
     customFields?: Maybe<Scalars['JSON']>;
     id: Scalars['ID'];
-    members: Array<Country>;
+    members: Array<Region>;
     name: Scalars['String'];
     updatedAt: Scalars['DateTime'];
 };

+ 65 - 22
packages/common/src/generated-shop-types.ts

@@ -147,8 +147,10 @@ export type Channel = Node & {
     __typename?: 'Channel';
     code: Scalars['String'];
     createdAt: Scalars['DateTime'];
+    /** @deprecated Use defaultCurrencyCode instead */
     currencyCode: CurrencyCode;
     customFields?: Maybe<Scalars['JSON']>;
+    defaultCurrencyCode: CurrencyCode;
     defaultLanguageCode: LanguageCode;
     defaultShippingZone?: Maybe<Zone>;
     defaultTaxZone?: Maybe<Zone>;
@@ -300,18 +302,22 @@ export type Coordinate = {
     y: Scalars['Float'];
 };
 
-export type Country = Node & {
-    __typename?: 'Country';
-    code: Scalars['String'];
-    createdAt: Scalars['DateTime'];
-    customFields?: Maybe<Scalars['JSON']>;
-    enabled: Scalars['Boolean'];
-    id: Scalars['ID'];
-    languageCode: LanguageCode;
-    name: Scalars['String'];
-    translations: Array<CountryTranslation>;
-    updatedAt: Scalars['DateTime'];
-};
+export type Country = Node &
+    Region & {
+        __typename?: 'Country';
+        code: Scalars['String'];
+        createdAt: Scalars['DateTime'];
+        customFields?: Maybe<Scalars['JSON']>;
+        enabled: Scalars['Boolean'];
+        id: Scalars['ID'];
+        languageCode: LanguageCode;
+        name: Scalars['String'];
+        parent?: Maybe<Region>;
+        parentId?: Maybe<Scalars['ID']>;
+        translations: Array<RegionTranslation>;
+        type: Scalars['String'];
+        updatedAt: Scalars['DateTime'];
+    };
 
 export type CountryList = PaginatedList & {
     __typename?: 'CountryList';
@@ -319,15 +325,6 @@ export type CountryList = PaginatedList & {
     totalItems: Scalars['Int'];
 };
 
-export type CountryTranslation = {
-    __typename?: 'CountryTranslation';
-    createdAt: Scalars['DateTime'];
-    id: Scalars['ID'];
-    languageCode: LanguageCode;
-    name: Scalars['String'];
-    updatedAt: Scalars['DateTime'];
-};
-
 /** Returned if the provided coupon code is invalid */
 export type CouponCodeExpiredError = ErrorResult & {
     __typename?: 'CouponCodeExpiredError';
@@ -2728,6 +2725,29 @@ export type PromotionTranslation = {
     updatedAt: Scalars['DateTime'];
 };
 
+export type Province = Node &
+    Region & {
+        __typename?: 'Province';
+        code: Scalars['String'];
+        createdAt: Scalars['DateTime'];
+        customFields?: Maybe<Scalars['JSON']>;
+        enabled: Scalars['Boolean'];
+        id: Scalars['ID'];
+        languageCode: LanguageCode;
+        name: Scalars['String'];
+        parent?: Maybe<Region>;
+        parentId?: Maybe<Scalars['ID']>;
+        translations: Array<RegionTranslation>;
+        type: Scalars['String'];
+        updatedAt: Scalars['DateTime'];
+    };
+
+export type ProvinceList = PaginatedList & {
+    __typename?: 'ProvinceList';
+    items: Array<Province>;
+    totalItems: Scalars['Int'];
+};
+
 export type Query = {
     __typename?: 'Query';
     /** The active Channel */
@@ -2845,6 +2865,29 @@ export type RefundLine = {
     refundId: Scalars['ID'];
 };
 
+export type Region = {
+    code: Scalars['String'];
+    createdAt: Scalars['DateTime'];
+    enabled: Scalars['Boolean'];
+    id: Scalars['ID'];
+    languageCode: LanguageCode;
+    name: Scalars['String'];
+    parent?: Maybe<Region>;
+    parentId?: Maybe<Scalars['ID']>;
+    translations: Array<RegionTranslation>;
+    type: Scalars['String'];
+    updatedAt: Scalars['DateTime'];
+};
+
+export type RegionTranslation = {
+    __typename?: 'RegionTranslation';
+    createdAt: Scalars['DateTime'];
+    id: Scalars['ID'];
+    languageCode: LanguageCode;
+    name: Scalars['String'];
+    updatedAt: Scalars['DateTime'];
+};
+
 export type RegisterCustomerAccountResult =
     | MissingPasswordError
     | NativeAuthStrategyError
@@ -3281,7 +3324,7 @@ export type Zone = Node & {
     createdAt: Scalars['DateTime'];
     customFields?: Maybe<Scalars['JSON']>;
     id: Scalars['ID'];
-    members: Array<Country>;
+    members: Array<Region>;
     name: Scalars['String'];
     updatedAt: Scalars['DateTime'];
 };

+ 148 - 13
packages/common/src/generated-types.ts

@@ -349,8 +349,10 @@ export type Channel = Node & {
   __typename?: 'Channel';
   code: Scalars['String'];
   createdAt: Scalars['DateTime'];
+  /** @deprecated Use defaultCurrencyCode instead */
   currencyCode: CurrencyCode;
   customFields?: Maybe<Scalars['JSON']>;
+  defaultCurrencyCode: CurrencyCode;
   defaultLanguageCode: LanguageCode;
   defaultShippingZone?: Maybe<Zone>;
   defaultTaxZone?: Maybe<Zone>;
@@ -524,7 +526,7 @@ export type CoordinateInput = {
   y: Scalars['Float'];
 };
 
-export type Country = Node & {
+export type Country = Node & Region & {
   __typename?: 'Country';
   code: Scalars['String'];
   createdAt: Scalars['DateTime'];
@@ -533,7 +535,10 @@ export type Country = Node & {
   id: Scalars['ID'];
   languageCode: LanguageCode;
   name: Scalars['String'];
-  translations: Array<CountryTranslation>;
+  parent?: Maybe<Region>;
+  parentId?: Maybe<Scalars['ID']>;
+  translations: Array<RegionTranslation>;
+  type: Scalars['String'];
   updatedAt: Scalars['DateTime'];
 };
 
@@ -544,6 +549,8 @@ export type CountryFilterParameter = {
   id?: InputMaybe<IdOperators>;
   languageCode?: InputMaybe<StringOperators>;
   name?: InputMaybe<StringOperators>;
+  parentId?: InputMaybe<IdOperators>;
+  type?: InputMaybe<StringOperators>;
   updatedAt?: InputMaybe<DateOperators>;
 };
 
@@ -571,18 +578,11 @@ export type CountrySortParameter = {
   createdAt?: InputMaybe<SortOrder>;
   id?: InputMaybe<SortOrder>;
   name?: InputMaybe<SortOrder>;
+  parentId?: InputMaybe<SortOrder>;
+  type?: InputMaybe<SortOrder>;
   updatedAt?: InputMaybe<SortOrder>;
 };
 
-export type CountryTranslation = {
-  __typename?: 'CountryTranslation';
-  createdAt: Scalars['DateTime'];
-  id: Scalars['ID'];
-  languageCode: LanguageCode;
-  name: Scalars['String'];
-  updatedAt: Scalars['DateTime'];
-};
-
 export type CountryTranslationInput = {
   customFields?: InputMaybe<Scalars['JSON']>;
   id?: InputMaybe<Scalars['ID']>;
@@ -807,6 +807,13 @@ export type CreatePromotionInput = {
 
 export type CreatePromotionResult = MissingConditionsError | Promotion;
 
+export type CreateProvinceInput = {
+  code: Scalars['String'];
+  customFields?: InputMaybe<Scalars['JSON']>;
+  enabled: Scalars['Boolean'];
+  translations: Array<ProvinceTranslationInput>;
+};
+
 export type CreateRoleInput = {
   channelIds?: InputMaybe<Array<Scalars['ID']>>;
   code: Scalars['String'];
@@ -1219,7 +1226,6 @@ export type CustomFields = {
   Asset: Array<CustomFieldConfig>;
   Channel: Array<CustomFieldConfig>;
   Collection: Array<CustomFieldConfig>;
-  Country: Array<CustomFieldConfig>;
   Customer: Array<CustomFieldConfig>;
   CustomerGroup: Array<CustomFieldConfig>;
   Facet: Array<CustomFieldConfig>;
@@ -1234,6 +1240,7 @@ export type CustomFields = {
   ProductOptionGroup: Array<CustomFieldConfig>;
   ProductVariant: Array<CustomFieldConfig>;
   Promotion: Array<CustomFieldConfig>;
+  Region: Array<CustomFieldConfig>;
   Seller: Array<CustomFieldConfig>;
   ShippingMethod: Array<CustomFieldConfig>;
   StockLocation: Array<CustomFieldConfig>;
@@ -2516,6 +2523,8 @@ export type Mutation = {
   /** Create a set of ProductVariants based on the OptionGroups assigned to the given Product */
   createProductVariants: Array<Maybe<ProductVariant>>;
   createPromotion: CreatePromotionResult;
+  /** Create a new Province */
+  createProvince: Province;
   /** Create a new Role */
   createRole: Role;
   /** Create a new Seller */
@@ -2574,6 +2583,8 @@ export type Mutation = {
   /** Delete multiple Products */
   deleteProducts: Array<DeletionResponse>;
   deletePromotion: DeletionResponse;
+  /** Delete a Province */
+  deleteProvince: DeletionResponse;
   /** Delete an existing Role */
   deleteRole: DeletionResponse;
   /** Delete a Seller */
@@ -2679,6 +2690,8 @@ export type Mutation = {
   /** Update multiple existing Products */
   updateProducts: Array<Product>;
   updatePromotion: UpdatePromotionResult;
+  /** Update an existing Province */
+  updateProvince: Province;
   /** Update an existing Role */
   updateRole: Role;
   /** Update an existing Seller */
@@ -2892,6 +2905,11 @@ export type MutationCreatePromotionArgs = {
 };
 
 
+export type MutationCreateProvinceArgs = {
+  input: CreateProvinceInput;
+};
+
+
 export type MutationCreateRoleArgs = {
   input: CreateRoleInput;
 };
@@ -3051,6 +3069,11 @@ export type MutationDeletePromotionArgs = {
 };
 
 
+export type MutationDeleteProvinceArgs = {
+  id: Scalars['ID'];
+};
+
+
 export type MutationDeleteRoleArgs = {
   id: Scalars['ID'];
 };
@@ -3353,6 +3376,11 @@ export type MutationUpdatePromotionArgs = {
 };
 
 
+export type MutationUpdateProvinceArgs = {
+  input: UpdateProvinceInput;
+};
+
+
 export type MutationUpdateRoleArgs = {
   input: UpdateRoleInput;
 };
@@ -4501,6 +4529,70 @@ export type PromotionTranslationInput = {
   name?: InputMaybe<Scalars['String']>;
 };
 
+export type Province = Node & Region & {
+  __typename?: 'Province';
+  code: Scalars['String'];
+  createdAt: Scalars['DateTime'];
+  customFields?: Maybe<Scalars['JSON']>;
+  enabled: Scalars['Boolean'];
+  id: Scalars['ID'];
+  languageCode: LanguageCode;
+  name: Scalars['String'];
+  parent?: Maybe<Region>;
+  parentId?: Maybe<Scalars['ID']>;
+  translations: Array<RegionTranslation>;
+  type: Scalars['String'];
+  updatedAt: Scalars['DateTime'];
+};
+
+export type ProvinceFilterParameter = {
+  code?: InputMaybe<StringOperators>;
+  createdAt?: InputMaybe<DateOperators>;
+  enabled?: InputMaybe<BooleanOperators>;
+  id?: InputMaybe<IdOperators>;
+  languageCode?: InputMaybe<StringOperators>;
+  name?: InputMaybe<StringOperators>;
+  parentId?: InputMaybe<IdOperators>;
+  type?: InputMaybe<StringOperators>;
+  updatedAt?: InputMaybe<DateOperators>;
+};
+
+export type ProvinceList = PaginatedList & {
+  __typename?: 'ProvinceList';
+  items: Array<Province>;
+  totalItems: Scalars['Int'];
+};
+
+export type ProvinceListOptions = {
+  /** Allows the results to be filtered */
+  filter?: InputMaybe<ProvinceFilterParameter>;
+  /** Specifies whether multiple "filter" arguments should be combines with a logical AND or OR operation. Defaults to AND. */
+  filterOperator?: InputMaybe<LogicalOperator>;
+  /** Skips the first n results, for use in pagination */
+  skip?: InputMaybe<Scalars['Int']>;
+  /** Specifies which properties to sort the results by */
+  sort?: InputMaybe<ProvinceSortParameter>;
+  /** Takes n results, for use in pagination */
+  take?: InputMaybe<Scalars['Int']>;
+};
+
+export type ProvinceSortParameter = {
+  code?: InputMaybe<SortOrder>;
+  createdAt?: InputMaybe<SortOrder>;
+  id?: InputMaybe<SortOrder>;
+  name?: InputMaybe<SortOrder>;
+  parentId?: InputMaybe<SortOrder>;
+  type?: InputMaybe<SortOrder>;
+  updatedAt?: InputMaybe<SortOrder>;
+};
+
+export type ProvinceTranslationInput = {
+  customFields?: InputMaybe<Scalars['JSON']>;
+  id?: InputMaybe<Scalars['ID']>;
+  languageCode: LanguageCode;
+  name?: InputMaybe<Scalars['String']>;
+};
+
 /** Returned if the specified quantity of an OrderLine is greater than the number of items in that line */
 export type QuantityTooGreatError = ErrorResult & {
   __typename?: 'QuantityTooGreatError';
@@ -4566,6 +4658,8 @@ export type Query = {
   promotionActions: Array<ConfigurableOperationDefinition>;
   promotionConditions: Array<ConfigurableOperationDefinition>;
   promotions: PromotionList;
+  province?: Maybe<Province>;
+  provinces: ProvinceList;
   role?: Maybe<Role>;
   roles: RoleList;
   search: SearchResponse;
@@ -4764,6 +4858,16 @@ export type QueryPromotionsArgs = {
 };
 
 
+export type QueryProvinceArgs = {
+  id: Scalars['ID'];
+};
+
+
+export type QueryProvincesArgs = {
+  options?: InputMaybe<ProvinceListOptions>;
+};
+
+
 export type QueryRoleArgs = {
   id: Scalars['ID'];
 };
@@ -4913,6 +5017,29 @@ export type RefundStateTransitionError = ErrorResult & {
   transitionError: Scalars['String'];
 };
 
+export type Region = {
+  code: Scalars['String'];
+  createdAt: Scalars['DateTime'];
+  enabled: Scalars['Boolean'];
+  id: Scalars['ID'];
+  languageCode: LanguageCode;
+  name: Scalars['String'];
+  parent?: Maybe<Region>;
+  parentId?: Maybe<Scalars['ID']>;
+  translations: Array<RegionTranslation>;
+  type: Scalars['String'];
+  updatedAt: Scalars['DateTime'];
+};
+
+export type RegionTranslation = {
+  __typename?: 'RegionTranslation';
+  createdAt: Scalars['DateTime'];
+  id: Scalars['ID'];
+  languageCode: LanguageCode;
+  name: Scalars['String'];
+  updatedAt: Scalars['DateTime'];
+};
+
 export type RelationCustomFieldConfig = CustomField & {
   __typename?: 'RelationCustomFieldConfig';
   description?: Maybe<Array<LocalizedString>>;
@@ -5838,6 +5965,14 @@ export type UpdatePromotionInput = {
 
 export type UpdatePromotionResult = MissingConditionsError | Promotion;
 
+export type UpdateProvinceInput = {
+  code?: InputMaybe<Scalars['String']>;
+  customFields?: InputMaybe<Scalars['JSON']>;
+  enabled?: InputMaybe<Scalars['Boolean']>;
+  id: Scalars['ID'];
+  translations?: InputMaybe<Array<ProvinceTranslationInput>>;
+};
+
 export type UpdateRoleInput = {
   channelIds?: InputMaybe<Array<Scalars['ID']>>;
   code?: InputMaybe<Scalars['String']>;
@@ -5916,7 +6051,7 @@ export type Zone = Node & {
   createdAt: Scalars['DateTime'];
   customFields?: Maybe<Scalars['JSON']>;
   id: Scalars['ID'];
-  members: Array<Country>;
+  members: Array<Region>;
   name: Scalars['String'];
   updatedAt: Scalars['DateTime'];
 };

+ 212 - 64
packages/core/e2e/graphql/generated-e2e-admin-types.ts

@@ -354,8 +354,10 @@ export type Cancellation = Node &
 export type Channel = Node & {
     code: Scalars['String'];
     createdAt: Scalars['DateTime'];
+    /** @deprecated Use defaultCurrencyCode instead */
     currencyCode: CurrencyCode;
     customFields?: Maybe<Scalars['JSON']>;
+    defaultCurrencyCode: CurrencyCode;
     defaultLanguageCode: LanguageCode;
     defaultShippingZone?: Maybe<Zone>;
     defaultTaxZone?: Maybe<Zone>;
@@ -517,17 +519,21 @@ export type CoordinateInput = {
     y: Scalars['Float'];
 };
 
-export type Country = Node & {
-    code: Scalars['String'];
-    createdAt: Scalars['DateTime'];
-    customFields?: Maybe<Scalars['JSON']>;
-    enabled: Scalars['Boolean'];
-    id: Scalars['ID'];
-    languageCode: LanguageCode;
-    name: Scalars['String'];
-    translations: Array<CountryTranslation>;
-    updatedAt: Scalars['DateTime'];
-};
+export type Country = Node &
+    Region & {
+        code: Scalars['String'];
+        createdAt: Scalars['DateTime'];
+        customFields?: Maybe<Scalars['JSON']>;
+        enabled: Scalars['Boolean'];
+        id: Scalars['ID'];
+        languageCode: LanguageCode;
+        name: Scalars['String'];
+        parent?: Maybe<Region>;
+        parentId?: Maybe<Scalars['ID']>;
+        translations: Array<RegionTranslation>;
+        type: Scalars['String'];
+        updatedAt: Scalars['DateTime'];
+    };
 
 export type CountryFilterParameter = {
     code?: InputMaybe<StringOperators>;
@@ -536,6 +542,8 @@ export type CountryFilterParameter = {
     id?: InputMaybe<IdOperators>;
     languageCode?: InputMaybe<StringOperators>;
     name?: InputMaybe<StringOperators>;
+    parentId?: InputMaybe<IdOperators>;
+    type?: InputMaybe<StringOperators>;
     updatedAt?: InputMaybe<DateOperators>;
 };
 
@@ -562,17 +570,11 @@ export type CountrySortParameter = {
     createdAt?: InputMaybe<SortOrder>;
     id?: InputMaybe<SortOrder>;
     name?: InputMaybe<SortOrder>;
+    parentId?: InputMaybe<SortOrder>;
+    type?: InputMaybe<SortOrder>;
     updatedAt?: InputMaybe<SortOrder>;
 };
 
-export type CountryTranslation = {
-    createdAt: Scalars['DateTime'];
-    id: Scalars['ID'];
-    languageCode: LanguageCode;
-    name: Scalars['String'];
-    updatedAt: Scalars['DateTime'];
-};
-
 export type CountryTranslationInput = {
     customFields?: InputMaybe<Scalars['JSON']>;
     id?: InputMaybe<Scalars['ID']>;
@@ -793,6 +795,13 @@ export type CreatePromotionInput = {
 
 export type CreatePromotionResult = MissingConditionsError | Promotion;
 
+export type CreateProvinceInput = {
+    code: Scalars['String'];
+    customFields?: InputMaybe<Scalars['JSON']>;
+    enabled: Scalars['Boolean'];
+    translations: Array<ProvinceTranslationInput>;
+};
+
 export type CreateRoleInput = {
     channelIds?: InputMaybe<Array<Scalars['ID']>>;
     code: Scalars['String'];
@@ -1211,7 +1220,6 @@ export type CustomFields = {
     Asset: Array<CustomFieldConfig>;
     Channel: Array<CustomFieldConfig>;
     Collection: Array<CustomFieldConfig>;
-    Country: Array<CustomFieldConfig>;
     Customer: Array<CustomFieldConfig>;
     CustomerGroup: Array<CustomFieldConfig>;
     Facet: Array<CustomFieldConfig>;
@@ -1226,6 +1234,7 @@ export type CustomFields = {
     ProductOptionGroup: Array<CustomFieldConfig>;
     ProductVariant: Array<CustomFieldConfig>;
     Promotion: Array<CustomFieldConfig>;
+    Region: Array<CustomFieldConfig>;
     Seller: Array<CustomFieldConfig>;
     ShippingMethod: Array<CustomFieldConfig>;
     StockLocation: Array<CustomFieldConfig>;
@@ -2470,6 +2479,8 @@ export type Mutation = {
     /** Create a set of ProductVariants based on the OptionGroups assigned to the given Product */
     createProductVariants: Array<Maybe<ProductVariant>>;
     createPromotion: CreatePromotionResult;
+    /** Create a new Province */
+    createProvince: Province;
     /** Create a new Role */
     createRole: Role;
     /** Create a new Seller */
@@ -2528,6 +2539,8 @@ export type Mutation = {
     /** Delete multiple Products */
     deleteProducts: Array<DeletionResponse>;
     deletePromotion: DeletionResponse;
+    /** Delete a Province */
+    deleteProvince: DeletionResponse;
     /** Delete an existing Role */
     deleteRole: DeletionResponse;
     /** Delete a Seller */
@@ -2633,6 +2646,8 @@ export type Mutation = {
     /** Update multiple existing Products */
     updateProducts: Array<Product>;
     updatePromotion: UpdatePromotionResult;
+    /** Update an existing Province */
+    updateProvince: Province;
     /** Update an existing Role */
     updateRole: Role;
     /** Update an existing Seller */
@@ -2808,6 +2823,10 @@ export type MutationCreatePromotionArgs = {
     input: CreatePromotionInput;
 };
 
+export type MutationCreateProvinceArgs = {
+    input: CreateProvinceInput;
+};
+
 export type MutationCreateRoleArgs = {
     input: CreateRoleInput;
 };
@@ -2936,6 +2955,10 @@ export type MutationDeletePromotionArgs = {
     id: Scalars['ID'];
 };
 
+export type MutationDeleteProvinceArgs = {
+    id: Scalars['ID'];
+};
+
 export type MutationDeleteRoleArgs = {
     id: Scalars['ID'];
 };
@@ -3181,6 +3204,10 @@ export type MutationUpdatePromotionArgs = {
     input: UpdatePromotionInput;
 };
 
+export type MutationUpdateProvinceArgs = {
+    input: UpdateProvinceInput;
+};
+
 export type MutationUpdateRoleArgs = {
     input: UpdateRoleInput;
 };
@@ -4277,6 +4304,69 @@ export type PromotionTranslationInput = {
     name?: InputMaybe<Scalars['String']>;
 };
 
+export type Province = Node &
+    Region & {
+        code: Scalars['String'];
+        createdAt: Scalars['DateTime'];
+        customFields?: Maybe<Scalars['JSON']>;
+        enabled: Scalars['Boolean'];
+        id: Scalars['ID'];
+        languageCode: LanguageCode;
+        name: Scalars['String'];
+        parent?: Maybe<Region>;
+        parentId?: Maybe<Scalars['ID']>;
+        translations: Array<RegionTranslation>;
+        type: Scalars['String'];
+        updatedAt: Scalars['DateTime'];
+    };
+
+export type ProvinceFilterParameter = {
+    code?: InputMaybe<StringOperators>;
+    createdAt?: InputMaybe<DateOperators>;
+    enabled?: InputMaybe<BooleanOperators>;
+    id?: InputMaybe<IdOperators>;
+    languageCode?: InputMaybe<StringOperators>;
+    name?: InputMaybe<StringOperators>;
+    parentId?: InputMaybe<IdOperators>;
+    type?: InputMaybe<StringOperators>;
+    updatedAt?: InputMaybe<DateOperators>;
+};
+
+export type ProvinceList = PaginatedList & {
+    items: Array<Province>;
+    totalItems: Scalars['Int'];
+};
+
+export type ProvinceListOptions = {
+    /** Allows the results to be filtered */
+    filter?: InputMaybe<ProvinceFilterParameter>;
+    /** Specifies whether multiple "filter" arguments should be combines with a logical AND or OR operation. Defaults to AND. */
+    filterOperator?: InputMaybe<LogicalOperator>;
+    /** Skips the first n results, for use in pagination */
+    skip?: InputMaybe<Scalars['Int']>;
+    /** Specifies which properties to sort the results by */
+    sort?: InputMaybe<ProvinceSortParameter>;
+    /** Takes n results, for use in pagination */
+    take?: InputMaybe<Scalars['Int']>;
+};
+
+export type ProvinceSortParameter = {
+    code?: InputMaybe<SortOrder>;
+    createdAt?: InputMaybe<SortOrder>;
+    id?: InputMaybe<SortOrder>;
+    name?: InputMaybe<SortOrder>;
+    parentId?: InputMaybe<SortOrder>;
+    type?: InputMaybe<SortOrder>;
+    updatedAt?: InputMaybe<SortOrder>;
+};
+
+export type ProvinceTranslationInput = {
+    customFields?: InputMaybe<Scalars['JSON']>;
+    id?: InputMaybe<Scalars['ID']>;
+    languageCode: LanguageCode;
+    name?: InputMaybe<Scalars['String']>;
+};
+
 /** Returned if the specified quantity of an OrderLine is greater than the number of items in that line */
 export type QuantityTooGreatError = ErrorResult & {
     errorCode: ErrorCode;
@@ -4340,6 +4430,8 @@ export type Query = {
     promotionActions: Array<ConfigurableOperationDefinition>;
     promotionConditions: Array<ConfigurableOperationDefinition>;
     promotions: PromotionList;
+    province?: Maybe<Province>;
+    provinces: ProvinceList;
     role?: Maybe<Role>;
     roles: RoleList;
     search: SearchResponse;
@@ -4503,6 +4595,14 @@ export type QueryPromotionsArgs = {
     options?: InputMaybe<PromotionListOptions>;
 };
 
+export type QueryProvinceArgs = {
+    id: Scalars['ID'];
+};
+
+export type QueryProvincesArgs = {
+    options?: InputMaybe<ProvinceListOptions>;
+};
+
 export type QueryRoleArgs = {
     id: Scalars['ID'];
 };
@@ -4640,6 +4740,28 @@ export type RefundStateTransitionError = ErrorResult & {
     transitionError: Scalars['String'];
 };
 
+export type Region = {
+    code: Scalars['String'];
+    createdAt: Scalars['DateTime'];
+    enabled: Scalars['Boolean'];
+    id: Scalars['ID'];
+    languageCode: LanguageCode;
+    name: Scalars['String'];
+    parent?: Maybe<Region>;
+    parentId?: Maybe<Scalars['ID']>;
+    translations: Array<RegionTranslation>;
+    type: Scalars['String'];
+    updatedAt: Scalars['DateTime'];
+};
+
+export type RegionTranslation = {
+    createdAt: Scalars['DateTime'];
+    id: Scalars['ID'];
+    languageCode: LanguageCode;
+    name: Scalars['String'];
+    updatedAt: Scalars['DateTime'];
+};
+
 export type RelationCustomFieldConfig = CustomField & {
     description?: Maybe<Array<LocalizedString>>;
     entity: Scalars['String'];
@@ -5544,6 +5666,14 @@ export type UpdatePromotionInput = {
 
 export type UpdatePromotionResult = MissingConditionsError | Promotion;
 
+export type UpdateProvinceInput = {
+    code?: InputMaybe<Scalars['String']>;
+    customFields?: InputMaybe<Scalars['JSON']>;
+    enabled?: InputMaybe<Scalars['Boolean']>;
+    id: Scalars['ID'];
+    translations?: InputMaybe<Array<ProvinceTranslationInput>>;
+};
+
 export type UpdateRoleInput = {
     channelIds?: InputMaybe<Array<Scalars['ID']>>;
     code?: InputMaybe<Scalars['String']>;
@@ -5620,7 +5750,7 @@ export type Zone = Node & {
     createdAt: Scalars['DateTime'];
     customFields?: Maybe<Scalars['JSON']>;
     id: Scalars['ID'];
-    members: Array<Country>;
+    members: Array<Region>;
     name: Scalars['String'];
     updatedAt: Scalars['DateTime'];
 };
@@ -7738,13 +7868,16 @@ export type PromotionFragment = {
 export type ZoneFragment = {
     id: string;
     name: string;
-    members: Array<{
-        id: string;
-        code: string;
-        name: string;
-        enabled: boolean;
-        translations: Array<{ id: string; languageCode: LanguageCode; name: string }>;
-    }>;
+    members: Array<
+        | {
+              id: string;
+              code: string;
+              name: string;
+              enabled: boolean;
+              translations: Array<{ id: string; languageCode: LanguageCode; name: string }>;
+          }
+        | {}
+    >;
 };
 
 export type TaxRateFragment = {
@@ -11110,13 +11243,16 @@ export type GetZoneQuery = {
     zone?: {
         id: string;
         name: string;
-        members: Array<{
-            id: string;
-            code: string;
-            name: string;
-            enabled: boolean;
-            translations: Array<{ id: string; languageCode: LanguageCode; name: string }>;
-        }>;
+        members: Array<
+            | {
+                  id: string;
+                  code: string;
+                  name: string;
+                  enabled: boolean;
+                  translations: Array<{ id: string; languageCode: LanguageCode; name: string }>;
+              }
+            | {}
+        >;
     } | null;
 };
 
@@ -11125,7 +11261,7 @@ export type GetActiveChannelWithZoneMembersQueryVariables = Exact<{ [key: string
 export type GetActiveChannelWithZoneMembersQuery = {
     activeChannel: {
         id: string;
-        defaultShippingZone?: { id: string; members: Array<{ name: string }> } | null;
+        defaultShippingZone?: { id: string; members: Array<{ name: string } | { name: string }> } | null;
     };
 };
 
@@ -11137,13 +11273,16 @@ export type CreateZoneMutation = {
     createZone: {
         id: string;
         name: string;
-        members: Array<{
-            id: string;
-            code: string;
-            name: string;
-            enabled: boolean;
-            translations: Array<{ id: string; languageCode: LanguageCode; name: string }>;
-        }>;
+        members: Array<
+            | {
+                  id: string;
+                  code: string;
+                  name: string;
+                  enabled: boolean;
+                  translations: Array<{ id: string; languageCode: LanguageCode; name: string }>;
+              }
+            | {}
+        >;
     };
 };
 
@@ -11155,13 +11294,16 @@ export type UpdateZoneMutation = {
     updateZone: {
         id: string;
         name: string;
-        members: Array<{
-            id: string;
-            code: string;
-            name: string;
-            enabled: boolean;
-            translations: Array<{ id: string; languageCode: LanguageCode; name: string }>;
-        }>;
+        members: Array<
+            | {
+                  id: string;
+                  code: string;
+                  name: string;
+                  enabled: boolean;
+                  translations: Array<{ id: string; languageCode: LanguageCode; name: string }>;
+              }
+            | {}
+        >;
     };
 };
 
@@ -11174,13 +11316,16 @@ export type AddMembersToZoneMutation = {
     addMembersToZone: {
         id: string;
         name: string;
-        members: Array<{
-            id: string;
-            code: string;
-            name: string;
-            enabled: boolean;
-            translations: Array<{ id: string; languageCode: LanguageCode; name: string }>;
-        }>;
+        members: Array<
+            | {
+                  id: string;
+                  code: string;
+                  name: string;
+                  enabled: boolean;
+                  translations: Array<{ id: string; languageCode: LanguageCode; name: string }>;
+              }
+            | {}
+        >;
     };
 };
 
@@ -11193,12 +11338,15 @@ export type RemoveMembersFromZoneMutation = {
     removeMembersFromZone: {
         id: string;
         name: string;
-        members: Array<{
-            id: string;
-            code: string;
-            name: string;
-            enabled: boolean;
-            translations: Array<{ id: string; languageCode: LanguageCode; name: string }>;
-        }>;
+        members: Array<
+            | {
+                  id: string;
+                  code: string;
+                  name: string;
+                  enabled: boolean;
+                  translations: Array<{ id: string; languageCode: LanguageCode; name: string }>;
+              }
+            | {}
+        >;
     };
 };

+ 61 - 20
packages/core/e2e/graphql/generated-e2e-shop-types.ts

@@ -139,8 +139,10 @@ export type BooleanOperators = {
 export type Channel = Node & {
     code: Scalars['String'];
     createdAt: Scalars['DateTime'];
+    /** @deprecated Use defaultCurrencyCode instead */
     currencyCode: CurrencyCode;
     customFields?: Maybe<Scalars['JSON']>;
+    defaultCurrencyCode: CurrencyCode;
     defaultLanguageCode: LanguageCode;
     defaultShippingZone?: Maybe<Zone>;
     defaultTaxZone?: Maybe<Zone>;
@@ -282,31 +284,27 @@ export type Coordinate = {
     y: Scalars['Float'];
 };
 
-export type Country = Node & {
-    code: Scalars['String'];
-    createdAt: Scalars['DateTime'];
-    customFields?: Maybe<Scalars['JSON']>;
-    enabled: Scalars['Boolean'];
-    id: Scalars['ID'];
-    languageCode: LanguageCode;
-    name: Scalars['String'];
-    translations: Array<CountryTranslation>;
-    updatedAt: Scalars['DateTime'];
-};
+export type Country = Node &
+    Region & {
+        code: Scalars['String'];
+        createdAt: Scalars['DateTime'];
+        customFields?: Maybe<Scalars['JSON']>;
+        enabled: Scalars['Boolean'];
+        id: Scalars['ID'];
+        languageCode: LanguageCode;
+        name: Scalars['String'];
+        parent?: Maybe<Region>;
+        parentId?: Maybe<Scalars['ID']>;
+        translations: Array<RegionTranslation>;
+        type: Scalars['String'];
+        updatedAt: Scalars['DateTime'];
+    };
 
 export type CountryList = PaginatedList & {
     items: Array<Country>;
     totalItems: Scalars['Int'];
 };
 
-export type CountryTranslation = {
-    createdAt: Scalars['DateTime'];
-    id: Scalars['ID'];
-    languageCode: LanguageCode;
-    name: Scalars['String'];
-    updatedAt: Scalars['DateTime'];
-};
-
 /** Returned if the provided coupon code is invalid */
 export type CouponCodeExpiredError = ErrorResult & {
     couponCode: Scalars['String'];
@@ -2633,6 +2631,27 @@ export type PromotionTranslation = {
     updatedAt: Scalars['DateTime'];
 };
 
+export type Province = Node &
+    Region & {
+        code: Scalars['String'];
+        createdAt: Scalars['DateTime'];
+        customFields?: Maybe<Scalars['JSON']>;
+        enabled: Scalars['Boolean'];
+        id: Scalars['ID'];
+        languageCode: LanguageCode;
+        name: Scalars['String'];
+        parent?: Maybe<Region>;
+        parentId?: Maybe<Scalars['ID']>;
+        translations: Array<RegionTranslation>;
+        type: Scalars['String'];
+        updatedAt: Scalars['DateTime'];
+    };
+
+export type ProvinceList = PaginatedList & {
+    items: Array<Province>;
+    totalItems: Scalars['Int'];
+};
+
 export type Query = {
     /** The active Channel */
     activeChannel: Channel;
@@ -2747,6 +2766,28 @@ export type RefundLine = {
     refundId: Scalars['ID'];
 };
 
+export type Region = {
+    code: Scalars['String'];
+    createdAt: Scalars['DateTime'];
+    enabled: Scalars['Boolean'];
+    id: Scalars['ID'];
+    languageCode: LanguageCode;
+    name: Scalars['String'];
+    parent?: Maybe<Region>;
+    parentId?: Maybe<Scalars['ID']>;
+    translations: Array<RegionTranslation>;
+    type: Scalars['String'];
+    updatedAt: Scalars['DateTime'];
+};
+
+export type RegionTranslation = {
+    createdAt: Scalars['DateTime'];
+    id: Scalars['ID'];
+    languageCode: LanguageCode;
+    name: Scalars['String'];
+    updatedAt: Scalars['DateTime'];
+};
+
 export type RegisterCustomerAccountResult =
     | MissingPasswordError
     | NativeAuthStrategyError
@@ -3154,7 +3195,7 @@ export type Zone = Node & {
     createdAt: Scalars['DateTime'];
     customFields?: Maybe<Scalars['JSON']>;
     id: Scalars['ID'];
-    members: Array<Country>;
+    members: Array<Region>;
     name: Scalars['String'];
     updatedAt: Scalars['DateTime'];
 };

+ 17 - 0
packages/core/src/api/config/generate-resolvers.ts

@@ -9,9 +9,11 @@ import {
     ErrorResult,
 } from '../../common/error/generated-graphql-admin-errors';
 import { shopErrorOperationTypeResolvers } from '../../common/error/generated-graphql-shop-errors';
+import { InternalServerError } from '../../common/index';
 import { Translatable } from '../../common/types/locale-types';
 import { ConfigService } from '../../config/config.service';
 import { CustomFieldConfig, RelationCustomFieldConfig } from '../../config/custom-field/custom-field-types';
+import { Region } from '../../entity/region/region.entity';
 import { getPluginAPIExtensions } from '../../plugin/plugin-metadata';
 import { CustomFieldRelationResolverService } from '../common/custom-field-relation-resolver.service';
 import { ApiType } from '../common/get-api-type';
@@ -57,6 +59,20 @@ export async function generateResolvers(
         },
     };
 
+    const regionResolveType = {
+        __resolveType(value: Region) {
+            switch (value.type) {
+                case 'country':
+                    return 'Country';
+                case 'province':
+                    return 'Province';
+                default: {
+                    throw new InternalServerError(`No __resolveType defined for Region type "${value.type}"`);
+                }
+            }
+        },
+    };
+
     const customFieldsConfigResolveType = {
         __resolveType(value: any) {
             switch (value.type) {
@@ -104,6 +120,7 @@ export async function generateResolvers(
                 return value.__typename;
             },
         },
+        Region: regionResolveType,
     };
 
     const customFieldRelationResolvers = generateCustomFieldRelationResolvers(

+ 15 - 1
packages/core/src/api/config/graphql-custom-fields.ts

@@ -5,6 +5,7 @@ import {
     GraphQLInputObjectType,
     GraphQLList,
     GraphQLSchema,
+    isInterfaceType,
     parse,
 } from 'graphql';
 
@@ -36,7 +37,20 @@ export function addGraphQLCustomFields(
         `;
     }
 
-    for (const entityName of Object.keys(customFieldConfig)) {
+    let entityNames = Object.keys(customFieldConfig);
+    if (entityNames.includes('Region')) {
+        // Region is an interface and cannot directly be extended. Instead we will use the
+        // concrete types that implement it.
+        const regionType = schema.getType('Region');
+        if (isInterfaceType(regionType)) {
+            const implementations = schema.getImplementations(regionType);
+            entityNames = entityNames
+                .filter(name => name !== 'Region')
+                .concat(implementations.objects.map(t => t.name));
+        }
+    }
+
+    for (const entityName of entityNames) {
         const customEntityFields = (customFieldConfig[entityName as keyof CustomFields] || []).filter(
             config => {
                 return !config.internal && (publicOnly === true ? config.public !== false : true);

+ 1 - 1
packages/core/src/api/resolvers/admin/country.resolver.ts

@@ -11,7 +11,7 @@ import {
 import { PaginatedList } from '@vendure/common/lib/shared-types';
 
 import { Translated } from '../../../common/types/locale-types';
-import { Country } from '../../../entity/country/country.entity';
+import { Country } from '../../../entity/region/country.entity';
 import { CountryService } from '../../../service/services/country.service';
 import { RequestContext } from '../../common/request-context';
 import { Allow } from '../../decorators/allow.decorator';

+ 1 - 1
packages/core/src/api/resolvers/entity/country-entity.resolver.ts

@@ -1,6 +1,6 @@
 import { Parent, ResolveField, Resolver } from '@nestjs/graphql';
 
-import { Country } from '../../../entity/country/country.entity';
+import { Country } from '../../../entity/region/country.entity';
 import { LocaleStringHydrator } from '../../../service/helpers/locale-string-hydrator/locale-string-hydrator';
 import { RequestContext } from '../../common/request-context';
 import { Ctx } from '../../decorators/request-context.decorator';

+ 1 - 1
packages/core/src/api/resolvers/entity/zone-entity.resolver.ts

@@ -1,6 +1,6 @@
 import { Parent, ResolveField, Resolver } from '@nestjs/graphql';
 
-import { Country } from '../../../entity/country/country.entity';
+import { Country } from '../../../entity/region/country.entity';
 import { Zone } from '../../../entity/zone/zone.entity';
 import { ZoneService } from '../../../service/services/zone.service';
 import { RequestContext } from '../../common/request-context';

+ 37 - 0
packages/core/src/api/schema/admin-api/province.api.graphql

@@ -0,0 +1,37 @@
+type Query {
+    provinces(options: ProvinceListOptions): ProvinceList!
+    province(id: ID!): Province
+}
+
+type Mutation {
+    "Create a new Province"
+    createProvince(input: CreateProvinceInput!): Province!
+
+    "Update an existing Province"
+    updateProvince(input: UpdateProvinceInput!): Province!
+
+    "Delete a Province"
+    deleteProvince(id: ID!): DeletionResponse!
+}
+
+input ProvinceTranslationInput {
+    id: ID
+    languageCode: LanguageCode!
+    name: String
+}
+
+input CreateProvinceInput {
+    code: String!
+    translations: [ProvinceTranslationInput!]!
+    enabled: Boolean!
+}
+
+input UpdateProvinceInput {
+    id: ID!
+    code: String
+    translations: [ProvinceTranslationInput!]
+    enabled: Boolean
+}
+
+# generated by generateListOptions function
+input ProvinceListOptions

+ 0 - 23
packages/core/src/api/schema/common/country.type.graphql

@@ -1,23 +0,0 @@
-type Country implements Node {
-    id: ID!
-    createdAt: DateTime!
-    updatedAt: DateTime!
-    languageCode: LanguageCode!
-    code: String!
-    name: String!
-    enabled: Boolean!
-    translations: [CountryTranslation!]!
-}
-
-type CountryTranslation {
-    id: ID!
-    createdAt: DateTime!
-    updatedAt: DateTime!
-    languageCode: LanguageCode!
-    name: String!
-}
-
-type CountryList implements PaginatedList {
-    items: [Country!]!
-    totalItems: Int!
-}

+ 59 - 0
packages/core/src/api/schema/common/region.type.graphql

@@ -0,0 +1,59 @@
+interface Region implements Node {
+    id: ID!
+    createdAt: DateTime!
+    updatedAt: DateTime!
+    languageCode: LanguageCode!
+    code: String!
+    type: String!
+    name: String!
+    enabled: Boolean!
+    parent: Region
+    parentId: ID
+    translations: [RegionTranslation!]!
+}
+
+type RegionTranslation {
+    id: ID!
+    createdAt: DateTime!
+    updatedAt: DateTime!
+    languageCode: LanguageCode!
+    name: String!
+}
+
+type Country implements Region & Node {
+    id: ID!
+    createdAt: DateTime!
+    updatedAt: DateTime!
+    languageCode: LanguageCode!
+    code: String!
+    type: String!
+    name: String!
+    enabled: Boolean!
+    parent: Region
+    parentId: ID
+    translations: [RegionTranslation!]!
+}
+
+type CountryList implements PaginatedList {
+    items: [Country!]!
+    totalItems: Int!
+}
+
+type Province implements Region & Node {
+    id: ID!
+    createdAt: DateTime!
+    updatedAt: DateTime!
+    languageCode: LanguageCode!
+    code: String!
+    type: String!
+    name: String!
+    enabled: Boolean!
+    parent: Region
+    parentId: ID
+    translations: [RegionTranslation!]!
+}
+
+type ProvinceList implements PaginatedList {
+    items: [Province!]!
+    totalItems: Int!
+}

+ 2 - 1
packages/core/src/api/schema/common/zone.type.graphql

@@ -3,5 +3,6 @@ type Zone implements Node {
     createdAt: DateTime!
     updatedAt: DateTime!
     name: String!
-    members: [Country!]!
+    members: [Region!]!
 }
+

+ 1 - 1
packages/core/src/config/custom-field/custom-field-types.ts

@@ -208,7 +208,6 @@ export interface CustomFields {
     Asset?: CustomFieldConfig[];
     Channel?: CustomFieldConfig[];
     Collection?: CustomFieldConfig[];
-    Country?: CustomFieldConfig[];
     Customer?: CustomFieldConfig[];
     CustomerGroup?: CustomFieldConfig[];
     Facet?: CustomFieldConfig[];
@@ -223,6 +222,7 @@ export interface CustomFields {
     ProductOptionGroup?: CustomFieldConfig[];
     ProductVariant?: CustomFieldConfig[];
     Promotion?: CustomFieldConfig[];
+    Region?: CustomFieldConfig[];
     Seller?: CustomFieldConfig[];
     ShippingMethod?: CustomFieldConfig[];
     StockLocation?: CustomFieldConfig[];

+ 1 - 1
packages/core/src/config/default-config.ts

@@ -184,7 +184,6 @@ export const defaultConfig: RuntimeVendureConfig = {
         Asset: [],
         Channel: [],
         Collection: [],
-        Country: [],
         Customer: [],
         CustomerGroup: [],
         Facet: [],
@@ -199,6 +198,7 @@ export const defaultConfig: RuntimeVendureConfig = {
         ProductOptionGroup: [],
         ProductVariant: [],
         Promotion: [],
+        Region: [],
         Seller: [],
         ShippingMethod: [],
         StockLocation: [],

+ 1 - 1
packages/core/src/entity/address/address.entity.ts

@@ -3,9 +3,9 @@ import { Column, Entity, Index, ManyToOne } from 'typeorm';
 
 import { HasCustomFields } from '../../config/custom-field/custom-field-types';
 import { VendureEntity } from '../base/base.entity';
-import { Country } from '../country/country.entity';
 import { CustomAddressFields } from '../custom-entity-fields';
 import { Customer } from '../customer/customer.entity';
+import { Country } from '../region/country.entity';
 
 /**
  * @description

+ 0 - 36
packages/core/src/entity/country/country.entity.ts

@@ -1,36 +0,0 @@
-import { DeepPartial } from '@vendure/common/lib/shared-types';
-import { Column, Entity, OneToMany } from 'typeorm';
-
-import { LocaleString, Translatable, Translation } from '../../common/types/locale-types';
-import { HasCustomFields } from '../../config/custom-field/custom-field-types';
-import { VendureEntity } from '../base/base.entity';
-import { CustomCountryFields } from '../custom-entity-fields';
-
-import { CountryTranslation } from './country-translation.entity';
-
-/**
- * @description
- * A country to which is available when creating / updating an {@link Address}. Countries are
- * grouped together into {@link Zone}s which are in turn used to determine applicable shipping
- * and taxes for an {@link Order}.
- *
- * @docsCategory entities
- */
-@Entity()
-export class Country extends VendureEntity implements Translatable, HasCustomFields {
-    constructor(input?: DeepPartial<Country>) {
-        super(input);
-    }
-
-    @Column() code: string;
-
-    name: LocaleString;
-
-    @Column() enabled: boolean;
-
-    @OneToMany(type => CountryTranslation, translation => translation.base, { eager: true })
-    translations: Array<Translation<Country>>;
-
-    @Column(type => CustomCountryFields)
-    customFields: CustomCountryFields;
-}

+ 2 - 2
packages/core/src/entity/custom-entity-fields.ts

@@ -4,8 +4,6 @@ export class CustomAssetFields {}
 export class CustomChannelFields {}
 export class CustomCollectionFields {}
 export class CustomCollectionFieldsTranslation {}
-export class CustomCountryFields {}
-export class CustomCountryFieldsTranslation {}
 export class CustomCustomerFields {}
 export class CustomCustomerGroupFields {}
 export class CustomFacetFields {}
@@ -28,6 +26,8 @@ export class CustomProductVariantFields {}
 export class CustomProductVariantFieldsTranslation {}
 export class CustomPromotionFields {}
 export class CustomPromotionFieldsTranslation {}
+export class CustomRegionFields {}
+export class CustomRegionFieldsTranslation {}
 export class CustomSellerFields {}
 export class CustomShippingMethodFields {}
 export class CustomShippingMethodFieldsTranslation {}

+ 7 - 3
packages/core/src/entity/entities.ts

@@ -8,8 +8,6 @@ import { Channel } from './channel/channel.entity';
 import { CollectionAsset } from './collection/collection-asset.entity';
 import { CollectionTranslation } from './collection/collection-translation.entity';
 import { Collection } from './collection/collection.entity';
-import { CountryTranslation } from './country/country-translation.entity';
-import { Country } from './country/country.entity';
 import { Customer } from './customer/customer.entity';
 import { CustomerGroup } from './customer-group/customer-group.entity';
 import { FacetTranslation } from './facet/facet-translation.entity';
@@ -45,6 +43,10 @@ import { ProductVariant } from './product-variant/product-variant.entity';
 import { PromotionTranslation } from './promotion/promotion-translation.entity';
 import { Promotion } from './promotion/promotion.entity';
 import { Refund } from './refund/refund.entity';
+import { Country } from './region/country.entity';
+import { Province } from './region/province.entity';
+import { RegionTranslation } from './region/region-translation.entity';
+import { Region } from './region/region.entity';
 import { Role } from './role/role.entity';
 import { Seller } from './seller/seller.entity';
 import { AnonymousSession } from './session/anonymous-session.entity';
@@ -85,7 +87,6 @@ export const coreEntitiesMap = {
     CollectionAsset,
     CollectionTranslation,
     Country,
-    CountryTranslation,
     Customer,
     CustomerGroup,
     CustomerHistoryEntry,
@@ -121,8 +122,11 @@ export const coreEntitiesMap = {
     ProductVariantTranslation,
     Promotion,
     PromotionTranslation,
+    Province,
     Refund,
     RefundLine,
+    Region,
+    RegionTranslation,
     Release,
     Role,
     Sale,

+ 1 - 2
packages/core/src/entity/index.ts

@@ -9,8 +9,7 @@ export * from './channel/channel.entity';
 export * from './collection/collection-asset.entity';
 export * from './collection/collection-translation.entity';
 export * from './collection/collection.entity';
-export * from './country/country-translation.entity';
-export * from './country/country.entity';
+export * from './region/country.entity';
 export * from './custom-entity-fields';
 export * from './customer-group/customer-group.entity';
 export * from './customer/customer.entity';

+ 21 - 0
packages/core/src/entity/region/country.entity.ts

@@ -0,0 +1,21 @@
+import { DeepPartial } from '@vendure/common/lib/shared-types';
+import { ChildEntity } from 'typeorm';
+
+import { Region, RegionType } from './region.entity';
+
+/**
+ * @description
+ * A country to which is available when creating / updating an {@link Address}. Countries are
+ * grouped together into {@link Zone}s which are in turn used to determine applicable shipping
+ * and taxes for an {@link Order}.
+ *
+ * @docsCategory entities
+ */
+@ChildEntity()
+export class Country extends Region {
+    constructor(input?: DeepPartial<Country>) {
+        super(input);
+    }
+
+    readonly type: RegionType = 'country';
+}

+ 20 - 0
packages/core/src/entity/region/province.entity.ts

@@ -0,0 +1,20 @@
+import { DeepPartial } from '@vendure/common/lib/shared-types';
+import { ChildEntity } from 'typeorm';
+
+import { Region, RegionType } from './region.entity';
+
+/**
+ * @description
+ * A Province represents an administrative subdivision of a {@link Country}. For example, in the
+ * United States, the country would be "United States" and the province would be "California".
+ *
+ * @docsCategory entities
+ */
+@ChildEntity()
+export class Province extends Region {
+    constructor(input?: DeepPartial<Province>) {
+        super(input);
+    }
+
+    readonly type: RegionType = 'province';
+}

+ 8 - 8
packages/core/src/entity/country/country-translation.entity.ts → packages/core/src/entity/region/region-translation.entity.ts

@@ -5,13 +5,13 @@ import { Column, Entity, Index, ManyToOne } from 'typeorm';
 import { Translation } from '../../common/types/locale-types';
 import { HasCustomFields } from '../../config/custom-field/custom-field-types';
 import { VendureEntity } from '../base/base.entity';
-import { CustomCountryFieldsTranslation } from '../custom-entity-fields';
+import { CustomRegionFieldsTranslation } from '../custom-entity-fields';
 
-import { Country } from './country.entity';
+import { Region } from './region.entity';
 
 @Entity()
-export class CountryTranslation extends VendureEntity implements Translation<Country>, HasCustomFields {
-    constructor(input?: DeepPartial<Translation<CountryTranslation>>) {
+export class RegionTranslation extends VendureEntity implements Translation<Region>, HasCustomFields {
+    constructor(input?: DeepPartial<Translation<RegionTranslation>>) {
         super(input);
     }
 
@@ -20,9 +20,9 @@ export class CountryTranslation extends VendureEntity implements Translation<Cou
     @Column() name: string;
 
     @Index()
-    @ManyToOne(type => Country, base => base.translations, { onDelete: 'CASCADE' })
-    base: Country;
+    @ManyToOne(type => Region, base => base.translations, { onDelete: 'CASCADE' })
+    base: Region;
 
-    @Column(type => CustomCountryFieldsTranslation)
-    customFields: CustomCountryFieldsTranslation;
+    @Column(type => CustomRegionFieldsTranslation)
+    customFields: CustomRegionFieldsTranslation;
 }

+ 51 - 0
packages/core/src/entity/region/region.entity.ts

@@ -0,0 +1,51 @@
+import { ID } from '@vendure/common/lib/shared-types';
+import { Column, Entity, JoinColumn, ManyToOne, OneToMany, TableInheritance } from 'typeorm';
+
+import { LocaleString, Translatable, Translation } from '../../common/types/locale-types';
+import { HasCustomFields } from '../../config/custom-field/custom-field-types';
+import { VendureEntity } from '../base/base.entity';
+import { CustomRegionFields } from '../custom-entity-fields';
+import { EntityId } from '../entity-id.decorator';
+
+import { RegionTranslation } from './region-translation.entity';
+
+export type RegionType = 'country' | 'province' | string;
+
+/**
+ * @description
+ * A Region represents a geographical administrative unit, such as a Country, Province, State, Prefecture etc.
+ * This is an abstract class which is extended by the {@link Country} and {@link Province} entities.
+ * Regions can be grouped into {@link Zone}s which are in turn used to determine applicable shipping and taxes for an {@link Order}.
+ *
+ * @docsCategory entities
+ */
+@Entity()
+@TableInheritance({ column: { type: 'varchar', name: 'discriminator' } })
+export abstract class Region extends VendureEntity implements Translatable, HasCustomFields {
+    /**
+     * @description
+     * A code representing the region. The code format will depend on the type of region. For
+     * example, a Country code will be a 2-letter ISO code, whereas a Province code could use
+     * a format relevant to the type of province, e.g. a US state code like "CA".
+     */
+    @Column() code: string;
+
+    @Column({ nullable: false, type: 'varchar' })
+    readonly type: RegionType;
+
+    name: LocaleString;
+
+    @ManyToOne(type => Region, { nullable: true, onDelete: 'SET NULL' })
+    parent?: Region;
+
+    @EntityId({ nullable: true })
+    parentId?: ID;
+
+    @Column() enabled: boolean;
+
+    @OneToMany(type => RegionTranslation, translation => translation.base, { eager: true })
+    translations: Array<Translation<Region>>;
+
+    @Column(type => CustomRegionFields)
+    customFields: CustomRegionFields;
+}

+ 4 - 4
packages/core/src/entity/register-custom-entity-fields.ts

@@ -24,8 +24,6 @@ import {
     CustomChannelFields,
     CustomCollectionFields,
     CustomCollectionFieldsTranslation,
-    CustomCountryFields,
-    CustomCountryFieldsTranslation,
     CustomCustomerFields,
     CustomCustomerGroupFields,
     CustomFacetFields,
@@ -46,6 +44,8 @@ import {
     CustomProductVariantFields,
     CustomProductVariantFieldsTranslation,
     CustomPromotionFields,
+    CustomRegionFields,
+    CustomRegionFieldsTranslation,
     CustomSellerFields,
     CustomShippingMethodFields,
     CustomShippingMethodFieldsTranslation,
@@ -260,8 +260,6 @@ export function registerCustomEntityFields(config: VendureConfig) {
     registerCustomFieldsForEntity(config, 'Collection', CustomCollectionFields);
     registerCustomFieldsForEntity(config, 'Collection', CustomCollectionFieldsTranslation, true);
     registerCustomFieldsForEntity(config, 'Channel', CustomChannelFields);
-    registerCustomFieldsForEntity(config, 'Country', CustomCountryFields);
-    registerCustomFieldsForEntity(config, 'Country', CustomCountryFieldsTranslation, true);
     registerCustomFieldsForEntity(config, 'Customer', CustomCustomerFields);
     registerCustomFieldsForEntity(config, 'CustomerGroup', CustomCustomerGroupFields);
     registerCustomFieldsForEntity(config, 'Facet', CustomFacetFields);
@@ -290,6 +288,8 @@ export function registerCustomEntityFields(config: VendureConfig) {
     registerCustomFieldsForEntity(config, 'TaxRate', CustomTaxRateFields);
     registerCustomFieldsForEntity(config, 'User', CustomUserFields);
     registerCustomFieldsForEntity(config, 'GlobalSettings', CustomGlobalSettingsFields);
+    registerCustomFieldsForEntity(config, 'Region', CustomRegionFields);
+    registerCustomFieldsForEntity(config, 'Region', CustomRegionFieldsTranslation, true);
     registerCustomFieldsForEntity(config, 'Seller', CustomSellerFields);
     registerCustomFieldsForEntity(config, 'ShippingMethod', CustomShippingMethodFields);
     registerCustomFieldsForEntity(config, 'ShippingMethod', CustomShippingMethodFieldsTranslation, true);

+ 4 - 3
packages/core/src/entity/zone/zone.entity.ts

@@ -3,8 +3,9 @@ import { Column, Entity, JoinTable, ManyToMany } from 'typeorm';
 
 import { HasCustomFields } from '../../config/custom-field/custom-field-types';
 import { VendureEntity } from '../base/base.entity';
-import { Country } from '../country/country.entity';
 import { CustomZoneFields } from '../custom-entity-fields';
+import { Country } from '../region/country.entity';
+import { Region } from '../region/region.entity';
 
 /**
  * @description
@@ -21,9 +22,9 @@ export class Zone extends VendureEntity implements HasCustomFields {
 
     @Column() name: string;
 
-    @ManyToMany(type => Country)
+    @ManyToMany(type => Region)
     @JoinTable()
-    members: Country[];
+    members: Region[];
 
     @Column(type => CustomZoneFields)
     customFields: CustomZoneFields;

+ 27 - 0
packages/core/src/event-bus/events/province-event.ts

@@ -0,0 +1,27 @@
+import { CreateProvinceInput, UpdateProvinceInput } from '@vendure/common/lib/generated-types';
+import { ID } from '@vendure/common/lib/shared-types';
+
+import { RequestContext } from '../../api';
+import { Province } from '../../entity/region/province.entity';
+import { VendureEntityEvent } from '../vendure-entity-event';
+
+type ProvinceInputTypes = CreateProvinceInput | UpdateProvinceInput | ID;
+
+/**
+ * @description
+ * This event is fired whenever a {@link Province} is added, updated or deleted.
+ *
+ * @docsCategory events
+ * @docsPage Event Types
+ * @since 2.0
+ */
+export class ProvinceEvent extends VendureEntityEvent<Province, ProvinceInputTypes> {
+    constructor(
+        ctx: RequestContext,
+        entity: Province,
+        type: 'created' | 'updated' | 'deleted',
+        input?: ProvinceInputTypes,
+    ) {
+        super(entity, type, ctx, input);
+    }
+}

+ 6 - 5
packages/core/src/service/services/country.service.ts

@@ -5,7 +5,7 @@ import {
     DeletionResult,
     UpdateCountryInput,
 } from '@vendure/common/lib/generated-types';
-import { ID, PaginatedList } from '@vendure/common/lib/shared-types';
+import { ID, PaginatedList, Type } from '@vendure/common/lib/shared-types';
 
 import { RequestContext } from '../../api/common/request-context';
 import { RelationPaths } from '../../api/index';
@@ -15,8 +15,9 @@ import { Translated } from '../../common/types/locale-types';
 import { assertFound } from '../../common/utils';
 import { TransactionalConnection } from '../../connection/transactional-connection';
 import { Address } from '../../entity';
-import { CountryTranslation } from '../../entity/country/country-translation.entity';
-import { Country } from '../../entity/country/country.entity';
+import { Country } from '../../entity/region/country.entity';
+import { RegionTranslation } from '../../entity/region/region-translation.entity';
+import { Region } from '../../entity/region/region.entity';
 import { EventBus } from '../../event-bus';
 import { CountryEvent } from '../../event-bus/events/country-event';
 import { ListQueryBuilder } from '../helpers/list-query-builder/list-query-builder';
@@ -99,7 +100,7 @@ export class CountryService {
             ctx,
             input,
             entityType: Country,
-            translationType: CountryTranslation,
+            translationType: RegionTranslation,
         });
         this.eventBus.publish(new CountryEvent(ctx, country, 'created', input));
         return assertFound(this.findOne(ctx, country.id));
@@ -110,7 +111,7 @@ export class CountryService {
             ctx,
             input,
             entityType: Country,
-            translationType: CountryTranslation,
+            translationType: RegionTranslation,
         });
         this.eventBus.publish(new CountryEvent(ctx, country, 'updated', input));
         return assertFound(this.findOne(ctx, country.id));

+ 102 - 0
packages/core/src/service/services/province.service.ts

@@ -0,0 +1,102 @@
+import { Injectable } from '@nestjs/common';
+import {
+    CreateProvinceInput,
+    DeletionResponse,
+    DeletionResult,
+    UpdateProvinceInput,
+} from '@vendure/common/lib/generated-types';
+import { ID, PaginatedList, Type } from '@vendure/common/lib/shared-types';
+
+import { RequestContext } from '../../api/common/request-context';
+import { RelationPaths } from '../../api/index';
+import { ListQueryOptions } from '../../common/types/common-types';
+import { Translated } from '../../common/types/locale-types';
+import { assertFound } from '../../common/utils';
+import { TransactionalConnection } from '../../connection/transactional-connection';
+import { Province } from '../../entity/region/province.entity';
+import { RegionTranslation } from '../../entity/region/region-translation.entity';
+import { Region } from '../../entity/region/region.entity';
+import { EventBus } from '../../event-bus';
+import { ProvinceEvent } from '../../event-bus/events/province-event';
+import { ListQueryBuilder } from '../helpers/list-query-builder/list-query-builder';
+import { TranslatableSaver } from '../helpers/translatable-saver/translatable-saver';
+import { TranslatorService } from '../helpers/translator/translator.service';
+
+/**
+ * @description
+ * Contains methods relating to {@link Province} entities.
+ *
+ * @docsCategory services
+ */
+@Injectable()
+export class ProvinceService {
+    constructor(
+        private connection: TransactionalConnection,
+        private listQueryBuilder: ListQueryBuilder,
+        private translatableSaver: TranslatableSaver,
+        private eventBus: EventBus,
+        private translator: TranslatorService,
+    ) {}
+
+    findAll(
+        ctx: RequestContext,
+        options?: ListQueryOptions<Province>,
+        relations: RelationPaths<Province> = [],
+    ): Promise<PaginatedList<Translated<Province>>> {
+        return this.listQueryBuilder
+            .build(Province as Type<Province>, options, { ctx, relations })
+            .getManyAndCount()
+            .then(([provinces, totalItems]) => {
+                const items = provinces.map(province => this.translator.translate(province, ctx));
+                return {
+                    items,
+                    totalItems,
+                };
+            });
+    }
+
+    findOne(
+        ctx: RequestContext,
+        provinceId: ID,
+        relations: RelationPaths<Province> = [],
+    ): Promise<Translated<Province> | undefined> {
+        return this.connection
+            .getRepository(ctx, Province)
+            .findOne({ where: { id: provinceId }, relations })
+            .then(province => (province && this.translator.translate(province, ctx)) ?? undefined);
+    }
+
+    async create(ctx: RequestContext, input: CreateProvinceInput): Promise<Translated<Province>> {
+        const province = await this.translatableSaver.create({
+            ctx,
+            input,
+            entityType: Province as Type<Region>,
+            translationType: RegionTranslation,
+        });
+        this.eventBus.publish(new ProvinceEvent(ctx, province, 'created', input));
+        return assertFound(this.findOne(ctx, province.id));
+    }
+
+    async update(ctx: RequestContext, input: UpdateProvinceInput): Promise<Translated<Province>> {
+        const province = await this.translatableSaver.update({
+            ctx,
+            input,
+            entityType: Province as Type<Region>,
+            translationType: RegionTranslation,
+        });
+        this.eventBus.publish(new ProvinceEvent(ctx, province, 'updated', input));
+        return assertFound(this.findOne(ctx, province.id));
+    }
+
+    async delete(ctx: RequestContext, id: ID): Promise<DeletionResponse> {
+        const region = await this.connection.getEntityOrThrow(ctx, Province as Type<Province>, id);
+
+        const deletedProvince = new Province(region);
+        await this.connection.getRepository(ctx, Province).remove(region);
+        this.eventBus.publish(new ProvinceEvent(ctx, deletedProvince, 'deleted', id));
+        return {
+            result: DeletionResult.DELETED,
+            message: '',
+        };
+    }
+}

+ 1 - 1
packages/core/src/service/services/zone.service.ts

@@ -17,7 +17,7 @@ import { assertFound } from '../../common/utils';
 import { ConfigService } from '../../config/config.service';
 import { TransactionalConnection } from '../../connection/transactional-connection';
 import { Channel, TaxRate } from '../../entity';
-import { Country } from '../../entity/country/country.entity';
+import { Country } from '../../entity/region/country.entity';
 import { Zone } from '../../entity/zone/zone.entity';
 import { EventBus } from '../../event-bus';
 import { ZoneEvent } from '../../event-bus/events/zone-event';

+ 151 - 21
packages/elasticsearch-plugin/e2e/graphql/generated-e2e-elasticsearch-plugin-types.ts

@@ -354,8 +354,10 @@ export type Cancellation = Node &
 export type Channel = Node & {
     code: Scalars['String'];
     createdAt: Scalars['DateTime'];
+    /** @deprecated Use defaultCurrencyCode instead */
     currencyCode: CurrencyCode;
     customFields?: Maybe<Scalars['JSON']>;
+    defaultCurrencyCode: CurrencyCode;
     defaultLanguageCode: LanguageCode;
     defaultShippingZone?: Maybe<Zone>;
     defaultTaxZone?: Maybe<Zone>;
@@ -517,17 +519,21 @@ export type CoordinateInput = {
     y: Scalars['Float'];
 };
 
-export type Country = Node & {
-    code: Scalars['String'];
-    createdAt: Scalars['DateTime'];
-    customFields?: Maybe<Scalars['JSON']>;
-    enabled: Scalars['Boolean'];
-    id: Scalars['ID'];
-    languageCode: LanguageCode;
-    name: Scalars['String'];
-    translations: Array<CountryTranslation>;
-    updatedAt: Scalars['DateTime'];
-};
+export type Country = Node &
+    Region & {
+        code: Scalars['String'];
+        createdAt: Scalars['DateTime'];
+        customFields?: Maybe<Scalars['JSON']>;
+        enabled: Scalars['Boolean'];
+        id: Scalars['ID'];
+        languageCode: LanguageCode;
+        name: Scalars['String'];
+        parent?: Maybe<Region>;
+        parentId?: Maybe<Scalars['ID']>;
+        translations: Array<RegionTranslation>;
+        type: Scalars['String'];
+        updatedAt: Scalars['DateTime'];
+    };
 
 export type CountryFilterParameter = {
     code?: InputMaybe<StringOperators>;
@@ -536,6 +542,8 @@ export type CountryFilterParameter = {
     id?: InputMaybe<IdOperators>;
     languageCode?: InputMaybe<StringOperators>;
     name?: InputMaybe<StringOperators>;
+    parentId?: InputMaybe<IdOperators>;
+    type?: InputMaybe<StringOperators>;
     updatedAt?: InputMaybe<DateOperators>;
 };
 
@@ -562,17 +570,11 @@ export type CountrySortParameter = {
     createdAt?: InputMaybe<SortOrder>;
     id?: InputMaybe<SortOrder>;
     name?: InputMaybe<SortOrder>;
+    parentId?: InputMaybe<SortOrder>;
+    type?: InputMaybe<SortOrder>;
     updatedAt?: InputMaybe<SortOrder>;
 };
 
-export type CountryTranslation = {
-    createdAt: Scalars['DateTime'];
-    id: Scalars['ID'];
-    languageCode: LanguageCode;
-    name: Scalars['String'];
-    updatedAt: Scalars['DateTime'];
-};
-
 export type CountryTranslationInput = {
     customFields?: InputMaybe<Scalars['JSON']>;
     id?: InputMaybe<Scalars['ID']>;
@@ -793,6 +795,13 @@ export type CreatePromotionInput = {
 
 export type CreatePromotionResult = MissingConditionsError | Promotion;
 
+export type CreateProvinceInput = {
+    code: Scalars['String'];
+    customFields?: InputMaybe<Scalars['JSON']>;
+    enabled: Scalars['Boolean'];
+    translations: Array<ProvinceTranslationInput>;
+};
+
 export type CreateRoleInput = {
     channelIds?: InputMaybe<Array<Scalars['ID']>>;
     code: Scalars['String'];
@@ -1211,7 +1220,6 @@ export type CustomFields = {
     Asset: Array<CustomFieldConfig>;
     Channel: Array<CustomFieldConfig>;
     Collection: Array<CustomFieldConfig>;
-    Country: Array<CustomFieldConfig>;
     Customer: Array<CustomFieldConfig>;
     CustomerGroup: Array<CustomFieldConfig>;
     Facet: Array<CustomFieldConfig>;
@@ -1226,6 +1234,7 @@ export type CustomFields = {
     ProductOptionGroup: Array<CustomFieldConfig>;
     ProductVariant: Array<CustomFieldConfig>;
     Promotion: Array<CustomFieldConfig>;
+    Region: Array<CustomFieldConfig>;
     Seller: Array<CustomFieldConfig>;
     ShippingMethod: Array<CustomFieldConfig>;
     StockLocation: Array<CustomFieldConfig>;
@@ -2470,6 +2479,8 @@ export type Mutation = {
     /** Create a set of ProductVariants based on the OptionGroups assigned to the given Product */
     createProductVariants: Array<Maybe<ProductVariant>>;
     createPromotion: CreatePromotionResult;
+    /** Create a new Province */
+    createProvince: Province;
     /** Create a new Role */
     createRole: Role;
     /** Create a new Seller */
@@ -2528,6 +2539,8 @@ export type Mutation = {
     /** Delete multiple Products */
     deleteProducts: Array<DeletionResponse>;
     deletePromotion: DeletionResponse;
+    /** Delete a Province */
+    deleteProvince: DeletionResponse;
     /** Delete an existing Role */
     deleteRole: DeletionResponse;
     /** Delete a Seller */
@@ -2633,6 +2646,8 @@ export type Mutation = {
     /** Update multiple existing Products */
     updateProducts: Array<Product>;
     updatePromotion: UpdatePromotionResult;
+    /** Update an existing Province */
+    updateProvince: Province;
     /** Update an existing Role */
     updateRole: Role;
     /** Update an existing Seller */
@@ -2808,6 +2823,10 @@ export type MutationCreatePromotionArgs = {
     input: CreatePromotionInput;
 };
 
+export type MutationCreateProvinceArgs = {
+    input: CreateProvinceInput;
+};
+
 export type MutationCreateRoleArgs = {
     input: CreateRoleInput;
 };
@@ -2936,6 +2955,10 @@ export type MutationDeletePromotionArgs = {
     id: Scalars['ID'];
 };
 
+export type MutationDeleteProvinceArgs = {
+    id: Scalars['ID'];
+};
+
 export type MutationDeleteRoleArgs = {
     id: Scalars['ID'];
 };
@@ -3181,6 +3204,10 @@ export type MutationUpdatePromotionArgs = {
     input: UpdatePromotionInput;
 };
 
+export type MutationUpdateProvinceArgs = {
+    input: UpdateProvinceInput;
+};
+
 export type MutationUpdateRoleArgs = {
     input: UpdateRoleInput;
 };
@@ -4277,6 +4304,69 @@ export type PromotionTranslationInput = {
     name?: InputMaybe<Scalars['String']>;
 };
 
+export type Province = Node &
+    Region & {
+        code: Scalars['String'];
+        createdAt: Scalars['DateTime'];
+        customFields?: Maybe<Scalars['JSON']>;
+        enabled: Scalars['Boolean'];
+        id: Scalars['ID'];
+        languageCode: LanguageCode;
+        name: Scalars['String'];
+        parent?: Maybe<Region>;
+        parentId?: Maybe<Scalars['ID']>;
+        translations: Array<RegionTranslation>;
+        type: Scalars['String'];
+        updatedAt: Scalars['DateTime'];
+    };
+
+export type ProvinceFilterParameter = {
+    code?: InputMaybe<StringOperators>;
+    createdAt?: InputMaybe<DateOperators>;
+    enabled?: InputMaybe<BooleanOperators>;
+    id?: InputMaybe<IdOperators>;
+    languageCode?: InputMaybe<StringOperators>;
+    name?: InputMaybe<StringOperators>;
+    parentId?: InputMaybe<IdOperators>;
+    type?: InputMaybe<StringOperators>;
+    updatedAt?: InputMaybe<DateOperators>;
+};
+
+export type ProvinceList = PaginatedList & {
+    items: Array<Province>;
+    totalItems: Scalars['Int'];
+};
+
+export type ProvinceListOptions = {
+    /** Allows the results to be filtered */
+    filter?: InputMaybe<ProvinceFilterParameter>;
+    /** Specifies whether multiple "filter" arguments should be combines with a logical AND or OR operation. Defaults to AND. */
+    filterOperator?: InputMaybe<LogicalOperator>;
+    /** Skips the first n results, for use in pagination */
+    skip?: InputMaybe<Scalars['Int']>;
+    /** Specifies which properties to sort the results by */
+    sort?: InputMaybe<ProvinceSortParameter>;
+    /** Takes n results, for use in pagination */
+    take?: InputMaybe<Scalars['Int']>;
+};
+
+export type ProvinceSortParameter = {
+    code?: InputMaybe<SortOrder>;
+    createdAt?: InputMaybe<SortOrder>;
+    id?: InputMaybe<SortOrder>;
+    name?: InputMaybe<SortOrder>;
+    parentId?: InputMaybe<SortOrder>;
+    type?: InputMaybe<SortOrder>;
+    updatedAt?: InputMaybe<SortOrder>;
+};
+
+export type ProvinceTranslationInput = {
+    customFields?: InputMaybe<Scalars['JSON']>;
+    id?: InputMaybe<Scalars['ID']>;
+    languageCode: LanguageCode;
+    name?: InputMaybe<Scalars['String']>;
+};
+
 /** Returned if the specified quantity of an OrderLine is greater than the number of items in that line */
 export type QuantityTooGreatError = ErrorResult & {
     errorCode: ErrorCode;
@@ -4340,6 +4430,8 @@ export type Query = {
     promotionActions: Array<ConfigurableOperationDefinition>;
     promotionConditions: Array<ConfigurableOperationDefinition>;
     promotions: PromotionList;
+    province?: Maybe<Province>;
+    provinces: ProvinceList;
     role?: Maybe<Role>;
     roles: RoleList;
     search: SearchResponse;
@@ -4503,6 +4595,14 @@ export type QueryPromotionsArgs = {
     options?: InputMaybe<PromotionListOptions>;
 };
 
+export type QueryProvinceArgs = {
+    id: Scalars['ID'];
+};
+
+export type QueryProvincesArgs = {
+    options?: InputMaybe<ProvinceListOptions>;
+};
+
 export type QueryRoleArgs = {
     id: Scalars['ID'];
 };
@@ -4640,6 +4740,28 @@ export type RefundStateTransitionError = ErrorResult & {
     transitionError: Scalars['String'];
 };
 
+export type Region = {
+    code: Scalars['String'];
+    createdAt: Scalars['DateTime'];
+    enabled: Scalars['Boolean'];
+    id: Scalars['ID'];
+    languageCode: LanguageCode;
+    name: Scalars['String'];
+    parent?: Maybe<Region>;
+    parentId?: Maybe<Scalars['ID']>;
+    translations: Array<RegionTranslation>;
+    type: Scalars['String'];
+    updatedAt: Scalars['DateTime'];
+};
+
+export type RegionTranslation = {
+    createdAt: Scalars['DateTime'];
+    id: Scalars['ID'];
+    languageCode: LanguageCode;
+    name: Scalars['String'];
+    updatedAt: Scalars['DateTime'];
+};
+
 export type RelationCustomFieldConfig = CustomField & {
     description?: Maybe<Array<LocalizedString>>;
     entity: Scalars['String'];
@@ -5544,6 +5666,14 @@ export type UpdatePromotionInput = {
 
 export type UpdatePromotionResult = MissingConditionsError | Promotion;
 
+export type UpdateProvinceInput = {
+    code?: InputMaybe<Scalars['String']>;
+    customFields?: InputMaybe<Scalars['JSON']>;
+    enabled?: InputMaybe<Scalars['Boolean']>;
+    id: Scalars['ID'];
+    translations?: InputMaybe<Array<ProvinceTranslationInput>>;
+};
+
 export type UpdateRoleInput = {
     channelIds?: InputMaybe<Array<Scalars['ID']>>;
     code?: InputMaybe<Scalars['String']>;
@@ -5620,7 +5750,7 @@ export type Zone = Node & {
     createdAt: Scalars['DateTime'];
     customFields?: Maybe<Scalars['JSON']>;
     id: Scalars['ID'];
-    members: Array<Country>;
+    members: Array<Region>;
     name: Scalars['String'];
     updatedAt: Scalars['DateTime'];
 };

+ 153 - 21
packages/payments-plugin/e2e/graphql/generated-admin-types.ts

@@ -354,8 +354,10 @@ export type Cancellation = Node &
 export type Channel = Node & {
     code: Scalars['String'];
     createdAt: Scalars['DateTime'];
+    /** @deprecated Use defaultCurrencyCode instead */
     currencyCode: CurrencyCode;
     customFields?: Maybe<Scalars['JSON']>;
+    defaultCurrencyCode: CurrencyCode;
     defaultLanguageCode: LanguageCode;
     defaultShippingZone?: Maybe<Zone>;
     defaultTaxZone?: Maybe<Zone>;
@@ -517,17 +519,21 @@ export type CoordinateInput = {
     y: Scalars['Float'];
 };
 
-export type Country = Node & {
-    code: Scalars['String'];
-    createdAt: Scalars['DateTime'];
-    customFields?: Maybe<Scalars['JSON']>;
-    enabled: Scalars['Boolean'];
-    id: Scalars['ID'];
-    languageCode: LanguageCode;
-    name: Scalars['String'];
-    translations: Array<CountryTranslation>;
-    updatedAt: Scalars['DateTime'];
-};
+export type Country = Node &
+    Region & {
+        code: Scalars['String'];
+        createdAt: Scalars['DateTime'];
+        customFields?: Maybe<Scalars['JSON']>;
+        enabled: Scalars['Boolean'];
+        id: Scalars['ID'];
+        languageCode: LanguageCode;
+        name: Scalars['String'];
+        parent?: Maybe<Region>;
+        parentId?: Maybe<Scalars['ID']>;
+        translations: Array<RegionTranslation>;
+        type: Scalars['String'];
+        updatedAt: Scalars['DateTime'];
+    };
 
 export type CountryFilterParameter = {
     code?: InputMaybe<StringOperators>;
@@ -536,6 +542,8 @@ export type CountryFilterParameter = {
     id?: InputMaybe<IdOperators>;
     languageCode?: InputMaybe<StringOperators>;
     name?: InputMaybe<StringOperators>;
+    parentId?: InputMaybe<IdOperators>;
+    type?: InputMaybe<StringOperators>;
     updatedAt?: InputMaybe<DateOperators>;
 };
 
@@ -562,17 +570,11 @@ export type CountrySortParameter = {
     createdAt?: InputMaybe<SortOrder>;
     id?: InputMaybe<SortOrder>;
     name?: InputMaybe<SortOrder>;
+    parentId?: InputMaybe<SortOrder>;
+    type?: InputMaybe<SortOrder>;
     updatedAt?: InputMaybe<SortOrder>;
 };
 
-export type CountryTranslation = {
-    createdAt: Scalars['DateTime'];
-    id: Scalars['ID'];
-    languageCode: LanguageCode;
-    name: Scalars['String'];
-    updatedAt: Scalars['DateTime'];
-};
-
 export type CountryTranslationInput = {
     customFields?: InputMaybe<Scalars['JSON']>;
     id?: InputMaybe<Scalars['ID']>;
@@ -793,6 +795,13 @@ export type CreatePromotionInput = {
 
 export type CreatePromotionResult = MissingConditionsError | Promotion;
 
+export type CreateProvinceInput = {
+    code: Scalars['String'];
+    customFields?: InputMaybe<Scalars['JSON']>;
+    enabled: Scalars['Boolean'];
+    translations: Array<ProvinceTranslationInput>;
+};
+
 export type CreateRoleInput = {
     channelIds?: InputMaybe<Array<Scalars['ID']>>;
     code: Scalars['String'];
@@ -1211,7 +1220,6 @@ export type CustomFields = {
     Asset: Array<CustomFieldConfig>;
     Channel: Array<CustomFieldConfig>;
     Collection: Array<CustomFieldConfig>;
-    Country: Array<CustomFieldConfig>;
     Customer: Array<CustomFieldConfig>;
     CustomerGroup: Array<CustomFieldConfig>;
     Facet: Array<CustomFieldConfig>;
@@ -1226,6 +1234,7 @@ export type CustomFields = {
     ProductOptionGroup: Array<CustomFieldConfig>;
     ProductVariant: Array<CustomFieldConfig>;
     Promotion: Array<CustomFieldConfig>;
+    Region: Array<CustomFieldConfig>;
     Seller: Array<CustomFieldConfig>;
     ShippingMethod: Array<CustomFieldConfig>;
     StockLocation: Array<CustomFieldConfig>;
@@ -2470,6 +2479,8 @@ export type Mutation = {
     /** Create a set of ProductVariants based on the OptionGroups assigned to the given Product */
     createProductVariants: Array<Maybe<ProductVariant>>;
     createPromotion: CreatePromotionResult;
+    /** Create a new Province */
+    createProvince: Province;
     /** Create a new Role */
     createRole: Role;
     /** Create a new Seller */
@@ -2528,6 +2539,8 @@ export type Mutation = {
     /** Delete multiple Products */
     deleteProducts: Array<DeletionResponse>;
     deletePromotion: DeletionResponse;
+    /** Delete a Province */
+    deleteProvince: DeletionResponse;
     /** Delete an existing Role */
     deleteRole: DeletionResponse;
     /** Delete a Seller */
@@ -2633,6 +2646,8 @@ export type Mutation = {
     /** Update multiple existing Products */
     updateProducts: Array<Product>;
     updatePromotion: UpdatePromotionResult;
+    /** Update an existing Province */
+    updateProvince: Province;
     /** Update an existing Role */
     updateRole: Role;
     /** Update an existing Seller */
@@ -2808,6 +2823,10 @@ export type MutationCreatePromotionArgs = {
     input: CreatePromotionInput;
 };
 
+export type MutationCreateProvinceArgs = {
+    input: CreateProvinceInput;
+};
+
 export type MutationCreateRoleArgs = {
     input: CreateRoleInput;
 };
@@ -2936,6 +2955,10 @@ export type MutationDeletePromotionArgs = {
     id: Scalars['ID'];
 };
 
+export type MutationDeleteProvinceArgs = {
+    id: Scalars['ID'];
+};
+
 export type MutationDeleteRoleArgs = {
     id: Scalars['ID'];
 };
@@ -3181,6 +3204,10 @@ export type MutationUpdatePromotionArgs = {
     input: UpdatePromotionInput;
 };
 
+export type MutationUpdateProvinceArgs = {
+    input: UpdateProvinceInput;
+};
+
 export type MutationUpdateRoleArgs = {
     input: UpdateRoleInput;
 };
@@ -4277,6 +4304,69 @@ export type PromotionTranslationInput = {
     name?: InputMaybe<Scalars['String']>;
 };
 
+export type Province = Node &
+    Region & {
+        code: Scalars['String'];
+        createdAt: Scalars['DateTime'];
+        customFields?: Maybe<Scalars['JSON']>;
+        enabled: Scalars['Boolean'];
+        id: Scalars['ID'];
+        languageCode: LanguageCode;
+        name: Scalars['String'];
+        parent?: Maybe<Region>;
+        parentId?: Maybe<Scalars['ID']>;
+        translations: Array<RegionTranslation>;
+        type: Scalars['String'];
+        updatedAt: Scalars['DateTime'];
+    };
+
+export type ProvinceFilterParameter = {
+    code?: InputMaybe<StringOperators>;
+    createdAt?: InputMaybe<DateOperators>;
+    enabled?: InputMaybe<BooleanOperators>;
+    id?: InputMaybe<IdOperators>;
+    languageCode?: InputMaybe<StringOperators>;
+    name?: InputMaybe<StringOperators>;
+    parentId?: InputMaybe<IdOperators>;
+    type?: InputMaybe<StringOperators>;
+    updatedAt?: InputMaybe<DateOperators>;
+};
+
+export type ProvinceList = PaginatedList & {
+    items: Array<Province>;
+    totalItems: Scalars['Int'];
+};
+
+export type ProvinceListOptions = {
+    /** Allows the results to be filtered */
+    filter?: InputMaybe<ProvinceFilterParameter>;
+    /** Specifies whether multiple "filter" arguments should be combines with a logical AND or OR operation. Defaults to AND. */
+    filterOperator?: InputMaybe<LogicalOperator>;
+    /** Skips the first n results, for use in pagination */
+    skip?: InputMaybe<Scalars['Int']>;
+    /** Specifies which properties to sort the results by */
+    sort?: InputMaybe<ProvinceSortParameter>;
+    /** Takes n results, for use in pagination */
+    take?: InputMaybe<Scalars['Int']>;
+};
+
+export type ProvinceSortParameter = {
+    code?: InputMaybe<SortOrder>;
+    createdAt?: InputMaybe<SortOrder>;
+    id?: InputMaybe<SortOrder>;
+    name?: InputMaybe<SortOrder>;
+    parentId?: InputMaybe<SortOrder>;
+    type?: InputMaybe<SortOrder>;
+    updatedAt?: InputMaybe<SortOrder>;
+};
+
+export type ProvinceTranslationInput = {
+    customFields?: InputMaybe<Scalars['JSON']>;
+    id?: InputMaybe<Scalars['ID']>;
+    languageCode: LanguageCode;
+    name?: InputMaybe<Scalars['String']>;
+};
+
 /** Returned if the specified quantity of an OrderLine is greater than the number of items in that line */
 export type QuantityTooGreatError = ErrorResult & {
     errorCode: ErrorCode;
@@ -4340,6 +4430,8 @@ export type Query = {
     promotionActions: Array<ConfigurableOperationDefinition>;
     promotionConditions: Array<ConfigurableOperationDefinition>;
     promotions: PromotionList;
+    province?: Maybe<Province>;
+    provinces: ProvinceList;
     role?: Maybe<Role>;
     roles: RoleList;
     search: SearchResponse;
@@ -4503,6 +4595,14 @@ export type QueryPromotionsArgs = {
     options?: InputMaybe<PromotionListOptions>;
 };
 
+export type QueryProvinceArgs = {
+    id: Scalars['ID'];
+};
+
+export type QueryProvincesArgs = {
+    options?: InputMaybe<ProvinceListOptions>;
+};
+
 export type QueryRoleArgs = {
     id: Scalars['ID'];
 };
@@ -4640,6 +4740,28 @@ export type RefundStateTransitionError = ErrorResult & {
     transitionError: Scalars['String'];
 };
 
+export type Region = {
+    code: Scalars['String'];
+    createdAt: Scalars['DateTime'];
+    enabled: Scalars['Boolean'];
+    id: Scalars['ID'];
+    languageCode: LanguageCode;
+    name: Scalars['String'];
+    parent?: Maybe<Region>;
+    parentId?: Maybe<Scalars['ID']>;
+    translations: Array<RegionTranslation>;
+    type: Scalars['String'];
+    updatedAt: Scalars['DateTime'];
+};
+
+export type RegionTranslation = {
+    createdAt: Scalars['DateTime'];
+    id: Scalars['ID'];
+    languageCode: LanguageCode;
+    name: Scalars['String'];
+    updatedAt: Scalars['DateTime'];
+};
+
 export type RelationCustomFieldConfig = CustomField & {
     description?: Maybe<Array<LocalizedString>>;
     entity: Scalars['String'];
@@ -5544,6 +5666,14 @@ export type UpdatePromotionInput = {
 
 export type UpdatePromotionResult = MissingConditionsError | Promotion;
 
+export type UpdateProvinceInput = {
+    code?: InputMaybe<Scalars['String']>;
+    customFields?: InputMaybe<Scalars['JSON']>;
+    enabled?: InputMaybe<Scalars['Boolean']>;
+    id: Scalars['ID'];
+    translations?: InputMaybe<Array<ProvinceTranslationInput>>;
+};
+
 export type UpdateRoleInput = {
     channelIds?: InputMaybe<Array<Scalars['ID']>>;
     code?: InputMaybe<Scalars['String']>;
@@ -5620,7 +5750,7 @@ export type Zone = Node & {
     createdAt: Scalars['DateTime'];
     customFields?: Maybe<Scalars['JSON']>;
     id: Scalars['ID'];
-    members: Array<Country>;
+    members: Array<Region>;
     name: Scalars['String'];
     updatedAt: Scalars['DateTime'];
 };
@@ -5712,6 +5842,8 @@ export type OrderQueryVariables = Exact<{
 export type OrderQuery = {
     order?: {
         id: string;
+        state: string;
+        totalWithTax: number;
         payments?: Array<{
             id: string;
             transactionId?: string | null;

+ 61 - 20
packages/payments-plugin/e2e/graphql/generated-shop-types.ts

@@ -139,8 +139,10 @@ export type BooleanOperators = {
 export type Channel = Node & {
     code: Scalars['String'];
     createdAt: Scalars['DateTime'];
+    /** @deprecated Use defaultCurrencyCode instead */
     currencyCode: CurrencyCode;
     customFields?: Maybe<Scalars['JSON']>;
+    defaultCurrencyCode: CurrencyCode;
     defaultLanguageCode: LanguageCode;
     defaultShippingZone?: Maybe<Zone>;
     defaultTaxZone?: Maybe<Zone>;
@@ -282,31 +284,27 @@ export type Coordinate = {
     y: Scalars['Float'];
 };
 
-export type Country = Node & {
-    code: Scalars['String'];
-    createdAt: Scalars['DateTime'];
-    customFields?: Maybe<Scalars['JSON']>;
-    enabled: Scalars['Boolean'];
-    id: Scalars['ID'];
-    languageCode: LanguageCode;
-    name: Scalars['String'];
-    translations: Array<CountryTranslation>;
-    updatedAt: Scalars['DateTime'];
-};
+export type Country = Node &
+    Region & {
+        code: Scalars['String'];
+        createdAt: Scalars['DateTime'];
+        customFields?: Maybe<Scalars['JSON']>;
+        enabled: Scalars['Boolean'];
+        id: Scalars['ID'];
+        languageCode: LanguageCode;
+        name: Scalars['String'];
+        parent?: Maybe<Region>;
+        parentId?: Maybe<Scalars['ID']>;
+        translations: Array<RegionTranslation>;
+        type: Scalars['String'];
+        updatedAt: Scalars['DateTime'];
+    };
 
 export type CountryList = PaginatedList & {
     items: Array<Country>;
     totalItems: Scalars['Int'];
 };
 
-export type CountryTranslation = {
-    createdAt: Scalars['DateTime'];
-    id: Scalars['ID'];
-    languageCode: LanguageCode;
-    name: Scalars['String'];
-    updatedAt: Scalars['DateTime'];
-};
-
 /** Returned if the provided coupon code is invalid */
 export type CouponCodeExpiredError = ErrorResult & {
     couponCode: Scalars['String'];
@@ -2633,6 +2631,27 @@ export type PromotionTranslation = {
     updatedAt: Scalars['DateTime'];
 };
 
+export type Province = Node &
+    Region & {
+        code: Scalars['String'];
+        createdAt: Scalars['DateTime'];
+        customFields?: Maybe<Scalars['JSON']>;
+        enabled: Scalars['Boolean'];
+        id: Scalars['ID'];
+        languageCode: LanguageCode;
+        name: Scalars['String'];
+        parent?: Maybe<Region>;
+        parentId?: Maybe<Scalars['ID']>;
+        translations: Array<RegionTranslation>;
+        type: Scalars['String'];
+        updatedAt: Scalars['DateTime'];
+    };
+
+export type ProvinceList = PaginatedList & {
+    items: Array<Province>;
+    totalItems: Scalars['Int'];
+};
+
 export type Query = {
     /** The active Channel */
     activeChannel: Channel;
@@ -2747,6 +2766,28 @@ export type RefundLine = {
     refundId: Scalars['ID'];
 };
 
+export type Region = {
+    code: Scalars['String'];
+    createdAt: Scalars['DateTime'];
+    enabled: Scalars['Boolean'];
+    id: Scalars['ID'];
+    languageCode: LanguageCode;
+    name: Scalars['String'];
+    parent?: Maybe<Region>;
+    parentId?: Maybe<Scalars['ID']>;
+    translations: Array<RegionTranslation>;
+    type: Scalars['String'];
+    updatedAt: Scalars['DateTime'];
+};
+
+export type RegionTranslation = {
+    createdAt: Scalars['DateTime'];
+    id: Scalars['ID'];
+    languageCode: LanguageCode;
+    name: Scalars['String'];
+    updatedAt: Scalars['DateTime'];
+};
+
 export type RegisterCustomerAccountResult =
     | MissingPasswordError
     | NativeAuthStrategyError
@@ -3154,7 +3195,7 @@ export type Zone = Node & {
     createdAt: Scalars['DateTime'];
     customFields?: Maybe<Scalars['JSON']>;
     id: Scalars['ID'];
-    members: Array<Country>;
+    members: Array<Region>;
     name: Scalars['String'];
     updatedAt: Scalars['DateTime'];
 };

+ 65 - 22
packages/payments-plugin/src/mollie/graphql/generated-shop-types.ts

@@ -151,8 +151,10 @@ export type Channel = Node & {
     __typename?: 'Channel';
     code: Scalars['String'];
     createdAt: Scalars['DateTime'];
+    /** @deprecated Use defaultCurrencyCode instead */
     currencyCode: CurrencyCode;
     customFields?: Maybe<Scalars['JSON']>;
+    defaultCurrencyCode: CurrencyCode;
     defaultLanguageCode: LanguageCode;
     defaultShippingZone?: Maybe<Zone>;
     defaultTaxZone?: Maybe<Zone>;
@@ -304,18 +306,22 @@ export type Coordinate = {
     y: Scalars['Float'];
 };
 
-export type Country = Node & {
-    __typename?: 'Country';
-    code: Scalars['String'];
-    createdAt: Scalars['DateTime'];
-    customFields?: Maybe<Scalars['JSON']>;
-    enabled: Scalars['Boolean'];
-    id: Scalars['ID'];
-    languageCode: LanguageCode;
-    name: Scalars['String'];
-    translations: Array<CountryTranslation>;
-    updatedAt: Scalars['DateTime'];
-};
+export type Country = Node &
+    Region & {
+        __typename?: 'Country';
+        code: Scalars['String'];
+        createdAt: Scalars['DateTime'];
+        customFields?: Maybe<Scalars['JSON']>;
+        enabled: Scalars['Boolean'];
+        id: Scalars['ID'];
+        languageCode: LanguageCode;
+        name: Scalars['String'];
+        parent?: Maybe<Region>;
+        parentId?: Maybe<Scalars['ID']>;
+        translations: Array<RegionTranslation>;
+        type: Scalars['String'];
+        updatedAt: Scalars['DateTime'];
+    };
 
 export type CountryList = PaginatedList & {
     __typename?: 'CountryList';
@@ -323,15 +329,6 @@ export type CountryList = PaginatedList & {
     totalItems: Scalars['Int'];
 };
 
-export type CountryTranslation = {
-    __typename?: 'CountryTranslation';
-    createdAt: Scalars['DateTime'];
-    id: Scalars['ID'];
-    languageCode: LanguageCode;
-    name: Scalars['String'];
-    updatedAt: Scalars['DateTime'];
-};
-
 /** Returned if the provided coupon code is invalid */
 export type CouponCodeExpiredError = ErrorResult & {
     __typename?: 'CouponCodeExpiredError';
@@ -2782,6 +2779,29 @@ export type PromotionTranslation = {
     updatedAt: Scalars['DateTime'];
 };
 
+export type Province = Node &
+    Region & {
+        __typename?: 'Province';
+        code: Scalars['String'];
+        createdAt: Scalars['DateTime'];
+        customFields?: Maybe<Scalars['JSON']>;
+        enabled: Scalars['Boolean'];
+        id: Scalars['ID'];
+        languageCode: LanguageCode;
+        name: Scalars['String'];
+        parent?: Maybe<Region>;
+        parentId?: Maybe<Scalars['ID']>;
+        translations: Array<RegionTranslation>;
+        type: Scalars['String'];
+        updatedAt: Scalars['DateTime'];
+    };
+
+export type ProvinceList = PaginatedList & {
+    __typename?: 'ProvinceList';
+    items: Array<Province>;
+    totalItems: Scalars['Int'];
+};
+
 export type Query = {
     __typename?: 'Query';
     /** The active Channel */
@@ -2904,6 +2924,29 @@ export type RefundLine = {
     refundId: Scalars['ID'];
 };
 
+export type Region = {
+    code: Scalars['String'];
+    createdAt: Scalars['DateTime'];
+    enabled: Scalars['Boolean'];
+    id: Scalars['ID'];
+    languageCode: LanguageCode;
+    name: Scalars['String'];
+    parent?: Maybe<Region>;
+    parentId?: Maybe<Scalars['ID']>;
+    translations: Array<RegionTranslation>;
+    type: Scalars['String'];
+    updatedAt: Scalars['DateTime'];
+};
+
+export type RegionTranslation = {
+    __typename?: 'RegionTranslation';
+    createdAt: Scalars['DateTime'];
+    id: Scalars['ID'];
+    languageCode: LanguageCode;
+    name: Scalars['String'];
+    updatedAt: Scalars['DateTime'];
+};
+
 export type RegisterCustomerAccountResult =
     | MissingPasswordError
     | NativeAuthStrategyError
@@ -3340,7 +3383,7 @@ export type Zone = Node & {
     createdAt: Scalars['DateTime'];
     customFields?: Maybe<Scalars['JSON']>;
     id: Scalars['ID'];
-    members: Array<Country>;
+    members: Array<Region>;
     name: Scalars['String'];
     updatedAt: Scalars['DateTime'];
 };

ファイルの差分が大きいため隠しています
+ 0 - 0
schema-admin.json


ファイルの差分が大きいため隠しています
+ 0 - 0
schema-shop.json


この差分においてかなりの量のファイルが変更されているため、一部のファイルを表示していません