Merge branch 'develop'

This commit is contained in:
Methapon2001 2025-01-27 14:29:43 +07:00
commit 06c54780d3
5 changed files with 146 additions and 16 deletions

View file

@ -0,0 +1,17 @@
/*
Warnings:
- Made the column `vatIncluded` on table `Product` required. This step will fail if there are existing NULL values in that column.
*/
-- AlterTable
ALTER TABLE "Product" ADD COLUMN "agentPriceCalcVat" BOOLEAN,
ADD COLUMN "agentPriceVatIncluded" BOOLEAN,
ADD COLUMN "serviceChargeCalcVat" BOOLEAN,
ADD COLUMN "serviceChargeVatIncluded" BOOLEAN,
ALTER COLUMN "vatIncluded" SET NOT NULL,
ALTER COLUMN "vatIncluded" SET DEFAULT true;
UPDATE "Product" SET "agentPriceCalcVat" = "Product"."calcVat" WHERE "agentPriceCalcVat" IS NULL;
UPDATE "Product" SET "agentPriceVatIncluded" = "Product"."vatIncluded" WHERE "agentPriceVatIncluded" IS NULL;
UPDATE "Product" SET "serviceChargeCalcVat" = "Product"."calcVat" WHERE "serviceChargeCalcVat" IS NULL;
UPDATE "Product" SET "serviceChargeVatIncluded" = "Product"."vatIncluded" WHERE "serviceChargeVatIncluded" IS NULL;

View file

@ -0,0 +1,5 @@
-- AlterTable
ALTER TABLE "Product" ALTER COLUMN "agentPriceCalcVat" SET DEFAULT true,
ALTER COLUMN "agentPriceVatIncluded" SET DEFAULT true,
ALTER COLUMN "serviceChargeCalcVat" SET DEFAULT true,
ALTER COLUMN "serviceChargeVatIncluded" SET DEFAULT true;

View file

