diff --git a/src/config.json b/src/config.json index 88ff9cb..6835a21 100644 --- a/src/config.json +++ b/src/config.json @@ -1,8 +1,3 @@ { - "branch": { - "maxHeadOfficeBranch": 10 - }, - "personnel": { - "type": ["USER", "MESSENGER", "DELEGATE", "AGENCY"] - } + "vat": 0.07 } diff --git a/src/controllers/05-quotation-controller.ts b/src/controllers/05-quotation-controller.ts index 442552a..2b91d87 100644 --- a/src/controllers/05-quotation-controller.ts +++ b/src/controllers/05-quotation-controller.ts @@ -1,4 +1,5 @@ import { PayCondition, Prisma, Status } from "@prisma/client"; +import config from "../config.json"; import { Body, Controller, @@ -63,6 +64,7 @@ type QuotationCreate = { urgent?: boolean; agentPrice?: boolean; + discount?: number; productServiceList: { serviceId?: string; @@ -74,12 +76,7 @@ type QuotationCreate = { * @minimum 0 */ discount?: number; - pricePerUnit?: number; - /** - * @maximum 1 - * @minimum 0 - */ - vat?: number; + workerIndex?: number[]; }[]; }; @@ -122,6 +119,8 @@ type QuotationUpdate = { urgent?: boolean; + discount?: number; + productServiceList?: { serviceId?: string; workId?: string; @@ -132,15 +131,12 @@ type QuotationUpdate = { * @minimum 0 */ discount: number; - pricePerUnit?: number; - /** - * @maximum 1 - * @minimum 0 - */ - vat?: number; + workerIndex?: number[]; }[]; }; +const VAT_DEFAULT = config.vat; + const MANAGE_ROLES = [ "system", "head_of_admin", @@ -150,7 +146,6 @@ const MANAGE_ROLES = [ "head_of_sale", "sale", ]; -const VAT_DEFAULT = 0.07; function globalAllow(user: RequestWithUser["user"]) { const allowList = ["system", "head_of_admin", "head_of_account", "head_of_sale"]; @@ -322,28 +317,38 @@ export class QuotationController extends Controller { update: { value: { increment: 1 } }, }); - const list = body.productServiceList.map((v, i) => ({ - order: i + 1, - productId: v.productId, - workId: v.workId, - serviceId: v.serviceId, - pricePerUnit: - product.find((p) => p.id === v.productId)?.[body.agentPrice ? "agentPrice" : "price"] || - 0, - amount: v.amount, - discount: v.discount || 0, - vat: v.vat || VAT_DEFAULT, - })); + const list = body.productServiceList.map((v, i) => { + const p = product.find((p) => p.id === v.productId)!; + const price = body.agentPrice ? p.agentPrice : p.price; + const pricePerUnit = p.vatIncluded ? precisionRound(price / 1 + VAT_DEFAULT) : price; + const vat = precisionRound(p.vatIncluded ? price - pricePerUnit : price * VAT_DEFAULT); + + return { + order: i + 1, + productId: v.productId, + workId: v.workId, + serviceId: v.serviceId, + pricePerUnit: + product.find((p) => p.id === v.productId)?.[body.agentPrice ? "agentPrice" : "price"] || + 0, + amount: v.amount, + discount: v.discount || 0, + vat, + worker: { + create: sortedEmployeeId + .filter((_, i) => !v.workerIndex || i in v.workerIndex) + .map((employeeId) => ({ employeeId })), + }, + }; + }); const price = list.reduce( (a, c) => { - const price = precisionRound(c.pricePerUnit * c.amount); - const discount = precisionRound(price - (c.discount || 0)); - const vat = precisionRound((price - discount) * c.vat); + const multiply = precisionRound(c.pricePerUnit * c.amount); - a.totalPrice = precisionRound(a.totalPrice + price); - a.totalDiscount = precisionRound(a.totalDiscount + discount); - a.vat = precisionRound(a.vat + vat); + a.totalPrice = precisionRound(a.totalPrice + multiply); + a.totalDiscount = precisionRound(a.totalDiscount + c.discount); + a.vat = precisionRound(a.vat + c.vat); a.finalPrice = precisionRound(a.totalPrice - a.totalDiscount + a.vat); return a; @@ -352,7 +357,8 @@ export class QuotationController extends Controller { totalPrice: 0, totalDiscount: 0, vat: 0, - finalPrice: 0, + discount: body.discount, + finalPrice: -(body.discount || 0), }, ); @@ -397,7 +403,9 @@ export class QuotationController extends Controller { })), }, }, - productServiceList: { create: list }, + productServiceList: { + create: list, + }, createdByUserId: req.user.sub, updatedByUserId: req.user.sub, }, @@ -517,28 +525,39 @@ export class QuotationController extends Controller { } } - const list = body.productServiceList?.map((v, i) => ({ - order: i + 1, - productId: v.productId, - workId: v.workId, - serviceId: v.serviceId, - pricePerUnit: - product.find((p) => p.id === v.productId)?.[record.agentPrice ? "agentPrice" : "price"] || - 0, - amount: v.amount, - discount: v.discount || 0, - vat: v.vat || VAT_DEFAULT, - })); + const list = body.productServiceList?.map((v, i) => { + const p = product.find((p) => p.id === v.productId)!; + const price = record.agentPrice ? p.agentPrice : p.price; + const pricePerUnit = p.vatIncluded ? precisionRound(price / 1 + VAT_DEFAULT) : price; + const vat = precisionRound(p.vatIncluded ? price - pricePerUnit : price * VAT_DEFAULT); + + return { + order: i + 1, + productId: v.productId, + workId: v.workId, + serviceId: v.serviceId, + pricePerUnit: + product.find((p) => p.id === v.productId)?.[ + record.agentPrice ? "agentPrice" : "price" + ] || 0, + amount: v.amount, + discount: v.discount || 0, + vat, + worker: { + create: sortedEmployeeId + .filter((_, i) => !v.workerIndex || i in v.workerIndex) + .map((employeeId) => ({ employeeId })), + }, + }; + }); const price = list?.reduce( (a, c) => { - const price = precisionRound(c.pricePerUnit * c.amount); - const discount = precisionRound(price - (c.discount || 0)); - const vat = precisionRound((price - discount) * c.vat); + const multiply = precisionRound(c.pricePerUnit * c.amount); - a.totalPrice = precisionRound(a.totalPrice + price); - a.totalDiscount = precisionRound(a.totalDiscount + discount); - a.vat = precisionRound(a.vat + vat); + a.totalPrice = precisionRound(a.totalPrice + multiply); + a.totalDiscount = precisionRound(a.totalDiscount + c.discount); + a.vat = precisionRound(a.vat + c.vat); a.finalPrice = precisionRound(a.totalPrice - a.totalDiscount + a.vat); return a; @@ -547,7 +566,8 @@ export class QuotationController extends Controller { totalPrice: 0, totalDiscount: 0, vat: 0, - finalPrice: 0, + discount: body.discount, + finalPrice: -(body.discount || 0), }, );