Fixes merge conflicts
This commit is contained in:
@@ -5,9 +5,7 @@ Object.defineProperty(exports, "__esModule", {
|
||||
});
|
||||
exports["default"] = void 0;
|
||||
|
||||
var _medusaInterfaces = _interopRequireDefault(require("medusa-interfaces"));
|
||||
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
|
||||
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; } }
|
||||
|
||||
@@ -23,7 +21,7 @@ function _inherits(subClass, superClass) { if (typeof superClass !== "function"
|
||||
|
||||
function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
|
||||
|
||||
function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function () { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }
|
||||
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); }
|
||||
|
||||
@@ -35,8 +33,8 @@ function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.g
|
||||
|
||||
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 (_BaseFulfillmentServi) {
|
||||
_inherits(ManualFulfillmentService, _BaseFulfillmentServi);
|
||||
var ManualFulfillmentService = /*#__PURE__*/function (_FulfillmentService) {
|
||||
_inherits(ManualFulfillmentService, _FulfillmentService);
|
||||
|
||||
var _super = _createSuper(ManualFulfillmentService);
|
||||
|
||||
@@ -61,11 +59,7 @@ var ManualFulfillmentService = /*#__PURE__*/function (_BaseFulfillmentServi) {
|
||||
}, {
|
||||
key: "validateOption",
|
||||
value: function validateOption(data) {
|
||||
if (data.id === "manual-fulfillment") {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
}, {
|
||||
key: "canCalculate",
|
||||
@@ -86,7 +80,7 @@ var ManualFulfillmentService = /*#__PURE__*/function (_BaseFulfillmentServi) {
|
||||
}]);
|
||||
|
||||
return ManualFulfillmentService;
|
||||
}(_medusaInterfaces["default"]);
|
||||
}(_medusaInterfaces.FulfillmentService);
|
||||
|
||||
_defineProperty(ManualFulfillmentService, "identifier", "manual");
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "medusa-fulfillment-manual",
|
||||
"version": "1.0",
|
||||
"version": "1.0.0",
|
||||
"description": "A manual fulfillment provider for Medusa",
|
||||
"main": "index.js",
|
||||
"repository": {
|
||||
@@ -34,4 +34,4 @@
|
||||
"express": "^4.17.1",
|
||||
"medusa-core-utils": "^0.3.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,3 +3,4 @@ export { default as BaseModel } from "./base-model"
|
||||
export { default as PaymentService } from "./payment-service"
|
||||
export { default as FulfillmentService } from "./fulfillment-service"
|
||||
export { default as FileService } from "./file-service"
|
||||
export { default as OauthService } from "./oauth-service"
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
import BaseService from "./base-service"
|
||||
|
||||
/**
|
||||
* Interface for file connectors
|
||||
* @interface
|
||||
*/
|
||||
class BaseOauthService extends BaseService {
|
||||
constructor() {
|
||||
super()
|
||||
}
|
||||
|
||||
generateToken() {
|
||||
throw Error("generateToken must be overridden by the child class")
|
||||
}
|
||||
|
||||
refreshToken() {
|
||||
throw Error("refreshToken must be overridden by the child class")
|
||||
}
|
||||
|
||||
destroyToken() {
|
||||
throw Error("destroyToken must be overridden by the child class")
|
||||
}
|
||||
}
|
||||
|
||||
export default BaseOauthService
|
||||
@@ -10,15 +10,18 @@ export default async (req, res) => {
|
||||
const cart = await cartService.retrieve(merchant_data)
|
||||
const shippingOptions = await shippingProfileService.fetchCartOptions(cart)
|
||||
|
||||
const option = shippingOptions.find(({ _id }) => _id.equals(selected_shipping_option.id))
|
||||
const ids = selected_shipping_option.id.split(".")
|
||||
await Promise.all(ids.map(async id => {
|
||||
const option = shippingOptions.find(({ _id }) => _id.equals(id))
|
||||
|
||||
if (option) {
|
||||
const newCart = await cartService.addShippingMethod(cart._id, option._id, option.data)
|
||||
const order = await klarnaProviderService.cartToKlarnaOrder(newCart)
|
||||
res.json(order)
|
||||
} else {
|
||||
res.sendStatus(400)
|
||||
}
|
||||
if (option) {
|
||||
await cartService.addShippingMethod(cart._id, option._id, option.data)
|
||||
}
|
||||
}))
|
||||
|
||||
const newCart = await cartService.retrieve(cart._id)
|
||||
const order = await klarnaProviderService.cartToKlarnaOrder(newCart)
|
||||
res.json(order)
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ class KlarnaProviderService extends PaymentService {
|
||||
this.klarnaOrderManagementUrl_ = "/ordermanagement/v1/orders"
|
||||
|
||||
this.backendUrl_ =
|
||||
process.env.BACKEND_URL || "https://7e9a5bc2a2eb.ngrok.io"
|
||||
process.env.BACKEND_URL || "https://c8e1abe7d8b3.ngrok.io"
|
||||
|
||||
this.totalsService_ = totalsService
|
||||
|
||||
@@ -73,10 +73,14 @@ class KlarnaProviderService extends PaymentService {
|
||||
})
|
||||
|
||||
if (cart.shipping_methods.length) {
|
||||
const shippingMethod = cart.shipping_methods[0]
|
||||
const price = shippingMethod.price
|
||||
const { name, price } = cart.shipping_methods.reduce((acc, next) => {
|
||||
acc.name = [...acc.name, next.name]
|
||||
acc.price += next.price
|
||||
return acc
|
||||
}, { name: [], price: 0 })
|
||||
|
||||
order_lines.push({
|
||||
name: `${shippingMethod.name}`,
|
||||
name: name.join(" + "),
|
||||
quantity: 1,
|
||||
type: "shipping_fee",
|
||||
unit_price: price * (1 + taxRate) * 100,
|
||||
@@ -167,18 +171,42 @@ class KlarnaProviderService extends PaymentService {
|
||||
}
|
||||
}
|
||||
|
||||
// If the cart does have shipping methods, set the selected shipping method
|
||||
order.shipping_options = shippingOptions.map((so) => ({
|
||||
id: so._id,
|
||||
name: so.name,
|
||||
price: so.price * (1 + tax_rate) * 100,
|
||||
tax_amount: so.price * tax_rate * 100,
|
||||
tax_rate: tax_rate * 10000,
|
||||
preselected: shippingOptions.length === 1
|
||||
}))
|
||||
const partitioned = shippingOptions.reduce((acc, next) => {
|
||||
if (acc[next.profile_id]) {
|
||||
acc[next.profile_id] = [...acc[next.profile_id], next]
|
||||
} else {
|
||||
acc[next.profile_id] = [next]
|
||||
}
|
||||
return acc
|
||||
}, {})
|
||||
|
||||
let f = (a, b) => [].concat(...a.map(a => b.map(b => [].concat(a, b))))
|
||||
let cartesian = (a, b, ...c) => b ? cartesian(f(a, b), ...c) : a
|
||||
|
||||
const methods = Object.keys(partitioned).map(k => partitioned[k])
|
||||
const combinations = cartesian(...methods)
|
||||
|
||||
|
||||
order.shipping_options = combinations.map((combination) => {
|
||||
combination = Array.isArray(combination) ? combination : [combination]
|
||||
const details = combination.reduce((acc, next) => {
|
||||
acc.id = [...acc.id, next._id]
|
||||
acc.name = [...acc.name, next.name]
|
||||
acc.price += next.price
|
||||
return acc
|
||||
}, { id: [], name: [], price: 0 })
|
||||
|
||||
return {
|
||||
id: details.id.join("."),
|
||||
name: details.name.join(" + "),
|
||||
price: details.price * (1 + tax_rate) * 100,
|
||||
tax_amount: details.price * tax_rate * 100,
|
||||
tax_rate: tax_rate * 10000,
|
||||
preselected: combinations.length === 1
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
console.log(order)
|
||||
return order
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
class OrderSubscriber {
|
||||
constructor({ klarnaProviderService, eventBusService }) {
|
||||
this.klarnaProviderService_ = klarnaProviderService
|
||||
|
||||
this.eventBus_ = eventBusService
|
||||
|
||||
this.eventBus_.subscribe("order.completed", async (order) => {
|
||||
const klarnaOrderId = order.payment_method.data.id
|
||||
await this.klarnaProviderService_.acknowledgeOrder(klarnaOrderId)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export default OrderSubscriber
|
||||
@@ -162,6 +162,12 @@ class StripeProviderService extends PaymentService {
|
||||
try {
|
||||
const { id } = data
|
||||
return this.stripe_.paymentIntents.cancel(id)
|
||||
.catch(err => {
|
||||
if (err.statusCode === 400) {
|
||||
return
|
||||
}
|
||||
throw err
|
||||
})
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
|
||||
@@ -13,11 +13,6 @@ class CartSubscriber {
|
||||
this.eventBus_.subscribe("cart.customer_updated", async (cart) => {
|
||||
await this.onCustomerUpdated(cart)
|
||||
})
|
||||
|
||||
this.eventBus_.subscribe("order.completed", async (order) => {
|
||||
const paymentData = order.payment_method.data
|
||||
await this.stripeProviderService_.capturePayment(paymentData)
|
||||
})
|
||||
}
|
||||
|
||||
async onCustomerUpdated(cart) {
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"plugins": [
|
||||
"@babel/plugin-proposal-class-properties",
|
||||
"@babel/plugin-transform-instanceof",
|
||||
"@babel/plugin-transform-classes"
|
||||
],
|
||||
"presets": ["@babel/preset-env"],
|
||||
"env": {
|
||||
"test": {
|
||||
"plugins": ["@babel/plugin-transform-runtime"]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"plugins": ["prettier"],
|
||||
"extends": ["prettier"],
|
||||
"rules": {
|
||||
"prettier/prettier": "error",
|
||||
"semi": "error",
|
||||
"no-unused-expressions": "true"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
/lib
|
||||
node_modules
|
||||
.DS_store
|
||||
.env*
|
||||
/*.js
|
||||
!index.js
|
||||
yarn.lock
|
||||
|
||||
/dist
|
||||
|
||||
/api
|
||||
/services
|
||||
/models
|
||||
/subscribers
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
/lib
|
||||
node_modules
|
||||
.DS_store
|
||||
.env*
|
||||
/*.js
|
||||
!index.js
|
||||
yarn.lock
|
||||
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"endOfLine": "lf",
|
||||
"semi": false,
|
||||
"singleQuote": false,
|
||||
"tabWidth": 2,
|
||||
"trailingComma": "es5"
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
// noop
|
||||
@@ -0,0 +1,61 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports["default"] = void 0;
|
||||
|
||||
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }
|
||||
|
||||
function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
|
||||
|
||||
var inventorySync = /*#__PURE__*/function () {
|
||||
var _ref = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee(container) {
|
||||
var brightpearlService, eventBus, client, pattern;
|
||||
return regeneratorRuntime.wrap(function _callee$(_context) {
|
||||
while (1) {
|
||||
switch (_context.prev = _context.next) {
|
||||
case 0:
|
||||
brightpearlService = container.resolve("brightpearlService");
|
||||
eventBus = container.resolve("eventBusService");
|
||||
_context.prev = 2;
|
||||
_context.next = 5;
|
||||
return brightpearlService.getClient();
|
||||
|
||||
case 5:
|
||||
client = _context.sent;
|
||||
pattern = "43 4,10,14,20 * * *"; // nice for tests "*/10 * * * * *"
|
||||
|
||||
eventBus.createCronJob("inventory-sync", {}, pattern, brightpearlService.syncInventory());
|
||||
_context.next = 15;
|
||||
break;
|
||||
|
||||
case 10:
|
||||
_context.prev = 10;
|
||||
_context.t0 = _context["catch"](2);
|
||||
|
||||
if (!(_context.t0.name === "not_allowed")) {
|
||||
_context.next = 14;
|
||||
break;
|
||||
}
|
||||
|
||||
return _context.abrupt("return");
|
||||
|
||||
case 14:
|
||||
throw _context.t0;
|
||||
|
||||
case 15:
|
||||
case "end":
|
||||
return _context.stop();
|
||||
}
|
||||
}
|
||||
}, _callee, null, [[2, 10]]);
|
||||
}));
|
||||
|
||||
return function inventorySync(_x) {
|
||||
return _ref.apply(this, arguments);
|
||||
};
|
||||
}();
|
||||
|
||||
var _default = inventorySync;
|
||||
exports["default"] = _default;
|
||||
@@ -0,0 +1,61 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports["default"] = void 0;
|
||||
|
||||
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }
|
||||
|
||||
function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
|
||||
|
||||
var webhookLoader = /*#__PURE__*/function () {
|
||||
var _ref = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee(container) {
|
||||
var brightpearlService, client;
|
||||
return regeneratorRuntime.wrap(function _callee$(_context) {
|
||||
while (1) {
|
||||
switch (_context.prev = _context.next) {
|
||||
case 0:
|
||||
brightpearlService = container.resolve("brightpearlService");
|
||||
_context.prev = 1;
|
||||
_context.next = 4;
|
||||
return brightpearlService.getClient();
|
||||
|
||||
case 4:
|
||||
client = _context.sent;
|
||||
_context.next = 7;
|
||||
return brightpearlService.verifyWebhooks();
|
||||
|
||||
case 7:
|
||||
_context.next = 14;
|
||||
break;
|
||||
|
||||
case 9:
|
||||
_context.prev = 9;
|
||||
_context.t0 = _context["catch"](1);
|
||||
|
||||
if (!(_context.t0.name === "not_allowed")) {
|
||||
_context.next = 13;
|
||||
break;
|
||||
}
|
||||
|
||||
return _context.abrupt("return");
|
||||
|
||||
case 13:
|
||||
throw _context.t0;
|
||||
|
||||
case 14:
|
||||
case "end":
|
||||
return _context.stop();
|
||||
}
|
||||
}
|
||||
}, _callee, null, [[1, 9]]);
|
||||
}));
|
||||
|
||||
return function webhookLoader(_x) {
|
||||
return _ref.apply(this, arguments);
|
||||
};
|
||||
}();
|
||||
|
||||
var _default = webhookLoader;
|
||||
exports["default"] = _default;
|
||||
@@ -0,0 +1,44 @@
|
||||
{
|
||||
"name": "medusa-plugin-brightpearl",
|
||||
"version": "1.0.0",
|
||||
"description": "Brightpearl plugin for Medusa Commerce",
|
||||
"main": "index.js",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/medusajs/medusa",
|
||||
"directory": "packages/medusa-plugin-brightpearl"
|
||||
},
|
||||
"author": "Sebastian Rindom",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.7.5",
|
||||
"@babel/core": "^7.7.5",
|
||||
"@babel/node": "^7.7.4",
|
||||
"@babel/plugin-proposal-class-properties": "^7.7.4",
|
||||
"@babel/plugin-transform-classes": "^7.9.5",
|
||||
"@babel/plugin-transform-instanceof": "^7.8.3",
|
||||
"@babel/plugin-transform-runtime": "^7.7.6",
|
||||
"@babel/preset-env": "^7.7.5",
|
||||
"@babel/register": "^7.7.4",
|
||||
"@babel/runtime": "^7.9.6",
|
||||
"client-sessions": "^0.8.0",
|
||||
"cross-env": "^5.2.1",
|
||||
"eslint": "^6.8.0",
|
||||
"jest": "^25.5.2",
|
||||
"medusa-test-utils": "^0.3.0",
|
||||
"prettier": "^2.0.5"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "babel src -d dist",
|
||||
"prepare": "cross-env NODE_ENV=production npm run build",
|
||||
"watch": "babel -w src --out-dir . --ignore **/__tests__",
|
||||
"test": "jest"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^0.19.2",
|
||||
"express": "^4.17.1",
|
||||
"medusa-core-utils": "^0.3.0",
|
||||
"medusa-interfaces": "^0.3.0",
|
||||
"randomatic": "^3.1.1"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import { Router } from "express"
|
||||
import bodyParser from "body-parser"
|
||||
|
||||
export default (container) => {
|
||||
const app = Router()
|
||||
|
||||
app.post("/brightpearl/inventory-update", bodyParser.json(), async (req, res) => {
|
||||
const { id } = req.body
|
||||
const brightpearlService = req.scope.resolve("brightpearlService")
|
||||
await brightpearlService.updateInventory(id)
|
||||
res.sendStatus(200)
|
||||
})
|
||||
|
||||
return app
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
const inventorySync = async (container) => {
|
||||
const brightpearlService = container.resolve("brightpearlService")
|
||||
const eventBus = container.resolve("eventBusService")
|
||||
|
||||
try {
|
||||
const client = await brightpearlService.getClient()
|
||||
const pattern = "43 4,10,14,20 * * *" // nice for tests "*/10 * * * * *"
|
||||
eventBus.createCronJob(
|
||||
"inventory-sync",
|
||||
{},
|
||||
pattern,
|
||||
brightpearlService.syncInventory()
|
||||
)
|
||||
} catch (err) {
|
||||
if (err.name === "not_allowed") {
|
||||
return
|
||||
}
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
export default inventorySync
|
||||
@@ -0,0 +1,14 @@
|
||||
const webhookLoader = async (container) => {
|
||||
const brightpearlService = container.resolve("brightpearlService")
|
||||
try {
|
||||
const client = await brightpearlService.getClient()
|
||||
await brightpearlService.verifyWebhooks()
|
||||
} catch (err) {
|
||||
if (err.name === "not_allowed") {
|
||||
return
|
||||
}
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
export default webhookLoader
|
||||
@@ -0,0 +1,394 @@
|
||||
import { MedusaError } from "medusa-core-utils"
|
||||
import { BaseService } from "medusa-interfaces"
|
||||
import Brightpearl from "../utils/brightpearl"
|
||||
|
||||
class BrightpearlService extends BaseService {
|
||||
constructor(
|
||||
{
|
||||
oauthService,
|
||||
totalsService,
|
||||
productVariantService,
|
||||
regionService,
|
||||
orderService,
|
||||
discountService,
|
||||
},
|
||||
options
|
||||
) {
|
||||
super()
|
||||
|
||||
this.options = options
|
||||
this.productVariantService_ = productVariantService
|
||||
this.regionService_ = regionService
|
||||
this.orderService_ = orderService
|
||||
this.totalsService_ = totalsService
|
||||
this.discountService_ = discountService
|
||||
this.oauthService_ = oauthService
|
||||
}
|
||||
|
||||
async getClient() {
|
||||
if (this.brightpearlClient_) {
|
||||
return this.brightpearlClient_
|
||||
}
|
||||
|
||||
const authData = await this.oauthService_.retrieveByName("brightpearl")
|
||||
const { data } = authData
|
||||
|
||||
if (!data || !data.access_token) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.NOT_ALLOWED,
|
||||
"You must authenticate the Brightpearl app in settings before continuing"
|
||||
)
|
||||
}
|
||||
|
||||
const client = new Brightpearl({
|
||||
url: data.api_domain,
|
||||
auth_type: data.token_type,
|
||||
access_token: data.access_token,
|
||||
})
|
||||
|
||||
this.brightpearlClient_ = client
|
||||
return client
|
||||
}
|
||||
|
||||
async verifyWebhooks() {
|
||||
const brightpearl = await this.getClient()
|
||||
const hooks = [
|
||||
{
|
||||
subscribeTo: "product.modified.on-hand-modified",
|
||||
httpMethod: "POST",
|
||||
uriTemplate: `${this.options.backend_url}/brightpearl/inventory-update`,
|
||||
bodyTemplate:
|
||||
'{"account": "${account-code}", "lifecycleEvent": "${lifecycle-event}", "resourceType": "${resource-type}", "id": "${resource-id}" }',
|
||||
contentType: "application/json",
|
||||
idSetAccepted: false,
|
||||
},
|
||||
]
|
||||
|
||||
const installedHooks = await brightpearl.webhooks.list().catch(() => [])
|
||||
for (const hook of hooks) {
|
||||
const isInstalled = installedHooks.find(
|
||||
(i) =>
|
||||
i.subscribeTo === hook.subscribeTo &&
|
||||
i.httpMethod === hook.httpMethod &&
|
||||
i.uriTemplate === hook.uriTemplate &&
|
||||
i.bodyTemplate === hook.bodyTemplate &&
|
||||
i.contentType === hook.contentType &&
|
||||
i.idSetAccepted === hook.idSetAccepted
|
||||
)
|
||||
|
||||
if (!isInstalled) {
|
||||
await brightpearl.webhooks.create(hook)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async syncInventory() {
|
||||
const client = await this.getClient()
|
||||
const variants = await this.productVariantService_.list()
|
||||
return Promise.all(
|
||||
variants.map(async (v) => {
|
||||
const brightpearlProduct = await this.retrieveProductBySKU(v.sku)
|
||||
if (!brightpearlProduct) {
|
||||
return
|
||||
}
|
||||
|
||||
const { productId } = brightpearlProduct
|
||||
const availability = await client.products.retrieveAvailability(
|
||||
productId
|
||||
)
|
||||
const onHand = availability[productId].total.onHand
|
||||
|
||||
return this.productVariantService_.update(v._id, {
|
||||
inventory_quantity: onHand,
|
||||
})
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
async updateInventory(productId) {
|
||||
const client = await this.getClient()
|
||||
const brightpearlProduct = await client.products.retrieve(productId)
|
||||
const availability = await client.products.retrieveAvailability(productId)
|
||||
|
||||
const onHand = availability[productId].total.onHand
|
||||
|
||||
const sku = brightpearlProduct.identity.sku
|
||||
const [variant] = await this.productVariantService_.list({ sku })
|
||||
|
||||
if (variant && variant.manage_inventory) {
|
||||
await this.productVariantService_.update(variant._id, {
|
||||
inventory_quantity: onHand,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
async createGoodsOutNote(fromOrder, shipment) {
|
||||
const client = await this.getClient()
|
||||
const id =
|
||||
fromOrder.metadata && fromOrder.metadata.brightpearl_sales_order_id
|
||||
|
||||
if (!id) {
|
||||
return
|
||||
}
|
||||
|
||||
const order = await client.orders.retrieve(id)
|
||||
const productRows = shipment.item_ids.map((id) => {
|
||||
const row = order.rows.find(({ externalRef }) => externalRef === id)
|
||||
return {
|
||||
productId: row.productId,
|
||||
salesOrderRowId: row.id,
|
||||
quantity: row.quantity,
|
||||
}
|
||||
})
|
||||
|
||||
const goodsOut = {
|
||||
warehouses: [
|
||||
{
|
||||
releaseDate: new Date(),
|
||||
warehouseId: this.options.warehouse,
|
||||
transfer: false,
|
||||
products: productRows,
|
||||
},
|
||||
],
|
||||
priority: false,
|
||||
}
|
||||
|
||||
return client.warehouses.createGoodsOutNote(id, goodsOut)
|
||||
}
|
||||
|
||||
async registerGoodsOutShipped(noteId, shipment) {
|
||||
const client = await this.getClient()
|
||||
return client.warehouses.registerGoodsOutEvent(noteId, {
|
||||
events: [
|
||||
{
|
||||
eventCode: "SHW",
|
||||
occured: new Date(),
|
||||
eventOwnerId: this.options.event_owner,
|
||||
},
|
||||
],
|
||||
})
|
||||
}
|
||||
|
||||
async registerGoodsOutTrackingNumber(noteId, shipment) {
|
||||
const client = await this.getClient()
|
||||
return client.warehouses.updateGoodsOutNote(noteId, {
|
||||
priority: false,
|
||||
shipping: {
|
||||
reference: shipment.tracking_number,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
async createSalesOrder(fromOrder) {
|
||||
const client = await this.getClient()
|
||||
let customer = await this.retrieveCustomerByEmail(fromOrder.email)
|
||||
|
||||
// All sales orders must have a customer
|
||||
if (!customer) {
|
||||
customer = await this.createCustomer(fromOrder)
|
||||
}
|
||||
|
||||
const { shipping_address } = fromOrder
|
||||
const order = {
|
||||
currency: {
|
||||
code: fromOrder.currency_code,
|
||||
},
|
||||
externalRef: fromOrder._id,
|
||||
customer: {
|
||||
id: customer.contactId,
|
||||
address: {
|
||||
addressFullName: `${shipping_address.first_name} ${shipping_address.last_name}`,
|
||||
addressLine1: shipping_address.address_1,
|
||||
addressLine2: shipping_address.address_2,
|
||||
postalCode: shipping_address.postal_code,
|
||||
countryIsoCode: shipping_address.country_code,
|
||||
telephone: shipping_address.phone,
|
||||
email: fromOrder.email,
|
||||
},
|
||||
},
|
||||
delivery: {
|
||||
shippingMethodId: 0,
|
||||
address: {
|
||||
addressFullName: `${shipping_address.first_name} ${shipping_address.last_name}`,
|
||||
addressLine1: shipping_address.address_1,
|
||||
addressLine2: shipping_address.address_2,
|
||||
postalCode: shipping_address.postal_code,
|
||||
countryIsoCode: shipping_address.country_code,
|
||||
telephone: shipping_address.phone,
|
||||
email: fromOrder.email,
|
||||
},
|
||||
},
|
||||
rows: await this.getBrightpearlRows(fromOrder),
|
||||
}
|
||||
|
||||
return client.orders
|
||||
.create(order)
|
||||
.then(async (salesOrderId) => {
|
||||
const order = await client.orders.retrieve(salesOrderId)
|
||||
const resResult = await client.warehouses.createReservation(
|
||||
order,
|
||||
this.options.warehouse
|
||||
)
|
||||
return salesOrderId
|
||||
})
|
||||
.then(async (salesOrderId) => {
|
||||
const paymentMethod = fromOrder.payment_method
|
||||
const paymentType = "AUTH"
|
||||
const payment = {
|
||||
transactionRef: `${paymentMethod._id}.${paymentType}`, // Brightpearl cannot accept an auth and capture with same ref
|
||||
transactionCode: fromOrder._id,
|
||||
paymentMethodCode: "1220",
|
||||
orderId: salesOrderId,
|
||||
currencyIsoCode: fromOrder.currency_code,
|
||||
paymentDate: new Date(),
|
||||
paymentType,
|
||||
}
|
||||
|
||||
// Only if authorization type
|
||||
if (paymentType === "AUTH") {
|
||||
const today = new Date()
|
||||
const authExpire = today.setDate(today.getDate() + 7)
|
||||
payment.amountAuthorized = await this.totalsService_.getTotal(
|
||||
fromOrder
|
||||
)
|
||||
payment.authorizationExpiry = new Date(authExpire)
|
||||
} else {
|
||||
// For captured
|
||||
}
|
||||
|
||||
await client.payments.create(payment)
|
||||
|
||||
return salesOrderId
|
||||
})
|
||||
.then((salesOrderId) => {
|
||||
return this.orderService_.setMetadata(
|
||||
fromOrder._id,
|
||||
"brightpearl_sales_order_id",
|
||||
salesOrderId
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
async createCapturedPayment(fromOrder) {
|
||||
const client = await this.getClient()
|
||||
const soId =
|
||||
fromOrder.metadata && fromOrder.metadata.brightpearl_sales_order_id
|
||||
if (!soId) {
|
||||
return
|
||||
}
|
||||
|
||||
const paymentType = "CAPTURE"
|
||||
const paymentMethod = fromOrder.payment_method
|
||||
const payment = {
|
||||
transactionRef: `${paymentMethod._id}.${paymentType}`, // Brightpearl cannot accept an auth and capture with same ref
|
||||
transactionCode: fromOrder._id,
|
||||
paymentMethodCode: "1220",
|
||||
orderId: soId,
|
||||
paymentDate: new Date(),
|
||||
currencyIsoCode: fromOrder.currency_code,
|
||||
amountPaid: await this.totalsService_.getTotal(fromOrder),
|
||||
paymentType,
|
||||
}
|
||||
|
||||
await client.payments.create(payment)
|
||||
}
|
||||
|
||||
async getBrightpearlRows(fromOrder) {
|
||||
const region = await this.regionService_.retrieve(fromOrder.region_id)
|
||||
const discount = fromOrder.discounts.find(
|
||||
({ discount_rule }) => discount_rule.type !== "free_shipping"
|
||||
)
|
||||
let lineDiscounts = []
|
||||
if (discount) {
|
||||
lineDiscounts = this.discountService_.getLineDiscounts(
|
||||
fromOrder,
|
||||
discount
|
||||
)
|
||||
}
|
||||
|
||||
const lines = await Promise.all(
|
||||
fromOrder.items.map(async (item) => {
|
||||
const bpProduct = await this.retrieveProductBySKU(
|
||||
item.content.variant.sku
|
||||
)
|
||||
|
||||
const discount = lineDiscounts.find((l) =>
|
||||
l.item._id.equals(item._id)
|
||||
) || { amount: 0 }
|
||||
|
||||
const row = {}
|
||||
if (bpProduct) {
|
||||
row.productId = bpProduct.productId
|
||||
} else {
|
||||
row.name = item.title
|
||||
}
|
||||
row.net = item.content.unit_price * item.quantity - discount.amount
|
||||
row.tax = row.net * fromOrder.tax_rate
|
||||
row.quantity = item.quantity
|
||||
row.taxCode = region.tax_code
|
||||
row.externalRef = item._id
|
||||
row.nominalCode = this.options.sales_account_code || "4000"
|
||||
|
||||
return row
|
||||
})
|
||||
)
|
||||
|
||||
const shippingTotal = this.totalsService_.getShippingTotal(fromOrder)
|
||||
const shippingMethods = fromOrder.shipping_methods
|
||||
if (shippingMethods.length > 0) {
|
||||
lines.push({
|
||||
name: `Shipping: ${shippingMethods.map((m) => m.name).join(" + ")}`,
|
||||
quantity: 1,
|
||||
net: shippingTotal,
|
||||
tax: shippingTotal * fromOrder.tax_rate,
|
||||
taxCode: region.tax_code,
|
||||
nominalCode: this.options.shipping_account_code || "4040",
|
||||
})
|
||||
}
|
||||
return lines
|
||||
}
|
||||
|
||||
async retrieveCustomerByEmail(email) {
|
||||
const client = await this.getClient()
|
||||
return client.customers.retrieveByEmail(email).then((customers) => {
|
||||
if (!customers.length) {
|
||||
return null
|
||||
}
|
||||
return customers.find((c) => c.primaryEmail === email)
|
||||
})
|
||||
}
|
||||
|
||||
async retrieveProductBySKU(sku) {
|
||||
const client = await this.getClient()
|
||||
return client.products.retrieveBySKU(sku).then((products) => {
|
||||
if (!products.length) {
|
||||
return null
|
||||
}
|
||||
return products[0]
|
||||
})
|
||||
}
|
||||
|
||||
async createCustomer(fromOrder) {
|
||||
const client = await this.getClient()
|
||||
const address = await client.addresses.create({
|
||||
addressLine1: fromOrder.shipping_address.address_1,
|
||||
addressLine2: fromOrder.shipping_address.address_2,
|
||||
postalCode: fromOrder.shipping_address.postal_code,
|
||||
countryIsoCode: fromOrder.shipping_address.country_code,
|
||||
})
|
||||
|
||||
const customer = await client.customers.create({
|
||||
firstName: fromOrder.shipping_address.first_name,
|
||||
lastName: fromOrder.shipping_address.last_name,
|
||||
postAddressIds: {
|
||||
DEF: address,
|
||||
BIL: address,
|
||||
DEL: address,
|
||||
},
|
||||
})
|
||||
|
||||
return { contactId: customer }
|
||||
}
|
||||
}
|
||||
|
||||
export default BrightpearlService
|
||||
@@ -0,0 +1,40 @@
|
||||
import randomize from "randomatic"
|
||||
import { OauthService } from "medusa-interfaces"
|
||||
import Brightpearl from "../utils/brightpearl"
|
||||
|
||||
const CLIENT_SECRET = process.env.BP_CLIENT_SECRET || ""
|
||||
|
||||
class BrightpearlOauth extends OauthService {
|
||||
constructor({}, options) {
|
||||
super()
|
||||
|
||||
this.account_ = options.account
|
||||
}
|
||||
|
||||
static getAppDetails(options) {
|
||||
const client_id = "medusa-dev"
|
||||
const client_secret = CLIENT_SECRET
|
||||
const state = randomize("A0", 16)
|
||||
const redirect = "https://localhost:8000/a/oauth/brightpearl"
|
||||
return {
|
||||
application_name: "brightpearl",
|
||||
display_name: "Brightpearl",
|
||||
install_url: `https://oauth.brightpearl.com/authorize/${options.account}?response_type=code&client_id=${client_id}&redirect_uri=${redirect}&state=${state}`,
|
||||
state,
|
||||
}
|
||||
}
|
||||
|
||||
async generateToken(code) {
|
||||
const params = {
|
||||
client_id: "medusa-dev",
|
||||
client_secret: CLIENT_SECRET,
|
||||
redirect: "https://localhost:8000/a/oauth/brightpearl",
|
||||
code,
|
||||
}
|
||||
|
||||
const data = await Brightpearl.createToken(this.account_, params)
|
||||
return data
|
||||
}
|
||||
}
|
||||
|
||||
export default BrightpearlOauth
|
||||
@@ -0,0 +1,31 @@
|
||||
class OrderSubscriber {
|
||||
constructor({ eventBusService, orderService, brightpearlService }) {
|
||||
this.orderService_ = orderService
|
||||
this.brightpearlService_ = brightpearlService
|
||||
|
||||
eventBusService.subscribe("order.placed", this.sendToBrightpearl)
|
||||
eventBusService.subscribe("order.payment_captured", this.registerCapturedPayment)
|
||||
eventBusService.subscribe("order.shipment_created", this.registerShipment)
|
||||
}
|
||||
|
||||
sendToBrightpearl = order => {
|
||||
return this.brightpearlService_.createSalesOrder(order)
|
||||
}
|
||||
|
||||
registerCapturedPayment = order => {
|
||||
return this.brightpearlService_.createCapturedPayment(order)
|
||||
}
|
||||
|
||||
registerShipment = async (data) => {
|
||||
const { order_id, shipment } = data
|
||||
const order = await this.orderService_.retrieve(order_id)
|
||||
const notes = await this.brightpearlService_.createGoodsOutNote(order, shipment)
|
||||
if (notes.length) {
|
||||
const noteId = notes[0]
|
||||
await this.brightpearlService_.registerGoodsOutTrackingNumber(noteId, shipment)
|
||||
await this.brightpearlService_.registerGoodsOutShipped(noteId, shipment)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default OrderSubscriber
|
||||
@@ -0,0 +1,233 @@
|
||||
import axios from "axios"
|
||||
import qs from "querystring"
|
||||
|
||||
class BrightpearlClient {
|
||||
static createToken(account, data) {
|
||||
const params = {
|
||||
grant_type: "authorization_code",
|
||||
code: data.code,
|
||||
client_id: data.client_id,
|
||||
client_secret: data.client_secret,
|
||||
redirect_uri: data.redirect,
|
||||
}
|
||||
|
||||
return axios({
|
||||
url: `https://ws-eu1.brightpearl.com/${account}/oauth/token`,
|
||||
method: "POST",
|
||||
headers: {
|
||||
"content-type": "application/x-www-form-urlencoded",
|
||||
},
|
||||
data: qs.stringify(params),
|
||||
}).then(({ data }) => data)
|
||||
}
|
||||
|
||||
constructor(options) {
|
||||
this.client_ = axios.create({
|
||||
baseURL: `${options.url}/public-api/${options.account}`,
|
||||
headers: {
|
||||
"brightpearl-app-ref": "medusa-dev",
|
||||
"brightpearl-dev-ref": "sebrindom",
|
||||
Authorization: `${options.auth_type} ${options.access_token}`,
|
||||
},
|
||||
})
|
||||
|
||||
this.webhooks = this.buildWebhookEndpoints()
|
||||
this.payments = this.buildPaymentEndpoints()
|
||||
this.warehouses = this.buildWarehouseEndpoints()
|
||||
this.orders = this.buildOrderEndpoints()
|
||||
this.addresses = this.buildAddressEndpoints()
|
||||
this.customers = this.buildCustomerEndpoints()
|
||||
this.products = this.buildProductEndpoints()
|
||||
}
|
||||
|
||||
buildSearchResults_(response) {
|
||||
const { results, metaData } = response
|
||||
// Map the column names to the columns
|
||||
return results.map((resColumns) => {
|
||||
const object = {}
|
||||
for (let i = 0; i < resColumns.length; i++) {
|
||||
const fieldName = metaData.columns[i].name
|
||||
object[fieldName] = resColumns[i]
|
||||
}
|
||||
return object
|
||||
})
|
||||
}
|
||||
|
||||
buildWebhookEndpoints = () => {
|
||||
return {
|
||||
list: () => {
|
||||
return this.client_
|
||||
.request({
|
||||
url: `/integration-service/webhook`,
|
||||
method: "GET",
|
||||
})
|
||||
.then(({ data }) => data.response)
|
||||
},
|
||||
create: (data) => {
|
||||
return this.client_.request({
|
||||
url: `/integration-service/webhook`,
|
||||
method: "POST",
|
||||
data,
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
buildPaymentEndpoints = () => {
|
||||
return {
|
||||
create: (payment) => {
|
||||
return this.client_
|
||||
.request({
|
||||
url: `/accounting-service/customer-payment`,
|
||||
method: "POST",
|
||||
data: payment,
|
||||
})
|
||||
.then(({ data }) => data.response)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
buildWarehouseEndpoints = () => {
|
||||
return {
|
||||
retrieveReservation: (orderId) => {
|
||||
return this.client_
|
||||
.request({
|
||||
url: `/warehouse-service/order/${orderId}/reservation`,
|
||||
method: "GET",
|
||||
})
|
||||
.then(({ data }) => data.response)
|
||||
},
|
||||
createGoodsOutNote: (orderId, data) => {
|
||||
return this.client_
|
||||
.request({
|
||||
url: `/warehouse-service/order/${orderId}/goods-note/goods-out`,
|
||||
method: "POST",
|
||||
data,
|
||||
})
|
||||
.then(({ data }) => data.response)
|
||||
},
|
||||
updateGoodsOutNote: (noteId, update) => {
|
||||
return this.client_.request({
|
||||
url: `/warehouse-service/goods-note/goods-out/${noteId}`,
|
||||
method: "PUT",
|
||||
data: update,
|
||||
})
|
||||
},
|
||||
registerGoodsOutEvent: (noteId, data) => {
|
||||
return this.client_.request({
|
||||
url: `/warehouse-service/goods-note/goods-out/${noteId}/event`,
|
||||
method: "POST",
|
||||
data,
|
||||
})
|
||||
},
|
||||
createReservation: (order, warehouse) => {
|
||||
const id = order.id
|
||||
const data = order.rows.map((r) => ({
|
||||
productId: r.productId,
|
||||
salesOrderRowId: r.id,
|
||||
quantity: r.quantity,
|
||||
}))
|
||||
return this.client_
|
||||
.request({
|
||||
url: `/warehouse-service/order/${id}/reservation/warehouse/${warehouse}`,
|
||||
method: "POST",
|
||||
data: {
|
||||
products: data,
|
||||
},
|
||||
})
|
||||
.then(({ data }) => data.response)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
buildOrderEndpoints = () => {
|
||||
return {
|
||||
retrieve: (orderId) => {
|
||||
return this.client_
|
||||
.request({
|
||||
url: `/order-service/sales-order/${orderId}`,
|
||||
method: "GET",
|
||||
})
|
||||
.then(({ data }) => data.response.length && data.response[0])
|
||||
.catch((err) => console.log(err))
|
||||
},
|
||||
create: (order) => {
|
||||
return this.client_
|
||||
.request({
|
||||
url: `/order-service/sales-order`,
|
||||
method: "POST",
|
||||
data: order,
|
||||
})
|
||||
.then(({ data }) => data.response)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
buildAddressEndpoints = () => {
|
||||
return {
|
||||
create: (address) => {
|
||||
return this.client_
|
||||
.request({
|
||||
url: `/contact-service/postal-address`,
|
||||
method: "POST",
|
||||
data: address,
|
||||
})
|
||||
.then(({ data }) => data.response)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
buildProductEndpoints = () => {
|
||||
return {
|
||||
retrieveAvailability: (productId) => {
|
||||
return this.client_
|
||||
.request({
|
||||
url: `/warehouse-service/product-availability/${productId}`,
|
||||
})
|
||||
.then(({ data }) => data.response && data.response)
|
||||
},
|
||||
retrieve: (productId) => {
|
||||
return this.client_
|
||||
.request({
|
||||
url: `/product-service/product/${productId}`,
|
||||
})
|
||||
.then(({ data }) => data.response && data.response[0])
|
||||
},
|
||||
retrieveBySKU: (sku) => {
|
||||
return this.client_
|
||||
.request({
|
||||
url: `/product-service/product-search?SKU=${sku}`,
|
||||
})
|
||||
.then(({ data }) => {
|
||||
return this.buildSearchResults_(data.response)
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
buildCustomerEndpoints = () => {
|
||||
return {
|
||||
retrieveByEmail: (email) => {
|
||||
return this.client_
|
||||
.request({
|
||||
url: `/contact-service/contact-search?primaryEmail=${email}`,
|
||||
})
|
||||
.then(({ data }) => {
|
||||
return this.buildSearchResults_(data.response)
|
||||
})
|
||||
},
|
||||
|
||||
create: (customerData) => {
|
||||
return this.client_
|
||||
.request({
|
||||
url: `/contact-service/contact`,
|
||||
method: "POST",
|
||||
data: customerData,
|
||||
})
|
||||
.then(({ data }) => data.response)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default BrightpearlClient
|
||||
@@ -0,0 +1,281 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports["default"] = void 0;
|
||||
|
||||
var _axios = _interopRequireDefault(require("axios"));
|
||||
|
||||
var _querystring = _interopRequireDefault(require("querystring"));
|
||||
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
|
||||
|
||||
function _instanceof(left, right) { if (right != null && typeof Symbol !== "undefined" && right[Symbol.hasInstance]) { return !!right[Symbol.hasInstance](left); } else { return left instanceof right; } }
|
||||
|
||||
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 _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 BrightpearlClient = /*#__PURE__*/function () {
|
||||
_createClass(BrightpearlClient, null, [{
|
||||
key: "createToken",
|
||||
value: function createToken(account, data) {
|
||||
var params = {
|
||||
grant_type: "authorization_code",
|
||||
code: data.code,
|
||||
client_id: data.client_id,
|
||||
client_secret: data.client_secret,
|
||||
redirect_uri: data.redirect
|
||||
};
|
||||
return (0, _axios["default"])({
|
||||
url: "https://ws-eu1.brightpearl.com/".concat(account, "/oauth/token"),
|
||||
method: "POST",
|
||||
headers: {
|
||||
"content-type": "application/x-www-form-urlencoded"
|
||||
},
|
||||
data: _querystring["default"].stringify(params)
|
||||
}).then(function (_ref) {
|
||||
var data = _ref.data;
|
||||
return data;
|
||||
});
|
||||
}
|
||||
}]);
|
||||
|
||||
function BrightpearlClient(options) {
|
||||
var _this = this;
|
||||
|
||||
_classCallCheck(this, BrightpearlClient);
|
||||
|
||||
_defineProperty(this, "buildWebhookEndpoints", function () {
|
||||
return {
|
||||
list: function list() {
|
||||
return _this.client_.request({
|
||||
url: "/integration-service/webhook",
|
||||
method: "GET"
|
||||
}).then(function (_ref2) {
|
||||
var data = _ref2.data;
|
||||
return data.response;
|
||||
});
|
||||
},
|
||||
create: function create(data) {
|
||||
return _this.client_.request({
|
||||
url: "/integration-service/webhook",
|
||||
method: "POST",
|
||||
data: data
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
_defineProperty(this, "buildPaymentEndpoints", function () {
|
||||
return {
|
||||
create: function create(payment) {
|
||||
return _this.client_.request({
|
||||
url: "/accounting-service/customer-payment",
|
||||
method: "POST",
|
||||
data: payment
|
||||
}).then(function (_ref3) {
|
||||
var data = _ref3.data;
|
||||
return data.response;
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
_defineProperty(this, "buildWarehouseEndpoints", function () {
|
||||
return {
|
||||
retrieveReservation: function retrieveReservation(orderId) {
|
||||
return _this.client_.request({
|
||||
url: "/warehouse-service/order/".concat(orderId, "/reservation"),
|
||||
method: "GET"
|
||||
}).then(function (_ref4) {
|
||||
var data = _ref4.data;
|
||||
return data.response;
|
||||
});
|
||||
},
|
||||
createGoodsOutNote: function createGoodsOutNote(orderId, data) {
|
||||
return _this.client_.request({
|
||||
url: "/warehouse-service/order/".concat(orderId, "/goods-note/goods-out"),
|
||||
method: "POST",
|
||||
data: data
|
||||
}).then(function (_ref5) {
|
||||
var data = _ref5.data;
|
||||
return data.response;
|
||||
});
|
||||
},
|
||||
updateGoodsOutNote: function updateGoodsOutNote(noteId, update) {
|
||||
return _this.client_.request({
|
||||
url: "/warehouse-service/goods-note/goods-out/".concat(noteId),
|
||||
method: "PUT",
|
||||
data: update
|
||||
});
|
||||
},
|
||||
registerGoodsOutEvent: function registerGoodsOutEvent(noteId, data) {
|
||||
return _this.client_.request({
|
||||
url: "/warehouse-service/goods-note/goods-out/".concat(noteId, "/event"),
|
||||
method: "POST",
|
||||
data: data
|
||||
});
|
||||
},
|
||||
createReservation: function createReservation(order, warehouse) {
|
||||
var id = order.id;
|
||||
var data = order.rows.map(function (r) {
|
||||
return {
|
||||
productId: r.productId,
|
||||
salesOrderRowId: r.id,
|
||||
quantity: r.quantity
|
||||
};
|
||||
});
|
||||
return _this.client_.request({
|
||||
url: "/warehouse-service/order/".concat(id, "/reservation/warehouse/").concat(warehouse),
|
||||
method: "POST",
|
||||
data: {
|
||||
products: data
|
||||
}
|
||||
}).then(function (_ref6) {
|
||||
var data = _ref6.data;
|
||||
return data.response;
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
_defineProperty(this, "buildOrderEndpoints", function () {
|
||||
return {
|
||||
retrieve: function retrieve(orderId) {
|
||||
return _this.client_.request({
|
||||
url: "/order-service/sales-order/".concat(orderId),
|
||||
method: "GET"
|
||||
}).then(function (_ref7) {
|
||||
var data = _ref7.data;
|
||||
return data.response.length && data.response[0];
|
||||
})["catch"](function (err) {
|
||||
return console.log(err);
|
||||
});
|
||||
},
|
||||
create: function create(order) {
|
||||
return _this.client_.request({
|
||||
url: "/order-service/sales-order",
|
||||
method: "POST",
|
||||
data: order
|
||||
}).then(function (_ref8) {
|
||||
var data = _ref8.data;
|
||||
return data.response;
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
_defineProperty(this, "buildAddressEndpoints", function () {
|
||||
return {
|
||||
create: function create(address) {
|
||||
return _this.client_.request({
|
||||
url: "/contact-service/postal-address",
|
||||
method: "POST",
|
||||
data: address
|
||||
}).then(function (_ref9) {
|
||||
var data = _ref9.data;
|
||||
return data.response;
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
_defineProperty(this, "buildProductEndpoints", function () {
|
||||
return {
|
||||
retrieveAvailability: function retrieveAvailability(productId) {
|
||||
return _this.client_.request({
|
||||
url: "/warehouse-service/product-availability/".concat(productId)
|
||||
}).then(function (_ref10) {
|
||||
var data = _ref10.data;
|
||||
return data.response && data.response;
|
||||
});
|
||||
},
|
||||
retrieve: function retrieve(productId) {
|
||||
return _this.client_.request({
|
||||
url: "/product-service/product/".concat(productId)
|
||||
}).then(function (_ref11) {
|
||||
var data = _ref11.data;
|
||||
return data.response && data.response[0];
|
||||
});
|
||||
},
|
||||
retrieveBySKU: function retrieveBySKU(sku) {
|
||||
return _this.client_.request({
|
||||
url: "/product-service/product-search?SKU=".concat(sku)
|
||||
}).then(function (_ref12) {
|
||||
var data = _ref12.data;
|
||||
return _this.buildSearchResults_(data.response);
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
_defineProperty(this, "buildCustomerEndpoints", function () {
|
||||
return {
|
||||
retrieveByEmail: function retrieveByEmail(email) {
|
||||
return _this.client_.request({
|
||||
url: "/contact-service/contact-search?primaryEmail=".concat(email)
|
||||
}).then(function (_ref13) {
|
||||
var data = _ref13.data;
|
||||
return _this.buildSearchResults_(data.response);
|
||||
});
|
||||
},
|
||||
create: function create(customerData) {
|
||||
return _this.client_.request({
|
||||
url: "/contact-service/contact",
|
||||
method: "POST",
|
||||
data: customerData
|
||||
}).then(function (_ref14) {
|
||||
var data = _ref14.data;
|
||||
return data.response;
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
this.client_ = _axios["default"].create({
|
||||
baseURL: "".concat(options.url, "/public-api/").concat(options.account),
|
||||
headers: {
|
||||
"brightpearl-app-ref": "medusa-dev",
|
||||
"brightpearl-dev-ref": "sebrindom",
|
||||
Authorization: "".concat(options.auth_type, " ").concat(options.access_token)
|
||||
}
|
||||
});
|
||||
this.webhooks = this.buildWebhookEndpoints();
|
||||
this.payments = this.buildPaymentEndpoints();
|
||||
this.warehouses = this.buildWarehouseEndpoints();
|
||||
this.orders = this.buildOrderEndpoints();
|
||||
this.addresses = this.buildAddressEndpoints();
|
||||
this.customers = this.buildCustomerEndpoints();
|
||||
this.products = this.buildProductEndpoints();
|
||||
}
|
||||
|
||||
_createClass(BrightpearlClient, [{
|
||||
key: "buildSearchResults_",
|
||||
value: function buildSearchResults_(response) {
|
||||
var results = response.results,
|
||||
metaData = response.metaData; // Map the column names to the columns
|
||||
|
||||
return results.map(function (resColumns) {
|
||||
var object = {};
|
||||
|
||||
for (var i = 0; i < resColumns.length; i++) {
|
||||
var fieldName = metaData.columns[i].name;
|
||||
object[fieldName] = resColumns[i];
|
||||
}
|
||||
|
||||
return object;
|
||||
});
|
||||
}
|
||||
}]);
|
||||
|
||||
return BrightpearlClient;
|
||||
}();
|
||||
|
||||
var _default = BrightpearlClient;
|
||||
exports["default"] = _default;
|
||||
@@ -14,6 +14,7 @@
|
||||
"@babel/core": "^7.7.5",
|
||||
"@babel/node": "^7.7.4",
|
||||
"@babel/plugin-proposal-class-properties": "^7.7.4",
|
||||
"@babel/plugin-transform-classes": "^7.9.5",
|
||||
"@babel/plugin-transform-instanceof": "^7.8.3",
|
||||
"@babel/plugin-transform-runtime": "^7.7.6",
|
||||
"@babel/preset-env": "^7.7.5",
|
||||
|
||||
@@ -497,7 +497,7 @@
|
||||
dependencies:
|
||||
"@babel/helper-plugin-utils" "^7.10.4"
|
||||
|
||||
"@babel/plugin-transform-classes@^7.10.4":
|
||||
"@babel/plugin-transform-classes@^7.10.4", "@babel/plugin-transform-classes@^7.9.5":
|
||||
version "7.10.4"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.10.4.tgz#405136af2b3e218bc4a1926228bc917ab1a0adc7"
|
||||
integrity sha512-2oZ9qLjt161dn1ZE0Ms66xBncQH4In8Sqw1YWgBUZuGVJJS5c0OFZXL6dP2MRHrkU/eKhWg8CzFJhRQl50rQxA==
|
||||
|
||||
@@ -881,44 +881,6 @@
|
||||
exec-sh "^0.3.2"
|
||||
minimist "^1.2.0"
|
||||
|
||||
"@hapi/address@^2.1.2":
|
||||
version "2.1.4"
|
||||
resolved "https://registry.yarnpkg.com/@hapi/address/-/address-2.1.4.tgz#5d67ed43f3fd41a69d4b9ff7b56e7c0d1d0a81e5"
|
||||
integrity sha512-QD1PhQk+s31P1ixsX0H0Suoupp3VMXzIVMSwobR3F3MSUO2YCV0B7xqLcUw/Bh8yuvd3LhpyqLQWTNcRmp6IdQ==
|
||||
|
||||
"@hapi/formula@^1.2.0":
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@hapi/formula/-/formula-1.2.0.tgz#994649c7fea1a90b91a0a1e6d983523f680e10cd"
|
||||
integrity sha512-UFbtbGPjstz0eWHb+ga/GM3Z9EzqKXFWIbSOFURU0A/Gku0Bky4bCk9/h//K2Xr3IrCfjFNhMm4jyZ5dbCewGA==
|
||||
|
||||
"@hapi/hoek@^8.2.4", "@hapi/hoek@^8.3.0":
|
||||
version "8.5.1"
|
||||
resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-8.5.1.tgz#fde96064ca446dec8c55a8c2f130957b070c6e06"
|
||||
integrity sha512-yN7kbciD87WzLGc5539Tn0sApjyiGHAJgKvG9W8C7O+6c7qmoQMfVs0W4bX17eqz6C78QJqqFrtgdK5EWf6Qow==
|
||||
|
||||
"@hapi/joi@^16.1.8":
|
||||
version "16.1.8"
|
||||
resolved "https://registry.yarnpkg.com/@hapi/joi/-/joi-16.1.8.tgz#84c1f126269489871ad4e2decc786e0adef06839"
|
||||
integrity sha512-wAsVvTPe+FwSrsAurNt5vkg3zo+TblvC5Bb1zMVK6SJzZqw9UrJnexxR+76cpePmtUZKHAPxcQ2Bf7oVHyahhg==
|
||||
dependencies:
|
||||
"@hapi/address" "^2.1.2"
|
||||
"@hapi/formula" "^1.2.0"
|
||||
"@hapi/hoek" "^8.2.4"
|
||||
"@hapi/pinpoint" "^1.0.2"
|
||||
"@hapi/topo" "^3.1.3"
|
||||
|
||||
"@hapi/pinpoint@^1.0.2":
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@hapi/pinpoint/-/pinpoint-1.0.2.tgz#025b7a36dbbf4d35bf1acd071c26b20ef41e0d13"
|
||||
integrity sha512-dtXC/WkZBfC5vxscazuiJ6iq4j9oNx1SHknmIr8hofarpKUZKmlUVYVIhNVzIEgK5Wrc4GMHL5lZtt1uS2flmQ==
|
||||
|
||||
"@hapi/topo@^3.1.3":
|
||||
version "3.1.6"
|
||||
resolved "https://registry.yarnpkg.com/@hapi/topo/-/topo-3.1.6.tgz#68d935fa3eae7fdd5ab0d7f953f3205d8b2bfc29"
|
||||
integrity sha512-tAag0jEcjwH+P2quUfipd7liWCNX2F8NvYjQp2wtInsZxnMlypdw0FtAOLxtvvkO+GSRRbmNi8m/5y42PQJYCQ==
|
||||
dependencies:
|
||||
"@hapi/hoek" "^8.3.0"
|
||||
|
||||
"@istanbuljs/load-nyc-config@^1.0.0":
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced"
|
||||
@@ -1500,19 +1462,6 @@ bindings@^1.5.0:
|
||||
dependencies:
|
||||
file-uri-to-path "1.0.0"
|
||||
|
||||
bl@^2.2.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/bl/-/bl-2.2.0.tgz#e1a574cdf528e4053019bb800b041c0ac88da493"
|
||||
integrity sha512-wbgvOpqopSr7uq6fJrLH8EsvYMJf9gzfo2jCsL2eTy75qXPukA4pCgHamOQkZtY5vmfVtjB+P3LNlMHW5CEZXA==
|
||||
dependencies:
|
||||
readable-stream "^2.3.5"
|
||||
safe-buffer "^5.1.1"
|
||||
|
||||
bluebird@3.5.1:
|
||||
version "3.5.1"
|
||||
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.1.tgz#d9551f9de98f1fcda1e683d17ee91a0602ee2eb9"
|
||||
integrity sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA==
|
||||
|
||||
body-parser@1.19.0, body-parser@^1.19.0:
|
||||
version "1.19.0"
|
||||
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a"
|
||||
@@ -1589,11 +1538,6 @@ bser@2.1.1:
|
||||
dependencies:
|
||||
node-int64 "^0.4.0"
|
||||
|
||||
bson@^1.1.4:
|
||||
version "1.1.4"
|
||||
resolved "https://registry.yarnpkg.com/bson/-/bson-1.1.4.tgz#f76870d799f15b854dffb7ee32f0a874797f7e89"
|
||||
integrity sha512-S/yKGU1syOMzO86+dGpg2qGoDL0zvzcb262G+gqEy6TgP6rt6z6qxSFX/8X6vLC91P7G7C3nLs0+bvDzmvBA3Q==
|
||||
|
||||
buffer-from@^1.0.0:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef"
|
||||
@@ -1934,7 +1878,7 @@ debug@2.6.9, debug@^2.2.0, debug@^2.3.3:
|
||||
dependencies:
|
||||
ms "2.0.0"
|
||||
|
||||
debug@3.1.0, debug@=3.1.0:
|
||||
debug@=3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
|
||||
integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==
|
||||
@@ -2002,11 +1946,6 @@ delayed-stream@~1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
|
||||
integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk=
|
||||
|
||||
denque@^1.4.1:
|
||||
version "1.4.1"
|
||||
resolved "https://registry.yarnpkg.com/denque/-/denque-1.4.1.tgz#6744ff7641c148c3f8a69c307e51235c1f4a37cf"
|
||||
integrity sha512-OfzPuSZKGcgr96rf1oODnfjqBFmr1DVoc/TrItj3Ohe0Ah1C5WX5Baquw/9U9KovnQ88EqmJbD66rKYUQYN1tQ==
|
||||
|
||||
depd@~1.1.2:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9"
|
||||
@@ -3516,11 +3455,6 @@ jest@^25.5.2:
|
||||
import-local "^3.0.2"
|
||||
jest-cli "^25.5.4"
|
||||
|
||||
joi-objectid@^3.0.1:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/joi-objectid/-/joi-objectid-3.0.1.tgz#63ace7860f8e1a993a28d40c40ffd8eff01a3668"
|
||||
integrity sha512-V/3hbTlGpvJ03Me6DJbdBI08hBTasFOmipsauOsxOSnsF1blxV537WTl1zPwbfcKle4AK0Ma4OPnzMH4LlvTpQ==
|
||||
|
||||
"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
|
||||
@@ -3623,11 +3557,6 @@ jsprim@^1.2.2:
|
||||
json-schema "0.2.3"
|
||||
verror "1.10.0"
|
||||
|
||||
kareem@2.3.1:
|
||||
version "2.3.1"
|
||||
resolved "https://registry.yarnpkg.com/kareem/-/kareem-2.3.1.tgz#def12d9c941017fabfb00f873af95e9c99e1be87"
|
||||
integrity sha512-l3hLhffs9zqoDe8zjmb/mAN4B8VT3L56EUvKNqLFVs9YlFA+zx7ke1DO8STAdDyYNkeSo1nKmjuvQeI12So8Xw==
|
||||
|
||||
keygrip@~1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/keygrip/-/keygrip-1.0.3.tgz#399d709f0aed2bab0a059e0cdd3a5023a053e1dc"
|
||||
@@ -3765,33 +3694,6 @@ 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@^0.3.0:
|
||||
version "0.1.39"
|
||||
resolved "https://registry.yarnpkg.com/medusa-core-utils/-/medusa-core-utils-0.1.39.tgz#d57816c9bd43f9a92883650c1e66add1665291df"
|
||||
integrity sha512-R8+U1ile7if+nR6Cjh5exunx0ETV0OfkWUUBUpz1KmHSDv0V0CcvQqU9lcZesPFDEbu3Y2iEjsCqidVA4nG2nQ==
|
||||
dependencies:
|
||||
"@hapi/joi" "^16.1.8"
|
||||
joi-objectid "^3.0.1"
|
||||
|
||||
medusa-interfaces@^0.3.0:
|
||||
version "0.1.39"
|
||||
resolved "https://registry.yarnpkg.com/medusa-interfaces/-/medusa-interfaces-0.1.39.tgz#633db3c8c6afd7fec9ae24496737369d840105cf"
|
||||
integrity sha512-byKIcK7o3L4shmGn+pgZAUyLrT991zCqK4jWXIleQJbGImQy6TmdXido+tEzFptVBJWMIQ8BWnP/b7r29D8EXA==
|
||||
dependencies:
|
||||
mongoose "^5.8.0"
|
||||
|
||||
medusa-test-utils@^0.3.0:
|
||||
version "0.1.39"
|
||||
resolved "https://registry.yarnpkg.com/medusa-test-utils/-/medusa-test-utils-0.1.39.tgz#b7c166006a2fa4f02e52ab3bfafc19a3ae787f3e"
|
||||
integrity sha512-M/Br8/HYvl7x2oLnme4NxdQwoyV0XUyOWiCyvPp7q1HUTB684lhJf1MikZVrcSjsh2L1rpyi3GRbKdf4cpJWvw==
|
||||
dependencies:
|
||||
mongoose "^5.8.0"
|
||||
|
||||
memory-pager@^1.0.2:
|
||||
version "1.5.0"
|
||||
resolved "https://registry.yarnpkg.com/memory-pager/-/memory-pager-1.5.0.tgz#d8751655d22d384682741c972f2c3d6dfa3e66b5"
|
||||
integrity sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==
|
||||
|
||||
merge-descriptors@1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61"
|
||||
@@ -3888,57 +3790,6 @@ moment@^2.27.0:
|
||||
resolved "https://registry.yarnpkg.com/moment/-/moment-2.27.0.tgz#8bff4e3e26a236220dfe3e36de756b6ebaa0105d"
|
||||
integrity sha512-al0MUK7cpIcglMv3YF13qSgdAIqxHTO7brRtaz3DlSULbqfazqkc5kEjNrLDOM7fsjshoFIihnU8snrP7zUvhQ==
|
||||
|
||||
mongodb@3.5.9:
|
||||
version "3.5.9"
|
||||
resolved "https://registry.yarnpkg.com/mongodb/-/mongodb-3.5.9.tgz#799b72be8110b7e71a882bb7ce0d84d05429f772"
|
||||
integrity sha512-vXHBY1CsGYcEPoVWhwgxIBeWqP3dSu9RuRDsoLRPTITrcrgm1f0Ubu1xqF9ozMwv53agmEiZm0YGo+7WL3Nbug==
|
||||
dependencies:
|
||||
bl "^2.2.0"
|
||||
bson "^1.1.4"
|
||||
denque "^1.4.1"
|
||||
require_optional "^1.0.1"
|
||||
safe-buffer "^5.1.2"
|
||||
optionalDependencies:
|
||||
saslprep "^1.0.0"
|
||||
|
||||
mongoose-legacy-pluralize@1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/mongoose-legacy-pluralize/-/mongoose-legacy-pluralize-1.0.2.tgz#3ba9f91fa507b5186d399fb40854bff18fb563e4"
|
||||
integrity sha512-Yo/7qQU4/EyIS8YDFSeenIvXxZN+ld7YdV9LqFVQJzTLye8unujAWPZ4NWKfFA+RNjh+wvTWKY9Z3E5XM6ZZiQ==
|
||||
|
||||
mongoose@^5.8.0:
|
||||
version "5.9.23"
|
||||
resolved "https://registry.yarnpkg.com/mongoose/-/mongoose-5.9.23.tgz#cb16687fbd19082bbbf0b8fab4778e2e4d85e7b1"
|
||||
integrity sha512-fMYlMRJz0T6Ax2K2P0jt+kxXd4qaRxyfZCha1YBMczmA2EBlT5SnBlcDyJ4YQa4/z+GoDh06uH090w7BfBcdWg==
|
||||
dependencies:
|
||||
bson "^1.1.4"
|
||||
kareem "2.3.1"
|
||||
mongodb "3.5.9"
|
||||
mongoose-legacy-pluralize "1.0.2"
|
||||
mpath "0.7.0"
|
||||
mquery "3.2.2"
|
||||
ms "2.1.2"
|
||||
regexp-clone "1.0.0"
|
||||
safe-buffer "5.2.1"
|
||||
sift "7.0.1"
|
||||
sliced "1.0.1"
|
||||
|
||||
mpath@0.7.0:
|
||||
version "0.7.0"
|
||||
resolved "https://registry.yarnpkg.com/mpath/-/mpath-0.7.0.tgz#20e8102e276b71709d6e07e9f8d4d0f641afbfb8"
|
||||
integrity sha512-Aiq04hILxhz1L+f7sjGyn7IxYzWm1zLNNXcfhDtx04kZ2Gk7uvFdgZ8ts1cWa/6d0TQmag2yR8zSGZUmp0tFNg==
|
||||
|
||||
mquery@3.2.2:
|
||||
version "3.2.2"
|
||||
resolved "https://registry.yarnpkg.com/mquery/-/mquery-3.2.2.tgz#e1383a3951852ce23e37f619a9b350f1fb3664e7"
|
||||
integrity sha512-XB52992COp0KP230I3qloVUbkLUxJIu328HBP2t2EsxSFtf4W1HPSOBWOXf1bqxK4Xbb66lfMJ+Bpfd9/yZE1Q==
|
||||
dependencies:
|
||||
bluebird "3.5.1"
|
||||
debug "3.1.0"
|
||||
regexp-clone "^1.0.0"
|
||||
safe-buffer "5.1.2"
|
||||
sliced "1.0.1"
|
||||
|
||||
ms@2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
|
||||
@@ -3949,7 +3800,7 @@ ms@2.1.1:
|
||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a"
|
||||
integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==
|
||||
|
||||
ms@2.1.2, ms@^2.1.1:
|
||||
ms@^2.1.1:
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
|
||||
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
|
||||
@@ -4437,7 +4288,7 @@ read-pkg@^5.2.0:
|
||||
parse-json "^5.0.0"
|
||||
type-fest "^0.6.0"
|
||||
|
||||
readable-stream@^2.0.2, readable-stream@^2.3.5:
|
||||
readable-stream@^2.0.2:
|
||||
version "2.3.7"
|
||||
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57"
|
||||
integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==
|
||||
@@ -4496,11 +4347,6 @@ regex-not@^1.0.0, regex-not@^1.0.2:
|
||||
extend-shallow "^3.0.2"
|
||||
safe-regex "^1.1.0"
|
||||
|
||||
regexp-clone@1.0.0, regexp-clone@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/regexp-clone/-/regexp-clone-1.0.0.tgz#222db967623277056260b992626354a04ce9bf63"
|
||||
integrity sha512-TuAasHQNamyyJ2hb97IuBEif4qBHGjPHBS64sZwytpLEqtBQ1gPJTnOaQ6qmpET16cK14kkjbazl6+p0RRv0yw==
|
||||
|
||||
regexpp@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f"
|
||||
@@ -4597,14 +4443,6 @@ require-main-filename@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b"
|
||||
integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==
|
||||
|
||||
require_optional@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/require_optional/-/require_optional-1.0.1.tgz#4cf35a4247f64ca3df8c2ef208cc494b1ca8fc2e"
|
||||
integrity sha512-qhM/y57enGWHAe3v/NcwML6a3/vfESLe/sGM2dII+gEO0BpKRUkWZow/tyloNqJyN6kXSl3RyyM8Ll5D/sJP8g==
|
||||
dependencies:
|
||||
resolve-from "^2.0.0"
|
||||
semver "^5.1.0"
|
||||
|
||||
resolve-cwd@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d"
|
||||
@@ -4612,11 +4450,6 @@ resolve-cwd@^3.0.0:
|
||||
dependencies:
|
||||
resolve-from "^5.0.0"
|
||||
|
||||
resolve-from@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-2.0.0.tgz#9480ab20e94ffa1d9e80a804c7ea147611966b57"
|
||||
integrity sha1-lICrIOlP+h2egKgEx+oUdhGWa1c=
|
||||
|
||||
resolve-from@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6"
|
||||
@@ -4693,7 +4526,7 @@ safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
|
||||
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
|
||||
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
|
||||
|
||||
safe-buffer@5.2.1, safe-buffer@^5.0.1, safe-buffer@^5.1.1, safe-buffer@^5.1.2:
|
||||
safe-buffer@^5.0.1, safe-buffer@^5.1.2:
|
||||
version "5.2.1"
|
||||
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
|
||||
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
|
||||
@@ -4725,13 +4558,6 @@ sane@^4.0.3:
|
||||
minimist "^1.1.1"
|
||||
walker "~1.0.5"
|
||||
|
||||
saslprep@^1.0.0:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/saslprep/-/saslprep-1.0.3.tgz#4c02f946b56cf54297e347ba1093e7acac4cf226"
|
||||
integrity sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag==
|
||||
dependencies:
|
||||
sparse-bitfield "^3.0.3"
|
||||
|
||||
saxes@^3.1.9:
|
||||
version "3.1.11"
|
||||
resolved "https://registry.yarnpkg.com/saxes/-/saxes-3.1.11.tgz#d59d1fd332ec92ad98a2e0b2ee644702384b1c5b"
|
||||
@@ -4739,7 +4565,7 @@ saxes@^3.1.9:
|
||||
dependencies:
|
||||
xmlchars "^2.1.1"
|
||||
|
||||
"semver@2 || 3 || 4 || 5", semver@^5.1.0, semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0, semver@^5.7.0:
|
||||
"semver@2 || 3 || 4 || 5", semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0, semver@^5.7.0:
|
||||
version "5.7.1"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
|
||||
integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
|
||||
@@ -4832,11 +4658,6 @@ shellwords@^0.1.1:
|
||||
resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b"
|
||||
integrity sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==
|
||||
|
||||
sift@7.0.1:
|
||||
version "7.0.1"
|
||||
resolved "https://registry.yarnpkg.com/sift/-/sift-7.0.1.tgz#47d62c50b159d316f1372f8b53f9c10cd21a4b08"
|
||||
integrity sha512-oqD7PMJ+uO6jV9EQCl0LrRw1OwsiPsiFQR5AR30heR+4Dl7jBBbDLnNvWiak20tzZlSE1H7RB30SX/1j/YYT7g==
|
||||
|
||||
signal-exit@^3.0.0, signal-exit@^3.0.2:
|
||||
version "3.0.3"
|
||||
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c"
|
||||
@@ -4866,11 +4687,6 @@ slice-ansi@^2.1.0:
|
||||
astral-regex "^1.0.0"
|
||||
is-fullwidth-code-point "^2.0.0"
|
||||
|
||||
sliced@1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/sliced/-/sliced-1.0.1.tgz#0b3a662b5d04c3177b1926bea82b03f837a2ef41"
|
||||
integrity sha1-CzpmK10Ewxd7GSa+qCsD+Dei70E=
|
||||
|
||||
snapdragon-node@^2.0.1:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b"
|
||||
@@ -4940,13 +4756,6 @@ source-map@^0.7.3:
|
||||
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383"
|
||||
integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==
|
||||
|
||||
sparse-bitfield@^3.0.3:
|
||||
version "3.0.3"
|
||||
resolved "https://registry.yarnpkg.com/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz#ff4ae6e68656056ba4b3e792ab3334d38273ca11"
|
||||
integrity sha1-/0rm5oZWBWuks+eSqzM004JzyhE=
|
||||
dependencies:
|
||||
memory-pager "^1.0.2"
|
||||
|
||||
spdx-correct@^3.0.0:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9"
|
||||
|
||||
@@ -50,7 +50,6 @@ class SegmentService extends BaseService {
|
||||
|
||||
|
||||
async buildOrder(order) {
|
||||
console.log("build", order)
|
||||
const subtotal = await this.totalsService_.getSubtotal(order)
|
||||
const total = await this.totalsService_.getTotal(order)
|
||||
const tax = await this.totalsService_.getTaxTotal(order)
|
||||
|
||||
@@ -33,6 +33,9 @@ class SendGridService extends BaseService {
|
||||
async transactionalEmail(event, data) {
|
||||
let templateId
|
||||
switch (event) {
|
||||
case "order.gift_card_created":
|
||||
templateId = this.options_.gift_card_created_template
|
||||
break
|
||||
case "order.placed":
|
||||
templateId = this.options_.order_placed_template
|
||||
break
|
||||
@@ -55,12 +58,14 @@ class SendGridService extends BaseService {
|
||||
return
|
||||
}
|
||||
try {
|
||||
return SendGrid.send({
|
||||
template_id: templateId,
|
||||
from: this.options_.from,
|
||||
to: data.email,
|
||||
dynamic_template_data: data,
|
||||
})
|
||||
if (templateId) {
|
||||
return SendGrid.send({
|
||||
template_id: templateId,
|
||||
from: this.options_.from,
|
||||
to: data.email,
|
||||
dynamic_template_data: data,
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
|
||||
@@ -4,6 +4,10 @@ class OrderSubscriber {
|
||||
|
||||
this.eventBus_ = eventBusService
|
||||
|
||||
this.eventBus_.subscribe("order.gift_card_created", async (order) => {
|
||||
await this.sendgridService_.transactionalEmail("order.gift_card_created", order)
|
||||
})
|
||||
|
||||
this.eventBus_.subscribe("order.placed", async (order) => {
|
||||
await this.sendgridService_.transactionalEmail("order.placed", order)
|
||||
})
|
||||
|
||||
@@ -881,44 +881,6 @@
|
||||
exec-sh "^0.3.2"
|
||||
minimist "^1.2.0"
|
||||
|
||||
"@hapi/address@^2.1.2":
|
||||
version "2.1.4"
|
||||
resolved "https://registry.yarnpkg.com/@hapi/address/-/address-2.1.4.tgz#5d67ed43f3fd41a69d4b9ff7b56e7c0d1d0a81e5"
|
||||
integrity sha512-QD1PhQk+s31P1ixsX0H0Suoupp3VMXzIVMSwobR3F3MSUO2YCV0B7xqLcUw/Bh8yuvd3LhpyqLQWTNcRmp6IdQ==
|
||||
|
||||
"@hapi/formula@^1.2.0":
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@hapi/formula/-/formula-1.2.0.tgz#994649c7fea1a90b91a0a1e6d983523f680e10cd"
|
||||
integrity sha512-UFbtbGPjstz0eWHb+ga/GM3Z9EzqKXFWIbSOFURU0A/Gku0Bky4bCk9/h//K2Xr3IrCfjFNhMm4jyZ5dbCewGA==
|
||||
|
||||
"@hapi/hoek@^8.2.4", "@hapi/hoek@^8.3.0":
|
||||
version "8.5.1"
|
||||
resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-8.5.1.tgz#fde96064ca446dec8c55a8c2f130957b070c6e06"
|
||||
integrity sha512-yN7kbciD87WzLGc5539Tn0sApjyiGHAJgKvG9W8C7O+6c7qmoQMfVs0W4bX17eqz6C78QJqqFrtgdK5EWf6Qow==
|
||||
|
||||
"@hapi/joi@^16.1.8":
|
||||
version "16.1.8"
|
||||
resolved "https://registry.yarnpkg.com/@hapi/joi/-/joi-16.1.8.tgz#84c1f126269489871ad4e2decc786e0adef06839"
|
||||
integrity sha512-wAsVvTPe+FwSrsAurNt5vkg3zo+TblvC5Bb1zMVK6SJzZqw9UrJnexxR+76cpePmtUZKHAPxcQ2Bf7oVHyahhg==
|
||||
dependencies:
|
||||
"@hapi/address" "^2.1.2"
|
||||
"@hapi/formula" "^1.2.0"
|
||||
"@hapi/hoek" "^8.2.4"
|
||||
"@hapi/pinpoint" "^1.0.2"
|
||||
"@hapi/topo" "^3.1.3"
|
||||
|
||||
"@hapi/pinpoint@^1.0.2":
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@hapi/pinpoint/-/pinpoint-1.0.2.tgz#025b7a36dbbf4d35bf1acd071c26b20ef41e0d13"
|
||||
integrity sha512-dtXC/WkZBfC5vxscazuiJ6iq4j9oNx1SHknmIr8hofarpKUZKmlUVYVIhNVzIEgK5Wrc4GMHL5lZtt1uS2flmQ==
|
||||
|
||||
"@hapi/topo@^3.1.3":
|
||||
version "3.1.6"
|
||||
resolved "https://registry.yarnpkg.com/@hapi/topo/-/topo-3.1.6.tgz#68d935fa3eae7fdd5ab0d7f953f3205d8b2bfc29"
|
||||
integrity sha512-tAag0jEcjwH+P2quUfipd7liWCNX2F8NvYjQp2wtInsZxnMlypdw0FtAOLxtvvkO+GSRRbmNi8m/5y42PQJYCQ==
|
||||
dependencies:
|
||||
"@hapi/hoek" "^8.3.0"
|
||||
|
||||
"@istanbuljs/load-nyc-config@^1.0.0":
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced"
|
||||
@@ -1500,19 +1462,6 @@ bindings@^1.5.0:
|
||||
dependencies:
|
||||
file-uri-to-path "1.0.0"
|
||||
|
||||
bl@^2.2.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/bl/-/bl-2.2.0.tgz#e1a574cdf528e4053019bb800b041c0ac88da493"
|
||||
integrity sha512-wbgvOpqopSr7uq6fJrLH8EsvYMJf9gzfo2jCsL2eTy75qXPukA4pCgHamOQkZtY5vmfVtjB+P3LNlMHW5CEZXA==
|
||||
dependencies:
|
||||
readable-stream "^2.3.5"
|
||||
safe-buffer "^5.1.1"
|
||||
|
||||
bluebird@3.5.1:
|
||||
version "3.5.1"
|
||||
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.1.tgz#d9551f9de98f1fcda1e683d17ee91a0602ee2eb9"
|
||||
integrity sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA==
|
||||
|
||||
body-parser@1.19.0, body-parser@^1.19.0:
|
||||
version "1.19.0"
|
||||
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a"
|
||||
@@ -1589,11 +1538,6 @@ bser@2.1.1:
|
||||
dependencies:
|
||||
node-int64 "^0.4.0"
|
||||
|
||||
bson@^1.1.4:
|
||||
version "1.1.4"
|
||||
resolved "https://registry.yarnpkg.com/bson/-/bson-1.1.4.tgz#f76870d799f15b854dffb7ee32f0a874797f7e89"
|
||||
integrity sha512-S/yKGU1syOMzO86+dGpg2qGoDL0zvzcb262G+gqEy6TgP6rt6z6qxSFX/8X6vLC91P7G7C3nLs0+bvDzmvBA3Q==
|
||||
|
||||
buffer-from@^1.0.0:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef"
|
||||
@@ -1934,7 +1878,7 @@ debug@2.6.9, debug@^2.2.0, debug@^2.3.3:
|
||||
dependencies:
|
||||
ms "2.0.0"
|
||||
|
||||
debug@3.1.0, debug@=3.1.0:
|
||||
debug@=3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
|
||||
integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==
|
||||
@@ -2002,11 +1946,6 @@ delayed-stream@~1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
|
||||
integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk=
|
||||
|
||||
denque@^1.4.1:
|
||||
version "1.4.1"
|
||||
resolved "https://registry.yarnpkg.com/denque/-/denque-1.4.1.tgz#6744ff7641c148c3f8a69c307e51235c1f4a37cf"
|
||||
integrity sha512-OfzPuSZKGcgr96rf1oODnfjqBFmr1DVoc/TrItj3Ohe0Ah1C5WX5Baquw/9U9KovnQ88EqmJbD66rKYUQYN1tQ==
|
||||
|
||||
depd@~1.1.2:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9"
|
||||
@@ -3516,11 +3455,6 @@ jest@^25.5.2:
|
||||
import-local "^3.0.2"
|
||||
jest-cli "^25.5.4"
|
||||
|
||||
joi-objectid@^3.0.1:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/joi-objectid/-/joi-objectid-3.0.1.tgz#63ace7860f8e1a993a28d40c40ffd8eff01a3668"
|
||||
integrity sha512-V/3hbTlGpvJ03Me6DJbdBI08hBTasFOmipsauOsxOSnsF1blxV537WTl1zPwbfcKle4AK0Ma4OPnzMH4LlvTpQ==
|
||||
|
||||
"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
|
||||
@@ -3623,11 +3557,6 @@ jsprim@^1.2.2:
|
||||
json-schema "0.2.3"
|
||||
verror "1.10.0"
|
||||
|
||||
kareem@2.3.1:
|
||||
version "2.3.1"
|
||||
resolved "https://registry.yarnpkg.com/kareem/-/kareem-2.3.1.tgz#def12d9c941017fabfb00f873af95e9c99e1be87"
|
||||
integrity sha512-l3hLhffs9zqoDe8zjmb/mAN4B8VT3L56EUvKNqLFVs9YlFA+zx7ke1DO8STAdDyYNkeSo1nKmjuvQeI12So8Xw==
|
||||
|
||||
keygrip@~1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/keygrip/-/keygrip-1.0.3.tgz#399d709f0aed2bab0a059e0cdd3a5023a053e1dc"
|
||||
@@ -3765,33 +3694,6 @@ 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@^0.3.0:
|
||||
version "0.1.39"
|
||||
resolved "https://registry.yarnpkg.com/medusa-core-utils/-/medusa-core-utils-0.1.39.tgz#d57816c9bd43f9a92883650c1e66add1665291df"
|
||||
integrity sha512-R8+U1ile7if+nR6Cjh5exunx0ETV0OfkWUUBUpz1KmHSDv0V0CcvQqU9lcZesPFDEbu3Y2iEjsCqidVA4nG2nQ==
|
||||
dependencies:
|
||||
"@hapi/joi" "^16.1.8"
|
||||
joi-objectid "^3.0.1"
|
||||
|
||||
medusa-interfaces@^0.3.0:
|
||||
version "0.1.39"
|
||||
resolved "https://registry.yarnpkg.com/medusa-interfaces/-/medusa-interfaces-0.1.39.tgz#633db3c8c6afd7fec9ae24496737369d840105cf"
|
||||
integrity sha512-byKIcK7o3L4shmGn+pgZAUyLrT991zCqK4jWXIleQJbGImQy6TmdXido+tEzFptVBJWMIQ8BWnP/b7r29D8EXA==
|
||||
dependencies:
|
||||
mongoose "^5.8.0"
|
||||
|
||||
medusa-test-utils@^0.3.0:
|
||||
version "0.1.39"
|
||||
resolved "https://registry.yarnpkg.com/medusa-test-utils/-/medusa-test-utils-0.1.39.tgz#b7c166006a2fa4f02e52ab3bfafc19a3ae787f3e"
|
||||
integrity sha512-M/Br8/HYvl7x2oLnme4NxdQwoyV0XUyOWiCyvPp7q1HUTB684lhJf1MikZVrcSjsh2L1rpyi3GRbKdf4cpJWvw==
|
||||
dependencies:
|
||||
mongoose "^5.8.0"
|
||||
|
||||
memory-pager@^1.0.2:
|
||||
version "1.5.0"
|
||||
resolved "https://registry.yarnpkg.com/memory-pager/-/memory-pager-1.5.0.tgz#d8751655d22d384682741c972f2c3d6dfa3e66b5"
|
||||
integrity sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==
|
||||
|
||||
merge-descriptors@1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61"
|
||||
@@ -3888,57 +3790,6 @@ moment@^2.27.0:
|
||||
resolved "https://registry.yarnpkg.com/moment/-/moment-2.27.0.tgz#8bff4e3e26a236220dfe3e36de756b6ebaa0105d"
|
||||
integrity sha512-al0MUK7cpIcglMv3YF13qSgdAIqxHTO7brRtaz3DlSULbqfazqkc5kEjNrLDOM7fsjshoFIihnU8snrP7zUvhQ==
|
||||
|
||||
mongodb@3.5.9:
|
||||
version "3.5.9"
|
||||
resolved "https://registry.yarnpkg.com/mongodb/-/mongodb-3.5.9.tgz#799b72be8110b7e71a882bb7ce0d84d05429f772"
|
||||
integrity sha512-vXHBY1CsGYcEPoVWhwgxIBeWqP3dSu9RuRDsoLRPTITrcrgm1f0Ubu1xqF9ozMwv53agmEiZm0YGo+7WL3Nbug==
|
||||
dependencies:
|
||||
bl "^2.2.0"
|
||||
bson "^1.1.4"
|
||||
denque "^1.4.1"
|
||||
require_optional "^1.0.1"
|
||||
safe-buffer "^5.1.2"
|
||||
optionalDependencies:
|
||||
saslprep "^1.0.0"
|
||||
|
||||
mongoose-legacy-pluralize@1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/mongoose-legacy-pluralize/-/mongoose-legacy-pluralize-1.0.2.tgz#3ba9f91fa507b5186d399fb40854bff18fb563e4"
|
||||
integrity sha512-Yo/7qQU4/EyIS8YDFSeenIvXxZN+ld7YdV9LqFVQJzTLye8unujAWPZ4NWKfFA+RNjh+wvTWKY9Z3E5XM6ZZiQ==
|
||||
|
||||
mongoose@^5.8.0:
|
||||
version "5.9.23"
|
||||
resolved "https://registry.yarnpkg.com/mongoose/-/mongoose-5.9.23.tgz#cb16687fbd19082bbbf0b8fab4778e2e4d85e7b1"
|
||||
integrity sha512-fMYlMRJz0T6Ax2K2P0jt+kxXd4qaRxyfZCha1YBMczmA2EBlT5SnBlcDyJ4YQa4/z+GoDh06uH090w7BfBcdWg==
|
||||
dependencies:
|
||||
bson "^1.1.4"
|
||||
kareem "2.3.1"
|
||||
mongodb "3.5.9"
|
||||
mongoose-legacy-pluralize "1.0.2"
|
||||
mpath "0.7.0"
|
||||
mquery "3.2.2"
|
||||
ms "2.1.2"
|
||||
regexp-clone "1.0.0"
|
||||
safe-buffer "5.2.1"
|
||||
sift "7.0.1"
|
||||
sliced "1.0.1"
|
||||
|
||||
mpath@0.7.0:
|
||||
version "0.7.0"
|
||||
resolved "https://registry.yarnpkg.com/mpath/-/mpath-0.7.0.tgz#20e8102e276b71709d6e07e9f8d4d0f641afbfb8"
|
||||
integrity sha512-Aiq04hILxhz1L+f7sjGyn7IxYzWm1zLNNXcfhDtx04kZ2Gk7uvFdgZ8ts1cWa/6d0TQmag2yR8zSGZUmp0tFNg==
|
||||
|
||||
mquery@3.2.2:
|
||||
version "3.2.2"
|
||||
resolved "https://registry.yarnpkg.com/mquery/-/mquery-3.2.2.tgz#e1383a3951852ce23e37f619a9b350f1fb3664e7"
|
||||
integrity sha512-XB52992COp0KP230I3qloVUbkLUxJIu328HBP2t2EsxSFtf4W1HPSOBWOXf1bqxK4Xbb66lfMJ+Bpfd9/yZE1Q==
|
||||
dependencies:
|
||||
bluebird "3.5.1"
|
||||
debug "3.1.0"
|
||||
regexp-clone "^1.0.0"
|
||||
safe-buffer "5.1.2"
|
||||
sliced "1.0.1"
|
||||
|
||||
ms@2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
|
||||
@@ -3949,7 +3800,7 @@ ms@2.1.1:
|
||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a"
|
||||
integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==
|
||||
|
||||
ms@2.1.2, ms@^2.1.1:
|
||||
ms@^2.1.1:
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
|
||||
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
|
||||
@@ -4437,7 +4288,7 @@ read-pkg@^5.2.0:
|
||||
parse-json "^5.0.0"
|
||||
type-fest "^0.6.0"
|
||||
|
||||
readable-stream@^2.0.2, readable-stream@^2.3.5:
|
||||
readable-stream@^2.0.2:
|
||||
version "2.3.7"
|
||||
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57"
|
||||
integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==
|
||||
@@ -4496,11 +4347,6 @@ regex-not@^1.0.0, regex-not@^1.0.2:
|
||||
extend-shallow "^3.0.2"
|
||||
safe-regex "^1.1.0"
|
||||
|
||||
regexp-clone@1.0.0, regexp-clone@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/regexp-clone/-/regexp-clone-1.0.0.tgz#222db967623277056260b992626354a04ce9bf63"
|
||||
integrity sha512-TuAasHQNamyyJ2hb97IuBEif4qBHGjPHBS64sZwytpLEqtBQ1gPJTnOaQ6qmpET16cK14kkjbazl6+p0RRv0yw==
|
||||
|
||||
regexpp@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f"
|
||||
@@ -4597,14 +4443,6 @@ require-main-filename@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b"
|
||||
integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==
|
||||
|
||||
require_optional@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/require_optional/-/require_optional-1.0.1.tgz#4cf35a4247f64ca3df8c2ef208cc494b1ca8fc2e"
|
||||
integrity sha512-qhM/y57enGWHAe3v/NcwML6a3/vfESLe/sGM2dII+gEO0BpKRUkWZow/tyloNqJyN6kXSl3RyyM8Ll5D/sJP8g==
|
||||
dependencies:
|
||||
resolve-from "^2.0.0"
|
||||
semver "^5.1.0"
|
||||
|
||||
resolve-cwd@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d"
|
||||
@@ -4612,11 +4450,6 @@ resolve-cwd@^3.0.0:
|
||||
dependencies:
|
||||
resolve-from "^5.0.0"
|
||||
|
||||
resolve-from@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-2.0.0.tgz#9480ab20e94ffa1d9e80a804c7ea147611966b57"
|
||||
integrity sha1-lICrIOlP+h2egKgEx+oUdhGWa1c=
|
||||
|
||||
resolve-from@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6"
|
||||
@@ -4693,7 +4526,7 @@ safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
|
||||
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
|
||||
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
|
||||
|
||||
safe-buffer@5.2.1, safe-buffer@^5.0.1, safe-buffer@^5.1.1, safe-buffer@^5.1.2:
|
||||
safe-buffer@^5.0.1, safe-buffer@^5.1.2:
|
||||
version "5.2.1"
|
||||
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
|
||||
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
|
||||
@@ -4725,13 +4558,6 @@ sane@^4.0.3:
|
||||
minimist "^1.1.1"
|
||||
walker "~1.0.5"
|
||||
|
||||
saslprep@^1.0.0:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/saslprep/-/saslprep-1.0.3.tgz#4c02f946b56cf54297e347ba1093e7acac4cf226"
|
||||
integrity sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag==
|
||||
dependencies:
|
||||
sparse-bitfield "^3.0.3"
|
||||
|
||||
saxes@^3.1.9:
|
||||
version "3.1.11"
|
||||
resolved "https://registry.yarnpkg.com/saxes/-/saxes-3.1.11.tgz#d59d1fd332ec92ad98a2e0b2ee644702384b1c5b"
|
||||
@@ -4739,7 +4565,7 @@ saxes@^3.1.9:
|
||||
dependencies:
|
||||
xmlchars "^2.1.1"
|
||||
|
||||
"semver@2 || 3 || 4 || 5", semver@^5.1.0, semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0, semver@^5.7.0:
|
||||
"semver@2 || 3 || 4 || 5", semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0, semver@^5.7.0:
|
||||
version "5.7.1"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
|
||||
integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
|
||||
@@ -4832,11 +4658,6 @@ shellwords@^0.1.1:
|
||||
resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b"
|
||||
integrity sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==
|
||||
|
||||
sift@7.0.1:
|
||||
version "7.0.1"
|
||||
resolved "https://registry.yarnpkg.com/sift/-/sift-7.0.1.tgz#47d62c50b159d316f1372f8b53f9c10cd21a4b08"
|
||||
integrity sha512-oqD7PMJ+uO6jV9EQCl0LrRw1OwsiPsiFQR5AR30heR+4Dl7jBBbDLnNvWiak20tzZlSE1H7RB30SX/1j/YYT7g==
|
||||
|
||||
signal-exit@^3.0.0, signal-exit@^3.0.2:
|
||||
version "3.0.3"
|
||||
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c"
|
||||
@@ -4866,11 +4687,6 @@ slice-ansi@^2.1.0:
|
||||
astral-regex "^1.0.0"
|
||||
is-fullwidth-code-point "^2.0.0"
|
||||
|
||||
sliced@1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/sliced/-/sliced-1.0.1.tgz#0b3a662b5d04c3177b1926bea82b03f837a2ef41"
|
||||
integrity sha1-CzpmK10Ewxd7GSa+qCsD+Dei70E=
|
||||
|
||||
snapdragon-node@^2.0.1:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b"
|
||||
@@ -4940,13 +4756,6 @@ source-map@^0.7.3:
|
||||
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383"
|
||||
integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==
|
||||
|
||||
sparse-bitfield@^3.0.3:
|
||||
version "3.0.3"
|
||||
resolved "https://registry.yarnpkg.com/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz#ff4ae6e68656056ba4b3e792ab3334d38273ca11"
|
||||
integrity sha1-/0rm5oZWBWuks+eSqzM004JzyhE=
|
||||
dependencies:
|
||||
memory-pager "^1.0.2"
|
||||
|
||||
spdx-correct@^3.0.0:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9"
|
||||
|
||||
@@ -67,7 +67,8 @@
|
||||
"passport-http-bearer": "^1.0.1",
|
||||
"passport-jwt": "^4.0.0",
|
||||
"passport-local": "^1.0.0",
|
||||
"randomatic": "^3.1.1",
|
||||
"winston": "^3.2.1"
|
||||
},
|
||||
"gitHead": "35e0930650d5f4aedf2610749cd131ae8b7e17cc"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,39 @@ import errorHandler from "./middlewares/error-handler"
|
||||
export default (container, config) => {
|
||||
const app = Router()
|
||||
|
||||
app.post("/create-shipment/:order_id", async (req, res) => {
|
||||
const orderService = req.scope.resolve("orderService")
|
||||
const eventBus = req.scope.resolve("eventBusService")
|
||||
const order = await orderService.retrieve(req.params.order_id)
|
||||
|
||||
await orderService.createShipment(order._id, {
|
||||
item_ids: order.items.map(({ _id }) => `${_id}`),
|
||||
tracking_number: "1234",
|
||||
})
|
||||
|
||||
res.sendStatus(200)
|
||||
})
|
||||
|
||||
app.post("/run-hook/:order_id/capture", async (req, res) => {
|
||||
const orderService = req.scope.resolve("orderService")
|
||||
const eventBus = req.scope.resolve("eventBusService")
|
||||
const order = await orderService.retrieve(req.params.order_id)
|
||||
|
||||
eventBus.emit("order.payment_captured", order)
|
||||
|
||||
res.sendStatus(200)
|
||||
})
|
||||
|
||||
app.post("/run-hook/:order_id", async (req, res) => {
|
||||
const orderService = req.scope.resolve("orderService")
|
||||
const eventBus = req.scope.resolve("eventBusService")
|
||||
const order = await orderService.retrieve(req.params.order_id)
|
||||
|
||||
eventBus.emit("order.placed", order)
|
||||
|
||||
res.sendStatus(200)
|
||||
})
|
||||
|
||||
admin(app, container, config)
|
||||
store(app, container, config)
|
||||
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
import { MedusaError, Validator } from "medusa-core-utils"
|
||||
|
||||
export default async (req, res) => {
|
||||
const schema = Validator.object().keys({
|
||||
application_name: Validator.string().required(),
|
||||
state: Validator.string().required(),
|
||||
code: Validator.string().required(),
|
||||
})
|
||||
|
||||
const { value, error } = schema.validate(req.body)
|
||||
if (error) {
|
||||
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
|
||||
}
|
||||
try {
|
||||
const oauthService = req.scope.resolve("oauthService")
|
||||
const data = await oauthService.generateToken(
|
||||
value.application_name,
|
||||
value.code,
|
||||
value.state
|
||||
)
|
||||
res.status(200).json({ apps: data })
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { Router } from "express"
|
||||
import middlewares from "../../../middlewares"
|
||||
|
||||
const route = Router()
|
||||
|
||||
export default app => {
|
||||
app.use("/apps", route)
|
||||
|
||||
route.get("/", middlewares.wrap(require("./list").default))
|
||||
route.post(
|
||||
"/authorizations",
|
||||
middlewares.wrap(require("./authorize-app").default)
|
||||
)
|
||||
|
||||
return app
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import { MedusaError, Validator } from "medusa-core-utils"
|
||||
|
||||
export default async (req, res) => {
|
||||
try {
|
||||
const oauthService = req.scope.resolve("oauthService")
|
||||
const data = await oauthService.list({})
|
||||
|
||||
res.status(200).json({ apps: data })
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@ import orderRoutes from "./orders"
|
||||
import storeRoutes from "./store"
|
||||
import uploadRoutes from "./uploads"
|
||||
import customerRoutes from "./customers"
|
||||
import appRoutes from "./apps"
|
||||
|
||||
const route = Router()
|
||||
|
||||
@@ -40,6 +41,7 @@ export default (app, container, config) => {
|
||||
// Calls all middleware that has been registered to run after authentication.
|
||||
middlewareService.usePostAuthentication(app)
|
||||
|
||||
appRoutes(route)
|
||||
productRoutes(route)
|
||||
userRoutes(route)
|
||||
regionRoutes(route)
|
||||
|
||||
@@ -14,20 +14,7 @@ describe("POST /admin/orders/:id/return", () => {
|
||||
payload: {
|
||||
items: [
|
||||
{
|
||||
_id: IdMap.getId("existingLine"),
|
||||
title: "merge line",
|
||||
description: "This is a new line",
|
||||
thumbnail: "test-img-yeah.com/thumb",
|
||||
content: {
|
||||
unit_price: 123,
|
||||
variant: {
|
||||
_id: IdMap.getId("can-cover"),
|
||||
},
|
||||
product: {
|
||||
_id: IdMap.getId("validId"),
|
||||
},
|
||||
quantity: 1,
|
||||
},
|
||||
item_id: IdMap.getId("existingLine"),
|
||||
quantity: 10,
|
||||
},
|
||||
],
|
||||
@@ -51,20 +38,7 @@ describe("POST /admin/orders/:id/return", () => {
|
||||
IdMap.getId("test-order"),
|
||||
[
|
||||
{
|
||||
_id: IdMap.getId("existingLine"),
|
||||
title: "merge line",
|
||||
description: "This is a new line",
|
||||
thumbnail: "test-img-yeah.com/thumb",
|
||||
content: {
|
||||
unit_price: 123,
|
||||
variant: {
|
||||
_id: IdMap.getId("can-cover"),
|
||||
},
|
||||
product: {
|
||||
_id: IdMap.getId("validId"),
|
||||
},
|
||||
quantity: 1,
|
||||
},
|
||||
item_id: IdMap.getId("existingLine"),
|
||||
quantity: 10,
|
||||
},
|
||||
]
|
||||
|
||||
@@ -4,7 +4,13 @@ export default async (req, res) => {
|
||||
const { id } = req.params
|
||||
|
||||
const schema = Validator.object().keys({
|
||||
items: Validator.array().required(),
|
||||
items: Validator.array()
|
||||
.items({
|
||||
item_id: Validator.string().required(),
|
||||
quantity: Validator.number().required(),
|
||||
})
|
||||
.required(),
|
||||
refund: Validator.number().optional(),
|
||||
})
|
||||
|
||||
const { value, error } = schema.validate(req.body)
|
||||
|
||||
@@ -3,7 +3,7 @@ import { MedusaError, Validator } from "medusa-core-utils"
|
||||
export default async (req, res) => {
|
||||
const schema = Validator.object().keys({
|
||||
title: Validator.string().required(),
|
||||
description: Validator.string(),
|
||||
description: Validator.string().allow(""),
|
||||
tags: Validator.string(),
|
||||
is_giftcard: Validator.boolean().default(false),
|
||||
options: Validator.array().items({
|
||||
@@ -15,6 +15,7 @@ export default async (req, res) => {
|
||||
title: Validator.string().required(),
|
||||
sku: Validator.string(),
|
||||
ean: Validator.string(),
|
||||
barcode: Validator.string(),
|
||||
prices: Validator.array()
|
||||
.items({
|
||||
currency_code: Validator.string().required(),
|
||||
|
||||
@@ -36,6 +36,7 @@ export default async (req, res) => {
|
||||
[
|
||||
"title",
|
||||
"description",
|
||||
"is_giftcard",
|
||||
"tags",
|
||||
"thumbnail",
|
||||
"handle",
|
||||
|
||||
@@ -9,6 +9,7 @@ export default async (req, res) => {
|
||||
[
|
||||
"title",
|
||||
"description",
|
||||
"is_giftcard",
|
||||
"tags",
|
||||
"thumbnail",
|
||||
"handle",
|
||||
|
||||
@@ -10,6 +10,7 @@ export default async (req, res) => {
|
||||
[
|
||||
"title",
|
||||
"description",
|
||||
"is_giftcard",
|
||||
"tags",
|
||||
"thumbnail",
|
||||
"handle",
|
||||
|
||||
@@ -18,6 +18,13 @@ export default async (req, res) => {
|
||||
title: Validator.string().optional(),
|
||||
sku: Validator.string().optional(),
|
||||
ean: Validator.string().optional(),
|
||||
published: Validator.boolean(),
|
||||
image: Validator.string()
|
||||
.allow("")
|
||||
.optional(),
|
||||
barcode: Validator.string()
|
||||
.allow("")
|
||||
.optional(),
|
||||
prices: Validator.array().items(
|
||||
Validator.object()
|
||||
.keys({
|
||||
|
||||
@@ -87,6 +87,7 @@ export default async (req, res) => {
|
||||
"options",
|
||||
"thumbnail",
|
||||
"variants",
|
||||
"is_giftcard",
|
||||
"published",
|
||||
],
|
||||
["variants"]
|
||||
|
||||
@@ -33,13 +33,14 @@ export default async (req, res) => {
|
||||
const shippingProfileService = req.scope.resolve("shippingProfileService")
|
||||
|
||||
// Add to default shipping profile
|
||||
const { _id } = await shippingProfileService.retrieveDefault()
|
||||
if (!value.profile_id) {
|
||||
const { _id } = await shippingProfileService.retrieveDefault()
|
||||
value.profile_id = _id
|
||||
}
|
||||
|
||||
const data = await optionService.create({
|
||||
...value,
|
||||
profile_id: _id,
|
||||
})
|
||||
await shippingProfileService.addShippingOption(_id, data._id)
|
||||
const data = await optionService.create(value)
|
||||
|
||||
await shippingProfileService.addShippingOption(value.profile_id, data._id)
|
||||
|
||||
res.status(200).json({ shipping_option: data })
|
||||
} catch (err) {
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
PaymentService,
|
||||
FulfillmentService,
|
||||
FileService,
|
||||
OauthService,
|
||||
} from "medusa-interfaces"
|
||||
import { getConfigFile, createRequireFromPath } from "medusa-core-utils"
|
||||
import _ from "lodash"
|
||||
@@ -16,7 +17,7 @@ import { sync as existsSync } from "fs-exists-cached"
|
||||
/**
|
||||
* Registers all services in the services directory
|
||||
*/
|
||||
export default ({ rootDirectory, container, app }) => {
|
||||
export default async ({ rootDirectory, container, app }) => {
|
||||
const { configModule, configFilePath } = getConfigFile(
|
||||
rootDirectory,
|
||||
`medusa-config`
|
||||
@@ -47,14 +48,39 @@ export default ({ rootDirectory, container, app }) => {
|
||||
version: createFileContentHash(process.cwd(), `**`),
|
||||
})
|
||||
|
||||
resolved.forEach(pluginDetails => {
|
||||
registerModels(pluginDetails, container)
|
||||
registerServices(pluginDetails, container)
|
||||
registerMedusaApi(pluginDetails, container)
|
||||
registerApi(pluginDetails, app, rootDirectory)
|
||||
registerCoreRouters(pluginDetails, container)
|
||||
registerSubscribers(pluginDetails, container)
|
||||
})
|
||||
await Promise.all(
|
||||
resolved.map(async pluginDetails => {
|
||||
registerModels(pluginDetails, container)
|
||||
await registerServices(pluginDetails, container)
|
||||
registerMedusaApi(pluginDetails, container)
|
||||
registerApi(pluginDetails, app, rootDirectory)
|
||||
registerCoreRouters(pluginDetails, container)
|
||||
registerSubscribers(pluginDetails, container)
|
||||
})
|
||||
)
|
||||
|
||||
await Promise.all(
|
||||
resolved.map(async pluginDetails => runLoaders(pluginDetails, container))
|
||||
)
|
||||
}
|
||||
|
||||
async function runLoaders(pluginDetails, container) {
|
||||
const loaderFiles = glob.sync(
|
||||
`${pluginDetails.resolve}/loaders/[!__]*.js`,
|
||||
{}
|
||||
)
|
||||
await Promise.all(
|
||||
loaderFiles.map(async loader => {
|
||||
try {
|
||||
const module = require(loader).default
|
||||
if (typeof module === "function") {
|
||||
await module(container)
|
||||
}
|
||||
} catch (err) {
|
||||
return Promise.resolve()
|
||||
}
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
function registerMedusaApi(pluginDetails, container) {
|
||||
@@ -133,58 +159,80 @@ function registerApi(pluginDetails, app, rootDirectory = "") {
|
||||
* registered
|
||||
* @return {void}
|
||||
*/
|
||||
function registerServices(pluginDetails, container) {
|
||||
async function registerServices(pluginDetails, container) {
|
||||
const files = glob.sync(`${pluginDetails.resolve}/services/[!__]*`, {})
|
||||
files.forEach(fn => {
|
||||
const loaded = require(fn).default
|
||||
const name = formatRegistrationName(fn)
|
||||
await Promise.all(
|
||||
files.map(async fn => {
|
||||
const loaded = require(fn).default
|
||||
const name = formatRegistrationName(fn)
|
||||
|
||||
if (!(loaded.prototype instanceof BaseService)) {
|
||||
const logger = container.resolve("logger")
|
||||
const message = `Services must inherit from BaseService, please check ${fn}`
|
||||
logger.error(message)
|
||||
throw new Error(message)
|
||||
}
|
||||
if (!(loaded.prototype instanceof BaseService)) {
|
||||
const logger = container.resolve("logger")
|
||||
const message = `Services must inherit from BaseService, please check ${fn}`
|
||||
logger.error(message)
|
||||
throw new Error(message)
|
||||
}
|
||||
|
||||
if (loaded.prototype instanceof PaymentService) {
|
||||
// Register our payment providers to paymentProviders
|
||||
container.registerAdd(
|
||||
"paymentProviders",
|
||||
asFunction(cradle => new loaded(cradle, pluginDetails.options))
|
||||
)
|
||||
if (loaded.prototype instanceof PaymentService) {
|
||||
// Register our payment providers to paymentProviders
|
||||
container.registerAdd(
|
||||
"paymentProviders",
|
||||
asFunction(cradle => new loaded(cradle, pluginDetails.options))
|
||||
)
|
||||
|
||||
// Add the service directly to the container in order to make simple
|
||||
// resolution if we already know which payment provider we need to use
|
||||
container.register({
|
||||
[name]: asFunction(cradle => new loaded(cradle, pluginDetails.options)),
|
||||
[`pp_${loaded.identifier}`]: aliasTo(name),
|
||||
})
|
||||
} else if (loaded.prototype instanceof FulfillmentService) {
|
||||
// Register our payment providers to paymentProviders
|
||||
container.registerAdd(
|
||||
"fulfillmentProviders",
|
||||
asFunction(cradle => new loaded(cradle, pluginDetails.options))
|
||||
)
|
||||
// Add the service directly to the container in order to make simple
|
||||
// resolution if we already know which payment provider we need to use
|
||||
container.register({
|
||||
[name]: asFunction(
|
||||
cradle => new loaded(cradle, pluginDetails.options)
|
||||
),
|
||||
[`pp_${loaded.identifier}`]: aliasTo(name),
|
||||
})
|
||||
} else if (loaded.prototype instanceof OauthService) {
|
||||
const oauthService = container.resolve("oauthService")
|
||||
|
||||
// Add the service directly to the container in order to make simple
|
||||
// resolution if we already know which payment provider we need to use
|
||||
container.register({
|
||||
[name]: asFunction(cradle => new loaded(cradle, pluginDetails.options)),
|
||||
[`fp_${loaded.identifier}`]: aliasTo(name),
|
||||
})
|
||||
} else if (loaded.prototype instanceof FileService) {
|
||||
// Add the service directly to the container in order to make simple
|
||||
// resolution if we already know which payment provider we need to use
|
||||
container.register({
|
||||
[name]: asFunction(cradle => new loaded(cradle, pluginDetails.options)),
|
||||
[`fileService`]: aliasTo(name),
|
||||
})
|
||||
} else {
|
||||
container.register({
|
||||
[name]: asFunction(cradle => new loaded(cradle, pluginDetails.options)),
|
||||
})
|
||||
}
|
||||
})
|
||||
const appDetails = loaded.getAppDetails(pluginDetails.options)
|
||||
await oauthService.registerOauthApp(appDetails)
|
||||
|
||||
const name = appDetails.application_name
|
||||
container.register({
|
||||
[`${name}Oauth`]: asFunction(
|
||||
cradle => new loaded(cradle, pluginDetails.options)
|
||||
),
|
||||
})
|
||||
} else if (loaded.prototype instanceof FulfillmentService) {
|
||||
// Register our payment providers to paymentProviders
|
||||
container.registerAdd(
|
||||
"fulfillmentProviders",
|
||||
asFunction(cradle => new loaded(cradle, pluginDetails.options))
|
||||
)
|
||||
|
||||
// Add the service directly to the container in order to make simple
|
||||
// resolution if we already know which payment provider we need to use
|
||||
container.register({
|
||||
[name]: asFunction(
|
||||
cradle => new loaded(cradle, pluginDetails.options)
|
||||
),
|
||||
[`fp_${loaded.identifier}`]: aliasTo(name),
|
||||
})
|
||||
} else if (loaded.prototype instanceof FileService) {
|
||||
// Add the service directly to the container in order to make simple
|
||||
// resolution if we already know which payment provider we need to use
|
||||
container.register({
|
||||
[name]: asFunction(
|
||||
cradle => new loaded(cradle, pluginDetails.options)
|
||||
),
|
||||
[`fileService`]: aliasTo(name),
|
||||
})
|
||||
} else {
|
||||
container.register({
|
||||
[name]: asFunction(
|
||||
cradle => new loaded(cradle, pluginDetails.options)
|
||||
),
|
||||
})
|
||||
}
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -41,6 +41,7 @@ export const carts = {
|
||||
cartWithPaySessionsDifRegion: {
|
||||
_id: IdMap.getId("cartWithPaySessionsDifRegion"),
|
||||
region_id: IdMap.getId("region-france"),
|
||||
total: 1,
|
||||
items: [
|
||||
{
|
||||
_id: IdMap.getId("existingLine"),
|
||||
@@ -81,6 +82,7 @@ export const carts = {
|
||||
},
|
||||
cartWithPaySessions: {
|
||||
_id: IdMap.getId("cartWithPaySessions"),
|
||||
total: 1,
|
||||
region_id: IdMap.getId("testRegion"),
|
||||
shipping_methods: [],
|
||||
items: [
|
||||
@@ -123,6 +125,7 @@ export const carts = {
|
||||
},
|
||||
cartWithLine: {
|
||||
_id: IdMap.getId("cartWithLine"),
|
||||
total: 1,
|
||||
title: "test",
|
||||
region_id: IdMap.getId("testRegion"),
|
||||
items: [
|
||||
@@ -166,6 +169,92 @@ export const carts = {
|
||||
discounts: [],
|
||||
customer_id: "",
|
||||
},
|
||||
withGiftCard: {
|
||||
_id: IdMap.getId("withGiftCard"),
|
||||
region_id: IdMap.getId("region-france"),
|
||||
items: [
|
||||
{
|
||||
_id: IdMap.getId("existingLine"),
|
||||
title: "merge line",
|
||||
description: "This is a new line",
|
||||
is_giftcard: false,
|
||||
thumbnail: "test-img-yeah.com/thumb",
|
||||
content: {
|
||||
unit_price: 123,
|
||||
variant: {
|
||||
_id: IdMap.getId("can-cover"),
|
||||
},
|
||||
product: {
|
||||
_id: IdMap.getId("product"),
|
||||
},
|
||||
quantity: 1,
|
||||
},
|
||||
quantity: 10,
|
||||
},
|
||||
{
|
||||
_id: IdMap.getId("giftline"),
|
||||
title: "GiftCard",
|
||||
description: "Gift card line",
|
||||
thumbnail: "test-img-yeah.com/thumb",
|
||||
metadata: {
|
||||
name: "Test Name",
|
||||
},
|
||||
is_giftcard: true,
|
||||
content: {
|
||||
unit_price: 100,
|
||||
variant: {
|
||||
_id: IdMap.getId("giftCardVar"),
|
||||
},
|
||||
product: {
|
||||
_id: IdMap.getId("giftCardProd"),
|
||||
},
|
||||
quantity: 1,
|
||||
},
|
||||
quantity: 1,
|
||||
},
|
||||
],
|
||||
email: "test",
|
||||
payment_sessions: [
|
||||
{
|
||||
provider_id: "default_provider",
|
||||
data: {
|
||||
money_id: "success",
|
||||
},
|
||||
},
|
||||
],
|
||||
payment_method: {
|
||||
provider_id: "default_provider",
|
||||
data: {
|
||||
money_id: "success",
|
||||
},
|
||||
},
|
||||
shipping_methods: [
|
||||
{
|
||||
provider_id: "gls",
|
||||
data: {
|
||||
yes: "sir",
|
||||
},
|
||||
},
|
||||
],
|
||||
shipping_address: {
|
||||
first_name: "hi",
|
||||
last_name: "you",
|
||||
country_code: "DK",
|
||||
city: "of lights",
|
||||
address_1: "You bet street",
|
||||
postal_code: "4242",
|
||||
},
|
||||
billing_address: {
|
||||
first_name: "hi",
|
||||
last_name: "you",
|
||||
country_code: "DK",
|
||||
city: "of lights",
|
||||
address_1: "You bet street",
|
||||
postal_code: "4242",
|
||||
},
|
||||
discounts: [],
|
||||
customer_id: "",
|
||||
},
|
||||
completeCart: {
|
||||
_id: IdMap.getId("complete-cart"),
|
||||
region_id: IdMap.getId("region-france"),
|
||||
|
||||
@@ -122,7 +122,7 @@ export const discounts = {
|
||||
}
|
||||
|
||||
export const DiscountModelMock = {
|
||||
create: jest.fn().mockReturnValue(Promise.resolve()),
|
||||
create: jest.fn().mockImplementation(data => Promise.resolve(data)),
|
||||
updateOne: jest.fn().mockImplementation((query, update) => {
|
||||
return Promise.resolve()
|
||||
}),
|
||||
|
||||
@@ -109,6 +109,9 @@ export const orders = {
|
||||
customer_id: IdMap.getId("test-customer"),
|
||||
payment_method: {
|
||||
provider_id: "default_provider",
|
||||
data: {
|
||||
hi: "hi",
|
||||
},
|
||||
},
|
||||
shipping_methods: [
|
||||
{
|
||||
@@ -133,6 +136,7 @@ export const orders = {
|
||||
orderToRefund: {
|
||||
_id: IdMap.getId("refund-order"),
|
||||
email: "oliver@test.dk",
|
||||
tax_rate: 0.25,
|
||||
billing_address: {
|
||||
first_name: "Oli",
|
||||
last_name: "Medusa",
|
||||
|
||||
@@ -11,7 +11,13 @@ export const profiles = {
|
||||
_id: IdMap.getId("profile1"),
|
||||
name: "Profile One",
|
||||
products: [IdMap.getId("product1")],
|
||||
shipping_options: [IdMap.getId("shipping1")],
|
||||
shipping_options: [IdMap.getId("shipping_1")],
|
||||
},
|
||||
profile2: {
|
||||
_id: IdMap.getId("profile2"),
|
||||
name: "Profile two",
|
||||
products: [IdMap.getId("product2")],
|
||||
shipping_options: [IdMap.getId("shipping_2")],
|
||||
},
|
||||
}
|
||||
|
||||
@@ -21,6 +27,10 @@ export const ShippingProfileModelMock = {
|
||||
return Promise.resolve()
|
||||
}),
|
||||
find: jest.fn().mockImplementation(query => {
|
||||
if (query.products && query.products.$in) {
|
||||
return Promise.resolve([profiles.profile1, profiles.profile2])
|
||||
}
|
||||
|
||||
return Promise.resolve([])
|
||||
}),
|
||||
deleteOne: jest.fn().mockReturnValue(Promise.resolve()),
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
import mongoose from "mongoose"
|
||||
import { BaseModel } from "medusa-interfaces"
|
||||
|
||||
class OauthModel extends BaseModel {
|
||||
static modelName = "Oauth"
|
||||
|
||||
static schema = {
|
||||
display_name: { type: String, required: true },
|
||||
application_name: { type: String, required: true, unique: true },
|
||||
install_url: { type: String, required: true },
|
||||
uninstall_url: { type: String, default: "" },
|
||||
data: { type: mongoose.Schema.Types.Mixed, default: {} },
|
||||
}
|
||||
}
|
||||
|
||||
export default OauthModel
|
||||
@@ -6,6 +6,7 @@ import PaymentMethodSchema from "./schemas/payment-method"
|
||||
import ShippingMethodSchema from "./schemas/shipping-method"
|
||||
import AddressSchema from "./schemas/address"
|
||||
import DiscountSchema from "./schemas/discount"
|
||||
import ShipmentSchema from "./schemas/shipment"
|
||||
|
||||
class OrderModel extends BaseModel {
|
||||
static modelName = "Order"
|
||||
@@ -23,6 +24,8 @@ class OrderModel extends BaseModel {
|
||||
shipping_address: { type: AddressSchema, required: true },
|
||||
items: { type: [LineItemSchema], required: true },
|
||||
currency_code: { type: String, required: true },
|
||||
tax_rate: { type: Number, required: true },
|
||||
shipments: { type: [ShipmentSchema], default: [] },
|
||||
region_id: { type: String, required: true },
|
||||
discounts: { type: [DiscountSchema], default: [] },
|
||||
customer_id: { type: String },
|
||||
|
||||
@@ -7,6 +7,7 @@ class RegionModel extends BaseModel {
|
||||
name: { type: String, required: true },
|
||||
currency_code: { type: String, required: true },
|
||||
tax_rate: { type: Number, required: true, default: 0 },
|
||||
tax_code: { type: String },
|
||||
countries: { type: [String], default: [] },
|
||||
payment_providers: { type: [String], default: [] },
|
||||
fulfillment_providers: { type: [String], default: [] },
|
||||
|
||||
@@ -3,6 +3,7 @@ import mongoose from "mongoose"
|
||||
import DiscountRule from "./discount-rule"
|
||||
|
||||
export default new mongoose.Schema({
|
||||
is_giftcard: { type: Boolean },
|
||||
code: { type: String },
|
||||
discount_rule: { type: DiscountRule },
|
||||
usage_count: { type: Number },
|
||||
|
||||
@@ -8,6 +8,7 @@ export default new mongoose.Schema({
|
||||
description: { type: String },
|
||||
thumbnail: { type: String },
|
||||
is_giftcard: { type: Boolean, default: false },
|
||||
has_shipping: { type: Boolean, default: false },
|
||||
|
||||
// mongoose doesn't allow multi-type validation but this field allows both
|
||||
// an object containing:
|
||||
@@ -35,5 +36,6 @@ export default new mongoose.Schema({
|
||||
content: { type: mongoose.Schema.Types.Mixed, required: true },
|
||||
quantity: { type: Number, required: true },
|
||||
returned: { type: Boolean, default: false },
|
||||
returned_quantity: { type: Number, default: 0 },
|
||||
metadata: { type: mongoose.Schema.Types.Mixed, default: {} },
|
||||
})
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
import mongoose from "mongoose"
|
||||
|
||||
export default new mongoose.Schema({
|
||||
item_ids: { type: [String], required: true },
|
||||
tracking_number: { type: String, default: "" },
|
||||
metadata: { type: mongoose.Schema.Types.Mixed, default: {} },
|
||||
})
|
||||
@@ -46,6 +46,11 @@ export const DiscountServiceMock = {
|
||||
removeRegion: jest.fn().mockReturnValue(Promise.resolve()),
|
||||
addValidVariant: jest.fn().mockReturnValue(Promise.resolve()),
|
||||
removeValidVariant: jest.fn().mockReturnValue(Promise.resolve()),
|
||||
generateGiftCard: jest.fn().mockReturnValue(
|
||||
Promise.resolve({
|
||||
_id: IdMap.getId("gift_card_id"),
|
||||
})
|
||||
),
|
||||
}
|
||||
|
||||
const mock = jest.fn().mockImplementation(() => {
|
||||
|
||||
@@ -110,6 +110,7 @@ export const ShippingOptionServiceMock = {
|
||||
_id: IdMap.getId("fail"),
|
||||
})
|
||||
}
|
||||
return Promise.resolve({ _id: methodId })
|
||||
}),
|
||||
delete: jest.fn().mockReturnValue(Promise.resolve()),
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ export const ShippingProfileServiceMock = {
|
||||
if (data === IdMap.getId("profile1")) {
|
||||
return Promise.resolve(profiles.other)
|
||||
}
|
||||
return Promise.resolve()
|
||||
return Promise.resolve(profiles.default)
|
||||
}),
|
||||
retrieveGiftCardDefault: jest.fn().mockImplementation(data => {
|
||||
return Promise.resolve({ _id: IdMap.getId("giftCardProfile") })
|
||||
|
||||
@@ -10,6 +10,7 @@ import { RegionServiceMock } from "../__mocks__/region"
|
||||
import { EventBusServiceMock } from "../__mocks__/event-bus"
|
||||
import { CustomerServiceMock } from "../__mocks__/customer"
|
||||
import { ShippingOptionServiceMock } from "../__mocks__/shipping-option"
|
||||
import { TotalsServiceMock } from "../__mocks__/totals"
|
||||
import { ShippingProfileServiceMock } from "../__mocks__/shipping-profile"
|
||||
import { CartModelMock, carts } from "../../models/__mocks__/cart"
|
||||
import { LineItemServiceMock } from "../__mocks__/line-item"
|
||||
@@ -193,7 +194,12 @@ describe("CartService", () => {
|
||||
_id: IdMap.getId("emptyCart"),
|
||||
},
|
||||
{
|
||||
$push: { items: lineItem },
|
||||
$push: {
|
||||
items: {
|
||||
...lineItem,
|
||||
has_shipping: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
})
|
||||
@@ -225,7 +231,10 @@ describe("CartService", () => {
|
||||
"items._id": IdMap.getId("existingLine"),
|
||||
},
|
||||
{
|
||||
$set: { "items.$.quantity": 20 },
|
||||
$set: {
|
||||
"items.$.quantity": 20,
|
||||
"items.$.has_shipping": false,
|
||||
},
|
||||
}
|
||||
)
|
||||
})
|
||||
@@ -267,7 +276,12 @@ describe("CartService", () => {
|
||||
_id: IdMap.getId("cartWithLine"),
|
||||
},
|
||||
{
|
||||
$push: { items: lineItem },
|
||||
$push: {
|
||||
items: {
|
||||
...lineItem,
|
||||
has_shipping: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
})
|
||||
@@ -390,10 +404,9 @@ describe("CartService", () => {
|
||||
expect(CartModelMock.updateOne).toHaveBeenCalledWith(
|
||||
{
|
||||
_id: IdMap.getId("cartWithLine"),
|
||||
"items._id": IdMap.getId("existingLine"),
|
||||
},
|
||||
{
|
||||
$set: { "items.$.quantity": 9 },
|
||||
$pull: { items: { _id: IdMap.getId("existingLine") } },
|
||||
}
|
||||
)
|
||||
})
|
||||
@@ -905,6 +918,7 @@ describe("CartService", () => {
|
||||
cartModel: CartModelMock,
|
||||
regionService: RegionServiceMock,
|
||||
paymentProviderService: PaymentProviderServiceMock,
|
||||
totalsService: TotalsServiceMock,
|
||||
eventBusService: EventBusServiceMock,
|
||||
})
|
||||
|
||||
@@ -1183,6 +1197,26 @@ describe("CartService", () => {
|
||||
},
|
||||
{
|
||||
$set: {
|
||||
items: [
|
||||
{
|
||||
_id: IdMap.getId("existingLine"),
|
||||
title: "merge line",
|
||||
description: "This is a new line",
|
||||
has_shipping: true,
|
||||
thumbnail: "test-img-yeah.com/thumb",
|
||||
content: {
|
||||
unit_price: 123,
|
||||
variant: {
|
||||
_id: IdMap.getId("can-cover"),
|
||||
},
|
||||
product: {
|
||||
_id: IdMap.getId("product"),
|
||||
},
|
||||
quantity: 1,
|
||||
},
|
||||
quantity: 10,
|
||||
},
|
||||
],
|
||||
shipping_methods: [
|
||||
{
|
||||
_id: IdMap.getId("freeShipping"),
|
||||
@@ -1221,6 +1255,10 @@ describe("CartService", () => {
|
||||
},
|
||||
{
|
||||
$set: {
|
||||
items: carts.frCart.items.map(i => ({
|
||||
...i,
|
||||
has_shipping: false,
|
||||
})),
|
||||
shipping_methods: [
|
||||
{
|
||||
_id: IdMap.getId("freeShipping"),
|
||||
@@ -1261,6 +1299,10 @@ describe("CartService", () => {
|
||||
},
|
||||
{
|
||||
$set: {
|
||||
items: carts.frCart.items.map(i => ({
|
||||
...i,
|
||||
has_shipping: false,
|
||||
})),
|
||||
shipping_methods: [
|
||||
{
|
||||
_id: IdMap.getId("freeShipping"),
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
} from "../../models/__mocks__/dynamic-discount-code"
|
||||
import { IdMap } from "medusa-test-utils"
|
||||
import { ProductVariantServiceMock } from "../__mocks__/product-variant"
|
||||
import { EventBusServiceMock } from "../__mocks__/event-bus"
|
||||
import { RegionServiceMock } from "../__mocks__/region"
|
||||
|
||||
describe("DiscountService", () => {
|
||||
@@ -274,4 +275,32 @@ describe("DiscountService", () => {
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("generateGiftCard", () => {
|
||||
const discountService = new DiscountService({
|
||||
discountModel: DiscountModelMock,
|
||||
regionService: RegionServiceMock,
|
||||
eventBusService: EventBusServiceMock,
|
||||
})
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
it("calls model layer create", async () => {
|
||||
await discountService.generateGiftCard(100, IdMap.getId("testRegion"))
|
||||
|
||||
expect(DiscountModelMock.create).toHaveBeenCalledTimes(1)
|
||||
expect(DiscountModelMock.create).toHaveBeenCalledWith({
|
||||
code: expect.stringMatching(/(([A-Z0-9]){4}(-?)){4}/),
|
||||
is_giftcard: true,
|
||||
discount_rule: {
|
||||
type: "fixed",
|
||||
allocation: "total",
|
||||
value: 100,
|
||||
},
|
||||
regions: [IdMap.getId("testRegion")],
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -22,7 +22,7 @@ describe("EventBusService", () => {
|
||||
})
|
||||
|
||||
it("creates bull queue", () => {
|
||||
expect(Bull).toHaveBeenCalledTimes(1)
|
||||
expect(Bull).toHaveBeenCalledTimes(2)
|
||||
expect(Bull).toHaveBeenCalledWith("EventBusService:queue", "testhost")
|
||||
})
|
||||
})
|
||||
|
||||
@@ -2,7 +2,11 @@ import { IdMap } from "medusa-test-utils"
|
||||
import { OrderModelMock, orders } from "../../models/__mocks__/order"
|
||||
import { carts } from "../../models/__mocks__/cart"
|
||||
import OrderService from "../order"
|
||||
import { PaymentProviderServiceMock } from "../__mocks__/payment-provider"
|
||||
import {
|
||||
PaymentProviderServiceMock,
|
||||
DefaultProviderMock,
|
||||
} from "../__mocks__/payment-provider"
|
||||
import { DiscountServiceMock } from "../__mocks__/discount"
|
||||
import { FulfillmentProviderServiceMock } from "../__mocks__/fulfillment-provider"
|
||||
import { ShippingProfileServiceMock } from "../__mocks__/shipping-profile"
|
||||
import { TotalsServiceMock } from "../__mocks__/totals"
|
||||
@@ -36,6 +40,7 @@ describe("OrderService", () => {
|
||||
const orderService = new OrderService({
|
||||
orderModel: OrderModelMock,
|
||||
paymentProviderService: PaymentProviderServiceMock,
|
||||
discountService: DiscountServiceMock,
|
||||
regionService: RegionServiceMock,
|
||||
eventBusService: EventBusServiceMock,
|
||||
})
|
||||
@@ -51,6 +56,7 @@ describe("OrderService", () => {
|
||||
...carts.completeCart,
|
||||
currency_code: "eur",
|
||||
cart_id: carts.completeCart._id,
|
||||
tax_rate: 0.25,
|
||||
}
|
||||
delete order._id
|
||||
delete order.payment_sessions
|
||||
@@ -60,6 +66,84 @@ describe("OrderService", () => {
|
||||
session: expect.anything(),
|
||||
})
|
||||
})
|
||||
|
||||
it("creates cart with gift card", async () => {
|
||||
await orderService.createFromCart(carts.withGiftCard)
|
||||
|
||||
const order = {
|
||||
...carts.withGiftCard,
|
||||
items: [
|
||||
{
|
||||
_id: IdMap.getId("existingLine"),
|
||||
title: "merge line",
|
||||
description: "This is a new line",
|
||||
is_giftcard: false,
|
||||
thumbnail: "test-img-yeah.com/thumb",
|
||||
content: {
|
||||
unit_price: 123,
|
||||
variant: {
|
||||
_id: IdMap.getId("can-cover"),
|
||||
},
|
||||
product: {
|
||||
_id: IdMap.getId("product"),
|
||||
},
|
||||
quantity: 1,
|
||||
},
|
||||
quantity: 10,
|
||||
},
|
||||
{
|
||||
_id: IdMap.getId("giftline"),
|
||||
title: "GiftCard",
|
||||
description: "Gift card line",
|
||||
thumbnail: "test-img-yeah.com/thumb",
|
||||
metadata: {
|
||||
giftcard: IdMap.getId("gift_card_id"),
|
||||
name: "Test Name",
|
||||
},
|
||||
is_giftcard: true,
|
||||
content: {
|
||||
unit_price: 100,
|
||||
variant: {
|
||||
_id: IdMap.getId("giftCardVar"),
|
||||
},
|
||||
product: {
|
||||
_id: IdMap.getId("giftCardProd"),
|
||||
},
|
||||
quantity: 1,
|
||||
},
|
||||
quantity: 1,
|
||||
},
|
||||
],
|
||||
currency_code: "eur",
|
||||
cart_id: carts.withGiftCard._id,
|
||||
tax_rate: 0.25,
|
||||
}
|
||||
|
||||
delete order._id
|
||||
delete order.payment_sessions
|
||||
|
||||
expect(EventBusServiceMock.emit).toHaveBeenCalledTimes(2)
|
||||
expect(EventBusServiceMock.emit).toHaveBeenCalledWith(
|
||||
"order.gift_card_created",
|
||||
{
|
||||
currency_code: "eur",
|
||||
tax_rate: 0.25,
|
||||
email: "test",
|
||||
giftcard: expect.any(Object),
|
||||
}
|
||||
)
|
||||
|
||||
expect(DiscountServiceMock.generateGiftCard).toHaveBeenCalledTimes(1)
|
||||
expect(DiscountServiceMock.generateGiftCard).toHaveBeenCalledWith(
|
||||
100,
|
||||
IdMap.getId("region-france")
|
||||
)
|
||||
|
||||
expect(OrderModelMock.create).toHaveBeenCalledTimes(1)
|
||||
expect(OrderModelMock.create).toHaveBeenCalledWith([order], {
|
||||
session: expect.anything(),
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("retrieve", () => {
|
||||
@@ -280,6 +364,7 @@ describe("OrderService", () => {
|
||||
const orderService = new OrderService({
|
||||
orderModel: OrderModelMock,
|
||||
paymentProviderService: PaymentProviderServiceMock,
|
||||
eventBusService: EventBusServiceMock,
|
||||
})
|
||||
|
||||
beforeEach(async () => {
|
||||
@@ -297,11 +382,9 @@ describe("OrderService", () => {
|
||||
})
|
||||
|
||||
it("throws if payment is already processed", async () => {
|
||||
try {
|
||||
await orderService.capturePayment(IdMap.getId("payed-order"))
|
||||
} catch (error) {
|
||||
expect(error.message).toEqual("Payment already captured")
|
||||
}
|
||||
await expect(
|
||||
orderService.capturePayment(IdMap.getId("payed-order"))
|
||||
).rejects.toThrow("Payment already captured")
|
||||
})
|
||||
})
|
||||
|
||||
@@ -324,16 +407,36 @@ describe("OrderService", () => {
|
||||
expect(OrderModelMock.updateOne).toHaveBeenCalledTimes(1)
|
||||
expect(OrderModelMock.updateOne).toHaveBeenCalledWith(
|
||||
{ _id: IdMap.getId("test-order") },
|
||||
{ $set: { fulfillment_status: "fulfilled" } }
|
||||
{
|
||||
$set: {
|
||||
fulfillment_status: "fulfilled",
|
||||
shipping_methods: [
|
||||
{
|
||||
_id: IdMap.getId("expensiveShipping"),
|
||||
items: [],
|
||||
name: "Expensive Shipping",
|
||||
price: 100,
|
||||
profile_id: IdMap.getId("default"),
|
||||
provider_id: "default_provider",
|
||||
},
|
||||
{
|
||||
_id: IdMap.getId("freeShipping"),
|
||||
items: [],
|
||||
name: "Free Shipping",
|
||||
price: 10,
|
||||
profile_id: IdMap.getId("profile1"),
|
||||
provider_id: "default_provider",
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
it("throws if payment is already processed", async () => {
|
||||
try {
|
||||
await orderService.createFulfillment(IdMap.getId("fulfilled-order"))
|
||||
} catch (error) {
|
||||
expect(error.message).toEqual("Order is already fulfilled")
|
||||
}
|
||||
await expect(
|
||||
orderService.createFulfillment(IdMap.getId("fulfilled-order"))
|
||||
).rejects.toThrow("Order is already fulfilled")
|
||||
})
|
||||
})
|
||||
|
||||
@@ -342,6 +445,7 @@ describe("OrderService", () => {
|
||||
orderModel: OrderModelMock,
|
||||
paymentProviderService: PaymentProviderServiceMock,
|
||||
totalsService: TotalsServiceMock,
|
||||
eventBusService: EventBusServiceMock,
|
||||
})
|
||||
|
||||
beforeEach(async () => {
|
||||
@@ -351,20 +455,7 @@ describe("OrderService", () => {
|
||||
it("calls order model functions", async () => {
|
||||
await orderService.return(IdMap.getId("processed-order"), [
|
||||
{
|
||||
_id: IdMap.getId("existingLine"),
|
||||
title: "merge line",
|
||||
description: "This is a new line",
|
||||
thumbnail: "test-img-yeah.com/thumb",
|
||||
content: {
|
||||
unit_price: 123,
|
||||
variant: {
|
||||
_id: IdMap.getId("can-cover"),
|
||||
},
|
||||
product: {
|
||||
_id: IdMap.getId("validId"),
|
||||
},
|
||||
quantity: 1,
|
||||
},
|
||||
item_id: IdMap.getId("existingLine"),
|
||||
quantity: 10,
|
||||
},
|
||||
])
|
||||
@@ -392,31 +483,75 @@ describe("OrderService", () => {
|
||||
returned_quantity: 10,
|
||||
thumbnail: "test-img-yeah.com/thumb",
|
||||
title: "merge line",
|
||||
returned: true,
|
||||
},
|
||||
],
|
||||
fulfillment_status: "returned",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
expect(DefaultProviderMock.refundPayment).toHaveBeenCalledTimes(1)
|
||||
expect(DefaultProviderMock.refundPayment).toHaveBeenCalledWith(
|
||||
{ hi: "hi" },
|
||||
1230
|
||||
)
|
||||
})
|
||||
|
||||
it("calls order model functions and sets partially_fulfilled", async () => {
|
||||
it("return with custom refund", async () => {
|
||||
await orderService.return(
|
||||
IdMap.getId("processed-order"),
|
||||
[
|
||||
{
|
||||
item_id: IdMap.getId("existingLine"),
|
||||
quantity: 10,
|
||||
},
|
||||
],
|
||||
102
|
||||
)
|
||||
|
||||
expect(OrderModelMock.updateOne).toHaveBeenCalledTimes(1)
|
||||
expect(OrderModelMock.updateOne).toHaveBeenCalledWith(
|
||||
{ _id: IdMap.getId("processed-order") },
|
||||
{
|
||||
$set: {
|
||||
items: [
|
||||
{
|
||||
_id: IdMap.getId("existingLine"),
|
||||
content: {
|
||||
product: {
|
||||
_id: IdMap.getId("validId"),
|
||||
},
|
||||
quantity: 1,
|
||||
unit_price: 123,
|
||||
variant: {
|
||||
_id: IdMap.getId("can-cover"),
|
||||
},
|
||||
},
|
||||
description: "This is a new line",
|
||||
quantity: 10,
|
||||
returned_quantity: 10,
|
||||
thumbnail: "test-img-yeah.com/thumb",
|
||||
title: "merge line",
|
||||
returned: true,
|
||||
},
|
||||
],
|
||||
fulfillment_status: "returned",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
expect(DefaultProviderMock.refundPayment).toHaveBeenCalledTimes(1)
|
||||
expect(DefaultProviderMock.refundPayment).toHaveBeenCalledWith(
|
||||
{ hi: "hi" },
|
||||
102
|
||||
)
|
||||
})
|
||||
|
||||
it("calls order model functions and sets partially_returned", async () => {
|
||||
await orderService.return(IdMap.getId("order-refund"), [
|
||||
{
|
||||
_id: IdMap.getId("existingLine"),
|
||||
title: "merge line",
|
||||
description: "This is a new line",
|
||||
thumbnail: "test-img-yeah.com/thumb",
|
||||
content: {
|
||||
unit_price: 100,
|
||||
variant: {
|
||||
_id: IdMap.getId("eur-8-us-10"),
|
||||
},
|
||||
product: {
|
||||
_id: IdMap.getId("product"),
|
||||
},
|
||||
quantity: 1,
|
||||
},
|
||||
item_id: IdMap.getId("existingLine"),
|
||||
quantity: 2,
|
||||
},
|
||||
])
|
||||
@@ -441,6 +576,7 @@ describe("OrderService", () => {
|
||||
},
|
||||
description: "This is a new line",
|
||||
quantity: 10,
|
||||
returned: false,
|
||||
returned_quantity: 2,
|
||||
thumbnail: "test-img-yeah.com/thumb",
|
||||
title: "merge line",
|
||||
@@ -463,7 +599,7 @@ describe("OrderService", () => {
|
||||
quantity: 10,
|
||||
},
|
||||
],
|
||||
fulfillment_status: "partially_fulfilled",
|
||||
fulfillment_status: "partially_returned",
|
||||
},
|
||||
}
|
||||
)
|
||||
@@ -471,7 +607,7 @@ describe("OrderService", () => {
|
||||
|
||||
it("throws if payment is already processed", async () => {
|
||||
try {
|
||||
await orderService.return(IdMap.getId("fulfilled-order"))
|
||||
await orderService.return(IdMap.getId("fulfilled-order"), [])
|
||||
} catch (error) {
|
||||
expect(error.message).toEqual(
|
||||
"Can't return an order with payment unprocessed"
|
||||
@@ -481,7 +617,7 @@ describe("OrderService", () => {
|
||||
|
||||
it("throws if return is attempted on unfulfilled order", async () => {
|
||||
try {
|
||||
await orderService.return(IdMap.getId("not-fulfilled-order"))
|
||||
await orderService.return(IdMap.getId("not-fulfilled-order"), [])
|
||||
} catch (error) {
|
||||
expect(error.message).toEqual(
|
||||
"Can't return an unfulfilled or already returned order"
|
||||
|
||||
@@ -353,6 +353,37 @@ describe("ShippingProfileService", () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe("fetchCartOptions", () => {
|
||||
const profileService = new ShippingProfileService({
|
||||
shippingProfileModel: ShippingProfileModelMock,
|
||||
shippingOptionService: ShippingOptionServiceMock,
|
||||
})
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
it("fetches correct options", async () => {
|
||||
await profileService.fetchCartOptions({
|
||||
items: [
|
||||
{
|
||||
content: { product: { _id: IdMap.getId("product_1") } },
|
||||
},
|
||||
{
|
||||
content: { product: { _id: IdMap.getId("product_2") } },
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
expect(ShippingProfileModelMock.find).toBeCalledTimes(1)
|
||||
expect(ShippingProfileModelMock.find).toBeCalledWith({
|
||||
products: { $in: [IdMap.getId("product_1"), IdMap.getId("product_2")] },
|
||||
})
|
||||
|
||||
expect(ShippingOptionServiceMock.validateCartOption).toBeCalledTimes(2)
|
||||
})
|
||||
})
|
||||
|
||||
describe("addShippingOption", () => {
|
||||
const profileService = new ShippingProfileService({
|
||||
shippingProfileModel: ShippingProfileModelMock,
|
||||
|
||||
@@ -193,7 +193,7 @@ describe("TotalsService", () => {
|
||||
description: "This is a new line",
|
||||
thumbnail: "test-img-yeah.com/thumb",
|
||||
content: {
|
||||
unit_price: 123,
|
||||
unit_price: 100,
|
||||
variant: {
|
||||
_id: IdMap.getId("can-cover"),
|
||||
},
|
||||
@@ -206,7 +206,7 @@ describe("TotalsService", () => {
|
||||
},
|
||||
])
|
||||
|
||||
expect(res).toEqual(1107)
|
||||
expect(res).toEqual(1125)
|
||||
})
|
||||
|
||||
it("calculates refund with total fixed discount", async () => {
|
||||
@@ -218,7 +218,7 @@ describe("TotalsService", () => {
|
||||
description: "This is a new line",
|
||||
thumbnail: "test-img-yeah.com/thumb",
|
||||
content: {
|
||||
unit_price: 123,
|
||||
unit_price: 100,
|
||||
variant: {
|
||||
_id: IdMap.getId("can-cover"),
|
||||
},
|
||||
@@ -231,7 +231,7 @@ describe("TotalsService", () => {
|
||||
},
|
||||
])
|
||||
|
||||
expect(res).toEqual(359)
|
||||
expect(res).toEqual(373.125)
|
||||
})
|
||||
|
||||
it("calculates refund with item fixed discount", async () => {
|
||||
@@ -243,7 +243,7 @@ describe("TotalsService", () => {
|
||||
description: "This is a new line",
|
||||
thumbnail: "test-img-yeah.com/thumb",
|
||||
content: {
|
||||
unit_price: 123,
|
||||
unit_price: 100,
|
||||
variant: {
|
||||
_id: IdMap.getId("eur-8-us-10"),
|
||||
},
|
||||
@@ -256,7 +256,7 @@ describe("TotalsService", () => {
|
||||
},
|
||||
])
|
||||
|
||||
expect(res).toEqual(363)
|
||||
expect(res).toEqual(367.5)
|
||||
})
|
||||
|
||||
it("calculates refund with item percentage discount", async () => {
|
||||
@@ -268,7 +268,7 @@ describe("TotalsService", () => {
|
||||
description: "This is a new line",
|
||||
thumbnail: "test-img-yeah.com/thumb",
|
||||
content: {
|
||||
unit_price: 123,
|
||||
unit_price: 100,
|
||||
variant: {
|
||||
_id: IdMap.getId("eur-8-us-10"),
|
||||
},
|
||||
@@ -281,7 +281,7 @@ describe("TotalsService", () => {
|
||||
},
|
||||
])
|
||||
|
||||
expect(res).toEqual(332.1)
|
||||
expect(res).toEqual(337.5)
|
||||
})
|
||||
|
||||
it("throws if line items to return is not in order", async () => {
|
||||
|
||||
@@ -249,6 +249,23 @@ class CartService extends BaseService {
|
||||
return c
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of product ids in a line item.
|
||||
* @param {LineItem} item - the line item to fetch products from
|
||||
* @return {[string]} an array of product ids
|
||||
*/
|
||||
getItemProducts_(item) {
|
||||
// Find all the products in the line item
|
||||
const products = []
|
||||
if (Array.isArray(item.content)) {
|
||||
item.content.forEach(c => products.push(`${c.product._id}`))
|
||||
} else {
|
||||
products.push(`${item.content.product._id}`)
|
||||
}
|
||||
|
||||
return products
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a line item from the cart.
|
||||
* @param {string} cartId - the id of the cart that we will remove from
|
||||
@@ -258,33 +275,42 @@ class CartService extends BaseService {
|
||||
async removeLineItem(cartId, lineItemId) {
|
||||
const cart = await this.retrieve(cartId)
|
||||
const itemToRemove = cart.items.find(line => line._id.equals(lineItemId))
|
||||
|
||||
if (!itemToRemove) {
|
||||
return Promise.resolve(cart)
|
||||
}
|
||||
|
||||
// If cart has more than one of those line items, we update the quantity
|
||||
// instead of removing it
|
||||
if (itemToRemove.quantity > 1) {
|
||||
const newQuantity = itemToRemove.quantity - 1
|
||||
const update = {
|
||||
$pull: { items: { _id: itemToRemove._id } },
|
||||
}
|
||||
|
||||
return this.cartModel_
|
||||
.updateOne(
|
||||
{
|
||||
_id: cartId,
|
||||
"items._id": itemToRemove._id,
|
||||
},
|
||||
{
|
||||
$set: {
|
||||
"items.$.quantity": newQuantity,
|
||||
},
|
||||
// Remove shipping methods if they are not needed
|
||||
if (cart.shipping_methods && cart.shipping_methods.length) {
|
||||
const filteredItems = cart.items.filter(i => !i._id.equals(lineItemId))
|
||||
|
||||
let newShippingMethods = await Promise.all(
|
||||
cart.shipping_methods.map(async m => {
|
||||
const profile = await this.shippingProfileService_.retrieve(
|
||||
m.profile_id
|
||||
)
|
||||
const hasItem = filteredItems.find(item => {
|
||||
const products = this.getItemProducts_(item)
|
||||
return products.some(p => profile.products.includes(p))
|
||||
})
|
||||
|
||||
if (hasItem) {
|
||||
return m
|
||||
}
|
||||
)
|
||||
.then(result => {
|
||||
// Notify subscribers
|
||||
this.eventBus_.emit(CartService.Events.UPDATED, result)
|
||||
return result
|
||||
|
||||
return null
|
||||
})
|
||||
)
|
||||
newShippingMethods = newShippingMethods.filter(n => !!n)
|
||||
|
||||
if (newShippingMethods.length !== cart.shipping_methods.length) {
|
||||
update.$set = {
|
||||
shipping_methods: newShippingMethods,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return this.cartModel_
|
||||
@@ -292,11 +318,7 @@ class CartService extends BaseService {
|
||||
{
|
||||
_id: cartId,
|
||||
},
|
||||
{
|
||||
$pull: {
|
||||
items: { _id: itemToRemove._id },
|
||||
},
|
||||
}
|
||||
update
|
||||
)
|
||||
.then(result => {
|
||||
// Notify subscribers
|
||||
@@ -305,6 +327,34 @@ class CartService extends BaseService {
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a given line item has a shipping method that can fulfill it.
|
||||
* Returns true if all products in the cart can be fulfilled with the current
|
||||
* shipping methods.
|
||||
* @param {Cart} cart - the cart
|
||||
* @param {LineItem} lineItem - the line item
|
||||
* @return {boolean}
|
||||
*/
|
||||
async validateLineItemShipping_(shippingMethods, lineItem) {
|
||||
if (shippingMethods && shippingMethods.length) {
|
||||
const profiles = await Promise.all(
|
||||
shippingMethods.map(m =>
|
||||
this.shippingProfileService_.retrieve(m.profile_id)
|
||||
)
|
||||
)
|
||||
|
||||
const products = this.getItemProducts_(lineItem)
|
||||
|
||||
// Check if there is a shipping method for each product
|
||||
const hasShipping = products.map(
|
||||
p => !!profiles.find(profile => profile.products.includes(p))
|
||||
)
|
||||
return hasShipping.every(b => b)
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a line item to the cart.
|
||||
* @param {string} cartId - the id of the cart that we will add to
|
||||
@@ -318,6 +368,11 @@ class CartService extends BaseService {
|
||||
this.lineItemService_.isEqual(line, validatedLineItem)
|
||||
)
|
||||
|
||||
const hasShipping = await this.validateLineItemShipping_(
|
||||
cart.shipping_methods,
|
||||
validatedLineItem
|
||||
)
|
||||
|
||||
// If content matches one of the line items currently in the cart we can
|
||||
// simply update the quantity of the existing line item
|
||||
if (currentItem) {
|
||||
@@ -345,6 +400,7 @@ class CartService extends BaseService {
|
||||
{
|
||||
$set: {
|
||||
"items.$.quantity": newQuantity,
|
||||
"items.$.has_shipping": hasShipping,
|
||||
},
|
||||
}
|
||||
)
|
||||
@@ -375,7 +431,12 @@ class CartService extends BaseService {
|
||||
_id: cartId,
|
||||
},
|
||||
{
|
||||
$push: { items: validatedLineItem },
|
||||
$push: {
|
||||
items: {
|
||||
...validatedLineItem,
|
||||
has_shipping: hasShipping,
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
.then(result => {
|
||||
@@ -808,6 +869,25 @@ class CartService extends BaseService {
|
||||
const cart = await this.retrieve(cartId)
|
||||
const region = await this.regionService_.retrieve(cart.region_id)
|
||||
|
||||
const total = await this.totalsService_.getTotal(cart)
|
||||
|
||||
if (total === 0) {
|
||||
return this.cartModel_
|
||||
.updateOne(
|
||||
{
|
||||
_id: cart._id,
|
||||
},
|
||||
{
|
||||
$set: { payment_sessions: [] },
|
||||
}
|
||||
)
|
||||
.then(result => {
|
||||
// Notify subscribers
|
||||
this.eventBus_.emit(CartService.Events.UPDATED, result)
|
||||
return result
|
||||
})
|
||||
}
|
||||
|
||||
// If there are existing payment sessions ensure that these are up to date
|
||||
let sessions = []
|
||||
if (cart.payment_sessions && cart.payment_sessions.length) {
|
||||
@@ -817,13 +897,22 @@ class CartService extends BaseService {
|
||||
return null
|
||||
}
|
||||
|
||||
// const data = await this.paymentProviderService_.updateSession(
|
||||
// pSession,
|
||||
// cart
|
||||
// )
|
||||
let data
|
||||
try {
|
||||
data = await this.paymentProviderService_.updateSession(
|
||||
pSession,
|
||||
cart
|
||||
)
|
||||
} catch (err) {
|
||||
data = await this.paymentProviderService_.createSession(
|
||||
pSession.provider_id,
|
||||
cart
|
||||
)
|
||||
}
|
||||
|
||||
return {
|
||||
provider_id: pSession.provider_id,
|
||||
data: {},
|
||||
data,
|
||||
}
|
||||
})
|
||||
)
|
||||
@@ -981,10 +1070,19 @@ class CartService extends BaseService {
|
||||
newMethods.push(option)
|
||||
}
|
||||
|
||||
const finalMethods = newMethods.map(m => {
|
||||
const { _id, ...rest } = m
|
||||
return rest
|
||||
})
|
||||
const newItems = await Promise.all(
|
||||
cart.items.map(async item => {
|
||||
const hasShipping = await this.validateLineItemShipping_(
|
||||
newMethods,
|
||||
item
|
||||
)
|
||||
|
||||
return {
|
||||
...item,
|
||||
has_shipping: hasShipping,
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
return this.cartModel_
|
||||
.updateOne(
|
||||
@@ -992,7 +1090,7 @@ class CartService extends BaseService {
|
||||
_id: cart._id,
|
||||
},
|
||||
{
|
||||
$set: { shipping_methods: finalMethods },
|
||||
$set: { shipping_methods: newMethods, items: newItems },
|
||||
}
|
||||
)
|
||||
.then(result => {
|
||||
@@ -1050,20 +1148,15 @@ class CartService extends BaseService {
|
||||
update.shipping_methods = []
|
||||
}
|
||||
|
||||
//if (cart.items.length && cart.payment_sessions.length) {
|
||||
// update.payment_sessions = await Promise.all(
|
||||
// region.payment_providers.map(async pId => {
|
||||
// const data = await this.paymentProviderService_.createSession(pId, {
|
||||
// ...cart,
|
||||
// ...update,
|
||||
// })
|
||||
// return {
|
||||
// provider_id: pId,
|
||||
// data,
|
||||
// }
|
||||
// })
|
||||
// )
|
||||
//}
|
||||
if (cart.discounts && cart.discounts.length) {
|
||||
const newDiscounts = cart.discounts.map(d => {
|
||||
if (d.regions.includes(regionId)) {
|
||||
return d
|
||||
}
|
||||
})
|
||||
|
||||
update.discounts = newDiscounts.filter(d => !!d)
|
||||
}
|
||||
|
||||
// Payment methods are region specific so the user needs to find a
|
||||
// new payment method
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import _ from "lodash"
|
||||
import randomize from "randomatic"
|
||||
import { BaseService } from "medusa-interfaces"
|
||||
import { Validator, MedusaError } from "medusa-core-utils"
|
||||
import _ from "lodash"
|
||||
|
||||
/**
|
||||
* Provides layer to manipulate discounts.
|
||||
@@ -13,6 +14,7 @@ class DiscountService extends BaseService {
|
||||
totalsService,
|
||||
productVariantService,
|
||||
regionService,
|
||||
eventBusService,
|
||||
}) {
|
||||
super()
|
||||
|
||||
@@ -30,6 +32,9 @@ class DiscountService extends BaseService {
|
||||
|
||||
/** @private @const {RegionService} */
|
||||
this.regionService_ = regionService
|
||||
|
||||
/** @private @const {EventBus} */
|
||||
this.eventBus_ = eventBusService
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -60,7 +65,7 @@ class DiscountService extends BaseService {
|
||||
description: Validator.string(),
|
||||
type: Validator.string().required(),
|
||||
value: Validator.number()
|
||||
.positive()
|
||||
.min(0)
|
||||
.required(),
|
||||
allocation: Validator.string().required(),
|
||||
valid_for: Validator.array().items(Validator.string()),
|
||||
@@ -210,6 +215,38 @@ class DiscountService extends BaseService {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a gift card with the specified value which is valid in the
|
||||
* specified region.
|
||||
* @param {number} value - the value that the gift card represents
|
||||
* @param {string} regionId - the id of the region in which the gift card can
|
||||
* be used
|
||||
* @return {Discount} the newly created gift card
|
||||
*/
|
||||
async generateGiftCard(value, regionId) {
|
||||
const region = await this.regionService_.retrieve(regionId)
|
||||
|
||||
const code = [
|
||||
randomize("A0", 4),
|
||||
randomize("A0", 4),
|
||||
randomize("A0", 4),
|
||||
randomize("A0", 4),
|
||||
].join("-")
|
||||
|
||||
const discountRule = this.validateDiscountRule_({
|
||||
type: "fixed",
|
||||
allocation: "total",
|
||||
value,
|
||||
})
|
||||
|
||||
return this.discountModel_.create({
|
||||
code,
|
||||
discount_rule: discountRule,
|
||||
is_giftcard: true,
|
||||
regions: [region._id],
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a dynamic code for a discount id.
|
||||
* @param {string} discountId - the id of the discount to create a code for
|
||||
|
||||
@@ -13,11 +13,20 @@ class EventBusService {
|
||||
/** @private {object} */
|
||||
this.observers_ = {}
|
||||
|
||||
/** @private {object} to handle cron jobs */
|
||||
this.cronHandlers_ = {}
|
||||
|
||||
/** @private {BullQueue} used for cron jobs */
|
||||
this.cronQueue_ = new Bull(`cron-jobs:queue`, config.redisURI)
|
||||
|
||||
/** @private {BullQueue} */
|
||||
this.queue_ = new Bull(`${this.constructor.name}:queue`, config.redisURI)
|
||||
|
||||
// Register our worker to handle emit calls
|
||||
this.queue_.process(this.worker_)
|
||||
|
||||
// Register cron worker
|
||||
this.cronQueue_.process(this.cronWorker_)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -38,6 +47,21 @@ class EventBusService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
registerCronHandler_(event, subscriber) {
|
||||
if (typeof subscriber !== "function") {
|
||||
throw new Error("Handler must be a function")
|
||||
}
|
||||
|
||||
if (this.observers_[event]) {
|
||||
this.cronHandlers_[event].push(subscriber)
|
||||
} else {
|
||||
this.cronHandlers_[event] = [subscriber]
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls all subscribers when an event occurs.
|
||||
* @param {string} eventName - the name of the event to be process.
|
||||
@@ -77,6 +101,37 @@ class EventBusService {
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
cronWorker_ = job => {
|
||||
const { eventName, data } = job.data
|
||||
const observers = this.cronHandlers_[eventName] || []
|
||||
this.logger_.info(`Processing cron job: ${eventName}`)
|
||||
|
||||
return Promise.all(
|
||||
observers.map(subscriber => {
|
||||
return subscriber(data).catch(err => {
|
||||
this.logger_.warn(
|
||||
`An error occured while processing ${eventName}: ${err}`
|
||||
)
|
||||
return err
|
||||
})
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a cron job.
|
||||
*/
|
||||
createCronJob(eventName, data, cron, handler) {
|
||||
this.registerCronHandler(eventName, handler)
|
||||
return this.cronQueue_.add(
|
||||
{
|
||||
eventName,
|
||||
data,
|
||||
},
|
||||
{ repeat: { cron } }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default EventBusService
|
||||
|
||||
@@ -37,6 +37,7 @@ class LineItemService extends BaseService {
|
||||
|
||||
const lineItemSchema = Validator.object({
|
||||
title: Validator.string().required(),
|
||||
is_giftcard: Validator.bool().optional(),
|
||||
description: Validator.string()
|
||||
.allow("")
|
||||
.optional(),
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
import _ from "lodash"
|
||||
import { Validator, MedusaError } from "medusa-core-utils"
|
||||
import { OauthService } from "medusa-interfaces"
|
||||
|
||||
class Oauth extends OauthService {
|
||||
static Events = {
|
||||
TOKEN_GENERATED: "oauth.token_generated",
|
||||
TOKEN_REFRESHED: "oauth.token_refreshed",
|
||||
}
|
||||
|
||||
constructor(cradle) {
|
||||
super()
|
||||
this.container_ = cradle
|
||||
this.model_ = cradle.oauthModel
|
||||
this.eventBus_ = cradle.eventBusService
|
||||
}
|
||||
|
||||
retrieveByName(appName) {
|
||||
return this.model_.findOne({
|
||||
application_name: appName,
|
||||
})
|
||||
}
|
||||
|
||||
list(selector) {
|
||||
return this.model_.find(selector)
|
||||
}
|
||||
|
||||
create(data) {
|
||||
return this.model_.create({
|
||||
display_name: data.display_name,
|
||||
application_name: data.application_name,
|
||||
install_url: data.install_url,
|
||||
uninstall_url: data.uninstall_url,
|
||||
})
|
||||
}
|
||||
|
||||
update(id, update) {
|
||||
return this.model_.updateOne(
|
||||
{
|
||||
_id: id,
|
||||
},
|
||||
update
|
||||
)
|
||||
}
|
||||
|
||||
async registerOauthApp(appDetails) {
|
||||
const { application_name } = appDetails
|
||||
const existing = await this.retrieveByName(application_name)
|
||||
if (existing) {
|
||||
return
|
||||
}
|
||||
|
||||
return this.create(appDetails)
|
||||
}
|
||||
|
||||
async generateToken(appName, code, state) {
|
||||
const app = await this.retrieveByName(appName)
|
||||
const service = this.container_[`${app.application_name}Oauth`]
|
||||
if (!service) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`An OAuth handler for ${app.display_name} could not be found make sure the plugin is installed`
|
||||
)
|
||||
}
|
||||
|
||||
if (!app.state === state) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.NOT_ALLOWED,
|
||||
`${app.display_name} could not match state`
|
||||
)
|
||||
}
|
||||
|
||||
const authData = await service.generateToken(code)
|
||||
|
||||
return this.update(app._id, {
|
||||
data: authData,
|
||||
}).then(result => {
|
||||
this.eventBus_.emit(
|
||||
`${Oauth.Events.TOKEN_GENERATED}.${appName}`,
|
||||
authData
|
||||
)
|
||||
return result
|
||||
})
|
||||
}
|
||||
|
||||
async refreshToken(appName, refreshToken) {
|
||||
const app = await this.retrieveByName(appName)
|
||||
const service = this.container_[`${app.application_name}Oauth`]
|
||||
if (!service) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`An OAuth handler for ${app.display_name} could not be found make sure the plugin is installed`
|
||||
)
|
||||
}
|
||||
|
||||
const authData = await service.refreshToken(refreshToken)
|
||||
|
||||
return this.update(app._id, {
|
||||
data: authData,
|
||||
}).then(result => {
|
||||
this.eventBus_.emit(
|
||||
`${Oauth.Events.TOKEN_REFRESHED}.${appName}`,
|
||||
authData
|
||||
)
|
||||
return result
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export default Oauth
|
||||
@@ -4,6 +4,10 @@ import { BaseService } from "medusa-interfaces"
|
||||
|
||||
class OrderService extends BaseService {
|
||||
static Events = {
|
||||
GIFT_CARD_CREATED: "order.gift_card_created",
|
||||
PAYMENT_CAPTURED: "order.payment_captured",
|
||||
SHIPMENT_CREATED: "order.shipment_created",
|
||||
ITEMS_RETURNED: "order.items_returned",
|
||||
PLACED: "order.placed",
|
||||
UPDATED: "order.updated",
|
||||
CANCELLED: "order.cancelled",
|
||||
@@ -14,6 +18,7 @@ class OrderService extends BaseService {
|
||||
orderModel,
|
||||
paymentProviderService,
|
||||
shippingProfileService,
|
||||
discountService,
|
||||
fulfillmentProviderService,
|
||||
lineItemService,
|
||||
totalsService,
|
||||
@@ -43,6 +48,9 @@ class OrderService extends BaseService {
|
||||
/** @private @const {RegionService} */
|
||||
this.regionService_ = regionService
|
||||
|
||||
/** @private @const {DiscountService} */
|
||||
this.discountService_ = discountService
|
||||
|
||||
/** @private @const {EventBus} */
|
||||
this.eventBus_ = eventBusService
|
||||
}
|
||||
@@ -233,28 +241,28 @@ class OrderService extends BaseService {
|
||||
*/
|
||||
async completeOrder(orderId) {
|
||||
const order = await this.retrieve(orderId)
|
||||
this.orderModel_
|
||||
|
||||
// Capture the payment
|
||||
await this.capturePayment(orderId)
|
||||
|
||||
// Run all other registered events
|
||||
const completeOrderJob = await this.eventBus_.emit(
|
||||
OrderService.Events.COMPLETED,
|
||||
result
|
||||
)
|
||||
|
||||
await completeOrderJob.finished().catch(error => {
|
||||
throw error
|
||||
})
|
||||
|
||||
return this.orderModel_
|
||||
.updateOne(
|
||||
{ _id: order._id },
|
||||
{
|
||||
$set: { status: "completed" },
|
||||
}
|
||||
)
|
||||
.then(async result => {
|
||||
const completeOrderJob = await this.eventBus_.emit(
|
||||
OrderService.Events.COMPLETED,
|
||||
result
|
||||
)
|
||||
|
||||
return completeOrderJob
|
||||
.finished()
|
||||
.then(async () => {
|
||||
return this.retrieve(order._id)
|
||||
})
|
||||
.catch(error => {
|
||||
throw error
|
||||
})
|
||||
})
|
||||
.then(async result => {})
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -328,6 +336,33 @@ class OrderService extends BaseService {
|
||||
paymentSession.data
|
||||
)
|
||||
|
||||
// Generate gift cards if in cart
|
||||
const items = await Promise.all(
|
||||
cart.items.map(async i => {
|
||||
if (i.is_giftcard) {
|
||||
const giftcard = await this.discountService_
|
||||
.generateGiftCard(i.content.unit_price, region._id)
|
||||
.then(result => {
|
||||
this.eventBus_.emit(OrderService.Events.GIFT_CARD_CREATED, {
|
||||
currency_code: region.currency_code,
|
||||
tax_rate: region.tax_rate,
|
||||
giftcard: result,
|
||||
email: cart.email,
|
||||
})
|
||||
return result
|
||||
})
|
||||
return {
|
||||
...i,
|
||||
metadata: {
|
||||
...i.metadata,
|
||||
giftcard: giftcard._id,
|
||||
},
|
||||
}
|
||||
}
|
||||
return i
|
||||
})
|
||||
)
|
||||
|
||||
const o = {
|
||||
payment_method: {
|
||||
provider_id: paymentSession.provider_id,
|
||||
@@ -335,13 +370,14 @@ class OrderService extends BaseService {
|
||||
},
|
||||
discounts: cart.discounts,
|
||||
shipping_methods: cart.shipping_methods,
|
||||
items: cart.items,
|
||||
items,
|
||||
shipping_address: cart.shipping_address,
|
||||
billing_address: cart.shipping_address,
|
||||
region_id: cart.region_id,
|
||||
email: cart.email,
|
||||
customer_id: cart.customer_id,
|
||||
cart_id: cart._id,
|
||||
tax_rate: region.tax_rate,
|
||||
currency_code: region.currency_code,
|
||||
metadata: cart.metadata,
|
||||
}
|
||||
@@ -352,11 +388,63 @@ class OrderService extends BaseService {
|
||||
|
||||
// Emit and return
|
||||
this.eventBus_.emit(OrderService.Events.PLACED, orderDocument[0])
|
||||
return orderDocument[0].toObject()
|
||||
return orderDocument[0]
|
||||
})
|
||||
.then(() => this.orderModel_.findOne({ cart_id: cart._id }))
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a shipment to the order to indicate that an order has left the warehouse
|
||||
*/
|
||||
async createShipment(orderId, shipment) {
|
||||
const order = await this.retrieve(orderId)
|
||||
|
||||
console.log(order)
|
||||
if (order.fulfillment_status === "shipped") {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.NOT_ALLOWED,
|
||||
"Order has already been shipped"
|
||||
)
|
||||
}
|
||||
|
||||
const shipmentSchema = Validator.object({
|
||||
item_ids: Validator.array()
|
||||
.items(Validator.string())
|
||||
.required(),
|
||||
tracking_number: Validator.string().required(),
|
||||
})
|
||||
|
||||
const { value, error } = shipmentSchema.validate(shipment)
|
||||
if (error) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Shipment not valid: ${error}`
|
||||
)
|
||||
}
|
||||
|
||||
const existing = order.shipments || []
|
||||
const shipments = [...existing, value]
|
||||
const allCovered = order.items.every(
|
||||
i => !!shipments.find(s => s.item_ids.includes(`${i._id}`))
|
||||
)
|
||||
|
||||
const update = {
|
||||
$push: { shipments: value },
|
||||
$set: {
|
||||
fulfillment_status: allCovered ? "shipped" : "partially_shipped",
|
||||
},
|
||||
}
|
||||
|
||||
// Add the shipment to the order
|
||||
return this.orderModel_.updateOne({ _id: orderId }, update).then(result => {
|
||||
this.eventBus_.emit(OrderService.Events.SHIPMENT_CREATED, {
|
||||
order_id: orderId,
|
||||
shipment,
|
||||
})
|
||||
return result
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an order
|
||||
* @param {object} order - the order to create
|
||||
@@ -538,14 +626,19 @@ class OrderService extends BaseService {
|
||||
)
|
||||
}
|
||||
|
||||
return this.orderModel_.updateOne(
|
||||
{
|
||||
_id: orderId,
|
||||
},
|
||||
{
|
||||
$set: updateFields,
|
||||
}
|
||||
)
|
||||
return this.orderModel_
|
||||
.updateOne(
|
||||
{
|
||||
_id: orderId,
|
||||
},
|
||||
{
|
||||
$set: updateFields,
|
||||
}
|
||||
)
|
||||
.then(result => {
|
||||
this.eventBus_.emit(OrderService.Events.PAYMENT_CAPTURED, result)
|
||||
return result
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -576,14 +669,17 @@ class OrderService extends BaseService {
|
||||
}
|
||||
|
||||
// partition order items to their dedicated shipping method
|
||||
order.shipping_methods = await this.partitionItems_(shipping_methods, items)
|
||||
updateFields.shipping_methods = await this.partitionItems_(
|
||||
shipping_methods,
|
||||
items
|
||||
)
|
||||
|
||||
await Promise.all(
|
||||
order.shipping_methods.map(method => {
|
||||
updateFields.shipping_methods.map(method => {
|
||||
const provider = this.fulfillmentProviderService_.retrieveProvider(
|
||||
method.provider_id
|
||||
)
|
||||
provider.createOrder(method.data, method.items)
|
||||
return provider.createOrder(method.data, method.items)
|
||||
})
|
||||
)
|
||||
|
||||
@@ -612,9 +708,25 @@ class OrderService extends BaseService {
|
||||
* @param {string[]} lineItems - the line items to return
|
||||
* @return {Promise} the result of the update operation
|
||||
*/
|
||||
async return(orderId, lineItems) {
|
||||
async return(orderId, lineItems, refundAmount) {
|
||||
const order = await this.retrieve(orderId)
|
||||
|
||||
// Find the lines to return
|
||||
const returnLines = lineItems.map(({ item_id, quantity }) => {
|
||||
const item = order.items.find(i => i._id.equals(item_id))
|
||||
if (!item) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
"Return contains invalid line item"
|
||||
)
|
||||
}
|
||||
|
||||
return {
|
||||
...item,
|
||||
quantity,
|
||||
}
|
||||
})
|
||||
|
||||
if (
|
||||
order.fulfillment_status === "not_fulfilled" ||
|
||||
order.fulfillment_status === "returned"
|
||||
@@ -637,31 +749,50 @@ class OrderService extends BaseService {
|
||||
provider_id
|
||||
)
|
||||
|
||||
const amount = this.totalsService_.getRefundTotal(order, lineItems)
|
||||
const amount =
|
||||
refundAmount || this.totalsService_.getRefundTotal(order, returnLines)
|
||||
await paymentProvider.refundPayment(data, amount)
|
||||
|
||||
lineItems.map(item => {
|
||||
const returnedItem = order.items.find(({ _id }) => _id === item._id)
|
||||
if (returnedItem) {
|
||||
returnedItem.returned_quantity = item.quantity
|
||||
let isFullReturn = true
|
||||
const newItems = order.items.map(i => {
|
||||
const isReturn = returnLines.find(r => r._id.equals(i._id))
|
||||
if (isReturn) {
|
||||
let returned = false
|
||||
if (i.quantity === isReturn.quantity) {
|
||||
returned = true
|
||||
}
|
||||
return {
|
||||
...i,
|
||||
returned_quantity: isReturn.quantity,
|
||||
returned,
|
||||
}
|
||||
} else {
|
||||
isFullReturn = false
|
||||
return i
|
||||
}
|
||||
})
|
||||
|
||||
const fullReturn = order.items.every(
|
||||
item => item.quantity === item.returned_quantity
|
||||
)
|
||||
|
||||
return this.orderModel_.updateOne(
|
||||
{
|
||||
_id: orderId,
|
||||
},
|
||||
{
|
||||
$set: {
|
||||
items: order.items,
|
||||
fulfillment_status: fullReturn ? "returned" : "partially_fulfilled",
|
||||
return this.orderModel_
|
||||
.updateOne(
|
||||
{
|
||||
_id: orderId,
|
||||
},
|
||||
}
|
||||
)
|
||||
{
|
||||
$set: {
|
||||
items: newItems,
|
||||
fulfillment_status: isFullReturn
|
||||
? "returned"
|
||||
: "partially_returned",
|
||||
},
|
||||
}
|
||||
)
|
||||
.then(result => {
|
||||
this.eventBus_.emit(OrderService.Events.ITEMS_RETURNED, {
|
||||
order: result,
|
||||
items: returnLines,
|
||||
})
|
||||
return result
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -708,6 +839,14 @@ class OrderService extends BaseService {
|
||||
if (expandFields.includes("region")) {
|
||||
o.region = await this.regionService_.retrieve(order.region_id)
|
||||
}
|
||||
|
||||
o.items = o.items.map(i => {
|
||||
return {
|
||||
...i,
|
||||
refundable: this.totalsService_.getLineItemRefund(o, i),
|
||||
}
|
||||
})
|
||||
|
||||
return o
|
||||
}
|
||||
|
||||
|
||||
@@ -143,7 +143,14 @@ class ProductService extends BaseService {
|
||||
}
|
||||
|
||||
if (update.variants) {
|
||||
update.variants = await Promise.all(
|
||||
const existingVariants = await this.retrieveVariants(validatedId)
|
||||
for (const existing of existingVariants) {
|
||||
if (!update.variants.find(v => v._id && existing._id.equals(v._id))) {
|
||||
await this.deleteVariant(productId, existing._id)
|
||||
}
|
||||
}
|
||||
|
||||
await Promise.all(
|
||||
update.variants.map(async variant => {
|
||||
if (variant._id) {
|
||||
if (variant.prices && variant.prices.length) {
|
||||
|
||||
@@ -101,7 +101,7 @@ class ShippingProfileService extends BaseService {
|
||||
* Retrieves the default gift card profile
|
||||
* @return the shipping profile for gift cards
|
||||
*/
|
||||
async retrieveGiftCardProfile() {
|
||||
async retrieveGiftCardDefault() {
|
||||
return await this.profileModel_
|
||||
.findOne({ name: "default_gift_card_profile" })
|
||||
.catch(err => {
|
||||
@@ -115,7 +115,7 @@ class ShippingProfileService extends BaseService {
|
||||
* @return {Promise<ShippingProfile>} the shipping profile
|
||||
*/
|
||||
async createGiftCardDefault() {
|
||||
const profile = await this.retrieveGiftCardProfile()
|
||||
const profile = await this.retrieveGiftCardDefault()
|
||||
if (!profile) {
|
||||
return this.profileModel_.create({ name: "default_gift_card_profile" })
|
||||
}
|
||||
@@ -388,13 +388,21 @@ class ShippingProfileService extends BaseService {
|
||||
)
|
||||
|
||||
const options = await Promise.all(
|
||||
optionIds.map(oId => {
|
||||
return this.shippingOptionService_
|
||||
optionIds.map(async oId => {
|
||||
const option = await this.shippingOptionService_
|
||||
.validateCartOption(oId, cart)
|
||||
.catch(err => {
|
||||
// If validation failed we skip the option
|
||||
return null
|
||||
})
|
||||
|
||||
if (option) {
|
||||
return {
|
||||
...option,
|
||||
profile: profiles.find(p => p._id.equals(option.profile_id)),
|
||||
}
|
||||
}
|
||||
return null
|
||||
})
|
||||
)
|
||||
|
||||
|
||||
@@ -80,6 +80,32 @@ class TotalsService extends BaseService {
|
||||
return (subtotal - discountTotal + shippingTotal) * tax_rate
|
||||
}
|
||||
|
||||
getLineItemRefund(order, lineItem) {
|
||||
const { tax_rate, discounts } = order
|
||||
const taxRate = tax_rate || 0
|
||||
|
||||
const discount = discounts.find(
|
||||
({ discount_rule }) => discount_rule.type !== "free_shipping"
|
||||
)
|
||||
|
||||
if (!discount) {
|
||||
return lineItem.content.unit_price * lineItem.quantity * (1 + taxRate)
|
||||
}
|
||||
|
||||
const lineDiscounts = this.getLineDiscounts(order, discount)
|
||||
const discountedLine = lineDiscounts.find(line =>
|
||||
line.item._id.equals(lineItem._id)
|
||||
)
|
||||
|
||||
const discountAmount =
|
||||
(discountedLine.amount / discountedLine.item.quantity) * lineItem.quantity
|
||||
|
||||
return (
|
||||
(lineItem.content.unit_price * lineItem.quantity - discountAmount) *
|
||||
(1 + taxRate)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates refund total of line items.
|
||||
* If any of the items to return have been discounted, we need to
|
||||
@@ -88,101 +114,9 @@ class TotalsService extends BaseService {
|
||||
* @param {[LineItem]} lineItems -
|
||||
* @return {int} the calculated subtotal
|
||||
*/
|
||||
async getRefundTotal(order, lineItems) {
|
||||
const discount = order.discounts.find(
|
||||
({ discount_rule }) => discount_rule.type !== "free_shipping"
|
||||
)
|
||||
|
||||
if (_.differenceBy(lineItems, order.items, "_id").length !== 0) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_ARGUMENT,
|
||||
"Line items does not exist on order"
|
||||
)
|
||||
}
|
||||
|
||||
const subtotal = this.getSubtotal({ items: lineItems })
|
||||
|
||||
const region = await this.regionService_.retrieve(order.region_id)
|
||||
|
||||
// if nothing is discounted, return the subtotal of line items
|
||||
if (!discount) {
|
||||
return subtotal * (1 + region.tax_rate)
|
||||
}
|
||||
|
||||
const { value, type, allocation } = discount.discount_rule
|
||||
|
||||
if (type === "percentage" && allocation === "total") {
|
||||
const discountTotal = (subtotal / 100) * value
|
||||
return subtotal - discountTotal
|
||||
}
|
||||
|
||||
if (type === "fixed" && allocation === "total") {
|
||||
return subtotal - value
|
||||
}
|
||||
|
||||
if (type === "percentage" && allocation === "item") {
|
||||
// Find discounted items
|
||||
const itemPercentageDiscounts = await this.getAllocationItemDiscounts(
|
||||
discount,
|
||||
{ items: lineItems },
|
||||
"percentage"
|
||||
)
|
||||
|
||||
// Find discount total by taking each discounted item, reducing it by
|
||||
// its discount value. Then summing all those items together.
|
||||
const discountRefundTotal = _.sumBy(
|
||||
itemPercentageDiscounts,
|
||||
d => d.lineItem.content.unit_price * d.lineItem.quantity - d.amount
|
||||
)
|
||||
|
||||
// Find the items that weren't discounted
|
||||
const notDiscountedItems = _.differenceBy(
|
||||
lineItems,
|
||||
Array.from(itemPercentageDiscounts, el => el.lineItem),
|
||||
"_id"
|
||||
)
|
||||
|
||||
// If all items were discounted, we return the total of the discounted
|
||||
// items
|
||||
if (!notDiscountedItems) {
|
||||
return discountRefundTotal
|
||||
}
|
||||
|
||||
// Otherwise, we find the total those not discounted
|
||||
const notDiscRefundTotal = this.getSubtotal({ items: notDiscountedItems })
|
||||
|
||||
// Finally, return the sum of discounted and not discounted items
|
||||
return notDiscRefundTotal + discountRefundTotal
|
||||
}
|
||||
|
||||
// See immediate `if`-statement above for a elaboration on the following
|
||||
// calculations. This time with fixed discount type.
|
||||
if (type === "fixed" && allocation === "item") {
|
||||
const itemPercentageDiscounts = await this.getAllocationItemDiscounts(
|
||||
discount,
|
||||
{ items: lineItems },
|
||||
"fixed"
|
||||
)
|
||||
|
||||
const discountRefundTotal = _.sumBy(
|
||||
itemPercentageDiscounts,
|
||||
d => d.lineItem.content.unit_price * d.lineItem.quantity - d.amount
|
||||
)
|
||||
|
||||
const notDiscountedItems = _.differenceBy(
|
||||
lineItems,
|
||||
Array.from(itemPercentageDiscounts, el => el.lineItem),
|
||||
"_id"
|
||||
)
|
||||
|
||||
if (!notDiscountedItems) {
|
||||
return notDiscRefundTotal
|
||||
}
|
||||
|
||||
const notDiscRefundTotal = this.getSubtotal({ items: notDiscountedItems })
|
||||
|
||||
return notDiscRefundTotal + discountRefundTotal
|
||||
}
|
||||
getRefundTotal(order, lineItems) {
|
||||
const refunds = lineItems.map(i => this.getLineItemRefund(order, i))
|
||||
return refunds.reduce((acc, next) => acc + next, 0)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -225,7 +159,7 @@ class TotalsService extends BaseService {
|
||||
* @return {[{ string, string, int }]} array of triples of lineitem, variant
|
||||
* and applied discount
|
||||
*/
|
||||
async getAllocationItemDiscounts(discount, cart) {
|
||||
getAllocationItemDiscounts(discount, cart) {
|
||||
const discounts = []
|
||||
for (const item of cart.items) {
|
||||
if (discount.discount_rule.valid_for.length > 0) {
|
||||
@@ -252,6 +186,45 @@ class TotalsService extends BaseService {
|
||||
return discounts
|
||||
}
|
||||
|
||||
getLineDiscounts(cart, discount) {
|
||||
const subtotal = this.getSubtotal(cart)
|
||||
const { type, allocation, value } = discount.discount_rule
|
||||
if (allocation === "total") {
|
||||
let percentage = 0
|
||||
if (type === "percentage") {
|
||||
percentage = value / 100
|
||||
} else if (type === "fixed") {
|
||||
percentage = value / subtotal
|
||||
}
|
||||
|
||||
return cart.items.map(item => {
|
||||
const lineTotal = item.content.unit_price * item.quantity
|
||||
|
||||
return {
|
||||
item,
|
||||
amount: lineTotal * percentage,
|
||||
}
|
||||
})
|
||||
} else if (allocation === "item") {
|
||||
const allocationDiscounts = this.getAllocationItemDiscounts(
|
||||
discount,
|
||||
cart,
|
||||
type
|
||||
)
|
||||
return cart.items.map(item => {
|
||||
const discounted = allocationDiscounts.find(a =>
|
||||
a.lineItem._id.equals(item._id)
|
||||
)
|
||||
return {
|
||||
item,
|
||||
amount: !!discounted ? discounted.amount : 0,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return cart.items.map(i => ({ item: i, amount: 0 }))
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the total discount amount for each of the different supported
|
||||
* discount types. If discounts aren't present or invalid returns 0.
|
||||
@@ -292,36 +265,29 @@ class TotalsService extends BaseService {
|
||||
}
|
||||
|
||||
const { type, allocation, value } = discount.discount_rule
|
||||
let toReturn = 0
|
||||
|
||||
if (type === "percentage" && allocation === "total") {
|
||||
return (subtotal / 100) * value
|
||||
}
|
||||
|
||||
if (type === "percentage" && allocation === "item") {
|
||||
toReturn = (subtotal / 100) * value
|
||||
} else if (type === "percentage" && allocation === "item") {
|
||||
const itemPercentageDiscounts = await this.getAllocationItemDiscounts(
|
||||
discount,
|
||||
cart,
|
||||
"percentage"
|
||||
)
|
||||
const totalDiscount = _.sumBy(itemPercentageDiscounts, d => d.amount)
|
||||
return totalDiscount
|
||||
}
|
||||
|
||||
if (type === "fixed" && allocation === "total") {
|
||||
return value
|
||||
}
|
||||
|
||||
if (type === "fixed" && allocation === "item") {
|
||||
toReturn = _.sumBy(itemPercentageDiscounts, d => d.amount)
|
||||
} else if (type === "fixed" && allocation === "total") {
|
||||
toReturn = value
|
||||
} else if (type === "fixed" && allocation === "item") {
|
||||
const itemFixedDiscounts = await this.getAllocationItemDiscounts(
|
||||
discount,
|
||||
cart,
|
||||
"fixed"
|
||||
)
|
||||
const totalDiscount = _.sumBy(itemFixedDiscounts, d => d.amount)
|
||||
return totalDiscount
|
||||
toReturn = _.sumBy(itemFixedDiscounts, d => d.amount)
|
||||
}
|
||||
|
||||
return 0
|
||||
return Math.min(subtotal, toReturn)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,21 +4,21 @@ class OrderSubscriber {
|
||||
cartService,
|
||||
customerService,
|
||||
eventBusService,
|
||||
discountService,
|
||||
totalsService,
|
||||
}) {
|
||||
this.totalsService_ = totalsService
|
||||
|
||||
this.paymentProviderService_ = paymentProviderService
|
||||
|
||||
this.customerService_ = customerService
|
||||
|
||||
this.discountService_ = discountService
|
||||
|
||||
this.cartService_ = cartService
|
||||
|
||||
this.eventBus_ = eventBusService
|
||||
|
||||
this.eventBus_.subscribe("order.completed", async order => {
|
||||
const paymentProvider = this.paymentProviderService_.retrieveProvider(
|
||||
order.payment_method.provider_id
|
||||
)
|
||||
|
||||
await paymentProvider.capturePayment(order._id)
|
||||
})
|
||||
|
||||
this.eventBus_.subscribe("order.placed", async order => {
|
||||
await this.customerService_.addOrder(order.customer_id, order._id)
|
||||
|
||||
@@ -29,6 +29,42 @@ class OrderSubscriber {
|
||||
|
||||
await this.customerService_.addAddress(order.customer_id, address)
|
||||
})
|
||||
|
||||
this.eventBus_.subscribe("order.placed", async order => {
|
||||
await this.cartService_.delete(order.cart_id).catch(err => {
|
||||
if (err.type !== "not_found") {
|
||||
throw err
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
this.eventBus_.subscribe("order.placed", this.handleDiscounts)
|
||||
}
|
||||
|
||||
handleDiscounts = async order => {
|
||||
await Promise.all(
|
||||
order.discounts.map(async d => {
|
||||
const subtotal = await this.totalsService_.getSubtotal(order)
|
||||
if (d.is_giftcard) {
|
||||
const discountRule = {
|
||||
...d.discount_rule,
|
||||
value: Math.max(0, d.discount_rule.value - subtotal),
|
||||
}
|
||||
|
||||
delete discountRule._id
|
||||
|
||||
return this.discountService_.update(d._id, {
|
||||
discount_rule: discountRule,
|
||||
usage_count: d.usage_count + 1,
|
||||
disabled: discountRule.value === 0,
|
||||
})
|
||||
} else {
|
||||
return this.discountService_.update(d._id, {
|
||||
usage_count: d.usage_count + 1,
|
||||
})
|
||||
}
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+19
-15
@@ -3665,6 +3665,11 @@ is-number@^3.0.0:
|
||||
dependencies:
|
||||
kind-of "^3.0.2"
|
||||
|
||||
is-number@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/is-number/-/is-number-4.0.0.tgz#0026e37f5454d73e356dfe6564699867c6a7f0ff"
|
||||
integrity sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==
|
||||
|
||||
is-number@^7.0.0:
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
|
||||
@@ -4553,19 +4558,16 @@ map-visit@^1.0.0:
|
||||
dependencies:
|
||||
object-visit "^1.0.0"
|
||||
|
||||
math-random@^1.0.1:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/math-random/-/math-random-1.0.4.tgz#5dd6943c938548267016d4e34f057583080c514c"
|
||||
integrity sha512-rUxjysqif/BZQH2yhd5Aaq7vXMSx9NdEsQcyA07uEzIvxgI7zIr33gGsh+RU0/XjmQpCW7RsVof1vlkvQVCK5A==
|
||||
|
||||
media-typer@0.3.0:
|
||||
version "0.3.0"
|
||||
resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
|
||||
integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=
|
||||
|
||||
medusa-core-utils@^0.3.0:
|
||||
version "0.1.39"
|
||||
resolved "https://registry.yarnpkg.com/medusa-core-utils/-/medusa-core-utils-0.1.39.tgz#d57816c9bd43f9a92883650c1e66add1665291df"
|
||||
integrity sha512-R8+U1ile7if+nR6Cjh5exunx0ETV0OfkWUUBUpz1KmHSDv0V0CcvQqU9lcZesPFDEbu3Y2iEjsCqidVA4nG2nQ==
|
||||
dependencies:
|
||||
"@hapi/joi" "^16.1.8"
|
||||
joi-objectid "^3.0.1"
|
||||
|
||||
medusa-interfaces@^0.1.27:
|
||||
version "0.1.27"
|
||||
resolved "https://registry.yarnpkg.com/medusa-interfaces/-/medusa-interfaces-0.1.27.tgz#e77f9a9f82a7118eac8b35c1498ef8a5cec78898"
|
||||
@@ -4573,13 +4575,6 @@ medusa-interfaces@^0.1.27:
|
||||
dependencies:
|
||||
mongoose "^5.8.0"
|
||||
|
||||
medusa-test-utils@^0.3.0:
|
||||
version "0.1.39"
|
||||
resolved "https://registry.yarnpkg.com/medusa-test-utils/-/medusa-test-utils-0.1.39.tgz#b7c166006a2fa4f02e52ab3bfafc19a3ae787f3e"
|
||||
integrity sha512-M/Br8/HYvl7x2oLnme4NxdQwoyV0XUyOWiCyvPp7q1HUTB684lhJf1MikZVrcSjsh2L1rpyi3GRbKdf4cpJWvw==
|
||||
dependencies:
|
||||
mongoose "^5.8.0"
|
||||
|
||||
memory-pager@^1.0.2:
|
||||
version "1.5.0"
|
||||
resolved "https://registry.yarnpkg.com/memory-pager/-/memory-pager-1.5.0.tgz#d8751655d22d384682741c972f2c3d6dfa3e66b5"
|
||||
@@ -5501,6 +5496,15 @@ random-bytes@~1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/random-bytes/-/random-bytes-1.0.0.tgz#4f68a1dc0ae58bd3fb95848c30324db75d64360b"
|
||||
integrity sha1-T2ih3Arli9P7lYSMMDJNt11kNgs=
|
||||
|
||||
randomatic@^3.1.1:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-3.1.1.tgz#b776efc59375984e36c537b2f51a1f0aff0da1ed"
|
||||
integrity sha512-TuDE5KxZ0J461RVjrJZCJc+J+zCkTb1MbH9AQUq68sMhOMcy9jLcb3BrZKgp9q9Ncltdg4QVqWrH02W2EFFVYw==
|
||||
dependencies:
|
||||
is-number "^4.0.0"
|
||||
kind-of "^6.0.0"
|
||||
math-random "^1.0.1"
|
||||
|
||||
range-parser@~1.2.1:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031"
|
||||
|
||||
Reference in New Issue
Block a user