Feat/nested return reasons (#418)

* api endpoints for nested return_reasons

* add nested return reasons to database

* add parent reason to update

* integration tests

* add children relation

* integration tests for nested returns and failing doubly nesting returns

* add delete-route and nested relations

* delete return reason route

* doubly nested return reason creation check and deletion

* nested return reasons migration

* list only parent reasons

* removed null filter

* remove empty migration

* add return reason filter to get list of categories with children

* removed console log

* corrected delete route

* return reason testing

* return reasons query

* listREasonsFromIDs

* create return testing

* listReasonsFromIds

* return reason tests

* failing if returnreason has child on return

* integration tests

* cascading deletes on return reasons

* more elegant checking for children of return reasons when creating a return

* remove console.log

* pr adjust

Co-authored-by: Philip Korsholm <phko@MacBook-Pro.localdomain>
This commit is contained in:
pKorsholm
2021-09-29 09:22:09 +02:00
committed by GitHub
parent 4db860f9ab
commit 42cdfde6d9
15 changed files with 744 additions and 95 deletions

View File

@@ -0,0 +1,16 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`/admin/return-reasons POST /admin/return-reasons creates a return_reason 1`] = `
Object {
"created_at": Any<String>,
"deleted_at": null,
"description": "Use this if the size was too big",
"id": Any<String>,
"label": "Too Big",
"parent_return_reason": null,
"parent_return_reason_id": null,
"return_reason_children": Array [],
"updated_at": Any<String>,
"value": "too_big",
}
`;

View File

