Repository: saleor/saleor-dashboard
Pull request #5498: Fix quantity field value source for unfulfilled order products
Author: login: Cloud11PL
Labels: name: run pw-e2e
description: Run e2e (basic suite from PR automation)
Created: 2025-03-21T08:38:34Z | Merged: 2025-03-24T09:56:27Z

## Scope of the change

<!-- Describe changed made in this PR. You can attach screenshots or mention related issues as well. -->
* Shows `quantityToFulfill` in quantity filed for unfulfilled products in order details page

Before:
![CleanShot 2025-03-21 at 09 37 39](https://github.com/user-attachments/assets/6d092296-9ebc-41c6-8716-a0adbdb71e0b)


After:
![CleanShot 2025-03-21 at 09 36 23](https://github.com/user-attachments/assets/68860153-ee4d-4d2b-91d0-a1d9a738be7b)



Diff:
diff --git a/.changeset/shiny-olives-pretend.md b/.changeset/shiny-olives-pretend.md
new file mode 100644
index 00000000000..0b2cee7308e
--- /dev/null
+++ b/.changeset/shiny-olives-pretend.md
@@ -0,0 +1,5 @@
+---
+"saleor-dashboard": patch
+---
+
+You can now see the correct quantity for unfulfilled lines in Order details page.
diff --git a/src/orders/components/OrderUnfulfilledProductsCard/OrderUnfulfilledProductsCard.tsx b/src/orders/components/OrderUnfulfilledProductsCard/OrderUnfulfilledProductsCard.tsx
index 3e9405eab70..991aa2ac0ff 100644
--- a/src/orders/components/OrderUnfulfilledProductsCard/OrderUnfulfilledProductsCard.tsx
+++ b/src/orders/components/OrderUnfulfilledProductsCard/OrderUnfulfilledProductsCard.tsx
@@ -10,6 +10,7 @@ import { FormattedMessage } from "react-intl";
 import OrderCardTitle from "../OrderCardTitle";
 import { OrderDetailsDatagrid } from "../OrderDetailsDatagrid";
 import { useStyles } from "./styles";
+import { toLineWithUnfulfilledQuantity } from "./utils";
 
 interface OrderUnfulfilledProductsCardProps {
   showFulfillmentAction: boolean;
@@ -39,7 +40,11 @@ const OrderUnfulfilledProductsCard: React.FC<OrderUnfulfilledProductsCardProps>
       <DashboardCard>
         <OrderCardTitle withStatus status="unfulfilled" className={classes.cardTitle} />
         <DashboardCard.Content paddingX={0}>
-          <OrderDetailsDatagrid lines={lines} loading={loading} onShowMetadata={onShowMetadata} />
+          <OrderDetailsDatagrid
+            lines={toLineWithUnfulfilledQuantity(lines)}
+            loading={loading}
+            onShowMetadata={onShowMetadata}
+          />
           {showFulfillmentAction && (
             <DashboardCard.BottomActions justifyContent="flex-end">
               <Button
diff --git a/src/orders/components/OrderUnfulfilledProductsCard/utils.test.ts b/src/orders/components/OrderUnfulfilledProductsCard/utils.test.ts
new file mode 100644
index 00000000000..3101d8f2f63
--- /dev/null
+++ b/src/orders/components/OrderUnfulfilledProductsCard/utils.test.ts
@@ -0,0 +1,96 @@
+import { OrderLineFragment } from "@dashboard/graphql";
+
+import { toLineWithUnfulfilledQuantity } from "./utils";
+
+const line = {
+  __typename: "OrderLine",
+  id: "id==",
+  isShippingRequired: true,
+  allocations: [],
+  variant: {
+    __typename: "ProductVariant",
+    id: "variantId==",
+    name: "M",
+    quantityAvailable: 700,
+    preorder: null,
+    stocks: [],
+    product: {
+      __typename: "Product",
+      id: "UHJvZHVjdDoxMTY=",
+      isAvailableForPurchase: true,
+    },
+  },
+  productName: "Blue Hoodie 4",
+  productSku: "21599567",
+  isGift: false,
+  quantity: 5,
+  quantityFulfilled: 4,
+  quantityToFulfill: 1,
+  totalPrice: {
+    __typename: "TaxedMoney",
+    net: {
+      __typename: "Money",
+      amount: 20,
+      currency: "PLN",
+    },
+    gross: {
+      __typename: "Money",
+      amount: 20,
+      currency: "PLN",
+    },
+  },
+  unitDiscount: {
+    __typename: "Money",
+    amount: 0,
+    currency: "PLN",
+  },
+  unitDiscountValue: 0,
+  unitDiscountReason: null,
+  unitDiscountType: null,
+  undiscountedUnitPrice: {
+    __typename: "TaxedMoney",
+    currency: "PLN",
+    gross: {
+      __typename: "Money",
+      amount: 20,
+      currency: "PLN",
+    },
+    net: {
+      __typename: "Money",
+      amount: 20,
+      currency: "PLN",
+    },
+  },
+  unitPrice: {
+    __typename: "TaxedMoney",
+    gross: {
+      __typename: "Money",
+      amount: 20,
+      currency: "PLN",
+    },
+    net: {
+      __typename: "Money",
+      amount: 20,
+      currency: "PLN",
+    },
+  },
+  thumbnail: null,
+};
+
+describe("toLineWithUnfulfilledQuantity", () => {
+  it("should return lines with quantityToFulfill as quantity", () => {
+    // Arrange
+    const lines = [line] as OrderLineFragment[];
+
+    // Act
+    const result = toLineWithUnfulfilledQuantity(lines);
+
+    // Assert
+    expect(result).toEqual([
+      {
+        ...line,
+        quantity: line.quantityToFulfill,
+      },
+    ]);
+  });
+});
diff --git a/src/orders/components/OrderUnfulfilledProductsCard/utils.ts b/src/orders/components/OrderUnfulfilledProductsCard/utils.ts
new file mode 100644
index 00000000000..aafd2c80bdb
--- /dev/null
+++ b/src/orders/components/OrderUnfulfilledProductsCard/utils.ts
@@ -0,0 +1,10 @@
+import { OrderLineFragment } from "@dashboard/graphql";
+
+export const toLineWithUnfulfilledQuantity = (lines: OrderLineFragment[]) =>
+  lines.map(({ quantityToFulfill, ...rest }) => ({
+    ...rest,
+    quantityToFulfill,
+    // 'OrderLineFragment.quantity' is the total ordered quantity
+    // When order is unfulfilled, we should show the remaining quantity to fulfill as the quantity.
+    quantity: quantityToFulfill,
+  }));


Reviews:
user: login: poulch
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-21T13:48:17Z
user: login: Cloud11PL
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-21T14:14:21Z
user: login: poulch
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-24T08:30:42Z
user: login: poulch
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-24T08:39:11Z
user: login: Cloud11PL
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-24T09:08:02Z
user: login: poulch
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-24T09:32:15Z

Review comments:
diff_hunk: @@ -20,6 +20,13 @@ interface OrderUnfulfilledProductsCardProps {
   onShowMetadata: (id: string) => void;
 }
 
+const toLineWithUnfulfilledQuantity = (lines: OrderLineFragment[]) =>
+  lines.map(({ quantityToFulfill, ...rest }) => ({
+    ...rest,
+    quantityToFulfill,
+    quantity: quantityToFulfill,
+  }));
path: src/orders/components/OrderUnfulfilledProductsCard/OrderUnfulfilledProductsCard.tsx
user: login: poulch
body: Any test, any information why you did it
created_at: 2025-03-21T13:48:14Z
author_association: CONTRIBUTOR
diff_hunk: @@ -20,6 +20,13 @@ interface OrderUnfulfilledProductsCardProps {
   onShowMetadata: (id: string) => void;
 }
 
+const toLineWithUnfulfilledQuantity = (lines: OrderLineFragment[]) =>
+  lines.map(({ quantityToFulfill, ...rest }) => ({
+    ...rest,
+    quantityToFulfill,
+    quantity: quantityToFulfill,
+  }));
path: src/orders/components/OrderUnfulfilledProductsCard/OrderUnfulfilledProductsCard.tsx
user: login: Cloud11PL
body: I'll add a comment, but I'm not sure there's any meaningful test to add here 
created_at: 2025-03-21T14:14:21Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,10 @@
+import { OrderLineFragment } from "@dashboard/graphql";
+
+export const toLineWithUnfulfilledQuantity = (lines: OrderLineFragment[]) =>
path: src/orders/components/OrderUnfulfilledProductsCard/utils.ts
user: login: poulch
body: suggestion: it's asking for a unit test
created_at: 2025-03-24T08:30:42Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,10 @@
+import { OrderLineFragment } from "@dashboard/graphql";
+
+export const toLineWithUnfulfilledQuantity = (lines: OrderLineFragment[]) =>
+  lines.map(({ quantityToFulfill, ...rest }) => ({
+    ...rest,
+    quantityToFulfill,
path: src/orders/components/OrderUnfulfilledProductsCard/utils.ts
user: login: poulch
body: question: do you need this?
created_at: 2025-03-24T08:39:11Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,10 @@
+import { OrderLineFragment } from "@dashboard/graphql";
+
+export const toLineWithUnfulfilledQuantity = (lines: OrderLineFragment[]) =>
+  lines.map(({ quantityToFulfill, ...rest }) => ({
+    ...rest,
+    quantityToFulfill,
path: src/orders/components/OrderUnfulfilledProductsCard/utils.ts
user: login: Cloud11PL
body: The component that consumes the lines expects this value (the type is generated from graphql)
created_at: 2025-03-24T09:08:02Z
author_association: CONTRIBUTOR

PR comments:
user: login: changeset-bot[bot]
created_at: 2025-03-21T08:38:38Z
body: ###  🦋  Changeset detected

Latest commit: 7a49a19b43848d2f0bc955ba99c9a1ba876244ad

**The changes in this PR will be included in the next version bump.**

<details><summary>This PR includes changesets to release 1 package</summary>

  | Name             | Type  |
| ---------------- | ----- |
| saleor-dashboard | Patch |

</details>

Not sure what this means? [Click here  to learn what changesets are](https://github.com/changesets/changesets/blob/main/docs/adding-a-changeset.md).

[Click here if you're a maintainer who wants to add another changeset to this PR](https://github.com/saleor/saleor-dashboard/new/BCK-1569-quantity-values-fix-fulfilled?filename=.changeset/stupid-dogs-repair.md&value=---%0A%22saleor-dashboard%22%3A%20patch%0A---%0A%0AFix%20quantity%20field%20value%20source%20for%20unfulfilled%20order%20products%0A)


author_association: NONE
user: login: codecov[bot]
created_at: 2025-03-21T08:39:09Z
body: ## [Codecov](https://app.codecov.io/gh/saleor/saleor-dashboard/pull/5498?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=saleor) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 63.08%. Comparing base [(`f152c5b`)](https://app.codecov.io/gh/saleor/saleor-dashboard/commit/f152c5b91868d716d2213c468f70ffac165d206a?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=saleor) to head [(`7a49a19`)](https://app.codecov.io/gh/saleor/saleor-dashboard/commit/7a49a19b43848d2f0bc955ba99c9a1ba876244ad?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=saleor).
> Report is 1 commits behind head on main.

<details><summary>Additional details and impacted files</summary>


```diff
@@           Coverage Diff            @@
##             main    #5498    +/-   ##
========================================
  Coverage   63.08%   63.08%            
========================================
  Files        1288     1289     +1     
  Lines       22306    22308     +2     
  Branches     4435     4593   +158     
========================================
+ Hits        14072    14074     +2     
  Misses       8198     8198            
  Partials       36       36            
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/saleor/saleor-dashboard/pull/5498?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=saleor).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=saleor).

<details><summary> :rocket: New features to boost your workflow: </summary>

- :snowflake: [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
- :package: [JS Bundle Analysis](https://docs.codecov.com/docs/javascript-bundle-analysis): Save yourself from yourself by tracking and limiting bundle sizes in JS merges.
</details>
author_association: NONE
user: login: github-actions[bot]
created_at: 2025-03-21T08:57:38Z
body: 

### merge-reports: [Run #5045](https://github.com/saleor/saleor-dashboard/actions/runs/14032047211#summary)

| **Tests 📝** | **Passed ✅** | **Failed ❌** | **Skipped ⏭️** | **Pending ⏳** | **Other ❓** | **Flaky 🍂** | **Duration ⏱️** |
| --- | --- | --- | --- | --- | --- | --- | --- |
| 235 | 235 | 0 | 0 | 0 | 0 | 0 | 5m21s |

### 🎉 **All tests passed!**



[Github Test Reporter](https://github.com/ctrf-io/github-test-reporter) by [CTRF](https://ctrf.io) 💚

🔄 This comment has been updated
<!-- CTRF PR COMMENT TAG: false -->
author_association: CONTRIBUTOR