Parcourir la source

feat(core): Extend API with additional Fulfillment info

Relates to #1727. This commit adds

- `fulfillments` field to the `OrderLine` type
- `summary` field to `Fulfillment` type

These API extensions allow much more efficient querying of data required to display
fulfillment info in the Admin UI. It allows us to fetch much less data at the OrderItem
level, enabling much smaller and faster queries on the OrderDetail page.
Michael Bromley il y a 3 ans
Parent
commit
3f0115b1d1

+ 7 - 0
packages/asset-server-plugin/e2e/graphql/generated-e2e-asset-server-plugin-types.ts

@@ -1571,12 +1571,18 @@ export type Fulfillment = Node & {
     createdAt: Scalars['DateTime'];
     createdAt: Scalars['DateTime'];
     updatedAt: Scalars['DateTime'];
     updatedAt: Scalars['DateTime'];
     orderItems: Array<OrderItem>;
     orderItems: Array<OrderItem>;
+    summary: Array<FulfillmentLineSummary>;
     state: Scalars['String'];
     state: Scalars['String'];
     method: Scalars['String'];
     method: Scalars['String'];
     trackingCode?: Maybe<Scalars['String']>;
     trackingCode?: Maybe<Scalars['String']>;
     customFields?: Maybe<Scalars['JSON']>;
     customFields?: Maybe<Scalars['JSON']>;
 };
 };
 
 
+export type FulfillmentLineSummary = {
+    orderLine: OrderLine;
+    quantity: Scalars['Int'];
+};
+
 /** Returned when there is an error in transitioning the Fulfillment state */
 /** Returned when there is an error in transitioning the Fulfillment state */
 export type FulfillmentStateTransitionError = ErrorResult & {
 export type FulfillmentStateTransitionError = ErrorResult & {
     errorCode: ErrorCode;
     errorCode: ErrorCode;
@@ -3167,6 +3173,7 @@ export type OrderLine = Node & {
     discounts: Array<Discount>;
     discounts: Array<Discount>;
     taxLines: Array<TaxLine>;
     taxLines: Array<TaxLine>;
     order: Order;
     order: Order;
+    fulfillments?: Maybe<Array<Fulfillment>>;
     customFields?: Maybe<Scalars['JSON']>;
     customFields?: Maybe<Scalars['JSON']>;
 };
 };
 
 

+ 8 - 0
packages/common/src/generated-shop-types.ts

@@ -1038,12 +1038,19 @@ export type Fulfillment = Node & {
     createdAt: Scalars['DateTime'];
     createdAt: Scalars['DateTime'];
     updatedAt: Scalars['DateTime'];
     updatedAt: Scalars['DateTime'];
     orderItems: Array<OrderItem>;
     orderItems: Array<OrderItem>;
+    summary: Array<FulfillmentLineSummary>;
     state: Scalars['String'];
     state: Scalars['String'];
     method: Scalars['String'];
     method: Scalars['String'];
     trackingCode?: Maybe<Scalars['String']>;
     trackingCode?: Maybe<Scalars['String']>;
     customFields?: Maybe<Scalars['JSON']>;
     customFields?: Maybe<Scalars['JSON']>;
 };
 };
 
 
+export type FulfillmentLineSummary = {
+    __typename?: 'FulfillmentLineSummary';
+    orderLine: OrderLine;
+    quantity: Scalars['Int'];
+};
+
 export enum GlobalFlag {
 export enum GlobalFlag {
     TRUE = 'TRUE',
     TRUE = 'TRUE',
     FALSE = 'FALSE',
     FALSE = 'FALSE',
@@ -2025,6 +2032,7 @@ export type OrderLine = Node & {
     discounts: Array<Discount>;
     discounts: Array<Discount>;
     taxLines: Array<TaxLine>;
     taxLines: Array<TaxLine>;
     order: Order;
     order: Order;
+    fulfillments?: Maybe<Array<Fulfillment>>;
     customFields?: Maybe<Scalars['JSON']>;
     customFields?: Maybe<Scalars['JSON']>;
 };
 };
 
 

+ 8 - 0
packages/common/src/generated-types.ts

@@ -1605,12 +1605,19 @@ export type Fulfillment = Node & {
   createdAt: Scalars['DateTime'];
   createdAt: Scalars['DateTime'];
   updatedAt: Scalars['DateTime'];
   updatedAt: Scalars['DateTime'];
   orderItems: Array<OrderItem>;
   orderItems: Array<OrderItem>;
+  summary: Array<FulfillmentLineSummary>;
   state: Scalars['String'];
   state: Scalars['String'];
   method: Scalars['String'];
   method: Scalars['String'];
   trackingCode?: Maybe<Scalars['String']>;
   trackingCode?: Maybe<Scalars['String']>;
   customFields?: Maybe<Scalars['JSON']>;
   customFields?: Maybe<Scalars['JSON']>;
 };
 };
 
 
