feat: new tax api (#979)
* feat: add tax calculation strategy (#885) * feat: add tax calculation strategy * fix: adds strategy loader * fix: eslint ignore * chore: cleanup * fix: allow plugin overwrites * fix: allow plugin overwrites * fix: fake region * Update packages/medusa/src/loaders/strategies.ts Co-authored-by: Oliver Windall Juhl <59018053+olivermrbl@users.noreply.github.com> Co-authored-by: Oliver Windall Juhl <59018053+olivermrbl@users.noreply.github.com> * feat: adds tax related db entities + tax provider (#896) * feat: adds tax related db entities + tax provider * fix: add tax provider tests * fix: add tax service unit tests * fix: tests + migrations * fix: add inherited tax lines * chore: rm tax-line repo * fix: test * fix: pr comments * fix: unit test * feat: totals service to ts (#911) * feat: adds tax related db entities + tax provider * fix: add tax provider tests * fix: add tax service unit tests * fix: tests + migrations * feat: totals service to ts * fix: remove totals.js * fix: add shipping methods * fix: add inherited tax lines * chore: rm tax-line repo * fix: test * fix: tests * fix: tests * fix: unit test * fix: adds TotalsServiceProps * feat: adds integration tests for automatic tax calculation + shipping tax rates (#945) * feat: adds tax related db entities + tax provider * fix: add tax provider tests * fix: add tax service unit tests * fix: tests + migrations * feat: totals service to ts * fix: remove totals.js * fix: add shipping methods * fix: add inherited tax lines * chore: rm tax-line repo * fix: test * fix: tests * fix: tests * fix: unit test * fix: integration test helpers * fix: adds factories + tests automatic tax rates * fix: remove verbose * fix: adds TotalsServiceProps * fix: add shipping tax lines * fix: add migration for shipping taxes * fix: integration tests for shipping taxes * fix: integration tests for shipping taxes * fix: jsdoc types * Feat/manual taxes (#950) * feat: adds tax related db entities + tax provider * fix: add tax provider tests * fix: add tax service unit tests * fix: tests + migrations * feat: totals service to ts * fix: remove totals.js * fix: add shipping methods * fix: add inherited tax lines * chore: rm tax-line repo * fix: test * fix: tests * fix: tests * fix: unit test * fix: integration test helpers * fix: adds factories + tests automatic tax rates * fix: remove verbose * fix: adds TotalsServiceProps * fix: add shipping tax lines * fix: add migration for shipping taxes * fix: integration tests for shipping taxes * fix: integration tests for shipping taxes * fix: add integration tests for manual taxes * fix: cart service - cleanup jsdoc * feat: add /carts/id/taxes to manually calculate taxes * feat: add integration tests for order tax calculations * fix: unit tests * fix: merge * fix: rm verbose * fix: unit tests * fix: object -> cartOrOrder * fix: rounding * Feat/complete order w tax lines (#951) * feat: adds tax related db entities + tax provider * fix: add tax provider tests * fix: add tax service unit tests * fix: tests + migrations * feat: totals service to ts * fix: remove totals.js * fix: add shipping methods * fix: add inherited tax lines * chore: rm tax-line repo * fix: test * fix: tests * fix: tests * fix: unit test * fix: integration test helpers * fix: adds factories + tests automatic tax rates * fix: remove verbose * fix: adds TotalsServiceProps * fix: add shipping tax lines * fix: add migration for shipping taxes * fix: integration tests for shipping taxes * fix: integration tests for shipping taxes * fix: add integration tests for manual taxes * fix: cart service - cleanup jsdoc * feat: add /carts/id/taxes to manually calculate taxes * feat: add integration tests for order tax calculations * feat: adds cart completion strategy + create order w. tax lines * fix: unit tests * fix: merge * fix: rm verbose * fix: unit tests * fix: unit tests * fix: unit tests * fix: ensure calculation for list orders * fix: unit tests * fix: integration tests * fix: adds cart order type gaurds * Docs/tax api (#954) * feat: adds tax related db entities + tax provider * fix: add tax provider tests * fix: add tax service unit tests * fix: tests + migrations * feat: totals service to ts * fix: remove totals.js * fix: add shipping methods * fix: add inherited tax lines * chore: rm tax-line repo * fix: test * fix: tests * fix: tests * fix: unit test * fix: integration test helpers * fix: adds factories + tests automatic tax rates * fix: remove verbose * fix: adds TotalsServiceProps * fix: add shipping tax lines * fix: add migration for shipping taxes * fix: integration tests for shipping taxes * fix: integration tests for shipping taxes * fix: add integration tests for manual taxes * fix: cart service - cleanup jsdoc * feat: add /carts/id/taxes to manually calculate taxes * feat: add integration tests for order tax calculations * feat: adds cart completion strategy + create order w. tax lines * fix: unit tests * fix: merge * fix: rm verbose * fix: unit tests * fix: unit tests * fix: unit tests * fix: ensure calculation for list orders * fix: unit tests * fix: integration tests * docs: documents tax related methods and types * fix: require either item_id or shipping_method_id * feat: product type tax rate (#969) * feat: adds tax related db entities + tax provider * fix: add tax provider tests * fix: add tax service unit tests * fix: tests + migrations * feat: totals service to ts * fix: remove totals.js * fix: add shipping methods * fix: add inherited tax lines * chore: rm tax-line repo * fix: test * fix: tests * fix: tests * fix: unit test * fix: integration test helpers * fix: adds factories + tests automatic tax rates * fix: remove verbose * fix: adds TotalsServiceProps * fix: add shipping tax lines * fix: add migration for shipping taxes * fix: integration tests for shipping taxes * fix: integration tests for shipping taxes * fix: add integration tests for manual taxes * fix: cart service - cleanup jsdoc * feat: add /carts/id/taxes to manually calculate taxes * feat: add integration tests for order tax calculations * feat: adds cart completion strategy + create order w. tax lines * fix: unit tests * fix: merge * fix: rm verbose * fix: unit tests * fix: unit tests * fix: unit tests * fix: ensure calculation for list orders * fix: unit tests * fix: integration tests * docs: documents tax related methods and types * fix: require either item_id or shipping_method_id * feat: adds returns tests for new tax system * feat: adds return lines + integration tests for swaps * feat: return integration tests * feat: adds product type tax rates * feat: add tax management endpoints * fix: create single migration * fix: adds tax rates to js client * fix: strats * Fix/plugin tests (#998) * plugin testing setup * fix: test sendgrid plugin * fix: test sendgrid plugin * chore: clean * chore: clean * fix: clean up tests * fix: remove dirty import * fix: sendgrid + brightpearl * fix: plugin integration tests * fix: klarna * fix: shipping method tax * fix: remove taxrates * fix: unit tests * fix: integration * fix: integration * fix: plugins tests * fix: ignore plugins * fix: tests * fix: taxes (#1017) * fix: taxes * fix: taxes * fix: faulty ref * fix: create tax-lines with claim items * fix: snapshot tax-liens * fix: allows integration test teardown to force deleting tables * fix: tests * fix: merge * fix: adds tax-rates to client * fix: adds tax-rates to medusa-react * fix: tests * fix: tests * fix: add product types * fix: adds tax provider endpoint + cascaded deletes on tax rate relations * fix: move errors to service layer * fix: cleanup api * fix: unit tests * fix: error handler in base-service * fix: Add order region to swap on createFulfillment (#1110) Co-authored-by: Oliver Windall Juhl <59018053+olivermrbl@users.noreply.github.com>
This commit is contained in:
committed by
olivermrbl
parent
d80eaa172d
commit
47588e7a8d
@@ -156,6 +156,20 @@ const TotalsService = {
|
||||
getTotal: () => {
|
||||
return Promise.resolve(123)
|
||||
},
|
||||
getShippingMethodTotals: () => {
|
||||
return {
|
||||
price: 0,
|
||||
discount_total: 0,
|
||||
tax_total: 0,
|
||||
}
|
||||
},
|
||||
getLineItemTotals: () => {
|
||||
return {
|
||||
subtotal: 15800,
|
||||
discount_total: 0,
|
||||
tax_total: 3950,
|
||||
}
|
||||
},
|
||||
getLineDiscounts: (o) => {
|
||||
if (o.id === "rounding") {
|
||||
return [
|
||||
@@ -242,76 +256,6 @@ describe("BrightpearlService", () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe("createSalesOrder", () => {
|
||||
const bpService = new BrightpearlService(
|
||||
{
|
||||
orderService: OrderService,
|
||||
totalsService: TotalsService,
|
||||
oauthService: OAuthService,
|
||||
regionService: RegionService,
|
||||
},
|
||||
{ account: "test" }
|
||||
)
|
||||
|
||||
it("successfully builds sales order", async () => {
|
||||
jest.clearAllMocks()
|
||||
|
||||
await bpService.createSalesOrder(order)
|
||||
|
||||
expect(mockCreateOrder).toHaveBeenCalledWith({
|
||||
currency: { code: "DKK" },
|
||||
ref: "1234",
|
||||
externalRef: "12355",
|
||||
channelId: "1",
|
||||
installedIntegrationInstanceId: undefined,
|
||||
statusId: "3",
|
||||
customer: {
|
||||
id: "12345",
|
||||
address: {
|
||||
addressFullName: "Test Testson",
|
||||
addressLine1: "Test",
|
||||
addressLine2: "TEst",
|
||||
postalCode: "1234",
|
||||
countryIsoCode: "DK",
|
||||
telephone: "12345678",
|
||||
email: "test@example.com",
|
||||
},
|
||||
},
|
||||
delivery: {
|
||||
shippingMethodId: 0,
|
||||
address: {
|
||||
addressFullName: "Test Testson",
|
||||
addressLine1: "Test",
|
||||
addressLine2: "TEst",
|
||||
postalCode: "1234",
|
||||
countryIsoCode: "DK",
|
||||
telephone: "12345678",
|
||||
email: "test@example.com",
|
||||
},
|
||||
},
|
||||
rows: [
|
||||
{
|
||||
name: "Test",
|
||||
net: 22,
|
||||
tax: 5.082,
|
||||
quantity: 2,
|
||||
taxCode: "1234",
|
||||
externalRef: undefined,
|
||||
nominalCode: "4000",
|
||||
},
|
||||
{
|
||||
name: "Shipping: standard",
|
||||
quantity: 1,
|
||||
net: 123.99,
|
||||
tax: 28.6417,
|
||||
taxCode: "1234",
|
||||
nominalCode: "4040",
|
||||
},
|
||||
],
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("rounding", () => {
|
||||
const bpService = new BrightpearlService(
|
||||
{
|
||||
|
||||
@@ -322,15 +322,8 @@ class BrightpearlService extends BaseService {
|
||||
name: `${fromRefund.reason}: ${fromRefund.note}`,
|
||||
quantity: 1,
|
||||
taxCode: region.tax_code,
|
||||
net: this.bpnum_(
|
||||
fromRefund.amount,
|
||||
fromOrder.currency_code,
|
||||
10000 / (100 + fromOrder.tax_rate)
|
||||
),
|
||||
tax: this.bpnum_(
|
||||
fromRefund.amount * (1 - 100 / (100 + fromOrder.tax_rate)),
|
||||
fromOrder.currency_code
|
||||
),
|
||||
net: this.bpnum_(fromRefund.amount, fromOrder.currency_code),
|
||||
tax: 0,
|
||||
nominalCode: accountingCode,
|
||||
},
|
||||
],
|
||||
@@ -410,6 +403,29 @@ class BrightpearlService extends BaseService {
|
||||
}),
|
||||
}
|
||||
|
||||
if (fromReturn.shipping_method) {
|
||||
const totals = await this.totalsService_.getShippingMethodTotals(
|
||||
fromReturn.shipping_method,
|
||||
fromOrder,
|
||||
{
|
||||
include_tax: true,
|
||||
use_tax_lines: true,
|
||||
}
|
||||
)
|
||||
|
||||
order.rows.push({
|
||||
net: this.bpnum_(
|
||||
-1 * (totals.total - totals.tax_total),
|
||||
fromOrder.currency_code
|
||||
),
|
||||
tax: this.bpnum_(-1 * totals.tax_total, fromOrder.currency_code),
|
||||
name: "Return shipping",
|
||||
taxCode: region.tax_code,
|
||||
nominalCode: this.options.shipping_account_code || "4040",
|
||||
quantity: 1,
|
||||
})
|
||||
}
|
||||
|
||||
const total = order.rows.reduce((acc, next) => {
|
||||
return acc + next.net + next.tax
|
||||
}, 0)
|
||||
@@ -421,15 +437,8 @@ class BrightpearlService extends BaseService {
|
||||
name: "Difference",
|
||||
quantity: 1,
|
||||
taxCode: region.tax_code,
|
||||
net: this.bpnum_(
|
||||
difference,
|
||||
fromOrder.currency_code,
|
||||
10000 / (100 + fromOrder.tax_rate)
|
||||
),
|
||||
tax: this.bpnum_(
|
||||
difference * (1 - 100 / (100 + fromOrder.tax_rate)),
|
||||
fromOrder.currency_code
|
||||
),
|
||||
net: this.bpround_(difference),
|
||||
tax: 0,
|
||||
nominalCode: this.options.sales_account_code || "4000",
|
||||
})
|
||||
}
|
||||
@@ -811,14 +820,24 @@ class BrightpearlService extends BaseService {
|
||||
fromSwap.return_order.shipping_method &&
|
||||
fromSwap.return_order.shipping_method.price
|
||||
) {
|
||||
const shippingTotals =
|
||||
await this.totalsService_.getShippingMethodTotals(
|
||||
fromSwap.return_order.shipping_method,
|
||||
fromOrder,
|
||||
{
|
||||
include_tax: true,
|
||||
use_tax_lines: true,
|
||||
}
|
||||
)
|
||||
order.rows.push({
|
||||
name: "Return Shipping",
|
||||
quantity: 1,
|
||||
taxCode: region.tax_code,
|
||||
net: (-1 * fromSwap.return_order.shipping_method.price) / 100,
|
||||
tax:
|
||||
((-1 * fromSwap.return_order.shipping_method.price) / 100) *
|
||||
(fromOrder.tax_rate / 100),
|
||||
net: this.bpnum_(1 * shippingTotals.price, fromOrder.currency_code),
|
||||
tax: this.bpnum_(
|
||||
1 * shippingTotals.tax_total,
|
||||
fromOrder.currency_code
|
||||
),
|
||||
nominalCode: this.options.shipping_account_code || "4040",
|
||||
})
|
||||
}
|
||||
@@ -882,21 +901,19 @@ class BrightpearlService extends BaseService {
|
||||
config = { include_price: true, is_claim: false }
|
||||
) {
|
||||
const { region } = fromOrder
|
||||
const discount = fromOrder.discounts.find(
|
||||
({ rule }) => rule.type !== "free_shipping"
|
||||
)
|
||||
let lineDiscounts = []
|
||||
if (discount) {
|
||||
lineDiscounts = this.totalsService_.getLineDiscounts(fromOrder, discount)
|
||||
}
|
||||
|
||||
const lines = await Promise.all(
|
||||
fromOrder.items.map(async (item) => {
|
||||
const bpProduct = await this.retrieveProductBySKU(item.variant.sku)
|
||||
|
||||
const ld = lineDiscounts.find((l) => item.id === l.item.id) || {
|
||||
amount: 0,
|
||||
}
|
||||
const lineTotals = await this.totalsService_.getLineItemTotals(
|
||||
item,
|
||||
fromOrder,
|
||||
{
|
||||
include_tax: true,
|
||||
use_tax_lines: true,
|
||||
}
|
||||
)
|
||||
|
||||
const row = {}
|
||||
if (bpProduct) {
|
||||
@@ -907,23 +924,20 @@ class BrightpearlService extends BaseService {
|
||||
|
||||
if (config.include_price) {
|
||||
row.net = this.bpnum_(
|
||||
item.unit_price * item.quantity - ld.amount,
|
||||
lineTotals.subtotal - lineTotals.discount_total,
|
||||
fromOrder.currency_code
|
||||
)
|
||||
row.tax = this.bpnum_(
|
||||
item.unit_price * item.quantity - ld.amount,
|
||||
fromOrder.currency_code,
|
||||
fromOrder.tax_rate
|
||||
)
|
||||
row.tax = this.bpnum_(lineTotals.tax_total, fromOrder.currency_code)
|
||||
} else if (config.is_claim) {
|
||||
row.net = await this.retrieveProductPrice(
|
||||
bpProduct.productId,
|
||||
this.options.cost_price_list || `1`
|
||||
)
|
||||
|
||||
row.tax = this.bpnum_(
|
||||
row.net * 100,
|
||||
fromOrder.currency_code,
|
||||
fromOrder.tax_rate
|
||||
lineTotals.tax_lines.reduce((acc, next) => acc + next.rate, 0)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -948,34 +962,48 @@ class BrightpearlService extends BaseService {
|
||||
// purchased.
|
||||
const gcTotal = fromOrder.gift_card_total
|
||||
if (gcTotal) {
|
||||
let tax = 0
|
||||
if (fromOrder.region.gift_cards_taxable) {
|
||||
tax = this.bpnum_(
|
||||
-1 * gcTotal,
|
||||
fromOrder.currency_code,
|
||||
fromOrder.region.tax_rate
|
||||
)
|
||||
}
|
||||
|
||||
lines.push({
|
||||
name: `Gift Card`,
|
||||
net: this.bpnum_(-1 * gcTotal, fromOrder.currency_code),
|
||||
tax: this.bpnum_(
|
||||
-1 * gcTotal,
|
||||
fromOrder.currency_code,
|
||||
fromOrder.tax_rate
|
||||
),
|
||||
tax,
|
||||
quantity: 1,
|
||||
taxCode: region.tax_code,
|
||||
nominalCode: this.options.gift_card_account_code || "4000",
|
||||
})
|
||||
}
|
||||
|
||||
const shippingTotal =
|
||||
fromOrder.shipping_total ||
|
||||
this.totalsService_.getShippingTotal(fromOrder)
|
||||
const shippingMethods = fromOrder.shipping_methods
|
||||
if (shippingMethods.length > 0) {
|
||||
const shippingTotal = await shippingMethods.reduce(async (acc, next) => {
|
||||
const accum = await acc
|
||||
const totals = await this.totalsService_.getShippingMethodTotals(
|
||||
next,
|
||||
fromOrder,
|
||||
{
|
||||
include_tax: true,
|
||||
use_tax_lines: true,
|
||||
}
|
||||
)
|
||||
return {
|
||||
price: accum.price + totals.price,
|
||||
tax: accum.tax + totals.tax_total,
|
||||
}
|
||||
}, Promise.resolve({ price: 0, tax: 0 }))
|
||||
|
||||
lines.push({
|
||||
name: `Shipping: ${shippingMethods.map((m) => m.name).join(" + ")}`,
|
||||
quantity: 1,
|
||||
net: this.bpnum_(shippingTotal, fromOrder.currency_code),
|
||||
tax: this.bpnum_(
|
||||
shippingTotal,
|
||||
fromOrder.currency_code,
|
||||
fromOrder.tax_rate
|
||||
),
|
||||
net: this.bpnum_(shippingTotal.price, fromOrder.currency_code),
|
||||
tax: this.bpnum_(shippingTotal.tax, fromOrder.currency_code),
|
||||
taxCode: region.tax_code,
|
||||
nominalCode: this.options.shipping_account_code || "4040",
|
||||
})
|
||||
|
||||
@@ -72,21 +72,20 @@ class OrderSubscriber {
|
||||
|
||||
const fromSwap = await this.swapService_.retrieve(id, {
|
||||
relations: [
|
||||
"order",
|
||||
"order.payments",
|
||||
"order.region",
|
||||
"order.swaps",
|
||||
"order.discounts",
|
||||
"order.discounts.rule",
|
||||
"return_order",
|
||||
"return_order.items",
|
||||
"return_order.shipping_method",
|
||||
"return_order.shipping_method.tax_lines",
|
||||
"additional_items",
|
||||
"additional_items.tax_lines",
|
||||
"shipping_address",
|
||||
"shipping_methods",
|
||||
"shipping_methods.tax_lines",
|
||||
],
|
||||
})
|
||||
let fromOrder = fromSwap.order
|
||||
const fromOrder = await this.orderService_.retrieve(fromSwap.order_id, {
|
||||
relations: ["payments", "region", "swaps", "discounts", "discounts.rule"],
|
||||
})
|
||||
|
||||
if (
|
||||
!(
|
||||
@@ -105,23 +104,27 @@ class OrderSubscriber {
|
||||
const { id } = data
|
||||
const fromClaim = await this.claimService_.retrieve(id, {
|
||||
relations: [
|
||||
"order",
|
||||
"order.payments",
|
||||
"order.region",
|
||||
"order.claims",
|
||||
"order.discounts",
|
||||
"order.discounts.rule",
|
||||
"claim_items",
|
||||
"return_order",
|
||||
"return_order.items",
|
||||
"return_order.shipping_method",
|
||||
"return_order.shipping_method.tax_lines",
|
||||
"additional_items",
|
||||
"additional_items.tax_lines",
|
||||
"shipping_address",
|
||||
"shipping_methods",
|
||||
],
|
||||
})
|
||||
|
||||
const fromOrder = fromClaim.order
|
||||
const fromOrder = await this.orderService_.retrieve(fromClaim.order_id, {
|
||||
relations: [
|
||||
"payments",
|
||||
"region",
|
||||
"claims",
|
||||
"discounts",
|
||||
"discounts.rule",
|
||||
],
|
||||
})
|
||||
|
||||
if (fromClaim.type === "replace") {
|
||||
await this.brightpearlService_.createClaim(fromOrder, fromClaim)
|
||||
@@ -147,11 +150,11 @@ class OrderSubscriber {
|
||||
const { id, return_id } = data
|
||||
|
||||
const order = await this.orderService_.retrieve(id, {
|
||||
relations: ["region", "swaps", "payments"],
|
||||
relations: ["discounts", "region", "swaps", "payments"],
|
||||
})
|
||||
|
||||
const fromReturn = await this.returnService_.retrieve(return_id, {
|
||||
relations: ["items"],
|
||||
relations: ["items", "shipping_method", "shipping_method.tax_lines"],
|
||||
})
|
||||
|
||||
return this.brightpearlService_
|
||||
|
||||
Reference in New Issue
Block a user