Browse Source

Merge branch 'master' into minor

Michael Bromley 2 years ago
parent
commit
e6fdd1411a
18 changed files with 244 additions and 106 deletions
  1. 1 1
      packages/admin-ui/src/lib/catalog/src/components/product-variant-detail/product-variant-detail.component.ts
  2. 10 6
      packages/admin-ui/src/lib/core/src/providers/dashboard-widget/dashboard-widget.service.ts
  3. 3 1
      packages/admin-ui/src/lib/core/src/shared/components/affixed-input/affixed-input.component.scss
  4. 2 0
      packages/admin-ui/src/lib/core/src/shared/dynamic-form-inputs/relation-form-input/asset/relation-asset-input.component.scss
  5. 3 3
      packages/admin-ui/src/lib/dashboard/src/components/dashboard/dashboard.component.html
  6. 4 4
      packages/admin-ui/src/lib/dashboard/src/components/dashboard/dashboard.component.ts
  7. 6 2
      packages/admin-ui/src/lib/order/src/components/fulfill-order-dialog/fulfill-order-dialog.component.ts
  8. 1 1
      packages/admin-ui/src/lib/order/src/components/order-detail/order-detail.component.html
  9. 3 4
      packages/admin-ui/src/lib/order/src/components/order-detail/order-detail.component.ts
  10. 66 1
      packages/core/e2e/entity-hydrator.e2e-spec.ts
  11. 47 0
      packages/core/e2e/order.e2e-spec.ts
  12. 0 32
      packages/core/src/config/entity-metadata/add-foreign-key-indices.ts
  13. 0 1
      packages/core/src/config/index.ts
  14. 25 11
      packages/core/src/service/helpers/entity-hydrator/entity-hydrator.service.ts
  15. 30 5
      packages/core/src/service/helpers/utils/order-utils.ts
  16. 19 7
      packages/core/src/service/services/order.service.ts
  17. 0 4
      packages/dev-server/load-testing/load-test-config.ts
  18. 24 23
      packages/payments-plugin/e2e/mollie-dev-server.ts

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