+export type FulfillmentLineSummary = {
+  __typename?: 'FulfillmentLineSummary';
+  orderLine: OrderLine;
+  quantity: Scalars['Int'];
+};
+
 /** Returned when there is an error in transitioning the Fulfillment state */
 /** Returned when there is an error in transitioning the Fulfillment state */
 export type FulfillmentStateTransitionError = ErrorResult & {
 export type FulfillmentStateTransitionError = ErrorResult & {
   __typename?: 'FulfillmentStateTransitionError';
   __typename?: 'FulfillmentStateTransitionError';
@@ -3331,6 +3338,7 @@ export type OrderLine = Node & {
   discounts: Array<Discount>;
   discounts: Array<Discount>;
   taxLines: Array<TaxLine>;
   taxLines: Array<TaxLine>;
   order: Order;
   order: Order;
+  fulfillments?: Maybe<Array<Fulfillment>>;
   customFields?: Maybe<Scalars['JSON']>;
   customFields?: Maybe<Scalars['JSON']>;
 };
 };
 
 

+ 98 - 1
packages/core/e2e/graphql/generated-e2e-admin-types.ts

@@ -1571,12 +1571,18 @@ export type Fulfillment = Node & {
     createdAt: Scalars['DateTime'];
     createdAt: Scalars['DateTime'];
     updatedAt: Scalars['DateTime'];
     updatedAt: Scalars['DateTime'];
     orderItems: Array<OrderItem>;
     orderItems: Array<OrderItem>;
+    summary: Array<FulfillmentLineSummary>;
     state: Scalars['String'];
     state: Scalars['String'];
     method: Scalars['String'];
     method: Scalars['String'];
     trackingCode?: Maybe<Scalars['String']>;
     trackingCode?: Maybe<Scalars['String']>;
     customFields?: Maybe<Scalars['JSON']>;
     customFields?: Maybe<Scalars['JSON']>;
 };
 };
 
 
