feat: update quotation endpoints with permission
This commit is contained in:
parent
3ee1f5ed0f
commit
56c2f1d5ed
3 changed files with 158 additions and 128 deletions
|
|
@ -0,0 +1,25 @@
|
||||||
|
/*
|
||||||
|
Warnings:
|
||||||
|
|
||||||
|
- Added the required column `actorName` to the `Quotation` table without a default value. This is not possible if the table is not empty.
|
||||||
|
- Added the required column `contactName` to the `Quotation` table without a default value. This is not possible if the table is not empty.
|
||||||
|
- Added the required column `contactTel` to the `Quotation` table without a default value. This is not possible if the table is not empty.
|
||||||
|
- Added the required column `documentReceivePoint` to the `Quotation` table without a default value. This is not possible if the table is not empty.
|
||||||
|
- Added the required column `dueDate` to the `Quotation` table without a default value. This is not possible if the table is not empty.
|
||||||
|
- Added the required column `workName` to the `Quotation` table without a default value. This is not possible if the table is not empty.
|
||||||
|
|
||||||
|
*/
|
||||||
|
-- DropForeignKey
|
||||||
|
ALTER TABLE "Quotation" DROP CONSTRAINT "Quotation_customerId_fkey";
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "Quotation" ADD COLUMN "actorName" TEXT NOT NULL,
|
||||||
|
ADD COLUMN "contactName" TEXT NOT NULL,
|
||||||
|
ADD COLUMN "contactTel" TEXT NOT NULL,
|
||||||
|
ADD COLUMN "documentReceivePoint" TEXT NOT NULL,
|
||||||
|
ADD COLUMN "dueDate" DATE NOT NULL,
|
||||||
|
ADD COLUMN "workName" TEXT NOT NULL,
|
||||||
|
ALTER COLUMN "customerId" DROP NOT NULL;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "Quotation" ADD CONSTRAINT "Quotation_customerId_fkey" FOREIGN KEY ("customerId") REFERENCES "Customer"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||||
|
|
@ -1020,16 +1020,21 @@ enum PayCondition {
|
||||||
model Quotation {
|
model Quotation {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
|
|
||||||
customerId String
|
|
||||||
customer Customer @relation(fields: [customerId], references: [id])
|
|
||||||
|
|
||||||
customerBranchId String
|
customerBranchId String
|
||||||
customerBranch CustomerBranch @relation(fields: [customerBranchId], references: [id])
|
customerBranch CustomerBranch @relation(fields: [customerBranchId], references: [id])
|
||||||
|
|
||||||
status Status @default(CREATED)
|
status Status @default(CREATED)
|
||||||
statusOrder Int @default(0)
|
statusOrder Int @default(0)
|
||||||
|
|
||||||
code String
|
code String
|
||||||
|
|
||||||
|
actorName String
|
||||||
|
workName String
|
||||||
|
contactName String
|
||||||
|
contactTel String
|
||||||
|
documentReceivePoint String
|
||||||
|
dueDate DateTime @db.Date
|
||||||
|
|
||||||
date DateTime @default(now())
|
date DateTime @default(now())
|
||||||
payCondition PayCondition
|
payCondition PayCondition
|
||||||
|
|
||||||
|
|
@ -1051,12 +1056,14 @@ model Quotation {
|
||||||
vatExcluded Float
|
vatExcluded Float
|
||||||
finalPrice Float
|
finalPrice Float
|
||||||
|
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
createdBy User? @relation(name: "QuotationCreatedByUser", fields: [createdByUserId], references: [id], onDelete: SetNull)
|
createdBy User? @relation(name: "QuotationCreatedByUser", fields: [createdByUserId], references: [id], onDelete: SetNull)
|
||||||
createdByUserId String?
|
createdByUserId String?
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
updatedBy User? @relation(name: "QuotationUpdatedByUser", fields: [updatedByUserId], references: [id], onDelete: SetNull)
|
updatedBy User? @relation(name: "QuotationUpdatedByUser", fields: [updatedByUserId], references: [id], onDelete: SetNull)
|
||||||
updatedByUserId String?
|
updatedByUserId String?
|
||||||
|
Customer Customer? @relation(fields: [customerId], references: [id])
|
||||||
|
customerId String?
|
||||||
}
|
}
|
||||||
|
|
||||||
model QuotationPaySplit {
|
model QuotationPaySplit {
|
||||||
|
|
|
||||||
|
|
@ -17,10 +17,24 @@ import { RequestWithUser } from "../interfaces/user";
|
||||||
import prisma from "../db";
|
import prisma from "../db";
|
||||||
import HttpError from "../interfaces/http-error";
|
import HttpError from "../interfaces/http-error";
|
||||||
import HttpStatus from "../interfaces/http-status";
|
import HttpStatus from "../interfaces/http-status";
|
||||||
|
import {
|
||||||
|
branchRelationPermInclude,
|
||||||
|
createPermCheck,
|
||||||
|
createPermCondition,
|
||||||
|
} from "../services/permission";
|
||||||
|
import { isSystem } from "../utils/keycloak";
|
||||||
|
import { notFoundError, relationError } from "../utils/error";
|
||||||
|
|
||||||
type QuotationCreate = {
|
type QuotationCreate = {
|
||||||
status?: Status;
|
status?: Status;
|
||||||
|
|
||||||
|
actorName: string;
|
||||||
|
workName: string;
|
||||||
|
contactName: string;
|
||||||
|
contactTel: string;
|
||||||
|
documentReceivePoint: string;
|
||||||
|
dueDate: Date;
|
||||||
|
|
||||||
payCondition: PayCondition;
|
payCondition: PayCondition;
|
||||||
|
|
||||||
paySplitCount?: number;
|
paySplitCount?: number;
|
||||||
|
|
@ -47,7 +61,6 @@ type QuotationCreate = {
|
||||||
)[];
|
)[];
|
||||||
|
|
||||||
customerBranchId: string;
|
customerBranchId: string;
|
||||||
customerId: string;
|
|
||||||
|
|
||||||
urgent?: boolean;
|
urgent?: boolean;
|
||||||
|
|
||||||
|
|
@ -79,6 +92,13 @@ type QuotationCreate = {
|
||||||
type QuotationUpdate = {
|
type QuotationUpdate = {
|
||||||
status?: "ACTIVE" | "INACTIVE";
|
status?: "ACTIVE" | "INACTIVE";
|
||||||
|
|
||||||
|
actorName?: string;
|
||||||
|
workName?: string;
|
||||||
|
contactName?: string;
|
||||||
|
contactTel?: string;
|
||||||
|
documentReceivePoint?: string;
|
||||||
|
dueDate?: Date;
|
||||||
|
|
||||||
payCondition?: PayCondition;
|
payCondition?: PayCondition;
|
||||||
|
|
||||||
paySplitCount?: number;
|
paySplitCount?: number;
|
||||||
|
|
@ -106,7 +126,6 @@ type QuotationUpdate = {
|
||||||
)[];
|
)[];
|
||||||
|
|
||||||
customerBranchId?: string;
|
customerBranchId?: string;
|
||||||
customerId?: string;
|
|
||||||
|
|
||||||
urgent?: boolean;
|
urgent?: boolean;
|
||||||
|
|
||||||
|
|
@ -142,24 +161,40 @@ const MANAGE_ROLES = [
|
||||||
"system",
|
"system",
|
||||||
"head_of_admin",
|
"head_of_admin",
|
||||||
"admin",
|
"admin",
|
||||||
"branch_manager",
|
|
||||||
"head_of_account",
|
"head_of_account",
|
||||||
"account",
|
"account",
|
||||||
|
"head_of_sale",
|
||||||
|
"sale",
|
||||||
];
|
];
|
||||||
|
const VAT_DEFAULT = 0.07;
|
||||||
|
|
||||||
function globalAllow(user: RequestWithUser["user"]) {
|
function globalAllow(user: RequestWithUser["user"]) {
|
||||||
const allowList = ["system", "head_of_admin", "admin", "branch_manager", "head_of_account"];
|
const allowList = ["system", "head_of_admin", "head_of_account", "head_of_sale"];
|
||||||
return allowList.some((v) => user.roles?.includes(v));
|
return allowList.some((v) => user.roles?.includes(v));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const permissionCheck = createPermCheck(globalAllow);
|
||||||
|
const permissionCond = createPermCondition(globalAllow);
|
||||||
|
|
||||||
@Route("/api/v1/quotation")
|
@Route("/api/v1/quotation")
|
||||||
@Tags("Quotation")
|
@Tags("Quotation")
|
||||||
export class QuotationController extends Controller {
|
export class QuotationController extends Controller {
|
||||||
@Get()
|
@Get()
|
||||||
@Security("keycloak")
|
@Security("keycloak")
|
||||||
async getQuotationList(@Query() page: number = 1, @Query() pageSize: number = 30) {
|
async getQuotationList(
|
||||||
|
@Request() req: RequestWithUser,
|
||||||
|
@Query() page: number = 1,
|
||||||
|
@Query() pageSize: number = 30,
|
||||||
|
) {
|
||||||
const [result, total] = await prisma.$transaction([
|
const [result, total] = await prisma.$transaction([
|
||||||
prisma.quotation.findMany({
|
prisma.quotation.findMany({
|
||||||
|
where: {
|
||||||
|
customerBranch: {
|
||||||
|
customer: {
|
||||||
|
registeredBranch: isSystem(req.user) ? undefined : { OR: permissionCond(req.user) },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
include: {
|
include: {
|
||||||
worker: true,
|
worker: true,
|
||||||
service: {
|
service: {
|
||||||
|
|
@ -168,9 +203,6 @@ export class QuotationController extends Controller {
|
||||||
work: {
|
work: {
|
||||||
include: {
|
include: {
|
||||||
_count: { select: { productOnWork: true } },
|
_count: { select: { productOnWork: true } },
|
||||||
productOnWork: {
|
|
||||||
include: { product: true },
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -206,9 +238,7 @@ export class QuotationController extends Controller {
|
||||||
where: { id: quotationId },
|
where: { id: quotationId },
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!record) {
|
if (!record) throw notFoundError("Quotation");
|
||||||
throw new HttpError(HttpStatus.NOT_FOUND, "Quotation not found.", "quotationNotFound");
|
|
||||||
}
|
|
||||||
|
|
||||||
return record;
|
return record;
|
||||||
}
|
}
|
||||||
|
|
@ -222,12 +252,17 @@ export class QuotationController extends Controller {
|
||||||
a.work.flatMap((b) => b.product.map((c) => c.id)),
|
a.work.flatMap((b) => b.product.map((c) => c.id)),
|
||||||
);
|
);
|
||||||
|
|
||||||
const [customer, customerBranch, employee, service, product] = await prisma.$transaction([
|
const [customerBranch, employee, service, product] = await prisma.$transaction([
|
||||||
prisma.customer.findUnique({
|
|
||||||
where: { id: body.customerId },
|
|
||||||
}),
|
|
||||||
prisma.customerBranch.findUnique({
|
prisma.customerBranch.findUnique({
|
||||||
include: { customer: true },
|
include: {
|
||||||
|
customer: {
|
||||||
|
include: {
|
||||||
|
registeredBranch: {
|
||||||
|
include: branchRelationPermInclude(req.user),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
where: { id: body.customerBranchId },
|
where: { id: body.customerBranchId },
|
||||||
}),
|
}),
|
||||||
prisma.employee.findMany({
|
prisma.employee.findMany({
|
||||||
|
|
@ -242,45 +277,12 @@ export class QuotationController extends Controller {
|
||||||
}),
|
}),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if (serviceIdList.length !== service.length) {
|
if (serviceIdList.length !== service.length) throw relationError("Service");
|
||||||
throw new HttpError(
|
if (productIdList.length !== product.length) throw relationError("Product");
|
||||||
HttpStatus.BAD_REQUEST,
|
if (existingEmployee.length !== employee.length) throw relationError("Worker");
|
||||||
"Some service cannot be found.",
|
if (!customerBranch) throw relationError("Customer Branch");
|
||||||
"relationServiceNotFound",
|
|
||||||
);
|
await permissionCheck(req.user, customerBranch.customer.registeredBranch);
|
||||||
}
|
|
||||||
if (productIdList.length !== product.length) {
|
|
||||||
throw new HttpError(
|
|
||||||
HttpStatus.BAD_REQUEST,
|
|
||||||
"Some product cannot be found.",
|
|
||||||
"relationProductNotFound",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (existingEmployee.length !== employee.length) {
|
|
||||||
throw new HttpError(
|
|
||||||
HttpStatus.BAD_REQUEST,
|
|
||||||
"Some worker(employee) cannot be found.",
|
|
||||||
"relationWorkerNotFound",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (!customer)
|
|
||||||
throw new HttpError(
|
|
||||||
HttpStatus.BAD_REQUEST,
|
|
||||||
"Customer cannot be found.",
|
|
||||||
"relationCustomerNotFound",
|
|
||||||
);
|
|
||||||
if (!customerBranch)
|
|
||||||
throw new HttpError(
|
|
||||||
HttpStatus.BAD_REQUEST,
|
|
||||||
"Customer Branch cannot be found.",
|
|
||||||
"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;
|
||||||
|
|
||||||
|
|
@ -292,7 +294,7 @@ export class QuotationController extends Controller {
|
||||||
},
|
},
|
||||||
create: {
|
create: {
|
||||||
key: `EMPLOYEE_${customerBranch.code}-${`${new Date().getFullYear()}`.slice(-2).padStart(2, "0")}`,
|
key: `EMPLOYEE_${customerBranch.code}-${`${new Date().getFullYear()}`.slice(-2).padStart(2, "0")}`,
|
||||||
value: 1,
|
value: nonExistEmployee.length,
|
||||||
},
|
},
|
||||||
update: { value: { increment: nonExistEmployee.length } },
|
update: { value: { increment: nonExistEmployee.length } },
|
||||||
});
|
});
|
||||||
|
|
@ -301,7 +303,7 @@ export class QuotationController extends Controller {
|
||||||
tx.employee.create({
|
tx.employee.create({
|
||||||
data: {
|
data: {
|
||||||
...v,
|
...v,
|
||||||
code: `${customerBranch.code}-${`${new Date().getFullYear()}`.slice(-2).padStart(2, "0")}${`${lastEmployee.value + i}`.padStart(7, "0")}`,
|
code: `${customerBranch.code}-${`${new Date().getFullYear()}`.slice(-2).padStart(2, "0")}${`${lastEmployee.value - nonExistEmployee.length + i + 1}`.padStart(7, "0")}`,
|
||||||
customerBranchId: customerBranch.id,
|
customerBranchId: customerBranch.id,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
|
@ -355,13 +357,13 @@ export class QuotationController extends Controller {
|
||||||
Math.round(
|
Math.round(
|
||||||
(currentProduct.price * e.amount -
|
(currentProduct.price * e.amount -
|
||||||
currentProduct.price * e.amount * e.discount) *
|
currentProduct.price * e.amount * e.discount) *
|
||||||
(e.vat === undefined ? 0.07 : e.vat) *
|
(e.vat === undefined ? VAT_DEFAULT : e.vat) *
|
||||||
100,
|
100,
|
||||||
) / 100;
|
) / 100;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...e,
|
...e,
|
||||||
vat: e.vat === undefined ? 0.07 : e.vat,
|
vat: e.vat === undefined ? VAT_DEFAULT : e.vat,
|
||||||
pricePerUnit: currentProduct.price,
|
pricePerUnit: currentProduct.price,
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
|
|
@ -477,13 +479,21 @@ export class QuotationController extends Controller {
|
||||||
@Body() body: QuotationUpdate,
|
@Body() body: QuotationUpdate,
|
||||||
) {
|
) {
|
||||||
const record = await prisma.quotation.findUnique({
|
const record = await prisma.quotation.findUnique({
|
||||||
include: { customer: true },
|
include: {
|
||||||
|
customerBranch: {
|
||||||
|
include: {
|
||||||
|
customer: {
|
||||||
|
include: {
|
||||||
|
registeredBranch: { include: branchRelationPermInclude(req.user) },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
where: { id: quotationId },
|
where: { id: quotationId },
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!record) {
|
if (!record) throw notFoundError("Quotation");
|
||||||
throw new HttpError(HttpStatus.NOT_FOUND, "Quotation not found.", "quotationNotFound");
|
|
||||||
}
|
|
||||||
|
|
||||||
const existingEmployee = body.worker?.filter((v) => typeof v === "string");
|
const existingEmployee = body.worker?.filter((v) => typeof v === "string");
|
||||||
const serviceIdList = body.service?.map((v) => v.id);
|
const serviceIdList = body.service?.map((v) => v.id);
|
||||||
|
|
@ -491,16 +501,21 @@ export class QuotationController extends Controller {
|
||||||
a.work.flatMap((b) => b.product.map((c) => c.id)),
|
a.work.flatMap((b) => b.product.map((c) => c.id)),
|
||||||
);
|
);
|
||||||
|
|
||||||
const [customer, customerBranch, employee, service, product] = await prisma.$transaction(
|
const [customerBranch, employee, service, product] = await prisma.$transaction(
|
||||||
async (tx) =>
|
async (tx) =>
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
tx.customer.findFirst({
|
body.customerBranchId
|
||||||
where: { id: body.customerId },
|
? tx.customerBranch.findFirst({
|
||||||
}),
|
include: {
|
||||||
tx.customerBranch.findFirst({
|
customer: {
|
||||||
include: { customer: true },
|
include: {
|
||||||
where: { id: body.customerBranchId },
|
registeredBranch: { include: branchRelationPermInclude(req.user) },
|
||||||
}),
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
where: { id: body.customerBranchId },
|
||||||
|
})
|
||||||
|
: null,
|
||||||
body.worker
|
body.worker
|
||||||
? tx.employee.findMany({
|
? tx.employee.findMany({
|
||||||
where: { id: { in: existingEmployee } },
|
where: { id: { in: existingEmployee } },
|
||||||
|
|
@ -520,59 +535,32 @@ export class QuotationController extends Controller {
|
||||||
]),
|
]),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (serviceIdList?.length !== service?.length) {
|
if (serviceIdList?.length !== service?.length) throw relationError("Service");
|
||||||
throw new HttpError(
|
if (productIdList?.length !== product?.length) throw relationError("Product");
|
||||||
HttpStatus.BAD_REQUEST,
|
if (existingEmployee?.length !== employee?.length) throw relationError("Worker");
|
||||||
"Some service cannot be found.",
|
if (body.customerBranchId && !customerBranch) throw relationError("Customer Branch");
|
||||||
"relationServiceNotFound",
|
|
||||||
);
|
await permissionCheck(req.user, record.customerBranch.customer.registeredBranch);
|
||||||
|
if (customerBranch && record.customerBranchId !== body.customerBranchId) {
|
||||||
|
await permissionCheck(req.user, customerBranch.customer.registeredBranch);
|
||||||
}
|
}
|
||||||
if (productIdList?.length !== product?.length) {
|
|
||||||
throw new HttpError(
|
|
||||||
HttpStatus.BAD_REQUEST,
|
|
||||||
"Some product cannot be found.",
|
|
||||||
"relationProductNotFound",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (existingEmployee?.length !== employee?.length) {
|
|
||||||
throw new HttpError(
|
|
||||||
HttpStatus.BAD_REQUEST,
|
|
||||||
"Some worker(employee) cannot be found.",
|
|
||||||
"relationWorkerNotFound",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (!customer)
|
|
||||||
throw new HttpError(
|
|
||||||
HttpStatus.BAD_REQUEST,
|
|
||||||
"Customer cannot be found.",
|
|
||||||
"relationCustomerNotFound",
|
|
||||||
);
|
|
||||||
if (!customerBranch)
|
|
||||||
throw new HttpError(
|
|
||||||
HttpStatus.BAD_REQUEST,
|
|
||||||
"Customer Branch cannot be found.",
|
|
||||||
"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;
|
||||||
|
|
||||||
return await prisma.$transaction(async (tx) => {
|
return await prisma.$transaction(async (tx) => {
|
||||||
const sortedEmployeeId: string[] = [];
|
const sortedEmployeeId: string[] = [];
|
||||||
|
|
||||||
|
const branchId = (customerBranch || record.customerBranch).id;
|
||||||
|
const branchCode = (customerBranch || record.customerBranch).code;
|
||||||
|
|
||||||
if (body.worker) {
|
if (body.worker) {
|
||||||
const nonExistEmployee = body.worker.filter((v) => typeof v !== "string");
|
const nonExistEmployee = body.worker.filter((v) => typeof v !== "string");
|
||||||
const lastEmployee = await tx.runningNo.upsert({
|
const lastEmployee = await tx.runningNo.upsert({
|
||||||
where: {
|
where: {
|
||||||
key: `EMPLOYEE_${customerBranch.code}-${`${new Date().getFullYear()}`.slice(-2).padStart(2, "0")}`,
|
key: `EMPLOYEE_${branchCode}-${`${new Date().getFullYear()}`.slice(-2).padStart(2, "0")}`,
|
||||||
},
|
},
|
||||||
create: {
|
create: {
|
||||||
key: `EMPLOYEE_${customerBranch.code}-${`${new Date().getFullYear()}`.slice(-2).padStart(2, "0")}`,
|
key: `EMPLOYEE_${branchCode}-${`${new Date().getFullYear()}`.slice(-2).padStart(2, "0")}`,
|
||||||
value: 1,
|
value: 1,
|
||||||
},
|
},
|
||||||
update: { value: { increment: nonExistEmployee.length } },
|
update: { value: { increment: nonExistEmployee.length } },
|
||||||
|
|
@ -582,8 +570,8 @@ export class QuotationController extends Controller {
|
||||||
tx.employee.create({
|
tx.employee.create({
|
||||||
data: {
|
data: {
|
||||||
...v,
|
...v,
|
||||||
code: `${customerBranch.code}-${`${new Date().getFullYear()}`.slice(-2).padStart(2, "0")}${`${lastEmployee.value - nonExistEmployee.length + i + 1}`.padStart(7, "0")}`,
|
code: `${branchCode}-${`${new Date().getFullYear()}`.slice(-2).padStart(2, "0")}${`${lastEmployee.value - nonExistEmployee.length + i + 1}`.padStart(7, "0")}`,
|
||||||
customerBranchId: customerBranch.id,
|
customerBranchId: branchId,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
|
|
@ -636,13 +624,13 @@ export class QuotationController extends Controller {
|
||||||
Math.round(
|
Math.round(
|
||||||
(currentProduct.price * e.amount -
|
(currentProduct.price * e.amount -
|
||||||
currentProduct.price * e.amount * e.discount) *
|
currentProduct.price * e.amount * e.discount) *
|
||||||
(e.vat === undefined ? 0.07 : e.vat) *
|
(e.vat === undefined ? VAT_DEFAULT : e.vat) *
|
||||||
100,
|
100,
|
||||||
) / 100;
|
) / 100;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...e,
|
...e,
|
||||||
vat: e.vat === undefined ? 0.07 : e.vat,
|
vat: e.vat === undefined ? VAT_DEFAULT : e.vat,
|
||||||
pricePerUnit: currentProduct.price,
|
pricePerUnit: currentProduct.price,
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
|
|
@ -677,7 +665,6 @@ export class QuotationController extends Controller {
|
||||||
data: {
|
data: {
|
||||||
...rest,
|
...rest,
|
||||||
statusOrder: +(rest.status === "INACTIVE"),
|
statusOrder: +(rest.status === "INACTIVE"),
|
||||||
code: "",
|
|
||||||
worker:
|
worker:
|
||||||
sortedEmployeeId.length > 0
|
sortedEmployeeId.length > 0
|
||||||
? {
|
? {
|
||||||
|
|
@ -753,14 +740,25 @@ export class QuotationController extends Controller {
|
||||||
|
|
||||||
@Delete("{quotationId}")
|
@Delete("{quotationId}")
|
||||||
@Security("keycloak", MANAGE_ROLES)
|
@Security("keycloak", MANAGE_ROLES)
|
||||||
async deleteQuotationById(@Path() quotationId: string) {
|
async deleteQuotationById(@Request() req: RequestWithUser, @Path() quotationId: string) {
|
||||||
const record = await prisma.quotation.findUnique({
|
const record = await prisma.quotation.findUnique({
|
||||||
|
include: {
|
||||||
|
customerBranch: {
|
||||||
|
include: {
|
||||||
|
customer: {
|
||||||
|
include: {
|
||||||
|
registeredBranch: { include: branchRelationPermInclude(req.user) },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
where: { id: quotationId },
|
where: { id: quotationId },
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!record) {
|
if (!record) throw notFoundError("Quotation");
|
||||||
throw new HttpError(HttpStatus.NOT_FOUND, "Quotation not found.", "quotationNotFound");
|
|
||||||
}
|
await permissionCheck(req.user, record.customerBranch.customer.registeredBranch);
|
||||||
|
|
||||||
if (record.status !== Status.CREATED) {
|
if (record.status !== Status.CREATED) {
|
||||||
throw new HttpError(HttpStatus.FORBIDDEN, "Quotation is in used.", "quotationInUsed");
|
throw new HttpError(HttpStatus.FORBIDDEN, "Quotation is in used.", "quotationInUsed");
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue