feat: line item adjustments (#1319)
* add: crud services + model + totals * fix: enforce unique constraint on line item adjustment model and update service (#1241) * add: unique constraint on model + fix service * fix: unique constraint * fix: add cascade on delete + fix discount relation * fix: remove optional unique prop * add: tests for ensuring line item adjustment db constraints (#1279) * add: tests for ensuring db constraints * fix: use given when then * feat: adjust cart to include line item adjustments (#1242) * fix: cart service + cart tests * fix: remaining tests * fix: swap tests * fix: add relationship + fix oas * refactor: applyDiscount * fix: refactor applyDiscount and fix + add unit tests * fix: plugins tests * feat: line item adjustments draft orders (#1243) * fix: draft order tests * fix: constraint * fix: wrong variable name * fix: unique constraint * progress: add tests * fix: add cascade on delete + fix discount relation * fix: remove optional unique prop * fix: cart removeLineItem + tests * fix: cart unit tests * fix: update snapshot * remove: verbose option * rename arg Co-authored-by: Sebastian Rindom <skrindom@gmail.com> * add: create adjustments for swap additional_items * add: create adjustments for return lines * fix: unit test for creating adjustment for additional_items * fix: create adjustments only for non return items + no deletion when item is a return item * add: integration tests * refactor: use refreshAdjustments method * refactor test Co-authored-by: Sebastian Rindom <skrindom@gmail.com> Co-authored-by: Sebastian Rindom <skrindom@gmail.com> Co-authored-by: Sebastian Rindom <skrindom@gmail.com>
This commit is contained in:
1 parent
607a382b4e
commit
1cfeb5dbd8
45 files changed
+3290
-581
No files matched your search
@@ -682,6 +682,472 @@ describe("DiscountService", () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe("validateDiscountForCartOrThrow", () => {
|
||||
const getCart = (id) => {
|
||||
if (id === "with-d") {
|
||||
return {
|
||||
id: "cart",
|
||||
discounts: [
|
||||
{
|
||||
code: "1234",
|
||||
rule: {
|
||||
type: "fixed",
|
||||
},
|
||||
},
|
||||
{
|
||||
code: "FS1234",
|
||||
rule: {
|
||||
type: "free_shipping",
|
||||
},
|
||||
},
|
||||
],
|
||||
region_id: "good",
|
||||
items: [
|
||||
{
|
||||
id: "li1",
|
||||
quantity: 2,
|
||||
unit_price: 1000,
|
||||
},
|
||||
{
|
||||
id: "li2",
|
||||
quantity: 1,
|
||||
unit_price: 500,
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
if (id === "with-d-and-customer") {
|
||||
return {
|
||||
id: "with-d-and-customer",
|
||||
discounts: [
|
||||
{
|
||||
code: "ApplicableForCustomer",
|
||||
rule: {
|
||||
type: "fixed",
|
||||
},
|
||||
},
|
||||
],
|
||||
region_id: "good",
|
||||
customer_id: "test-customer",
|
||||
}
|
||||
}
|
||||
return {
|
||||
id: "cart",
|
||||
discounts: [
|
||||
{
|
||||
code: "CODE",
|
||||
rule: {
|
||||
type: "percentage",
|
||||
},
|
||||
},
|
||||
],
|
||||
region_id: "good",
|
||||
items: [
|
||||
{
|
||||
id: "li1",
|
||||
quantity: 2,
|
||||
unit_price: 1000,
|
||||
},
|
||||
{
|
||||
id: "li2",
|
||||
quantity: 1,
|
||||
unit_price: 500,
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
let discountService
|
||||
|
||||
beforeEach(async () => {
|
||||
discountService = new DiscountService({})
|
||||
const hasReachedLimitMock = jest.fn().mockImplementation(() => false)
|
||||
const isDisabledMock = jest.fn().mockImplementation(() => false)
|
||||
const isValidForRegionMock = jest
|
||||
.fn()
|
||||
.mockImplementation(() => Promise.resolve(true))
|
||||
const canApplyForCustomerMock = jest.fn().mockImplementation(() => {
|
||||
return Promise.resolve(true)
|
||||
})
|
||||
discountService.hasReachedLimit = hasReachedLimitMock
|
||||
discountService.isDisabled = isDisabledMock
|
||||
discountService.canApplyForCustomer = canApplyForCustomerMock
|
||||
discountService.isValidForRegion = isValidForRegionMock
|
||||
})
|
||||
|
||||
it("calls all validation methods when cart includes a customer_id and doesn't throw", async () => {
|
||||
const discount = {
|
||||
id: "10off",
|
||||
code: "10%OFF",
|
||||
regions: [{ id: "good" }],
|
||||
rule: {
|
||||
id: "10off-rule",
|
||||
type: "percentage",
|
||||
},
|
||||
}
|
||||
const cart = getCart("with-d-and-customer")
|
||||
const result = await discountService.validateDiscountForCartOrThrow(
|
||||
cart,
|
||||
discount
|
||||
)
|
||||
expect(result).toBeUndefined()
|
||||
|
||||
expect(discountService.hasReachedLimit).toHaveBeenCalledTimes(1)
|
||||
expect(discountService.hasReachedLimit).toHaveBeenCalledWith(discount)
|
||||
|
||||
expect(discountService.isDisabled).toHaveBeenCalledTimes(1)
|
||||
expect(discountService.isDisabled).toHaveBeenCalledWith(discount)
|
||||
|
||||
expect(discountService.isValidForRegion).toHaveBeenCalledTimes(1)
|
||||
expect(discountService.isValidForRegion).toHaveBeenCalledWith(
|
||||
discount,
|
||||
cart.region_id
|
||||
)
|
||||
|
||||
expect(discountService.canApplyForCustomer).toHaveBeenCalledTimes(1)
|
||||
expect(discountService.canApplyForCustomer).toHaveBeenCalledWith(
|
||||
discount.rule.id,
|
||||
cart.customer_id
|
||||
)
|
||||
})
|
||||
|
||||
it("throws when hasReachedLimit returns true", async () => {
|
||||
const discount = {}
|
||||
const cart = getCart("with-d-and-customer")
|
||||
discountService.hasReachedLimit = jest.fn().mockImplementation(() => true)
|
||||
|
||||
expect(
|
||||
discountService.validateDiscountForCartOrThrow(cart, discount)
|
||||
).rejects.toThrow({
|
||||
message: "Discount has been used maximum allowed times",
|
||||
})
|
||||
})
|
||||
|
||||
it("throws when hasNotStarted returns true", async () => {
|
||||
const discount = {}
|
||||
const cart = getCart("with-d-and-customer")
|
||||
discountService.hasNotStarted = jest.fn().mockImplementation(() => true)
|
||||
|
||||
expect(
|
||||
discountService.validateDiscountForCartOrThrow(cart, discount)
|
||||
).rejects.toThrow({
|
||||
message: "Discount is not valid yet",
|
||||
})
|
||||
})
|
||||
|
||||
it("throws when hasExpired returns true", async () => {
|
||||
const discount = {}
|
||||
const cart = getCart("with-d-and-customer")
|
||||
discountService.hasExpired = jest.fn().mockImplementation(() => true)
|
||||
|
||||
expect(
|
||||
discountService.validateDiscountForCartOrThrow(cart, discount)
|
||||
).rejects.toThrow({
|
||||
message: "Discount is expired",
|
||||
})
|
||||
})
|
||||
|
||||
it("throws when isDisabled returns true", async () => {
|
||||
const discount = {}
|
||||
const cart = getCart("with-d-and-customer")
|
||||
discountService.isDisabled = jest.fn().mockImplementation(() => true)
|
||||
|
||||
expect(
|
||||
discountService.validateDiscountForCartOrThrow(cart, discount)
|
||||
).rejects.toThrow({
|
||||
message: "The discount code is disabled",
|
||||
})
|
||||
})
|
||||
|
||||
it("throws when isValidForRegion returns false", async () => {
|
||||
const discount = {}
|
||||
const cart = getCart("with-d-and-customer")
|
||||
discountService.isValidForRegion = jest
|
||||
.fn()
|
||||
.mockImplementation(() => Promise.resolve(false))
|
||||
|
||||
expect(
|
||||
discountService.validateDiscountForCartOrThrow(cart, discount)
|
||||
).rejects.toThrow({
|
||||
message: "The discount is not available in current region",
|
||||
})
|
||||
})
|
||||
|
||||
it("throws when canApplyForCustomer returns false", async () => {
|
||||
const discount = { rule: { id: "" } }
|
||||
const cart = getCart("with-d-and-customer")
|
||||
discountService.canApplyForCustomer = jest
|
||||
.fn()
|
||||
.mockImplementation(() => Promise.resolve(false))
|
||||
|
||||
expect(
|
||||
discountService.validateDiscountForCartOrThrow(cart, discount)
|
||||
).rejects.toThrow({
|
||||
message: "Discount is not valid for customer",
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("hasReachedLimit", () => {
|
||||
const discountService = new DiscountService({})
|
||||
|
||||
it("returns true if discount limit is reached", () => {
|
||||
const discount = {
|
||||
id: "limit-reached",
|
||||
code: "limit-reached",
|
||||
regions: [{ id: "good" }],
|
||||
rule: {},
|
||||
usage_count: 2,
|
||||
usage_limit: 2,
|
||||
}
|
||||
const hasReachedLimit = discountService.hasReachedLimit(discount)
|
||||
expect(hasReachedLimit).toBe(true)
|
||||
})
|
||||
|
||||
it("returns false if discount limit is not reached", () => {
|
||||
const discount = {
|
||||
id: "limit-reached",
|
||||
code: "limit-reached",
|
||||
regions: [{ id: "good" }],
|
||||
rule: {},
|
||||
usage_count: 1,
|
||||
usage_limit: 100,
|
||||
}
|
||||
|
||||
const hasReachedLimit = discountService.hasReachedLimit(discount)
|
||||
expect(hasReachedLimit).toBe(false)
|
||||
})
|
||||
|
||||
it("returns false if discount limit is not set", () => {
|
||||
const discount = {
|
||||
id: "limit-reached",
|
||||
code: "limit-reached",
|
||||
regions: [{ id: "good" }],
|
||||
rule: {},
|
||||
usage_count: 1,
|
||||
}
|
||||
|
||||
const hasReachedLimit = discountService.hasReachedLimit(discount)
|
||||
expect(hasReachedLimit).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe("isDisabled", () => {
|
||||
const discountService = new DiscountService({})
|
||||
|
||||
it("returns false if discount not disabled", async () => {
|
||||
const discount = {
|
||||
id: "10off",
|
||||
code: "10%OFF",
|
||||
regions: [{ id: "good" }],
|
||||
rule: {
|
||||
id: "10off-rule",
|
||||
type: "percentage",
|
||||
},
|
||||
is_disabled: false,
|
||||
}
|
||||
|
||||
const isDisabled = discountService.isDisabled(discount)
|
||||
expect(isDisabled).toBe(false)
|
||||
})
|
||||
|
||||
it("returns true if discount is disabled", async () => {
|
||||
const discount = {
|
||||
id: "10off",
|
||||
code: "10%OFF",
|
||||
regions: [{ id: "good" }],
|
||||
rule: {
|
||||
id: "10off-rule",
|
||||
type: "percentage",
|
||||
},
|
||||
is_disabled: true,
|
||||
}
|
||||
|
||||
const isDisabled = discountService.isDisabled(discount)
|
||||
expect(isDisabled).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe("hasNotStarted", () => {
|
||||
const discountService = new DiscountService({})
|
||||
|
||||
it("returns true if discount has a future starts_at date", async () => {
|
||||
const discount = {
|
||||
id: "10off",
|
||||
code: "10%OFF",
|
||||
regions: [{ id: "good" }],
|
||||
rule: {
|
||||
type: "percentage",
|
||||
},
|
||||
starts_at: getOffsetDate(1),
|
||||
ends_at: getOffsetDate(10),
|
||||
}
|
||||
|
||||
const hasNotStarted = discountService.hasNotStarted(discount)
|
||||
expect(hasNotStarted).toBe(true)
|
||||
})
|
||||
|
||||
it("returns false if discount has a past starts_at date", async () => {
|
||||
const discount = {
|
||||
id: "10off",
|
||||
code: "10%OFF",
|
||||
regions: [{ id: "good" }],
|
||||
rule: {
|
||||
type: "percentage",
|
||||
},
|
||||
starts_at: getOffsetDate(-2),
|
||||
ends_at: getOffsetDate(10),
|
||||
}
|
||||
|
||||
const hasNotStarted = discountService.hasNotStarted(discount)
|
||||
expect(hasNotStarted).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe("hasExpired", () => {
|
||||
const discountService = new DiscountService({})
|
||||
|
||||
it("returns false if discount has a future ends_at date", async () => {
|
||||
const discount = {
|
||||
id: "10off",
|
||||
code: "10%OFF",
|
||||
regions: [{ id: "good" }],
|
||||
rule: {
|
||||
type: "percentage",
|
||||
},
|
||||
ends_at: getOffsetDate(10),
|
||||
starts_at: getOffsetDate(-1),
|
||||
}
|
||||
|
||||
const hasExpired = discountService.hasExpired(discount)
|
||||
expect(hasExpired).toBe(false)
|
||||
})
|
||||
|
||||
it("returns true if discount has a past ends_at date", async () => {
|
||||
const discount = {
|
||||
id: "10off",
|
||||
code: "10%OFF",
|
||||
regions: [{ id: "good" }],
|
||||
rule: {
|
||||
type: "percentage",
|
||||
},
|
||||
starts_at: getOffsetDate(-10),
|
||||
ends_at: getOffsetDate(-1),
|
||||
}
|
||||
|
||||
const hasExpired = discountService.hasExpired(discount)
|
||||
expect(hasExpired).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe("isValidForRegion", () => {
|
||||
const retrieveMock = jest.fn().mockImplementation((id) => {
|
||||
if (id === "parent-discount-us") {
|
||||
return Promise.resolve({
|
||||
id,
|
||||
regions: [{ id: "us" }],
|
||||
rule: {
|
||||
id: "10off-rule",
|
||||
type: "percentage",
|
||||
},
|
||||
})
|
||||
} else if (id === "parent-discount-dk") {
|
||||
return Promise.resolve({
|
||||
id,
|
||||
regions: [{ id: "dk" }],
|
||||
rule: {
|
||||
id: "10off-rule",
|
||||
type: "percentage",
|
||||
},
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
const discountService = new DiscountService({})
|
||||
discountService.retrieve = retrieveMock
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
it("returns false if discount is not available in a given region", async () => {
|
||||
const discount = {
|
||||
id: "10off",
|
||||
code: "10%OFF",
|
||||
regions: [{ id: "us" }],
|
||||
rule: {
|
||||
id: "10off-rule",
|
||||
type: "percentage",
|
||||
},
|
||||
}
|
||||
|
||||
const isValidForRegion = await discountService.isValidForRegion(
|
||||
discount,
|
||||
"dk"
|
||||
)
|
||||
|
||||
expect(retrieveMock).toBeCalledTimes(0)
|
||||
expect(isValidForRegion).toBe(false)
|
||||
})
|
||||
|
||||
it("returns true if discount is available in a given region", async () => {
|
||||
const discount = {
|
||||
id: "10off",
|
||||
code: "10%OFF",
|
||||
regions: [{ id: "us" }],
|
||||
rule: {
|
||||
id: "10off-rule",
|
||||
type: "percentage",
|
||||
},
|
||||
}
|
||||
|
||||
const isValidForRegion = await discountService.isValidForRegion(
|
||||
discount,
|
||||
"us"
|
||||
)
|
||||
|
||||
expect(retrieveMock).toBeCalledTimes(0)
|
||||
expect(isValidForRegion).toBe(true)
|
||||
})
|
||||
|
||||
it("returns false if discount has a parent discount and is not available in region", async () => {
|
||||
const discount = {
|
||||
id: "10off",
|
||||
code: "10%OFF",
|
||||
parent_discount_id: "parent-discount-us",
|
||||
}
|
||||
|
||||
const isValidForRegion = await discountService.isValidForRegion(
|
||||
discount,
|
||||
"dk"
|
||||
)
|
||||
|
||||
expect(retrieveMock).toBeCalledTimes(1)
|
||||
expect(retrieveMock).toBeCalledWith(discount.parent_discount_id, {
|
||||
relations: ["rule", "regions"],
|
||||
})
|
||||
expect(isValidForRegion).toBe(false)
|
||||
})
|
||||
|
||||
it("returns true if discount has a parent discount and is available in region", async () => {
|
||||
const discount = {
|
||||
id: "10off",
|
||||
code: "10%OFF",
|
||||
parent_discount_id: "parent-discount-dk",
|
||||
}
|
||||
|
||||
const isValidForRegion = await discountService.isValidForRegion(
|
||||
discount,
|
||||
"dk"
|
||||
)
|
||||
expect(retrieveMock).toBeCalledTimes(1)
|
||||
expect(retrieveMock).toBeCalledWith(discount.parent_discount_id, {
|
||||
relations: ["rule", "regions"],
|
||||
})
|
||||
expect(isValidForRegion).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe("canApplyForCustomer", () => {
|
||||
const discountConditionRepository = {
|
||||
canApplyForCustomer: jest
|
||||
@@ -736,3 +1202,9 @@ describe("DiscountService", () => {
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
const getOffsetDate = (offset) => {
|
||||
const date = new Date()
|
||||
date.setDate(date.getDate() + offset)
|
||||
return date
|
||||
}
|
||||
Reference in new issue
Block a user