+export type FulfillmentLineSummary = {
+    orderLine: OrderLine;
+    quantity: Scalars['Int'];
+};
+
 /** Returned when there is an error in transitioning the Fulfillment state */
 /** Returned when there is an error in transitioning the Fulfillment state */
 export type FulfillmentStateTransitionError = ErrorResult & {
 export type FulfillmentStateTransitionError = ErrorResult & {
     errorCode: ErrorCode;
     errorCode: ErrorCode;
@@ -3167,6 +3173,7 @@ export type OrderLine = Node & {
     discounts: Array<Discount>;
     discounts: Array<Discount>;
     taxLines: Array<TaxLine>;
     taxLines: Array<TaxLine>;
     order: Order;
     order: Order;
+    fulfillments?: Maybe<Array<Fulfillment>>;
     customFields?: Maybe<Scalars['JSON']>;
     customFields?: Maybe<Scalars['JSON']>;
 };
 };
 
 
@@ -6212,7 +6219,15 @@ export type GetOrderFulfillmentsQueryVariables = Exact<{
 export type GetOrderFulfillmentsQuery = {
 export type GetOrderFulfillmentsQuery = {
     order?: Maybe<
     order?: Maybe<
         Pick<Order, 'id' | 'state'> & {
         Pick<Order, 'id' | 'state'> & {
-            fulfillments?: Maybe<Array<Pick<Fulfillment, 'id' | 'state' | 'nextStates' | 'method'>>>;
+            fulfillments?: Maybe<
+                Array<
+                    Pick<Fulfillment, 'id' | 'state' | 'nextStates' | 'method'> & {
+                        summary: Array<
+                            Pick<FulfillmentLineSummary, 'quantity'> & { orderLine: Pick<OrderLine, 'id'> }
+                        >;
+                    }
+                >
+            >;
         }
         }
     >;
     >;
 };
 };
@@ -6774,6 +6789,32 @@ export type GetOrderWithPaymentsQuery = {
     >;
     >;
 };
 };
 
 
+export type GetOrderLineFulfillmentsQueryVariables = Exact<{
+    id: Scalars['ID'];
+}>;
+
+export type GetOrderLineFulfillmentsQuery = {
+    order?: Maybe<
+        Pick<Order, 'id'> & {
+            lines: Array<
+                Pick<OrderLine, 'id'> & {
+                    fulfillments?: Maybe<
+                        Array<
+                            Pick<Fulfillment, 'id' | 'state'> & {
+                                summary: Array<
+                                    Pick<FulfillmentLineSummary, 'quantity'> & {
+                                        orderLine: Pick<OrderLine, 'id'>;
+                                    }
+                                >;
+                            }
+                        >
+                    >;
+                }
+            >;
+        }
+    >;
+};
+
 export type GetOrderListWithQtyQueryVariables = Exact<{
 export type GetOrderListWithQtyQueryVariables = Exact<{
     options?: Maybe<OrderListOptions>;
     options?: Maybe<OrderListOptions>;
 }>;
 }>;
@@ -8649,6 +8690,22 @@ export namespace GetOrderFulfillments {
     export type Fulfillments = NonNullable<
     export type Fulfillments = NonNullable<
         NonNullable<NonNullable<GetOrderFulfillmentsQuery['order']>['fulfillments']>[number]
         NonNullable<NonNullable<GetOrderFulfillmentsQuery['order']>['fulfillments']>[number]
     >;
     >;
+    export type Summary = NonNullable<
+        NonNullable<
+            NonNullable<
+                NonNullable<NonNullable<GetOrderFulfillmentsQuery['order']>['fulfillments']>[number]
+            >['summary']
+        >[number]
+    >;
+    export type OrderLine = NonNullable<
+        NonNullable<
+            NonNullable<
+                NonNullable<
+                    NonNullable<NonNullable<GetOrderFulfillmentsQuery['order']>['fulfillments']>[number]
+                >['summary']
+            >[number]
+        >['orderLine']
+    >;
 }
 }
 
 
 export namespace GetOrderList {
 export namespace GetOrderList {
@@ -9210,6 +9267,46 @@ export namespace GetOrderWithPayments {
     >;
     >;
 }
 }
 
 
+export namespace GetOrderLineFulfillments {
+    export type Variables = GetOrderLineFulfillmentsQueryVariables;
+    export type Query = GetOrderLineFulfillmentsQuery;
+    export type Order = NonNullable<GetOrderLineFulfillmentsQuery['order']>;
+    export type Lines = NonNullable<
+        NonNullable<NonNullable<GetOrderLineFulfillmentsQuery['order']>['lines']>[number]
+    >;
+    export type Fulfillments = NonNullable<
+        NonNullable<
+            NonNullable<
+                NonNullable<NonNullable<GetOrderLineFulfillmentsQuery['order']>['lines']>[number]
+            >['fulfillments']
+        >[number]
+    >;
+    export type Summary = NonNullable<
+        NonNullable<
+            NonNullable<
+                NonNullable<
+                    NonNullable<
+                        NonNullable<NonNullable<GetOrderLineFulfillmentsQuery['order']>['lines']>[number]
+                    >['fulfillments']
+                >[number]
+            >['summary']
+        >[number]
+    >;
+    export type OrderLine = NonNullable<
+        NonNullable<
+            NonNullable<
+                NonNullable<
+                    NonNullable<
+                        NonNullable<
+                            NonNullable<NonNullable<GetOrderLineFulfillmentsQuery['order']>['lines']>[number]
+                        >['fulfillments']
+                    >[number]
+                >['summary']
+            >[number]
+        >['orderLine']
+    >;
+}
+
 export namespace GetOrderListWithQty {
 export namespace GetOrderListWithQty {
     export type Variables = GetOrderListWithQtyQueryVariables;
     export type Variables = GetOrderListWithQtyQueryVariables;
     export type Query = GetOrderListWithQtyQuery;
     export type Query = GetOrderListWithQtyQuery;

+ 7 - 0
packages/core/e2e/graphql/generated-e2e-shop-types.ts

@@ -997,12 +997,18 @@ export type Fulfillment = Node & {
     createdAt: Scalars['DateTime'];
     createdAt: Scalars['DateTime'];
     updatedAt: Scalars['DateTime'];
     updatedAt: Scalars['DateTime'];
     orderItems: Array<OrderItem>;
     orderItems: Array<OrderItem>;
+    summary: Array<FulfillmentLineSummary>;
     state: Scalars['String'];
     state: Scalars['String'];
     method: Scalars['String'];
     method: Scalars['String'];
     trackingCode?: Maybe<Scalars['String']>;
     trackingCode?: Maybe<Scalars['String']>;
     customFields?: Maybe<Scalars['JSON']>;
     customFields?: Maybe<Scalars['JSON']>;
 };
 };
 
 
+export type FulfillmentLineSummary = {
+    orderLine: OrderLine;
+    quantity: Scalars['Int'];
+};
+
 export enum GlobalFlag {
 export enum GlobalFlag {
     TRUE = 'TRUE',
     TRUE = 'TRUE',
     FALSE = 'FALSE',
     FALSE = 'FALSE',
@@ -1962,6 +1968,7 @@ export type OrderLine = Node & {
     discounts: Array<Discount>;
     discounts: Array<Discount>;
     taxLines: Array<TaxLine>;
     taxLines: Array<TaxLine>;
     order: Order;
     order: Order;
+    fulfillments?: Maybe<Array<Fulfillment>>;
     customFields?: Maybe<Scalars['JSON']>;
     customFields?: Maybe<Scalars['JSON']>;
 };
 };
 
 

+ 6 - 0
packages/core/e2e/graphql/shared-definitions.ts

@@ -512,6 +512,12 @@ export const GET_ORDER_FULFILLMENTS = gql`
                 state
                 state
                 nextStates
                 nextStates
                 method
                 method
+                summary {
+                    orderLine {
+                        id
+                    }
+                    quantity
+                }
             }
             }
         }
         }
     }
     }

+ 57 - 1
packages/core/e2e/order.e2e-spec.ts

@@ -47,7 +47,11 @@ import {
     GetOrder,
     GetOrder,
     GetOrderFulfillmentItems,
     GetOrderFulfillmentItems,
     GetOrderFulfillments,
     GetOrderFulfillments,
+    GetOrderFulfillmentsQuery,
+    GetOrderFulfillmentsQueryVariables,
     GetOrderHistory,
     GetOrderHistory,
+    GetOrderLineFulfillmentsQuery,
+    GetOrderLineFulfillmentsQueryVariables,
     GetOrderList,
     GetOrderList,
     GetOrderListFulfillments,
     GetOrderListFulfillments,
     GetOrderListWithQty,
     GetOrderListWithQty,
@@ -756,6 +760,35 @@ describe('Orders resolver', () => {
             ]);
             ]);
         });
         });
 
 
+        it('order.fulfillments.summary', async () => {
+            const { order } = await adminClient.query<
+                GetOrderFulfillmentsQuery,
+                GetOrderFulfillmentsQueryVariables
+            >(GET_ORDER_FULFILLMENTS, {
+                id: orderId,
+            });
+
+            expect(order?.fulfillments?.map(pick(['id', 'state', 'summary']))).toEqual([
+                { id: f1Id, state: 'Pending', summary: [{ orderLine: { id: 'T_3' }, quantity: 1 }] },
+                { id: f2Id, state: 'Cancelled', summary: [{ orderLine: { id: 'T_4' }, quantity: 3 }] },
+            ]);
+        });
+
+        it('lines.fulfillments', async () => {
+            const { order } = await adminClient.query<
+                GetOrderLineFulfillmentsQuery,
+                GetOrderLineFulfillmentsQueryVariables
+            >(GET_ORDER_LINE_FULFILLMENTS, {
+                id: orderId,
+            });
+
+            expect(order?.lines.find(l => l.id === 'T_3')!.fulfillments).toEqual([
+                { id: f1Id, state: 'Pending', summary: [{ orderLine: { id: 'T_3' }, quantity: 1 }] },
+            ]);
+            // Cancelled Fulfillments do not appear in the line field
+            expect(order?.lines.find(l => l.id === 'T_4')!.fulfillments).toEqual([]);
+        });
+
         it('creates third fulfillment with same items from second fulfillment', async () => {
         it('creates third fulfillment with same items from second fulfillment', async () => {
             const lines = await getUnfulfilledOrderLineInput(adminClient, orderId);
             const lines = await getUnfulfilledOrderLineInput(adminClient, orderId);
             const { addFulfillmentToOrder } = await adminClient.query<
             const { addFulfillmentToOrder } = await adminClient.query<
@@ -1019,7 +1052,9 @@ describe('Orders resolver', () => {
                 id: orderId,
                 id: orderId,
             });
             });
 
 
-            expect(order!.fulfillments?.sort(sortById)).toEqual([
+            expect(
+                order!.fulfillments?.sort(sortById).map(pick(['id', 'method', 'state', 'nextStates'])),
+            ).toEqual([
                 { id: f1Id, method: 'Test1', state: 'Delivered', nextStates: ['Cancelled'] },
                 { id: f1Id, method: 'Test1', state: 'Delivered', nextStates: ['Cancelled'] },
                 { id: f2Id, method: 'Test2', state: 'Cancelled', nextStates: [] },
                 { id: f2Id, method: 'Test2', state: 'Cancelled', nextStates: [] },
                 { id: f3Id, method: 'Test3', state: 'Delivered', nextStates: ['Cancelled'] },
                 { id: f3Id, method: 'Test3', state: 'Delivered', nextStates: ['Cancelled'] },
@@ -2662,6 +2697,27 @@ const GET_ORDER_WITH_PAYMENTS = gql`
     }
     }
 `;
 `;
 
 
+export const GET_ORDER_LINE_FULFILLMENTS = gql`
+    query GetOrderLineFulfillments($id: ID!) {
+        order(id: $id) {
+            id
+            lines {
+                id
+                fulfillments {
+                    id
+                    state
+                    summary {
+                        orderLine {
+                            id
+                        }
+                        quantity
+                    }
+                }
+            }
+        }
+    }
+`;
+
 const GET_ORDERS_LIST_WITH_QUANTITIES = gql`
 const GET_ORDERS_LIST_WITH_QUANTITIES = gql`
     query GetOrderListWithQty($options: OrderListOptions) {
     query GetOrderListWithQty($options: OrderListOptions) {
         orders(options: $options) {
         orders(options: $options) {

+ 8 - 0
packages/core/src/api/resolvers/entity/fulfillment-entity.resolver.ts

@@ -1,4 +1,5 @@
 import { Parent, ResolveField, Resolver } from '@nestjs/graphql';
 import { Parent, ResolveField, Resolver } from '@nestjs/graphql';
+import { FulfillmentLineSummary } from '@vendure/payments-plugin/e2e/graphql/generated-admin-types';
 
 
 import { RequestContextCacheService } from '../../../cache/index';
 import { RequestContextCacheService } from '../../../cache/index';
 import { Fulfillment } from '../../../entity/fulfillment/fulfillment.entity';
 import { Fulfillment } from '../../../entity/fulfillment/fulfillment.entity';
@@ -22,6 +23,13 @@ export class FulfillmentEntityResolver {
             () => this.fulfillmentService.getOrderItemsByFulfillmentId(ctx, fulfillment.id),
             () => this.fulfillmentService.getOrderItemsByFulfillmentId(ctx, fulfillment.id),
         );
         );
     }
     }
+
+    @ResolveField()
+    async summary(@Ctx() ctx: RequestContext, @Parent() fulfillment: Fulfillment) {
+        return this.requestContextCache.get(ctx, `FulfillmentEntityResolver.summary(${fulfillment.id})`, () =>
+            this.fulfillmentService.getFulfillmentLineSummary(ctx, fulfillment.id),
+        );
+    }
 }
 }
 
 
 @Resolver('Fulfillment')
 @Resolver('Fulfillment')

