Files
medusa-store/integration-tests/http/__tests__/claims/claims.spec.ts
Leonardo Benini 9c957e1da0 chore(core-flows): only allow published products in addToCartWorkflow (#13182)
Closes #13163 

I have a few questions about expected behaviour, since this currently breaks some tests:

- Many tests use the productModule to create products, with default status == "draft", and use the addToCart workflow which now throws. Should I change all breaking tests to specify status == "published" whne creating the product? The alternative would be to check the status in the store API route before the workflow but 1. it would be an extra query and 2. the addToCart workflow is only used in the store currently, and even if it was to be used admin-side, it still doesn't make sense to add a draft product to cart

- After this PR an unpublished product would give the same error as a variant that doesn't exist. While imho this is correct, the thrown error (for both) is "Items  do not have a price" which doesn't make much sense(i believe the workflows goes through with an empty variants list and then errors at the price check point). Should I throw a different error when a variant doesn't exists/isn't published?


---

> [!NOTE]
> Enforces that only variants from published products can be added to carts, adds status fetching, refines errors, and updates tests to use ProductStatus.PUBLISHED.
> 
> - **Core Flows**:
>   - addToCart: Validate variants exist and belong to `product.status = PUBLISHED`; throw clear `INVALID_DATA` when not found/unpublished.
>   - Data fetching: Include `product.status` in `cart` and `order` variant field selections.
> - **Tests/Fixtures**:
>   - Update integration tests to set `status: ProductStatus.PUBLISHED` when creating products and import `ProductStatus` where needed.
>   - Add cases for unpublished products and non-existent variants producing the new error message.
> 
> <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit ca72532e957964d2d8e6bcecbb0905054c677ded. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup>
2025-10-02 12:31:53 +00:00

1446 lines
40 KiB
TypeScript

import { medusaIntegrationTestRunner } from "@medusajs/test-utils"
import {
ClaimReason,
ClaimType,
ContainerRegistrationKeys,
Modules,
ProductStatus,
RuleOperator,
} from "@medusajs/utils"
import {
adminHeaders,
createAdminUser,
} from "../../../helpers/create-admin-user"
import { setupTaxStructure } from "../../../modules/__tests__/fixtures"
jest.setTimeout(30000)
medusaIntegrationTestRunner({
testSuite: ({ dbConnection, getContainer, api }) => {
let baseClaim
let order, order2
let returnShippingOption
let outboundShippingOption
let shippingProfile
let fulfillmentSet
let returnReason
let inventoryItem
let inventoryItemExtra
let location
let productExtra
let item
const shippingProviderId = "manual_test-provider"
beforeEach(async () => {
const container = getContainer()
await createAdminUser(dbConnection, adminHeaders, container)
shippingProfile = (
await api.post(
`/admin/shipping-profiles`,
{
name: "Test",
type: "default",
},
adminHeaders
)
).data.shipping_profile
const region = (
await api.post(
"/admin/regions",
{
name: "test-region",
currency_code: "usd",
},
adminHeaders
)
).data.region
const customer = (
await api.post(
"/admin/customers",
{
first_name: "joe",
email: "joe@admin.com",
},
adminHeaders
)
).data.customer
const salesChannel = (
await api.post(
"/admin/sales-channels",
{
name: "Test channel",
},
adminHeaders
)
).data.sales_channel
const product = (
await api.post(
"/admin/products",
{
title: "Test product",
status: ProductStatus.PUBLISHED,
options: [{ title: "size", values: ["large", "small"] }],
shipping_profile_id: shippingProfile.id,
variants: [
{
title: "Test variant",
sku: "test-variant",
options: { size: "large" },
prices: [
{
currency_code: "usd",
amount: 10,
},
],
},
],
},
adminHeaders
)
).data.product
productExtra = (
await api.post(
"/admin/products",
{
title: "Extra product",
status: ProductStatus.PUBLISHED,
options: [{ title: "size", values: ["large", "small"] }],
shipping_profile_id: shippingProfile.id,
variants: [
{
title: "my variant",
sku: "variant-sku",
options: { size: "large" },
prices: [
{
currency_code: "usd",
amount: 50.25,
},
],
},
],
},
adminHeaders
)
).data.product
returnReason = (
await api.post(
"/admin/return-reasons",
{
value: "return-reason-test",
label: "Test return reason",
},
adminHeaders
)
).data.return_reason
const orderModule = container.resolve(Modules.ORDER)
order = await orderModule.createOrders({
region_id: region.id,
email: "foo@bar.com",
items: [
{
title: "Custom Item 2",
variant_id: product.variants[0].id,
quantity: 2,
unit_price: 25,
},
],
sales_channel_id: salesChannel.id,
shipping_address: {
first_name: "Test",
last_name: "Test",
address_1: "Test",
city: "Test",
country_code: "US",
postal_code: "12345",
phone: "12345",
},
billing_address: {
first_name: "Test",
last_name: "Test",
address_1: "Test",
city: "Test",
country_code: "US",
postal_code: "12345",
},
shipping_methods: [
{
name: "Test shipping method",
amount: 10,
data: {},
tax_lines: [
{
description: "shipping Tax 1",
tax_rate_id: "tax_usa_shipping",
code: "code",
rate: 10,
},
],
},
],
currency_code: "usd",
customer_id: customer.id,
transactions: [
{
amount: 61,
currency_code: "usd",
},
],
})
order2 = await orderModule.createOrders({
region_id: region.id,
email: "foo@bar2.com",
items: [
{
title: "Custom Iasdasd2",
quantity: 1,
unit_price: 20,
},
],
sales_channel_id: salesChannel.id,
shipping_address: {
first_name: "Test",
last_name: "Test",
address_1: "Test",
city: "Test",
country_code: "US",
postal_code: "12345",
phone: "12345",
},
billing_address: {
first_name: "Test",
last_name: "Test",
address_1: "Test",
city: "Test",
country_code: "US",
postal_code: "12345",
},
currency_code: "usd",
customer_id: customer.id,
})
location = (
await api.post(
`/admin/stock-locations`,
{
name: "Test location",
},
adminHeaders
)
).data.stock_location
location = (
await api.post(
`/admin/stock-locations/${location.id}/fulfillment-sets?fields=*fulfillment_sets`,
{
name: "Test",
type: "test-type",
},
adminHeaders
)
).data.stock_location
fulfillmentSet = (
await api.post(
`/admin/fulfillment-sets/${location.fulfillment_sets[0].id}/service-zones`,
{
name: "Test",
geo_zones: [{ type: "country", country_code: "us" }],
},
adminHeaders
)
).data.fulfillment_set
inventoryItem = (
await api.get(`/admin/inventory-items?sku=test-variant`, adminHeaders)
).data.inventory_items[0]
await api.post(
`/admin/inventory-items/${inventoryItem.id}/location-levels`,
{
location_id: location.id,
stocked_quantity: 10,
},
adminHeaders
)
inventoryItemExtra = (
await api.get(`/admin/inventory-items?sku=variant-sku`, adminHeaders)
).data.inventory_items[0]
await api.post(
`/admin/inventory-items/${inventoryItemExtra.id}/location-levels`,
{
location_id: location.id,
stocked_quantity: 10,
},
adminHeaders
)
const remoteLink = container.resolve(
ContainerRegistrationKeys.REMOTE_LINK
)
await remoteLink.create([
{
[Modules.STOCK_LOCATION]: {
stock_location_id: location.id,
},
[Modules.FULFILLMENT]: {
fulfillment_provider_id: shippingProviderId,
},
},
{
[Modules.STOCK_LOCATION]: {
stock_location_id: location.id,
},
[Modules.FULFILLMENT]: {
fulfillment_set_id: fulfillmentSet.id,
},
},
{
[Modules.SALES_CHANNEL]: {
sales_channel_id: salesChannel.id,
},
[Modules.STOCK_LOCATION]: {
stock_location_id: location.id,
},
},
{
[Modules.PRODUCT]: {
variant_id: product.variants[0].id,
},
[Modules.INVENTORY]: {
inventory_item_id: inventoryItem.id,
},
},
{
[Modules.PRODUCT]: {
variant_id: productExtra.variants[0].id,
},
[Modules.INVENTORY]: {
inventory_item_id: inventoryItemExtra.id,
},
},
])
// create reservation for inventory item that is initially on the order
const inventoryModule = container.resolve(Modules.INVENTORY)
await inventoryModule.createReservationItems([
{
inventory_item_id: inventoryItem.id,
location_id: location.id,
quantity: 2,
line_item_id: order.items[0].id,
},
])
const shippingOptionPayload = {
name: "Return shipping",
service_zone_id: fulfillmentSet.service_zones[0].id,
shipping_profile_id: shippingProfile.id,
provider_id: shippingProviderId,
price_type: "flat",
type: {
label: "Test type",
description: "Test description",
code: "test-code",
},
prices: [
{
currency_code: "usd",
amount: 15,
},
],
rules: [
{
operator: RuleOperator.EQ,
attribute: "is_return",
value: "true",
},
],
}
const outboundShippingOptionPayload = {
name: "Oubound shipping",
service_zone_id: fulfillmentSet.service_zones[0].id,
shipping_profile_id: shippingProfile.id,
provider_id: shippingProviderId,
price_type: "flat",
type: {
label: "Test type",
description: "Test description",
code: "test-code",
},
prices: [
{
currency_code: "usd",
amount: 20,
},
],
rules: [
{
operator: RuleOperator.EQ,
attribute: "is_return",
value: "false",
},
{
operator: RuleOperator.EQ,
attribute: "enabled_in_store",
value: "true",
},
],
}
returnShippingOption = (
await api.post(
"/admin/shipping-options",
shippingOptionPayload,
adminHeaders
)
).data.shipping_option
outboundShippingOption = (
await api.post(
"/admin/shipping-options",
outboundShippingOptionPayload,
adminHeaders
)
).data.shipping_option
item = order.items[0]
await setupTaxStructure(container.resolve(Modules.TAX))
})
describe("Claims lifecycle", () => {
let claimId
describe("with accurate order summary", () => {
it("should verify order summary at each level", async () => {
/* Case:
Purchased:
items: {
unit_price: 25,
qty: 2
tax_total: 0
total: 50
}
shipping_methods: {
unit_price: 10,
qty: 1
tax_total: 1
total: 11
}
*/
const orderResult = (
await api.get(`/admin/orders/${order.id}`, adminHeaders)
).data.order
expect(orderResult.summary).toEqual(
expect.objectContaining({
paid_total: 61,
refunded_total: 0,
transaction_total: 61,
pending_difference: 0,
current_order_total: 61,
original_order_total: 61,
})
)
/* TODO: Check summary after each of these events in order
- Add payment collection + payment + capture
- Fulfill items
- Submit inbound claim
- Fulfill claim items
- Make payment
- Submit outbound claim
- Make payment
- Fulfill claim items
*/
})
})
describe("with inbound and outbound items", () => {
beforeEach(async () => {
await api.post(
`/admin/orders/${order.id}/fulfillments`,
{
items: [
{
id: item.id,
quantity: 2,
},
],
},
adminHeaders
)
await api.post(
`/admin/orders/${order2.id}/fulfillments`,
{
items: [
{
id: order2.items[0].id,
quantity: 1,
},
],
},
adminHeaders
)
baseClaim = (
await api.post(
"/admin/claims",
{
order_id: order.id,
type: ClaimType.REPLACE,
description: "Base claim",
},
adminHeaders
)
).data.claim
let r2 = await api.post(
"/admin/claims",
{
order_id: order2.id,
type: ClaimType.REFUND,
},
adminHeaders
)
const claimId2 = r2.data.claim.id
const item2 = order2.items[0]
const {
data: {
order_preview: {
items: [previewItem],
},
},
} = await api.post(
`/admin/claims/${claimId2}/inbound/items`,
{ items: [{ id: item2.id, quantity: 1 }] },
adminHeaders
)
// Delete & recreate again to ensure it works for both delete and create
await api.delete(
`/admin/claims/${claimId2}/inbound/items/${previewItem.actions[0].id}`,
adminHeaders
)
const {
data: { return: returnData },
} = await api.post(
`/admin/claims/${claimId2}/inbound/items`,
{ items: [{ id: item2.id, quantity: 1 }] },
adminHeaders
)
await api.post(
`/admin/returns/${returnData.id}`,
{
location_id: location.id,
no_notification: true,
},
adminHeaders
)
const {
data: {
order_preview: { shipping_methods: inboundShippingMethods },
},
} = await api.post(
`/admin/claims/${claimId2}/inbound/shipping-method`,
{ shipping_option_id: returnShippingOption.id },
adminHeaders
)
const inboundShippingMethod = inboundShippingMethods.find(
(m) => m.shipping_option_id == returnShippingOption.id
)
// Delete & recreate again to ensure it works for both delete and create
await api.delete(
`/admin/claims/${claimId2}/inbound/shipping-method/${inboundShippingMethod.actions[0].id}`,
adminHeaders
)
await api.post(
`/admin/claims/${claimId2}/inbound/shipping-method`,
{ shipping_option_id: returnShippingOption.id },
adminHeaders
)
const testRes = await api.post(
`/admin/claims/${claimId2}/request`,
{},
adminHeaders
)
claimId = baseClaim.id
item = order.items[0]
let result = await api.post(
`/admin/claims/${claimId}/inbound/items`,
{
items: [
{
id: item.id,
reason_id: returnReason.id,
quantity: 2,
},
],
},
adminHeaders
)
await api.post(
`/admin/claims/${claimId}/inbound/shipping-method`,
{ shipping_option_id: returnShippingOption.id },
adminHeaders
)
// shipping Options w/ custom price
const {
data: {
order_preview: { shipping_methods: outboundShippingMethods },
},
} = await api.post(
`/admin/claims/${claimId}/outbound/shipping-method`,
{
shipping_option_id: outboundShippingOption.id,
custom_amount: 12.5,
},
adminHeaders
)
const outboundShippingMethod = outboundShippingMethods.find(
(m) => m.shipping_option_id == outboundShippingOption.id
)
expect(outboundShippingMethod.subtotal).toBe(12.5)
expect(outboundShippingMethod.is_custom_amount).toBe(true)
// Reset shipping custom price
const {
data: {
order_preview: { shipping_methods: outboundShippingMethods2 },
},
} = await api.post(
`/admin/claims/${claimId}/outbound/shipping-method/${outboundShippingMethod.actions[0].id}`,
{
custom_amount: null,
},
adminHeaders
)
const outboundShippingMethodReset = outboundShippingMethods2.find(
(m) => m.shipping_option_id == outboundShippingOption.id
)
expect(outboundShippingMethodReset.subtotal).toBe(20)
expect(outboundShippingMethodReset.is_custom_amount).toBe(false)
// Delete & recreate again to ensure it works for both delete and create
await api.delete(
`/admin/claims/${claimId}/outbound/shipping-method/${outboundShippingMethodReset.actions[0].id}`,
adminHeaders
)
await api.post(
`/admin/claims/${claimId}/outbound/shipping-method`,
{ shipping_option_id: outboundShippingOption.id },
adminHeaders
)
// updated the requested quantity
const updateReturnItemActionId =
result.data.order_preview.items[0].actions[0].id
result = await api.post(
`/admin/claims/${claimId}/inbound/items/${updateReturnItemActionId}`,
{
quantity: 1,
},
adminHeaders
)
// New Items
await api.post(
`/admin/claims/${claimId}/outbound/items`,
{
items: [
{
variant_id: productExtra.variants[0].id,
quantity: 2,
},
],
},
adminHeaders
)
// Claim Items
await api.post(
`/admin/claims/${claimId}/claim-items`,
{
items: [
{
id: item.id,
reason: ClaimReason.PRODUCTION_FAILURE,
quantity: 1,
},
],
},
adminHeaders
)
result = await api.post(
`/admin/claims/${claimId}/request`,
{},
adminHeaders
)
result = (
await api.get(
`/admin/claims?fields=*claim_items,*additional_items`,
adminHeaders
)
).data.claims
expect(result).toHaveLength(2)
expect(result[0].additional_items).toHaveLength(1)
expect(result[0].claim_items).toHaveLength(1)
expect(result[0].canceled_at).toBeNull()
const reservationsResponse = (
await api.get(
`/admin/reservations?location_id[]=${location.id}`,
adminHeaders
)
).data
expect(reservationsResponse.reservations).toHaveLength(1)
})
it("should complete flow with fulfilled items successfully", async () => {
const fulfillOrder = (
await api.get(`/admin/orders/${order.id}`, adminHeaders)
).data.order
const paymentCollections = fulfillOrder.payment_collections
expect(paymentCollections).toHaveLength(1)
expect(paymentCollections[0]).toEqual(
expect.objectContaining({
status: "not_paid",
amount: 113.21,
currency_code: "usd",
})
)
const fulfillableItem = fulfillOrder.items.find(
(item) => item.detail.fulfilled_quantity === 0
)
await api.post(
`/admin/orders/${order.id}/fulfillments`,
{
location_id: location.id,
items: [{ id: fulfillableItem.id, quantity: 1 }],
},
adminHeaders
)
})
it("should go through cancel flow successfully", async () => {
// fetch claim to get return id
let claim = (
await api.get(
`/admin/claims/${claimId}?fields=return_id`,
adminHeaders
)
).data.claim
// fetch return and fulfillment
const return_ = (
await api.get(
`/admin/returns/${claim.return_id}?fields=fulfillments.id`,
adminHeaders
)
).data.return
/**
* Claim cannot be canceled unless all fulfillments are not canceled
*/
await api.post(
`/admin/fulfillments/${return_.fulfillments[0].id}/cancel`,
{},
adminHeaders
)
await api.post(`/admin/claims/${claimId}/cancel`, {}, adminHeaders)
claim = (
await api.get(
`/admin/claims/${claimId}?fields=*claim_items,*additional_items`,
adminHeaders
)
).data.claim
expect(claim.canceled_at).toBeDefined()
const reservationsResponseAfterCanceling = (
await api.get(
`/admin/reservations?location_id[]=${location.id}`,
adminHeaders
)
).data
expect(reservationsResponseAfterCanceling.reservations).toHaveLength(
0
)
})
it("should create a payment collection successfully & mark as paid", async () => {
const paymentDelta = 113.21
const orderForPayment = (
await api.get(`/admin/orders/${order.id}`, adminHeaders)
).data.order
const paymentCollections = orderForPayment.payment_collections
expect(paymentCollections).toHaveLength(1)
expect(paymentCollections[0]).toEqual(
expect.objectContaining({
status: "not_paid",
amount: paymentDelta,
currency_code: "usd",
})
)
const createdPaymentCollection = (
await api.post(
`/admin/payment-collections`,
{ order_id: order.id, amount: 100 },
adminHeaders
)
).data.payment_collection
expect(createdPaymentCollection).toEqual(
expect.objectContaining({
currency_code: "usd",
amount: 100,
status: "not_paid",
})
)
const deleted = (
await api.delete(
`/admin/payment-collections/${createdPaymentCollection.id}`,
adminHeaders
)
).data
expect(deleted).toEqual({
id: createdPaymentCollection.id,
object: "payment-collection",
deleted: true,
})
const finalPaymentCollection = (
await api.post(
`/admin/payment-collections/${paymentCollections[0].id}/mark-as-paid`,
{ order_id: order.id },
adminHeaders
)
).data.payment_collection
expect(finalPaymentCollection).toEqual(
expect.objectContaining({
currency_code: "usd",
amount: paymentDelta,
status: "completed",
authorized_amount: paymentDelta,
captured_amount: paymentDelta,
refunded_amount: 0,
})
)
})
})
describe("with only outbound items", () => {
let orderResult
beforeEach(async () => {
await api.post(
`/admin/orders/${order.id}/fulfillments`,
{
items: [
{
id: item.id,
quantity: 2,
},
],
},
adminHeaders
)
await api.post(
`/admin/orders/${order2.id}/fulfillments`,
{
items: [
{
id: order2.items[0].id,
quantity: 1,
},
],
},
adminHeaders
)
baseClaim = (
await api.post(
"/admin/claims",
{
order_id: order.id,
type: ClaimType.REPLACE,
description: "Base claim",
},
adminHeaders
)
).data.claim
claimId = baseClaim.id
item = order.items[0]
const {
data: {
order_preview: { shipping_methods: outboundShippingMethods },
},
} = await api.post(
`/admin/claims/${claimId}/outbound/shipping-method`,
{ shipping_option_id: outboundShippingOption.id },
adminHeaders
)
const outboundShippingMethod = outboundShippingMethods.find(
(m) => m.shipping_option_id == outboundShippingOption.id
)
// Delete & recreate again to ensure it works for both delete and create
await api.delete(
`/admin/claims/${claimId}/outbound/shipping-method/${outboundShippingMethod.actions[0].id}`,
adminHeaders
)
await api.post(
`/admin/claims/${claimId}/outbound/shipping-method`,
{ shipping_option_id: outboundShippingOption.id },
adminHeaders
)
const { data } = await api.post(
`/admin/claims/${claimId}/outbound/items`,
{
items: [
{
variant_id: productExtra.variants[0].id,
quantity: 3,
},
],
},
adminHeaders
)
await api.post(
`/admin/claims/${claimId}/claim-items`,
{
items: [
{
id: item.id,
reason: ClaimReason.PRODUCTION_FAILURE,
quantity: 1,
},
],
},
adminHeaders
)
})
it("should complete flow with fulfilled items successfully", async () => {
await api.post(`/admin/claims/${claimId}/request`, {}, adminHeaders)
const claim = (
await api.get(
`/admin/claims/${claimId}?fields=*claim_items,*additional_items`,
adminHeaders
)
).data.claim
expect(claim.additional_items).toHaveLength(1)
expect(claim.claim_items).toHaveLength(1)
expect(claim.canceled_at).toBeNull()
const fulfillOrder = (
await api.get(`/admin/orders/${order.id}`, adminHeaders)
).data.order
const fulfillableItem = fulfillOrder.items.filter(
(item) => item.detail.fulfilled_quantity === 0
)
await api.post(
`/admin/orders/${order.id}/fulfillments`,
{
location_id: location.id,
items: [{ id: fulfillableItem[0].id, quantity: 1 }],
},
adminHeaders
)
orderResult = (
await api.get(`/admin/orders/${order.id}`, adminHeaders)
).data.order
expect(orderResult.fulfillment_status).toEqual("partially_fulfilled")
await api.post(
`/admin/orders/${order.id}/fulfillments`,
{
location_id: location.id,
items: [{ id: fulfillableItem[0].id, quantity: 2 }],
},
adminHeaders
)
orderResult = (
await api.get(
`/admin/orders/${order.id}?fields=*fulfillments,*fulfillments.items`,
adminHeaders
)
).data.order
expect(orderResult.fulfillment_status).toEqual("fulfilled")
await api.post(
`admin/orders/${order.id}/fulfillments/${orderResult.fulfillments[0].id}/shipments`,
{
items: orderResult.fulfillments[0]?.items?.map((i) => ({
id: i.line_item_id,
quantity: i.quantity,
})),
},
adminHeaders
)
orderResult = (
await api.get(
`/admin/orders/${order.id}?fields=*fulfillments,*fulfillments.items`,
adminHeaders
)
).data.order
expect(orderResult.fulfillment_status).toEqual("partially_shipped")
await api.post(
`admin/orders/${order.id}/fulfillments/${orderResult.fulfillments[1].id}/shipments`,
{
items: orderResult.fulfillments[1]?.items?.map((i) => ({
id: i.line_item_id,
quantity: i.quantity,
})),
},
adminHeaders
)
await api.post(
`admin/orders/${order.id}/fulfillments/${orderResult.fulfillments[2].id}/shipments`,
{
items: orderResult.fulfillments[2]?.items?.map((i) => ({
id: i.line_item_id,
quantity: i.quantity,
})),
},
adminHeaders
)
orderResult = (
await api.get(
`/admin/orders/${order.id}?fields=*fulfillments`,
adminHeaders
)
).data.order
expect(orderResult.fulfillment_status).toEqual("shipped")
})
it("should remove outbound shipping method when outbound items are completely removed", async () => {
orderResult = (
await api.get(`/admin/orders/${order.id}/preview`, adminHeaders)
).data.order
const claimItems = orderResult.items.filter(
(item) =>
!!item.actions?.find((action) => action.action === "ITEM_ADD")
)
const claimShippingMethods = orderResult.shipping_methods.filter(
(item) =>
!!item.actions?.find((action) => action.action === "SHIPPING_ADD")
)
expect(claimItems).toHaveLength(1)
expect(claimShippingMethods).toHaveLength(1)
await api.delete(
`/admin/claims/${claimId}/outbound/items/${claimItems[0].actions[0].id}`,
adminHeaders
)
orderResult = (
await api.get(`/admin/orders/${order.id}/preview`, adminHeaders)
).data.order
const updatedClaimItems = orderResult.items.filter(
(item) =>
!!item.actions?.find((action) => action.action === "ITEM_ADD")
)
const updatedClaimShippingMethods =
orderResult.shipping_methods.filter(
(item) =>
!!item.actions?.find(
(action) => action.action === "SHIPPING_ADD"
)
)
expect(updatedClaimItems).toHaveLength(0)
expect(updatedClaimShippingMethods).toHaveLength(0)
})
})
describe("with only inbound items", () => {
let orderResult
beforeEach(async () => {
await api.post(
`/admin/orders/${order.id}/fulfillments`,
{
items: [
{
id: item.id,
quantity: 2,
},
],
},
adminHeaders
)
baseClaim = (
await api.post(
"/admin/claims",
{
order_id: order.id,
type: ClaimType.REPLACE,
description: "Base claim",
},
adminHeaders
)
).data.claim
claimId = baseClaim.id
item = order.items[0]
await api.post(
`/admin/claims/${claimId}/inbound/items`,
{
items: [
{
id: item.id,
reason_id: returnReason.id,
quantity: 1,
},
],
},
adminHeaders
)
await api.post(
`/admin/claims/${claimId}/inbound/shipping-method`,
{ shipping_option_id: returnShippingOption.id },
adminHeaders
)
})
it("test inbound only", async () => {
// Claim Items
await api.post(
`/admin/claims/${claimId}/claim-items`,
{
items: [
{
id: item.id,
reason: ClaimReason.PRODUCTION_FAILURE,
quantity: 1,
},
],
},
adminHeaders
)
await api.post(`/admin/claims/${claimId}/request`, {}, adminHeaders)
const claims = (
await api.get(
`/admin/claims?fields=*claim_items,*additional_items`,
adminHeaders
)
).data.claims
expect(claims).toHaveLength(1)
expect(claims[0].additional_items).toHaveLength(0)
expect(claims[0].claim_items).toHaveLength(1)
expect(claims[0].canceled_at).toBeNull()
const orderCheck = (
await api.get(`/admin/orders/${order.id}`, adminHeaders)
).data.order
expect(orderCheck.summary).toEqual(
expect.objectContaining({
pending_difference: -9.7,
current_order_total: 76.3,
original_order_total: 61,
})
)
expect(true).toBe(true)
})
it("should remove inbound shipping method when inbound items are completely removed", async () => {
orderResult = (
await api.get(`/admin/orders/${order.id}/preview`, adminHeaders)
).data.order
const returnItems = orderResult.items.filter(
(item) =>
!!item.actions?.find((action) => action.action === "RETURN_ITEM")
)
const returnShippingMethods = orderResult.shipping_methods.filter(
(item) =>
!!item.actions?.find((action) => action.action === "SHIPPING_ADD")
)
expect(returnItems).toHaveLength(1)
expect(returnShippingMethods).toHaveLength(1)
await api.delete(
`/admin/claims/${claimId}/inbound/items/${returnItems[0].actions[0].id}`,
adminHeaders
)
orderResult = (
await api.get(`/admin/orders/${order.id}/preview`, adminHeaders)
).data.order
const updatedReturnItems = orderResult.items.filter(
(item) =>
!!item.actions?.find((action) => action.action === "RETURN_ITEM")
)
const updatedReturnShippingMethods =
orderResult.shipping_methods.filter(
(item) =>
!!item.actions?.find(
(action) => action.action === "SHIPPING_ADD"
)
)
expect(updatedReturnItems).toHaveLength(0)
expect(updatedReturnShippingMethods).toHaveLength(0)
})
})
})
describe("GET /admin/claims/:id", () => {
beforeEach(async () => {
await api.post(
`/admin/orders/${order.id}/fulfillments`,
{
items: [
{
id: item.id,
quantity: 2,
},
],
},
adminHeaders
)
await api.post(
`/admin/orders/${order2.id}/fulfillments`,
{
items: [
{
id: order2.items[0].id,
quantity: 1,
},
],
},
adminHeaders
)
baseClaim = (
await api.post(
"/admin/claims",
{
order_id: order.id,
type: ClaimType.REPLACE,
description: "Base claim",
},
adminHeaders
)
).data.claim
expect(baseClaim.created_by).toEqual(expect.any(String))
await api.post(
`/admin/claims/${baseClaim.id}/inbound/items`,
{
items: [
{
id: item.id,
reason_id: returnReason.id,
quantity: 1,
},
],
},
adminHeaders
)
await api.post(
`/admin/claims/${baseClaim.id}/inbound/shipping-method`,
{
shipping_option_id: returnShippingOption.id,
},
adminHeaders
)
// Claim Items
await api.post(
`/admin/claims/${baseClaim.id}/claim-items`,
{
items: [
{
id: item.id,
reason: ClaimReason.PRODUCTION_FAILURE,
quantity: 1,
},
],
},
adminHeaders
)
const { response } = await api.post(
`/admin/claims/${baseClaim.id}/request`,
{},
adminHeaders
)
})
it("should get an existing claim for given id", async () => {
let res = await api.get(`/admin/claims/${baseClaim.id}`, adminHeaders)
expect(res.status).toEqual(200)
expect(res.data.claim.id).toEqual(baseClaim.id)
let errorRes = await api
.get(`/admin/claims/invalid-claim-id`, adminHeaders)
.catch((e) => e)
expect(errorRes.response.status).toEqual(404)
expect(errorRes.response.data.message).toEqual(
"Claim with id: invalid-claim-id was not found"
)
})
it("should get an existing claim for given id", async () => {
let res = await api.get(`/admin/claims/${baseClaim.id}`, adminHeaders)
const keysInResponse = Object.keys(res.data.claim)
expect(res.status).toEqual(200)
expect(keysInResponse).toEqual(
expect.arrayContaining([
"id",
"created_at",
"updated_at",
"canceled_at",
"type",
"order_id",
"return_id",
"display_id",
"order_version",
"refund_amount",
"additional_items",
"claim_items",
])
)
})
it("should get claim with claim items", async () => {
let res = await api.get(
`/admin/claims/${baseClaim.id}?fields=id,*claim_items`,
adminHeaders
)
expect(res.status).toEqual(200)
expect(res.data.claim.id).toEqual(baseClaim.id)
expect(res.data.claim.claim_items).toEqual(
expect.arrayContaining([
expect.objectContaining({
item_id: item.id,
reason: ClaimReason.PRODUCTION_FAILURE,
}),
])
)
// additional items is one of the props that should be undefined
expect(res.data.claim.additional_items).toBeUndefined()
const resLineItems = await api.get(
`/admin/orders/${order.id}/line-items`,
adminHeaders
)
expect(resLineItems.data).toEqual(
expect.objectContaining({
order_items: [
expect.objectContaining({
order_id: order.id,
item_id: item.id,
version: 3,
item: expect.objectContaining({
title: "Custom Item 2",
unit_price: 25,
}),
history: {
version: {
from: 1,
to: 3,
},
},
}),
],
})
)
})
})
},
})