Fulfillment creation refactor (#1896)
* Create change warehouse dialog (#1850) * Add allocations & variant stocks to order details query * Add asc sorting to warehouse search query * Add OrderChangeWarehouseDialog component * Add key to warehouse list in dialog * Update snapshots * Remove debug directive * Remove IDs from messages * Fix typo in method name & extract messages * Add quantity to allocations in order details query * Add types to functions * Move functions to local utils file * Add utility type WithOptional * Fix warehouse types * Change multiple items unavailable message name * Fix fetching onScroll * Fix types in utils * Add backdrop click support * Add id to stocks and allocations * Change unavailableLines from .map to .filter Co-authored-by: Wojciech Mista <wojciech.mista@hotmail.com> * Fix linter issue Co-authored-by: Wojciech Mista <wojciech.mista@hotmail.com> * Refactor order cards headers (#1875) * Add keys to TableLines * Bump macaw * Move & rename CardTitle used in Cards with order lines * Improve OrderCardTitle typography * Replace StatusLabels with CircleIndicators * Fix card title divs placement * Add warehouse selection button to OrderUnfulfilledCard * Fix fulfill button placement * Update snapshots * Make warehouse in order details view optional so that it works with uncofirmed orders * Fix undefined lines in warehouse dialog * Fix spacing in warehouse change button * Fix macaw dependency * Bump macaw-ui * Extract messages * Implement default warehouse selection logic * Move CircleIndicator render condition to wrapper * Fix failing reduce on orders with no lines * Improve warehousesAvailable early return * Drop counter in favor of filter().length * Fix tests post-rebase * Refactor fulfillment details page (#1915) * Add shipment information card * Refactor multiple warehouse fulfilling to one warehouse * Fix fulfill button navigation * Remove redundant code from OrderFulfill view * Fix OrderFulfill story * Move styling to seperate file & remove unused classes * Replace colQuantityTotal class with colStock * Add warehouse label under page header * Fix preorder cases * Change default values to maximum * Simplify logic * Add badge for preorder & deleted variant cases * Remove unused data * Add yellow outline for exceeding stock * Fix failing tests * Extract messages * Fix tests post-rebase * Add support for tracking number * Block fulfilling no items * Fix deleted variant order details bug * Fix preorder & deleted variant cases * Update snapshots * Remove redundant import * Fix linter issue * Extract fulfillment lines as separate component * Fix types * Export styles & messages to seperate files * Simplify formset changes * Fix warning input styling * Fix shouldEnableSave for overfulfillment cases * Simplify preorder rendering * Move empty line rendering * Change Warehouse prop to just id of it * Add endAdornment for deleted variant cases * Update snapshots * Fix linter issue * Extract messages * Fix incorrect operator precedence resulting in NaN values * Extract fulfillment lines to fragment * Replace nested types with fragment type * Match fragment names * Update snapshots * Create exceeding stock confirmation dialog (#1970) * Cherry-pick OrderFulfillStockExceededDialog * Fix types to graphql-codegen * Unify names in OrderFulfillStockExceededDialogLines * Fix types in OrderFulfillStockExceededDialogLines * Fix types in util * Change utils for usage with single warehouse context * Refactor OrderFulfillStockExceededDialogLine for usage with single warehouse context * Fix deleted variant cases in OrderFulfillStockExceededDialogLine * Include only exceeded lines * Display stock exceeded dialog on error * Add confirm button state * Change fixed height in OrderFulfillStockExceededDialog to responsive * Extract messages * Move initial form data after interfaces * Change nested type to fragment * Reuse logic * Remove unused import * Remove redundant isStockError * Remove unused imports * Fix minor bugs in fulfillment creation refactor (#1972) * Fix unfulfilled card header quantity calculation * Fix formset default value for deleted variants * Update snapshots * Fix default warehouse selection in order draft (#1971) * Fix default warehouse selection * Replace Skeleton with circular progress * Reuse logic * Reuse logic * Apply CR fixes * Remove unused imports * Fix canceled order header status * Update snapshots * Revert CircularProgress & change to Skeleton * Change complex types to fragments * Extract default warehouse logic to hook * Fix linter issue * Remove type assertion from OrderFulfillPage story * Handle exceeding stock fulfillment approvals (#1988) * wip Add OrderFulfillStockExceeded modal for fulfillment approvals * wip Fix types & imports * wip Fix union typing in stock exceeded dialog for both views * Add allowStockToBeExceeded flag on submit * Fix lines keys in FulfilledCard * Remove redundant import * Extract attributes caption function * Use getById util * Fix deleted warehouse cases in approvals * Fix typo * Fix styling for long warehouse names (#2019) * Fix styling in change warehouse dialog * Fix styling in warehouse selection button * Add extra margin in button * Update snapshots Co-authored-by: Wojciech Mista <wojciech.mista@hotmail.com>
This commit is contained in:
parent
e0cc89478f
commit
6cf148c4c3
45 changed files with 5536 additions and 2501 deletions
|
@ -4313,6 +4313,70 @@
|
||||||
"src_dot_orders_dot_components_dot_OrderCannotCancelOrderDialog_dot_775268031": {
|
"src_dot_orders_dot_components_dot_OrderCannotCancelOrderDialog_dot_775268031": {
|
||||||
"string": "There are still fulfillments created for this order. Cancel the fulfillments first before you cancel the order."
|
"string": "There are still fulfillments created for this order. Cancel the fulfillments first before you cancel the order."
|
||||||
},
|
},
|
||||||
|
"src_dot_orders_dot_components_dot_OrderCardTitle_dot_canceled": {
|
||||||
|
"context": "canceled fulfillment, section header",
|
||||||
|
"string": "Canceled ({quantity})"
|
||||||
|
},
|
||||||
|
"src_dot_orders_dot_components_dot_OrderCardTitle_dot_fulfilled": {
|
||||||
|
"context": "section header",
|
||||||
|
"string": "Fulfilled ({quantity})"
|
||||||
|
},
|
||||||
|
"src_dot_orders_dot_components_dot_OrderCardTitle_dot_fulfilledFrom": {
|
||||||
|
"context": "fulfilled fulfillment, section header",
|
||||||
|
"string": "Fulfilled from {warehouseName}"
|
||||||
|
},
|
||||||
|
"src_dot_orders_dot_components_dot_OrderCardTitle_dot_refunded": {
|
||||||
|
"context": "refunded fulfillment, section header",
|
||||||
|
"string": "Refunded ({quantity})"
|
||||||
|
},
|
||||||
|
"src_dot_orders_dot_components_dot_OrderCardTitle_dot_refundedAndReturned": {
|
||||||
|
"context": "cancelled fulfillment, section header",
|
||||||
|
"string": "Refunded and Returned ({quantity})"
|
||||||
|
},
|
||||||
|
"src_dot_orders_dot_components_dot_OrderCardTitle_dot_replaced": {
|
||||||
|
"context": "refunded fulfillment, section header",
|
||||||
|
"string": "Replaced ({quantity})"
|
||||||
|
},
|
||||||
|
"src_dot_orders_dot_components_dot_OrderCardTitle_dot_returned": {
|
||||||
|
"context": "refunded fulfillment, section header",
|
||||||
|
"string": "Returned ({quantity})"
|
||||||
|
},
|
||||||
|
"src_dot_orders_dot_components_dot_OrderCardTitle_dot_unfulfilled": {
|
||||||
|
"context": "section header",
|
||||||
|
"string": "Unfulfilled ({quantity})"
|
||||||
|
},
|
||||||
|
"src_dot_orders_dot_components_dot_OrderCardTitle_dot_waitingForApproval": {
|
||||||
|
"context": "unapproved fulfillment, section header",
|
||||||
|
"string": "Waiting for approval ({quantity})"
|
||||||
|
},
|
||||||
|
"src_dot_orders_dot_components_dot_OrderChangeWarehouseDialog_dot_currentSelection": {
|
||||||
|
"context": "label for currently selected warehouse",
|
||||||
|
"string": "currently selected"
|
||||||
|
},
|
||||||
|
"src_dot_orders_dot_components_dot_OrderChangeWarehouseDialog_dot_dialogDescription": {
|
||||||
|
"context": "change warehouse dialog description",
|
||||||
|
"string": "Choose warehouse you want to fulfill this order from"
|
||||||
|
},
|
||||||
|
"src_dot_orders_dot_components_dot_OrderChangeWarehouseDialog_dot_dialogTitle": {
|
||||||
|
"context": "change warehouse dialog title",
|
||||||
|
"string": "Change warehouse"
|
||||||
|
},
|
||||||
|
"src_dot_orders_dot_components_dot_OrderChangeWarehouseDialog_dot_multipleProductsUnavailable": {
|
||||||
|
"context": "warehouse label when multiple products are unavailable",
|
||||||
|
"string": "{productCount} products are unavailable at this location"
|
||||||
|
},
|
||||||
|
"src_dot_orders_dot_components_dot_OrderChangeWarehouseDialog_dot_productUnavailable": {
|
||||||
|
"context": "warehouse label when one product is unavailable",
|
||||||
|
"string": "{productName} is unavailable at this location"
|
||||||
|
},
|
||||||
|
"src_dot_orders_dot_components_dot_OrderChangeWarehouseDialog_dot_searchFieldPlaceholder": {
|
||||||
|
"context": "change warehouse dialog search placeholder",
|
||||||
|
"string": "Search warehouses"
|
||||||
|
},
|
||||||
|
"src_dot_orders_dot_components_dot_OrderChangeWarehouseDialog_dot_warehouseListLabel": {
|
||||||
|
"context": "change warehouse dialog warehouse list label",
|
||||||
|
"string": "Warehouses A to Z"
|
||||||
|
},
|
||||||
"src_dot_orders_dot_components_dot_OrderChannelSectionCard_dot_1243773440": {
|
"src_dot_orders_dot_components_dot_OrderChannelSectionCard_dot_1243773440": {
|
||||||
"context": "section header",
|
"context": "section header",
|
||||||
"string": "Sales channel"
|
"string": "Sales channel"
|
||||||
|
@ -4600,6 +4664,18 @@
|
||||||
"context": "button",
|
"context": "button",
|
||||||
"string": "Finalize"
|
"string": "Finalize"
|
||||||
},
|
},
|
||||||
|
"src_dot_orders_dot_components_dot_OrderFulfillLine_dot_deletedVariantWarning": {
|
||||||
|
"context": "tooltip content when line's variant has been deleted",
|
||||||
|
"string": "This variant no longer exists. You can still fulfill it."
|
||||||
|
},
|
||||||
|
"src_dot_orders_dot_components_dot_OrderFulfillLine_dot_preorderWarning": {
|
||||||
|
"context": "tooltip content when product is in preorder",
|
||||||
|
"string": "This product is still in preorder. You will be able to fulfill it after it reaches it’s release date"
|
||||||
|
},
|
||||||
|
"src_dot_orders_dot_components_dot_OrderFulfillPage_dot_fulfillingFrom": {
|
||||||
|
"context": "Support text under page header",
|
||||||
|
"string": "Fulfilling from {warehouseName}"
|
||||||
|
},
|
||||||
"src_dot_orders_dot_components_dot_OrderFulfillPage_dot_headerOrder": {
|
"src_dot_orders_dot_components_dot_OrderFulfillPage_dot_headerOrder": {
|
||||||
"context": "page header",
|
"context": "page header",
|
||||||
"string": "Order"
|
"string": "Order"
|
||||||
|
@ -4624,6 +4700,10 @@
|
||||||
"context": "name",
|
"context": "name",
|
||||||
"string": "Product name"
|
"string": "Product name"
|
||||||
},
|
},
|
||||||
|
"src_dot_orders_dot_components_dot_OrderFulfillPage_dot_quantity": {
|
||||||
|
"context": "Header row quantity label",
|
||||||
|
"string": "Quantity"
|
||||||
|
},
|
||||||
"src_dot_orders_dot_components_dot_OrderFulfillPage_dot_quantityToFulfill": {
|
"src_dot_orders_dot_components_dot_OrderFulfillPage_dot_quantityToFulfill": {
|
||||||
"context": "quantity of fulfilled products",
|
"context": "quantity of fulfilled products",
|
||||||
"string": "Quantity to fulfill"
|
"string": "Quantity to fulfill"
|
||||||
|
@ -4632,10 +4712,18 @@
|
||||||
"context": "checkbox label",
|
"context": "checkbox label",
|
||||||
"string": "Send shipment details to customer"
|
"string": "Send shipment details to customer"
|
||||||
},
|
},
|
||||||
|
"src_dot_orders_dot_components_dot_OrderFulfillPage_dot_shipmentInformation": {
|
||||||
|
"context": "Shipment information card header",
|
||||||
|
"string": "Shipment information"
|
||||||
|
},
|
||||||
"src_dot_orders_dot_components_dot_OrderFulfillPage_dot_sku": {
|
"src_dot_orders_dot_components_dot_OrderFulfillPage_dot_sku": {
|
||||||
"context": "product's sku",
|
"context": "product's sku",
|
||||||
"string": "SKU"
|
"string": "SKU"
|
||||||
},
|
},
|
||||||
|
"src_dot_orders_dot_components_dot_OrderFulfillPage_dot_stock": {
|
||||||
|
"context": "Header row stock label",
|
||||||
|
"string": "Stock"
|
||||||
|
},
|
||||||
"src_dot_orders_dot_components_dot_OrderFulfillPage_dot_submitFulfillment": {
|
"src_dot_orders_dot_components_dot_OrderFulfillPage_dot_submitFulfillment": {
|
||||||
"context": "fulfill order, button",
|
"context": "fulfill order, button",
|
||||||
"string": "Fulfill"
|
"string": "Fulfill"
|
||||||
|
@ -4644,6 +4732,46 @@
|
||||||
"context": "prepare order fulfillment, button",
|
"context": "prepare order fulfillment, button",
|
||||||
"string": "Prepare fulfillment"
|
"string": "Prepare fulfillment"
|
||||||
},
|
},
|
||||||
|
"src_dot_orders_dot_components_dot_OrderFulfillPage_dot_trackingNumber": {
|
||||||
|
"context": "Tracking number input label",
|
||||||
|
"string": "Tracking number"
|
||||||
|
},
|
||||||
|
"src_dot_orders_dot_components_dot_OrderFulfillStockExceededDialog_dot_availableStockLabel": {
|
||||||
|
"context": "table header available stock label",
|
||||||
|
"string": "Available"
|
||||||
|
},
|
||||||
|
"src_dot_orders_dot_components_dot_OrderFulfillStockExceededDialog_dot_cancelButton": {
|
||||||
|
"context": "cancel button label",
|
||||||
|
"string": "Cancel"
|
||||||
|
},
|
||||||
|
"src_dot_orders_dot_components_dot_OrderFulfillStockExceededDialog_dot_fulfillButton": {
|
||||||
|
"context": "fulfill button label",
|
||||||
|
"string": "Fulfill anyway"
|
||||||
|
},
|
||||||
|
"src_dot_orders_dot_components_dot_OrderFulfillStockExceededDialog_dot_infoLabel": {
|
||||||
|
"context": "stock exceeded dialog description",
|
||||||
|
"string": "Stock for items shown below are not enough to prepare fulfillment:"
|
||||||
|
},
|
||||||
|
"src_dot_orders_dot_components_dot_OrderFulfillStockExceededDialog_dot_productLabel": {
|
||||||
|
"context": "table header product label",
|
||||||
|
"string": "Product"
|
||||||
|
},
|
||||||
|
"src_dot_orders_dot_components_dot_OrderFulfillStockExceededDialog_dot_questionLabel": {
|
||||||
|
"context": "stock exceeded action question label",
|
||||||
|
"string": "Are you sure you want to fulfill those products anyway?"
|
||||||
|
},
|
||||||
|
"src_dot_orders_dot_components_dot_OrderFulfillStockExceededDialog_dot_requiredStockLabel": {
|
||||||
|
"context": "table header required stock label",
|
||||||
|
"string": "Required"
|
||||||
|
},
|
||||||
|
"src_dot_orders_dot_components_dot_OrderFulfillStockExceededDialog_dot_title": {
|
||||||
|
"context": "stock exceeded dialog title",
|
||||||
|
"string": "Not enough stock"
|
||||||
|
},
|
||||||
|
"src_dot_orders_dot_components_dot_OrderFulfillStockExceededDialog_dot_warehouseStockLabel": {
|
||||||
|
"context": "table header warehouse stock label",
|
||||||
|
"string": "Warehouse stock"
|
||||||
|
},
|
||||||
"src_dot_orders_dot_components_dot_OrderFulfilledProductsCard_dot_addTracking": {
|
"src_dot_orders_dot_components_dot_OrderFulfilledProductsCard_dot_addTracking": {
|
||||||
"context": "add tracking button",
|
"context": "add tracking button",
|
||||||
"string": "Add tracking"
|
"string": "Add tracking"
|
||||||
|
@ -5345,42 +5473,14 @@
|
||||||
"context": "button",
|
"context": "button",
|
||||||
"string": "Set maximal quantities"
|
"string": "Set maximal quantities"
|
||||||
},
|
},
|
||||||
"src_dot_orders_dot_components_dot_OrderReturnPage_dot_OrderReturnRefundItemsCard_dot_cancelled": {
|
|
||||||
"context": "cancelled fulfillment, section header",
|
|
||||||
"string": "Cancelled ({quantity})"
|
|
||||||
},
|
|
||||||
"src_dot_orders_dot_components_dot_OrderReturnPage_dot_OrderReturnRefundItemsCard_dot_description": {
|
"src_dot_orders_dot_components_dot_OrderReturnPage_dot_OrderReturnRefundItemsCard_dot_description": {
|
||||||
"context": "product no longer exists error description",
|
"context": "product no longer exists error description",
|
||||||
"string": "This product is no longer in database so it can’t be replaced, nor returned"
|
"string": "This product is no longer in database so it can’t be replaced, nor returned"
|
||||||
},
|
},
|
||||||
"src_dot_orders_dot_components_dot_OrderReturnPage_dot_OrderReturnRefundItemsCard_dot_fulfilled": {
|
|
||||||
"context": "section header",
|
|
||||||
"string": "Fulfilled ({quantity})"
|
|
||||||
},
|
|
||||||
"src_dot_orders_dot_components_dot_OrderReturnPage_dot_OrderReturnRefundItemsCard_dot_fulfilledFrom": {
|
|
||||||
"context": "fulfilled fulfillment, section header",
|
|
||||||
"string": "Fulfilled from {warehouseName}"
|
|
||||||
},
|
|
||||||
"src_dot_orders_dot_components_dot_OrderReturnPage_dot_OrderReturnRefundItemsCard_dot_improperValue": {
|
"src_dot_orders_dot_components_dot_OrderReturnPage_dot_OrderReturnRefundItemsCard_dot_improperValue": {
|
||||||
"context": "error message",
|
"context": "error message",
|
||||||
"string": "Improper value"
|
"string": "Improper value"
|
||||||
},
|
},
|
||||||
"src_dot_orders_dot_components_dot_OrderReturnPage_dot_OrderReturnRefundItemsCard_dot_refunded": {
|
|
||||||
"context": "refunded fulfillment, section header",
|
|
||||||
"string": "Refunded ({quantity})"
|
|
||||||
},
|
|
||||||
"src_dot_orders_dot_components_dot_OrderReturnPage_dot_OrderReturnRefundItemsCard_dot_refundedAndReturned": {
|
|
||||||
"context": "cancelled fulfillment, section header",
|
|
||||||
"string": "Refunded and Returned ({quantity})"
|
|
||||||
},
|
|
||||||
"src_dot_orders_dot_components_dot_OrderReturnPage_dot_OrderReturnRefundItemsCard_dot_replaced": {
|
|
||||||
"context": "refunded fulfillment, section header",
|
|
||||||
"string": "Replaced ({quantity})"
|
|
||||||
},
|
|
||||||
"src_dot_orders_dot_components_dot_OrderReturnPage_dot_OrderReturnRefundItemsCard_dot_returned": {
|
|
||||||
"context": "refunded fulfillment, section header",
|
|
||||||
"string": "Returned ({quantity})"
|
|
||||||
},
|
|
||||||
"src_dot_orders_dot_components_dot_OrderReturnPage_dot_OrderReturnRefundItemsCard_dot_title": {
|
"src_dot_orders_dot_components_dot_OrderReturnPage_dot_OrderReturnRefundItemsCard_dot_title": {
|
||||||
"context": "product no longer exists error title",
|
"context": "product no longer exists error title",
|
||||||
"string": "Product no longer exists"
|
"string": "Product no longer exists"
|
||||||
|
@ -5393,14 +5493,6 @@
|
||||||
"context": "section header",
|
"context": "section header",
|
||||||
"string": "Unfulfilled Items"
|
"string": "Unfulfilled Items"
|
||||||
},
|
},
|
||||||
"src_dot_orders_dot_components_dot_OrderReturnPage_dot_OrderReturnRefundItemsCard_dot_unfulfilled": {
|
|
||||||
"context": "section header",
|
|
||||||
"string": "Unfulfilled"
|
|
||||||
},
|
|
||||||
"src_dot_orders_dot_components_dot_OrderReturnPage_dot_OrderReturnRefundItemsCard_dot_waitingForApproval": {
|
|
||||||
"context": "unapproved fulfillment, section header",
|
|
||||||
"string": "Waiting for approval ({quantity})"
|
|
||||||
},
|
|
||||||
"src_dot_orders_dot_components_dot_OrderReturnPage_dot_appTitle": {
|
"src_dot_orders_dot_components_dot_OrderReturnPage_dot_appTitle": {
|
||||||
"context": "page header with order number",
|
"context": "page header with order number",
|
||||||
"string": "Order #{orderNumber}"
|
"string": "Order #{orderNumber}"
|
||||||
|
|
|
@ -74,12 +74,22 @@ export const fragmentOrderLine = gql`
|
||||||
fragment OrderLine on OrderLine {
|
fragment OrderLine on OrderLine {
|
||||||
id
|
id
|
||||||
isShippingRequired
|
isShippingRequired
|
||||||
|
allocations {
|
||||||
|
id
|
||||||
|
quantity
|
||||||
|
warehouse {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
variant {
|
variant {
|
||||||
id
|
id
|
||||||
quantityAvailable
|
quantityAvailable
|
||||||
preorder {
|
preorder {
|
||||||
endDate
|
endDate
|
||||||
}
|
}
|
||||||
|
stocks {
|
||||||
|
...Stock
|
||||||
|
}
|
||||||
}
|
}
|
||||||
productName
|
productName
|
||||||
productSku
|
productSku
|
||||||
|
@ -324,3 +334,60 @@ export const fragmentShopOrderSettings = gql`
|
||||||
fulfillmentAllowUnpaid
|
fulfillmentAllowUnpaid
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export const fragmentOrderFulfillLine = gql`
|
||||||
|
fragment OrderFulfillLine on OrderLine {
|
||||||
|
id
|
||||||
|
isShippingRequired
|
||||||
|
productName
|
||||||
|
quantity
|
||||||
|
allocations {
|
||||||
|
quantity
|
||||||
|
warehouse {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
quantityFulfilled
|
||||||
|
quantityToFulfill
|
||||||
|
variant {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
sku
|
||||||
|
preorder {
|
||||||
|
endDate
|
||||||
|
}
|
||||||
|
attributes {
|
||||||
|
values {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stocks {
|
||||||
|
...Stock
|
||||||
|
}
|
||||||
|
trackInventory
|
||||||
|
}
|
||||||
|
thumbnail(size: 64) {
|
||||||
|
url
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const fragmentOrderLineStockData = gql`
|
||||||
|
fragment OrderLineStockData on OrderLine {
|
||||||
|
id
|
||||||
|
allocations {
|
||||||
|
quantity
|
||||||
|
warehouse {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
quantity
|
||||||
|
quantityToFulfill
|
||||||
|
variant {
|
||||||
|
stocks {
|
||||||
|
...Stock
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
|
@ -6,8 +6,7 @@ export const stockFragment = gql`
|
||||||
quantity
|
quantity
|
||||||
quantityAllocated
|
quantityAllocated
|
||||||
warehouse {
|
warehouse {
|
||||||
id
|
...Warehouse
|
||||||
name
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
|
@ -1182,16 +1182,42 @@ export const OrderEventFragmentDoc = gql`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
export const WarehouseFragmentDoc = gql`
|
||||||
|
fragment Warehouse on Warehouse {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
export const StockFragmentDoc = gql`
|
||||||
|
fragment Stock on Stock {
|
||||||
|
id
|
||||||
|
quantity
|
||||||
|
quantityAllocated
|
||||||
|
warehouse {
|
||||||
|
...Warehouse
|
||||||
|
}
|
||||||
|
}
|
||||||
|
${WarehouseFragmentDoc}`;
|
||||||
export const OrderLineFragmentDoc = gql`
|
export const OrderLineFragmentDoc = gql`
|
||||||
fragment OrderLine on OrderLine {
|
fragment OrderLine on OrderLine {
|
||||||
id
|
id
|
||||||
isShippingRequired
|
isShippingRequired
|
||||||
|
allocations {
|
||||||
|
id
|
||||||
|
quantity
|
||||||
|
warehouse {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
variant {
|
variant {
|
||||||
id
|
id
|
||||||
quantityAvailable
|
quantityAvailable
|
||||||
preorder {
|
preorder {
|
||||||
endDate
|
endDate
|
||||||
}
|
}
|
||||||
|
stocks {
|
||||||
|
...Stock
|
||||||
|
}
|
||||||
}
|
}
|
||||||
productName
|
productName
|
||||||
productSku
|
productSku
|
||||||
|
@ -1230,7 +1256,7 @@ export const OrderLineFragmentDoc = gql`
|
||||||
url
|
url
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
${StockFragmentDoc}`;
|
||||||
export const FulfillmentFragmentDoc = gql`
|
export const FulfillmentFragmentDoc = gql`
|
||||||
fragment Fulfillment on Fulfillment {
|
fragment Fulfillment on Fulfillment {
|
||||||
id
|
id
|
||||||
|
@ -1421,6 +1447,61 @@ export const ShopOrderSettingsFragmentDoc = gql`
|
||||||
fulfillmentAllowUnpaid
|
fulfillmentAllowUnpaid
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
export const OrderFulfillLineFragmentDoc = gql`
|
||||||
|
fragment OrderFulfillLine on OrderLine {
|
||||||
|
id
|
||||||
|
isShippingRequired
|
||||||
|
productName
|
||||||
|
quantity
|
||||||
|
allocations {
|
||||||
|
quantity
|
||||||
|
warehouse {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
quantityFulfilled
|
||||||
|
quantityToFulfill
|
||||||
|
variant {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
sku
|
||||||
|
preorder {
|
||||||
|
endDate
|
||||||
|
}
|
||||||
|
attributes {
|
||||||
|
values {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stocks {
|
||||||
|
...Stock
|
||||||
|
}
|
||||||
|
trackInventory
|
||||||
|
}
|
||||||
|
thumbnail(size: 64) {
|
||||||
|
url
|
||||||
|
}
|
||||||
|
}
|
||||||
|
${StockFragmentDoc}`;
|
||||||
|
export const OrderLineStockDataFragmentDoc = gql`
|
||||||
|
fragment OrderLineStockData on OrderLine {
|
||||||
|
id
|
||||||
|
allocations {
|
||||||
|
quantity
|
||||||
|
warehouse {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
quantity
|
||||||
|
quantityToFulfill
|
||||||
|
variant {
|
||||||
|
stocks {
|
||||||
|
...Stock
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
${StockFragmentDoc}`;
|
||||||
export const PageTypeFragmentDoc = gql`
|
export const PageTypeFragmentDoc = gql`
|
||||||
fragment PageType on PageType {
|
fragment PageType on PageType {
|
||||||
id
|
id
|
||||||
|
@ -1803,17 +1884,6 @@ export const ProductMediaFragmentDoc = gql`
|
||||||
oembedData
|
oembedData
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
export const StockFragmentDoc = gql`
|
|
||||||
fragment Stock on Stock {
|
|
||||||
id
|
|
||||||
quantity
|
|
||||||
quantityAllocated
|
|
||||||
warehouse {
|
|
||||||
id
|
|
||||||
name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
export const PreorderFragmentDoc = gql`
|
export const PreorderFragmentDoc = gql`
|
||||||
fragment Preorder on PreorderData {
|
fragment Preorder on PreorderData {
|
||||||
globalThreshold
|
globalThreshold
|
||||||
|
@ -2566,12 +2636,6 @@ export const AttributeValueTranslatableContentFragmentDoc = gql`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
${AttributeChoicesTranslationFragmentDoc}`;
|
${AttributeChoicesTranslationFragmentDoc}`;
|
||||||
export const WarehouseFragmentDoc = gql`
|
|
||||||
fragment Warehouse on Warehouse {
|
|
||||||
id
|
|
||||||
name
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
export const WarehouseWithShippingFragmentDoc = gql`
|
export const WarehouseWithShippingFragmentDoc = gql`
|
||||||
fragment WarehouseWithShipping on Warehouse {
|
fragment WarehouseWithShipping on Warehouse {
|
||||||
...Warehouse
|
...Warehouse
|
||||||
|
@ -8334,8 +8398,12 @@ export type OrderFulfillmentUpdateTrackingMutationHookResult = ReturnType<typeof
|
||||||
export type OrderFulfillmentUpdateTrackingMutationResult = Apollo.MutationResult<Types.OrderFulfillmentUpdateTrackingMutation>;
|
export type OrderFulfillmentUpdateTrackingMutationResult = Apollo.MutationResult<Types.OrderFulfillmentUpdateTrackingMutation>;
|
||||||
export type OrderFulfillmentUpdateTrackingMutationOptions = Apollo.BaseMutationOptions<Types.OrderFulfillmentUpdateTrackingMutation, Types.OrderFulfillmentUpdateTrackingMutationVariables>;
|
export type OrderFulfillmentUpdateTrackingMutationOptions = Apollo.BaseMutationOptions<Types.OrderFulfillmentUpdateTrackingMutation, Types.OrderFulfillmentUpdateTrackingMutationVariables>;
|
||||||
export const OrderFulfillmentApproveDocument = gql`
|
export const OrderFulfillmentApproveDocument = gql`
|
||||||
mutation OrderFulfillmentApprove($id: ID!, $notifyCustomer: Boolean!) {
|
mutation OrderFulfillmentApprove($id: ID!, $notifyCustomer: Boolean!, $allowStockToBeExceeded: Boolean) {
|
||||||
orderFulfillmentApprove(id: $id, notifyCustomer: $notifyCustomer) {
|
orderFulfillmentApprove(
|
||||||
|
id: $id
|
||||||
|
notifyCustomer: $notifyCustomer
|
||||||
|
allowStockToBeExceeded: $allowStockToBeExceeded
|
||||||
|
) {
|
||||||
errors {
|
errors {
|
||||||
...OrderError
|
...OrderError
|
||||||
}
|
}
|
||||||
|
@ -8363,6 +8431,7 @@ export type OrderFulfillmentApproveMutationFn = Apollo.MutationFunction<Types.Or
|
||||||
* variables: {
|
* variables: {
|
||||||
* id: // value for 'id'
|
* id: // value for 'id'
|
||||||
* notifyCustomer: // value for 'notifyCustomer'
|
* notifyCustomer: // value for 'notifyCustomer'
|
||||||
|
* allowStockToBeExceeded: // value for 'allowStockToBeExceeded'
|
||||||
* },
|
* },
|
||||||
* });
|
* });
|
||||||
*/
|
*/
|
||||||
|
@ -9147,49 +9216,12 @@ export const OrderFulfillDataDocument = gql`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
lines {
|
lines {
|
||||||
id
|
...OrderFulfillLine
|
||||||
isShippingRequired
|
|
||||||
productName
|
|
||||||
quantity
|
|
||||||
allocations {
|
|
||||||
quantity
|
|
||||||
warehouse {
|
|
||||||
id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
quantityFulfilled
|
|
||||||
quantityToFulfill
|
|
||||||
variant {
|
|
||||||
id
|
|
||||||
name
|
|
||||||
sku
|
|
||||||
preorder {
|
|
||||||
endDate
|
|
||||||
}
|
|
||||||
attributes {
|
|
||||||
values {
|
|
||||||
id
|
|
||||||
name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
stocks {
|
|
||||||
id
|
|
||||||
warehouse {
|
|
||||||
...Warehouse
|
|
||||||
}
|
|
||||||
quantity
|
|
||||||
quantityAllocated
|
|
||||||
}
|
|
||||||
trackInventory
|
|
||||||
}
|
|
||||||
thumbnail(size: 64) {
|
|
||||||
url
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
number
|
number
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
${WarehouseFragmentDoc}`;
|
${OrderFulfillLineFragmentDoc}`;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* __useOrderFulfillDataQuery__
|
* __useOrderFulfillDataQuery__
|
||||||
|
@ -13441,7 +13473,12 @@ export type SearchStaffMembersLazyQueryHookResult = ReturnType<typeof useSearchS
|
||||||
export type SearchStaffMembersQueryResult = Apollo.QueryResult<Types.SearchStaffMembersQuery, Types.SearchStaffMembersQueryVariables>;
|
export type SearchStaffMembersQueryResult = Apollo.QueryResult<Types.SearchStaffMembersQuery, Types.SearchStaffMembersQueryVariables>;
|
||||||
export const SearchWarehousesDocument = gql`
|
export const SearchWarehousesDocument = gql`
|
||||||
query SearchWarehouses($after: String, $first: Int!, $query: String!) {
|
query SearchWarehouses($after: String, $first: Int!, $query: String!) {
|
||||||
search: warehouses(after: $after, first: $first, filter: {search: $query}) {
|
search: warehouses(
|
||||||
|
after: $after
|
||||||
|
first: $first
|
||||||
|
sortBy: {direction: ASC, field: NAME}
|
||||||
|
filter: {search: $query}
|
||||||
|
) {
|
||||||
edges {
|
edges {
|
||||||
node {
|
node {
|
||||||
id
|
id
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -23,7 +23,7 @@ export { useLazyQuery, LazyQueryHookOptions } from "@apollo/client";
|
||||||
const getPermissionKey = (permission: string) =>
|
const getPermissionKey = (permission: string) =>
|
||||||
`PERMISSION_${permission}` as PrefixedPermissions;
|
`PERMISSION_${permission}` as PrefixedPermissions;
|
||||||
|
|
||||||
const allPermissions: Record<PrefixedPermissions, boolean> = Object.keys(
|
export const allPermissions: Record<PrefixedPermissions, boolean> = Object.keys(
|
||||||
PermissionEnum
|
PermissionEnum
|
||||||
).reduce(
|
).reduce(
|
||||||
(prev, code) => ({
|
(prev, code) => ({
|
||||||
|
|
|
@ -514,3 +514,6 @@ export const combinedMultiAutocompleteChoices = (
|
||||||
|
|
||||||
export const isInDevelopment =
|
export const isInDevelopment =
|
||||||
!process.env.NODE_ENV || process.env.NODE_ENV === "development";
|
!process.env.NODE_ENV || process.env.NODE_ENV === "development";
|
||||||
|
|
||||||
|
export type WithOptional<T, K extends keyof T> = Omit<T, K> &
|
||||||
|
Partial<Pick<T, K>>;
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import { Typography } from "@material-ui/core";
|
import { Typography } from "@material-ui/core";
|
||||||
|
import HorizontalSpacer from "@saleor/apps/components/HorizontalSpacer";
|
||||||
import DefaultCardTitle from "@saleor/components/CardTitle";
|
import DefaultCardTitle from "@saleor/components/CardTitle";
|
||||||
import { FulfillmentStatus } from "@saleor/graphql";
|
import { FulfillmentStatus } from "@saleor/graphql";
|
||||||
import { makeStyles, Pill } from "@saleor/macaw-ui";
|
import { CircleIndicator, makeStyles } from "@saleor/macaw-ui";
|
||||||
import { StatusType } from "@saleor/types";
|
import { StatusType } from "@saleor/types";
|
||||||
import camelCase from "lodash/camelCase";
|
import camelCase from "lodash/camelCase";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
@ -12,7 +13,7 @@ const useStyles = makeStyles(
|
||||||
title: {
|
title: {
|
||||||
width: "100%",
|
width: "100%",
|
||||||
display: "flex",
|
display: "flex",
|
||||||
justifyContent: "space-between"
|
justifyContent: "flex-start"
|
||||||
},
|
},
|
||||||
orderNumber: {
|
orderNumber: {
|
||||||
display: "inline",
|
display: "inline",
|
||||||
|
@ -23,15 +24,26 @@ const useStyles = makeStyles(
|
||||||
alignSelf: "center",
|
alignSelf: "center",
|
||||||
color: theme.palette.text.secondary,
|
color: theme.palette.text.secondary,
|
||||||
margin: `auto ${theme.spacing(1)} auto auto`
|
margin: `auto ${theme.spacing(1)} auto auto`
|
||||||
|
},
|
||||||
|
cardHeader: {
|
||||||
|
fontSize: "24px",
|
||||||
|
fontWeight: 500,
|
||||||
|
lineHeight: "29px",
|
||||||
|
letterSpacing: "0.02em",
|
||||||
|
textAlign: "left"
|
||||||
|
},
|
||||||
|
indicator: {
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center"
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
{ name: "CardTitle" }
|
{ name: "OrderCardTitle" }
|
||||||
);
|
);
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
cancelled: {
|
canceled: {
|
||||||
defaultMessage: "Cancelled ({quantity})",
|
defaultMessage: "Canceled ({quantity})",
|
||||||
description: "cancelled fulfillment, section header"
|
description: "canceled fulfillment, section header"
|
||||||
},
|
},
|
||||||
fulfilled: {
|
fulfilled: {
|
||||||
defaultMessage: "Fulfilled ({quantity})",
|
defaultMessage: "Fulfilled ({quantity})",
|
||||||
|
@ -58,7 +70,7 @@ const messages = defineMessages({
|
||||||
description: "unapproved fulfillment, section header"
|
description: "unapproved fulfillment, section header"
|
||||||
},
|
},
|
||||||
unfulfilled: {
|
unfulfilled: {
|
||||||
defaultMessage: "Unfulfilled",
|
defaultMessage: "Unfulfilled ({quantity})",
|
||||||
description: "section header"
|
description: "section header"
|
||||||
},
|
},
|
||||||
fulfilledFrom: {
|
fulfilledFrom: {
|
||||||
|
@ -71,9 +83,10 @@ type CardTitleStatus = FulfillmentStatus | "unfulfilled";
|
||||||
|
|
||||||
type CardTitleLines = Array<{
|
type CardTitleLines = Array<{
|
||||||
quantity: number;
|
quantity: number;
|
||||||
|
quantityToFulfill?: number;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
interface CardTitleProps {
|
interface OrderCardTitleProps {
|
||||||
lines?: CardTitleLines;
|
lines?: CardTitleLines;
|
||||||
fulfillmentOrder?: number;
|
fulfillmentOrder?: number;
|
||||||
status: CardTitleStatus;
|
status: CardTitleStatus;
|
||||||
|
@ -81,6 +94,7 @@ interface CardTitleProps {
|
||||||
orderNumber?: string;
|
orderNumber?: string;
|
||||||
warehouseName?: string;
|
warehouseName?: string;
|
||||||
withStatus?: boolean;
|
withStatus?: boolean;
|
||||||
|
className?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const selectStatus = (status: CardTitleStatus) => {
|
const selectStatus = (status: CardTitleStatus) => {
|
||||||
|
@ -100,18 +114,19 @@ const selectStatus = (status: CardTitleStatus) => {
|
||||||
case FulfillmentStatus.CANCELED:
|
case FulfillmentStatus.CANCELED:
|
||||||
return StatusType.ERROR;
|
return StatusType.ERROR;
|
||||||
default:
|
default:
|
||||||
return StatusType.WARNING;
|
return StatusType.ERROR;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const CardTitle: React.FC<CardTitleProps> = ({
|
const OrderCardTitle: React.FC<OrderCardTitleProps> = ({
|
||||||
lines = [],
|
lines = [],
|
||||||
fulfillmentOrder,
|
fulfillmentOrder,
|
||||||
status,
|
status,
|
||||||
orderNumber = "",
|
orderNumber = "",
|
||||||
warehouseName,
|
warehouseName,
|
||||||
withStatus = false,
|
withStatus = false,
|
||||||
toolbar
|
toolbar,
|
||||||
|
className
|
||||||
}) => {
|
}) => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const classes = useStyles({});
|
const classes = useStyles({});
|
||||||
|
@ -123,35 +138,36 @@ const CardTitle: React.FC<CardTitleProps> = ({
|
||||||
|
|
||||||
const messageForStatus = messages[camelCase(status)] || messages.unfulfilled;
|
const messageForStatus = messages[camelCase(status)] || messages.unfulfilled;
|
||||||
|
|
||||||
const totalQuantity = lines.reduce(
|
const totalQuantity =
|
||||||
(resultQuantity, { quantity }) => resultQuantity + quantity,
|
status === "unfulfilled"
|
||||||
0
|
? lines.reduce(
|
||||||
);
|
(resultQuantity, line) =>
|
||||||
|
resultQuantity + (line.quantityToFulfill ?? line.quantity),
|
||||||
const title = (
|
0
|
||||||
<>
|
)
|
||||||
{intl.formatMessage(messageForStatus, {
|
: lines.reduce(
|
||||||
fulfillmentName,
|
(resultQuantity, { quantity }) => resultQuantity + quantity,
|
||||||
quantity: totalQuantity
|
0
|
||||||
})}
|
);
|
||||||
{fulfillmentName && (
|
|
||||||
<Typography className={classes.orderNumber} variant="body1">
|
|
||||||
{fulfillmentName}
|
|
||||||
</Typography>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DefaultCardTitle
|
<DefaultCardTitle
|
||||||
toolbar={toolbar}
|
toolbar={toolbar}
|
||||||
|
className={className}
|
||||||
title={
|
title={
|
||||||
<div className={classes.title}>
|
<div className={classes.title}>
|
||||||
{withStatus ? (
|
{withStatus && (
|
||||||
<Pill label={title} color={selectStatus(status)} />
|
<div className={classes.indicator}>
|
||||||
) : (
|
<CircleIndicator color={selectStatus(status)} />
|
||||||
title
|
</div>
|
||||||
)}
|
)}
|
||||||
|
<HorizontalSpacer spacing={2} />
|
||||||
|
<Typography className={classes.cardHeader}>
|
||||||
|
{intl.formatMessage(messageForStatus, {
|
||||||
|
fulfillmentName,
|
||||||
|
quantity: totalQuantity
|
||||||
|
})}
|
||||||
|
</Typography>
|
||||||
{!!warehouseName && (
|
{!!warehouseName && (
|
||||||
<Typography className={classes.warehouseName} variant="caption">
|
<Typography className={classes.warehouseName} variant="caption">
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
|
@ -168,4 +184,5 @@ const CardTitle: React.FC<CardTitleProps> = ({
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default CardTitle;
|
OrderCardTitle.displayName = "OrderCardTitle";
|
||||||
|
export default OrderCardTitle;
|
2
src/orders/components/OrderCardTitle/index.ts
Normal file
2
src/orders/components/OrderCardTitle/index.ts
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
export { default } from "./OrderCardTitle";
|
||||||
|
export * from "./OrderCardTitle";
|
|
@ -0,0 +1,45 @@
|
||||||
|
import { MockedProvider, MockedResponse } from "@apollo/client/testing";
|
||||||
|
import { allPermissions } from "@saleor/hooks/makeQuery";
|
||||||
|
import { order, warehouseSearch } from "@saleor/orders/fixtures";
|
||||||
|
import { searchWarehouses } from "@saleor/searches/useWarehouseSearch";
|
||||||
|
import Decorator from "@saleor/storybook/Decorator";
|
||||||
|
import { storiesOf } from "@storybook/react";
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
import OrderChangeWarehouseDialog, { OrderChangeWarehouseDialogProps } from ".";
|
||||||
|
|
||||||
|
const props: OrderChangeWarehouseDialogProps = {
|
||||||
|
open: true,
|
||||||
|
lines: order("abc").lines,
|
||||||
|
currentWarehouse: null,
|
||||||
|
onConfirm: () => null,
|
||||||
|
onClose: () => null
|
||||||
|
};
|
||||||
|
|
||||||
|
const mocks: MockedResponse[] = [
|
||||||
|
{
|
||||||
|
request: {
|
||||||
|
query: searchWarehouses,
|
||||||
|
variables: {
|
||||||
|
first: 20,
|
||||||
|
after: null,
|
||||||
|
query: "",
|
||||||
|
...allPermissions
|
||||||
|
}
|
||||||
|
},
|
||||||
|
result: {
|
||||||
|
data: { search: warehouseSearch }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
storiesOf(
|
||||||
|
"Orders / Order details fulfillment warehouse selection modal",
|
||||||
|
module
|
||||||
|
)
|
||||||
|
.addDecorator(Decorator)
|
||||||
|
.add("default", () => (
|
||||||
|
<MockedProvider mocks={mocks}>
|
||||||
|
<OrderChangeWarehouseDialog {...props} />
|
||||||
|
</MockedProvider>
|
||||||
|
));
|
|
@ -0,0 +1,213 @@
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogActions,
|
||||||
|
DialogContent,
|
||||||
|
FormControlLabel,
|
||||||
|
InputAdornment,
|
||||||
|
Radio,
|
||||||
|
RadioGroup,
|
||||||
|
TableCell,
|
||||||
|
TableRow,
|
||||||
|
TextField,
|
||||||
|
Typography
|
||||||
|
} from "@material-ui/core";
|
||||||
|
import Debounce from "@saleor/components/Debounce";
|
||||||
|
import Skeleton from "@saleor/components/Skeleton";
|
||||||
|
import { OrderLineFragment, WarehouseFragment } from "@saleor/graphql";
|
||||||
|
import { buttonMessages } from "@saleor/intl";
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTable,
|
||||||
|
isScrolledToBottom,
|
||||||
|
isScrolledToTop,
|
||||||
|
ScrollShadow,
|
||||||
|
SearchIcon,
|
||||||
|
useElementScroll
|
||||||
|
} from "@saleor/macaw-ui";
|
||||||
|
import { isLineAvailableInWarehouse } from "@saleor/orders/utils/data";
|
||||||
|
import useWarehouseSearch from "@saleor/searches/useWarehouseSearch";
|
||||||
|
import { mapEdgesToItems } from "@saleor/utils/maps";
|
||||||
|
import React from "react";
|
||||||
|
import { FormattedMessage, useIntl } from "react-intl";
|
||||||
|
|
||||||
|
import { getById } from "../OrderReturnPage/utils";
|
||||||
|
import { changeWarehouseDialogMessages as messages } from "./messages";
|
||||||
|
import { useStyles } from "./styles";
|
||||||
|
|
||||||
|
export interface OrderChangeWarehouseDialogProps {
|
||||||
|
open: boolean;
|
||||||
|
lines: OrderLineFragment[];
|
||||||
|
currentWarehouse: WarehouseFragment;
|
||||||
|
onConfirm: (warehouse: WarehouseFragment) => void;
|
||||||
|
onClose();
|
||||||
|
}
|
||||||
|
|
||||||
|
export const OrderChangeWarehouseDialog: React.FC<OrderChangeWarehouseDialogProps> = ({
|
||||||
|
open,
|
||||||
|
lines,
|
||||||
|
currentWarehouse,
|
||||||
|
onConfirm,
|
||||||
|
onClose
|
||||||
|
}) => {
|
||||||
|
const classes = useStyles();
|
||||||
|
const intl = useIntl();
|
||||||
|
|
||||||
|
const { anchor, position, setAnchor } = useElementScroll();
|
||||||
|
const topShadow = isScrolledToTop(anchor, position, 20) === false;
|
||||||
|
const bottomShadow = isScrolledToBottom(anchor, position, 20) === false;
|
||||||
|
|
||||||
|
const [query, setQuery] = React.useState<string>("");
|
||||||
|
const [selectedWarehouseId, setSelectedWarehouseId] = React.useState<
|
||||||
|
string | null
|
||||||
|
>(null);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (currentWarehouse?.id) {
|
||||||
|
setSelectedWarehouseId(currentWarehouse.id);
|
||||||
|
}
|
||||||
|
}, [currentWarehouse]);
|
||||||
|
|
||||||
|
const { result: warehousesOpts, loadMore, search } = useWarehouseSearch({
|
||||||
|
variables: {
|
||||||
|
after: null,
|
||||||
|
first: 20,
|
||||||
|
query: ""
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const filteredWarehouses = mapEdgesToItems(warehousesOpts?.data?.search);
|
||||||
|
|
||||||
|
const selectedWarehouse = filteredWarehouses?.find(
|
||||||
|
getById(selectedWarehouseId ?? "")
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
setSelectedWarehouseId(e.target.value);
|
||||||
|
};
|
||||||
|
const handleSubmit = () => {
|
||||||
|
onConfirm(selectedWarehouse);
|
||||||
|
onClose();
|
||||||
|
};
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (!bottomShadow) {
|
||||||
|
loadMore();
|
||||||
|
}
|
||||||
|
}, [bottomShadow]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dialog fullWidth open={open} onClose={onClose}>
|
||||||
|
<ScrollShadow variant="top" show={topShadow}>
|
||||||
|
<DialogHeader onClose={onClose}>
|
||||||
|
<FormattedMessage {...messages.dialogTitle} />
|
||||||
|
</DialogHeader>
|
||||||
|
|
||||||
|
<DialogContent className={classes.container}>
|
||||||
|
<FormattedMessage {...messages.dialogDescription} />
|
||||||
|
<Debounce debounceFn={search}>
|
||||||
|
{debounceSearchChange => {
|
||||||
|
const handleSearchChange = (
|
||||||
|
event: React.ChangeEvent<HTMLInputElement>
|
||||||
|
) => {
|
||||||
|
const value = event.target.value;
|
||||||
|
setQuery(value);
|
||||||
|
debounceSearchChange(value);
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<TextField
|
||||||
|
className={classes.searchBox}
|
||||||
|
value={query}
|
||||||
|
variant="outlined"
|
||||||
|
onChange={handleSearchChange}
|
||||||
|
placeholder={intl.formatMessage(
|
||||||
|
messages.searchFieldPlaceholder
|
||||||
|
)}
|
||||||
|
fullWidth
|
||||||
|
InputProps={{
|
||||||
|
startAdornment: (
|
||||||
|
<InputAdornment position="start">
|
||||||
|
<SearchIcon />
|
||||||
|
</InputAdornment>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
inputProps={{ className: classes.searchInput }}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
</Debounce>
|
||||||
|
|
||||||
|
<Typography className={classes.supportHeader}>
|
||||||
|
<FormattedMessage {...messages.warehouseListLabel} />
|
||||||
|
</Typography>
|
||||||
|
</DialogContent>
|
||||||
|
</ScrollShadow>
|
||||||
|
|
||||||
|
<DialogTable ref={setAnchor}>
|
||||||
|
{filteredWarehouses ? (
|
||||||
|
<RadioGroup value={selectedWarehouseId} onChange={handleChange}>
|
||||||
|
{filteredWarehouses.map(warehouse => {
|
||||||
|
const unavailableLines = lines?.filter(
|
||||||
|
line => !isLineAvailableInWarehouse(line, warehouse)
|
||||||
|
);
|
||||||
|
const someLinesUnavailable = unavailableLines?.length > 0;
|
||||||
|
return (
|
||||||
|
<TableRow key={warehouse.id}>
|
||||||
|
<TableCell>
|
||||||
|
<FormControlLabel
|
||||||
|
value={warehouse.id}
|
||||||
|
control={<Radio color="primary" />}
|
||||||
|
label={
|
||||||
|
<div className={classes.radioLabelContainer}>
|
||||||
|
<span className={classes.warehouseName}>
|
||||||
|
{warehouse.name}
|
||||||
|
</span>
|
||||||
|
{someLinesUnavailable && (
|
||||||
|
<Typography className={classes.supportText}>
|
||||||
|
{unavailableLines.length === 1
|
||||||
|
? intl.formatMessage(
|
||||||
|
messages.productUnavailable,
|
||||||
|
{
|
||||||
|
productName:
|
||||||
|
unavailableLines[0].productName
|
||||||
|
}
|
||||||
|
)
|
||||||
|
: intl.formatMessage(
|
||||||
|
messages.multipleProductsUnavailable,
|
||||||
|
{ productCount: unavailableLines.length }
|
||||||
|
)}
|
||||||
|
</Typography>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
{currentWarehouse?.id === warehouse?.id && (
|
||||||
|
<Typography className={classes.helpText}>
|
||||||
|
{intl.formatMessage(messages.currentSelection)}
|
||||||
|
</Typography>
|
||||||
|
)}
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</RadioGroup>
|
||||||
|
) : (
|
||||||
|
<Skeleton />
|
||||||
|
)}
|
||||||
|
</DialogTable>
|
||||||
|
<ScrollShadow variant="bottom" show={bottomShadow}>
|
||||||
|
<DialogActions>
|
||||||
|
<Button
|
||||||
|
onClick={handleSubmit}
|
||||||
|
color="primary"
|
||||||
|
variant="primary"
|
||||||
|
disabled={!selectedWarehouse}
|
||||||
|
>
|
||||||
|
{intl.formatMessage(buttonMessages.select)}
|
||||||
|
</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</ScrollShadow>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
OrderChangeWarehouseDialog.displayName = "OrderChangeWarehouseDialog";
|
||||||
|
export default OrderChangeWarehouseDialog;
|
|
@ -0,0 +1,2 @@
|
||||||
|
export { default } from "./OrderChangeWarehouseDialog";
|
||||||
|
export * from "./OrderChangeWarehouseDialog";
|
32
src/orders/components/OrderChangeWarehouseDialog/messages.ts
Normal file
32
src/orders/components/OrderChangeWarehouseDialog/messages.ts
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
import { defineMessages } from "react-intl";
|
||||||
|
|
||||||
|
export const changeWarehouseDialogMessages = defineMessages({
|
||||||
|
dialogTitle: {
|
||||||
|
defaultMessage: "Change warehouse",
|
||||||
|
description: "change warehouse dialog title"
|
||||||
|
},
|
||||||
|
dialogDescription: {
|
||||||
|
defaultMessage: "Choose warehouse you want to fulfill this order from",
|
||||||
|
description: "change warehouse dialog description"
|
||||||
|
},
|
||||||
|
searchFieldPlaceholder: {
|
||||||
|
defaultMessage: "Search warehouses",
|
||||||
|
description: "change warehouse dialog search placeholder"
|
||||||
|
},
|
||||||
|
warehouseListLabel: {
|
||||||
|
defaultMessage: "Warehouses A to Z",
|
||||||
|
description: "change warehouse dialog warehouse list label"
|
||||||
|
},
|
||||||
|
productUnavailable: {
|
||||||
|
defaultMessage: "{productName} is unavailable at this location",
|
||||||
|
description: "warehouse label when one product is unavailable"
|
||||||
|
},
|
||||||
|
multipleProductsUnavailable: {
|
||||||
|
defaultMessage: "{productCount} products are unavailable at this location",
|
||||||
|
description: "warehouse label when multiple products are unavailable"
|
||||||
|
},
|
||||||
|
currentSelection: {
|
||||||
|
defaultMessage: "currently selected",
|
||||||
|
description: "label for currently selected warehouse"
|
||||||
|
}
|
||||||
|
});
|
46
src/orders/components/OrderChangeWarehouseDialog/styles.ts
Normal file
46
src/orders/components/OrderChangeWarehouseDialog/styles.ts
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
import { makeStyles } from "@saleor/macaw-ui";
|
||||||
|
|
||||||
|
export const useStyles = makeStyles(
|
||||||
|
theme => ({
|
||||||
|
container: {
|
||||||
|
paddingTop: 0
|
||||||
|
},
|
||||||
|
searchBox: {
|
||||||
|
marginTop: theme.spacing(2),
|
||||||
|
marginBottom: theme.spacing(2)
|
||||||
|
},
|
||||||
|
searchInput: {
|
||||||
|
paddingTop: theme.spacing(2),
|
||||||
|
paddingBottom: theme.spacing(2)
|
||||||
|
},
|
||||||
|
supportHeader: {
|
||||||
|
textTransform: "uppercase",
|
||||||
|
color: theme.palette.saleor.main[3],
|
||||||
|
fontWeight: 500,
|
||||||
|
letterSpacing: "0.1em",
|
||||||
|
fontSize: "12px",
|
||||||
|
lineHeight: "160%"
|
||||||
|
},
|
||||||
|
helpText: {
|
||||||
|
display: "inline",
|
||||||
|
fontSize: "12px",
|
||||||
|
lineHeight: "160%",
|
||||||
|
color: theme.palette.saleor.main[3]
|
||||||
|
},
|
||||||
|
supportText: {
|
||||||
|
fontSize: "14px",
|
||||||
|
lineHeight: "160%",
|
||||||
|
color: theme.palette.saleor.main[3]
|
||||||
|
},
|
||||||
|
radioLabelContainer: {
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column"
|
||||||
|
},
|
||||||
|
warehouseName: {
|
||||||
|
maxWidth: "350px",
|
||||||
|
overflow: "hidden",
|
||||||
|
textOverflow: "ellipsis"
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
{ name: "OrderChangeWarehouseDialog" }
|
||||||
|
);
|
|
@ -12,7 +12,8 @@ import Skeleton from "@saleor/components/Skeleton";
|
||||||
import {
|
import {
|
||||||
OrderDetailsFragment,
|
OrderDetailsFragment,
|
||||||
OrderDetailsQuery,
|
OrderDetailsQuery,
|
||||||
OrderStatus
|
OrderStatus,
|
||||||
|
WarehouseFragment
|
||||||
} from "@saleor/graphql";
|
} from "@saleor/graphql";
|
||||||
import { SubmitPromise } from "@saleor/hooks/useForm";
|
import { SubmitPromise } from "@saleor/hooks/useForm";
|
||||||
import { sectionNames } from "@saleor/intl";
|
import { sectionNames } from "@saleor/intl";
|
||||||
|
@ -65,6 +66,7 @@ export interface OrderDetailsPageProps {
|
||||||
}>;
|
}>;
|
||||||
disabled: boolean;
|
disabled: boolean;
|
||||||
saveButtonBarState: ConfirmButtonTransitionState;
|
saveButtonBarState: ConfirmButtonTransitionState;
|
||||||
|
selectedWarehouse?: WarehouseFragment;
|
||||||
onOrderLineAdd?: () => void;
|
onOrderLineAdd?: () => void;
|
||||||
onOrderLineChange?: (
|
onOrderLineChange?: (
|
||||||
id: string,
|
id: string,
|
||||||
|
@ -91,6 +93,7 @@ export interface OrderDetailsPageProps {
|
||||||
onInvoiceClick(invoiceId: string);
|
onInvoiceClick(invoiceId: string);
|
||||||
onInvoiceGenerate();
|
onInvoiceGenerate();
|
||||||
onInvoiceSend(invoiceId: string);
|
onInvoiceSend(invoiceId: string);
|
||||||
|
onWarehouseChange?();
|
||||||
onSubmit(data: MetadataFormData): SubmitPromise;
|
onSubmit(data: MetadataFormData): SubmitPromise;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -115,6 +118,7 @@ const OrderDetailsPage: React.FC<OrderDetailsPageProps> = props => {
|
||||||
order,
|
order,
|
||||||
shop,
|
shop,
|
||||||
saveButtonBarState,
|
saveButtonBarState,
|
||||||
|
selectedWarehouse,
|
||||||
onBack,
|
onBack,
|
||||||
onBillingAddressEdit,
|
onBillingAddressEdit,
|
||||||
onFulfillmentApprove,
|
onFulfillmentApprove,
|
||||||
|
@ -137,6 +141,7 @@ const OrderDetailsPage: React.FC<OrderDetailsPageProps> = props => {
|
||||||
onOrderLineChange,
|
onOrderLineChange,
|
||||||
onOrderLineRemove,
|
onOrderLineRemove,
|
||||||
onShippingMethodEdit,
|
onShippingMethodEdit,
|
||||||
|
onWarehouseChange,
|
||||||
onSubmit
|
onSubmit
|
||||||
} = props;
|
} = props;
|
||||||
const classes = useStyles(props);
|
const classes = useStyles(props);
|
||||||
|
@ -243,6 +248,8 @@ const OrderDetailsPage: React.FC<OrderDetailsPageProps> = props => {
|
||||||
notAllowedToFulfillUnpaid={notAllowedToFulfillUnpaid}
|
notAllowedToFulfillUnpaid={notAllowedToFulfillUnpaid}
|
||||||
lines={unfulfilled}
|
lines={unfulfilled}
|
||||||
onFulfill={onOrderFulfill}
|
onFulfill={onOrderFulfill}
|
||||||
|
onWarehouseChange={onWarehouseChange}
|
||||||
|
selectedWarehouse={selectedWarehouse}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
|
|
148
src/orders/components/OrderFulfillLine/OrderFulfillLine.tsx
Normal file
148
src/orders/components/OrderFulfillLine/OrderFulfillLine.tsx
Normal file
|
@ -0,0 +1,148 @@
|
||||||
|
import { TableCell, TableRow, TextField, Typography } from "@material-ui/core";
|
||||||
|
import Skeleton from "@saleor/components/Skeleton";
|
||||||
|
import TableCellAvatar from "@saleor/components/TableCellAvatar";
|
||||||
|
import {
|
||||||
|
OrderFulfillLineFragment,
|
||||||
|
OrderFulfillStockInput
|
||||||
|
} from "@saleor/graphql";
|
||||||
|
import { FormsetChange, FormsetData } from "@saleor/hooks/useFormset";
|
||||||
|
import { Tooltip, WarningIcon } from "@saleor/macaw-ui";
|
||||||
|
import {
|
||||||
|
getAttributesCaption,
|
||||||
|
getOrderLineAvailableQuantity,
|
||||||
|
getWarehouseStock
|
||||||
|
} from "@saleor/orders/utils/data";
|
||||||
|
import classNames from "classnames";
|
||||||
|
import React from "react";
|
||||||
|
import { useIntl } from "react-intl";
|
||||||
|
|
||||||
|
import { messages } from "./messages";
|
||||||
|
import { useStyles } from "./styles";
|
||||||
|
|
||||||
|
interface OrderFulfillLineProps {
|
||||||
|
line: OrderFulfillLineFragment;
|
||||||
|
lineIndex: number;
|
||||||
|
warehouseId: string;
|
||||||
|
formsetData: FormsetData<null, OrderFulfillStockInput[]>;
|
||||||
|
formsetChange: FormsetChange<OrderFulfillStockInput[]>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const OrderFulfillLine: React.FC<OrderFulfillLineProps> = props => {
|
||||||
|
const { line, lineIndex, warehouseId, formsetData, formsetChange } = props;
|
||||||
|
const classes = useStyles();
|
||||||
|
const intl = useIntl();
|
||||||
|
|
||||||
|
const isDeletedVariant = !line?.variant;
|
||||||
|
const isPreorder = !!line.variant?.preorder;
|
||||||
|
const lineFormQuantity = isPreorder
|
||||||
|
? 0
|
||||||
|
: formsetData[lineIndex].value?.[0]?.quantity;
|
||||||
|
|
||||||
|
const overfulfill = lineFormQuantity > line.quantityToFulfill;
|
||||||
|
const warehouseStock = getWarehouseStock(line?.variant?.stocks, warehouseId);
|
||||||
|
const availableQuantity = getOrderLineAvailableQuantity(line, warehouseStock);
|
||||||
|
|
||||||
|
const isStockExceeded = lineFormQuantity > availableQuantity;
|
||||||
|
|
||||||
|
if (!line) {
|
||||||
|
return (
|
||||||
|
<TableRow key={lineIndex}>
|
||||||
|
<TableCellAvatar className={classes.colName}>
|
||||||
|
<Skeleton />
|
||||||
|
</TableCellAvatar>
|
||||||
|
<TableCell className={classes.colSku}>
|
||||||
|
<Skeleton />
|
||||||
|
</TableCell>
|
||||||
|
<TableCell className={classes.colQuantity}>
|
||||||
|
<Skeleton />
|
||||||
|
</TableCell>
|
||||||
|
<TableCell className={classes.colStock}>
|
||||||
|
<Skeleton />
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TableRow key={line.id}>
|
||||||
|
<TableCellAvatar
|
||||||
|
className={classes.colName}
|
||||||
|
thumbnail={line?.thumbnail?.url}
|
||||||
|
badge={
|
||||||
|
isPreorder || !line?.variant ? (
|
||||||
|
<Tooltip
|
||||||
|
variant="warning"
|
||||||
|
title={intl.formatMessage(
|
||||||
|
isPreorder
|
||||||
|
? messages.preorderWarning
|
||||||
|
: messages.deletedVariantWarning
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div className={classes.warningIcon}>
|
||||||
|
<WarningIcon />
|
||||||
|
</div>
|
||||||
|
</Tooltip>
|
||||||
|
) : (
|
||||||
|
undefined
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{line.productName}
|
||||||
|
<Typography color="textSecondary" variant="caption">
|
||||||
|
{getAttributesCaption(line.variant?.attributes)}
|
||||||
|
</Typography>
|
||||||
|
</TableCellAvatar>
|
||||||
|
<TableCell className={classes.colSku}>{line.variant?.sku}</TableCell>
|
||||||
|
{isPreorder ? (
|
||||||
|
<TableCell className={classes.colQuantity} />
|
||||||
|
) : (
|
||||||
|
<TableCell
|
||||||
|
className={classes.colQuantity}
|
||||||
|
key={warehouseStock?.id ?? "deletedVariant" + lineIndex}
|
||||||
|
>
|
||||||
|
<TextField
|
||||||
|
type="number"
|
||||||
|
inputProps={{
|
||||||
|
className: classNames(classes.quantityInnerInput, {
|
||||||
|
[classes.quantityInnerInputNoRemaining]: !line.variant
|
||||||
|
?.trackInventory
|
||||||
|
}),
|
||||||
|
min: 0,
|
||||||
|
style: { textAlign: "right" }
|
||||||
|
}}
|
||||||
|
fullWidth
|
||||||
|
value={lineFormQuantity}
|
||||||
|
onChange={event =>
|
||||||
|
formsetChange(line.id, [
|
||||||
|
{
|
||||||
|
quantity: parseInt(event.target.value, 10),
|
||||||
|
warehouse: warehouseId
|
||||||
|
}
|
||||||
|
])
|
||||||
|
}
|
||||||
|
error={overfulfill}
|
||||||
|
variant="outlined"
|
||||||
|
InputProps={{
|
||||||
|
classes: {
|
||||||
|
...(isStockExceeded &&
|
||||||
|
!overfulfill && {
|
||||||
|
notchedOutline: classes.warning
|
||||||
|
})
|
||||||
|
},
|
||||||
|
endAdornment: (
|
||||||
|
<div className={classes.remainingQuantity}>
|
||||||
|
/ {line.quantityToFulfill}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</TableCell>
|
||||||
|
)}
|
||||||
|
<TableCell className={classes.colStock} key="total">
|
||||||
|
{isPreorder || isDeletedVariant ? undefined : availableQuantity}
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
OrderFulfillLine.displayName = "OrderFulfillLine";
|
||||||
|
export default OrderFulfillLine;
|
2
src/orders/components/OrderFulfillLine/index.ts
Normal file
2
src/orders/components/OrderFulfillLine/index.ts
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
export { default } from "./OrderFulfillLine";
|
||||||
|
export * from "./OrderFulfillLine";
|
13
src/orders/components/OrderFulfillLine/messages.ts
Normal file
13
src/orders/components/OrderFulfillLine/messages.ts
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
import { defineMessages } from "react-intl";
|
||||||
|
|
||||||
|
export const messages = defineMessages({
|
||||||
|
preorderWarning: {
|
||||||
|
defaultMessage:
|
||||||
|
"This product is still in preorder. You will be able to fulfill it after it reaches it’s release date",
|
||||||
|
description: "tooltip content when product is in preorder"
|
||||||
|
},
|
||||||
|
deletedVariantWarning: {
|
||||||
|
defaultMessage: "This variant no longer exists. You can still fulfill it.",
|
||||||
|
description: "tooltip content when line's variant has been deleted"
|
||||||
|
}
|
||||||
|
});
|
47
src/orders/components/OrderFulfillLine/styles.ts
Normal file
47
src/orders/components/OrderFulfillLine/styles.ts
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
import { makeStyles } from "@saleor/macaw-ui";
|
||||||
|
|
||||||
|
export const useStyles = makeStyles(
|
||||||
|
theme => ({
|
||||||
|
colStock: {
|
||||||
|
textAlign: "right",
|
||||||
|
width: 180
|
||||||
|
},
|
||||||
|
colName: {
|
||||||
|
width: 250
|
||||||
|
},
|
||||||
|
colQuantity: {
|
||||||
|
textAlign: "right",
|
||||||
|
width: 210
|
||||||
|
},
|
||||||
|
colSku: {
|
||||||
|
textAlign: "right",
|
||||||
|
textOverflow: "ellipsis",
|
||||||
|
width: 150
|
||||||
|
},
|
||||||
|
warningIcon: {
|
||||||
|
color: theme.palette.saleor.warning.mid,
|
||||||
|
marginRight: theme.spacing(2)
|
||||||
|
},
|
||||||
|
error: {
|
||||||
|
color: theme.palette.error.main
|
||||||
|
},
|
||||||
|
warning: {
|
||||||
|
borderColor: theme.palette.saleor.warning.dark + " !important",
|
||||||
|
boxShadow: `0 0 0 3px ${theme.palette.saleor.warning.light}`
|
||||||
|
},
|
||||||
|
quantityInnerInput: {
|
||||||
|
paddingBottom: theme.spacing(2),
|
||||||
|
paddingTop: theme.spacing(2)
|
||||||
|
},
|
||||||
|
quantityInnerInputNoRemaining: {
|
||||||
|
paddingRight: 0
|
||||||
|
},
|
||||||
|
remainingQuantity: {
|
||||||
|
paddingBottom: theme.spacing(2),
|
||||||
|
paddingTop: theme.spacing(2),
|
||||||
|
color: theme.palette.text.secondary,
|
||||||
|
whiteSpace: "nowrap"
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
{ name: "OrderFulfillLine" }
|
||||||
|
);
|
|
@ -14,7 +14,7 @@ const props: OrderFulfillPageProps = {
|
||||||
onSubmit: () => undefined,
|
onSubmit: () => undefined,
|
||||||
order: orderToFulfill,
|
order: orderToFulfill,
|
||||||
saveButtonBar: "default",
|
saveButtonBar: "default",
|
||||||
warehouses: warehouseList
|
warehouse: warehouseList[0]
|
||||||
};
|
};
|
||||||
|
|
||||||
storiesOf("Views / Orders / Fulfill order", module)
|
storiesOf("Views / Orders / Fulfill order", module)
|
||||||
|
@ -25,7 +25,7 @@ storiesOf("Views / Orders / Fulfill order", module)
|
||||||
{...props}
|
{...props}
|
||||||
loading={true}
|
loading={true}
|
||||||
order={undefined}
|
order={undefined}
|
||||||
warehouses={undefined}
|
warehouse={undefined}
|
||||||
/>
|
/>
|
||||||
))
|
))
|
||||||
.add("error", () => (
|
.add("error", () => (
|
||||||
|
@ -44,6 +44,4 @@ storiesOf("Views / Orders / Fulfill order", module)
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
))
|
))
|
||||||
.add("one warehouse", () => (
|
.add("one warehouse", () => <OrderFulfillPage {...props} />);
|
||||||
<OrderFulfillPage {...props} warehouses={warehouseList.slice(0, 1)} />
|
|
||||||
));
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import {
|
import {
|
||||||
Card,
|
Card,
|
||||||
CardActions,
|
|
||||||
TableBody,
|
TableBody,
|
||||||
TableCell,
|
TableCell,
|
||||||
TableHead,
|
TableHead,
|
||||||
|
@ -8,19 +7,20 @@ import {
|
||||||
TextField,
|
TextField,
|
||||||
Typography
|
Typography
|
||||||
} from "@material-ui/core";
|
} from "@material-ui/core";
|
||||||
import { CSSProperties } from "@material-ui/styles";
|
|
||||||
import CardTitle from "@saleor/components/CardTitle";
|
import CardTitle from "@saleor/components/CardTitle";
|
||||||
import Container from "@saleor/components/Container";
|
import Container from "@saleor/components/Container";
|
||||||
import ControlledCheckbox from "@saleor/components/ControlledCheckbox";
|
import ControlledCheckbox from "@saleor/components/ControlledCheckbox";
|
||||||
import Form from "@saleor/components/Form";
|
import Form from "@saleor/components/Form";
|
||||||
|
import { Grid } from "@saleor/components/Grid";
|
||||||
import PageHeader from "@saleor/components/PageHeader";
|
import PageHeader from "@saleor/components/PageHeader";
|
||||||
import ResponsiveTable from "@saleor/components/ResponsiveTable";
|
import ResponsiveTable from "@saleor/components/ResponsiveTable";
|
||||||
import Savebar from "@saleor/components/Savebar";
|
import Savebar from "@saleor/components/Savebar";
|
||||||
import Skeleton from "@saleor/components/Skeleton";
|
import Skeleton from "@saleor/components/Skeleton";
|
||||||
import TableCellAvatar from "@saleor/components/TableCellAvatar";
|
|
||||||
import {
|
import {
|
||||||
FulfillOrderMutation,
|
FulfillOrderMutation,
|
||||||
|
OrderErrorCode,
|
||||||
OrderFulfillDataQuery,
|
OrderFulfillDataQuery,
|
||||||
|
OrderFulfillLineFragment,
|
||||||
OrderFulfillStockInput,
|
OrderFulfillStockInput,
|
||||||
ShopOrderSettingsFragment,
|
ShopOrderSettingsFragment,
|
||||||
WarehouseFragment
|
WarehouseFragment
|
||||||
|
@ -28,91 +28,25 @@ import {
|
||||||
import { SubmitPromise } from "@saleor/hooks/useForm";
|
import { SubmitPromise } from "@saleor/hooks/useForm";
|
||||||
import useFormset, { FormsetData } from "@saleor/hooks/useFormset";
|
import useFormset, { FormsetData } from "@saleor/hooks/useFormset";
|
||||||
import { commonMessages } from "@saleor/intl";
|
import { commonMessages } from "@saleor/intl";
|
||||||
import {
|
import { Backlink, ConfirmButtonTransitionState } from "@saleor/macaw-ui";
|
||||||
Backlink,
|
|
||||||
ConfirmButtonTransitionState,
|
|
||||||
makeStyles
|
|
||||||
} from "@saleor/macaw-ui";
|
|
||||||
import { renderCollection } from "@saleor/misc";
|
import { renderCollection } from "@saleor/misc";
|
||||||
import {
|
import {
|
||||||
getToFulfillOrderLines,
|
getAttributesCaption,
|
||||||
isStockError
|
getToFulfillOrderLines
|
||||||
} from "@saleor/orders/utils/data";
|
} from "@saleor/orders/utils/data";
|
||||||
import { update } from "@saleor/utils/lists";
|
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { FormattedMessage, useIntl } from "react-intl";
|
import { FormattedMessage, useIntl } from "react-intl";
|
||||||
|
|
||||||
|
import OrderFulfillLine from "../OrderFulfillLine/OrderFulfillLine";
|
||||||
|
import OrderFulfillStockExceededDialog from "../OrderFulfillStockExceededDialog";
|
||||||
import { messages } from "./messages";
|
import { messages } from "./messages";
|
||||||
|
import { useStyles } from "./styles";
|
||||||
const useStyles = makeStyles(
|
|
||||||
theme => {
|
|
||||||
const inputPadding: CSSProperties = {
|
|
||||||
paddingBottom: theme.spacing(2),
|
|
||||||
paddingTop: theme.spacing(2)
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
actionBar: {
|
|
||||||
flexDirection: "row",
|
|
||||||
padding: theme.spacing(1, 4)
|
|
||||||
},
|
|
||||||
colName: {
|
|
||||||
width: 250,
|
|
||||||
[theme.breakpoints.up("lg")]: {
|
|
||||||
width: ({ warehouses }: OrderFulfillPageProps) =>
|
|
||||||
warehouses?.length > 3 ? 250 : "auto"
|
|
||||||
},
|
|
||||||
[theme.breakpoints.only("md")]: {
|
|
||||||
width: ({ warehouses }: OrderFulfillPageProps) =>
|
|
||||||
warehouses?.length > 2 ? 250 : "auto"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
colQuantity: {
|
|
||||||
textAlign: "right",
|
|
||||||
width: 210
|
|
||||||
},
|
|
||||||
colQuantityHeader: {
|
|
||||||
textAlign: "right"
|
|
||||||
},
|
|
||||||
colQuantityTotal: {
|
|
||||||
textAlign: "right",
|
|
||||||
width: 180
|
|
||||||
},
|
|
||||||
colSku: {
|
|
||||||
textAlign: "right",
|
|
||||||
textOverflow: "ellipsis",
|
|
||||||
width: 150
|
|
||||||
},
|
|
||||||
error: {
|
|
||||||
color: theme.palette.error.main
|
|
||||||
},
|
|
||||||
full: {
|
|
||||||
fontWeight: 600
|
|
||||||
},
|
|
||||||
quantityInnerInput: {
|
|
||||||
...inputPadding
|
|
||||||
},
|
|
||||||
quantityInnerInputNoRemaining: {
|
|
||||||
paddingRight: 0
|
|
||||||
},
|
|
||||||
remainingQuantity: {
|
|
||||||
...inputPadding,
|
|
||||||
color: theme.palette.text.secondary,
|
|
||||||
whiteSpace: "nowrap"
|
|
||||||
},
|
|
||||||
table: {
|
|
||||||
"&&": {
|
|
||||||
tableLayout: "fixed"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
},
|
|
||||||
{ name: "OrderFulfillPage" }
|
|
||||||
);
|
|
||||||
|
|
||||||
interface OrderFulfillFormData {
|
interface OrderFulfillFormData {
|
||||||
sendInfo: boolean;
|
sendInfo: boolean;
|
||||||
|
trackingNumber: string;
|
||||||
|
allowStockToBeExceeded: boolean;
|
||||||
}
|
}
|
||||||
export interface OrderFulfillSubmitData extends OrderFulfillFormData {
|
export interface OrderFulfillSubmitData extends OrderFulfillFormData {
|
||||||
items: FormsetData<null, OrderFulfillStockInput[]>;
|
items: FormsetData<null, OrderFulfillStockInput[]>;
|
||||||
|
@ -122,14 +56,16 @@ export interface OrderFulfillPageProps {
|
||||||
errors: FulfillOrderMutation["orderFulfill"]["errors"];
|
errors: FulfillOrderMutation["orderFulfill"]["errors"];
|
||||||
order: OrderFulfillDataQuery["order"];
|
order: OrderFulfillDataQuery["order"];
|
||||||
saveButtonBar: ConfirmButtonTransitionState;
|
saveButtonBar: ConfirmButtonTransitionState;
|
||||||
warehouses: WarehouseFragment[];
|
warehouse: WarehouseFragment;
|
||||||
shopSettings?: ShopOrderSettingsFragment;
|
shopSettings?: ShopOrderSettingsFragment;
|
||||||
onBack: () => void;
|
onBack: () => void;
|
||||||
onSubmit: (data: OrderFulfillSubmitData) => SubmitPromise;
|
onSubmit: (data: OrderFulfillSubmitData) => SubmitPromise;
|
||||||
}
|
}
|
||||||
|
|
||||||
const initialFormData: OrderFulfillFormData = {
|
const initialFormData: OrderFulfillFormData = {
|
||||||
sendInfo: true
|
sendInfo: true,
|
||||||
|
trackingNumber: "",
|
||||||
|
allowStockToBeExceeded: false
|
||||||
};
|
};
|
||||||
|
|
||||||
const OrderFulfillPage: React.FC<OrderFulfillPageProps> = props => {
|
const OrderFulfillPage: React.FC<OrderFulfillPageProps> = props => {
|
||||||
|
@ -138,7 +74,7 @@ const OrderFulfillPage: React.FC<OrderFulfillPageProps> = props => {
|
||||||
errors,
|
errors,
|
||||||
order,
|
order,
|
||||||
saveButtonBar,
|
saveButtonBar,
|
||||||
warehouses,
|
warehouse,
|
||||||
shopSettings,
|
shopSettings,
|
||||||
onBack,
|
onBack,
|
||||||
onSubmit
|
onSubmit
|
||||||
|
@ -151,28 +87,50 @@ const OrderFulfillPage: React.FC<OrderFulfillPageProps> = props => {
|
||||||
null,
|
null,
|
||||||
OrderFulfillStockInput[]
|
OrderFulfillStockInput[]
|
||||||
>(
|
>(
|
||||||
getToFulfillOrderLines(order?.lines).map(line => ({
|
(getToFulfillOrderLines(order?.lines) as OrderFulfillLineFragment[]).map(
|
||||||
data: null,
|
line => ({
|
||||||
id: line.id,
|
data: null,
|
||||||
label: line.variant?.attributes
|
id: line.id,
|
||||||
.map(attribute =>
|
label: getAttributesCaption(line?.variant?.attributes),
|
||||||
attribute.values
|
value: line?.variant?.preorder
|
||||||
.map(attributeValue => attributeValue.name)
|
? null
|
||||||
.join(" , ")
|
: [
|
||||||
)
|
{
|
||||||
.join(" / "),
|
quantity: line.quantityToFulfill,
|
||||||
value: line.variant?.stocks?.map(stock => ({
|
warehouse: warehouse?.id
|
||||||
quantity: 0,
|
}
|
||||||
warehouse: stock.warehouse.id
|
]
|
||||||
}))
|
})
|
||||||
}))
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleSubmit = (formData: OrderFulfillFormData) =>
|
const [
|
||||||
onSubmit({
|
displayStockExceededDialog,
|
||||||
|
setDisplayStockExceededDialog
|
||||||
|
] = React.useState(false);
|
||||||
|
|
||||||
|
const handleSubmit = ({
|
||||||
|
formData,
|
||||||
|
allowStockToBeExceeded
|
||||||
|
}: {
|
||||||
|
formData: OrderFulfillFormData;
|
||||||
|
allowStockToBeExceeded: boolean;
|
||||||
|
}) => {
|
||||||
|
setDisplayStockExceededDialog(false);
|
||||||
|
return onSubmit({
|
||||||
...formData,
|
...formData,
|
||||||
items: formsetData
|
allowStockToBeExceeded,
|
||||||
|
items: formsetData.filter(item => !!item.value)
|
||||||
});
|
});
|
||||||
|
};
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (
|
||||||
|
errors &&
|
||||||
|
errors.every(err => err.code === OrderErrorCode.INSUFFICIENT_STOCK)
|
||||||
|
) {
|
||||||
|
setDisplayStockExceededDialog(true);
|
||||||
|
}
|
||||||
|
}, [errors]);
|
||||||
|
|
||||||
const notAllowedToFulfillUnpaid =
|
const notAllowedToFulfillUnpaid =
|
||||||
shopSettings?.fulfillmentAutoApprove &&
|
shopSettings?.fulfillmentAutoApprove &&
|
||||||
|
@ -188,26 +146,21 @@ const OrderFulfillPage: React.FC<OrderFulfillPageProps> = props => {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const isAtLeastOneFulfilled = formsetData?.some(({ value }) =>
|
const isAtLeastOneFulfilled = formsetData?.some(
|
||||||
value?.some(({ quantity }) => quantity > 0)
|
el => el.value?.[0]?.quantity > 0
|
||||||
);
|
);
|
||||||
|
|
||||||
const areProperlyFulfilled = formsetData?.every(({ id, value }) => {
|
const overfulfill = formsetData
|
||||||
const { lines } = order;
|
.filter(item => !!item?.value) // this can be removed after preorder is dropped
|
||||||
|
.some(item => {
|
||||||
|
const formQuantityFulfilled = item?.value?.[0]?.quantity;
|
||||||
|
const quantityToFulfill = order?.lines?.find(
|
||||||
|
line => line.id === item.id
|
||||||
|
).quantityToFulfill;
|
||||||
|
return formQuantityFulfilled > quantityToFulfill;
|
||||||
|
});
|
||||||
|
|
||||||
const { quantityToFulfill } = lines.find(
|
return !overfulfill && isAtLeastOneFulfilled;
|
||||||
({ id: lineId }) => lineId === id
|
|
||||||
);
|
|
||||||
|
|
||||||
const formQuantityFulfilled = value?.reduce(
|
|
||||||
(result, { quantity }) => result + quantity,
|
|
||||||
0
|
|
||||||
);
|
|
||||||
|
|
||||||
return formQuantityFulfilled <= quantityToFulfill;
|
|
||||||
});
|
|
||||||
|
|
||||||
return isAtLeastOneFulfilled && areProperlyFulfilled;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -224,247 +177,94 @@ const OrderFulfillPage: React.FC<OrderFulfillPageProps> = props => {
|
||||||
orderNumber: order?.number
|
orderNumber: order?.number
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
<Form confirmLeave initial={initialFormData} onSubmit={handleSubmit}>
|
<Typography className={classes.warehouseLabel}>
|
||||||
|
<FormattedMessage
|
||||||
|
{...messages.fulfillingFrom}
|
||||||
|
values={{ warehouseName: warehouse?.name }}
|
||||||
|
/>
|
||||||
|
</Typography>
|
||||||
|
<Form
|
||||||
|
confirmLeave
|
||||||
|
initial={initialFormData}
|
||||||
|
onSubmit={formData =>
|
||||||
|
handleSubmit({
|
||||||
|
formData,
|
||||||
|
allowStockToBeExceeded: displayStockExceededDialog
|
||||||
|
})
|
||||||
|
}
|
||||||
|
>
|
||||||
{({ change, data, submit }) => (
|
{({ change, data, submit }) => (
|
||||||
<>
|
<>
|
||||||
<Card>
|
<Grid>
|
||||||
<CardTitle
|
<Card>
|
||||||
title={intl.formatMessage(messages.itemsReadyToShip)}
|
<CardTitle
|
||||||
/>
|
title={intl.formatMessage(messages.itemsReadyToShip)}
|
||||||
<ResponsiveTable className={classes.table}>
|
/>
|
||||||
<TableHead>
|
{warehouse ? (
|
||||||
<TableRow>
|
<ResponsiveTable className={classes.table}>
|
||||||
<TableCell className={classes.colName}>
|
<TableHead>
|
||||||
<FormattedMessage {...messages.productName} />
|
<TableRow>
|
||||||
</TableCell>
|
<TableCell className={classes.colName}>
|
||||||
<TableCell className={classes.colSku}>
|
<FormattedMessage {...messages.productName} />
|
||||||
<FormattedMessage {...messages.sku} />
|
</TableCell>
|
||||||
</TableCell>
|
<TableCell className={classes.colSku}>
|
||||||
{warehouses?.map(warehouse => (
|
<FormattedMessage {...messages.sku} />
|
||||||
<TableCell
|
</TableCell>
|
||||||
key={warehouse.id}
|
<TableCell
|
||||||
className={classNames(
|
className={classNames(
|
||||||
classes.colQuantity,
|
classes.colQuantity,
|
||||||
classes.colQuantityHeader
|
classes.colQuantityHeader
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{warehouse.name}
|
<FormattedMessage {...messages.quantity} />
|
||||||
</TableCell>
|
</TableCell>
|
||||||
))}
|
<TableCell className={classes.colStock}>
|
||||||
<TableCell className={classes.colQuantityTotal}>
|
<FormattedMessage {...messages.stock} />
|
||||||
<FormattedMessage {...messages.quantityToFulfill} />
|
</TableCell>
|
||||||
</TableCell>
|
</TableRow>
|
||||||
</TableRow>
|
</TableHead>
|
||||||
</TableHead>
|
<TableBody>
|
||||||
<TableBody>
|
{renderCollection(
|
||||||
{renderCollection(
|
getToFulfillOrderLines(order?.lines),
|
||||||
getToFulfillOrderLines(order?.lines),
|
(line: OrderFulfillLineFragment, lineIndex) => (
|
||||||
(
|
<OrderFulfillLine
|
||||||
line: OrderFulfillDataQuery["order"]["lines"][0],
|
line={line}
|
||||||
lineIndex
|
lineIndex={lineIndex}
|
||||||
) => {
|
warehouseId={warehouse?.id}
|
||||||
if (!line) {
|
formsetData={formsetData}
|
||||||
return (
|
formsetChange={formsetChange}
|
||||||
<TableRow key={lineIndex}>
|
/>
|
||||||
<TableCellAvatar className={classes.colName}>
|
)
|
||||||
<Skeleton />
|
)}
|
||||||
</TableCellAvatar>
|
</TableBody>
|
||||||
<TableCell className={classes.colSku}>
|
</ResponsiveTable>
|
||||||
<Skeleton />
|
) : (
|
||||||
</TableCell>
|
<Skeleton />
|
||||||
{warehouses?.map(warehouse => (
|
)}
|
||||||
<TableCell
|
</Card>
|
||||||
className={classes.colQuantity}
|
|
||||||
key={warehouse.id}
|
|
||||||
>
|
|
||||||
<Skeleton />
|
|
||||||
</TableCell>
|
|
||||||
))}
|
|
||||||
<TableCell className={classes.colQuantityTotal}>
|
|
||||||
{" "}
|
|
||||||
<Skeleton />
|
|
||||||
</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const remainingQuantity = line.quantityToFulfill;
|
<Card className={classes.shipmentInformationCard}>
|
||||||
const quantityToFulfill = formsetData[
|
<Typography className={classes.supportHeader}>
|
||||||
lineIndex
|
<FormattedMessage {...messages.shipmentInformation} />
|
||||||
].value?.reduce(
|
</Typography>
|
||||||
(quantityToFulfill, lineInput) =>
|
<TextField
|
||||||
quantityToFulfill + (lineInput.quantity || 0),
|
value={data.trackingNumber}
|
||||||
0
|
name="trackingNumber"
|
||||||
);
|
label={intl.formatMessage(messages.trackingNumber)}
|
||||||
const overfulfill = remainingQuantity < quantityToFulfill;
|
fullWidth
|
||||||
const isPreorder = !!line.variant?.preorder;
|
onChange={change}
|
||||||
|
/>
|
||||||
return (
|
{shopSettings?.fulfillmentAutoApprove && (
|
||||||
<TableRow key={line.id}>
|
|
||||||
<TableCellAvatar
|
|
||||||
className={classes.colName}
|
|
||||||
thumbnail={line?.thumbnail?.url}
|
|
||||||
>
|
|
||||||
{line.productName}
|
|
||||||
<Typography color="textSecondary" variant="caption">
|
|
||||||
{line.variant?.attributes
|
|
||||||
?.map(attribute =>
|
|
||||||
attribute.values
|
|
||||||
.map(attributeValue => attributeValue.name)
|
|
||||||
.join(", ")
|
|
||||||
)
|
|
||||||
?.join(" / ")}
|
|
||||||
</Typography>
|
|
||||||
</TableCellAvatar>
|
|
||||||
<TableCell className={classes.colSku}>
|
|
||||||
{line.variant?.sku}
|
|
||||||
</TableCell>
|
|
||||||
{warehouses?.map(warehouse => {
|
|
||||||
if (isPreorder) {
|
|
||||||
return (
|
|
||||||
<TableCell
|
|
||||||
key="skeleton"
|
|
||||||
className={classNames(
|
|
||||||
classes.colQuantity,
|
|
||||||
classes.error
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const warehouseStock = line.variant?.stocks?.find(
|
|
||||||
stock => stock.warehouse.id === warehouse.id
|
|
||||||
);
|
|
||||||
const formsetStock = formsetData[
|
|
||||||
lineIndex
|
|
||||||
].value.find(
|
|
||||||
line => line.warehouse === warehouse.id
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!warehouseStock) {
|
|
||||||
return (
|
|
||||||
<TableCell
|
|
||||||
key="skeleton"
|
|
||||||
className={classNames(
|
|
||||||
classes.colQuantity,
|
|
||||||
classes.error
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<FormattedMessage {...messages.noStock} />
|
|
||||||
</TableCell>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const warehouseAllocation = line.allocations.find(
|
|
||||||
allocation =>
|
|
||||||
allocation.warehouse.id === warehouse.id
|
|
||||||
);
|
|
||||||
const allocatedQuantityForLine =
|
|
||||||
warehouseAllocation?.quantity || 0;
|
|
||||||
const availableQuantity =
|
|
||||||
warehouseStock.quantity -
|
|
||||||
warehouseStock.quantityAllocated +
|
|
||||||
allocatedQuantityForLine;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<TableCell
|
|
||||||
className={classes.colQuantity}
|
|
||||||
key={warehouseStock.id}
|
|
||||||
>
|
|
||||||
<TextField
|
|
||||||
type="number"
|
|
||||||
inputProps={{
|
|
||||||
className: classNames(
|
|
||||||
classes.quantityInnerInput,
|
|
||||||
{
|
|
||||||
[classes.quantityInnerInputNoRemaining]: !line
|
|
||||||
.variant.trackInventory
|
|
||||||
}
|
|
||||||
),
|
|
||||||
max: (
|
|
||||||
line.variant.trackInventory &&
|
|
||||||
availableQuantity
|
|
||||||
).toString(),
|
|
||||||
min: 0,
|
|
||||||
style: { textAlign: "right" }
|
|
||||||
}}
|
|
||||||
fullWidth
|
|
||||||
value={formsetStock.quantity}
|
|
||||||
onChange={event =>
|
|
||||||
formsetChange(
|
|
||||||
line.id,
|
|
||||||
update(
|
|
||||||
{
|
|
||||||
quantity: parseInt(
|
|
||||||
event.target.value,
|
|
||||||
10
|
|
||||||
),
|
|
||||||
warehouse: warehouse.id
|
|
||||||
},
|
|
||||||
formsetData[lineIndex].value,
|
|
||||||
(a, b) => a.warehouse === b.warehouse
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
error={isStockError(
|
|
||||||
overfulfill,
|
|
||||||
formsetStock,
|
|
||||||
availableQuantity,
|
|
||||||
warehouse,
|
|
||||||
line,
|
|
||||||
errors
|
|
||||||
)}
|
|
||||||
InputProps={{
|
|
||||||
endAdornment: line.variant
|
|
||||||
.trackInventory && (
|
|
||||||
<div
|
|
||||||
className={classes.remainingQuantity}
|
|
||||||
>
|
|
||||||
/ {availableQuantity}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</TableCell>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
|
|
||||||
<TableCell
|
|
||||||
className={classes.colQuantityTotal}
|
|
||||||
key="total"
|
|
||||||
>
|
|
||||||
{!isPreorder && (
|
|
||||||
<>
|
|
||||||
<span
|
|
||||||
className={classNames({
|
|
||||||
[classes.error]: overfulfill,
|
|
||||||
[classes.full]:
|
|
||||||
remainingQuantity <= quantityToFulfill
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
{quantityToFulfill}
|
|
||||||
</span>{" "}
|
|
||||||
/ {remainingQuantity}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
)}
|
|
||||||
</TableBody>
|
|
||||||
</ResponsiveTable>
|
|
||||||
{shopSettings?.fulfillmentAutoApprove && (
|
|
||||||
<CardActions className={classes.actionBar}>
|
|
||||||
<ControlledCheckbox
|
<ControlledCheckbox
|
||||||
checked={data.sendInfo}
|
checked={data.sendInfo}
|
||||||
label={intl.formatMessage(messages.sentShipmentDetails)}
|
label={intl.formatMessage(messages.sentShipmentDetails)}
|
||||||
name="sendInfo"
|
name="sendInfo"
|
||||||
onChange={change}
|
onChange={change}
|
||||||
/>
|
/>
|
||||||
</CardActions>
|
)}
|
||||||
)}
|
</Card>
|
||||||
</Card>
|
</Grid>
|
||||||
|
|
||||||
<Savebar
|
<Savebar
|
||||||
disabled={!shouldEnableSave()}
|
disabled={!shouldEnableSave()}
|
||||||
labels={{
|
labels={{
|
||||||
|
@ -481,6 +281,15 @@ const OrderFulfillPage: React.FC<OrderFulfillPageProps> = props => {
|
||||||
onSubmit={submit}
|
onSubmit={submit}
|
||||||
onCancel={onBack}
|
onCancel={onBack}
|
||||||
/>
|
/>
|
||||||
|
<OrderFulfillStockExceededDialog
|
||||||
|
open={displayStockExceededDialog}
|
||||||
|
lines={order?.lines}
|
||||||
|
formsetData={formsetData}
|
||||||
|
warehouseId={warehouse?.id}
|
||||||
|
confirmButtonState={saveButtonBar}
|
||||||
|
onSubmit={submit}
|
||||||
|
onClose={() => setDisplayStockExceededDialog(false)}
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</Form>
|
</Form>
|
||||||
|
|
|
@ -37,6 +37,14 @@ export const messages = defineMessages({
|
||||||
defaultMessage: "Quantity to fulfill",
|
defaultMessage: "Quantity to fulfill",
|
||||||
description: "quantity of fulfilled products"
|
description: "quantity of fulfilled products"
|
||||||
},
|
},
|
||||||
|
quantity: {
|
||||||
|
defaultMessage: "Quantity",
|
||||||
|
description: "Header row quantity label"
|
||||||
|
},
|
||||||
|
stock: {
|
||||||
|
defaultMessage: "Stock",
|
||||||
|
description: "Header row stock label"
|
||||||
|
},
|
||||||
noStock: {
|
noStock: {
|
||||||
defaultMessage: "No Stock",
|
defaultMessage: "No Stock",
|
||||||
description: "no variant stock in warehouse"
|
description: "no variant stock in warehouse"
|
||||||
|
@ -44,5 +52,17 @@ export const messages = defineMessages({
|
||||||
sentShipmentDetails: {
|
sentShipmentDetails: {
|
||||||
defaultMessage: "Send shipment details to customer",
|
defaultMessage: "Send shipment details to customer",
|
||||||
description: "checkbox label"
|
description: "checkbox label"
|
||||||
|
},
|
||||||
|
shipmentInformation: {
|
||||||
|
defaultMessage: "Shipment information",
|
||||||
|
description: "Shipment information card header"
|
||||||
|
},
|
||||||
|
trackingNumber: {
|
||||||
|
defaultMessage: "Tracking number",
|
||||||
|
description: "Tracking number input label"
|
||||||
|
},
|
||||||
|
fulfillingFrom: {
|
||||||
|
defaultMessage: "Fulfilling from {warehouseName}",
|
||||||
|
description: "Support text under page header"
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
49
src/orders/components/OrderFulfillPage/styles.ts
Normal file
49
src/orders/components/OrderFulfillPage/styles.ts
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
import { makeStyles } from "@saleor/macaw-ui";
|
||||||
|
|
||||||
|
export const useStyles = makeStyles(
|
||||||
|
theme => ({
|
||||||
|
colQuantityHeader: {
|
||||||
|
textAlign: "right"
|
||||||
|
},
|
||||||
|
colStock: {
|
||||||
|
textAlign: "right",
|
||||||
|
width: 180
|
||||||
|
},
|
||||||
|
colName: {
|
||||||
|
width: 250
|
||||||
|
},
|
||||||
|
colQuantity: {
|
||||||
|
textAlign: "right",
|
||||||
|
width: 210
|
||||||
|
},
|
||||||
|
colSku: {
|
||||||
|
textAlign: "right",
|
||||||
|
textOverflow: "ellipsis",
|
||||||
|
width: 150
|
||||||
|
},
|
||||||
|
table: {
|
||||||
|
"&&": {
|
||||||
|
tableLayout: "fixed"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
shipmentInformationCard: {
|
||||||
|
padding: theme.spacing(3),
|
||||||
|
alignSelf: "start",
|
||||||
|
display: "grid",
|
||||||
|
gridRowGap: theme.spacing(1)
|
||||||
|
},
|
||||||
|
supportHeader: {
|
||||||
|
textTransform: "uppercase",
|
||||||
|
color: theme.palette.saleor.main[3],
|
||||||
|
fontWeight: 500,
|
||||||
|
letterSpacing: "0.1em",
|
||||||
|
fontSize: "12px",
|
||||||
|
lineHeight: "160%",
|
||||||
|
marginBottom: theme.spacing(2)
|
||||||
|
},
|
||||||
|
warehouseLabel: {
|
||||||
|
marginBottom: theme.spacing(4)
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
{ name: "OrderFulfillPage" }
|
||||||
|
);
|
|
@ -0,0 +1,113 @@
|
||||||
|
import {
|
||||||
|
TableBody,
|
||||||
|
TableCell,
|
||||||
|
TableHead,
|
||||||
|
TableRow,
|
||||||
|
Typography
|
||||||
|
} from "@material-ui/core";
|
||||||
|
import ActionDialog from "@saleor/components/ActionDialog";
|
||||||
|
import { CardSpacer } from "@saleor/components/CardSpacer";
|
||||||
|
import ResponsiveTable from "@saleor/components/ResponsiveTable";
|
||||||
|
import { FulfillmentFragment, OrderFulfillLineFragment } from "@saleor/graphql";
|
||||||
|
import { ConfirmButtonTransitionState } from "@saleor/macaw-ui";
|
||||||
|
import { renderCollection } from "@saleor/misc";
|
||||||
|
import {
|
||||||
|
getFulfillmentFormsetQuantity,
|
||||||
|
getOrderLineAvailableQuantity,
|
||||||
|
OrderFulfillStockInputFormsetData
|
||||||
|
} from "@saleor/orders/utils/data";
|
||||||
|
import React from "react";
|
||||||
|
import { useIntl } from "react-intl";
|
||||||
|
|
||||||
|
import OrderFulfillStockExceededDialogLine from "../OrderFulfillStockExceededDialogLine";
|
||||||
|
import { stockExceededDialogMessages as messages } from "./messages";
|
||||||
|
import { useStyles } from "./styles";
|
||||||
|
|
||||||
|
export interface OrderFulfillStockExceededDialogProps {
|
||||||
|
lines: Array<FulfillmentFragment["lines"][0] | OrderFulfillLineFragment>;
|
||||||
|
open: boolean;
|
||||||
|
formsetData: OrderFulfillStockInputFormsetData;
|
||||||
|
warehouseId: string;
|
||||||
|
confirmButtonState: ConfirmButtonTransitionState;
|
||||||
|
onSubmit();
|
||||||
|
onClose();
|
||||||
|
}
|
||||||
|
|
||||||
|
const OrderFulfillStockExceededDialog: React.FC<OrderFulfillStockExceededDialogProps> = props => {
|
||||||
|
const {
|
||||||
|
lines,
|
||||||
|
open,
|
||||||
|
formsetData,
|
||||||
|
warehouseId,
|
||||||
|
confirmButtonState,
|
||||||
|
onClose,
|
||||||
|
onSubmit
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
const intl = useIntl();
|
||||||
|
const classes = useStyles(props);
|
||||||
|
|
||||||
|
const exceededLines = lines?.filter(el => {
|
||||||
|
const line = "orderLine" in el ? el.orderLine : el;
|
||||||
|
const stock = line.variant?.stocks.find(
|
||||||
|
stock => stock.warehouse.id === warehouseId
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
getFulfillmentFormsetQuantity(formsetData, line) >
|
||||||
|
getOrderLineAvailableQuantity(line, stock)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<ActionDialog
|
||||||
|
open={open}
|
||||||
|
title={intl.formatMessage(messages.title)}
|
||||||
|
onConfirm={onSubmit}
|
||||||
|
onClose={onClose}
|
||||||
|
confirmButtonState={confirmButtonState}
|
||||||
|
maxWidth="sm"
|
||||||
|
confirmButtonLabel={intl.formatMessage(messages.fulfillButton)}
|
||||||
|
>
|
||||||
|
<Typography>{intl.formatMessage(messages.infoLabel)}</Typography>
|
||||||
|
<CardSpacer />
|
||||||
|
<div className={classes.scrollable}>
|
||||||
|
<ResponsiveTable className={classes.table}>
|
||||||
|
{!!lines?.length && (
|
||||||
|
<TableHead>
|
||||||
|
<TableRow>
|
||||||
|
<TableCell className={classes.colName}>
|
||||||
|
{intl.formatMessage(messages.productLabel)}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell className={classes.colQuantity}>
|
||||||
|
{intl.formatMessage(messages.requiredStockLabel)}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell className={classes.colWarehouseStock}>
|
||||||
|
{intl.formatMessage(messages.warehouseStockLabel)}
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
</TableHead>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<TableBody>
|
||||||
|
{renderCollection(exceededLines, line => (
|
||||||
|
<OrderFulfillStockExceededDialogLine
|
||||||
|
key={line?.id}
|
||||||
|
line={line}
|
||||||
|
formsetData={formsetData}
|
||||||
|
warehouseId={warehouseId}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</TableBody>
|
||||||
|
</ResponsiveTable>
|
||||||
|
</div>
|
||||||
|
<CardSpacer />
|
||||||
|
<Typography>{intl.formatMessage(messages.questionLabel)}</Typography>
|
||||||
|
</ActionDialog>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
OrderFulfillStockExceededDialog.displayName = "OrderFulfillStockExceededDialog";
|
||||||
|
export default OrderFulfillStockExceededDialog;
|
|
@ -0,0 +1,2 @@
|
||||||
|
export { default } from "./OrderFulfillStockExceededDialog";
|
||||||
|
export * from "./OrderFulfillStockExceededDialog";
|
|
@ -0,0 +1,41 @@
|
||||||
|
import { defineMessages } from "react-intl";
|
||||||
|
|
||||||
|
export const stockExceededDialogMessages = defineMessages({
|
||||||
|
title: {
|
||||||
|
defaultMessage: "Not enough stock",
|
||||||
|
description: "stock exceeded dialog title"
|
||||||
|
},
|
||||||
|
infoLabel: {
|
||||||
|
defaultMessage:
|
||||||
|
"Stock for items shown below are not enough to prepare fulfillment:",
|
||||||
|
description: "stock exceeded dialog description"
|
||||||
|
},
|
||||||
|
questionLabel: {
|
||||||
|
defaultMessage: "Are you sure you want to fulfill those products anyway?",
|
||||||
|
description: "stock exceeded action question label"
|
||||||
|
},
|
||||||
|
cancelButton: {
|
||||||
|
defaultMessage: "Cancel",
|
||||||
|
description: "cancel button label"
|
||||||
|
},
|
||||||
|
fulfillButton: {
|
||||||
|
defaultMessage: "Fulfill anyway",
|
||||||
|
description: "fulfill button label"
|
||||||
|
},
|
||||||
|
productLabel: {
|
||||||
|
defaultMessage: "Product",
|
||||||
|
description: "table header product label"
|
||||||
|
},
|
||||||
|
requiredStockLabel: {
|
||||||
|
defaultMessage: "Required",
|
||||||
|
description: "table header required stock label"
|
||||||
|
},
|
||||||
|
availableStockLabel: {
|
||||||
|
defaultMessage: "Available",
|
||||||
|
description: "table header available stock label"
|
||||||
|
},
|
||||||
|
warehouseStockLabel: {
|
||||||
|
defaultMessage: "Warehouse stock",
|
||||||
|
description: "table header warehouse stock label"
|
||||||
|
}
|
||||||
|
});
|
|
@ -0,0 +1,31 @@
|
||||||
|
import { makeStyles } from "@saleor/macaw-ui";
|
||||||
|
|
||||||
|
export const useStyles = makeStyles(
|
||||||
|
theme => ({
|
||||||
|
colName: {
|
||||||
|
width: "auto",
|
||||||
|
margin: "0px"
|
||||||
|
},
|
||||||
|
colQuantity: {
|
||||||
|
textAlign: "right",
|
||||||
|
width: 100,
|
||||||
|
padding: "4px 4px"
|
||||||
|
},
|
||||||
|
colWarehouseStock: {
|
||||||
|
textAlign: "right",
|
||||||
|
width: 150,
|
||||||
|
padding: "4px 24px"
|
||||||
|
},
|
||||||
|
table: {
|
||||||
|
tableLayout: "fixed"
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
margin: theme.spacing(2)
|
||||||
|
},
|
||||||
|
scrollable: {
|
||||||
|
maxHeight: 450,
|
||||||
|
overflow: "scroll"
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
{ name: "OrderFulfillStockExceededDialog" }
|
||||||
|
);
|
|
@ -0,0 +1,59 @@
|
||||||
|
import { TableCell, TableRow, Typography } from "@material-ui/core";
|
||||||
|
import TableCellAvatar from "@saleor/components/TableCellAvatar";
|
||||||
|
import { FulfillmentFragment, OrderFulfillLineFragment } from "@saleor/graphql";
|
||||||
|
import {
|
||||||
|
getAttributesCaption,
|
||||||
|
getFulfillmentFormsetQuantity,
|
||||||
|
getOrderLineAvailableQuantity,
|
||||||
|
OrderFulfillStockInputFormsetData
|
||||||
|
} from "@saleor/orders/utils/data";
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
import { useStyles } from "../OrderFulfillStockExceededDialog/styles";
|
||||||
|
|
||||||
|
export interface OrderFulfillStockExceededDialogLineProps {
|
||||||
|
line: OrderFulfillLineFragment | FulfillmentFragment["lines"][0];
|
||||||
|
warehouseId: string;
|
||||||
|
formsetData: OrderFulfillStockInputFormsetData;
|
||||||
|
}
|
||||||
|
|
||||||
|
const OrderFulfillStockExceededDialogLine: React.FC<OrderFulfillStockExceededDialogLineProps> = props => {
|
||||||
|
const { line: genericLine, warehouseId, formsetData } = props;
|
||||||
|
|
||||||
|
if (!genericLine) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const line = "orderLine" in genericLine ? genericLine.orderLine : genericLine;
|
||||||
|
const classes = useStyles(props);
|
||||||
|
|
||||||
|
const stock = line?.variant?.stocks.find(
|
||||||
|
stock => stock.warehouse.id === warehouseId
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TableRow key={line?.id}>
|
||||||
|
<TableCellAvatar
|
||||||
|
className={classes.colName}
|
||||||
|
thumbnail={line?.thumbnail?.url}
|
||||||
|
>
|
||||||
|
{line?.productName}
|
||||||
|
{"attributes" in line.variant && (
|
||||||
|
<Typography color="textSecondary" variant="caption">
|
||||||
|
{getAttributesCaption(line.variant?.attributes)}
|
||||||
|
</Typography>
|
||||||
|
)}
|
||||||
|
</TableCellAvatar>
|
||||||
|
<TableCell className={classes.colQuantity}>
|
||||||
|
{getFulfillmentFormsetQuantity(formsetData, line)}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell className={classes.colWarehouseStock}>
|
||||||
|
{getOrderLineAvailableQuantity(line, stock)}
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
OrderFulfillStockExceededDialogLine.displayName =
|
||||||
|
"OrderFulfillStockExceededDialogLine";
|
||||||
|
export default OrderFulfillStockExceededDialogLine;
|
|
@ -0,0 +1,2 @@
|
||||||
|
export { default } from "./OrderFulfillStockExceededDialogLine";
|
||||||
|
export * from "./OrderFulfillStockExceededDialogLine";
|
|
@ -8,9 +8,9 @@ import { mergeRepeatedOrderLines } from "@saleor/orders/utils/data";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
import { renderCollection } from "../../../misc";
|
import { renderCollection } from "../../../misc";
|
||||||
|
import OrderCardTitle from "../OrderCardTitle";
|
||||||
import TableHeader from "../OrderProductsCardElements/OrderProductsCardHeader";
|
import TableHeader from "../OrderProductsCardElements/OrderProductsCardHeader";
|
||||||
import TableLine from "../OrderProductsCardElements/OrderProductsTableRow";
|
import TableLine from "../OrderProductsCardElements/OrderProductsTableRow";
|
||||||
import CardTitle from "../OrderReturnPage/OrderReturnRefundItemsCard/CardTitle";
|
|
||||||
import ActionButtons from "./ActionButtons";
|
import ActionButtons from "./ActionButtons";
|
||||||
import ExtraInfoLines from "./ExtraInfoLines";
|
import ExtraInfoLines from "./ExtraInfoLines";
|
||||||
import useStyles from "./styles";
|
import useStyles from "./styles";
|
||||||
|
@ -63,7 +63,7 @@ const OrderFulfilledProductsCard: React.FC<OrderFulfilledProductsCardProps> = pr
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Card>
|
<Card>
|
||||||
<CardTitle
|
<OrderCardTitle
|
||||||
withStatus
|
withStatus
|
||||||
lines={fulfillment?.lines}
|
lines={fulfillment?.lines}
|
||||||
fulfillmentOrder={fulfillment?.fulfillmentOrder}
|
fulfillmentOrder={fulfillment?.fulfillmentOrder}
|
||||||
|
@ -87,7 +87,7 @@ const OrderFulfilledProductsCard: React.FC<OrderFulfilledProductsCardProps> = pr
|
||||||
<TableHeader />
|
<TableHeader />
|
||||||
<TableBody>
|
<TableBody>
|
||||||
{renderCollection(getLines(), line => (
|
{renderCollection(getLines(), line => (
|
||||||
<TableLine line={line} />
|
<TableLine key={line.id} line={line} />
|
||||||
))}
|
))}
|
||||||
</TableBody>
|
</TableBody>
|
||||||
<ExtraInfoLines fulfillment={fulfillment} />
|
<ExtraInfoLines fulfillment={fulfillment} />
|
||||||
|
|
|
@ -22,9 +22,9 @@ import { renderCollection } from "@saleor/misc";
|
||||||
import React, { CSSProperties } from "react";
|
import React, { CSSProperties } from "react";
|
||||||
import { defineMessages, FormattedMessage, useIntl } from "react-intl";
|
import { defineMessages, FormattedMessage, useIntl } from "react-intl";
|
||||||
|
|
||||||
|
import OrderCardTitle from "../../OrderCardTitle";
|
||||||
import { FormsetQuantityData, FormsetReplacementData } from "../form";
|
import { FormsetQuantityData, FormsetReplacementData } from "../form";
|
||||||
import { getById } from "../utils";
|
import { getById } from "../utils";
|
||||||
import CardTitle from "./CardTitle";
|
|
||||||
import MaximalButton from "./MaximalButton";
|
import MaximalButton from "./MaximalButton";
|
||||||
import ProductErrorCell from "./ProductErrorCell";
|
import ProductErrorCell from "./ProductErrorCell";
|
||||||
|
|
||||||
|
@ -120,7 +120,7 @@ const ItemsCard: React.FC<OrderReturnRefundLinesCardProps> = ({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card>
|
<Card>
|
||||||
<CardTitle
|
<OrderCardTitle
|
||||||
orderNumber={order?.number}
|
orderNumber={order?.number}
|
||||||
lines={lines}
|
lines={lines}
|
||||||
fulfillmentOrder={fulfillment?.fulfillmentOrder}
|
fulfillmentOrder={fulfillment?.fulfillmentOrder}
|
||||||
|
|
|
@ -1,16 +1,17 @@
|
||||||
import { Card, CardActions, TableBody, Typography } from "@material-ui/core";
|
import { Card, CardActions, TableBody, Typography } from "@material-ui/core";
|
||||||
import CardSpacer from "@saleor/components/CardSpacer";
|
import CardSpacer from "@saleor/components/CardSpacer";
|
||||||
import ResponsiveTable from "@saleor/components/ResponsiveTable";
|
import ResponsiveTable from "@saleor/components/ResponsiveTable";
|
||||||
import { OrderLineFragment } from "@saleor/graphql";
|
import Skeleton from "@saleor/components/Skeleton";
|
||||||
|
import { OrderLineFragment, WarehouseFragment } from "@saleor/graphql";
|
||||||
import { commonMessages } from "@saleor/intl";
|
import { commonMessages } from "@saleor/intl";
|
||||||
import { Button, makeStyles } from "@saleor/macaw-ui";
|
import { Button, ChevronIcon, makeStyles } from "@saleor/macaw-ui";
|
||||||
import { renderCollection } from "@saleor/misc";
|
import { renderCollection } from "@saleor/misc";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { FormattedMessage } from "react-intl";
|
import { FormattedMessage } from "react-intl";
|
||||||
|
|
||||||
|
import OrderCardTitle from "../OrderCardTitle";
|
||||||
import TableHeader from "../OrderProductsCardElements/OrderProductsCardHeader";
|
import TableHeader from "../OrderProductsCardElements/OrderProductsCardHeader";
|
||||||
import TableLine from "../OrderProductsCardElements/OrderProductsTableRow";
|
import TableLine from "../OrderProductsCardElements/OrderProductsTableRow";
|
||||||
import CardTitle from "../OrderReturnPage/OrderReturnRefundItemsCard/CardTitle";
|
|
||||||
|
|
||||||
const useStyles = makeStyles(
|
const useStyles = makeStyles(
|
||||||
theme => ({
|
theme => ({
|
||||||
|
@ -26,6 +27,41 @@ const useStyles = makeStyles(
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
tableLayout: "fixed"
|
tableLayout: "fixed"
|
||||||
|
},
|
||||||
|
toolbar: {
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "center",
|
||||||
|
alignItems: "center",
|
||||||
|
cursor: "pointer",
|
||||||
|
borderRadius: "4px",
|
||||||
|
paddingTop: theme.spacing(1),
|
||||||
|
paddingBottom: theme.spacing(1),
|
||||||
|
paddingRight: theme.spacing(0.5),
|
||||||
|
paddingLeft: theme.spacing(1.5),
|
||||||
|
"&:hover": {
|
||||||
|
backgroundColor: theme.palette.saleor.active[5],
|
||||||
|
color: theme.palette.saleor.active[1]
|
||||||
|
},
|
||||||
|
"& > div": {
|
||||||
|
minWidth: 0,
|
||||||
|
whiteSpace: "nowrap",
|
||||||
|
overflow: "hidden",
|
||||||
|
textOverflow: "ellipsis"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
cardTitle: {
|
||||||
|
justifyContent: "space-between",
|
||||||
|
"& > div": {
|
||||||
|
"&:first-child": {
|
||||||
|
flex: 0,
|
||||||
|
whiteSpace: "nowrap"
|
||||||
|
},
|
||||||
|
"&:last-child": {
|
||||||
|
flex: "0 1 auto",
|
||||||
|
minWidth: 0,
|
||||||
|
marginLeft: theme.spacing(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
{ name: "OrderUnfulfilledItems" }
|
{ name: "OrderUnfulfilledItems" }
|
||||||
|
@ -36,6 +72,8 @@ interface OrderUnfulfilledProductsCardProps {
|
||||||
notAllowedToFulfillUnpaid: boolean;
|
notAllowedToFulfillUnpaid: boolean;
|
||||||
lines: OrderLineFragment[];
|
lines: OrderLineFragment[];
|
||||||
onFulfill: () => void;
|
onFulfill: () => void;
|
||||||
|
selectedWarehouse: WarehouseFragment;
|
||||||
|
onWarehouseChange: () => null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const OrderUnfulfilledProductsCard: React.FC<OrderUnfulfilledProductsCardProps> = props => {
|
const OrderUnfulfilledProductsCard: React.FC<OrderUnfulfilledProductsCardProps> = props => {
|
||||||
|
@ -43,7 +81,9 @@ const OrderUnfulfilledProductsCard: React.FC<OrderUnfulfilledProductsCardProps>
|
||||||
showFulfillmentAction,
|
showFulfillmentAction,
|
||||||
notAllowedToFulfillUnpaid,
|
notAllowedToFulfillUnpaid,
|
||||||
lines,
|
lines,
|
||||||
onFulfill
|
onFulfill,
|
||||||
|
selectedWarehouse,
|
||||||
|
onWarehouseChange
|
||||||
} = props;
|
} = props;
|
||||||
const classes = useStyles({});
|
const classes = useStyles({});
|
||||||
|
|
||||||
|
@ -54,19 +94,30 @@ const OrderUnfulfilledProductsCard: React.FC<OrderUnfulfilledProductsCardProps>
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Card>
|
<Card>
|
||||||
<CardTitle withStatus status="unfulfilled" />
|
<OrderCardTitle
|
||||||
|
lines={lines}
|
||||||
|
withStatus
|
||||||
|
status="unfulfilled"
|
||||||
|
className={classes.cardTitle}
|
||||||
|
toolbar={
|
||||||
|
<div className={classes.toolbar} onClick={onWarehouseChange}>
|
||||||
|
<div>{selectedWarehouse?.name ?? <Skeleton />}</div>
|
||||||
|
<ChevronIcon />
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
/>
|
||||||
<ResponsiveTable className={classes.table}>
|
<ResponsiveTable className={classes.table}>
|
||||||
<TableHeader />
|
<TableHeader />
|
||||||
<TableBody>
|
<TableBody>
|
||||||
{renderCollection(lines, line => (
|
{renderCollection(lines, line => (
|
||||||
<TableLine isOrderLine line={line} />
|
<TableLine key={line.id} isOrderLine line={line} />
|
||||||
))}
|
))}
|
||||||
</TableBody>
|
</TableBody>
|
||||||
</ResponsiveTable>
|
</ResponsiveTable>
|
||||||
{showFulfillmentAction && (
|
{showFulfillmentAction && (
|
||||||
<CardActions>
|
<CardActions className={classes.actions}>
|
||||||
<Button
|
<Button
|
||||||
variant="tertiary"
|
variant="primary"
|
||||||
onClick={onFulfill}
|
onClick={onFulfill}
|
||||||
disabled={notAllowedToFulfillUnpaid}
|
disabled={notAllowedToFulfillUnpaid}
|
||||||
>
|
>
|
||||||
|
|
|
@ -14,6 +14,7 @@ import {
|
||||||
PaymentChargeStatusEnum,
|
PaymentChargeStatusEnum,
|
||||||
SearchCustomersQuery,
|
SearchCustomersQuery,
|
||||||
SearchOrderVariantQuery,
|
SearchOrderVariantQuery,
|
||||||
|
SearchWarehousesQuery,
|
||||||
ShopOrderSettingsFragment,
|
ShopOrderSettingsFragment,
|
||||||
WeightUnitsEnum
|
WeightUnitsEnum
|
||||||
} from "@saleor/graphql";
|
} from "@saleor/graphql";
|
||||||
|
@ -1073,6 +1074,18 @@ export const order = (placeholder: string): OrderDetailsFragment => ({
|
||||||
quantity: 2,
|
quantity: 2,
|
||||||
quantityFulfilled: 2,
|
quantityFulfilled: 2,
|
||||||
quantityToFulfill: 0,
|
quantityToFulfill: 0,
|
||||||
|
allocations: [
|
||||||
|
{
|
||||||
|
id: "allocation_test_id",
|
||||||
|
warehouse: {
|
||||||
|
id:
|
||||||
|
"V2FyZWhvdXNlOjk1NWY0ZDk2LWRmNTAtNGY0Zi1hOTM4LWM5MTYzYTA4YTViNg==",
|
||||||
|
__typename: "Warehouse"
|
||||||
|
},
|
||||||
|
quantity: 1,
|
||||||
|
__typename: "Allocation"
|
||||||
|
}
|
||||||
|
],
|
||||||
thumbnail: {
|
thumbnail: {
|
||||||
__typename: "Image" as "Image",
|
__typename: "Image" as "Image",
|
||||||
url: placeholder
|
url: placeholder
|
||||||
|
@ -1116,7 +1129,33 @@ export const order = (placeholder: string): OrderDetailsFragment => ({
|
||||||
__typename: "ProductVariant",
|
__typename: "ProductVariant",
|
||||||
id: "dsfsfuhb",
|
id: "dsfsfuhb",
|
||||||
quantityAvailable: 10,
|
quantityAvailable: 10,
|
||||||
preorder: null
|
preorder: null,
|
||||||
|
stocks: [
|
||||||
|
{
|
||||||
|
id: "stock_test_id1",
|
||||||
|
warehouse: {
|
||||||
|
name: "stock_warehouse1",
|
||||||
|
id:
|
||||||
|
"V2FyZWhvdXNlOjc4OGUyMGRlLTlmYTAtNDI5My1iZDk2LWUwM2RjY2RhMzc0ZQ==",
|
||||||
|
__typename: "Warehouse"
|
||||||
|
},
|
||||||
|
quantity: 166,
|
||||||
|
quantityAllocated: 0,
|
||||||
|
__typename: "Stock"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "stock_test_id2",
|
||||||
|
warehouse: {
|
||||||
|
name: "stock_warehouse2",
|
||||||
|
id:
|
||||||
|
"V2FyZWhvdXNlOjczYzI0OGNmLTliNzAtNDlmMi1hMDRlLTM4ZTYxMmQ5MDYwMQ==",
|
||||||
|
__typename: "Warehouse"
|
||||||
|
},
|
||||||
|
quantity: 166,
|
||||||
|
quantityAllocated: 0,
|
||||||
|
__typename: "Stock"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
quantity: 1
|
quantity: 1
|
||||||
|
@ -1143,6 +1182,18 @@ export const order = (placeholder: string): OrderDetailsFragment => ({
|
||||||
quantity: 2,
|
quantity: 2,
|
||||||
quantityFulfilled: 2,
|
quantityFulfilled: 2,
|
||||||
quantityToFulfill: 0,
|
quantityToFulfill: 0,
|
||||||
|
allocations: [
|
||||||
|
{
|
||||||
|
id: "allocation_test_id",
|
||||||
|
warehouse: {
|
||||||
|
id:
|
||||||
|
"V2FyZWhvdXNlOjk1NWY0ZDk2LWRmNTAtNGY0Zi1hOTM4LWM5MTYzYTA4YTViNg==",
|
||||||
|
__typename: "Warehouse"
|
||||||
|
},
|
||||||
|
quantity: 1,
|
||||||
|
__typename: "Allocation"
|
||||||
|
}
|
||||||
|
],
|
||||||
thumbnail: {
|
thumbnail: {
|
||||||
__typename: "Image" as "Image",
|
__typename: "Image" as "Image",
|
||||||
url: placeholder
|
url: placeholder
|
||||||
|
@ -1186,7 +1237,33 @@ export const order = (placeholder: string): OrderDetailsFragment => ({
|
||||||
__typename: "ProductVariant",
|
__typename: "ProductVariant",
|
||||||
id: "dsfsfuhb",
|
id: "dsfsfuhb",
|
||||||
quantityAvailable: 10,
|
quantityAvailable: 10,
|
||||||
preorder: null
|
preorder: null,
|
||||||
|
stocks: [
|
||||||
|
{
|
||||||
|
id: "stock_test_id1",
|
||||||
|
warehouse: {
|
||||||
|
name: "stock_warehouse1",
|
||||||
|
id:
|
||||||
|
"V2FyZWhvdXNlOjc4OGUyMGRlLTlmYTAtNDI5My1iZDk2LWUwM2RjY2RhMzc0ZQ==",
|
||||||
|
__typename: "Warehouse"
|
||||||
|
},
|
||||||
|
quantity: 166,
|
||||||
|
quantityAllocated: 0,
|
||||||
|
__typename: "Stock"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "stock_test_id2",
|
||||||
|
warehouse: {
|
||||||
|
name: "stock_warehouse2",
|
||||||
|
id:
|
||||||
|
"V2FyZWhvdXNlOjczYzI0OGNmLTliNzAtNDlmMi1hMDRlLTM4ZTYxMmQ5MDYwMQ==",
|
||||||
|
__typename: "Warehouse"
|
||||||
|
},
|
||||||
|
quantity: 166,
|
||||||
|
quantityAllocated: 0,
|
||||||
|
__typename: "Stock"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
quantity: 1
|
quantity: 1
|
||||||
|
@ -1221,6 +1298,18 @@ export const order = (placeholder: string): OrderDetailsFragment => ({
|
||||||
quantity: 3,
|
quantity: 3,
|
||||||
quantityFulfilled: 0,
|
quantityFulfilled: 0,
|
||||||
quantityToFulfill: 3,
|
quantityToFulfill: 3,
|
||||||
|
allocations: [
|
||||||
|
{
|
||||||
|
id: "allocation_test_id",
|
||||||
|
warehouse: {
|
||||||
|
id:
|
||||||
|
"V2FyZWhvdXNlOjk1NWY0ZDk2LWRmNTAtNGY0Zi1hOTM4LWM5MTYzYTA4YTViNg==",
|
||||||
|
__typename: "Warehouse"
|
||||||
|
},
|
||||||
|
quantity: 1,
|
||||||
|
__typename: "Allocation"
|
||||||
|
}
|
||||||
|
],
|
||||||
thumbnail: {
|
thumbnail: {
|
||||||
__typename: "Image" as "Image",
|
__typename: "Image" as "Image",
|
||||||
url: placeholder
|
url: placeholder
|
||||||
|
@ -1264,7 +1353,33 @@ export const order = (placeholder: string): OrderDetailsFragment => ({
|
||||||
__typename: "ProductVariant",
|
__typename: "ProductVariant",
|
||||||
id: "dsfsfuhb",
|
id: "dsfsfuhb",
|
||||||
quantityAvailable: 10,
|
quantityAvailable: 10,
|
||||||
preorder: null
|
preorder: null,
|
||||||
|
stocks: [
|
||||||
|
{
|
||||||
|
id: "stock_test_id1",
|
||||||
|
warehouse: {
|
||||||
|
name: "stock_warehouse1",
|
||||||
|
id:
|
||||||
|
"V2FyZWhvdXNlOjc4OGUyMGRlLTlmYTAtNDI5My1iZDk2LWUwM2RjY2RhMzc0ZQ==",
|
||||||
|
__typename: "Warehouse"
|
||||||
|
},
|
||||||
|
quantity: 166,
|
||||||
|
quantityAllocated: 0,
|
||||||
|
__typename: "Stock"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "stock_test_id2",
|
||||||
|
warehouse: {
|
||||||
|
name: "stock_warehouse2",
|
||||||
|
id:
|
||||||
|
"V2FyZWhvdXNlOjczYzI0OGNmLTliNzAtNDlmMi1hMDRlLTM4ZTYxMmQ5MDYwMQ==",
|
||||||
|
__typename: "Warehouse"
|
||||||
|
},
|
||||||
|
quantity: 166,
|
||||||
|
quantityAllocated: 0,
|
||||||
|
__typename: "Stock"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -1276,6 +1391,18 @@ export const order = (placeholder: string): OrderDetailsFragment => ({
|
||||||
quantity: 2,
|
quantity: 2,
|
||||||
quantityFulfilled: 2,
|
quantityFulfilled: 2,
|
||||||
quantityToFulfill: 0,
|
quantityToFulfill: 0,
|
||||||
|
allocations: [
|
||||||
|
{
|
||||||
|
id: "allocation_test_id",
|
||||||
|
warehouse: {
|
||||||
|
id:
|
||||||
|
"V2FyZWhvdXNlOjk1NWY0ZDk2LWRmNTAtNGY0Zi1hOTM4LWM5MTYzYTA4YTViNg==",
|
||||||
|
__typename: "Warehouse"
|
||||||
|
},
|
||||||
|
quantity: 1,
|
||||||
|
__typename: "Allocation"
|
||||||
|
}
|
||||||
|
],
|
||||||
thumbnail: {
|
thumbnail: {
|
||||||
__typename: "Image" as "Image",
|
__typename: "Image" as "Image",
|
||||||
url: placeholder
|
url: placeholder
|
||||||
|
@ -1320,7 +1447,33 @@ export const order = (placeholder: string): OrderDetailsFragment => ({
|
||||||
__typename: "ProductVariant",
|
__typename: "ProductVariant",
|
||||||
id: "dsfsfuhb",
|
id: "dsfsfuhb",
|
||||||
quantityAvailable: 10,
|
quantityAvailable: 10,
|
||||||
preorder: null
|
preorder: null,
|
||||||
|
stocks: [
|
||||||
|
{
|
||||||
|
id: "stock_test_id1",
|
||||||
|
warehouse: {
|
||||||
|
name: "stock_warehouse1",
|
||||||
|
id:
|
||||||
|
"V2FyZWhvdXNlOjc4OGUyMGRlLTlmYTAtNDI5My1iZDk2LWUwM2RjY2RhMzc0ZQ==",
|
||||||
|
__typename: "Warehouse"
|
||||||
|
},
|
||||||
|
quantity: 166,
|
||||||
|
quantityAllocated: 0,
|
||||||
|
__typename: "Stock"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "stock_test_id2",
|
||||||
|
warehouse: {
|
||||||
|
name: "stock_warehouse2",
|
||||||
|
id:
|
||||||
|
"V2FyZWhvdXNlOjczYzI0OGNmLTliNzAtNDlmMi1hMDRlLTM4ZTYxMmQ5MDYwMQ==",
|
||||||
|
__typename: "Warehouse"
|
||||||
|
},
|
||||||
|
quantity: 166,
|
||||||
|
quantityAllocated: 0,
|
||||||
|
__typename: "Stock"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
@ -1471,6 +1624,18 @@ export const draftOrder = (placeholder: string): OrderDetailsFragment => ({
|
||||||
quantity: 2,
|
quantity: 2,
|
||||||
quantityFulfilled: 0,
|
quantityFulfilled: 0,
|
||||||
quantityToFulfill: 2,
|
quantityToFulfill: 2,
|
||||||
|
allocations: [
|
||||||
|
{
|
||||||
|
id: "allocation_test_id",
|
||||||
|
warehouse: {
|
||||||
|
id:
|
||||||
|
"V2FyZWhvdXNlOjk1NWY0ZDk2LWRmNTAtNGY0Zi1hOTM4LWM5MTYzYTA4YTViNg==",
|
||||||
|
__typename: "Warehouse"
|
||||||
|
},
|
||||||
|
quantity: 1,
|
||||||
|
__typename: "Allocation"
|
||||||
|
}
|
||||||
|
],
|
||||||
thumbnail: {
|
thumbnail: {
|
||||||
__typename: "Image" as "Image",
|
__typename: "Image" as "Image",
|
||||||
url: placeholder
|
url: placeholder
|
||||||
|
@ -1514,7 +1679,33 @@ export const draftOrder = (placeholder: string): OrderDetailsFragment => ({
|
||||||
__typename: "ProductVariant",
|
__typename: "ProductVariant",
|
||||||
id: "dsfsfuhb",
|
id: "dsfsfuhb",
|
||||||
quantityAvailable: 10,
|
quantityAvailable: 10,
|
||||||
preorder: null
|
preorder: null,
|
||||||
|
stocks: [
|
||||||
|
{
|
||||||
|
id: "stock_test_id1",
|
||||||
|
warehouse: {
|
||||||
|
name: "stock_warehouse1",
|
||||||
|
id:
|
||||||
|
"V2FyZWhvdXNlOjc4OGUyMGRlLTlmYTAtNDI5My1iZDk2LWUwM2RjY2RhMzc0ZQ==",
|
||||||
|
__typename: "Warehouse"
|
||||||
|
},
|
||||||
|
quantity: 166,
|
||||||
|
quantityAllocated: 0,
|
||||||
|
__typename: "Stock"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "stock_test_id2",
|
||||||
|
warehouse: {
|
||||||
|
name: "stock_warehouse2",
|
||||||
|
id:
|
||||||
|
"V2FyZWhvdXNlOjczYzI0OGNmLTliNzAtNDlmMi1hMDRlLTM4ZTYxMmQ5MDYwMQ==",
|
||||||
|
__typename: "Warehouse"
|
||||||
|
},
|
||||||
|
quantity: 166,
|
||||||
|
quantityAllocated: 0,
|
||||||
|
__typename: "Stock"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -1526,6 +1717,18 @@ export const draftOrder = (placeholder: string): OrderDetailsFragment => ({
|
||||||
quantity: 2,
|
quantity: 2,
|
||||||
quantityFulfilled: 0,
|
quantityFulfilled: 0,
|
||||||
quantityToFulfill: 2,
|
quantityToFulfill: 2,
|
||||||
|
allocations: [
|
||||||
|
{
|
||||||
|
id: "allocation_test_id",
|
||||||
|
warehouse: {
|
||||||
|
id:
|
||||||
|
"V2FyZWhvdXNlOjk1NWY0ZDk2LWRmNTAtNGY0Zi1hOTM4LWM5MTYzYTA4YTViNg==",
|
||||||
|
__typename: "Warehouse"
|
||||||
|
},
|
||||||
|
quantity: 1,
|
||||||
|
__typename: "Allocation"
|
||||||
|
}
|
||||||
|
],
|
||||||
thumbnail: {
|
thumbnail: {
|
||||||
__typename: "Image" as "Image",
|
__typename: "Image" as "Image",
|
||||||
url: placeholder
|
url: placeholder
|
||||||
|
@ -1569,7 +1772,33 @@ export const draftOrder = (placeholder: string): OrderDetailsFragment => ({
|
||||||
__typename: "ProductVariant",
|
__typename: "ProductVariant",
|
||||||
id: "dsfsfuhb",
|
id: "dsfsfuhb",
|
||||||
quantityAvailable: 10,
|
quantityAvailable: 10,
|
||||||
preorder: null
|
preorder: null,
|
||||||
|
stocks: [
|
||||||
|
{
|
||||||
|
id: "stock_test_id1",
|
||||||
|
warehouse: {
|
||||||
|
name: "stock_warehouse1",
|
||||||
|
id:
|
||||||
|
"V2FyZWhvdXNlOjc4OGUyMGRlLTlmYTAtNDI5My1iZDk2LWUwM2RjY2RhMzc0ZQ==",
|
||||||
|
__typename: "Warehouse"
|
||||||
|
},
|
||||||
|
quantity: 166,
|
||||||
|
quantityAllocated: 0,
|
||||||
|
__typename: "Stock"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "stock_test_id2",
|
||||||
|
warehouse: {
|
||||||
|
name: "stock_warehouse2",
|
||||||
|
id:
|
||||||
|
"V2FyZWhvdXNlOjczYzI0OGNmLTliNzAtNDlmMi1hMDRlLTM4ZTYxMmQ5MDYwMQ==",
|
||||||
|
__typename: "Warehouse"
|
||||||
|
},
|
||||||
|
quantity: 166,
|
||||||
|
quantityAllocated: 0,
|
||||||
|
__typename: "Stock"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
@ -2088,3 +2317,105 @@ export const shopOrderSettings: ShopOrderSettingsFragment = {
|
||||||
fulfillmentAutoApprove: true,
|
fulfillmentAutoApprove: true,
|
||||||
fulfillmentAllowUnpaid: true
|
fulfillmentAllowUnpaid: true
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const warehouseSearch: SearchWarehousesQuery["search"] = {
|
||||||
|
edges: [
|
||||||
|
{
|
||||||
|
node: {
|
||||||
|
id: "V2FyZWhvdXNlOmJiZTEwZjk1LTQyYjAtNDRlMS04Yjc5LWU5MjllMmViYTRjMQ==",
|
||||||
|
name: "CyVou-97803",
|
||||||
|
__typename: "Warehouse"
|
||||||
|
},
|
||||||
|
__typename: "WarehouseCountableEdge"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
node: {
|
||||||
|
id: "V2FyZWhvdXNlOjdhOGViNThhLTYwN2QtNGMxNC04ODVmLTBiMWU3ZDcyMTIyNQ==",
|
||||||
|
name: "CyWarehouse72715",
|
||||||
|
__typename: "Warehouse"
|
||||||
|
},
|
||||||
|
__typename: "WarehouseCountableEdge"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
node: {
|
||||||
|
id: "V2FyZWhvdXNlOjY2NWIxZWFmLTU5MDYtNGE0Mi1iYWVkLTc1ODQ3YWNhMWI1NQ==",
|
||||||
|
name: "CyWarehouseCheckout70441",
|
||||||
|
__typename: "Warehouse"
|
||||||
|
},
|
||||||
|
__typename: "WarehouseCountableEdge"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
node: {
|
||||||
|
id: "V2FyZWhvdXNlOjdkNmVmNmFkLWY4NTMtNGVmNS1iMzQ5LTUyY2I2N2U3NmIwZQ==",
|
||||||
|
name: "CyWeightRates-78849",
|
||||||
|
__typename: "Warehouse"
|
||||||
|
},
|
||||||
|
__typename: "WarehouseCountableEdge"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
node: {
|
||||||
|
id: "V2FyZWhvdXNlOjcwZjMyYTUyLWVlODQtNGExYi1iMjgzLTgwYjllMzgyNDlkNg==",
|
||||||
|
name: "EditShipping-82885",
|
||||||
|
__typename: "Warehouse"
|
||||||
|
},
|
||||||
|
__typename: "WarehouseCountableEdge"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
node: {
|
||||||
|
id: "V2FyZWhvdXNlOjczYzI0OGNmLTliNzAtNDlmMi1hMDRlLTM4ZTYxMmQ5MDYwMQ==",
|
||||||
|
name: "Europe for click and collect",
|
||||||
|
__typename: "Warehouse"
|
||||||
|
},
|
||||||
|
__typename: "WarehouseCountableEdge"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
node: {
|
||||||
|
id: "V2FyZWhvdXNlOjc4OGUyMGRlLTlmYTAtNDI5My1iZDk2LWUwM2RjY2RhMzc0ZQ==",
|
||||||
|
name: "Oceania",
|
||||||
|
__typename: "Warehouse"
|
||||||
|
},
|
||||||
|
__typename: "WarehouseCountableEdge"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
node: {
|
||||||
|
id: "V2FyZWhvdXNlOjNiZDM0YjEyLTllNDktNDMwZC1iM2QyLTRkYmRhMjM1MGUyOQ==",
|
||||||
|
name: "ProductsWithoutSkuInOrder",
|
||||||
|
__typename: "Warehouse"
|
||||||
|
},
|
||||||
|
__typename: "WarehouseCountableEdge"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
node: {
|
||||||
|
id: "V2FyZWhvdXNlOmU4M2U2NjQ2LTFhYjctNGNmNC05N2M4LTFiZjI2NGE2NjQ4Yw==",
|
||||||
|
name: "StocksThreshold",
|
||||||
|
__typename: "Warehouse"
|
||||||
|
},
|
||||||
|
__typename: "WarehouseCountableEdge"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
node: {
|
||||||
|
id: "V2FyZWhvdXNlOmJkMmQ1NDFjLWQwMjMtNDAwNi05YmRjLWZhZTA4OWZlNzZiYg==",
|
||||||
|
name: "UpdateProductsSku59844",
|
||||||
|
__typename: "Warehouse"
|
||||||
|
},
|
||||||
|
__typename: "WarehouseCountableEdge"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
node: {
|
||||||
|
id: "V2FyZWhvdXNlOjgzNDMwMzI4LTI2YWItNDNkZS1hNzdhLTVmNGNhMTljMDJhNg==",
|
||||||
|
name: "WithoutShipmentCheckout-4505",
|
||||||
|
__typename: "Warehouse"
|
||||||
|
},
|
||||||
|
__typename: "WarehouseCountableEdge"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
pageInfo: {
|
||||||
|
endCursor:
|
||||||
|
"WyJXaXRob3V0U2hpcG1lbnRDaGVja291dC00NTA1IiwgIldpdGhvdXRTaGlwbWVudENoZWNrb3V0LTQ1MDUiXQ==",
|
||||||
|
hasNextPage: false,
|
||||||
|
hasPreviousPage: true,
|
||||||
|
startCursor: "WyJDeVZvdS05NzgwMyIsICJDeVZvdS05NzgwMyJd",
|
||||||
|
__typename: "PageInfo"
|
||||||
|
},
|
||||||
|
__typename: "WarehouseCountableConnection"
|
||||||
|
};
|
||||||
|
|
|
@ -11,6 +11,7 @@ import {
|
||||||
OrderDraftListUrlQueryParams,
|
OrderDraftListUrlQueryParams,
|
||||||
OrderDraftListUrlSortField,
|
OrderDraftListUrlSortField,
|
||||||
orderFulfillPath,
|
orderFulfillPath,
|
||||||
|
OrderFulfillUrlQueryParams,
|
||||||
orderListPath,
|
orderListPath,
|
||||||
OrderListUrlQueryParams,
|
OrderListUrlQueryParams,
|
||||||
OrderListUrlSortField,
|
OrderListUrlSortField,
|
||||||
|
@ -61,9 +62,19 @@ const OrderDetails: React.FC<RouteComponentProps<any>> = ({
|
||||||
return <OrderDetailsComponent id={decodeURIComponent(id)} params={params} />;
|
return <OrderDetailsComponent id={decodeURIComponent(id)} params={params} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
const OrderFulfill: React.FC<RouteComponentProps<any>> = ({ match }) => (
|
const OrderFulfill: React.FC<RouteComponentProps<any>> = ({
|
||||||
<OrderFulfillComponent orderId={decodeURIComponent(match.params.id)} />
|
location,
|
||||||
);
|
match
|
||||||
|
}) => {
|
||||||
|
const qs = parseQs(location.search.substr(1));
|
||||||
|
const params: OrderFulfillUrlQueryParams = qs;
|
||||||
|
return (
|
||||||
|
<OrderFulfillComponent
|
||||||
|
orderId={decodeURIComponent(match.params.id)}
|
||||||
|
params={params}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const OrderRefund: React.FC<RouteComponentProps<any>> = ({ match }) => (
|
const OrderRefund: React.FC<RouteComponentProps<any>> = ({ match }) => (
|
||||||
<OrderRefundComponent orderId={decodeURIComponent(match.params.id)} />
|
<OrderRefundComponent orderId={decodeURIComponent(match.params.id)} />
|
||||||
|
|
|
@ -243,8 +243,16 @@ export const orderFulfillmentUpdateTrackingMutation = gql`
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const orderFulfillmentApproveMutation = gql`
|
export const orderFulfillmentApproveMutation = gql`
|
||||||
mutation OrderFulfillmentApprove($id: ID!, $notifyCustomer: Boolean!) {
|
mutation OrderFulfillmentApprove(
|
||||||
orderFulfillmentApprove(id: $id, notifyCustomer: $notifyCustomer) {
|
$id: ID!
|
||||||
|
$notifyCustomer: Boolean!
|
||||||
|
$allowStockToBeExceeded: Boolean
|
||||||
|
) {
|
||||||
|
orderFulfillmentApprove(
|
||||||
|
id: $id
|
||||||
|
notifyCustomer: $notifyCustomer
|
||||||
|
allowStockToBeExceeded: $allowStockToBeExceeded
|
||||||
|
) {
|
||||||
errors {
|
errors {
|
||||||
...OrderError
|
...OrderError
|
||||||
}
|
}
|
||||||
|
|
|
@ -130,44 +130,7 @@ export const orderFulfillData = gql`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
lines {
|
lines {
|
||||||
id
|
...OrderFulfillLine
|
||||||
isShippingRequired
|
|
||||||
productName
|
|
||||||
quantity
|
|
||||||
allocations {
|
|
||||||
quantity
|
|
||||||
warehouse {
|
|
||||||
id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
quantityFulfilled
|
|
||||||
quantityToFulfill
|
|
||||||
variant {
|
|
||||||
id
|
|
||||||
name
|
|
||||||
sku
|
|
||||||
preorder {
|
|
||||||
endDate
|
|
||||||
}
|
|
||||||
attributes {
|
|
||||||
values {
|
|
||||||
id
|
|
||||||
name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
stocks {
|
|
||||||
id
|
|
||||||
warehouse {
|
|
||||||
...Warehouse
|
|
||||||
}
|
|
||||||
quantity
|
|
||||||
quantityAllocated
|
|
||||||
}
|
|
||||||
trackInventory
|
|
||||||
}
|
|
||||||
thumbnail(size: 64) {
|
|
||||||
url
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
number
|
number
|
||||||
}
|
}
|
||||||
|
|
|
@ -111,6 +111,7 @@ export type OrderUrlDialog =
|
||||||
| "cancel"
|
| "cancel"
|
||||||
| "cancel-fulfillment"
|
| "cancel-fulfillment"
|
||||||
| "capture"
|
| "capture"
|
||||||
|
| "change-warehouse"
|
||||||
| "customer-change"
|
| "customer-change"
|
||||||
| "edit-customer-addresses"
|
| "edit-customer-addresses"
|
||||||
| "edit-billing-address"
|
| "edit-billing-address"
|
||||||
|
@ -124,6 +125,8 @@ export type OrderUrlDialog =
|
||||||
|
|
||||||
export type OrderUrlQueryParams = Dialog<OrderUrlDialog> & SingleAction;
|
export type OrderUrlQueryParams = Dialog<OrderUrlDialog> & SingleAction;
|
||||||
|
|
||||||
|
export type OrderFulfillUrlQueryParams = Partial<{ warehouse: string }>;
|
||||||
|
|
||||||
export const orderUrl = (id: string, params?: OrderUrlQueryParams) =>
|
export const orderUrl = (id: string, params?: OrderUrlQueryParams) =>
|
||||||
orderPath(encodeURIComponent(id)) + "?" + stringifyQs(params);
|
orderPath(encodeURIComponent(id)) + "?" + stringifyQs(params);
|
||||||
|
|
||||||
|
@ -132,8 +135,10 @@ export const orderFulfillPath = (id: string) =>
|
||||||
|
|
||||||
export const orderReturnPath = (id: string) => urlJoin(orderPath(id), "return");
|
export const orderReturnPath = (id: string) => urlJoin(orderPath(id), "return");
|
||||||
|
|
||||||
export const orderFulfillUrl = (id: string) =>
|
export const orderFulfillUrl = (
|
||||||
orderFulfillPath(encodeURIComponent(id));
|
id: string,
|
||||||
|
params?: OrderFulfillUrlQueryParams
|
||||||
|
) => orderFulfillPath(encodeURIComponent(id)) + "?" + stringifyQs(params);
|
||||||
|
|
||||||
export const orderSettingsPath = urlJoin(orderSectionUrl, "settings");
|
export const orderSettingsPath = urlJoin(orderSectionUrl, "settings");
|
||||||
|
|
||||||
|
|
|
@ -521,11 +521,49 @@ describe("Get the total value of all replaced products", () => {
|
||||||
{
|
{
|
||||||
id: "1",
|
id: "1",
|
||||||
isShippingRequired: false,
|
isShippingRequired: false,
|
||||||
|
allocations: [
|
||||||
|
{
|
||||||
|
id: "allocation_test_id",
|
||||||
|
warehouse: {
|
||||||
|
id:
|
||||||
|
"V2FyZWhvdXNlOjk1NWY0ZDk2LWRmNTAtNGY0Zi1hOTM4LWM5MTYzYTA4YTViNg==",
|
||||||
|
__typename: "Warehouse"
|
||||||
|
},
|
||||||
|
quantity: 1,
|
||||||
|
__typename: "Allocation"
|
||||||
|
}
|
||||||
|
],
|
||||||
variant: {
|
variant: {
|
||||||
id: "UHJvZHVjdFZhcmlhbnQ6MzE3",
|
id: "UHJvZHVjdFZhcmlhbnQ6MzE3",
|
||||||
quantityAvailable: 50,
|
quantityAvailable: 50,
|
||||||
preorder: null,
|
preorder: null,
|
||||||
__typename: "ProductVariant"
|
__typename: "ProductVariant",
|
||||||
|
stocks: [
|
||||||
|
{
|
||||||
|
id: "stock_test_id1",
|
||||||
|
warehouse: {
|
||||||
|
name: "warehouse_stock1",
|
||||||
|
id:
|
||||||
|
"V2FyZWhvdXNlOjc4OGUyMGRlLTlmYTAtNDI5My1iZDk2LWUwM2RjY2RhMzc0ZQ==",
|
||||||
|
__typename: "Warehouse"
|
||||||
|
},
|
||||||
|
quantity: 166,
|
||||||
|
quantityAllocated: 0,
|
||||||
|
__typename: "Stock"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "stock_test_id2",
|
||||||
|
warehouse: {
|
||||||
|
name: "warehouse_stock2",
|
||||||
|
id:
|
||||||
|
"V2FyZWhvdXNlOjczYzI0OGNmLTliNzAtNDlmMi1hMDRlLTM4ZTYxMmQ5MDYwMQ==",
|
||||||
|
__typename: "Warehouse"
|
||||||
|
},
|
||||||
|
quantity: 166,
|
||||||
|
quantityAllocated: 0,
|
||||||
|
__typename: "Stock"
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
productName: "Lake Tunes",
|
productName: "Lake Tunes",
|
||||||
productSku: "lake-tunes-mp3",
|
productSku: "lake-tunes-mp3",
|
||||||
|
@ -577,10 +615,48 @@ describe("Get the total value of all replaced products", () => {
|
||||||
{
|
{
|
||||||
id: "2",
|
id: "2",
|
||||||
isShippingRequired: false,
|
isShippingRequired: false,
|
||||||
|
allocations: [
|
||||||
|
{
|
||||||
|
id: "allocation_test_id",
|
||||||
|
warehouse: {
|
||||||
|
id:
|
||||||
|
"V2FyZWhvdXNlOjk1NWY0ZDk2LWRmNTAtNGY0Zi1hOTM4LWM5MTYzYTA4YTViNg==",
|
||||||
|
__typename: "Warehouse"
|
||||||
|
},
|
||||||
|
quantity: 1,
|
||||||
|
__typename: "Allocation"
|
||||||
|
}
|
||||||
|
],
|
||||||
variant: {
|
variant: {
|
||||||
id: "UHJvZHVjdFZhcmlhbnQ6MzE3",
|
id: "UHJvZHVjdFZhcmlhbnQ6MzE3",
|
||||||
quantityAvailable: 50,
|
quantityAvailable: 50,
|
||||||
preorder: null,
|
preorder: null,
|
||||||
|
stocks: [
|
||||||
|
{
|
||||||
|
id: "stock_test_id1",
|
||||||
|
warehouse: {
|
||||||
|
name: "warehouse_stock1",
|
||||||
|
id:
|
||||||
|
"V2FyZWhvdXNlOjc4OGUyMGRlLTlmYTAtNDI5My1iZDk2LWUwM2RjY2RhMzc0ZQ==",
|
||||||
|
__typename: "Warehouse"
|
||||||
|
},
|
||||||
|
quantity: 166,
|
||||||
|
quantityAllocated: 0,
|
||||||
|
__typename: "Stock"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "stock_test_id2",
|
||||||
|
warehouse: {
|
||||||
|
name: "warehouse_stock2",
|
||||||
|
id:
|
||||||
|
"V2FyZWhvdXNlOjczYzI0OGNmLTliNzAtNDlmMi1hMDRlLTM4ZTYxMmQ5MDYwMQ==",
|
||||||
|
__typename: "Warehouse"
|
||||||
|
},
|
||||||
|
quantity: 166,
|
||||||
|
quantityAllocated: 0,
|
||||||
|
__typename: "Stock"
|
||||||
|
}
|
||||||
|
],
|
||||||
__typename: "ProductVariant"
|
__typename: "ProductVariant"
|
||||||
},
|
},
|
||||||
productName: "Lake Tunes",
|
productName: "Lake Tunes",
|
||||||
|
@ -633,10 +709,48 @@ describe("Get the total value of all replaced products", () => {
|
||||||
{
|
{
|
||||||
id: "3",
|
id: "3",
|
||||||
isShippingRequired: true,
|
isShippingRequired: true,
|
||||||
|
allocations: [
|
||||||
|
{
|
||||||
|
id: "allocation_test_id",
|
||||||
|
warehouse: {
|
||||||
|
id:
|
||||||
|
"V2FyZWhvdXNlOjk1NWY0ZDk2LWRmNTAtNGY0Zi1hOTM4LWM5MTYzYTA4YTViNg==",
|
||||||
|
__typename: "Warehouse"
|
||||||
|
},
|
||||||
|
quantity: 1,
|
||||||
|
__typename: "Allocation"
|
||||||
|
}
|
||||||
|
],
|
||||||
variant: {
|
variant: {
|
||||||
id: "UHJvZHVjdFZhcmlhbnQ6Mjg2",
|
id: "UHJvZHVjdFZhcmlhbnQ6Mjg2",
|
||||||
quantityAvailable: 50,
|
quantityAvailable: 50,
|
||||||
preorder: null,
|
preorder: null,
|
||||||
|
stocks: [
|
||||||
|
{
|
||||||
|
id: "stock_test_id1",
|
||||||
|
warehouse: {
|
||||||
|
name: "warehouse_stock1",
|
||||||
|
id:
|
||||||
|
"V2FyZWhvdXNlOjc4OGUyMGRlLTlmYTAtNDI5My1iZDk2LWUwM2RjY2RhMzc0ZQ==",
|
||||||
|
__typename: "Warehouse"
|
||||||
|
},
|
||||||
|
quantity: 166,
|
||||||
|
quantityAllocated: 0,
|
||||||
|
__typename: "Stock"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "stock_test_id2",
|
||||||
|
warehouse: {
|
||||||
|
name: "warehouse_stock2",
|
||||||
|
id:
|
||||||
|
"V2FyZWhvdXNlOjczYzI0OGNmLTliNzAtNDlmMi1hMDRlLTM4ZTYxMmQ5MDYwMQ==",
|
||||||
|
__typename: "Warehouse"
|
||||||
|
},
|
||||||
|
quantity: 166,
|
||||||
|
quantityAllocated: 0,
|
||||||
|
__typename: "Stock"
|
||||||
|
}
|
||||||
|
],
|
||||||
__typename: "ProductVariant"
|
__typename: "ProductVariant"
|
||||||
},
|
},
|
||||||
productName: "T-shirt",
|
productName: "T-shirt",
|
||||||
|
@ -695,10 +809,48 @@ describe("Get the total value of all replaced products", () => {
|
||||||
orderLine: {
|
orderLine: {
|
||||||
id: "T3JkZXJMaW5lOjQ1",
|
id: "T3JkZXJMaW5lOjQ1",
|
||||||
isShippingRequired: false,
|
isShippingRequired: false,
|
||||||
|
allocations: [
|
||||||
|
{
|
||||||
|
id: "allocation_test_id",
|
||||||
|
warehouse: {
|
||||||
|
id:
|
||||||
|
"V2FyZWhvdXNlOjk1NWY0ZDk2LWRmNTAtNGY0Zi1hOTM4LWM5MTYzYTA4YTViNg==",
|
||||||
|
__typename: "Warehouse"
|
||||||
|
},
|
||||||
|
quantity: 1,
|
||||||
|
__typename: "Allocation"
|
||||||
|
}
|
||||||
|
],
|
||||||
variant: {
|
variant: {
|
||||||
id: "UHJvZHVjdFZhcmlhbnQ6MzE3",
|
id: "UHJvZHVjdFZhcmlhbnQ6MzE3",
|
||||||
quantityAvailable: 50,
|
quantityAvailable: 50,
|
||||||
preorder: null,
|
preorder: null,
|
||||||
|
stocks: [
|
||||||
|
{
|
||||||
|
id: "stock_test_id1",
|
||||||
|
warehouse: {
|
||||||
|
name: "warehouse_stock1",
|
||||||
|
id:
|
||||||
|
"V2FyZWhvdXNlOjc4OGUyMGRlLTlmYTAtNDI5My1iZDk2LWUwM2RjY2RhMzc0ZQ==",
|
||||||
|
__typename: "Warehouse"
|
||||||
|
},
|
||||||
|
quantity: 166,
|
||||||
|
quantityAllocated: 0,
|
||||||
|
__typename: "Stock"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "stock_test_id2",
|
||||||
|
warehouse: {
|
||||||
|
name: "warehouse_stock2",
|
||||||
|
id:
|
||||||
|
"V2FyZWhvdXNlOjczYzI0OGNmLTliNzAtNDlmMi1hMDRlLTM4ZTYxMmQ5MDYwMQ==",
|
||||||
|
__typename: "Warehouse"
|
||||||
|
},
|
||||||
|
quantity: 166,
|
||||||
|
quantityAllocated: 0,
|
||||||
|
__typename: "Stock"
|
||||||
|
}
|
||||||
|
],
|
||||||
__typename: "ProductVariant"
|
__typename: "ProductVariant"
|
||||||
},
|
},
|
||||||
productName: "Lake Tunes",
|
productName: "Lake Tunes",
|
||||||
|
@ -756,10 +908,48 @@ describe("Get the total value of all replaced products", () => {
|
||||||
orderLine: {
|
orderLine: {
|
||||||
id: "T3JkZXJMaW5lOjQ1",
|
id: "T3JkZXJMaW5lOjQ1",
|
||||||
isShippingRequired: false,
|
isShippingRequired: false,
|
||||||
|
allocations: [
|
||||||
|
{
|
||||||
|
id: "allocation_test_id",
|
||||||
|
warehouse: {
|
||||||
|
id:
|
||||||
|
"V2FyZWhvdXNlOjk1NWY0ZDk2LWRmNTAtNGY0Zi1hOTM4LWM5MTYzYTA4YTViNg==",
|
||||||
|
__typename: "Warehouse"
|
||||||
|
},
|
||||||
|
quantity: 1,
|
||||||
|
__typename: "Allocation"
|
||||||
|
}
|
||||||
|
],
|
||||||
variant: {
|
variant: {
|
||||||
id: "UHJvZHVjdFZhcmlhbnQ6MzE3",
|
id: "UHJvZHVjdFZhcmlhbnQ6MzE3",
|
||||||
quantityAvailable: 50,
|
quantityAvailable: 50,
|
||||||
preorder: null,
|
preorder: null,
|
||||||
|
stocks: [
|
||||||
|
{
|
||||||
|
id: "stock_test_id1",
|
||||||
|
warehouse: {
|
||||||
|
name: "warehouse_stock1",
|
||||||
|
id:
|
||||||
|
"V2FyZWhvdXNlOjc4OGUyMGRlLTlmYTAtNDI5My1iZDk2LWUwM2RjY2RhMzc0ZQ==",
|
||||||
|
__typename: "Warehouse"
|
||||||
|
},
|
||||||
|
quantity: 166,
|
||||||
|
quantityAllocated: 0,
|
||||||
|
__typename: "Stock"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "stock_test_id2",
|
||||||
|
warehouse: {
|
||||||
|
name: "warehouse_stock2",
|
||||||
|
id:
|
||||||
|
"V2FyZWhvdXNlOjczYzI0OGNmLTliNzAtNDlmMi1hMDRlLTM4ZTYxMmQ5MDYwMQ==",
|
||||||
|
__typename: "Warehouse"
|
||||||
|
},
|
||||||
|
quantity: 166,
|
||||||
|
quantityAllocated: 0,
|
||||||
|
__typename: "Stock"
|
||||||
|
}
|
||||||
|
],
|
||||||
__typename: "ProductVariant"
|
__typename: "ProductVariant"
|
||||||
},
|
},
|
||||||
productName: "Lake Tunes",
|
productName: "Lake Tunes",
|
||||||
|
@ -817,10 +1007,48 @@ describe("Get the total value of all replaced products", () => {
|
||||||
orderLine: {
|
orderLine: {
|
||||||
id: "T3JkZXJMaW5lOjQ3",
|
id: "T3JkZXJMaW5lOjQ3",
|
||||||
isShippingRequired: true,
|
isShippingRequired: true,
|
||||||
|
allocations: [
|
||||||
|
{
|
||||||
|
id: "allocation_test_id",
|
||||||
|
warehouse: {
|
||||||
|
id:
|
||||||
|
"V2FyZWhvdXNlOjk1NWY0ZDk2LWRmNTAtNGY0Zi1hOTM4LWM5MTYzYTA4YTViNg==",
|
||||||
|
__typename: "Warehouse"
|
||||||
|
},
|
||||||
|
quantity: 1,
|
||||||
|
__typename: "Allocation"
|
||||||
|
}
|
||||||
|
],
|
||||||
variant: {
|
variant: {
|
||||||
id: "UHJvZHVjdFZhcmlhbnQ6Mjg2",
|
id: "UHJvZHVjdFZhcmlhbnQ6Mjg2",
|
||||||
quantityAvailable: 50,
|
quantityAvailable: 50,
|
||||||
preorder: null,
|
preorder: null,
|
||||||
|
stocks: [
|
||||||
|
{
|
||||||
|
id: "stock_test_id1",
|
||||||
|
warehouse: {
|
||||||
|
name: "warehouse_stock1",
|
||||||
|
id:
|
||||||
|
"V2FyZWhvdXNlOjc4OGUyMGRlLTlmYTAtNDI5My1iZDk2LWUwM2RjY2RhMzc0ZQ==",
|
||||||
|
__typename: "Warehouse"
|
||||||
|
},
|
||||||
|
quantity: 166,
|
||||||
|
quantityAllocated: 0,
|
||||||
|
__typename: "Stock"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "stock_test_id2",
|
||||||
|
warehouse: {
|
||||||
|
name: "warehouse_stock2",
|
||||||
|
id:
|
||||||
|
"V2FyZWhvdXNlOjczYzI0OGNmLTliNzAtNDlmMi1hMDRlLTM4ZTYxMmQ5MDYwMQ==",
|
||||||
|
__typename: "Warehouse"
|
||||||
|
},
|
||||||
|
quantity: 166,
|
||||||
|
quantityAllocated: 0,
|
||||||
|
__typename: "Stock"
|
||||||
|
}
|
||||||
|
],
|
||||||
__typename: "ProductVariant"
|
__typename: "ProductVariant"
|
||||||
},
|
},
|
||||||
productName: "T-shirt",
|
productName: "T-shirt",
|
||||||
|
@ -878,10 +1106,48 @@ describe("Get the total value of all replaced products", () => {
|
||||||
orderLine: {
|
orderLine: {
|
||||||
id: "T3JkZXJMaW5lOjQ1",
|
id: "T3JkZXJMaW5lOjQ1",
|
||||||
isShippingRequired: false,
|
isShippingRequired: false,
|
||||||
|
allocations: [
|
||||||
|
{
|
||||||
|
id: "allocation_test_id",
|
||||||
|
warehouse: {
|
||||||
|
id:
|
||||||
|
"V2FyZWhvdXNlOjk1NWY0ZDk2LWRmNTAtNGY0Zi1hOTM4LWM5MTYzYTA4YTViNg==",
|
||||||
|
__typename: "Warehouse"
|
||||||
|
},
|
||||||
|
quantity: 1,
|
||||||
|
__typename: "Allocation"
|
||||||
|
}
|
||||||
|
],
|
||||||
variant: {
|
variant: {
|
||||||
id: "UHJvZHVjdFZhcmlhbnQ6MzE3",
|
id: "UHJvZHVjdFZhcmlhbnQ6MzE3",
|
||||||
quantityAvailable: 50,
|
quantityAvailable: 50,
|
||||||
preorder: null,
|
preorder: null,
|
||||||
|
stocks: [
|
||||||
|
{
|
||||||
|
id: "stock_test_id1",
|
||||||
|
warehouse: {
|
||||||
|
name: "warehouse_stock1",
|
||||||
|
id:
|
||||||
|
"V2FyZWhvdXNlOjc4OGUyMGRlLTlmYTAtNDI5My1iZDk2LWUwM2RjY2RhMzc0ZQ==",
|
||||||
|
__typename: "Warehouse"
|
||||||
|
},
|
||||||
|
quantity: 166,
|
||||||
|
quantityAllocated: 0,
|
||||||
|
__typename: "Stock"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "stock_test_id2",
|
||||||
|
warehouse: {
|
||||||
|
name: "warehouse_stock2",
|
||||||
|
id:
|
||||||
|
"V2FyZWhvdXNlOjczYzI0OGNmLTliNzAtNDlmMi1hMDRlLTM4ZTYxMmQ5MDYwMQ==",
|
||||||
|
__typename: "Warehouse"
|
||||||
|
},
|
||||||
|
quantity: 166,
|
||||||
|
quantityAllocated: 0,
|
||||||
|
__typename: "Stock"
|
||||||
|
}
|
||||||
|
],
|
||||||
__typename: "ProductVariant"
|
__typename: "ProductVariant"
|
||||||
},
|
},
|
||||||
productName: "Lake Tunes",
|
productName: "Lake Tunes",
|
||||||
|
@ -939,10 +1205,48 @@ describe("Get the total value of all replaced products", () => {
|
||||||
orderLine: {
|
orderLine: {
|
||||||
id: "T3JkZXJMaW5lOjQ1",
|
id: "T3JkZXJMaW5lOjQ1",
|
||||||
isShippingRequired: false,
|
isShippingRequired: false,
|
||||||
|
allocations: [
|
||||||
|
{
|
||||||
|
id: "allocation_test_id",
|
||||||
|
warehouse: {
|
||||||
|
id:
|
||||||
|
"V2FyZWhvdXNlOjk1NWY0ZDk2LWRmNTAtNGY0Zi1hOTM4LWM5MTYzYTA4YTViNg==",
|
||||||
|
__typename: "Warehouse"
|
||||||
|
},
|
||||||
|
quantity: 1,
|
||||||
|
__typename: "Allocation"
|
||||||
|
}
|
||||||
|
],
|
||||||
variant: {
|
variant: {
|
||||||
id: "UHJvZHVjdFZhcmlhbnQ6MzE3",
|
id: "UHJvZHVjdFZhcmlhbnQ6MzE3",
|
||||||
quantityAvailable: 50,
|
quantityAvailable: 50,
|
||||||
preorder: null,
|
preorder: null,
|
||||||
|
stocks: [
|
||||||
|
{
|
||||||
|
id: "stock_test_id1",
|
||||||
|
warehouse: {
|
||||||
|
name: "warehouse_stock1",
|
||||||
|
id:
|
||||||
|
"V2FyZWhvdXNlOjc4OGUyMGRlLTlmYTAtNDI5My1iZDk2LWUwM2RjY2RhMzc0ZQ==",
|
||||||
|
__typename: "Warehouse"
|
||||||
|
},
|
||||||
|
quantity: 166,
|
||||||
|
quantityAllocated: 0,
|
||||||
|
__typename: "Stock"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "stock_test_id2",
|
||||||
|
warehouse: {
|
||||||
|
name: "warehouse_stock2",
|
||||||
|
id:
|
||||||
|
"V2FyZWhvdXNlOjczYzI0OGNmLTliNzAtNDlmMi1hMDRlLTM4ZTYxMmQ5MDYwMQ==",
|
||||||
|
__typename: "Warehouse"
|
||||||
|
},
|
||||||
|
quantity: 166,
|
||||||
|
quantityAllocated: 0,
|
||||||
|
__typename: "Stock"
|
||||||
|
}
|
||||||
|
],
|
||||||
__typename: "ProductVariant"
|
__typename: "ProductVariant"
|
||||||
},
|
},
|
||||||
productName: "Lake Tunes",
|
productName: "Lake Tunes",
|
||||||
|
@ -1134,10 +1438,48 @@ describe("Get the total value of all selected products", () => {
|
||||||
{
|
{
|
||||||
id: "1",
|
id: "1",
|
||||||
isShippingRequired: false,
|
isShippingRequired: false,
|
||||||
|
allocations: [
|
||||||
|
{
|
||||||
|
id: "allocation_test_id",
|
||||||
|
warehouse: {
|
||||||
|
id:
|
||||||
|
"V2FyZWhvdXNlOjk1NWY0ZDk2LWRmNTAtNGY0Zi1hOTM4LWM5MTYzYTA4YTViNg==",
|
||||||
|
__typename: "Warehouse"
|
||||||
|
},
|
||||||
|
quantity: 1,
|
||||||
|
__typename: "Allocation"
|
||||||
|
}
|
||||||
|
],
|
||||||
variant: {
|
variant: {
|
||||||
id: "UHJvZHVjdFZhcmlhbnQ6MzE3",
|
id: "UHJvZHVjdFZhcmlhbnQ6MzE3",
|
||||||
quantityAvailable: 50,
|
quantityAvailable: 50,
|
||||||
preorder: null,
|
preorder: null,
|
||||||
|
stocks: [
|
||||||
|
{
|
||||||
|
id: "stock_test_id1",
|
||||||
|
warehouse: {
|
||||||
|
name: "warehouse_stock1",
|
||||||
|
id:
|
||||||
|
"V2FyZWhvdXNlOjc4OGUyMGRlLTlmYTAtNDI5My1iZDk2LWUwM2RjY2RhMzc0ZQ==",
|
||||||
|
__typename: "Warehouse"
|
||||||
|
},
|
||||||
|
quantity: 166,
|
||||||
|
quantityAllocated: 0,
|
||||||
|
__typename: "Stock"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "stock_test_id2",
|
||||||
|
warehouse: {
|
||||||
|
name: "warehouse_stock2",
|
||||||
|
id:
|
||||||
|
"V2FyZWhvdXNlOjczYzI0OGNmLTliNzAtNDlmMi1hMDRlLTM4ZTYxMmQ5MDYwMQ==",
|
||||||
|
__typename: "Warehouse"
|
||||||
|
},
|
||||||
|
quantity: 166,
|
||||||
|
quantityAllocated: 0,
|
||||||
|
__typename: "Stock"
|
||||||
|
}
|
||||||
|
],
|
||||||
__typename: "ProductVariant"
|
__typename: "ProductVariant"
|
||||||
},
|
},
|
||||||
productName: "Lake Tunes",
|
productName: "Lake Tunes",
|
||||||
|
@ -1190,10 +1532,48 @@ describe("Get the total value of all selected products", () => {
|
||||||
{
|
{
|
||||||
id: "2",
|
id: "2",
|
||||||
isShippingRequired: false,
|
isShippingRequired: false,
|
||||||
|
allocations: [
|
||||||
|
{
|
||||||
|
id: "allocation_test_id",
|
||||||
|
warehouse: {
|
||||||
|
id:
|
||||||
|
"V2FyZWhvdXNlOjk1NWY0ZDk2LWRmNTAtNGY0Zi1hOTM4LWM5MTYzYTA4YTViNg==",
|
||||||
|
__typename: "Warehouse"
|
||||||
|
},
|
||||||
|
quantity: 1,
|
||||||
|
__typename: "Allocation"
|
||||||
|
}
|
||||||
|
],
|
||||||
variant: {
|
variant: {
|
||||||
id: "UHJvZHVjdFZhcmlhbnQ6MzE3",
|
id: "UHJvZHVjdFZhcmlhbnQ6MzE3",
|
||||||
quantityAvailable: 50,
|
quantityAvailable: 50,
|
||||||
preorder: null,
|
preorder: null,
|
||||||
|
stocks: [
|
||||||
|
{
|
||||||
|
id: "stock_test_id1",
|
||||||
|
warehouse: {
|
||||||
|
name: "warehouse_stock1",
|
||||||
|
id:
|
||||||
|
"V2FyZWhvdXNlOjc4OGUyMGRlLTlmYTAtNDI5My1iZDk2LWUwM2RjY2RhMzc0ZQ==",
|
||||||
|
__typename: "Warehouse"
|
||||||
|
},
|
||||||
|
quantity: 166,
|
||||||
|
quantityAllocated: 0,
|
||||||
|
__typename: "Stock"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "stock_test_id2",
|
||||||
|
warehouse: {
|
||||||
|
name: "warehouse_stock2",
|
||||||
|
id:
|
||||||
|
"V2FyZWhvdXNlOjczYzI0OGNmLTliNzAtNDlmMi1hMDRlLTM4ZTYxMmQ5MDYwMQ==",
|
||||||
|
__typename: "Warehouse"
|
||||||
|
},
|
||||||
|
quantity: 166,
|
||||||
|
quantityAllocated: 0,
|
||||||
|
__typename: "Stock"
|
||||||
|
}
|
||||||
|
],
|
||||||
__typename: "ProductVariant"
|
__typename: "ProductVariant"
|
||||||
},
|
},
|
||||||
productName: "Lake Tunes",
|
productName: "Lake Tunes",
|
||||||
|
@ -1246,10 +1626,48 @@ describe("Get the total value of all selected products", () => {
|
||||||
{
|
{
|
||||||
id: "3",
|
id: "3",
|
||||||
isShippingRequired: true,
|
isShippingRequired: true,
|
||||||
|
allocations: [
|
||||||
|
{
|
||||||
|
id: "allocation_test_id",
|
||||||
|
warehouse: {
|
||||||
|
id:
|
||||||
|
"V2FyZWhvdXNlOjk1NWY0ZDk2LWRmNTAtNGY0Zi1hOTM4LWM5MTYzYTA4YTViNg==",
|
||||||
|
__typename: "Warehouse"
|
||||||
|
},
|
||||||
|
quantity: 1,
|
||||||
|
__typename: "Allocation"
|
||||||
|
}
|
||||||
|
],
|
||||||
variant: {
|
variant: {
|
||||||
id: "UHJvZHVjdFZhcmlhbnQ6Mjg2",
|
id: "UHJvZHVjdFZhcmlhbnQ6Mjg2",
|
||||||
quantityAvailable: 50,
|
quantityAvailable: 50,
|
||||||
preorder: null,
|
preorder: null,
|
||||||
|
stocks: [
|
||||||
|
{
|
||||||
|
id: "stock_test_id1",
|
||||||
|
warehouse: {
|
||||||
|
name: "warehouse_stock1",
|
||||||
|
id:
|
||||||
|
"V2FyZWhvdXNlOjc4OGUyMGRlLTlmYTAtNDI5My1iZDk2LWUwM2RjY2RhMzc0ZQ==",
|
||||||
|
__typename: "Warehouse"
|
||||||
|
},
|
||||||
|
quantity: 166,
|
||||||
|
quantityAllocated: 0,
|
||||||
|
__typename: "Stock"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "stock_test_id2",
|
||||||
|
warehouse: {
|
||||||
|
name: "warehouse_stock2",
|
||||||
|
id:
|
||||||
|
"V2FyZWhvdXNlOjczYzI0OGNmLTliNzAtNDlmMi1hMDRlLTM4ZTYxMmQ5MDYwMQ==",
|
||||||
|
__typename: "Warehouse"
|
||||||
|
},
|
||||||
|
quantity: 166,
|
||||||
|
quantityAllocated: 0,
|
||||||
|
__typename: "Stock"
|
||||||
|
}
|
||||||
|
],
|
||||||
__typename: "ProductVariant"
|
__typename: "ProductVariant"
|
||||||
},
|
},
|
||||||
productName: "T-shirt",
|
productName: "T-shirt",
|
||||||
|
@ -1308,10 +1726,48 @@ describe("Get the total value of all selected products", () => {
|
||||||
orderLine: {
|
orderLine: {
|
||||||
id: "T3JkZXJMaW5lOjQ1",
|
id: "T3JkZXJMaW5lOjQ1",
|
||||||
isShippingRequired: false,
|
isShippingRequired: false,
|
||||||
|
allocations: [
|
||||||
|
{
|
||||||
|
id: "allocation_test_id",
|
||||||
|
warehouse: {
|
||||||
|
id:
|
||||||
|
"V2FyZWhvdXNlOjk1NWY0ZDk2LWRmNTAtNGY0Zi1hOTM4LWM5MTYzYTA4YTViNg==",
|
||||||
|
__typename: "Warehouse"
|
||||||
|
},
|
||||||
|
quantity: 1,
|
||||||
|
__typename: "Allocation"
|
||||||
|
}
|
||||||
|
],
|
||||||
variant: {
|
variant: {
|
||||||
id: "UHJvZHVjdFZhcmlhbnQ6MzE3",
|
id: "UHJvZHVjdFZhcmlhbnQ6MzE3",
|
||||||
quantityAvailable: 50,
|
quantityAvailable: 50,
|
||||||
preorder: null,
|
preorder: null,
|
||||||
|
stocks: [
|
||||||
|
{
|
||||||
|
id: "stock_test_id1",
|
||||||
|
warehouse: {
|
||||||
|
name: "warehouse_stock1",
|
||||||
|
id:
|
||||||
|
"V2FyZWhvdXNlOjc4OGUyMGRlLTlmYTAtNDI5My1iZDk2LWUwM2RjY2RhMzc0ZQ==",
|
||||||
|
__typename: "Warehouse"
|
||||||
|
},
|
||||||
|
quantity: 166,
|
||||||
|
quantityAllocated: 0,
|
||||||
|
__typename: "Stock"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "stock_test_id2",
|
||||||
|
warehouse: {
|
||||||
|
name: "warehouse_stock2",
|
||||||
|
id:
|
||||||
|
"V2FyZWhvdXNlOjczYzI0OGNmLTliNzAtNDlmMi1hMDRlLTM4ZTYxMmQ5MDYwMQ==",
|
||||||
|
__typename: "Warehouse"
|
||||||
|
},
|
||||||
|
quantity: 166,
|
||||||
|
quantityAllocated: 0,
|
||||||
|
__typename: "Stock"
|
||||||
|
}
|
||||||
|
],
|
||||||
__typename: "ProductVariant"
|
__typename: "ProductVariant"
|
||||||
},
|
},
|
||||||
productName: "Lake Tunes",
|
productName: "Lake Tunes",
|
||||||
|
@ -1369,10 +1825,48 @@ describe("Get the total value of all selected products", () => {
|
||||||
orderLine: {
|
orderLine: {
|
||||||
id: "T3JkZXJMaW5lOjQ1",
|
id: "T3JkZXJMaW5lOjQ1",
|
||||||
isShippingRequired: false,
|
isShippingRequired: false,
|
||||||
|
allocations: [
|
||||||
|
{
|
||||||
|
id: "allocation_test_id",
|
||||||
|
warehouse: {
|
||||||
|
id:
|
||||||
|
"V2FyZWhvdXNlOjk1NWY0ZDk2LWRmNTAtNGY0Zi1hOTM4LWM5MTYzYTA4YTViNg==",
|
||||||
|
__typename: "Warehouse"
|
||||||
|
},
|
||||||
|
quantity: 1,
|
||||||
|
__typename: "Allocation"
|
||||||
|
}
|
||||||
|
],
|
||||||
variant: {
|
variant: {
|
||||||
id: "UHJvZHVjdFZhcmlhbnQ6MzE3",
|
id: "UHJvZHVjdFZhcmlhbnQ6MzE3",
|
||||||
quantityAvailable: 50,
|
quantityAvailable: 50,
|
||||||
preorder: null,
|
preorder: null,
|
||||||
|
stocks: [
|
||||||
|
{
|
||||||
|
id: "stock_test_id1",
|
||||||
|
warehouse: {
|
||||||
|
name: "warehouse_stock1",
|
||||||
|
id:
|
||||||
|
"V2FyZWhvdXNlOjc4OGUyMGRlLTlmYTAtNDI5My1iZDk2LWUwM2RjY2RhMzc0ZQ==",
|
||||||
|
__typename: "Warehouse"
|
||||||
|
},
|
||||||
|
quantity: 166,
|
||||||
|
quantityAllocated: 0,
|
||||||
|
__typename: "Stock"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "stock_test_id2",
|
||||||
|
warehouse: {
|
||||||
|
name: "warehouse_stock2",
|
||||||
|
id:
|
||||||
|
"V2FyZWhvdXNlOjczYzI0OGNmLTliNzAtNDlmMi1hMDRlLTM4ZTYxMmQ5MDYwMQ==",
|
||||||
|
__typename: "Warehouse"
|
||||||
|
},
|
||||||
|
quantity: 166,
|
||||||
|
quantityAllocated: 0,
|
||||||
|
__typename: "Stock"
|
||||||
|
}
|
||||||
|
],
|
||||||
__typename: "ProductVariant"
|
__typename: "ProductVariant"
|
||||||
},
|
},
|
||||||
productName: "Lake Tunes",
|
productName: "Lake Tunes",
|
||||||
|
@ -1430,10 +1924,48 @@ describe("Get the total value of all selected products", () => {
|
||||||
orderLine: {
|
orderLine: {
|
||||||
id: "T3JkZXJMaW5lOjQ3",
|
id: "T3JkZXJMaW5lOjQ3",
|
||||||
isShippingRequired: true,
|
isShippingRequired: true,
|
||||||
|
allocations: [
|
||||||
|
{
|
||||||
|
id: "allocation_test_id",
|
||||||
|
warehouse: {
|
||||||
|
id:
|
||||||
|
"V2FyZWhvdXNlOjk1NWY0ZDk2LWRmNTAtNGY0Zi1hOTM4LWM5MTYzYTA4YTViNg==",
|
||||||
|
__typename: "Warehouse"
|
||||||
|
},
|
||||||
|
quantity: 1,
|
||||||
|
__typename: "Allocation"
|
||||||
|
}
|
||||||
|
],
|
||||||
variant: {
|
variant: {
|
||||||
id: "UHJvZHVjdFZhcmlhbnQ6Mjg2",
|
id: "UHJvZHVjdFZhcmlhbnQ6Mjg2",
|
||||||
quantityAvailable: 50,
|
quantityAvailable: 50,
|
||||||
preorder: null,
|
preorder: null,
|
||||||
|
stocks: [
|
||||||
|
{
|
||||||
|
id: "stock_test_id1",
|
||||||
|
warehouse: {
|
||||||
|
name: "warehouse_stock1",
|
||||||
|
id:
|
||||||
|
"V2FyZWhvdXNlOjc4OGUyMGRlLTlmYTAtNDI5My1iZDk2LWUwM2RjY2RhMzc0ZQ==",
|
||||||
|
__typename: "Warehouse"
|
||||||
|
},
|
||||||
|
quantity: 166,
|
||||||
|
quantityAllocated: 0,
|
||||||
|
__typename: "Stock"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "stock_test_id2",
|
||||||
|
warehouse: {
|
||||||
|
name: "warehouse_stock2",
|
||||||
|
id:
|
||||||
|
"V2FyZWhvdXNlOjczYzI0OGNmLTliNzAtNDlmMi1hMDRlLTM4ZTYxMmQ5MDYwMQ==",
|
||||||
|
__typename: "Warehouse"
|
||||||
|
},
|
||||||
|
quantity: 166,
|
||||||
|
quantityAllocated: 0,
|
||||||
|
__typename: "Stock"
|
||||||
|
}
|
||||||
|
],
|
||||||
__typename: "ProductVariant"
|
__typename: "ProductVariant"
|
||||||
},
|
},
|
||||||
productName: "T-shirt",
|
productName: "T-shirt",
|
||||||
|
@ -1619,10 +2151,48 @@ describe("Merge repeated order lines of fulfillment lines", () => {
|
||||||
orderLine: {
|
orderLine: {
|
||||||
id: "T3JkZXJMaW5lOjQ1",
|
id: "T3JkZXJMaW5lOjQ1",
|
||||||
isShippingRequired: false,
|
isShippingRequired: false,
|
||||||
|
allocations: [
|
||||||
|
{
|
||||||
|
id: "allocation_test_id",
|
||||||
|
warehouse: {
|
||||||
|
id:
|
||||||
|
"V2FyZWhvdXNlOjk1NWY0ZDk2LWRmNTAtNGY0Zi1hOTM4LWM5MTYzYTA4YTViNg==",
|
||||||
|
__typename: "Warehouse"
|
||||||
|
},
|
||||||
|
quantity: 1,
|
||||||
|
__typename: "Allocation"
|
||||||
|
}
|
||||||
|
],
|
||||||
variant: {
|
variant: {
|
||||||
id: "UHJvZHVjdFZhcmlhbnQ6MzE3",
|
id: "UHJvZHVjdFZhcmlhbnQ6MzE3",
|
||||||
quantityAvailable: 50,
|
quantityAvailable: 50,
|
||||||
preorder: null,
|
preorder: null,
|
||||||
|
stocks: [
|
||||||
|
{
|
||||||
|
id: "stock_test_id1",
|
||||||
|
warehouse: {
|
||||||
|
name: "warehouse_stock1",
|
||||||
|
id:
|
||||||
|
"V2FyZWhvdXNlOjc4OGUyMGRlLTlmYTAtNDI5My1iZDk2LWUwM2RjY2RhMzc0ZQ==",
|
||||||
|
__typename: "Warehouse"
|
||||||
|
},
|
||||||
|
quantity: 166,
|
||||||
|
quantityAllocated: 0,
|
||||||
|
__typename: "Stock"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "stock_test_id2",
|
||||||
|
warehouse: {
|
||||||
|
name: "warehouse_stock2",
|
||||||
|
id:
|
||||||
|
"V2FyZWhvdXNlOjczYzI0OGNmLTliNzAtNDlmMi1hMDRlLTM4ZTYxMmQ5MDYwMQ==",
|
||||||
|
__typename: "Warehouse"
|
||||||
|
},
|
||||||
|
quantity: 166,
|
||||||
|
quantityAllocated: 0,
|
||||||
|
__typename: "Stock"
|
||||||
|
}
|
||||||
|
],
|
||||||
__typename: "ProductVariant"
|
__typename: "ProductVariant"
|
||||||
},
|
},
|
||||||
productName: "Lake Tunes",
|
productName: "Lake Tunes",
|
||||||
|
@ -1680,10 +2250,48 @@ describe("Merge repeated order lines of fulfillment lines", () => {
|
||||||
orderLine: {
|
orderLine: {
|
||||||
id: "T3JkZXJMaW5lOjQ1",
|
id: "T3JkZXJMaW5lOjQ1",
|
||||||
isShippingRequired: false,
|
isShippingRequired: false,
|
||||||
|
allocations: [
|
||||||
|
{
|
||||||
|
id: "allocation_test_id",
|
||||||
|
warehouse: {
|
||||||
|
id:
|
||||||
|
"V2FyZWhvdXNlOjk1NWY0ZDk2LWRmNTAtNGY0Zi1hOTM4LWM5MTYzYTA4YTViNg==",
|
||||||
|
__typename: "Warehouse"
|
||||||
|
},
|
||||||
|
quantity: 1,
|
||||||
|
__typename: "Allocation"
|
||||||
|
}
|
||||||
|
],
|
||||||
variant: {
|
variant: {
|
||||||
id: "UHJvZHVjdFZhcmlhbnQ6MzE3",
|
id: "UHJvZHVjdFZhcmlhbnQ6MzE3",
|
||||||
quantityAvailable: 50,
|
quantityAvailable: 50,
|
||||||
preorder: null,
|
preorder: null,
|
||||||
|
stocks: [
|
||||||
|
{
|
||||||
|
id: "stock_test_id1",
|
||||||
|
warehouse: {
|
||||||
|
name: "warehouse_stock1",
|
||||||
|
id:
|
||||||
|
"V2FyZWhvdXNlOjc4OGUyMGRlLTlmYTAtNDI5My1iZDk2LWUwM2RjY2RhMzc0ZQ==",
|
||||||
|
__typename: "Warehouse"
|
||||||
|
},
|
||||||
|
quantity: 166,
|
||||||
|
quantityAllocated: 0,
|
||||||
|
__typename: "Stock"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "stock_test_id2",
|
||||||
|
warehouse: {
|
||||||
|
name: "warehouse_stock2",
|
||||||
|
id:
|
||||||
|
"V2FyZWhvdXNlOjczYzI0OGNmLTliNzAtNDlmMi1hMDRlLTM4ZTYxMmQ5MDYwMQ==",
|
||||||
|
__typename: "Warehouse"
|
||||||
|
},
|
||||||
|
quantity: 166,
|
||||||
|
quantityAllocated: 0,
|
||||||
|
__typename: "Stock"
|
||||||
|
}
|
||||||
|
],
|
||||||
__typename: "ProductVariant"
|
__typename: "ProductVariant"
|
||||||
},
|
},
|
||||||
productName: "Lake Tunes",
|
productName: "Lake Tunes",
|
||||||
|
@ -1741,10 +2349,48 @@ describe("Merge repeated order lines of fulfillment lines", () => {
|
||||||
orderLine: {
|
orderLine: {
|
||||||
id: "T3JkZXJMaW5lOjQ3",
|
id: "T3JkZXJMaW5lOjQ3",
|
||||||
isShippingRequired: true,
|
isShippingRequired: true,
|
||||||
|
allocations: [
|
||||||
|
{
|
||||||
|
id: "allocation_test_id",
|
||||||
|
warehouse: {
|
||||||
|
id:
|
||||||
|
"V2FyZWhvdXNlOjk1NWY0ZDk2LWRmNTAtNGY0Zi1hOTM4LWM5MTYzYTA4YTViNg==",
|
||||||
|
__typename: "Warehouse"
|
||||||
|
},
|
||||||
|
quantity: 1,
|
||||||
|
__typename: "Allocation"
|
||||||
|
}
|
||||||
|
],
|
||||||
variant: {
|
variant: {
|
||||||
id: "UHJvZHVjdFZhcmlhbnQ6Mjg2",
|
id: "UHJvZHVjdFZhcmlhbnQ6Mjg2",
|
||||||
quantityAvailable: 50,
|
quantityAvailable: 50,
|
||||||
preorder: null,
|
preorder: null,
|
||||||
|
stocks: [
|
||||||
|
{
|
||||||
|
id: "stock_test_id1",
|
||||||
|
warehouse: {
|
||||||
|
name: "warehouse_stock1",
|
||||||
|
id:
|
||||||
|
"V2FyZWhvdXNlOjc4OGUyMGRlLTlmYTAtNDI5My1iZDk2LWUwM2RjY2RhMzc0ZQ==",
|
||||||
|
__typename: "Warehouse"
|
||||||
|
},
|
||||||
|
quantity: 166,
|
||||||
|
quantityAllocated: 0,
|
||||||
|
__typename: "Stock"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "stock_test_id2",
|
||||||
|
warehouse: {
|
||||||
|
name: "warehouse_stock2",
|
||||||
|
id:
|
||||||
|
"V2FyZWhvdXNlOjczYzI0OGNmLTliNzAtNDlmMi1hMDRlLTM4ZTYxMmQ5MDYwMQ==",
|
||||||
|
__typename: "Warehouse"
|
||||||
|
},
|
||||||
|
quantity: 166,
|
||||||
|
quantityAllocated: 0,
|
||||||
|
__typename: "Stock"
|
||||||
|
}
|
||||||
|
],
|
||||||
__typename: "ProductVariant"
|
__typename: "ProductVariant"
|
||||||
},
|
},
|
||||||
productName: "T-shirt",
|
productName: "T-shirt",
|
||||||
|
|
|
@ -3,13 +3,15 @@ import {
|
||||||
AddressFragment,
|
AddressFragment,
|
||||||
AddressInput,
|
AddressInput,
|
||||||
CountryCode,
|
CountryCode,
|
||||||
|
FulfillmentFragment,
|
||||||
FulfillmentStatus,
|
FulfillmentStatus,
|
||||||
FulfillOrderMutation,
|
|
||||||
OrderDetailsFragment,
|
OrderDetailsFragment,
|
||||||
OrderErrorCode,
|
OrderFulfillLineFragment,
|
||||||
OrderFulfillDataQuery,
|
OrderFulfillStockInput,
|
||||||
OrderLineFragment,
|
OrderLineFragment,
|
||||||
|
OrderLineStockDataFragment,
|
||||||
OrderRefundDataQuery,
|
OrderRefundDataQuery,
|
||||||
|
StockFragment,
|
||||||
WarehouseFragment
|
WarehouseFragment
|
||||||
} from "@saleor/graphql";
|
} from "@saleor/graphql";
|
||||||
import { FormsetData } from "@saleor/hooks/useFormset";
|
import { FormsetData } from "@saleor/hooks/useFormset";
|
||||||
|
@ -36,9 +38,7 @@ export interface OrderLineWithStockWarehouses {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getToFulfillOrderLines(
|
export function getToFulfillOrderLines(lines?: OrderLineStockDataFragment[]) {
|
||||||
lines?: OrderFulfillDataQuery["order"]["lines"]
|
|
||||||
) {
|
|
||||||
return lines?.filter(line => line.quantityToFulfill > 0) || [];
|
return lines?.filter(line => line.quantityToFulfill > 0) || [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -276,31 +276,6 @@ export function mergeRepeatedOrderLines(
|
||||||
}, Array<OrderDetailsFragment["fulfillments"][0]["lines"][0]>());
|
}, Array<OrderDetailsFragment["fulfillments"][0]["lines"][0]>());
|
||||||
}
|
}
|
||||||
|
|
||||||
export const isStockError = (
|
|
||||||
overfulfill: boolean,
|
|
||||||
formsetStock: { quantity: number },
|
|
||||||
availableQuantity: number,
|
|
||||||
warehouse: WarehouseFragment,
|
|
||||||
line: OrderFulfillDataQuery["order"]["lines"][0],
|
|
||||||
errors: FulfillOrderMutation["orderFulfill"]["errors"]
|
|
||||||
) => {
|
|
||||||
if (overfulfill) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
const isQuantityLargerThanAvailable =
|
|
||||||
line.variant.trackInventory && formsetStock.quantity > availableQuantity;
|
|
||||||
|
|
||||||
const isError = !!errors?.find(
|
|
||||||
err =>
|
|
||||||
err.warehouse === warehouse.id &&
|
|
||||||
err.orderLines.find((id: string) => id === line.id) &&
|
|
||||||
err.code === OrderErrorCode.INSUFFICIENT_STOCK
|
|
||||||
);
|
|
||||||
|
|
||||||
return isQuantityLargerThanAvailable || isError;
|
|
||||||
};
|
|
||||||
|
|
||||||
export function addressToAddressInput<T>(
|
export function addressToAddressInput<T>(
|
||||||
address: T & AddressFragment
|
address: T & AddressFragment
|
||||||
): AddressInput {
|
): AddressInput {
|
||||||
|
@ -324,3 +299,83 @@ export const getVariantSearchAddress = (
|
||||||
|
|
||||||
return { country: order.channel.defaultCountry.code as CountryCode };
|
return { country: order.channel.defaultCountry.code as CountryCode };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getAllocatedQuantityForLine = (
|
||||||
|
line: OrderLineStockDataFragment,
|
||||||
|
warehouseId: string
|
||||||
|
) => {
|
||||||
|
const warehouseAllocation = line.allocations.find(
|
||||||
|
allocation => allocation.warehouse.id === warehouseId
|
||||||
|
);
|
||||||
|
return warehouseAllocation?.quantity || 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getOrderLineAvailableQuantity = (
|
||||||
|
line: OrderLineStockDataFragment,
|
||||||
|
stock: StockFragment
|
||||||
|
) => {
|
||||||
|
if (!stock) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
const allocatedQuantityForLine = getAllocatedQuantityForLine(
|
||||||
|
line,
|
||||||
|
stock.warehouse.id
|
||||||
|
);
|
||||||
|
|
||||||
|
const availableQuantity =
|
||||||
|
stock.quantity - stock.quantityAllocated + allocatedQuantityForLine;
|
||||||
|
|
||||||
|
return availableQuantity;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type OrderFulfillStockInputFormsetData = Array<
|
||||||
|
Pick<FormsetData<null, OrderFulfillStockInput[]>[0], "id" | "value">
|
||||||
|
>;
|
||||||
|
|
||||||
|
export const getFulfillmentFormsetQuantity = (
|
||||||
|
formsetData: OrderFulfillStockInputFormsetData,
|
||||||
|
line: OrderLineStockDataFragment
|
||||||
|
) => formsetData?.find(getById(line.id))?.value?.[0]?.quantity;
|
||||||
|
|
||||||
|
export const getWarehouseStock = (
|
||||||
|
stocks: StockFragment[],
|
||||||
|
warehouseId: string
|
||||||
|
) => stocks?.find(stock => stock.warehouse.id === warehouseId);
|
||||||
|
|
||||||
|
export const isLineAvailableInWarehouse = (
|
||||||
|
line: OrderLineStockDataFragment,
|
||||||
|
warehouse: WarehouseFragment
|
||||||
|
) => {
|
||||||
|
if (!line?.variant?.stocks) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const stock = getWarehouseStock(line.variant.stocks, warehouse.id);
|
||||||
|
if (stock) {
|
||||||
|
return line.quantityToFulfill <= getOrderLineAvailableQuantity(line, stock);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const transformFuflillmentLinesToStockInputFormsetData = (
|
||||||
|
lines: FulfillmentFragment["lines"],
|
||||||
|
warehouseId: string
|
||||||
|
): OrderFulfillStockInputFormsetData =>
|
||||||
|
lines?.map(line => ({
|
||||||
|
data: null,
|
||||||
|
id: line.orderLine.id,
|
||||||
|
value: [
|
||||||
|
{
|
||||||
|
quantity: line.quantity,
|
||||||
|
warehouse: warehouseId
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}));
|
||||||
|
|
||||||
|
export const getAttributesCaption = (
|
||||||
|
attributes: OrderFulfillLineFragment["variant"]["attributes"]
|
||||||
|
): string =>
|
||||||
|
attributes
|
||||||
|
.map(attribute =>
|
||||||
|
attribute.values.map(attributeValue => attributeValue.name).join(", ")
|
||||||
|
)
|
||||||
|
.join(" / ");
|
||||||
|
|
|
@ -1,18 +1,25 @@
|
||||||
import { WindowTitle } from "@saleor/components/WindowTitle";
|
import { WindowTitle } from "@saleor/components/WindowTitle";
|
||||||
import {
|
import {
|
||||||
|
FulfillmentFragment,
|
||||||
FulfillmentStatus,
|
FulfillmentStatus,
|
||||||
|
OrderDetailsQueryResult,
|
||||||
OrderFulfillmentApproveMutation,
|
OrderFulfillmentApproveMutation,
|
||||||
OrderFulfillmentApproveMutationVariables,
|
OrderFulfillmentApproveMutationVariables,
|
||||||
OrderUpdateMutation,
|
OrderUpdateMutation,
|
||||||
OrderUpdateMutationVariables,
|
OrderUpdateMutationVariables,
|
||||||
useCustomerAddressesQuery,
|
useCustomerAddressesQuery,
|
||||||
useWarehouseListQuery
|
useWarehouseListQuery,
|
||||||
|
WarehouseFragment
|
||||||
} from "@saleor/graphql";
|
} from "@saleor/graphql";
|
||||||
import useNavigator from "@saleor/hooks/useNavigator";
|
import useNavigator from "@saleor/hooks/useNavigator";
|
||||||
import OrderCannotCancelOrderDialog from "@saleor/orders/components/OrderCannotCancelOrderDialog";
|
import OrderCannotCancelOrderDialog from "@saleor/orders/components/OrderCannotCancelOrderDialog";
|
||||||
|
import OrderChangeWarehouseDialog from "@saleor/orders/components/OrderChangeWarehouseDialog";
|
||||||
import { OrderCustomerAddressesEditDialogOutput } from "@saleor/orders/components/OrderCustomerAddressesEditDialog/types";
|
import { OrderCustomerAddressesEditDialogOutput } from "@saleor/orders/components/OrderCustomerAddressesEditDialog/types";
|
||||||
import OrderFulfillmentApproveDialog from "@saleor/orders/components/OrderFulfillmentApproveDialog";
|
import OrderFulfillmentApproveDialog from "@saleor/orders/components/OrderFulfillmentApproveDialog";
|
||||||
|
import OrderFulfillStockExceededDialog from "@saleor/orders/components/OrderFulfillStockExceededDialog";
|
||||||
import OrderInvoiceEmailSendDialog from "@saleor/orders/components/OrderInvoiceEmailSendDialog";
|
import OrderInvoiceEmailSendDialog from "@saleor/orders/components/OrderInvoiceEmailSendDialog";
|
||||||
|
import { getById } from "@saleor/orders/components/OrderReturnPage/utils";
|
||||||
|
import { transformFuflillmentLinesToStockInputFormsetData } from "@saleor/orders/utils/data";
|
||||||
import { PartialMutationProviderOutput } from "@saleor/types";
|
import { PartialMutationProviderOutput } from "@saleor/types";
|
||||||
import { mapEdgesToItems } from "@saleor/utils/maps";
|
import { mapEdgesToItems } from "@saleor/utils/maps";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
@ -42,11 +49,12 @@ import {
|
||||||
OrderUrlQueryParams
|
OrderUrlQueryParams
|
||||||
} from "../../../urls";
|
} from "../../../urls";
|
||||||
import { isAnyAddressEditModalOpen } from "../OrderDraftDetails";
|
import { isAnyAddressEditModalOpen } from "../OrderDraftDetails";
|
||||||
|
import { useDefaultWarehouse } from "./useDefaultWarehouse";
|
||||||
|
|
||||||
interface OrderNormalDetailsProps {
|
interface OrderNormalDetailsProps {
|
||||||
id: string;
|
id: string;
|
||||||
params: OrderUrlQueryParams;
|
params: OrderUrlQueryParams;
|
||||||
data: any;
|
data: OrderDetailsQueryResult["data"];
|
||||||
orderAddNote: any;
|
orderAddNote: any;
|
||||||
orderInvoiceRequest: any;
|
orderInvoiceRequest: any;
|
||||||
handleSubmit: any;
|
handleSubmit: any;
|
||||||
|
@ -70,6 +78,10 @@ interface OrderNormalDetailsProps {
|
||||||
openModal: any;
|
openModal: any;
|
||||||
closeModal: any;
|
closeModal: any;
|
||||||
}
|
}
|
||||||
|
interface ApprovalState {
|
||||||
|
fulfillment: FulfillmentFragment;
|
||||||
|
notifyCustomer: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export const OrderNormalDetails: React.FC<OrderNormalDetailsProps> = ({
|
export const OrderNormalDetails: React.FC<OrderNormalDetailsProps> = ({
|
||||||
id,
|
id,
|
||||||
|
@ -96,13 +108,27 @@ export const OrderNormalDetails: React.FC<OrderNormalDetailsProps> = ({
|
||||||
const shop = data?.shop;
|
const shop = data?.shop;
|
||||||
const navigate = useNavigator();
|
const navigate = useNavigator();
|
||||||
|
|
||||||
const warehouses = useWarehouseListQuery({
|
const {
|
||||||
|
data: warehousesData,
|
||||||
|
loading: warehousesLoading
|
||||||
|
} = useWarehouseListQuery({
|
||||||
displayLoader: true,
|
displayLoader: true,
|
||||||
variables: {
|
variables: {
|
||||||
first: 30
|
first: 30
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const warehouses = mapEdgesToItems(warehousesData?.warehouses);
|
||||||
|
|
||||||
|
const [fulfillmentWarehouse, setFulfillmentWarehouse] = React.useState<
|
||||||
|
WarehouseFragment
|
||||||
|
>(null);
|
||||||
|
|
||||||
|
useDefaultWarehouse({ warehouses, order, setter: setFulfillmentWarehouse }, [
|
||||||
|
warehousesData,
|
||||||
|
warehousesLoading
|
||||||
|
]);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
data: customerAddresses,
|
data: customerAddresses,
|
||||||
loading: customerAddressesLoading
|
loading: customerAddressesLoading
|
||||||
|
@ -125,6 +151,22 @@ export const OrderNormalDetails: React.FC<OrderNormalDetailsProps> = ({
|
||||||
|
|
||||||
const handleBack = () => navigate(orderListUrl());
|
const handleBack = () => navigate(orderListUrl());
|
||||||
|
|
||||||
|
const [
|
||||||
|
currentApproval,
|
||||||
|
setCurrentApproval
|
||||||
|
] = React.useState<ApprovalState | null>(null);
|
||||||
|
const [stockExceeded, setStockExceeded] = React.useState(false);
|
||||||
|
const approvalErrors =
|
||||||
|
orderFulfillmentApprove.opts.data?.orderFulfillmentApprove.errors || [];
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (
|
||||||
|
approvalErrors.length &&
|
||||||
|
approvalErrors.every(err => err.code === "INSUFFICIENT_STOCK")
|
||||||
|
) {
|
||||||
|
setStockExceeded(true);
|
||||||
|
}
|
||||||
|
}, [approvalErrors]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<WindowTitle
|
<WindowTitle
|
||||||
|
@ -167,8 +209,11 @@ export const OrderNormalDetails: React.FC<OrderNormalDetailsProps> = ({
|
||||||
]
|
]
|
||||||
)}
|
)}
|
||||||
shippingMethods={data?.order?.shippingMethods || []}
|
shippingMethods={data?.order?.shippingMethods || []}
|
||||||
|
selectedWarehouse={fulfillmentWarehouse}
|
||||||
onOrderCancel={() => openModal("cancel")}
|
onOrderCancel={() => openModal("cancel")}
|
||||||
onOrderFulfill={() => navigate(orderFulfillUrl(id))}
|
onOrderFulfill={() =>
|
||||||
|
navigate(orderFulfillUrl(id, { warehouse: fulfillmentWarehouse?.id }))
|
||||||
|
}
|
||||||
onFulfillmentApprove={fulfillmentId =>
|
onFulfillmentApprove={fulfillmentId =>
|
||||||
navigate(
|
navigate(
|
||||||
orderUrl(id, {
|
orderUrl(id, {
|
||||||
|
@ -213,6 +258,7 @@ export const OrderNormalDetails: React.FC<OrderNormalDetailsProps> = ({
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
onInvoiceSend={id => openModal("invoice-send", { id })}
|
onInvoiceSend={id => openModal("invoice-send", { id })}
|
||||||
|
onWarehouseChange={() => openModal("change-warehouse")}
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
/>
|
/>
|
||||||
<OrderCannotCancelOrderDialog
|
<OrderCannotCancelOrderDialog
|
||||||
|
@ -279,21 +325,44 @@ export const OrderNormalDetails: React.FC<OrderNormalDetailsProps> = ({
|
||||||
[]
|
[]
|
||||||
}
|
}
|
||||||
open={params.action === "approve-fulfillment"}
|
open={params.action === "approve-fulfillment"}
|
||||||
onConfirm={({ notifyCustomer }) =>
|
onConfirm={({ notifyCustomer }) => {
|
||||||
orderFulfillmentApprove.mutate({
|
setCurrentApproval({
|
||||||
|
fulfillment: order?.fulfillments.find(getById(params.id)),
|
||||||
|
notifyCustomer
|
||||||
|
});
|
||||||
|
return orderFulfillmentApprove.mutate({
|
||||||
id: params.id,
|
id: params.id,
|
||||||
notifyCustomer
|
notifyCustomer
|
||||||
})
|
});
|
||||||
}
|
}}
|
||||||
onClose={closeModal}
|
onClose={closeModal}
|
||||||
/>
|
/>
|
||||||
|
<OrderFulfillStockExceededDialog
|
||||||
|
lines={currentApproval?.fulfillment.lines}
|
||||||
|
formsetData={transformFuflillmentLinesToStockInputFormsetData(
|
||||||
|
currentApproval?.fulfillment.lines,
|
||||||
|
currentApproval?.fulfillment.warehouse?.id
|
||||||
|
)}
|
||||||
|
open={stockExceeded}
|
||||||
|
warehouseId={currentApproval?.fulfillment.warehouse?.id}
|
||||||
|
onClose={() => setStockExceeded(false)}
|
||||||
|
confirmButtonState="default"
|
||||||
|
onSubmit={() => {
|
||||||
|
setStockExceeded(false);
|
||||||
|
return orderFulfillmentApprove.mutate({
|
||||||
|
id: params.id,
|
||||||
|
notifyCustomer: currentApproval?.notifyCustomer,
|
||||||
|
allowStockToBeExceeded: true
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
<OrderFulfillmentCancelDialog
|
<OrderFulfillmentCancelDialog
|
||||||
confirmButtonState={orderFulfillmentCancel.opts.status}
|
confirmButtonState={orderFulfillmentCancel.opts.status}
|
||||||
errors={
|
errors={
|
||||||
orderFulfillmentCancel.opts.data?.orderFulfillmentCancel.errors || []
|
orderFulfillmentCancel.opts.data?.orderFulfillmentCancel.errors || []
|
||||||
}
|
}
|
||||||
open={params.action === "cancel-fulfillment"}
|
open={params.action === "cancel-fulfillment"}
|
||||||
warehouses={mapEdgesToItems(warehouses?.data?.warehouses) || []}
|
warehouses={warehouses || []}
|
||||||
onConfirm={variables =>
|
onConfirm={variables =>
|
||||||
orderFulfillmentCancel.mutate({
|
orderFulfillmentCancel.mutate({
|
||||||
id: params.id,
|
id: params.id,
|
||||||
|
@ -325,6 +394,13 @@ export const OrderNormalDetails: React.FC<OrderNormalDetailsProps> = ({
|
||||||
}
|
}
|
||||||
onClose={closeModal}
|
onClose={closeModal}
|
||||||
/>
|
/>
|
||||||
|
<OrderChangeWarehouseDialog
|
||||||
|
open={params.action === "change-warehouse"}
|
||||||
|
lines={order?.lines}
|
||||||
|
currentWarehouse={fulfillmentWarehouse}
|
||||||
|
onConfirm={warehouse => setFulfillmentWarehouse(warehouse)}
|
||||||
|
onClose={closeModal}
|
||||||
|
/>
|
||||||
<OrderInvoiceEmailSendDialog
|
<OrderInvoiceEmailSendDialog
|
||||||
confirmButtonState={orderInvoiceSend.opts.status}
|
confirmButtonState={orderInvoiceSend.opts.status}
|
||||||
errors={orderInvoiceSend.opts.data?.invoiceSendEmail.errors || []}
|
errors={orderInvoiceSend.opts.data?.invoiceSendEmail.errors || []}
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
import { OrderDetailsFragment, WarehouseFragment } from "@saleor/graphql";
|
||||||
|
import {
|
||||||
|
getToFulfillOrderLines,
|
||||||
|
isLineAvailableInWarehouse
|
||||||
|
} from "@saleor/orders/utils/data";
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
export interface UseDefaultWarehouseOpts {
|
||||||
|
warehouses: WarehouseFragment[];
|
||||||
|
order: OrderDetailsFragment;
|
||||||
|
setter: React.Dispatch<React.SetStateAction<WarehouseFragment>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface WarehousesAvailibility {
|
||||||
|
warehouse: WarehouseFragment;
|
||||||
|
linesAvailable: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useDefaultWarehouse(
|
||||||
|
{ warehouses, order, setter }: UseDefaultWarehouseOpts,
|
||||||
|
deps: unknown[]
|
||||||
|
) {
|
||||||
|
React.useEffect(() => {
|
||||||
|
const warehousesAvailability: WarehousesAvailibility[] = warehouses?.map(
|
||||||
|
warehouse => {
|
||||||
|
if (!order?.lines) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
const linesToFulfill = getToFulfillOrderLines(order.lines);
|
||||||
|
|
||||||
|
const linesAvailable = linesToFulfill.filter(line =>
|
||||||
|
isLineAvailableInWarehouse(line, warehouse)
|
||||||
|
).length;
|
||||||
|
|
||||||
|
return {
|
||||||
|
warehouse,
|
||||||
|
linesAvailable
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
const defaultWarehouse = order?.lines
|
||||||
|
? warehousesAvailability?.reduce((prev, curr) =>
|
||||||
|
curr.linesAvailable > prev.linesAvailable ? curr : prev
|
||||||
|
).warehouse
|
||||||
|
: undefined;
|
||||||
|
setter(defaultWarehouse);
|
||||||
|
}, [order, ...deps]);
|
||||||
|
}
|
|
@ -1,43 +1,27 @@
|
||||||
import { WindowTitle } from "@saleor/components/WindowTitle";
|
import { WindowTitle } from "@saleor/components/WindowTitle";
|
||||||
import {
|
import {
|
||||||
OrderFulfillDataQuery,
|
|
||||||
useFulfillOrderMutation,
|
useFulfillOrderMutation,
|
||||||
useOrderFulfillDataQuery,
|
useOrderFulfillDataQuery,
|
||||||
|
useOrderFulfillmentUpdateTrackingMutation,
|
||||||
useOrderFulfillSettingsQuery,
|
useOrderFulfillSettingsQuery,
|
||||||
WarehouseClickAndCollectOptionEnum,
|
useWarehouseDetailsQuery
|
||||||
WarehouseFragment
|
|
||||||
} from "@saleor/graphql";
|
} from "@saleor/graphql";
|
||||||
import useNavigator from "@saleor/hooks/useNavigator";
|
import useNavigator from "@saleor/hooks/useNavigator";
|
||||||
import useNotifier from "@saleor/hooks/useNotifier";
|
import useNotifier from "@saleor/hooks/useNotifier";
|
||||||
import { extractMutationErrors } from "@saleor/misc";
|
import { getMutationErrors } from "@saleor/misc";
|
||||||
import OrderFulfillPage from "@saleor/orders/components/OrderFulfillPage";
|
import OrderFulfillPage, {
|
||||||
import { orderUrl } from "@saleor/orders/urls";
|
OrderFulfillSubmitData
|
||||||
import { getWarehousesFromOrderLines } from "@saleor/orders/utils/data";
|
} from "@saleor/orders/components/OrderFulfillPage";
|
||||||
|
import { OrderFulfillUrlQueryParams, orderUrl } from "@saleor/orders/urls";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { useIntl } from "react-intl";
|
import { useIntl } from "react-intl";
|
||||||
|
|
||||||
export interface OrderFulfillProps {
|
export interface OrderFulfillProps {
|
||||||
orderId: string;
|
orderId: string;
|
||||||
|
params: OrderFulfillUrlQueryParams;
|
||||||
}
|
}
|
||||||
|
|
||||||
const resolveLocalFulfillment = (
|
const OrderFulfill: React.FC<OrderFulfillProps> = ({ orderId, params }) => {
|
||||||
order: OrderFulfillDataQuery["order"],
|
|
||||||
orderLineWarehouses: WarehouseFragment[]
|
|
||||||
) => {
|
|
||||||
const deliveryMethod = order?.deliveryMethod;
|
|
||||||
if (
|
|
||||||
deliveryMethod?.__typename === "Warehouse" &&
|
|
||||||
deliveryMethod?.clickAndCollectOption ===
|
|
||||||
WarehouseClickAndCollectOptionEnum.LOCAL
|
|
||||||
) {
|
|
||||||
return orderLineWarehouses?.filter(
|
|
||||||
warehouse => warehouse?.id === deliveryMethod?.id
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return orderLineWarehouses;
|
|
||||||
};
|
|
||||||
|
|
||||||
const OrderFulfill: React.FC<OrderFulfillProps> = ({ orderId }) => {
|
|
||||||
const navigate = useNavigator();
|
const navigate = useNavigator();
|
||||||
const notify = useNotifier();
|
const notify = useNotifier();
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
@ -54,7 +38,7 @@ const OrderFulfill: React.FC<OrderFulfillProps> = ({ orderId }) => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const orderLinesWarehouses = getWarehousesFromOrderLines(data?.order?.lines);
|
const [updateTracking] = useOrderFulfillmentUpdateTrackingMutation();
|
||||||
|
|
||||||
const [fulfillOrder, fulfillOrderOpts] = useFulfillOrderMutation({
|
const [fulfillOrder, fulfillOrderOpts] = useFulfillOrderMutation({
|
||||||
onCompleted: data => {
|
onCompleted: data => {
|
||||||
|
@ -71,10 +55,11 @@ const OrderFulfill: React.FC<OrderFulfillProps> = ({ orderId }) => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const resolvedOrderLinesWarehouses = resolveLocalFulfillment(
|
const { data: warehouseData } = useWarehouseDetailsQuery({
|
||||||
data?.order,
|
variables: {
|
||||||
orderLinesWarehouses
|
id: params?.warehouse
|
||||||
);
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -100,26 +85,44 @@ const OrderFulfill: React.FC<OrderFulfillProps> = ({ orderId }) => {
|
||||||
loading={loading || settingsLoading || fulfillOrderOpts.loading}
|
loading={loading || settingsLoading || fulfillOrderOpts.loading}
|
||||||
errors={fulfillOrderOpts.data?.orderFulfill.errors}
|
errors={fulfillOrderOpts.data?.orderFulfill.errors}
|
||||||
onBack={() => navigate(orderUrl(orderId))}
|
onBack={() => navigate(orderUrl(orderId))}
|
||||||
onSubmit={formData =>
|
onSubmit={async (formData: OrderFulfillSubmitData) => {
|
||||||
extractMutationErrors(
|
const res = await fulfillOrder({
|
||||||
fulfillOrder({
|
variables: {
|
||||||
variables: {
|
input: {
|
||||||
input: {
|
lines: formData.items
|
||||||
lines: formData.items.map(line => ({
|
.filter(line => !!line?.value)
|
||||||
|
.map(line => ({
|
||||||
orderLineId: line.id,
|
orderLineId: line.id,
|
||||||
stocks: line.value
|
stocks: line.value
|
||||||
})),
|
})),
|
||||||
|
notifyCustomer:
|
||||||
|
settings?.shop?.fulfillmentAutoApprove && formData.sendInfo,
|
||||||
|
allowStockToBeExceeded: formData.allowStockToBeExceeded
|
||||||
|
},
|
||||||
|
orderId
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const fulfillments = res?.data?.orderFulfill?.order?.fulfillments;
|
||||||
|
if (fulfillments && formData.trackingNumber) {
|
||||||
|
updateTracking({
|
||||||
|
variables: {
|
||||||
|
id: fulfillments[fulfillments.length - 1].id,
|
||||||
|
input: {
|
||||||
|
...(formData?.trackingNumber && {
|
||||||
|
trackingNumber: formData.trackingNumber
|
||||||
|
}),
|
||||||
notifyCustomer:
|
notifyCustomer:
|
||||||
settings?.shop?.fulfillmentAutoApprove && formData.sendInfo
|
settings?.shop?.fulfillmentAutoApprove && formData.sendInfo
|
||||||
},
|
}
|
||||||
orderId
|
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
)
|
}
|
||||||
}
|
return getMutationErrors(res);
|
||||||
|
}}
|
||||||
order={data?.order}
|
order={data?.order}
|
||||||
saveButtonBar="default"
|
saveButtonBar={fulfillOrderOpts.status}
|
||||||
warehouses={resolvedOrderLinesWarehouses}
|
warehouse={warehouseData?.warehouse}
|
||||||
shopSettings={settings?.shop}
|
shopSettings={settings?.shop}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
|
|
|
@ -11,6 +11,7 @@ export const searchWarehouses = gql`
|
||||||
search: warehouses(
|
search: warehouses(
|
||||||
after: $after
|
after: $after
|
||||||
first: $first
|
first: $first
|
||||||
|
sortBy: { direction: ASC, field: NAME }
|
||||||
filter: { search: $query }
|
filter: { search: $query }
|
||||||
) {
|
) {
|
||||||
edges {
|
edges {
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -20,6 +20,7 @@ const order = orderFixture(placeholderImage);
|
||||||
|
|
||||||
const props: Omit<OrderDetailsPageProps, "classes"> = {
|
const props: Omit<OrderDetailsPageProps, "classes"> = {
|
||||||
disabled: false,
|
disabled: false,
|
||||||
|
selectedWarehouse: undefined,
|
||||||
onBack: () => undefined,
|
onBack: () => undefined,
|
||||||
onBillingAddressEdit: undefined,
|
onBillingAddressEdit: undefined,
|
||||||
onFulfillmentApprove: () => undefined,
|
onFulfillmentApprove: () => undefined,
|
||||||
|
@ -39,6 +40,7 @@ const props: Omit<OrderDetailsPageProps, "classes"> = {
|
||||||
onProductClick: undefined,
|
onProductClick: undefined,
|
||||||
onProfileView: () => undefined,
|
onProfileView: () => undefined,
|
||||||
onShippingAddressEdit: undefined,
|
onShippingAddressEdit: undefined,
|
||||||
|
onWarehouseChange: undefined,
|
||||||
onSubmit: () => undefined,
|
onSubmit: () => undefined,
|
||||||
order,
|
order,
|
||||||
shop: shopFixture,
|
shop: shopFixture,
|
||||||
|
|
Loading…
Reference in a new issue