feat: return shipping and flow (#125)
Adds support for return shipping methods and changes returns to have a request/receive flow. Store operators should now first request a return, noting the line items that will be returned. When the inventory is received the return will be registered triggering the refund. Return shipping methods can now be created for all regions.
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
import { IdMap } from "medusa-test-utils"
|
||||
|
||||
export const documents = [
|
||||
{
|
||||
_id: IdMap.getId("doc"),
|
||||
name: "test doc",
|
||||
base_64: "verylongstring",
|
||||
},
|
||||
]
|
||||
|
||||
export const DocumentModelMock = {
|
||||
findOne: jest.fn().mockImplementation(query => {
|
||||
return Promise.resolve(documents[0])
|
||||
}),
|
||||
}
|
||||
@@ -59,6 +59,7 @@ export const orders = {
|
||||
],
|
||||
fulfillments: [
|
||||
{
|
||||
_id: IdMap.getId("fulfillment"),
|
||||
provider_id: "default_provider",
|
||||
data: {},
|
||||
},
|
||||
@@ -137,6 +138,114 @@ export const orders = {
|
||||
payment_status: "captured",
|
||||
status: "completed",
|
||||
},
|
||||
returnedOrder: {
|
||||
_id: IdMap.getId("returned-order"),
|
||||
email: "oliver@test.dk",
|
||||
billing_address: {
|
||||
first_name: "Oli",
|
||||
last_name: "Medusa",
|
||||
address_1: "testaddress",
|
||||
city: "LA",
|
||||
country_code: "US",
|
||||
postal_code: "90002",
|
||||
},
|
||||
shipping_address: {
|
||||
first_name: "Oli",
|
||||
last_name: "Medusa",
|
||||
address_1: "testaddress",
|
||||
city: "LA",
|
||||
country_code: "US",
|
||||
postal_code: "90002",
|
||||
},
|
||||
items: [
|
||||
{
|
||||
_id: IdMap.getId("existingLine"),
|
||||
title: "merge line",
|
||||
description: "This is a new line",
|
||||
thumbnail: "test-img-yeah.com/thumb",
|
||||
returned_quantity: 0,
|
||||
content: {
|
||||
unit_price: 123,
|
||||
variant: {
|
||||
_id: IdMap.getId("can-cover"),
|
||||
},
|
||||
product: {
|
||||
_id: IdMap.getId("validId"),
|
||||
},
|
||||
quantity: 1,
|
||||
},
|
||||
quantity: 10,
|
||||
},
|
||||
],
|
||||
region_id: IdMap.getId("region-france"),
|
||||
customer_id: IdMap.getId("test-customer"),
|
||||
payment_method: {
|
||||
provider_id: "default_provider",
|
||||
data: {
|
||||
hi: "hi",
|
||||
},
|
||||
},
|
||||
returns: [
|
||||
{
|
||||
_id: IdMap.getId("return"),
|
||||
status: "requested",
|
||||
shipping_method: {
|
||||
_id: IdMap.getId("return-shipping"),
|
||||
is_return: true,
|
||||
name: "Return Shipping",
|
||||
region_id: IdMap.getId("region-france"),
|
||||
profile_id: IdMap.getId("default-profile"),
|
||||
data: {
|
||||
id: "return_shipment",
|
||||
},
|
||||
price: 2,
|
||||
provider_id: "default_provider",
|
||||
},
|
||||
shipping_data: {
|
||||
id: "return_shipment",
|
||||
shipped: true,
|
||||
},
|
||||
documents: ["doc1234"],
|
||||
items: [
|
||||
{
|
||||
item_id: IdMap.getId("existingLine"),
|
||||
content: {
|
||||
unit_price: 123,
|
||||
variant: {
|
||||
_id: IdMap.getId("can-cover"),
|
||||
},
|
||||
product: {
|
||||
_id: IdMap.getId("validId"),
|
||||
},
|
||||
quantity: 1,
|
||||
},
|
||||
is_requested: true,
|
||||
quantity: 10,
|
||||
},
|
||||
],
|
||||
refund_amount: 1228,
|
||||
},
|
||||
],
|
||||
shipping_methods: [
|
||||
{
|
||||
_id: IdMap.getId("expensiveShipping"),
|
||||
name: "Expensive Shipping",
|
||||
price: 100,
|
||||
provider_id: "default_provider",
|
||||
profile_id: IdMap.getId("default"),
|
||||
},
|
||||
{
|
||||
_id: IdMap.getId("freeShipping"),
|
||||
name: "Free Shipping",
|
||||
price: 10,
|
||||
provider_id: "default_provider",
|
||||
profile_id: IdMap.getId("profile1"),
|
||||
},
|
||||
],
|
||||
fulfillment_status: "fulfilled",
|
||||
payment_status: "captured",
|
||||
status: "completed",
|
||||
},
|
||||
orderToRefund: {
|
||||
_id: IdMap.getId("refund-order"),
|
||||
email: "oliver@test.dk",
|
||||
@@ -192,6 +301,8 @@ export const orders = {
|
||||
quantity: 1,
|
||||
},
|
||||
quantity: 10,
|
||||
returned_quantity: 0,
|
||||
metadata: {},
|
||||
},
|
||||
],
|
||||
region_id: IdMap.getId("region-france"),
|
||||
@@ -199,6 +310,48 @@ export const orders = {
|
||||
payment_method: {
|
||||
provider_id: "default_provider",
|
||||
},
|
||||
returns: [
|
||||
{
|
||||
_id: IdMap.getId("return"),
|
||||
status: "requested",
|
||||
shipping_method: {
|
||||
_id: IdMap.getId("return-shipping"),
|
||||
is_return: true,
|
||||
name: "Return Shipping",
|
||||
region_id: IdMap.getId("region-france"),
|
||||
profile_id: IdMap.getId("default-profile"),
|
||||
data: {
|
||||
id: "return_shipment",
|
||||
},
|
||||
price: 2,
|
||||
provider_id: "default_provider",
|
||||
},
|
||||
shipping_data: {
|
||||
id: "return_shipment",
|
||||
shipped: true,
|
||||
},
|
||||
documents: ["doc1234"],
|
||||
items: [
|
||||
{
|
||||
item_id: IdMap.getId("existingLine"),
|
||||
content: {
|
||||
unit_price: 100,
|
||||
variant: {
|
||||
_id: IdMap.getId("can-cover"),
|
||||
},
|
||||
product: {
|
||||
_id: IdMap.getId("product"),
|
||||
},
|
||||
quantity: 1,
|
||||
},
|
||||
is_requested: true,
|
||||
quantity: 2,
|
||||
metadata: {},
|
||||
},
|
||||
],
|
||||
refund_amount: 246,
|
||||
},
|
||||
],
|
||||
shipping_methods: [
|
||||
{
|
||||
provider_id: "default_provider",
|
||||
@@ -220,6 +373,13 @@ export const orders = {
|
||||
export const OrderModelMock = {
|
||||
create: jest.fn().mockImplementation(data => Promise.resolve(data)),
|
||||
updateOne: jest.fn().mockImplementation((query, update) => {
|
||||
if (query._id === IdMap.getId("returned-order")) {
|
||||
return Promise.resolve(orders.returnedOrder)
|
||||
}
|
||||
if (query._id === IdMap.getId("order-refund")) {
|
||||
orders.orderToRefund.payment_status = "captured"
|
||||
return Promise.resolve(orders.orderToRefund)
|
||||
}
|
||||
return Promise.resolve()
|
||||
}),
|
||||
deleteOne: jest.fn().mockReturnValue(Promise.resolve()),
|
||||
@@ -250,6 +410,9 @@ export const OrderModelMock = {
|
||||
if (query._id === IdMap.getId("processed-order")) {
|
||||
return Promise.resolve(orders.processedOrder)
|
||||
}
|
||||
if (query._id === IdMap.getId("returned-order")) {
|
||||
return Promise.resolve(orders.returnedOrder)
|
||||
}
|
||||
if (query._id === IdMap.getId("order-refund")) {
|
||||
orders.orderToRefund.payment_status = "captured"
|
||||
return Promise.resolve(orders.orderToRefund)
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
import mongoose from "mongoose"
|
||||
import { BaseModel } from "medusa-interfaces"
|
||||
|
||||
class DocumentModel extends BaseModel {
|
||||
static modelName = "Document"
|
||||
|
||||
static schema = {
|
||||
base_64: { type: String, required: true },
|
||||
name: { type: String, required: true },
|
||||
type: { type: String, required: true },
|
||||
created: { type: String, default: Date.now },
|
||||
metadata: { type: mongoose.Schema.Types.Mixed, default: {} },
|
||||
}
|
||||
}
|
||||
|
||||
export default DocumentModel
|
||||
@@ -38,6 +38,7 @@ class OrderModel extends BaseModel {
|
||||
customer_id: { type: String },
|
||||
payment_method: { type: PaymentMethodSchema, required: true },
|
||||
shipping_methods: { type: [ShippingMethodSchema], required: true },
|
||||
documents: { type: [String], default: [] },
|
||||
created: { type: String, default: Date.now },
|
||||
metadata: { type: mongoose.Schema.Types.Mixed, default: {} },
|
||||
}
|
||||
|
||||
@@ -8,5 +8,6 @@ export default new mongoose.Schema({
|
||||
tracking_numbers: { type: [String], default: [] },
|
||||
shipped_at: { type: String },
|
||||
is_canceled: { type: Boolean, default: false },
|
||||
documents: { type: [String], default: [] },
|
||||
metadata: { type: mongoose.Schema.Types.Mixed, default: {} },
|
||||
})
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
/*******************************************************************************
|
||||
*
|
||||
******************************************************************************/
|
||||
import mongoose from "mongoose"
|
||||
|
||||
/**
|
||||
* @typedef ReturnLineItem
|
||||
* @property {String} item_id
|
||||
* @property {Object} content
|
||||
* @property {Number} quantity
|
||||
* @property {Boolean} is_requested
|
||||
* @property {Boolean} is_registered
|
||||
* @property {Object} metadata
|
||||
*/
|
||||
export default new mongoose.Schema({
|
||||
item_id: { type: String, required: true, unique: true },
|
||||
content: { type: mongoose.Schema.Types.Mixed, required: true },
|
||||
quantity: { type: Number, required: true },
|
||||
is_requested: { type: Boolean, required: true },
|
||||
requested_quantity: { type: Number },
|
||||
is_registered: { type: Boolean, default: false },
|
||||
metadata: { type: mongoose.Schema.Types.Mixed, default: {} },
|
||||
})
|
||||
@@ -1,8 +1,15 @@
|
||||
import mongoose from "mongoose"
|
||||
import ReturnLineItemSchema from "./return-line-item"
|
||||
import ShippingMethodSchema from "./shipping-method"
|
||||
|
||||
export default new mongoose.Schema({
|
||||
created: { type: String, default: Date.now },
|
||||
status: { type: String, default: "requested" },
|
||||
refund_amount: { type: Number, required: true },
|
||||
items: { type: [mongoose.Schema.Types.Mixed], required: true },
|
||||
items: { type: [ReturnLineItemSchema], required: true },
|
||||
shipping_method: { type: ShippingMethodSchema, default: {} },
|
||||
shipping_data: { type: mongoose.Schema.Types.Mixed, default: {} },
|
||||
documents: { type: [String], default: [] },
|
||||
received_at: { type: String },
|
||||
created: { type: String, default: Date.now },
|
||||
metadata: { type: mongoose.Schema.Types.Mixed, default: {} },
|
||||
})
|
||||
|
||||
@@ -14,6 +14,7 @@ class ShippingOptionModel extends BaseModel {
|
||||
data: { type: mongoose.Schema.Types.Mixed, default: {} },
|
||||
price: { type: ShippingOptionPrice, required: true },
|
||||
requirements: { type: [ShippingOptionRequirement], default: [] },
|
||||
is_return: { type: Boolean, default: false },
|
||||
metadata: { type: mongoose.Schema.Types.Mixed, default: {} },
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user