pdf generation
This commit is contained in:
parent
26bfaeaa0b
commit
ecbf959ec7
3
.gitignore
vendored
3
.gitignore
vendored
@ -3,6 +3,9 @@
|
|||||||
/node_modules
|
/node_modules
|
||||||
/build
|
/build
|
||||||
|
|
||||||
|
# pdf
|
||||||
|
/public/carnet-pdf/*.pdf
|
||||||
|
|
||||||
# Logs
|
# Logs
|
||||||
logs
|
logs
|
||||||
*.log
|
*.log
|
||||||
|
|||||||
220
package-lock.json
generated
220
package-lock.json
generated
@ -32,6 +32,8 @@
|
|||||||
"oracledb": "^6.7.2",
|
"oracledb": "^6.7.2",
|
||||||
"passport": "^0.7.0",
|
"passport": "^0.7.0",
|
||||||
"passport-jwt": "^4.0.1",
|
"passport-jwt": "^4.0.1",
|
||||||
|
"pdf-merger-js": "^5.1.2",
|
||||||
|
"pdfkit": "^0.17.1",
|
||||||
"pg": "^8.13.3",
|
"pg": "^8.13.3",
|
||||||
"reflect-metadata": "^0.2.2",
|
"reflect-metadata": "^0.2.2",
|
||||||
"rxjs": "^7.8.1",
|
"rxjs": "^7.8.1",
|
||||||
@ -1145,9 +1147,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@eslint/plugin-kit": {
|
"node_modules/@eslint/plugin-kit": {
|
||||||
"version": "0.3.3",
|
"version": "0.3.4",
|
||||||
"resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.4.tgz",
|
||||||
"integrity": "sha512-1+WqvgNMhmlAambTvT3KPtCl/Ibr68VldY2XY40SL1CE0ZXiakFR/cbTspaF5HsnpDMvcYYoJHfl4980NBjGag==",
|
"integrity": "sha512-Ul5l+lHEcw3L5+k8POx6r74mxEYKG5kOb6Xpy2gCRW6zweT6TEhAf8vhxGgjhqrd/VO/Dirhsb+1hNpD1ue9hw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@ -2892,6 +2894,24 @@
|
|||||||
"@noble/hashes": "^1.1.5"
|
"@noble/hashes": "^1.1.5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@pdf-lib/standard-fonts": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@pdf-lib/standard-fonts/-/standard-fonts-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-hU30BK9IUN/su0Mn9VdlVKsWBS6GyhVfqjwl1FjZN4TxP6cCw0jP2w7V3Hf5uX7M0AZJ16vey9yE0ny7Sa59ZA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"pako": "^1.0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@pdf-lib/upng": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@pdf-lib/upng/-/upng-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-dQK2FUMQtowVP00mtIksrlZhdFXQZPC+taih1q4CvPZ5vqdxR/LKBaFg0oAfzd1GlHZXXSPdQfzQnt+ViGvEIQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"pako": "^1.0.10"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@pkgjs/parseargs": {
|
"node_modules/@pkgjs/parseargs": {
|
||||||
"version": "0.11.0",
|
"version": "0.11.0",
|
||||||
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
|
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
|
||||||
@ -3283,6 +3303,15 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "Apache-2.0"
|
"license": "Apache-2.0"
|
||||||
},
|
},
|
||||||
|
"node_modules/@swc/helpers": {
|
||||||
|
"version": "0.5.17",
|
||||||
|
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.17.tgz",
|
||||||
|
"integrity": "sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^2.8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@swc/types": {
|
"node_modules/@swc/types": {
|
||||||
"version": "0.1.17",
|
"version": "0.1.17",
|
||||||
"resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.17.tgz",
|
"resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.17.tgz",
|
||||||
@ -4944,6 +4973,15 @@
|
|||||||
"integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==",
|
"integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/brotli": {
|
||||||
|
"version": "1.3.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/brotli/-/brotli-1.3.3.tgz",
|
||||||
|
"integrity": "sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"base64-js": "^1.1.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/browserslist": {
|
"node_modules/browserslist": {
|
||||||
"version": "4.24.4",
|
"version": "4.24.4",
|
||||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz",
|
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz",
|
||||||
@ -5682,6 +5720,12 @@
|
|||||||
"node": ">= 8"
|
"node": ">= 8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/crypto-js": {
|
||||||
|
"version": "4.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz",
|
||||||
|
"integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/dayjs": {
|
"node_modules/dayjs": {
|
||||||
"version": "1.11.13",
|
"version": "1.11.13",
|
||||||
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz",
|
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz",
|
||||||
@ -5886,6 +5930,12 @@
|
|||||||
"wrappy": "1"
|
"wrappy": "1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/dfa": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/dfa/-/dfa-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-ED3jP8saaweFTjeGX8HQPjeC1YYyZs98jGNZx6IiBvxW7JG5v492kamAQB3m2wop07CvU/RQmzcKr6bgcC5D/Q==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/diff": {
|
"node_modules/diff": {
|
||||||
"version": "4.0.2",
|
"version": "4.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
|
||||||
@ -6572,7 +6622,6 @@
|
|||||||
"version": "3.1.3",
|
"version": "3.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
||||||
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
|
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/fast-diff": {
|
"node_modules/fast-diff": {
|
||||||
@ -6935,6 +6984,32 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/fontkit": {
|
||||||
|
"version": "2.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/fontkit/-/fontkit-2.0.4.tgz",
|
||||||
|
"integrity": "sha512-syetQadaUEDNdxdugga9CpEYVaQIxOwk7GlwZWWZ19//qW4zE5bknOKeMBDYAASwnpaSHKJITRLMF9m1fp3s6g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@swc/helpers": "^0.5.12",
|
||||||
|
"brotli": "^1.3.2",
|
||||||
|
"clone": "^2.1.2",
|
||||||
|
"dfa": "^1.2.0",
|
||||||
|
"fast-deep-equal": "^3.1.3",
|
||||||
|
"restructure": "^3.0.0",
|
||||||
|
"tiny-inflate": "^1.0.3",
|
||||||
|
"unicode-properties": "^1.4.0",
|
||||||
|
"unicode-trie": "^2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/fontkit/node_modules/clone": {
|
||||||
|
"version": "2.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz",
|
||||||
|
"integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/foreground-child": {
|
"node_modules/foreground-child": {
|
||||||
"version": "3.3.0",
|
"version": "3.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz",
|
||||||
@ -7045,14 +7120,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/form-data": {
|
"node_modules/form-data": {
|
||||||
"version": "4.0.2",
|
"version": "4.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz",
|
||||||
"integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==",
|
"integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"asynckit": "^0.4.0",
|
"asynckit": "^0.4.0",
|
||||||
"combined-stream": "^1.0.8",
|
"combined-stream": "^1.0.8",
|
||||||
"es-set-tostringtag": "^2.1.0",
|
"es-set-tostringtag": "^2.1.0",
|
||||||
|
"hasown": "^2.0.2",
|
||||||
"mime-types": "^2.1.12"
|
"mime-types": "^2.1.12"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
@ -8723,6 +8799,12 @@
|
|||||||
"@sideway/pinpoint": "^2.0.0"
|
"@sideway/pinpoint": "^2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/jpeg-exif": {
|
||||||
|
"version": "1.1.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/jpeg-exif/-/jpeg-exif-1.1.4.tgz",
|
||||||
|
"integrity": "sha512-a+bKEcCjtuW5WTdgeXFzswSrdqi0jk4XlEtZlx5A94wCoBpFjfFTbo/Tra5SpNCl/YFZPvcV1dJc+TAYeg6ROQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/js-md4": {
|
"node_modules/js-md4": {
|
||||||
"version": "0.3.2",
|
"version": "0.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/js-md4/-/js-md4-0.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/js-md4/-/js-md4-0.3.2.tgz",
|
||||||
@ -8936,6 +9018,25 @@
|
|||||||
"integrity": "sha512-/ipwAMvtSZRdiQBHqW1qxqeYiBMzncOQLVA+62MWYr7N4m7Q2jqpJ0WgT7zlOEOpyLRSqrMXidbJpC0J77AaKA==",
|
"integrity": "sha512-/ipwAMvtSZRdiQBHqW1qxqeYiBMzncOQLVA+62MWYr7N4m7Q2jqpJ0WgT7zlOEOpyLRSqrMXidbJpC0J77AaKA==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/linebreak": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/linebreak/-/linebreak-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-MHp03UImeVhB7XZtjd0E4n6+3xr5Dq/9xI/5FptGk5FrbDR3zagPa2DS6U8ks/3HjbKWG9Q1M2ufOzxV2qLYSQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"base64-js": "0.0.8",
|
||||||
|
"unicode-trie": "^2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/linebreak/node_modules/base64-js": {
|
||||||
|
"version": "0.0.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-0.0.8.tgz",
|
||||||
|
"integrity": "sha512-3XSA2cR/h/73EzlXXdU6YNycmYI7+kicTxks4eJg2g39biHR84slg2+des+p7iHYhbRg/udIS4TD53WabcOUkw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/lines-and-columns": {
|
"node_modules/lines-and-columns": {
|
||||||
"version": "1.2.4",
|
"version": "1.2.4",
|
||||||
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
|
||||||
@ -9844,6 +9945,12 @@
|
|||||||
"integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==",
|
"integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==",
|
||||||
"license": "BlueOak-1.0.0"
|
"license": "BlueOak-1.0.0"
|
||||||
},
|
},
|
||||||
|
"node_modules/pako": {
|
||||||
|
"version": "1.0.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
|
||||||
|
"integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==",
|
||||||
|
"license": "(MIT AND Zlib)"
|
||||||
|
},
|
||||||
"node_modules/parent-module": {
|
"node_modules/parent-module": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
|
||||||
@ -10008,6 +10115,62 @@
|
|||||||
"resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz",
|
||||||
"integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg=="
|
"integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg=="
|
||||||
},
|
},
|
||||||
|
"node_modules/pdf-lib": {
|
||||||
|
"version": "1.17.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/pdf-lib/-/pdf-lib-1.17.1.tgz",
|
||||||
|
"integrity": "sha512-V/mpyJAoTsN4cnP31vc0wfNA1+p20evqqnap0KLoRUN0Yk/p3wN52DOEsL4oBFcLdb76hlpKPtzJIgo67j/XLw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@pdf-lib/standard-fonts": "^1.0.0",
|
||||||
|
"@pdf-lib/upng": "^1.0.1",
|
||||||
|
"pako": "^1.0.11",
|
||||||
|
"tslib": "^1.11.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/pdf-lib/node_modules/tslib": {
|
||||||
|
"version": "1.14.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
|
||||||
|
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
|
||||||
|
"license": "0BSD"
|
||||||
|
},
|
||||||
|
"node_modules/pdf-merger-js": {
|
||||||
|
"version": "5.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/pdf-merger-js/-/pdf-merger-js-5.1.2.tgz",
|
||||||
|
"integrity": "sha512-RCBjLQILZ8UA4keO/Ip2/gjUuxigMMoK7mO5eJg6zjlnyymboFnRTgzKwOs/FiU9ornS2m72Qr95oARX1C24fw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"commander": "^11.1.0",
|
||||||
|
"pdf-lib": "^1.17.1"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"pdf-merge": "cli.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/pdf-merger-js/node_modules/commander": {
|
||||||
|
"version": "11.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz",
|
||||||
|
"integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/pdfkit": {
|
||||||
|
"version": "0.17.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/pdfkit/-/pdfkit-0.17.1.tgz",
|
||||||
|
"integrity": "sha512-Kkf1I9no14O/uo593DYph5u3QwiMfby7JsBSErN1WqeyTgCBNJE3K4pXBn3TgkdKUIVu+buSl4bYUNC+8Up4xg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"crypto-js": "^4.2.0",
|
||||||
|
"fontkit": "^2.0.4",
|
||||||
|
"jpeg-exif": "^1.1.4",
|
||||||
|
"linebreak": "^1.1.0",
|
||||||
|
"png-js": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/peek-readable": {
|
"node_modules/peek-readable": {
|
||||||
"version": "5.4.2",
|
"version": "5.4.2",
|
||||||
"resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-5.4.2.tgz",
|
"resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-5.4.2.tgz",
|
||||||
@ -10237,6 +10400,11 @@
|
|||||||
"node": ">=4"
|
"node": ">=4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/png-js": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/png-js/-/png-js-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-k+YsbhpA9e+EFfKjTCH3VW6aoKlyNYI6NYdTfDL4CIvFnvsuO84ttonmZE7rc+v23SLTH8XX+5w/Ak9v0xGY4g=="
|
||||||
|
},
|
||||||
"node_modules/postgres-array": {
|
"node_modules/postgres-array": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz",
|
||||||
@ -10698,6 +10866,12 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
|
"node_modules/restructure": {
|
||||||
|
"version": "3.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/restructure/-/restructure-3.0.2.tgz",
|
||||||
|
"integrity": "sha512-gSfoiOEA0VPE6Tukkrr7I0RBdE0s7H1eFCDBk05l1KIQT1UIKNc5JZy6jdyW6eYH3aR3g5b3PuL77rq0hvwtAw==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/reusify": {
|
"node_modules/reusify": {
|
||||||
"version": "1.0.4",
|
"version": "1.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
|
||||||
@ -11931,6 +12105,12 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/tiny-inflate": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/tmp": {
|
"node_modules/tmp": {
|
||||||
"version": "0.0.33",
|
"version": "0.0.33",
|
||||||
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
|
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
|
||||||
@ -12541,6 +12721,32 @@
|
|||||||
"integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==",
|
"integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/unicode-properties": {
|
||||||
|
"version": "1.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/unicode-properties/-/unicode-properties-1.4.1.tgz",
|
||||||
|
"integrity": "sha512-CLjCCLQ6UuMxWnbIylkisbRj31qxHPAurvena/0iwSVbQ2G1VY5/HjV0IRabOEbDHlzZlRdCrD4NhB0JtU40Pg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"base64-js": "^1.3.0",
|
||||||
|
"unicode-trie": "^2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/unicode-trie": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/unicode-trie/-/unicode-trie-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-x7bc76x0bm4prf1VLg79uhAzKw8DVboClSN5VxJuQ+LKDOVEW9CdH+VY7SP+vX7xCYQqzzgQpFqz15zeLvAtZQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"pako": "^0.2.5",
|
||||||
|
"tiny-inflate": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/unicode-trie/node_modules/pako": {
|
||||||
|
"version": "0.2.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz",
|
||||||
|
"integrity": "sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/universalify": {
|
"node_modules/universalify": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
|
||||||
|
|||||||
@ -43,6 +43,8 @@
|
|||||||
"oracledb": "^6.7.2",
|
"oracledb": "^6.7.2",
|
||||||
"passport": "^0.7.0",
|
"passport": "^0.7.0",
|
||||||
"passport-jwt": "^4.0.1",
|
"passport-jwt": "^4.0.1",
|
||||||
|
"pdf-merger-js": "^5.1.2",
|
||||||
|
"pdfkit": "^0.17.1",
|
||||||
"pg": "^8.13.3",
|
"pg": "^8.13.3",
|
||||||
"reflect-metadata": "^0.2.2",
|
"reflect-metadata": "^0.2.2",
|
||||||
"rxjs": "^7.8.1",
|
"rxjs": "^7.8.1",
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { Body, Controller, Delete, Get, Param, Patch, Post, Put, UseGuards } from '@nestjs/common';
|
import { Body, Controller, Delete, Get, Param, Patch, Post, Put, Res, StreamableFile, UseGuards } from '@nestjs/common';
|
||||||
import { CarnetApplicationService } from './carnet-application.service';
|
import { CarnetApplicationService } from './carnet-application.service';
|
||||||
import { ApiTags } from '@nestjs/swagger';
|
import { ApiTags } from '@nestjs/swagger';
|
||||||
|
|
||||||
@ -14,6 +14,9 @@ import {
|
|||||||
import { Roles } from 'src/decorators/roles.decorator';
|
import { Roles } from 'src/decorators/roles.decorator';
|
||||||
import { JwtAuthGuard } from 'src/guards/jwt-auth.guard';
|
import { JwtAuthGuard } from 'src/guards/jwt-auth.guard';
|
||||||
import { RolesGuard } from 'src/guards/roles.guard';
|
import { RolesGuard } from 'src/guards/roles.guard';
|
||||||
|
import { Response } from 'express';
|
||||||
|
import { join } from 'path';
|
||||||
|
import { createReadStream } from 'fs';
|
||||||
|
|
||||||
|
|
||||||
@ApiTags('Carnet Application - Oracle')
|
@ApiTags('Carnet Application - Oracle')
|
||||||
@ -152,8 +155,22 @@ export class CarnetApplicationController {
|
|||||||
|
|
||||||
// [PRINT_PKG]
|
// [PRINT_PKG]
|
||||||
@Post('PrintCarnet')
|
@Post('PrintCarnet')
|
||||||
PrintCarnet(@Body() body: PrintCarnetDTO) {
|
@Roles('ca', 'sa', 'ua')
|
||||||
return this.carnetApplicationService.PrintCarnet(body);
|
async PrintCarnet(@Body() body: PrintCarnetDTO, @Res({ passthrough: true }) res: Response) {
|
||||||
|
try {
|
||||||
|
await this.carnetApplicationService.PrintCarnet(body);
|
||||||
|
|
||||||
|
const filePath = join(process.cwd(), 'public/carnet-pdf', 'carnet.pdf');
|
||||||
|
|
||||||
|
res.set({
|
||||||
|
'Content-Type': 'application/pdf',
|
||||||
|
'Content-Disposition': `attachment; filename="${'carnet.pdf'}"`,
|
||||||
|
});
|
||||||
|
const file = createReadStream(filePath);
|
||||||
|
return new StreamableFile(file);
|
||||||
|
} catch (error) {
|
||||||
|
return error.message
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import { Injectable, Logger } from '@nestjs/common';
|
|||||||
import { OracleDBService } from 'src/db/db.service';
|
import { OracleDBService } from 'src/db/db.service';
|
||||||
import * as oracledb from 'oracledb'
|
import * as oracledb from 'oracledb'
|
||||||
import { InternalServerException } from 'src/exceptions/internalServerError.exception';
|
import { InternalServerException } from 'src/exceptions/internalServerError.exception';
|
||||||
import { closeOracleDbConnection, fetchCursor, handleError, setEmptyStringsToNull } from 'src/utils/helper';
|
import { closeOracleDbConnection, deleteFilesAsync, fetchCursor, generateFinalPDF, generateGeneralistPDF1, generateGeneralistPDF2, handleError, pdfCarnetData, pdfGLData, setEmptyStringsToNull } from 'src/utils/helper';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
CarnetProcessingCenterDTO, SaveCarnetApplicationDTO, TransmitApplicationtoProcessDTO,
|
CarnetProcessingCenterDTO, SaveCarnetApplicationDTO, TransmitApplicationtoProcessDTO,
|
||||||
@ -1114,7 +1114,8 @@ export class CarnetApplicationService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// [PRINT_PKG]
|
// [PRINT_PKG]
|
||||||
async PrintCarnet(body: PrintCarnetDTO) {
|
|
||||||
|
async getPrintingData(body: PrintCarnetDTO) {
|
||||||
let connection;
|
let connection;
|
||||||
try {
|
try {
|
||||||
connection = await this.oracleDBService.getConnection();
|
connection = await this.oracleDBService.getConnection();
|
||||||
@ -1147,12 +1148,17 @@ export class CarnetApplicationService {
|
|||||||
const fres: any = await fetchCursor(outBinds.P_CARNETDATACURSOR, CarnetApplicationService.name);
|
const fres: any = await fetchCursor(outBinds.P_CARNETDATACURSOR, CarnetApplicationService.name);
|
||||||
const fres1: any = await fetchCursor(outBinds.P_GLDATACURSOR, CarnetApplicationService.name);
|
const fres1: any = await fetchCursor(outBinds.P_GLDATACURSOR, CarnetApplicationService.name);
|
||||||
|
|
||||||
|
if (fres.length === 0 || fres1.length === 0) {
|
||||||
|
throw new BadRequestException("Print failed please check the submitted details");
|
||||||
|
}
|
||||||
|
|
||||||
if (fres.length > 0 && fres[0].ERRORMESG) {
|
if (fres.length > 0 && fres[0].ERRORMESG) {
|
||||||
this.logger.warn(fres[0].ERRORMESG);
|
this.logger.warn(fres[0].ERRORMESG);
|
||||||
throw new BadRequestException(fres[0].ERRORMESG)
|
throw new BadRequestException(fres[0].ERRORMESG)
|
||||||
}
|
}
|
||||||
|
|
||||||
return { P_CARNETDATACURSOR: fres, P_GLDATACURSOR: fres1 };
|
return { fres: fres, fres1: fres1 }
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleError(error, CarnetApplicationService.name)
|
handleError(error, CarnetApplicationService.name)
|
||||||
} finally {
|
} finally {
|
||||||
@ -1160,4 +1166,63 @@ export class CarnetApplicationService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async PrintCarnet(body: PrintCarnetDTO) {
|
||||||
|
let filesArray = ['p2.pdf']
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
const fres: any = await this.getPrintingData(body);
|
||||||
|
const fres1: any = await this.getPrintingData(body);
|
||||||
|
|
||||||
|
if (fres.length === 0 || fres1.length === 0) {
|
||||||
|
throw new BadRequestException("Print failed please check the submitted details");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fres.length > 0 && fres[0].ERRORMESG) {
|
||||||
|
this.logger.warn(fres[0].ERRORMESG);
|
||||||
|
throw new BadRequestException(fres[0].ERRORMESG)
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const glData: pdfGLData[] = fres1.map(x => {
|
||||||
|
return {
|
||||||
|
itemNo: x.ITEMNO,
|
||||||
|
description: x.ITEMDESCRIPTION,
|
||||||
|
noOfPieces: x.NOOFPIECES,
|
||||||
|
weight: x.WEIGHT,
|
||||||
|
itemValue: x.ITEMVALUE,
|
||||||
|
countryOfOrigin: x.GOODSORIGINCOUNTRY
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const carnetData: pdfCarnetData = {
|
||||||
|
carnetNo: fres[0].CARNETNO,
|
||||||
|
contPageNo: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
let p2Status: string = "";
|
||||||
|
|
||||||
|
if (body.P_PRINTGL === 'Y') {
|
||||||
|
p2Status = await generateGeneralistPDF1(glData, carnetData);
|
||||||
|
} else {
|
||||||
|
p2Status = await generateGeneralistPDF2(carnetData.carnetNo);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p2Status === "p2 done") {
|
||||||
|
await generateFinalPDF(['p2.pdf'], 'carnet.pdf');
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
throw new InternalServerException()
|
||||||
|
}
|
||||||
|
|
||||||
|
// return { P_CARNETDATACURSOR: fres, P_GLDATACURSOR: fres1 };
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
handleError(error, CarnetApplicationService.name)
|
||||||
|
} finally {
|
||||||
|
await deleteFilesAsync([...filesArray, 'carnet.pdf']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,6 +5,25 @@ import {
|
|||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
import * as oracledb from 'oracledb';
|
import * as oracledb from 'oracledb';
|
||||||
|
|
||||||
|
const PDFDocument = require('pdfkit');
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
import { PDFDocument as pdfl } from 'pdf-lib';
|
||||||
|
import PDFMerger from 'pdf-merger-js';
|
||||||
|
import * as fsPromises from 'fs/promises';
|
||||||
|
|
||||||
|
const doc = new PDFDocument({
|
||||||
|
size: 'A4',
|
||||||
|
margins: {
|
||||||
|
top: 50,
|
||||||
|
bottom: 50,
|
||||||
|
left: 50,
|
||||||
|
right: 50
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const generalistFilePath = path.join(__dirname, '../../../public/carnet-pdf', 'p2.pdf')
|
||||||
|
doc.pipe(fs.createWriteStream(generalistFilePath));
|
||||||
|
|
||||||
const logger = new Logger('Helper');
|
const logger = new Logger('Helper');
|
||||||
|
|
||||||
export const handleError = (error: any, context: string = 'UnknownService'): never => {
|
export const handleError = (error: any, context: string = 'UnknownService'): never => {
|
||||||
@ -57,3 +76,562 @@ export const setEmptyStringsToNull = (obj: any): void => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface pdfGLData {
|
||||||
|
itemNo: number;
|
||||||
|
description: string;
|
||||||
|
noOfPieces: number;
|
||||||
|
weight: string;
|
||||||
|
itemValue: number;
|
||||||
|
countryOfOrigin: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface pdfCarnetData {
|
||||||
|
carnetNo: string;
|
||||||
|
contPageNo: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatNumber(num) {
|
||||||
|
if (isNaN(num)) return 'N/A';
|
||||||
|
return num.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
|
||||||
|
}
|
||||||
|
|
||||||
|
export const generateGeneralistPDF1 = async (jsonData: pdfGLData[], headerData: pdfCarnetData) => {
|
||||||
|
// --- Table Configuration ---
|
||||||
|
const table = {
|
||||||
|
x: doc.x,
|
||||||
|
y: doc.y + 20, // Start a bit below the current cursor
|
||||||
|
cellTopPadding: 5,
|
||||||
|
cellBottomPadding: 2,
|
||||||
|
cellHorizontalPadding: 5,
|
||||||
|
cellSpacing: 0,
|
||||||
|
headerColor: '#FFFFFF',
|
||||||
|
rowColor: '#FFFFFF',
|
||||||
|
alternatingRowColor: '#FFFFFF',
|
||||||
|
lineColor: '#000000',
|
||||||
|
headerFontColor: '#000000',
|
||||||
|
bodyFontColor: '#000000',
|
||||||
|
fontSize: 10,
|
||||||
|
headerFontSize: 10,
|
||||||
|
lineWidth: 0.5,
|
||||||
|
widths: [50, 120, 70, 80, 70, 100], //
|
||||||
|
totalRowPaddingTop: 10 // New padding for the total row
|
||||||
|
};
|
||||||
|
|
||||||
|
// Define the maximum number of lines for the entire page
|
||||||
|
const maxLinesPerPage = 37;
|
||||||
|
const fixedHeaderRowHeight = 22; // You can tweak this value
|
||||||
|
const fixedHeaderFontSize = 10; // Same font size for all rows
|
||||||
|
|
||||||
|
|
||||||
|
// Fixed header rows, which are not part of the dynamic data
|
||||||
|
function getFixedHeaderData(headerValues) {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
isMergedRow: true,
|
||||||
|
cols: [
|
||||||
|
{ text: 'General List', startCol: 0, endCol: 5, }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
isMergedRow: true,
|
||||||
|
cols: [
|
||||||
|
{ text: '', startCol: 0, endCol: 2 },
|
||||||
|
{ text: 'Carnet No:', startCol: 3, endCol: 4 },
|
||||||
|
{ text: headerValues.carnetNo, startCol: 5, endCol: 5 },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
isMergedRow: true,
|
||||||
|
cols: [
|
||||||
|
{ text: '', startCol: 0, endCol: 2 },
|
||||||
|
{ text: 'Contn.Sheet No:', startCol: 3, endCol: 4 },
|
||||||
|
{ text: headerValues.contPageNo, startCol: 5, endCol: 5 },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{ // This is the new blank row
|
||||||
|
isMergedRow: true,
|
||||||
|
cols: [
|
||||||
|
{ text: '', startCol: 0, endCol: 5 }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
['Item No', 'Description', 'No of Pieces', 'Weight', 'Item Value', 'Country Of Origin']
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Helper function to draw a single horizontal line
|
||||||
|
function drawHorizontalLine(doc, x, y, width, lineColor, lineWidth) {
|
||||||
|
doc.strokeColor(lineColor)
|
||||||
|
.lineWidth(lineWidth)
|
||||||
|
.moveTo(x, y)
|
||||||
|
.lineTo(x + width, y)
|
||||||
|
.stroke();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to draw vertical lines between columns for a specific height range
|
||||||
|
function drawVerticalLines(doc, table, startY, endY) {
|
||||||
|
doc.strokeColor(table.lineColor)
|
||||||
|
.lineWidth(table.lineWidth);
|
||||||
|
|
||||||
|
let verticalX = table.x;
|
||||||
|
for (let i = 0; i < table.widths.length; i++) {
|
||||||
|
doc.moveTo(verticalX, startY)
|
||||||
|
.lineTo(verticalX, endY)
|
||||||
|
.stroke();
|
||||||
|
verticalX += table.widths[i];
|
||||||
|
}
|
||||||
|
// Draw the final right vertical line
|
||||||
|
doc.moveTo(verticalX, startY)
|
||||||
|
.lineTo(verticalX, endY)
|
||||||
|
.stroke();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to draw the table header
|
||||||
|
function drawHeader(doc, table, headerData, cursorY, initialX, totalTableWidth) {
|
||||||
|
const totalVerticalPadding = table.cellTopPadding + table.cellBottomPadding;
|
||||||
|
const totalHorizontalPadding = table.cellHorizontalPadding * 2;
|
||||||
|
|
||||||
|
let headerHeight = 0;
|
||||||
|
let maxTextHeight = 0;
|
||||||
|
doc.fontSize(table.headerFontSize).font('Helvetica');
|
||||||
|
|
||||||
|
for (let i = 0; i < headerData.length; i++) {
|
||||||
|
const textOptions = {
|
||||||
|
width: table.widths[i] - totalHorizontalPadding,
|
||||||
|
lineGap: 3
|
||||||
|
};
|
||||||
|
const h = doc.heightOfString(headerData[i], textOptions);
|
||||||
|
if (h > maxTextHeight) maxTextHeight = h;
|
||||||
|
}
|
||||||
|
|
||||||
|
headerHeight = maxTextHeight + totalVerticalPadding;
|
||||||
|
const headerLines = Math.ceil(maxTextHeight / doc.heightOfString('a', { lineGap: 3 }));
|
||||||
|
|
||||||
|
let currentX = initialX;
|
||||||
|
for (let i = 0; i < headerData.length; i++) {
|
||||||
|
doc.rect(currentX, cursorY, table.widths[i], headerHeight).fill(table.headerColor);
|
||||||
|
currentX += table.widths[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
currentX = initialX;
|
||||||
|
for (let i = 0; i < headerData.length; i++) {
|
||||||
|
doc.fillColor(table.headerFontColor);
|
||||||
|
doc.text(headerData[i], currentX + table.cellHorizontalPadding, cursorY + table.cellTopPadding, {
|
||||||
|
width: table.widths[i] - totalHorizontalPadding,
|
||||||
|
lineGap: 3
|
||||||
|
});
|
||||||
|
currentX += table.widths[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return { height: headerHeight, lines: headerLines };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to draw a merged row
|
||||||
|
function drawMergedRow(doc, table, rowData, cursorY, initialX) {
|
||||||
|
const totalHorizontalPadding = table.cellHorizontalPadding * 2;
|
||||||
|
|
||||||
|
const rowHeight = fixedHeaderRowHeight; // Use fixed height
|
||||||
|
const fontSize = fixedHeaderFontSize;
|
||||||
|
|
||||||
|
let currentX = initialX;
|
||||||
|
|
||||||
|
doc.fontSize(fontSize).font('Helvetica'); // Ensure consistent font
|
||||||
|
|
||||||
|
for (let i = 0; i < table.widths.length; i++) {
|
||||||
|
const cell = rowData.cols.find(c => c.startCol === i);
|
||||||
|
if (cell) {
|
||||||
|
let mergedWidth = 0;
|
||||||
|
for (let j = cell.startCol; j <= cell.endCol; j++) {
|
||||||
|
mergedWidth += table.widths[j];
|
||||||
|
}
|
||||||
|
const fillColor = table.rowColor;
|
||||||
|
doc.rect(currentX, cursorY, mergedWidth, rowHeight).fill(fillColor);
|
||||||
|
|
||||||
|
if (cell.text) {
|
||||||
|
doc.fillColor(table.bodyFontColor);
|
||||||
|
doc.text(cell.text, currentX + table.cellHorizontalPadding, cursorY + table.cellTopPadding, {
|
||||||
|
width: mergedWidth - totalHorizontalPadding,
|
||||||
|
height: rowHeight - table.cellTopPadding - table.cellBottomPadding,
|
||||||
|
lineGap: 3
|
||||||
|
});
|
||||||
|
}
|
||||||
|
currentX += mergedWidth;
|
||||||
|
i = cell.endCol;
|
||||||
|
} else {
|
||||||
|
currentX += table.widths[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { height: rowHeight, lines: 1 };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to draw the fixed header block
|
||||||
|
function drawFixedHeader(doc, table, fixedHeaderData, cursorY, initialX, totalTableWidth) {
|
||||||
|
let headerBlockHeight = 0;
|
||||||
|
let headerBlockLines = 0;
|
||||||
|
let horizontalLineYCoords: number[] = [];
|
||||||
|
|
||||||
|
horizontalLineYCoords.push(cursorY); // Top of the entire header block
|
||||||
|
|
||||||
|
// Draw merged rows (first 4 rows, including the new blank one)
|
||||||
|
for (let i = 0; i < 4; i++) {
|
||||||
|
const rowData = fixedHeaderData[i];
|
||||||
|
const { height: rowHeight, lines: rowLines } = drawMergedRow(doc, table, rowData, cursorY, initialX);
|
||||||
|
cursorY += rowHeight;
|
||||||
|
headerBlockHeight += rowHeight;
|
||||||
|
headerBlockLines += rowLines;
|
||||||
|
horizontalLineYCoords.push(cursorY);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw the main header
|
||||||
|
const headerRow = fixedHeaderData[4];
|
||||||
|
const { height: headerHeight, lines: headerLines } = drawHeader(doc, table, headerRow, cursorY, initialX, totalTableWidth);
|
||||||
|
cursorY += headerHeight;
|
||||||
|
headerBlockHeight += headerHeight;
|
||||||
|
headerBlockLines += headerLines;
|
||||||
|
horizontalLineYCoords.push(cursorY);
|
||||||
|
|
||||||
|
// Now draw the horizontal lines for the entire header block
|
||||||
|
for (const yCoord of horizontalLineYCoords) {
|
||||||
|
drawHorizontalLine(doc, initialX, yCoord, totalTableWidth, table.lineColor, table.lineWidth);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now, manually draw the vertical lines for each header row based on its merged cells
|
||||||
|
// Outer left border for the entire header block
|
||||||
|
doc.moveTo(initialX, horizontalLineYCoords[0]).lineTo(initialX, cursorY).stroke();
|
||||||
|
// Outer right border for the entire header block
|
||||||
|
doc.moveTo(initialX + totalTableWidth, horizontalLineYCoords[0]).lineTo(initialX + totalTableWidth, cursorY).stroke();
|
||||||
|
|
||||||
|
// Loop through the fixed header rows to draw internal vertical lines
|
||||||
|
for (let i = 0; i < fixedHeaderData.length; i++) {
|
||||||
|
const rowData = fixedHeaderData[i];
|
||||||
|
const topY = horizontalLineYCoords[i];
|
||||||
|
const bottomY = horizontalLineYCoords[i + 1];
|
||||||
|
let currentX = initialX;
|
||||||
|
|
||||||
|
if (Array.isArray(rowData)) {
|
||||||
|
// Draw all vertical lines for the non-merged header row
|
||||||
|
for (let j = 0; j < table.widths.length - 1; j++) {
|
||||||
|
currentX += table.widths[j];
|
||||||
|
doc.moveTo(currentX, topY).lineTo(currentX, bottomY).stroke();
|
||||||
|
}
|
||||||
|
} else if (i === 3) {
|
||||||
|
// Special case for the blank merged row (4th row, index 3)
|
||||||
|
// We want to draw all the vertical lines as if it's a regular row
|
||||||
|
for (let j = 0; j < table.widths.length - 1; j++) {
|
||||||
|
currentX += table.widths[j];
|
||||||
|
doc.moveTo(currentX, topY).lineTo(currentX, bottomY).stroke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Draw internal lines for other merged rows
|
||||||
|
for (const cell of rowData.cols) {
|
||||||
|
let mergedWidth = 0;
|
||||||
|
for (let j = cell.startCol; j <= cell.endCol; j++) {
|
||||||
|
mergedWidth += table.widths[j];
|
||||||
|
}
|
||||||
|
currentX += mergedWidth;
|
||||||
|
// Don't draw a vertical line if it's the very last column's right border
|
||||||
|
if (cell.endCol < table.widths.length - 1) {
|
||||||
|
doc.moveTo(currentX, topY).lineTo(currentX, bottomY).stroke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { height: headerBlockHeight, endY: cursorY, lines: headerBlockLines };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to draw the total row without borders or vertical lines
|
||||||
|
function drawTotalRow(doc, table, totalValue, cursorY, initialX) {
|
||||||
|
// const totalRowDataArray = ['', '', '', 'Total', totalValue.toFixed(2), ''];
|
||||||
|
const totalRowDataArray = ['', '', '', 'Total', formatNumber(totalValue), ''];
|
||||||
|
const totalVerticalPadding = table.cellTopPadding + table.cellBottomPadding;
|
||||||
|
const totalHorizontalPadding = table.cellHorizontalPadding * 2;
|
||||||
|
const totalTableWidth = table.widths.reduce((a, b) => a + b, 0);
|
||||||
|
|
||||||
|
let totalRowHeight = 0;
|
||||||
|
let maxTotalHeightInRow = 0;
|
||||||
|
for (let j = 0; j < totalRowDataArray.length; j++) {
|
||||||
|
const textOptions = {
|
||||||
|
width: table.widths[j] - totalHorizontalPadding,
|
||||||
|
lineGap: 3
|
||||||
|
};
|
||||||
|
const h = doc.heightOfString(totalRowDataArray[j], textOptions);
|
||||||
|
if (h > maxTotalHeightInRow) {
|
||||||
|
maxTotalHeightInRow = h;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
totalRowHeight = maxTotalHeightInRow + totalVerticalPadding;
|
||||||
|
const linesInTotalRow = Math.ceil(maxTotalHeightInRow / doc.heightOfString('a', { lineGap: 3 }));
|
||||||
|
|
||||||
|
const startY = cursorY + table.totalRowPaddingTop;
|
||||||
|
|
||||||
|
// Draw the text only, without any rectangles or lines
|
||||||
|
let currentX = initialX;
|
||||||
|
doc.fontSize(table.fontSize);
|
||||||
|
for (let j = 0; j < totalRowDataArray.length; j++) {
|
||||||
|
doc.fillColor(table.bodyFontColor);
|
||||||
|
doc.text(totalRowDataArray[j], currentX + table.cellHorizontalPadding, startY + table.cellTopPadding, {
|
||||||
|
width: table.widths[j] - totalHorizontalPadding,
|
||||||
|
lineGap: 3
|
||||||
|
});
|
||||||
|
currentX += table.widths[j];
|
||||||
|
}
|
||||||
|
|
||||||
|
return { height: totalRowHeight + table.totalRowPaddingTop, lines: linesInTotalRow, endY: startY + totalRowHeight };
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// --- Function to manually draw a single data row ---
|
||||||
|
function drawDataRow(doc, table, rowData, cursorY, initialX, isEvenRow) {
|
||||||
|
const totalVerticalPadding = table.cellTopPadding + table.cellBottomPadding;
|
||||||
|
const totalHorizontalPadding = table.cellHorizontalPadding * 2;
|
||||||
|
const totalTableWidth = table.widths.reduce((a, b) => a + b, 0);
|
||||||
|
|
||||||
|
let rowHeight = 0;
|
||||||
|
let maxHeightInRow = 0;
|
||||||
|
|
||||||
|
const rowDataArray = [
|
||||||
|
rowData.itemNo,
|
||||||
|
rowData.description,
|
||||||
|
rowData.noOfPieces,
|
||||||
|
rowData.weight,
|
||||||
|
formatNumber(rowData.itemValue), // formatted Item Value
|
||||||
|
rowData.countryOfOrigin
|
||||||
|
];
|
||||||
|
|
||||||
|
for (let j = 0; j < rowDataArray.length; j++) {
|
||||||
|
const textOptions = { width: table.widths[j] - totalHorizontalPadding, lineGap: 3 };
|
||||||
|
const h = doc.heightOfString(rowDataArray[j] || '', textOptions);
|
||||||
|
if (h > maxHeightInRow) {
|
||||||
|
maxHeightInRow = h;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rowHeight = maxHeightInRow + totalVerticalPadding;
|
||||||
|
const linesInRow = Math.ceil(maxHeightInRow / doc.heightOfString('a', { lineGap: 3 }));
|
||||||
|
|
||||||
|
// Draw the data row
|
||||||
|
const fillColor = isEvenRow ? table.alternatingRowColor : table.rowColor;
|
||||||
|
doc.rect(initialX, cursorY, totalTableWidth, rowHeight).fill(fillColor); // Fill the entire row rectangle
|
||||||
|
let currentX = initialX;
|
||||||
|
doc.fontSize(table.fontSize);
|
||||||
|
// for (let j = 0; j < rowDataArray.length; j++) {
|
||||||
|
// doc.fillColor(table.bodyFontColor);
|
||||||
|
// doc.text(rowDataArray[j], currentX + table.cellHorizontalPadding, cursorY + table.cellTopPadding, {
|
||||||
|
// width: table.widths[j] - totalHorizontalPadding,
|
||||||
|
// lineGap: 3
|
||||||
|
// });
|
||||||
|
// currentX += table.widths[j];
|
||||||
|
// }
|
||||||
|
|
||||||
|
for (let j = 0; j < rowDataArray.length; j++) {
|
||||||
|
doc.fillColor(table.bodyFontColor);
|
||||||
|
|
||||||
|
const alignRight = (j === 2 || j === 4); // right-align No of Pieces and Item Value
|
||||||
|
|
||||||
|
doc.text(rowDataArray[j], currentX + table.cellHorizontalPadding, cursorY + table.cellTopPadding, {
|
||||||
|
width: table.widths[j] - totalHorizontalPadding,
|
||||||
|
lineGap: 3,
|
||||||
|
align: alignRight ? 'right' : 'left'
|
||||||
|
});
|
||||||
|
|
||||||
|
currentX += table.widths[j];
|
||||||
|
}
|
||||||
|
|
||||||
|
return { height: rowHeight, lines: linesInRow, endY: cursorY + rowHeight };
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Function to manually draw the table from JSON data ---
|
||||||
|
function drawTable(doc, table, jsonData, headerData) {
|
||||||
|
let cursorY = table.y;
|
||||||
|
const initialX = table.x;
|
||||||
|
let continuationPageNo = 0;
|
||||||
|
|
||||||
|
const totalTableWidth = table.widths.reduce((a, b) => a + b, 0);
|
||||||
|
|
||||||
|
let rowStart = 0;
|
||||||
|
|
||||||
|
while (rowStart < jsonData.length) {
|
||||||
|
let linesUsed = 0;
|
||||||
|
let rowsOnPage: any = [];
|
||||||
|
let currentY = cursorY;
|
||||||
|
let currentPageTotal = 0;
|
||||||
|
|
||||||
|
// Draw fixed header block and count its visual lines
|
||||||
|
const fixedHeaderData = getFixedHeaderData({ ...headerData, contPageNo: String(continuationPageNo) });
|
||||||
|
const fixedHeaderInfo = drawFixedHeader(doc, table, fixedHeaderData, currentY, initialX, totalTableWidth);
|
||||||
|
currentY = fixedHeaderInfo.endY;
|
||||||
|
linesUsed += fixedHeaderInfo.lines;
|
||||||
|
|
||||||
|
// Collect data rows that can fit in remaining lines
|
||||||
|
for (let i = rowStart; i < jsonData.length; i++) {
|
||||||
|
const rowData = jsonData[i];
|
||||||
|
const isEvenRow = i % 2 === 0;
|
||||||
|
|
||||||
|
const { height, lines } = drawDataRow(doc, table, rowData, -1000, initialX, isEvenRow); // simulate row height
|
||||||
|
const remainingRows = jsonData.length - i - 1;
|
||||||
|
const willBeLastPage = remainingRows === 0;
|
||||||
|
const extraLines = 1 + (willBeLastPage ? 1 : 0);
|
||||||
|
if (linesUsed + lines + extraLines + 1 > maxLinesPerPage) break; // +1 for total row
|
||||||
|
rowsOnPage.push({ data: rowData, isEven: isEvenRow, height, lines });
|
||||||
|
linesUsed += lines;
|
||||||
|
rowStart++;
|
||||||
|
currentPageTotal += parseFloat(rowData.itemValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render data rows
|
||||||
|
let horizontalLineYCoords: number[] = [currentY];
|
||||||
|
for (const row of rowsOnPage as any) {
|
||||||
|
drawDataRow(doc, table, row.data, currentY, initialX, row.isEven);
|
||||||
|
currentY += row.height;
|
||||||
|
horizontalLineYCoords.push(currentY);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw horizontal lines for data rows
|
||||||
|
for (const yCoord of horizontalLineYCoords) {
|
||||||
|
drawHorizontalLine(doc, initialX, yCoord, totalTableWidth, table.lineColor, table.lineWidth);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw vertical lines for data block
|
||||||
|
drawVerticalLines(doc, table, horizontalLineYCoords[0], currentY);
|
||||||
|
|
||||||
|
// Draw total row
|
||||||
|
const totalRowInfo = drawTotalRow(doc, table, currentPageTotal, currentY, initialX);
|
||||||
|
currentY = totalRowInfo.endY;
|
||||||
|
|
||||||
|
const isLastDataPage = rowStart >= jsonData.length;
|
||||||
|
if (isLastDataPage) {
|
||||||
|
const endMessage = '***** End of list, No item can be added *****';
|
||||||
|
const fontSize = 10;
|
||||||
|
const verticalGap = 10;
|
||||||
|
|
||||||
|
doc.fontSize(fontSize).font('Helvetica-Bold');
|
||||||
|
|
||||||
|
const textWidth = doc.widthOfString(endMessage);
|
||||||
|
const centerX = (doc.page.width - textWidth) / 2;
|
||||||
|
const endY = currentY + verticalGap;
|
||||||
|
|
||||||
|
doc.text(endMessage, centerX, endY);
|
||||||
|
currentY = endY + doc.heightOfString(endMessage); // update Y if needed
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Add new page if more data remains
|
||||||
|
if (rowStart < jsonData.length) {
|
||||||
|
doc.addPage();
|
||||||
|
continuationPageNo++;
|
||||||
|
cursorY = doc.page.margins.top;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return continuationPageNo + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw the custom table and get the total page count
|
||||||
|
const totalPages = drawTable(doc, table, jsonData, headerData);
|
||||||
|
|
||||||
|
// Check if the total number of pages is even and add a "VOID" page if it is
|
||||||
|
if (totalPages % 2 === 0) {
|
||||||
|
doc.addPage();
|
||||||
|
const voidText = 'VOID';
|
||||||
|
doc.fontSize(14).font('Helvetica');
|
||||||
|
const textWidth = doc.widthOfString(voidText);
|
||||||
|
const centerX = (doc.page.width - textWidth) / 2;
|
||||||
|
doc.text(voidText, centerX, doc.page.margins.top);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finalize the PDF file
|
||||||
|
doc.end();
|
||||||
|
|
||||||
|
console.log('PDF created successfully!');
|
||||||
|
|
||||||
|
return "p2 done";
|
||||||
|
}
|
||||||
|
|
||||||
|
export const generateGeneralistPDF2 = async (carnetNumber: string) => {
|
||||||
|
const pageWidth = doc.page.width;
|
||||||
|
const seeAttachedText = 'See Attached';
|
||||||
|
const seeAttachedWidth = doc.widthOfString(seeAttachedText);
|
||||||
|
const uniqueNoText = `Carnet No: ${carnetNumber}`;
|
||||||
|
const uniqueNoWidth = doc.widthOfString(uniqueNoText);
|
||||||
|
|
||||||
|
// Center position for "See Attached"
|
||||||
|
const centerX = pageWidth / 2;
|
||||||
|
const seeAttachedX = centerX - (seeAttachedWidth / 2);
|
||||||
|
|
||||||
|
// Position "Unique No" next to "See Attached"
|
||||||
|
const spacing = 90; // gap between texts
|
||||||
|
const uniqueNoX = seeAttachedX + seeAttachedWidth + spacing;
|
||||||
|
|
||||||
|
// Vertically align both (e.g., 100 from top)
|
||||||
|
const y = 100;
|
||||||
|
|
||||||
|
// If unique number will overflow, adjust its X position to stay in same line
|
||||||
|
let adjustedUniqueNoX = uniqueNoX;
|
||||||
|
if (uniqueNoX + uniqueNoWidth > pageWidth - 50) {
|
||||||
|
adjustedUniqueNoX = pageWidth - uniqueNoWidth - 50;
|
||||||
|
}
|
||||||
|
|
||||||
|
doc.text(seeAttachedText, seeAttachedX, y);
|
||||||
|
doc.text(uniqueNoText, adjustedUniqueNoX, y);
|
||||||
|
|
||||||
|
// Finalize PDF
|
||||||
|
doc.end();
|
||||||
|
|
||||||
|
return "p2 done"
|
||||||
|
}
|
||||||
|
|
||||||
|
export const generateGreenCoverPDF = async () => {
|
||||||
|
const templatePath = path.join(__dirname, '../../..', 'public/pdf-templates', '2GeneralList.pdf');
|
||||||
|
const existingPdfBytes = fs.readFileSync(templatePath);
|
||||||
|
|
||||||
|
const pdfDoc = await pdfl.load(existingPdfBytes);
|
||||||
|
const form = pdfDoc.getForm();
|
||||||
|
|
||||||
|
let fields = form.getFields();
|
||||||
|
}
|
||||||
|
|
||||||
|
export const generateFinalPDF = async (inputFiles: string[], outputFile: string) => {
|
||||||
|
const merger = new PDFMerger();
|
||||||
|
|
||||||
|
for (const fileName of inputFiles) {
|
||||||
|
const filePath = path.join(__dirname, '../../../public/carnet-pdf', fileName);
|
||||||
|
console.log(`📄 Adding: ${filePath}`);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await merger.add(filePath);
|
||||||
|
} catch (err) {
|
||||||
|
console.error(`❌ Failed to add PDF: ${fileName}`, err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const outputPath = path.join(__dirname, '../../../public/carnet-pdf', outputFile);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await merger.save(outputPath);
|
||||||
|
console.log(`✅ Merged PDF saved to ${outputPath}`);
|
||||||
|
} catch (err) {
|
||||||
|
console.error(`❌ Failed to save merged PDF`, err);
|
||||||
|
throw new Error("Failed to save merged PDF");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const deleteFilesAsync = async (relativeFilePaths: string[]): Promise<void> => {
|
||||||
|
for (const relativeFilePath of relativeFilePaths) {
|
||||||
|
const filePath = path.join(__dirname, '../../../public/carnet-pdf', relativeFilePath);
|
||||||
|
|
||||||
|
if (fs.existsSync(filePath)) {
|
||||||
|
try {
|
||||||
|
await fsPromises.unlink(filePath);
|
||||||
|
console.log(`🗑️ Deleted: ${filePath}`);
|
||||||
|
} catch (err) {
|
||||||
|
console.error(`❌ Error deleting file: ${filePath}`, err);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.warn(`⚠️ File not found: ${filePath}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
Loading…
x
Reference in New Issue
Block a user