Forráskód Böngészése

fix(admin-ui): Add ProductVariantPrice custom fields ui inputs (#3327)

Co-authored-by: Michael Bromley <michael@michaelbromley.co.uk>
Drayke 1 éve
szülő
commit
0d22b254fe
20 módosított fájl, 4439 hozzáadás és 4462 törlés
  1. 14 3
      package-lock.json
  2. 14 0
      packages/admin-ui/src/lib/catalog/src/components/product-variant-detail/product-variant-detail.component.html
  3. 9 0
      packages/admin-ui/src/lib/catalog/src/components/product-variant-detail/product-variant-detail.component.scss
  4. 14 7
      packages/admin-ui/src/lib/catalog/src/components/product-variant-detail/product-variant-detail.component.ts
  5. 7 3
      packages/admin-ui/src/lib/catalog/src/components/product-variant-detail/product-variant-detail.graphql.ts
  6. 22 11
      packages/admin-ui/src/lib/core/src/common/generated-types.ts
  7. 7 0
      packages/admin-ui/src/lib/core/src/data/definitions/product-definitions.ts
  8. 437 458
      packages/asset-server-plugin/e2e/graphql/generated-e2e-asset-server-plugin-types.ts
  9. 653 674
      packages/common/src/generated-shop-types.ts
  10. 19 11
      packages/common/src/generated-types.ts
  11. 51 0
      packages/core/e2e/custom-fields.e2e-spec.ts
  12. 437 458
      packages/core/e2e/graphql/generated-e2e-admin-types.ts
  13. 624 645
      packages/core/e2e/graphql/generated-e2e-shop-types.ts
  14. 9 2
      packages/core/src/api/schema/admin-api/product.api.graphql
  15. 17 2
      packages/core/src/service/services/product-variant.service.ts
  16. 437 458
      packages/elasticsearch-plugin/e2e/graphql/generated-e2e-elasticsearch-plugin-types.ts
  17. 383 403
      packages/payments-plugin/e2e/graphql/generated-admin-types.ts
  18. 628 649
      packages/payments-plugin/e2e/graphql/generated-shop-types.ts
  19. 657 678
      packages/payments-plugin/src/mollie/graphql/generated-shop-types.ts
  20. 0 0
      schema-admin.json

+ 14 - 3
package-lock.json

@@ -14,8 +14,7 @@
                 "eslint-config-prettier": "^8.8.0",
                 "eslint-plugin-import": "^2.27.5",
                 "eslint-plugin-jsdoc": "^45.0.0",
-                "eslint-plugin-prefer-arrow": "^1.2.3",
-                "sharp": "^0.33.5"
+                "eslint-plugin-prefer-arrow": "^1.2.3"
             },
             "devDependencies": {
                 "@commitlint/cli": "^19.1.0",
@@ -27,7 +26,7 @@
                 "@graphql-codegen/typescript": "4.0.9",
                 "@graphql-codegen/typescript-operations": "4.2.3",
                 "@graphql-tools/schema": "^10.0.4",
-                "@swc/core": "^1.10.1",
+                "@swc/core": "^1.4.6",
                 "@types/klaw-sync": "^6.0.5",
                 "@types/node": "^20.11.19",
                 "concurrently": "^8.2.2",
@@ -3634,6 +3633,18 @@
                 "sisteransi": "^1.0.5"
             }
         },
