fix: merge develop

This commit is contained in:
Sebastian Rindom
2021-10-15 18:44:19 +02:00
31 changed files with 2145 additions and 1769 deletions
-6
View File
@@ -2,25 +2,19 @@
/packages/medusa/src/services/cart.js
/packages/medusa/src/services/claim-item.js
/packages/medusa/src/services/customer.js
/packages/medusa/src/services/draft-order.js
/packages/medusa/src/services/event-bus.js
/packages/medusa/src/services/fulfillment-provider.js
/packages/medusa/src/services/idempotency-key.js
/packages/medusa/src/services/inventory.js
/packages/medusa/src/services/line-item.js
/packages/medusa/src/services/middleware.js
/packages/medusa/src/services/note.js
/packages/medusa/src/services/notification.js
/packages/medusa/src/services/oauth.js
/packages/medusa/src/services/payment-provider.js
/packages/medusa/src/services/product-collection.js
/packages/medusa/src/services/product-variant.js
/packages/medusa/src/services/product.js
/packages/medusa/src/services/query-builder.js
/packages/medusa/src/services/return-reason.js
/packages/medusa/src/services/return.js
/packages/medusa/src/services/shipping-option.js
/packages/medusa/src/services/shipping-profile.js
/packages/medusa/src/services/store.js
/packages/medusa/src/services/swap.js
+134 -1
View File
@@ -80,7 +80,6 @@ describe("/admin/orders", () => {
await adminSeeder(dbConnection)
await orderSeeder(dbConnection)
await swapSeeder(dbConnection)
await claimSeeder(dbConnection)
} catch (err) {
console.log(err)
throw err
@@ -223,6 +222,48 @@ describe("/admin/orders", () => {
})
})
describe("POST /admin/orders/:id/swaps", () => {
beforeEach(async () => {
try {
await adminSeeder(dbConnection)
await orderSeeder(dbConnection)
await claimSeeder(dbConnection)
} catch (err) {
console.log(err)
throw err
}
})
afterEach(async () => {
const db = useDb()
await db.teardown()
})
it("creates a swap on a claim", async () => {
const api = useApi()
const swapOnSwap = await api.post(
"/admin/orders/order-with-claim/swaps",
{
return_items: [
{
item_id: "test-item-co-2",
quantity: 1,
},
],
additional_items: [{ variant_id: "test-variant", quantity: 1 }],
},
{
headers: {
authorization: "Bearer test_token",
},
}
)
expect(swapOnSwap.status).toEqual(200)
})
})
describe("POST /admin/orders/:id/claims", () => {
beforeEach(async () => {
try {
@@ -318,6 +359,43 @@ describe("/admin/orders", () => {
)
})
it("creates a claim on a claim", async () => {
const api = useApi()
const claimOnClaim = await api
.post(
"/admin/orders/order-with-claim/claims",
{
type: "replace",
claim_items: [
{
item_id: "test-item-co-2",
quantity: 1,
reason: "production_failure",
tags: ["fluff"],
images: ["https://test.image.com"],
},
],
additional_items: [
{
variant_id: "test-variant",
quantity: 1,
},
],
},
{
headers: {
authorization: "Bearer test_token",
},
}
)
.catch((err) => {
console.log(err)
})
expect(claimOnClaim.status).toEqual(200)
})
it("creates a claim with a shipping address", async () => {
const api = useApi()
@@ -841,6 +919,61 @@ describe("/admin/orders", () => {
})
})
describe("POST /admin/orders/:id/claims", () => {
beforeEach(async () => {
try {
await adminSeeder(dbConnection)
await orderSeeder(dbConnection)
await swapSeeder(dbConnection)
} catch (err) {
console.log(err)
throw err
}
})
afterEach(async () => {
const db = useDb()
await db.teardown()
})
it("creates a claim on a swap", async () => {
const api = useApi()
const claimOnClaim = await api
.post(
"/admin/orders/order-with-swap/claims",
{
type: "replace",
claim_items: [
{
item_id: "return-item-1",
quantity: 1,
reason: "production_failure",
tags: ["fluff"],
images: ["https://test.image.com"],
},
],
additional_items: [
{
variant_id: "test-variant",
quantity: 1,
},
],
},
{
headers: {
authorization: "Bearer test_token",
},
}
)
.catch((err) => {
console.log(err)
})
expect(claimOnClaim.status).toEqual(200)
})
})
describe("POST /admin/orders/:id/return", () => {
let rrId
beforeEach(async () => {
@@ -16,20 +16,3 @@ Object {
"updated_at": Any<String>,
}
`;
exports[`/admin/discounts creates store session correctly 1`] = `
Object {
"billing_address_id": null,
"created_at": Any<String>,
"deleted_at": null,
"email": "test@testesen.dk",
"first_name": "test",
"has_account": true,
"id": Any<String>,
"last_name": "testesen",
"metadata": null,
"orders": Array [],
"phone": "12345678",
"updated_at": Any<String>,
}
`;
@@ -159,7 +159,7 @@ describe("/store/carts", () => {
expect.assertions(2)
const api = useApi()
let response = await api
await api
.post("/store/carts/test-cart", {
discounts: [{ code: "SPENT" }],
})
@@ -150,6 +150,17 @@ module.exports = async (connection, data = {}) => {
await manager.save(d)
const usedDiscount = manager.create(Discount, {
id: "used-discount",
code: "USED",
is_dynamic: false,
is_disabled: false,
usage_limit: 1,
usage_count: 1,
})
await manager.save(usedDiscount)
const expiredRule = manager.create(DiscountRule, {
id: "expiredRule",
description: "expired rule",
+3 -3
View File
@@ -8,15 +8,15 @@
"build": "babel src -d dist --extensions \".ts,.js\""
},
"dependencies": {
"@medusajs/medusa": "1.1.41-dev-1634220658423",
"medusa-interfaces": "1.1.23-dev-1634220658423",
"@medusajs/medusa": "1.1.41-dev-1634316075104",
"medusa-interfaces": "1.1.23-dev-1634316075104",
"typeorm": "^0.2.31"
},
"devDependencies": {
"@babel/cli": "^7.12.10",
"@babel/core": "^7.12.10",
"@babel/node": "^7.12.10",
"babel-preset-medusa-package": "1.1.15-dev-1634220658423",
"babel-preset-medusa-package": "1.1.15-dev-1634316075104",
"jest": "^26.6.3"
}
}
File diff suppressed because it is too large Load Diff
@@ -1,88 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports["default"] = void 0;
var _medusaInterfaces = require("medusa-interfaces");
function _instanceof(left, right) { if (right != null && typeof Symbol !== "undefined" && right[Symbol.hasInstance]) { return !!right[Symbol.hasInstance](left); } else { return left instanceof right; } }
function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
function _classCallCheck(instance, Constructor) { if (!_instanceof(instance, Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }
function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
function _createSuper(Derived) { return function () { var Super = _getPrototypeOf(Derived), result; if (_isNativeReflectConstruct()) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }
function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); }
function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }
function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } }
function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
var ManualFulfillmentService = /*#__PURE__*/function (_FulfillmentService) {
_inherits(ManualFulfillmentService, _FulfillmentService);
var _super = _createSuper(ManualFulfillmentService);
function ManualFulfillmentService() {
_classCallCheck(this, ManualFulfillmentService);
return _super.call(this);
}
_createClass(ManualFulfillmentService, [{
key: "getFulfillmentOptions",
value: function getFulfillmentOptions() {
return [{
id: "manual-fulfillment"
}];
}
}, {
key: "validateFulfillmentData",
value: function validateFulfillmentData(data, cart) {
return data;
}
}, {
key: "validateOption",
value: function validateOption(data) {
return true;
}
}, {
key: "canCalculate",
value: function canCalculate() {
return false;
}
}, {
key: "calculatePrice",
value: function calculatePrice() {
throw Error("Manual Fulfillment service cannot calculatePrice");
}
}, {
key: "createOrder",
value: function createOrder() {
// No data is being sent anywhere
return;
}
}]);
return ManualFulfillmentService;
}(_medusaInterfaces.FulfillmentService);
_defineProperty(ManualFulfillmentService, "identifier", "manual");
var _default = ManualFulfillmentService;
exports["default"] = _default;
@@ -0,0 +1,10 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.ContentfulMock = void 0;
var ContentfulMock = {
createClient: jest.fn()
};
exports.ContentfulMock = ContentfulMock;
@@ -0,0 +1,6 @@
export const createClient = jest.fn()
const mock = jest.fn().mockImplementation(() => {
return { createClient }
})
export default mock
@@ -0,0 +1,179 @@
import ContentfulService from "../contentful"
describe("ContentfulService", () => {
describe("delete in medusa", () => {
const regionService = {
retrieve: jest.fn((id) => {
if (id === "exists") {
return Promise.resolve({ id: "exists" })
}
return Promise.resolve(undefined)
}),
}
const productService = {
retrieve: jest.fn((id) => {
if (id === "exists") {
return Promise.resolve({ id: "exists" })
}
return Promise.resolve(undefined)
}),
}
const redisClient = {
get: async (id) => {
// const key = `${id}_ignore_${side}`
if (id === `ignored_ignore_contentful`) {
return { id }
}
return undefined
},
set: async (id) => {
return undefined
},
}
const productVariantService = {
retrieve: jest.fn((id) => {
if (id === "exists") {
return Promise.resolve({ id: "exists" })
}
return Promise.resolve(undefined)
}),
}
const eventBusService = {}
const service = new ContentfulService(
{
regionService,
productService,
redisClient,
productVariantService,
eventBusService,
},
{
space_id: "test_id",
environment: "master",
access_token: "test_token",
}
)
const entry = {
unpublish: jest.fn(async () => {
return {
id: "id",
}
}),
archive: jest.fn(async () => {
return {
id: "id",
}
}),
}
service.contentful_ = {
getSpace: async (space_id) => {
return {
getEnvironment: async (env) => {
return {
getEntry: async (id) => {
if (id === "onlyMedusa") {
throw new Error("doesn't exist")
}
return entry
},
}
},
}
},
}
beforeEach(() => {
jest.clearAllMocks()
})
describe("archiveProductInContentful", () => {
it("Calls entry.unpublish and entry.archive", async () => {
await service.archiveProductInContentful({ id: "test" })
expect(entry.unpublish).toHaveBeenCalledTimes(1)
expect(entry.archive).toHaveBeenCalledTimes(1)
})
it("Doesn't call entry.unpublish and entry.archive if the product still exists in medusa", async () => {
await service.archiveProductInContentful({ id: "exists" })
expect(entry.unpublish).toHaveBeenCalledTimes(0)
expect(entry.archive).toHaveBeenCalledTimes(0)
})
it("Doesn't call productService if request should be ignored", async () => {
await service.archiveProductInContentful({ id: "ignored" })
expect(productService.retrieve).toHaveBeenCalledTimes(0)
expect(entry.unpublish).toHaveBeenCalledTimes(0)
expect(entry.archive).toHaveBeenCalledTimes(0)
})
})
describe("archiveProductVariantInContentful", () => {
it("Calls entry.unpublish and entry.archive", async () => {
await service.archiveProductVariantInContentful({ id: "test" })
expect(entry.unpublish).toHaveBeenCalledTimes(1)
expect(entry.archive).toHaveBeenCalledTimes(1)
})
it("Doesn't call entry.unpublish and entry.archive if the variant still exists in medusa", async () => {
await service.archiveProductVariantInContentful({ id: "exists" })
expect(entry.unpublish).toHaveBeenCalledTimes(0)
expect(entry.archive).toHaveBeenCalledTimes(0)
})
it("Doesn't call productVariantService if request should be ignored", async () => {
await service.archiveProductVariantInContentful({ id: "ignored" })
expect(productVariantService.retrieve).toHaveBeenCalledTimes(0)
expect(entry.unpublish).toHaveBeenCalledTimes(0)
expect(entry.archive).toHaveBeenCalledTimes(0)
})
})
describe("archiveRegionInContentful", () => {
it("Calls entry.unpublish and entry.archive", async () => {
await service.archiveRegionInContentful({ id: "test" })
expect(entry.unpublish).toHaveBeenCalledTimes(1)
expect(entry.archive).toHaveBeenCalledTimes(1)
})
it("Doesn't call entry.unpublish and entry.archive if the region still exists in medusa", async () => {
await service.archiveRegionInContentful({ id: "exists" })
expect(entry.unpublish).toHaveBeenCalledTimes(0)
expect(entry.archive).toHaveBeenCalledTimes(0)
})
it("Doesn't call RegionService if request should be ignored", async () => {
await service.archiveRegionInContentful({ id: "ignored" })
expect(regionService.retrieve).toHaveBeenCalledTimes(0)
expect(entry.unpublish).toHaveBeenCalledTimes(0)
expect(entry.archive).toHaveBeenCalledTimes(0)
})
})
describe("archiveEntryWidthId", () => {
it("Calls archive if entry exists", async () => {
await service.archiveEntryWidthId("exists")
expect(entry.unpublish).toHaveBeenCalledTimes(1)
expect(entry.archive).toHaveBeenCalledTimes(1)
})
it("Doesnt call archive if entry doesn't exists", async () => {
await service.archiveEntryWidthId("onlyMedusa")
expect(entry.unpublish).toHaveBeenCalledTimes(0)
expect(entry.archive).toHaveBeenCalledTimes(0)
})
})
})
})
@@ -91,7 +91,7 @@ class ContentfulService extends BaseService {
async createImageAssets(product) {
const environment = await this.getContentfulEnvironment_()
let assets = []
const assets = []
await Promise.all(
product.images
.filter((image) => image.url !== product.thumbnail)
@@ -646,6 +646,92 @@ class ContentfulService extends BaseService {
}
}
async archiveProductVariantInContentful(variant) {
let variantEntity
try {
const ignore = await this.shouldIgnore_(variant.id, "contentful")
if (ignore) {
return Promise.resolve()
}
try {
variantEntity = await this.productVariantService_.retrieve(variant.id)
} catch (err) {
// ignore
}
if (variantEntity) {
return Promise.resolve()
}
return await this.archiveEntryWidthId(variant.id)
} catch (error) {
throw error
}
}
async archiveProductInContentful(product) {
let productEntity
try {
const ignore = await this.shouldIgnore_(product.id, "contentful")
if (ignore) {
return Promise.resolve()
}
try {
productEntity = await this.productService_.retrieve(product.id)
} catch (err) {}
if (productEntity) {
return Promise.resolve()
}
return await this.archiveEntryWidthId(product.id)
} catch (error) {
throw error
}
}
async archiveRegionInContentful(region) {
let regionEntity
try {
const ignore = await this.shouldIgnore_(region.id, "contentful")
if (ignore) {
return Promise.resolve()
}
try {
regionEntity = await this.regionService_.retrieve(region.id)
} catch (err) {}
if (regionEntity) {
return Promise.resolve()
}
return await this.archiveEntryWidthId(region.id)
} catch (error) {
throw error
}
}
async archiveEntryWidthId(id) {
const environment = await this.getContentfulEnvironment_()
// check if product exists
let entry = undefined
try {
entry = await environment.getEntry(id)
} catch (error) {
return Promise.resolve()
}
const unpublishEntry = await entry.unpublish()
const archivedEntry = await entry.archive()
await this.addIgnore_(id, "medusa")
return archivedEntry
}
async sendContentfulProductToAdmin(productId) {
const ignore = await this.shouldIgnore_(productId, "medusa")
if (ignore) {
@@ -658,7 +744,7 @@ class ContentfulService extends BaseService {
const product = await this.productService_.retrieve(productId)
let update = {}
const update = {}
const title =
productEntry.fields[this.getCustomField("title", "product")]["en-US"]
@@ -741,9 +827,9 @@ class ContentfulService extends BaseService {
isArray = false
}
let output = []
const output = []
for (const obj of input) {
let transformed = Object.assign({}, obj)
const transformed = Object.assign({}, obj)
transformed.medusaId = obj.id
output.push(transformed)
}
@@ -18,10 +18,18 @@ class ContentfulSubscriber {
await this.contentfulService_.updateRegionInContentful(data)
})
this.eventBus_.subscribe("region.deleted", async (data) => {
await this.contentfulService_.updateRegionInContentful(data)
})
this.eventBus_.subscribe("product-variant.updated", async (data) => {
await this.contentfulService_.updateProductVariantInContentful(data)
})
this.eventBus_.subscribe("product-variant.deleted", async (data) => {
await this.contentfulService_.archiveProductVariantInContentful(data)
})
this.eventBus_.subscribe("product.updated", async (data) => {
await this.contentfulService_.updateProductInContentful(data)
})
@@ -29,6 +37,10 @@ class ContentfulSubscriber {
this.eventBus_.subscribe("product.created", async (data) => {
await this.contentfulService_.createProductInContentful(data)
})
this.eventBus_.subscribe("product.deleted", async (data) => {
await this.contentfulService_.archiveProductInContentful(data)
})
}
}
@@ -27,6 +27,7 @@ const defaultRelations = [
"claims.additional_items",
"claims.fulfillments",
"claims.claim_items",
"claims.claim_items.item",
"claims.claim_items.images",
"swaps",
"swaps.return_order",
@@ -56,6 +57,7 @@ const defaultFields = [
"metadata",
"items.refundable",
"swaps.additional_items.refundable",
"claims.additional_items.refundable",
"shipping_total",
"discount_total",
"tax_total",
@@ -241,6 +241,7 @@ export const defaultRelations = [
"claims.additional_items",
"claims.fulfillments",
"claims.claim_items",
"claims.claim_items.item",
"claims.claim_items.images",
// "claims.claim_items.tags",
"swaps",
@@ -271,6 +272,7 @@ export const defaultFields = [
"metadata",
"items.refundable",
"swaps.additional_items.refundable",
"claims.additional_items.refundable",
"shipping_total",
"discount_total",
"tax_total",
+56 -48
View File
@@ -6,29 +6,29 @@ import { MedusaError } from "medusa-core-utils"
const eventBusService = {
emit: jest.fn(),
withTransaction: function() {
withTransaction: function () {
return this
},
}
describe("CartService", () => {
const totalsService = {
getTotal: o => {
getTotal: (o) => {
return o.total || 0
},
getSubtotal: o => {
getSubtotal: (o) => {
return o.subtotal || 0
},
getTaxTotal: o => {
getTaxTotal: (o) => {
return o.tax_total || 0
},
getDiscountTotal: o => {
getDiscountTotal: (o) => {
return o.discount_total || 0
},
getShippingTotal: o => {
getShippingTotal: (o) => {
return o.shipping_total || 0
},
getGiftCardTotal: o => {
getGiftCardTotal: (o) => {
return o.gift_card_total || 0
},
}
@@ -117,7 +117,7 @@ describe("CartService", () => {
describe("deleteMetadata", () => {
const cartRepository = MockRepository({
findOne: id => {
findOne: (id) => {
if (id === "empty") {
return Promise.resolve({
metadata: {},
@@ -200,7 +200,7 @@ describe("CartService", () => {
},
}
const addressRepository = MockRepository({ create: c => c })
const addressRepository = MockRepository({ create: (c) => c })
const cartRepository = MockRepository()
const customerService = {
retrieveByEmail: jest.fn().mockReturnValue(
@@ -209,7 +209,7 @@ describe("CartService", () => {
email: "email@test.com",
})
),
withTransaction: function() {
withTransaction: function () {
return this
},
}
@@ -317,14 +317,14 @@ describe("CartService", () => {
const lineItemService = {
update: jest.fn(),
create: jest.fn(),
withTransaction: function() {
withTransaction: function () {
return this
},
}
const shippingOptionService = {
deleteShippingMethod: jest.fn(),
withTransaction: function() {
withTransaction: function () {
return this
},
}
@@ -505,7 +505,7 @@ describe("CartService", () => {
const lineItemService = {
delete: jest.fn(),
update: jest.fn(),
withTransaction: function() {
withTransaction: function () {
return this
},
}
@@ -547,7 +547,7 @@ describe("CartService", () => {
const shippingOptionService = {
deleteShippingMethod: jest.fn(),
withTransaction: function() {
withTransaction: function () {
return this
},
}
@@ -666,7 +666,7 @@ describe("CartService", () => {
describe("updateLineItem", () => {
const lineItemService = {
update: jest.fn(),
withTransaction: function() {
withTransaction: function () {
return this
},
}
@@ -674,7 +674,7 @@ describe("CartService", () => {
...InventoryServiceMock,
confirmInventory: jest
.fn()
.mockImplementation(id => id !== IdMap.getId("cannot-cover")),
.mockImplementation((id) => id !== IdMap.getId("cannot-cover")),
}
const cartRepository = MockRepository({
@@ -747,7 +747,7 @@ describe("CartService", () => {
describe("updateEmail", () => {
const customerService = {
retrieveByEmail: jest.fn().mockImplementation(email => {
retrieveByEmail: jest.fn().mockImplementation((email) => {
if (email === "no@mail.com") {
return Promise.reject()
}
@@ -756,13 +756,13 @@ describe("CartService", () => {
email,
})
}),
create: jest.fn().mockImplementation(data =>
create: jest.fn().mockImplementation((data) =>
Promise.resolve({
id: IdMap.getId("newCus"),
email: data.email,
})
),
withTransaction: function() {
withTransaction: function () {
return this
},
}
@@ -847,7 +847,7 @@ describe("CartService", () => {
}),
})
const addressRepository = MockRepository({ create: c => c })
const addressRepository = MockRepository({ create: (c) => c })
const cartService = new CartService({
manager: MockManager,
@@ -908,7 +908,7 @@ describe("CartService", () => {
region: { countries: [{ iso_2: "us" }] },
}),
})
const addressRepository = MockRepository({ create: c => c })
const addressRepository = MockRepository({ create: (c) => c })
const cartService = new CartService({
manager: MockManager,
@@ -982,15 +982,15 @@ describe("CartService", () => {
describe("setRegion", () => {
const lineItemService = {
update: jest.fn(r => r),
update: jest.fn((r) => r),
delete: jest.fn(),
withTransaction: function() {
withTransaction: function () {
return this
},
}
const addressRepository = MockRepository({ create: c => c })
const addressRepository = MockRepository({ create: (c) => c })
const productVariantService = {
getRegionPrice: jest.fn().mockImplementation(id => {
getRegionPrice: jest.fn().mockImplementation((id) => {
if (id === IdMap.getId("fail")) {
return Promise.reject()
}
@@ -1034,7 +1034,7 @@ describe("CartService", () => {
deleteSession: jest.fn(),
updateSession: jest.fn(),
createSession: jest.fn(),
withTransaction: function() {
withTransaction: function () {
return this
},
}
@@ -1238,7 +1238,7 @@ describe("CartService", () => {
deleteSession: jest.fn(),
updateSession: jest.fn(),
createSession: jest.fn(),
withTransaction: function() {
withTransaction: function () {
return this
},
}
@@ -1352,7 +1352,7 @@ describe("CartService", () => {
const buildCart = (id, config = {}) => {
return {
id: IdMap.getId(id),
items: (config.items || []).map(i => ({
items: (config.items || []).map((i) => ({
id: IdMap.getId(i.id),
variant: {
product: {
@@ -1360,7 +1360,7 @@ describe("CartService", () => {
},
},
})),
shipping_methods: (config.shipping_methods || []).map(m => ({
shipping_methods: (config.shipping_methods || []).map((m) => ({
id: IdMap.getId(m.id),
shipping_option: {
profile_id: IdMap.getId(m.profile),
@@ -1396,12 +1396,12 @@ describe("CartService", () => {
const lineItemService = {
update: jest.fn(),
withTransaction: function() {
withTransaction: function () {
return this
},
}
const shippingOptionService = {
createShippingMethod: jest.fn().mockImplementation(id => {
createShippingMethod: jest.fn().mockImplementation((id) => {
return Promise.resolve({
shipping_option: {
profile_id: id,
@@ -1409,7 +1409,7 @@ describe("CartService", () => {
})
}),
deleteShippingMethod: jest.fn(),
withTransaction: function() {
withTransaction: function () {
return this
},
}
@@ -1453,9 +1453,11 @@ describe("CartService", () => {
IdMap.getId("option"),
data
)
expect(
shippingOptionService.createShippingMethod
).toHaveBeenCalledWith(IdMap.getId("option"), data, { cart: cart1 })
expect(shippingOptionService.createShippingMethod).toHaveBeenCalledWith(
IdMap.getId("option"),
data,
{ cart: cart1 }
)
})
it("successfully overrides existing profile shipping method", async () => {
@@ -1467,9 +1469,11 @@ describe("CartService", () => {
IdMap.getId("profile1"),
data
)
expect(
shippingOptionService.createShippingMethod
).toHaveBeenCalledWith(IdMap.getId("profile1"), data, { cart: cart2 })
expect(shippingOptionService.createShippingMethod).toHaveBeenCalledWith(
IdMap.getId("profile1"),
data,
{ cart: cart2 }
)
expect(shippingOptionService.deleteShippingMethod).toHaveBeenCalledWith({
id: IdMap.getId("ship1"),
shipping_option: {
@@ -1495,9 +1499,11 @@ describe("CartService", () => {
expect(shippingOptionService.createShippingMethod).toHaveBeenCalledTimes(
1
)
expect(
shippingOptionService.createShippingMethod
).toHaveBeenCalledWith(IdMap.getId("additional"), data, { cart: cart2 })
expect(shippingOptionService.createShippingMethod).toHaveBeenCalledWith(
IdMap.getId("additional"),
data,
{ cart: cart2 }
)
})
it("updates item shipping", async () => {
@@ -1517,9 +1523,11 @@ describe("CartService", () => {
expect(shippingOptionService.createShippingMethod).toHaveBeenCalledTimes(
1
)
expect(
shippingOptionService.createShippingMethod
).toHaveBeenCalledWith(IdMap.getId("profile1"), data, { cart: cart3 })
expect(shippingOptionService.createShippingMethod).toHaveBeenCalledWith(
IdMap.getId("profile1"),
data,
{ cart: cart3 }
)
expect(lineItemService.update).toHaveBeenCalledTimes(1)
expect(lineItemService.update).toHaveBeenCalledWith(IdMap.getId("line"), {
@@ -1535,7 +1543,7 @@ describe("CartService", () => {
cartService.findCustomShippingOption = jest
.fn()
.mockImplementation(cartCustomShippingOptions => {
.mockImplementation((cartCustomShippingOptions) => {
return {
price: 0,
}
@@ -1550,7 +1558,7 @@ describe("CartService", () => {
IdMap.getId("test-so"),
data,
{
cart: cartWithCustomSO,
cart_id: IdMap.getId("cart-with-custom-so"),
price: 0,
}
)
@@ -1558,7 +1566,7 @@ describe("CartService", () => {
})
describe("applyDiscount", () => {
const getOffsetDate = offset => {
const getOffsetDate = (offset) => {
const date = new Date()
date.setDate(date.getDate() + offset)
return date
@@ -1595,7 +1603,7 @@ describe("CartService", () => {
})
const discountService = {
retrieveByCode: jest.fn().mockImplementation(code => {
retrieveByCode: jest.fn().mockImplementation((code) => {
if (code === "US10") {
return Promise.resolve({
regions: [{ id: IdMap.getId("bad") }],
@@ -415,6 +415,7 @@ describe("ProductService", () => {
const productService = new ProductService({
manager: MockManager,
eventBusService,
productRepository,
})
+32 -30
View File
@@ -5,7 +5,7 @@ import { InventoryServiceMock } from "../__mocks__/inventory"
const eventBusService = {
emit: jest.fn(),
withTransaction: function() {
withTransaction: function () {
return this
},
}
@@ -161,7 +161,7 @@ describe("SwapService", () => {
const cartService = {
create: jest.fn().mockReturnValue(Promise.resolve({ id: "cart" })),
update: jest.fn().mockReturnValue(Promise.resolve()),
withTransaction: function() {
withTransaction: function () {
return this
},
}
@@ -179,10 +179,10 @@ describe("SwapService", () => {
}
const lineItemService = {
create: jest.fn().mockImplementation(d => Promise.resolve(d)),
update: jest.fn().mockImplementation(d => Promise.resolve(d)),
create: jest.fn().mockImplementation((d) => Promise.resolve(d)),
update: jest.fn().mockImplementation((d) => Promise.resolve(d)),
retrieve: () => Promise.resolve({}),
withTransaction: function() {
withTransaction: function () {
return this
},
}
@@ -210,6 +210,8 @@ describe("SwapService", () => {
"order.swaps.additional_items",
"order.discounts",
"order.discounts.rule",
"order.claims",
"order.claims.additional_items",
"additional_items",
"return_order",
"return_order.items",
@@ -315,7 +317,7 @@ describe("SwapService", () => {
const swapRepo = MockRepository()
const returnService = {
create: jest.fn().mockReturnValue(Promise.resolve({ id: "ret" })),
withTransaction: function() {
withTransaction: function () {
return this
},
}
@@ -412,7 +414,7 @@ describe("SwapService", () => {
receiveReturn: jest
.fn()
.mockReturnValue(Promise.resolve({ test: "received" })),
withTransaction: function() {
withTransaction: function () {
return this
},
}
@@ -458,7 +460,7 @@ describe("SwapService", () => {
receiveReturn: jest
.fn()
.mockReturnValue(Promise.resolve({ status: "requires_action" })),
withTransaction: function() {
withTransaction: function () {
return this
},
}
@@ -536,7 +538,7 @@ describe("SwapService", () => {
{ items: [{ item_id: "1234", quantity: 2 }], data: "new" },
])
),
withTransaction: function() {
withTransaction: function () {
return this
},
}
@@ -557,7 +559,7 @@ describe("SwapService", () => {
const lineItemService = {
update: jest.fn(),
retrieve: () => Promise.resolve({}),
withTransaction: function() {
withTransaction: function () {
return this
},
}
@@ -632,11 +634,11 @@ describe("SwapService", () => {
describe("cancelFulfillment", () => {
const swapRepo = MockRepository({
findOneWithRelations: () => Promise.resolve({}),
save: f => Promise.resolve(f),
save: (f) => Promise.resolve(f),
})
const fulfillmentService = {
cancelFulfillment: jest.fn().mockImplementation(f => {
cancelFulfillment: jest.fn().mockImplementation((f) => {
switch (f) {
case IdMap.getId("no-swap"):
return Promise.resolve({})
@@ -646,7 +648,7 @@ describe("SwapService", () => {
})
}
}),
withTransaction: function() {
withTransaction: function () {
return this
},
}
@@ -700,14 +702,14 @@ describe("SwapService", () => {
data: "new",
})
}),
withTransaction: function() {
withTransaction: function () {
return this
},
}
const eventBusService = {
emit: jest.fn().mockReturnValue(Promise.resolve()),
withTransaction: function() {
withTransaction: function () {
return this
},
}
@@ -748,14 +750,14 @@ describe("SwapService", () => {
const lineItemService = {
update: jest.fn(),
retrieve: () => Promise.resolve({}),
withTransaction: function() {
withTransaction: function () {
return this
},
}
const cartService = {
update: jest.fn(),
withTransaction: function() {
withTransaction: function () {
return this
},
}
@@ -831,7 +833,7 @@ describe("SwapService", () => {
const eventBusService = {
emit: jest.fn().mockReturnValue(Promise.resolve()),
withTransaction: function() {
withTransaction: function () {
return this
},
}
@@ -846,7 +848,7 @@ describe("SwapService", () => {
updateShippingMethod: () => {
return Promise.resolve()
},
withTransaction: function() {
withTransaction: function () {
return this
},
}
@@ -855,7 +857,7 @@ describe("SwapService", () => {
update: () => {
return Promise.resolve()
},
withTransaction: function() {
withTransaction: function () {
return this
},
}
@@ -870,14 +872,14 @@ describe("SwapService", () => {
cancelPayment: jest.fn(() => {
return Promise.resolve()
}),
withTransaction: function() {
withTransaction: function () {
return this
},
}
const inventoryService = {
...InventoryServiceMock,
withTransaction: function() {
withTransaction: function () {
return this
},
}
@@ -998,19 +1000,19 @@ describe("SwapService", () => {
describe("success", () => {
const eventBusService = {
emit: jest.fn().mockReturnValue(Promise.resolve()),
withTransaction: function() {
withTransaction: function () {
return this
},
}
const paymentProviderService = {
capturePayment: jest.fn(g =>
capturePayment: jest.fn((g) =>
g.id === "good" ? Promise.resolve() : Promise.reject()
),
refundPayment: jest.fn(g =>
refundPayment: jest.fn((g) =>
g[0].id === "good" ? Promise.resolve() : Promise.reject()
),
withTransaction: function() {
withTransaction: function () {
return this
},
}
@@ -1138,7 +1140,7 @@ describe("SwapService", () => {
const eventBusService = {
emit: jest.fn().mockReturnValue(Promise.resolve()),
withTransaction: function() {
withTransaction: function () {
return this
},
}
@@ -1201,7 +1203,7 @@ describe("SwapService", () => {
const paymentProviderService = {
cancelPayment: jest.fn(() => Promise.resolve({})),
withTransaction: function() {
withTransaction: function () {
return this
},
}
@@ -1233,7 +1235,7 @@ describe("SwapService", () => {
return Promise.resolve(swap)
}
},
save: f => f,
save: (f) => f,
})
const swapService = new SwapService({
@@ -1282,7 +1284,7 @@ describe("SwapService", () => {
it.each([["fail-refund-1"], ["fail-refund-2"], ["fail-refund-3"]])(
"fails to cancel swap when contains refund",
async input => {
async (input) => {
await expect(swapService.cancel(IdMap.getId(input))).rejects.toThrow(
"Swap with a refund cannot be canceled"
)
+34 -33
View File
@@ -7,7 +7,7 @@ import { Brackets, ILike } from "typeorm"
/**
* Provides layer to manipulate customers.
* @implements BaseService
* @implements {BaseService}
*/
class CustomerService extends BaseService {
static Events = {
@@ -60,9 +60,7 @@ class CustomerService extends BaseService {
* @return {string} the validated email
*/
validateEmail_(email) {
const schema = Validator.string()
.email()
.required()
const schema = Validator.string().email().required()
const { value, error } = schema.validate(email)
if (error) {
throw new MedusaError(
@@ -93,7 +91,7 @@ class CustomerService extends BaseService {
* secret a long side a payload with userId and the expiry time for the token,
* which is always 15 minutes.
* @param {string} customerId - the customer to reset the password for
* @returns {string} the generated JSON web token
* @return {string} the generated JSON web token
*/
async generateResetPasswordToken(customerId) {
const customer = await this.retrieve(customerId)
@@ -122,6 +120,7 @@ class CustomerService extends BaseService {
/**
* @param {Object} selector - the query object for find
* @param {Object} config - the config object containing query settings
* @return {Promise} the result of the find operation
*/
async list(selector = {}, config = { relations: [], skip: 0, take: 50 }) {
@@ -144,11 +143,11 @@ class CustomerService extends BaseService {
delete where.first_name
delete where.last_name
query.where = qb => {
query.where = (qb) => {
qb.where(where)
qb.andWhere(
new Brackets(qb => {
new Brackets((qb) => {
qb.where({ email: ILike(`%${q}%`) })
.orWhere({ first_name: ILike(`%${q}%`) })
.orWhere({ last_name: ILike(`%${q}%`) })
@@ -183,11 +182,11 @@ class CustomerService extends BaseService {
delete where.first_name
delete where.last_name
query.where = qb => {
query.where = (qb) => {
qb.where(where)
qb.andWhere(
new Brackets(qb => {
new Brackets((qb) => {
qb.where({ email: ILike(`%${q}%`) })
.orWhere({ first_name: ILike(`%${q}%`) })
.orWhere({ last_name: ILike(`%${q}%`) })
@@ -214,6 +213,7 @@ class CustomerService extends BaseService {
/**
* Gets a customer by id.
* @param {string} customerId - the id of the customer to get.
* @param {Object} config - the config object containing query settings
* @return {Promise<Customer>} the customer document.
*/
async retrieve(customerId, config = {}) {
@@ -238,6 +238,7 @@ class CustomerService extends BaseService {
/**
* Gets a customer by email.
* @param {string} email - the email of the customer to get.
* @param {Object} config - the config object containing query settings
* @return {Promise<Customer>} the customer document.
*/
async retrieveByEmail(email, config = {}) {
@@ -261,6 +262,7 @@ class CustomerService extends BaseService {
/**
* Gets a customer by phone.
* @param {string} phone - the phone of the customer to get.
* @param {Object} config - the config object containing query settings
* @return {Promise<Customer>} the customer document.
*/
async retrieveByPhone(phone, config = {}) {
@@ -284,7 +286,7 @@ class CustomerService extends BaseService {
/**
* Hashes a password
* @param {string} password - the value to hash
* @return hashed password
* @return {string} hashed password
*/
async hashPassword_(password) {
const buf = await Scrypt.kdf(password, { logN: 1, r: 1, p: 1 })
@@ -300,7 +302,7 @@ class CustomerService extends BaseService {
* @return {Promise} the result of create
*/
async create(customer) {
return this.atomicPhase_(async manager => {
return this.atomicPhase_(async (manager) => {
const customerRepository = manager.getCustomRepository(
this.customerRepository_
)
@@ -312,7 +314,9 @@ class CustomerService extends BaseService {
customer.billing_address = this.validateBillingAddress_(billing_address)
}
const existing = await this.retrieveByEmail(email).catch(err => undefined)
const existing = await this.retrieveByEmail(email).catch(
(err) => undefined
)
if (existing && existing.has_account) {
throw new MedusaError(
@@ -353,13 +357,13 @@ class CustomerService extends BaseService {
/**
* Updates a customer.
* @param {string} variantId - the id of the variant. Must be a string that
* @param {string} customerId - the id of the variant. Must be a string that
* can be casted to an ObjectId
* @param {object} update - an object with the update values.
* @return {Promise} resolves to the update result.
*/
async update(customerId, update) {
return this.atomicPhase_(async manager => {
return this.atomicPhase_(async (manager) => {
const customerRepository = manager.getCustomRepository(
this.customerRepository_
)
@@ -367,15 +371,7 @@ class CustomerService extends BaseService {
const customer = await this.retrieve(customerId)
const {
email,
password,
password_hash,
billing_address,
billing_address_id,
metadata,
...rest
} = update
const { email, password, metadata, ...rest } = update
if (metadata) {
customer.metadata = this.setMetadata_(customer, metadata)
@@ -408,9 +404,10 @@ class CustomerService extends BaseService {
}
/**
* Updates the customers's billing address.
* Updates the customers' billing address.
* @param {Customer} customer - the Customer to update
* @param {object} address - the value to set the billing address to
* @param {Object|string} addressOrId - the value to set the billing address to
* @param {Object} addrRepo - address repository
* @return {Promise} the result of the update operation
*/
async updateBillingAddress_(customer, addressOrId, addrRepo) {
@@ -443,7 +440,7 @@ class CustomerService extends BaseService {
}
async updateAddress(customerId, addressId, address) {
return this.atomicPhase_(async manager => {
return this.atomicPhase_(async (manager) => {
const addressRepo = manager.getCustomRepository(this.addressRepository_)
address.country_code = address.country_code.toLowerCase()
@@ -464,7 +461,7 @@ class CustomerService extends BaseService {
}
async removeAddress(customerId, addressId) {
return this.atomicPhase_(async manager => {
return this.atomicPhase_(async (manager) => {
const addressRepo = manager.getCustomRepository(this.addressRepository_)
// Should not fail, if user does not exist, since delete is idempotent
@@ -472,7 +469,9 @@ class CustomerService extends BaseService {
where: { id: addressId, customer_id: customerId },
})
if (!address) return Promise.resolve()
if (!address) {
return Promise.resolve()
}
await addressRepo.softRemove(address)
@@ -481,7 +480,7 @@ class CustomerService extends BaseService {
}
async addAddress(customerId, address) {
return this.atomicPhase_(async manager => {
return this.atomicPhase_(async (manager) => {
const addressRepository = manager.getCustomRepository(
this.addressRepository_
)
@@ -493,8 +492,8 @@ class CustomerService extends BaseService {
})
this.validateBillingAddress_(address)
let shouldAdd = !customer.shipping_addresses.find(
a =>
const shouldAdd = !customer.shipping_addresses.find(
(a) =>
a.country_code.toLowerCase() === address.country_code.toLowerCase() &&
a.address_1 === address.address_1 &&
a.address_2 === address.address_2 &&
@@ -526,13 +525,15 @@ class CustomerService extends BaseService {
* @return {Promise} the result of the delete operation.
*/
async delete(customerId) {
return this.atomicPhase_(async manager => {
return this.atomicPhase_(async (manager) => {
const customerRepo = manager.getCustomRepository(this.customerRepository_)
// Should not fail, if user does not exist, since delete is idempotent
const customer = await customerRepo.findOne({ where: { id: customerId } })
if (!customer) return Promise.resolve()
if (!customer) {
return Promise.resolve()
}
await customerRepo.softRemove(customer)
+13 -12
View File
@@ -1,11 +1,10 @@
import _ from "lodash"
import { BaseService } from "medusa-interfaces"
import { MedusaError } from "medusa-core-utils"
import { Brackets, ILike } from "typeorm"
import { Brackets } from "typeorm"
/**
* Handles draft orders
* @implements BaseService
* @implements {BaseService}
*/
class DraftOrderService extends BaseService {
static Events = {
@@ -106,7 +105,7 @@ class DraftOrderService extends BaseService {
/**
* Retrieves a draft order based on its associated cart id
* @param {string} cartId - cart id that the draft orders's cart has
* * @param {object} config - query object for findOne
* @param {object} config - query object for findOne
* @return {Promise<DraftOrder>} the draft order
*/
async retrieveByCartId(cartId, config = {}) {
@@ -134,7 +133,7 @@ class DraftOrderService extends BaseService {
* @return {Promise} empty promise
*/
async delete(draftOrderId) {
return this.atomicPhase_(async manager => {
return this.atomicPhase_(async (manager) => {
const draftOrderRepo = manager.getCustomRepository(
this.draftOrderRepository_
)
@@ -143,7 +142,9 @@ class DraftOrderService extends BaseService {
where: { id: draftOrderId },
})
if (!draftOrder) return Promise.resolve()
if (!draftOrder) {
return Promise.resolve()
}
await draftOrderRepo.remove(draftOrder)
@@ -185,11 +186,11 @@ class DraftOrderService extends BaseService {
},
}
query.where = qb => {
query.where = (qb) => {
qb.where(where)
qb.andWhere(
new Brackets(qb => {
new Brackets((qb) => {
qb.where(`cart.email ILIKE :q`, {
q: `%${q}%`,
}).orWhere(`draft_order.display_id::varchar(255) ILIKE :dId`, {
@@ -230,7 +231,7 @@ class DraftOrderService extends BaseService {
* @return {Promise<DraftOrder>} the created draft order
*/
async create(data) {
return this.atomicPhase_(async manager => {
return this.atomicPhase_(async (manager) => {
const draftOrderRepo = manager.getCustomRepository(
this.draftOrderRepository_
)
@@ -332,7 +333,7 @@ class DraftOrderService extends BaseService {
* @return {Promise} the created order
*/
async registerCartCompletion(doId, orderId) {
return this.atomicPhase_(async manager => {
return this.atomicPhase_(async (manager) => {
const draftOrderRepo = manager.getCustomRepository(
this.draftOrderRepository_
)
@@ -350,10 +351,10 @@ class DraftOrderService extends BaseService {
* Updates a draft order with the given data
* @param {String} doId - id of the draft order
* @param {DraftOrder} data - values to update the order with
* @returns {Promise<DraftOrder>} the updated draft order
* @return {Promise<DraftOrder>} the updated draft order
*/
async update(doId, data) {
return this.atomicPhase_(async manager => {
return this.atomicPhase_(async (manager) => {
const doRepo = manager.getCustomRepository(this.draftOrderRepository_)
const draftOrder = await this.retrieve(doId)
let touched = false
+10 -8
View File
@@ -1,10 +1,9 @@
import { Validator, MedusaError } from "medusa-core-utils"
import { MedusaError } from "medusa-core-utils"
import { BaseService } from "medusa-interfaces"
import _ from "lodash"
/**
* Provides layer to manipulate line items.
* @implements BaseService
* @extends BaseService
*/
class LineItemService extends BaseService {
constructor({
@@ -67,6 +66,7 @@ class LineItemService extends BaseService {
/**
* Retrieves a line item by its id.
* @param {string} id - the id of the line item to retrieve
* @param {object} config - the config to be used at query building
* @return {LineItem} the line item
*/
async retrieve(id, config = {}) {
@@ -90,7 +90,7 @@ class LineItemService extends BaseService {
}
async generate(variantId, regionId, quantity, config = {}) {
return this.atomicPhase_(async manager => {
return this.atomicPhase_(async (manager) => {
const variant = await this.productVariantService_
.withTransaction(manager)
.retrieve(variantId, {
@@ -142,7 +142,7 @@ class LineItemService extends BaseService {
* @return {LineItem} the created line item
*/
async create(lineItem) {
return this.atomicPhase_(async manager => {
return this.atomicPhase_(async (manager) => {
const lineItemRepository = manager.getCustomRepository(
this.lineItemRepository_
)
@@ -160,7 +160,7 @@ class LineItemService extends BaseService {
* @return {LineItem} the update line item
*/
async update(id, update) {
return this.atomicPhase_(async manager => {
return this.atomicPhase_(async (manager) => {
const lineItemRepository = manager.getCustomRepository(
this.lineItemRepository_
)
@@ -188,14 +188,16 @@ class LineItemService extends BaseService {
* @return {Promise} the result of the delete operation
*/
async delete(id) {
return this.atomicPhase_(async manager => {
return this.atomicPhase_(async (manager) => {
const lineItemRepository = manager.getCustomRepository(
this.lineItemRepository_
)
const lineItem = await lineItemRepository.findOne({ where: { id } })
if (!lineItem) return Promise.resolve()
if (!lineItem) {
return Promise.resolve()
}
await lineItemRepository.remove(lineItem)
+10 -6
View File
@@ -1,10 +1,9 @@
import { MedusaError } from "medusa-core-utils"
import { BaseService } from "medusa-interfaces"
import _ from "lodash"
/**
* Provides layer to manipulate orchestrate notifications.
* @implements BaseService
* @extends BaseService
*/
class NotificationService extends BaseService {
constructor(container) {
@@ -34,6 +33,7 @@ class NotificationService extends BaseService {
/**
* Registers an attachment generator to the service. The generator can be
* used to generate on demand invoices or other documents.
* @param {object} service
*/
registerAttachmentGenerator(service) {
this.attachmentGenerator_ = service
@@ -42,16 +42,18 @@ class NotificationService extends BaseService {
/**
* Sets the service's manager to a given transaction manager.
* @param {EntityManager} transactionManager - the manager to use
* return {NotificationService} a cloned notification service
* @return {NotificationService} a cloned notification service
*/
withTransaction(transactionManager) {
if (!transactionManager) {
return this
}
const cloned = new LineItemService({
const cloned = new NotificationService({
manager: transactionManager,
notificationProviderRepository: this.notificationProviderRepository_,
notificationRepository: this.notificationRepository_,
logger: this.logger_,
})
cloned.transactionManager_ = transactionManager
@@ -93,6 +95,7 @@ class NotificationService extends BaseService {
/**
* Retrieves a notification with a given id
* @param {string} id - the id of the notification
* @param {object} config - the configuration to apply to the query
* @return {Notification} the notification
*/
async retrieve(id, config = {}) {
@@ -158,6 +161,7 @@ class NotificationService extends BaseService {
* order to allow for resends. Will log any errors that are encountered.
* @param {string} eventName - the event to handle
* @param {object} data - the data the event was sent with
* @return {Promise} - the result of notification subscribed
*/
handleEvent(eventName, data) {
const subs = this.subscribers_[eventName]
@@ -169,8 +173,8 @@ class NotificationService extends BaseService {
}
return Promise.all(
subs.map(async providerId => {
return this.send(eventName, data, providerId).catch(err => {
subs.map(async (providerId) => {
return this.send(eventName, data, providerId).catch((err) => {
console.log(err)
this.logger_.warn(
`An error occured while ${providerId} was processing a notification for ${eventName}: ${err.message}`
+19
View File
@@ -286,6 +286,7 @@ class OrderService extends BaseService {
"refundable_amount",
"items.refundable",
"swaps.additional_items.refundable",
"claims.additional_items.refundable",
]
const totalsToSelect = select.filter((v) => totalFields.includes(v))
@@ -294,6 +295,8 @@ class OrderService extends BaseService {
relationSet.add("items")
relationSet.add("swaps")
relationSet.add("swaps.additional_items")
relationSet.add("claims")
relationSet.add("claims.additional_items")
relationSet.add("discounts")
relationSet.add("discounts.rule")
relationSet.add("discounts.rule.valid_for")
@@ -1407,6 +1410,22 @@ class OrderService extends BaseService {
}
}
if (
totalsFields.includes("claims.additional_items.refundable") &&
order.claims &&
order.claims.length
) {
for (const c of order.claims) {
c.additional_items = c.additional_items.map((i) => ({
...i,
refundable: this.totalsService_.getLineItemRefund(order, {
...i,
quantity: i.quantity - (i.returned_quantity || 0),
}),
}))
}
}
return order
}
@@ -11,6 +11,7 @@ class ProductVariantService extends BaseService {
static Events = {
UPDATED: "product-variant.updated",
CREATED: "product-variant.created",
DELETED: "product-variant.deleted",
}
/** @param { productVariantModel: (ProductVariantModel) } */
@@ -588,6 +589,12 @@ class ProductVariantService extends BaseService {
await variantRepo.softRemove(variant)
await this.eventBus_
.withTransaction(manager)
.emit(ProductVariantService.Events.DELETED, {
id: variantId,
})
return Promise.resolve()
})
}
+7
View File
@@ -11,6 +11,7 @@ class ProductService extends BaseService {
static Events = {
UPDATED: "product.updated",
CREATED: "product.created",
DELETED: "product.deleted",
}
constructor({
@@ -475,6 +476,12 @@ class ProductService extends BaseService {
await productRepo.softRemove(product)
await this.eventBus_
.withTransaction(manager)
.emit(ProductService.Events.DELETED, {
id: productId,
})
return Promise.resolve()
})
}
+7
View File
@@ -10,6 +10,7 @@ class RegionService extends BaseService {
static Events = {
UPDATED: "region.updated",
CREATED: "region.created",
DELETED: "region.deleted",
}
constructor({
@@ -389,6 +390,12 @@ class RegionService extends BaseService {
await regionRepo.softRemove(region)
await this.eventBus_
.withTransaction(manager)
.emit(RegionService.Events.DELETED, {
id: regionId,
})
return Promise.resolve()
})
}
@@ -1,6 +1,5 @@
import { Validator, MedusaError } from "medusa-core-utils"
import { MedusaError } from "medusa-core-utils"
import { BaseService } from "medusa-interfaces"
import { In } from "typeorm"
class ReturnReasonService extends BaseService {
constructor({ manager, returnReasonRepository }) {
@@ -29,7 +28,7 @@ class ReturnReasonService extends BaseService {
}
create(data) {
return this.atomicPhase_(async manager => {
return this.atomicPhase_(async (manager) => {
const rrRepo = manager.getCustomRepository(this.retReasonRepo_)
if (data.parent_return_reason_id && data.parent_return_reason_id !== "") {
@@ -51,7 +50,7 @@ class ReturnReasonService extends BaseService {
}
update(id, data) {
return this.atomicPhase_(async manager => {
return this.atomicPhase_(async (manager) => {
const rrRepo = manager.getCustomRepository(this.retReasonRepo_)
const reason = await this.retrieve(id)
@@ -77,6 +76,7 @@ class ReturnReasonService extends BaseService {
/**
* @param {Object} selector - the query object for find
* @param {Object} config - config object
* @return {Promise} the result of the find operation
*/
async list(
@@ -90,7 +90,8 @@ class ReturnReasonService extends BaseService {
/**
* Gets an order by id.
* @param {string} orderId - id of order to retrieve
* @param {string} id - id of order to retrieve
* @param {Object} config - config object
* @return {Promise<Order>} the order document
*/
async retrieve(id, config = {}) {
@@ -111,7 +112,7 @@ class ReturnReasonService extends BaseService {
}
async delete(returnReasonId) {
return this.atomicPhase_(async manager => {
return this.atomicPhase_(async (manager) => {
const rrRepo = manager.getCustomRepository(this.retReasonRepo_)
// We include the relation 'return_reason_children' to enable cascading deletes of return reasons if a parent is removed
+23 -6
View File
@@ -93,6 +93,12 @@ class ReturnService extends BaseService {
}
}
if (order.claims && order.claims.length) {
for (const c of order.claims) {
merged = [...merged, ...c.additional_items]
}
}
const toReturn = await Promise.all(
items.map(async data => {
const item = merged.find(i => i.id === data.item_id)
@@ -325,7 +331,13 @@ class ReturnService extends BaseService {
.withTransaction(manager)
.retrieve(orderId, {
select: ["refunded_total", "total", "refundable_amount"],
relations: ["swaps", "swaps.additional_items", "items"],
relations: [
"swaps",
"swaps.additional_items",
"claims",
"claims.additional_items",
"items",
],
})
const returnLines = await this.getFulfillmentItems_(
@@ -487,13 +499,18 @@ class ReturnService extends BaseService {
* @param {string[]} lineItems - the line items to return
* @return {Promise} the result of the update operation
*/
async receive(returnId, receivedItems, refundAmount, allowMismatch = false) {
async receive(
return_id,
received_items,
refund_amount,
allow_mismatch = false
) {
return this.atomicPhase_(async manager => {
const returnRepository = manager.getCustomRepository(
this.returnRepository_
)
const returnObj = await this.retrieve(returnId, {
const returnObj = await this.retrieve(return_id, {
relations: ["items", "swap", "swap.additional_items"],
})
@@ -537,7 +554,7 @@ class ReturnService extends BaseService {
const returnLines = await this.getFulfillmentItems_(
order,
receivedItems,
received_items,
this.validateReturnLineItem_
)
@@ -566,12 +583,12 @@ class ReturnService extends BaseService {
let returnStatus = "received"
const isMatching = newLines.every(l => l.is_requested)
if (!isMatching && !allowMismatch) {
if (!isMatching && !allow_mismatch) {
// Should update status
returnStatus = "requires_action"
}
const totalRefundableAmount = refundAmount || returnObj.refund_amount
const totalRefundableAmount = refund_amount || returnObj.refund_amount
const now = new Date()
const updateObj = {
+17 -16
View File
@@ -1,11 +1,9 @@
import _ from "lodash"
import { MedusaError } from "medusa-core-utils"
import { BaseService } from "medusa-interfaces"
import { In } from "typeorm"
/**
* Provides layer to manipulate profiles.
* @implements BaseService
* @extends BaseService
*/
class ShippingOptionService extends BaseService {
constructor({
@@ -64,6 +62,7 @@ class ShippingOptionService extends BaseService {
/**
* Validates a requirement
* @param {ShippingRequirement} requirement - the requirement to validate
* @param {string} optionId - the id to validate the requirement
* @return {ShippingRequirement} a validated shipping requirement
*/
async validateRequirement_(requirement, optionId) {
@@ -123,6 +122,7 @@ class ShippingOptionService extends BaseService {
/**
* @param {Object} selector - the query object for find
* @param {object} config - config object
* @return {Promise} the result of the find operation
*/
async list(selector, config = { skip: 0, take: 50 }) {
@@ -136,6 +136,7 @@ class ShippingOptionService extends BaseService {
* Gets a profile by id.
* Throws in case of DB Error and if profile was not found.
* @param {string} optionId - the id of the profile to get.
* @param {object} options - the options to get a profile
* @return {Promise<Product>} the profile document.
*/
async retrieve(optionId, options = {}) {
@@ -171,7 +172,7 @@ class ShippingOptionService extends BaseService {
* and its methods should be copied to an order/swap entity.
* @param {string} id - the id of the shipping method to update
* @param {object} update - the values to update the method with
* @returns {Promise<ShippingMethod>} the resulting shipping method
* @return {Promise<ShippingMethod>} the resulting shipping method
*/
async updateShippingMethod(id, update) {
return this.atomicPhase_(async (manager) => {
@@ -200,7 +201,7 @@ class ShippingOptionService extends BaseService {
/**
* Removes a given shipping method
* @param {string} id - the id of the option to use for the method.
* @param {string} sm - the shipping method to remove
*/
async deleteShippingMethod(sm) {
return this.atomicPhase_(async (manager) => {
@@ -214,7 +215,7 @@ class ShippingOptionService extends BaseService {
* @param {string} optionId - the id of the option to use for the method.
* @param {object} data - the optional provider data to use.
* @param {object} config - the cart to create the shipping method for.
* @returns {ShippingMethod} the resulting shipping method.
* @return {ShippingMethod} the resulting shipping method.
*/
async createShippingMethod(optionId, data, config) {
return this.atomicPhase_(async (manager) => {
@@ -290,7 +291,7 @@ class ShippingOptionService extends BaseService {
* Checks if a given option id is a valid option for a cart. If it is the
* option is returned with the correct price. Throws when region_ids do not
* match, or when the shipping option requirements are not satisfied.
* @param {string} optionId - the id of the option to check
* @param {object} option - the option object to check
* @param {Cart} cart - the cart object to check against
* @return {ShippingOption} the validated shipping option
*/
@@ -332,7 +333,7 @@ class ShippingOptionService extends BaseService {
* Creates a new shipping option. Used both for outbound and inbound shipping
* options. The difference is registered by the `is_return` field which
* defaults to false.
* @param {ShippingOption} option - the shipping option to create
* @param {ShippingOption} data - the data to create shipping options
* @return {Promise<ShippingOption>} the result of the create operation
*/
async create(data) {
@@ -412,7 +413,7 @@ class ShippingOptionService extends BaseService {
/**
* Validates a shipping option price
* @param {ShippingOptionPrice} price - the price to validate
* @param {ShippingOptionPrice} priceType - the price to validate
* @param {ShippingOption} option - the option to validate against
* @return {Promise<ShippingOptionPrice>} the validated price
*/
@@ -556,7 +557,7 @@ class ShippingOptionService extends BaseService {
*/
async delete(optionId) {
try {
let option = await this.retrieve(optionId)
const option = await this.retrieve(optionId)
const optionRepo = this.manager_.getCustomRepository(
this.optionRepository_
@@ -628,7 +629,7 @@ class ShippingOptionService extends BaseService {
/**
* Decorates a shipping option.
* @param {ShippingOption} shippingOption - the shipping option to decorate.
* @param {ShippingOption} optionId - the shipping option to decorate using optionId.
* @param {string[]} fields - the fields to include.
* @param {string[]} expandFields - fields to expand.
* @return {ShippingOption} the decorated ShippingOption.
@@ -648,9 +649,8 @@ class ShippingOptionService extends BaseService {
/**
* Dedicated method to set metadata for a shipping option.
* @param {string} optionId - the option to set metadata for.
* @param {string} key - key for metadata field
* @param {string} value - value for metadata field.
* @param {object} option - the option to set metadata for.
* @param {object} metadata - object for metadata field
* @return {Promise} resolves to the updated result.
*/
async setMetadata_(option, metadata) {
@@ -681,9 +681,10 @@ class ShippingOptionService extends BaseService {
* price type "calculated".
* @param {ShippingOption} option - the shipping option to retrieve the price
* for.
* @param {Cart || Order} cart - the context in which the price should be
* @param {ShippingData} data - the shipping data to retrieve the price.
* @param {Cart | Order} cart - the context in which the price should be
* retrieved.
* @returns {Promise<Number>} the price of the shipping option.
* @return {Promise<Number>} the price of the shipping option.
*/
async getPrice_(option, data, cart) {
if (option.price_type === "calculated") {
+29 -22
View File
@@ -121,7 +121,7 @@ class SwapService extends BaseService {
"cart.total",
]
const totalsToSelect = select.filter(v => totalFields.includes(v))
const totalsToSelect = select.filter((v) => totalFields.includes(v))
if (totalsToSelect.length > 0) {
const relationSet = new Set(relations)
relationSet.add("cart")
@@ -132,7 +132,7 @@ class SwapService extends BaseService {
relationSet.add("cart.region")
relations = [...relationSet]
select = select.filter(v => !totalFields.includes(v))
select = select.filter((v) => !totalFields.includes(v))
}
return {
@@ -175,9 +175,8 @@ class SwapService extends BaseService {
const validatedId = this.validateId_(id)
const { totalsToSelect, ...newConfig } = this.transformQueryForTotals_(
config
)
const { totalsToSelect, ...newConfig } =
this.transformQueryForTotals_(config)
const query = this.buildQuery_({ id: validatedId }, newConfig)
@@ -256,7 +255,7 @@ class SwapService extends BaseService {
*/
validateReturnItems_(order, returnItems) {
return returnItems.map(({ item_id, quantity }) => {
const item = order.items.find(i => i.id === item_id)
const item = order.items.find((i) => i.id === item_id)
// The item must exist in the order
if (!item) {
@@ -308,7 +307,7 @@ class SwapService extends BaseService {
}
) {
const { no_notification, ...rest } = custom
return this.atomicPhase_(async manager => {
return this.atomicPhase_(async (manager) => {
if (
order.fulfillment_status === "not_fulfilled" ||
order.payment_status !== "captured"
@@ -381,7 +380,7 @@ class SwapService extends BaseService {
}
async processDifference(swapId) {
return this.atomicPhase_(async manager => {
return this.atomicPhase_(async (manager) => {
const swap = await this.retrieve(swapId, {
relations: ["payment", "order", "order.payments"],
})
@@ -497,7 +496,7 @@ class SwapService extends BaseService {
}
async update(swapId, update) {
return this.atomicPhase_(async manager => {
return this.atomicPhase_(async (manager) => {
const swap = await this.retrieve(swapId)
if ("metadata" in update) {
@@ -529,7 +528,7 @@ class SwapService extends BaseService {
* the new cart.
*/
async createCart(swapId, customShippingOptions = []) {
return this.atomicPhase_(async manager => {
return this.atomicPhase_(async (manager) => {
const swap = await this.retrieve(swapId, {
relations: [
"order",
@@ -538,6 +537,8 @@ class SwapService extends BaseService {
"order.swaps.additional_items",
"order.discounts",
"order.discounts.rule",
"order.claims",
"order.claims.additional_items",
"additional_items",
"return_order",
"return_order.items",
@@ -621,7 +622,13 @@ class SwapService extends BaseService {
}
}
const lineItem = allItems.find(i => i.id === r.item_id)
if (order.claims && order.claims.length) {
for (const c of order.claims) {
allItems = [...allItems, ...c.additional_items]
}
}
const lineItem = allItems.find((i) => i.id === r.item_id)
const toCreate = {
cart_id: cart.id,
@@ -652,7 +659,7 @@ class SwapService extends BaseService {
*
*/
async registerCartCompletion(swapId) {
return this.atomicPhase_(async manager => {
return this.atomicPhase_(async (manager) => {
const swap = await this.retrieve(swapId, {
relations: [
"cart",
@@ -784,7 +791,7 @@ class SwapService extends BaseService {
* status.
*/
async receiveReturn(swapId, returnItems) {
return this.atomicPhase_(async manager => {
return this.atomicPhase_(async (manager) => {
const swap = await this.retrieve(swapId, { relations: ["return_order"] })
if (swap.canceled_at) {
@@ -825,7 +832,7 @@ class SwapService extends BaseService {
* @returns {Promise<Swap>} the canceled swap.
*/
async cancel(swapId) {
return this.atomicPhase_(async manager => {
return this.atomicPhase_(async (manager) => {
const swap = await this.retrieve(swapId, {
relations: ["payment", "fulfillments", "return_order"],
})
@@ -891,7 +898,7 @@ class SwapService extends BaseService {
) {
const { metadata, no_notification } = config
return this.atomicPhase_(async manager => {
return this.atomicPhase_(async (manager) => {
const swap = await this.retrieve(swapId, {
relations: [
"payment",
@@ -951,7 +958,7 @@ class SwapService extends BaseService {
is_swap: true,
no_notification: evaluatedNoNotification,
},
swap.additional_items.map(i => ({
swap.additional_items.map((i) => ({
item_id: i.id,
quantity: i.quantity,
})),
@@ -968,7 +975,7 @@ class SwapService extends BaseService {
// Update all line items to reflect fulfillment
for (const item of swap.additional_items) {
const fulfillmentItem = successfullyFulfilled.find(
f => item.id === f.item_id
(f) => item.id === f.item_id
)
if (fulfillmentItem) {
@@ -1013,7 +1020,7 @@ class SwapService extends BaseService {
* @returns updated swap
*/
async cancelFulfillment(fulfillmentId) {
return this.atomicPhase_(async manager => {
return this.atomicPhase_(async (manager) => {
const canceled = await this.fulfillmentService_
.withTransaction(manager)
.cancelFulfillment(fulfillmentId)
@@ -1056,7 +1063,7 @@ class SwapService extends BaseService {
) {
const { metadata, no_notification } = config
return this.atomicPhase_(async manager => {
return this.atomicPhase_(async (manager) => {
const swap = await this.retrieve(swapId, {
relations: ["additional_items"],
})
@@ -1082,7 +1089,7 @@ class SwapService extends BaseService {
// Go through all the additional items in the swap
for (const i of swap.additional_items) {
const shipped = shipment.items.find(si => si.item_id === i.id)
const shipped = shipment.items.find((si) => si.item_id === i.id)
if (shipped) {
const shippedQty = (i.shipped_quantity || 0) + shipped.quantity
await this.lineItemService_.withTransaction(manager).update(i.id, {
@@ -1131,7 +1138,7 @@ class SwapService extends BaseService {
const keyPath = `metadata.${key}`
return this.swapModel_
.updateOne({ _id: validatedId }, { $unset: { [keyPath]: "" } })
.catch(err => {
.catch((err) => {
throw new MedusaError(MedusaError.Types.DB_ERROR, err.message)
})
}
@@ -1144,7 +1151,7 @@ class SwapService extends BaseService {
* @returns {Promise<Order>} the resulting order
*/
async registerReceived(id) {
return this.atomicPhase_(async manager => {
return this.atomicPhase_(async (manager) => {
const swap = await this.retrieve(id, {
relations: ["return_order", "return_order.items"],
})
+14
View File
@@ -1,6 +1,7 @@
import _ from "lodash"
import { BaseService } from "medusa-interfaces"
import { MedusaError } from "medusa-core-utils"
import carts from "../api/routes/store/carts"
/**
* A service that calculates total and subtotals for orders, carts etc..
@@ -158,6 +159,13 @@ class TotalsService extends BaseService {
}
}
if (order.claims && order.claims.length) {
for (const c of order.claims) {
const claimItemIds = c.additional_items.map(el => el.id)
itemIds = [...itemIds, ...claimItemIds]
}
}
const refunds = lineItems.map(i => {
if (!itemIds.includes(i.id)) {
throw new MedusaError(
@@ -253,6 +261,12 @@ class TotalsService extends BaseService {
}
}
if (cart.claims && cart.claims.length) {
for (const c of cart.claims) {
merged = [...merged, ...c.additional_items]
}
}
const { type, allocation, value } = discount.rule
if (allocation === "total") {
let percentage = 0