fix: Compute "virtual" adjustments for order previews (#13306)
* wip * add wip * wip * reuse action * finish first draft * fix tests * cleanup * Only compute adjustments when necessary * Create hot-carrots-look.md * address comments * minor tweaks * fix pay col * fix test * wip * Dwip * wip * fix: adjustment typo * fix: import * fix: workflow imports * wip: update test * feat: upsert versioned adjustments when previewing order * fix: revert unique codes change * fix: order spec test with versioning * wip: save * feat: make adjustments work for preview and confirm flow, wip base repo filtering of older version adjustments * fix: missing populate where * wip: populate where loading versioned adjustments * fix: filter out older adjustment versions * temp: comment adjustments in repo * test: add adjustment if no version * wip: configure populate where in order base repository * fix: rm manual filtering * fix: revert base repo changes * fix: revert * fix: use order item version instead of order version * fix: rm only in test * fix: update case spec * fix: remove sceanrio, wip test with draft promotion * feat: test correct adjustments when disabling promotion * feat: complex test case * feat: test consecutive order edits * feat: 2 promotions test case with a fixed promo * feat: migrate existing order line item adjustments to order items latest version * feat: update dep after merge * wip: load adjustments separatley * feat: adjustments collections * fix: spread result, handle related entity case * fix: update lock * feat: make sure version is loaded, refactor, handle related entity case * fix: check fields * feat: loading adjustments for list and count * fix: correct items version field * fix: rm empty array * fix: wip order modules spec * fix: order module specs * feat: preinit items adjustments * fix: rm only * fix: rm only * chore: cleanup * fix: migration files * fix: dont change formatting * fix: core package build * chore: more cleanup * fix: item update util * fix: duplicate import * fix: refresh adjustments for exchanges (#13992) * wip: exchange adjustments * feat: test - receive items * feat: finish test case * fix: casing * fix(draft-orders, core-flows, orders) refresh adjustments for draft orders (#14025) * wip: draft orders adjustments refresh * feat: rewrite to use REPLACE action + test * fix: rm only * feat: cleanup old REPLACE actions * feat: cleanup adjustemnts when 0 promotions * wip: canceling draft order * fix: make version arg optional * fix: restore promotion links * feat: test reverting on cancelation * fix: address comments in tests * wip: fix summary on preview * fix: get pending diff on preview summary from total * fix: revert pending diff change --------- Co-authored-by: fPolic <mainacc.polic@gmail.com> Co-authored-by: Frane Polić <16856471+fPolic@users.noreply.github.com>
This commit is contained in:
@@ -1,8 +1,11 @@
|
||||
import { medusaIntegrationTestRunner } from "@medusajs/test-utils"
|
||||
import { IOrderModuleService, IPromotionModuleService } from "@medusajs/types"
|
||||
import {
|
||||
ContainerRegistrationKeys,
|
||||
Modules,
|
||||
ProductStatus,
|
||||
PromotionStatus,
|
||||
PromotionType,
|
||||
RuleOperator,
|
||||
} from "@medusajs/utils"
|
||||
import {
|
||||
@@ -913,6 +916,305 @@ medusaIntegrationTestRunner({
|
||||
expect(updatedClaimShippingMethods).toHaveLength(0)
|
||||
})
|
||||
})
|
||||
|
||||
describe("Exchange adjustments", () => {
|
||||
let appliedPromotion
|
||||
let promotionModule: IPromotionModuleService
|
||||
let orderModule: IOrderModuleService
|
||||
let remoteLink
|
||||
let orderWithPromotion
|
||||
let productForAdjustmentTest
|
||||
|
||||
beforeEach(async () => {
|
||||
const container = getContainer()
|
||||
promotionModule = container.resolve(Modules.PROMOTION)
|
||||
orderModule = container.resolve(Modules.ORDER)
|
||||
remoteLink = container.resolve(ContainerRegistrationKeys.LINK)
|
||||
|
||||
productForAdjustmentTest = (
|
||||
await api.post(
|
||||
"/admin/products",
|
||||
{
|
||||
title: "Product for adjustment test",
|
||||
status: ProductStatus.PUBLISHED,
|
||||
shipping_profile_id: shippingProfile.id,
|
||||
options: [{ title: "size", values: ["large", "small"] }],
|
||||
variants: [
|
||||
{
|
||||
title: "Test variant",
|
||||
sku: "test-variant-adjustment",
|
||||
manage_inventory: false,
|
||||
options: { size: "large" },
|
||||
prices: [
|
||||
{
|
||||
currency_code: "usd",
|
||||
amount: 12,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
adminHeaders
|
||||
)
|
||||
).data.product
|
||||
|
||||
appliedPromotion = await promotionModule.createPromotions({
|
||||
code: "PROMOTION_APPLIED",
|
||||
type: PromotionType.STANDARD,
|
||||
status: PromotionStatus.ACTIVE,
|
||||
application_method: {
|
||||
type: "percentage",
|
||||
target_type: "order",
|
||||
allocation: "each",
|
||||
value: 10,
|
||||
max_quantity: 5,
|
||||
currency_code: "usd",
|
||||
target_rules: [],
|
||||
},
|
||||
})
|
||||
|
||||
await remoteLink.create([
|
||||
{
|
||||
[Modules.SALES_CHANNEL]: {
|
||||
sales_channel_id: (
|
||||
await api.get("/admin/sales-channels", adminHeaders)
|
||||
).data.sales_channels[0].id,
|
||||
},
|
||||
[Modules.STOCK_LOCATION]: {
|
||||
stock_location_id: location.id,
|
||||
},
|
||||
},
|
||||
])
|
||||
|
||||
// @ts-ignore
|
||||
orderWithPromotion = await orderModule.createOrders({
|
||||
email: "foo@bar.com",
|
||||
region_id: (
|
||||
await api.get("/admin/regions", adminHeaders)
|
||||
).data.regions[0].id,
|
||||
sales_channel_id: (
|
||||
await api.get("/admin/sales-channels", adminHeaders)
|
||||
).data.sales_channels[0].id,
|
||||
items: [
|
||||
{
|
||||
// @ts-ignore
|
||||
id: "item-1",
|
||||
title: "Custom Item",
|
||||
quantity: 1,
|
||||
unit_price: 10,
|
||||
},
|
||||
],
|
||||
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",
|
||||
})
|
||||
|
||||
await orderModule.createOrderLineItemTaxLines(orderWithPromotion.id, [
|
||||
{
|
||||
// @ts-ignore
|
||||
item_id: "item-1",
|
||||
code: "standard",
|
||||
rate: 10,
|
||||
description: "tax-1",
|
||||
provider_id: "system",
|
||||
total: 1.2,
|
||||
subtotal: 1.2,
|
||||
},
|
||||
])
|
||||
|
||||
await orderModule.createOrderLineItemAdjustments([
|
||||
{
|
||||
version: 1,
|
||||
code: appliedPromotion.code!,
|
||||
amount: 1,
|
||||
item_id: "item-1",
|
||||
promotion_id: appliedPromotion.id,
|
||||
},
|
||||
])
|
||||
|
||||
await remoteLink.create({
|
||||
[Modules.ORDER]: { order_id: orderWithPromotion.id },
|
||||
[Modules.PROMOTION]: { promotion_id: appliedPromotion.id },
|
||||
})
|
||||
})
|
||||
|
||||
it("should update adjustments when adding an inbound and outbound item", async () => {
|
||||
// First item -> 10$ | 10% discount tax excl. | 10% tax
|
||||
// Second item -> 12$ | 10% discount tax excl. | 2% tax
|
||||
|
||||
// fulfill item so it can be returned
|
||||
await api.post(
|
||||
`/admin/orders/${orderWithPromotion.id}/fulfillments`,
|
||||
{
|
||||
items: [
|
||||
{
|
||||
id: orderWithPromotion.items[0].id,
|
||||
quantity: 1,
|
||||
},
|
||||
],
|
||||
},
|
||||
adminHeaders
|
||||
)
|
||||
|
||||
let result = await api.post(
|
||||
"/admin/exchanges",
|
||||
{
|
||||
order_id: orderWithPromotion.id,
|
||||
description: "Test",
|
||||
},
|
||||
adminHeaders
|
||||
)
|
||||
|
||||
const exchangeId = result.data.exchange.id
|
||||
const orderId = result.data.exchange.order_id
|
||||
|
||||
result = (await api.get(`/admin/orders/${orderId}`, adminHeaders))
|
||||
.data.order
|
||||
|
||||
expect(result.original_total).toEqual(11) // $10 + 10% tax
|
||||
expect(result.total).toEqual(10 * 0.9 * 1.1) // ($10 - 10% discount) + 10% tax
|
||||
|
||||
// Add outbound item with price $12, 10% discount and 10% tax
|
||||
result = (
|
||||
await api
|
||||
.post(
|
||||
`/admin/exchanges/${exchangeId}/outbound/items`,
|
||||
{
|
||||
items: [
|
||||
{
|
||||
variant_id: productForAdjustmentTest.variants[0].id,
|
||||
quantity: 1,
|
||||
},
|
||||
],
|
||||
},
|
||||
adminHeaders
|
||||
)
|
||||
.catch((e) => console.log(e))
|
||||
).data.order_preview
|
||||
|
||||
expect(result.total).toEqual(20.916) // 10 * 0.9 * 1.1 + 12 * 0.9 * 1.02
|
||||
expect(result.original_total).toEqual(23.24) // 10 * 1.1 + 12 * 1.02
|
||||
|
||||
// Confirm that the adjustment values are correct
|
||||
const adjustments = result.items[0].adjustments
|
||||
const adjustments2 = result.items[1].adjustments
|
||||
expect(adjustments).toEqual([
|
||||
expect.objectContaining({
|
||||
amount: 1,
|
||||
}),
|
||||
])
|
||||
expect(adjustments2).toEqual([
|
||||
expect.objectContaining({
|
||||
amount: 1.2,
|
||||
}),
|
||||
])
|
||||
|
||||
let orderResult = (
|
||||
await api.get(`/admin/orders/${orderId}`, adminHeaders)
|
||||
).data.order
|
||||
|
||||
// confirm original order is not updated
|
||||
expect(orderResult.total).toEqual(9.9) // initial item 10$ and 10% discount and 10% tax
|
||||
expect(orderResult.original_total).toEqual(11) // initial item 10$ + 10% tax
|
||||
|
||||
const originalItemId = result.items[0].id
|
||||
|
||||
// Request inbound item return
|
||||
result = (
|
||||
await api
|
||||
.post(
|
||||
`/admin/exchanges/${exchangeId}/inbound/items`,
|
||||
{
|
||||
items: [
|
||||
{
|
||||
id: originalItemId,
|
||||
reason_id: returnReason.id,
|
||||
quantity: 1,
|
||||
},
|
||||
],
|
||||
},
|
||||
adminHeaders
|
||||
)
|
||||
.catch((e) => console.log(e))
|
||||
).data.order_preview
|
||||
|
||||
const returnId = result.order_change.return_id
|
||||
|
||||
await api.post(
|
||||
`/admin/exchanges/${exchangeId}/request`,
|
||||
{},
|
||||
adminHeaders
|
||||
)
|
||||
|
||||
orderResult = (
|
||||
await api.get(`/admin/orders/${orderId}`, adminHeaders)
|
||||
).data.order
|
||||
|
||||
// after exchange request order contains both items and adjustments untill return is received
|
||||
expect(orderResult.total).toEqual(20.916) // 10 * 0.9 * 1.1 + 12 * 0.9 * 1.02
|
||||
expect(orderResult.original_total).toEqual(23.24) // 10 * 1.1 + 12 * 1.02
|
||||
|
||||
await api.post(`/admin/returns/${returnId}/receive`, {}, adminHeaders)
|
||||
|
||||
orderResult = (
|
||||
await api.get(`/admin/orders/${orderId}`, adminHeaders)
|
||||
).data.order
|
||||
|
||||
// still the same state while return receive process is pending
|
||||
expect(orderResult.total).toEqual(20.916) // 10 * 0.9 * 1.1 + 12 * 0.9 * 1.02
|
||||
expect(orderResult.original_total).toEqual(23.24) // 10 * 1.1 + 12 * 1.02
|
||||
|
||||
await api.post(
|
||||
`/admin/returns/${returnId}/receive-items`,
|
||||
{
|
||||
items: [
|
||||
{
|
||||
id: originalItemId,
|
||||
quantity: 1,
|
||||
},
|
||||
],
|
||||
},
|
||||
adminHeaders
|
||||
)
|
||||
|
||||
orderResult = (
|
||||
await api.get(`/admin/orders/${orderId}`, adminHeaders)
|
||||
).data.order
|
||||
|
||||
// still the same state while return receive process is pending
|
||||
expect(orderResult.total).toEqual(20.916) // 10 * 0.9 * 1.1 + 12 * 0.9 * 1.02
|
||||
expect(orderResult.original_total).toEqual(23.24) // 10 * 1.1 + 12 * 1.02
|
||||
|
||||
await api.post(
|
||||
`/admin/returns/${returnId}/receive/confirm`,
|
||||
{},
|
||||
adminHeaders
|
||||
)
|
||||
|
||||
const orderResult2 = (
|
||||
await api.get(`/admin/orders/${orderId}`, adminHeaders)
|
||||
).data.order
|
||||
|
||||
// after confirmation only first added item is active
|
||||
expect(orderResult2.total).toEqual(11.016)
|
||||
expect(orderResult2.original_total).toEqual(12.24)
|
||||
})
|
||||
})
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -20,6 +20,7 @@ import {
|
||||
createAdminUser,
|
||||
} from "../../../helpers/create-admin-user"
|
||||
import { setupTaxStructure } from "../fixtures"
|
||||
import { removeDraftOrderPromotionsWorkflow } from "../../../../packages/core/core-flows/dist/draft-order/workflows/remove-draft-order-promotions"
|
||||
|
||||
jest.setTimeout(100000)
|
||||
|
||||
@@ -770,6 +771,702 @@ medusaIntegrationTestRunner({
|
||||
expect(response.status).toEqual(200)
|
||||
})
|
||||
|
||||
it("should apply promotion to a draft order via order edit", async () => {
|
||||
const region = await regionModuleService.createRegions({
|
||||
name: "US",
|
||||
currency_code: "usd",
|
||||
})
|
||||
|
||||
const salesChannel = await scModuleService.createSalesChannels({
|
||||
name: "Webshop",
|
||||
})
|
||||
|
||||
const location = await stockLocationModule.createStockLocations({
|
||||
name: "Warehouse",
|
||||
})
|
||||
|
||||
const [product, product_2] = await productModule.createProducts([
|
||||
{
|
||||
title: "Test product",
|
||||
status: ProductStatus.PUBLISHED,
|
||||
variants: [
|
||||
{
|
||||
title: "Test variant",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Another product",
|
||||
status: ProductStatus.PUBLISHED,
|
||||
variants: [
|
||||
{
|
||||
title: "Variant variable",
|
||||
manage_inventory: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
])
|
||||
|
||||
const inventoryItem = await inventoryModule.createInventoryItems({
|
||||
sku: "inv-1234",
|
||||
})
|
||||
|
||||
await inventoryModule.createInventoryLevels([
|
||||
{
|
||||
inventory_item_id: inventoryItem.id,
|
||||
location_id: location.id,
|
||||
stocked_quantity: 2,
|
||||
reserved_quantity: 0,
|
||||
},
|
||||
])
|
||||
|
||||
const [priceSet, priceSet_2] = await pricingModule.createPriceSets([
|
||||
{
|
||||
prices: [
|
||||
{
|
||||
amount: 3000,
|
||||
currency_code: "usd",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
prices: [
|
||||
{
|
||||
amount: 1000,
|
||||
currency_code: "usd",
|
||||
},
|
||||
],
|
||||
},
|
||||
])
|
||||
|
||||
/**
|
||||
* Create a promotion to test with
|
||||
*/
|
||||
const promotion = (
|
||||
await api.post(
|
||||
`/admin/promotions`,
|
||||
{
|
||||
code: "testytest",
|
||||
type: PromotionType.STANDARD,
|
||||
status: PromotionStatus.ACTIVE,
|
||||
is_tax_inclusive: true,
|
||||
application_method: {
|
||||
target_type: "items",
|
||||
type: "percentage",
|
||||
allocation: "each",
|
||||
currency_code: "usd",
|
||||
value: 10,
|
||||
max_quantity: 10,
|
||||
},
|
||||
},
|
||||
adminHeaders
|
||||
)
|
||||
).data.promotion
|
||||
|
||||
await api.post(
|
||||
"/admin/price-preferences",
|
||||
{
|
||||
attribute: "currency_code",
|
||||
value: "usd",
|
||||
is_tax_inclusive: true,
|
||||
},
|
||||
adminHeaders
|
||||
)
|
||||
|
||||
await remoteLink.create([
|
||||
{
|
||||
[Modules.PRODUCT]: {
|
||||
variant_id: product.variants[0].id,
|
||||
},
|
||||
[Modules.PRICING]: {
|
||||
price_set_id: priceSet.id,
|
||||
},
|
||||
},
|
||||
{
|
||||
[Modules.PRODUCT]: {
|
||||
variant_id: product_2.variants[0].id,
|
||||
},
|
||||
[Modules.PRICING]: {
|
||||
price_set_id: priceSet_2.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: product_2.variants[0].id,
|
||||
},
|
||||
[Modules.INVENTORY]: {
|
||||
inventory_item_id: inventoryItem.id,
|
||||
},
|
||||
},
|
||||
])
|
||||
|
||||
await setupTaxStructure(taxModule)
|
||||
|
||||
const payload = {
|
||||
email: "oli@test.dk",
|
||||
region_id: region.id,
|
||||
sales_channel_id: salesChannel.id,
|
||||
currency_code: "usd",
|
||||
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",
|
||||
},
|
||||
items: [
|
||||
{
|
||||
variant_id: product.variants[0].id,
|
||||
is_discountable: true,
|
||||
quantity: 2, // 2 * 3000$ = 6000$ TAX INCLUSIVE
|
||||
},
|
||||
{
|
||||
variant_id: product_2.variants[0].id,
|
||||
is_discountable: true,
|
||||
quantity: 1, // 1 * 1000$ = 1000$ TAX INCLUSIVE
|
||||
},
|
||||
],
|
||||
shipping_methods: [
|
||||
{
|
||||
name: "test-method",
|
||||
shipping_option_id: "test-option",
|
||||
amount: 0,
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
const response = await api.post(
|
||||
"/admin/draft-orders",
|
||||
payload,
|
||||
adminHeaders
|
||||
)
|
||||
|
||||
// begin Order Edit on a Draft Order
|
||||
let orderPreview = (
|
||||
await api.post(
|
||||
`/admin/draft-orders/${response.data.draft_order.id}/edit`,
|
||||
{},
|
||||
adminHeaders
|
||||
)
|
||||
).data.draft_order_preview
|
||||
|
||||
orderPreview = (
|
||||
await api.post(
|
||||
`/admin/draft-orders/${response.data.draft_order.id}/edit/promotions`,
|
||||
{
|
||||
promo_codes: [promotion.code],
|
||||
},
|
||||
adminHeaders
|
||||
)
|
||||
).data.draft_order_preview
|
||||
|
||||
expect(orderPreview.total).toEqual(6300) // 2 * 3000$ - 10% + 1 * 1000$ - 10%
|
||||
expect(orderPreview.original_total).toEqual(7000)
|
||||
expect(orderPreview.discount_total).toEqual(700)
|
||||
|
||||
expect(orderPreview.items).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
total: 5400, // 2 * 3000$ - 10% = 5400$
|
||||
original_total: 6000,
|
||||
discount_total: 600,
|
||||
variant_id: product.variants[0].id,
|
||||
adjustments: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
code: "testytest",
|
||||
amount: 600,
|
||||
promotion_id: promotion.id,
|
||||
is_tax_inclusive: true,
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
total: 900, // 1000$ - 10% = 900$
|
||||
original_total: 1000,
|
||||
discount_total: 100,
|
||||
variant_id: product_2.variants[0].id,
|
||||
adjustments: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
code: "testytest",
|
||||
amount: 100,
|
||||
promotion_id: promotion.id,
|
||||
is_tax_inclusive: true,
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
])
|
||||
)
|
||||
|
||||
orderPreview = (
|
||||
await api.post(
|
||||
`/admin/draft-orders/${response.data.draft_order.id}/edit/confirm`,
|
||||
{},
|
||||
adminHeaders
|
||||
)
|
||||
).data.draft_order_preview
|
||||
|
||||
const draftOrder = (
|
||||
await api.get(
|
||||
`/admin/draft-orders/${response.data.draft_order.id}?fields=+total,+original_total,+discount_total,+version,items.*`,
|
||||
adminHeaders
|
||||
)
|
||||
).data.draft_order
|
||||
|
||||
expect(draftOrder.total).toEqual(6300) // 2 * 3000$ - 10% + 1 * 1000$ - 10%
|
||||
expect(draftOrder.original_total).toEqual(7000)
|
||||
expect(draftOrder.discount_total).toEqual(700)
|
||||
expect(draftOrder.version).toEqual(2)
|
||||
|
||||
expect(draftOrder.items).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
total: 5400,
|
||||
original_total: 6000,
|
||||
discount_total: 600,
|
||||
variant_id: product.variants[0].id,
|
||||
adjustments: [
|
||||
expect.objectContaining({
|
||||
code: "testytest",
|
||||
amount: 600,
|
||||
promotion_id: promotion.id,
|
||||
is_tax_inclusive: true,
|
||||
version: 2,
|
||||
}),
|
||||
],
|
||||
}),
|
||||
expect.objectContaining({
|
||||
total: 900,
|
||||
original_total: 1000,
|
||||
discount_total: 100,
|
||||
variant_id: product_2.variants[0].id,
|
||||
adjustments: [
|
||||
expect.objectContaining({
|
||||
code: "testytest",
|
||||
amount: 100,
|
||||
promotion_id: promotion.id,
|
||||
is_tax_inclusive: true,
|
||||
version: 2,
|
||||
}),
|
||||
],
|
||||
}),
|
||||
])
|
||||
)
|
||||
})
|
||||
|
||||
it("should restore promotions and adjustments on a draft order after edit is canceled", async () => {
|
||||
const region = await regionModuleService.createRegions({
|
||||
name: "US",
|
||||
currency_code: "usd",
|
||||
})
|
||||
|
||||
const salesChannel = await scModuleService.createSalesChannels({
|
||||
name: "Webshop",
|
||||
})
|
||||
|
||||
const location = await stockLocationModule.createStockLocations({
|
||||
name: "Warehouse",
|
||||
})
|
||||
|
||||
const [product, product_2] = await productModule.createProducts([
|
||||
{
|
||||
title: "Test product",
|
||||
status: ProductStatus.PUBLISHED,
|
||||
variants: [
|
||||
{
|
||||
title: "Test variant",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Another product",
|
||||
status: ProductStatus.PUBLISHED,
|
||||
variants: [
|
||||
{
|
||||
title: "Variant variable",
|
||||
manage_inventory: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
])
|
||||
|
||||
const inventoryItem = await inventoryModule.createInventoryItems({
|
||||
sku: "inv-1234",
|
||||
})
|
||||
|
||||
await inventoryModule.createInventoryLevels([
|
||||
{
|
||||
inventory_item_id: inventoryItem.id,
|
||||
location_id: location.id,
|
||||
stocked_quantity: 2,
|
||||
reserved_quantity: 0,
|
||||
},
|
||||
])
|
||||
|
||||
const [priceSet, priceSet_2] = await pricingModule.createPriceSets([
|
||||
{
|
||||
prices: [
|
||||
{
|
||||
amount: 3000,
|
||||
currency_code: "usd",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
prices: [
|
||||
{
|
||||
amount: 1000,
|
||||
currency_code: "usd",
|
||||
},
|
||||
],
|
||||
},
|
||||
])
|
||||
|
||||
/**
|
||||
* Create a promotion to test with
|
||||
*/
|
||||
const promotion = (
|
||||
await api.post(
|
||||
`/admin/promotions`,
|
||||
{
|
||||
code: "testytest",
|
||||
type: PromotionType.STANDARD,
|
||||
status: PromotionStatus.ACTIVE,
|
||||
is_tax_inclusive: true,
|
||||
application_method: {
|
||||
target_type: "items",
|
||||
type: "percentage",
|
||||
allocation: "each",
|
||||
currency_code: "usd",
|
||||
value: 10,
|
||||
max_quantity: 10,
|
||||
},
|
||||
},
|
||||
adminHeaders
|
||||
)
|
||||
).data.promotion
|
||||
|
||||
// additional promotion that will be added then removed
|
||||
const promotionAdditional20 = (
|
||||
await api.post(
|
||||
`/admin/promotions`,
|
||||
{
|
||||
code: "additional_20",
|
||||
type: PromotionType.STANDARD,
|
||||
status: PromotionStatus.ACTIVE,
|
||||
is_tax_inclusive: true,
|
||||
application_method: {
|
||||
target_type: "items",
|
||||
type: "percentage",
|
||||
allocation: "each",
|
||||
currency_code: "usd",
|
||||
value: 20,
|
||||
max_quantity: 10,
|
||||
},
|
||||
},
|
||||
adminHeaders
|
||||
)
|
||||
).data.promotion
|
||||
|
||||
await api.post(
|
||||
"/admin/price-preferences",
|
||||
{
|
||||
attribute: "currency_code",
|
||||
value: "usd",
|
||||
is_tax_inclusive: true,
|
||||
},
|
||||
adminHeaders
|
||||
)
|
||||
|
||||
await remoteLink.create([
|
||||
{
|
||||
[Modules.PRODUCT]: {
|
||||
variant_id: product.variants[0].id,
|
||||
},
|
||||
[Modules.PRICING]: {
|
||||
price_set_id: priceSet.id,
|
||||
},
|
||||
},
|
||||
{
|
||||
[Modules.PRODUCT]: {
|
||||
variant_id: product_2.variants[0].id,
|
||||
},
|
||||
[Modules.PRICING]: {
|
||||
price_set_id: priceSet_2.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: product_2.variants[0].id,
|
||||
},
|
||||
[Modules.INVENTORY]: {
|
||||
inventory_item_id: inventoryItem.id,
|
||||
},
|
||||
},
|
||||
])
|
||||
|
||||
await setupTaxStructure(taxModule)
|
||||
|
||||
const payload = {
|
||||
email: "oli@test.dk",
|
||||
region_id: region.id,
|
||||
sales_channel_id: salesChannel.id,
|
||||
currency_code: "usd",
|
||||
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",
|
||||
},
|
||||
items: [
|
||||
{
|
||||
variant_id: product.variants[0].id,
|
||||
is_discountable: true,
|
||||
quantity: 2, // 2 * 3000$ = 6000$ TAX INCLUSIVE
|
||||
},
|
||||
{
|
||||
variant_id: product_2.variants[0].id,
|
||||
is_discountable: true,
|
||||
quantity: 1, // 1 * 1000$ = 1000$ TAX INCLUSIVE
|
||||
},
|
||||
],
|
||||
shipping_methods: [
|
||||
{
|
||||
name: "test-method",
|
||||
shipping_option_id: "test-option",
|
||||
amount: 0,
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
const response = await api.post(
|
||||
"/admin/draft-orders",
|
||||
payload,
|
||||
adminHeaders
|
||||
)
|
||||
|
||||
// begin Order Edit on a Draft Order
|
||||
let orderPreview = (
|
||||
await api.post(
|
||||
`/admin/draft-orders/${response.data.draft_order.id}/edit`,
|
||||
{},
|
||||
adminHeaders
|
||||
)
|
||||
).data.draft_order_preview
|
||||
|
||||
// add the promotion
|
||||
orderPreview = (
|
||||
await api.post(
|
||||
`/admin/draft-orders/${response.data.draft_order.id}/edit/promotions`,
|
||||
{
|
||||
promo_codes: [promotion.code],
|
||||
},
|
||||
adminHeaders
|
||||
)
|
||||
).data.draft_order_preview
|
||||
|
||||
// confirm changes which applies adjustemnts to the draft order
|
||||
orderPreview = (
|
||||
await api.post(
|
||||
`/admin/draft-orders/${response.data.draft_order.id}/edit/confirm`,
|
||||
{},
|
||||
adminHeaders
|
||||
)
|
||||
).data.draft_order_preview
|
||||
|
||||
const draftOrder = (
|
||||
await api.get(
|
||||
`/admin/draft-orders/${response.data.draft_order.id}?fields=+total,+original_total,+discount_total,+version,items.*`,
|
||||
adminHeaders
|
||||
)
|
||||
).data.draft_order
|
||||
|
||||
expect(draftOrder.total).toEqual(6300) // 2 * 3000$ - 10% + 1 * 1000$ - 10%
|
||||
expect(draftOrder.original_total).toEqual(7000)
|
||||
expect(draftOrder.discount_total).toEqual(700)
|
||||
expect(draftOrder.version).toEqual(2)
|
||||
|
||||
expect(draftOrder.items[0].adjustments[0].version).toEqual(2)
|
||||
expect(draftOrder.items[1].adjustments[0].version).toEqual(2)
|
||||
|
||||
expect(orderPreview.items).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
total: 5400,
|
||||
original_total: 6000,
|
||||
discount_total: 600,
|
||||
variant_id: product.variants[0].id,
|
||||
adjustments: [
|
||||
expect.objectContaining({
|
||||
code: "testytest",
|
||||
amount: 600,
|
||||
promotion_id: promotion.id,
|
||||
is_tax_inclusive: true,
|
||||
}),
|
||||
],
|
||||
}),
|
||||
expect.objectContaining({
|
||||
total: 900,
|
||||
original_total: 1000,
|
||||
discount_total: 100,
|
||||
variant_id: product_2.variants[0].id,
|
||||
adjustments: [
|
||||
expect.objectContaining({
|
||||
code: "testytest",
|
||||
amount: 100,
|
||||
promotion_id: promotion.id,
|
||||
is_tax_inclusive: true,
|
||||
}),
|
||||
],
|
||||
}),
|
||||
])
|
||||
)
|
||||
|
||||
// begin another edit...
|
||||
orderPreview = (
|
||||
await api.post(
|
||||
`/admin/draft-orders/${response.data.draft_order.id}/edit`,
|
||||
{},
|
||||
adminHeaders
|
||||
)
|
||||
).data.draft_order_preview
|
||||
|
||||
// ...and add the additional promotion
|
||||
orderPreview = (
|
||||
await api.post(
|
||||
`/admin/draft-orders/${response.data.draft_order.id}/edit/promotions`,
|
||||
{
|
||||
promo_codes: [promotionAdditional20.code],
|
||||
},
|
||||
adminHeaders
|
||||
)
|
||||
).data.draft_order_preview
|
||||
|
||||
expect(orderPreview.total).toEqual(5040) // (2 * 3000$ - 10%) - 20% + (1 * 1000$ - 10%) - 20%
|
||||
expect(orderPreview.original_total).toEqual(7000)
|
||||
expect(orderPreview.discount_total).toEqual(600 + 100 + 1080 + 180)
|
||||
|
||||
expect(orderPreview.version).toEqual(2)
|
||||
expect(orderPreview.items[0].adjustments.length).toEqual(2)
|
||||
expect(orderPreview.items[1].adjustments.length).toEqual(2)
|
||||
|
||||
// new adjustments exists on the preview
|
||||
expect(orderPreview.items).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
total: 4320,
|
||||
original_total: 6000,
|
||||
discount_total: 1680,
|
||||
variant_id: product.variants[0].id,
|
||||
adjustments: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
code: "testytest",
|
||||
amount: 480,
|
||||
promotion_id: promotion.id,
|
||||
is_tax_inclusive: true,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
code: "additional_20",
|
||||
amount: 1200,
|
||||
promotion_id: promotionAdditional20.id,
|
||||
is_tax_inclusive: true,
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
total: 720,
|
||||
original_total: 1000,
|
||||
discount_total: 280,
|
||||
variant_id: product_2.variants[0].id,
|
||||
adjustments: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
code: "testytest",
|
||||
amount: 80,
|
||||
promotion_id: promotion.id,
|
||||
is_tax_inclusive: true,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
code: "additional_20",
|
||||
amount: 200,
|
||||
promotion_id: promotionAdditional20.id,
|
||||
is_tax_inclusive: true,
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
])
|
||||
)
|
||||
|
||||
await api.delete(
|
||||
`/admin/draft-orders/${response.data.draft_order.id}/edit`,
|
||||
adminHeaders
|
||||
)
|
||||
|
||||
const draftOrderAfterCancel = (
|
||||
await api.get(
|
||||
`/admin/draft-orders/${response.data.draft_order.id}?fields=+total,+original_total,+discount_total`,
|
||||
adminHeaders
|
||||
)
|
||||
).data.draft_order
|
||||
|
||||
// reverted to be same as before second edit started (10% discount only applied)
|
||||
expect(draftOrderAfterCancel.total).toEqual(6300)
|
||||
expect(draftOrderAfterCancel.original_total).toEqual(7000)
|
||||
expect(draftOrderAfterCancel.discount_total).toEqual(700)
|
||||
expect(draftOrderAfterCancel.version).toEqual(2)
|
||||
})
|
||||
|
||||
it("should create a draft order and apply tax by product type", async () => {
|
||||
const productType = await productModule.createProductTypes({
|
||||
value: "test_product_type",
|
||||
|
||||
@@ -372,6 +372,7 @@ medusaIntegrationTestRunner({
|
||||
value: "5e-18",
|
||||
precision: 20,
|
||||
},
|
||||
version: 1,
|
||||
provider_id: expect.any(String),
|
||||
created_at: expect.any(String),
|
||||
updated_at: expect.any(String),
|
||||
|
||||
Reference in New Issue
Block a user