+ 14 - 2
packages/core/src/api/resolvers/entity/order-line-entity.resolver.ts

@@ -1,7 +1,7 @@
 import { Parent, ResolveField, Resolver } from '@nestjs/graphql';
 import { Parent, ResolveField, Resolver } from '@nestjs/graphql';
 
 
-import { Asset, Order, OrderLine, ProductVariant } from '../../../entity';
-import { AssetService, OrderService, ProductVariantService } from '../../../service';
+import { Asset, Fulfillment, Order, OrderLine, ProductVariant } from '../../../entity';
+import { AssetService, FulfillmentService, OrderService, ProductVariantService } from '../../../service';
 import { RequestContext } from '../../common/request-context';
 import { RequestContext } from '../../common/request-context';
 import { RelationPaths, Relations } from '../../decorators/relations.decorator';
 import { RelationPaths, Relations } from '../../decorators/relations.decorator';
 import { Ctx } from '../../decorators/request-context.decorator';
 import { Ctx } from '../../decorators/request-context.decorator';
@@ -12,6 +12,7 @@ export class OrderLineEntityResolver {
         private productVariantService: ProductVariantService,
         private productVariantService: ProductVariantService,
         private assetService: AssetService,
         private assetService: AssetService,
         private orderService: OrderService,
         private orderService: OrderService,
+        private fulfillmentService: FulfillmentService,
     ) {}
     ) {}
 
 
     @ResolveField()
     @ResolveField()
