Skip to content

Commit

Permalink
feat(core): Implement constraints on adding & fulfilling OrderItems
Browse files Browse the repository at this point in the history
Relates to #319. This commit introduces the concept of `outOfStockThreshold`,
on both the global and ProductVariant level. This determines whether back-orders are allowed.
  • Loading branch information
michaelbromley committed Oct 21, 2020
1 parent d2333fc commit 87d07f8
Show file tree
Hide file tree
Showing 29 changed files with 1,051 additions and 219 deletions.
41 changes: 37 additions & 4 deletions packages/admin-ui/src/lib/core/src/common/generated-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1255,6 +1255,7 @@ export type Fulfillment = Node & {
export type UpdateGlobalSettingsInput = {
availableLanguages?: Maybe<Array<LanguageCode>>;
trackInventory?: Maybe<Scalars['Boolean']>;
outOfStockThreshold?: Maybe<Scalars['Int']>;
customFields?: Maybe<Scalars['JSON']>;
};

Expand Down Expand Up @@ -1426,6 +1427,19 @@ export type ItemsAlreadyFulfilledError = ErrorResult & {
message: Scalars['String'];
};

/**
* Returned if attempting to create a Fulfillment when there is insufficient
* stockOnHand of a ProductVariant to satisfy the requested quantity.
*/
export type InsufficientStockOnHandError = ErrorResult & {
__typename?: 'InsufficientStockOnHandError';
errorCode: ErrorCode;
message: Scalars['String'];
productVariantId: Scalars['ID'];
productVariantName: Scalars['String'];
stockOnHand: Scalars['Int'];
};

/** Returned if an operation has specified OrderLines from multiple Orders */
export type MultipleOrderError = ErrorResult & {
__typename?: 'MultipleOrderError';
Expand Down Expand Up @@ -1512,7 +1526,7 @@ export type TransitionOrderToStateResult = Order | OrderStateTransitionError;

export type SettlePaymentResult = Payment | SettlePaymentError | PaymentStateTransitionError | OrderStateTransitionError;

export type AddFulfillmentToOrderResult = Fulfillment | EmptyOrderLineSelectionError | ItemsAlreadyFulfilledError;
export type AddFulfillmentToOrderResult = Fulfillment | EmptyOrderLineSelectionError | ItemsAlreadyFulfilledError | InsufficientStockOnHandError;

export type CancelOrderResult = Order | EmptyOrderLineSelectionError | QuantityTooGreatError | MultipleOrderError | CancelActiveOrderError | OrderStateTransitionError;

Expand Down Expand Up @@ -1635,9 +1649,11 @@ export type Product = Node & {
export type ProductVariant = Node & {
__typename?: 'ProductVariant';
enabled: Scalars['Boolean'];
trackInventory: GlobalFlag;
stockOnHand: Scalars['Int'];
stockAllocated: Scalars['Int'];
trackInventory: GlobalFlag;
outOfStockThreshold: Scalars['Int'];
useGlobalOutOfStockThreshold: Scalars['Boolean'];
stockMovements: StockMovementList;
id: Scalars['ID'];
product: Product;
Expand Down Expand Up @@ -1723,6 +1739,8 @@ export type CreateProductVariantInput = {
featuredAssetId?: Maybe<Scalars['ID']>;
assetIds?: Maybe<Array<Scalars['ID']>>;
stockOnHand?: Maybe<Scalars['Int']>;
outOfStockThreshold?: Maybe<Scalars['Int']>;
useGlobalOutOfStockThreshold?: Maybe<Scalars['Boolean']>;
trackInventory?: Maybe<GlobalFlag>;
customFields?: Maybe<Scalars['JSON']>;
};
Expand All @@ -1738,6 +1756,8 @@ export type UpdateProductVariantInput = {
featuredAssetId?: Maybe<Scalars['ID']>;
assetIds?: Maybe<Array<Scalars['ID']>>;
stockOnHand?: Maybe<Scalars['Int']>;
outOfStockThreshold?: Maybe<Scalars['Int']>;
useGlobalOutOfStockThreshold?: Maybe<Scalars['Boolean']>;
trackInventory?: Maybe<GlobalFlag>;
customFields?: Maybe<Scalars['JSON']>;
};
Expand Down Expand Up @@ -1999,6 +2019,7 @@ export enum ErrorCode {
SETTLE_PAYMENT_ERROR = 'SETTLE_PAYMENT_ERROR',
EMPTY_ORDER_LINE_SELECTION_ERROR = 'EMPTY_ORDER_LINE_SELECTION_ERROR',
ITEMS_ALREADY_FULFILLED_ERROR = 'ITEMS_ALREADY_FULFILLED_ERROR',
INSUFFICIENT_STOCK_ON_HAND_ERROR = 'INSUFFICIENT_STOCK_ON_HAND_ERROR',
MULTIPLE_ORDER_ERROR = 'MULTIPLE_ORDER_ERROR',
CANCEL_ACTIVE_ORDER_ERROR = 'CANCEL_ACTIVE_ORDER_ERROR',
PAYMENT_ORDER_MISMATCH_ERROR = 'PAYMENT_ORDER_MISMATCH_ERROR',
Expand Down Expand Up @@ -3177,6 +3198,7 @@ export type GlobalSettings = {
updatedAt: Scalars['DateTime'];
availableLanguages: Array<LanguageCode>;
trackInventory: Scalars['Boolean'];
outOfStockThreshold: Scalars['Int'];
serverConfig: ServerConfig;
customFields?: Maybe<Scalars['JSON']>;
};
Expand Down Expand Up @@ -4074,9 +4096,11 @@ export type TaxRateSortParameter = {

export type ProductVariantFilterParameter = {
enabled?: Maybe<BooleanOperators>;
trackInventory?: Maybe<StringOperators>;
stockOnHand?: Maybe<NumberOperators>;
stockAllocated?: Maybe<NumberOperators>;
trackInventory?: Maybe<StringOperators>;
outOfStockThreshold?: Maybe<NumberOperators>;
useGlobalOutOfStockThreshold?: Maybe<BooleanOperators>;
createdAt?: Maybe<DateOperators>;
updatedAt?: Maybe<DateOperators>;
languageCode?: Maybe<StringOperators>;
Expand All @@ -4091,6 +4115,7 @@ export type ProductVariantFilterParameter = {
export type ProductVariantSortParameter = {
stockOnHand?: Maybe<SortOrder>;
stockAllocated?: Maybe<SortOrder>;
outOfStockThreshold?: Maybe<SortOrder>;
id?: Maybe<SortOrder>;
productId?: Maybe<SortOrder>;
createdAt?: Maybe<SortOrder>;
Expand Down Expand Up @@ -5102,6 +5127,9 @@ export type CreateFulfillmentMutation = { addFulfillmentToOrder: (
) | (
{ __typename?: 'ItemsAlreadyFulfilledError' }
& ErrorResult_ItemsAlreadyFulfilledError_Fragment
) | (
{ __typename?: 'InsufficientStockOnHandError' }
& ErrorResult_InsufficientStockOnHandError_Fragment
) };

export type CancelOrderMutationVariables = Exact<{
Expand Down Expand Up @@ -6786,6 +6814,11 @@ type ErrorResult_ItemsAlreadyFulfilledError_Fragment = (
& Pick<ItemsAlreadyFulfilledError, 'errorCode' | 'message'>
);

type ErrorResult_InsufficientStockOnHandError_Fragment = (
{ __typename?: 'InsufficientStockOnHandError' }
& Pick<InsufficientStockOnHandError, 'errorCode' | 'message'>
);

type ErrorResult_MultipleOrderError_Fragment = (
{ __typename?: 'MultipleOrderError' }
& Pick<MultipleOrderError, 'errorCode' | 'message'>
Expand Down Expand Up @@ -6866,7 +6899,7 @@ type ErrorResult_EmailAddressConflictError_Fragment = (
& Pick<EmailAddressConflictError, 'errorCode' | 'message'>
);

export type ErrorResultFragment = ErrorResult_MimeTypeError_Fragment | ErrorResult_LanguageNotAvailableError_Fragment | ErrorResult_ChannelDefaultLanguageError_Fragment | ErrorResult_SettlePaymentError_Fragment | ErrorResult_EmptyOrderLineSelectionError_Fragment | ErrorResult_ItemsAlreadyFulfilledError_Fragment | ErrorResult_MultipleOrderError_Fragment | ErrorResult_CancelActiveOrderError_Fragment | ErrorResult_PaymentOrderMismatchError_Fragment | ErrorResult_RefundOrderStateError_Fragment | ErrorResult_NothingToRefundError_Fragment | ErrorResult_AlreadyRefundedError_Fragment | ErrorResult_QuantityTooGreatError_Fragment | ErrorResult_RefundStateTransitionError_Fragment | ErrorResult_PaymentStateTransitionError_Fragment | ErrorResult_FulfillmentStateTransitionError_Fragment | ErrorResult_ProductOptionInUseError_Fragment | ErrorResult_MissingConditionsError_Fragment | ErrorResult_NativeAuthStrategyError_Fragment | ErrorResult_InvalidCredentialsError_Fragment | ErrorResult_OrderStateTransitionError_Fragment | ErrorResult_EmailAddressConflictError_Fragment;
export type ErrorResultFragment = ErrorResult_MimeTypeError_Fragment | ErrorResult_LanguageNotAvailableError_Fragment | ErrorResult_ChannelDefaultLanguageError_Fragment | ErrorResult_SettlePaymentError_Fragment | ErrorResult_EmptyOrderLineSelectionError_Fragment | ErrorResult_ItemsAlreadyFulfilledError_Fragment | ErrorResult_InsufficientStockOnHandError_Fragment | ErrorResult_MultipleOrderError_Fragment | ErrorResult_CancelActiveOrderError_Fragment | ErrorResult_PaymentOrderMismatchError_Fragment | ErrorResult_RefundOrderStateError_Fragment | ErrorResult_NothingToRefundError_Fragment | ErrorResult_AlreadyRefundedError_Fragment | ErrorResult_QuantityTooGreatError_Fragment | ErrorResult_RefundStateTransitionError_Fragment | ErrorResult_PaymentStateTransitionError_Fragment | ErrorResult_FulfillmentStateTransitionError_Fragment | ErrorResult_ProductOptionInUseError_Fragment | ErrorResult_MissingConditionsError_Fragment | ErrorResult_NativeAuthStrategyError_Fragment | ErrorResult_InvalidCredentialsError_Fragment | ErrorResult_OrderStateTransitionError_Fragment | ErrorResult_EmailAddressConflictError_Fragment;

export type ShippingMethodFragment = (
{ __typename?: 'ShippingMethod' }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ const result: PossibleTypesResultData = {
'Fulfillment',
'EmptyOrderLineSelectionError',
'ItemsAlreadyFulfilledError',
'InsufficientStockOnHandError',
],
CancelOrderResult: [
'Order',
Expand Down Expand Up @@ -116,6 +117,7 @@ const result: PossibleTypesResultData = {
'SettlePaymentError',
'EmptyOrderLineSelectionError',
'ItemsAlreadyFulfilledError',
'InsufficientStockOnHandError',
'MultipleOrderError',
'CancelActiveOrderError',
'PaymentOrderMismatchError',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1083,6 +1083,7 @@ export type Fulfillment = Node & {
export type UpdateGlobalSettingsInput = {
availableLanguages?: Maybe<Array<LanguageCode>>;
trackInventory?: Maybe<Scalars['Boolean']>;
outOfStockThreshold?: Maybe<Scalars['Int']>;
customFields?: Maybe<Scalars['JSON']>;
};

Expand Down Expand Up @@ -1245,6 +1246,18 @@ export type ItemsAlreadyFulfilledError = ErrorResult & {
message: Scalars['String'];
};

/**
* Returned if attempting to create a Fulfillment when there is insufficient
* stockOnHand of a ProductVariant to satisfy the requested quantity.
*/
export type InsufficientStockOnHandError = ErrorResult & {
errorCode: ErrorCode;
message: Scalars['String'];
productVariantId: Scalars['ID'];
productVariantName: Scalars['String'];
stockOnHand: Scalars['Int'];
};

/** Returned if an operation has specified OrderLines from multiple Orders */
export type MultipleOrderError = ErrorResult & {
errorCode: ErrorCode;
Expand Down Expand Up @@ -1328,7 +1341,8 @@ export type SettlePaymentResult =
export type AddFulfillmentToOrderResult =
| Fulfillment
| EmptyOrderLineSelectionError
| ItemsAlreadyFulfilledError;
| ItemsAlreadyFulfilledError
| InsufficientStockOnHandError;

export type CancelOrderResult =
| Order
Expand Down Expand Up @@ -1462,9 +1476,11 @@ export type Product = Node & {

export type ProductVariant = Node & {
enabled: Scalars['Boolean'];
trackInventory: GlobalFlag;
stockOnHand: Scalars['Int'];
stockAllocated: Scalars['Int'];
trackInventory: GlobalFlag;
outOfStockThreshold: Scalars['Int'];
useGlobalOutOfStockThreshold: Scalars['Boolean'];
stockMovements: StockMovementList;
id: Scalars['ID'];
product: Product;
Expand Down Expand Up @@ -1549,6 +1565,8 @@ export type CreateProductVariantInput = {
featuredAssetId?: Maybe<Scalars['ID']>;
assetIds?: Maybe<Array<Scalars['ID']>>;
stockOnHand?: Maybe<Scalars['Int']>;
outOfStockThreshold?: Maybe<Scalars['Int']>;
useGlobalOutOfStockThreshold?: Maybe<Scalars['Boolean']>;
trackInventory?: Maybe<GlobalFlag>;
customFields?: Maybe<Scalars['JSON']>;
};
Expand All @@ -1564,6 +1582,8 @@ export type UpdateProductVariantInput = {
featuredAssetId?: Maybe<Scalars['ID']>;
assetIds?: Maybe<Array<Scalars['ID']>>;
stockOnHand?: Maybe<Scalars['Int']>;
outOfStockThreshold?: Maybe<Scalars['Int']>;
useGlobalOutOfStockThreshold?: Maybe<Scalars['Boolean']>;
trackInventory?: Maybe<GlobalFlag>;
customFields?: Maybe<Scalars['JSON']>;
};
Expand Down Expand Up @@ -1812,6 +1832,7 @@ export enum ErrorCode {
SETTLE_PAYMENT_ERROR = 'SETTLE_PAYMENT_ERROR',
EMPTY_ORDER_LINE_SELECTION_ERROR = 'EMPTY_ORDER_LINE_SELECTION_ERROR',
ITEMS_ALREADY_FULFILLED_ERROR = 'ITEMS_ALREADY_FULFILLED_ERROR',
INSUFFICIENT_STOCK_ON_HAND_ERROR = 'INSUFFICIENT_STOCK_ON_HAND_ERROR',
MULTIPLE_ORDER_ERROR = 'MULTIPLE_ORDER_ERROR',
CANCEL_ACTIVE_ORDER_ERROR = 'CANCEL_ACTIVE_ORDER_ERROR',
PAYMENT_ORDER_MISMATCH_ERROR = 'PAYMENT_ORDER_MISMATCH_ERROR',
Expand Down Expand Up @@ -2959,6 +2980,7 @@ export type GlobalSettings = {
updatedAt: Scalars['DateTime'];
availableLanguages: Array<LanguageCode>;
trackInventory: Scalars['Boolean'];
outOfStockThreshold: Scalars['Int'];
serverConfig: ServerConfig;
customFields?: Maybe<Scalars['JSON']>;
};
Expand Down Expand Up @@ -3817,9 +3839,11 @@ export type TaxRateSortParameter = {

export type ProductVariantFilterParameter = {
enabled?: Maybe<BooleanOperators>;
trackInventory?: Maybe<StringOperators>;
stockOnHand?: Maybe<NumberOperators>;
stockAllocated?: Maybe<NumberOperators>;
trackInventory?: Maybe<StringOperators>;
outOfStockThreshold?: Maybe<NumberOperators>;
useGlobalOutOfStockThreshold?: Maybe<BooleanOperators>;
createdAt?: Maybe<DateOperators>;
updatedAt?: Maybe<DateOperators>;
languageCode?: Maybe<StringOperators>;
Expand All @@ -3834,6 +3858,7 @@ export type ProductVariantFilterParameter = {
export type ProductVariantSortParameter = {
stockOnHand?: Maybe<SortOrder>;
stockAllocated?: Maybe<SortOrder>;
outOfStockThreshold?: Maybe<SortOrder>;
id?: Maybe<SortOrder>;
productId?: Maybe<SortOrder>;
createdAt?: Maybe<SortOrder>;
Expand Down
18 changes: 17 additions & 1 deletion packages/common/src/generated-shop-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,7 @@ export enum ErrorCode {
ORDER_MODIFICATION_ERROR = 'ORDER_MODIFICATION_ERROR',
ORDER_LIMIT_ERROR = 'ORDER_LIMIT_ERROR',
NEGATIVE_QUANTITY_ERROR = 'NEGATIVE_QUANTITY_ERROR',
INSUFFICIENT_STOCK_ERROR = 'INSUFFICIENT_STOCK_ERROR',
ORDER_PAYMENT_STATE_ERROR = 'ORDER_PAYMENT_STATE_ERROR',
PAYMENT_FAILED_ERROR = 'PAYMENT_FAILED_ERROR',
PAYMENT_DECLINED_ERROR = 'PAYMENT_DECLINED_ERROR',
Expand Down Expand Up @@ -1405,6 +1406,15 @@ export type NegativeQuantityError = ErrorResult & {
message: Scalars['String'];
};

/** Returned when attempting to add more items to the Order than are available */
export type InsufficientStockError = ErrorResult & {
__typename?: 'InsufficientStockError';
errorCode: ErrorCode;
message: Scalars['String'];
quantityAvailable: Scalars['Int'];
order: Order;
};

/** Returned when attempting to add a Payment to an Order that is not in the `ArrangingPayment` state. */
export type OrderPaymentStateError = ErrorResult & {
__typename?: 'OrderPaymentStateError';
Expand Down Expand Up @@ -1544,7 +1554,12 @@ export type NotVerifiedError = ErrorResult & {
message: Scalars['String'];
};

export type UpdateOrderItemsResult = Order | OrderModificationError | OrderLimitError | NegativeQuantityError;
export type UpdateOrderItemsResult =
| Order
| OrderModificationError
| OrderLimitError
| NegativeQuantityError
| InsufficientStockError;

export type RemoveOrderItemsResult = Order | OrderModificationError;

Expand Down Expand Up @@ -1886,6 +1901,7 @@ export type GlobalSettings = {
updatedAt: Scalars['DateTime'];
availableLanguages: Array<LanguageCode>;
trackInventory: Scalars['Boolean'];
outOfStockThreshold: Scalars['Int'];
serverConfig: ServerConfig;
customFields?: Maybe<Scalars['JSON']>;
};
Expand Down
Loading

0 comments on commit 87d07f8

Please sign in to comment.