@ -1103,10 +1103,16 @@ model Product {
price Float price Float
agentPrice Float agentPrice Float
serviceCharge Float serviceCharge Float
vatIncluded Boolean?
expenseType String? expenseType String?
calcVat Boolean @default(true) vatIncluded Boolean @default(true)
calcVat Boolean @default(true)
agentPriceVatIncluded Boolean? @default(true)
agentPriceCalcVat Boolean? @default(true)
serviceChargeVatIncluded Boolean? @default(true)
serviceChargeCalcVat Boolean? @default(true)
status Status @default(CREATED) status Status @default(CREATED)
statusOrder Int @default(0) statusOrder Int @default(0)

View file

@ -58,6 +58,10 @@ type ProductCreate = {
serviceCharge: number; serviceCharge: number;
vatIncluded?: boolean; vatIncluded?: boolean;
calcVat?: boolean; calcVat?: boolean;
agentPriceVatIncluded?: boolean;
agentPriceCalcVat?: boolean;
serviceChargeVatIncluded?: boolean;
serviceChargeCalcVat?: boolean;
expenseType?: string; expenseType?: string;
selectedImage?: string; selectedImage?: string;
shared?: boolean; shared?: boolean;
@ -77,6 +81,10 @@ type ProductUpdate = {
remark?: string; remark?: string;
vatIncluded?: boolean; vatIncluded?: boolean;
calcVat?: boolean; calcVat?: boolean;
agentPriceVatIncluded?: boolean;
agentPriceCalcVat?: boolean;
serviceChargeVatIncluded?: boolean;
serviceChargeCalcVat?: boolean;
expenseType?: string; expenseType?: string;
selectedImage?: string; selectedImage?: string;
shared?: boolean; shared?: boolean;

View file

@ -478,9 +478,11 @@ export class QuotationController extends Controller {
const list = body.productServiceList.map((v, i) => { const list = body.productServiceList.map((v, i) => {
const p = product.find((p) => p.id === v.productId)!; const p = product.find((p) => p.id === v.productId)!;
const vatIncluded = body.agentPrice ? p.agentPriceVatIncluded : p.vatIncluded;
const originalPrice = body.agentPrice ? p.agentPrice : p.price; const originalPrice = body.agentPrice ? p.agentPrice : p.price;
const finalPriceWithVat = precisionRound( const finalPriceWithVat = precisionRound(
originalPrice + (p.vatIncluded ? 0 : originalPrice * VAT_DEFAULT), originalPrice + (vatIncluded ? 0 : originalPrice * VAT_DEFAULT),
); );
const price = finalPriceWithVat; const price = finalPriceWithVat;
@ -743,9 +745,11 @@ export class QuotationController extends Controller {
const list = body.productServiceList?.map((v, i) => { const list = body.productServiceList?.map((v, i) => {
const p = product.find((p) => p.id === v.productId)!; const p = product.find((p) => p.id === v.productId)!;
const vatIncluded = record.agentPrice ? p.agentPriceVatIncluded : p.vatIncluded;
const originalPrice = record.agentPrice ? p.agentPrice : p.price; const originalPrice = record.agentPrice ? p.agentPrice : p.price;
const finalPriceWithVat = precisionRound( const finalPriceWithVat = precisionRound(
originalPrice + (p.vatIncluded ? 0 : originalPrice * VAT_DEFAULT), originalPrice + (vatIncluded ? 0 : originalPrice * VAT_DEFAULT),
); );
const price = finalPriceWithVat; const price = finalPriceWithVat;
@ -919,14 +923,63 @@ export class QuotationActionController extends Controller {
@Request() req: RequestWithUser, @Request() req: RequestWithUser,
@Path() quotationId: string, @Path() quotationId: string,
@Body() @Body()
body: { body: (
workerId: string; | {
productServiceId: string[]; workerId: string;
}[], productServiceId: string[];
}
| {
workerData: {
dateOfBirth: Date;
gender: string;
nationality: string;
namePrefix?: string;
firstName: string;
firstNameEN: string;
middleName?: string;
middleNameEN?: string;
lastName: string;
lastNameEN: string;
};
productServiceId: string[];
}
)[],
) { ) {
const { existsEmployee, newEmployee } = body.reduce<{
existsEmployee: {
workerId: string;
productServiceId: string[];
}[];
newEmployee: {
workerData: {
dateOfBirth: Date;
gender: string;
nationality: string;
namePrefix?: string;
firstName: string;
firstNameEN: string;
middleName?: string;
middleNameEN?: string;
lastName: string;
lastNameEN: string;
};
productServiceId: string[];
}[];
}>(
(acc, current) => {
if ("workerId" in current) {
acc.existsEmployee.push(current);
} else {
acc.newEmployee.push(current);
}
return acc;
},
{ existsEmployee: [], newEmployee: [] },
);
const ids = { const ids = {
employee: body.map((v) => v.workerId), employee: existsEmployee.map((v) => v.workerId),
productService: body productService: existsEmployee
.flatMap((v) => v.productServiceId) .flatMap((v) => v.productServiceId)
.filter((lhs, i, a) => a.findIndex((rhs) => lhs === rhs) === i), .filter((lhs, i, a) => a.findIndex((rhs) => lhs === rhs) === i),
}; };
@ -936,6 +989,7 @@ export class QuotationActionController extends Controller {
await Promise.all([ await Promise.all([
tx.quotation.findFirst({ tx.quotation.findFirst({
include: { include: {
customerBranch: true,
worker: true, worker: true,
_count: { _count: {
select: { select: {
@ -961,11 +1015,13 @@ export class QuotationActionController extends Controller {
if (ids.productService.length !== productService.length) throw relationError("Product"); if (ids.productService.length !== productService.length) throw relationError("Product");
if ( if (
quotation._count.worker + quotation._count.worker +
body.filter((lhs) => !quotation.worker.find((rhs) => rhs.employeeId === lhs.workerId)) existsEmployee.filter(
.length > (lhs) => !quotation.worker.find((rhs) => rhs.employeeId === lhs.workerId),
).length +
newEmployee.length >
(quotation.workerMax || 0) (quotation.workerMax || 0)
) { ) {
if (body.length === 0) return; if (existsEmployee.length === 0) return;
throw new HttpError( throw new HttpError(
HttpStatus.PRECONDITION_FAILED, HttpStatus.PRECONDITION_FAILED,
"Worker exceed current quotation max worker.", "Worker exceed current quotation max worker.",
@ -974,8 +1030,46 @@ export class QuotationActionController extends Controller {
} }
await prisma.$transaction(async (tx) => { await prisma.$transaction(async (tx) => {
const customerBranch = quotation.customerBranch;
const lastEmployee = await tx.runningNo.upsert({
where: {
key: `EMPLOYEE_${customerBranch.id}-${`${new Date().getFullYear()}`.slice(-2).padStart(2, "0")}`,
},
create: {
key: `EMPLOYEE_${customerBranch.id}-${`${new Date().getFullYear()}`.slice(-2).padStart(2, "0")}`,
value: newEmployee.length,
},
update: { value: { increment: newEmployee.length } },
});
const newEmployeeWithId = await Promise.all(
newEmployee.map(async (v, i) => {
const data = await tx.employee.create({
data: {
...v.workerData,
code: `${customerBranch.code}-${`${new Date().getFullYear()}`.slice(-2).padStart(2, "0")}${`${lastEmployee.value - nonExistEmployee.length + i + 1}`.padStart(7, "0")}`,
customerBranchId: customerBranch.id,
},
});
return { workerId: data.id, productServiceId: v.productServiceId };
}),
);
const rearrange: typeof existsEmployee = [];
while (body.length > 0) {
const item = body.shift();
if (item && "workerId" in item) {
rearrange.push(item);
} else {
const popNew = newEmployeeWithId.shift();
popNew && rearrange.push(popNew);
}
}
await tx.quotationProductServiceWorker.createMany({ await tx.quotationProductServiceWorker.createMany({
data: body data: existsEmployee
.filter((lhs) => !quotation.worker.find((rhs) => rhs.employeeId === lhs.workerId)) .filter((lhs) => !quotation.worker.find((rhs) => rhs.employeeId === lhs.workerId))
.flatMap((lhs) => .flatMap((lhs) =>
lhs.productServiceId.map((rhs) => ({ lhs.productServiceId.map((rhs) => ({
@ -1006,7 +1100,7 @@ export class QuotationActionController extends Controller {
quotationStatus: QuotationStatus.PaymentSuccess, // NOTE: change back if already complete or canceled quotationStatus: QuotationStatus.PaymentSuccess, // NOTE: change back if already complete or canceled
worker: { worker: {
createMany: { createMany: {
data: body data: rearrange
.filter((lhs) => !quotation.worker.find((rhs) => rhs.employeeId === lhs.workerId)) .filter((lhs) => !quotation.worker.find((rhs) => rhs.employeeId === lhs.workerId))
.map((v, i) => ({ .map((v, i) => ({
no: quotation._count.worker + i + 1, no: quotation._count.worker + i + 1,
@ -1018,7 +1112,7 @@ export class QuotationActionController extends Controller {
quotation.quotationStatus === "PaymentInProcess" || quotation.quotationStatus === "PaymentInProcess" ||
quotation.quotationStatus === "PaymentSuccess" quotation.quotationStatus === "PaymentSuccess"
? { ? {
create: body create: rearrange
.filter( .filter(
(lhs) => (lhs) =>
!quotation.worker.find((rhs) => rhs.employeeId === lhs.workerId) && !quotation.worker.find((rhs) => rhs.employeeId === lhs.workerId) &&