@@ -45,4 +46,15 @@ export class OrderLineEntityResolver {
     ): Promise<Order | undefined> {
     ): Promise<Order | undefined> {
         return this.orderService.findOneByOrderLineId(ctx, orderLine.id, relations);
         return this.orderService.findOneByOrderLineId(ctx, orderLine.id, relations);
     }
     }
+
+    @ResolveField()
+    async fulfillments(
+        @Ctx() ctx: RequestContext,
+        @Parent() orderLine: OrderLine,
+        @Relations(Order) relations: RelationPaths<Order>,
+    ): Promise<Fulfillment[]> {
+        return this.fulfillmentService
+            .getFulfillmentsByOrderLineId(ctx, orderLine.id)
+            .then(results => results.map(r => r.fulfillment));
+    }
 }
 }

+ 7 - 0
packages/core/src/api/schema/common/order.type.graphql

@@ -210,6 +210,7 @@ type OrderLine implements Node {
     discounts: [Discount!]!
     discounts: [Discount!]!
     taxLines: [TaxLine!]!
     taxLines: [TaxLine!]!
     order: Order!
     order: Order!
+    fulfillments: [Fulfillment!]
 }
 }
 
 
 type Payment implements Node {
 type Payment implements Node {
@@ -242,11 +243,17 @@ type Refund implements Node {
     metadata: JSON
     metadata: JSON
 }
 }
 
 
+type FulfillmentLineSummary {
+    orderLine: OrderLine!
+    quantity: Int!
+}
+
 type Fulfillment implements Node {
 type Fulfillment implements Node {
     id: ID!
     id: ID!
     createdAt: DateTime!
     createdAt: DateTime!
     updatedAt: DateTime!
     updatedAt: DateTime!
     orderItems: [OrderItem!]!
     orderItems: [OrderItem!]!
+    summary: [FulfillmentLineSummary!]!
     state: String!
     state: String!
     method: String!
     method: String!
     trackingCode: String
     trackingCode: String

+ 27 - 0
packages/core/src/service/services/fulfillment.service.ts

@@ -3,6 +3,7 @@ import { ConfigurableOperationInput } from '@vendure/common/lib/generated-types'
 import { ID } from '@vendure/common/lib/shared-types';
 import { ID } from '@vendure/common/lib/shared-types';
 import { isObject } from '@vendure/common/lib/shared-utils';
 import { isObject } from '@vendure/common/lib/shared-utils';
 import { unique } from '@vendure/common/lib/unique';
 import { unique } from '@vendure/common/lib/unique';
+import { FulfillmentLineSummary } from '@vendure/payments-plugin/e2e/graphql/generated-admin-types';
 
 
 import { RequestContext } from '../../api/common/request-context';
 import { RequestContext } from '../../api/common/request-context';
 import {
 import {
@@ -13,6 +14,7 @@ import {
 import { ConfigService } from '../../config/config.service';
 import { ConfigService } from '../../config/config.service';
 import { TransactionalConnection } from '../../connection/transactional-connection';
 import { TransactionalConnection } from '../../connection/transactional-connection';
 import { Fulfillment } from '../../entity/fulfillment/fulfillment.entity';
 import { Fulfillment } from '../../entity/fulfillment/fulfillment.entity';
+import { OrderLine } from '../../entity/index';
 import { OrderItem } from '../../entity/order-item/order-item.entity';
 import { OrderItem } from '../../entity/order-item/order-item.entity';
 import { Order } from '../../entity/order/order.entity';
 import { Order } from '../../entity/order/order.entity';
 import { EventBus } from '../../event-bus/event-bus';
 import { EventBus } from '../../event-bus/event-bus';
@@ -116,6 +118,31 @@ export class FulfillmentService {
         return fulfillment.orderItems;
         return fulfillment.orderItems;
     }
     }
 
 
+    async getFulfillmentLineSummary(
+        ctx: RequestContext,
+        id: ID,
+    ): Promise<Array<{ orderLine: OrderLine; quantity: number }>> {
+        const result = await this.connection
+            .getRepository(ctx, OrderLine)
+            .createQueryBuilder('line')
+            .leftJoinAndSelect('line.items', 'item')
+            .leftJoin('item.fulfillments', 'fulfillment')
+            .select('line.id', 'lineId')
+            .addSelect('COUNT(item.id)', 'itemCount')
+            .groupBy('line.id')
+            .where('fulfillment.id = :id', { id })
+            .getRawMany();
+
+        return Promise.all(
+            result.map(async ({ lineId, itemCount }: { lineId: ID; itemCount: string }) => {
+                return {
+                    orderLine: await this.connection.getEntityOrThrow(ctx, OrderLine, lineId),
+                    quantity: +itemCount,
+                };
+            }),
+        );
+    }
+
     async getFulfillmentsByOrderLineId(
     async getFulfillmentsByOrderLineId(
         ctx: RequestContext,
         ctx: RequestContext,
         orderLineId: ID,
         orderLineId: ID,

+ 7 - 0
packages/elasticsearch-plugin/e2e/graphql/generated-e2e-elasticsearch-plugin-types.ts

@@ -1571,12 +1571,18 @@ export type Fulfillment = Node & {
     createdAt: Scalars['DateTime'];
     createdAt: Scalars['DateTime'];
     updatedAt: Scalars['DateTime'];
     updatedAt: Scalars['DateTime'];
     orderItems: Array<OrderItem>;
     orderItems: Array<OrderItem>;
+    summary: Array<FulfillmentLineSummary>;
     state: Scalars['String'];
     state: Scalars['String'];
     method: Scalars['String'];
     method: Scalars['String'];
     trackingCode?: Maybe<Scalars['String']>;
     trackingCode?: Maybe<Scalars['String']>;
     customFields?: Maybe<Scalars['JSON']>;
     customFields?: Maybe<Scalars['JSON']>;
 };
 };
 
 
+export type FulfillmentLineSummary = {
+    orderLine: OrderLine;
+    quantity: Scalars['Int'];
+};
+
 /** Returned when there is an error in transitioning the Fulfillment state */
 /** Returned when there is an error in transitioning the Fulfillment state */
 export type FulfillmentStateTransitionError = ErrorResult & {
 export type FulfillmentStateTransitionError = ErrorResult & {
     errorCode: ErrorCode;
     errorCode: ErrorCode;
@@ -3167,6 +3173,7 @@ export type OrderLine = Node & {
     discounts: Array<Discount>;
     discounts: Array<Discount>;
     taxLines: Array<TaxLine>;
     taxLines: Array<TaxLine>;
     order: Order;
     order: Order;
+    fulfillments?: Maybe<Array<Fulfillment>>;
     customFields?: Maybe<Scalars['JSON']>;
     customFields?: Maybe<Scalars['JSON']>;
 };
 };
 
 

+ 7 - 0
packages/payments-plugin/e2e/graphql/generated-admin-types.ts

@@ -1571,12 +1571,18 @@ export type Fulfillment = Node & {
     createdAt: Scalars['DateTime'];
     createdAt: Scalars['DateTime'];
     updatedAt: Scalars['DateTime'];
     updatedAt: Scalars['DateTime'];
     orderItems: Array<OrderItem>;
     orderItems: Array<OrderItem>;
+    summary: Array<FulfillmentLineSummary>;
     state: Scalars['String'];
     state: Scalars['String'];
     method: Scalars['String'];
     method: Scalars['String'];
     trackingCode?: Maybe<Scalars['String']>;
     trackingCode?: Maybe<Scalars['String']>;
     customFields?: Maybe<Scalars['JSON']>;
     customFields?: Maybe<Scalars['JSON']>;
 };
 };
 
 
+export type FulfillmentLineSummary = {
+    orderLine: OrderLine;
+    quantity: Scalars['Int'];
+};
+
 /** Returned when there is an error in transitioning the Fulfillment state */
 /** Returned when there is an error in transitioning the Fulfillment state */
 export type FulfillmentStateTransitionError = ErrorResult & {
 export type FulfillmentStateTransitionError = ErrorResult & {
     errorCode: ErrorCode;
     errorCode: ErrorCode;
@@ -3167,6 +3173,7 @@ export type OrderLine = Node & {
     discounts: Array<Discount>;
     discounts: Array<Discount>;
     taxLines: Array<TaxLine>;
     taxLines: Array<TaxLine>;
     order: Order;
     order: Order;
+    fulfillments?: Maybe<Array<Fulfillment>>;
     customFields?: Maybe<Scalars['JSON']>;
     customFields?: Maybe<Scalars['JSON']>;
 };
 };
 
 

+ 7 - 0
packages/payments-plugin/e2e/graphql/generated-shop-types.ts

@@ -997,12 +997,18 @@ export type Fulfillment = Node & {
     createdAt: Scalars['DateTime'];
     createdAt: Scalars['DateTime'];
     updatedAt: Scalars['DateTime'];
     updatedAt: Scalars['DateTime'];
     orderItems: Array<OrderItem>;
     orderItems: Array<OrderItem>;
+    summary: Array<FulfillmentLineSummary>;
     state: Scalars['String'];
     state: Scalars['String'];
     method: Scalars['String'];
     method: Scalars['String'];
     trackingCode?: Maybe<Scalars['String']>;
     trackingCode?: Maybe<Scalars['String']>;
     customFields?: Maybe<Scalars['JSON']>;
     customFields?: Maybe<Scalars['JSON']>;
 };
 };
 
 
+export type FulfillmentLineSummary = {
+    orderLine: OrderLine;
+    quantity: Scalars['Int'];
+};
+
 export enum GlobalFlag {
 export enum GlobalFlag {
     TRUE = 'TRUE',
     TRUE = 'TRUE',
     FALSE = 'FALSE',
     FALSE = 'FALSE',
@@ -1962,6 +1968,7 @@ export type OrderLine = Node & {
     discounts: Array<Discount>;
     discounts: Array<Discount>;
     taxLines: Array<TaxLine>;
     taxLines: Array<TaxLine>;
     order: Order;
     order: Order;
+    fulfillments?: Maybe<Array<Fulfillment>>;
     customFields?: Maybe<Scalars['JSON']>;
     customFields?: Maybe<Scalars['JSON']>;
 };
 };
 
 

+ 8 - 0
packages/payments-plugin/src/mollie/graphql/generated-shop-types.ts

@@ -1038,12 +1038,19 @@ export type Fulfillment = Node & {
     createdAt: Scalars['DateTime'];
     createdAt: Scalars['DateTime'];
     updatedAt: Scalars['DateTime'];
     updatedAt: Scalars['DateTime'];
     orderItems: Array<OrderItem>;
     orderItems: Array<OrderItem>;
+    summary: Array<FulfillmentLineSummary>;
     state: Scalars['String'];
     state: Scalars['String'];
     method: Scalars['String'];
     method: Scalars['String'];
     trackingCode?: Maybe<Scalars['String']>;
     trackingCode?: Maybe<Scalars['String']>;
     customFields?: Maybe<Scalars['JSON']>;
     customFields?: Maybe<Scalars['JSON']>;
 };
 };
 
 
+export type FulfillmentLineSummary = {
+    __typename?: 'FulfillmentLineSummary';
+    orderLine: OrderLine;
+    quantity: Scalars['Int'];
+};
+
 export enum GlobalFlag {
 export enum GlobalFlag {
     TRUE = 'TRUE',
     TRUE = 'TRUE',
     FALSE = 'FALSE',
     FALSE = 'FALSE',
@@ -2047,6 +2054,7 @@ export type OrderLine = Node & {
     discounts: Array<Discount>;
     discounts: Array<Discount>;
     taxLines: Array<TaxLine>;
     taxLines: Array<TaxLine>;
     order: Order;
     order: Order;
+    fulfillments?: Maybe<Array<Fulfillment>>;
     customFields?: Maybe<Scalars['JSON']>;
     customFields?: Maybe<Scalars['JSON']>;
 };
 };
 
 

Fichier diff supprimé car celui-ci est trop grand
+ 0 - 0
schema-admin.json


Fichier diff supprimé car celui-ci est trop grand
+ 0 - 0
schema-shop.json


Certains fichiers n'ont pas été affichés car il y a eu trop de fichiers modifiés dans ce diff