@@ -207,7 +207,6 @@ export class ProductVariantDetailComponent
             .pipe(
                 take(1),
                 mergeMap(([variant, languageCode]) => {
-                    const formValue = this.detailForm.value;
                     const input = pick(
                         this.getUpdatedVariant(
                             variant,
@@ -400,6 +399,7 @@ export class ProductVariantDetailComponent
             assetIds: this.assetChanges.assets?.map(a => a.id),
             featuredAssetId: this.assetChanges.featuredAsset?.id,
             facetValueIds: variantFormGroup.value.facetValueIds,
+            taxCategoryId: variantFormGroup.value.taxCategoryId,
         } as UpdateProductVariantInput | CreateProductVariantInput;
     }
 }

+ 10 - 6
packages/admin-ui/src/lib/core/src/providers/dashboard-widget/dashboard-widget.service.ts

@@ -28,15 +28,19 @@ export class DashboardWidgetService {
         this.registry.set(id, config);
     }
 
-    getAvailableIds(currentUserPermissions: Permission[]): string[] {
-        const hasAllPermissions = (requiredPerms: string[], userPerms: string[]): boolean => requiredPerms.every(p => userPerms.includes(p));
+    getAvailableWidgets(
+        currentUserPermissions: Permission[],
+    ): Array<{ id: string; config: DashboardWidgetConfig }> {
+        const hasAllPermissions = (requiredPerms: string[], userPerms: string[]): boolean =>
+            requiredPerms.every(p => userPerms.includes(p));
 
         return [...this.registry.entries()]
-            .filter(([id, config]) => (
+            .filter(
+                ([id, config]) =>
                     !config.requiresPermissions ||
-                    hasAllPermissions(config.requiresPermissions, currentUserPermissions)
-                ))
-            .map(([id]) => id);
+                    hasAllPermissions(config.requiresPermissions, currentUserPermissions),
+            )
+            .map(([id, config]) => ({ id, config }));
     }
 
     getWidgetById(id: string) {

+ 3 - 1
packages/admin-ui/src/lib/core/src/shared/components/affixed-input/affixed-input.component.scss

@@ -23,7 +23,8 @@
 ::ng-deep .has-prefix > {
     input[type="text"], input[type="number"] {
         border-top-left-radius: 0 !important;
-        border-bottom-left-radius: 0 !important
+        border-bottom-left-radius: 0 !important;
+        width: 100%;
     }
 }
 
@@ -37,6 +38,7 @@
     input[type="text"], input[type="number"] {
         border-top-right-radius: 0 !important;
         border-bottom-right-radius: 0 !important;
+        width: 100%;
     }
 }
 

+ 2 - 0
packages/admin-ui/src/lib/core/src/shared/dynamic-form-inputs/relation-form-input/asset/relation-asset-input.component.scss

@@ -1,6 +1,8 @@
 .preview {
     cursor: pointer;
     border-radius: var(--border-radius);
+    max-width: 100px;
+    max-height: 100px;
 }
 
 .detail {

+ 3 - 3
packages/admin-ui/src/lib/dashboard/src/components/dashboard/dashboard.component.html

@@ -9,10 +9,10 @@
             <vdr-dropdown-menu vdrPosition="bottom-right">
                 <button
                     vdrDropdownItem
-                    *ngFor="let id of availableWidgetIds$ | async"
-                    (click)="addWidget(id)"
+                    *ngFor="let widget of availableWidgets$ | async"
+                    (click)="addWidget(widget.id)"
                 >
-                    {{ id }}
+                    {{ (widget.config.title ?? widget.id) | translate }}
                 </button>
             </vdr-dropdown-menu>
         </vdr-dropdown>

+ 4 - 4
packages/admin-ui/src/lib/dashboard/src/components/dashboard/dashboard.component.ts

@@ -22,7 +22,7 @@ import { map, tap } from 'rxjs/operators';
 })
 export class DashboardComponent implements OnInit {
     widgetLayout: WidgetLayout | undefined;
-    availableWidgetIds$: Observable<string[]>;
+    availableWidgets$: Observable<Array<{ id: string; config: DashboardWidgetConfig }>>;
     private readonly deletionMarker = '__delete__';
     private setTitle = titleSetter();
     constructor(
@@ -33,10 +33,10 @@ export class DashboardComponent implements OnInit {
     ) {}
 
     ngOnInit() {
-        this.availableWidgetIds$ = this.dataService.client.userStatus().stream$.pipe(
+        this.availableWidgets$ = this.dataService.client.userStatus().stream$.pipe(
             map(({ userStatus }) => userStatus.permissions),
-            map(permissions => this.dashboardWidgetService.getAvailableIds(permissions)),
-            tap(ids => (this.widgetLayout = this.initLayout(ids))),
+            map(permissions => this.dashboardWidgetService.getAvailableWidgets(permissions)),
+            tap(widgets => (this.widgetLayout = this.initLayout(widgets.map(w => w.id)))),
         );
         this.setTitle('breadcrumb.dashboard');
     }

+ 6 - 2
packages/admin-ui/src/lib/order/src/components/fulfill-order-dialog/fulfill-order-dialog.component.ts

@@ -69,7 +69,8 @@ export class FulfillOrderDialogComponent implements Dialog<FulfillOrderInput>, O
     getUnfulfilledCount(line: OrderDetailFragment['lines'][number]): number {
         const fulfilled =
             this.order.fulfillments
-                ?.map(f => f.lines)
+                ?.filter(f => f.state !== 'Cancelled')
+                .map(f => f.lines)
                 .flat()
                 .filter(row => row.orderLineId === line.id)
                 .reduce((sum, row) => sum + row.quantity, 0) ?? 0;
@@ -81,12 +82,15 @@ export class FulfillOrderDialogComponent implements Dialog<FulfillOrderInput>, O
             (total, { fulfillCount }) => total + fulfillCount,
             0,
         );
+        const fulfillmentQuantityIsValid = Object.values(this.fulfillmentQuantities).every(
+            ({ fulfillCount, max }) => fulfillCount <= max,
+        );
         const formIsValid =
             configurableOperationValueIsValid(
                 this.fulfillmentHandlerDef,
                 this.fulfillmentHandlerControl.value,
             ) && this.fulfillmentHandlerControl.valid;
-        return formIsValid && 0 < totalCount;
+        return formIsValid && 0 < totalCount && fulfillmentQuantityIsValid;
     }
 
     select() {

+ 1 - 1
packages/admin-ui/src/lib/order/src/components/order-detail/order-detail.component.html

@@ -169,7 +169,7 @@
                 <tbody>
                     <tr *ngFor="let row of order.taxSummary">
                         <td>{{ row.description }}</td>
-                        <td>{{ row.taxRate / 100 | percent }}</td>
+                        <td>{{ row.taxRate / 100 | percent:'0.0-2' }}</td>
                         <td>{{ row.taxBase | localeCurrency : order.currencyCode }}</td>
                         <td>{{ row.taxTotal | localeCurrency : order.currencyCode }}</td>
                     </tr>

+ 3 - 4
packages/admin-ui/src/lib/order/src/components/order-detail/order-detail.component.ts

@@ -257,10 +257,9 @@ export class OrderDetailComponent
     }
 
     canAddFulfillment(order: OrderDetailFragment): boolean {
-        const allFulfillmentLines: FulfillmentFragment['lines'] = (order.fulfillments ?? []).reduce(
-            (all, fulfillment) => [...all, ...fulfillment.lines],
-            [] as FulfillmentFragment['lines'],
-        );
+        const allFulfillmentLines: FulfillmentFragment['lines'] = (order.fulfillments ?? [])
+            .filter(fulfillment => fulfillment.state !== 'Cancelled')
+            .reduce((all, fulfillment) => [...all, ...fulfillment.lines], [] as FulfillmentFragment['lines']);
         let allItemsFulfilled = true;
         for (const line of order.lines) {
             const totalFulfilledCount = allFulfillmentLines

+ 66 - 1
packages/core/e2e/entity-hydrator.e2e-spec.ts

@@ -1,5 +1,14 @@
 /* eslint-disable @typescript-eslint/no-non-null-assertion */
-import { mergeConfig, Order, Product, ProductVariant } from '@vendure/core';
+import {
+    ChannelService,
+    EntityHydrator,
+    mergeConfig,
+    Order,
+    Product,
+    ProductVariant,
+    RequestContext,
+    ActiveOrderService,
+} from '@vendure/core';
 import { createErrorResultGuard, createTestEnvironment, ErrorResultGuard } from '@vendure/testing';
 import gql from 'graphql-tag';
 import path from 'path';
@@ -11,6 +20,7 @@ import { testConfig, TEST_SETUP_TIMEOUT_MS } from '../../../e2e-common/test-conf
 import { HydrationTestPlugin } from './fixtures/test-plugins/hydration-test-plugin';
 import { UpdateChannelMutation, UpdateChannelMutationVariables } from './graphql/generated-e2e-admin-types';
 import {
+    AddItemToOrderDocument,
     AddItemToOrderMutation,
     AddItemToOrderMutationVariables,
     UpdatedOrderFragment,
@@ -225,6 +235,61 @@ describe('Entity hydration', () => {
         expect(hydrateChannel.customFields.thumb).toBeDefined();
         expect(hydrateChannel.customFields.thumb.id).toBe('T_2');
     });
+
+    // https://github.com/vendure-ecommerce/vendure/issues/2013
+    describe('hydration of OrderLine ProductVariantPrices', () => {
+        let order: Order | undefined;
+
+        it('Create order with 3 items', async () => {
+            await shopClient.asUserWithCredentials('hayden.zieme12@hotmail.com', 'test');
+            await shopClient.query(AddItemToOrderDocument, {
+                productVariantId: '1',
+                quantity: 1,
+            });
+            await shopClient.query(AddItemToOrderDocument, {
+                productVariantId: '2',
+                quantity: 1,
+            });
+            const { addItemToOrder } = await shopClient.query(AddItemToOrderDocument, {
+                productVariantId: '3',
+                quantity: 1,
+            });
+            orderResultGuard.assertSuccess(addItemToOrder);
+            const channel = await server.app.get(ChannelService).getDefaultChannel();
+            // This is ugly, but in our real life example we use a CTX constructed by Vendure
+            const internalOrderId = +addItemToOrder.id.replace(/^\D+/g, '');
+            const ctx = new RequestContext({
+                channel,
+                authorizedAsOwnerOnly: true,
+                apiType: 'shop',
+                isAuthorized: true,
+                session: {
+                    activeOrderId: internalOrderId,
+                    activeChannelId: 1,
+                    user: {
+                        id: 2,
+                    },
+                } as any,
+            });
+            order = await server.app.get(ActiveOrderService).getActiveOrder(ctx, undefined);
+            await server.app.get(EntityHydrator).hydrate(ctx, order!, {
+                relations: ['lines.productVariant'],
+                applyProductVariantPrices: true,
+            });
+        });
+
+        it('Variant of orderLine 1 has a price', async () => {
+            expect(order!.lines[0].productVariant.priceWithTax).toBeGreaterThan(0);
+        });
+
+        it('Variant of orderLine 2 has a price', async () => {
+            expect(order!.lines[1].productVariant.priceWithTax).toBeGreaterThan(0);
+        });
+
+        it('Variant of orderLine 3 has a price', async () => {
+            expect(order!.lines[1].productVariant.priceWithTax).toBeGreaterThan(0);
+        });
+    });
 });
 
 function getVariantWithName(product: Product, name: string) {

+ 47 - 0
packages/core/e2e/order.e2e-spec.ts

@@ -35,6 +35,7 @@ import * as Codegen from './graphql/generated-e2e-admin-types';
 import {
     AddManualPaymentDocument,
     CanceledOrderFragment,
+    CreateFulfillmentDocument,
     ErrorCode,
     FulfillmentFragment,
     GetOrderDocument,
@@ -47,6 +48,7 @@ import {
     RefundFragment,
     SortOrder,
     StockMovementType,
+    TransitFulfillmentDocument,
 } from './graphql/generated-e2e-admin-types';
 import * as CodegenShop from './graphql/generated-e2e-shop-types';
 import {
@@ -2609,6 +2611,51 @@ describe('Orders resolver', () => {
 
             expect(order!.state).toBe('PaymentSettled');
         });
+
+        // https://github.com/vendure-ecommerce/vendure/issues/2191
+        it('correctly transitions order & fulfillment on partial fulfillment being shipped', async () => {
+            await shopClient.asUserWithCredentials(customers[0].emailAddress, password);
+            const { addItemToOrder } = await shopClient.query<
+                CodegenShop.AddItemToOrderMutation,
+                CodegenShop.AddItemToOrderMutationVariables
+            >(ADD_ITEM_TO_ORDER, {
+                productVariantId: 'T_6',
+                quantity: 3,
+            });
+            await proceedToArrangingPayment(shopClient);
+            orderGuard.assertSuccess(addItemToOrder);
+
+            const order = await addPaymentToOrder(shopClient, singleStageRefundablePaymentMethod);
+            orderGuard.assertSuccess(order);
+
+            const { addFulfillmentToOrder } = await adminClient.query(CreateFulfillmentDocument, {
+                input: {
+                    lines: [{ orderLineId: order.lines[0].id, quantity: 2 }],
+                    handler: {
+                        code: manualFulfillmentHandler.code,
+                        arguments: [
+                            { name: 'method', value: 'Test2' },
+                            { name: 'trackingCode', value: '222' },
+                        ],
+                    },
+                },
+            });
+            fulfillmentGuard.assertSuccess(addFulfillmentToOrder);
+
+            const { transitionFulfillmentToState } = await adminClient.query(TransitFulfillmentDocument, {
+                id: addFulfillmentToOrder.id,
+                state: 'Shipped',
+            });
+            fulfillmentGuard.assertSuccess(transitionFulfillmentToState);
+
+            expect(transitionFulfillmentToState.id).toBe(addFulfillmentToOrder.id);
+            expect(transitionFulfillmentToState.state).toBe('Shipped');
+
+            const { order: order2 } = await adminClient.query(GetOrderDocument, {
+                id: order.id,
+            });
+            expect(order2?.state).toBe('PartiallyShipped');
+        });
     });
 });
 

+ 0 - 32
packages/core/src/config/entity-metadata/add-foreign-key-indices.ts

@@ -1,32 +0,0 @@
-import { Index } from 'typeorm';
-import { MetadataArgsStorage } from 'typeorm/metadata-args/MetadataArgsStorage';
-
-import { StockMovement } from '../../entity/stock-movement/stock-movement.entity';
-
-import { EntityMetadataModifier } from './entity-metadata-modifier';
-
-/**
- * @description
- * Dynamically adds `@Index()` metadata to all many-to-one relations. These are already added
- * by default in MySQL/MariaDB, but not in Postgres. So this modification can lead to improved
- * performance with Postgres - especially when dealing with large numbers of products, orders etc.
- *
- * See https://github.com/vendure-ecommerce/vendure/issues/1502
- *
- * TODO: In v2 we will add the Index to all relations manually, this making this redundant.
- */
-export const addForeignKeyIndices: EntityMetadataModifier = (metadata: MetadataArgsStorage) => {
-    for (const relationMetadata of metadata.relations) {
-        const { relationType, target } = relationMetadata;
-        if (relationType === 'many-to-one') {
-            const embeddedIn = metadata.embeddeds.find(e => e.type() === relationMetadata.target)?.target;
-            const targetClass = (embeddedIn ?? target) as FunctionConstructor;
-            if (typeof targetClass === 'function') {
-                const instance = new targetClass();
-                if (!(instance instanceof StockMovement)) {
-                    Index()(instance, relationMetadata.propertyName);
-                }
-            }
-        }
-    }
-};

+ 0 - 1
packages/core/src/config/index.ts

@@ -29,7 +29,6 @@ export * from './entity/bigint-money-strategy';
 export * from './entity/entity-id-strategy';
 export * from './entity/money-strategy';
 export * from './entity/uuid-id-strategy';
-export * from './entity-metadata/add-foreign-key-indices';
 export * from './entity-metadata/entity-metadata-modifier';
 export * from './fulfillment/default-fulfillment-process';
 export * from './fulfillment/fulfillment-handler';

+ 25 - 11
packages/core/src/service/helpers/entity-hydrator/entity-hydrator.service.ts

@@ -212,20 +212,34 @@ export class EntityHydrator {
         entity: VendureEntity,
         path: string[],
     ): VendureEntity | VendureEntity[] | undefined {
-        let relation: any = entity;
-        for (let i = 0; i < path.length; i++) {
-            const part = path[i];
-            const isLast = i === path.length - 1;
-            if (relation[part]) {
-                relation =
-                    Array.isArray(relation[part]) && relation[part].length && !isLast
-                        ? relation[part][0]
-                        : relation[part];
-            } else {
+        let isArrayResult = false;
+        const result: VendureEntity[] = [];
+
+        function visit(parent: any, parts: string[]): any {
+            if (parts.length === 0) {
                 return;
             }
+            const part = parts.shift() as string;
+            const target = parent[part];
+            if (Array.isArray(target)) {
+                isArrayResult = true;
+                if (parts.length === 0) {
+                    result.push(...target);
+                } else {
+                    for (const item of target) {
+                        visit(item, parts.slice());
+                    }
+                }
+            } else {
+                if (parts.length === 0) {
+                    result.push(target);
+                } else {
+                    visit(target, parts.slice());
+                }
+            }
         }
-        return relation;
+        visit(entity, path.slice());
+        return isArrayResult ? result : result[0];
     }
 
     private getRelationEntityTypeAtPath(entity: VendureEntity, path: string): Type<VendureEntity> {

+ 30 - 5
packages/core/src/service/helpers/utils/order-utils.ts

@@ -44,7 +44,10 @@ export function totalCoveredByPayments(order: Order, state?: PaymentState | Paym
  * Returns true if all (non-cancelled) OrderItems are delivered.
  */
 export function orderItemsAreDelivered(order: Order) {
-    return getOrderLinesFulfillmentStates(order).every(state => state === 'Delivered');
+    return (
+        getOrderLinesFulfillmentStates(order).every(state => state === 'Delivered') &&
+        !isOrderPartiallyFulfilled(order)
+    );
 }
 
 /**
@@ -52,7 +55,10 @@ export function orderItemsAreDelivered(order: Order) {
  */
 export function orderItemsArePartiallyDelivered(order: Order) {
     const states = getOrderLinesFulfillmentStates(order);
-    return states.some(state => state === 'Delivered') && !states.every(state => state === 'Delivered');
+    return (
+        states.some(state => state === 'Delivered') &&
+        (!states.every(state => state === 'Delivered') || isOrderPartiallyFulfilled(order))
+    );
 }
 
 function getOrderLinesFulfillmentStates(order: Order): Array<FulfillmentState | undefined> {
@@ -65,7 +71,7 @@ function getOrderLinesFulfillmentStates(order: Order): Array<FulfillmentState |
                     idsAreEqual(fl.orderLineId, line.id),
                 );
                 const totalFulfilled = summate(matchingFulfillmentLines, 'quantity');
-                if (totalFulfilled === line.quantity) {
+                if (0 < totalFulfilled) {
                     return matchingFulfillmentLines.map(l => l.fulfillment.state);
                 } else {
                     return undefined;
@@ -81,14 +87,20 @@ function getOrderLinesFulfillmentStates(order: Order): Array<FulfillmentState |
  */
 export function orderItemsArePartiallyShipped(order: Order) {
     const states = getOrderLinesFulfillmentStates(order);
-    return states.some(state => state === 'Shipped') && !states.every(state => state === 'Shipped');
+    return (
+        states.some(state => state === 'Shipped') &&
+        (!states.every(state => state === 'Shipped') || isOrderPartiallyFulfilled(order))
+    );
 }
 
 /**
  * Returns true if all (non-cancelled) OrderItems are shipped.
  */
 export function orderItemsAreShipped(order: Order) {
-    return getOrderLinesFulfillmentStates(order).every(state => state === 'Shipped');
+    return (
+        getOrderLinesFulfillmentStates(order).every(state => state === 'Shipped') &&
+        !isOrderPartiallyFulfilled(order)
+    );
 }
 
 /**
@@ -107,6 +119,19 @@ function getOrderFulfillmentLines(order: Order): FulfillmentLine[] {
         );
 }
 
+/**
+ * Returns true if Fulfillments exist for only some but not all of the
+ * order items.
+ */
+function isOrderPartiallyFulfilled(order: Order) {
+    const fulfillmentLines = getOrderFulfillmentLines(order);
+    const lines = fulfillmentLines.reduce((acc, item) => {
+        acc[item.orderLineId] = (acc[item.orderLineId] || 0) + item.quantity;
+        return acc;
+    }, {} as { [orderLineId: string]: number });
+    return order.lines.some(line => line.quantity > lines[line.id]);
+}
+
 export async function getOrdersFromLines(
     ctx: RequestContext,
     connection: TransactionalConnection,

+ 19 - 7
packages/core/src/service/services/order.service.ts

@@ -1233,7 +1233,7 @@ export class OrderService {
         ctx: RequestContext,
         input: FulfillOrderInput,
     ) {
-        const linesToBeFulfilled = await this.connection
+        const existingFulfillmentLines = await this.connection
             .getRepository(ctx, FulfillmentLine)
             .createQueryBuilder('fulfillmentLine')
             .leftJoinAndSelect('fulfillmentLine.orderLine', 'orderLine')
@@ -1244,13 +1244,25 @@ export class OrderService {
             .andWhere('fulfillment.state != :state', { state: 'Cancelled' })
             .getMany();
 
-        for (const lineToBeFulfilled of linesToBeFulfilled) {
-            const unfulfilledQuantity = lineToBeFulfilled.orderLine.quantity - lineToBeFulfilled.quantity;
-            const lineInput = input.lines.find(l =>
-                idsAreEqual(l.orderLineId, lineToBeFulfilled.orderLine.id),
+        for (const inputLine of input.lines) {
+            const existingFulfillmentLine = existingFulfillmentLines.find(l =>
+                idsAreEqual(l.orderLineId, inputLine.orderLineId),
             );
-            if (unfulfilledQuantity < (lineInput?.quantity ?? 0)) {
-                return true;
+            if (existingFulfillmentLine) {
+                const unfulfilledQuantity =
+                    existingFulfillmentLine.orderLine.quantity - existingFulfillmentLine.quantity;
+                if (unfulfilledQuantity < inputLine.quantity) {
+                    return true;
+                }
+            } else {
+                const orderLine = await this.connection.getEntityOrThrow(
+                    ctx,
+                    OrderLine,
+                    inputLine.orderLineId,
+                );
+                if (orderLine.quantity < inputLine.quantity) {
+                    return true;
+                }
             }
         }
         return false;

+ 0 - 4
packages/dev-server/load-testing/load-test-config.ts

@@ -1,7 +1,6 @@
 /* eslint-disable no-console */
 import { AssetServerPlugin } from '@vendure/asset-server-plugin';
 import {
-    addForeignKeyIndices,
     defaultConfig,
     DefaultJobQueuePlugin,
     DefaultLogger,
@@ -59,9 +58,6 @@ export function getLoadTestConfig(
             ...connectionOptions,
             synchronize: true,
         },
-        entityOptions: {
-            metadataModifiers: [addForeignKeyIndices],
-        },
         authOptions: {
             tokenMethod,
             requireVerification: false,

+ 24 - 23
packages/payments-plugin/e2e/mollie-dev-server.ts

@@ -20,8 +20,12 @@ import { MolliePlugin } from '../package/mollie';
 import { molliePaymentHandler } from '../package/mollie/mollie.handler';
 
 import { CREATE_PAYMENT_METHOD } from './graphql/admin-queries';
-import { CreatePaymentMethod } from './graphql/generated-admin-types';
-import { AddItemToOrder } from './graphql/generated-shop-types';
+import {
+    CreatePaymentMethodMutation,
+    CreatePaymentMethodMutationVariables,
+    LanguageCode,
+} from './graphql/generated-admin-types';
+import { AddItemToOrderMutation, AddItemToOrderMutationVariables } from './graphql/generated-shop-types';
 import { ADD_ITEM_TO_ORDER } from './graphql/shop-queries';
 import { CREATE_MOLLIE_PAYMENT_INTENT, setShipping } from './payment-helpers';
 
@@ -30,6 +34,7 @@ import { CREATE_MOLLIE_PAYMENT_INTENT, setShipping } from './payment-helpers';
  */
 /* eslint-disable @typescript-eslint/no-floating-promises */
 async function runMollieDevServer(useDynamicRedirectUrl: boolean) {
+    // eslint-disable-next-line no-console
     console.log('Starting Mollie dev server with dynamic redirectUrl: ', useDynamicRedirectUrl);
     // eslint-disable-next-line @typescript-eslint/no-var-requires
     require('dotenv').config();
@@ -68,18 +73,26 @@ async function runMollieDevServer(useDynamicRedirectUrl: boolean) {
         }
     `);
     // Create method
-    await adminClient.query<CreatePaymentMethod.Mutation, CreatePaymentMethod.Variables>(
+    await adminClient.query<CreatePaymentMethodMutation, CreatePaymentMethodMutationVariables>(
         CREATE_PAYMENT_METHOD,
         {
             input: {
                 code: 'mollie',
-                name: 'Mollie payment test',
-                description: 'This is a Mollie test payment method',
+                translations: [
+                    {
+                        languageCode: LanguageCode.en,
+                        name: 'Mollie payment test',
+                        description: 'This is a Mollie test payment method',
+                    },
+                ],
                 enabled: true,
                 handler: {
                     code: molliePaymentHandler.code,
                     arguments: [
-                        { name: 'redirectUrl', value: `${tunnel.url}/admin/orders?filter=open&page=1&dynamicRedirectUrl=false` },
+                        {
+                            name: 'redirectUrl',
+                            value: `${tunnel.url}/admin/orders?filter=open&page=1&dynamicRedirectUrl=false`,
+                        },
                         // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
                         { name: 'apiKey', value: process.env.MOLLIE_APIKEY! },
                         { name: 'autoCapture', value: 'false' },
@@ -90,9 +103,9 @@ async function runMollieDevServer(useDynamicRedirectUrl: boolean) {
     );
     // Prepare order for payment
     await shopClient.asUserWithCredentials('hayden.zieme12@hotmail.com', 'test');
-    await shopClient.query<AddItemToOrder.Order, AddItemToOrder.Variables>(ADD_ITEM_TO_ORDER, {
+    await shopClient.query<AddItemToOrderMutation, AddItemToOrderMutationVariables>(ADD_ITEM_TO_ORDER, {
         productVariantId: 'T_5',
-        quantity: 10,
+        quantity: 1,
     });
     const ctx = new RequestContext({
         apiType: 'admin',
@@ -100,22 +113,9 @@ async function runMollieDevServer(useDynamicRedirectUrl: boolean) {
         authorizedAsOwnerOnly: false,
         channel: await server.app.get(ChannelService).getDefaultChannel(),
     });
-    await server.app.get(OrderService).addSurchargeToOrder(ctx, 1, {
-        description: 'Negative test surcharge',
-        listPrice: -20000,
-    });
     await setShipping(shopClient);
     // Add pre payment to order
     const order = await server.app.get(OrderService).findOne(ctx, 1);
-    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-    await server.app.get(PaymentService).createManualPayment(ctx, order!, 10000, {
-        method: 'Manual',
-        // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-        orderId: order!.id,
-        metadata: {
-            bogus: 'test',
-        },
-    });
     const { createMolliePaymentIntent } = await shopClient.query(CREATE_MOLLIE_PAYMENT_INTENT, {
         input: {
             redirectUrl: `${tunnel.url}/admin/orders?filter=open&page=1&dynamicRedirectUrl=true`,
@@ -126,10 +126,11 @@ async function runMollieDevServer(useDynamicRedirectUrl: boolean) {
     if (createMolliePaymentIntent.errorCode) {
         throw createMolliePaymentIntent;
     }
-    Logger.info(`Mollie payment link: ${createMolliePaymentIntent.url as string}`, 'Mollie DevServer');
+    // eslint-disable-next-line no-console
+    console.log('\x1b[41m', `Mollie payment link: ${createMolliePaymentIntent.url as string}`, '\x1b[0m');
 }
 
 (async () => {
     // Change the value of the parameter to true to test with the dynamic redirectUrl functionality
     await runMollieDevServer(false);
-})()
+})();