+        "node_modules/@clack/prompts/node_modules/is-unicode-supported": {
+            "version": "1.3.0",
+            "extraneous": true,
+            "inBundle": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=12"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/sindresorhus"
+            }
+        },
         "node_modules/@clr/angular": {
             "version": "17.0.1",
             "license": "MIT",

+ 14 - 0
packages/admin-ui/src/lib/catalog/src/components/product-variant-detail/product-variant-detail.component.html

@@ -195,12 +195,26 @@
                         [priceIncludesTax]="channelPriceIncludesTax$ | async"
                         [taxCategoryId]="detailForm.get('taxCategoryId')!.value"
                     />
+
+                    <div class="form-grid-span" *ngIf="customPriceFields.length">
+                        <div class="title-row">
+                            <span class="title">{{ 'common.custom-fields' | translate }}</span>
+                        </div>
+                        <vdr-tabbed-custom-fields
+                            entityName="ProductVariantPrice"
+                            [customFields]="customPriceFields"
+                            [customFieldsFormGroup]="price.get(['customFields'])"
+                            [readonly]="!(updatePermissions | hasPermission)"
+                        />
+                    </div>
                 </div>
+
                 <vdr-variant-price-strategy-detail
                     [channelPriceIncludesTax]="channelPriceIncludesTax$ | async"
                     [channelDefaultCurrencyCode]="channelDefaultCurrencyCode"
                     [variant]="variant"
                 />
+
                 <ng-container *ngIf="unusedCurrencyCodes$ | async as unusedCurrencyCodes">
                     <div *ngIf="unusedCurrencyCodes.length">
                         <vdr-dropdown>

+ 9 - 0
packages/admin-ui/src/lib/catalog/src/components/product-variant-detail/product-variant-detail.component.scss

@@ -29,3 +29,12 @@ vdr-product-variant-quick-jump {
         }
     }
 }
+
+.title-row {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+}
+.title {
+    font-size: var(--font-size-base);
+}

+ 14 - 7
packages/admin-ui/src/lib/catalog/src/components/product-variant-detail/product-variant-detail.component.ts

@@ -71,6 +71,7 @@ export class ProductVariantDetailComponent
 {
     public readonly updatePermissions = [Permission.UpdateCatalog, Permission.UpdateProduct];
     readonly customFields = this.getCustomFieldConfig('ProductVariant');
+    readonly customPriceFields = this.getCustomFieldConfig('ProductVariantPrice');
     readonly customOptionFields = this.getCustomFieldConfig('ProductOption');
     stockLevels$: Observable<NonNullable<GetProductVariantDetailQuery['productVariant']>['stockLevels']>;
     detailForm = this.formBuilder.group<VariantFormValue>({
@@ -99,6 +100,7 @@ export class ProductVariantDetailComponent
             price: FormControl<number | null>;
             currencyCode: FormControl<CurrencyCode | null>;
             delete: FormControl<boolean | null>;
+            customFields: FormGroup<any>; //TODO: Add type
         }>
     >([]);
     assetChanges: SelectedAssets = {};
@@ -181,6 +183,7 @@ export class ProductVariantDetailComponent
                 currencyCode,
                 price: 0,
                 delete: false as boolean,
+                customFields: this.formBuilder.group(getCustomFieldsDefaults(this.customPriceFields)),
             }),
         );
     }
@@ -246,6 +249,7 @@ export class ProductVariantDetailComponent
                                 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
                                 currencyCode: control.value.currencyCode!,
                                 delete: control.value.delete === true,
