fix: merge
This commit is contained in:
@@ -0,0 +1,161 @@
|
||||
const { dropDatabase } = require("pg-god");
|
||||
const path = require("path");
|
||||
const { Region, DiscountRule, Discount } = require("@medusajs/medusa");
|
||||
|
||||
const setupServer = require("../../../helpers/setup-server");
|
||||
const { useApi } = require("../../../helpers/use-api");
|
||||
const { initDb } = require("../../../helpers/use-db");
|
||||
const adminSeeder = require("../../helpers/admin-seeder");
|
||||
|
||||
jest.setTimeout(30000);
|
||||
|
||||
describe("/admin/discounts", () => {
|
||||
let medusaProcess;
|
||||
let dbConnection;
|
||||
|
||||
beforeAll(async () => {
|
||||
const cwd = path.resolve(path.join(__dirname, "..", ".."));
|
||||
dbConnection = await initDb({ cwd });
|
||||
medusaProcess = await setupServer({ cwd });
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await dbConnection.close();
|
||||
await dropDatabase({ databaseName: "medusa-integration" });
|
||||
|
||||
medusaProcess.kill();
|
||||
});
|
||||
|
||||
describe("POST /admin/discounts", () => {
|
||||
beforeEach(async () => {
|
||||
const manager = dbConnection.manager;
|
||||
try {
|
||||
await adminSeeder(dbConnection);
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
throw err;
|
||||
}
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
const manager = dbConnection.manager;
|
||||
await manager.query(`DELETE FROM "discount"`);
|
||||
await manager.query(`DELETE FROM "discount_rule"`);
|
||||
await manager.query(`DELETE FROM "user"`);
|
||||
});
|
||||
|
||||
it("creates a discount and updates it", async () => {
|
||||
const api = useApi();
|
||||
|
||||
const response = await api
|
||||
.post(
|
||||
"/admin/discounts",
|
||||
{
|
||||
code: "HELLOWORLD",
|
||||
rule: {
|
||||
description: "test",
|
||||
type: "percentage",
|
||||
value: 10,
|
||||
allocation: "total",
|
||||
},
|
||||
usage_limit: 10,
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
Authorization: "Bearer test_token",
|
||||
},
|
||||
}
|
||||
)
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
});
|
||||
|
||||
expect(response.status).toEqual(200);
|
||||
expect(response.data.discount).toEqual(
|
||||
expect.objectContaining({
|
||||
code: "HELLOWORLD",
|
||||
usage_limit: 10,
|
||||
})
|
||||
);
|
||||
|
||||
const updated = await api
|
||||
.post(
|
||||
`/admin/discounts/${response.data.discount.id}`,
|
||||
{
|
||||
usage_limit: 20,
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
Authorization: "Bearer test_token",
|
||||
},
|
||||
}
|
||||
)
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
});
|
||||
|
||||
expect(updated.status).toEqual(200);
|
||||
expect(updated.data.discount).toEqual(
|
||||
expect.objectContaining({
|
||||
code: "HELLOWORLD",
|
||||
usage_limit: 20,
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("POST /admin/discounts/:discount_id/dynamic-codes", () => {
|
||||
beforeEach(async () => {
|
||||
const manager = dbConnection.manager;
|
||||
try {
|
||||
await adminSeeder(dbConnection);
|
||||
await manager.insert(DiscountRule, {
|
||||
id: "test-discount-rule",
|
||||
description: "Dynamic rule",
|
||||
type: "percentage",
|
||||
value: 10,
|
||||
allocation: "total",
|
||||
});
|
||||
await manager.insert(Discount, {
|
||||
id: "test-discount",
|
||||
code: "DYNAMIC",
|
||||
is_dynamic: true,
|
||||
is_disabled: false,
|
||||
rule_id: "test-discount-rule",
|
||||
});
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
throw err;
|
||||
}
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
const manager = dbConnection.manager;
|
||||
await manager.query(`DELETE FROM "discount"`);
|
||||
await manager.query(`DELETE FROM "discount_rule"`);
|
||||
await manager.query(`DELETE FROM "user"`);
|
||||
});
|
||||
|
||||
it("creates a dynamic discount", async () => {
|
||||
const api = useApi();
|
||||
|
||||
const response = await api
|
||||
.post(
|
||||
"/admin/discounts/test-discount/dynamic-codes",
|
||||
{
|
||||
code: "HELLOWORLD",
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
Authorization: "Bearer test_token",
|
||||
},
|
||||
}
|
||||
)
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
});
|
||||
|
||||
expect(response.status).toEqual(200);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -16,6 +16,24 @@ module.exports = async (connection, data = {}) => {
|
||||
tax_rate: 0,
|
||||
});
|
||||
|
||||
await manager.insert(DiscountRule, {
|
||||
id: "test-discount-rule",
|
||||
description: "Dynamic rule",
|
||||
type: "percentage",
|
||||
value: 10,
|
||||
allocation: "total",
|
||||
});
|
||||
|
||||
await manager.insert(Discount, {
|
||||
id: "test-discount",
|
||||
code: "DYNAMIC",
|
||||
rule_id: "test-discount-rule",
|
||||
is_dynamic: true,
|
||||
usage_count: 0,
|
||||
usage_limit: 1,
|
||||
is_disabled: false,
|
||||
});
|
||||
|
||||
const d = await manager.create(Discount, {
|
||||
id: "test-discount",
|
||||
code: "CREATED",
|
||||
@@ -29,8 +47,6 @@ module.exports = async (connection, data = {}) => {
|
||||
type: "fixed",
|
||||
value: 10000,
|
||||
allocation: "total",
|
||||
usage_limit: 2,
|
||||
usage_count: 2,
|
||||
});
|
||||
|
||||
d.rule = dr;
|
||||
|
||||
@@ -1174,9 +1174,9 @@
|
||||
chalk "^4.0.0"
|
||||
|
||||
"@medusajs/medusa@1.1.13-dev-1615987548667":
|
||||
version "1.1.13"
|
||||
resolved "https://registry.yarnpkg.com/@medusajs/medusa/-/medusa-1.1.13.tgz#1aad18445c062e298bfea2ac362ad2740a53fde6"
|
||||
integrity sha512-yPQ+uA9qQsJiqME7nR8ggeg/qi2Kypqi6iNsFrdXbA99djjm3Cpkl3kmkTorRvzQ6jbKZ1QMLS+Dx5FwbKuiTw==
|
||||
version "1.1.16"
|
||||
resolved "https://registry.yarnpkg.com/@medusajs/medusa/-/medusa-1.1.16.tgz#692578b1eeced9d3603326fda172d43abe5cce95"
|
||||
integrity sha512-SXK9YEBMxlfP7KgnIYfGTflFhUB2vx6aKI2MiVesCzB5wR2OKkOn6W+oIWeF0/pBttdSitCP4G2iJx2y8dYL5g==
|
||||
dependencies:
|
||||
"@babel/plugin-transform-classes" "^7.9.5"
|
||||
"@hapi/joi" "^16.1.8"
|
||||
@@ -1198,8 +1198,8 @@
|
||||
joi "^17.3.0"
|
||||
joi-objectid "^3.0.1"
|
||||
jsonwebtoken "^8.5.1"
|
||||
medusa-core-utils "^1.1.2"
|
||||
medusa-test-utils "^1.1.5"
|
||||
medusa-core-utils "^1.1.3"
|
||||
medusa-test-utils "^1.1.6"
|
||||
morgan "^1.9.1"
|
||||
multer "^1.4.2"
|
||||
passport "^0.4.0"
|
||||
@@ -4314,28 +4314,28 @@ media-typer@0.3.0:
|
||||
resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
|
||||
integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=
|
||||
|
||||
medusa-core-utils@^1.1.2:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/medusa-core-utils/-/medusa-core-utils-1.1.2.tgz#3d9ccd37b052bc4701040fbf0618210f075f459a"
|
||||
integrity sha512-YAGkLkS5DqCSHWlMz2Bfh0nKJQ8n35IfH/39q6J9DXfFUPYU+d5i8lSvIS8PNsXuSs9Es0dzY2ZS/sOIFKWzxw==
|
||||
medusa-core-utils@^1.1.3:
|
||||
version "1.1.3"
|
||||
resolved "https://registry.yarnpkg.com/medusa-core-utils/-/medusa-core-utils-1.1.3.tgz#e740de04a08655b9b037ef135afcc99914498f24"
|
||||
integrity sha512-Xk7SuHEo4kBgJFHIyd6OkBvK0KO23hF5pPHY2R9Luf26vRvyD3mUZUBIHPnxuT2f576vgJJ7verMN7peizXXkg==
|
||||
dependencies:
|
||||
joi "^17.3.0"
|
||||
joi-objectid "^3.0.1"
|
||||
|
||||
medusa-interfaces@1.1.3-dev-1615987548667:
|
||||
version "1.1.3"
|
||||
resolved "https://registry.yarnpkg.com/medusa-interfaces/-/medusa-interfaces-1.1.3.tgz#b1fb889c321433d31e6d998dbd7d47eb71a27589"
|
||||
integrity sha512-6WNzOfcHDM7CaRFOerZBt5eRqje97gMbTcoEiFOxJy8a5B7OIq/wt7UBI1Et3C9lYFKpb5fSUwfDwRWY/yO3UA==
|
||||
version "1.1.4"
|
||||
resolved "https://registry.yarnpkg.com/medusa-interfaces/-/medusa-interfaces-1.1.4.tgz#f71e9eb885cd6f51105986d861f83a2b497da240"
|
||||
integrity sha512-uMjfXbIkJSqkd87/wPGFFQ47ZmNdImW2sGWgrEa8pMGYfE8rGQxgLvGEEnn34ejJHWYGPQRH5Az/J76tY/Zs2g==
|
||||
dependencies:
|
||||
medusa-core-utils "^1.1.2"
|
||||
medusa-core-utils "^1.1.3"
|
||||
|
||||
medusa-test-utils@^1.1.5:
|
||||
version "1.1.5"
|
||||
resolved "https://registry.yarnpkg.com/medusa-test-utils/-/medusa-test-utils-1.1.5.tgz#5a93c52117c7a8659058512abf939e2aaed619b7"
|
||||
integrity sha512-oM6lIRdnq6T3VRi702hYOQ9m3T9Zy2IwZAp2nBnJRlXpXbWlNnIS3y0E638m5wsGbFzSKKxrDNdGtEpujxwzyQ==
|
||||
medusa-test-utils@^1.1.6:
|
||||
version "1.1.6"
|
||||
resolved "https://registry.yarnpkg.com/medusa-test-utils/-/medusa-test-utils-1.1.6.tgz#c9b2675532338be760a47bd8df7566b162c171d4"
|
||||
integrity sha512-72k2DMKrxPDgm1JeCCjtNVTl6nFlbdp5HHtE3mWEpl0+88pZvNSp/HL41nENHHZ0YsK2w34FTbm0YMqU4EHg+Q==
|
||||
dependencies:
|
||||
"@babel/plugin-transform-classes" "^7.9.5"
|
||||
medusa-core-utils "^1.1.2"
|
||||
medusa-core-utils "^1.1.3"
|
||||
randomatic "^3.1.1"
|
||||
|
||||
merge-descriptors@1.0.1:
|
||||
|
||||
@@ -244,6 +244,8 @@ class WebshipperFulfillmentService extends FulfillmentService {
|
||||
|
||||
if (!webshipperOrder) {
|
||||
let invoice
|
||||
let certificateOfOrigin
|
||||
|
||||
if (this.invoiceGenerator_) {
|
||||
const base64Invoice = await this.invoiceGenerator_.createInvoice(
|
||||
fromOrder,
|
||||
@@ -263,6 +265,27 @@ class WebshipperFulfillmentService extends FulfillmentService {
|
||||
.catch((err) => {
|
||||
throw err
|
||||
})
|
||||
|
||||
if (this.invoiceGenerator_.createCertificateOfOrigin) {
|
||||
const base64Coo = await this.invoiceGenerator_.createCertificateOfOrigin(
|
||||
fromOrder,
|
||||
fulfillmentItems
|
||||
)
|
||||
|
||||
certificateOfOrigin = await this.client_.documents
|
||||
.create({
|
||||
type: "documents",
|
||||
attributes: {
|
||||
document_size: this.options_.document_size || "A4",
|
||||
document_format: "PDF",
|
||||
base64: base64Coo,
|
||||
document_type: "certificate",
|
||||
},
|
||||
})
|
||||
.catch((err) => {
|
||||
throw err
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
let id = fulfillment.id
|
||||
@@ -335,14 +358,25 @@ class WebshipperFulfillmentService extends FulfillmentService {
|
||||
country_code: methodData.drop_point_country_code.toUpperCase(),
|
||||
}
|
||||
}
|
||||
if (invoice) {
|
||||
|
||||
if (invoice || certificateOfOrigin) {
|
||||
const docData = []
|
||||
if (invoice) {
|
||||
docData.push({
|
||||
id: invoice.data.id,
|
||||
type: invoice.data.type,
|
||||
})
|
||||
}
|
||||
|
||||
if (certificateOfOrigin) {
|
||||
docData.push({
|
||||
id: certificateOfOrigin.data.id,
|
||||
type: certificateOfOrigin.data.type,
|
||||
})
|
||||
}
|
||||
|
||||
newOrder.relationships.documents = {
|
||||
data: [
|
||||
{
|
||||
id: invoice.data.id,
|
||||
type: invoice.data.type,
|
||||
},
|
||||
],
|
||||
data: docData,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -76,7 +76,7 @@ class ContentfulService extends BaseService {
|
||||
let assets = []
|
||||
await Promise.all(
|
||||
product.images
|
||||
.filter((image) => image !== product.thumbnail)
|
||||
.filter((image) => image.url !== product.thumbnail)
|
||||
.map(async (image, i) => {
|
||||
const asset = await environment.createAsset({
|
||||
fields: {
|
||||
@@ -89,8 +89,8 @@ class ContentfulService extends BaseService {
|
||||
file: {
|
||||
"en-US": {
|
||||
contentType: "image/xyz",
|
||||
fileName: image,
|
||||
upload: image,
|
||||
fileName: image.url,
|
||||
upload: image.url,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -450,7 +450,7 @@ class ContentfulService extends BaseService {
|
||||
]
|
||||
|
||||
// Update came directly from product variant service so only act on a couple
|
||||
// of fields. When the update comes from the product we want to ensure
|
||||
// of fields. When the update comes from the product we want to ensure
|
||||
// references are set up correctly so we run through everything.
|
||||
if (variant.fields) {
|
||||
const found = variant.fields.find((f) => updateFields.includes(f))
|
||||
|
||||
@@ -2090,9 +2090,9 @@ camelcase@^5.0.0, camelcase@^5.3.1:
|
||||
integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==
|
||||
|
||||
caniuse-lite@^1.0.30001181:
|
||||
version "1.0.30001207"
|
||||
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001207.tgz#364d47d35a3007e528f69adb6fecb07c2bb2cc50"
|
||||
integrity sha512-UPQZdmAsyp2qfCTiMU/zqGSWOYaY9F9LL61V8f+8MrubsaDGpaHD9HRV/EWZGULZn0Hxu48SKzI5DgFwTvHuYw==
|
||||
version "1.0.30001208"
|
||||
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001208.tgz#a999014a35cebd4f98c405930a057a0d75352eb9"
|
||||
integrity sha512-OE5UE4+nBOro8Dyvv0lfx+SRtfVIOM9uhKqFmJeUbGriqhhStgp1A0OyBpgy3OUF8AhYCT+PVwPC1gMl2ZcQMA==
|
||||
|
||||
capture-exit@^2.0.0:
|
||||
version "2.0.0"
|
||||
@@ -2359,17 +2359,17 @@ copy-descriptor@^0.1.0:
|
||||
integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=
|
||||
|
||||
core-js-compat@^3.8.1, core-js-compat@^3.9.0:
|
||||
version "3.10.0"
|
||||
resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.10.0.tgz#3600dc72869673c110215ee7a005a8609dea0fe1"
|
||||
integrity sha512-9yVewub2MXNYyGvuLnMHcN1k9RkvB7/ofktpeKTIaASyB88YYqGzUnu0ywMMhJrDHOMiTjSHWGzR+i7Wb9Z1kQ==
|
||||
version "3.10.1"
|
||||
resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.10.1.tgz#62183a3a77ceeffcc420d907a3e6fc67d9b27f1c"
|
||||
integrity sha512-ZHQTdTPkqvw2CeHiZC970NNJcnwzT6YIueDMASKt+p3WbZsLXOcoD392SkcWhkC0wBBHhlfhqGKKsNCQUozYtg==
|
||||
dependencies:
|
||||
browserslist "^4.16.3"
|
||||
semver "7.0.0"
|
||||
|
||||
core-js@^3.6.5, core-js@^3.7.0:
|
||||
version "3.10.0"
|
||||
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.10.0.tgz#9a020547c8b6879f929306949e31496bbe2ae9b3"
|
||||
integrity sha512-MQx/7TLgmmDVamSyfE+O+5BHvG1aUGj/gHhLn1wVtm2B5u1eVIPvh7vkfjwWKNCjrTJB8+He99IntSQ1qP+vYQ==
|
||||
version "3.10.1"
|
||||
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.10.1.tgz#e683963978b6806dcc6c0a4a8bd4ab0bdaf3f21a"
|
||||
integrity sha512-pwCxEXnj27XG47mu7SXAwhLP3L5CrlvCB91ANUkIz40P27kUcvNfSdvyZJ9CLHiVoKSp+TTChMQMSKQEH/IQxA==
|
||||
|
||||
core-util-is@1.0.2, core-util-is@~1.0.0:
|
||||
version "1.0.2"
|
||||
@@ -2617,9 +2617,9 @@ ee-first@1.1.1:
|
||||
integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=
|
||||
|
||||
electron-to-chromium@^1.3.649:
|
||||
version "1.3.709"
|
||||
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.709.tgz#d7be0b5686a2fdfe8bad898faa3a428d04d8f656"
|
||||
integrity sha512-LolItk2/ikSGQ7SN8UkuKVNMBZp3RG7Itgaxj1npsHRzQobj9JjMneZOZfLhtwlYBe5fCJ75k+cVCiDFUs23oA==
|
||||
version "1.3.710"
|
||||
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.710.tgz#b33d316e5d6de92b916e766d8a478d19796ffe11"
|
||||
integrity sha512-b3r0E2o4yc7mNmBeJviejF1rEx49PUBi+2NPa7jHEX3arkAXnVgLhR0YmV8oi6/Qf3HH2a8xzQmCjHNH0IpXWQ==
|
||||
|
||||
emoji-regex@^7.0.1:
|
||||
version "7.0.3"
|
||||
|
||||
@@ -18,6 +18,17 @@ class OrderSubscriber {
|
||||
|
||||
this.fulfillmentService_ = fulfillmentService
|
||||
|
||||
|
||||
// Swaps
|
||||
// order.swap_received <--- Will be deprecated
|
||||
// swap.created
|
||||
// swap.received
|
||||
// swap.shipment_created
|
||||
// swap.payment_completed
|
||||
// swap.payment_captured
|
||||
// swap.refund_processed
|
||||
|
||||
|
||||
eventBusService.subscribe(
|
||||
"order.shipment_created",
|
||||
async ({ id, fulfillment_id }) => {
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
import { humanizeAmount } from "medusa-core-utils"
|
||||
|
||||
class OrderSubscriber {
|
||||
constructor({
|
||||
segmentService,
|
||||
eventBusService,
|
||||
swapService,
|
||||
lineItemService,
|
||||
fulfillmentService,
|
||||
}) {
|
||||
this.fulfillmentService_ = fulfillmentService
|
||||
|
||||
this.lineItemService_ = lineItemService
|
||||
|
||||
this.swapService_ = swapService
|
||||
|
||||
this.segmentService_ = segmentService
|
||||
|
||||
eventBusService.subscribe(
|
||||
"swap.shipment_created",
|
||||
async ({ id, fulfillment_id }) => {
|
||||
const [swap, swapReport] = await this.gatherSwapReport(id)
|
||||
const fulfillment = await this.fulfillmentService_.retrieve(
|
||||
fulfillment_id
|
||||
)
|
||||
|
||||
const currency = swapReport.currency
|
||||
const total = humanizeAmount(swap.difference_due, currency)
|
||||
const reporting_total = await this.segmentService_.getReportingValue(
|
||||
currency,
|
||||
total
|
||||
)
|
||||
|
||||
return await segmentService.track({
|
||||
event: "Swap Shipped",
|
||||
userId: swap.order.customer_id,
|
||||
timestamp: fulfillment.shipped_at,
|
||||
properties: {
|
||||
reporting_total,
|
||||
total,
|
||||
...swapReport,
|
||||
},
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
eventBusService.subscribe("swap.payment_completed", async ({ id }) => {
|
||||
const [swap, swapReport] = await this.gatherSwapReport(id)
|
||||
|
||||
const currency = swapReport.currency
|
||||
const total = humanizeAmount(swap.difference_due, currency)
|
||||
const reporting_total = await this.segmentService_.getReportingValue(
|
||||
currency,
|
||||
total
|
||||
)
|
||||
|
||||
return await segmentService.track({
|
||||
event: "Swap Confirmed",
|
||||
userId: swap.order.customer_id,
|
||||
timestamp: swap.confirmed_at,
|
||||
properties: {
|
||||
reporting_total,
|
||||
total,
|
||||
...swapReport,
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
eventBusService.subscribe("swap.created", async ({ id }) => {
|
||||
const [swap, swapReport] = await this.gatherSwapReport(id)
|
||||
|
||||
return await segmentService.track({
|
||||
event: "Swap Created",
|
||||
userId: swap.order.customer_id,
|
||||
timestamp: swap.created_at,
|
||||
properties: swapReport,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
async gatherSwapReport(id) {
|
||||
const swap = await this.swapService_.retrieve(id, {
|
||||
relations: [
|
||||
"order",
|
||||
"additional_items",
|
||||
"additional_items.variant",
|
||||
"return_order",
|
||||
"return_order.items",
|
||||
"return_order.shipping_method",
|
||||
],
|
||||
})
|
||||
|
||||
const currency = swap.order.currency_code
|
||||
|
||||
const newItems = await Promise.all(
|
||||
swap.additional_items.map(async (i) => {
|
||||
const price = humanizeAmount(i.unit_price, currency)
|
||||
const reporting_price = await this.segmentService_.getReportingValue(
|
||||
currency,
|
||||
price
|
||||
)
|
||||
|
||||
return {
|
||||
name: i.title,
|
||||
product_id: i.variant.product_id,
|
||||
variant: i.variant.sku,
|
||||
quantity: i.quantity,
|
||||
price,
|
||||
reporting_price,
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
const returnItems = await Promise.all(
|
||||
swap.return_order.items.map(async (ri) => {
|
||||
const i = await this.lineItemService_.retrieve(ri.item_id, {
|
||||
relations: ["variant"],
|
||||
})
|
||||
const price = humanizeAmount(i.unit_price, currency)
|
||||
const reporting_price = await this.segmentService_.getReportingValue(
|
||||
currency,
|
||||
price
|
||||
)
|
||||
|
||||
return {
|
||||
name: i.title,
|
||||
product_id: i.variant.product_id,
|
||||
variant: i.variant.sku,
|
||||
quantity: ri.quantity,
|
||||
price,
|
||||
reporting_price,
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
return [
|
||||
swap,
|
||||
{
|
||||
swap_id: swap.id,
|
||||
order_id: swap.order_id,
|
||||
new_items: newItems,
|
||||
return_items: returnItems,
|
||||
currency,
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
export default OrderSubscriber
|
||||
@@ -38,6 +38,8 @@ describe("POST /admin/discounts/:discount_id/regions/:region_id", () => {
|
||||
"is_disabled",
|
||||
"rule_id",
|
||||
"parent_discount_id",
|
||||
"usage_limit",
|
||||
"usage_count",
|
||||
"starts_at",
|
||||
"ends_at",
|
||||
"created_at",
|
||||
|
||||
@@ -38,6 +38,8 @@ describe("POST /admin/discounts/:discount_id/variants/:variant_id", () => {
|
||||
"is_disabled",
|
||||
"rule_id",
|
||||
"parent_discount_id",
|
||||
"usage_limit",
|
||||
"usage_count",
|
||||
"starts_at",
|
||||
"ends_at",
|
||||
"created_at",
|
||||
|
||||
@@ -11,6 +11,7 @@ describe("POST /admin/discounts", () => {
|
||||
payload: {
|
||||
code: "TEST",
|
||||
rule: {
|
||||
description: "Test",
|
||||
type: "fixed",
|
||||
value: 10,
|
||||
allocation: "total",
|
||||
@@ -33,6 +34,7 @@ describe("POST /admin/discounts", () => {
|
||||
expect(DiscountServiceMock.create).toHaveBeenCalledWith({
|
||||
code: "TEST",
|
||||
rule: {
|
||||
description: "Test",
|
||||
type: "fixed",
|
||||
value: 10,
|
||||
allocation: "total",
|
||||
@@ -51,6 +53,7 @@ describe("POST /admin/discounts", () => {
|
||||
payload: {
|
||||
code: "10%OFF",
|
||||
rule: {
|
||||
description: "Test",
|
||||
value: 10,
|
||||
allocation: "total",
|
||||
},
|
||||
|
||||
@@ -9,6 +9,8 @@ const defaultFields = [
|
||||
"is_disabled",
|
||||
"rule_id",
|
||||
"parent_discount_id",
|
||||
"usage_limit",
|
||||
"usage_count",
|
||||
"starts_at",
|
||||
"ends_at",
|
||||
"created_at",
|
||||
|
||||
@@ -9,6 +9,8 @@ const defaultFields = [
|
||||
"is_disabled",
|
||||
"rule_id",
|
||||
"parent_discount_id",
|
||||
"usage_limit",
|
||||
"usage_count",
|
||||
"starts_at",
|
||||
"ends_at",
|
||||
"created_at",
|
||||
|
||||
@@ -9,6 +9,8 @@ const defaultFields = [
|
||||
"is_disabled",
|
||||
"rule_id",
|
||||
"parent_discount_id",
|
||||
"usage_limit",
|
||||
"usage_count",
|
||||
"starts_at",
|
||||
"ends_at",
|
||||
"created_at",
|
||||
|
||||
@@ -36,6 +36,9 @@ import { MedusaError, Validator } from "medusa-core-utils"
|
||||
* type: array
|
||||
* items:
|
||||
* type: string
|
||||
* usage_limit:
|
||||
* type: number
|
||||
* description: Maximum times the discount can be used
|
||||
* metadata:
|
||||
* description: An optional set of key-value pairs to hold additional information.
|
||||
* type: object
|
||||
@@ -64,14 +67,14 @@ export default async (req, res) => {
|
||||
.required(),
|
||||
allocation: Validator.string().required(),
|
||||
valid_for: Validator.array().items(Validator.string()),
|
||||
usage_limit: Validator.number()
|
||||
.positive()
|
||||
.optional(),
|
||||
})
|
||||
.required(),
|
||||
is_disabled: Validator.boolean().default(false),
|
||||
starts_at: Validator.date().optional(),
|
||||
ends_at: Validator.date().optional(),
|
||||
usage_limit: Validator.number()
|
||||
.positive()
|
||||
.optional(),
|
||||
regions: Validator.array()
|
||||
.items(Validator.string())
|
||||
.optional(),
|
||||
|
||||
@@ -26,6 +26,7 @@ export default async (req, res) => {
|
||||
|
||||
const schema = Validator.object().keys({
|
||||
code: Validator.string().required(),
|
||||
usage_limit: Validator.number().default(1),
|
||||
metadata: Validator.object().optional(),
|
||||
})
|
||||
|
||||
|
||||
@@ -62,6 +62,8 @@ export const defaultFields = [
|
||||
"is_disabled",
|
||||
"rule_id",
|
||||
"parent_discount_id",
|
||||
"usage_limit",
|
||||
"usage_count",
|
||||
"starts_at",
|
||||
"ends_at",
|
||||
"created_at",
|
||||
|
||||
@@ -64,14 +64,14 @@ export default async (req, res) => {
|
||||
value: Validator.number().required(),
|
||||
allocation: Validator.string().required(),
|
||||
valid_for: Validator.array().items(Validator.string()),
|
||||
usage_limit: Validator.number()
|
||||
.positive()
|
||||
.optional(),
|
||||
})
|
||||
.optional(),
|
||||
is_disabled: Validator.boolean().optional(),
|
||||
starts_at: Validator.date().optional(),
|
||||
ends_at: Validator.date().optional(),
|
||||
usage_limit: Validator.number()
|
||||
.positive()
|
||||
.optional(),
|
||||
regions: Validator.array()
|
||||
.items(Validator.string())
|
||||
.optional(),
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
import { MigrationInterface, QueryRunner } from "typeorm"
|
||||
|
||||
export class discountUsage1617002207608 implements MigrationInterface {
|
||||
name = "discountUsage1617002207608"
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "discount_rule" DROP COLUMN "usage_limit"`
|
||||
)
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "discount_rule" DROP COLUMN "usage_count"`
|
||||
)
|
||||
await queryRunner.query(`ALTER TABLE "discount" ADD "usage_limit" integer`)
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "discount" ADD "usage_count" integer NOT NULL DEFAULT '0'`
|
||||
)
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "discount_rule" ALTER COLUMN "description" DROP NOT NULL`
|
||||
)
|
||||
await queryRunner.query(
|
||||
`COMMENT ON COLUMN "discount_rule"."description" IS NULL`
|
||||
)
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE "discount" DROP COLUMN "usage_count"`)
|
||||
await queryRunner.query(`ALTER TABLE "discount" DROP COLUMN "usage_limit"`)
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "discount_rule" ADD "usage_count" integer NOT NULL DEFAULT '0'`
|
||||
)
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "discount_rule" ADD "usage_limit" integer`
|
||||
)
|
||||
await queryRunner.query(
|
||||
`COMMENT ON COLUMN "discount_rule"."description" IS NULL`
|
||||
)
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "discount_rule" ALTER COLUMN "description" SET NOT NULL`
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -30,7 +30,7 @@ export class DiscountRule {
|
||||
@PrimaryColumn()
|
||||
id: string
|
||||
|
||||
@Column()
|
||||
@Column({ nullable: true })
|
||||
description: string
|
||||
|
||||
@Column({
|
||||
@@ -63,12 +63,6 @@ export class DiscountRule {
|
||||
})
|
||||
valid_for: Product[]
|
||||
|
||||
@Column({ nullable: true })
|
||||
usage_limit: number
|
||||
|
||||
@Column({ default: 0 })
|
||||
usage_count: number
|
||||
|
||||
@CreateDateColumn({ type: "timestamptz" })
|
||||
created_at: Date
|
||||
|
||||
@@ -121,12 +115,6 @@ export class DiscountRule {
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/product"
|
||||
* usage_limit:
|
||||
* description: "The maximum number of times that a discount can be used."
|
||||
* type: integer
|
||||
* usage_count:
|
||||
* description: "The number of times a discount has been used."
|
||||
* type: integer
|
||||
* created_at:
|
||||
* description: "The date with timezone at which the resource was created."
|
||||
* type: string
|
||||
|
||||
@@ -68,6 +68,12 @@ export class Discount {
|
||||
})
|
||||
regions: Region[]
|
||||
|
||||
@Column({ nullable: true })
|
||||
usage_limit: number
|
||||
|
||||
@Column({ default: 0 })
|
||||
usage_count: number
|
||||
|
||||
@CreateDateColumn({ type: "timestamptz" })
|
||||
created_at: Date
|
||||
|
||||
@@ -127,6 +133,12 @@ export class Discount {
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/region"
|
||||
* usage_limit:
|
||||
* description: "The maximum number of times that a discount can be used."
|
||||
* type: integer
|
||||
* usage_count:
|
||||
* description: "The number of times a discount has been used."
|
||||
* type: integer
|
||||
* created_at:
|
||||
* description: "The date with timezone at which the resource was created."
|
||||
* type: string
|
||||
|
||||
@@ -1467,10 +1467,9 @@ describe("CartService", () => {
|
||||
id: IdMap.getId("limit-reached"),
|
||||
code: "limit-reached",
|
||||
regions: [{ id: IdMap.getId("good") }],
|
||||
rule: {
|
||||
usage_count: 2,
|
||||
usage_limit: 2,
|
||||
},
|
||||
rule: {},
|
||||
usage_count: 2,
|
||||
usage_limit: 2,
|
||||
})
|
||||
}
|
||||
if (code === "null-count") {
|
||||
@@ -1478,10 +1477,9 @@ describe("CartService", () => {
|
||||
id: IdMap.getId("null-count"),
|
||||
code: "null-count",
|
||||
regions: [{ id: IdMap.getId("good") }],
|
||||
rule: {
|
||||
usage_count: null,
|
||||
usage_limit: 2,
|
||||
},
|
||||
rule: {},
|
||||
usage_count: null,
|
||||
usage_limit: 2,
|
||||
})
|
||||
}
|
||||
if (code === "FREESHIPPING") {
|
||||
@@ -1630,10 +1628,9 @@ describe("CartService", () => {
|
||||
id: IdMap.getId("null-count"),
|
||||
code: "null-count",
|
||||
regions: [{ id: IdMap.getId("good") }],
|
||||
rule: {
|
||||
usage_count: 0,
|
||||
usage_limit: 2,
|
||||
},
|
||||
usage_count: 0,
|
||||
usage_limit: 2,
|
||||
rule: {},
|
||||
},
|
||||
],
|
||||
discount_total: 0,
|
||||
|
||||
@@ -814,10 +814,10 @@ class CartService extends BaseService {
|
||||
const rule = discount.rule
|
||||
|
||||
// if limit is set and reached, we make an early exit
|
||||
if (rule?.usage_limit) {
|
||||
rule.usage_count = rule.usage_count || 0
|
||||
if (discount.usage_limit) {
|
||||
discount.usage_count = discount.usage_count || 0
|
||||
|
||||
if (rule.usage_limit === rule.usage_count)
|
||||
if (discount.usage_limit === discount.usage_count)
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.NOT_ALLOWED,
|
||||
"Discount has been used maximum allowed times"
|
||||
|
||||
@@ -83,13 +83,6 @@ class DiscountService extends BaseService {
|
||||
.required(),
|
||||
allocation: Validator.string().required(),
|
||||
valid_for: Validator.array().optional(),
|
||||
usage_limit: Validator.number()
|
||||
.positive()
|
||||
.allow(null)
|
||||
.optional(),
|
||||
usage_count: Validator.number()
|
||||
.positive()
|
||||
.optional(),
|
||||
created_at: Validator.date().optional(),
|
||||
updated_at: Validator.date()
|
||||
.allow(null)
|
||||
@@ -337,6 +330,7 @@ class DiscountService extends BaseService {
|
||||
is_disabled: false,
|
||||
code: data.code.toUpperCase(),
|
||||
parent_discount_id: discount.id,
|
||||
usage_limit: discount.usage_limit,
|
||||
}
|
||||
|
||||
const created = await discountRepo.create(toCreate)
|
||||
|
||||
@@ -48,12 +48,9 @@ class OrderSubscriber {
|
||||
|
||||
await Promise.all(
|
||||
order.discounts.map(async d => {
|
||||
const usageCount = d.rule?.usage_count || 0
|
||||
const usageCount = d?.usage_count || 0
|
||||
return this.discountService_.update(d.id, {
|
||||
rule: {
|
||||
...d.rule,
|
||||
usage_count: usageCount + 1,
|
||||
},
|
||||
usage_count: usageCount + 1,
|
||||
})
|
||||
})
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user