feat(core-flows,types,order,cart): assign tax lines only to regular products (#11994)

what:

- assign tax lines only to regular products
This commit is contained in:
Riqwan Thamir
2025-03-26 23:26:34 +01:00
committed by GitHub
parent e950e2b2d2
commit 1f8fab3636
19 changed files with 387 additions and 143 deletions

View File

@@ -0,0 +1,8 @@
---
"@medusajs/core-flows": patch
"@medusajs/order": patch
"@medusajs/cart": patch
"@medusajs/types": patch
---
feat(core-flows,types,order,cart): assign tax lines only to regular products

View File

@@ -2001,6 +2001,72 @@ medusaIntegrationTestRunner({
)
})
it("should not generate tax lines for gift card products", async () => {
const giftCardProduct = (
await api.post(
`/admin/products`,
{
title: "Gift Card",
description: "test",
is_giftcard: true,
options: [
{
title: "Denomination",
values: ["10", "20", "50", "100"],
},
],
variants: [
{
title: "10",
sku: "special-shirt",
options: {
Denomination: "10",
},
manage_inventory: false,
prices: [
{
amount: 1000,
currency_code: "usd",
},
],
},
],
},
adminHeaders
)
).data.product
let updated = await api.post(
`/store/carts/${cart.id}/line-items?fields=+items.is_giftcard`,
{ variant_id: giftCardProduct.variants[0].id, quantity: 1 },
storeHeaders
)
expect(updated.status).toEqual(200)
expect(updated.data.cart).toEqual(
expect.objectContaining({
id: cart.id,
items: expect.arrayContaining([
expect.objectContaining({
is_giftcard: false,
tax_lines: [
expect.objectContaining({
description: "CA Default Rate",
code: "CADEFAULT",
rate: 5,
provider_id: "system",
}),
],
}),
expect.objectContaining({
is_giftcard: true,
tax_lines: [],
}),
]),
})
)
})
it("should update a cart's region, sales channel, customer data and tax lines", async () => {
const newSalesChannel = (
await api.post(

View File

@@ -820,6 +820,7 @@ medusaIntegrationTestRunner({
deleted_at: null,
id: expect.any(String),
is_discountable: false,
is_giftcard: false,
is_tax_inclusive: false,
is_custom_price: true,
metadata: {
@@ -1179,6 +1180,7 @@ medusaIntegrationTestRunner({
// Custom line item
id: expect.any(String),
is_discountable: false,
is_giftcard: false,
is_tax_inclusive: false,
is_custom_price: true,
quantity: 1,
@@ -1415,6 +1417,7 @@ medusaIntegrationTestRunner({
deleted_at: null,
id: expect.any(String),
is_discountable: false,
is_giftcard: false,
is_tax_inclusive: false,
is_custom_price: true,
metadata: {
@@ -1876,6 +1879,7 @@ medusaIntegrationTestRunner({
deleted_at: null,
id: expect.any(String),
is_discountable: false,
is_giftcard: false,
is_tax_inclusive: false,
is_custom_price: true,
metadata: {
@@ -1937,6 +1941,7 @@ medusaIntegrationTestRunner({
deleted_at: null,
id: expect.any(String),
is_discountable: false,
is_giftcard: false,
is_tax_inclusive: false,
is_custom_price: true,
metadata: {

View File

@@ -172,6 +172,7 @@ medusaIntegrationTestRunner({
variant_option_values: null,
requires_shipping: true,
is_discountable: true,
is_giftcard: false,
is_tax_inclusive: false,
raw_compare_at_unit_price: null,
raw_unit_price: {

View File

@@ -16,6 +16,7 @@ export const cartFieldsForRefreshSteps = [
"region.*",
"items.*",
"items.product.id",
"items.product.is_giftcard",
"items.product.collection_id",
"items.product.categories.id",
"items.product.tags.id",
@@ -105,6 +106,7 @@ export const completeCartFields = [
"payment_collection.payment_sessions.*",
"items.variant.id",
"items.variant.product.id",
"items.variant.product.is_giftcard",
"items.variant.product.shipping_profile.id",
"items.variant.manage_inventory",
"items.variant.allow_backorder",
@@ -157,6 +159,7 @@ export const productVariantsFields = [
"product.collection.title",
"product.handle",
"product.discountable",
"product.is_giftcard",
"product.shipping_profile.id",
"calculated_price.*",
"inventory_items.inventory_item_id",

View File

@@ -150,6 +150,7 @@ export function prepareLineItemData(data: PrepareLineItemDataInput) {
variant_option_values: item?.variant_option_values,
is_discountable: variant?.product?.discountable ?? item?.is_discountable,
is_giftcard: variant?.product?.is_giftcard ?? false,
requires_shipping: requiresShipping,
unit_price: unitPrice,

View File

@@ -21,6 +21,7 @@ const cartFields = [
"items.id",
"items.variant_id",
"items.product_id",
"items.product.is_giftcard",
"items.product_title",
"items.product_description",
"items.product_subtitle",

View File

@@ -86,13 +86,13 @@ export const createOrdersWorkflowId = "create-orders"
/**
* This workflow creates an order. It's used by the [Create Draft Order Admin API Route](https://docs.medusajs.com/api/admin#draft-orders_postdraftorders), but
* you can also use it to create any order.
*
* This workflow has a hook that allows you to perform custom actions on the created order. For example, you can pass under `additional_data` custom data that
*
* This workflow has a hook that allows you to perform custom actions on the created order. For example, you can pass under `additional_data` custom data that
* allows you to create custom data models linked to the order.
*
*
* You can also use this workflow within your customizations or your own custom workflows, allowing you to wrap custom logic around creating an order. For example,
* you can create a workflow that imports orders from an external system, then uses this workflow to create the orders in Medusa.
*
*
* @example
* const { result } = await createOrderWorkflow(container)
* .run({
@@ -121,11 +121,11 @@ export const createOrdersWorkflowId = "create-orders"
* }
* }
* })
*
*
* @summary
*
*
* Create an order.
*
*
* @property hooks.orderCreated - This hook is executed after the order is created. You can consume this hook to perform custom actions on the created order.
*/
export const createOrderWorkflow = createWorkflow(

View File

@@ -17,6 +17,7 @@ const completeOrderFields = [
"region.automatic_taxes",
"items.id",
"items.is_tax_inclusive",
"items.is_giftcard",
"items.variant_id",
"items.product_id",
"items.product_title",
@@ -101,6 +102,7 @@ const lineItemFields = [
"variant_id",
"product_id",
"is_tax_inclusive",
"is_giftcard",
"product_title",
"product_description",
"product_subtitle",
@@ -137,7 +139,7 @@ export type UpdateOrderTaxLinesWorkflowInput = {
shipping_method_ids?: string[]
/**
* Whether to force the tax calculation. If enabled, the tax provider
* may send request to a third-party service to retrieve the calculated
* may send request to a third-party service to retrieve the calculated
* tax rates. This depends on the chosen tax provider in the order's tax region.
*/
force_tax_calculation?: boolean
@@ -156,10 +158,10 @@ export const updateOrderTaxLinesWorkflowId = "update-order-tax-lines"
* This workflow updates the tax lines of items and shipping methods in an order. It's used by
* other order-related workflows, such as the {@link createOrderWorkflow} to set the order's
* tax lines.
*
*
* You can use this workflow within your customizations or your own custom workflows, allowing you to update an
* order's tax lines in your custom flows.
*
*
* @example
* const { result } = await updateOrderTaxLinesWorkflow(container)
* .run({
@@ -168,9 +170,9 @@ export const updateOrderTaxLinesWorkflowId = "update-order-tax-lines"
* item_ids: ["orli_123", "orli_456"],
* }
* })
*
*
* @summary
*
*
* Update the tax lines of items and shipping methods in an order.
*/
export const updateOrderTaxLinesWorkflow = createWorkflow(

View File

@@ -12,7 +12,7 @@ import {
TaxableShippingDTO,
TaxCalculationContext,
} from "@medusajs/framework/types"
import { MedusaError, Modules } from "@medusajs/framework/utils"
import { isDefined, MedusaError, Modules } from "@medusajs/framework/utils"
import { createStep, StepResponse } from "@medusajs/framework/workflows-sdk"
/**
@@ -168,6 +168,11 @@ export const getItemTaxLinesStep = createStep(
is_return: isReturn = false,
shipping_address: shippingAddress,
} = data
const filteredItems = items.filter(
(item) => !item.is_giftcard || !isDefined(item.is_giftcard)
) as OrderLineItemDTO[] | CartLineItemDTO[]
const taxService = container.resolve<ITaxModuleService>(Modules.TAX)
const taxContext = normalizeTaxModuleContext(
@@ -188,7 +193,7 @@ export const getItemTaxLinesStep = createStep(
if (items.length) {
stepResponseData.lineItemTaxLines = (await taxService.getTaxLines(
normalizeLineItemsForTax(orderOrCart, items),
normalizeLineItemsForTax(orderOrCart, filteredItems),
taxContext
)) as ItemTaxLineDTO[]
}

View File

@@ -658,6 +658,11 @@ export interface CartLineItemDTO extends CartLineItemTotalsDTO {
*/
is_discountable: boolean
/**
* Whether the line item is a gift card.
*/
is_giftcard: boolean
/**
* Whether the line item price is tax inclusive.
*/

View File

@@ -789,6 +789,11 @@ export interface OrderLineItemDTO extends OrderLineItemTotalsDTO {
*/
is_discountable: boolean
/**
* Indicates whether the line item is a gift card.
*/
is_giftcard: boolean
/**
* Indicates whether the line item price is tax inclusive.
*/

View File

@@ -2886,6 +2886,7 @@ moduleIntegrationTestRunner<ICartModuleService>({
variant_option_values: null,
requires_shipping: true,
is_discountable: true,
is_giftcard: false,
is_tax_inclusive: false,
is_custom_price: false,
raw_compare_at_unit_price: null,
@@ -2992,6 +2993,7 @@ moduleIntegrationTestRunner<ICartModuleService>({
variant_option_values: null,
requires_shipping: true,
is_discountable: true,
is_giftcard: false,
is_tax_inclusive: false,
is_custom_price: false,
raw_compare_at_unit_price: null,

View File

@@ -757,6 +757,16 @@
"default": "true",
"mappedType": "boolean"
},
"is_giftcard": {
"name": "is_giftcard",
"type": "boolean",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": false,
"default": "false",
"mappedType": "boolean"
},
"is_tax_inclusive": {
"name": "is_tax_inclusive",
"type": "boolean",

View File

@@ -0,0 +1,15 @@
import { Migration } from "@mikro-orm/migrations"
export class Migration20250326151602 extends Migration {
override async up(): Promise<void> {
this.addSql(
`alter table if exists "cart_line_item" add column if not exists "is_giftcard" boolean not null default false;`
)
}
override async down(): Promise<void> {
this.addSql(
`alter table if exists "cart_line_item" drop column if exists "is_giftcard";`
)
}
}

View File

@@ -27,6 +27,7 @@ const LineItem = model
variant_option_values: model.json().nullable(),
requires_shipping: model.boolean().default(true),
is_discountable: model.boolean().default(true),
is_giftcard: model.boolean().default(false),
is_tax_inclusive: model.boolean().default(false),
is_custom_price: model.boolean().default(false),
compare_at_unit_price: model.bigNumber().nullable(),

View File

@@ -0,0 +1,15 @@
import { Migration } from "@mikro-orm/migrations"
export class Migration20250326151554 extends Migration {
override async up(): Promise<void> {
this.addSql(
`alter table if exists "order_line_item" add column if not exists "is_giftcard" boolean not null default false;`
)
}
override async down(): Promise<void> {
this.addSql(
`alter table if exists "order_line_item" drop column if exists "is_giftcard";`
)
}
}

View File

@@ -22,6 +22,7 @@ const _OrderLineItem = model
variant_title: model.text().nullable(),
variant_option_values: model.json().nullable(),
requires_shipping: model.boolean().default(true),
is_giftcard: model.boolean().default(false),
is_discountable: model.boolean().default(true),
is_tax_inclusive: model.boolean().default(false),
compare_at_unit_price: model.bigNumber().nullable(),