+                                customFields: control.get('customFields')?.value,
                             }));
                     }
                     return this.dataService.mutate(ProductVariantUpdateMutationDocument, {
@@ -353,13 +357,16 @@ export class ProductVariantDetailComponent
         }
         this.pricesForm.clear();
         for (const price of variant.prices) {
-            this.pricesForm.push(
-                this.formBuilder.group({
-                    price: price.price,
-                    currencyCode: price.currencyCode,
-                    delete: false as boolean,
-                }),
-            );
+            const priceForm = this.formBuilder.group({
+                price: price.price,
+                currencyCode: price.currencyCode,
+                delete: false as boolean,
+                customFields: this.formBuilder.group(getCustomFieldsDefaults(this.customPriceFields)),
+            });
+            if (this.customPriceFields.length) {
+                this.setCustomFieldFormValues(this.customPriceFields, priceForm.get(['customFields']), price);
+            }
+            this.pricesForm.push(priceForm);
         }
         if (this.customFields.length) {
             this.setCustomFieldFormValues(

+ 7 - 3
packages/admin-ui/src/lib/catalog/src/components/product-variant-detail/product-variant-detail.graphql.ts

@@ -1,4 +1,8 @@
-import { ASSET_FRAGMENT, PRODUCT_OPTION_FRAGMENT } from '@vendure/admin-ui/core';
+import {
+    ASSET_FRAGMENT,
+    PRODUCT_OPTION_FRAGMENT,
+    PRODUCT_VARIANT_PRICE_FRAGMENT,
+} from '@vendure/admin-ui/core';
 import { gql } from 'apollo-angular';
 
 export const PRODUCT_VARIANT_DETAIL_QUERY_PRODUCT_VARIANT_FRAGMENT = gql`
@@ -12,8 +16,7 @@ export const PRODUCT_VARIANT_DETAIL_QUERY_PRODUCT_VARIANT_FRAGMENT = gql`
         price
         currencyCode
         prices {
-            price
-            currencyCode
+            ...ProductVariantPrice
         }
         priceWithTax
         stockOnHand
@@ -87,6 +90,7 @@ export const PRODUCT_VARIANT_DETAIL_QUERY_PRODUCT_VARIANT_FRAGMENT = gql`
             }
         }
     }
+    ${PRODUCT_VARIANT_PRICE_FRAGMENT}
 `;
 
 export const PRODUCT_VARIANT_DETAIL_QUERY = gql`

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 22 - 11
packages/admin-ui/src/lib/core/src/common/generated-types.ts


+ 7 - 0
packages/admin-ui/src/lib/core/src/data/definitions/product-definitions.ts

@@ -62,6 +62,13 @@ export const PRODUCT_OPTION_FRAGMENT = gql`
     }
 `;
 
+export const PRODUCT_VARIANT_PRICE_FRAGMENT = gql`
+    fragment ProductVariantPrice on ProductVariantPrice {
+        price
+        currencyCode
+    }
+`;
+
 export const PRODUCT_VARIANT_FRAGMENT = gql`
     fragment ProductVariant on ProductVariant {
         id

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 437 - 458
packages/asset-server-plugin/e2e/graphql/generated-e2e-asset-server-plugin-types.ts


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 653 - 674
packages/common/src/generated-shop-types.ts


+ 19 - 11
packages/common/src/generated-types.ts

@@ -894,6 +894,7 @@ export type CreateProductVariantInput = {
   optionIds?: InputMaybe<Array<Scalars['ID']['input']>>;
   outOfStockThreshold?: InputMaybe<Scalars['Int']['input']>;
   price?: InputMaybe<Scalars['Money']['input']>;
+  prices?: InputMaybe<Array<InputMaybe<CreateProductVariantPriceInput>>>;
   productId: Scalars['ID']['input'];
   sku: Scalars['String']['input'];
   stockLevels?: InputMaybe<Array<StockLevelInput>>;
@@ -910,6 +911,12 @@ export type CreateProductVariantOptionInput = {
   translations: Array<ProductOptionTranslationInput>;
 };
 
+export type CreateProductVariantPriceInput = {
+  currencyCode: CurrencyCode;
+  customFields?: InputMaybe<Scalars['JSON']['input']>;
+  price: Scalars['Money']['input'];
+};
+
 export type CreatePromotionInput = {
   actions: Array<ConfigurableOperationInput>;
   conditions: Array<ConfigurableOperationInput>;
@@ -4877,16 +4884,6 @@ export type ProductVariantPrice = {
   price: Scalars['Money']['output'];
 };
 
-/**
- * Used to set up update the price of a ProductVariant in a particular Channel.
- * If the `delete` flag is `true`, the price will be deleted for the given Channel.
- */
-export type ProductVariantPriceInput = {
-  currencyCode: CurrencyCode;
-  delete?: InputMaybe<Scalars['Boolean']['input']>;
-  price: Scalars['Money']['input'];
-};
-
 export type ProductVariantSortParameter = {
   createdAt?: InputMaybe<SortOrder>;
   id?: InputMaybe<SortOrder>;
@@ -6604,7 +6601,7 @@ export type UpdateProductVariantInput = {
   /** Sets the price for the ProductVariant in the Channel's default currency */
   price?: InputMaybe<Scalars['Money']['input']>;
   /** Allows multiple prices to be set for the ProductVariant in different currencies. */
-  prices?: InputMaybe<Array<ProductVariantPriceInput>>;
+  prices?: InputMaybe<Array<UpdateProductVariantPriceInput>>;
   sku?: InputMaybe<Scalars['String']['input']>;
   stockLevels?: InputMaybe<Array<StockLevelInput>>;
   stockOnHand?: InputMaybe<Scalars['Int']['input']>;
@@ -6614,6 +6611,17 @@ export type UpdateProductVariantInput = {
   useGlobalOutOfStockThreshold?: InputMaybe<Scalars['Boolean']['input']>;
 };
 
+/**
+ * Used to set up update the price of a ProductVariant in a particular Channel.
+ * If the `delete` flag is `true`, the price will be deleted for the given Channel.
+ */
+export type UpdateProductVariantPriceInput = {
+  currencyCode: CurrencyCode;
+  customFields?: InputMaybe<Scalars['JSON']['input']>;
+  delete?: InputMaybe<Scalars['Boolean']['input']>;
+  price: Scalars['Money']['input'];
+};
+
 export type UpdatePromotionInput = {
   actions?: InputMaybe<Array<ConfigurableOperationInput>>;
   conditions?: InputMaybe<Array<ConfigurableOperationInput>>;

+ 51 - 0
packages/core/e2e/custom-fields.e2e-spec.ts

@@ -188,6 +188,12 @@ const customConfig = mergeConfig(testConfig(), {
             { name: 'secretKey2', type: 'string', defaultValue: '', public: false, internal: false },
         ],
         OrderLine: [{ name: 'validateInt', type: 'int', min: 0, max: 10 }],
+        ProductVariantPrice: [
+            {
+                name: 'costPrice',
+                type: 'int',
+            }
+        ],  
         // Single readonly Address custom field to test
         // https://github.com/vendure-ecommerce/vendure/issues/3326
         Address: [
@@ -1165,4 +1171,49 @@ describe('Custom fields', () => {
             }
         });
     });
+
+    it('on ProductVariantPrice', async () => {
+        const { updateProductVariants } = await adminClient.query(
+            gql`
+                mutation UpdateProductVariants($input: [UpdateProductVariantInput!]!) {
+                    updateProductVariants(input: $input) {
+                        id
+                        prices {
+                            currencyCode
+                            price
+                            customFields {
+                                costPrice
+                            }
+                        }
+                    }
+                }
+            `,
+            {
+                input: [
+                    {
+                        id: 'T_1',
+                        prices: [
+                            {
+                                price: 129900,
+                                currencyCode: 'USD',
+                                customFields: {
+                                    costPrice: 100,
+                                },
+                            },
+                        ],
+                    },
+                ],
+            },
+        );
+
+        expect(updateProductVariants[0].prices).toEqual([
+            {
+                currencyCode: 'USD',
+                price: 129900,
+                customFields: {
+                    costPrice: 100,
+                },
+            },
+        ]);
+    });
 });

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 437 - 458
packages/core/e2e/graphql/generated-e2e-admin-types.ts


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 624 - 645
packages/core/e2e/graphql/generated-e2e-shop-types.ts


+ 9 - 2
packages/core/src/api/schema/admin-api/product.api.graphql

@@ -130,11 +130,17 @@ input StockLevelInput {
     stockOnHand: Int!
 }
 
+
+input CreateProductVariantPriceInput {
+    currencyCode: CurrencyCode!
+    price: Money!
+}
+
 """
 Used to set up update the price of a ProductVariant in a particular Channel.
 If the `delete` flag is `true`, the price will be deleted for the given Channel.
 """
-input ProductVariantPriceInput {
+input UpdateProductVariantPriceInput {
     currencyCode: CurrencyCode!
     price: Money!
     delete: Boolean
@@ -146,6 +152,7 @@ input CreateProductVariantInput {
     facetValueIds: [ID!]
     sku: String!
     price: Money
+    prices: [CreateProductVariantPriceInput]
     taxCategoryId: ID
     optionIds: [ID!]
     featuredAssetId: ID
@@ -172,7 +179,7 @@ input UpdateProductVariantInput {
     """
     Allows multiple prices to be set for the ProductVariant in different currencies.
     """
-    prices: [ProductVariantPriceInput!]
+    prices: [UpdateProductVariantPriceInput!]
     featuredAssetId: ID
     assetIds: [ID!]
     stockOnHand: Int

+ 17 - 2
packages/core/src/service/services/product-variant.service.ts

@@ -11,7 +11,7 @@ import {
     RemoveProductVariantsFromChannelInput,
     UpdateProductVariantInput,
 } from '@vendure/common/lib/generated-types';
-import { ID, PaginatedList } from '@vendure/common/lib/shared-types';
+import { CustomFieldsObject, ID, PaginatedList } from '@vendure/common/lib/shared-types';
 import { unique } from '@vendure/common/lib/unique';
 import { In, IsNull } from 'typeorm';
 
@@ -48,6 +48,7 @@ import { ListQueryBuilder } from '../helpers/list-query-builder/list-query-build
 import { ProductPriceApplicator } from '../helpers/product-price-applicator/product-price-applicator';
 import { TranslatableSaver } from '../helpers/translatable-saver/translatable-saver';
 import { TranslatorService } from '../helpers/translator/translator.service';
+import { patchEntity } from '../helpers/utils/patch-entity';
 import { samplesEach } from '../helpers/utils/samples-each';
 
 import { AssetService } from './asset.service';
@@ -552,6 +553,7 @@ export class ProductVariantService {
                         priceInput.price,
                         ctx.channelId,
                         priceInput.currencyCode,
+                        priceInput.customFields,
                     );
                 }
             }
@@ -570,6 +572,7 @@ export class ProductVariantService {
         price: number,
         channelId: ID,
         currencyCode?: CurrencyCode,
+        customFields?: CustomFieldsObject,
     ): Promise<ProductVariantPrice> {
         const { productVariantPriceUpdateStrategy } = this.configService.catalogOptions;
         const allPrices = await this.connection.getRepository(ctx, ProductVariantPrice).find({
@@ -608,10 +611,22 @@ export class ProductVariantService {
             );
             targetPrice = createdPrice;
         } else {
-            targetPrice.price = price;
+            patchEntity(targetPrice, {
+                price,
+                customFields: customFields || targetPrice.customFields,
+            });
             const updatedPrice = await this.connection
                 .getRepository(ctx, ProductVariantPrice)
                 .save(targetPrice);
+
+            if (customFields) {
+                await this.customFieldRelationService.updateRelations(
+                    ctx,
+                    ProductVariantPrice,
+                    customFields,
+                    updatedPrice,
+                );
+            }
             await this.eventBus.publish(new ProductVariantPriceEvent(ctx, [updatedPrice], 'updated'));
             additionalPricesToUpdate = await productVariantPriceUpdateStrategy.onPriceUpdated(
                 ctx,

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 437 - 458
packages/elasticsearch-plugin/e2e/graphql/generated-e2e-elasticsearch-plugin-types.ts


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 383 - 403
packages/payments-plugin/e2e/graphql/generated-admin-types.ts


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 628 - 649
packages/payments-plugin/e2e/graphql/generated-shop-types.ts


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 657 - 678
packages/payments-plugin/src/mollie/graphql/generated-shop-types.ts


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 0 - 0
schema-admin.json


Nem az összes módosított fájl került megjelenítésre, mert túl sok fájl változott