From 7e937333dc96241cb6249a99c009db1c8fce3323 Mon Sep 17 00:00:00 2001 From: Kanjana Date: Wed, 9 Apr 2025 09:51:52 +0700 Subject: [PATCH 01/38] add payment attachment --- src/controllers/09-line-controller.ts | 67 +++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/src/controllers/09-line-controller.ts b/src/controllers/09-line-controller.ts index cf8d7f3..93996c7 100644 --- a/src/controllers/09-line-controller.ts +++ b/src/controllers/09-line-controller.ts @@ -1368,3 +1368,70 @@ export class LineQuotationFileController extends Controller { return await deleteFile(fileLocation.quotation.attachment(quotationId, name)); } } + +@Route("api/v1/line/payment/{paymentId}/attachment") +@Tags("Line") +export class PaymentFileLineController extends Controller { + private async checkPermission(_user: RequestWithUser["user"], id: string) { + const data = await prisma.payment.findUnique({ + include: { + invoice: { + include: { + quotation: true, + }, + }, + }, + where: { id }, + }); + console.log("data", data); + + if (!data) throw notFoundError("Payment"); + return { paymentId: id, quotationId: data.invoice.quotationId }; + } + + @Get() + @Security("line") + async listAttachment(@Request() req: RequestWithUser, @Path() paymentId: string) { + console.log("req", req); + + const { quotationId } = await this.checkPermission(req.user, paymentId); + console.log("quotationId", quotationId); + + return await listFile(fileLocation.quotation.payment(quotationId, paymentId)); + } + + @Head("{name}") + async headAttachment( + @Request() req: RequestWithUser, + @Path() paymentId: string, + @Path() name: string, + ) { + const data = await prisma.payment.findUnique({ + where: { id: paymentId }, + include: { invoice: true }, + }); + if (!data) throw notFoundError("Payment"); + return req.res?.redirect( + await getPresigned( + "head", + fileLocation.quotation.payment(data.invoice.quotationId, paymentId, name), + ), + ); + } + + @Get("{name}") + async getAttachment( + @Request() req: RequestWithUser, + @Path() paymentId: string, + @Path() name: string, + ) { + const data = await prisma.payment.findUnique({ + where: { id: paymentId }, + include: { invoice: true }, + }); + if (!data) throw notFoundError("Payment"); + return req.res?.redirect( + await getFile(fileLocation.quotation.payment(data.invoice.quotationId, paymentId, name)), + ); + } +} From 743fde5493abc62bbb53b99a896964a05fc4aff6 Mon Sep 17 00:00:00 2001 From: Kanjana Date: Wed, 9 Apr 2025 11:54:52 +0700 Subject: [PATCH 02/38] add mode: "insensitive" --- src/controllers/00-notification-controller.ts | 4 +- src/controllers/01-branch-controller.ts | 24 ++++----- src/controllers/01-branch-user-controller.ts | 16 +++--- src/controllers/02-user-controller.ts | 12 ++--- .../03-customer-branch-controller.ts | 26 ++++----- src/controllers/03-customer-controller.ts | 16 +++--- src/controllers/03-employee-controller.ts | 20 +++---- .../04-flow-template-controller.ts | 4 +- src/controllers/04-institution-controller.ts | 2 +- src/controllers/04-invoice-controller.ts | 16 +++--- src/controllers/04-product-controller.ts | 4 +- .../04-product-group-controller.ts | 4 +- src/controllers/04-properties-controller.ts | 5 +- src/controllers/04-service-controller.ts | 4 +- src/controllers/05-quotation-controller.ts | 12 ++--- src/controllers/06-request-list-controller.ts | 24 ++++----- src/controllers/07-task-controller.ts | 12 ++--- src/controllers/08-credit-note-controller.ts | 22 ++++---- src/controllers/09-debit-note-controller.ts | 12 ++--- src/controllers/09-line-controller.ts | 53 +++++++++---------- 20 files changed, 145 insertions(+), 147 deletions(-) diff --git a/src/controllers/00-notification-controller.ts b/src/controllers/00-notification-controller.ts index 2bc4bf0..da40b93 100644 --- a/src/controllers/00-notification-controller.ts +++ b/src/controllers/00-notification-controller.ts @@ -36,8 +36,8 @@ export class NotificationController extends Controller { AND: [ { OR: queryOrNot<(typeof where)[]>(query, [ - { title: { contains: query } }, - { detail: { contains: query } }, + { title: { contains: query, mode: "insensitive" } }, + { detail: { contains: query, mode: "insensitive" } }, ]), }, { diff --git a/src/controllers/01-branch-controller.ts b/src/controllers/01-branch-controller.ts index acb204f..f0cc512 100644 --- a/src/controllers/01-branch-controller.ts +++ b/src/controllers/01-branch-controller.ts @@ -265,20 +265,20 @@ export class BranchController extends Controller { }, OR: queryOrNot(query, [ { code: { contains: query, mode: "insensitive" } }, - { nameEN: { contains: query } }, - { name: { contains: query } }, - { email: { contains: query } }, - { telephoneNo: { contains: query } }, + { nameEN: { contains: query, mode: "insensitive" } }, + { name: { contains: query, mode: "insensitive" } }, + { email: { contains: query, mode: "insensitive" } }, + { telephoneNo: { contains: query, mode: "insensitive" } }, ...whereAddressQuery(query), { branch: { some: { OR: [ { code: { contains: query, mode: "insensitive" } }, - { nameEN: { contains: query } }, - { name: { contains: query } }, - { email: { contains: query } }, - { telephoneNo: { contains: query } }, + { nameEN: { contains: query, mode: "insensitive" } }, + { name: { contains: query, mode: "insensitive" } }, + { email: { contains: query, mode: "insensitive" } }, + { telephoneNo: { contains: query, mode: "insensitive" } }, ...whereAddressQuery(query), ], }, @@ -309,10 +309,10 @@ export class BranchController extends Controller { where: { AND: { OR: permissionCond(req.user) }, OR: [ - { nameEN: { contains: query } }, - { name: { contains: query } }, - { email: { contains: query } }, - { telephoneNo: { contains: query } }, + { nameEN: { contains: query, mode: "insensitive" } }, + { name: { contains: query, mode: "insensitive" } }, + { email: { contains: query, mode: "insensitive" } }, + { telephoneNo: { contains: query, mode: "insensitive" } }, ...whereAddressQuery(query), ], }, diff --git a/src/controllers/01-branch-user-controller.ts b/src/controllers/01-branch-user-controller.ts index 2b0ec23..99d77a4 100644 --- a/src/controllers/01-branch-user-controller.ts +++ b/src/controllers/01-branch-user-controller.ts @@ -104,8 +104,8 @@ export class UserBranchController extends Controller { userId, }, OR: queryOrNot(query, [ - { branch: { name: { contains: query } } }, - { branch: { nameEN: { contains: query } } }, + { branch: { name: { contains: query, mode: "insensitive" } } }, + { branch: { nameEN: { contains: query, mode: "insensitive" } } }, ]), } satisfies Prisma.BranchUserWhereInput; @@ -157,12 +157,12 @@ export class BranchUserController extends Controller { branchId, }, OR: [ - { user: { firstName: { contains: query } } }, - { user: { firstNameEN: { contains: query } } }, - { user: { lastName: { contains: query } } }, - { user: { lastNameEN: { contains: query } } }, - { user: { email: { contains: query } } }, - { user: { telephoneNo: { contains: query } } }, + { user: { firstName: { contains: query, mode: "insensitive" } } }, + { user: { firstNameEN: { contains: query, mode: "insensitive" } } }, + { user: { lastName: { contains: query, mode: "insensitive" } } }, + { user: { lastNameEN: { contains: query, mode: "insensitive" } } }, + { user: { email: { contains: query, mode: "insensitive" } } }, + { user: { telephoneNo: { contains: query, mode: "insensitive" } } }, ], } satisfies Prisma.BranchUserWhereInput; diff --git a/src/controllers/02-user-controller.ts b/src/controllers/02-user-controller.ts index f1dca32..7ba34e6 100644 --- a/src/controllers/02-user-controller.ts +++ b/src/controllers/02-user-controller.ts @@ -324,12 +324,12 @@ export class UserController extends Controller { const where = { OR: queryOrNot(query, [ { code: { contains: query, mode: "insensitive" } }, - { firstName: { contains: query } }, - { firstNameEN: { contains: query } }, - { lastName: { contains: query } }, - { lastNameEN: { contains: query } }, - { email: { contains: query } }, - { telephoneNo: { contains: query } }, + { firstName: { contains: query, mode: "insensitive" } }, + { firstNameEN: { contains: query, mode: "insensitive" } }, + { lastName: { contains: query, mode: "insensitive" } }, + { lastNameEN: { contains: query, mode: "insensitive" } }, + { email: { contains: query, mode: "insensitive" } }, + { telephoneNo: { contains: query, mode: "insensitive" } }, ...whereAddressQuery(query), ]), AND: { diff --git a/src/controllers/03-customer-branch-controller.ts b/src/controllers/03-customer-branch-controller.ts index 772c4df..7818bef 100644 --- a/src/controllers/03-customer-branch-controller.ts +++ b/src/controllers/03-customer-branch-controller.ts @@ -198,15 +198,15 @@ export class CustomerBranchController extends Controller { ) { const where = { OR: queryOrNot(query, [ - { customerName: { contains: query } }, - { registerName: { contains: query } }, - { registerNameEN: { contains: query } }, - { email: { contains: query } }, - { code: { contains: query } }, - { firstName: { contains: query } }, - { firstNameEN: { contains: query } }, - { lastName: { contains: query } }, - { lastNameEN: { contains: query } }, + { customerName: { contains: query, mode: "insensitive" } }, + { registerName: { contains: query, mode: "insensitive" } }, + { registerNameEN: { contains: query, mode: "insensitive" } }, + { email: { contains: query, mode: "insensitive" } }, + { code: { contains: query, mode: "insensitive" } }, + { firstName: { contains: query, mode: "insensitive" } }, + { firstNameEN: { contains: query, mode: "insensitive" } }, + { lastName: { contains: query, mode: "insensitive" } }, + { lastNameEN: { contains: query, mode: "insensitive" } }, ...whereAddressQuery(query), ]), AND: { @@ -288,10 +288,10 @@ export class CustomerBranchController extends Controller { ) { const where = { OR: queryOrNot(query, [ - { firstName: { contains: query } }, - { firstNameEN: { contains: query } }, - { lastName: { contains: query } }, - { lastNameEN: { contains: query } }, + { firstName: { contains: query, mode: "insensitive" } }, + { firstNameEN: { contains: query, mode: "insensitive" } }, + { lastName: { contains: query, mode: "insensitive" } }, + { lastNameEN: { contains: query, mode: "insensitive" } }, ...whereAddressQuery(query), ]), AND: { diff --git a/src/controllers/03-customer-controller.ts b/src/controllers/03-customer-controller.ts index bef51e3..8cac3d9 100644 --- a/src/controllers/03-customer-controller.ts +++ b/src/controllers/03-customer-controller.ts @@ -168,14 +168,14 @@ export class CustomerController extends Controller { ) { const where = { OR: queryOrNot(query, [ - { branch: { some: { namePrefix: { contains: query } } } }, - { branch: { some: { customerName: { contains: query } } } }, - { branch: { some: { registerName: { contains: query } } } }, - { branch: { some: { registerNameEN: { contains: query } } } }, - { branch: { some: { firstName: { contains: query } } } }, - { branch: { some: { firstNameEN: { contains: query } } } }, - { branch: { some: { lastName: { contains: query } } } }, - { branch: { some: { lastNameEN: { contains: query } } } }, + { branch: { some: { namePrefix: { contains: query, mode: "insensitive" } } } }, + { branch: { some: { customerName: { contains: query, mode: "insensitive" } } } }, + { branch: { some: { registerName: { contains: query, mode: "insensitive" } } } }, + { branch: { some: { registerNameEN: { contains: query, mode: "insensitive" } } } }, + { branch: { some: { firstName: { contains: query, mode: "insensitive" } } } }, + { branch: { some: { firstNameEN: { contains: query, mode: "insensitive" } } } }, + { branch: { some: { lastName: { contains: query, mode: "insensitive" } } } }, + { branch: { some: { lastNameEN: { contains: query, mode: "insensitive" } } } }, ]), AND: { customerType, diff --git a/src/controllers/03-employee-controller.ts b/src/controllers/03-employee-controller.ts index eb08db7..44629c1 100644 --- a/src/controllers/03-employee-controller.ts +++ b/src/controllers/03-employee-controller.ts @@ -163,13 +163,13 @@ export class EmployeeController extends Controller { OR: queryOrNot(query, [ { employeePassport: { - some: { number: { contains: query } }, + some: { number: { contains: query, mode: "insensitive" } }, }, }, - { firstName: { contains: query } }, - { firstNameEN: { contains: query } }, - { lastName: { contains: query } }, - { lastNameEN: { contains: query } }, + { firstName: { contains: query, mode: "insensitive" } }, + { firstNameEN: { contains: query, mode: "insensitive" } }, + { lastName: { contains: query, mode: "insensitive" } }, + { lastNameEN: { contains: query, mode: "insensitive" } }, ...whereAddressQuery(query), ]), AND: { @@ -252,13 +252,13 @@ export class EmployeeController extends Controller { ...(queryOrNot(query, [ { employeePassport: { - some: { number: { contains: query } }, + some: { number: { contains: query, mode: "insensitive" } }, }, }, - { firstName: { contains: query } }, - { firstNameEN: { contains: query } }, - { lastName: { contains: query } }, - { lastNameEN: { contains: query } }, + { firstName: { contains: query, mode: "insensitive" } }, + { firstNameEN: { contains: query, mode: "insensitive" } }, + { lastName: { contains: query, mode: "insensitive" } }, + { lastNameEN: { contains: query, mode: "insensitive" } }, ...whereAddressQuery(query), ]) ?? []), ...(queryOrNot(!!body, [ diff --git a/src/controllers/04-flow-template-controller.ts b/src/controllers/04-flow-template-controller.ts index 54510d5..0f80d61 100644 --- a/src/controllers/04-flow-template-controller.ts +++ b/src/controllers/04-flow-template-controller.ts @@ -61,10 +61,10 @@ export class FlowTemplateController extends Controller { ) { const where = { OR: queryOrNot(query, [ - { name: { contains: query } }, + { name: { contains: query, mode: "insensitive" } }, { step: { - some: { name: { contains: query } }, + some: { name: { contains: query, mode: "insensitive" } }, }, }, ]), diff --git a/src/controllers/04-institution-controller.ts b/src/controllers/04-institution-controller.ts index 5e21445..76c0fc4 100644 --- a/src/controllers/04-institution-controller.ts +++ b/src/controllers/04-institution-controller.ts @@ -131,7 +131,7 @@ export class InstitutionController extends Controller { ...filterStatus(activeOnly ? Status.ACTIVE : status), group: body?.group ? { in: body.group } : group, OR: queryOrNot(query, [ - { name: { contains: query } }, + { name: { contains: query, mode: "insensitive" } }, { code: { contains: query, mode: "insensitive" } }, ]), } satisfies Prisma.InstitutionWhereInput; diff --git a/src/controllers/04-invoice-controller.ts b/src/controllers/04-invoice-controller.ts index 20dd91a..64efee2 100644 --- a/src/controllers/04-invoice-controller.ts +++ b/src/controllers/04-invoice-controller.ts @@ -99,19 +99,19 @@ export class InvoiceController extends Controller { const where: Prisma.InvoiceWhereInput = { OR: [ { code: { contains: query, mode: "insensitive" } }, - { quotation: { workName: { contains: query } } }, + { quotation: { workName: { contains: query, mode: "insensitive" } } }, { quotation: { customerBranch: { OR: [ { code: { contains: query, mode: "insensitive" } }, - { customerName: { contains: query } }, - { registerName: { contains: query } }, - { registerNameEN: { contains: query } }, - { firstName: { contains: query } }, - { firstNameEN: { contains: query } }, - { lastName: { contains: query } }, - { lastNameEN: { contains: query } }, + { customerName: { contains: query, mode: "insensitive" } }, + { registerName: { contains: query, mode: "insensitive" } }, + { registerNameEN: { contains: query, mode: "insensitive" } }, + { firstName: { contains: query, mode: "insensitive" } }, + { firstNameEN: { contains: query, mode: "insensitive" } }, + { lastName: { contains: query, mode: "insensitive" } }, + { lastNameEN: { contains: query, mode: "insensitive" } }, ], }, }, diff --git a/src/controllers/04-product-controller.ts b/src/controllers/04-product-controller.ts index 3646479..8cb8c3e 100644 --- a/src/controllers/04-product-controller.ts +++ b/src/controllers/04-product-controller.ts @@ -154,8 +154,8 @@ export class ProductController extends Controller { const where = { OR: queryOrNot(query, [ - { name: { contains: query } }, - { detail: { contains: query } }, + { name: { contains: query, mode: "insensitive" } }, + { detail: { contains: query, mode: "insensitive" } }, { code: { contains: query, mode: "insensitive" } }, ]), AND: { diff --git a/src/controllers/04-product-group-controller.ts b/src/controllers/04-product-group-controller.ts index 2fdcb50..ee27265 100644 --- a/src/controllers/04-product-group-controller.ts +++ b/src/controllers/04-product-group-controller.ts @@ -93,8 +93,8 @@ export class ProductGroup extends Controller { ) { const where = { OR: queryOrNot(query, [ - { name: { contains: query } }, - { detail: { contains: query } }, + { name: { contains: query, mode: "insensitive" } }, + { detail: { contains: query, mode: "insensitive" } }, { code: { contains: query, mode: "insensitive" } }, ]), AND: [ diff --git a/src/controllers/04-properties-controller.ts b/src/controllers/04-properties-controller.ts index 46cea13..35f94a8 100644 --- a/src/controllers/04-properties-controller.ts +++ b/src/controllers/04-properties-controller.ts @@ -51,7 +51,10 @@ export class PropertiesController extends Controller { @Query() activeOnly?: boolean, ) { const where = { - OR: queryOrNot(query, [{ name: { contains: query } }, { nameEN: { contains: query } }]), + OR: queryOrNot(query, [ + { name: { contains: query, mode: "insensitive" } }, + { nameEN: { contains: query, mode: "insensitive" } }, + ]), AND: { ...filterStatus(activeOnly ? Status.ACTIVE : status), registeredBranch: { diff --git a/src/controllers/04-service-controller.ts b/src/controllers/04-service-controller.ts index a60c555..c193fbd 100644 --- a/src/controllers/04-service-controller.ts +++ b/src/controllers/04-service-controller.ts @@ -179,8 +179,8 @@ export class ServiceController extends Controller { const where = { OR: queryOrNot(query, [ - { name: { contains: query } }, - { detail: { contains: query } }, + { name: { contains: query, mode: "insensitive" } }, + { detail: { contains: query, mode: "insensitive" } }, { code: { contains: query, mode: "insensitive" } }, ]), AND: { diff --git a/src/controllers/05-quotation-controller.ts b/src/controllers/05-quotation-controller.ts index 0369627..fa3d6b3 100644 --- a/src/controllers/05-quotation-controller.ts +++ b/src/controllers/05-quotation-controller.ts @@ -210,16 +210,16 @@ export class QuotationController extends Controller { const where = { OR: queryOrNot(query, [ { code: { contains: query, mode: "insensitive" } }, - { workName: { contains: query } }, + { workName: { contains: query, mode: "insensitive" } }, { customerBranch: { OR: [ { code: { contains: query, mode: "insensitive" } }, - { customerName: { contains: query } }, - { firstName: { contains: query } }, - { firstNameEN: { contains: query } }, - { lastName: { contains: query } }, - { lastNameEN: { contains: query } }, + { customerName: { contains: query, mode: "insensitive" } }, + { firstName: { contains: query, mode: "insensitive" } }, + { firstNameEN: { contains: query, mode: "insensitive" } }, + { lastName: { contains: query, mode: "insensitive" } }, + { lastNameEN: { contains: query, mode: "insensitive" } }, ], }, }, diff --git a/src/controllers/06-request-list-controller.ts b/src/controllers/06-request-list-controller.ts index 4484ce3..464b5fc 100644 --- a/src/controllers/06-request-list-controller.ts +++ b/src/controllers/06-request-list-controller.ts @@ -91,13 +91,13 @@ export class RequestDataController extends Controller { customerBranch: { OR: [ { code: { contains: query, mode: "insensitive" } }, - { customerName: { contains: query } }, - { registerName: { contains: query } }, - { registerNameEN: { contains: query } }, - { firstName: { contains: query } }, - { firstNameEN: { contains: query } }, - { lastName: { contains: query } }, - { lastNameEN: { contains: query } }, + { customerName: { contains: query, mode: "insensitive" } }, + { registerName: { contains: query, mode: "insensitive" } }, + { registerNameEN: { contains: query, mode: "insensitive" } }, + { firstName: { contains: query, mode: "insensitive" } }, + { firstNameEN: { contains: query, mode: "insensitive" } }, + { lastName: { contains: query, mode: "insensitive" } }, + { lastNameEN: { contains: query, mode: "insensitive" } }, ], }, }, @@ -105,14 +105,14 @@ export class RequestDataController extends Controller { OR: [ { employeePassport: { - some: { number: { contains: query } }, + some: { number: { contains: query, mode: "insensitive" } }, }, }, { code: { contains: query, mode: "insensitive" } }, - { firstName: { contains: query } }, - { firstNameEN: { contains: query } }, - { lastName: { contains: query } }, - { lastNameEN: { contains: query } }, + { firstName: { contains: query, mode: "insensitive" } }, + { firstNameEN: { contains: query, mode: "insensitive" } }, + { lastName: { contains: query, mode: "insensitive" } }, + { lastNameEN: { contains: query, mode: "insensitive" } }, ], }, }, diff --git a/src/controllers/07-task-controller.ts b/src/controllers/07-task-controller.ts index 2da73b2..03009b3 100644 --- a/src/controllers/07-task-controller.ts +++ b/src/controllers/07-task-controller.ts @@ -121,9 +121,9 @@ export class TaskController extends Controller { code: body?.code ? { in: body.code } : undefined, OR: queryOrNot(query, [ { code: { contains: query, mode: "insensitive" } }, - { taskName: { contains: query } }, - { contactName: { contains: query } }, - { contactTel: { contains: query } }, + { taskName: { contains: query, mode: "insensitive" } }, + { contactName: { contains: query, mode: "insensitive" } }, + { contactTel: { contains: query, mode: "insensitive" } }, ]), } satisfies Prisma.TaskOrderWhereInput; @@ -1021,9 +1021,9 @@ export class UserTaskController extends Controller { : undefined, OR: queryOrNot(query, [ { code: { contains: query, mode: "insensitive" } }, - { taskName: { contains: query } }, - { contactName: { contains: query } }, - { contactTel: { contains: query } }, + { taskName: { contains: query, mode: "insensitive" } }, + { contactName: { contains: query, mode: "insensitive" } }, + { contactTel: { contains: query, mode: "insensitive" } }, ]), } satisfies Prisma.TaskOrderWhereInput; diff --git a/src/controllers/08-credit-note-controller.ts b/src/controllers/08-credit-note-controller.ts index e5d2759..5fda3eb 100644 --- a/src/controllers/08-credit-note-controller.ts +++ b/src/controllers/08-credit-note-controller.ts @@ -153,17 +153,17 @@ export class CreditNoteController extends Controller { request: { OR: queryOrNot(query, [ { quotation: { code: { contains: query, mode: "insensitive" } } }, - { quotation: { workName: { contains: query } } }, + { quotation: { workName: { contains: query, mode: "insensitive" } } }, { quotation: { customerBranch: { OR: [ { code: { contains: query, mode: "insensitive" } }, - { customerName: { contains: query } }, - { firstName: { contains: query } }, - { firstNameEN: { contains: query } }, - { lastName: { contains: query } }, - { lastNameEN: { contains: query } }, + { customerName: { contains: query, mode: "insensitive" } }, + { firstName: { contains: query, mode: "insensitive" } }, + { firstNameEN: { contains: query, mode: "insensitive" } }, + { lastName: { contains: query, mode: "insensitive" } }, + { lastNameEN: { contains: query, mode: "insensitive" } }, ], }, }, @@ -171,14 +171,14 @@ export class CreditNoteController extends Controller { OR: [ { employeePassport: { - some: { number: { contains: query } }, + some: { number: { contains: query, mode: "insensitive" } }, }, }, { code: { contains: query, mode: "insensitive" } }, - { firstName: { contains: query } }, - { firstNameEN: { contains: query } }, - { lastName: { contains: query } }, - { lastNameEN: { contains: query } }, + { firstName: { contains: query, mode: "insensitive" } }, + { firstNameEN: { contains: query, mode: "insensitive" } }, + { lastName: { contains: query, mode: "insensitive" } }, + { lastNameEN: { contains: query, mode: "insensitive" } }, ], }, }, diff --git a/src/controllers/09-debit-note-controller.ts b/src/controllers/09-debit-note-controller.ts index bca4057..a499f88 100644 --- a/src/controllers/09-debit-note-controller.ts +++ b/src/controllers/09-debit-note-controller.ts @@ -200,16 +200,16 @@ export class DebitNoteController extends Controller { const where = { OR: queryOrNot(query, [ { code: { contains: query, mode: "insensitive" } }, - { workName: { contains: query } }, + { workName: { contains: query, mode: "insensitive" } }, { customerBranch: { OR: [ { code: { contains: query, mode: "insensitive" } }, - { customerName: { contains: query } }, - { firstName: { contains: query } }, - { firstNameEN: { contains: query } }, - { lastName: { contains: query } }, - { lastNameEN: { contains: query } }, + { customerName: { contains: query, mode: "insensitive" } }, + { firstName: { contains: query, mode: "insensitive" } }, + { firstNameEN: { contains: query, mode: "insensitive" } }, + { lastName: { contains: query, mode: "insensitive" } }, + { lastNameEN: { contains: query, mode: "insensitive" } }, ], }, }, diff --git a/src/controllers/09-line-controller.ts b/src/controllers/09-line-controller.ts index 93996c7..0a6ea3c 100644 --- a/src/controllers/09-line-controller.ts +++ b/src/controllers/09-line-controller.ts @@ -58,13 +58,13 @@ export class LineController extends Controller { ...(queryOrNot(query, [ { employeePassport: { - some: { number: { contains: query } }, + some: { number: { contains: query, mode: "insensitive" } }, }, }, - { firstName: { contains: query } }, - { firstNameEN: { contains: query } }, - { lastName: { contains: query } }, - { lastNameEN: { contains: query } }, + { firstName: { contains: query, mode: "insensitive" } }, + { firstNameEN: { contains: query, mode: "insensitive" } }, + { lastName: { contains: query, mode: "insensitive" } }, + { lastNameEN: { contains: query, mode: "insensitive" } }, ...whereAddressQuery(query), ]) ?? []), ] @@ -178,19 +178,19 @@ export class LineController extends Controller { OR: queryOrNot(query, [ { code: { contains: query, mode: "insensitive" } }, { quotation: { code: { contains: query, mode: "insensitive" } } }, - { quotation: { workName: { contains: query } } }, + { quotation: { workName: { contains: query, mode: "insensitive" } } }, { quotation: { customerBranch: { OR: [ { code: { contains: query, mode: "insensitive" } }, - { customerName: { contains: query } }, - { registerName: { contains: query } }, - { registerNameEN: { contains: query } }, - { firstName: { contains: query } }, - { firstNameEN: { contains: query } }, - { lastName: { contains: query } }, - { lastNameEN: { contains: query } }, + { customerName: { contains: query, mode: "insensitive" } }, + { registerName: { contains: query, mode: "insensitive" } }, + { registerNameEN: { contains: query, mode: "insensitive" } }, + { firstName: { contains: query, mode: "insensitive" } }, + { firstNameEN: { contains: query, mode: "insensitive" } }, + { lastName: { contains: query, mode: "insensitive" } }, + { lastNameEN: { contains: query, mode: "insensitive" } }, ], }, }, @@ -198,14 +198,14 @@ export class LineController extends Controller { OR: [ { employeePassport: { - some: { number: { contains: query } }, + some: { number: { contains: query, mode: "insensitive" } }, }, }, { code: { contains: query, mode: "insensitive" } }, - { firstName: { contains: query } }, - { firstNameEN: { contains: query } }, - { lastName: { contains: query } }, - { lastNameEN: { contains: query } }, + { firstName: { contains: query, mode: "insensitive" } }, + { firstNameEN: { contains: query, mode: "insensitive" } }, + { lastName: { contains: query, mode: "insensitive" } }, + { lastNameEN: { contains: query, mode: "insensitive" } }, ], }, }, @@ -611,16 +611,16 @@ export class LineController extends Controller { ? [ ...(queryOrNot(query, [ { code: { contains: query, mode: "insensitive" } }, - { workName: { contains: query } }, + { workName: { contains: query, mode: "insensitive" } }, { customerBranch: { OR: [ { code: { contains: query, mode: "insensitive" } }, - { customerName: { contains: query } }, - { firstName: { contains: query } }, - { firstNameEN: { contains: query } }, - { lastName: { contains: query } }, - { lastNameEN: { contains: query } }, + { customerName: { contains: query, mode: "insensitive" } }, + { firstName: { contains: query, mode: "insensitive" } }, + { firstNameEN: { contains: query, mode: "insensitive" } }, + { lastName: { contains: query, mode: "insensitive" } }, + { lastNameEN: { contains: query, mode: "insensitive" } }, ], }, }, @@ -1383,7 +1383,6 @@ export class PaymentFileLineController extends Controller { }, where: { id }, }); - console.log("data", data); if (!data) throw notFoundError("Payment"); return { paymentId: id, quotationId: data.invoice.quotationId }; @@ -1392,11 +1391,7 @@ export class PaymentFileLineController extends Controller { @Get() @Security("line") async listAttachment(@Request() req: RequestWithUser, @Path() paymentId: string) { - console.log("req", req); - const { quotationId } = await this.checkPermission(req.user, paymentId); - console.log("quotationId", quotationId); - return await listFile(fileLocation.quotation.payment(quotationId, paymentId)); } From afe54b1a4e7658cce4bca61bd2149df2963ace60 Mon Sep 17 00:00:00 2001 From: Kanjana Date: Wed, 9 Apr 2025 14:05:05 +0700 Subject: [PATCH 03/38] add insensitive search --- src/controllers/00-employment-office-controller.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/controllers/00-employment-office-controller.ts b/src/controllers/00-employment-office-controller.ts index e7efc24..95adc5a 100644 --- a/src/controllers/00-employment-office-controller.ts +++ b/src/controllers/00-employment-office-controller.ts @@ -2,6 +2,7 @@ import { Body, Controller, Get, Path, Post, Query, Route, Tags } from "tsoa"; import prisma from "../db"; import { queryOrNot } from "../utils/relation"; import { notFoundError } from "../utils/error"; +import { Prisma } from "@prisma/client"; @Route("/api/v1/employment-office") @Tags("Employment Office") @@ -40,11 +41,14 @@ export class EmploymentOfficeController extends Controller { ], [], ), - ...queryOrNot( + ...(queryOrNot( query, - [{ name: { contains: query } }, { nameEN: { contains: query } }], + [ + { name: { contains: query, mode: "insensitive" } }, + { nameEN: { contains: query, mode: "insensitive" } }, + ], [], - ), + ) satisfies Prisma.EmploymentOfficeWhereInput["OR"]), ...queryOrNot(!!body?.id, [{ id: { in: body?.id } }], []), ] : undefined, From 2e71c86b363c971753231fdbfe7cb94f3284cf7b Mon Sep 17 00:00:00 2001 From: Kanjana Date: Thu, 10 Apr 2025 15:01:18 +0700 Subject: [PATCH 04/38] add incomplete in RequestData --- src/controllers/06-request-list-controller.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/controllers/06-request-list-controller.ts b/src/controllers/06-request-list-controller.ts index 464b5fc..62f594a 100644 --- a/src/controllers/06-request-list-controller.ts +++ b/src/controllers/06-request-list-controller.ts @@ -80,6 +80,7 @@ export class RequestDataController extends Controller { @Query() requestDataStatus?: RequestDataStatus, @Query() quotationId?: string, @Query() code?: string, + @Query() incomplete?: boolean, ) { const where = { OR: queryOrNot(query, [ @@ -118,7 +119,11 @@ export class RequestDataController extends Controller { }, ]), code, - requestDataStatus, + requestDataStatus: incomplete + ? { + notIn: [RequestDataStatus.Completed, RequestDataStatus.Canceled], + } + : requestDataStatus, requestWork: responsibleOnly ? { some: { From 7b28ddd2b2b1a14abc80018f16c66c9e987940d6 Mon Sep 17 00:00:00 2001 From: Kanjana Date: Thu, 10 Apr 2025 15:38:14 +0700 Subject: [PATCH 05/38] change update --- src/controllers/06-request-list-controller.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controllers/06-request-list-controller.ts b/src/controllers/06-request-list-controller.ts index 62f594a..ce0e03f 100644 --- a/src/controllers/06-request-list-controller.ts +++ b/src/controllers/06-request-list-controller.ts @@ -236,7 +236,7 @@ export class RequestDataController extends Controller { return record; } - @Post("updata-messenger") + @Post("update-messenger") @Security("keycloak") async updateRequestData( @Request() req: RequestWithUser, From a06d5514fc44cf82c69b075ec69845f7cdcca5ef Mon Sep 17 00:00:00 2001 From: Kanjana Date: Fri, 11 Apr 2025 11:28:24 +0700 Subject: [PATCH 06/38] updata user add employmentOffice --- .../20250410102415_add/migration.sql | 3 ++ .../20250410104307_change/migration.sql | 3 ++ prisma/schema.prisma | 7 ++-- src/controllers/02-user-controller.ts | 18 ++++++---- src/controllers/06-request-list-controller.ts | 33 ++++++++++++++++++- 5 files changed, 55 insertions(+), 9 deletions(-) create mode 100644 prisma/migrations/20250410102415_add/migration.sql create mode 100644 prisma/migrations/20250410104307_change/migration.sql diff --git a/prisma/migrations/20250410102415_add/migration.sql b/prisma/migrations/20250410102415_add/migration.sql new file mode 100644 index 0000000..eaf0561 --- /dev/null +++ b/prisma/migrations/20250410102415_add/migration.sql @@ -0,0 +1,3 @@ +-- AlterTable +ALTER TABLE "User" ADD COLUMN "contactName" TEXT, +ADD COLUMN "contactTel" TEXT; diff --git a/prisma/migrations/20250410104307_change/migration.sql b/prisma/migrations/20250410104307_change/migration.sql new file mode 100644 index 0000000..3239108 --- /dev/null +++ b/prisma/migrations/20250410104307_change/migration.sql @@ -0,0 +1,3 @@ +-- AlterTable +ALTER TABLE "User" ALTER COLUMN "firstName" DROP NOT NULL, +ALTER COLUMN "lastName" DROP NOT NULL; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index cb38c88..d40957e 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -371,11 +371,11 @@ model User { code String? namePrefix String? - firstName String + firstName String? firstNameEN String middleName String? middleNameEN String? - lastName String + lastName String? lastNameEN String username String gender String @@ -497,6 +497,9 @@ model User { remark String? agencyStatus String? + + contactName String? + contactTel String? } model UserResponsibleArea { diff --git a/src/controllers/02-user-controller.ts b/src/controllers/02-user-controller.ts index 7ba34e6..6639511 100644 --- a/src/controllers/02-user-controller.ts +++ b/src/controllers/02-user-controller.ts @@ -79,11 +79,11 @@ type UserCreate = { citizenExpire?: Date | null; namePrefix?: string | null; - firstName: string; + firstName?: string; firstNameEN: string; middleName?: string | null; middleNameEN?: string | null; - lastName: string; + lastName?: string; lastNameEN: string; gender: string; @@ -123,6 +123,9 @@ type UserCreate = { remark?: string; agencyStatus?: string; + + contactName?: string; + contactTel?: string; }; type UserUpdate = { @@ -139,9 +142,9 @@ type UserUpdate = { namePrefix?: string | null; firstName?: string; - firstNameEN?: string; + firstNameEN: string; middleName?: string | null; - middleNameEN?: string | null; + middleNameEN: string | null; lastName?: string; lastNameEN?: string; gender?: string; @@ -182,6 +185,9 @@ type UserUpdate = { remark?: string; agencyStatus?: string; + + contactName?: string; + contactTel?: string; }; const permissionCondCompany = createPermCondition((_) => true); @@ -477,8 +483,8 @@ export class UserController extends Controller { } const userId = await createUser(username, username, { - firstName: body.firstName, - lastName: body.lastName, + firstName: body.firstNameEN, + lastName: body.lastNameEN, email: body.email, requiredActions: ["UPDATE_PASSWORD"], enabled: rest.status !== "INACTIVE", diff --git a/src/controllers/06-request-list-controller.ts b/src/controllers/06-request-list-controller.ts index ce0e03f..9df79b3 100644 --- a/src/controllers/06-request-list-controller.ts +++ b/src/controllers/06-request-list-controller.ts @@ -187,6 +187,16 @@ export class RequestDataController extends Controller { employeePassport: { orderBy: { expireDate: "desc" }, }, + province: { + include: { + employmentOffice: true, + }, + }, + district: { + include: { + employmentOffice: true, + }, + }, }, }, }, @@ -197,7 +207,28 @@ export class RequestDataController extends Controller { prisma.requestData.count({ where }), ]); - return { result, page, pageSize, total }; + const dataRequestData = result.map((item) => { + const employee = item.employee; + const dataOffice = + item.employee.provinceId === "10" + ? employee.district?.employmentOffice + : employee.province?.employmentOffice; + + return { + ...item, + employee: { + ...employee, + dataOffice, + }, + }; + }); + + return { + result: dataRequestData, + page, + pageSize, + total, + }; } @Get("{requestDataId}") From 62def572de360a598bf76353171392de43edc65d Mon Sep 17 00:00:00 2001 From: Kanjana Date: Fri, 11 Apr 2025 12:06:11 +0700 Subject: [PATCH 07/38] Pull data with CustomerBranch --- src/controllers/06-request-list-controller.ts | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/controllers/06-request-list-controller.ts b/src/controllers/06-request-list-controller.ts index 9df79b3..1a0c7c6 100644 --- a/src/controllers/06-request-list-controller.ts +++ b/src/controllers/06-request-list-controller.ts @@ -187,14 +187,18 @@ export class RequestDataController extends Controller { employeePassport: { orderBy: { expireDate: "desc" }, }, - province: { + customerBranch: { include: { - employmentOffice: true, - }, - }, - district: { - include: { - employmentOffice: true, + province: { + include: { + employmentOffice: true, + }, + }, + district: { + include: { + employmentOffice: true, + }, + }, }, }, }, @@ -210,9 +214,8 @@ export class RequestDataController extends Controller { const dataRequestData = result.map((item) => { const employee = item.employee; const dataOffice = - item.employee.provinceId === "10" - ? employee.district?.employmentOffice - : employee.province?.employmentOffice; + employee.customerBranch.district?.employmentOffice.at(0) ?? + employee.customerBranch.province?.employmentOffice.at(0); return { ...item, From ee610c56867a458acfb17aa8a672cdb0a30d1384 Mon Sep 17 00:00:00 2001 From: Kanjana Date: Fri, 11 Apr 2025 12:50:44 +0700 Subject: [PATCH 08/38] change position --- src/controllers/06-request-list-controller.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/controllers/06-request-list-controller.ts b/src/controllers/06-request-list-controller.ts index 1a0c7c6..e1da5f2 100644 --- a/src/controllers/06-request-list-controller.ts +++ b/src/controllers/06-request-list-controller.ts @@ -219,10 +219,7 @@ export class RequestDataController extends Controller { return { ...item, - employee: { - ...employee, - dataOffice, - }, + dataOffice, }; }); From f2d0c20ece942c271c62cd8b14ea2861c02b5315 Mon Sep 17 00:00:00 2001 From: Methapon2001 <61303214+Methapon2001@users.noreply.github.com> Date: Thu, 17 Apr 2025 12:56:31 +0700 Subject: [PATCH 09/38] feat: add endpoint for get same office district --- .../00-employment-office-controller.ts | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/controllers/00-employment-office-controller.ts b/src/controllers/00-employment-office-controller.ts index 95adc5a..9c6995b 100644 --- a/src/controllers/00-employment-office-controller.ts +++ b/src/controllers/00-employment-office-controller.ts @@ -12,6 +12,39 @@ export class EmploymentOfficeController extends Controller { return this.getEmploymentOfficeListByCriteria(districtId, query); } + @Post("list-same-office-area") + async getSameOfficeArea(@Body() body: { districtId: string }) { + const office = await prisma.employmentOffice.findFirst({ + include: { + province: { + include: { + district: true, + }, + }, + district: true, + }, + where: { + OR: [ + { + province: { district: { some: { id: body.districtId } } }, + district: { none: {} }, + }, + { + district: { + some: { districtId: body.districtId }, + }, + }, + ], + }, + }); + if (!office) return []; + + return [ + ...office.district.map((v) => v.districtId), + ...office.province.district.map((v) => v.id), + ]; + } + @Post("list") async getEmploymentOfficeListByCriteria( @Query() districtId?: string, From 0aba9f9865b9ffd1f3b95051da0e7a5dd6f0726f Mon Sep 17 00:00:00 2001 From: Kanjana Date: Thu, 17 Apr 2025 13:41:22 +0700 Subject: [PATCH 10/38] search startDate and endDate --- src/controllers/01-branch-controller.ts | 5 ++ src/controllers/01-branch-user-controller.ts | 8 +++- src/controllers/02-user-controller.ts | 4 ++ .../03-customer-branch-controller.ts | 7 +++ src/controllers/03-customer-controller.ts | 5 +- src/controllers/03-employee-controller.ts | 7 +++ .../04-flow-template-controller.ts | 5 +- src/controllers/04-invoice-controller.ts | 4 ++ src/controllers/04-product-controller.ts | 5 +- .../04-product-group-controller.ts | 5 +- src/controllers/04-properties-controller.ts | 5 +- src/controllers/04-service-controller.ts | 5 +- src/controllers/04-work-controller.ts | 4 ++ src/controllers/05-quotation-controller.ts | 5 +- src/controllers/06-request-list-controller.ts | 5 +- src/controllers/07-task-controller.ts | 8 +++- src/controllers/08-credit-note-controller.ts | 5 +- src/controllers/09-debit-note-controller.ts | 5 +- src/controllers/09-line-controller.ts | 11 ++++- src/utils/relation.ts | 47 +++++++++++-------- 20 files changed, 123 insertions(+), 32 deletions(-) diff --git a/src/controllers/01-branch-controller.ts b/src/controllers/01-branch-controller.ts index f0cc512..003c4cd 100644 --- a/src/controllers/01-branch-controller.ts +++ b/src/controllers/01-branch-controller.ts @@ -39,6 +39,7 @@ import { connectOrNot, queryOrNot, whereAddressQuery, + whereDateQuery, } from "../utils/relation"; import { isUsedError, notFoundError, relationError } from "../utils/error"; @@ -250,6 +251,8 @@ export class BranchController extends Controller { @Query() query: string = "", @Query() page: number = 1, @Query() pageSize: number = 30, + @Query() startDate?: Date, + @Query() endDate?: Date, ) { const where = { AND: { @@ -285,6 +288,7 @@ export class BranchController extends Controller { }, }, ]), + ...whereDateQuery(startDate, endDate), } satisfies Prisma.BranchWhereInput; const [result, total] = await prisma.$transaction([ @@ -321,6 +325,7 @@ export class BranchController extends Controller { district: true, subDistrict: true, }, + ...whereDateQuery(startDate, endDate), orderBy: { code: "asc" }, } : false, diff --git a/src/controllers/01-branch-user-controller.ts b/src/controllers/01-branch-user-controller.ts index 99d77a4..05177d2 100644 --- a/src/controllers/01-branch-user-controller.ts +++ b/src/controllers/01-branch-user-controller.ts @@ -18,7 +18,7 @@ import HttpError from "../interfaces/http-error"; import HttpStatus from "../interfaces/http-status"; import { RequestWithUser } from "../interfaces/user"; import { branchRelationPermInclude, createPermCheck } from "../services/permission"; -import { queryOrNot } from "../utils/relation"; +import { queryOrNot, whereDateQuery } from "../utils/relation"; const MANAGE_ROLES = ["system", "head_of_admin", "admin", "branch_manager"]; @@ -97,6 +97,8 @@ export class UserBranchController extends Controller { @Query() query: string = "", @Query() page: number = 1, @Query() pageSize: number = 30, + @Query() startDate?: Date, + @Query() endDate?: Date, ) { const where = { AND: { @@ -107,6 +109,7 @@ export class UserBranchController extends Controller { { branch: { name: { contains: query, mode: "insensitive" } } }, { branch: { nameEN: { contains: query, mode: "insensitive" } } }, ]), + ...whereDateQuery(startDate, endDate), } satisfies Prisma.BranchUserWhereInput; const [result, total] = await prisma.$transaction([ @@ -150,6 +153,8 @@ export class BranchUserController extends Controller { @Query() query: string = "", @Query() page: number = 1, @Query() pageSize: number = 30, + @Query() startDate?: Date, + @Query() endDate?: Date, ) { const where = { AND: { @@ -164,6 +169,7 @@ export class BranchUserController extends Controller { { user: { email: { contains: query, mode: "insensitive" } } }, { user: { telephoneNo: { contains: query, mode: "insensitive" } } }, ], + ...whereDateQuery(startDate, endDate), } satisfies Prisma.BranchUserWhereInput; const [result, total] = await prisma.$transaction([ diff --git a/src/controllers/02-user-controller.ts b/src/controllers/02-user-controller.ts index 6639511..17630eb 100644 --- a/src/controllers/02-user-controller.ts +++ b/src/controllers/02-user-controller.ts @@ -51,6 +51,7 @@ import { connectOrNot, queryOrNot, whereAddressQuery, + whereDateQuery, } from "../utils/relation"; import { isUsedError, notFoundError, relationError } from "../utils/error"; import { retry } from "../utils/func"; @@ -305,6 +306,8 @@ export class UserController extends Controller { @Query() status?: Status, @Query() responsibleDistrictId?: string, @Query() activeBranchOnly?: boolean, + @Query() startDate?: Date, + @Query() endDate?: Date, @Body() body?: { userId?: string[]; @@ -368,6 +371,7 @@ export class UserController extends Controller { }, }, }, + ...whereDateQuery(startDate, endDate), } satisfies Prisma.UserWhereInput; const [result, total] = await prisma.$transaction([ diff --git a/src/controllers/03-customer-branch-controller.ts b/src/controllers/03-customer-branch-controller.ts index 7818bef..5c94a2f 100644 --- a/src/controllers/03-customer-branch-controller.ts +++ b/src/controllers/03-customer-branch-controller.ts @@ -30,6 +30,7 @@ import { connectOrNot, queryOrNot, whereAddressQuery, + whereDateQuery, } from "../utils/relation"; import { isUsedError, notFoundError, relationError } from "../utils/error"; import { @@ -195,6 +196,8 @@ export class CustomerBranchController extends Controller { @Query() page: number = 1, @Query() pageSize: number = 30, @Query() activeRegisBranchOnly?: boolean, + @Query() startDate?: Date, + @Query() endDate?: Date, ) { const where = { OR: queryOrNot(query, [ @@ -229,6 +232,7 @@ export class CustomerBranchController extends Controller { subDistrict: zipCode ? { zipCode } : undefined, ...filterStatus(activeRegisBranchOnly ? Status.ACTIVE : status), }, + ...whereDateQuery(startDate, endDate), } satisfies Prisma.CustomerBranchWhereInput; const [result, total] = await prisma.$transaction([ @@ -285,6 +289,8 @@ export class CustomerBranchController extends Controller { @Query() visa?: boolean, @Query() page: number = 1, @Query() pageSize: number = 30, + @Query() startDate?: Date, + @Query() endDate?: Date, ) { const where = { OR: queryOrNot(query, [ @@ -300,6 +306,7 @@ export class CustomerBranchController extends Controller { subDistrict: zipCode ? { zipCode } : undefined, gender, }, + ...whereDateQuery(startDate, endDate), } satisfies Prisma.EmployeeWhereInput; const [result, total] = await prisma.$transaction([ diff --git a/src/controllers/03-customer-controller.ts b/src/controllers/03-customer-controller.ts index 8cac3d9..122726f 100644 --- a/src/controllers/03-customer-controller.ts +++ b/src/controllers/03-customer-controller.ts @@ -36,7 +36,7 @@ import { setFile, } from "../utils/minio"; import { isUsedError, notFoundError, relationError } from "../utils/error"; -import { connectOrNot, queryOrNot } from "../utils/relation"; +import { connectOrNot, queryOrNot, whereDateQuery } from "../utils/relation"; const MANAGE_ROLES = [ "system", @@ -165,6 +165,8 @@ export class CustomerController extends Controller { @Query() includeBranch: boolean = false, @Query() company: boolean = false, @Query() activeBranchOnly?: boolean, + @Query() startDate?: Date, + @Query() endDate?: Date, ) { const where = { OR: queryOrNot(query, [ @@ -188,6 +190,7 @@ export class CustomerController extends Controller { : permissionCond(req.user, { activeOnly: activeBranchOnly }), }, }, + ...whereDateQuery(startDate, endDate), } satisfies Prisma.CustomerWhereInput; const [result, total] = await prisma.$transaction([ diff --git a/src/controllers/03-employee-controller.ts b/src/controllers/03-employee-controller.ts index 44629c1..5f8a1d4 100644 --- a/src/controllers/03-employee-controller.ts +++ b/src/controllers/03-employee-controller.ts @@ -30,6 +30,7 @@ import { connectOrNot, queryOrNot, whereAddressQuery, + whereDateQuery, } from "../utils/relation"; import { isUsedError, notFoundError, relationError } from "../utils/error"; import { @@ -154,6 +155,8 @@ export class EmployeeController extends Controller { @Query() customerBranchId?: string, @Query() status?: Status, @Query() query: string = "", + @Query() startDate?: Date, + @Query() endDate?: Date, ) { return await prisma.employee .groupBy({ @@ -183,6 +186,7 @@ export class EmployeeController extends Controller { }, }, }, + ...whereDateQuery(startDate, endDate), }, }) .then((res) => @@ -240,6 +244,8 @@ export class EmployeeController extends Controller { @Query() page: number = 1, @Query() pageSize: number = 30, @Query() activeOnly?: boolean, + @Query() startDate?: Date, + @Query() endDate?: Date, @Body() body?: { passport?: string[]; @@ -288,6 +294,7 @@ export class EmployeeController extends Controller { subDistrict: zipCode ? { zipCode } : undefined, gender, }, + ...whereDateQuery(startDate, endDate), } satisfies Prisma.EmployeeWhereInput; const [result, total] = await prisma.$transaction([ diff --git a/src/controllers/04-flow-template-controller.ts b/src/controllers/04-flow-template-controller.ts index 0f80d61..97bd1b9 100644 --- a/src/controllers/04-flow-template-controller.ts +++ b/src/controllers/04-flow-template-controller.ts @@ -24,7 +24,7 @@ import HttpError from "../interfaces/http-error"; import HttpStatus from "../interfaces/http-status"; import { notFoundError } from "../utils/error"; import { filterStatus } from "../services/prisma"; -import { queryOrNot } from "../utils/relation"; +import { queryOrNot, whereDateQuery } from "../utils/relation"; type WorkflowPayload = { name: string; @@ -58,6 +58,8 @@ export class FlowTemplateController extends Controller { @Query() status?: Status, @Query() query = "", @Query() activeOnly?: boolean, + @Query() startDate?: Date, + @Query() endDate?: Date, ) { const where = { OR: queryOrNot(query, [ @@ -74,6 +76,7 @@ export class FlowTemplateController extends Controller { OR: permissionCondCompany(req.user, { activeOnly: true }), }, }, + ...whereDateQuery(startDate, endDate), } satisfies Prisma.WorkflowTemplateWhereInput; const [result, total] = await prisma.$transaction([ prisma.workflowTemplate.findMany({ diff --git a/src/controllers/04-invoice-controller.ts b/src/controllers/04-invoice-controller.ts index 64efee2..fb3fdf0 100644 --- a/src/controllers/04-invoice-controller.ts +++ b/src/controllers/04-invoice-controller.ts @@ -21,6 +21,7 @@ import { createPermCondition, } from "../services/permission"; import { PaymentStatus } from "../generated/kysely/types"; +import { whereDateQuery } from "../utils/relation"; type InvoicePayload = { quotationId: string; @@ -95,6 +96,8 @@ export class InvoiceController extends Controller { @Query() quotationId?: string, @Query() debitNoteId?: string, @Query() pay?: boolean, + @Query() startDate?: Date, + @Query() endDate?: Date, ) { const where: Prisma.InvoiceWhereInput = { OR: [ @@ -132,6 +135,7 @@ export class InvoiceController extends Controller { OR: permissionCondCompany(req.user), }, }, + ...whereDateQuery(startDate, endDate), }; const [result, total] = await prisma.$transaction([ diff --git a/src/controllers/04-product-controller.ts b/src/controllers/04-product-controller.ts index 8cb8c3e..09dce42 100644 --- a/src/controllers/04-product-controller.ts +++ b/src/controllers/04-product-controller.ts @@ -27,7 +27,7 @@ import { isSystem } from "../utils/keycloak"; import { filterStatus } from "../services/prisma"; import { deleteFile, deleteFolder, fileLocation, getFile, listFile, setFile } from "../utils/minio"; import { isUsedError, notFoundError, relationError } from "../utils/error"; -import { queryOrNot } from "../utils/relation"; +import { queryOrNot, whereDateQuery } from "../utils/relation"; const MANAGE_ROLES = [ "system", @@ -139,6 +139,8 @@ export class ProductController extends Controller { @Query() orderField?: keyof Product, @Query() orderBy?: "asc" | "desc", @Query() activeOnly?: boolean, + @Query() startDate?: Date, + @Query() endDate?: Date, ) { // NOTE: will be used to scope product within product group that is shared between branch but not company when select shared product if user is system const targetGroup = @@ -194,6 +196,7 @@ export class ProductController extends Controller { : []), ], }, + ...whereDateQuery(startDate, endDate), } satisfies Prisma.ProductWhereInput; const [result, total] = await prisma.$transaction([ diff --git a/src/controllers/04-product-group-controller.ts b/src/controllers/04-product-group-controller.ts index ee27265..ecd4ac6 100644 --- a/src/controllers/04-product-group-controller.ts +++ b/src/controllers/04-product-group-controller.ts @@ -27,7 +27,7 @@ import { } from "../services/permission"; import { filterStatus } from "../services/prisma"; import { isUsedError, notFoundError, relationError } from "../utils/error"; -import { queryOrNot } from "../utils/relation"; +import { queryOrNot, whereDateQuery } from "../utils/relation"; type ProductGroupCreate = { name: string; @@ -90,6 +90,8 @@ export class ProductGroup extends Controller { @Query() page: number = 1, @Query() pageSize: number = 30, @Query() activeOnly?: boolean, + @Query() startDate?: Date, + @Query() endDate?: Date, ) { const where = { OR: queryOrNot(query, [ @@ -105,6 +107,7 @@ export class ProductGroup extends Controller { : { OR: permissionCond(req.user, { activeOnly }) }, }, ], + ...whereDateQuery(startDate, endDate), } satisfies Prisma.ProductGroupWhereInput; const [result, total] = await prisma.$transaction([ diff --git a/src/controllers/04-properties-controller.ts b/src/controllers/04-properties-controller.ts index 35f94a8..6203ec6 100644 --- a/src/controllers/04-properties-controller.ts +++ b/src/controllers/04-properties-controller.ts @@ -24,7 +24,7 @@ import HttpError from "../interfaces/http-error"; import HttpStatus from "../interfaces/http-status"; import { notFoundError } from "../utils/error"; import { filterStatus } from "../services/prisma"; -import { queryOrNot } from "../utils/relation"; +import { queryOrNot, whereDateQuery } from "../utils/relation"; type PropertyPayload = { name: string; @@ -49,6 +49,8 @@ export class PropertiesController extends Controller { @Query() status?: Status, @Query() query = "", @Query() activeOnly?: boolean, + @Query() startDate?: Date, + @Query() endDate?: Date, ) { const where = { OR: queryOrNot(query, [ @@ -61,6 +63,7 @@ export class PropertiesController extends Controller { OR: permissionCondCompany(req.user, { activeOnly: true }), }, }, + ...whereDateQuery(startDate, endDate), } satisfies Prisma.PropertyWhereInput; const [result, total] = await prisma.$transaction([ prisma.property.findMany({ diff --git a/src/controllers/04-service-controller.ts b/src/controllers/04-service-controller.ts index c193fbd..ed46c18 100644 --- a/src/controllers/04-service-controller.ts +++ b/src/controllers/04-service-controller.ts @@ -36,7 +36,7 @@ import { listFile, setFile, } from "../utils/minio"; -import { queryOrNot } from "../utils/relation"; +import { queryOrNot, whereDateQuery } from "../utils/relation"; const MANAGE_ROLES = [ "system", @@ -164,6 +164,8 @@ export class ServiceController extends Controller { @Query() fullDetail?: boolean, @Query() activeOnly?: boolean, @Query() shared?: boolean, + @Query() startDate?: Date, + @Query() endDate?: Date, ) { // NOTE: will be used to scope product within product group that is shared between branch but not company when select shared product if user is system const targetGroup = @@ -219,6 +221,7 @@ export class ServiceController extends Controller { : []), ], }, + ...whereDateQuery(startDate, endDate), } satisfies Prisma.ServiceWhereInput; const [result, total] = await prisma.$transaction([ diff --git a/src/controllers/04-work-controller.ts b/src/controllers/04-work-controller.ts index 9b875b6..c8a140b 100644 --- a/src/controllers/04-work-controller.ts +++ b/src/controllers/04-work-controller.ts @@ -18,6 +18,7 @@ import prisma from "../db"; import { RequestWithUser } from "../interfaces/user"; import HttpStatus from "../interfaces/http-status"; import { isUsedError, notFoundError } from "../utils/error"; +import { whereDateQuery } from "../utils/relation"; type WorkCreate = { order: number; @@ -45,9 +46,12 @@ export class WorkController extends Controller { @Query() query: string = "", @Query() page: number = 1, @Query() pageSize: number = 30, + @Query() startDate?: Date, + @Query() endDate?: Date, ) { const where = { OR: [{ name: { contains: query }, serviceId: baseOnly ? null : undefined }], + ...whereDateQuery(startDate, endDate), } satisfies Prisma.WorkWhereInput; const [result, total] = await prisma.$transaction([ diff --git a/src/controllers/05-quotation-controller.ts b/src/controllers/05-quotation-controller.ts index fa3d6b3..b454db3 100644 --- a/src/controllers/05-quotation-controller.ts +++ b/src/controllers/05-quotation-controller.ts @@ -25,7 +25,7 @@ import { import { isSystem } from "../utils/keycloak"; import { isUsedError, notFoundError, relationError } from "../utils/error"; import { precisionRound } from "../utils/arithmetic"; -import { queryOrNot } from "../utils/relation"; +import { queryOrNot, whereDateQuery } from "../utils/relation"; import { deleteFile, fileLocation, getFile, getPresigned, listFile, setFile } from "../utils/minio"; import HttpError from "../interfaces/http-error"; import HttpStatus from "../interfaces/http-status"; @@ -206,6 +206,8 @@ export class QuotationController extends Controller { @Query() forDebitNote?: boolean, @Query() code?: string, @Query() query = "", + @Query() startDate?: Date, + @Query() endDate?: Date, ) { const where = { OR: queryOrNot(query, [ @@ -253,6 +255,7 @@ export class QuotationController extends Controller { }, } : undefined, + ...whereDateQuery(startDate, endDate), } satisfies Prisma.QuotationWhereInput; const [result, total] = await prisma.$transaction([ diff --git a/src/controllers/06-request-list-controller.ts b/src/controllers/06-request-list-controller.ts index e1da5f2..6b5e5f8 100644 --- a/src/controllers/06-request-list-controller.ts +++ b/src/controllers/06-request-list-controller.ts @@ -27,7 +27,7 @@ import { createPermCheck, createPermCondition, } from "../services/permission"; -import { queryOrNot } from "../utils/relation"; +import { queryOrNot, whereDateQuery } from "../utils/relation"; import { notFoundError } from "../utils/error"; import { deleteFile, fileLocation, getFile, getPresigned, listFile, setFile } from "../utils/minio"; import HttpError from "../interfaces/http-error"; @@ -81,6 +81,8 @@ export class RequestDataController extends Controller { @Query() quotationId?: string, @Query() code?: string, @Query() incomplete?: boolean, + @Query() startDate?: Date, + @Query() endDate?: Date, ) { const where = { OR: queryOrNot(query, [ @@ -147,6 +149,7 @@ export class RequestDataController extends Controller { id: quotationId, registeredBranch: { OR: permissionCond(req.user) }, }, + ...whereDateQuery(startDate, endDate), } satisfies Prisma.RequestDataWhereInput; const [result, total] = await prisma.$transaction([ diff --git a/src/controllers/07-task-controller.ts b/src/controllers/07-task-controller.ts index 03009b3..fd5c4a2 100644 --- a/src/controllers/07-task-controller.ts +++ b/src/controllers/07-task-controller.ts @@ -42,7 +42,7 @@ import { listFile, setFile, } from "../utils/minio"; -import { queryOrNot } from "../utils/relation"; +import { queryOrNot, whereDateQuery } from "../utils/relation"; const MANAGE_ROLES = ["system", "head_of_admin", "admin", "document_checker"]; @@ -107,6 +107,8 @@ export class TaskController extends Controller { @Query() assignedUserId?: string, @Query() taskOrderStatus?: TaskOrderStatus, @Body() body?: { code?: string[] }, + @Query() startDate?: Date, + @Query() endDate?: Date, ) { const where = { taskOrderStatus, @@ -125,6 +127,7 @@ export class TaskController extends Controller { { contactName: { contains: query, mode: "insensitive" } }, { contactTel: { contains: query, mode: "insensitive" } }, ]), + ...whereDateQuery(startDate, endDate), } satisfies Prisma.TaskOrderWhereInput; const [result, total] = await prisma.$transaction([ @@ -979,6 +982,8 @@ export class UserTaskController extends Controller { @Query() page = 1, @Query() pageSize = 30, @Query() userTaskStatus?: UserTaskStatus, + @Query() startDate?: Date, + @Query() endDate?: Date, ) { const where = { taskList: { @@ -1025,6 +1030,7 @@ export class UserTaskController extends Controller { { contactName: { contains: query, mode: "insensitive" } }, { contactTel: { contains: query, mode: "insensitive" } }, ]), + ...whereDateQuery(startDate, endDate), } satisfies Prisma.TaskOrderWhereInput; const [result, total] = await prisma.$transaction([ diff --git a/src/controllers/08-credit-note-controller.ts b/src/controllers/08-credit-note-controller.ts index 5fda3eb..2e80575 100644 --- a/src/controllers/08-credit-note-controller.ts +++ b/src/controllers/08-credit-note-controller.ts @@ -35,7 +35,7 @@ import { } from "../utils/minio"; import { notFoundError } from "../utils/error"; import { CreditNotePaybackType, CreditNoteStatus, Prisma, RequestDataStatus } from "@prisma/client"; -import { queryOrNot } from "../utils/relation"; +import { queryOrNot, whereDateQuery } from "../utils/relation"; import { PaybackStatus, RequestWorkStatus } from "../generated/kysely/types"; const MANAGE_ROLES = [ @@ -143,6 +143,8 @@ export class CreditNoteController extends Controller { @Query() quotationId?: string, @Query() creditNoteStatus?: CreditNoteStatus, @Body() body?: {}, + @Query() startDate?: Date, + @Query() endDate?: Date, ) { const where = { OR: queryOrNot(query, [ @@ -199,6 +201,7 @@ export class CreditNoteController extends Controller { }, }, }, + ...whereDateQuery(startDate, endDate), } satisfies Prisma.CreditNoteWhereInput; const [result, total] = await prisma.$transaction([ diff --git a/src/controllers/09-debit-note-controller.ts b/src/controllers/09-debit-note-controller.ts index a499f88..993037a 100644 --- a/src/controllers/09-debit-note-controller.ts +++ b/src/controllers/09-debit-note-controller.ts @@ -36,7 +36,7 @@ import { setFile, } from "../utils/minio"; import { isUsedError, notFoundError, relationError } from "../utils/error"; -import { queryOrNot } from "../utils/relation"; +import { queryOrNot, whereDateQuery } from "../utils/relation"; import { isSystem } from "../utils/keycloak"; import { precisionRound } from "../utils/arithmetic"; @@ -196,6 +196,8 @@ export class DebitNoteController extends Controller { @Query() includeRegisteredBranch?: boolean, @Query() code?: string, @Body() body?: {}, + @Query() startDate?: Date, + @Query() endDate?: Date, ) { const where = { OR: queryOrNot(query, [ @@ -220,6 +222,7 @@ export class DebitNoteController extends Controller { debitNoteQuotationId: quotationId, registeredBranch: isSystem(req.user) ? undefined : { OR: permissionCond(req.user) }, quotationStatus: status, + ...whereDateQuery(startDate, endDate), } satisfies Prisma.QuotationWhereInput; const [result, total] = await prisma.$transaction([ diff --git a/src/controllers/09-line-controller.ts b/src/controllers/09-line-controller.ts index 0a6ea3c..91c06bb 100644 --- a/src/controllers/09-line-controller.ts +++ b/src/controllers/09-line-controller.ts @@ -25,7 +25,7 @@ import { TaskStatus, RequestWorkStatus, } from "@prisma/client"; -import { queryOrNot, whereAddressQuery } from "../utils/relation"; +import { queryOrNot, whereAddressQuery, whereDateQuery } from "../utils/relation"; import { filterStatus } from "../services/prisma"; // import { RequestWorkStatus } from "../generated/kysely/types"; import { deleteFile, fileLocation, getFile, getPresigned, listFile, setFile } from "../utils/minio"; @@ -51,6 +51,8 @@ export class LineController extends Controller { @Query() page: number = 1, @Query() pageSize: number = 30, @Query() activeOnly?: boolean, + @Query() startDate?: Date, + @Query() endDate?: Date, ) { const where = { OR: !!query @@ -87,6 +89,7 @@ export class LineController extends Controller { subDistrict: zipCode ? { zipCode } : undefined, gender, }, + ...whereDateQuery(startDate, endDate), } satisfies Prisma.EmployeeWhereInput; const [result, total] = await prisma.$transaction([ @@ -173,6 +176,8 @@ export class LineController extends Controller { @Query() requestDataStatus?: RequestDataStatus, @Query() quotationId?: string, @Query() code?: string, + @Query() startDate?: Date, + @Query() endDate?: Date, ) { const where = { OR: queryOrNot(query, [ @@ -247,6 +252,7 @@ export class LineController extends Controller { ], }, }, + ...whereDateQuery(startDate, endDate), } satisfies Prisma.RequestDataWhereInput; const [result, total] = await prisma.$transaction([ @@ -604,6 +610,8 @@ export class LineController extends Controller { @Query() includeRegisteredBranch?: boolean, @Query() code?: string, @Query() query = "", + @Query() startDate?: Date, + @Query() endDate?: Date, ) { const where = { OR: @@ -660,6 +668,7 @@ export class LineController extends Controller { }, } : undefined, + ...whereDateQuery(startDate, endDate), } satisfies Prisma.QuotationWhereInput; const [result, total] = await prisma.$transaction([ diff --git a/src/utils/relation.ts b/src/utils/relation.ts index 3e96f11..aebb744 100644 --- a/src/utils/relation.ts +++ b/src/utils/relation.ts @@ -10,26 +10,35 @@ export function connectOrDisconnect(id?: string | null) { export function whereAddressQuery(query: string) { return [ - { address: { contains: query } }, - { addressEN: { contains: query } }, - { soi: { contains: query } }, - { soiEN: { contains: query } }, - { moo: { contains: query } }, - { mooEN: { contains: query } }, - { street: { contains: query } }, - { streetEN: { contains: query } }, - { province: { name: { contains: query } } }, - { province: { nameEN: { contains: query } } }, - { district: { name: { contains: query } } }, - { district: { nameEN: { contains: query } } }, - { subDistrict: { name: { contains: query } } }, - { subDistrict: { nameEN: { contains: query } } }, - { subDistrict: { zipCode: { contains: query } } }, - ]; + { address: { contains: query, mode: "insensitive" } }, + { addressEN: { contains: query, mode: "insensitive" } }, + { soi: { contains: query, mode: "insensitive" } }, + { soiEN: { contains: query, mode: "insensitive" } }, + { moo: { contains: query, mode: "insensitive" } }, + { mooEN: { contains: query, mode: "insensitive" } }, + { street: { contains: query, mode: "insensitive" } }, + { streetEN: { contains: query, mode: "insensitive" } }, + { province: { name: { contains: query, mode: "insensitive" } } }, + { province: { nameEN: { contains: query, mode: "insensitive" } } }, + { district: { name: { contains: query, mode: "insensitive" } } }, + { district: { nameEN: { contains: query, mode: "insensitive" } } }, + { subDistrict: { name: { contains: query, mode: "insensitive" } } }, + { subDistrict: { nameEN: { contains: query, mode: "insensitive" } } }, + { subDistrict: { zipCode: { contains: query, mode: "insensitive" } } }, + ] as const; } -export function queryOrNot(query: string | boolean, where: T): T | undefined; -export function queryOrNot(query: string | boolean, where: T, fallback: U): T | U; -export function queryOrNot(query: string | boolean, where: T, fallback?: U) { +export function queryOrNot(query: any, where: T): T | undefined; +export function queryOrNot(query: any, where: T, fallback: U): T | U; +export function queryOrNot(query: any, where: T, fallback?: U) { return !!query ? where : fallback; } + +export function whereDateQuery(startDate: Date | undefined, endDate: Date | undefined) { + return { + createdAt: { + gte: startDate, + lte: endDate, + }, + }; +} From 27d3ce6573d9be857a4069ed153cf339079d1611 Mon Sep 17 00:00:00 2001 From: Kanjana Date: Thu, 17 Apr 2025 16:21:05 +0700 Subject: [PATCH 11/38] change whereDateQuery in branch --- src/controllers/01-branch-controller.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controllers/01-branch-controller.ts b/src/controllers/01-branch-controller.ts index 003c4cd..da4c958 100644 --- a/src/controllers/01-branch-controller.ts +++ b/src/controllers/01-branch-controller.ts @@ -319,13 +319,13 @@ export class BranchController extends Controller { { telephoneNo: { contains: query, mode: "insensitive" } }, ...whereAddressQuery(query), ], + ...whereDateQuery(startDate, endDate), }, include: { province: true, district: true, subDistrict: true, }, - ...whereDateQuery(startDate, endDate), orderBy: { code: "asc" }, } : false, From d52680c23f94a9a81504183e10087bf984f9c7bc Mon Sep 17 00:00:00 2001 From: Kanjana Date: Thu, 17 Apr 2025 17:05:19 +0700 Subject: [PATCH 12/38] add startDate, endDate --- src/controllers/03-employee-controller.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/controllers/03-employee-controller.ts b/src/controllers/03-employee-controller.ts index 5f8a1d4..9699b6c 100644 --- a/src/controllers/03-employee-controller.ts +++ b/src/controllers/03-employee-controller.ts @@ -212,6 +212,8 @@ export class EmployeeController extends Controller { @Query() page: number = 1, @Query() pageSize: number = 30, @Query() activeOnly?: boolean, + @Query() startDate?: Date, + @Query() endDate?: Date, ) { return this.listByCriteria( req, @@ -226,6 +228,8 @@ export class EmployeeController extends Controller { page, pageSize, activeOnly, + startDate, + endDate, ); } From fd7833a5925a30c7672ae48bace0d1fd61ee71c8 Mon Sep 17 00:00:00 2001 From: Kanjana Date: Thu, 17 Apr 2025 17:56:55 +0700 Subject: [PATCH 13/38] add endDate,startDate --- src/controllers/02-user-controller.ts | 4 ++++ src/controllers/07-task-controller.ts | 6 +++++- src/controllers/08-credit-note-controller.ts | 6 +++++- src/controllers/09-debit-note-controller.ts | 6 +++++- 4 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/controllers/02-user-controller.ts b/src/controllers/02-user-controller.ts index 17630eb..20848be 100644 --- a/src/controllers/02-user-controller.ts +++ b/src/controllers/02-user-controller.ts @@ -280,6 +280,8 @@ export class UserController extends Controller { @Query() status?: Status, @Query() responsibleDistrictId?: string, @Query() activeBranchOnly?: boolean, + @Query() startDate?: Date, + @Query() endDate?: Date, ) { return this.getUserByCriteria( req, @@ -291,6 +293,8 @@ export class UserController extends Controller { status, responsibleDistrictId, activeBranchOnly, + startDate, + endDate, ); } diff --git a/src/controllers/07-task-controller.ts b/src/controllers/07-task-controller.ts index fd5c4a2..58acbbe 100644 --- a/src/controllers/07-task-controller.ts +++ b/src/controllers/07-task-controller.ts @@ -86,6 +86,8 @@ export class TaskController extends Controller { @Query() pageSize = 30, @Query() assignedByUserId?: string, @Query() taskOrderStatus?: TaskOrderStatus, + @Query() startDate?: Date, + @Query() endDate?: Date, ) { return this.getTaskOrderListByCriteria( req, @@ -94,6 +96,8 @@ export class TaskController extends Controller { pageSize, assignedByUserId, taskOrderStatus, + startDate, + endDate, ); } @@ -106,9 +110,9 @@ export class TaskController extends Controller { @Query() pageSize = 30, @Query() assignedUserId?: string, @Query() taskOrderStatus?: TaskOrderStatus, - @Body() body?: { code?: string[] }, @Query() startDate?: Date, @Query() endDate?: Date, + @Body() body?: { code?: string[] }, ) { const where = { taskOrderStatus, diff --git a/src/controllers/08-credit-note-controller.ts b/src/controllers/08-credit-note-controller.ts index 2e80575..4a6f622 100644 --- a/src/controllers/08-credit-note-controller.ts +++ b/src/controllers/08-credit-note-controller.ts @@ -121,6 +121,8 @@ export class CreditNoteController extends Controller { @Query() query: string = "", @Query() quotationId?: string, @Query() creditNoteStatus?: CreditNoteStatus, + @Query() startDate?: Date, + @Query() endDate?: Date, ) { return await this.getCreditNoteListByCriteria( req, @@ -129,6 +131,8 @@ export class CreditNoteController extends Controller { query, quotationId, creditNoteStatus, + startDate, + endDate, ); } @@ -142,9 +146,9 @@ export class CreditNoteController extends Controller { @Query() query: string = "", @Query() quotationId?: string, @Query() creditNoteStatus?: CreditNoteStatus, - @Body() body?: {}, @Query() startDate?: Date, @Query() endDate?: Date, + @Body() body?: {}, ) { const where = { OR: queryOrNot(query, [ diff --git a/src/controllers/09-debit-note-controller.ts b/src/controllers/09-debit-note-controller.ts index 993037a..8ff5d94 100644 --- a/src/controllers/09-debit-note-controller.ts +++ b/src/controllers/09-debit-note-controller.ts @@ -168,6 +168,8 @@ export class DebitNoteController extends Controller { @Query() payCondition?: PayCondition, @Query() includeRegisteredBranch?: boolean, @Query() code?: string, + @Query() startDate?: Date, + @Query() endDate?: Date, ) { return await this.getDebitNoteListByCriteria( req, @@ -179,6 +181,8 @@ export class DebitNoteController extends Controller { payCondition, includeRegisteredBranch, code, + startDate, + endDate, ); } @@ -195,9 +199,9 @@ export class DebitNoteController extends Controller { @Query() payCondition?: PayCondition, @Query() includeRegisteredBranch?: boolean, @Query() code?: string, - @Body() body?: {}, @Query() startDate?: Date, @Query() endDate?: Date, + @Body() body?: {}, ) { const where = { OR: queryOrNot(query, [ From 05d16f22de2da7baa72b5e9816c89b58ca76619d Mon Sep 17 00:00:00 2001 From: Kanjana Date: Fri, 18 Apr 2025 15:39:02 +0700 Subject: [PATCH 14/38] add import file product --- package.json | 3 + src/controllers/04-product-controller.ts | 128 +++++++++++++++++++++++ src/utils/spreadsheet.ts | 105 +++++++++++++++++++ 3 files changed, 236 insertions(+) create mode 100644 src/utils/spreadsheet.ts diff --git a/package.json b/package.json index b576e46..6294f71 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "@types/cors": "^2.8.17", "@types/express": "^4.17.21", "@types/morgan": "^1.9.9", + "@types/multer": "^1.4.12", "@types/node": "^20.17.10", "@types/nodemailer": "^6.4.17", "nodemon": "^3.1.9", @@ -46,12 +47,14 @@ "dayjs-plugin-utc": "^0.1.2", "docx-templates": "^4.13.0", "dotenv": "^16.4.7", + "exceljs": "^4.4.0", "express": "^4.21.2", "fast-jwt": "^5.0.5", "json-2-csv": "^5.5.8", "kysely": "^0.27.5", "minio": "^8.0.2", "morgan": "^1.10.0", + "multer": "^1.4.5-lts.2", "nodemailer": "^6.10.0", "prisma-extension-kysely": "^3.0.0", "promise.any": "^2.0.6", diff --git a/src/controllers/04-product-controller.ts b/src/controllers/04-product-controller.ts index 09dce42..146f6d1 100644 --- a/src/controllers/04-product-controller.ts +++ b/src/controllers/04-product-controller.ts @@ -11,6 +11,7 @@ import { Security, Tags, Query, + UploadedFile, } from "tsoa"; import { Prisma, Product, Status } from "@prisma/client"; @@ -28,6 +29,7 @@ import { filterStatus } from "../services/prisma"; import { deleteFile, deleteFolder, fileLocation, getFile, listFile, setFile } from "../utils/minio"; import { isUsedError, notFoundError, relationError } from "../utils/error"; import { queryOrNot, whereDateQuery } from "../utils/relation"; +import spreadsheet from "../utils/spreadsheet"; const MANAGE_ROLES = [ "system", @@ -447,6 +449,132 @@ export class ProductController extends Controller { where: { id: productId }, }); } + + @Post("uploadedFile") + @Security("keycloak", MANAGE_ROLES) + async importProduct( + @Request() req: RequestWithUser, + @UploadedFile() file: Express.Multer.File, + @Query() productGroupId: string, + ) { + if (!file?.buffer) throw notFoundError("File"); + + const buffer = new Uint8Array(file.buffer).buffer; + const dataFile = await spreadsheet.readExcel(buffer, { + header: true, + worksheet: "Sheet1", + }); + + let dataName: string[] = []; + const data = await dataFile.map((item: any) => { + dataName.push(item.name); + return { + ...item, + expenseType: + item.expenseType === "ค่าธรรมเนียม" + ? "fee" + : item.expenseType === "ค่าบริการ" + ? "serviceFee" + : "processingFee", + shared: item.shared === "ใช่" ? true : false, + calcVat: item.calcVat === "ใช่" ? true : false, + vatIncluded: item.vatIncluded === "รวม" ? true : false, + agentPriceCalcVat: item.agentPriceCalcVat === "ใช่" ? true : false, + agentPriceVatIncluded: item.agentPriceVatIncluded === "รวม" ? true : false, + serviceChargeCalcVat: item.serviceChargeCalcVat === "ใช่" ? true : false, + serviceChargeVatIncluded: item.serviceChargeVatIncluded === "รวม" ? true : false, + }; + }); + + const [productGroup, productSameName] = await prisma.$transaction([ + prisma.productGroup.findFirst({ + include: { + registeredBranch: { + include: branchRelationPermInclude(req.user), + }, + createdBy: true, + updatedBy: true, + }, + where: { id: productGroupId }, + }), + prisma.product.findMany({ + where: { + productGroup: { + registeredBranch: { + OR: permissionCondCompany(req.user), + }, + }, + name: { in: dataName }, + }, + }), + ]); + + if (!productGroup) throw relationError("Product Group"); + + await permissionCheck(req.user, productGroup.registeredBranch); + let dataProduct: ProductCreate[] = []; + + const record = await prisma.$transaction( + async (tx) => { + const branch = productGroup.registeredBranch; + const company = (branch.headOffice || branch).code; + console.log(branch, company); + for (const item of data) { + const dataDuplicate = productSameName.some( + (v) => v.code.slice(0, -3) === item.code.toUpperCase() && v.name === item.name, + ); + + if (!dataDuplicate) { + const last = await tx.runningNo.upsert({ + where: { + key: `PRODUCT_${company}_${item.code.toLocaleUpperCase()}`, + }, + create: { + key: `PRODUCT_${company}_${item.code.toLocaleUpperCase()}`, + value: 1, + }, + update: { value: { increment: 1 } }, + }); + + dataProduct.push({ + ...item, + code: `${item.code.toLocaleUpperCase()}${last.value.toString().padStart(3, "0")}`, + createdByUserId: req.user.sub, + updatedByUserId: req.user.sub, + productGroupId: productGroupId, + }); + } + } + console.log("dataProduct", dataProduct); + + return await prisma.product.createManyAndReturn({ + data: dataProduct, + include: { + createdBy: true, + updatedBy: true, + }, + }); + }, + { + isolationLevel: Prisma.TransactionIsolationLevel.Serializable, + }, + ); + + if (productGroup.status === "CREATED") { + await prisma.productGroup.update({ + include: { + createdBy: true, + updatedBy: true, + }, + where: { id: productGroupId }, + data: { status: Status.ACTIVE }, + }); + } + + this.setStatus(HttpStatus.CREATED); + + return record; + } } @Route("api/v1/product/{productId}") diff --git a/src/utils/spreadsheet.ts b/src/utils/spreadsheet.ts new file mode 100644 index 0000000..b04a4f2 --- /dev/null +++ b/src/utils/spreadsheet.ts @@ -0,0 +1,105 @@ +import Excel from "exceljs"; + +export default class spreadsheet { + static async readCsv() { + // TODO: read csv + } + + /** + * This function read data from excel file. + * + * @param buffer - Excel file. + * @param opts.header - Interprets the first row as the names of the fields. + * @param opts.worksheet - Specifies the worksheet to read. Can be the worksheet's name or its 1-based index. + * + * @returns + */ + static async readExcel( + buffer: Excel.Buffer, + opts?: { header?: boolean; worksheet?: number | string }, + ): Promise { + const workbook = new Excel.Workbook(); + await workbook.xlsx.load(buffer); + const worksheet = workbook.getWorksheet(opts?.worksheet ?? 1); + + if (!worksheet) return []; + + const header: Record = {}; + const values: any[] = []; + + worksheet.eachRow((row, rowId) => { + if (rowId === 1 && opts?.header !== false) { + row.eachCell((cell, cellId) => { + if (typeof cell.value === "string") { + header[cellId] = nameValue(cell.value); + } else { + header[cellId] = cellId.toString(); + } + }); + } else { + const data: Record = {}; + row.eachCell((cell, cellId) => { + data[opts?.header !== false ? header[cellId] : cellId - 1] = cell.value; + }); + values.push(opts?.header !== false ? data : Object.values(data)); + } + }); + + return values; + } +} + +function nameValue(value: string) { + let code: string; + switch (value) { + case "ชื่อสินค้าและบริการ": + code = "name"; + break; + case "ระยะเวลาดำเนินการ": + code = "process"; + break; + case "ประเภทค่าใช้จ่าย": + code = "expenseType"; + break; + case "รายละเอียด": + code = "detail"; + break; + case "หมายเหตุ": + code = "remark"; + break; + case "ใช้งานร่วมกัน": + code = "shared"; + break; + case "คำนวณภาษีราคาขาย": + code = "calcVat"; + break; + case "รวม VAT ราคาขาย": + code = "vatIncluded"; + break; + case "ราคาต่อหน่วย (บาท) ราคาขาย": + code = "price"; + break; + case "คำนวณภาษีราคาตัวแทน": + code = "agentPriceCalcVat"; + break; + case "รวม VAT ราคาตัวแทน": + code = "agentPriceVatIncluded"; + break; + case "ราคาต่อหน่วย (บาท) ราคาตัวแทน": + code = "agentPrice"; + break; + case "คำนวณภาษีราคาดำเนินการ": + code = "serviceChargeCalcVat"; + break; + case "รวม VAT ราคาดำเนินการ": + code = "serviceChargeVatIncluded"; + break; + case "ราคาต่อหน่วย (บาท) ราคาดำเนินการ": + code = "serviceCharge"; + break; + default: + code = "code"; + break; + } + return code; +} From e42b772dcf5eb60e8b5e386d8f7bfa24b01033d3 Mon Sep 17 00:00:00 2001 From: Kanjana Date: Fri, 18 Apr 2025 16:23:02 +0700 Subject: [PATCH 15/38] use await Promise.all in uploadedFile Product --- src/controllers/04-product-controller.ts | 53 ++++++++++++------------ 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/src/controllers/04-product-controller.ts b/src/controllers/04-product-controller.ts index 146f6d1..1035beb 100644 --- a/src/controllers/04-product-controller.ts +++ b/src/controllers/04-product-controller.ts @@ -518,34 +518,35 @@ export class ProductController extends Controller { async (tx) => { const branch = productGroup.registeredBranch; const company = (branch.headOffice || branch).code; - console.log(branch, company); - for (const item of data) { - const dataDuplicate = productSameName.some( - (v) => v.code.slice(0, -3) === item.code.toUpperCase() && v.name === item.name, - ); - if (!dataDuplicate) { - const last = await tx.runningNo.upsert({ - where: { - key: `PRODUCT_${company}_${item.code.toLocaleUpperCase()}`, - }, - create: { - key: `PRODUCT_${company}_${item.code.toLocaleUpperCase()}`, - value: 1, - }, - update: { value: { increment: 1 } }, - }); + await Promise.all( + data.map(async (item) => { + const dataDuplicate = productSameName.some( + (v) => v.code.slice(0, -3) === item.code.toUpperCase() && v.name === item.name, + ); - dataProduct.push({ - ...item, - code: `${item.code.toLocaleUpperCase()}${last.value.toString().padStart(3, "0")}`, - createdByUserId: req.user.sub, - updatedByUserId: req.user.sub, - productGroupId: productGroupId, - }); - } - } - console.log("dataProduct", dataProduct); + if (!dataDuplicate) { + const last = await tx.runningNo.upsert({ + where: { + key: `PRODUCT_${company}_${item.code.toLocaleUpperCase()}`, + }, + create: { + key: `PRODUCT_${company}_${item.code.toLocaleUpperCase()}`, + value: 1, + }, + update: { value: { increment: 1 } }, + }); + + dataProduct.push({ + ...item, + code: `${item.code.toLocaleUpperCase()}${last.value.toString().padStart(3, "0")}`, + createdByUserId: req.user.sub, + updatedByUserId: req.user.sub, + productGroupId: productGroupId, + }); + } + }), + ); return await prisma.product.createManyAndReturn({ data: dataProduct, From a25968786d46fc98608bd5858132411bcb7adde8 Mon Sep 17 00:00:00 2001 From: Methapon2001 <61303214+Methapon2001@users.noreply.github.com> Date: Fri, 18 Apr 2025 16:32:05 +0700 Subject: [PATCH 16/38] chore: update lock file --- pnpm-lock.yaml | 254 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 254 insertions(+) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index bb7ddd5..9ad8f82 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -44,6 +44,9 @@ importers: dotenv: specifier: ^16.4.7 version: 16.4.7 + exceljs: + specifier: ^4.4.0 + version: 4.4.0 express: specifier: ^4.21.2 version: 4.21.2 @@ -62,6 +65,9 @@ importers: morgan: specifier: ^1.10.0 version: 1.10.0 + multer: + specifier: ^1.4.5-lts.2 + version: 1.4.5-lts.2 nodemailer: specifier: ^6.10.0 version: 6.10.0 @@ -99,6 +105,9 @@ importers: '@types/morgan': specifier: ^1.9.9 version: 1.9.9 + '@types/multer': + specifier: ^1.4.12 + version: 1.4.12 '@types/node': specifier: ^20.17.10 version: 20.17.10 @@ -177,6 +186,12 @@ packages: resolution: {integrity: sha512-jasKNQeOb1vNf9aEYg+8zXmetaFjApDTSCC4QTl6aTixvyiRiSLcCiB8P6Q0lY9JIII/BhqNl8WbpFnsKitntw==} engines: {node: '>=18'} + '@fast-csv/format@4.3.5': + resolution: {integrity: sha512-8iRn6QF3I8Ak78lNAa+Gdl5MJJBM5vRHivFtMRUWINdevNo00K7OXxS2PshawLKTejVwieIlPmK5YlLu6w4u8A==} + + '@fast-csv/parse@4.3.6': + resolution: {integrity: sha512-uRsLYksqpbDmWaSmzvJcuApSEe38+6NQZBUsuAyMZKqHxH0g1wcJgsKUvN3WC8tewaqFjBMMGrkHmC+T7k8LvA==} + '@fast-csv/parse@5.0.2': resolution: {integrity: sha512-gMu1Btmm99TP+wc0tZnlH30E/F1Gw1Tah3oMDBHNPe9W8S68ixVHjt89Wg5lh7d9RuQMtwN+sGl5kxR891+fzw==} @@ -492,6 +507,9 @@ packages: '@types/multer@1.4.12': resolution: {integrity: sha512-pQ2hoqvXiJt2FP9WQVLPRO+AmiIm/ZYkavPlIQnx282u4ZrVdztx0pkh3jjpQt0Kz+YI0YhSG264y08UJKoUQg==} + '@types/node@14.18.63': + resolution: {integrity: sha512-fAtCfv4jJg+ExtXhvCkCqUKZ+4ok/JQk01qDKhL5BDDoS3AxKXhV5/MAVUZyQnSEd2GT92fkgZl0pz0Q0AzcIQ==} + '@types/node@20.17.10': resolution: {integrity: sha512-/jrvh5h6NXhEauFFexRin69nA0uHJ5gwk4iDivp/DeoEua3uwCUto6PC86IpRITBOs4+6i2I56K5x5b6WYGXHA==} @@ -587,6 +605,9 @@ packages: resolution: {integrity: sha512-v/ShMp57iBnBp4lDgV8Jx3d3Q5/Hac25FWmQ98eMahUiHPXcvwIMKJD0hBIgclm/FCG+LwPkAKtkRO1O/W0YGg==} hasBin: true + append-field@1.0.0: + resolution: {integrity: sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==} + archiver-utils@2.1.0: resolution: {integrity: sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==} engines: {node: '>= 6'} @@ -682,6 +703,10 @@ packages: resolution: {integrity: sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==} engines: {node: '>= 0.8'} + big-integer@1.6.52: + resolution: {integrity: sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==} + engines: {node: '>=0.6'} + binary-extensions@2.3.0: resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} engines: {node: '>=8'} @@ -689,12 +714,18 @@ packages: binary-search@1.3.6: resolution: {integrity: sha512-nbE1WxOTTrUWIfsfZ4aHGYu5DOuNkbxGokjV6Z2kxfJK3uaAb8zNK1muzOeipoLHZjInT4Br88BHpzevc681xA==} + binary@0.3.0: + resolution: {integrity: sha512-D4H1y5KYwpJgK8wk1Cue5LLPgmwHKYSChkbspQg5JtVuR5ulGckxfR62H3AE9UDkdMC8yyXlqYihuz3Aqg2XZg==} + bl@4.1.0: resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} block-stream2@2.1.0: resolution: {integrity: sha512-suhjmLI57Ewpmq00qaygS8UgEq2ly2PCItenIyhMqVjo4t4pGzqMvfgJuX8iWTeSDdfSSqS6j38fL4ToNL7Pfg==} + bluebird@3.4.7: + resolution: {integrity: sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA==} + bn.js@4.12.1: resolution: {integrity: sha512-k8TVBiPkPJT9uHLdOKfFpqcfprwBFOAAXXozRubr7R7PfIuKvQlzcI4M0pALeqXN09vdaMbUdUj+pass+uULAg==} @@ -725,9 +756,24 @@ packages: resolution: {integrity: sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==} engines: {node: '>=8.0.0'} + buffer-from@1.1.2: + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + + buffer-indexof-polyfill@1.0.2: + resolution: {integrity: sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A==} + engines: {node: '>=0.10'} + buffer@5.7.1: resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + buffers@0.1.1: + resolution: {integrity: sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ==} + engines: {node: '>=0.2.0'} + + busboy@1.6.0: + resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==} + engines: {node: '>=10.16.0'} + bytes@3.1.2: resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} engines: {node: '>= 0.8'} @@ -744,6 +790,9 @@ packages: resolution: {integrity: sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==} engines: {node: '>= 0.4'} + chainsaw@0.1.0: + resolution: {integrity: sha512-75kWfWt6MEKNC8xYXIdRpDehRYY/tNSgwKaJq+dbbDcxORuVrrQ+SEHoWsniVn9XPYfP4gmdWIeDk/4YNp1rNQ==} + chalk-template@0.4.0: resolution: {integrity: sha512-/ghrgmhfY8RaSdeo43hNXxpoHAtxdbskUHjPpfqUWGttFgycUhYPGx3YZBCnUCvOa7Doivn1IZec3DEGFoMgLg==} engines: {node: '>=12'} @@ -821,6 +870,10 @@ packages: concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + concat-stream@1.6.2: + resolution: {integrity: sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==} + engines: {'0': node >= 0.8} + console-log-level@1.4.1: resolution: {integrity: sha512-VZzbIORbP+PPcN/gg3DXClTLPLg5Slwd5fL2MIc+o1qZ4BXBvWyc6QxPk6T/Mkr6IVjRpoAGf32XxP3ZWMVRcQ==} @@ -985,6 +1038,9 @@ packages: resolution: {integrity: sha512-9+Sj30DIu+4KvHqMfLUGLFYL2PkURSYMVXJyXe92nFRvlYq5hBjLEhblKB+vkd/WVlUYMWigiY07T91Fkk0+4A==} engines: {node: '>= 0.4'} + duplexer2@0.1.4: + resolution: {integrity: sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==} + eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} @@ -1087,6 +1143,10 @@ packages: eventemitter3@5.0.1: resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} + exceljs@4.4.0: + resolution: {integrity: sha512-XctvKaEMaj1Ii9oDOqbW/6e1gXknSY4g/aLCDicOXqBE4M0nRWkUu0PTp++UPNzoFY12BNHMfs/VadKIS6llvg==} + engines: {node: '>=8.3.0'} + execa@5.1.1: resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} engines: {node: '>=10'} @@ -1095,6 +1155,10 @@ packages: resolution: {integrity: sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==} engines: {node: '>= 0.10.0'} + fast-csv@4.3.6: + resolution: {integrity: sha512-2RNSpuwwsJGP0frGsOmTb9oUF+VkFSM4SyLTDgwf2ciHWTarN0lQTC+F2f/t5J9QjW+c65VFIAAu85GsvMIusw==} + engines: {node: '>=10.0.0'} + fast-glob@3.3.2: resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} engines: {node: '>=8.6.0'} @@ -1203,6 +1267,11 @@ packages: engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] + fstream@1.0.12: + resolution: {integrity: sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==} + engines: {node: '>=0.6'} + deprecated: This package is no longer supported. + function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} @@ -1623,6 +1692,9 @@ packages: lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + listenercount@1.0.1: + resolution: {integrity: sha512-3mk/Zag0+IJxeDrxSgaDPy4zZ3w05PRZeJNnlWhzFz5OkX49J4krc+A8X2d2M69vGMBEX0uyl8M+W+8gH+kBqQ==} + locate-path@5.0.0: resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} engines: {node: '>=8'} @@ -1649,6 +1721,13 @@ packages: lodash.groupby@4.6.0: resolution: {integrity: sha512-5dcWxm23+VAoz+awKmBaiBvzox8+RqMgFhi7UvX9DHZr2HdxHXM/Wrf8cfKpsW37RNrvtPn6hSwNqurSILbmJw==} + lodash.isboolean@3.0.3: + resolution: {integrity: sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==} + + lodash.isequal@4.5.0: + resolution: {integrity: sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==} + deprecated: This package is deprecated. Use require('node:util').isDeepStrictEqual instead. + lodash.isfunction@3.0.9: resolution: {integrity: sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw==} @@ -1796,6 +1875,10 @@ packages: resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} engines: {node: '>=16 || 14 >=14.17'} + mkdirp@0.5.6: + resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} + hasBin: true + mnemonist@0.39.8: resolution: {integrity: sha512-vyWo2K3fjrUw8YeeZ1zF0fy6Mu59RHokURlld8ymdUPjMlD9EC9ov1/YPqTgqRvUN9nTr3Gqfz29LYAmu0PHPQ==} @@ -1818,6 +1901,10 @@ packages: ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + multer@1.4.5-lts.2: + resolution: {integrity: sha512-VzGiVigcG9zUAoCNU+xShztrlr1auZOlurXynNvO9GiWD1/mTBbUljOKY+qMeazBqXgRnjzeEgJI/wyjJUHg9A==} + engines: {node: '>= 6.0.0'} + negotiator@0.6.3: resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} engines: {node: '>= 0.6'} @@ -2190,6 +2277,11 @@ packages: resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + rimraf@2.7.1: + resolution: {integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==} + deprecated: Rimraf versions prior to v4 are no longer supported + hasBin: true + rimraf@3.0.2: resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} deprecated: Rimraf versions prior to v4 are no longer supported @@ -2225,6 +2317,10 @@ packages: sax@1.4.1: resolution: {integrity: sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==} + saxes@5.0.1: + resolution: {integrity: sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==} + engines: {node: '>=10'} + secure-json-parse@2.7.0: resolution: {integrity: sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==} @@ -2372,6 +2468,10 @@ packages: resolution: {integrity: sha512-LsvisgE3iThboRqA+XLmtnY9ktPLVPOj3zZxXMhlezeCcAh0RhomquXJgB8H+lb/RR/pPcbNVGHVKFUwjpoRtw==} engines: {node: '>= 0.8'} + streamsearch@1.1.0: + resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} + engines: {node: '>=10.0.0'} + strict-uri-encode@2.0.0: resolution: {integrity: sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==} engines: {node: '>=4'} @@ -2498,6 +2598,9 @@ packages: tr46@1.0.1: resolution: {integrity: sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==} + traverse@0.3.9: + resolution: {integrity: sha512-iawgk0hLP3SxGKDfnDJf8wTz4p2qImnyihM5Hh/sGvQ3K37dPi/w8sRhdNIxYA1TwFwc5mDhIJq+O0RsvXBKdQ==} + triple-beam@1.4.1: resolution: {integrity: sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==} engines: {node: '>= 14.0.0'} @@ -2567,6 +2670,9 @@ packages: resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==} engines: {node: '>= 0.4'} + typedarray@0.0.6: + resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==} + typescript@5.7.2: resolution: {integrity: sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==} engines: {node: '>=14.17'} @@ -2616,6 +2722,9 @@ packages: resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} engines: {node: '>= 0.8'} + unzipper@0.10.14: + resolution: {integrity: sha512-ti4wZj+0bQTiX2KmKWuwj7lhV+2n//uXEotUmGuQqrbVZSEGFMbI68+c6JCQ8aAmUWYvtHEz2A8K6wXvueR/6g==} + util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} @@ -2626,6 +2735,10 @@ packages: resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} engines: {node: '>= 0.4.0'} + uuid@8.3.2: + resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} + hasBin: true + uuid@9.0.0: resolution: {integrity: sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==} hasBin: true @@ -2718,6 +2831,13 @@ packages: resolution: {integrity: sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==} engines: {node: '>=4.0'} + xmlchars@2.2.0: + resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} + + xtend@4.0.2: + resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} + engines: {node: '>=0.4'} + y18n@5.0.8: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} @@ -2826,6 +2946,25 @@ snapshots: transitivePeerDependencies: - supports-color + '@fast-csv/format@4.3.5': + dependencies: + '@types/node': 14.18.63 + lodash.escaperegexp: 4.1.2 + lodash.isboolean: 3.0.3 + lodash.isequal: 4.5.0 + lodash.isfunction: 3.0.9 + lodash.isnil: 4.0.0 + + '@fast-csv/parse@4.3.6': + dependencies: + '@types/node': 14.18.63 + lodash.escaperegexp: 4.1.2 + lodash.groupby: 4.6.0 + lodash.isfunction: 3.0.9 + lodash.isnil: 4.0.0 + lodash.isundefined: 3.0.1 + lodash.uniq: 4.5.0 + '@fast-csv/parse@5.0.2': dependencies: lodash.escaperegexp: 4.1.2 @@ -3341,6 +3480,8 @@ snapshots: dependencies: '@types/express': 4.17.21 + '@types/node@14.18.63': {} + '@types/node@20.17.10': dependencies: undici-types: 6.19.8 @@ -3440,6 +3581,8 @@ snapshots: json-bignum: 0.0.3 tslib: 2.8.1 + append-field@1.0.0: {} + archiver-utils@2.1.0: dependencies: glob: 7.2.3 @@ -3563,11 +3706,18 @@ snapshots: dependencies: safe-buffer: 5.1.2 + big-integer@1.6.52: {} + binary-extensions@2.3.0: {} binary-search@1.3.6: optional: true + binary@0.3.0: + dependencies: + buffers: 0.1.1 + chainsaw: 0.1.0 + bl@4.1.0: dependencies: buffer: 5.7.1 @@ -3578,6 +3728,8 @@ snapshots: dependencies: readable-stream: 3.6.2 + bluebird@3.4.7: {} + bn.js@4.12.1: {} body-parser@1.20.3: @@ -3621,11 +3773,21 @@ snapshots: buffer-crc32@1.0.0: {} + buffer-from@1.1.2: {} + + buffer-indexof-polyfill@1.0.2: {} + buffer@5.7.1: dependencies: base64-js: 1.5.1 ieee754: 1.2.1 + buffers@0.1.1: {} + + busboy@1.6.0: + dependencies: + streamsearch: 1.1.0 + bytes@3.1.2: {} call-bind-apply-helpers@1.0.1: @@ -3645,6 +3807,10 @@ snapshots: call-bind-apply-helpers: 1.0.1 get-intrinsic: 1.2.6 + chainsaw@0.1.0: + dependencies: + traverse: 0.3.9 + chalk-template@0.4.0: dependencies: chalk: 4.1.2 @@ -3756,6 +3922,13 @@ snapshots: concat-map@0.0.1: {} + concat-stream@1.6.2: + dependencies: + buffer-from: 1.1.2 + inherits: 2.0.4 + readable-stream: 2.3.8 + typedarray: 0.0.6 + console-log-level@1.4.1: optional: true @@ -3899,6 +4072,10 @@ snapshots: es-errors: 1.3.0 gopd: 1.2.0 + duplexer2@0.1.4: + dependencies: + readable-stream: 2.3.8 + eastasianwidth@0.2.0: {} ecdsa-sig-formatter@1.0.11: @@ -4088,6 +4265,18 @@ snapshots: eventemitter3@5.0.1: {} + exceljs@4.4.0: + dependencies: + archiver: 5.3.2 + dayjs: 1.11.13 + fast-csv: 4.3.6 + jszip: 3.10.1 + readable-stream: 3.6.2 + saxes: 5.0.1 + tmp: 0.2.1 + unzipper: 0.10.14 + uuid: 8.3.2 + execa@5.1.1: dependencies: cross-spawn: 7.0.6 @@ -4136,6 +4325,11 @@ snapshots: transitivePeerDependencies: - supports-color + fast-csv@4.3.6: + dependencies: + '@fast-csv/format': 4.3.5 + '@fast-csv/parse': 4.3.6 + fast-glob@3.3.2: dependencies: '@nodelib/fs.stat': 2.0.5 @@ -4258,6 +4452,13 @@ snapshots: fsevents@2.3.3: optional: true + fstream@1.0.12: + dependencies: + graceful-fs: 4.2.11 + inherits: 2.0.4 + mkdirp: 0.5.6 + rimraf: 2.7.1 + function-bind@1.1.2: {} function.prototype.name@1.1.7: @@ -4693,6 +4894,8 @@ snapshots: lines-and-columns@1.2.4: {} + listenercount@1.0.1: {} + locate-path@5.0.0: dependencies: p-locate: 4.1.0 @@ -4713,6 +4916,10 @@ snapshots: lodash.groupby@4.6.0: {} + lodash.isboolean@3.0.3: {} + + lodash.isequal@4.5.0: {} + lodash.isfunction@3.0.9: {} lodash.isnil@4.0.0: {} @@ -4853,6 +5060,10 @@ snapshots: minipass@7.1.2: {} + mkdirp@0.5.6: + dependencies: + minimist: 1.2.8 + mnemonist@0.39.8: dependencies: obliterator: 2.0.4 @@ -4879,6 +5090,16 @@ snapshots: ms@2.1.3: {} + multer@1.4.5-lts.2: + dependencies: + append-field: 1.0.0 + busboy: 1.6.0 + concat-stream: 1.6.2 + mkdirp: 0.5.6 + object-assign: 4.1.1 + type-is: 1.6.18 + xtend: 4.0.2 + negotiator@0.6.3: {} neo-async@2.6.2: {} @@ -5273,6 +5494,10 @@ snapshots: reusify@1.0.4: {} + rimraf@2.7.1: + dependencies: + glob: 7.2.3 + rimraf@3.0.2: dependencies: glob: 7.2.3 @@ -5307,6 +5532,10 @@ snapshots: sax@1.4.1: {} + saxes@5.0.1: + dependencies: + xmlchars: 2.2.0 + secure-json-parse@2.7.0: {} semver@5.7.2: {} @@ -5478,6 +5707,8 @@ snapshots: stream-to-buffer@0.0.1: {} + streamsearch@1.1.0: {} + strict-uri-encode@2.0.0: {} string-width@4.2.3: @@ -5618,6 +5849,8 @@ snapshots: punycode: 2.3.1 optional: true + traverse@0.3.9: {} + triple-beam@1.4.1: {} ts-deepmerge@7.0.2: {} @@ -5697,6 +5930,8 @@ snapshots: possible-typed-array-names: 1.0.0 reflect.getprototypeof: 1.0.8 + typedarray@0.0.6: {} + typescript@5.7.2: {} typical@4.0.0: {} @@ -5736,6 +5971,19 @@ snapshots: unpipe@1.0.0: {} + unzipper@0.10.14: + dependencies: + big-integer: 1.6.52 + binary: 0.3.0 + bluebird: 3.4.7 + buffer-indexof-polyfill: 1.0.2 + duplexer2: 0.1.4 + fstream: 1.0.12 + graceful-fs: 4.2.11 + listenercount: 1.0.1 + readable-stream: 2.3.8 + setimmediate: 1.0.5 + util-deprecate@1.0.2: {} util@0.12.5: @@ -5748,6 +5996,8 @@ snapshots: utils-merge@1.0.1: {} + uuid@8.3.2: {} + uuid@9.0.0: {} v8-compile-cache-lib@3.0.1: {} @@ -5888,6 +6138,10 @@ snapshots: xmlbuilder@11.0.1: {} + xmlchars@2.2.0: {} + + xtend@4.0.2: {} + y18n@5.0.8: {} yallist@2.1.2: From f98371132a09a480be85020774090283cd4774aa Mon Sep 17 00:00:00 2001 From: Kanjana Date: Mon, 21 Apr 2025 11:07:06 +0700 Subject: [PATCH 17/38] add startDate endDate in instition and receipt , codeProductRecieve in taskOrder --- .../20250418095201_add/migration.sql | 2 ++ .../20250418103300_add/migration.sql | 18 +++++++++++++++ prisma/schema.prisma | 21 ++++++++++++++--- src/controllers/04-institution-controller.ts | 23 +++++++++++++++++-- src/controllers/04-receipt-controller.ts | 4 ++++ src/controllers/05-payment-controller.ts | 2 ++ src/controllers/07-task-controller.ts | 19 +++++++++++++++ 7 files changed, 84 insertions(+), 5 deletions(-) create mode 100644 prisma/migrations/20250418095201_add/migration.sql create mode 100644 prisma/migrations/20250418103300_add/migration.sql diff --git a/prisma/migrations/20250418095201_add/migration.sql b/prisma/migrations/20250418095201_add/migration.sql new file mode 100644 index 0000000..be3e4d0 --- /dev/null +++ b/prisma/migrations/20250418095201_add/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "TaskOrder" ADD COLUMN "codeProductReceived" TEXT; diff --git a/prisma/migrations/20250418103300_add/migration.sql b/prisma/migrations/20250418103300_add/migration.sql new file mode 100644 index 0000000..2e63034 --- /dev/null +++ b/prisma/migrations/20250418103300_add/migration.sql @@ -0,0 +1,18 @@ +-- AlterTable +ALTER TABLE "Institution" ADD COLUMN "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, +ADD COLUMN "createdByUserId" TEXT, +ADD COLUMN "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, +ADD COLUMN "updatedByUserId" TEXT; + +-- AlterTable +ALTER TABLE "Payment" ADD COLUMN "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, +ADD COLUMN "updatedByUserId" TEXT; + +-- AddForeignKey +ALTER TABLE "Institution" ADD CONSTRAINT "Institution_createdByUserId_fkey" FOREIGN KEY ("createdByUserId") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "Institution" ADD CONSTRAINT "Institution_updatedByUserId_fkey" FOREIGN KEY ("updatedByUserId") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "Payment" ADD CONSTRAINT "Payment_updatedByUserId_fkey" FOREIGN KEY ("updatedByUserId") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index d40957e..f51e7fa 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -484,12 +484,15 @@ model User { flowCreated WorkflowTemplate[] @relation("FlowCreatedByUser") flowUpdated WorkflowTemplate[] @relation("FlowUpdatedByUser") invoiceCreated Invoice[] - paymentCreated Payment[] + paymentCreated Payment[] @relation("PaymentCreatedByUser") + paymentUpdated Payment[] @relation("PaymentUpdatedByUser") notificationReceive Notification[] @relation("NotificationReceiver") notificationRead Notification[] @relation("NotificationRead") notificationDelete Notification[] @relation("NotificationDelete") taskOrderCreated TaskOrder[] @relation("TaskOrderCreatedByUser") creditNoteCreated CreditNote[] @relation("CreditNoteCreatedByUser") + institutionCreated Institution[] @relation("InstitutionCreatedByUser") + institutionUpdated Institution[] @relation("InstitutionUpdatedByUser") requestWorkStepStatus RequestWorkStepStatus[] userTask UserTask[] @@ -1015,6 +1018,13 @@ model Institution { contactEmail String? contactTel String? + createdAt DateTime @default(now()) + createdBy User? @relation(name: "InstitutionCreatedByUser", fields: [createdByUserId], references: [id], onDelete: SetNull) + createdByUserId String? + updatedAt DateTime @default(now()) @updatedAt + updatedBy User? @relation(name: "InstitutionUpdatedByUser", fields: [updatedByUserId], references: [id], onDelete: SetNull) + updatedByUserId String? + bank InstitutionBank[] } @@ -1463,8 +1473,12 @@ model Payment { date DateTime? createdAt DateTime @default(now()) - createdBy User? @relation(fields: [createdByUserId], references: [id], onDelete: SetNull) + createdBy User? @relation(name: "PaymentCreatedByUser", fields: [createdByUserId], references: [id], onDelete: SetNull) createdByUserId String? + + updatedAt DateTime @default(now()) @updatedAt + updatedBy User? @relation(name: "PaymentUpdatedByUser", fields: [updatedByUserId], references: [id], onDelete: SetNull) + updatedByUserId String? } enum RequestDataStatus { @@ -1617,7 +1631,8 @@ model TaskProduct { model TaskOrder { id String @id @default(cuid()) - code String + code String + codeProductReceived String? taskName String taskOrderStatus TaskOrderStatus @default(Pending) diff --git a/src/controllers/04-institution-controller.ts b/src/controllers/04-institution-controller.ts index 76c0fc4..21611a7 100644 --- a/src/controllers/04-institution-controller.ts +++ b/src/controllers/04-institution-controller.ts @@ -17,7 +17,7 @@ import { } from "tsoa"; import prisma from "../db"; import { isUsedError, notFoundError } from "../utils/error"; -import { queryOrNot } from "../utils/relation"; +import { queryOrNot, whereDateQuery } from "../utils/relation"; import { RequestWithUser } from "../interfaces/user"; import { deleteFile, fileLocation, getFile, getPresigned, listFile, setFile } from "../utils/minio"; import HttpError from "../interfaces/http-error"; @@ -108,8 +108,19 @@ export class InstitutionController extends Controller { @Query() status?: Status, @Query() activeOnly?: boolean, @Query() group?: string, + @Query() startDate?: Date, + @Query() endDate?: Date, ) { - return this.getInstitutionListByCriteria(query, page, pageSize, status, activeOnly, group); + return this.getInstitutionListByCriteria( + query, + page, + pageSize, + status, + activeOnly, + group, + startDate, + endDate, + ); } @Post("list") @@ -122,6 +133,8 @@ export class InstitutionController extends Controller { @Query() status?: Status, @Query() activeOnly?: boolean, @Query() group?: string, + @Query() startDate?: Date, + @Query() endDate?: Date, @Body() body?: { group?: string[]; @@ -134,6 +147,7 @@ export class InstitutionController extends Controller { { name: { contains: query, mode: "insensitive" } }, { code: { contains: query, mode: "insensitive" } }, ]), + ...whereDateQuery(startDate, endDate), } satisfies Prisma.InstitutionWhereInput; const [result, total] = await prisma.$transaction([ @@ -178,6 +192,7 @@ export class InstitutionController extends Controller { body: InstitutionPayload & { status?: Status; }, + @Request() req: RequestWithUser, ) { return await prisma.$transaction(async (tx) => { const last = await tx.runningNo.upsert({ @@ -194,6 +209,8 @@ export class InstitutionController extends Controller { return await tx.institution.create({ include: { bank: true, + createdBy: true, + updatedBy: true, }, data: { ...body, @@ -204,6 +221,8 @@ export class InstitutionController extends Controller { data: body.bank ?? [], }, }, + createdByUserId: req.user.sub, + updatedByUserId: req.user.sub, }, }); }); diff --git a/src/controllers/04-receipt-controller.ts b/src/controllers/04-receipt-controller.ts index caad04b..55a52c6 100644 --- a/src/controllers/04-receipt-controller.ts +++ b/src/controllers/04-receipt-controller.ts @@ -4,6 +4,7 @@ import { Prisma } from "@prisma/client"; import { notFoundError } from "../utils/error"; import { RequestWithUser } from "../interfaces/user"; import { createPermCondition } from "../services/permission"; +import { whereDateQuery } from "../utils/relation"; const permissionCondCompany = createPermCondition((_) => true); @@ -21,6 +22,8 @@ export class ReceiptController extends Controller { @Query() quotationId?: string, @Query() debitNoteId?: string, @Query() debitNoteOnly?: boolean, + @Query() startDate?: Date, + @Query() endDate?: Date, ) { const where: Prisma.PaymentWhereInput = { paymentStatus: "PaymentSuccess", @@ -33,6 +36,7 @@ export class ReceiptController extends Controller { }, }, }, + ...whereDateQuery(startDate, endDate), }; const [result, total] = await prisma.$transaction([ diff --git a/src/controllers/05-payment-controller.ts b/src/controllers/05-payment-controller.ts index a4b7339..e5bf0a4 100644 --- a/src/controllers/05-payment-controller.ts +++ b/src/controllers/05-payment-controller.ts @@ -105,6 +105,7 @@ export class QuotationPayment extends Controller { async updatePayment( @Path() paymentId: string, @Body() body: { amount?: number; date?: Date; paymentStatus?: PaymentStatus }, + @Request() req: RequestWithUser, ) { const record = await prisma.payment.findUnique({ where: { id: paymentId }, @@ -164,6 +165,7 @@ export class QuotationPayment extends Controller { code: lastReceipt ? `RE${year}${month}${lastReceipt.value.toString().padStart(6, "0")}` : undefined, + updatedByUserId: req.user.sub, }, }); diff --git a/src/controllers/07-task-controller.ts b/src/controllers/07-task-controller.ts index 58acbbe..92fc696 100644 --- a/src/controllers/07-task-controller.ts +++ b/src/controllers/07-task-controller.ts @@ -697,12 +697,31 @@ export class TaskActionController extends Controller { if (!record) throw notFoundError("Task Order"); await prisma.$transaction(async (tx) => { + const last = await tx.runningNo.upsert({ + where: { + key: "TASK_RI", + }, + create: { + key: "TASK_RI", + value: 1, + }, + update: { + value: { increment: 1 }, + }, + }); + const current = new Date(); + const year = `${current.getFullYear()}`.padStart(2, "0"); + const month = `${current.getMonth() + 1}`.padStart(2, "0"); + + const code = `RI${year}${month}${last.value.toString().padStart(6, "0")}`; + await Promise.all([ tx.taskOrder.update({ where: { id: taskOrderId }, data: { urgent: false, taskOrderStatus: TaskOrderStatus.Complete, + codeProductReceived: code, userTask: { updateMany: { where: { taskOrderId }, From 209ef05d3de74cb5098a2f874c58fa0aafa3eb24 Mon Sep 17 00:00:00 2001 From: Methapon2001 <61303214+Methapon2001@users.noreply.github.com> Date: Tue, 22 Apr 2025 09:37:10 +0700 Subject: [PATCH 18/38] chore: change endpoint --- src/controllers/04-product-controller.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/controllers/04-product-controller.ts b/src/controllers/04-product-controller.ts index 1035beb..ed4d401 100644 --- a/src/controllers/04-product-controller.ts +++ b/src/controllers/04-product-controller.ts @@ -450,7 +450,7 @@ export class ProductController extends Controller { }); } - @Post("uploadedFile") + @Post("import-product") @Security("keycloak", MANAGE_ROLES) async importProduct( @Request() req: RequestWithUser, @@ -466,7 +466,7 @@ export class ProductController extends Controller { }); let dataName: string[] = []; - const data = await dataFile.map((item: any) => { + const data = dataFile.map((item: any) => { dataName.push(item.name); return { ...item, From 7bd1f57c329bbdac6f2f484848873fc3ae175d3a Mon Sep 17 00:00:00 2001 From: Methapon2001 <61303214+Methapon2001@users.noreply.github.com> Date: Tue, 22 Apr 2025 09:47:17 +0700 Subject: [PATCH 19/38] feat: change employee name requirement --- prisma/schema.prisma | 2 +- src/controllers/03-employee-controller.ts | 2 +- src/controllers/05-quotation-controller.ts | 8 ++++---- src/controllers/09-debit-note-controller.ts | 6 +++--- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/prisma/schema.prisma b/prisma/schema.prisma index f51e7fa..ab8414b 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -777,7 +777,7 @@ model Employee { middleName String? middleNameEN String? lastName String? - lastNameEN String + lastNameEN String? dateOfBirth DateTime? @db.Date gender String diff --git a/src/controllers/03-employee-controller.ts b/src/controllers/03-employee-controller.ts index 9699b6c..fe4c57b 100644 --- a/src/controllers/03-employee-controller.ts +++ b/src/controllers/03-employee-controller.ts @@ -117,7 +117,7 @@ type EmployeeUpdate = { middleName?: string | null; middleNameEN?: string | null; lastName?: string; - lastNameEN: string; + lastNameEN?: string; addressEN?: string; address?: string; diff --git a/src/controllers/05-quotation-controller.ts b/src/controllers/05-quotation-controller.ts index b454db3..8d53973 100644 --- a/src/controllers/05-quotation-controller.ts +++ b/src/controllers/05-quotation-controller.ts @@ -61,7 +61,7 @@ type QuotationCreate = { middleName?: string; middleNameEN?: string; lastName: string; - lastNameEN: string; + lastNameEN?: string; } )[]; @@ -114,12 +114,12 @@ type QuotationUpdate = { nationality: string; namePrefix?: string; - firstName: string; + firstName?: string; firstNameEN: string; middleName?: string; middleNameEN?: string; - lastName: string; - lastNameEN: string; + lastName?: string; + lastNameEN?: string; } )[]; diff --git a/src/controllers/09-debit-note-controller.ts b/src/controllers/09-debit-note-controller.ts index 8ff5d94..5d6307d 100644 --- a/src/controllers/09-debit-note-controller.ts +++ b/src/controllers/09-debit-note-controller.ts @@ -112,12 +112,12 @@ type DebitNoteUpdate = { gender: string; nationality: string; namePrefix?: string; - firstName: string; + firstName?: string; firstNameEN: string; middleName?: string; middleNameEN?: string; - lastName: string; - lastNameEN: string; + lastName?: string; + lastNameEN?: string; } )[]; From 35ec6cc061a4075b4e383d1103627256d6d57dd7 Mon Sep 17 00:00:00 2001 From: Methapon2001 <61303214+Methapon2001@users.noreply.github.com> Date: Tue, 22 Apr 2025 09:47:20 +0700 Subject: [PATCH 20/38] chore: migration --- .../migration.sql | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 prisma/migrations/20250422024534_remove_lastname_requirement_employee/migration.sql diff --git a/prisma/migrations/20250422024534_remove_lastname_requirement_employee/migration.sql b/prisma/migrations/20250422024534_remove_lastname_requirement_employee/migration.sql new file mode 100644 index 0000000..bd03e24 --- /dev/null +++ b/prisma/migrations/20250422024534_remove_lastname_requirement_employee/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "Employee" ALTER COLUMN "lastNameEN" DROP NOT NULL; From 40e5f495e52e915d03a72d18d6e1b2b5da15c5cc Mon Sep 17 00:00:00 2001 From: Methapon2001 <61303214+Methapon2001@users.noreply.github.com> Date: Tue, 22 Apr 2025 11:20:11 +0700 Subject: [PATCH 21/38] fix: do not check global name conflict --- src/controllers/04-product-controller.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/controllers/04-product-controller.ts b/src/controllers/04-product-controller.ts index ed4d401..9e3ba30 100644 --- a/src/controllers/04-product-controller.ts +++ b/src/controllers/04-product-controller.ts @@ -500,6 +500,7 @@ export class ProductController extends Controller { prisma.product.findMany({ where: { productGroup: { + id: productGroupId, registeredBranch: { OR: permissionCondCompany(req.user), }, From 027326a9e437bdc5019f07ae9365cacaf27113bf Mon Sep 17 00:00:00 2001 From: Kanjana Date: Tue, 22 Apr 2025 11:23:12 +0700 Subject: [PATCH 22/38] add check point price --- src/controllers/04-product-controller.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/controllers/04-product-controller.ts b/src/controllers/04-product-controller.ts index ed4d401..69b1848 100644 --- a/src/controllers/04-product-controller.ts +++ b/src/controllers/04-product-controller.ts @@ -477,10 +477,13 @@ export class ProductController extends Controller { ? "serviceFee" : "processingFee", shared: item.shared === "ใช่" ? true : false, + price: +parseFloat(item.price.replace(",", "")).toFixed(6), calcVat: item.calcVat === "ใช่" ? true : false, vatIncluded: item.vatIncluded === "รวม" ? true : false, + agentPrice: +parseFloat(item.agentPrice.replace(",", "")).toFixed(6), agentPriceCalcVat: item.agentPriceCalcVat === "ใช่" ? true : false, agentPriceVatIncluded: item.agentPriceVatIncluded === "รวม" ? true : false, + serviceCharge: +parseFloat(item.serviceCharge.replace(",", "")).toFixed(6), serviceChargeCalcVat: item.serviceChargeCalcVat === "ใช่" ? true : false, serviceChargeVatIncluded: item.serviceChargeVatIncluded === "รวม" ? true : false, }; From 94c7de89eb7c5cbae8a20655673ea8dceefe4979 Mon Sep 17 00:00:00 2001 From: Kanjana Date: Tue, 22 Apr 2025 14:02:36 +0700 Subject: [PATCH 23/38] add group from keycloak --- src/controllers/00-keycloak-controller.ts | 10 +++- src/controllers/02-user-controller.ts | 15 +++++ src/services/keycloak.ts | 68 +++++++++++++++++++++++ 3 files changed, 92 insertions(+), 1 deletion(-) diff --git a/src/controllers/00-keycloak-controller.ts b/src/controllers/00-keycloak-controller.ts index 87ca8d0..e17331f 100644 --- a/src/controllers/00-keycloak-controller.ts +++ b/src/controllers/00-keycloak-controller.ts @@ -1,5 +1,5 @@ import { Body, Controller, Delete, Get, Path, Post, Route, Security, Tags } from "tsoa"; -import { addUserRoles, listRole, removeUserRoles } from "../services/keycloak"; +import { addUserRoles, getGroup, listRole, removeUserRoles } from "../services/keycloak"; @Route("api/v1/keycloak") @Tags("Single-Sign On") @@ -44,4 +44,12 @@ export class KeycloakController extends Controller { ); if (!result) throw new Error("Failed. Cannot remove user's role."); } + + @Get("group") + async getGroup() { + const group = await getGroup(); + if (!Array.isArray(group)) throw new Error("Failed. Cannot get group(s) data from the server."); + + return group; + } } diff --git a/src/controllers/02-user-controller.ts b/src/controllers/02-user-controller.ts index 20848be..f11255b 100644 --- a/src/controllers/02-user-controller.ts +++ b/src/controllers/02-user-controller.ts @@ -27,6 +27,7 @@ import { listRole, getUserRoles, removeUserRoles, + getGroupUser, } from "../services/keycloak"; import { isSystem } from "../utils/keycloak"; import { @@ -947,3 +948,17 @@ export class UserSignatureController extends Controller { await deleteFile(fileLocation.user.signature(userId)); } } + +@Route("api/v1/user/{userId}/group") +@Tags("User") +@Security("keycloak") +export class UserGroupController extends Controller { + @Get() + async getUserGroup(@Path() userId: string) { + const groupUser = await getGroupUser(userId); + if (!Array.isArray(groupUser)) + throw new Error("Failed. Cannot get user group(s) data from the server."); + + return groupUser; + } +} diff --git a/src/services/keycloak.ts b/src/services/keycloak.ts index db2d15d..3919b42 100644 --- a/src/services/keycloak.ts +++ b/src/services/keycloak.ts @@ -346,6 +346,74 @@ export async function removeUserRoles(userId: string, roles: { id: string; name: return true; } +export async function getGroup() { + const res = await fetch(`${KC_URL}/admin/realms/${KC_REALM}/groups`, { + headers: { + authorization: `Bearer ${await getToken()}`, + "content-type": `application/json`, + }, + method: "GET", + }); + + const dataMainGroup = await res.json(); + + const fetchSubGroups = async (group: any) => { + const resSub = await fetch(`${KC_URL}/admin/realms/${KC_REALM}/groups/${group.id}/children`, { + headers: { + authorization: `Bearer ${await getToken()}`, + "content-type": `application/json`, + }, + method: "GET", + }); + + const dataSubGroup = await resSub.json(); + let fullSubGroup = await Promise.all( + dataSubGroup.map(async (subGroupsData: any) => { + if (subGroupsData.subGroupCount > 0) { + return await fetchSubGroups(subGroupsData); + } else { + return { + id: subGroupsData.id, + name: subGroupsData.name, + path: subGroupsData.path, + subGroupCount: subGroupsData.subGroupCount, + subGroups: [], + }; + } + }), + ); + return { + id: group.id, + name: group.name, + path: group.path, + subGroupCount: group.subGroupCount, + subGroups: fullSubGroup, + }; + }; + + const fullMainGroup = await Promise.all(dataMainGroup.map(fetchSubGroups)); + return fullMainGroup; +} + +export async function getGroupUser(userId: string) { + const res = await fetch(`${KC_URL}/admin/realms/${KC_REALM}/users/${userId}/groups`, { + headers: { + authorization: `Bearer ${await getToken()}`, + "content-type": `application/json`, + }, + method: "GET", + }); + + const data = await res.json(); + return data.map((item: any) => { + return { + id: item.id, + name: item.name, + path: item.path, + }; + }); +} + export default { createUser, listRole, From 8b26f91dbae6941ab72a953cd9a6d41dcb6080a9 Mon Sep 17 00:00:00 2001 From: Kanjana Date: Tue, 22 Apr 2025 15:12:24 +0700 Subject: [PATCH 24/38] getGroup --- src/services/keycloak.ts | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/src/services/keycloak.ts b/src/services/keycloak.ts index 3919b42..3875b18 100644 --- a/src/services/keycloak.ts +++ b/src/services/keycloak.ts @@ -347,7 +347,7 @@ export async function removeUserRoles(userId: string, roles: { id: string; name: } export async function getGroup() { - const res = await fetch(`${KC_URL}/admin/realms/${KC_REALM}/groups`, { + const res = await fetch(`${KC_URL}/admin/realms/${KC_REALM}/groups?q`, { headers: { authorization: `Bearer ${await getToken()}`, "content-type": `application/json`, @@ -356,20 +356,10 @@ export async function getGroup() { }); const dataMainGroup = await res.json(); - const fetchSubGroups = async (group: any) => { - const resSub = await fetch(`${KC_URL}/admin/realms/${KC_REALM}/groups/${group.id}/children`, { - headers: { - authorization: `Bearer ${await getToken()}`, - "content-type": `application/json`, - }, - method: "GET", - }); - - const dataSubGroup = await resSub.json(); let fullSubGroup = await Promise.all( - dataSubGroup.map(async (subGroupsData: any) => { - if (subGroupsData.subGroupCount > 0) { + group.subGroups.map(async (subGroupsData: any) => { + if (group.subGroupCount > 0) { return await fetchSubGroups(subGroupsData); } else { return { From 3193403f9017936cee118ac431c304c49d32f493 Mon Sep 17 00:00:00 2001 From: Kanjana Date: Tue, 22 Apr 2025 16:27:13 +0700 Subject: [PATCH 25/38] check price where null --- src/controllers/04-product-controller.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/controllers/04-product-controller.ts b/src/controllers/04-product-controller.ts index ccb0b6a..dd17c5e 100644 --- a/src/controllers/04-product-controller.ts +++ b/src/controllers/04-product-controller.ts @@ -477,13 +477,15 @@ export class ProductController extends Controller { ? "serviceFee" : "processingFee", shared: item.shared === "ใช่" ? true : false, - price: +parseFloat(item.price.replace(",", "")).toFixed(6), + price: item?.price ? +parseFloat(item.price.replace(",", "")).toFixed(6) : 0, calcVat: item.calcVat === "ใช่" ? true : false, vatIncluded: item.vatIncluded === "รวม" ? true : false, - agentPrice: +parseFloat(item.agentPrice.replace(",", "")).toFixed(6), + agentPrice: item?.agentPrice ? +parseFloat(item.agentPrice.replace(",", "")).toFixed(6) : 0, agentPriceCalcVat: item.agentPriceCalcVat === "ใช่" ? true : false, agentPriceVatIncluded: item.agentPriceVatIncluded === "รวม" ? true : false, - serviceCharge: +parseFloat(item.serviceCharge.replace(",", "")).toFixed(6), + serviceCharge: item?.serviceCharge + ? +parseFloat(item.serviceCharge.replace(",", "")).toFixed(6) + : 0, serviceChargeCalcVat: item.serviceChargeCalcVat === "ใช่" ? true : false, serviceChargeVatIncluded: item.serviceChargeVatIncluded === "รวม" ? true : false, }; From 601deffce404878b607f620727c4d69741f0c3ba Mon Sep 17 00:00:00 2001 From: Methapon2001 <61303214+Methapon2001@users.noreply.github.com> Date: Wed, 23 Apr 2025 09:01:10 +0700 Subject: [PATCH 26/38] feat: check for type before try parse field --- src/controllers/04-product-controller.ts | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/controllers/04-product-controller.ts b/src/controllers/04-product-controller.ts index dd17c5e..80b7c71 100644 --- a/src/controllers/04-product-controller.ts +++ b/src/controllers/04-product-controller.ts @@ -477,15 +477,22 @@ export class ProductController extends Controller { ? "serviceFee" : "processingFee", shared: item.shared === "ใช่" ? true : false, - price: item?.price ? +parseFloat(item.price.replace(",", "")).toFixed(6) : 0, + price: + typeof item.price === "number" + ? item.price + : +parseFloat(item.price.replace(",", "")).toFixed(6), calcVat: item.calcVat === "ใช่" ? true : false, vatIncluded: item.vatIncluded === "รวม" ? true : false, - agentPrice: item?.agentPrice ? +parseFloat(item.agentPrice.replace(",", "")).toFixed(6) : 0, + agentPrice: + typeof item.agentPrice === "number" + ? item.agentPrice + : +parseFloat(item.agentPrice.replace(",", "")).toFixed(6), agentPriceCalcVat: item.agentPriceCalcVat === "ใช่" ? true : false, agentPriceVatIncluded: item.agentPriceVatIncluded === "รวม" ? true : false, - serviceCharge: item?.serviceCharge - ? +parseFloat(item.serviceCharge.replace(",", "")).toFixed(6) - : 0, + serviceCharge: + typeof item.serviceCharge === "number" + ? item.serviceCharge + : +parseFloat(item.serviceCharge.replace(",", "")).toFixed(6), serviceChargeCalcVat: item.serviceChargeCalcVat === "ใช่" ? true : false, serviceChargeVatIncluded: item.serviceChargeVatIncluded === "รวม" ? true : false, }; From d15aa488c15d02af98f6aed3c6a1816993698e34 Mon Sep 17 00:00:00 2001 From: Methapon2001 <61303214+Methapon2001@users.noreply.github.com> Date: Wed, 23 Apr 2025 09:03:03 +0700 Subject: [PATCH 27/38] feat: fallback to 0 if empty --- src/controllers/04-product-controller.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/controllers/04-product-controller.ts b/src/controllers/04-product-controller.ts index 80b7c71..3f2ff4e 100644 --- a/src/controllers/04-product-controller.ts +++ b/src/controllers/04-product-controller.ts @@ -480,19 +480,19 @@ export class ProductController extends Controller { price: typeof item.price === "number" ? item.price - : +parseFloat(item.price.replace(",", "")).toFixed(6), + : +parseFloat(item.price?.replace(",", "") || "0").toFixed(6), calcVat: item.calcVat === "ใช่" ? true : false, vatIncluded: item.vatIncluded === "รวม" ? true : false, agentPrice: typeof item.agentPrice === "number" ? item.agentPrice - : +parseFloat(item.agentPrice.replace(",", "")).toFixed(6), + : +parseFloat(item.agentPrice?.replace(",", "") || "0").toFixed(6), agentPriceCalcVat: item.agentPriceCalcVat === "ใช่" ? true : false, agentPriceVatIncluded: item.agentPriceVatIncluded === "รวม" ? true : false, serviceCharge: typeof item.serviceCharge === "number" ? item.serviceCharge - : +parseFloat(item.serviceCharge.replace(",", "")).toFixed(6), + : +parseFloat(item.serviceCharge?.replace(",", "") || "0").toFixed(6), serviceChargeCalcVat: item.serviceChargeCalcVat === "ใช่" ? true : false, serviceChargeVatIncluded: item.serviceChargeVatIncluded === "รวม" ? true : false, }; From 1d6224da73977f056390d05fea24f98869d27cfc Mon Sep 17 00:00:00 2001 From: Kanjana Date: Thu, 24 Apr 2025 10:41:22 +0700 Subject: [PATCH 28/38] add query in keycloak --- src/controllers/00-keycloak-controller.ts | 7 ++++--- src/services/keycloak.ts | 4 ++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/controllers/00-keycloak-controller.ts b/src/controllers/00-keycloak-controller.ts index e17331f..1d5d9b9 100644 --- a/src/controllers/00-keycloak-controller.ts +++ b/src/controllers/00-keycloak-controller.ts @@ -1,4 +1,4 @@ -import { Body, Controller, Delete, Get, Path, Post, Route, Security, Tags } from "tsoa"; +import { Body, Controller, Delete, Get, Path, Post, Query, Route, Security, Tags } from "tsoa"; import { addUserRoles, getGroup, listRole, removeUserRoles } from "../services/keycloak"; @Route("api/v1/keycloak") @@ -46,8 +46,9 @@ export class KeycloakController extends Controller { } @Get("group") - async getGroup() { - const group = await getGroup(); + async getGroup(@Query() query: string = "") { + const querySearch = query === "" ? "q" : `search=${query}`; + const group = await getGroup(querySearch); if (!Array.isArray(group)) throw new Error("Failed. Cannot get group(s) data from the server."); return group; diff --git a/src/services/keycloak.ts b/src/services/keycloak.ts index 3875b18..5352b2c 100644 --- a/src/services/keycloak.ts +++ b/src/services/keycloak.ts @@ -346,8 +346,8 @@ export async function removeUserRoles(userId: string, roles: { id: string; name: return true; } -export async function getGroup() { - const res = await fetch(`${KC_URL}/admin/realms/${KC_REALM}/groups?q`, { +export async function getGroup(query: string) { + const res = await fetch(`${KC_URL}/admin/realms/${KC_REALM}/groups?${query}`, { headers: { authorization: `Bearer ${await getToken()}`, "content-type": `application/json`, From afadea2d64754ab81c1a85501eb996ef80647e35 Mon Sep 17 00:00:00 2001 From: Methapon2001 <61303214+Methapon2001@users.noreply.github.com> Date: Thu, 24 Apr 2025 10:49:59 +0700 Subject: [PATCH 29/38] fix: search not work as expected --- src/controllers/06-request-list-controller.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/controllers/06-request-list-controller.ts b/src/controllers/06-request-list-controller.ts index 6b5e5f8..42b32ad 100644 --- a/src/controllers/06-request-list-controller.ts +++ b/src/controllers/06-request-list-controller.ts @@ -104,6 +104,8 @@ export class RequestDataController extends Controller { ], }, }, + }, + { employee: { OR: [ { From 109494c6d7619d62a415dd1c13645215b8aab507 Mon Sep 17 00:00:00 2001 From: Kanjana Date: Thu, 24 Apr 2025 11:40:02 +0700 Subject: [PATCH 30/38] add responsibleGroup in step --- prisma/migrations/20250424042834_add/migration.sql | 11 +++++++++++ prisma/schema.prisma | 10 ++++++++++ src/controllers/04-flow-template-controller.ts | 12 ++++++++++++ 3 files changed, 33 insertions(+) create mode 100644 prisma/migrations/20250424042834_add/migration.sql diff --git a/prisma/migrations/20250424042834_add/migration.sql b/prisma/migrations/20250424042834_add/migration.sql new file mode 100644 index 0000000..13999bc --- /dev/null +++ b/prisma/migrations/20250424042834_add/migration.sql @@ -0,0 +1,11 @@ +-- CreateTable +CREATE TABLE "WorkflowTemplateStepGroup" ( + "id" TEXT NOT NULL, + "group" TEXT NOT NULL, + "workflowTemplateStepId" TEXT NOT NULL, + + CONSTRAINT "WorkflowTemplateStepGroup_pkey" PRIMARY KEY ("id") +); + +-- AddForeignKey +ALTER TABLE "WorkflowTemplateStepGroup" ADD CONSTRAINT "WorkflowTemplateStepGroup_workflowTemplateStepId_fkey" FOREIGN KEY ("workflowTemplateStepId") REFERENCES "WorkflowTemplateStep"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index ab8414b..bc100b5 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -1089,6 +1089,15 @@ model WorkflowTemplateStepInstitution { workflowTemplateStepId String } +model WorkflowTemplateStepGroup { + id String @id @default(cuid()) + + group String + + workflowTemplateStep WorkflowTemplateStep @relation(fields: [workflowTemplateStepId], references: [id], onDelete: Cascade) + workflowTemplateStepId String +} + model WorkflowTemplateStep { id String @id @default(cuid()) @@ -1099,6 +1108,7 @@ model WorkflowTemplateStep { value WorkflowTemplateStepValue[] // NOTE: For enum or options type responsiblePerson WorkflowTemplateStepUser[] responsibleInstitution WorkflowTemplateStepInstitution[] + responsibleGroup WorkflowTemplateStepGroup[] messengerByArea Boolean @default(false) attributes Json? diff --git a/src/controllers/04-flow-template-controller.ts b/src/controllers/04-flow-template-controller.ts index 97bd1b9..940fd1d 100644 --- a/src/controllers/04-flow-template-controller.ts +++ b/src/controllers/04-flow-template-controller.ts @@ -37,6 +37,7 @@ type WorkflowPayload = { attributes?: { [key: string]: any }; responsiblePersonId?: string[]; responsibleInstitution?: string[]; + responsibleGroup?: string[]; messengerByArea?: boolean; }[]; registeredBranchId?: string; @@ -89,6 +90,7 @@ export class FlowTemplateController extends Controller { include: { user: true }, }, responsibleInstitution: true, + responsibleGroup: true, }, orderBy: { order: "asc" }, }, @@ -106,6 +108,7 @@ export class FlowTemplateController extends Controller { step: r.step.map((v) => ({ ...v, responsibleInstitution: v.responsibleInstitution.map((institution) => institution.group), + responsibleGroup: v.responsibleGroup.map((group) => group.group), })), })), page, @@ -126,6 +129,7 @@ export class FlowTemplateController extends Controller { include: { user: true }, }, responsibleInstitution: true, + responsibleGroup: true, }, }, }, @@ -140,6 +144,7 @@ export class FlowTemplateController extends Controller { step: record.step.map((v) => ({ ...v, responsibleInstitution: v.responsibleInstitution.map((institution) => institution.group), + responsibleGroup: v.responsibleGroup.map((group) => group.group), })), }; } @@ -215,6 +220,9 @@ export class FlowTemplateController extends Controller { responsibleInstitution: { create: v.responsibleInstitution?.map((group) => ({ group })), }, + responsibleGroup: { + create: v.responsibleGroup?.map((group) => ({ group })), + }, })), }, }, @@ -295,6 +303,10 @@ export class FlowTemplateController extends Controller { deleteMany: {}, create: v.responsibleInstitution?.map((group) => ({ group })), }, + responsibleGroup: { + deleteMany: {}, + create: v.responsibleGroup?.map((group) => ({ group })), + }, }, })), }, From d92e3bc57ded01dcef3029f6372eb5f5f6bf120b Mon Sep 17 00:00:00 2001 From: Methapon2001 <61303214+Methapon2001@users.noreply.github.com> Date: Thu, 24 Apr 2025 13:55:22 +0700 Subject: [PATCH 31/38] feat: add relation to response --- src/controllers/07-task-controller.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/controllers/07-task-controller.ts b/src/controllers/07-task-controller.ts index 92fc696..5e9977c 100644 --- a/src/controllers/07-task-controller.ts +++ b/src/controllers/07-task-controller.ts @@ -200,6 +200,7 @@ export class TaskController extends Controller { step: { include: { value: true, + responsibleGroup: true, responsiblePerson: { include: { user: true }, }, From 08b9ddd2e1c255d5ee5455d46ff3353dd962d950 Mon Sep 17 00:00:00 2001 From: Methapon2001 <61303214+Methapon2001@users.noreply.github.com> Date: Thu, 24 Apr 2025 14:07:30 +0700 Subject: [PATCH 32/38] feat: reponse relation responsible gropu in request list --- src/controllers/06-request-list-controller.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/controllers/06-request-list-controller.ts b/src/controllers/06-request-list-controller.ts index 42b32ad..3289473 100644 --- a/src/controllers/06-request-list-controller.ts +++ b/src/controllers/06-request-list-controller.ts @@ -849,6 +849,7 @@ export class RequestListController extends Controller { include: { user: true }, }, responsibleInstitution: true, + responsibleGroup: true, }, }, }, From 4dbe89f2903ac69294a10b555989ad10b5c0ddb1 Mon Sep 17 00:00:00 2001 From: Methapon2001 <61303214+Methapon2001@users.noreply.github.com> Date: Thu, 24 Apr 2025 14:09:57 +0700 Subject: [PATCH 33/38] feat: response more relation --- src/controllers/06-request-list-controller.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/controllers/06-request-list-controller.ts b/src/controllers/06-request-list-controller.ts index 3289473..387c1d5 100644 --- a/src/controllers/06-request-list-controller.ts +++ b/src/controllers/06-request-list-controller.ts @@ -174,6 +174,7 @@ export class RequestDataController extends Controller { include: { user: true }, }, responsibleInstitution: true, + responsibleGroup: true, }, }, }, @@ -789,6 +790,7 @@ export class RequestListController extends Controller { include: { user: true }, }, responsibleInstitution: true, + responsibleGroup: true, }, }, }, From 92104c05cb313c5018ecb02ffb44af2ef68a88b9 Mon Sep 17 00:00:00 2001 From: Methapon2001 <61303214+Methapon2001@users.noreply.github.com> Date: Thu, 24 Apr 2025 14:53:04 +0700 Subject: [PATCH 34/38] feat: filter responsible only --- src/controllers/06-request-list-controller.ts | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/src/controllers/06-request-list-controller.ts b/src/controllers/06-request-list-controller.ts index 387c1d5..ca5398f 100644 --- a/src/controllers/06-request-list-controller.ts +++ b/src/controllers/06-request-list-controller.ts @@ -32,6 +32,7 @@ import { notFoundError } from "../utils/error"; import { deleteFile, fileLocation, getFile, getPresigned, listFile, setFile } from "../utils/minio"; import HttpError from "../interfaces/http-error"; import HttpStatus from "../interfaces/http-status"; +import { getGroupUser } from "../services/keycloak"; // User in company can edit. const permissionCheck = createPermCheck((_) => true); @@ -136,9 +137,24 @@ export class RequestDataController extends Controller { workflow: { step: { some: { - responsiblePerson: { - some: { userId: req.user.sub }, - }, + OR: [ + { + responsiblePerson: { + some: { userId: req.user.sub }, + }, + }, + { + responsibleGroup: { + some: { + group: { + in: await getGroupUser(req.user.sub).then((r) => + r.map(({ name }: { name: string }) => name), + ), + }, + }, + }, + }, + ], }, }, }, From a57e8d939f20c00d921e0f787728b007c84cfee1 Mon Sep 17 00:00:00 2001 From: Kanjana Date: Thu, 24 Apr 2025 15:10:42 +0700 Subject: [PATCH 35/38] async await in getGroup --- src/services/keycloak.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/services/keycloak.ts b/src/services/keycloak.ts index 5352b2c..494e19e 100644 --- a/src/services/keycloak.ts +++ b/src/services/keycloak.ts @@ -358,9 +358,9 @@ export async function getGroup(query: string) { const dataMainGroup = await res.json(); const fetchSubGroups = async (group: any) => { let fullSubGroup = await Promise.all( - group.subGroups.map(async (subGroupsData: any) => { + group.subGroups.map((subGroupsData: any) => { if (group.subGroupCount > 0) { - return await fetchSubGroups(subGroupsData); + return fetchSubGroups(subGroupsData); } else { return { id: subGroupsData.id, From ffb1ce2d40fc581c83473820af2a6729916032b7 Mon Sep 17 00:00:00 2001 From: Methapon2001 <61303214+Methapon2001@users.noreply.github.com> Date: Thu, 24 Apr 2025 17:55:18 +0700 Subject: [PATCH 36/38] feat: allow multiple import nationality for user --- prisma/schema.prisma | 10 +++++++++- src/controllers/02-user-controller.ts | 21 ++++++++++++++++++--- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/prisma/schema.prisma b/prisma/schema.prisma index bc100b5..ab19cb5 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -366,6 +366,14 @@ enum UserType { AGENCY } +model UserImportNationality { + id String @id @default(cuid()) + name String + + user User @relation(fields: [userId], references: [id]) + userId String +} + model User { id String @id @default(cuid()) @@ -424,7 +432,7 @@ model User { licenseExpireDate DateTime? @db.Date sourceNationality String? - importNationality String? + importNationality UserImportNationality[] trainingPlace String? responsibleArea UserResponsibleArea[] diff --git a/src/controllers/02-user-controller.ts b/src/controllers/02-user-controller.ts index f11255b..2c864e6 100644 --- a/src/controllers/02-user-controller.ts +++ b/src/controllers/02-user-controller.ts @@ -99,7 +99,7 @@ type UserCreate = { licenseIssueDate?: Date | null; licenseExpireDate?: Date | null; sourceNationality?: string | null; - importNationality?: string | null; + importNationality?: string[] | null; trainingPlace?: string | null; responsibleArea?: string[] | null; birthDate?: Date | null; @@ -161,7 +161,7 @@ type UserUpdate = { licenseIssueDate?: Date | null; licenseExpireDate?: Date | null; sourceNationality?: string | null; - importNationality?: string | null; + importNationality?: string[] | null; trainingPlace?: string | null; responsibleArea?: string[] | null; birthDate?: Date | null; @@ -383,6 +383,7 @@ export class UserController extends Controller { prisma.user.findMany({ orderBy: [{ statusOrder: "asc" }, { createdAt: "asc" }], include: { + importNationality: true, responsibleArea: true, province: true, district: true, @@ -401,6 +402,7 @@ export class UserController extends Controller { return { result: result.map((v) => ({ ...v, + importNationality: v.importNationality.map((v) => v.name), responsibleArea: v.responsibleArea.map((v) => v.area), branch: includeBranch ? v.branch.map((a) => a.branch) : undefined, })), @@ -415,6 +417,7 @@ export class UserController extends Controller { async getUserById(@Path() userId: string) { const record = await prisma.user.findFirst({ include: { + importNationality: true, province: true, district: true, subDistrict: true, @@ -426,7 +429,11 @@ export class UserController extends Controller { if (!record) throw notFoundError("User"); - return record; + const { importNationality, ...rest } = record; + + return Object.assign(rest, { + importNationality: importNationality.map((v) => v.name), + }); } @Post() @@ -528,6 +535,9 @@ export class UserController extends Controller { create: rest.responsibleArea.map((v) => ({ area: v })), } : undefined, + importNationality: { + createMany: { data: rest.importNationality?.map((v) => ({ name: v })) || [] }, + }, statusOrder: +(rest.status === "INACTIVE"), username, userRole: role.name, @@ -683,6 +693,7 @@ export class UserController extends Controller { const record = await prisma.user.update({ include: { + importNationality: true, province: true, district: true, subDistrict: true, @@ -697,6 +708,10 @@ export class UserController extends Controller { create: rest.responsibleArea.map((v) => ({ area: v })), } : undefined, + importNationality: { + deleteMany: {}, + createMany: { data: rest.importNationality?.map((v) => ({ name: v })) || [] }, + }, statusOrder: +(rest.status === "INACTIVE"), userRole, province: connectOrDisconnect(provinceId), From 7bc12f00b0dbcd1ab8d35400bd601748c10bfa6d Mon Sep 17 00:00:00 2001 From: Methapon2001 <61303214+Methapon2001@users.noreply.github.com> Date: Thu, 24 Apr 2025 17:55:21 +0700 Subject: [PATCH 37/38] chore: migration --- .../migration.sql | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 prisma/migrations/20250424094947_change_import_nationality_to_relation/migration.sql diff --git a/prisma/migrations/20250424094947_change_import_nationality_to_relation/migration.sql b/prisma/migrations/20250424094947_change_import_nationality_to_relation/migration.sql new file mode 100644 index 0000000..c961d96 --- /dev/null +++ b/prisma/migrations/20250424094947_change_import_nationality_to_relation/migration.sql @@ -0,0 +1,20 @@ +/* + Warnings: + + - You are about to drop the column `importNationality` on the `User` table. All the data in the column will be lost. + +*/ +-- AlterTable +ALTER TABLE "User" DROP COLUMN "importNationality"; + +-- CreateTable +CREATE TABLE "UserImportNationality" ( + "id" TEXT NOT NULL, + "name" TEXT NOT NULL, + "userId" TEXT NOT NULL, + + CONSTRAINT "UserImportNationality_pkey" PRIMARY KEY ("id") +); + +-- AddForeignKey +ALTER TABLE "UserImportNationality" ADD CONSTRAINT "UserImportNationality_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE; From 7fe0512a2f3ba5d5bb9ebd533d5b53e5e1b074ea Mon Sep 17 00:00:00 2001 From: Kanjana Date: Fri, 25 Apr 2025 11:59:46 +0700 Subject: [PATCH 38/38] add troubleshooting controller and field otherNationality --- .../20250425040315_add/migration.sql | 2 ++ .../20250425041426_add/migration.sql | 2 ++ prisma/schema.prisma | 32 ++++++++++--------- src/controllers/03-employee-controller.ts | 2 ++ .../03-employee-passport-controller.ts | 1 + src/controllers/05-quotation-controller.ts | 4 +++ src/controllers/09-debit-note-controller.ts | 2 ++ .../10-troubleshooting-controller.ts | 25 +++++++++++++++ 8 files changed, 55 insertions(+), 15 deletions(-) create mode 100644 prisma/migrations/20250425040315_add/migration.sql create mode 100644 prisma/migrations/20250425041426_add/migration.sql create mode 100644 src/controllers/10-troubleshooting-controller.ts diff --git a/prisma/migrations/20250425040315_add/migration.sql b/prisma/migrations/20250425040315_add/migration.sql new file mode 100644 index 0000000..9393a9a --- /dev/null +++ b/prisma/migrations/20250425040315_add/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "Employee" ADD COLUMN "otherNationality" TEXT; diff --git a/prisma/migrations/20250425041426_add/migration.sql b/prisma/migrations/20250425041426_add/migration.sql new file mode 100644 index 0000000..a004873 --- /dev/null +++ b/prisma/migrations/20250425041426_add/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "EmployeePassport" ADD COLUMN "otherNationality" TEXT; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index ab19cb5..73be520 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -787,9 +787,10 @@ model Employee { lastName String? lastNameEN String? - dateOfBirth DateTime? @db.Date - gender String - nationality String + dateOfBirth DateTime? @db.Date + gender String + nationality String + otherNationality String? address String? addressEN String? @@ -864,18 +865,19 @@ model EmployeePassport { issuePlace String previousPassportRef String? - workerStatus String? - nationality String? - namePrefix String? - firstName String? - firstNameEN String? - middleName String? - middleNameEN String? - lastName String? - lastNameEN String? - gender String? - birthDate String? - birthCountry String? + workerStatus String? + nationality String? + otherNationality String? + namePrefix String? + firstName String? + firstNameEN String? + middleName String? + middleNameEN String? + lastName String? + lastNameEN String? + gender String? + birthDate String? + birthCountry String? employee Employee @relation(fields: [employeeId], references: [id], onDelete: Cascade) employeeId String diff --git a/src/controllers/03-employee-controller.ts b/src/controllers/03-employee-controller.ts index fe4c57b..03ab1cc 100644 --- a/src/controllers/03-employee-controller.ts +++ b/src/controllers/03-employee-controller.ts @@ -74,6 +74,7 @@ type EmployeeCreate = { dateOfBirth?: Date | null; gender: string; nationality: string; + otherNationality?: string; namePrefix?: string | null; firstName?: string; @@ -110,6 +111,7 @@ type EmployeeUpdate = { dateOfBirth?: Date; gender?: string; nationality?: string; + otherNationality?: string; namePrefix?: string | null; firstName?: string; diff --git a/src/controllers/03-employee-passport-controller.ts b/src/controllers/03-employee-passport-controller.ts index 8f8c253..5509373 100644 --- a/src/controllers/03-employee-passport-controller.ts +++ b/src/controllers/03-employee-passport-controller.ts @@ -43,6 +43,7 @@ type EmployeePassportPayload = { workerStatus: string; nationality: string; + otherNationality: string; namePrefix?: string | null; firstName: string; firstNameEN: string; diff --git a/src/controllers/05-quotation-controller.ts b/src/controllers/05-quotation-controller.ts index 8d53973..9fddd04 100644 --- a/src/controllers/05-quotation-controller.ts +++ b/src/controllers/05-quotation-controller.ts @@ -55,6 +55,7 @@ type QuotationCreate = { dateOfBirth: Date; gender: string; nationality: string; + otherNationality?: string; namePrefix?: string; firstName: string; firstNameEN: string; @@ -112,6 +113,7 @@ type QuotationUpdate = { dateOfBirth: Date; gender: string; nationality: string; + otherNationality?: string; namePrefix?: string; firstName?: string; @@ -1008,6 +1010,7 @@ export class QuotationActionController extends Controller { dateOfBirth: Date; gender: string; nationality: string; + otherNationality?: string; namePrefix?: string; firstName: string; firstNameEN: string; @@ -1030,6 +1033,7 @@ export class QuotationActionController extends Controller { dateOfBirth: Date; gender: string; nationality: string; + otherNationality?: string; namePrefix?: string; firstName: string; firstNameEN: string; diff --git a/src/controllers/09-debit-note-controller.ts b/src/controllers/09-debit-note-controller.ts index 5d6307d..118575b 100644 --- a/src/controllers/09-debit-note-controller.ts +++ b/src/controllers/09-debit-note-controller.ts @@ -76,6 +76,7 @@ type DebitNoteCreate = { dateOfBirth: Date; gender: string; nationality: string; + otherNationality: string; namePrefix?: string; firstName: string; firstNameEN: string; @@ -111,6 +112,7 @@ type DebitNoteUpdate = { dateOfBirth: Date; gender: string; nationality: string; + otherNationality: string; namePrefix?: string; firstName?: string; firstNameEN: string; diff --git a/src/controllers/10-troubleshooting-controller.ts b/src/controllers/10-troubleshooting-controller.ts new file mode 100644 index 0000000..2c5418a --- /dev/null +++ b/src/controllers/10-troubleshooting-controller.ts @@ -0,0 +1,25 @@ +import express from "express"; +import { Controller, Get, Path, Request, Route } from "tsoa"; +import { getFile } from "../utils/minio"; + +@Route("api/v1/troubleshooting") +export class TroubleshootingController extends Controller { + @Get() + async get(@Request() req: express.Request) { + return req.res?.redirect(await getFile(".troubleshooting/toc.json")); + } + + @Get("{category}/assets/{name}") + async getAsset(@Request() req: express.Request, @Path() category: string, @Path() name: string) { + return req.res?.redirect(await getFile(`.troubleshooting/${category}/assets/${name}`)); + } + + @Get("{category}/page/{page}") + async getContent( + @Request() req: express.Request, + @Path() category: string, + @Path() page: string, + ) { + return req.res?.redirect(await getFile(`.troubleshooting/${category}/${page}.md`)); + } +}