@@ -1,53 +1,55 @@
const path = require("path");
const { match } = require("assert")
const path = require("path")
const { RepositoryNotTreeError } = require("typeorm")
const setupServer = require("../../../helpers/setup-server");
const { useApi } = require("../../../helpers/use-api");
const { initDb, useDb } = require("../../../helpers/use-db");
const setupServer = require("../../../helpers/setup-server")
const { useApi } = require("../../../helpers/use-api")
const { initDb, useDb } = require("../../../helpers/use-db")
const adminSeeder = require("../../helpers/admin-seeder");
const adminSeeder = require("../../helpers/admin-seeder")
jest.setTimeout(30000);
jest.setTimeout(30000)
describe("/admin/return-reasons", () => {
let medusaProcess;
let dbConnection;
let medusaProcess
let dbConnection
beforeAll(async () => {
const cwd = path.resolve(path.join(__dirname, "..", ".."));
dbConnection = await initDb({ cwd });
medusaProcess = await setupServer({ cwd });
});
const cwd = path.resolve(path.join(__dirname, "..", ".."))
dbConnection = await initDb({ cwd })
medusaProcess = await setupServer({ cwd })
})
afterAll(async () => {
const db = useDb();
await db.shutdown();
const db = useDb()
await db.shutdown()
medusaProcess.kill();
});
medusaProcess.kill()
})
describe("POST /admin/return-reasons", () => {
beforeEach(async () => {
try {
await adminSeeder(dbConnection);
await adminSeeder(dbConnection)
} catch (err) {
console.log(err);
throw err;
console.log(err)
throw err
}
});
})
afterEach(async () => {
const db = useDb();
await db.teardown();
});
const db = useDb()
await db.teardown()
})
it("creates a return_reason", async () => {
const api = useApi();
const api = useApi()
const payload = {
label: "Too Big",
description: "Use this if the size was too big",
value: "too_big",
};
}
const response = await api
.post("/admin/return-reasons", payload, {
@@ -56,10 +58,172 @@ describe("/admin/return-reasons", () => {
},
})
.catch((err) => {
console.log(err);
});
console.log(err)
})
expect(response.status).toEqual(200);
expect(response.status).toEqual(200)
expect(response.data.return_reason).toMatchSnapshot({
id: expect.any(String),
created_at: expect.any(String),
updated_at: expect.any(String),
parent_return_reason: null,
parent_return_reason_id: null,
label: "Too Big",
description: "Use this if the size was too big",
value: "too_big",
})
})
it("creates a nested return reason", async () => {
const api = useApi()
const payload = {
label: "Wrong size",
description: "Use this if the size was too big",
value: "wrong_size",
}
const response = await api
.post("/admin/return-reasons", payload, {
headers: {
Authorization: "Bearer test_token",
},
})
.catch((err) => {
console.log(err)
})
expect(response.status).toEqual(200)
expect(response.data.return_reason).toEqual(
expect.objectContaining({
label: "Wrong size",
description: "Use this if the size was too big",
value: "wrong_size",
})
)
const nested_payload = {
parent_return_reason_id: response.data.return_reason.id,
label: "Too Big",
description: "Use this if the size was too big",
value: "too_big",
}
const nested_response = await api
.post("/admin/return-reasons", nested_payload, {
headers: {
Authorization: "Bearer test_token",
},
})
.catch((err) => {
console.log(err)
})
expect(nested_response.status).toEqual(200)
expect(nested_response.data.return_reason).toEqual(
expect.objectContaining({
parent_return_reason_id: response.data.return_reason.id,
label: "Too Big",
description: "Use this if the size was too big",
value: "too_big",
})
)
})
it("fails to create a doubly nested return reason", async () => {
expect.assertions(5)
const api = useApi()
const payload = {
label: "Wrong size",
description: "Use this if the size was too big",
value: "wrong_size",
}
const response = await api
.post("/admin/return-reasons", payload, {
headers: {
Authorization: "Bearer test_token",
},
})
.catch((err) => {
console.log(err)
})
expect(response.status).toEqual(200)
expect(response.data.return_reason).toEqual(
expect.objectContaining({
label: "Wrong size",
description: "Use this if the size was too big",
value: "wrong_size",
})
)
const nested_payload = {
parent_return_reason_id: response.data.return_reason.id,
label: "Too Big",
description: "Use this if the size was too big",
value: "too_big",
}
const nested_response = await api
.post("/admin/return-reasons", nested_payload, {
headers: {
Authorization: "Bearer test_token",
},
})
.catch((err) => {
console.log(err)
})
const dbl_nested_payload = {
parent_return_reason_id: nested_response.data.return_reason.id,
label: "Too large size",
description: "Use this if the size was too big",
value: "large_size",
}
const dbl_nested_response = await api
.post("/admin/return-reasons", dbl_nested_payload, {
headers: {
Authorization: "Bearer test_token",
},
})
.catch((err) => {
expect(err.response.status).toEqual(400)
expect(err.response.data.type).toEqual("invalid_data")
expect(err.response.data.message).toEqual(
"Doubly nested return reasons is not supported"
)
})
})
it("deletes a return_reason", async () => {
const api = useApi()
const payload = {
label: "Too Big",
description: "Use this if the size was too big",
value: "too_big",
}
const response = await api
.post("/admin/return-reasons", payload, {
headers: {
Authorization: "Bearer test_token",
},
})
.catch((err) => {
console.log(err)
})
expect(response.status).toEqual(200)
expect(response.data.return_reason).toEqual(
expect.objectContaining({
@@ -67,17 +231,37 @@ describe("/admin/return-reasons", () => {
description: "Use this if the size was too big",
value: "too_big",
})
);
});
)
const deleteResponse = await api
.delete(`/admin/return-reasons/${response.data.return_reason.id}`, {
headers: {
Authorization: "Bearer test_token",
},
})
.catch((err) => {
console.log(err)
})
expect(response.status).toEqual(200)
expect(deleteResponse.data).toEqual(
expect.objectContaining({
id: response.data.return_reason.id,
object: "return_reason",
deleted: true,
})
)
})
it("update a return reason", async () => {
const api = useApi();
const api = useApi()
const payload = {
label: "Too Big Typo",
description: "Use this if the size was too big",
value: "too_big",
};
}
const response = await api
.post("/admin/return-reasons", payload, {
@@ -86,10 +270,10 @@ describe("/admin/return-reasons", () => {
},
})
.catch((err) => {
console.log(err);
});
console.log(err)
})
expect(response.status).toEqual(200);
expect(response.status).toEqual(200)
expect(response.data.return_reason).toEqual(
expect.objectContaining({
@@ -97,7 +281,7 @@ describe("/admin/return-reasons", () => {
description: "Use this if the size was too big",
value: "too_big",
})
);
)
const newResponse = await api
.post(
@@ -113,8 +297,8 @@ describe("/admin/return-reasons", () => {
}
)
.catch((err) => {
console.log(err);
});
console.log(err)
})
expect(newResponse.data.return_reason).toEqual(
expect.objectContaining({
@@ -122,17 +306,81 @@ describe("/admin/return-reasons", () => {
description: "new desc",
value: "too_big",
})
);
});
)
})
it("lists nested return reasons", async () => {
const api = useApi()
const payload = {
label: "Wrong size",
description: "Use this if the size was too big",
value: "wrong_size",
}
const response = await api
.post("/admin/return-reasons", payload, {
headers: {
Authorization: "Bearer test_token",
},
})
.catch((err) => {
console.log(err)
})
const nested_payload = {
parent_return_reason_id: response.data.return_reason.id,
label: "Too Big",
description: "Use this if the size was too big",
value: "too_big",
}
const resp = await api
.post("/admin/return-reasons", nested_payload, {
headers: {
Authorization: "Bearer test_token",
},
})
.catch((err) => {
console.log(err)
})
const nested_response = await api
.get("/admin/return-reasons", {
headers: {
Authorization: "Bearer test_token",
},
})
.catch((err) => {
console.log(err)
})
expect(nested_response.status).toEqual(200)
expect(nested_response.data.return_reasons).toEqual([
expect.objectContaining({
label: "Wrong size",
description: "Use this if the size was too big",
value: "wrong_size",
return_reason_children: expect.arrayContaining([
expect.objectContaining({
label: "Too Big",
description: "Use this if the size was too big",
value: "too_big",
}),
]),
}),
])
})
it("list return reasons", async () => {
const api = useApi();
const api = useApi()
const payload = {
label: "Too Big Typo",
description: "Use this if the size was too big",
value: "too_big",
};
}
await api
.post("/admin/return-reasons", payload, {
@@ -141,8 +389,8 @@ describe("/admin/return-reasons", () => {
},
})
.catch((err) => {
console.log(err);
});
console.log(err)
})
const response = await api
.get("/admin/return-reasons", {
@@ -151,15 +399,191 @@ describe("/admin/return-reasons", () => {
},
})
.catch((err) => {
console.log(err);
});
console.log(err)
})
expect(response.status).toEqual(200);
expect(response.status).toEqual(200)
expect(response.data.return_reasons).toEqual([
expect.objectContaining({
value: "too_big",
}),
]);
});
});
});
])
})
})
describe("DELETE /admin/return-reasons", () => {
beforeEach(async () => {
try {
await adminSeeder(dbConnection)
} catch (err) {
console.log(err)
throw err
}
})
afterEach(async () => {
const db = useDb()
await db.teardown()
})
it("deletes single return reason", async () => {
expect.assertions(6)
const api = useApi()
const payload = {
label: "Too Big",
description: "Use this if the size was too big",
value: "too_big",
}
const response = await api
.post("/admin/return-reasons", payload, {
headers: {
Authorization: "Bearer test_token",
},
})
.catch((err) => {
console.log(err)
})
expect(response.status).toEqual(200)
expect(response.data.return_reason).toEqual(
expect.objectContaining({
label: "Too Big",
description: "Use this if the size was too big",
value: "too_big",
})
)
const deleteResult = await api.delete(
`/admin/return-reasons/${response.data.return_reason.id}`,
{
headers: {
Authorization: "Bearer test_token",
},
}
)
expect(deleteResult.status).toEqual(200)
expect(deleteResult.data).toEqual({
id: response.data.return_reason.id,
object: "return_reason",
deleted: true,
})
const getResult = await api
.get(`/admin/return-reasons/${response.data.return_reason.id}`, {
headers: {
Authorization: "Bearer test_token",
},
})
.catch((err) => {
expect(err.response.status).toEqual(404)
expect(err.response.data.type).toEqual("not_found")
})
})
it("deletes cascade through nested return reasons", async () => {
expect.assertions(10)
const api = useApi()
const payload = {
label: "Wrong Size",
description: "Use this if the size was wrong",
value: "wrong_size",
}
const response = await api
.post("/admin/return-reasons", payload, {
headers: {
Authorization: "Bearer test_token",
},
})
.catch((err) => {
console.log(err)
})
expect(response.status).toEqual(200)
expect(response.data.return_reason).toEqual(
expect.objectContaining({
label: "Wrong Size",
description: "Use this if the size was wrong",
value: "wrong_size",
})
)
const payload_child = {
label: "Too Big",
description: "Use this if the size was too big",
value: "too_big",
parent_return_reason_id: response.data.return_reason.id,
}
const response_child = await api
.post("/admin/return-reasons", payload_child, {
headers: {
Authorization: "Bearer test_token",
},
})
.catch((err) => {
console.log(err)
})
expect(response_child.status).toEqual(200)
expect(response_child.data.return_reason).toEqual(
expect.objectContaining({
label: "Too Big",
description: "Use this if the size was too big",
value: "too_big",
parent_return_reason_id: response.data.return_reason.id,
})
)
const deleteResult = await api
.delete(`/admin/return-reasons/${response.data.return_reason.id}`, {
headers: {
Authorization: "Bearer test_token",
},
})
.catch((err) => {
console.log(err.response.data)
})
expect(deleteResult.status).toEqual(200)
expect(deleteResult.data).toEqual({
id: response.data.return_reason.id,
object: "return_reason",
deleted: true,
})
await api
.get(`/admin/return-reasons/${response.data.return_reason.id}`, {
headers: {
Authorization: "Bearer test_token",
},
})
.catch((err) => {
expect(err.response.status).toEqual(404)
expect(err.response.data.type).toEqual("not_found")
})
await api
.get(`/admin/return-reasons/${response_child.data.return_reason.id}`, {
headers: {
Authorization: "Bearer test_token",
},
})
.catch((err) => {
expect(err.response.status).toEqual(404)
expect(err.response.data.type).toEqual("not_found")
})
})
})
})

