Merge branch 'develop'
All checks were successful
Spell Check / Spell Check with Typos (push) Successful in 5s
All checks were successful
Spell Check / Spell Check with Typos (push) Successful in 5s
This commit is contained in:
commit
c1cd2b9518
11 changed files with 233 additions and 16 deletions
|
|
@ -9,7 +9,7 @@ env:
|
||||||
REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }}
|
REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }}
|
||||||
CONTAINER_IMAGE_NAME: ${{ vars.CONTAINER_REGISTRY }}/${{ vars.CONTAINER_IMAGE_OWNER }}/${{ vars.CONTAINER_IMAGE_NAME }}:latest
|
CONTAINER_IMAGE_NAME: ${{ vars.CONTAINER_REGISTRY }}/${{ vars.CONTAINER_IMAGE_OWNER }}/${{ vars.CONTAINER_IMAGE_NAME }}:latest
|
||||||
jobs:
|
jobs:
|
||||||
gitea-release:
|
build-deploy:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
|
|
@ -51,7 +51,7 @@ jobs:
|
||||||
"description": "**Details:**\n- Image: `${{ env.CONTAINER_IMAGE_NAME }}`\n- Deployed by: `${{ github.actor }}`",
|
"description": "**Details:**\n- Image: `${{ env.CONTAINER_IMAGE_NAME }}`\n- Deployed by: `${{ github.actor }}`",
|
||||||
"color": 3066993,
|
"color": 3066993,
|
||||||
"footer": {
|
"footer": {
|
||||||
"text": "Gitea Local Release Notification",
|
"text": "Local Release Notification",
|
||||||
"icon_url": "https://example.com/success-icon.png"
|
"icon_url": "https://example.com/success-icon.png"
|
||||||
},
|
},
|
||||||
"timestamp": "'$(date -u +%Y-%m-%dT%H:%M:%SZ)'"
|
"timestamp": "'$(date -u +%Y-%m-%dT%H:%M:%SZ)'"
|
||||||
|
|
@ -68,7 +68,7 @@ jobs:
|
||||||
"description": "**Details:**\n- Image: `${{ env.CONTAINER_IMAGE_NAME }}`\n- Attempted by: `${{ github.actor }}`",
|
"description": "**Details:**\n- Image: `${{ env.CONTAINER_IMAGE_NAME }}`\n- Attempted by: `${{ github.actor }}`",
|
||||||
"color": 15158332,
|
"color": 15158332,
|
||||||
"footer": {
|
"footer": {
|
||||||
"text": "Gitea Local Release Notification",
|
"text": "Local Release Notification",
|
||||||
"icon_url": "https://example.com/failure-icon.png"
|
"icon_url": "https://example.com/failure-icon.png"
|
||||||
},
|
},
|
||||||
"timestamp": "'$(date -u +%Y-%m-%dT%H:%M:%SZ)'"
|
"timestamp": "'$(date -u +%Y-%m-%dT%H:%M:%SZ)'"
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@
|
||||||
"author": "Frappe'T",
|
"author": "Frappe'T",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@types/barcode": "^0.0.33",
|
||||||
"@types/cors": "^2.8.17",
|
"@types/cors": "^2.8.17",
|
||||||
"@types/express": "^4.17.21",
|
"@types/express": "^4.17.21",
|
||||||
"@types/morgan": "^1.9.9",
|
"@types/morgan": "^1.9.9",
|
||||||
|
|
@ -38,6 +39,7 @@
|
||||||
"@prisma/client": "^6.3.0",
|
"@prisma/client": "^6.3.0",
|
||||||
"@scalar/express-api-reference": "^0.4.182",
|
"@scalar/express-api-reference": "^0.4.182",
|
||||||
"@tsoa/runtime": "^6.6.0",
|
"@tsoa/runtime": "^6.6.0",
|
||||||
|
"barcode": "^0.1.0",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"cron": "^3.3.1",
|
"cron": "^3.3.1",
|
||||||
"dayjs": "^1.11.13",
|
"dayjs": "^1.11.13",
|
||||||
|
|
|
||||||
68
pnpm-lock.yaml
generated
68
pnpm-lock.yaml
generated
|
|
@ -23,6 +23,9 @@ importers:
|
||||||
'@tsoa/runtime':
|
'@tsoa/runtime':
|
||||||
specifier: ^6.6.0
|
specifier: ^6.6.0
|
||||||
version: 6.6.0
|
version: 6.6.0
|
||||||
|
barcode:
|
||||||
|
specifier: ^0.1.0
|
||||||
|
version: 0.1.0
|
||||||
cors:
|
cors:
|
||||||
specifier: ^2.8.5
|
specifier: ^2.8.5
|
||||||
version: 2.8.5
|
version: 2.8.5
|
||||||
|
|
@ -84,6 +87,9 @@ importers:
|
||||||
specifier: ^0.19.0
|
specifier: ^0.19.0
|
||||||
version: 0.19.0
|
version: 0.19.0
|
||||||
devDependencies:
|
devDependencies:
|
||||||
|
'@types/barcode':
|
||||||
|
specifier: ^0.0.33
|
||||||
|
version: 0.0.33
|
||||||
'@types/cors':
|
'@types/cors':
|
||||||
specifier: ^2.8.17
|
specifier: ^2.8.17
|
||||||
version: 2.8.17
|
version: 2.8.17
|
||||||
|
|
@ -420,6 +426,9 @@ packages:
|
||||||
'@types/accepts@1.3.7':
|
'@types/accepts@1.3.7':
|
||||||
resolution: {integrity: sha512-Pay9fq2lM2wXPWbteBsRAGiWH2hig4ZE2asK+mm7kUzlxRTfL961rj89I6zV/E3PcIkDqyuBEcMxFT7rccugeQ==}
|
resolution: {integrity: sha512-Pay9fq2lM2wXPWbteBsRAGiWH2hig4ZE2asK+mm7kUzlxRTfL961rj89I6zV/E3PcIkDqyuBEcMxFT7rccugeQ==}
|
||||||
|
|
||||||
|
'@types/barcode@0.0.33':
|
||||||
|
resolution: {integrity: sha512-PotMNcya1L26CgWRfLNxNw0VIyhb104YhXB12e6xjoo/+hZaitrpRnToU9Ru0yTfty6tGc1Bk/1JNcGs9YVKXQ==}
|
||||||
|
|
||||||
'@types/body-parser@1.19.5':
|
'@types/body-parser@1.19.5':
|
||||||
resolution: {integrity: sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==}
|
resolution: {integrity: sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==}
|
||||||
|
|
||||||
|
|
@ -611,6 +620,12 @@ packages:
|
||||||
array-flatten@1.1.1:
|
array-flatten@1.1.1:
|
||||||
resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==}
|
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:
|
array-union@2.1.0:
|
||||||
resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==}
|
resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
|
|
@ -657,6 +672,9 @@ packages:
|
||||||
balanced-match@1.0.2:
|
balanced-match@1.0.2:
|
||||||
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
|
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
|
||||||
|
|
||||||
|
barcode@0.1.0:
|
||||||
|
resolution: {integrity: sha512-GslbXakjG61fwHnIN/vrUkPsa61WVAJDnb7jAwmbjRW5bZdwINymo1+FgjYJrGf2MDHxAt3bUWgmEMF8ETZxHQ==}
|
||||||
|
|
||||||
base64-js@1.5.1:
|
base64-js@1.5.1:
|
||||||
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
|
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
|
||||||
|
|
||||||
|
|
@ -877,6 +895,14 @@ packages:
|
||||||
dayjs@1.11.13:
|
dayjs@1.11.13:
|
||||||
resolution: {integrity: sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==}
|
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:
|
debug@2.6.9:
|
||||||
resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==}
|
resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
|
|
@ -1232,6 +1258,11 @@ packages:
|
||||||
resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==}
|
resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==}
|
||||||
engines: {node: '>=10'}
|
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:
|
gopd@1.2.0:
|
||||||
resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==}
|
resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
@ -2337,6 +2368,10 @@ packages:
|
||||||
stream-json@1.9.1:
|
stream-json@1.9.1:
|
||||||
resolution: {integrity: sha512-uWkjJ+2Nt/LO9Z/JyKZbMusL8Dkh97uUBTv3AJQ74y07lVahLY4eEFsPsE97pxYBwr8nnjMAIch5eqI0gPShyw==}
|
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:
|
strict-uri-encode@2.0.0:
|
||||||
resolution: {integrity: sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==}
|
resolution: {integrity: sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==}
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
|
|
@ -2431,6 +2466,9 @@ packages:
|
||||||
through2@4.0.2:
|
through2@4.0.2:
|
||||||
resolution: {integrity: sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==}
|
resolution: {integrity: sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==}
|
||||||
|
|
||||||
|
through@2.3.8:
|
||||||
|
resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==}
|
||||||
|
|
||||||
tmp@0.2.1:
|
tmp@0.2.1:
|
||||||
resolution: {integrity: sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==}
|
resolution: {integrity: sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==}
|
||||||
engines: {node: '>=8.17.0'}
|
engines: {node: '>=8.17.0'}
|
||||||
|
|
@ -3216,6 +3254,10 @@ snapshots:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/node': 20.17.10
|
'@types/node': 20.17.10
|
||||||
|
|
||||||
|
'@types/barcode@0.0.33':
|
||||||
|
dependencies:
|
||||||
|
'@types/node': 20.17.10
|
||||||
|
|
||||||
'@types/body-parser@1.19.5':
|
'@types/body-parser@1.19.5':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/connect': 3.4.38
|
'@types/connect': 3.4.38
|
||||||
|
|
@ -3449,6 +3491,10 @@ snapshots:
|
||||||
|
|
||||||
array-flatten@1.1.1: {}
|
array-flatten@1.1.1: {}
|
||||||
|
|
||||||
|
array-parallel@0.1.3: {}
|
||||||
|
|
||||||
|
array-series@0.1.5: {}
|
||||||
|
|
||||||
array-union@2.1.0: {}
|
array-union@2.1.0: {}
|
||||||
|
|
||||||
array.prototype.map@1.0.7:
|
array.prototype.map@1.0.7:
|
||||||
|
|
@ -3505,6 +3551,12 @@ snapshots:
|
||||||
|
|
||||||
balanced-match@1.0.2: {}
|
balanced-match@1.0.2: {}
|
||||||
|
|
||||||
|
barcode@0.1.0:
|
||||||
|
dependencies:
|
||||||
|
gm: 1.16.0
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
base64-js@1.5.1: {}
|
base64-js@1.5.1: {}
|
||||||
|
|
||||||
basic-auth@2.0.1:
|
basic-auth@2.0.1:
|
||||||
|
|
@ -3777,6 +3829,8 @@ snapshots:
|
||||||
|
|
||||||
dayjs@1.11.13: {}
|
dayjs@1.11.13: {}
|
||||||
|
|
||||||
|
debug@0.7.0: {}
|
||||||
|
|
||||||
debug@2.6.9:
|
debug@2.6.9:
|
||||||
dependencies:
|
dependencies:
|
||||||
ms: 2.0.0
|
ms: 2.0.0
|
||||||
|
|
@ -4287,6 +4341,16 @@ snapshots:
|
||||||
merge2: 1.4.1
|
merge2: 1.4.1
|
||||||
slash: 3.0.0
|
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: {}
|
gopd@1.2.0: {}
|
||||||
|
|
||||||
graceful-fs@4.2.11: {}
|
graceful-fs@4.2.11: {}
|
||||||
|
|
@ -5412,6 +5476,8 @@ snapshots:
|
||||||
dependencies:
|
dependencies:
|
||||||
stream-chain: 2.2.5
|
stream-chain: 2.2.5
|
||||||
|
|
||||||
|
stream-to-buffer@0.0.1: {}
|
||||||
|
|
||||||
strict-uri-encode@2.0.0: {}
|
strict-uri-encode@2.0.0: {}
|
||||||
|
|
||||||
string-width@4.2.3:
|
string-width@4.2.3:
|
||||||
|
|
@ -5524,6 +5590,8 @@ snapshots:
|
||||||
dependencies:
|
dependencies:
|
||||||
readable-stream: 3.6.2
|
readable-stream: 3.6.2
|
||||||
|
|
||||||
|
through@2.3.8: {}
|
||||||
|
|
||||||
tmp@0.2.1:
|
tmp@0.2.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
rimraf: 3.0.2
|
rimraf: 3.0.2
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import barcode from "barcode";
|
||||||
import createReport from "docx-templates";
|
import createReport from "docx-templates";
|
||||||
import ThaiBahtText from "thai-baht-text";
|
import ThaiBahtText from "thai-baht-text";
|
||||||
import { District, Province, SubDistrict } from "@prisma/client";
|
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")
|
@Route("api/v1/doc-template")
|
||||||
@Tags("Document Template")
|
@Tags("Document Template")
|
||||||
export class DocTemplateController extends Controller {
|
export class DocTemplateController extends Controller {
|
||||||
@Get()
|
@Get()
|
||||||
async getTemplate() {
|
async getTemplate(@Query() templateGroup?: string) {
|
||||||
if (
|
if (
|
||||||
process.env.DOCUMENT_TEMPLATE_PROVIDER &&
|
process.env.DOCUMENT_TEMPLATE_PROVIDER &&
|
||||||
process.env.DOCUMENT_TEMPLATE_PROVIDER === "edm-api"
|
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);
|
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}")
|
@Get("{documentTemplate}")
|
||||||
|
|
@ -92,6 +126,7 @@ export class DocTemplateController extends Controller {
|
||||||
@Query() data: string,
|
@Query() data: string,
|
||||||
@Query() dataId: string,
|
@Query() dataId: string,
|
||||||
@Query() dataOnly?: boolean,
|
@Query() dataOnly?: boolean,
|
||||||
|
@Query() templateGroup?: string,
|
||||||
): Promise<Readable | Record<string, any>> {
|
): Promise<Readable | Record<string, any>> {
|
||||||
let record: Record<string, any>;
|
let record: Record<string, any>;
|
||||||
|
|
||||||
|
|
@ -132,12 +167,20 @@ export class DocTemplateController extends Controller {
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
case "request-work":
|
||||||
|
record = await requestWorkData(dataId).then((requestWork) => ({
|
||||||
|
request: replaceEmptyField(requestWork?.request),
|
||||||
|
requestWork: replaceEmptyField(requestWork),
|
||||||
|
employee: requestWork?.request.employee,
|
||||||
|
}));
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
throw new HttpError(HttpStatus.BAD_REQUEST, "No data for template", "noDataTemplate");
|
throw new HttpError(HttpStatus.BAD_REQUEST, "No data for template", "noDataTemplate");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!data) throw notFoundError("Data");
|
if (!data) throw notFoundError("Data");
|
||||||
if (dataOnly) return record;
|
if (dataOnly) return record;
|
||||||
|
if (templateGroup) documentTemplate = templateGroup + "/" + documentTemplate;
|
||||||
|
|
||||||
let template: Buffer<ArrayBufferLike> | null = null;
|
let template: Buffer<ArrayBufferLike> | null = null;
|
||||||
|
|
||||||
|
|
@ -210,6 +253,13 @@ export class DocTemplateController extends Controller {
|
||||||
thaiBahtText: (input: string | number) => {
|
thaiBahtText: (input: string | number) => {
|
||||||
ThaiBahtText(typeof input === "string" ? input.replaceAll(",", "") : input);
|
ThaiBahtText(typeof input === "string" ? input.replaceAll(",", "") : input);
|
||||||
},
|
},
|
||||||
|
barcode: async (data: string) =>
|
||||||
|
new Promise<string>((resolve, reject) =>
|
||||||
|
barcode("code39", { data, width: 400, height: 100 }).getBase64((err, data) => {
|
||||||
|
if (!err) return resolve(data);
|
||||||
|
return reject(err);
|
||||||
|
}),
|
||||||
|
),
|
||||||
},
|
},
|
||||||
}).then(Buffer.from);
|
}).then(Buffer.from);
|
||||||
|
|
||||||
|
|
@ -218,7 +268,11 @@ export class DocTemplateController extends Controller {
|
||||||
}
|
}
|
||||||
|
|
||||||
function replaceEmptyField<T>(data: T): T {
|
function replaceEmptyField<T>(data: T): T {
|
||||||
|
try {
|
||||||
return JSON.parse(JSON.stringify(data).replace(/null|\"\"/g, '"\-"'));
|
return JSON.parse(JSON.stringify(data).replace(/null|\"\"/g, '"\-"'));
|
||||||
|
} catch (e) {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type FullAddress = {
|
type FullAddress = {
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,7 @@ import {
|
||||||
getPresigned,
|
getPresigned,
|
||||||
listFile,
|
listFile,
|
||||||
setFile,
|
setFile,
|
||||||
|
uploadFile,
|
||||||
} from "../utils/minio";
|
} from "../utils/minio";
|
||||||
import { filterStatus } from "../services/prisma";
|
import { filterStatus } from "../services/prisma";
|
||||||
import {
|
import {
|
||||||
|
|
@ -878,3 +879,48 @@ export class UserAttachmentController extends Controller {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Route("api/v1/user/{userId}/signature")
|
||||||
|
@Security("keycloak")
|
||||||
|
export class UserSignatureController extends Controller {
|
||||||
|
#checkPermission(req: RequestWithUser, userId: string) {
|
||||||
|
if (req.user.sub !== userId) {
|
||||||
|
throw new HttpError(
|
||||||
|
HttpStatus.FORBIDDEN,
|
||||||
|
"You do not have permission to perform this action.",
|
||||||
|
"noPermission",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get()
|
||||||
|
async getSignature(@Request() req: RequestWithUser, @Path() userId: string) {
|
||||||
|
this.#checkPermission(req, userId);
|
||||||
|
return await getFile(fileLocation.user.signature(userId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Put()
|
||||||
|
async setSignature(
|
||||||
|
@Request() req: RequestWithUser,
|
||||||
|
@Path() userId: string,
|
||||||
|
@Body() signature?: { data: string },
|
||||||
|
) {
|
||||||
|
this.#checkPermission(req, userId);
|
||||||
|
|
||||||
|
const base64 = signature?.data;
|
||||||
|
|
||||||
|
if (base64) {
|
||||||
|
const buffer = Buffer.from(base64.replace(/^data:image\/\w+;base64,/, ""), "base64");
|
||||||
|
const mime = "image/" + base64.split(";")[0].split("/")[1];
|
||||||
|
await uploadFile(fileLocation.user.signature(userId), buffer, mime);
|
||||||
|
} else {
|
||||||
|
return await setFile(fileLocation.user.signature(userId));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Delete()
|
||||||
|
async deleteSignature(@Request() req: RequestWithUser, @Path() userId: string) {
|
||||||
|
this.#checkPermission(req, userId);
|
||||||
|
await deleteFile(fileLocation.user.signature(userId));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -219,6 +219,16 @@ export class InvoiceController extends Controller {
|
||||||
quotationStatus: "PaymentInProcess",
|
quotationStatus: "PaymentInProcess",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await tx.notification.create({
|
||||||
|
data: {
|
||||||
|
title: "ใบแจ้งหนี้ใหม่ / New Invoice",
|
||||||
|
detail: "รหัส / code : " + record.code,
|
||||||
|
registeredBranchId: record.registeredBranchId,
|
||||||
|
groupReceiver: { create: { name: "accountant" } },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
return await tx.invoice.create({
|
return await tx.invoice.create({
|
||||||
data: {
|
data: {
|
||||||
quotationId: body.quotationId,
|
quotationId: body.quotationId,
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ import {
|
||||||
} from "../services/permission";
|
} from "../services/permission";
|
||||||
import { isSystem } from "../utils/keycloak";
|
import { isSystem } from "../utils/keycloak";
|
||||||
import { filterStatus } from "../services/prisma";
|
import { filterStatus } from "../services/prisma";
|
||||||
import { deleteFile, fileLocation, getFile, listFile, setFile } from "../utils/minio";
|
import { deleteFile, deleteFolder, fileLocation, getFile, listFile, setFile } from "../utils/minio";
|
||||||
import { isUsedError, notFoundError, relationError } from "../utils/error";
|
import { isUsedError, notFoundError, relationError } from "../utils/error";
|
||||||
import { queryOrNot } from "../utils/relation";
|
import { queryOrNot } from "../utils/relation";
|
||||||
|
|
||||||
|
|
@ -434,6 +434,8 @@ export class ProductController extends Controller {
|
||||||
|
|
||||||
if (record.status !== Status.CREATED) throw isUsedError("Product");
|
if (record.status !== Status.CREATED) throw isUsedError("Product");
|
||||||
|
|
||||||
|
await deleteFolder(fileLocation.product.img(productId));
|
||||||
|
|
||||||
return await prisma.product.delete({
|
return await prisma.product.delete({
|
||||||
include: {
|
include: {
|
||||||
createdBy: true,
|
createdBy: true,
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,15 @@ import {
|
||||||
} from "../services/permission";
|
} from "../services/permission";
|
||||||
import { filterStatus } from "../services/prisma";
|
import { filterStatus } from "../services/prisma";
|
||||||
import { isUsedError, notFoundError, relationError } from "../utils/error";
|
import { isUsedError, notFoundError, relationError } from "../utils/error";
|
||||||
import { deleteFile, fileLocation, getFile, getPresigned, listFile, setFile } from "../utils/minio";
|
import {
|
||||||
|
deleteFile,
|
||||||
|
deleteFolder,
|
||||||
|
fileLocation,
|
||||||
|
getFile,
|
||||||
|
getPresigned,
|
||||||
|
listFile,
|
||||||
|
setFile,
|
||||||
|
} from "../utils/minio";
|
||||||
import { queryOrNot } from "../utils/relation";
|
import { queryOrNot } from "../utils/relation";
|
||||||
|
|
||||||
const MANAGE_ROLES = [
|
const MANAGE_ROLES = [
|
||||||
|
|
@ -560,6 +568,8 @@ export class ServiceController extends Controller {
|
||||||
|
|
||||||
if (record.status !== Status.CREATED) throw isUsedError("Service");
|
if (record.status !== Status.CREATED) throw isUsedError("Service");
|
||||||
|
|
||||||
|
await deleteFolder(fileLocation.service.img(serviceId));
|
||||||
|
|
||||||
return await prisma.service.delete({
|
return await prisma.service.delete({
|
||||||
include: {
|
include: {
|
||||||
createdBy: true,
|
createdBy: true,
|
||||||
|
|
|
||||||
|
|
@ -653,7 +653,7 @@ export class QuotationController extends Controller {
|
||||||
title: "ใบเสนอราคาใหม่ / New Quotation",
|
title: "ใบเสนอราคาใหม่ / New Quotation",
|
||||||
detail: "รหัส / code : " + ret.code,
|
detail: "รหัส / code : " + ret.code,
|
||||||
registeredBranchId: ret.registeredBranchId,
|
registeredBranchId: ret.registeredBranchId,
|
||||||
groupReceiver: { create: [{ name: "accountant" }, { name: "head_of_accountant" }] },
|
groupReceiver: { create: [{ name: "sale" }, { name: "head_of_sale" }] },
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -679,6 +679,7 @@ export class QuotationController extends Controller {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
productServiceList: true,
|
||||||
},
|
},
|
||||||
where: { id: quotationId, isDebitNote: false },
|
where: { id: quotationId, isDebitNote: false },
|
||||||
});
|
});
|
||||||
|
|
@ -850,7 +851,18 @@ export class QuotationController extends Controller {
|
||||||
finalPrice: 0,
|
finalPrice: 0,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
const changed = list?.some((lhs) => {
|
||||||
|
const found = record.productServiceList.find((rhs) => {
|
||||||
|
return (
|
||||||
|
lhs.serviceId === rhs.serviceId &&
|
||||||
|
lhs.workId === rhs.workId &&
|
||||||
|
lhs.productId === rhs.productId &&
|
||||||
|
lhs.amount === rhs.amount &&
|
||||||
|
precisionRound(lhs.pricePerUnit, 6) === precisionRound(rhs.pricePerUnit, 6)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
return !found;
|
||||||
|
});
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
tx.service.updateMany({
|
tx.service.updateMany({
|
||||||
where: { id: { in: ids.service }, status: Status.CREATED },
|
where: { id: { in: ids.service }, status: Status.CREATED },
|
||||||
|
|
@ -860,6 +872,16 @@ export class QuotationController extends Controller {
|
||||||
where: { id: { in: ids.product }, status: Status.CREATED },
|
where: { id: { in: ids.product }, status: Status.CREATED },
|
||||||
data: { status: Status.ACTIVE },
|
data: { status: Status.ACTIVE },
|
||||||
}),
|
}),
|
||||||
|
changed &&
|
||||||
|
tx.notification.create({
|
||||||
|
data: {
|
||||||
|
title: "ใบเสนอราคามีการเปลี่ยนแปลง / Quotation Detail Changes",
|
||||||
|
detail:
|
||||||
|
"รหัส / code : " + record.code + " มีการเปลี่ยนแปลงของสินค้า / Product Updated",
|
||||||
|
registeredBranchId: record.registeredBranchId,
|
||||||
|
groupReceiver: { create: [{ name: "sale" }, { name: "head_of_sale" }] },
|
||||||
|
},
|
||||||
|
}),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return await tx.quotation.update({
|
return await tx.quotation.update({
|
||||||
|
|
|
||||||
|
|
@ -47,12 +47,8 @@ export class RequestDataController extends Controller {
|
||||||
async getRequestDataStats(@Request() req: RequestWithUser) {
|
async getRequestDataStats(@Request() req: RequestWithUser) {
|
||||||
const where = {
|
const where = {
|
||||||
quotation: {
|
quotation: {
|
||||||
customerBranch: {
|
|
||||||
customer: {
|
|
||||||
registeredBranch: { OR: permissionCond(req.user) },
|
registeredBranch: { OR: permissionCond(req.user) },
|
||||||
},
|
},
|
||||||
},
|
|
||||||
},
|
|
||||||
} satisfies Prisma.RequestDataWhereInput;
|
} satisfies Prisma.RequestDataWhereInput;
|
||||||
|
|
||||||
const list = await prisma.requestData.groupBy({
|
const list = await prisma.requestData.groupBy({
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,12 @@ export async function setFile(path: string, exp = 6 * 60 * 60) {
|
||||||
return await minio.presignedPutObject(MINIO_BUCKET, path, exp);
|
return await minio.presignedPutObject(MINIO_BUCKET, path, exp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function uploadFile(path: string, buffer: Buffer, contentType?: string) {
|
||||||
|
await minio.putObject(MINIO_BUCKET, path, buffer, Buffer.byteLength(buffer), {
|
||||||
|
["Content-Type"]: contentType,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export async function deleteFile(path: string) {
|
export async function deleteFile(path: string) {
|
||||||
await minio.removeObject(MINIO_BUCKET, path, { forceDelete: true });
|
await minio.removeObject(MINIO_BUCKET, path, { forceDelete: true });
|
||||||
}
|
}
|
||||||
|
|
@ -70,6 +76,7 @@ export const fileLocation = {
|
||||||
`${ROOT}/user/profile-image-${userId}/${name || ""}`,
|
`${ROOT}/user/profile-image-${userId}/${name || ""}`,
|
||||||
attachment: (userId: string, name?: string) =>
|
attachment: (userId: string, name?: string) =>
|
||||||
`${ROOT}/user/attachment-${userId}/${name || ""}`,
|
`${ROOT}/user/attachment-${userId}/${name || ""}`,
|
||||||
|
signature: (userId: string) => `${ROOT}/user/signature-${userId}`,
|
||||||
},
|
},
|
||||||
customer: {
|
customer: {
|
||||||
img: (customerId: string, name?: string) => `${ROOT}/customer/img-${customerId}/${name || ""}`,
|
img: (customerId: string, name?: string) => `${ROOT}/customer/img-${customerId}/${name || ""}`,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue