From c6c187b8d3e428537aa71bf33b0199b9044e9800 Mon Sep 17 00:00:00 2001 From: Methapon2001 <61303214+Methapon2001@users.noreply.github.com> Date: Tue, 25 Mar 2025 13:48:31 +0700 Subject: [PATCH] feat: add request work support --- package.json | 2 + pnpm-lock.yaml | 68 +++++++++++++++++++ src/controllers/00-doc-template-controller.ts | 61 +++++++++++++++-- 3 files changed, 127 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 6133081..b576e46 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "author": "Frappe'T", "license": "ISC", "devDependencies": { + "@types/barcode": "^0.0.33", "@types/cors": "^2.8.17", "@types/express": "^4.17.21", "@types/morgan": "^1.9.9", @@ -38,6 +39,7 @@ "@prisma/client": "^6.3.0", "@scalar/express-api-reference": "^0.4.182", "@tsoa/runtime": "^6.6.0", + "barcode": "^0.1.0", "cors": "^2.8.5", "cron": "^3.3.1", "dayjs": "^1.11.13", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 85397d2..bb7ddd5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -23,6 +23,9 @@ importers: '@tsoa/runtime': specifier: ^6.6.0 version: 6.6.0 + barcode: + specifier: ^0.1.0 + version: 0.1.0 cors: specifier: ^2.8.5 version: 2.8.5 @@ -84,6 +87,9 @@ importers: specifier: ^0.19.0 version: 0.19.0 devDependencies: + '@types/barcode': + specifier: ^0.0.33 + version: 0.0.33 '@types/cors': specifier: ^2.8.17 version: 2.8.17 @@ -420,6 +426,9 @@ packages: '@types/accepts@1.3.7': resolution: {integrity: sha512-Pay9fq2lM2wXPWbteBsRAGiWH2hig4ZE2asK+mm7kUzlxRTfL961rj89I6zV/E3PcIkDqyuBEcMxFT7rccugeQ==} + '@types/barcode@0.0.33': + resolution: {integrity: sha512-PotMNcya1L26CgWRfLNxNw0VIyhb104YhXB12e6xjoo/+hZaitrpRnToU9Ru0yTfty6tGc1Bk/1JNcGs9YVKXQ==} + '@types/body-parser@1.19.5': resolution: {integrity: sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==} @@ -611,6 +620,12 @@ packages: array-flatten@1.1.1: resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==} + array-parallel@0.1.3: + resolution: {integrity: sha512-TDPTwSWW5E4oiFiKmz6RGJ/a80Y91GuLgUYuLd49+XBS75tYo8PNgaT2K/OxuQYqkoI852MDGBorg9OcUSTQ8w==} + + array-series@0.1.5: + resolution: {integrity: sha512-L0XlBwfx9QetHOsbLDrE/vh2t018w9462HM3iaFfxRiK83aJjAt/Ja3NMkOW7FICwWTlQBa3ZbL5FKhuQWkDrg==} + array-union@2.1.0: resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} engines: {node: '>=8'} @@ -657,6 +672,9 @@ packages: balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + barcode@0.1.0: + resolution: {integrity: sha512-GslbXakjG61fwHnIN/vrUkPsa61WVAJDnb7jAwmbjRW5bZdwINymo1+FgjYJrGf2MDHxAt3bUWgmEMF8ETZxHQ==} + base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} @@ -877,6 +895,14 @@ packages: dayjs@1.11.13: resolution: {integrity: sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==} + debug@0.7.0: + resolution: {integrity: sha512-UWZnvGiX9tQgtrsA+mhGLKnUFvr1moempl9IvqQKyFnEgN0T4kpCE+KJcqTLcVxQjRVRnLF3VSE1Hchki5N98g==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + debug@2.6.9: resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} peerDependencies: @@ -1232,6 +1258,11 @@ packages: resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} engines: {node: '>=10'} + gm@1.16.0: + resolution: {integrity: sha512-b5oVGr8wCI7VNfjzeXiFocCZrcpkRUxSoVYfksBMEp/jo2nYwRKhcfOURarxFwjXyW1GvEY2EmmupVLnh0vXjg==} + engines: {node: '>= 0.8.0'} + deprecated: The gm module has been sunset. Please migrate to an alternative. https://github.com/aheckmann/gm?tab=readme-ov-file#2025-02-24-this-project-is-not-maintained + gopd@1.2.0: resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} engines: {node: '>= 0.4'} @@ -2337,6 +2368,10 @@ packages: stream-json@1.9.1: resolution: {integrity: sha512-uWkjJ+2Nt/LO9Z/JyKZbMusL8Dkh97uUBTv3AJQ74y07lVahLY4eEFsPsE97pxYBwr8nnjMAIch5eqI0gPShyw==} + stream-to-buffer@0.0.1: + resolution: {integrity: sha512-LsvisgE3iThboRqA+XLmtnY9ktPLVPOj3zZxXMhlezeCcAh0RhomquXJgB8H+lb/RR/pPcbNVGHVKFUwjpoRtw==} + engines: {node: '>= 0.8'} + strict-uri-encode@2.0.0: resolution: {integrity: sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==} engines: {node: '>=4'} @@ -2431,6 +2466,9 @@ packages: through2@4.0.2: resolution: {integrity: sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==} + through@2.3.8: + resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} + tmp@0.2.1: resolution: {integrity: sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==} engines: {node: '>=8.17.0'} @@ -3216,6 +3254,10 @@ snapshots: dependencies: '@types/node': 20.17.10 + '@types/barcode@0.0.33': + dependencies: + '@types/node': 20.17.10 + '@types/body-parser@1.19.5': dependencies: '@types/connect': 3.4.38 @@ -3449,6 +3491,10 @@ snapshots: array-flatten@1.1.1: {} + array-parallel@0.1.3: {} + + array-series@0.1.5: {} + array-union@2.1.0: {} array.prototype.map@1.0.7: @@ -3505,6 +3551,12 @@ snapshots: balanced-match@1.0.2: {} + barcode@0.1.0: + dependencies: + gm: 1.16.0 + transitivePeerDependencies: + - supports-color + base64-js@1.5.1: {} basic-auth@2.0.1: @@ -3777,6 +3829,8 @@ snapshots: dayjs@1.11.13: {} + debug@0.7.0: {} + debug@2.6.9: dependencies: ms: 2.0.0 @@ -4287,6 +4341,16 @@ snapshots: merge2: 1.4.1 slash: 3.0.0 + gm@1.16.0: + dependencies: + array-parallel: 0.1.3 + array-series: 0.1.5 + debug: 0.7.0 + stream-to-buffer: 0.0.1 + through: 2.3.8 + transitivePeerDependencies: + - supports-color + gopd@1.2.0: {} graceful-fs@4.2.11: {} @@ -5412,6 +5476,8 @@ snapshots: dependencies: stream-chain: 2.2.5 + stream-to-buffer@0.0.1: {} + strict-uri-encode@2.0.0: {} string-width@4.2.3: @@ -5524,6 +5590,8 @@ snapshots: dependencies: readable-stream: 3.6.2 + through@2.3.8: {} + tmp@0.2.1: dependencies: rimraf: 3.0.2 diff --git a/src/controllers/00-doc-template-controller.ts b/src/controllers/00-doc-template-controller.ts index a809787..7da2498 100644 --- a/src/controllers/00-doc-template-controller.ts +++ b/src/controllers/00-doc-template-controller.ts @@ -1,3 +1,4 @@ +import barcode from "barcode"; import createReport from "docx-templates"; import ThaiBahtText from "thai-baht-text"; import { District, Province, SubDistrict } from "@prisma/client"; @@ -71,19 +72,52 @@ const quotationData = (id: string) => }, }); +const requestWorkData = (id: string, step?: number) => + prisma.requestWork.findFirst({ + where: { id }, + include: { + processByUser: true, + productService: { + include: { + product: true, + }, + }, + request: { + include: { + employee: { + include: { + subDistrict: true, + district: true, + province: true, + }, + }, + quotation: true, + }, + }, + stepStatus: { + where: { step }, + }, + }, + }); + @Route("api/v1/doc-template") @Tags("Document Template") export class DocTemplateController extends Controller { @Get() - async getTemplate() { + async getTemplate(@Query() templateGroup?: string) { if ( process.env.DOCUMENT_TEMPLATE_PROVIDER && process.env.DOCUMENT_TEMPLATE_PROVIDER === "edm-api" ) { - const ret = await edmList("file", DOCUMENT_PATH); + const ret = await edmList( + "file", + templateGroup ? [templateGroup, ...DOCUMENT_PATH] : DOCUMENT_PATH, + ); if (ret) return ret.map((v) => v.fileName); } - return await listFile(DOCUMENT_PATH.join("/") + "/"); + return await listFile( + (templateGroup ? [templateGroup, ...DOCUMENT_PATH] : DOCUMENT_PATH).join("/") + "/", + ); } @Get("{documentTemplate}") @@ -92,6 +126,7 @@ export class DocTemplateController extends Controller { @Query() data: string, @Query() dataId: string, @Query() dataOnly?: boolean, + @Query() templateGroup?: string, ): Promise> { let record: Record; @@ -132,12 +167,19 @@ export class DocTemplateController extends Controller { }), ); break; + case "request-work": + record = await requestWorkData(dataId).then((requestWork) => ({ + request: replaceEmptyField(requestWork?.request), + requestWork: replaceEmptyField(requestWork), + employee: requestWork?.request.employee, + })); default: throw new HttpError(HttpStatus.BAD_REQUEST, "No data for template", "noDataTemplate"); } if (!data) throw notFoundError("Data"); if (dataOnly) return record; + if (templateGroup) documentTemplate = templateGroup + "/" + documentTemplate; let template: Buffer | null = null; @@ -210,6 +252,13 @@ export class DocTemplateController extends Controller { thaiBahtText: (input: string | number) => { ThaiBahtText(typeof input === "string" ? input.replaceAll(",", "") : input); }, + barcode: async (data: string) => + new Promise((resolve, reject) => + barcode("code39", { data, width: 400, height: 100 }).getBase64((err, data) => { + if (!err) return resolve(data); + return reject(err); + }), + ), }, }).then(Buffer.from); @@ -218,7 +267,11 @@ export class DocTemplateController extends Controller { } function replaceEmptyField(data: T): T { - return JSON.parse(JSON.stringify(data).replace(/null|\"\"/g, '"\-"')); + try { + return JSON.parse(JSON.stringify(data).replace(/null|\"\"/g, '"\-"')); + } catch (e) { + return data; + } } type FullAddress = {