View File

@@ -26,16 +26,35 @@ describe("/store/return-reasons", () => {
describe("GET /store/return-reasons", () => {
let rrId;
let rrId_1;
let rrId_2;
beforeEach(async () => {
try {
const created = dbConnection.manager.create(ReturnReason, {
value: "too_big",
label: "Too Big",
value: "wrong_size",
label: "Wrong size",
});
const result = await dbConnection.manager.save(created);
rrId = result.id;
const created_child = dbConnection.manager.create(ReturnReason, {
value: "too_big",
label: "Too Big",
parent_return_reason_id: rrId
});
const result_child = await dbConnection.manager.save(created_child);
rrId_1 = result_child.id;
const created_2 = dbConnection.manager.create(ReturnReason, {
value: "too_big_1",
label: "Too Big 1",
});
const result_2 = await dbConnection.manager.save(created_2);
rrId_2 = result_2.id;
} catch (err) {
console.log(err);
throw err;
@@ -59,7 +78,15 @@ describe("/store/return-reasons", () => {
expect(response.data.return_reasons).toEqual([
expect.objectContaining({
id: rrId,
value: "too_big",
value: "wrong_size",
return_reason_children:[expect.objectContaining({
id: rrId_1,
value: "too_big",
}),]
}),
expect.objectContaining({
id: rrId_2,
value: "too_big_1",
}),
]);
});

View File

@@ -8,6 +8,7 @@ const {
Product,
ProductVariant,
ShippingOption,
FulfillmentProvider,
LineItem,
Discount,
DiscountRule,
@@ -37,6 +38,8 @@ describe("/store/carts", () => {
describe("POST /store/returns", () => {
let rrId;
let rrId_child;
let rrResult;
beforeEach(async () => {
const manager = dbConnection.manager;
@@ -44,13 +47,17 @@ describe("/store/carts", () => {
`ALTER SEQUENCE order_display_id_seq RESTART WITH 111`
);
const defaultProfile = await manager.findOne(ShippingProfile, {
type: "default",
});
await manager.insert(Region, {
id: "region",
name: "Test Region",
currency_code: "usd",
tax_rate: 0,
});
await manager.insert(Customer, {
id: "cus_1234",
email: "test@email.com",
@@ -98,10 +105,6 @@ describe("/store/carts", () => {
await manager.save(ord);
const defaultProfile = await manager.findOne(ShippingProfile, {
type: "default",
});
await manager.insert(Product, {
id: "test-product",
title: "test product",
@@ -147,12 +150,23 @@ describe("/store/carts", () => {
});
const created = dbConnection.manager.create(ReturnReason, {
value: "too_big",
label: "Too Big",
value: "wrong_size",
label: "Wrong Size",
});
const result = await dbConnection.manager.save(created);
rrResult = result
rrId = result.id;
const created_1 = dbConnection.manager.create(ReturnReason, {
value: "too_big",
label: "Too Big",
parent_return_reason_id: rrId,
});
const result_1 = await dbConnection.manager.save(created_1);
rrId_child = result_1.id;
});
afterEach(async () => {
@@ -181,6 +195,59 @@ describe("/store/carts", () => {
expect(response.data.return.refund_amount).toEqual(8000);
});
it("failes to create a return with a reason category", async () => {
const api = useApi();
const response = await api
.post("/store/returns", {
order_id: "order_test",
items: [
{
reason_id: rrId,
note: "TOO small",
item_id: "test-item",
quantity: 1,
},
],
})
.catch((err) => {
return err.response;
});
expect(response.status).toEqual(400);
expect(response.data.message).toEqual('Cannot apply return reason category')
});
it("creates a return with reasons", async () => {
const api = useApi();
const response = await api
.post("/store/returns", {
order_id: "order_test",
items: [
{
reason_id: rrId_child,
note: "TOO small",
item_id: "test-item",
quantity: 1,
},
],
})
.catch((err) => {
console.log(err.response)
return err.response;
});
expect(response.status).toEqual(200);
expect(response.data.return.items).toEqual([
expect.objectContaining({
reason_id: rrId_child,
note: "TOO small",
}),
]);
});
it("creates a return with discount and non-discountable item", async () => {
const api = useApi();
@@ -234,32 +301,5 @@ describe("/store/carts", () => {
expect(response.data.return.refund_amount).toEqual(7000);
});
it("creates a return with reasons", async () => {
const api = useApi();
const response = await api
.post("/store/returns", {
order_id: "order_test",
items: [
{
reason_id: rrId,
note: "TOO small",
item_id: "test-item",
quantity: 1,
},
],
})
.catch((err) => {
return err.response;
});
expect(response.status).toEqual(200);
expect(response.data.return.items).toEqual([
expect.objectContaining({
reason_id: rrId,
note: "TOO small",
}),
]);
});
});
});

View File

@@ -39,6 +39,7 @@ export default async (req, res) => {
const schema = Validator.object().keys({
value: Validator.string().required(),
label: Validator.string().required(),
parent_return_reason_id: Validator.string().optional(),
description: Validator.string()
.optional()
.allow(""),

View File

@@ -0,0 +1,41 @@
/**
* @oas [delete] /return-reason/{id}
* operationId: "DeleteReturnReason"
* summary: "Delete a return reason"
* description: "Deletes a return reason."
* parameters:
* - (path) id=* {string} The id of the return reason
* tags:
* - Return Reason
* responses:
* 200:
* description: OK
* content:
* application/json:
* schema:
* properties:
* id:
* type: string
* description: The id of the deleted return reason
* object:
* type: string
* description: The type of the object that was deleted.
* deleted:
* type: boolean
*/
export default async (req, res) => {
const { id } = req.params
try {
const returnReasonService = req.scope.resolve("returnReasonService")
await returnReasonService.delete(id)
res.json({
id: id,
object: "return_reason",
deleted: true,
})
} catch (err) {
throw err
}
}

View File

@@ -26,6 +26,11 @@ export default app => {
*/
route.post("/:id", middlewares.wrap(require("./update-reason").default))
/**
* Delete a reason
*/
route.delete("/:id", middlewares.wrap(require("./delete-reason").default))
return app
}
@@ -33,10 +38,14 @@ export const defaultFields = [
"id",
"value",
"label",
"parent_return_reason_id",
"description",
"created_at",
"updated_at",
"deleted_at",
]
export const defaultRelations = []
export const defaultRelations = [
"parent_return_reason",
"return_reason_children",
]

View File

@@ -24,7 +24,7 @@ export default async (req, res) => {
try {
const returnReasonService = req.scope.resolve("returnReasonService")
const query = {}
const query = { parent_return_reason_id: null }
const data = await returnReasonService.list(query, {
select: defaultFields,
relations: defaultRelations,

View File

@@ -42,6 +42,7 @@ export default async (req, res) => {
const schema = Validator.object().keys({
label: Validator.string().optional(),
parent_return_reason_id: Validator.string().optional(),
description: Validator.string()
.optional()
.allow(""),

View File

@@ -23,10 +23,14 @@ export const defaultFields = [
"id",
"value",
"label",
"parent_return_reason_id",
"description",
"created_at",
"updated_at",
"deleted_at",
]
export const defaultRelations = []
export const defaultRelations = [
"parent_return_reason",
"return_reason_children",
]

View File

@@ -24,7 +24,7 @@ export default async (req, res) => {
try {
const returnReasonService = req.scope.resolve("returnReasonService")
const query = {}
const query = { parent_return_reason_id: null}
const data = await returnReasonService.list(query, {
select: defaultFields,
relations: defaultRelations,

View File

@@ -0,0 +1,20 @@
import {MigrationInterface, QueryRunner} from "typeorm";
export class nestedReturnReasons1631800727788 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "return_reason" ADD "parent_return_reason_id" character varying`
)
await queryRunner.query(`ALTER TABLE "return_reason" ADD CONSTRAINT "FK_2250c5d9e975987ab212f61a657" FOREIGN KEY ("parent_return_reason_id") REFERENCES "return_reason"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "return_reason" DROP COLUMN "parent_return_reason_id"`
)
}
}

View File

@@ -7,6 +7,9 @@ import {
CreateDateColumn,
UpdateDateColumn,
PrimaryColumn,
ManyToOne,
OneToMany,
JoinColumn
} from "typeorm"
import { ulid } from "ulid"
import { resolveDbType, DbAwareColumn } from "../utils/db-aware-column"
@@ -26,6 +29,21 @@ export class ReturnReason {
@Column({ nullable: true })
description: string
@Column({ nullable: true })
parent_return_reason_id: string
@ManyToOne(() => ReturnReason, {cascade: ['soft-remove']}
)
@JoinColumn({ name: "parent_return_reason_id" })
parent_return_reason: ReturnReason
@OneToMany(
() => ReturnReason,
return_reason => return_reason.parent_return_reason,
{ cascade: ["insert", 'soft-remove'] }
)
return_reason_children: ReturnReason[]
@CreateDateColumn({ type: resolveDbType("timestamptz") })
created_at: Date

View File

@@ -1,6 +1,6 @@
import _ from "lodash"
import { Validator, MedusaError } from "medusa-core-utils"
import { BaseService } from "medusa-interfaces"
import { In } from "typeorm"
class ReturnReasonService extends BaseService {
constructor({ manager, returnReasonRepository }) {
@@ -32,6 +32,17 @@ class ReturnReasonService extends BaseService {
return this.atomicPhase_(async manager => {
const rrRepo = manager.getCustomRepository(this.retReasonRepo_)
if (data.parent_return_reason_id && data.parent_return_reason_id !== "") {
const parentReason = await this.retrieve(data.parent_return_reason_id)
if (parentReason.parent_return_reason_id) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
"Doubly nested return reasons is not supported"
)
}
}
const created = rrRepo.create(data)
const result = await rrRepo.save(created)
@@ -44,14 +55,20 @@ class ReturnReasonService extends BaseService {
const rrRepo = manager.getCustomRepository(this.retReasonRepo_)
const reason = await this.retrieve(id)
if ("description" in data) {
const { description, label, parent_return_reason_id } = data
if (description) {
reason.description = data.description
}
if ("label" in data) {
if (label) {
reason.label = data.label
}
if (parent_return_reason_id) {
reason.parent_return_reason_id = parent_return_reason_id
}
await rrRepo.save(reason)
return reason
@@ -92,6 +109,25 @@ class ReturnReasonService extends BaseService {
return item
}
async delete(returnReasonId) {
return this.atomicPhase_(async manager => {
const rrRepo = manager.getCustomRepository(this.retReasonRepo_)
// We include the relation 'return_reason_children' to enable cascading deletes of return reasons if a parent is removed
const reason = await this.retrieve(returnReasonId, {
relations: ["return_reason_children"],
})
if (!reason) {
return Promise.resolve()
}
await rrRepo.softRemove(reason)
return Promise.resolve()
})
}
}
export default ReturnReasonService

View File

@@ -374,6 +374,18 @@ class ReturnService extends BaseService {
refund_amount: Math.floor(toRefund),
}
const returnReasons = await this.returnReasonService_.list(
{ id: [...returnLines.map(rl => rl.reason_id)] },
{ relations: ["return_reason_children"] }
)
if (returnReasons.some(rr => rr.return_reason_children?.length > 0)) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
"Cannot apply return reason category"
)
}
const rItemRepo = manager.getCustomRepository(this.returnItemRepository_)
returnObject.items = returnLines.map(i =>
rItemRepo.create({