Compare commits

...

62 commits

Author SHA1 Message Date
HAM
426ffb27a7 fix: update Dockerfile
All checks were successful
Spell Check / Spell Check with Typos (push) Successful in 4s
2026-01-12 15:05:21 +07:00
HAM
5d3997343f fix: Dockerfile
All checks were successful
Spell Check / Spell Check with Typos (push) Successful in 5s
2026-01-12 14:52:16 +07:00
HAM
a10626e756 update: entrypoint.sh
All checks were successful
Spell Check / Spell Check with Typos (push) Successful in 4s
2026-01-12 14:49:14 +07:00
HAM
c11fed9832 fix: lock prisma to v6 and fix lockfile
All checks were successful
Spell Check / Spell Check with Typos (push) Successful in 5s
2026-01-12 14:10:11 +07:00
HAM
8909a763c9 chore: remove unuse file
All checks were successful
Spell Check / Spell Check with Typos (push) Successful in 5s
2026-01-12 13:51:54 +07:00
HAM
f0a106e5fe fix: use prisma v6 config
All checks were successful
Spell Check / Spell Check with Typos (push) Successful in 4s
2026-01-12 13:48:10 +07:00
HAM
84b9ddcd2b fix: prisma v7 config in schema.prisma
All checks were successful
Spell Check / Spell Check with Typos (push) Successful in 4s
2026-01-12 13:41:00 +07:00
HAM
e54f62a5b3 fix: prisma v7 config
All checks were successful
Spell Check / Spell Check with Typos (push) Successful in 4s
2026-01-12 13:39:04 +07:00
HAM
cef26278ba fix: missing prisma query take and skip
All checks were successful
Spell Check / Spell Check with Typos (push) Successful in 15s
2026-01-12 13:15:31 +07:00
net
16c4c64c89 refactor: handle code error
All checks were successful
Spell Check / Spell Check with Typos (push) Successful in 4s
2025-10-14 17:12:00 +07:00
HAM
78669ed7ae feat: check invalid data for create taskOrder
All checks were successful
Spell Check / Spell Check with Typos (push) Successful in 4s
2025-10-14 16:19:37 +07:00
HAM
5dc88c22dc feat: delete created data when flow account create fail one
All checks were successful
Spell Check / Spell Check with Typos (push) Successful in 6s
2025-09-29 09:44:41 +07:00
Methapon2001
3454e46212 feat: flowaccount handle installments
All checks were successful
Spell Check / Spell Check with Typos (push) Successful in 4s
2025-09-19 16:17:56 +07:00
HAM
334fb57b46 refactor: flowaccount move product code to front for product name
All checks were successful
Spell Check / Spell Check with Typos (push) Successful in 4s
2025-09-19 16:09:21 +07:00
HAM
a9201f715a fix: missing product code, buyPrice when create and edit and add (ราคาตัวแทน) for agentPrice product name
All checks were successful
Spell Check / Spell Check with Typos (push) Successful in 4s
2025-09-19 15:35:19 +07:00
Methapon2001
95ee32fc57 feat: response branch api
All checks were successful
Spell Check / Spell Check with Typos (push) Successful in 4s
2025-09-19 11:13:42 +07:00
Methapon2001
a33983c530 fix: transaction ended before create
All checks were successful
Spell Check / Spell Check with Typos (push) Successful in 5s
2025-09-19 09:11:21 +07:00
Methapon2001
e8278b6af3 Merge branch 'develop' 2025-09-18 17:12:16 +07:00
Methapon2001
9ef006c860 refactor: handle flow account installments
All checks were successful
Spell Check / Spell Check with Typos (push) Successful in 4s
2025-09-18 17:12:02 +07:00
Methapon2001
8d52a5e726 Merge branch 'develop' 2025-09-18 17:08:13 +07:00
Methapon2001
7858291ae5 fix: receipt flow account
All checks were successful
Spell Check / Spell Check with Typos (push) Successful in 4s
2025-09-18 17:04:40 +07:00
Methapon2001
6ea672a2cb Merge branch 'develop' 2025-09-18 16:56:18 +07:00
Methapon2001
a426e18025 fix: wrong number
All checks were successful
Spell Check / Spell Check with Typos (push) Successful in 4s
2025-09-18 16:55:34 +07:00
Methapon2001
0930c3c833 Merge branch 'develop'
All checks were successful
Spell Check / Spell Check with Typos (push) Successful in 4s
2025-09-18 10:15:36 +07:00
Methapon2001
be3c6405c6 feat: export product
All checks were successful
Spell Check / Spell Check with Typos (push) Successful in 3s
2025-09-18 10:15:32 +07:00
HAM
f50285161b fix: variable name for filter
All checks were successful
Spell Check / Spell Check with Typos (push) Successful in 4s
2025-09-18 10:13:39 +07:00
Methapon2001
4691d559f5 Merge branch 'develop' 2025-09-18 09:50:52 +07:00
HAM
7e7b8025c9 refactor: change to id
All checks were successful
Spell Check / Spell Check with Typos (push) Successful in 4s
2025-09-17 12:58:14 +07:00
HAM
e5a3d948a5 fix: missing query
All checks were successful
Spell Check / Spell Check with Typos (push) Successful in 5s
2025-09-17 12:39:10 +07:00
HAM
0d78ce4db3 feat: filter businessType, province, district, subDistrict for customer and customer-export endpoint 2025-09-17 12:37:41 +07:00
Methapon2001
9e74fb0fe6 Merge branch 'develop' 2025-09-17 12:01:55 +07:00
Methapon2001
ab4ea4ba4b fix: type error
All checks were successful
Spell Check / Spell Check with Typos (push) Successful in 4s
2025-09-17 11:55:06 +07:00
Methapon2001
068ba2d293 fix: wrong tax
All checks were successful
Spell Check / Spell Check with Typos (push) Successful in 4s
2025-09-17 11:53:15 +07:00
Methapon2001
beb7f4bcfe fix: flowaccount contact name
All checks were successful
Spell Check / Spell Check with Typos (push) Successful in 5s
2025-09-17 11:34:54 +07:00
Methapon2001
b6b35509e3 Merge branch 'develop' 2025-09-17 10:42:40 +07:00
Methapon2001
6598cd3bdf fix: error field ambigous 2025-09-17 10:42:35 +07:00
Methapon2001
2c790de606 fix: customer status not change after used 2025-09-17 09:51:35 +07:00
Methapon2001
6f1bca5234 Merge branch 'develop' 2025-09-16 13:20:30 +07:00
Methapon2001
158a6ff163 fix: timeout in some case
All checks were successful
Spell Check / Spell Check with Typos (push) Successful in 4s
2025-09-16 11:52:13 +07:00
Methapon2001
0772e4710a fix: cannot update payment data after set payment completed
All checks were successful
Spell Check / Spell Check with Typos (push) Successful in 4s
2025-09-16 11:44:13 +07:00
Methapon2001
25a4b50f8e fix: wrong condition
All checks were successful
Spell Check / Spell Check with Typos (push) Successful in 4s
2025-09-16 10:11:02 +07:00
Methapon2001
de33d03631 fix: calculate price
All checks were successful
Spell Check / Spell Check with Typos (push) Successful in 4s
2025-09-16 10:08:01 +07:00
HAM
4e71343af7 refactor: use function for correct decimal number
All checks were successful
Spell Check / Spell Check with Typos (push) Successful in 5s
2025-09-16 09:53:03 +07:00
HAM
6776188f7b fix: decimal number
All checks were successful
Spell Check / Spell Check with Typos (push) Successful in 4s
2025-09-16 09:50:23 +07:00
Methapon2001
892d76583f chore: flowaccount enable inline vat
All checks were successful
Spell Check / Spell Check with Typos (push) Successful in 4s
2025-09-16 09:48:59 +07:00
Methapon2001
f2def1b962 fix: wrong calc vat condition
All checks were successful
Spell Check / Spell Check with Typos (push) Successful in 4s
2025-09-16 09:40:31 +07:00
Methapon2001
1486ce79ab fix: calculate credit note price 2025-09-16 09:40:31 +07:00
HAM
d51531cd4d chore: clean
All checks were successful
Spell Check / Spell Check with Typos (push) Successful in 4s
2025-09-12 15:29:52 +07:00
HAM
61825309d1 feat: sync data from data to flowAccount
All checks were successful
Spell Check / Spell Check with Typos (push) Successful in 5s
2025-09-12 15:20:20 +07:00
HAM
250bbca226 feat: create product for flowAccount service 2025-09-12 15:19:51 +07:00
HAM
c774e9f44c chore: migration
All checks were successful
Spell Check / Spell Check with Typos (push) Successful in 4s
2025-09-12 11:56:58 +07:00
HAM
8f2810ea29 feat: add flowAccountProductId field 2025-09-12 11:56:40 +07:00
Methapon2001
eda0edbd29 feat: send remark to flowaccount
All checks were successful
Spell Check / Spell Check with Typos (push) Successful in 5s
2025-09-11 17:23:21 +07:00
Methapon2001
86db927efe fix: price calc
All checks were successful
Spell Check / Spell Check with Typos (push) Successful in 4s
2025-09-11 13:57:23 +07:00
Methapon2001
5674a18cc3 fix: price calc match with flow account
All checks were successful
Spell Check / Spell Check with Typos (push) Successful in 5s
2025-09-11 11:48:17 +07:00
Methapon2001
d3c5b49649 chore: update prisma
All checks were successful
Spell Check / Spell Check with Typos (push) Successful in 4s
2025-09-11 09:32:38 +07:00
Methapon2001
c47ffb5435 chore: migration
All checks were successful
Spell Check / Spell Check with Typos (push) Successful in 4s
2025-09-11 09:17:52 +07:00
Methapon2001
710382d544 feat: add more metadata for payment 2025-09-11 09:17:23 +07:00
Methapon2001
4042cbcea4 chore: add html to text dep
All checks were successful
Spell Check / Spell Check with Typos (push) Successful in 6s
2025-09-11 09:07:30 +07:00
Methapon2001
f487a9169c fix: prevent line user id and otp exposes 2025-09-10 11:44:22 +07:00
Methapon2001
ab8fd2ca43 feat: export customer and employee as csv 2025-09-10 11:44:22 +07:00
Methapon2001
d95eb349ec fix: update messenger also update work step messenger 2025-09-10 11:44:22 +07:00
22 changed files with 1367 additions and 328 deletions

View file

