Skip to content

Commit

Permalink
Merge pull request #145 from commercelayer/order-pending-authorized
Browse files Browse the repository at this point in the history
Manage pending orders with transactions in the order app
  • Loading branch information
marcomontalbano authored Nov 11, 2024
2 parents 457533f + 286beaf commit 4d4ab67
Show file tree
Hide file tree
Showing 8 changed files with 75 additions and 15 deletions.
10 changes: 8 additions & 2 deletions apps/orders/src/components/OrderAddresses.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
withSkeletonTemplate
} from '@commercelayer/app-elements'
import type { Order } from '@commercelayer/sdk'
import { useOrderStatus } from './OrderSummary/hooks/useOrderStatus'

interface Props {
order: Order
Expand All @@ -15,13 +16,18 @@ export const OrderAddresses = withSkeletonTemplate<Props>(
({ order }): JSX.Element | null => {
const { sdkClient } = useCoreSdkProvider()

const { isEditing } = useOrderStatus(order)

const isEditable =
isEditing || (order.status !== 'draft' && order.status !== 'pending')

return (
<Section border='none' title='Addresses'>
<Stack>
<ResourceAddress
title='Billing address'
address={order.billing_address}
editable
editable={isEditable}
onCreate={(address) => {
void sdkClient.orders.update({
id: order.id,
Expand All @@ -37,7 +43,7 @@ export const OrderAddresses = withSkeletonTemplate<Props>(
<ResourceAddress
title='Shipping address'
address={order.shipping_address}
editable
editable={isEditable}
onCreate={(address) => {
void sdkClient.orders.update({
id: order.id,
Expand Down
5 changes: 4 additions & 1 deletion apps/orders/src/components/OrderCustomer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
import type { Order } from '@commercelayer/sdk'
import { useEditCustomerOverlay } from './NewOrder/hooks/useEditCustomerOverlay'
import { languageList } from './NewOrder/languages'
import { useOrderStatus } from './OrderSummary/hooks/useOrderStatus'

interface Props {
order: Order
Expand All @@ -26,6 +27,7 @@ export const OrderCustomer = withSkeletonTemplate<Props>(
} = useTokenProvider()

const { mutateOrder } = useOrderDetails(order.id)
const { isEditing } = useOrderStatus(order)
const { Overlay: EditCustomerOverlay, open: openEditCustomerOverlay } =
useEditCustomerOverlay(order, () => {
void mutateOrder()
Expand All @@ -51,7 +53,8 @@ export const OrderCustomer = withSkeletonTemplate<Props>(
<Section
title='Customer'
actionButton={
order.status === 'draft' || order.status === 'pending' ? (
(order.status === 'draft' || order.status === 'pending') &&
isEditing ? (
<Button
alignItems='center'
variant='secondary'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,11 @@ export const useActionButtons = ({ order }: { order: Order }) => {
.map((triggerAttribute) => {
return {
label: getTriggerAttributeName(triggerAttribute),
variant: triggerAttribute === '_cancel' ? 'secondary' : 'primary',
variant:
triggerAttribute === '_cancel' ||
triggerAttribute === '__cancel_transactions'
? 'secondary'
: 'primary',
disabled: isLoading,
onClick: () => {
if (triggerAttribute === '_capture') {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,15 @@ export function useOrderStatus(order: Order) {
| null
| undefined

const isPendingWithTransactions =
order.payment_status !== 'unpaid' && order.status === 'pending'

const isEditing =
(order.status === 'editing' ||
order.status === 'draft' ||
order.status === 'pending') &&
canUser('update', 'orders')
canUser('update', 'orders') &&
!isPendingWithTransactions

const diffTotalAndPlacedTotal =
(order.total_amount_with_taxes_cents ?? 0) -
Expand Down Expand Up @@ -71,6 +75,8 @@ export function useOrderStatus(order: Order) {
hasInvalidShipments,
/** `true` when there's at least one shippable (do_not_ship = `false`) item. */
hasShippableLineItems,
/** `true` when the order has transactions, but the status is still `pending`. This is a kind of error status. */
isPendingWithTransactions,
/** Difference between the current `total_amount` and the `place_total_amount`. */
diffTotalAndPlacedTotal:
isOriginalOrderAmountExceeded && currencyCode != null
Expand Down
24 changes: 19 additions & 5 deletions apps/orders/src/components/OrderSummary/orderDictionary.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
import type { TriggerAttribute } from '@commercelayer/app-elements'
import type { Order, OrderUpdate } from '@commercelayer/sdk'

type UITriggerAttributes = Extract<
TriggerAttribute<OrderUpdate>,
'_approve' | '_cancel' | '_capture' | '_refund' | '_archive' | '_unarchive'
>
export type UITriggerAttributes =
| Extract<
TriggerAttribute<OrderUpdate>,
| '_approve'
| '_cancel'
| '_capture'
| '_refund'
| '_archive'
| '_unarchive'
| '_place'
>
| '__cancel_transactions'

export function getTriggerAttributes(order: Order): UITriggerAttributes[] {
const archiveTriggerAttribute: Extract<
Expand All @@ -19,6 +27,10 @@ export function getTriggerAttributes(order: Order): UITriggerAttributes[] {
return []
}

if (order.status === 'pending' && order.payment_status !== 'unpaid') {
return ['_place', '__cancel_transactions']
}

switch (combinedStatus) {
case 'placed:authorized:unfulfilled':
case 'placed:authorized:not_required':
Expand Down Expand Up @@ -76,7 +88,9 @@ export function getTriggerAttributeName(
_cancel: 'Cancel order',
_capture: 'Capture payment',
_refund: 'Refund',
_unarchive: 'Unarchive'
_unarchive: 'Unarchive',
_place: 'Place order',
__cancel_transactions: 'Cancel payment'
}

return dictionary[triggerAttribute]
Expand Down
1 change: 1 addition & 0 deletions apps/orders/src/hooks/useOrderDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export const orderIncludeAttribute = [
'shipments.stock_transfers',
'payment_method',
'payment_source',
'transactions',

// order editing
'line_items.sku',
Expand Down
31 changes: 27 additions & 4 deletions apps/orders/src/hooks/useTriggerAttribute.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { UITriggerAttributes } from '#components/OrderSummary/orderDictionary'
import { useOrderDetails } from '#hooks/useOrderDetails'
import {
useCoreSdkProvider,
Expand All @@ -10,21 +11,43 @@ import { orderIncludeAttribute } from './useOrderDetails'
interface TriggerAttributeHook {
isLoading: boolean
errors?: string[]
dispatch: (triggerAttribute: TriggerAttribute<OrderUpdate>) => Promise<void>
dispatch: (
triggerAttribute:
| TriggerAttribute<OrderUpdate>
| Exclude<UITriggerAttributes, TriggerAttribute<OrderUpdate>>
) => Promise<void>
}

export function useTriggerAttribute(orderId: string): TriggerAttributeHook {
const { mutateOrder } = useOrderDetails(orderId)
const { order, mutateOrder } = useOrderDetails(orderId)
const { sdkClient } = useCoreSdkProvider()

const [isLoading, setIsLoading] = useState(false)
const [errors, setErrors] = useState<string[] | undefined>()

const dispatch = useCallback(
async (triggerAttribute: string): Promise<void> => {
const dispatch = useCallback<TriggerAttributeHook['dispatch']>(
async (triggerAttribute) => {
setIsLoading(true)
setErrors(undefined)
try {
if (triggerAttribute === '__cancel_transactions') {
for (const transaction of order.transactions ?? []) {
if (
transaction.type === 'authorizations' ||
transaction.type === 'captures'
) {
await sdkClient[transaction.type].update({
id: transaction.id,
_cancel: true
})

void mutateOrder()
}
}

return
}

const updatedOrder = await sdkClient.orders.update(
{
id: orderId,
Expand Down
5 changes: 4 additions & 1 deletion apps/orders/src/pages/OrderDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { OrderReturns } from '#components/OrderReturns'
import { OrderShipments } from '#components/OrderShipments'
import { OrderSteps } from '#components/OrderSteps'
import { OrderSummary } from '#components/OrderSummary'
import { useOrderStatus } from '#components/OrderSummary/hooks/useOrderStatus'
import { Timeline } from '#components/Timeline'
import { appRoutes } from '#data/routes'
import { useOrderDetails } from '#hooks/useOrderDetails'
Expand Down Expand Up @@ -40,12 +41,14 @@ function OrderDetails(): JSX.Element {
const orderId = params?.orderId ?? ''

const { order, isLoading, error, mutateOrder } = useOrderDetails(orderId)
const { isPendingWithTransactions } = useOrderStatus(order)
const { returns, isLoadingReturns } = useOrderReturns(orderId)
const toolbar = useOrderToolbar({ order })

if (canUser('update', 'orders')) {
if (
order.status === 'pending' &&
!isPendingWithTransactions &&
extras?.salesChannels != null &&
extras?.salesChannels.length > 0
) {
Expand Down Expand Up @@ -206,7 +209,7 @@ function OrderDetails(): JSX.Element {
</Spacer>
</>
)}
{!['pending', 'draft'].includes(order.status) && (
{!['draft'].includes(order.status) && (
<Spacer top='14'>
<Timeline order={order} />
</Spacer>
Expand Down

0 comments on commit 4d4ab67

Please sign in to comment.