feat(cart): Item tax lines + shipping method tax lines (#6136)

This commit is contained in:
Oli Juhl
2024-01-24 12:42:27 +01:00
committed by GitHub
parent 43205914cb
commit 2b35700dbc
17 changed files with 1245 additions and 145 deletions

View File

@@ -1200,42 +1200,6 @@ describe("Cart Module Service", () => {
])
)
})
it("should throw if line item is not associated with cart", async () => {
const [cartOne] = await service.create([
{
currency_code: "eur",
},
])
const [cartTwo] = await service.create([
{
currency_code: "eur",
},
])
const [itemOne] = await service.addLineItems(cartOne.id, [
{
quantity: 1,
unit_price: 100,
title: "test",
},
])
const error = await service
.addLineItemAdjustments(cartTwo.id, [
{
item_id: itemOne.id,
amount: 100,
code: "FREE",
},
])
.catch((e) => e)
expect(error.message).toBe(
`Line item with id ${itemOne.id} does not exist on cart with id ${cartTwo.id}`
)
})
})
describe("removeLineItemAdjustments", () => {
@@ -1860,4 +1824,566 @@ describe("Cart Module Service", () => {
expect(adjustments?.length).toBe(0)
})
})
describe("setLineItemTaxLines", () => {
it("should set line item tax lines for a cart", async () => {
const [createdCart] = await service.create([
{
currency_code: "eur",
},
])
const [itemOne] = await service.addLineItems(createdCart.id, [
{
quantity: 1,
unit_price: 100,
title: "test",
},
])
const [itemTwo] = await service.addLineItems(createdCart.id, [
{
quantity: 2,
unit_price: 200,
title: "test-2",
},
])
const taxLines = await service.setLineItemTaxLines(createdCart.id, [
{
item_id: itemOne.id,
rate: 20,
code: "TX",
},
{
item_id: itemTwo.id,
rate: 20,
code: "TX",
},
])
expect(taxLines).toEqual(
expect.arrayContaining([
expect.objectContaining({
item_id: itemOne.id,
rate: 20,
code: "TX",
}),
expect.objectContaining({
item_id: itemTwo.id,
rate: 20,
code: "TX",
}),
])
)
})
it("should replace line item tax lines for a cart", async () => {
const [createdCart] = await service.create([
{
currency_code: "eur",
},
])
const [itemOne] = await service.addLineItems(createdCart.id, [
{
quantity: 1,
unit_price: 100,
title: "test",
},
])
const taxLines = await service.setLineItemTaxLines(createdCart.id, [
{
item_id: itemOne.id,
rate: 20,
code: "TX",
},
])
expect(taxLines).toEqual(
expect.arrayContaining([
expect.objectContaining({
item_id: itemOne.id,
rate: 20,
code: "TX",
}),
])
)
await service.setLineItemTaxLines(createdCart.id, [
{
item_id: itemOne.id,
rate: 25,
code: "TX-2",
},
])
const cart = await service.retrieve(createdCart.id, {
relations: ["items.tax_lines"],
})
expect(cart.items).toEqual(
expect.arrayContaining([
expect.objectContaining({
id: itemOne.id,
tax_lines: expect.arrayContaining([
expect.objectContaining({
item_id: itemOne.id,
rate: 25,
code: "TX-2",
}),
]),
}),
])
)
expect(cart.items?.length).toBe(1)
expect(cart.items?.[0].tax_lines?.length).toBe(1)
})
it("should remove all line item tax lines for a cart", async () => {
const [createdCart] = await service.create([
{
currency_code: "eur",
},
])
const [itemOne] = await service.addLineItems(createdCart.id, [
{
quantity: 1,
unit_price: 100,
title: "test",
},
])
const taxLines = await service.setLineItemTaxLines(createdCart.id, [
{
item_id: itemOne.id,
rate: 20,
code: "TX",
},
])
expect(taxLines).toEqual(
expect.arrayContaining([
expect.objectContaining({
item_id: itemOne.id,
rate: 20,
code: "TX",
}),
])
)
await service.setLineItemTaxLines(createdCart.id, [])
const cart = await service.retrieve(createdCart.id, {
relations: ["items.tax_lines"],
})
expect(cart.items).toEqual(
expect.arrayContaining([
expect.objectContaining({
id: itemOne.id,
tax_lines: [],
}),
])
)
expect(cart.items?.length).toBe(1)
expect(cart.items?.[0].tax_lines?.length).toBe(0)
})
it("should update line item tax lines for a cart", async () => {
const [createdCart] = await service.create([
{
currency_code: "eur",
},
])
const [itemOne] = await service.addLineItems(createdCart.id, [
{
quantity: 1,
unit_price: 100,
title: "test",
},
])
const taxLines = await service.setLineItemTaxLines(createdCart.id, [
{
item_id: itemOne.id,
rate: 20,
code: "TX",
},
])
expect(taxLines).toEqual(
expect.arrayContaining([
expect.objectContaining({
item_id: itemOne.id,
rate: 20,
code: "TX",
}),
])
)
await service.setLineItemTaxLines(createdCart.id, [
{
id: taxLines[0].id,
item_id: itemOne.id,
rate: 25,
code: "TX",
},
])
const cart = await service.retrieve(createdCart.id, {
relations: ["items.tax_lines"],
})
expect(cart.items).toEqual(
expect.arrayContaining([
expect.objectContaining({
id: itemOne.id,
tax_lines: [
expect.objectContaining({
id: taxLines[0].id,
item_id: itemOne.id,
rate: 25,
code: "TX",
}),
],
}),
])
)
expect(cart.items?.length).toBe(1)
expect(cart.items?.[0].tax_lines?.length).toBe(1)
})
it("should remove, update, and create line item tax lines for a cart", async () => {
const [createdCart] = await service.create([
{
currency_code: "eur",
},
])
const [itemOne] = await service.addLineItems(createdCart.id, [
{
quantity: 1,
unit_price: 100,
title: "test",
},
])
const taxLines = await service.setLineItemTaxLines(createdCart.id, [
{
item_id: itemOne.id,
rate: 20,
code: "TX",
},
{
item_id: itemOne.id,
rate: 25,
code: "TX",
},
])
expect(taxLines).toEqual(
expect.arrayContaining([
expect.objectContaining({
item_id: itemOne.id,
rate: 20,
code: "TX",
}),
expect.objectContaining({
item_id: itemOne.id,
rate: 25,
code: "TX",
}),
])
)
const taxLine = taxLines.find((tx) => tx.item_id === itemOne.id)
await service.setLineItemTaxLines(createdCart.id, [
// update
{
id: taxLine.id,
rate: 40,
code: "TX",
},
// create
{
item_id: itemOne.id,
rate: 25,
code: "TX-2",
},
// remove: should remove the initial tax line for itemOne
])
const cart = await service.retrieve(createdCart.id, {
relations: ["items.tax_lines"],
})
expect(cart.items).toEqual(
expect.arrayContaining([
expect.objectContaining({
id: itemOne.id,
tax_lines: [
expect.objectContaining({
id: taxLine!.id,
item_id: itemOne.id,
rate: 40,
code: "TX",
}),
expect.objectContaining({
item_id: itemOne.id,
rate: 25,
code: "TX-2",
}),
],
}),
])
)
expect(cart.items?.length).toBe(1)
expect(cart.items?.[0].tax_lines?.length).toBe(2)
})
})
describe("addLineItemAdjustments", () => {
it("should add line item tax lines for items in a cart", async () => {
const [createdCart] = await service.create([
{
currency_code: "eur",
},
])
const [itemOne] = await service.addLineItems(createdCart.id, [
{
quantity: 1,
unit_price: 100,
title: "test",
},
])
const taxLines = await service.addLineItemTaxLines(createdCart.id, [
{
item_id: itemOne.id,
rate: 20,
code: "TX",
},
])
expect(taxLines).toEqual(
expect.arrayContaining([
expect.objectContaining({
item_id: itemOne.id,
rate: 20,
code: "TX",
}),
])
)
})
it("should add multiple line item tax lines for multiple line items", async () => {
const [createdCart] = await service.create([
{
currency_code: "eur",
},
])
const [itemOne] = await service.addLineItems(createdCart.id, [
{
quantity: 1,
unit_price: 100,
title: "test",
},
])
const [itemTwo] = await service.addLineItems(createdCart.id, [
{
quantity: 2,
unit_price: 200,
title: "test-2",
},
])
const taxLines = await service.addLineItemTaxLines(createdCart.id, [
{
item_id: itemOne.id,
rate: 20,
code: "TX",
},
{
item_id: itemTwo.id,
rate: 20,
code: "TX",
},
])
expect(taxLines).toEqual(
expect.arrayContaining([
expect.objectContaining({
item_id: itemOne.id,
rate: 20,
code: "TX",
}),
expect.objectContaining({
item_id: itemTwo.id,
rate: 20,
code: "TX",
}),
])
)
})
it("should add line item tax lines for line items on multiple carts", async () => {
const [cartOne] = await service.create([
{
currency_code: "eur",
},
])
const [cartTwo] = await service.create([
{
currency_code: "usd",
},
])
const [itemOne] = await service.addLineItems(cartOne.id, [
{
quantity: 1,
unit_price: 100,
title: "test",
},
])
const [itemTwo] = await service.addLineItems(cartTwo.id, [
{
quantity: 2,
unit_price: 200,
title: "test-2",
},
])
await service.addLineItemTaxLines([
// item from cart one
{
item_id: itemOne.id,
rate: 20,
code: "TX",
},
// item from cart two
{
item_id: itemTwo.id,
rate: 25,
code: "TX-2",
},
])
const cartOneItems = await service.listLineItems(
{ cart_id: cartOne.id },
{ relations: ["tax_lines"] }
)
const cartTwoItems = await service.listLineItems(
{ cart_id: cartTwo.id },
{ relations: ["tax_lines"] }
)
expect(cartOneItems).toEqual(
expect.arrayContaining([
expect.objectContaining({
tax_lines: expect.arrayContaining([
expect.objectContaining({
item_id: itemOne.id,
rate: 20,
code: "TX",
}),
]),
}),
])
)
expect(cartTwoItems).toEqual(
expect.arrayContaining([
expect.objectContaining({
tax_lines: expect.arrayContaining([
expect.objectContaining({
item_id: itemTwo.id,
rate: 25,
code: "TX-2",
}),
]),
}),
])
)
})
})
describe("removeLineItemAdjustments", () => {
it("should remove line item tax line succesfully", async () => {
const [createdCart] = await service.create([
{
currency_code: "eur",
},
])
const [item] = await service.addLineItems(createdCart.id, [
{
quantity: 1,
unit_price: 100,
title: "test",
},
])
const [taxLine] = await service.addLineItemTaxLines(createdCart.id, [
{
item_id: item.id,
rate: 20,
code: "TX",
},
])
expect(taxLine.item_id).toBe(item.id)
await service.removeLineItemTaxLines(taxLine.id)
const taxLines = await service.listLineItemTaxLines({
item_id: item.id,
})
expect(taxLines?.length).toBe(0)
})
it("should remove line item tax lines succesfully with selector", async () => {
const [createdCart] = await service.create([
{
currency_code: "eur",
},
])
const [item] = await service.addLineItems(createdCart.id, [
{
quantity: 1,
unit_price: 100,
title: "test",
},
])
const [taxLine] = await service.addLineItemTaxLines(createdCart.id, [
{
item_id: item.id,
rate: 20,
code: "TX",
},
])
expect(taxLine.item_id).toBe(item.id)
await service.removeLineItemTaxLines({ item_id: item.id })
const taxLines = await service.listLineItemTaxLines({
item_id: item.id,
})
expect(taxLines?.length).toBe(0)
})
})
})

