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
agentPrice Float
serviceCharge Float
vatIncluded Boolean?
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)
statusOrder Int @default(0)

View file

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

View file

@ -478,9 +478,11 @@ export class QuotationController extends Controller {
const list = body.productServiceList.map((v, i) => {
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 finalPriceWithVat = precisionRound(
originalPrice + (p.vatIncluded ? 0 : originalPrice * VAT_DEFAULT),
originalPrice + (vatIncluded ? 0 : originalPrice * VAT_DEFAULT),
);
const price = finalPriceWithVat;
@ -743,9 +745,11 @@ export class QuotationController extends Controller {
const list = body.productServiceList?.map((v, i) => {
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 finalPriceWithVat = precisionRound(
originalPrice + (p.vatIncluded ? 0 : originalPrice * VAT_DEFAULT),
originalPrice + (vatIncluded ? 0 : originalPrice * VAT_DEFAULT),
);
const price = finalPriceWithVat;
@ -919,14 +923,63 @@ export class QuotationActionController extends Controller {
@Request() req: RequestWithUser,
@Path() quotationId: string,
@Body()
body: {
workerId: string;
productServiceId: string[];
}[],
body: (
| {
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 = {
employee: body.map((v) => v.workerId),
productService: body
employee: existsEmployee.map((v) => v.workerId),
productService: existsEmployee
.flatMap((v) => v.productServiceId)
.filter((lhs, i, a) => a.findIndex((rhs) => lhs === rhs) === i),
};
@ -936,6 +989,7 @@ export class QuotationActionController extends Controller {
await Promise.all([
tx.quotation.findFirst({
include: {
customerBranch: true,
worker: true,
_count: {
select: {
@ -961,11 +1015,13 @@ export class QuotationActionController extends Controller {
if (ids.productService.length !== productService.length) throw relationError("Product");
if (
quotation._count.worker +
body.filter((lhs) => !quotation.worker.find((rhs) => rhs.employeeId === lhs.workerId))
.length >
existsEmployee.filter(
(lhs) => !quotation.worker.find((rhs) => rhs.employeeId === lhs.workerId),
).length +
newEmployee.length >
(quotation.workerMax || 0)
) {
if (body.length === 0) return;
if (existsEmployee.length === 0) return;
throw new HttpError(
HttpStatus.PRECONDITION_FAILED,
"Worker exceed current quotation max worker.",
@ -974,8 +1030,46 @@ export class QuotationActionController extends Controller {
}
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({
data: body
data: existsEmployee
.filter((lhs) => !quotation.worker.find((rhs) => rhs.employeeId === lhs.workerId))
.flatMap((lhs) =>
lhs.productServiceId.map((rhs) => ({
@ -1006,7 +1100,7 @@ export class QuotationActionController extends Controller {
quotationStatus: QuotationStatus.PaymentSuccess, // NOTE: change back if already complete or canceled
worker: {
createMany: {
data: body
data: rearrange
.filter((lhs) => !quotation.worker.find((rhs) => rhs.employeeId === lhs.workerId))
.map((v, i) => ({
no: quotation._count.worker + i + 1,
@ -1018,7 +1112,7 @@ export class QuotationActionController extends Controller {
quotation.quotationStatus === "PaymentInProcess" ||
quotation.quotationStatus === "PaymentSuccess"
? {
create: body
create: rearrange
.filter(
(lhs) =>
!quotation.worker.find((rhs) => rhs.employeeId === lhs.workerId) &&