fix merge conflicts
This commit is contained in:
126
README.md
126
README.md
@@ -1,36 +1,96 @@
|
||||
# medusa
|
||||
Medusa Monorepo
|
||||
# Medusa
|
||||
|
||||
Medusa is a headless commerce engine built with Node.js using Express with a Postgresql database.
|
||||
|
||||
# MVP Roadmap (!)
|
||||
## Documentation
|
||||
|
||||
- Finish core
|
||||
- Services
|
||||
- [x] Auth
|
||||
- [x] Product
|
||||
- [x] Product Variant
|
||||
- [x] User
|
||||
- [ ] Region
|
||||
- [ ] Cart
|
||||
- [x] Customer
|
||||
- [ ] Order
|
||||
- [ ] Promo Code
|
||||
- [ ] Gift cards
|
||||
- REST API controllers
|
||||
- Admin
|
||||
- Store
|
||||
- Core plugins
|
||||
- Payment Providers
|
||||
- [ ] Stripe
|
||||
- [ ] Klarna
|
||||
- [ ] PayPal
|
||||
- [ ] QuickPay
|
||||
- Fulfillment providers
|
||||
- [ ] E-conomic
|
||||
- [ ] Brightpearl
|
||||
- [ ] Shipmondo
|
||||
- [ ] Webshipper
|
||||
- Transactional Emails
|
||||
- [ ] Twilio SendGrid
|
||||
- Role Based Permission
|
||||
- Contentful sync plugin (to send SKUs to Contentful for content enrichment)
|
||||
See [Medusa Commerce API docs](https://docs.medusa-commerce.com/api/store/) for Node.js.
|
||||
|
||||
## Get started in less than 5 minutes
|
||||
|
||||
You can get a Medusa engine up and running in your local development environment within a couple of minutes. Perform the following steps:
|
||||
|
||||
1. Install Medusa, the Medusa CLI, Medusa babel preset and Medusa interfaces
|
||||
|
||||
```bash
|
||||
# core medusa
|
||||
npm install @medusajs/medusa
|
||||
yarn add @medusajs/medusa
|
||||
|
||||
# CLI
|
||||
npm install -g @medusa/medusa-cli
|
||||
yarn add global @medusajs/medusa-cli
|
||||
|
||||
# babel preset
|
||||
npm install babel-preset-medusa-package
|
||||
yarn add babel-preset-medusa-package
|
||||
|
||||
# interfaces
|
||||
npm install medusa-interfaces
|
||||
yarn add medusa-interfaces
|
||||
```
|
||||
2. Create a file `medusa-config.js` at the root level of your Node.js project and fill in required settings
|
||||
```
|
||||
// CORS to avoid issues when consuming Medusa from a client
|
||||
const STORE_CORS = "http://localhost:8000";
|
||||
|
||||
// Database URL (here we use a local database called medusa-development)
|
||||
const DATABASE_URL = "postgres://localhost/medusa-development";
|
||||
|
||||
// Medusa uses Redis, so this needs configuration as well
|
||||
const REDIS_URL = "redis://localhost:6379"
|
||||
|
||||
// This is the place to include plugins. See API documentation for a thorough guide on plugins.
|
||||
const plugins = [];
|
||||
|
||||
module.exports = {
|
||||
projectConfig: {
|
||||
redis_url: REDIS_URL,
|
||||
database_url: DATABASE_URL,
|
||||
database_logging: true,
|
||||
database_extra:
|
||||
process.env.NODE_ENV === "production" ||
|
||||
process.env.NODE_ENV === "staging"
|
||||
? {
|
||||
ssl: { rejectUnauthorized: false },
|
||||
}
|
||||
: {},
|
||||
database_type: "postgres",
|
||||
store_cors: STORE_CORS,
|
||||
},
|
||||
plugins,
|
||||
};
|
||||
```
|
||||
|
||||
3. Create a Medusa user, such that you can perform authenticated calls
|
||||
|
||||
```bash
|
||||
# provide email and password to the command
|
||||
medusa user -e lebron@james.com -p lebronjames123
|
||||
```
|
||||
|
||||
4. Start your Medusa engine in your local environment
|
||||
|
||||
```bash
|
||||
medusa develop
|
||||
```
|
||||
|
||||
5. Open any client or API tool to start using your Medusa engine
|
||||
|
||||
Medusa is running at `http://localhost:4000`. You should now investigate our [API docs](https://docs.medusa-commerce.com/api/store/) to start playing around with your new headless commerce engine.
|
||||
|
||||
After these four steps and only a couple of minutes, you now have a complete commerce engine running locally.
|
||||
|
||||
## Contribution
|
||||
|
||||
Medusa is all about the community. Therefore, we would love for you to help us build the most robust and powerful commerce engine on the market. Whether its fixing bugs, improving our documentation or simply spreading the word, please feel free to join in.
|
||||
|
||||
## Repository structure
|
||||
|
||||
The Medusa repository is a mono-repository managed using Lerna. Lerna allows us to have all Medusa packages in one place, and still distribute them as separate NPM packages.
|
||||
|
||||
## Licensed
|
||||
|
||||
Licended under the [MIT License](https://github.com/medusajs/medusa/blob/master/LICENSE)
|
||||
|
||||
## Thank you!
|
||||
|
||||
135
integration-tests/api/__tests__/admin/customer.js
Normal file
135
integration-tests/api/__tests__/admin/customer.js
Normal file
@@ -0,0 +1,135 @@
|
||||
const { dropDatabase } = require("pg-god");
|
||||
const path = require("path");
|
||||
|
||||
const setupServer = require("../../../helpers/setup-server");
|
||||
const { useApi } = require("../../../helpers/use-api");
|
||||
const { initDb } = require("../../../helpers/use-db");
|
||||
|
||||
const customerSeeder = require("../../helpers/customer-seeder");
|
||||
const adminSeeder = require("../../helpers/admin-seeder");
|
||||
|
||||
jest.setTimeout(30000);
|
||||
|
||||
describe("/admin/customers", () => {
|
||||
let medusaProcess;
|
||||
let dbConnection;
|
||||
|
||||
beforeAll(async () => {
|
||||
const cwd = path.resolve(path.join(__dirname, "..", ".."));
|
||||
dbConnection = await initDb({ cwd });
|
||||
medusaProcess = await setupServer({ cwd });
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await dbConnection.close();
|
||||
await dropDatabase({ databaseName: "medusa-integration" });
|
||||
|
||||
medusaProcess.kill();
|
||||
});
|
||||
|
||||
describe("GET /admin/customers", () => {
|
||||
beforeEach(async () => {
|
||||
try {
|
||||
await adminSeeder(dbConnection);
|
||||
await customerSeeder(dbConnection);
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
throw err;
|
||||
}
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
const manager = dbConnection.manager;
|
||||
await manager.query(`DELETE FROM "address"`);
|
||||
await manager.query(`DELETE FROM "customer"`);
|
||||
await manager.query(`DELETE FROM "user"`);
|
||||
});
|
||||
|
||||
it("lists customers and query count", async () => {
|
||||
const api = useApi();
|
||||
|
||||
const response = await api
|
||||
.get("/admin/customers", {
|
||||
headers: {
|
||||
Authorization: "Bearer test_token",
|
||||
},
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
});
|
||||
|
||||
expect(response.status).toEqual(200);
|
||||
expect(response.data.count).toEqual(3);
|
||||
expect(response.data.customers).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: "test-customer-1",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: "test-customer-2",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: "test-customer-3",
|
||||
}),
|
||||
])
|
||||
);
|
||||
});
|
||||
|
||||
it("lists customers with specific query", async () => {
|
||||
const api = useApi();
|
||||
|
||||
const response = await api
|
||||
.get("/admin/customers?q=test2@email.com", {
|
||||
headers: {
|
||||
Authorization: "Bearer test_token",
|
||||
},
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
});
|
||||
|
||||
expect(response.status).toEqual(200);
|
||||
expect(response.data.count).toEqual(1);
|
||||
expect(response.data.customers).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: "test-customer-2",
|
||||
email: "test2@email.com",
|
||||
}),
|
||||
])
|
||||
);
|
||||
});
|
||||
|
||||
it("lists customers with expand query", async () => {
|
||||
const api = useApi();
|
||||
|
||||
const response = await api
|
||||
.get("/admin/customers?q=test1@email.com&expand=shipping_addresses", {
|
||||
headers: {
|
||||
Authorization: "Bearer test_token",
|
||||
},
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
});
|
||||
|
||||
expect(response.status).toEqual(200);
|
||||
expect(response.data.count).toEqual(1);
|
||||
console.log(response.data.customers);
|
||||
expect(response.data.customers).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: "test-customer-1",
|
||||
shipping_addresses: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: "test-address",
|
||||
first_name: "Lebron",
|
||||
last_name: "James",
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
])
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
27
integration-tests/api/helpers/customer-seeder.js
Normal file
27
integration-tests/api/helpers/customer-seeder.js
Normal file
@@ -0,0 +1,27 @@
|
||||
const { Customer, Address } = require("@medusajs/medusa");
|
||||
|
||||
module.exports = async (connection, data = {}) => {
|
||||
const manager = connection.manager;
|
||||
|
||||
await manager.insert(Customer, {
|
||||
id: "test-customer-1",
|
||||
email: "test1@email.com",
|
||||
});
|
||||
|
||||
await manager.insert(Customer, {
|
||||
id: "test-customer-2",
|
||||
email: "test2@email.com",
|
||||
});
|
||||
|
||||
await manager.insert(Customer, {
|
||||
id: "test-customer-3",
|
||||
email: "test3@email.com",
|
||||
});
|
||||
|
||||
await manager.insert(Address, {
|
||||
id: "test-address",
|
||||
first_name: "Lebron",
|
||||
last_name: "James",
|
||||
customer_id: "test-customer-1",
|
||||
});
|
||||
};
|
||||
19
integration-tests/api/src/services/test-not.js
Normal file
19
integration-tests/api/src/services/test-not.js
Normal file
@@ -0,0 +1,19 @@
|
||||
import { NotificationService } from "medusa-interfaces";
|
||||
|
||||
class TestNotiService extends NotificationService {
|
||||
static identifier = "test-not";
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
async sendNotification() {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
async resendNotification() {
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
export default TestNotiService;
|
||||
@@ -3,6 +3,25 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.1.3](https://github.com/medusajs/medusa/compare/medusa-fulfillment-webshipper@1.1.3-next.0...medusa-fulfillment-webshipper@1.1.3) (2021-02-25)
|
||||
|
||||
**Note:** Version bump only for package medusa-fulfillment-webshipper
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.1.3-next.0](https://github.com/medusajs/medusa/compare/medusa-fulfillment-webshipper@1.1.2...medusa-fulfillment-webshipper@1.1.3-next.0) (2021-02-22)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **medusa:** tracking links ([#177](https://github.com/medusajs/medusa/issues/177)) ([99ad43b](https://github.com/medusajs/medusa/commit/99ad43bf47c3922f391d433448b1c4affd88f457))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.1.2](https://github.com/medusajs/medusa/compare/medusa-fulfillment-webshipper@1.1.1...medusa-fulfillment-webshipper@1.1.2) (2021-02-17)
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "medusa-fulfillment-webshipper",
|
||||
"version": "1.1.2",
|
||||
"version": "1.1.3",
|
||||
"description": "Webshipper Fulfillment provider for Medusa",
|
||||
"main": "index.js",
|
||||
"repository": {
|
||||
|
||||
@@ -0,0 +1,211 @@
|
||||
import WebshipperFulfillmentService from "../webshipper-fulfillment"
|
||||
|
||||
describe("WebshipperFulfillmentService", () => {
|
||||
const orderService = {
|
||||
createShipment: jest.fn(),
|
||||
}
|
||||
const swapService = {
|
||||
createShipment: jest.fn(),
|
||||
}
|
||||
const claimService = {
|
||||
createShipment: jest.fn(),
|
||||
}
|
||||
|
||||
describe("handleWebhook", () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
it("creates an order shipment", async () => {
|
||||
const webshipper = new WebshipperFulfillmentService(
|
||||
{
|
||||
orderService,
|
||||
claimService,
|
||||
swapService,
|
||||
},
|
||||
{}
|
||||
)
|
||||
|
||||
webshipper.retrieveRelationship = () => {
|
||||
return {
|
||||
data: {
|
||||
attributes: {
|
||||
ext_ref: "order_test.ful_test",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
const body = {
|
||||
data: {
|
||||
attributes: {
|
||||
tracking_links: [
|
||||
{
|
||||
url: "https://test/1134",
|
||||
number: "12324245345",
|
||||
},
|
||||
{
|
||||
url: "https://test/1234",
|
||||
number: "12324245345",
|
||||
},
|
||||
],
|
||||
},
|
||||
relationships: {
|
||||
order: {
|
||||
id: "order",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
await webshipper.handleWebhook("", body)
|
||||
|
||||
expect(claimService.createShipment).toHaveBeenCalledTimes(0)
|
||||
expect(swapService.createShipment).toHaveBeenCalledTimes(0)
|
||||
|
||||
expect(orderService.createShipment).toHaveBeenCalledTimes(1)
|
||||
expect(orderService.createShipment).toHaveBeenCalledWith(
|
||||
"order_test",
|
||||
"ful_test",
|
||||
[
|
||||
{
|
||||
url: "https://test/1134",
|
||||
tracking_number: "12324245345",
|
||||
},
|
||||
{
|
||||
url: "https://test/1234",
|
||||
tracking_number: "12324245345",
|
||||
},
|
||||
]
|
||||
)
|
||||
})
|
||||
|
||||
it("creates a claim shipment", async () => {
|
||||
const webshipper = new WebshipperFulfillmentService(
|
||||
{
|
||||
orderService,
|
||||
claimService,
|
||||
swapService,
|
||||
},
|
||||
{}
|
||||
)
|
||||
|
||||
webshipper.retrieveRelationship = () => {
|
||||
return {
|
||||
data: {
|
||||
attributes: {
|
||||
ext_ref: "claim_test.ful_test",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
const body = {
|
||||
data: {
|
||||
attributes: {
|
||||
tracking_links: [
|
||||
{
|
||||
url: "https://test/1134",
|
||||
number: "12324245345",
|
||||
},
|
||||
{
|
||||
url: "https://test/1234",
|
||||
number: "12324245345",
|
||||
},
|
||||
],
|
||||
},
|
||||
relationships: {
|
||||
order: {
|
||||
id: "order",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
await webshipper.handleWebhook("", body)
|
||||
|
||||
expect(orderService.createShipment).toHaveBeenCalledTimes(0)
|
||||
expect(swapService.createShipment).toHaveBeenCalledTimes(0)
|
||||
|
||||
expect(claimService.createShipment).toHaveBeenCalledTimes(1)
|
||||
expect(claimService.createShipment).toHaveBeenCalledWith(
|
||||
"claim_test",
|
||||
"ful_test",
|
||||
[
|
||||
{
|
||||
url: "https://test/1134",
|
||||
tracking_number: "12324245345",
|
||||
},
|
||||
{
|
||||
url: "https://test/1234",
|
||||
tracking_number: "12324245345",
|
||||
},
|
||||
]
|
||||
)
|
||||
})
|
||||
|
||||
it("creates a swap shipment", async () => {
|
||||
const webshipper = new WebshipperFulfillmentService(
|
||||
{
|
||||
orderService,
|
||||
claimService,
|
||||
swapService,
|
||||
},
|
||||
{}
|
||||
)
|
||||
|
||||
webshipper.retrieveRelationship = () => {
|
||||
return {
|
||||
data: {
|
||||
attributes: {
|
||||
ext_ref: "swap_test.ful_test",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
const body = {
|
||||
data: {
|
||||
attributes: {
|
||||
tracking_links: [
|
||||
{
|
||||
url: "https://test/1134",
|
||||
number: "12324245345",
|
||||
},
|
||||
{
|
||||
url: "https://test/1234",
|
||||
number: "12324245345",
|
||||
},
|
||||
],
|
||||
},
|
||||
relationships: {
|
||||
order: {
|
||||
id: "order",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
await webshipper.handleWebhook("", body)
|
||||
|
||||
expect(orderService.createShipment).toHaveBeenCalledTimes(0)
|
||||
expect(claimService.createShipment).toHaveBeenCalledTimes(0)
|
||||
|
||||
expect(swapService.createShipment).toHaveBeenCalledTimes(1)
|
||||
expect(swapService.createShipment).toHaveBeenCalledWith(
|
||||
"swap_test",
|
||||
"ful_test",
|
||||
[
|
||||
{
|
||||
url: "https://test/1134",
|
||||
tracking_number: "12324245345",
|
||||
},
|
||||
{
|
||||
url: "https://test/1234",
|
||||
tracking_number: "12324245345",
|
||||
},
|
||||
]
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -363,9 +363,10 @@ class WebshipperFulfillmentService extends FulfillmentService {
|
||||
body.data.relationships.order
|
||||
)
|
||||
if (wsOrder.data && wsOrder.data.attributes.ext_ref) {
|
||||
const trackingNumbers = body.data.attributes.tracking_links.map(
|
||||
(l) => l.number
|
||||
)
|
||||
const trackingLinks = body.data.attributes.tracking_links.map((l) => ({
|
||||
url: l.url,
|
||||
tracking_number: l.number,
|
||||
}))
|
||||
const [orderId, fulfillmentIndex] = wsOrder.data.attributes.ext_ref.split(
|
||||
"."
|
||||
)
|
||||
@@ -375,7 +376,7 @@ class WebshipperFulfillmentService extends FulfillmentService {
|
||||
return this.swapService_.createShipment(
|
||||
orderId,
|
||||
fulfillmentIndex,
|
||||
trackingNumbers
|
||||
trackingLinks
|
||||
)
|
||||
} else {
|
||||
const swap = await this.swapService_.retrieve(orderId.substring(1), {
|
||||
@@ -385,21 +386,21 @@ class WebshipperFulfillmentService extends FulfillmentService {
|
||||
return this.swapService_.createShipment(
|
||||
swap.id,
|
||||
fulfillment.id,
|
||||
trackingNumbers
|
||||
trackingLinks
|
||||
)
|
||||
}
|
||||
} else if (orderId.charAt(0).toLowerCase() === "c") {
|
||||
return this.claimService_.createShipment(
|
||||
orderId,
|
||||
fulfillmentIndex,
|
||||
trackingNumbers
|
||||
trackingLinks
|
||||
)
|
||||
} else {
|
||||
if (fulfillmentIndex.startsWith("ful")) {
|
||||
return this.orderService_.createShipment(
|
||||
orderId,
|
||||
fulfillmentIndex,
|
||||
trackingNumbers
|
||||
trackingLinks
|
||||
)
|
||||
} else {
|
||||
const order = await this.orderService_.retrieve(orderId, {
|
||||
@@ -411,7 +412,7 @@ class WebshipperFulfillmentService extends FulfillmentService {
|
||||
return this.orderService_.createShipment(
|
||||
order.id,
|
||||
fulfillment.id,
|
||||
trackingNumbers
|
||||
trackingLinks
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,11 @@ export default async (req, res) => {
|
||||
const purchaseUnit = order.purchase_units[0]
|
||||
const cartId = purchaseUnit.custom_id
|
||||
|
||||
if (!cartId) {
|
||||
res.sendStatus(200)
|
||||
return
|
||||
}
|
||||
|
||||
const manager = req.scope.resolve("manager")
|
||||
const cartService = req.scope.resolve("cartService")
|
||||
const orderService = req.scope.resolve("orderService")
|
||||
|
||||
@@ -206,7 +206,7 @@ class PayPalProviderService extends PaymentService {
|
||||
|
||||
return sessionData
|
||||
} catch (error) {
|
||||
throw error
|
||||
return this.createPayment(cart)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -141,7 +141,7 @@ class StripeProviderService extends PaymentService {
|
||||
const amount = await this.totalsService_.getTotal(cart)
|
||||
|
||||
const intentRequest = {
|
||||
amount: amount,
|
||||
amount: Math.round(amount),
|
||||
currency: currency_code,
|
||||
setup_future_usage: "on_session",
|
||||
capture_method: this.options_.capture ? "automatic" : "manual",
|
||||
@@ -242,12 +242,12 @@ class StripeProviderService extends PaymentService {
|
||||
if (stripeId !== sessionData.customer) {
|
||||
return this.createPayment(cart)
|
||||
} else {
|
||||
if (cart.total && sessionData.amount === cart.total) {
|
||||
if (cart.total && sessionData.amount === Math.round(cart.total)) {
|
||||
return sessionData
|
||||
}
|
||||
|
||||
return this.stripe_.paymentIntents.update(sessionData.id, {
|
||||
amount: cart.total,
|
||||
amount: Math.round(cart.total),
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -309,7 +309,7 @@ class StripeProviderService extends PaymentService {
|
||||
const { id } = payment.data
|
||||
try {
|
||||
await this.stripe_.refunds.create({
|
||||
amount: amountToRefund,
|
||||
amount: Math.round(amountToRefund),
|
||||
payment_intent: id,
|
||||
})
|
||||
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.1.5](https://github.com/medusajs/medusa/compare/medusa-plugin-brightpearl@1.1.4...medusa-plugin-brightpearl@1.1.5) (2021-02-25)
|
||||
|
||||
**Note:** Version bump only for package medusa-plugin-brightpearl
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.1.4](https://github.com/medusajs/medusa/compare/medusa-plugin-brightpearl@1.1.3...medusa-plugin-brightpearl@1.1.4) (2021-02-17)
|
||||
|
||||
**Note:** Version bump only for package medusa-plugin-brightpearl
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "medusa-plugin-brightpearl",
|
||||
"version": "1.1.4",
|
||||
"version": "1.1.5",
|
||||
"description": "Brightpearl plugin for Medusa Commerce",
|
||||
"main": "index.js",
|
||||
"repository": {
|
||||
|
||||
@@ -5,6 +5,7 @@ import Brightpearl from "../utils/brightpearl"
|
||||
class BrightpearlService extends BaseService {
|
||||
constructor(
|
||||
{
|
||||
manager,
|
||||
oauthService,
|
||||
totalsService,
|
||||
productVariantService,
|
||||
@@ -18,6 +19,7 @@ class BrightpearlService extends BaseService {
|
||||
) {
|
||||
super()
|
||||
|
||||
this.manager_ = manager
|
||||
this.options = options
|
||||
this.productVariantService_ = productVariantService
|
||||
this.regionService_ = regionService
|
||||
@@ -189,8 +191,12 @@ class BrightpearlService extends BaseService {
|
||||
.retrieveBySKU(sku)
|
||||
.catch((_) => undefined)
|
||||
if (variant && variant.manage_inventory) {
|
||||
await this.productVariantService_.update(variant.id, {
|
||||
inventory_quantity: onHand,
|
||||
await this.manager_.transaction((m) => {
|
||||
return this.productVariantService_
|
||||
.withTransaction(m)
|
||||
.update(variant.id, {
|
||||
inventory_quantity: onHand,
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,17 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.1.4](https://github.com/medusajs/medusa/compare/medusa-plugin-contentful@1.1.3...medusa-plugin-contentful@1.1.4) (2021-02-25)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **medusa-plugin-contentful:** Allow custom fields in plugin options ([#180](https://github.com/medusajs/medusa/issues/180)) ([587a464](https://github.com/medusajs/medusa/commit/587a464e83576833ff616bde7bb26b1bb48472fe))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.1.3](https://github.com/medusajs/medusa/compare/medusa-plugin-contentful@1.1.2...medusa-plugin-contentful@1.1.3) (2021-02-17)
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "medusa-plugin-contentful",
|
||||
"version": "1.1.3",
|
||||
"version": "1.1.4",
|
||||
"description": "Contentful plugin for Medusa Commerce",
|
||||
"main": "index.js",
|
||||
"repository": {
|
||||
|
||||
@@ -19,8 +19,16 @@ const checkContentTypes = async (container) => {
|
||||
if (product && product.fields) {
|
||||
const productFields = product.fields
|
||||
|
||||
const customProductFields = Object.keys(
|
||||
contentfulService.options_.custom_product_fields || {}
|
||||
)
|
||||
const keys = Object.values(productFields).map((f) => f.id)
|
||||
if (!requiredProductFields.every((f) => keys.includes(f))) {
|
||||
|
||||
const missingKeys = requiredProductFields.filter(
|
||||
(rpf) => !keys.includes(rpf) && !customProductFields.includes(rpf)
|
||||
)
|
||||
|
||||
if (missingKeys.length) {
|
||||
throw Error(
|
||||
`Contentful: Content type ${`product`} is missing some required key(s). Required: ${requiredProductFields.join(
|
||||
", "
|
||||
@@ -32,8 +40,16 @@ const checkContentTypes = async (container) => {
|
||||
if (variant && variant.fields) {
|
||||
const variantFields = variant.fields
|
||||
|
||||
const customVariantFields = Object.keys(
|
||||
contentfulService.options_.custom_variant_fields || {}
|
||||
)
|
||||
const keys = Object.values(variantFields).map((f) => f.id)
|
||||
if (!requiredVariantFields.every((f) => keys.includes(f))) {
|
||||
|
||||
const missingKeys = requiredVariantFields.filter(
|
||||
(rpf) => !keys.includes(rpf) && !customVariantFields.includes(rpf)
|
||||
)
|
||||
|
||||
if (missingKeys.length) {
|
||||
throw Error(
|
||||
`Contentful: Content type ${`productVariant`} is missing some required key(s). Required: ${requiredVariantFields.join(
|
||||
", "
|
||||
@@ -47,13 +63,13 @@ const requiredProductFields = [
|
||||
"title",
|
||||
"variants",
|
||||
"options",
|
||||
"objectId",
|
||||
"medusaId",
|
||||
"type",
|
||||
"collection",
|
||||
"tags",
|
||||
"handle",
|
||||
]
|
||||
|
||||
const requiredVariantFields = ["title", "sku", "prices", "options", "objectId"]
|
||||
const requiredVariantFields = ["title", "sku", "prices", "options", "medusaId"]
|
||||
|
||||
export default checkContentTypes
|
||||
|
||||
@@ -111,10 +111,27 @@ class ContentfulService extends BaseService {
|
||||
return assets
|
||||
}
|
||||
|
||||
getCustomField(field, type) {
|
||||
const customOptions = this.options_[`custom_${type}_fields`]
|
||||
|
||||
if (customOptions) {
|
||||
return customOptions[field] || field
|
||||
} else {
|
||||
return field
|
||||
}
|
||||
}
|
||||
|
||||
async createProductInContentful(product) {
|
||||
try {
|
||||
const p = await this.productService_.retrieve(product.id, {
|
||||
relations: ["variants", "options", "tags", "type", "collection"],
|
||||
relations: [
|
||||
"variants",
|
||||
"options",
|
||||
"tags",
|
||||
"type",
|
||||
"collection",
|
||||
"images",
|
||||
],
|
||||
})
|
||||
|
||||
const environment = await this.getContentfulEnvironment_()
|
||||
@@ -122,46 +139,92 @@ class ContentfulService extends BaseService {
|
||||
const variantLinks = this.getVariantLinks_(variantEntries)
|
||||
|
||||
const fields = {
|
||||
title: {
|
||||
[this.getCustomField("title", "product")]: {
|
||||
"en-US": p.title,
|
||||
},
|
||||
variants: {
|
||||
[this.getCustomField("variants", "product")]: {
|
||||
"en-US": variantLinks,
|
||||
},
|
||||
options: {
|
||||
[this.getCustomField("options", "product")]: {
|
||||
"en-US": p.options,
|
||||
},
|
||||
objectId: {
|
||||
[this.getCustomField("medusaId", "product")]: {
|
||||
"en-US": p.id,
|
||||
},
|
||||
}
|
||||
|
||||
if (p.images.length > 0) {
|
||||
const imageLinks = await this.createImageAssets(product)
|
||||
|
||||
const thumbnailAsset = await environment.createAsset({
|
||||
fields: {
|
||||
title: {
|
||||
"en-US": `${p.title}`,
|
||||
},
|
||||
description: {
|
||||
"en-US": "",
|
||||
},
|
||||
file: {
|
||||
"en-US": {
|
||||
contentType: "image/xyz",
|
||||
fileName: p.thumbnail,
|
||||
upload: p.thumbnail,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
await thumbnailAsset.processForAllLocales()
|
||||
|
||||
const thumbnailLink = {
|
||||
sys: {
|
||||
type: "Link",
|
||||
linkType: "Asset",
|
||||
id: thumbnailAsset.sys.id,
|
||||
},
|
||||
}
|
||||
|
||||
fields.thumbnail = {
|
||||
"en-US": thumbnailLink,
|
||||
}
|
||||
|
||||
if (imageLinks) {
|
||||
fields.images = {
|
||||
"en-US": imageLinks,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (p.type) {
|
||||
const type = {
|
||||
"en-US": p.type.value,
|
||||
}
|
||||
fields.type = type
|
||||
|
||||
fields[this.getCustomField("type", "product")] = type
|
||||
}
|
||||
|
||||
if (p.collection) {
|
||||
const collection = {
|
||||
"en-US": p.collection.title,
|
||||
}
|
||||
fields.collection = collection
|
||||
|
||||
fields[this.getCustomField("collection", "product")] = collection
|
||||
}
|
||||
|
||||
if (p.tags) {
|
||||
const tags = {
|
||||
"en-US": p.tags,
|
||||
}
|
||||
fields.tags = tags
|
||||
|
||||
fields[this.getCustomField("tags", "product")] = tags
|
||||
}
|
||||
|
||||
if (p.handle) {
|
||||
const handle = {
|
||||
"en-US": p.handle,
|
||||
}
|
||||
fields.handle = handle
|
||||
|
||||
fields[this.getCustomField("handle", "product")] = handle
|
||||
}
|
||||
|
||||
const result = await environment.createEntryWithId("product", p.id, {
|
||||
@@ -189,19 +252,19 @@ class ContentfulService extends BaseService {
|
||||
v.id,
|
||||
{
|
||||
fields: {
|
||||
title: {
|
||||
[this.getCustomField("title", "variant")]: {
|
||||
"en-US": v.title,
|
||||
},
|
||||
sku: {
|
||||
[this.getCustomField("sku", "variant")]: {
|
||||
"en-US": v.sku,
|
||||
},
|
||||
prices: {
|
||||
[this.getCustomField("prices", "variant")]: {
|
||||
"en-US": v.prices,
|
||||
},
|
||||
options: {
|
||||
[this.getCustomField("options", "variant")]: {
|
||||
"en-US": v.options,
|
||||
},
|
||||
objectId: {
|
||||
[this.getCustomField("medusaId", "variant")]: {
|
||||
"en-US": v.id,
|
||||
},
|
||||
},
|
||||
@@ -240,7 +303,14 @@ class ContentfulService extends BaseService {
|
||||
}
|
||||
|
||||
const p = await this.productService_.retrieve(product.id, {
|
||||
relations: ["options", "variants", "type", "collection", "tags"],
|
||||
relations: [
|
||||
"options",
|
||||
"variants",
|
||||
"type",
|
||||
"collection",
|
||||
"tags",
|
||||
"images",
|
||||
],
|
||||
})
|
||||
|
||||
const variantEntries = await this.getVariantEntries_(p.variants)
|
||||
@@ -248,46 +318,86 @@ class ContentfulService extends BaseService {
|
||||
|
||||
const productEntryFields = {
|
||||
...productEntry.fields,
|
||||
title: {
|
||||
[this.getCustomField("title", "product")]: {
|
||||
"en-US": p.title,
|
||||
},
|
||||
options: {
|
||||
[this.getCustomField("options", "product")]: {
|
||||
"en-US": p.options,
|
||||
},
|
||||
variants: {
|
||||
[this.getCustomField("variants", "product")]: {
|
||||
"en-US": variantLinks,
|
||||
},
|
||||
objectId: {
|
||||
[this.getCustomField("medusaId", "product")]: {
|
||||
"en-US": p.id,
|
||||
},
|
||||
}
|
||||
|
||||
if (p.thumbnail) {
|
||||
const thumbnailAsset = await environment.createAsset({
|
||||
fields: {
|
||||
title: {
|
||||
"en-US": `${p.title}`,
|
||||
},
|
||||
description: {
|
||||
"en-US": "",
|
||||
},
|
||||
file: {
|
||||
"en-US": {
|
||||
contentType: "image/xyz",
|
||||
fileName: p.thumbnail,
|
||||
upload: p.thumbnail,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
await thumbnailAsset.processForAllLocales()
|
||||
|
||||
const thumbnailLink = {
|
||||
sys: {
|
||||
type: "Link",
|
||||
linkType: "Asset",
|
||||
id: thumbnailAsset.sys.id,
|
||||
},
|
||||
}
|
||||
|
||||
productEntryFields.thumbnail = {
|
||||
"en-US": thumbnailLink,
|
||||
}
|
||||
}
|
||||
|
||||
if (p.type) {
|
||||
const type = {
|
||||
"en-US": p.type.value,
|
||||
}
|
||||
productEntryFields.type = type
|
||||
|
||||
productEntryFields[this.getCustomField("type", "product")] = type
|
||||
}
|
||||
|
||||
if (p.collection) {
|
||||
const collection = {
|
||||
"en-US": p.collection.title,
|
||||
}
|
||||
productEntryFields.collection = collection
|
||||
|
||||
productEntryFields[
|
||||
this.getCustomField("collection", "product")
|
||||
] = collection
|
||||
}
|
||||
|
||||
if (p.tags) {
|
||||
const tags = {
|
||||
"en-US": p.tags,
|
||||
}
|
||||
productEntryFields.tags = tags
|
||||
|
||||
productEntryFields[this.getCustomField("tags", "product")] = tags
|
||||
}
|
||||
|
||||
if (p.handle) {
|
||||
const handle = {
|
||||
"en-US": p.handle,
|
||||
}
|
||||
productEntryFields.handle = handle
|
||||
|
||||
productEntryFields[this.getCustomField("handle", "product")] = handle
|
||||
}
|
||||
|
||||
productEntry.fields = productEntryFields
|
||||
@@ -333,19 +443,19 @@ class ContentfulService extends BaseService {
|
||||
|
||||
const variantEntryFields = {
|
||||
...variantEntry.fields,
|
||||
title: {
|
||||
[this.getCustomField("title", "variant")]: {
|
||||
"en-US": v.title,
|
||||
},
|
||||
sku: {
|
||||
[this.getCustomField("sku", "variant")]: {
|
||||
"en-US": v.sku,
|
||||
},
|
||||
options: {
|
||||
[this.getCustomField("options", "variant")]: {
|
||||
"en-US": v.options,
|
||||
},
|
||||
prices: {
|
||||
[this.getCustomField("prices", "variant")]: {
|
||||
"en-US": v.prices,
|
||||
},
|
||||
objectId: {
|
||||
[this.getCustomField("medusaId", "variant")]: {
|
||||
"en-US": v.id,
|
||||
},
|
||||
}
|
||||
@@ -377,7 +487,8 @@ class ContentfulService extends BaseService {
|
||||
}
|
||||
|
||||
let update = {
|
||||
title: productEntry.fields.title["en-US"],
|
||||
title:
|
||||
productEntry.fields[this.getCustomField("title", "product")]["en-US"],
|
||||
}
|
||||
|
||||
// Get the thumbnail, if present
|
||||
@@ -421,7 +532,10 @@ class ContentfulService extends BaseService {
|
||||
const updatedVariant = await this.productVariantService_.update(
|
||||
variantId,
|
||||
{
|
||||
title: variantEntry.fields.title["en-US"],
|
||||
title:
|
||||
variantEntry.fields[this.getCustomField("title", "variant")][
|
||||
"en-US"
|
||||
],
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -3,6 +3,59 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.1.5](https://github.com/medusajs/medusa/compare/medusa-plugin-segment@1.1.5-next.3...medusa-plugin-segment@1.1.5) (2021-02-25)
|
||||
|
||||
**Note:** Version bump only for package medusa-plugin-segment
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.1.5-next.3](https://github.com/medusajs/medusa/compare/medusa-plugin-segment@1.1.5-next.2...medusa-plugin-segment@1.1.5-next.3) (2021-02-25)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* normalize currency code ([98aa404](https://github.com/medusajs/medusa/commit/98aa404306d55f0818d48e56c51146351ebfe306))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.1.5-next.2](https://github.com/medusajs/medusa/compare/medusa-plugin-segment@1.1.5-next.1...medusa-plugin-segment@1.1.5-next.2) (2021-02-25)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **segment:** track shipments ([d156911](https://github.com/medusajs/medusa/commit/d15691188348c19fc22806d8cf7584fc5f249ce9))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.1.5-next.1](https://github.com/medusajs/medusa/compare/medusa-plugin-segment@1.1.5-next.0...medusa-plugin-segment@1.1.5-next.1) (2021-02-25)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add subtitle to tracks ([0c294b7](https://github.com/medusajs/medusa/commit/0c294b7b3acbc1b873aab7e90a8e596bdac48899))
|
||||
* versioning ([262af34](https://github.com/medusajs/medusa/commit/262af34125543d9a80bf469b5d380019b9bc8d3f))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.1.5-next.0](https://github.com/medusajs/medusa/compare/medusa-plugin-segment@1.1.4...medusa-plugin-segment@1.1.5-next.0) (2021-02-22)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **medusa-plugin-segment:** adds category and type to segment events ([#179](https://github.com/medusajs/medusa/issues/179)) ([e27cf72](https://github.com/medusajs/medusa/commit/e27cf72a8ca49a6586a82dde964d559c40a4415f))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.1.4](https://github.com/medusajs/medusa/compare/medusa-plugin-segment@1.1.3...medusa-plugin-segment@1.1.4) (2021-02-17)
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "medusa-plugin-segment",
|
||||
"version": "1.1.4",
|
||||
"version": "1.1.5",
|
||||
"description": "Segment Analytics",
|
||||
"main": "index.js",
|
||||
"repository": {
|
||||
@@ -42,5 +42,5 @@
|
||||
"medusa-core-utils": "^1.1.0",
|
||||
"medusa-test-utils": "^1.1.3"
|
||||
},
|
||||
"gitHead": "0646bd395a6056657cb0aa93c13699c4a9dbbcdd"
|
||||
"gitHead": "0c294b7b3acbc1b873aab7e90a8e596bdac48899"
|
||||
}
|
||||
|
||||
@@ -10,11 +10,12 @@ class SegmentService extends BaseService {
|
||||
* write_key: Segment write key given in Segment dashboard
|
||||
* }
|
||||
*/
|
||||
constructor({ totalsService }, options) {
|
||||
constructor({ totalsService, productService }, options) {
|
||||
super()
|
||||
|
||||
this.totalsService_ = totalsService
|
||||
this.options_ = options
|
||||
this.productService_ = productService
|
||||
|
||||
this.analytics_ = new Analytics(options.write_key)
|
||||
}
|
||||
@@ -102,7 +103,7 @@ class SegmentService extends BaseService {
|
||||
tax,
|
||||
discount,
|
||||
coupon,
|
||||
currency: order.currency_code,
|
||||
currency: order.currency_code.toUpperCase(),
|
||||
products: await Promise.all(
|
||||
order.items.map(async (item) => {
|
||||
let name = item.title
|
||||
@@ -129,12 +130,20 @@ class SegmentService extends BaseService {
|
||||
variant = item.variant.sku
|
||||
}
|
||||
|
||||
const product = await this.productService_.retrieve(
|
||||
item.variant.product_id,
|
||||
{ relations: ["collection", "type"] }
|
||||
)
|
||||
|
||||
return {
|
||||
name,
|
||||
variant,
|
||||
price: lineTotal / 100 / item.quantity,
|
||||
reporting_revenue: revenue,
|
||||
product_id: item.variant.product_id,
|
||||
category: product.collection?.title,
|
||||
subtitle: product.subtitle,
|
||||
type: product.type?.value,
|
||||
sku,
|
||||
quantity: item.quantity,
|
||||
}
|
||||
|
||||
@@ -5,12 +5,76 @@ class OrderSubscriber {
|
||||
orderService,
|
||||
claimService,
|
||||
returnService,
|
||||
fulfillmentService,
|
||||
}) {
|
||||
this.orderService_ = orderService
|
||||
|
||||
this.returnService_ = returnService
|
||||
|
||||
this.claimService_ = claimService
|
||||
this.fulfillmentService_ = fulfillmentService
|
||||
|
||||
eventBusService.subscribe(
|
||||
"order.shipment_created",
|
||||
async ({ id, fulfillment_id }) => {
|
||||
const order = await this.orderService_.retrieve(id, {
|
||||
select: [
|
||||
"shipping_total",
|
||||
"discount_total",
|
||||
"tax_total",
|
||||
"refunded_total",
|
||||
"gift_card_total",
|
||||
"subtotal",
|
||||
"total",
|
||||
],
|
||||
relations: [
|
||||
"customer",
|
||||
"billing_address",
|
||||
"shipping_address",
|
||||
"discounts",
|
||||
"shipping_methods",
|
||||
"payments",
|
||||
"fulfillments",
|
||||
"returns",
|
||||
"items",
|
||||
"gift_cards",
|
||||
"gift_card_transactions",
|
||||
"swaps",
|
||||
"swaps.return_order",
|
||||
"swaps.payment",
|
||||
"swaps.shipping_methods",
|
||||
"swaps.shipping_address",
|
||||
"swaps.additional_items",
|
||||
"swaps.fulfillments",
|
||||
],
|
||||
})
|
||||
|
||||
const fulfillment = await this.fulfillmentService_.retrieve(
|
||||
fulfillment_id,
|
||||
{
|
||||
relations: ["items"],
|
||||
}
|
||||
)
|
||||
|
||||
const toBuildFrom = {
|
||||
...order,
|
||||
provider_id: fulfillment.provider,
|
||||
items: fulfillment.items.map((i) =>
|
||||
order.items.find((l) => l.id === i.item_id)
|
||||
),
|
||||
}
|
||||
|
||||
const orderData = await segmentService.buildOrder(toBuildFrom)
|
||||
const orderEvent = {
|
||||
event: "Order Shipped",
|
||||
userId: order.customer_id,
|
||||
properties: orderData,
|
||||
timestamp: fulfillment.shipped_at,
|
||||
}
|
||||
|
||||
segmentService.track(orderEvent)
|
||||
}
|
||||
)
|
||||
|
||||
eventBusService.subscribe("claim.created", async ({ id }) => {
|
||||
const claim = await this.claimService_.retrieve(id, {
|
||||
@@ -74,6 +138,7 @@ class OrderSubscriber {
|
||||
"payments",
|
||||
"fulfillments",
|
||||
"returns",
|
||||
"items",
|
||||
"gift_cards",
|
||||
"gift_card_transactions",
|
||||
"swaps",
|
||||
@@ -160,6 +225,7 @@ class OrderSubscriber {
|
||||
"shipping_methods",
|
||||
"payments",
|
||||
"fulfillments",
|
||||
"items",
|
||||
"returns",
|
||||
"gift_cards",
|
||||
"gift_card_transactions",
|
||||
|
||||
@@ -3,6 +3,18 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.1.4](https://github.com/medusajs/medusa/compare/medusa-plugin-sendgrid@1.1.3...medusa-plugin-sendgrid@1.1.4) (2021-02-25)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add tracking links to shipments ([7be4bb5](https://github.com/medusajs/medusa/commit/7be4bb5f2daa0aad805abe0f97278f53cf3af402))
|
||||
* sendgrid tracking links ([5cfc8d8](https://github.com/medusajs/medusa/commit/5cfc8d80bd3eaee93595027d0cc3ce67ae98d275))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.1.3](https://github.com/medusajs/medusa/compare/medusa-plugin-sendgrid@1.1.2...medusa-plugin-sendgrid@1.1.3) (2021-02-17)
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "medusa-plugin-sendgrid",
|
||||
"version": "1.1.3",
|
||||
"version": "1.1.4",
|
||||
"description": "SendGrid transactional emails",
|
||||
"main": "index.js",
|
||||
"repository": {
|
||||
|
||||
@@ -275,7 +275,7 @@ class SendGridService extends NotificationService {
|
||||
})
|
||||
|
||||
const shipment = await this.fulfillmentService_.retrieve(fulfillment_id, {
|
||||
relations: ["items"],
|
||||
relations: ["items", "tracking_links"],
|
||||
})
|
||||
|
||||
return {
|
||||
@@ -283,6 +283,7 @@ class SendGridService extends NotificationService {
|
||||
date: shipment.shipped_at.toDateString(),
|
||||
email: order.email,
|
||||
fulfillment: shipment,
|
||||
tracking_links: shipment.tracking_links,
|
||||
tracking_number: shipment.tracking_numbers.join(", "),
|
||||
}
|
||||
}
|
||||
@@ -586,7 +587,9 @@ class SendGridService extends NotificationService {
|
||||
|
||||
const refundAmount = swap.return_order.refund_amount
|
||||
|
||||
const shipment = await this.fulfillmentService_.retrieve(fulfillment_id)
|
||||
const shipment = await this.fulfillmentService_.retrieve(fulfillment_id, {
|
||||
relations: ["tracking_links"],
|
||||
})
|
||||
|
||||
return {
|
||||
swap,
|
||||
@@ -602,6 +605,7 @@ class SendGridService extends NotificationService {
|
||||
refund_amount: `${this.humanPrice_(refundAmount)} ${currencyCode}`,
|
||||
additional_total: `${this.humanPrice_(additionalTotal)} ${currencyCode}`,
|
||||
fulfillment: shipment,
|
||||
tracking_links: shipment.tracking_links,
|
||||
tracking_number: shipment.tracking_numbers.join(", "),
|
||||
}
|
||||
}
|
||||
@@ -611,13 +615,16 @@ class SendGridService extends NotificationService {
|
||||
relations: ["order", "order.items", "order.shipping_address"],
|
||||
})
|
||||
|
||||
const shipment = await this.fulfillmentService_.retrieve(fulfillment_id)
|
||||
const shipment = await this.fulfillmentService_.retrieve(fulfillment_id, {
|
||||
relations: ["tracking_links"],
|
||||
})
|
||||
|
||||
return {
|
||||
email: claim.order.email,
|
||||
claim,
|
||||
order: claim.order,
|
||||
fulfillment: shipment,
|
||||
tracking_links: shipment.tracking_links,
|
||||
tracking_number: shipment.tracking_numbers.join(", "),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,47 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.1.11](https://github.com/medusajs/medusa/compare/@medusajs/medusa@1.1.10...@medusajs/medusa@1.1.11) (2021-02-25)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **medusa:** Add querying func. on customer retrievals ([#181](https://github.com/medusajs/medusa/issues/181)) ([22be418](https://github.com/medusajs/medusa/commit/22be418ec132944afe469106ba4b3b92f634d240))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.1.10](https://github.com/medusajs/medusa/compare/@medusajs/medusa@1.1.10-next.1...@medusajs/medusa@1.1.10) (2021-02-25)
|
||||
|
||||
**Note:** Version bump only for package @medusajs/medusa
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.1.10-next.1](https://github.com/medusajs/medusa/compare/@medusajs/medusa@1.1.10-next.0...@medusajs/medusa@1.1.10-next.1) (2021-02-25)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* update-product ([0320788](https://github.com/medusajs/medusa/commit/0320788aacf93da8a8951c6a540656da1772dba4))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.1.10-next.0](https://github.com/medusajs/medusa/compare/@medusajs/medusa@1.1.9...@medusajs/medusa@1.1.10-next.0) (2021-02-22)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **medusa:** tracking links ([#177](https://github.com/medusajs/medusa/issues/177)) ([99ad43b](https://github.com/medusajs/medusa/commit/99ad43bf47c3922f391d433448b1c4affd88f457))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.1.9](https://github.com/medusajs/medusa/compare/@medusajs/medusa@1.1.8...@medusajs/medusa@1.1.9) (2021-02-18)
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@medusajs/medusa",
|
||||
"version": "1.1.9",
|
||||
"version": "1.1.11",
|
||||
"description": "E-commerce for JAMstack",
|
||||
"main": "dist/index.js",
|
||||
"repository": {
|
||||
|
||||
@@ -2,18 +2,32 @@ export default async (req, res) => {
|
||||
try {
|
||||
const customerService = req.scope.resolve("customerService")
|
||||
|
||||
const limit = parseInt(req.query.limit) || 10
|
||||
const limit = parseInt(req.query.limit) || 50
|
||||
const offset = parseInt(req.query.offset) || 0
|
||||
|
||||
const selector = {}
|
||||
|
||||
if ("q" in req.query) {
|
||||
selector.q = req.query.q
|
||||
}
|
||||
|
||||
let expandFields = []
|
||||
if ("expand" in req.query) {
|
||||
expandFields = req.query.expand.split(",")
|
||||
}
|
||||
|
||||
const listConfig = {
|
||||
relations: [],
|
||||
relations: expandFields.length ? expandFields : [],
|
||||
skip: offset,
|
||||
take: limit,
|
||||
}
|
||||
|
||||
const customers = await customerService.list({}, listConfig)
|
||||
const [customers, count] = await customerService.listAndCount(
|
||||
selector,
|
||||
listConfig
|
||||
)
|
||||
|
||||
res.json({ customers, count: customers.length, offset, limit })
|
||||
res.json({ customers, count, offset, limit })
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
|
||||
@@ -10,6 +10,8 @@ const defaultRelations = [
|
||||
"shipping_methods",
|
||||
"payments",
|
||||
"fulfillments",
|
||||
"fulfillments.tracking_links",
|
||||
"fulfillments.items",
|
||||
"returns",
|
||||
"gift_cards",
|
||||
"gift_card_transactions",
|
||||
|
||||
@@ -23,7 +23,7 @@ export default async (req, res) => {
|
||||
await claimService.createShipment(
|
||||
claim_id,
|
||||
value.fulfillment_id,
|
||||
value.tracking_numbers
|
||||
value.tracking_numbers.map(n => ({ tracking_number: n }))
|
||||
)
|
||||
|
||||
const order = await orderService.retrieve(id, {
|
||||
|
||||
@@ -22,7 +22,7 @@ export default async (req, res) => {
|
||||
await orderService.createShipment(
|
||||
id,
|
||||
value.fulfillment_id,
|
||||
value.tracking_numbers
|
||||
value.tracking_numbers.map(n => ({ tracking_number: n }))
|
||||
)
|
||||
|
||||
const order = await orderService.retrieve(id, {
|
||||
|
||||
@@ -23,7 +23,7 @@ export default async (req, res) => {
|
||||
await swapService.createShipment(
|
||||
swap_id,
|
||||
value.fulfillment_id,
|
||||
value.tracking_numbers
|
||||
value.tracking_numbers.map(n => ({ tracking_number: n }))
|
||||
)
|
||||
|
||||
const order = await orderService.retrieve(id, {
|
||||
|
||||
@@ -188,6 +188,8 @@ export const defaultRelations = [
|
||||
"shipping_methods",
|
||||
"payments",
|
||||
"fulfillments",
|
||||
"fulfillments.tracking_links",
|
||||
"fulfillments.items",
|
||||
"returns",
|
||||
"gift_cards",
|
||||
"gift_card_transactions",
|
||||
@@ -271,6 +273,7 @@ export const allowedRelations = [
|
||||
"shipping_methods",
|
||||
"payments",
|
||||
"fulfillments",
|
||||
"fulfillments.tracking_links",
|
||||
"returns",
|
||||
"claims",
|
||||
"swaps",
|
||||
|
||||
@@ -14,13 +14,23 @@ export default async (req, res) => {
|
||||
selector.q = req.query.q
|
||||
}
|
||||
|
||||
let includeFields = []
|
||||
if ("fields" in req.query) {
|
||||
includeFields = req.query.fields.split(",")
|
||||
}
|
||||
|
||||
let expandFields = []
|
||||
if ("expand" in req.query) {
|
||||
expandFields = req.query.expand.split(",")
|
||||
}
|
||||
|
||||
if ("is_giftcard" in req.query) {
|
||||
selector.is_giftcard = req.query.is_giftcard === "true"
|
||||
}
|
||||
|
||||
const listConfig = {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
select: includeFields.length ? includeFields : defaultFields,
|
||||
relations: expandFields.length ? expandFields : defaultRelations,
|
||||
skip: offset,
|
||||
take: limit,
|
||||
}
|
||||
|
||||
@@ -6,6 +6,9 @@ export default async (req, res) => {
|
||||
|
||||
const schema = Validator.object().keys({
|
||||
title: Validator.string().optional(),
|
||||
subtitle: Validator.string()
|
||||
.optional()
|
||||
.allow(null, ""),
|
||||
description: Validator.string().optional(),
|
||||
type: Validator.object()
|
||||
.keys({
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
import {MigrationInterface, QueryRunner} from "typeorm";
|
||||
|
||||
export class trackingLinks1613656135167 implements MigrationInterface {
|
||||
name = 'trackingLinks1613656135167'
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`CREATE TABLE "tracking_link" ("id" character varying NOT NULL, "url" character varying, "tracking_number" character varying NOT NULL, "fulfillment_id" character varying NOT NULL, "created_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updated_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "deleted_at" TIMESTAMP WITH TIME ZONE, "metadata" jsonb, "idempotency_key" character varying, CONSTRAINT "PK_fcfd77feb9012ec2126d7c0bfb6" PRIMARY KEY ("id"))`);
|
||||
await queryRunner.query(`ALTER TABLE "tracking_link" ADD CONSTRAINT "FK_471e9e4c96e02ba209a307db32b" FOREIGN KEY ("fulfillment_id") REFERENCES "fulfillment"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE "tracking_link" DROP CONSTRAINT "FK_471e9e4c96e02ba209a307db32b"`);
|
||||
await queryRunner.query(`DROP TABLE "tracking_link"`);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -22,6 +22,7 @@ import { FulfillmentProvider } from "./fulfillment-provider"
|
||||
import { FulfillmentItem } from "./fulfillment-item"
|
||||
import { Swap } from "./swap"
|
||||
import { ClaimOrder } from "./claim-order"
|
||||
import { TrackingLink } from "./tracking-link"
|
||||
|
||||
@Entity()
|
||||
export class Fulfillment {
|
||||
@@ -76,6 +77,13 @@ export class Fulfillment {
|
||||
)
|
||||
items: FulfillmentItem[]
|
||||
|
||||
@OneToMany(
|
||||
() => TrackingLink,
|
||||
tl => tl.fulfillment,
|
||||
{ cascade: ["insert"] }
|
||||
)
|
||||
tracking_links: TrackingLink[]
|
||||
|
||||
@Column({ type: "jsonb", default: [] })
|
||||
tracking_numbers: string[]
|
||||
|
||||
|
||||
63
packages/medusa/src/models/tracking-link.ts
Normal file
63
packages/medusa/src/models/tracking-link.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
import {
|
||||
Entity,
|
||||
Index,
|
||||
BeforeInsert,
|
||||
Column,
|
||||
DeleteDateColumn,
|
||||
CreateDateColumn,
|
||||
UpdateDateColumn,
|
||||
PrimaryColumn,
|
||||
OneToOne,
|
||||
OneToMany,
|
||||
ManyToOne,
|
||||
ManyToMany,
|
||||
JoinColumn,
|
||||
JoinTable,
|
||||
} from "typeorm"
|
||||
import { ulid } from "ulid"
|
||||
|
||||
import { Fulfillment } from "./fulfillment"
|
||||
|
||||
@Entity()
|
||||
export class TrackingLink {
|
||||
@PrimaryColumn()
|
||||
id: string
|
||||
|
||||
@Column({ nullable: true })
|
||||
url: string
|
||||
|
||||
@Column()
|
||||
tracking_number: string
|
||||
|
||||
@Column()
|
||||
fulfillment_id: string
|
||||
|
||||
@ManyToOne(
|
||||
() => Fulfillment,
|
||||
ful => ful.tracking_links
|
||||
)
|
||||
@JoinColumn({ name: "fulfillment_id" })
|
||||
fulfillment: Fulfillment
|
||||
|
||||
@CreateDateColumn({ type: "timestamptz" })
|
||||
created_at: Date
|
||||
|
||||
@UpdateDateColumn({ type: "timestamptz" })
|
||||
updated_at: Date
|
||||
|
||||
@DeleteDateColumn({ type: "timestamptz" })
|
||||
deleted_at: Date
|
||||
|
||||
@Column({ type: "jsonb", nullable: true })
|
||||
metadata: any
|
||||
|
||||
@Column({ nullable: true })
|
||||
idempotency_key: string
|
||||
|
||||
@BeforeInsert()
|
||||
private beforeInsert() {
|
||||
if (this.id) return
|
||||
const id = ulid()
|
||||
this.id = `tlink_${id}`
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,53 @@
|
||||
import { EntityRepository, Repository } from "typeorm"
|
||||
import { flatten, groupBy, map, merge } from "lodash"
|
||||
import { EntityRepository, FindManyOptions, Repository } from "typeorm"
|
||||
import { Product } from "../models/product"
|
||||
|
||||
@EntityRepository(Product)
|
||||
export class ProductRepository extends Repository<Product> {}
|
||||
export class ProductRepository extends Repository<Product> {
|
||||
public async findWithRelations(
|
||||
relations: Array<keyof Product> = [],
|
||||
optionsWithoutRelations: Omit<FindManyOptions<Product>, "relations"> = {}
|
||||
): Promise<Product[]> {
|
||||
const entities = await this.find(optionsWithoutRelations)
|
||||
const entitiesIds = entities.map(({ id }) => id)
|
||||
|
||||
const groupedRelations = {}
|
||||
for (const rel of relations) {
|
||||
const [topLevel] = rel.split(".")
|
||||
if (groupedRelations[topLevel]) {
|
||||
groupedRelations[topLevel].push(rel)
|
||||
} else {
|
||||
groupedRelations[topLevel] = [rel]
|
||||
}
|
||||
}
|
||||
|
||||
const entitiesIdsWithRelations = await Promise.all(
|
||||
Object.entries(groupedRelations).map(([_, rels]) => {
|
||||
return this.findByIds(entitiesIds, {
|
||||
select: ["id"],
|
||||
relations: rels as string[],
|
||||
})
|
||||
})
|
||||
).then(flatten)
|
||||
const entitiesAndRelations = entitiesIdsWithRelations.concat(entities)
|
||||
|
||||
const entitiesAndRelationsById = groupBy(entitiesAndRelations, "id")
|
||||
return map(entitiesAndRelationsById, entityAndRelations =>
|
||||
merge({}, ...entityAndRelations)
|
||||
)
|
||||
}
|
||||
|
||||
public async findOneWithRelations(
|
||||
relations: Array<keyof Product> = [],
|
||||
optionsWithoutRelations: Omit<FindManyOptions<Product>, "relations"> = {}
|
||||
): Promise<Product> {
|
||||
// Limit 1
|
||||
optionsWithoutRelations.take = 1
|
||||
|
||||
const result = await this.findWithRelations(
|
||||
relations,
|
||||
optionsWithoutRelations
|
||||
)
|
||||
return result[0]
|
||||
}
|
||||
}
|
||||
|
||||
5
packages/medusa/src/repositories/tracking-link.ts
Normal file
5
packages/medusa/src/repositories/tracking-link.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import { EntityRepository, Repository } from "typeorm"
|
||||
import { TrackingLink } from "../models/tracking-link"
|
||||
|
||||
@EntityRepository(TrackingLink)
|
||||
export class TrackingLinkRepository extends Repository<TrackingLink> {}
|
||||
@@ -95,6 +95,7 @@ describe("FulfillmentService", () => {
|
||||
})
|
||||
|
||||
describe("createShipment", () => {
|
||||
const trackingLinkRepository = MockRepository({ create: c => c })
|
||||
const fulfillmentRepository = MockRepository({
|
||||
findOne: () => Promise.resolve({ id: IdMap.getId("fulfillment") }),
|
||||
})
|
||||
@@ -102,6 +103,7 @@ describe("FulfillmentService", () => {
|
||||
const fulfillmentService = new FulfillmentService({
|
||||
manager: MockManager,
|
||||
fulfillmentRepository,
|
||||
trackingLinkRepository,
|
||||
})
|
||||
|
||||
const now = new Date()
|
||||
@@ -113,14 +115,17 @@ describe("FulfillmentService", () => {
|
||||
it("calls order model functions", async () => {
|
||||
await fulfillmentService.createShipment(
|
||||
IdMap.getId("fulfillment"),
|
||||
["1234", "2345"],
|
||||
[{ tracking_number: "1234" }, { tracking_number: "2345" }],
|
||||
{}
|
||||
)
|
||||
|
||||
expect(fulfillmentRepository.save).toHaveBeenCalledTimes(1)
|
||||
expect(fulfillmentRepository.save).toHaveBeenCalledWith({
|
||||
id: IdMap.getId("fulfillment"),
|
||||
tracking_numbers: ["1234", "2345"],
|
||||
tracking_links: [
|
||||
{ tracking_number: "1234" },
|
||||
{ tracking_number: "2345" },
|
||||
],
|
||||
metadata: {},
|
||||
shipped_at: now,
|
||||
})
|
||||
|
||||
@@ -1182,14 +1182,14 @@ describe("OrderService", () => {
|
||||
await orderService.createShipment(
|
||||
IdMap.getId("test"),
|
||||
IdMap.getId("fulfillment"),
|
||||
["1234", "2345"],
|
||||
[{ tracking_number: "1234" }, { tracking_number: "2345" }],
|
||||
{}
|
||||
)
|
||||
|
||||
expect(fulfillmentService.createShipment).toHaveBeenCalledTimes(1)
|
||||
expect(fulfillmentService.createShipment).toHaveBeenCalledWith(
|
||||
IdMap.getId("fulfillment"),
|
||||
["1234", "2345"],
|
||||
[{ tracking_number: "1234" }, { tracking_number: "2345" }],
|
||||
{}
|
||||
)
|
||||
|
||||
|
||||
@@ -11,7 +11,8 @@ const eventBusService = {
|
||||
describe("ProductService", () => {
|
||||
describe("retrieve", () => {
|
||||
const productRepo = MockRepository({
|
||||
findOne: () => Promise.resolve({ id: IdMap.getId("ironman") }),
|
||||
findOneWithRelations: () =>
|
||||
Promise.resolve({ id: IdMap.getId("ironman") }),
|
||||
})
|
||||
const productService = new ProductService({
|
||||
manager: MockManager,
|
||||
@@ -25,8 +26,8 @@ describe("ProductService", () => {
|
||||
it("successfully retrieves a product", async () => {
|
||||
const result = await productService.retrieve(IdMap.getId("ironman"))
|
||||
|
||||
expect(productRepo.findOne).toHaveBeenCalledTimes(1)
|
||||
expect(productRepo.findOne).toHaveBeenCalledWith({
|
||||
expect(productRepo.findOneWithRelations).toHaveBeenCalledTimes(1)
|
||||
expect(productRepo.findOneWithRelations).toHaveBeenCalledWith(undefined, {
|
||||
where: { id: IdMap.getId("ironman") },
|
||||
})
|
||||
|
||||
@@ -42,7 +43,7 @@ describe("ProductService", () => {
|
||||
options: [],
|
||||
collection: { id: IdMap.getId("cat"), title: "Suits" },
|
||||
}),
|
||||
findOne: () => ({
|
||||
findOneWithRelations: () => ({
|
||||
id: IdMap.getId("ironman"),
|
||||
title: "Suit",
|
||||
options: [],
|
||||
@@ -137,7 +138,7 @@ describe("ProductService", () => {
|
||||
|
||||
describe("update", () => {
|
||||
const productRepository = MockRepository({
|
||||
findOne: query => {
|
||||
findOneWithRelations: (rels, query) => {
|
||||
if (query.where.id === IdMap.getId("ironman&co")) {
|
||||
return Promise.resolve({
|
||||
id: IdMap.getId("ironman&co"),
|
||||
@@ -322,7 +323,7 @@ describe("ProductService", () => {
|
||||
|
||||
describe("addOption", () => {
|
||||
const productRepository = MockRepository({
|
||||
findOne: query =>
|
||||
findOneWithRelations: query =>
|
||||
Promise.resolve({
|
||||
id: IdMap.getId("ironman"),
|
||||
options: [{ title: "Color" }],
|
||||
@@ -395,7 +396,7 @@ describe("ProductService", () => {
|
||||
|
||||
describe("reorderVariants", () => {
|
||||
const productRepository = MockRepository({
|
||||
findOne: query =>
|
||||
findOneWithRelations: query =>
|
||||
Promise.resolve({
|
||||
id: IdMap.getId("ironman"),
|
||||
variants: [{ id: IdMap.getId("green") }, { id: IdMap.getId("blue") }],
|
||||
@@ -453,7 +454,7 @@ describe("ProductService", () => {
|
||||
|
||||
describe("reorderOptions", () => {
|
||||
const productRepository = MockRepository({
|
||||
findOne: query =>
|
||||
findOneWithRelations: query =>
|
||||
Promise.resolve({
|
||||
id: IdMap.getId("ironman"),
|
||||
options: [
|
||||
@@ -519,7 +520,7 @@ describe("ProductService", () => {
|
||||
|
||||
describe("updateOption", () => {
|
||||
const productRepository = MockRepository({
|
||||
findOne: query =>
|
||||
findOneWithRelations: query =>
|
||||
Promise.resolve({
|
||||
id: IdMap.getId("ironman"),
|
||||
options: [
|
||||
@@ -594,7 +595,7 @@ describe("ProductService", () => {
|
||||
|
||||
describe("deleteOption", () => {
|
||||
const productRepository = MockRepository({
|
||||
findOne: query =>
|
||||
findOneWithRelations: query =>
|
||||
Promise.resolve({
|
||||
id: IdMap.getId("ironman"),
|
||||
variants: [
|
||||
|
||||
@@ -418,7 +418,7 @@ class ClaimService extends BaseService {
|
||||
})
|
||||
}
|
||||
|
||||
async createShipment(id, fulfillmentId, trackingNumbers, metadata = []) {
|
||||
async createShipment(id, fulfillmentId, trackingLinks, metadata = []) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
const claim = await this.retrieve(id, {
|
||||
relations: ["additional_items"],
|
||||
@@ -426,7 +426,7 @@ class ClaimService extends BaseService {
|
||||
|
||||
const shipment = await this.fulfillmentService_
|
||||
.withTransaction(manager)
|
||||
.createShipment(fulfillmentId, trackingNumbers, metadata)
|
||||
.createShipment(fulfillmentId, trackingLinks, metadata)
|
||||
|
||||
claim.fulfillment_status = "shipped"
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import Scrypt from "scrypt-kdf"
|
||||
import _ from "lodash"
|
||||
import { Validator, MedusaError } from "medusa-core-utils"
|
||||
import { BaseService } from "medusa-interfaces"
|
||||
import { Brackets } from "typeorm"
|
||||
|
||||
/**
|
||||
* Provides layer to manipulate customers.
|
||||
@@ -132,6 +133,50 @@ class CustomerService extends BaseService {
|
||||
return customerRepo.find(query)
|
||||
}
|
||||
|
||||
async listAndCount(
|
||||
selector,
|
||||
config = { relations: [], skip: 0, take: 50, order: { created_at: "DESC" } }
|
||||
) {
|
||||
const customerRepo = this.manager_.getCustomRepository(
|
||||
this.customerRepository_
|
||||
)
|
||||
|
||||
let q
|
||||
if ("q" in selector) {
|
||||
q = selector.q
|
||||
delete selector.q
|
||||
}
|
||||
|
||||
const query = this.buildQuery_(selector, config)
|
||||
|
||||
if (q) {
|
||||
const where = query.where
|
||||
|
||||
delete where.email
|
||||
delete where.first_name
|
||||
delete where.last_name
|
||||
|
||||
query.join = {
|
||||
alias: "customer",
|
||||
}
|
||||
|
||||
query.where = qb => {
|
||||
qb.where(where)
|
||||
|
||||
qb.andWhere(
|
||||
new Brackets(qb => {
|
||||
qb.where(`customer.first_name ILIKE :q`, { q: `%${q}%` })
|
||||
.orWhere(`customer.last_name ILIKE :q`, { q: `%${q}%` })
|
||||
.orWhere(`customer.email ILIKE :q`, { q: `%${q}%` })
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const [customers, count] = await customerRepo.findAndCount(query)
|
||||
return [customers, count]
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the total number of documents in database
|
||||
* @return {Promise} the result of the count operation
|
||||
|
||||
@@ -11,6 +11,7 @@ class FulfillmentService extends BaseService {
|
||||
manager,
|
||||
totalsService,
|
||||
fulfillmentRepository,
|
||||
trackingLinkRepository,
|
||||
shippingProfileService,
|
||||
lineItemService,
|
||||
fulfillmentProviderService,
|
||||
@@ -26,6 +27,9 @@ class FulfillmentService extends BaseService {
|
||||
/** @private @const {FulfillmentRepository} */
|
||||
this.fulfillmentRepository_ = fulfillmentRepository
|
||||
|
||||
/** @private @const {TrackingLinkRepository} */
|
||||
this.trackingLinkRepository_ = trackingLinkRepository
|
||||
|
||||
/** @private @const {ShippingProfileService} */
|
||||
this.shippingProfileService_ = shippingProfileService
|
||||
|
||||
@@ -44,6 +48,7 @@ class FulfillmentService extends BaseService {
|
||||
const cloned = new FulfillmentService({
|
||||
manager: transactionManager,
|
||||
totalsService: this.totalsService_,
|
||||
trackingLinkRepository: this.trackingLinkRepository_,
|
||||
fulfillmentRepository: this.fulfillmentRepository_,
|
||||
shippingProfileService: this.shippingProfileService_,
|
||||
lineItemService: this.lineItemService_,
|
||||
@@ -235,15 +240,18 @@ class FulfillmentService extends BaseService {
|
||||
* Creates a shipment by marking a fulfillment as shipped. Adds
|
||||
* tracking numbers and potentially more metadata.
|
||||
* @param {Order} fulfillmentId - the fulfillment to ship
|
||||
* @param {string[]} trackingNumbers - tracking numbers for the shipment
|
||||
* @param {TrackingLink[]} trackingNumbers - tracking numbers for the shipment
|
||||
* @param {object} metadata - potential metadata to add
|
||||
* @return {Fulfillment} the shipped fulfillment
|
||||
*/
|
||||
async createShipment(fulfillmentId, trackingNumbers, metadata) {
|
||||
async createShipment(fulfillmentId, trackingLinks, metadata) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
const fulfillmentRepository = manager.getCustomRepository(
|
||||
this.fulfillmentRepository_
|
||||
)
|
||||
const trackingLinkRepo = manager.getCustomRepository(
|
||||
this.trackingLinkRepository_
|
||||
)
|
||||
|
||||
const fulfillment = await this.retrieve(fulfillmentId, {
|
||||
relations: ["items"],
|
||||
@@ -251,7 +259,11 @@ class FulfillmentService extends BaseService {
|
||||
|
||||
const now = new Date()
|
||||
fulfillment.shipped_at = now
|
||||
fulfillment.tracking_numbers = trackingNumbers
|
||||
|
||||
fulfillment.tracking_links = trackingLinks.map(tl =>
|
||||
trackingLinkRepo.create(tl)
|
||||
)
|
||||
|
||||
fulfillment.metadata = {
|
||||
...fulfillment.metadata,
|
||||
...metadata,
|
||||
|
||||
@@ -553,13 +553,13 @@ class OrderService extends BaseService {
|
||||
* have been created in regards to the shipment.
|
||||
* @param {string} orderId - the id of the order that has been shipped
|
||||
* @param {string} fulfillmentId - the fulfillment that has now been shipped
|
||||
* @param {Array<String>} trackingNumbers - array of tracking numebers
|
||||
* @param {TrackingLink[]} trackingLinks - array of tracking numebers
|
||||
* associated with the shipment
|
||||
* @param {Dictionary<String, String>} metadata - optional metadata to add to
|
||||
* the fulfillment
|
||||
* @return {order} the resulting order following the update.
|
||||
*/
|
||||
async createShipment(orderId, fulfillmentId, trackingNumbers, metadata = {}) {
|
||||
async createShipment(orderId, fulfillmentId, trackingLinks, metadata = {}) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
const order = await this.retrieve(orderId, { relations: ["items"] })
|
||||
const shipment = await this.fulfillmentService_.retrieve(fulfillmentId)
|
||||
@@ -573,7 +573,7 @@ class OrderService extends BaseService {
|
||||
|
||||
const shipmentRes = await this.fulfillmentService_
|
||||
.withTransaction(manager)
|
||||
.createShipment(fulfillmentId, trackingNumbers, metadata)
|
||||
.createShipment(fulfillmentId, trackingLinks, metadata)
|
||||
|
||||
order.fulfillment_status = "shipped"
|
||||
for (const item of order.items) {
|
||||
|
||||
@@ -93,6 +93,17 @@ class ProductService extends BaseService {
|
||||
|
||||
const query = this.buildQuery_(selector, config)
|
||||
|
||||
if (config.relations && config.relations.length > 0) {
|
||||
query.relations = config.relations
|
||||
}
|
||||
|
||||
if (config.select && config.select.length > 0) {
|
||||
query.select = config.select
|
||||
}
|
||||
|
||||
const rels = query.relations
|
||||
delete query.relations
|
||||
|
||||
if (q) {
|
||||
const where = query.where
|
||||
|
||||
@@ -122,7 +133,7 @@ class ProductService extends BaseService {
|
||||
}
|
||||
}
|
||||
|
||||
return productRepo.find(query)
|
||||
return productRepo.findWithRelations(rels, query)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -147,8 +158,21 @@ class ProductService extends BaseService {
|
||||
this.productRepository_
|
||||
)
|
||||
const validatedId = this.validateId_(productId)
|
||||
const query = this.buildQuery_({ id: validatedId }, config)
|
||||
const product = await productRepo.findOne(query)
|
||||
|
||||
const query = { where: { id: validatedId } }
|
||||
|
||||
if (config.relations && config.relations.length > 0) {
|
||||
query.relations = config.relations
|
||||
}
|
||||
|
||||
if (config.select && config.select.length > 0) {
|
||||
query.select = config.select
|
||||
}
|
||||
|
||||
const rels = query.relations
|
||||
delete query.relations
|
||||
const product = await productRepo.findOneWithRelations(rels, query)
|
||||
|
||||
if (!product) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.NOT_FOUND,
|
||||
|
||||
@@ -671,12 +671,12 @@ class SwapService extends BaseService {
|
||||
* @param {string} swapId - the id of the swap that has been shipped.
|
||||
* @param {string} fulfillmentId - the id of the specific fulfillment that
|
||||
* has been shipped
|
||||
* @param {Array<string>} trackingNumbers - the tracking numbers associated
|
||||
* @param {TrackingLink[]} trackingLinks - the tracking numbers associated
|
||||
* with the shipment
|
||||
* @param {object} metadata - optional metadata to attach to the shipment.
|
||||
* @returns {Promise<Swap>} the updated swap with new fulfillments and status.
|
||||
*/
|
||||
async createShipment(swapId, fulfillmentId, trackingNumbers, metadata = {}) {
|
||||
async createShipment(swapId, fulfillmentId, trackingLinks, metadata = {}) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
const swap = await this.retrieve(swapId, {
|
||||
relations: ["additional_items"],
|
||||
@@ -685,7 +685,7 @@ class SwapService extends BaseService {
|
||||
// Update the fulfillment to register
|
||||
const shipment = await this.fulfillmentService_
|
||||
.withTransaction(manager)
|
||||
.createShipment(fulfillmentId, trackingNumbers, metadata)
|
||||
.createShipment(fulfillmentId, trackingLinks, metadata)
|
||||
|
||||
swap.fulfillment_status = "shipped"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user