View File

@@ -1,15 +1,25 @@
import { generateEntityId } from "@medusajs/utils"
import { BeforeCreate, Entity, ManyToOne, OnInit } from "@mikro-orm/core"
import {
BeforeCreate,
Entity,
ManyToOne,
OnInit,
Property,
} from "@mikro-orm/core"
import LineItem from "./line-item"
import TaxLine from "./tax-line"
@Entity({ tableName: "cart_line_item_tax_line" })
export default class LineItemTaxLine extends TaxLine {
@ManyToOne(() => LineItem, {
joinColumn: "line_item",
fieldName: "line_item_id",
onDelete: "cascade",
nullable: true,
index: "IDX_tax_line_item_id",
})
line_item: LineItem
item: LineItem | null
@Property({ columnType: "text" })
item_id: string
@BeforeCreate()
onCreate() {

View File

@@ -110,18 +110,14 @@ export default class LineItem {
@Check({ expression: "unit_price >= 0" }) // TODO: Validate that numeric types work with the expression
unit_price: number
@OneToMany(() => LineItemTaxLine, (taxLine) => taxLine.line_item, {
@OneToMany(() => LineItemTaxLine, (taxLine) => taxLine.item, {
cascade: [Cascade.REMOVE],
})
tax_lines = new Collection<LineItemTaxLine>(this)
@OneToMany(
() => LineItemAdjustment,
(adjustment) => adjustment.item,
{
cascade: [Cascade.REMOVE],
}
)
@OneToMany(() => LineItemAdjustment, (adjustment) => adjustment.item, {
cascade: [Cascade.REMOVE],
})
adjustments = new Collection<LineItemAdjustment>(this)
/** COMPUTED PROPERTIES - START */

View File

@@ -1,15 +1,25 @@
import { generateEntityId } from "@medusajs/utils"
import { BeforeCreate, Entity, ManyToOne, OnInit } from "@mikro-orm/core"
import {
BeforeCreate,
Entity,
ManyToOne,
OnInit,
Property,
} from "@mikro-orm/core"
import ShippingMethod from "./shipping-method"
import TaxLine from "./tax-line"
@Entity({ tableName: "cart_shipping_method_tax_line" })
export default class ShippingMethodTaxLine extends TaxLine {
@ManyToOne(() => ShippingMethod, {
joinColumn: "shipping_method",
fieldName: "shipping_method_id",
onDelete: "cascade",
nullable: true,
index: "IDX_tax_line_shipping_method_id",
})
shipping_method: ShippingMethod
shipping_method: ShippingMethod | null
@Property({ columnType: "text" })
shipping_method_id: string
@BeforeCreate()
onCreate() {

View File

@@ -1,6 +1,6 @@
import { PrimaryKey, Property } from "@mikro-orm/core"
/**
/**
* As per the Mikro ORM docs, superclasses should use the abstract class definition
* Source: https://mikro-orm.io/docs/inheritance-mapping
*/
@@ -17,7 +17,7 @@ export default abstract class TaxLine {
@Property({ columnType: "text" })
code: string
@Property({ columnType: "numeric" })
@Property({ columnType: "numeric", serializer: Number })
rate: number
@Property({ columnType: "text", nullable: true })

View File

@@ -2,6 +2,7 @@ import {
CartTypes,
Context,
DAL,
FilterableLineItemTaxLineProps,
FindConfig,
ICartModuleService,
InternalModuleDeclaration,
@@ -19,8 +20,10 @@ import {
Cart,
LineItem,
LineItemAdjustment,
LineItemTaxLine,
ShippingMethod,
ShippingMethodAdjustment,
ShippingMethodTaxLine,
} from "@models"
import { CreateLineItemDTO, UpdateLineItemDTO } from "@types"
import { joinerConfig } from "../joiner-config"
@@ -34,6 +37,8 @@ type InjectedDependencies = {
shippingMethodAdjustmentService: services.ShippingMethodAdjustmentService
shippingMethodService: services.ShippingMethodService
lineItemAdjustmentService: services.LineItemAdjustmentService
lineItemTaxLineService: services.LineItemTaxLineService
shippingMethodTaxLineService: services.ShippingMethodTaxLineService
}
export default class CartModuleService implements ICartModuleService {
@@ -44,6 +49,8 @@ export default class CartModuleService implements ICartModuleService {
protected shippingMethodAdjustmentService_: services.ShippingMethodAdjustmentService
protected shippingMethodService_: services.ShippingMethodService
protected lineItemAdjustmentService_: services.LineItemAdjustmentService
protected lineItemTaxLineService_: services.LineItemTaxLineService
protected shippingMethodTaxLineService_: services.ShippingMethodTaxLineService
constructor(
{
@@ -54,6 +61,8 @@ export default class CartModuleService implements ICartModuleService {
shippingMethodAdjustmentService,
shippingMethodService,
lineItemAdjustmentService,
shippingMethodTaxLineService,
lineItemTaxLineService,
}: InjectedDependencies,
protected readonly moduleDeclaration: InternalModuleDeclaration
) {
@@ -64,6 +73,8 @@ export default class CartModuleService implements ICartModuleService {
this.shippingMethodAdjustmentService_ = shippingMethodAdjustmentService
this.shippingMethodService_ = shippingMethodService
this.lineItemAdjustmentService_ = lineItemAdjustmentService
this.shippingMethodTaxLineService_ = shippingMethodTaxLineService
this.lineItemTaxLineService_ = lineItemTaxLineService
}
__joinerConfig(): ModuleJoinerConfig {
@@ -791,25 +802,17 @@ export default class CartModuleService implements ICartModuleService {
sharedContext
)
const lineIds = cart.items?.map((item) => item.id)
const existingAdjustments = await this.listLineItemAdjustments(
{ item_id: lineIds },
{ item: { cart_id: cart.id } },
{ select: ["id"] },
sharedContext
)
let toUpdate: CartTypes.UpdateLineItemAdjustmentDTO[] = []
let toCreate: CartTypes.CreateLineItemAdjustmentDTO[] = []
for (const adj of adjustments) {
if ("id" in adj) {
toUpdate.push(adj as CartTypes.UpdateLineItemAdjustmentDTO)
} else {
toCreate.push(adj as CartTypes.CreateLineItemAdjustmentDTO)
}
}
const adjustmentsSet = new Set(toUpdate.map((a) => a.id))
const adjustmentsSet = new Set(
adjustments
.map((a) => (a as CartTypes.UpdateLineItemAdjustmentDTO).id)
.filter(Boolean)
)
const toDelete: CartTypes.LineItemAdjustmentDTO[] = []
@@ -820,30 +823,18 @@ export default class CartModuleService implements ICartModuleService {
}
})
await this.lineItemAdjustmentService_.delete(
toDelete.map((adj) => adj!.id),
if (toDelete.length) {
await this.lineItemAdjustmentService_.delete(
toDelete.map((adj) => adj!.id),
sharedContext
)
}
let result = await this.lineItemAdjustmentService_.upsert(
adjustments,
sharedContext
)
let result: LineItemAdjustment[] = []
// TODO: Replace the following two calls with a single bulk upsert call
if (toUpdate?.length) {
const updated = await this.lineItemAdjustmentService_.update(
toUpdate,
sharedContext
)
result.push(...updated)
}
if (toCreate?.length) {
const created = await this.lineItemAdjustmentService_.create(
toCreate,
sharedContext
)
result.push(...created)
}
return await this.baseRepository_.serialize<
CartTypes.LineItemAdjustmentDTO[]
>(result, {
@@ -925,25 +916,17 @@ export default class CartModuleService implements ICartModuleService {
sharedContext
)
const methodIds = cart.shipping_methods?.map((method) => method.id)
const existingAdjustments = await this.listShippingMethodAdjustments(
{ shipping_method_id: methodIds ?? [] },
{ shipping_method: { cart_id: cart.id } },
{ select: ["id"] },
sharedContext
)
let toUpdate: CartTypes.UpdateShippingMethodAdjustmentDTO[] = []
let toCreate: CartTypes.CreateShippingMethodAdjustmentDTO[] = []
for (const adj of adjustments) {
if ("id" in adj) {
toUpdate.push(adj as CartTypes.UpdateShippingMethodAdjustmentDTO)
} else {
toCreate.push(adj as CartTypes.CreateShippingMethodAdjustmentDTO)
}
}
const adjustmentsSet = new Set(toUpdate.map((a) => a.id))
const adjustmentsSet = new Set(
adjustments
.map((a) => (a as CartTypes.UpdateShippingMethodAdjustmentDTO)?.id)
.filter(Boolean)
)
const toDelete: CartTypes.ShippingMethodAdjustmentDTO[] = []
@@ -963,24 +946,10 @@ export default class CartModuleService implements ICartModuleService {
)
}
let result: ShippingMethodAdjustment[] = []
if (toCreate.length) {
const created = await this.shippingMethodAdjustmentService_.create(
toCreate,
sharedContext
)
result.push(...created)
}
if (toUpdate.length) {
const updated = await this.shippingMethodAdjustmentService_.update(
toUpdate,
sharedContext
)
result.push(...updated)
}
const result = await this.shippingMethodAdjustmentService_.upsert(
adjustments,
sharedContext
)
return await this.baseRepository_.serialize<
CartTypes.ShippingMethodAdjustmentDTO[]
@@ -1100,4 +1069,352 @@ export default class CartModuleService implements ICartModuleService {
await this.shippingMethodAdjustmentService_.delete(ids, sharedContext)
}
@InjectManager("baseRepository_")
async listLineItemTaxLines(
filters: CartTypes.FilterableLineItemTaxLineProps = {},
config: FindConfig<CartTypes.LineItemTaxLineDTO> = {},
@MedusaContext() sharedContext: Context = {}
) {
const taxLines = await this.lineItemTaxLineService_.list(
filters,
config,
sharedContext
)
return await this.baseRepository_.serialize<CartTypes.LineItemTaxLineDTO[]>(
taxLines,
{
populate: true,
}
)
}
addLineItemTaxLines(
taxLines: CartTypes.CreateLineItemTaxLineDTO[]
): Promise<CartTypes.LineItemTaxLineDTO[]>
addLineItemTaxLines(
taxLine: CartTypes.CreateLineItemTaxLineDTO
): Promise<CartTypes.LineItemTaxLineDTO>
addLineItemTaxLines(
cartId: string,
taxLines:
| CartTypes.CreateLineItemTaxLineDTO[]
| CartTypes.CreateShippingMethodTaxLineDTO,
sharedContext?: Context
): Promise<CartTypes.LineItemTaxLineDTO[]>
@InjectTransactionManager("baseRepository_")
async addLineItemTaxLines(
cartIdOrData:
| string
| CartTypes.CreateLineItemTaxLineDTO[]
| CartTypes.CreateLineItemTaxLineDTO,
taxLines?:
| CartTypes.CreateLineItemTaxLineDTO[]
| CartTypes.CreateLineItemTaxLineDTO,
@MedusaContext() sharedContext: Context = {}
): Promise<CartTypes.LineItemTaxLineDTO[] | CartTypes.LineItemTaxLineDTO> {
let addedTaxLines: LineItemTaxLine[] = []
if (isString(cartIdOrData)) {
// existence check
await this.retrieve(cartIdOrData, { select: ["id"] }, sharedContext)
const lines = Array.isArray(taxLines) ? taxLines : [taxLines]
addedTaxLines = await this.lineItemTaxLineService_.create(
lines as CartTypes.CreateLineItemTaxLineDTO[],
sharedContext
)
} else {
const data = Array.isArray(cartIdOrData) ? cartIdOrData : [cartIdOrData]
addedTaxLines = await this.lineItemTaxLineService_.create(
data as CartTypes.CreateLineItemTaxLineDTO[],
sharedContext
)
}
const serialized = await this.baseRepository_.serialize<
CartTypes.LineItemTaxLineDTO[]
>(addedTaxLines, {
populate: true,
})
if (isObject(cartIdOrData)) {
return serialized[0]
}
return serialized
}
@InjectTransactionManager("baseRepository_")
async setLineItemTaxLines(
cartId: string,
taxLines: (
| CartTypes.CreateLineItemTaxLineDTO
| CartTypes.UpdateLineItemTaxLineDTO
)[],
@MedusaContext() sharedContext: Context = {}
): Promise<CartTypes.LineItemTaxLineDTO[]> {
const cart = await this.retrieve(
cartId,
{ select: ["id"], relations: ["items.tax_lines"] },
sharedContext
)
const existingTaxLines = await this.listLineItemTaxLines(
{ item: { cart_id: cart.id } },
{ select: ["id"] },
sharedContext
)
const taxLinesSet = new Set(
taxLines
.map((taxLine) => (taxLine as CartTypes.UpdateLineItemTaxLineDTO)?.id)
.filter(Boolean)
)
const toDelete: CartTypes.LineItemTaxLineDTO[] = []
// From the existing tax lines, find the ones that are not passed in taxLines
existingTaxLines.forEach((taxLine: CartTypes.LineItemTaxLineDTO) => {
if (!taxLinesSet.has(taxLine.id)) {
toDelete.push(taxLine)
}
})
await this.lineItemTaxLineService_.delete(
toDelete.map((taxLine) => taxLine!.id),
sharedContext
)
const result = await this.lineItemTaxLineService_.upsert(
taxLines,
sharedContext
)
return await this.baseRepository_.serialize<CartTypes.LineItemTaxLineDTO[]>(
result,
{
populate: true,
}
)
}
removeLineItemTaxLines(
taxLineIds: string[],
sharedContext?: Context
): Promise<void>
removeLineItemTaxLines(
taxLineIds: string,
sharedContext?: Context
): Promise<void>
removeLineItemTaxLines(
selector: FilterableLineItemTaxLineProps,
sharedContext?: Context
): Promise<void>
async removeLineItemTaxLines(
taxLineIdsOrSelector:
| string
| string[]
| CartTypes.FilterableShippingMethodTaxLineProps,
@MedusaContext() sharedContext: Context = {}
): Promise<void> {
let ids: string[] = []
if (isObject(taxLineIdsOrSelector)) {
const taxLines = await this.listLineItemTaxLines(
{
...(taxLineIdsOrSelector as CartTypes.FilterableLineItemTaxLineProps),
},
{ select: ["id"] },
sharedContext
)
ids = taxLines.map((taxLine) => taxLine.id)
} else {
ids = Array.isArray(taxLineIdsOrSelector)
? taxLineIdsOrSelector
: [taxLineIdsOrSelector]
}
await this.lineItemTaxLineService_.delete(ids, sharedContext)
}
@InjectManager("baseRepository_")
async listShippingMethodTaxLines(
filters: CartTypes.FilterableShippingMethodTaxLineProps = {},
config: FindConfig<CartTypes.ShippingMethodTaxLineDTO> = {},
@MedusaContext() sharedContext: Context = {}
) {
const taxLines = await this.shippingMethodTaxLineService_.list(
filters,
config,
sharedContext
)
return await this.baseRepository_.serialize<
CartTypes.ShippingMethodTaxLineDTO[]
>(taxLines, {
populate: true,
})
}
addShippingMethodTaxLines(
taxLines: CartTypes.CreateShippingMethodTaxLineDTO[]
): Promise<CartTypes.ShippingMethodTaxLineDTO[]>
addShippingMethodTaxLines(
taxLine: CartTypes.CreateShippingMethodTaxLineDTO
): Promise<CartTypes.ShippingMethodTaxLineDTO>
addShippingMethodTaxLines(
cartId: string,
taxLines:
| CartTypes.CreateShippingMethodTaxLineDTO[]
| CartTypes.CreateShippingMethodTaxLineDTO,
sharedContext?: Context
): Promise<CartTypes.ShippingMethodTaxLineDTO[]>
@InjectTransactionManager("baseRepository_")
async addShippingMethodTaxLines(
cartIdOrData:
| string
| CartTypes.CreateShippingMethodTaxLineDTO[]
| CartTypes.CreateShippingMethodTaxLineDTO,
taxLines?:
| CartTypes.CreateShippingMethodTaxLineDTO[]
| CartTypes.CreateShippingMethodTaxLineDTO,
@MedusaContext() sharedContext: Context = {}
): Promise<
CartTypes.ShippingMethodTaxLineDTO[] | CartTypes.ShippingMethodTaxLineDTO
> {
let addedTaxLines: ShippingMethodTaxLine[] = []
if (isString(cartIdOrData)) {
// existence check
await this.retrieve(cartIdOrData, { select: ["id"] }, sharedContext)
const lines = Array.isArray(taxLines) ? taxLines : [taxLines]
addedTaxLines = await this.shippingMethodTaxLineService_.create(
lines as CartTypes.CreateShippingMethodTaxLineDTO[],
sharedContext
)
} else {
addedTaxLines = await this.shippingMethodTaxLineService_.create(
taxLines as CartTypes.CreateShippingMethodTaxLineDTO[],
sharedContext
)
}
const serialized =
await this.baseRepository_.serialize<CartTypes.ShippingMethodTaxLineDTO>(
addedTaxLines[0],
{
populate: true,
}
)
if (isObject(cartIdOrData)) {
return serialized[0]
}
return serialized
}
@InjectTransactionManager("baseRepository_")
async setShippingMethodTaxLines(
cartId: string,
taxLines: (
| CartTypes.CreateShippingMethodTaxLineDTO
| CartTypes.UpdateShippingMethodTaxLineDTO
)[],
@MedusaContext() sharedContext: Context = {}
): Promise<CartTypes.ShippingMethodTaxLineDTO[]> {
const cart = await this.retrieve(
cartId,
{ select: ["id"], relations: ["shipping_methods.tax_lines"] },
sharedContext
)
const existingTaxLines = await this.listShippingMethodTaxLines(
{ shipping_method: { cart_id: cart.id } },
{ select: ["id"] },
sharedContext
)
const taxLinesSet = new Set(
taxLines
.map(
(taxLine) => (taxLine as CartTypes.UpdateShippingMethodTaxLineDTO)?.id
)
.filter(Boolean)
)
const toDelete: CartTypes.ShippingMethodTaxLineDTO[] = []
// From the existing tax lines, find the ones that are not passed in taxLines
existingTaxLines.forEach((taxLine: CartTypes.ShippingMethodTaxLineDTO) => {
if (!taxLinesSet.has(taxLine.id)) {
toDelete.push(taxLine)
}
})
if (toDelete.length) {
await this.shippingMethodTaxLineService_.delete(
toDelete.map((taxLine) => taxLine!.id),
sharedContext
)
}
const result = await this.shippingMethodTaxLineService_.upsert(
taxLines,
sharedContext
)
return await this.baseRepository_.serialize<
CartTypes.ShippingMethodTaxLineDTO[]
>(result, {
populate: true,
})
}
removeShippingMethodTaxLines(
taxLineIds: string[],
sharedContext?: Context
): Promise<void>
removeShippingMethodTaxLines(
taxLineIds: string,
sharedContext?: Context
): Promise<void>
removeShippingMethodTaxLines(
selector: Partial<CartTypes.ShippingMethodTaxLineDTO>,
sharedContext?: Context
): Promise<void>
async removeShippingMethodTaxLines(
taxLineIdsOrSelector:
| string
| string[]
| CartTypes.FilterableShippingMethodTaxLineProps,
@MedusaContext() sharedContext: Context = {}
): Promise<void> {
let ids: string[] = []
if (isObject(taxLineIdsOrSelector)) {
const taxLines = await this.listShippingMethodTaxLines(
{
...(taxLineIdsOrSelector as CartTypes.FilterableShippingMethodTaxLineProps),
},
{ select: ["id"] },
sharedContext
)
ids = taxLines.map((taxLine) => taxLine.id)
} else {
ids = Array.isArray(taxLineIdsOrSelector)
? taxLineIdsOrSelector
: [taxLineIdsOrSelector]
}
await this.shippingMethodTaxLineService_.delete(ids, sharedContext)
}
}

View File

@@ -3,6 +3,8 @@ export { default as CartService } from "./cart"
export { default as CartModuleService } from "./cart-module"
export { default as LineItemService } from "./line-item"
export { default as LineItemAdjustmentService } from "./line-item-adjustment"
export { default as LineItemTaxLineService } from "./line-item-tax-line"
export { default as ShippingMethodService } from "./shipping-method"
export { default as ShippingMethodAdjustmentService } from "./shipping-method-adjustment"
export { default as ShippingMethodTaxLineService } from "./shipping-method-tax-line"

View File

@@ -0,0 +1,26 @@
import {
CreateLineItemTaxLineDTO,
DAL,
UpdateLineItemTaxLineDTO,
} from "@medusajs/types"
import { ModulesSdkUtils } from "@medusajs/utils"
import { LineItemTaxLine } from "@models"
type InjectedDependencies = {
lineItemTaxLineRepository: DAL.RepositoryService
}
export default class LineItemTaxLineService<
TEntity extends LineItemTaxLine = LineItemTaxLine
> extends ModulesSdkUtils.abstractServiceFactory<
InjectedDependencies,
{
create: CreateLineItemTaxLineDTO
update: UpdateLineItemTaxLineDTO
}
>(LineItemTaxLine)<TEntity> {
constructor(container: InjectedDependencies) {
// @ts-ignore
super(...arguments)
}
}

View File

@@ -0,0 +1,22 @@
import { CreateShippingMethodTaxLineDTO, DAL, UpdateShippingMethodTaxLineDTO } from "@medusajs/types"
import { ModulesSdkUtils } from "@medusajs/utils"
import { ShippingMethodTaxLine } from "@models"
type InjectedDependencies = {
shippingMethodTaxLineRepository: DAL.RepositoryService
}
export default class ShippingMethodTaxLineService<
TEntity extends ShippingMethodTaxLine = ShippingMethodTaxLine
> extends ModulesSdkUtils.abstractServiceFactory<
InjectedDependencies,
{
create: CreateShippingMethodTaxLineDTO
update: UpdateShippingMethodTaxLineDTO
}
>(ShippingMethodTaxLine)<TEntity> {
constructor(container: InjectedDependencies) {
// @ts-ignore
super(...arguments)
}
}

View File

@@ -4,9 +4,11 @@ export * from "./address"
export * from "./cart"
export * from "./line-item"
export * from "./line-item-adjustment"
export * from "./line-item-tax-line"
export * from "./repositories"
export * from "./shipping-method"
export * from "./shipping-method-adjustment"
export * from "./shipping-method-tax-line"
export type InitializeModuleInjectableDependencies = {
logger?: Logger

View File

@@ -0,0 +1,9 @@
import { CreateTaxLineDTO, UpdateTaxLineDTO } from "./tax-line"
export interface CreateLineItemTaxLineDTO extends CreateTaxLineDTO {
item_id: string
}
export interface UpdateLineItemTaxLineDTO extends UpdateTaxLineDTO {
item_id: string
}

View File

@@ -3,8 +3,10 @@ import {
Cart,
LineItem,
LineItemAdjustment,
LineItemTaxLine,
ShippingMethod,
ShippingMethodAdjustment,
ShippingMethodTaxLine,
} from "@models"
import { CreateAddressDTO, UpdateAddressDTO } from "./address"
import { CreateCartDTO, UpdateCartDTO } from "./cart"
@@ -13,6 +15,10 @@ import {
CreateLineItemAdjustmentDTO,
UpdateLineItemAdjustmentDTO,
} from "./line-item-adjustment"
import {
CreateLineItemTaxLineDTO,
UpdateLineItemTaxLineDTO,
} from "./line-item-tax-line"
import {
CreateShippingMethodDTO,
UpdateShippingMethodDTO,
@@ -21,6 +27,10 @@ import {
CreateShippingMethodAdjustmentDTO,
UpdateShippingMethodAdjustmentDTO,
} from "./shipping-method-adjustment"
import {
CreateShippingMethodTaxLineDTO,
UpdateShippingMethodTaxLineDTO,
} from "./shipping-method-tax-line"
// eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface IAddressRepository<TEntity extends Cart = Cart>
@@ -82,3 +92,23 @@ export interface IShippingMethodAdjustmentRepository<
update: UpdateShippingMethodAdjustmentDTO
}
> {}
export interface IShippingMethodTaxLineRepository<
TEntity extends ShippingMethodTaxLine = ShippingMethodTaxLine
> extends DAL.RepositoryService<
TEntity,
{
create: CreateShippingMethodTaxLineDTO
update: UpdateShippingMethodTaxLineDTO
}
> {}
export interface ILineItemTaxLineRepository<
TEntity extends LineItemTaxLine = LineItemTaxLine
> extends DAL.RepositoryService<
TEntity,
{
create: CreateLineItemTaxLineDTO
update: UpdateLineItemTaxLineDTO
}
> {}

View File

@@ -0,0 +1,9 @@
import { CreateTaxLineDTO, UpdateTaxLineDTO } from "./tax-line"
export interface CreateShippingMethodTaxLineDTO extends CreateTaxLineDTO {
shipping_method_id: string
}
export interface UpdateShippingMethodTaxLineDTO extends UpdateTaxLineDTO {
shipping_method_id: string
}

View File

@@ -0,0 +1,16 @@
export interface UpdateTaxLineDTO {
id: string
description?: string
tax_rate_id?: string
code?: string
rate?: number
provider_id?: string
}
export interface CreateTaxLineDTO {
description?: string
tax_rate_id?: string
code: string
rate: number
provider_id?: string
}

View File

@@ -103,13 +103,21 @@ export interface ShippingMethodTaxLineDTO extends TaxLineDTO {
* The associated shipping method
*/
shipping_method: CartShippingMethodDTO
/**
* The ID of the associated shipping method
*/
shipping_method_id: string
}
export interface LineItemTaxLineDTO extends TaxLineDTO {
/**
* The associated line item
*/
line_item: CartLineItemDTO
item: CartLineItemDTO
/**
* The ID of the associated line item
*/
item_id: string
}
export interface CartAddressDTO {
@@ -513,6 +521,7 @@ export interface FilterableLineItemAdjustmentProps
item_id?: string | string[]
promotion_id?: string | string[]
provider_id?: string | string[]
item?: FilterableLineItemProps
}
export interface FilterableShippingMethodProps
extends BaseFilterable<FilterableShippingMethodProps> {
@@ -528,6 +537,29 @@ export interface FilterableShippingMethodAdjustmentProps
shipping_method_id?: string | string[]
promotion_id?: string | string[]
provider_id?: string | string[]
shipping_method?: FilterableShippingMethodProps
}
export interface FilterableLineItemTaxLineProps
extends BaseFilterable<FilterableLineItemTaxLineProps> {
id?: string | string[]
description?: string
code?: string | string[]
tax_rate_id?: string | string[]
provider_id?: string | string[]
item_id?: string | string[]
item?: FilterableLineItemProps
}
export interface FilterableShippingMethodTaxLineProps
extends BaseFilterable<FilterableShippingMethodTaxLineProps> {
id?: string | string[]
description?: string
code?: string | string[]
tax_rate_id?: string | string[]
provider_id?: string | string[]
shipping_method_id?: string | string[]
shipping_method?: FilterableShippingMethodProps
}
/**

View File

@@ -60,30 +60,9 @@ export interface UpdateCartDTO {
/** CART END */
/** TAXLINES START */
export interface CreateTaxLineDTO {
description?: string
tax_rate_id?: string
code: string
rate: number
provider_id?: string
}
export interface UpdateTaxLineDTO {
id: string
description?: string
tax_rate_id?: string
code?: string
rate?: number
provider_id?: string
}
/** TAXLINES END */
/** ADJUSTMENT START */
export interface CreateAdjustmentDTO {
item_id: string
code?: string
code: string
amount: number
description?: string
promotion_id?: string
@@ -117,7 +96,36 @@ export interface UpsertLineItemAdjustmentDTO {
provider_id?: string
}
/** ADJUSTMENT START */
/** ADJUSTMENTS END */
/** TAX LINES START */
export interface CreateTaxLineDTO {
description?: string
tax_rate_id?: string
code: string
rate: number
provider_id?: string
}
export interface UpdateTaxLineDTO {
id: string
description?: string
tax_rate_id?: string
code?: string
rate?: number
provider_id?: string
}
export interface CreateShippingMethodTaxLineDTO extends CreateTaxLineDTO {}
export interface UpdateShippingMethodTaxLineDTO extends UpdateTaxLineDTO {}
export interface CreateLineItemTaxLineDTO extends CreateTaxLineDTO {}
export interface UpdateLineItemTaxLineDTO extends UpdateTaxLineDTO {}
/** TAX LINES END */
/** LINE ITEMS START */
export interface CreateLineItemDTO {

View File

@@ -10,10 +10,14 @@ import {
FilterableCartProps,
FilterableLineItemAdjustmentProps,
FilterableLineItemProps,
FilterableLineItemTaxLineProps,
FilterableShippingMethodAdjustmentProps,
FilterableShippingMethodProps,
FilterableShippingMethodTaxLineProps,
LineItemAdjustmentDTO,
LineItemTaxLineDTO,
ShippingMethodAdjustmentDTO,
ShippingMethodTaxLineDTO,
} from "./common"
import {
CreateAddressDTO,
@@ -21,14 +25,18 @@ import {
CreateCartDTO,
CreateLineItemDTO,
CreateLineItemForCartDTO,
CreateLineItemTaxLineDTO,
CreateShippingMethodAdjustmentDTO,
CreateShippingMethodDTO,
CreateShippingMethodForSingleCartDTO,
CreateShippingMethodTaxLineDTO,
UpdateAddressDTO,
UpdateCartDTO,
UpdateLineItemDTO,
UpdateLineItemTaxLineDTO,
UpdateLineItemWithSelectorDTO,
UpdateShippingMethodAdjustmentDTO,
UpdateShippingMethodTaxLineDTO,
UpsertLineItemAdjustmentDTO,
} from "./mutations"
@@ -234,4 +242,81 @@ export interface ICartModuleService extends IModuleService {
selector: Partial<ShippingMethodAdjustmentDTO>,
sharedContext?: Context
): Promise<void>
listLineItemTaxLines(
filters: FilterableLineItemTaxLineProps,
config?: FindConfig<LineItemTaxLineDTO>,
sharedContext?: Context
): Promise<LineItemTaxLineDTO[]>
addLineItemTaxLines(
taxLines: CreateLineItemTaxLineDTO[]
): Promise<LineItemTaxLineDTO[]>
addLineItemTaxLines(
taxLine: CreateLineItemTaxLineDTO
): Promise<LineItemTaxLineDTO>
addLineItemTaxLines(
cartId: string,
taxLines: CreateLineItemTaxLineDTO[] | CreateLineItemTaxLineDTO,
sharedContext?: Context
): Promise<LineItemTaxLineDTO[]>
setLineItemTaxLines(
cartId: string,
taxLines: (CreateLineItemTaxLineDTO | UpdateLineItemTaxLineDTO)[],
sharedContext?: Context
): Promise<LineItemTaxLineDTO[]>
removeLineItemTaxLines(
taxLineIds: string[],
sharedContext?: Context
): Promise<void>
removeLineItemTaxLines(
taxLineIds: string,
sharedContext?: Context
): Promise<void>
removeLineItemTaxLines(
selector: FilterableLineItemTaxLineProps,
sharedContext?: Context
): Promise<void>
listShippingMethodTaxLines(
filters: FilterableShippingMethodTaxLineProps,
config?: FindConfig<ShippingMethodTaxLineDTO>,
sharedContext?: Context
): Promise<ShippingMethodTaxLineDTO[]>
addShippingMethodTaxLines(
taxLines: CreateShippingMethodTaxLineDTO[]
): Promise<ShippingMethodTaxLineDTO[]>
addShippingMethodTaxLines(
taxLine: CreateShippingMethodTaxLineDTO
): Promise<ShippingMethodTaxLineDTO>
addShippingMethodTaxLines(
cartId: string,
taxLines: CreateShippingMethodTaxLineDTO[] | CreateShippingMethodTaxLineDTO,
sharedContext?: Context
): Promise<ShippingMethodTaxLineDTO[]>
setShippingMethodTaxLines(
cartId: string,
taxLines: (
| CreateShippingMethodTaxLineDTO
| UpdateShippingMethodTaxLineDTO
)[],
sharedContext?: Context
): Promise<ShippingMethodTaxLineDTO[]>
removeShippingMethodTaxLines(
taxLineIds: string[],
sharedContext?: Context
): Promise<void>
removeShippingMethodTaxLines(
taxLineIds: string,
sharedContext?: Context
): Promise<void>
removeShippingMethodTaxLines(
selector: FilterableShippingMethodTaxLineProps,
sharedContext?: Context
): Promise<void>
}