@ -1,34 +1,22 @@
FROM node:23-slim AS base FROM node:20-slim
ENV PNPM_HOME="/pnpm" RUN apt-get update -y \
ENV PATH="$PNPM_HOME:$PATH" && apt-get install -y openssl \
&& npm install -g pnpm \
RUN corepack enable && apt-get clean \
RUN apt-get update && apt-get install -y openssl fontconfig && rm -rf /var/lib/apt/lists/*
RUN fc-cache -f -v
RUN pnpm i -g prisma prisma-kysely
WORKDIR /app WORKDIR /app
COPY package.json pnpm-lock.yaml ./
RUN pnpm install --frozen-lockfile
COPY . . COPY . .
FROM base AS deps
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --prod --frozen-lockfile
RUN pnpm prisma generate
FROM base AS build
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile
RUN pnpm prisma generate RUN pnpm prisma generate
RUN pnpm run build RUN pnpm run build
FROM base AS prod COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
ENV NODE_ENV="production" ENTRYPOINT ["/entrypoint.sh"]
COPY --from=deps /app/node_modules /app/node_modules
COPY --from=build /app/dist /app/dist
COPY --from=base /app/static /app/static
RUN chmod u+x ./entrypoint.sh
ENTRYPOINT ["./entrypoint.sh"]

View file

@ -31,7 +31,7 @@
"@vitest/ui": "^3.1.4", "@vitest/ui": "^3.1.4",
"nodemon": "^3.1.9", "nodemon": "^3.1.9",
"prettier": "^3.4.2", "prettier": "^3.4.2",
"prisma": "^6.3.0", "prisma": "6.16.2",
"prisma-kysely": "^1.8.0", "prisma-kysely": "^1.8.0",
"ts-node": "^10.9.2", "ts-node": "^10.9.2",
"typescript": "^5.7.2", "typescript": "^5.7.2",
@ -40,12 +40,14 @@
"dependencies": { "dependencies": {
"@elastic/elasticsearch": "^8.17.0", "@elastic/elasticsearch": "^8.17.0",
"@fast-csv/parse": "^5.0.2", "@fast-csv/parse": "^5.0.2",
"@prisma/client": "^6.3.0", "@prisma/client": "6.16.2",
"@scalar/express-api-reference": "^0.4.182", "@scalar/express-api-reference": "^0.4.182",
"@tsoa/runtime": "^6.6.0", "@tsoa/runtime": "^6.6.0",
"@types/html-to-text": "^9.0.4",
"canvas": "^3.1.0", "canvas": "^3.1.0",
"cors": "^2.8.5", "cors": "^2.8.5",
"cron": "^3.3.1", "cron": "^3.3.1",
"csv-parse": "^6.1.0",
"dayjs": "^1.11.13", "dayjs": "^1.11.13",
"dayjs-plugin-utc": "^0.1.2", "dayjs-plugin-utc": "^0.1.2",
"docx-templates": "^4.13.0", "docx-templates": "^4.13.0",
@ -53,6 +55,7 @@
"exceljs": "^4.4.0", "exceljs": "^4.4.0",
"express": "^4.21.2", "express": "^4.21.2",
"fast-jwt": "^5.0.5", "fast-jwt": "^5.0.5",
"html-to-text": "^9.0.5",
"jsbarcode": "^3.11.6", "jsbarcode": "^3.11.6",
"json-2-csv": "^5.5.8", "json-2-csv": "^5.5.8",
"kysely": "^0.27.5", "kysely": "^0.27.5",
@ -60,6 +63,7 @@
"morgan": "^1.10.0", "morgan": "^1.10.0",
"multer": "^1.4.5-lts.2", "multer": "^1.4.5-lts.2",
"nodemailer": "^6.10.0", "nodemailer": "^6.10.0",
"pnpm": "^10.18.3",
"prisma-extension-kysely": "^3.0.0", "prisma-extension-kysely": "^3.0.0",
"promise.any": "^2.0.6", "promise.any": "^2.0.6",
"thai-baht-text": "^2.0.5", "thai-baht-text": "^2.0.5",

425
pnpm-lock.yaml generated
View file

@ -15,14 +15,17 @@ importers:
specifier: ^5.0.2 specifier: ^5.0.2
version: 5.0.2 version: 5.0.2
'@prisma/client': '@prisma/client':
specifier: ^6.3.0 specifier: 6.16.2
version: 6.3.0(prisma@6.3.0(typescript@5.7.2))(typescript@5.7.2) version: 6.16.2(prisma@6.16.2(typescript@5.7.2))(typescript@5.7.2)
'@scalar/express-api-reference': '@scalar/express-api-reference':
specifier: ^0.4.182 specifier: ^0.4.182
version: 0.4.182 version: 0.4.182
'@tsoa/runtime': '@tsoa/runtime':
specifier: ^6.6.0 specifier: ^6.6.0
version: 6.6.0 version: 6.6.0
'@types/html-to-text':
specifier: ^9.0.4
version: 9.0.4
canvas: canvas:
specifier: ^3.1.0 specifier: ^3.1.0
version: 3.1.0 version: 3.1.0
@ -32,6 +35,9 @@ importers:
cron: cron:
specifier: ^3.3.1 specifier: ^3.3.1
version: 3.3.1 version: 3.3.1
csv-parse:
specifier: ^6.1.0
version: 6.1.0
dayjs: dayjs:
specifier: ^1.11.13 specifier: ^1.11.13
version: 1.11.13 version: 1.11.13
@ -53,6 +59,9 @@ importers:
fast-jwt: fast-jwt:
specifier: ^5.0.5 specifier: ^5.0.5
version: 5.0.5 version: 5.0.5
html-to-text:
specifier: ^9.0.5
version: 9.0.5
jsbarcode: jsbarcode:
specifier: ^3.11.6 specifier: ^3.11.6
version: 3.11.6 version: 3.11.6
@ -74,9 +83,12 @@ importers:
nodemailer: nodemailer:
specifier: ^6.10.0 specifier: ^6.10.0
version: 6.10.0 version: 6.10.0
pnpm:
specifier: ^10.18.3
version: 10.28.0
prisma-extension-kysely: prisma-extension-kysely:
specifier: ^3.0.0 specifier: ^3.0.0
version: 3.0.0(@prisma/client@6.3.0(prisma@6.3.0(typescript@5.7.2))(typescript@5.7.2)) version: 3.0.0(@prisma/client@6.16.2(prisma@6.16.2(typescript@5.7.2))(typescript@5.7.2))
promise.any: promise.any:
specifier: ^2.0.6 specifier: ^2.0.6
version: 2.0.6 version: 2.0.6
@ -127,8 +139,8 @@ importers:
specifier: ^3.4.2 specifier: ^3.4.2
version: 3.4.2 version: 3.4.2
prisma: prisma:
specifier: ^6.3.0 specifier: 6.16.2
version: 6.3.0(typescript@5.7.2) version: 6.16.2(typescript@5.7.2)
prisma-kysely: prisma-kysely:
specifier: ^1.8.0 specifier: ^1.8.0
version: 1.8.0(encoding@0.1.13) version: 1.8.0(encoding@0.1.13)
@ -140,7 +152,7 @@ importers:
version: 5.7.2 version: 5.7.2
vitest: vitest:
specifier: ^3.1.4 specifier: ^3.1.4
version: 3.1.4(@types/node@20.17.10)(@vitest/ui@3.1.4)(yaml@2.6.1) version: 3.1.4(@types/node@20.17.10)(@vitest/ui@3.1.4)(jiti@2.5.1)(yaml@2.6.1)
packages: packages:
@ -516,8 +528,8 @@ packages:
'@polka/url@1.0.0-next.29': '@polka/url@1.0.0-next.29':
resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==}
'@prisma/client@6.3.0': '@prisma/client@6.16.2':
resolution: {integrity: sha512-BY3Fi28PUSk447Bpv22LhZp4HgNPo7NsEN+EteM1CLDnLjig5863jpW+3c3HHLFmml+nB/eJv1CjSriFZ8z7Cg==} resolution: {integrity: sha512-E00PxBcalMfYO/TWnXobBVUai6eW/g5OsifWQsQDzJYm7yaY+IRLo7ZLsaefi0QkTpxfuhFcQ/w180i6kX3iJw==}
engines: {node: '>=18.18'} engines: {node: '>=18.18'}
peerDependencies: peerDependencies:
prisma: '*' prisma: '*'
@ -528,26 +540,29 @@ packages:
typescript: typescript:
optional: true optional: true
'@prisma/config@6.16.2':
resolution: {integrity: sha512-mKXSUrcqXj0LXWPmJsK2s3p9PN+aoAbyMx7m5E1v1FufofR1ZpPoIArjjzOIm+bJRLLvYftoNYLx1tbHgF9/yg==}
'@prisma/debug@5.3.1': '@prisma/debug@5.3.1':
resolution: {integrity: sha512-eYrxqslEKf+wpMFIIHgbcNYuZBXUdiJLA85Or3TwOhgPIN1ZoXT9CwJph3ynW8H1Xg0LkdYLwVmuULCwiMoU5A==} resolution: {integrity: sha512-eYrxqslEKf+wpMFIIHgbcNYuZBXUdiJLA85Or3TwOhgPIN1ZoXT9CwJph3ynW8H1Xg0LkdYLwVmuULCwiMoU5A==}
'@prisma/debug@6.3.0': '@prisma/debug@6.16.2':
resolution: {integrity: sha512-m1lQv//0Rc5RG8TBpNUuLCxC35Ghi5XfpPmL83Gh04/GICHD2J5H2ndMlaljrUNaQDF9dOxIuFAYP1rE9wkXkg==} resolution: {integrity: sha512-bo4/gA/HVV6u8YK2uY6glhNsJ7r+k/i5iQ9ny/3q5bt9ijCj7WMPUwfTKPvtEgLP+/r26Z686ly11hhcLiQ8zA==}
'@prisma/engines-version@6.3.0-17.acc0b9dd43eb689cbd20c9470515d719db10d0b0': '@prisma/engines-version@6.16.0-7.1c57fdcd7e44b29b9313256c76699e91c3ac3c43':
resolution: {integrity: sha512-R/ZcMuaWZT2UBmgX3Ko6PAV3f8//ZzsjRIG1eKqp3f2rqEqVtCv+mtzuH2rBPUC9ujJ5kCb9wwpxeyCkLcHVyA==} resolution: {integrity: sha512-ThvlDaKIVrnrv97ujNFDYiQbeMQpLa0O86HFA2mNoip4mtFqM7U5GSz2ie1i2xByZtvPztJlNRgPsXGeM/kqAA==}
'@prisma/engines@5.3.1': '@prisma/engines@5.3.1':
resolution: {integrity: sha512-6QkILNyfeeN67BNEPEtkgh3Xo2tm6D7V+UhrkBbRHqKw9CTaz/vvTP/ROwYSP/3JT2MtIutZm/EnhxUiuOPVDA==} resolution: {integrity: sha512-6QkILNyfeeN67BNEPEtkgh3Xo2tm6D7V+UhrkBbRHqKw9CTaz/vvTP/ROwYSP/3JT2MtIutZm/EnhxUiuOPVDA==}
'@prisma/engines@6.3.0': '@prisma/engines@6.16.2':
resolution: {integrity: sha512-RXqYhlZb9sx/xkUfYIZuEPn7sT0WgTxNOuEYQ7AGw3IMpP9QGVEDVsluc/GcNkM8NTJszeqk8AplJzI9lm7Jxw==} resolution: {integrity: sha512-7yf3AjfPUgsg/l7JSu1iEhsmZZ/YE00yURPjTikqm2z4btM0bCl2coFtTGfeSOWbQMmq45Jab+53yGUIAT1sjA==}
'@prisma/fetch-engine@5.3.1': '@prisma/fetch-engine@5.3.1':
resolution: {integrity: sha512-w1yk1YiK8N82Pobdq58b85l6e8akyrkxuzwV9DoiUTRf3gpsuhJJesHc4Yi0WzUC9/3znizl1UfCsI6dhkj3Vw==} resolution: {integrity: sha512-w1yk1YiK8N82Pobdq58b85l6e8akyrkxuzwV9DoiUTRf3gpsuhJJesHc4Yi0WzUC9/3znizl1UfCsI6dhkj3Vw==}
'@prisma/fetch-engine@6.3.0': '@prisma/fetch-engine@6.16.2':
resolution: {integrity: sha512-GBy0iT4f1mH31ePzfcpVSUa7JLRTeq4914FG2vR3LqDwRweSm4ja1o5flGDz+eVIa/BNYfkBvRRxv4D6ve6Eew==} resolution: {integrity: sha512-wPnZ8DMRqpgzye758ZvfAMiNJRuYpz+rhgEBZi60ZqDIgOU2694oJxiuu3GKFeYeR/hXxso4/2oBC243t/whxQ==}
'@prisma/generator-helper@5.3.1': '@prisma/generator-helper@5.3.1':
resolution: {integrity: sha512-zrYS0iHLgPlOJjYnd5KvVMMvSS+ktOL39EwooS5EnyvfzwfzxlKCeOUgxTfiKYs0WUWqzEvyNAYtramYgSknsQ==} resolution: {integrity: sha512-zrYS0iHLgPlOJjYnd5KvVMMvSS+ktOL39EwooS5EnyvfzwfzxlKCeOUgxTfiKYs0WUWqzEvyNAYtramYgSknsQ==}
@ -555,8 +570,8 @@ packages:
'@prisma/get-platform@5.3.1': '@prisma/get-platform@5.3.1':
resolution: {integrity: sha512-3IiZY2BUjKnAuZ0569zppZE6/rZbVAM09//c2nvPbbkGG9MqrirA8fbhhF7tfVmhyVfdmVCHnf/ujWPHJ8B46Q==} resolution: {integrity: sha512-3IiZY2BUjKnAuZ0569zppZE6/rZbVAM09//c2nvPbbkGG9MqrirA8fbhhF7tfVmhyVfdmVCHnf/ujWPHJ8B46Q==}
'@prisma/get-platform@6.3.0': '@prisma/get-platform@6.16.2':
resolution: {integrity: sha512-V8zZ1d0xfyi6FjpNP4AcYuwSpGcdmu35OXWnTPm8IW594PYALzKXHwIa9+o0f+Lo9AecFWrwrwaoYe56UNfTtQ==} resolution: {integrity: sha512-U/P36Uke5wS7r1+omtAgJpEB94tlT4SdlgaeTc6HVTTT93pXj7zZ+B/cZnmnvjcNPfWddgoDx8RLjmQwqGDYyA==}
'@prisma/internals@5.3.1': '@prisma/internals@5.3.1':
resolution: {integrity: sha512-zkW73hPHHNrMD21PeYgCTBfMu71vzJf+WtfydtJbS0JVJKyLfOel0iWSQg7wjNeQfccKp+NdHJ/5rTJ4NEUzgA==} resolution: {integrity: sha512-zkW73hPHHNrMD21PeYgCTBfMu71vzJf+WtfydtJbS0JVJKyLfOel0iWSQg7wjNeQfccKp+NdHJ/5rTJ4NEUzgA==}
@ -676,6 +691,12 @@ packages:
resolution: {integrity: sha512-4mQYkQJO0HHaoFd8Z+vSdQAvYcCJ2bRLN9ewE+GneB8kvoLG/oM3ynroqzGQdoytH8BmhnJwD3aEUagfbK2x5g==} resolution: {integrity: sha512-4mQYkQJO0HHaoFd8Z+vSdQAvYcCJ2bRLN9ewE+GneB8kvoLG/oM3ynroqzGQdoytH8BmhnJwD3aEUagfbK2x5g==}
engines: {node: '>=18'} engines: {node: '>=18'}
'@selderee/plugin-htmlparser2@0.11.0':
resolution: {integrity: sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ==}
'@standard-schema/spec@1.0.0':
resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==}
'@swc/helpers@0.5.15': '@swc/helpers@0.5.15':
resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==}
@ -742,6 +763,9 @@ packages:
'@types/express@4.17.21': '@types/express@4.17.21':
resolution: {integrity: sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==} resolution: {integrity: sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==}
'@types/html-to-text@9.0.4':
resolution: {integrity: sha512-pUY3cKH/Nm2yYrEmDlPR1mR7yszjGx4DrwPjQ702C4/D5CwHuZTgZdIdwPkRbcuhs7BAh2L5rg3CL5cbRiGTCQ==}
'@types/http-assert@1.5.6': '@types/http-assert@1.5.6':
resolution: {integrity: sha512-TTEwmtjgVbYAzZYWyeHPrrtWnfVkm8tQkP8P21uQifPgMRgjrow3XDEYqucuC8SKZJT7pUnhU/JymvjggxO9vw==} resolution: {integrity: sha512-TTEwmtjgVbYAzZYWyeHPrrtWnfVkm8tQkP8P21uQifPgMRgjrow3XDEYqucuC8SKZJT7pUnhU/JymvjggxO9vw==}
@ -1072,6 +1096,14 @@ packages:
resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==}
engines: {node: '>= 0.8'} engines: {node: '>= 0.8'}
c12@3.1.0:
resolution: {integrity: sha512-uWoS8OU1MEIsOv8p/5a82c3H31LsWVR5qiyXVfBNOzfffjUWtPnhAb4BYI2uG2HfGmZmFjCtui5XNWaps+iFuw==}
peerDependencies:
magicast: ^0.3.5
peerDependenciesMeta:
magicast:
optional: true
cac@6.7.14: cac@6.7.14:
resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==}
engines: {node: '>=8'} engines: {node: '>=8'}
@ -1121,6 +1153,10 @@ packages:
resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==}
engines: {node: '>= 8.10.0'} engines: {node: '>= 8.10.0'}
chokidar@4.0.3:
resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==}
engines: {node: '>= 14.16.0'}
chownr@1.1.4: chownr@1.1.4:
resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==}
@ -1128,6 +1164,9 @@ packages:
resolution: {integrity: sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==} resolution: {integrity: sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==}
engines: {node: '>=8'} engines: {node: '>=8'}
citty@0.1.6:
resolution: {integrity: sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==}
cjs-module-lexer@1.4.1: cjs-module-lexer@1.4.1:
resolution: {integrity: sha512-cuSVIHi9/9E/+821Qjdvngor+xpnlwnuwIyZOaLmHBVdXL+gP+I6QQB9VkO7RI77YIcTV+S1W9AreJ5eN63JBA==} resolution: {integrity: sha512-cuSVIHi9/9E/+821Qjdvngor+xpnlwnuwIyZOaLmHBVdXL+gP+I6QQB9VkO7RI77YIcTV+S1W9AreJ5eN63JBA==}
@ -1187,6 +1226,13 @@ packages:
resolution: {integrity: sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==} resolution: {integrity: sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==}
engines: {'0': node >= 0.8} engines: {'0': node >= 0.8}
confbox@0.2.2:
resolution: {integrity: sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==}
consola@3.4.2:
resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==}
engines: {node: ^14.18.0 || >=16.10.0}
console-log-level@1.4.1: console-log-level@1.4.1:
resolution: {integrity: sha512-VZzbIORbP+PPcN/gg3DXClTLPLg5Slwd5fL2MIc+o1qZ4BXBvWyc6QxPk6T/Mkr6IVjRpoAGf32XxP3ZWMVRcQ==} resolution: {integrity: sha512-VZzbIORbP+PPcN/gg3DXClTLPLg5Slwd5fL2MIc+o1qZ4BXBvWyc6QxPk6T/Mkr6IVjRpoAGf32XxP3ZWMVRcQ==}
@ -1243,6 +1289,9 @@ packages:
resolution: {integrity: sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==} resolution: {integrity: sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==}
engines: {node: '>=8'} engines: {node: '>=8'}
csv-parse@6.1.0:
resolution: {integrity: sha512-CEE+jwpgLn+MmtCpVcPtiCZpVtB6Z2OKPTr34pycYYoL7sxdOkXDdQ4lRiw6ioC0q6BLqhc6cKweCVvral8yhw==}
data-view-buffer@1.0.1: data-view-buffer@1.0.1:
resolution: {integrity: sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==} resolution: {integrity: sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
@ -1307,6 +1356,14 @@ packages:
resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==}
engines: {node: '>=4.0.0'} engines: {node: '>=4.0.0'}
deepmerge-ts@7.1.5:
resolution: {integrity: sha512-HOJkrhaYsweh+W+e74Yn7YStZOilkoPb6fycpwNLKzSPtruFs48nYis0zy5yJz1+ktUhHxoRDJ27RQAWLIJVJw==}
engines: {node: '>=16.0.0'}
deepmerge@4.3.1:
resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==}
engines: {node: '>=0.10.0'}
define-data-property@1.1.4: define-data-property@1.1.4:
resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
@ -1315,6 +1372,9 @@ packages:
resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
defu@6.1.4:
resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==}
del@6.1.1: del@6.1.1:
resolution: {integrity: sha512-ua8BhapfP0JUJKC/zV9yHHDW/rDoDxP4Zhn3AkA6/xT6gY7jYXJiaeyBZznYVujhZZET+UgcbZiQ7sN3WqcImg==} resolution: {integrity: sha512-ua8BhapfP0JUJKC/zV9yHHDW/rDoDxP4Zhn3AkA6/xT6gY7jYXJiaeyBZznYVujhZZET+UgcbZiQ7sN3WqcImg==}
engines: {node: '>=10'} engines: {node: '>=10'}
@ -1323,6 +1383,9 @@ packages:
resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==}
engines: {node: '>= 0.8'} engines: {node: '>= 0.8'}
destr@2.0.5:
resolution: {integrity: sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==}
destroy@1.2.0: destroy@1.2.0:
resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==}
engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
@ -1347,6 +1410,19 @@ packages:
resolution: {integrity: sha512-tTmR3WhROYctuyVReQ+PfCU3zprmC45/VuSVzn8EjovzpRkXYUdXiDatB9M8pasj0V+wuuOyY8bcSHvlQ2GNag==} resolution: {integrity: sha512-tTmR3WhROYctuyVReQ+PfCU3zprmC45/VuSVzn8EjovzpRkXYUdXiDatB9M8pasj0V+wuuOyY8bcSHvlQ2GNag==}
engines: {node: '>=6'} engines: {node: '>=6'}
dom-serializer@2.0.0:
resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==}
domelementtype@2.3.0:
resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==}
domhandler@5.0.3:
resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==}
engines: {node: '>= 4'}
domutils@3.2.2:
resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==}
dotenv@16.0.3: dotenv@16.0.3:
resolution: {integrity: sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==} resolution: {integrity: sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==}
engines: {node: '>=12'} engines: {node: '>=12'}
@ -1355,6 +1431,10 @@ packages:
resolution: {integrity: sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==} resolution: {integrity: sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==}
engines: {node: '>=12'} engines: {node: '>=12'}
dotenv@16.6.1:
resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==}
engines: {node: '>=12'}
dunder-proto@1.0.0: dunder-proto@1.0.0:
resolution: {integrity: sha512-9+Sj30DIu+4KvHqMfLUGLFYL2PkURSYMVXJyXe92nFRvlYq5hBjLEhblKB+vkd/WVlUYMWigiY07T91Fkk0+4A==} resolution: {integrity: sha512-9+Sj30DIu+4KvHqMfLUGLFYL2PkURSYMVXJyXe92nFRvlYq5hBjLEhblKB+vkd/WVlUYMWigiY07T91Fkk0+4A==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
@ -1371,6 +1451,9 @@ packages:
ee-first@1.1.1: ee-first@1.1.1:
resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==}
effect@3.16.12:
resolution: {integrity: sha512-N39iBk0K71F9nb442TLbTkjl24FLUzuvx2i1I2RsEAQsdAdUTuUoW0vlfUXgkMTUOnYqKnWcFfqw4hK4Pw27hg==}
elastic-apm-node@3.52.2: elastic-apm-node@3.52.2:
resolution: {integrity: sha512-NVFthDcoBOpTwtppF7b+BIeIu4Xon3RBNpddIaJv+DtjL6Q61x4j7ClYdiXjv3XKgyp7yUlOnLjU6PY/EYXwLQ==} resolution: {integrity: sha512-NVFthDcoBOpTwtppF7b+BIeIu4Xon3RBNpddIaJv+DtjL6Q61x4j7ClYdiXjv3XKgyp7yUlOnLjU6PY/EYXwLQ==}
engines: {node: '>=8.6.0'} engines: {node: '>=8.6.0'}
@ -1381,6 +1464,10 @@ packages:
emoji-regex@9.2.2: emoji-regex@9.2.2:
resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
empathic@2.0.0:
resolution: {integrity: sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA==}
engines: {node: '>=14'}
enabled@2.0.0: enabled@2.0.0:
resolution: {integrity: sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==} resolution: {integrity: sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==}
@ -1398,6 +1485,10 @@ packages:
end-of-stream@1.4.4: end-of-stream@1.4.4:
resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==}
entities@4.5.0:
resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
engines: {node: '>=0.12'}
env-paths@2.2.1: env-paths@2.2.1:
resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==}
engines: {node: '>=6'} engines: {node: '>=6'}
@ -1495,6 +1586,13 @@ packages:
resolution: {integrity: sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==} resolution: {integrity: sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==}
engines: {node: '>= 0.10.0'} engines: {node: '>= 0.10.0'}
exsolve@1.0.7:
resolution: {integrity: sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw==}
fast-check@3.23.2:
resolution: {integrity: sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A==}
engines: {node: '>=8.0.0'}
fast-csv@4.3.6: fast-csv@4.3.6:
resolution: {integrity: sha512-2RNSpuwwsJGP0frGsOmTb9oUF+VkFSM4SyLTDgwf2ciHWTarN0lQTC+F2f/t5J9QjW+c65VFIAAu85GsvMIusw==} resolution: {integrity: sha512-2RNSpuwwsJGP0frGsOmTb9oUF+VkFSM4SyLTDgwf2ciHWTarN0lQTC+F2f/t5J9QjW+c65VFIAAu85GsvMIusw==}
engines: {node: '>=10.0.0'} engines: {node: '>=10.0.0'}
@ -1652,6 +1750,10 @@ packages:
resolution: {integrity: sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==} resolution: {integrity: sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
giget@2.0.0:
resolution: {integrity: sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA==}
hasBin: true
github-from-package@0.0.0: github-from-package@0.0.0:
resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==} resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==}
@ -1740,6 +1842,13 @@ packages:
resolution: {integrity: sha512-A91dYTeIB6NoXG+PxTQpCCDDnfHsW9kc06Lvpu1TEe9gnd6ZFeiBoRO9JvzEv6xK7EX97/dUE8g/vBMTqTS3CA==} resolution: {integrity: sha512-A91dYTeIB6NoXG+PxTQpCCDDnfHsW9kc06Lvpu1TEe9gnd6ZFeiBoRO9JvzEv6xK7EX97/dUE8g/vBMTqTS3CA==}
engines: {node: '>=14'} engines: {node: '>=14'}
html-to-text@9.0.5:
resolution: {integrity: sha512-qY60FjREgVZL03vJU6IfMV4GDjGBIoOyvuFdpBDIX9yTlDw0TjxVBQp+P8NvpdIXNJvfWBTNul7fsAQJq2FNpg==}
engines: {node: '>=14'}
htmlparser2@8.0.2:
resolution: {integrity: sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==}
http-errors@2.0.0: http-errors@2.0.0:
resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==}
engines: {node: '>= 0.8'} engines: {node: '>= 0.8'}
@ -1998,6 +2107,10 @@ packages:
jackspeak@3.4.3: jackspeak@3.4.3:
resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==}
jiti@2.5.1:
resolution: {integrity: sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w==}
hasBin: true
js-tokens@4.0.0: js-tokens@4.0.0:
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
@ -2040,6 +2153,9 @@ packages:
resolution: {integrity: sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==} resolution: {integrity: sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==}
engines: {node: '>= 0.6.3'} engines: {node: '>= 0.6.3'}
leac@0.6.0:
resolution: {integrity: sha512-y+SqErxb8h7nE/fiEX07jsbuhrpO9lL8eca7/Y1nuWV2moNlXhyd59iDGcRf6moVyDMbmTNzL40SUyrFU/yDpg==}
lie@3.3.0: lie@3.3.0:
resolution: {integrity: sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==} resolution: {integrity: sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==}
@ -2309,6 +2425,9 @@ packages:
node-addon-api@7.1.1: node-addon-api@7.1.1:
resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==} resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==}
node-fetch-native@1.6.7:
resolution: {integrity: sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==}
node-fetch@2.6.12: node-fetch@2.6.12:
resolution: {integrity: sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g==} resolution: {integrity: sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g==}
engines: {node: 4.x || >=6.0.0} engines: {node: 4.x || >=6.0.0}
@ -2360,6 +2479,11 @@ packages:
resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==}
engines: {node: '>=8'} engines: {node: '>=8'}
nypm@0.6.1:
resolution: {integrity: sha512-hlacBiRiv1k9hZFiphPUkfSQ/ZfQzZDzC+8z0wL3lvDAOUu/2NnChkKuMoMjNur/9OpKuz2QsIeiPVN0xM5Q0w==}
engines: {node: ^14.16.0 || >=16.10.0}
hasBin: true
object-assign@4.1.1: object-assign@4.1.1:
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
@ -2389,6 +2513,9 @@ packages:
obliterator@2.0.4: obliterator@2.0.4:
resolution: {integrity: sha512-lgHwxlxV1qIg1Eap7LgIeoBWIMFibOjbrYPIPJZcI1mmGAI2m3lNYpK12Y+GBdPQ0U1hRwSord7GIaawz962qQ==} resolution: {integrity: sha512-lgHwxlxV1qIg1Eap7LgIeoBWIMFibOjbrYPIPJZcI1mmGAI2m3lNYpK12Y+GBdPQ0U1hRwSord7GIaawz962qQ==}
ohash@2.0.11:
resolution: {integrity: sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==}
on-finished@2.3.0: on-finished@2.3.0:
resolution: {integrity: sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==} resolution: {integrity: sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==}
engines: {node: '>= 0.8'} engines: {node: '>= 0.8'}
@ -2467,6 +2594,9 @@ packages:
resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==}
engines: {node: '>=8'} engines: {node: '>=8'}
parseley@0.12.1:
resolution: {integrity: sha512-e6qHKe3a9HWr0oMRVDTRhKce+bRO8VGQR3NyVwcjwrbhMmFCX9KszEV35+rn4AdilFAq9VPxP/Fe1wC9Qjd2lw==}
parseurl@1.3.3: parseurl@1.3.3:
resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==}
engines: {node: '>= 0.8'} engines: {node: '>= 0.8'}
@ -2504,6 +2634,12 @@ packages:
resolution: {integrity: sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==} resolution: {integrity: sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==}
engines: {node: '>= 14.16'} engines: {node: '>= 14.16'}
peberminta@0.9.0:
resolution: {integrity: sha512-XIxfHpEuSJbITd1H3EeQwpcZbTLHc+VVr8ANI9t5sit565tsI4/xK3KWTUFE2e6QiangUkh3B0jihzmGnNrRsQ==}
perfect-debounce@1.0.0:
resolution: {integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==}
picocolors@1.1.1: picocolors@1.1.1:
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
@ -2526,6 +2662,14 @@ packages:
resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==}
engines: {node: '>=8'} engines: {node: '>=8'}
pkg-types@2.3.0:
resolution: {integrity: sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==}
pnpm@10.28.0:
resolution: {integrity: sha512-Bd9x0UIfITmeBT/eVnzqNNRG+gLHZXFEG/wceVbpjjYwiJgtlARl/TRIDU2QoGaLwSNi+KqIAApk6D0LDke+SA==}
engines: {node: '>=18.12'}
hasBin: true
possible-typed-array-names@1.0.0: possible-typed-array-names@1.0.0:
resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==} resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
@ -2553,8 +2697,8 @@ packages:
resolution: {integrity: sha512-VpNpolZ8RXRgfU+j4R+fPZmX8EE95w3vJ2tt7+FwuiQc0leNTfLK5QLf3KbbPDes2rfjh3g20AjDxefQIo5GIA==} resolution: {integrity: sha512-VpNpolZ8RXRgfU+j4R+fPZmX8EE95w3vJ2tt7+FwuiQc0leNTfLK5QLf3KbbPDes2rfjh3g20AjDxefQIo5GIA==}
hasBin: true hasBin: true
prisma@6.3.0: prisma@6.16.2:
resolution: {integrity: sha512-y+Zh3Qg+xGCWyyrNUUNaFW/OltaV/yXYuTa0WRgYkz5LGyifmAsgpv94I47+qGRocZrMGcbF2A/78/oO2zgifA==} resolution: {integrity: sha512-aRvldGE5UUJTtVmFiH3WfNFNiqFlAtePUxcI0UEGlnXCX7DqhiMT5TRYwncHFeA/Reca5W6ToXXyCMTeFPdSXA==}
engines: {node: '>=18.18'} engines: {node: '>=18.18'}
hasBin: true hasBin: true
peerDependencies: peerDependencies:
@ -2601,6 +2745,9 @@ packages:
resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
engines: {node: '>=6'} engines: {node: '>=6'}
pure-rand@6.1.0:
resolution: {integrity: sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==}
qs@6.13.0: qs@6.13.0:
resolution: {integrity: sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==} resolution: {integrity: sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==}
engines: {node: '>=0.6'} engines: {node: '>=0.6'}
@ -2623,6 +2770,9 @@ packages:
resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==} resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==}
engines: {node: '>= 0.8'} engines: {node: '>= 0.8'}
rc9@2.1.2:
resolution: {integrity: sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==}
rc@1.2.8: rc@1.2.8:
resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==}
hasBin: true hasBin: true
@ -2649,6 +2799,10 @@ packages:
resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
engines: {node: '>=8.10.0'} engines: {node: '>=8.10.0'}
readdirp@4.1.2:
resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==}
engines: {node: '>= 14.18.0'}
reflect-metadata@0.2.2: reflect-metadata@0.2.2:
resolution: {integrity: sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==} resolution: {integrity: sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==}
@ -2746,6 +2900,9 @@ packages:
secure-json-parse@2.7.0: secure-json-parse@2.7.0:
resolution: {integrity: sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==} resolution: {integrity: sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==}
selderee@0.11.0:
resolution: {integrity: sha512-5TF+l7p4+OsnP8BCCvSyZiSPc4x4//p5uPwK8TCnVPJYRmU2aYKMpOXvw8zM5a5JvuuCGN1jmsMwuU2W02ukfA==}
semver@5.7.2: semver@5.7.2:
resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==}
hasBin: true hasBin: true
@ -3020,6 +3177,9 @@ packages:
tinyexec@0.3.2: tinyexec@0.3.2:
resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==}
tinyexec@1.0.1:
resolution: {integrity: sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==}
tinyglobby@0.2.13: tinyglobby@0.2.13:
resolution: {integrity: sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==} resolution: {integrity: sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==}
engines: {node: '>=12.0.0'} engines: {node: '>=12.0.0'}
@ -3833,11 +3993,20 @@ snapshots:
'@polka/url@1.0.0-next.29': {} '@polka/url@1.0.0-next.29': {}
'@prisma/client@6.3.0(prisma@6.3.0(typescript@5.7.2))(typescript@5.7.2)': '@prisma/client@6.16.2(prisma@6.16.2(typescript@5.7.2))(typescript@5.7.2)':
optionalDependencies: optionalDependencies:
prisma: 6.3.0(typescript@5.7.2) prisma: 6.16.2(typescript@5.7.2)
typescript: 5.7.2 typescript: 5.7.2
'@prisma/config@6.16.2':
dependencies:
c12: 3.1.0
deepmerge-ts: 7.1.5
effect: 3.16.12
empathic: 2.0.0
transitivePeerDependencies:
- magicast
'@prisma/debug@5.3.1': '@prisma/debug@5.3.1':
dependencies: dependencies:
'@types/debug': 4.1.8 '@types/debug': 4.1.8
@ -3846,18 +4015,18 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
'@prisma/debug@6.3.0': {} '@prisma/debug@6.16.2': {}
'@prisma/engines-version@6.3.0-17.acc0b9dd43eb689cbd20c9470515d719db10d0b0': {} '@prisma/engines-version@6.16.0-7.1c57fdcd7e44b29b9313256c76699e91c3ac3c43': {}
'@prisma/engines@5.3.1': {} '@prisma/engines@5.3.1': {}
'@prisma/engines@6.3.0': '@prisma/engines@6.16.2':
dependencies: dependencies:
'@prisma/debug': 6.3.0 '@prisma/debug': 6.16.2
'@prisma/engines-version': 6.3.0-17.acc0b9dd43eb689cbd20c9470515d719db10d0b0 '@prisma/engines-version': 6.16.0-7.1c57fdcd7e44b29b9313256c76699e91c3ac3c43
'@prisma/fetch-engine': 6.3.0 '@prisma/fetch-engine': 6.16.2
'@prisma/get-platform': 6.3.0 '@prisma/get-platform': 6.16.2
'@prisma/fetch-engine@5.3.1(encoding@0.1.13)': '@prisma/fetch-engine@5.3.1(encoding@0.1.13)':
dependencies: dependencies:
@ -3882,11 +4051,11 @@ snapshots:
- encoding - encoding
- supports-color - supports-color
'@prisma/fetch-engine@6.3.0': '@prisma/fetch-engine@6.16.2':
dependencies: dependencies:
'@prisma/debug': 6.3.0 '@prisma/debug': 6.16.2
'@prisma/engines-version': 6.3.0-17.acc0b9dd43eb689cbd20c9470515d719db10d0b0 '@prisma/engines-version': 6.16.0-7.1c57fdcd7e44b29b9313256c76699e91c3ac3c43
'@prisma/get-platform': 6.3.0 '@prisma/get-platform': 6.16.2
'@prisma/generator-helper@5.3.1': '@prisma/generator-helper@5.3.1':
dependencies: dependencies:
@ -3912,9 +4081,9 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
'@prisma/get-platform@6.3.0': '@prisma/get-platform@6.16.2':
dependencies: dependencies:
'@prisma/debug': 6.3.0 '@prisma/debug': 6.16.2
'@prisma/internals@5.3.1(encoding@0.1.13)': '@prisma/internals@5.3.1(encoding@0.1.13)':
dependencies: dependencies:
@ -4037,6 +4206,13 @@ snapshots:
'@scalar/openapi-types': 0.1.7 '@scalar/openapi-types': 0.1.7
'@unhead/schema': 1.11.14 '@unhead/schema': 1.11.14
'@selderee/plugin-htmlparser2@0.11.0':
dependencies:
domhandler: 5.0.3
selderee: 0.11.0
'@standard-schema/spec@1.0.0': {}
'@swc/helpers@0.5.15': '@swc/helpers@0.5.15':
dependencies: dependencies:
tslib: 2.8.1 tslib: 2.8.1
@ -4136,6 +4312,8 @@ snapshots:
'@types/qs': 6.9.17 '@types/qs': 6.9.17
'@types/serve-static': 1.15.7 '@types/serve-static': 1.15.7
'@types/html-to-text@9.0.4': {}
'@types/http-assert@1.5.6': {} '@types/http-assert@1.5.6': {}
'@types/http-errors@2.0.4': {} '@types/http-errors@2.0.4': {}
@ -4214,13 +4392,13 @@ snapshots:
chai: 5.2.0 chai: 5.2.0
tinyrainbow: 2.0.0 tinyrainbow: 2.0.0
'@vitest/mocker@3.1.4(vite@6.3.5(@types/node@20.17.10)(yaml@2.6.1))': '@vitest/mocker@3.1.4(vite@6.3.5(@types/node@20.17.10)(jiti@2.5.1)(yaml@2.6.1))':
dependencies: dependencies:
'@vitest/spy': 3.1.4 '@vitest/spy': 3.1.4
estree-walker: 3.0.3 estree-walker: 3.0.3
magic-string: 0.30.17 magic-string: 0.30.17
optionalDependencies: optionalDependencies:
vite: 6.3.5(@types/node@20.17.10)(yaml@2.6.1) vite: 6.3.5(@types/node@20.17.10)(jiti@2.5.1)(yaml@2.6.1)
'@vitest/pretty-format@3.1.4': '@vitest/pretty-format@3.1.4':
dependencies: dependencies:
@ -4250,7 +4428,7 @@ snapshots:
sirv: 3.0.1 sirv: 3.0.1
tinyglobby: 0.2.13 tinyglobby: 0.2.13
tinyrainbow: 2.0.0 tinyrainbow: 2.0.0
vitest: 3.1.4(@types/node@20.17.10)(@vitest/ui@3.1.4)(yaml@2.6.1) vitest: 3.1.4(@types/node@20.17.10)(@vitest/ui@3.1.4)(jiti@2.5.1)(yaml@2.6.1)
'@vitest/utils@3.1.4': '@vitest/utils@3.1.4':
dependencies: dependencies:
@ -4524,6 +4702,21 @@ snapshots:
bytes@3.1.2: {} bytes@3.1.2: {}
c12@3.1.0:
dependencies:
chokidar: 4.0.3
confbox: 0.2.2
defu: 6.1.4
dotenv: 16.6.1
exsolve: 1.0.7
giget: 2.0.0
jiti: 2.5.1
ohash: 2.0.11
pathe: 2.0.3
perfect-debounce: 1.0.0
pkg-types: 2.3.0
rc9: 2.1.2
cac@6.7.14: {} cac@6.7.14: {}
call-bind-apply-helpers@1.0.1: call-bind-apply-helpers@1.0.1:
@ -4603,10 +4796,18 @@ snapshots:
optionalDependencies: optionalDependencies:
fsevents: 2.3.3 fsevents: 2.3.3
chokidar@4.0.3:
dependencies:
readdirp: 4.1.2
chownr@1.1.4: {} chownr@1.1.4: {}
ci-info@3.8.0: {} ci-info@3.8.0: {}
citty@0.1.6:
dependencies:
consola: 3.4.2
cjs-module-lexer@1.4.1: cjs-module-lexer@1.4.1:
optional: true optional: true
@ -4682,6 +4883,10 @@ snapshots:
readable-stream: 2.3.8 readable-stream: 2.3.8
typedarray: 0.0.6 typedarray: 0.0.6
confbox@0.2.2: {}
consola@3.4.2: {}
console-log-level@1.4.1: console-log-level@1.4.1:
optional: true optional: true
@ -4733,6 +4938,8 @@ snapshots:
crypto-random-string@2.0.0: {} crypto-random-string@2.0.0: {}
csv-parse@6.1.0: {}
data-view-buffer@1.0.1: data-view-buffer@1.0.1:
dependencies: dependencies:
call-bind: 1.0.8 call-bind: 1.0.8
@ -4781,6 +4988,10 @@ snapshots:
deep-extend@0.6.0: {} deep-extend@0.6.0: {}
deepmerge-ts@7.1.5: {}
deepmerge@4.3.1: {}
define-data-property@1.1.4: define-data-property@1.1.4:
dependencies: dependencies:
es-define-property: 1.0.1 es-define-property: 1.0.1
@ -4793,6 +5004,8 @@ snapshots:
has-property-descriptors: 1.0.2 has-property-descriptors: 1.0.2
object-keys: 1.1.1 object-keys: 1.1.1
defu@6.1.4: {}
del@6.1.1: del@6.1.1:
dependencies: dependencies:
globby: 11.1.0 globby: 11.1.0
@ -4806,6 +5019,8 @@ snapshots:
depd@2.0.0: {} depd@2.0.0: {}
destr@2.0.5: {}
destroy@1.2.0: {} destroy@1.2.0: {}
detect-libc@2.0.4: {} detect-libc@2.0.4: {}
@ -4823,10 +5038,30 @@ snapshots:
jszip: 3.10.1 jszip: 3.10.1
sax: 1.3.0 sax: 1.3.0
dom-serializer@2.0.0:
dependencies:
domelementtype: 2.3.0
domhandler: 5.0.3
entities: 4.5.0
domelementtype@2.3.0: {}
domhandler@5.0.3:
dependencies:
domelementtype: 2.3.0
domutils@3.2.2:
dependencies:
dom-serializer: 2.0.0
domelementtype: 2.3.0
domhandler: 5.0.3
dotenv@16.0.3: {} dotenv@16.0.3: {}
dotenv@16.4.7: {} dotenv@16.4.7: {}
dotenv@16.6.1: {}
dunder-proto@1.0.0: dunder-proto@1.0.0:
dependencies: dependencies:
call-bind-apply-helpers: 1.0.1 call-bind-apply-helpers: 1.0.1
@ -4845,6 +5080,11 @@ snapshots:
ee-first@1.1.1: {} ee-first@1.1.1: {}
effect@3.16.12:
dependencies:
'@standard-schema/spec': 1.0.0
fast-check: 3.23.2
elastic-apm-node@3.52.2: elastic-apm-node@3.52.2:
dependencies: dependencies:
'@elastic/ecs-pino-format': 1.5.0 '@elastic/ecs-pino-format': 1.5.0
@ -4893,6 +5133,8 @@ snapshots:
emoji-regex@9.2.2: {} emoji-regex@9.2.2: {}
empathic@2.0.0: {}
enabled@2.0.0: {} enabled@2.0.0: {}
encodeurl@1.0.2: {} encodeurl@1.0.2: {}
@ -4908,6 +5150,8 @@ snapshots:
dependencies: dependencies:
once: 1.4.0 once: 1.4.0
entities@4.5.0: {}
env-paths@2.2.1: {} env-paths@2.2.1: {}
error-callsites@2.0.4: error-callsites@2.0.4:
@ -5124,6 +5368,12 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
exsolve@1.0.7: {}
fast-check@3.23.2:
dependencies:
pure-rand: 6.1.0
fast-csv@4.3.6: fast-csv@4.3.6:
dependencies: dependencies:
'@fast-csv/format': 4.3.5 '@fast-csv/format': 4.3.5
@ -5301,6 +5551,15 @@ snapshots:
es-errors: 1.3.0 es-errors: 1.3.0
get-intrinsic: 1.2.6 get-intrinsic: 1.2.6
giget@2.0.0:
dependencies:
citty: 0.1.6
consola: 3.4.2
defu: 6.1.4
node-fetch-native: 1.6.7
nypm: 0.6.1
pathe: 2.0.3
github-from-package@0.0.0: {} github-from-package@0.0.0: {}
glob-parent@5.1.2: glob-parent@5.1.2:
@ -5399,6 +5658,21 @@ snapshots:
hpagent@1.2.0: {} hpagent@1.2.0: {}
html-to-text@9.0.5:
dependencies:
'@selderee/plugin-htmlparser2': 0.11.0
deepmerge: 4.3.1
dom-serializer: 2.0.0
htmlparser2: 8.0.2
selderee: 0.11.0
htmlparser2@8.0.2:
dependencies:
domelementtype: 2.3.0
domhandler: 5.0.3
domutils: 3.2.2
entities: 4.5.0
http-errors@2.0.0: http-errors@2.0.0:
dependencies: dependencies:
depd: 2.0.0 depd: 2.0.0
@ -5651,6 +5925,8 @@ snapshots:
optionalDependencies: optionalDependencies:
'@pkgjs/parseargs': 0.11.0 '@pkgjs/parseargs': 0.11.0
jiti@2.5.1: {}
js-tokens@4.0.0: {} js-tokens@4.0.0: {}
jsbarcode@3.11.6: {} jsbarcode@3.11.6: {}
@ -5689,6 +5965,8 @@ snapshots:
dependencies: dependencies:
readable-stream: 2.3.8 readable-stream: 2.3.8
leac@0.6.0: {}
lie@3.3.0: lie@3.3.0:
dependencies: dependencies:
immediate: 3.0.6 immediate: 3.0.6
@ -5934,6 +6212,8 @@ snapshots:
node-addon-api@7.1.1: {} node-addon-api@7.1.1: {}
node-fetch-native@1.6.7: {}
node-fetch@2.6.12(encoding@0.1.13): node-fetch@2.6.12(encoding@0.1.13):
dependencies: dependencies:
whatwg-url: 5.0.0 whatwg-url: 5.0.0
@ -5987,6 +6267,14 @@ snapshots:
dependencies: dependencies:
path-key: 3.1.1 path-key: 3.1.1
nypm@0.6.1:
dependencies:
citty: 0.1.6
consola: 3.4.2
pathe: 2.0.3
pkg-types: 2.3.0
tinyexec: 1.0.1
object-assign@4.1.1: {} object-assign@4.1.1: {}
object-filter-sequence@1.0.0: object-filter-sequence@1.0.0:
@ -6017,6 +6305,8 @@ snapshots:
obliterator@2.0.4: {} obliterator@2.0.4: {}
ohash@2.0.11: {}
on-finished@2.3.0: on-finished@2.3.0:
dependencies: dependencies:
ee-first: 1.1.1 ee-first: 1.1.1
@ -6096,6 +6386,11 @@ snapshots:
json-parse-even-better-errors: 2.3.1 json-parse-even-better-errors: 2.3.1
lines-and-columns: 1.2.4 lines-and-columns: 1.2.4
parseley@0.12.1:
dependencies:
leac: 0.6.0
peberminta: 0.9.0
parseurl@1.3.3: {} parseurl@1.3.3: {}
path-exists@4.0.0: {} path-exists@4.0.0: {}
@ -6119,6 +6414,10 @@ snapshots:
pathval@2.0.0: {} pathval@2.0.0: {}
peberminta@0.9.0: {}
perfect-debounce@1.0.0: {}
picocolors@1.1.1: {} picocolors@1.1.1: {}
picomatch@2.3.1: {} picomatch@2.3.1: {}
@ -6143,6 +6442,14 @@ snapshots:
dependencies: dependencies:
find-up: 4.1.0 find-up: 4.1.0
pkg-types@2.3.0:
dependencies:
confbox: 0.2.2
exsolve: 1.0.7
pathe: 2.0.3
pnpm@10.28.0: {}
possible-typed-array-names@1.0.0: {} possible-typed-array-names@1.0.0: {}
postcss@8.5.3: postcss@8.5.3:
@ -6168,9 +6475,9 @@ snapshots:
prettier@3.4.2: {} prettier@3.4.2: {}
prisma-extension-kysely@3.0.0(@prisma/client@6.3.0(prisma@6.3.0(typescript@5.7.2))(typescript@5.7.2)): prisma-extension-kysely@3.0.0(@prisma/client@6.16.2(prisma@6.16.2(typescript@5.7.2))(typescript@5.7.2)):
dependencies: dependencies:
'@prisma/client': 6.3.0(prisma@6.3.0(typescript@5.7.2))(typescript@5.7.2) '@prisma/client': 6.16.2(prisma@6.16.2(typescript@5.7.2))(typescript@5.7.2)
prisma-kysely@1.8.0(encoding@0.1.13): prisma-kysely@1.8.0(encoding@0.1.13):
dependencies: dependencies:
@ -6183,12 +6490,14 @@ snapshots:
- encoding - encoding
- supports-color - supports-color
prisma@6.3.0(typescript@5.7.2): prisma@6.16.2(typescript@5.7.2):
dependencies: dependencies:
'@prisma/engines': 6.3.0 '@prisma/config': 6.16.2
'@prisma/engines': 6.16.2
optionalDependencies: optionalDependencies:
fsevents: 2.3.3
typescript: 5.7.2 typescript: 5.7.2
transitivePeerDependencies:
- magicast
process-nextick-args@2.0.1: {} process-nextick-args@2.0.1: {}
@ -6234,6 +6543,8 @@ snapshots:
punycode@2.3.1: punycode@2.3.1:
optional: true optional: true
pure-rand@6.1.0: {}
qs@6.13.0: qs@6.13.0:
dependencies: dependencies:
side-channel: 1.1.0 side-channel: 1.1.0
@ -6259,6 +6570,11 @@ snapshots:
iconv-lite: 0.4.24 iconv-lite: 0.4.24
unpipe: 1.0.0 unpipe: 1.0.0
rc9@2.1.2:
dependencies:
defu: 6.1.4
destr: 2.0.5
rc@1.2.8: rc@1.2.8:
dependencies: dependencies:
deep-extend: 0.6.0 deep-extend: 0.6.0
@ -6303,6 +6619,8 @@ snapshots:
dependencies: dependencies:
picomatch: 2.3.1 picomatch: 2.3.1
readdirp@4.1.2: {}
reflect-metadata@0.2.2: {} reflect-metadata@0.2.2: {}
reflect.getprototypeof@1.0.8: reflect.getprototypeof@1.0.8:
@ -6428,6 +6746,10 @@ snapshots:
secure-json-parse@2.7.0: {} secure-json-parse@2.7.0: {}
selderee@0.11.0:
dependencies:
parseley: 0.12.1
semver@5.7.2: {} semver@5.7.2: {}
semver@6.3.1: {} semver@6.3.1: {}
@ -6744,6 +7066,8 @@ snapshots:
tinyexec@0.3.2: {} tinyexec@0.3.2: {}
tinyexec@1.0.1: {}
tinyglobby@0.2.13: tinyglobby@0.2.13:
dependencies: dependencies:
fdir: 6.4.4(picomatch@4.0.2) fdir: 6.4.4(picomatch@4.0.2)
@ -6949,13 +7273,13 @@ snapshots:
vary@1.1.2: {} vary@1.1.2: {}
vite-node@3.1.4(@types/node@20.17.10)(yaml@2.6.1): vite-node@3.1.4(@types/node@20.17.10)(jiti@2.5.1)(yaml@2.6.1):
dependencies: dependencies:
cac: 6.7.14 cac: 6.7.14
debug: 4.4.0(supports-color@5.5.0) debug: 4.4.0(supports-color@5.5.0)
es-module-lexer: 1.7.0 es-module-lexer: 1.7.0
pathe: 2.0.3 pathe: 2.0.3
vite: 6.3.5(@types/node@20.17.10)(yaml@2.6.1) vite: 6.3.5(@types/node@20.17.10)(jiti@2.5.1)(yaml@2.6.1)
transitivePeerDependencies: transitivePeerDependencies:
- '@types/node' - '@types/node'
- jiti - jiti
@ -6970,7 +7294,7 @@ snapshots:
- tsx - tsx
- yaml - yaml
vite@6.3.5(@types/node@20.17.10)(yaml@2.6.1): vite@6.3.5(@types/node@20.17.10)(jiti@2.5.1)(yaml@2.6.1):
dependencies: dependencies:
esbuild: 0.25.4 esbuild: 0.25.4
fdir: 6.4.4(picomatch@4.0.2) fdir: 6.4.4(picomatch@4.0.2)
@ -6981,12 +7305,13 @@ snapshots:
optionalDependencies: optionalDependencies:
'@types/node': 20.17.10 '@types/node': 20.17.10
fsevents: 2.3.3 fsevents: 2.3.3
jiti: 2.5.1
yaml: 2.6.1 yaml: 2.6.1
vitest@3.1.4(@types/node@20.17.10)(@vitest/ui@3.1.4)(yaml@2.6.1): vitest@3.1.4(@types/node@20.17.10)(@vitest/ui@3.1.4)(jiti@2.5.1)(yaml@2.6.1):
dependencies: dependencies:
'@vitest/expect': 3.1.4 '@vitest/expect': 3.1.4
'@vitest/mocker': 3.1.4(vite@6.3.5(@types/node@20.17.10)(yaml@2.6.1)) '@vitest/mocker': 3.1.4(vite@6.3.5(@types/node@20.17.10)(jiti@2.5.1)(yaml@2.6.1))
'@vitest/pretty-format': 3.1.4 '@vitest/pretty-format': 3.1.4
'@vitest/runner': 3.1.4 '@vitest/runner': 3.1.4
'@vitest/snapshot': 3.1.4 '@vitest/snapshot': 3.1.4
@ -7003,8 +7328,8 @@ snapshots:
tinyglobby: 0.2.13 tinyglobby: 0.2.13
tinypool: 1.0.2 tinypool: 1.0.2
tinyrainbow: 2.0.0 tinyrainbow: 2.0.0
vite: 6.3.5(@types/node@20.17.10)(yaml@2.6.1) vite: 6.3.5(@types/node@20.17.10)(jiti@2.5.1)(yaml@2.6.1)
vite-node: 3.1.4(@types/node@20.17.10)(yaml@2.6.1) vite-node: 3.1.4(@types/node@20.17.10)(jiti@2.5.1)(yaml@2.6.1)
why-is-node-running: 2.3.0 why-is-node-running: 2.3.0
optionalDependencies: optionalDependencies:
'@types/node': 20.17.10 '@types/node': 20.17.10

View file

@ -0,0 +1,4 @@
-- AlterTable
ALTER TABLE "Payment" ADD COLUMN "account" TEXT,
ADD COLUMN "channel" TEXT,
ADD COLUMN "reference" TEXT;

View file

@ -0,0 +1,3 @@
-- AlterTable
ALTER TABLE "public"."Product" ADD COLUMN "flowAccountProductIdAgentPrice" TEXT,
ADD COLUMN "flowAccountProductIdSellPrice" TEXT;

View file

@ -1243,6 +1243,9 @@ model Product {
productGroup ProductGroup @relation(fields: [productGroupId], references: [id], onDelete: Cascade) productGroup ProductGroup @relation(fields: [productGroupId], references: [id], onDelete: Cascade)
productGroupId String productGroupId String
flowAccountProductIdSellPrice String?
flowAccountProductIdAgentPrice String?
workProduct WorkProduct[] workProduct WorkProduct[]
quotationProductServiceList QuotationProductServiceList[] quotationProductServiceList QuotationProductServiceList[]
taskProduct TaskProduct[] taskProduct TaskProduct[]
@ -1524,8 +1527,11 @@ model Payment {
paymentStatus PaymentStatus paymentStatus PaymentStatus
amount Float amount Float
date DateTime? date DateTime?
channel String?
account String?
reference String?
createdAt DateTime @default(now()) createdAt DateTime @default(now())
createdBy User? @relation(name: "PaymentCreatedByUser", fields: [createdByUserId], references: [id], onDelete: SetNull) createdBy User? @relation(name: "PaymentCreatedByUser", fields: [createdByUserId], references: [id], onDelete: SetNull)

View file

@ -34,6 +34,11 @@ const quotationData = (id: string) =>
}, },
}, },
customerBranch: { customerBranch: {
omit: {
otpCode: true,
otpExpires: true,
userId: true,
},
include: { include: {
customer: true, customer: true,
businessType: true, businessType: true,

View file

@ -618,9 +618,22 @@ export class StatsController extends Controller {
startDate = dayjs(startDate).startOf("month").add(1, "month").toDate(); startDate = dayjs(startDate).startOf("month").add(1, "month").toDate();
} }
const invoices = await tx.invoice.findMany({
select: { id: true },
where: {
quotation: {
quotationStatus: { notIn: [QuotationStatus.Canceled] },
registeredBranch: { OR: permissionCondCompany(req.user) },
},
},
});
if (invoices.length === 0) return [];
return await Promise.all( return await Promise.all(
months.map(async (v) => { months.map(async (v) => {
const date = dayjs(v); const date = dayjs(v);
return { return {
month: date.format("MM"), month: date.format("MM"),
year: date.format("YYYY"), year: date.format("YYYY"),
@ -629,12 +642,7 @@ export class StatsController extends Controller {
_sum: { amount: true }, _sum: { amount: true },
where: { where: {
createdAt: { gte: v, lte: date.endOf("month").toDate() }, createdAt: { gte: v, lte: date.endOf("month").toDate() },
invoice: { invoiceId: { in: invoices.map((v) => v.id) },
quotation: {
quotationStatus: { notIn: [QuotationStatus.Canceled] },
registeredBranch: { OR: permissionCondCompany(req.user) },
},
},
}, },
by: "paymentStatus", by: "paymentStatus",
}) })

View file

@ -387,6 +387,14 @@ export class BranchController extends Controller {
return record; return record;
} }
@Get("{branchId}/bank")
@Security("keycloak")
async getBranchBankById(@Path() branchId: string) {
return await prisma.branchBank.findMany({
where: { branchId },
});
}
@Post() @Post()
@Security("keycloak", MANAGE_ROLES) @Security("keycloak", MANAGE_ROLES)
async createBranch(@Request() req: RequestWithUser, @Body() body: BranchCreate) { async createBranch(@Request() req: RequestWithUser, @Body() body: BranchCreate) {

View file

@ -238,6 +238,11 @@ export class CustomerBranchController extends Controller {
const [result, total] = await prisma.$transaction([ const [result, total] = await prisma.$transaction([
prisma.customerBranch.findMany({ prisma.customerBranch.findMany({
orderBy: [{ code: "asc" }, { statusOrder: "asc" }, { createdAt: "asc" }], orderBy: [{ code: "asc" }, { statusOrder: "asc" }, { createdAt: "asc" }],
omit: {
otpCode: true,
otpExpires: true,
userId: true,
},
include: { include: {
customer: includeCustomer, customer: includeCustomer,
province: true, province: true,
@ -262,6 +267,11 @@ export class CustomerBranchController extends Controller {
@Security("keycloak") @Security("keycloak")
async getById(@Path() branchId: string) { async getById(@Path() branchId: string) {
const record = await prisma.customerBranch.findFirst({ const record = await prisma.customerBranch.findFirst({
omit: {
otpCode: true,
otpExpires: true,
userId: true,
},
include: { include: {
customer: true, customer: true,
province: true, province: true,
@ -352,6 +362,11 @@ export class CustomerBranchController extends Controller {
include: branchRelationPermInclude(req.user), include: branchRelationPermInclude(req.user),
}, },
branch: { branch: {
omit: {
otpCode: true,
otpExpires: true,
userId: true,
},
take: 1, take: 1,
orderBy: { createdAt: "asc" }, orderBy: { createdAt: "asc" },
}, },

View file

@ -37,6 +37,7 @@ import {
} from "../utils/minio"; } from "../utils/minio";
import { isUsedError, notFoundError, relationError } from "../utils/error"; import { isUsedError, notFoundError, relationError } from "../utils/error";
import { connectOrNot, queryOrNot, whereDateQuery } from "../utils/relation"; import { connectOrNot, queryOrNot, whereDateQuery } from "../utils/relation";
import { json2csv } from "json-2-csv";
const MANAGE_ROLES = [ const MANAGE_ROLES = [
"system", "system",
@ -169,6 +170,10 @@ export class CustomerController extends Controller {
@Query() activeBranchOnly?: boolean, @Query() activeBranchOnly?: boolean,
@Query() startDate?: Date, @Query() startDate?: Date,
@Query() endDate?: Date, @Query() endDate?: Date,
@Query() businessTypeId?: string,
@Query() provinceId?: string,
@Query() districtId?: string,
@Query() subDistrictId?: string,
) { ) {
const where = { const where = {
OR: queryOrNot<Prisma.CustomerWhereInput[]>(query, [ OR: queryOrNot<Prisma.CustomerWhereInput[]>(query, [
@ -191,6 +196,35 @@ export class CustomerController extends Controller {
: permissionCond(req.user, { activeOnly: activeBranchOnly }), : permissionCond(req.user, { activeOnly: activeBranchOnly }),
}, },
}, },
branch: {
some: {
AND: [
businessTypeId
? {
OR: [{ businessType: { id: businessTypeId } }],
}
: {},
provinceId
? {
OR: [{ province: { id: provinceId } }],
}
: {},
districtId
? {
OR: [{ district: { id: districtId } }],
}
: {},
subDistrictId
? {
OR: [{ subDistrict: { id: subDistrictId } }],
}
: {},
],
},
},
...whereDateQuery(startDate, endDate), ...whereDateQuery(startDate, endDate),
} satisfies Prisma.CustomerWhereInput; } satisfies Prisma.CustomerWhereInput;
@ -206,6 +240,11 @@ export class CustomerController extends Controller {
district: true, district: true,
subDistrict: true, subDistrict: true,
}, },
omit: {
otpCode: true,
otpExpires: true,
userId: true,
},
orderBy: [{ statusOrder: "asc" }, { createdAt: "asc" }], orderBy: [{ statusOrder: "asc" }, { createdAt: "asc" }],
} }
: { : {
@ -214,6 +253,11 @@ export class CustomerController extends Controller {
district: true, district: true,
subDistrict: true, subDistrict: true,
}, },
omit: {
otpCode: true,
otpExpires: true,
userId: true,
},
take: 1, take: 1,
orderBy: { createdAt: "asc" }, orderBy: { createdAt: "asc" },
}, },
@ -244,6 +288,11 @@ export class CustomerController extends Controller {
district: true, district: true,
subDistrict: true, subDistrict: true,
}, },
omit: {
otpCode: true,
otpExpires: true,
userId: true,
},
orderBy: { createdAt: "asc" }, orderBy: { createdAt: "asc" },
}, },
createdBy: true, createdBy: true,
@ -315,6 +364,11 @@ export class CustomerController extends Controller {
district: true, district: true,
subDistrict: true, subDistrict: true,
}, },
omit: {
otpCode: true,
otpExpires: true,
userId: true,
},
}, },
createdBy: true, createdBy: true,
updatedBy: true, updatedBy: true,
@ -414,6 +468,11 @@ export class CustomerController extends Controller {
district: true, district: true,
subDistrict: true, subDistrict: true,
}, },
omit: {
otpCode: true,
otpExpires: true,
userId: true,
},
}, },
createdBy: true, createdBy: true,
updatedBy: true, updatedBy: true,
@ -452,7 +511,13 @@ export class CustomerController extends Controller {
await deleteFolder(`customer/${customerId}`); await deleteFolder(`customer/${customerId}`);
const data = await tx.customer.delete({ const data = await tx.customer.delete({
include: { include: {
branch: true, branch: {
omit: {
otpCode: true,
otpExpires: true,
userId: true,
},
},
registeredBranch: { registeredBranch: {
include: { include: {
headOffice: true, headOffice: true,
@ -547,3 +612,52 @@ export class CustomerImageController extends Controller {
await deleteFile(fileLocation.customer.img(customerId, name)); await deleteFile(fileLocation.customer.img(customerId, name));
} }
} }
@Route("api/v1/customer-export")
@Tags("Customer")
export class CustomerExportController extends CustomerController {
@Get()
@Security("keycloak")
async exportCustomer(
@Request() req: RequestWithUser,
@Query() customerType?: CustomerType,
@Query() query: string = "",
@Query() status?: Status,
@Query() page: number = 1,
@Query() pageSize: number = 30,
@Query() includeBranch: boolean = false,
@Query() company: boolean = false,
@Query() activeBranchOnly?: boolean,
@Query() startDate?: Date,
@Query() endDate?: Date,
@Query() businessTypeId?: string,
@Query() provinceId?: string,
@Query() districtId?: string,
@Query() subDistrictId?: string,
) {
const ret = await this.list(
req,
customerType,
query,
status,
page,
pageSize,
includeBranch,
company,
activeBranchOnly,
startDate,
endDate,
businessTypeId,
provinceId,
districtId,
subDistrictId,
);
this.setHeader("Content-Type", "text/csv");
return json2csv(
ret.result.map((v) => Object.assign(v, { branch: v.branch.at(0) ?? null })),
{ useDateIso8601Format: true, expandNestedObjects: true },
);
}
}

View file

@ -42,6 +42,7 @@ import {
listFile, listFile,
setFile, setFile,
} from "../utils/minio"; } from "../utils/minio";
import { json2csv } from "json-2-csv";
if (!process.env.MINIO_BUCKET) { if (!process.env.MINIO_BUCKET) {
throw Error("Require MinIO bucket."); throw Error("Require MinIO bucket.");
@ -249,7 +250,6 @@ export class EmployeeController extends Controller {
endDate, endDate,
); );
} }
@Post("list") @Post("list")
@Security("keycloak") @Security("keycloak")
async listByCriteria( async listByCriteria(
@ -927,3 +927,55 @@ export class EmployeeFileController extends Controller {
return await deleteFile(fileLocation.employee.inCountryNotice(employeeId, noticeId)); return await deleteFile(fileLocation.employee.inCountryNotice(employeeId, noticeId));
} }
} }
@Route("api/v1/employee-export")
@Tags("Employee")
export class EmployeeExportController extends EmployeeController {
@Get()
@Security("keycloak")
async exportEmployee(
@Request() req: RequestWithUser,
@Query() zipCode?: string,
@Query() gender?: string,
@Query() status?: Status,
@Query() visa?: boolean,
@Query() passport?: boolean,
@Query() customerId?: string,
@Query() customerBranchId?: string,
@Query() query: string = "",
@Query() page: number = 1,
@Query() pageSize: number = 30,
@Query() activeOnly?: boolean,
@Query() startDate?: Date,
@Query() endDate?: Date,
) {
const ret = await this.listByCriteria(
req,
zipCode,
gender,
status,
visa,
passport,
customerId,
customerBranchId,
query,
page,
pageSize,
activeOnly,
startDate,
endDate,
);
this.setHeader("Content-Type", "text/csv");
return json2csv(
ret.result.map((v) =>
Object.assign(v, {
employeePassport: v.employeePassport?.at(0) ?? null,
employeeVisa: v.employeeVisa?.at(0) ?? null,
}),
),
{ useDateIso8601Format: true },
);
}
}

View file

@ -30,6 +30,8 @@ import { deleteFile, deleteFolder, fileLocation, getFile, listFile, setFile } fr
import { isUsedError, notFoundError, relationError } from "../utils/error"; import { isUsedError, notFoundError, relationError } from "../utils/error";
import { queryOrNot, whereDateQuery } from "../utils/relation"; import { queryOrNot, whereDateQuery } from "../utils/relation";
import spreadsheet from "../utils/spreadsheet"; import spreadsheet from "../utils/spreadsheet";
import flowAccount from "../services/flowaccount";
import { json2csv } from "json-2-csv";
const MANAGE_ROLES = [ const MANAGE_ROLES = [
"system", "system",
@ -76,6 +78,7 @@ type ProductCreate = {
type ProductUpdate = { type ProductUpdate = {
status?: "ACTIVE" | "INACTIVE"; status?: "ACTIVE" | "INACTIVE";
code?: string;
name?: string; name?: string;
detail?: string; detail?: string;
process?: number; process?: number;
@ -299,13 +302,21 @@ export class ProductController extends Controller {
}, },
update: { value: { increment: 1 } }, update: { value: { increment: 1 } },
}); });
return await prisma.product.create({
const listId = await flowAccount.createProducts(
`${body.code.toLocaleUpperCase()}${last.value.toString().padStart(3, "0")}`,
body,
);
return await tx.product.create({
include: { include: {
createdBy: true, createdBy: true,
updatedBy: true, updatedBy: true,
}, },
data: { data: {
...body, ...body,
flowAccountProductIdAgentPrice: `${listId.data.productIdAgentPrice}`,
flowAccountProductIdSellPrice: `${listId.data.productIdSellPrice}`,
document: body.document document: body.document
? { ? {
createMany: { data: body.document.map((v) => ({ name: v })) }, createMany: { data: body.document.map((v) => ({ name: v })) },
@ -379,6 +390,30 @@ export class ProductController extends Controller {
await permissionCheck(req.user, productGroup.registeredBranch); await permissionCheck(req.user, productGroup.registeredBranch);
} }
if (
product.flowAccountProductIdSellPrice !== null &&
product.flowAccountProductIdAgentPrice !== null
) {
const mergedBody = {
...body,
code: body.code ?? product.code,
price: body.price ?? product.price,
agentPrice: body.agentPrice ?? product.agentPrice,
serviceCharge: body.serviceCharge ?? product.serviceCharge,
vatIncluded: body.vatIncluded ?? product.vatIncluded,
agentPriceVatIncluded: body.agentPriceVatIncluded ?? product.agentPriceVatIncluded,
serviceChargeVatIncluded: body.serviceChargeVatIncluded ?? product.serviceChargeVatIncluded,
};
await flowAccount.editProducts(
product.flowAccountProductIdSellPrice,
product.flowAccountProductIdAgentPrice,
mergedBody,
);
} else {
throw notFoundError("FlowAccountProductId");
}
const record = await prisma.product.update({ const record = await prisma.product.update({
include: { include: {
productGroup: true, productGroup: true,
@ -441,6 +476,18 @@ export class ProductController extends Controller {
if (record.status !== Status.CREATED) throw isUsedError("Product"); if (record.status !== Status.CREATED) throw isUsedError("Product");
if (
record.flowAccountProductIdSellPrice !== null &&
record.flowAccountProductIdAgentPrice !== null
) {
await Promise.all([
flowAccount.deleteProduct(record.flowAccountProductIdSellPrice),
flowAccount.deleteProduct(record.flowAccountProductIdAgentPrice),
]);
} else {
throw notFoundError("FlowAccountProductId");
}
await deleteFolder(fileLocation.product.img(productId)); await deleteFolder(fileLocation.product.img(productId));
return await prisma.product.delete({ return await prisma.product.delete({
@ -642,3 +689,43 @@ export class ProductFileController extends Controller {
return await deleteFile(fileLocation.product.img(productId, name)); return await deleteFile(fileLocation.product.img(productId, name));
} }
} }
@Route("api/v1/product-export")
@Tags("Product")
export class ProductExportController extends ProductController {
@Get()
@Security("keycloak")
async exportCustomer(
@Request() req: RequestWithUser,
@Query() status?: Status,
@Query() shared?: boolean,
@Query() productGroupId?: string,
@Query() query: string = "",
@Query() page: number = 1,
@Query() pageSize: number = 30,
@Query() orderField?: keyof Product,
@Query() orderBy?: "asc" | "desc",
@Query() activeOnly?: boolean,
@Query() startDate?: Date,
@Query() endDate?: Date,
) {
const ret = await this.getProduct(
req,
status,
shared,
productGroupId,
query,
page,
pageSize,
orderField,
orderBy,
activeOnly,
startDate,
endDate,
);
this.setHeader("Content-Type", "text/csv");
return json2csv(ret.result, { useDateIso8601Format: true, expandNestedObjects: true });
}
}

View file

@ -113,7 +113,15 @@ export class QuotationPayment extends Controller {
@Security("keycloak", MANAGE_ROLES.concat(["head_of_sale", "sale"])) @Security("keycloak", MANAGE_ROLES.concat(["head_of_sale", "sale"]))
async updatePayment( async updatePayment(
@Path() paymentId: string, @Path() paymentId: string,
@Body() body: { amount?: number; date?: Date; paymentStatus?: PaymentStatus }, @Body()
body: {
amount?: number;
date?: Date;
paymentStatus?: PaymentStatus;
channel?: string | null;
account?: string | null;
reference?: string | null;
},
@Request() req: RequestWithUser, @Request() req: RequestWithUser,
) { ) {
const record = await prisma.payment.findUnique({ const record = await prisma.payment.findUnique({
@ -144,7 +152,18 @@ export class QuotationPayment extends Controller {
if (!record) throw notFoundError("Payment"); if (!record) throw notFoundError("Payment");
if (record.paymentStatus === "PaymentSuccess") return record; if (record.paymentStatus === "PaymentSuccess") {
const { channel, account, reference } = body;
return await prisma.payment.update({
where: { id: paymentId, invoice: { quotationId: record.invoice.quotationId } },
data: {
channel,
account,
reference,
updatedByUserId: req.user.sub,
},
});
}
return await prisma.$transaction(async (tx) => { return await prisma.$transaction(async (tx) => {
const current = new Date(); const current = new Date();

View file

@ -527,16 +527,15 @@ export class QuotationController extends Controller {
const vatIncluded = body.agentPrice ? p.agentPriceVatIncluded : p.vatIncluded; const vatIncluded = body.agentPrice ? p.agentPriceVatIncluded : p.vatIncluded;
const originalPrice = body.agentPrice ? p.agentPrice : p.price; const originalPrice = body.agentPrice ? p.agentPrice : p.price;
const finalPriceWithVat = precisionRound( const finalPrice = precisionRound(
originalPrice + (vatIncluded ? 0 : originalPrice * VAT_DEFAULT), originalPrice + (vatIncluded ? 0 : originalPrice * VAT_DEFAULT),
); );
const pricePerUnit = finalPrice / (1 + VAT_DEFAULT);
const price = finalPriceWithVat;
const pricePerUnit = price / (1 + VAT_DEFAULT);
const vat = (body.agentPrice ? p.agentPriceCalcVat : p.calcVat) const vat = (body.agentPrice ? p.agentPriceCalcVat : p.calcVat)
? (pricePerUnit * v.amount - (v.discount || 0)) * VAT_DEFAULT ? ((pricePerUnit * (1 + VAT_DEFAULT) * v.amount - (v.discount || 0)) /
(1 + VAT_DEFAULT)) *
VAT_DEFAULT
: 0; : 0;
return { return {
order: i + 1, order: i + 1,
productId: v.productId, productId: v.productId,
@ -557,13 +556,13 @@ export class QuotationController extends Controller {
const price = list.reduce( const price = list.reduce(
(a, c) => { (a, c) => {
a.totalPrice = precisionRound(a.totalPrice + c.pricePerUnit * c.amount); const vat = c.vat ? VAT_DEFAULT : 0;
const price = c.pricePerUnit * c.amount * (1 + vat) - c.discount;
a.totalPrice = precisionRound(a.totalPrice + price / (1 + vat) + c.discount);
a.totalDiscount = precisionRound(a.totalDiscount + c.discount); a.totalDiscount = precisionRound(a.totalDiscount + c.discount);
a.vat = precisionRound(a.vat + c.vat); a.vat = precisionRound(a.vat + c.vat);
a.vatExcluded = a.vatExcluded = c.vat === 0 ? precisionRound(a.vatExcluded + price) : a.vatExcluded;
c.vat === 0
? precisionRound(a.vatExcluded + (c.pricePerUnit * c.amount - (c.discount || 0)))
: a.vatExcluded;
a.finalPrice = precisionRound( a.finalPrice = precisionRound(
Math.max(a.totalPrice - a.totalDiscount + a.vat - (body.discount || 0), 0), Math.max(a.totalPrice - a.totalDiscount + a.vat - (body.discount || 0), 0),
); );
@ -815,14 +814,14 @@ export class QuotationController extends Controller {
const vatIncluded = record.agentPrice ? p.agentPriceVatIncluded : p.vatIncluded; const vatIncluded = record.agentPrice ? p.agentPriceVatIncluded : p.vatIncluded;
const originalPrice = record.agentPrice ? p.agentPrice : p.price; const originalPrice = record.agentPrice ? p.agentPrice : p.price;
const finalPriceWithVat = precisionRound( const finalPrice = precisionRound(
originalPrice + (vatIncluded ? 0 : originalPrice * VAT_DEFAULT), originalPrice + (vatIncluded ? 0 : originalPrice * VAT_DEFAULT),
); );
const pricePerUnit = finalPrice / (1 + VAT_DEFAULT);
const price = finalPriceWithVat;
const pricePerUnit = price / (1 + VAT_DEFAULT);
const vat = (record.agentPrice ? p.agentPriceCalcVat : p.calcVat) const vat = (record.agentPrice ? p.agentPriceCalcVat : p.calcVat)
? (pricePerUnit * v.amount - (v.discount || 0)) * VAT_DEFAULT ? ((pricePerUnit * (1 + VAT_DEFAULT) * v.amount - (v.discount || 0)) /
(1 + VAT_DEFAULT)) *
VAT_DEFAULT
: 0; : 0;
return { return {
@ -845,15 +844,13 @@ export class QuotationController extends Controller {
const price = list?.reduce( const price = list?.reduce(
(a, c) => { (a, c) => {
a.totalPrice = precisionRound(a.totalPrice + c.pricePerUnit * c.amount); const vat = c.vat ? VAT_DEFAULT : 0;
const price = c.pricePerUnit * c.amount * (1 + vat) - c.discount;
a.totalPrice = precisionRound(a.totalPrice + price / (1 + vat) + c.discount);
a.totalDiscount = precisionRound(a.totalDiscount + c.discount); a.totalDiscount = precisionRound(a.totalDiscount + c.discount);
a.vat = precisionRound(a.vat + c.vat); a.vat = precisionRound(a.vat + c.vat);
a.vatExcluded = a.vatExcluded = c.vat === 0 ? precisionRound(a.vatExcluded + price) : a.vatExcluded;
c.vat === 0
? precisionRound(
a.vatExcluded + (c.pricePerUnit * c.amount - (c.discount || 0)) * VAT_DEFAULT,
)
: a.vatExcluded;
a.finalPrice = precisionRound( a.finalPrice = precisionRound(
Math.max(a.totalPrice - a.totalDiscount + a.vat - (body.discount || 0), 0), Math.max(a.totalPrice - a.totalDiscount + a.vat - (body.discount || 0), 0),
); );
@ -869,6 +866,7 @@ export class QuotationController extends Controller {
finalPrice: 0, finalPrice: 0,
}, },
); );
const changed = list?.some((lhs) => { const changed = list?.some((lhs) => {
const found = record.productServiceList.find((rhs) => { const found = record.productServiceList.find((rhs) => {
return ( return (
@ -902,6 +900,20 @@ export class QuotationController extends Controller {
}), }),
]); ]);
if (customerBranch) {
await tx.customerBranch.update({
where: { id: customerBranch.id },
data: {
customer: {
update: {
status: Status.ACTIVE,
},
},
status: Status.ACTIVE,
},
});
}
return await tx.quotation.update({ return await tx.quotation.update({
include: { include: {
productServiceList: { productServiceList: {

View file

@ -293,28 +293,48 @@ export class RequestDataController extends Controller {
async updateRequestData( async updateRequestData(
@Request() req: RequestWithUser, @Request() req: RequestWithUser,
@Body() @Body()
boby: { body: {
defaultMessengerId: string; defaultMessengerId: string;
requestDataId: string[]; requestDataId: string[];
}, },
) { ) {
const record = await prisma.requestData.updateManyAndReturn({ if (body.requestDataId.length === 0) return;
where: {
id: { in: boby.requestDataId }, return await prisma.$transaction(async (tx) => {
quotation: { const record = await tx.requestData.updateManyAndReturn({
registeredBranch: { where: {
OR: permissionCond(req.user), id: { in: body.requestDataId },
quotation: {
registeredBranch: {
OR: permissionCond(req.user),
},
}, },
}, },
}, data: {
data: { defaultMessengerId: body.defaultMessengerId,
defaultMessengerId: boby.defaultMessengerId, },
}, });
if (record.length <= 0) throw notFoundError("Request Data");
await tx.requestWorkStepStatus.updateMany({
where: {
workStatus: {
in: [
RequestWorkStatus.Pending,
RequestWorkStatus.Waiting,
RequestWorkStatus.InProgress,
],
},
requestWork: {
requestDataId: { in: body.requestDataId },
},
},
data: { responsibleUserId: body.defaultMessengerId },
});
return record[0];
}); });
if (record.length <= 0) throw notFoundError("Request Data");
return record[0];
} }
} }

View file

@ -77,6 +77,7 @@ export class TaskController extends Controller {
by: ["taskOrderStatus"], by: ["taskOrderStatus"],
_count: true, _count: true,
}); });
return task.reduce<Record<TaskOrderStatus, number>>( return task.reduce<Record<TaskOrderStatus, number>>(
(a, c) => Object.assign(a, { [c.taskOrderStatus]: c._count }), (a, c) => Object.assign(a, { [c.taskOrderStatus]: c._count }),
{ {
@ -264,6 +265,12 @@ export class TaskController extends Controller {
taskProduct?: { productId: string; discount?: number }[]; taskProduct?: { productId: string; discount?: number }[];
}, },
) { ) {
if (body.taskList.length < 1 || !body.registeredBranchId)
throw new HttpError(
HttpStatus.BAD_REQUEST,
"Your created invalid task order",
"taskOrderInvalid",
);
return await prisma.$transaction(async (tx) => { return await prisma.$transaction(async (tx) => {
const last = await tx.runningNo.upsert({ const last = await tx.runningNo.upsert({
where: { where: {
@ -313,8 +320,8 @@ export class TaskController extends Controller {
if (updated.count !== taskList.length) { if (updated.count !== taskList.length) {
throw new HttpError( throw new HttpError(
HttpStatus.PRECONDITION_FAILED, HttpStatus.PRECONDITION_FAILED,
"All request work to issue task order must be in ready state.", "all request work to issue task order must be in ready state.",
"requestWorkMustReady", "requestworkmustready",
); );
} }
await tx.institution.updateMany({ await tx.institution.updateMany({

View file

@ -13,6 +13,7 @@ import {
Security, Security,
Tags, Tags,
} from "tsoa"; } from "tsoa";
import config from "../config.json";
import prisma from "../db"; import prisma from "../db";
@ -53,6 +54,7 @@ function globalAllow(user: RequestWithUser["user"]) {
const listAllowed = ["system", "head_of_admin", "admin", "executive", "accountant"]; const listAllowed = ["system", "head_of_admin", "admin", "executive", "accountant"];
return user.roles?.some((v) => listAllowed.includes(v)) || false; return user.roles?.some((v) => listAllowed.includes(v)) || false;
} }
const VAT_DEFAULT = config.vat;
const permissionCond = createPermCondition(globalAllow); const permissionCond = createPermCondition(globalAllow);
const permissionCheck = createPermCheck(globalAllow); const permissionCheck = createPermCheck(globalAllow);
@ -154,7 +156,6 @@ export class CreditNoteController extends Controller {
@Query() creditNoteStatus?: CreditNoteStatus, @Query() creditNoteStatus?: CreditNoteStatus,
@Query() startDate?: Date, @Query() startDate?: Date,
@Query() endDate?: Date, @Query() endDate?: Date,
@Body() body?: {},
) { ) {
const where = { const where = {
OR: queryOrNot<Prisma.CreditNoteWhereInput[]>(query, [ OR: queryOrNot<Prisma.CreditNoteWhereInput[]>(query, [
@ -216,6 +217,8 @@ export class CreditNoteController extends Controller {
const [result, total] = await prisma.$transaction([ const [result, total] = await prisma.$transaction([
prisma.creditNote.findMany({ prisma.creditNote.findMany({
where, where,
take: pageSize,
skip: (page - 1) * pageSize,
include: { include: {
quotation: { quotation: {
include: { include: {
@ -346,9 +349,8 @@ export class CreditNoteController extends Controller {
).length; ).length;
const price = const price =
c.productService.pricePerUnit - c.productService.pricePerUnit * (1 + (c.productService.vat > 0 ? VAT_DEFAULT : 0)) -
c.productService.discount / c.productService.amount + c.productService.discount;
c.productService.vat / c.productService.amount;
if (serviceChargeStepCount && successCount) { if (serviceChargeStepCount && successCount) {
return a + price - c.productService.product.serviceCharge * successCount; return a + price - c.productService.product.serviceCharge * successCount;
@ -424,7 +426,6 @@ export class CreditNoteController extends Controller {
let textData = ""; let textData = "";
let dataCustomerId: string[] = []; let dataCustomerId: string[] = [];
let textWorkList: string[] = [];
let dataUserId: string[] = []; let dataUserId: string[] = [];
if (res) { if (res) {
@ -541,9 +542,8 @@ export class CreditNoteController extends Controller {
).length; ).length;
const price = const price =
c.productService.pricePerUnit - c.productService.pricePerUnit * (1 + (c.productService.vat > 0 ? VAT_DEFAULT : 0)) -
c.productService.discount / c.productService.amount + c.productService.discount;
c.productService.vat / c.productService.amount;
if (serviceChargeStepCount && successCount) { if (serviceChargeStepCount && successCount) {
return a + price - c.productService.product.serviceCharge * successCount; return a + price - c.productService.product.serviceCharge * successCount;

View file

@ -430,12 +430,18 @@ export class DebitNoteController extends Controller {
const list = body.productServiceList.map((v, i) => { const list = body.productServiceList.map((v, i) => {
const p = product.find((p) => p.id === v.productId)!; const p = product.find((p) => p.id === v.productId)!;
const price = body.agentPrice ? p.agentPrice : p.price;
const pricePerUnit = p.vatIncluded ? price / (1 + VAT_DEFAULT) : price; const vatIncluded = body.agentPrice ? p.agentPriceVatIncluded : p.vatIncluded;
const vat = p.calcVat
? (pricePerUnit * (v.discount ? v.amount : 1) - (v.discount || 0)) * const originalPrice = body.agentPrice ? p.agentPrice : p.price;
VAT_DEFAULT * const finalPrice = precisionRound(
(!v.discount ? v.amount : 1) originalPrice + (vatIncluded ? 0 : originalPrice * VAT_DEFAULT),
);
const pricePerUnit = finalPrice / (1 + VAT_DEFAULT);
const vat = (body.agentPrice ? p.agentPriceCalcVat : p.calcVat)
? ((pricePerUnit * (1 + VAT_DEFAULT) * v.amount - (v.discount || 0)) /
(1 + VAT_DEFAULT)) *
VAT_DEFAULT
: 0; : 0;
return { return {
@ -458,15 +464,13 @@ export class DebitNoteController extends Controller {
const price = list.reduce( const price = list.reduce(
(a, c) => { (a, c) => {
a.totalPrice = precisionRound(a.totalPrice + c.pricePerUnit * c.amount); const vat = c.vat ? VAT_DEFAULT : 0;
const price = c.pricePerUnit * c.amount * (1 + vat) - c.discount;
a.totalPrice = precisionRound(a.totalPrice + price / (1 + vat) + c.discount);
a.totalDiscount = precisionRound(a.totalDiscount + c.discount); a.totalDiscount = precisionRound(a.totalDiscount + c.discount);
a.vat = precisionRound(a.vat + c.vat); a.vat = precisionRound(a.vat + c.vat);
a.vatExcluded = a.vatExcluded = c.vat === 0 ? precisionRound(a.vatExcluded + price) : a.vatExcluded;
c.vat === 0
? precisionRound(
a.vatExcluded + (c.pricePerUnit * c.amount - (c.discount || 0)) * VAT_DEFAULT,
)
: a.vatExcluded;
a.finalPrice = precisionRound( a.finalPrice = precisionRound(
Math.max(a.totalPrice - a.totalDiscount + a.vat - (body.discount || 0), 0), Math.max(a.totalPrice - a.totalDiscount + a.vat - (body.discount || 0), 0),
); );
@ -673,12 +677,18 @@ export class DebitNoteController extends Controller {
} }
const list = body.productServiceList.map((v, i) => { const list = body.productServiceList.map((v, i) => {
const p = product.find((p) => p.id === v.productId)!; const p = product.find((p) => p.id === v.productId)!;
const price = body.agentPrice ? p.agentPrice : p.price;
const pricePerUnit = p.vatIncluded ? price / (1 + VAT_DEFAULT) : price; const vatIncluded = record.agentPrice ? p.agentPriceVatIncluded : p.vatIncluded;
const vat = p.calcVat
? (pricePerUnit * (v.discount ? v.amount : 1) - (v.discount || 0)) * const originalPrice = record.agentPrice ? p.agentPrice : p.price;
VAT_DEFAULT * const finalPrice = precisionRound(
(!v.discount ? v.amount : 1) originalPrice + (vatIncluded ? 0 : originalPrice * VAT_DEFAULT),
);
const pricePerUnit = finalPrice / (1 + VAT_DEFAULT);
const vat = (record.agentPrice ? p.agentPriceCalcVat : p.calcVat)
? ((pricePerUnit * (1 + VAT_DEFAULT) * v.amount - (v.discount || 0)) /
(1 + VAT_DEFAULT)) *
VAT_DEFAULT
: 0; : 0;
return { return {
@ -701,15 +711,13 @@ export class DebitNoteController extends Controller {
const price = list.reduce( const price = list.reduce(
(a, c) => { (a, c) => {
a.totalPrice = precisionRound(a.totalPrice + c.pricePerUnit * c.amount); const vat = c.vat ? VAT_DEFAULT : 0;
const price = c.pricePerUnit * c.amount * (1 + vat) - c.discount;
a.totalPrice = precisionRound(a.totalPrice + price / (1 + vat) + c.discount);
a.totalDiscount = precisionRound(a.totalDiscount + c.discount); a.totalDiscount = precisionRound(a.totalDiscount + c.discount);
a.vat = precisionRound(a.vat + c.vat); a.vat = precisionRound(a.vat + c.vat);
a.vatExcluded = a.vatExcluded = c.vat === 0 ? precisionRound(a.vatExcluded + price) : a.vatExcluded;
c.vat === 0
? precisionRound(
a.vatExcluded + (c.pricePerUnit * c.amount - (c.discount || 0)) * VAT_DEFAULT,
)
: a.vatExcluded;
a.finalPrice = precisionRound( a.finalPrice = precisionRound(
Math.max(a.totalPrice - a.totalDiscount + a.vat - (body.discount || 0), 0), Math.max(a.totalPrice - a.totalDiscount + a.vat - (body.discount || 0), 0),
); );

View file

@ -1,6 +1,10 @@
import prisma from "../db"; import prisma from "../db";
import config from "../config.json"; import config from "../config.json";
import { CustomerType, PayCondition } from "@prisma/client"; import { CustomerType, PayCondition } from "@prisma/client";
import { convertTemplate } from "../utils/string-template";
import { htmlToText } from "html-to-text";
import { JsonObject } from "@prisma/client/runtime/library";
import { precisionRound } from "../utils/arithmetic";
if (!process.env.FLOW_ACCOUNT_URL) throw new Error("Require FLOW_ACCOUNT_URL"); if (!process.env.FLOW_ACCOUNT_URL) throw new Error("Require FLOW_ACCOUNT_URL");
if (!process.env.FLOW_ACCOUNT_CLIENT_ID) throw new Error("Require FLOW_ACCOUNT_CLIENT_ID"); if (!process.env.FLOW_ACCOUNT_CLIENT_ID) throw new Error("Require FLOW_ACCOUNT_CLIENT_ID");
@ -232,6 +236,29 @@ const flowAccount = {
installments: true, installments: true,
quotation: { quotation: {
include: { include: {
paySplit: true,
worker: {
select: {
employee: {
select: {
employeePassport: {
select: {
number: true,
},
orderBy: {
expireDate: "desc",
},
take: 1,
},
namePrefix: true,
firstName: true,
lastName: true,
firstNameEN: true,
lastNameEN: true,
},
},
},
},
registeredBranch: { registeredBranch: {
include: { include: {
province: true, province: true,
@ -262,19 +289,58 @@ const flowAccount = {
const quotation = data.quotation; const quotation = data.quotation;
const customer = quotation.customerBranch; const customer = quotation.customerBranch;
const product =
const summary = {
subTotal: 0,
discountAmount: 0,
vatableAmount: 0,
exemptAmount: 0,
vatAmount: 0,
grandTotal: 0,
};
const products = (
quotation.payCondition === PayCondition.BillFull || quotation.payCondition === PayCondition.BillFull ||
quotation.payCondition === PayCondition.Full quotation.payCondition === PayCondition.Full
? quotation.productServiceList ? quotation.productServiceList
: quotation.productServiceList.filter((lhs) => : quotation.productServiceList.filter((lhs) =>
data.installments.some((rhs) => rhs.no === lhs.installmentNo), data.installments.some((rhs) => rhs.no === lhs.installmentNo),
); )
).map((v) => {
// TODO: Use product's VAT field (not implemented) instead.
const VAT_RATE = VAT_DEFAULT;
summary.subTotal +=
precisionRound(v.pricePerUnit * (1 + (v.vat > 0 ? VAT_RATE : 0))) * v.amount;
summary.discountAmount += v.discount;
const total =
precisionRound(v.pricePerUnit * (1 + (v.vat > 0 ? VAT_RATE : 0))) * v.amount -
(v.discount ?? 0);
if (v.vat > 0) {
summary.vatableAmount += precisionRound(total / (1 + VAT_RATE));
summary.vatAmount += v.vat;
} else {
summary.exemptAmount += total;
}
summary.grandTotal += total;
return {
type: ProductAndServiceType.ProductNonInv,
name: v.product.name,
pricePerUnit: precisionRound(v.pricePerUnit),
quantity: v.amount,
discountAmount: v.discount,
vatRate: v.vat === 0 ? 0 : Math.round(VAT_RATE * 100),
total,
};
});
const payload = { const payload = {
contactCode: customer.code, contactCode: customer.code,
contactName: contactName: customer.contactName || "-",
(customer.customer.customerType === CustomerType.PERS
? [customer.firstName, customer.lastName].join(" ").trim()
: customer.registerName) || "-",
contactAddress: [ contactAddress: [
customer.address, customer.address,
!!customer.moo ? "หมู่ " + customer.moo : null, !!customer.moo ? "หมู่ " + customer.moo : null,
@ -283,11 +349,10 @@ const flowAccount = {
(customer.province?.id === "10" ? "แขวง" : "อำเภอ") + customer.subDistrict?.name, (customer.province?.id === "10" ? "แขวง" : "อำเภอ") + customer.subDistrict?.name,
(customer.province?.id === "10" ? "เขต" : "ตำบล") + customer.district?.name, (customer.province?.id === "10" ? "เขต" : "ตำบล") + customer.district?.name,
"จังหวัด" + customer.province?.name, "จังหวัด" + customer.province?.name,
customer.subDistrict?.zipCode,
] ]
.filter(Boolean) .filter(Boolean)
.join(" "), .join(" "),
contactTaxId: customer.citizenId || customer.code, contactTaxId: customer.citizenId || customer.legalPersonNo || "-",
contactBranch: contactBranch:
(customer.customer.customerType === CustomerType.PERS (customer.customer.customerType === CustomerType.PERS
? [customer.firstName, customer.lastName].join(" ").trim() ? [customer.firstName, customer.lastName].join(" ").trim()
@ -305,36 +370,35 @@ const flowAccount = {
isVat: true, isVat: true,
useReceiptDeduction: false, useReceiptDeduction: false,
useInlineVat: true,
discounPercentage: 0, discounPercentage: 0,
discountAmount: quotation.totalDiscount, discountAmount: quotation.totalDiscount,
subTotal: subTotal: summary.subTotal,
quotation.payCondition === "BillSplitCustom" || quotation.payCondition === "SplitCustom" totalAfterDiscount: summary.subTotal - summary.discountAmount,
? 0 vatableAmount: summary.vatableAmount,
: quotation.totalPrice, exemptAmount: summary.exemptAmount,
totalAfterDiscount: vatAmount: summary.vatAmount,
quotation.payCondition === "BillSplitCustom" || quotation.payCondition === "SplitCustom" grandTotal: summary.grandTotal,
? 0
: quotation.finalPrice,
vatAmount:
quotation.payCondition === "BillSplitCustom" || quotation.payCondition === "SplitCustom"
? 0
: quotation.vat,
grandTotal:
quotation.payCondition === "BillSplitCustom" || quotation.payCondition === "SplitCustom"
? data.installments.reduce((a, c) => a + c.amount, 0)
: quotation.finalPrice,
items: product.map((v) => ({ remarks: htmlToText(
type: ProductAndServiceType.ProductNonInv, convertTemplate(quotation.remark ?? "", {
name: v.product.name, "quotation-payment": {
pricePerUnit: v.pricePerUnit, paymentType: quotation?.payCondition || "Full",
quantity: v.amount, amount: quotation.finalPrice,
discountAmount: v.discount, installments: quotation?.paySplit,
total: (v.pricePerUnit - (v.discount || 0)) * v.amount + v.vat, },
vatRate: v.vat === 0 ? 0 : Math.round(VAT_DEFAULT * 100), "quotation-labor": {
})), name: quotation.worker.map(
(v, i) =>
`${i + 1}. ` +
`${v.employee.employeePassport.length !== 0 ? v.employee.employeePassport[0].number + "_" : ""}${v.employee.namePrefix}. ${v.employee.firstNameEN ? `${v.employee.firstNameEN} ${v.employee.lastNameEN}` : `${v.employee.firstName} ${v.employee.lastName}`} `.toUpperCase(),
),
},
}),
),
items: products,
}; };
return await flowAccountAPI.createReceipt(payload, false); return await flowAccountAPI.createReceipt(payload, false);
@ -347,6 +411,219 @@ const flowAccount = {
} }
return null; return null;
}, },
// flowAccount GET Product list
async getProducts() {
const { token } = await flowAccountAPI.auth();
const res = await fetch(api + "/products", {
method: "GET",
headers: {
["Content-Type"]: `application/json`,
["Authorization"]: `Bearer ${token}`,
},
});
return {
ok: res.ok,
status: res.status,
body: await res.json(),
};
},
// flowAccount GET Product by id
async getProductsById(recordId: string) {
const { token } = await flowAccountAPI.auth();
const res = await fetch(api + `/products/${recordId}`, {
method: "GET",
headers: {
["Content-Type"]: `application/json`,
["Authorization"]: `Bearer ${token}`,
},
});
const data = await res.json();
return {
ok: res.ok,
status: res.status,
list: data.data.list,
total: data.data.total,
};
},
// flowAccount POST create Product
async createProducts(code: string, body: JsonObject) {
const { token } = await flowAccountAPI.auth();
const commonBody = {
productStructureType: null,
type: 3,
name: body.name,
sellDescription: body.detail,
sellVatType: 3,
buyPrice: body.serviceCharge,
buyVatType: body.serviceChargeVatIncluded ? 1 : 3,
buyDescription: body.detail,
};
const createProduct = async (name: string, price: any, vatIncluded: boolean) => {
try {
const res = await fetch(`${api}/products`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${token}`,
},
body: JSON.stringify({
...commonBody,
name,
sellPrice: price,
sellVatType: vatIncluded ? 1 : 3,
}),
});
if (!res.ok) throw new Error(`HTTP ${res.status}: Failed to create product`);
const json = await res.json().catch(() => {
throw new Error("Invalid JSON response from FlowAccount API");
});
return json?.data?.list?.[0]?.id ?? null;
} catch (err) {
console.error("createProduct error:", err);
return null;
}
};
const deleteProduct = async (id: string) => {
try {
await fetch(`${api}/products/${id}`, {
method: "DELETE",
headers: { Authorization: `Bearer ${token}` },
});
} catch (err) {
console.error("Rollback delete failed:", err);
}
};
const [sellResult, agentResult] = await Promise.allSettled([
createProduct(`${code} ${body.name}`, body.price, /true/.test(`${body.vatIncluded}`)),
createProduct(
`${code} ${body.name} (ราคาตัวแทน)`,
body.agentPrice,
/true/.test(`${body.agentPriceVatIncluded}`),
),
]);
const sellId = sellResult.status === "fulfilled" ? sellResult.value : null;
const agentId = agentResult.status === "fulfilled" ? agentResult.value : null;
// --- validation ---
if (!sellId && !agentId) {
throw new Error("FlowAccountProductError.BOTH_CREATION_FAILED");
}
if (!sellId && agentId) {
await deleteProduct(agentId);
throw new Error("FlowAccountProductError.SELL_PRICE_CREATION_FAILED");
}
if (sellId && !agentId) {
await deleteProduct(sellId);
throw new Error("FlowAccountProductError.AGENT_PRICE_CREATION_FAILED");
}
return {
ok: true,
status: 200,
data: {
productIdSellPrice: sellId,
productIdAgentPrice: agentId,
},
};
},
// flowAccount PUT edit Product
async editProducts(sellPriceId: String, agentPriceId: String, body: JsonObject) {
const { token } = await flowAccountAPI.auth();
const commonBody = {
productStructureType: null,
type: 3,
name: body.name,
sellDescription: body.detail,
sellVatType: 3,
buyPrice: body.serviceCharge,
buyVatType: body.serviceChargeVatIncluded ? 1 : 3,
buyDescription: body.detail,
};
const editProduct = async (id: String, name: String, price: any, vatIncluded: boolean) => {
try {
const res = await fetch(api + `/products/${id}`, {
method: "PUT",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${token}`,
},
body: JSON.stringify({
...commonBody,
name: name,
sellPrice: price,
sellVatType: vatIncluded ? 1 : 3,
}),
});
if (!res.ok) {
throw new Error(`Request failed with status ${res.status} ${res}`);
}
let json: any = null;
try {
json = await res.json();
} catch {
throw new Error("Response is not valid JSON");
}
return json?.data?.list?.[0]?.id ?? null;
} catch (err) {
console.error("createProduct error:", err);
return null;
}
};
await Promise.all([
editProduct(
sellPriceId,
`${body.code} ${body.name}`,
body.price,
/true/.test(`${body.vatIncluded}`),
),
editProduct(
agentPriceId,
`${body.code} ${body.name} (ราคาตัวแทน)`,
body.agentPrice,
/true/.test(`${body.agentPriceVatIncluded}`),
),
]);
},
// flowAccount DELETE Product
async deleteProduct(recordId: string) {
const { token } = await flowAccountAPI.auth();
const res = await fetch(api + `/products/${recordId}`, {
method: "DELETE",
headers: {
["Authorization"]: `Bearer ${token}`,
},
});
return {
ok: res.ok,
status: res.status,
};
},
}; };
export default flowAccount; export default flowAccount;

View file

@ -0,0 +1,67 @@
export function formatNumberDecimal(num: number, point: number = 2): string {
return (num || 0).toLocaleString("eng", {
minimumFractionDigits: point,
maximumFractionDigits: point,
});
}
const templates = {
"quotation-labor": {
converter: (context?: { name: string[] }) => {
return context?.name.join("<br />") || "";
},
},
"quotation-payment": {
converter: (context?: {
paymentType: "Full" | "Split" | "SplitCustom" | "BillFull" | "BillSplit" | "BillSplitCustom";
amount?: number;
installments?: {
no: number;
amount: number;
}[];
}) => {
if (context?.paymentType === "Full") {
return [
"**** เงื่อนไขเพิ่มเติม",
"- เงื่อนไขการชำระเงิน แบบเต็มจำนวน",
`&nbsp; จำนวน ${formatNumberDecimal(context?.amount || 0, 2)}`,
].join("<br/>");
} else {
return [
"**** เงื่อนไขเพิ่มเติม",
`- เงื่อนไขการชำระเงิน แบบแบ่งจ่าย${context?.paymentType === "SplitCustom" ? " กำหนดเอง " : " "}${context?.installments?.length} งวด`,
...(context?.installments?.map(
(v) => `&nbsp; งวดที่ ${v.no} จำนวน ${formatNumberDecimal(v.amount, 2)}`,
) || []),
].join("<br />");
}
},
},
} as const;
type Template = typeof templates;
type TemplateName = keyof Template;
type TemplateContext = {
[key in TemplateName]?: Parameters<Template[key]["converter"]>[0];
};
export function convertTemplate(
text: string,
context?: TemplateContext,
templateUse?: TemplateName[],
) {
let ret = text;
for (const [name, template] of Object.entries(templates)) {
if (templateUse && !templateUse.includes(name as TemplateName)) continue;
ret = ret.replace(
new RegExp("\\#\\[" + name.replaceAll("-", "\\-") + "\\]", "g"),
typeof template.converter === "function"
? template.converter(context?.[name as TemplateName] as any)
: template.converter,
);
}
return ret;
}

View file

@ -62,85 +62,90 @@ export async function initThailandAreaDatabase() {
return result; return result;
} }
await prisma.$transaction(async (tx) => { await prisma.$transaction(
const meta = { async (tx) => {
createdBy: null, const meta = {
createdAt: new Date(), createdBy: null,
updatedBy: null, createdAt: new Date(),
updatedAt: new Date(), updatedBy: null,
}; updatedAt: new Date(),
};
await Promise.all( await Promise.all(
splitChunk(province, 1000, async (r) => { splitChunk(province, 1000, async (r) => {
return await tx.$kysely return await tx.$kysely
.insertInto("Province") .insertInto("Province")
.columns(["id", "name", "nameEN", "createdBy", "createdAt", "updatedBy", "updatedAt"]) .columns(["id", "name", "nameEN", "createdBy", "createdAt", "updatedBy", "updatedAt"])
.values(r.map((v) => ({ ...v, ...meta }))) .values(r.map((v) => ({ ...v, ...meta })))
.onConflict((oc) => .onConflict((oc) =>
oc.column("id").doUpdateSet({ oc.column("id").doUpdateSet({
name: (eb) => eb.ref("excluded.name"), name: (eb) => eb.ref("excluded.name"),
nameEN: (eb) => eb.ref("excluded.nameEN"), nameEN: (eb) => eb.ref("excluded.nameEN"),
updatedAt: (eb) => eb.ref("excluded.updatedAt"), updatedAt: (eb) => eb.ref("excluded.updatedAt"),
}), }),
) )
.execute(); .execute();
}), }),
); );
await Promise.all( await Promise.all(
splitChunk(district, 2000, async (r) => { splitChunk(district, 2000, async (r) => {
return await tx.$kysely return await tx.$kysely
.insertInto("District") .insertInto("District")
.columns([ .columns([
"id", "id",
"name", "name",
"nameEN", "nameEN",
"provinceId", "provinceId",
"createdBy", "createdBy",
"createdAt", "createdAt",
"updatedBy", "updatedBy",
"updatedAt", "updatedAt",
]) ])
.values(r.map((v) => ({ ...v, ...meta }))) .values(r.map((v) => ({ ...v, ...meta })))
.onConflict((oc) => .onConflict((oc) =>
oc.column("id").doUpdateSet({ oc.column("id").doUpdateSet({
name: (eb) => eb.ref("excluded.name"), name: (eb) => eb.ref("excluded.name"),
nameEN: (eb) => eb.ref("excluded.nameEN"), nameEN: (eb) => eb.ref("excluded.nameEN"),
provinceId: (eb) => eb.ref("excluded.provinceId"), provinceId: (eb) => eb.ref("excluded.provinceId"),
updatedAt: (eb) => eb.ref("excluded.updatedAt"), updatedAt: (eb) => eb.ref("excluded.updatedAt"),
}), }),
) )
.execute(); .execute();
}), }),
); );
await Promise.all( await Promise.all(
splitChunk(subDistrict, 1000, async (r) => { splitChunk(subDistrict, 1000, async (r) => {
return await tx.$kysely return await tx.$kysely
.insertInto("SubDistrict") .insertInto("SubDistrict")
.columns([ .columns([
"id", "id",
"name", "name",
"nameEN", "nameEN",
"districtId", "districtId",
"createdBy", "createdBy",
"createdAt", "createdAt",
"updatedBy", "updatedBy",
"updatedAt", "updatedAt",
]) ])
.values(r.map((v) => ({ ...v, ...meta }))) .values(r.map((v) => ({ ...v, ...meta })))
.onConflict((oc) => .onConflict((oc) =>
oc.column("id").doUpdateSet({ oc.column("id").doUpdateSet({
name: (eb) => eb.ref("excluded.name"), name: (eb) => eb.ref("excluded.name"),
nameEN: (eb) => eb.ref("excluded.nameEN"), nameEN: (eb) => eb.ref("excluded.nameEN"),
districtId: (eb) => eb.ref("excluded.districtId"), districtId: (eb) => eb.ref("excluded.districtId"),
updatedAt: (eb) => eb.ref("excluded.updatedAt"), updatedAt: (eb) => eb.ref("excluded.updatedAt"),
}), }),
) )
.execute(); .execute();
}), }),
); );
}); },
{
timeout: 15_000,
},
);
console.log("[INFO]: Sync thailand province, district and subdistrict, OK."); console.log("[INFO]: Sync thailand province, district and subdistrict, OK.");
} }
@ -170,67 +175,72 @@ export async function initEmploymentOffice() {
const list = await prisma.province.findMany(); const list = await prisma.province.findMany();
await prisma.$transaction(async (tx) => { await prisma.$transaction(
await Promise.all( async (tx) => {
list await Promise.all(
.map(async (province) => { list
if (special[province.id]) { .map(async (province) => {
await tx.employmentOffice.deleteMany({ if (special[province.id]) {
where: { provinceId: province.id, district: { none: {} } }, await tx.employmentOffice.deleteMany({
}); where: { provinceId: province.id, district: { none: {} } },
return await Promise.all( });
Object.entries(special[province.id]).map(async ([key, val]) => { return await Promise.all(
const id = province.id + "-" + key.padStart(2, "0"); Object.entries(special[province.id]).map(async ([key, val]) => {
return tx.employmentOffice.upsert({ const id = province.id + "-" + key.padStart(2, "0");
where: { id }, return tx.employmentOffice.upsert({
create: { where: { id },
id, create: {
name: nameSpecial(province.name, +key), id,
nameEN: nameSpecialEN(province.nameEN, +key), name: nameSpecial(province.name, +key),
provinceId: province.id, nameEN: nameSpecialEN(province.nameEN, +key),
district: { provinceId: province.id,
createMany: { district: {
data: val.map((districtId) => ({ districtId })), createMany: {
skipDuplicates: true, data: val.map((districtId) => ({ districtId })),
skipDuplicates: true,
},
}, },
}, },
}, update: {
update: { id,
id, name: nameSpecial(province.name, +key),
name: nameSpecial(province.name, +key), nameEN: nameSpecialEN(province.nameEN, +key),
nameEN: nameSpecialEN(province.nameEN, +key), provinceId: province.id,
provinceId: province.id, district: {
district: { deleteMany: { districtId: { notIn: val } },
deleteMany: { districtId: { notIn: val } }, createMany: {
createMany: { data: val.map((districtId) => ({ districtId })),
data: val.map((districtId) => ({ districtId })), skipDuplicates: true,
skipDuplicates: true, },
}, },
}, },
}, });
}); }),
}), );
); }
}
return tx.employmentOffice.upsert({ return tx.employmentOffice.upsert({
where: { id: province.id }, where: { id: province.id },
create: { create: {
id: province.id, id: province.id,
name: name(province.name), name: name(province.name),
nameEN: nameEN(province.nameEN), nameEN: nameEN(province.nameEN),
provinceId: province.id, provinceId: province.id,
}, },
update: { update: {
name: name(province.name), name: name(province.name),
nameEN: nameEN(province.nameEN), nameEN: nameEN(province.nameEN),
provinceId: province.id, provinceId: province.id,
}, },
}); });
}) })
.flat(), .flat(),
); );
}); },
{
timeout: 15_000,
},
);
console.log("[INFO]: Sync employment office, OK."); console.log("[INFO]: Sync employment office, OK.");
} }