feat: create quotation endpoint (test required)
This commit is contained in:
parent
d2e7a89211
commit
e509cd1fd5
2 changed files with 147 additions and 31 deletions
|
|
@ -878,7 +878,7 @@ model QuotationServiceWorkProduct {
|
||||||
product Product @relation(fields: [productId], references: [id], onDelete: Cascade)
|
product Product @relation(fields: [productId], references: [id], onDelete: Cascade)
|
||||||
productId String
|
productId String
|
||||||
|
|
||||||
vat Int
|
vat Float
|
||||||
amount Int
|
amount Int
|
||||||
discount Float
|
discount Float
|
||||||
pricePerUnit Float
|
pricePerUnit Float
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { PayCondition, Status } from "@prisma/client";
|
import { PayCondition, Prisma, Status } from "@prisma/client";
|
||||||
import {
|
import {
|
||||||
Body,
|
Body,
|
||||||
Controller,
|
Controller,
|
||||||
|
|
@ -70,7 +70,16 @@ type QuotationCreate = {
|
||||||
product: {
|
product: {
|
||||||
id: string;
|
id: string;
|
||||||
amount: number;
|
amount: number;
|
||||||
|
/**
|
||||||
|
* @maximum 1
|
||||||
|
* @minimum 0
|
||||||
|
*/
|
||||||
discount: number;
|
discount: number;
|
||||||
|
/**
|
||||||
|
* @maximum 1
|
||||||
|
* @minimum 0
|
||||||
|
*/
|
||||||
|
vat?: number;
|
||||||
}[];
|
}[];
|
||||||
}[];
|
}[];
|
||||||
}[];
|
}[];
|
||||||
|
|
@ -127,8 +136,20 @@ type QuotationUpdate = {
|
||||||
// Name field will come from original data
|
// Name field will come from original data
|
||||||
product: {
|
product: {
|
||||||
id: string;
|
id: string;
|
||||||
|
/**
|
||||||
|
* @isInt
|
||||||
|
*/
|
||||||
amount: number;
|
amount: number;
|
||||||
|
/**
|
||||||
|
* @maximum 1
|
||||||
|
* @minimum 0
|
||||||
|
*/
|
||||||
discount: number;
|
discount: number;
|
||||||
|
/**
|
||||||
|
* @maximum 1
|
||||||
|
* @minimum 0
|
||||||
|
*/
|
||||||
|
vat: number;
|
||||||
}[];
|
}[];
|
||||||
}[];
|
}[];
|
||||||
}[];
|
}[];
|
||||||
|
|
@ -216,9 +237,7 @@ export class QuotationController extends Controller {
|
||||||
where: { id: { in: existingEmployee } },
|
where: { id: { in: existingEmployee } },
|
||||||
}),
|
}),
|
||||||
prisma.service.findMany({
|
prisma.service.findMany({
|
||||||
include: {
|
include: { work: true },
|
||||||
work: true,
|
|
||||||
},
|
|
||||||
where: { id: { in: serviceIdList } },
|
where: { id: { in: serviceIdList } },
|
||||||
}),
|
}),
|
||||||
prisma.product.findMany({
|
prisma.product.findMany({
|
||||||
|
|
@ -226,6 +245,20 @@ export class QuotationController extends Controller {
|
||||||
}),
|
}),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
if (serviceIdList.length !== service.length) {
|
||||||
|
throw new HttpError(
|
||||||
|
HttpStatus.BAD_REQUEST,
|
||||||
|
"Some service cannot be found.",
|
||||||
|
"relationServiceNotFound",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (productIdList.length !== product.length) {
|
||||||
|
throw new HttpError(
|
||||||
|
HttpStatus.BAD_REQUEST,
|
||||||
|
"Some product cannot be found.",
|
||||||
|
"relationProductNotFound",
|
||||||
|
);
|
||||||
|
}
|
||||||
if (!customer)
|
if (!customer)
|
||||||
throw new HttpError(
|
throw new HttpError(
|
||||||
HttpStatus.BAD_REQUEST,
|
HttpStatus.BAD_REQUEST,
|
||||||
|
|
@ -238,6 +271,12 @@ export class QuotationController extends Controller {
|
||||||
"Customer Branch cannot be found.",
|
"Customer Branch cannot be found.",
|
||||||
"relationCustomerBranchNotFound",
|
"relationCustomerBranchNotFound",
|
||||||
);
|
);
|
||||||
|
if (customerBranch.customerId !== customer.id)
|
||||||
|
throw new HttpError(
|
||||||
|
HttpStatus.BAD_REQUEST,
|
||||||
|
"Customer conflict with customer branch.",
|
||||||
|
"customerConflictCustomerBranch",
|
||||||
|
);
|
||||||
|
|
||||||
const { service: _service, worker: _worker, ...rest } = body;
|
const { service: _service, worker: _worker, ...rest } = body;
|
||||||
|
|
||||||
|
|
@ -275,7 +314,8 @@ export class QuotationController extends Controller {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const price = { totalPrice: 0, totalDiscount };
|
const price = { totalPrice: 0, totalDiscount: 0, totalVat: 0 };
|
||||||
|
|
||||||
const restructureService = body.service.flatMap((a) => {
|
const restructureService = body.service.flatMap((a) => {
|
||||||
const currentService = service.find((b) => b.id === a.id);
|
const currentService = service.find((b) => b.id === a.id);
|
||||||
|
|
||||||
|
|
@ -286,6 +326,7 @@ export class QuotationController extends Controller {
|
||||||
name: currentService.name,
|
name: currentService.name,
|
||||||
code: currentService.code,
|
code: currentService.code,
|
||||||
detail: currentService.detail,
|
detail: currentService.detail,
|
||||||
|
attributes: currentService.attributes as Prisma.JsonObject,
|
||||||
work: a.work.flatMap((c) => {
|
work: a.work.flatMap((c) => {
|
||||||
const currentWork = currentService.work.find((d) => d.id === c.id);
|
const currentWork = currentService.work.find((d) => d.id === c.id);
|
||||||
|
|
||||||
|
|
@ -295,12 +336,27 @@ export class QuotationController extends Controller {
|
||||||
id: currentWork.id,
|
id: currentWork.id,
|
||||||
order: currentWork.order,
|
order: currentWork.order,
|
||||||
name: currentWork.name,
|
name: currentWork.name,
|
||||||
|
attributes: currentWork.attributes as Prisma.JsonObject,
|
||||||
product: c.product.flatMap((e) => {
|
product: c.product.flatMap((e) => {
|
||||||
const currentProduct = product.find((f) => f.id === e.id);
|
const currentProduct = product.find((f) => f.id === e.id);
|
||||||
|
|
||||||
if (!currentProduct) return []; // should not possible
|
if (!currentProduct) return []; // should not possible
|
||||||
|
|
||||||
return { ...e, pricePerUnit: currentProduct.price };
|
price.totalPrice += currentProduct.price;
|
||||||
|
price.totalDiscount += Math.round(currentProduct.price * e.discount * 100) / 100;
|
||||||
|
price.totalVat +=
|
||||||
|
Math.round(
|
||||||
|
(currentProduct.price - currentProduct.price * e.discount) *
|
||||||
|
(e.vat === undefined ? 0.07 : e.vat) *
|
||||||
|
100,
|
||||||
|
) / 100;
|
||||||
|
|
||||||
|
console.log(e.vat);
|
||||||
|
return {
|
||||||
|
...e,
|
||||||
|
vat: e.vat === undefined ? 0.07 : e.vat,
|
||||||
|
pricePerUnit: currentProduct.price,
|
||||||
|
};
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
|
|
@ -308,6 +364,9 @@ export class QuotationController extends Controller {
|
||||||
});
|
});
|
||||||
|
|
||||||
const quotation = await tx.quotation.create({
|
const quotation = await tx.quotation.create({
|
||||||
|
include: {
|
||||||
|
service: true,
|
||||||
|
},
|
||||||
data: {
|
data: {
|
||||||
...rest,
|
...rest,
|
||||||
statusOrder: +(rest.status === "INACTIVE"),
|
statusOrder: +(rest.status === "INACTIVE"),
|
||||||
|
|
@ -321,25 +380,14 @@ export class QuotationController extends Controller {
|
||||||
})),
|
})),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
service: {
|
totalPrice: price.totalPrice,
|
||||||
createMany: {
|
totalDiscount: price.totalDiscount,
|
||||||
data: body.service.flatMap((a) => {
|
|
||||||
const src = service.find((b) => b.id == a.id);
|
vat: price.totalVat,
|
||||||
return src
|
|
||||||
? {
|
|
||||||
id: a.id,
|
|
||||||
name: src.name,
|
|
||||||
code: src.code,
|
|
||||||
detail: src.detail,
|
|
||||||
}
|
|
||||||
: []; // should not be possible to not found.
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
totalPrice,
|
|
||||||
totalDiscount,
|
|
||||||
vatExcluded: 0,
|
vatExcluded: 0,
|
||||||
vat: 0,
|
|
||||||
|
finalPrice: price.totalPrice - price.totalDiscount,
|
||||||
|
|
||||||
paySplit: {
|
paySplit: {
|
||||||
createMany: {
|
createMany: {
|
||||||
data: (rest.paySplit || []).map((v, i) => ({
|
data: (rest.paySplit || []).map((v, i) => ({
|
||||||
|
|
@ -348,15 +396,83 @@ export class QuotationController extends Controller {
|
||||||
})),
|
})),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
createdByUserId: req.user.sub,
|
||||||
|
updatedByUserId: req.user.sub,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
// await tx.quotationServiceWork.createMany({
|
|
||||||
// data: service.flatMap((a) =>
|
|
||||||
// a.work.map((b) => ({ id: b.id, order: b.order, name: b.name, serviceId: a.id })),
|
|
||||||
// ),
|
|
||||||
// });
|
|
||||||
|
|
||||||
throw new Error("Test Quotation Structure");
|
await Promise.all(
|
||||||
|
restructureService.map(async (a) => {
|
||||||
|
await tx.quotationService.create({
|
||||||
|
data: {
|
||||||
|
id: a.id,
|
||||||
|
code: a.code,
|
||||||
|
name: a.name,
|
||||||
|
detail: a.detail,
|
||||||
|
attributes: a.attributes,
|
||||||
|
quotationId: quotation.id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await Promise.all(
|
||||||
|
a.work.map(async (b) => {
|
||||||
|
await tx.quotationServiceWork.create({
|
||||||
|
data: {
|
||||||
|
id: b.id,
|
||||||
|
order: b.order,
|
||||||
|
name: b.name,
|
||||||
|
attributes: b.attributes,
|
||||||
|
serviceId: a.id,
|
||||||
|
productOnWork: {
|
||||||
|
createMany: {
|
||||||
|
data: b.product.map((v, i) => ({
|
||||||
|
productId: v.id,
|
||||||
|
order: i + 1,
|
||||||
|
vat: v.vat,
|
||||||
|
amount: v.amount,
|
||||||
|
discount: v.discount,
|
||||||
|
pricePerUnit: v.pricePerUnit,
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
const result = await tx.quotation.findUnique({
|
||||||
|
include: {
|
||||||
|
service: {
|
||||||
|
include: {
|
||||||
|
work: {
|
||||||
|
include: {
|
||||||
|
productOnWork: {
|
||||||
|
include: { product: true },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
paySplit: true,
|
||||||
|
worker: true,
|
||||||
|
customerBranch: {
|
||||||
|
include: { customer: true },
|
||||||
|
},
|
||||||
|
_count: {
|
||||||
|
select: { service: true },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
where: { id: quotation.id },
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(JSON.stringify(result, null, 2));
|
||||||
|
|
||||||
|
// console.log("Re-Structure:", JSON.stringify(restructureService, null, 2));
|
||||||
|
// console.log("Price:", JSON.stringify(price, null, 2));
|
||||||
|
throw new Error("");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue