Skip to content

Commit

Permalink
Disable refunds for nonrefundable transactions (#4824)
Browse files Browse the repository at this point in the history
* Disable refunds for empty transactions and non-refundable transactions

* Add changeset

* Extract messages

* Adjust for strict mode

* Lint

---------

Co-authored-by: Paweł Chyła <[email protected]>
  • Loading branch information
Droniu and poulch authored Apr 30, 2024
1 parent 7b7f222 commit 9c5e786
Show file tree
Hide file tree
Showing 6 changed files with 140 additions and 6 deletions.
5 changes: 5 additions & 0 deletions .changeset/gentle-mice-move.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"saleor-dashboard": minor
---

This change disallows creating refund for orders created in transaction flow channels that have no transactions, as well as orders that have no refundable transactions
6 changes: 6 additions & 0 deletions locale/defaultMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -3978,6 +3978,9 @@
"ORQvOg": {
"string": "You don't have permission to perform this action"
},
"OSnO9P": {
"string": "All transactions are non-refundable"
},
"OTDo9I": {
"context": "tab name",
"string": "All staff members"
Expand Down Expand Up @@ -6059,6 +6062,9 @@
"context": "section header",
"string": "Refund Order"
},
"brkxbY": {
"string": "There are no transactions to refund"
},
"bsP4f3": {
"string": "No tokens found"
},
Expand Down
29 changes: 24 additions & 5 deletions src/orders/components/OrderRefundDatagrid/OrderRefundDatagrid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { Link } from "react-router-dom";
import { createGetCellContent, useDatagridOpts, useOrderRefundStaticColumns } from "./datagrid";
import { refundGridMessages } from "./messages";
import { manualRefundsExtractor, mergeRefunds } from "./refunds";
import { getNotEditabledRefundMessage, isRefundEditable } from "./utils";
import { canAddRefund, getNotEditabledRefundMessage, isRefundEditable } from "./utils";

interface OrderRefundDatagridProps {
orderId: string;
Expand Down Expand Up @@ -76,16 +76,35 @@ export const OrderRefundDatagrid: React.FC<OrderRefundDatagridProps> = ({
];
}, []);

const isRefundPossible = canAddRefund({
transactions: order?.transactions,
intl,
});

return (
<DashboardCard>
<Box paddingX={6} paddingTop={6} display="flex" justifyContent="space-between">
<Text size={5} fontWeight="bold">
<FormattedMessage {...refundGridMessages.refundSection} />
</Text>
<Button variant="secondary" onClick={onRefundAdd}>
<PlusIcon />
<FormattedMessage {...refundGridMessages.addNewRefund} />
</Button>
<Tooltip>
<Tooltip.Trigger>
<Button
variant="secondary"
onClick={onRefundAdd}
disabled={!isRefundPossible.canRefund}
>
<PlusIcon />
<FormattedMessage {...refundGridMessages.addNewRefund} />
</Button>
</Tooltip.Trigger>
{!isRefundPossible.canRefund && (
<Tooltip.Content>
<Tooltip.Arrow />
{isRefundPossible.reason}
</Tooltip.Content>
)}
</Tooltip>
</Box>
<DatagridChangeStateContext.Provider value={datagrid}>
<Datagrid
Expand Down
8 changes: 8 additions & 0 deletions src/orders/components/OrderRefundDatagrid/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,14 @@ export const refundGridMessages = defineMessages({
id: "FZTrzW",
defaultMessage: "Manual refund",
},
noTransactionsToRefund: {
id: "brkxbY",
defaultMessage: "There are no transactions to refund",
},
allTransactionsNonRefundable: {
id: "OSnO9P",
defaultMessage: "All transactions are non-refundable",
},
});

export const refundStatuses = defineMessages({
Expand Down
59 changes: 59 additions & 0 deletions src/orders/components/OrderRefundDatagrid/utils.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { OrderDetailsFragment, TransactionActionEnum } from "@dashboard/graphql";
import { intlMock } from "@test/intl";

import { refundGridMessages } from "./messages";
import { canAddRefund } from "./utils";

describe("canAddRefund", () => {
it("returns false when there are no transactions", () => {
// Arrange
const transactions: OrderDetailsFragment["transactions"] = [];
const intl = intlMock;

// Act
const result = canAddRefund({ transactions, intl });

// Assert
expect(result).toEqual({
canRefund: false,
reason: refundGridMessages.noTransactionsToRefund.defaultMessage,
});
});

it("returns false when all transactions are non-refundable", () => {
// Arrange
const transactions = [
{
actions: [TransactionActionEnum.CHARGE, TransactionActionEnum.CANCEL],
},
] as unknown as OrderDetailsFragment["transactions"];
const intl = intlMock;

// Act
const result = canAddRefund({ transactions, intl });

// Assert
expect(result).toEqual({
canRefund: false,
reason: refundGridMessages.allTransactionsNonRefundable.defaultMessage,
});
});

it("returns true when there is at least one refundable transaction", () => {
// Arrange
const transactions = [
{
actions: [TransactionActionEnum.REFUND],
},
] as unknown as OrderDetailsFragment["transactions"];
const intl = intlMock;

// Act
const result = canAddRefund({ transactions, intl });

// Assert
expect(result).toEqual({
canRefund: true,
});
});
});
39 changes: 38 additions & 1 deletion src/orders/components/OrderRefundDatagrid/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import { OrderGrantedRefundStatusEnum } from "@dashboard/graphql";
import {
OrderDetailsFragment,
OrderGrantedRefundStatusEnum,
TransactionActionEnum,
} from "@dashboard/graphql";
import { PillStatusType } from "@dashboard/misc";
import { IntlShape } from "react-intl";

Expand Down Expand Up @@ -87,3 +91,36 @@ export const getNotEditabledRefundMessage = (refund?: DatagridRefund) => {

return refundGridMessages.notEditablePending;
};

interface CanAddRefund {
canRefund: boolean;
reason?: string;
}

export const canAddRefund = ({
transactions,
intl,
}: {
transactions: OrderDetailsFragment["transactions"];
intl: IntlShape;
}): CanAddRefund => {
if (transactions.length === 0) {
return {
canRefund: false,
reason: intl.formatMessage(refundGridMessages.noTransactionsToRefund),
};
}

if (
transactions.every(transaction => !transaction.actions.includes(TransactionActionEnum.REFUND))
) {
return {
canRefund: false,
reason: intl.formatMessage(refundGridMessages.allTransactionsNonRefundable),
};
}

return {
canRefund: true,
};
};

0 comments on commit 9c5e786

Please sign in to comment.