Compare commits
243 Commits
v0.0.1-alp
...
3a24d62957
| Author | SHA1 | Date | |
|---|---|---|---|
| 3a24d62957 | |||
| 6a14bab30c | |||
| 2ebf695526 | |||
| 24af3ca403 | |||
| 6fbe3a9eed | |||
| 7dbc31c046 | |||
| ba09a77f29 | |||
| 3f04609f82 | |||
| 22a7b612e1 | |||
| 39db142db4 | |||
| d85f08cb19 | |||
| 15c939ebe8 | |||
| 9ff428f5ea | |||
| 2e460c7f8a | |||
| 7c4c5f980a | |||
| 86e1237509 | |||
| 706ab8b448 | |||
| 9440b44f3b | |||
| a2d9a6c127 | |||
| c0a7d4a125 | |||
| 8fcb2c66ed | |||
| 8fc3129f7d | |||
| 05c553e927 | |||
| 6eaae0f537 | |||
| 3ad84dab71 | |||
| 5a7046bacd | |||
| cfc497c1f2 | |||
| 7bbdd4e555 | |||
| e909e8deec | |||
| 4249e90307 | |||
| 2f495739e6 | |||
| 7d722c4aac | |||
| 5f2148f5f0 | |||
| 7671172d97 | |||
| 6dd1a5b332 | |||
| 865f280cfa | |||
| a717260b8d | |||
| 4464cea022 | |||
| 55418e2f09 | |||
| 5281118596 | |||
| f635415b75 | |||
| 40edbc3eae | |||
| 2558b2e5bb | |||
| 45a0dee9ca | |||
| bb7931d6c8 | |||
| 4f848bb649 | |||
| f8335f5217 | |||
| 2a35381fe4 | |||
| e6b92aeb10 | |||
| da87e2e1d3 | |||
| 3ef0f230dd | |||
| 78f7b8a179 | |||
| 1a3d8a7ddc | |||
| 2a648f6306 | |||
| 69a9a81a88 | |||
| 188fdd0eb7 | |||
| db28635c8c | |||
| bcdf9566bc | |||
| c15ee070e7 | |||
| 347edb7078 | |||
| fe0b1573f3 | |||
| 9c0ef1f5df | |||
| 8b076949a7 | |||
| 6d0fb8aee4 | |||
| 3a0c729b9a | |||
| 057a570e43 | |||
| 52974aa0b4 | |||
| ecfbda9036 | |||
| 389211186f | |||
| 3a27fd8542 | |||
| 1f6637c512 | |||
| 1840ac5e58 | |||
| 636daaed0a | |||
| 71c83062cb | |||
| cd67c4de80 | |||
| 36ac1dccb4 | |||
| 514a44b6de | |||
| a7bb364a2f | |||
| 047cc7cdf0 | |||
| 8dc4d70e28 | |||
| c8931c7249 | |||
| 67f36c5499 | |||
| ebf1060475 | |||
| c64392f457 | |||
| e9e73c829c | |||
| bcb7773007 | |||
| eb950d2c29 | |||
| 2616acf106 | |||
| 30ff7b71d9 | |||
| e7af3d1182 | |||
| 3e66c3920d | |||
| eb9d77c3d4 | |||
| 342a97f6b1 | |||
| b0c7277a6c | |||
| dc95e50a84 | |||
| d2a9e1d110 | |||
| a9c69250bd | |||
| d61be61f44 | |||
| f5bae2c0c2 | |||
| 05758791be | |||
| 51026e3e2c | |||
| 9631736e26 | |||
| ce9d8eaaf5 | |||
| 1bbf5c2a49 | |||
| 13718fe702 | |||
| 0de2579942 | |||
| 7c31b43a4a | |||
| 85e96f5ed1 | |||
| 6b515c608f | |||
| d8869b103b | |||
| 1dba774abc | |||
| 505d7cea5d | |||
| 1ff5e5032f | |||
| 5fa70da90c | |||
| 0459cd788a | |||
| 7d7d991122 | |||
| 2721bb2a3b | |||
| 4424c742d2 | |||
| 6d8499bfb8 | |||
| 9edafc9d28 | |||
| e9b0101095 | |||
| ca885fb01a | |||
| edb3668548 | |||
| 87803eed43 | |||
| e61038e004 | |||
| d99449ddc4 | |||
| 3552ca31f9 | |||
| b578f05d64 | |||
| 4ca74de279 | |||
| 12412536d1 | |||
| a38e2e0339 | |||
| 8c253a90b6 | |||
| ba30281e59 | |||
| 2ad78e22f1 | |||
| 518c0a8c19 | |||
| cd13360cfb | |||
| 4e0cf8c54c | |||
| 36995e9fb4 | |||
| 30ffd843c7 | |||
| bb6155c969 | |||
| 7d2f048932 | |||
| 649ae1ee9f | |||
| 8446dbc955 | |||
| 0b7318f856 | |||
| bddc9aca0d | |||
| 77b4533dea | |||
| 83a542d1b7 | |||
| 4855412733 | |||
| 251970ec8f | |||
| f7ea5f709e | |||
| 3d3c2aa964 | |||
| 781025dca0 | |||
| a593bb2baa | |||
| 759f96b0b6 | |||
| de5df2b00b | |||
| 4d53af0338 | |||
| f7276ca2d7 | |||
| d6328ab764 | |||
| a6d53f0266 | |||
| 7962463927 | |||
| f716de1a58 | |||
| 88cef2a56c | |||
| cb00addee9 | |||
| b832d7aa1e | |||
| 32517d0c98 | |||
| 82f8369640 | |||
| 3734d9daac | |||
| a1eeadeec4 | |||
| 3639c1b77c | |||
| cfbc156517 | |||
| fb3cd85b41 | |||
| 5b1c88546f | |||
| ba3227545d | |||
| 84909bfcf8 | |||
| e0d0ac2077 | |||
| 52a6c821f4 | |||
| eccaf17332 | |||
| 6307037985 | |||
| 4b6061c478 | |||
| fc6dc82d84 | |||
| 6ba905a887 | |||
| f33587a3d9 | |||
| 80189baf90 | |||
| 87f738702a | |||
| 38a0b65e94 | |||
| 9a0ef8e51a | |||
| dcb3f2dd13 | |||
| e47ea9ec52 | |||
| ca3425d327 | |||
| 3bf024cfc9 | |||
| 9d39c13510 | |||
| c9eb59e2ad | |||
| b0e5fd7999 | |||
| 07ebf88806 | |||
| 79e653efa3 | |||
| d05a0ce930 | |||
| 995b1dda7c | |||
| 97f93a1830 | |||
| 635635b356 | |||
| a691dc276e | |||
| 8dfcbc5720 | |||
| 103ae77e9f | |||
| beeccc6e8d | |||
| 0880298cf5 | |||
| 34b0abac36 | |||
| 28c226ddbc | |||
| 42861cc69e | |||
| 5f3d683a13 | |||
| a17787e852 | |||
| 5865ac3b99 | |||
| 637de857f9 | |||
| 3ecf5fb916 | |||
| 92ba3ef512 | |||
| 7d6c2db89c | |||
| 74262beb65 | |||
| f3b8dd94e5 | |||
| 0059b9b850 | |||
| 1ad789b2b9 | |||
| 079478f932 | |||
| d6d5b451cd | |||
| 76747cf917 | |||
| 6e85991062 | |||
| 98e408cb85 | |||
| ed052dff3c | |||
| 8f59bba614 | |||
| fb2c5609aa | |||
| 17aed6cb89 | |||
| b02b93b83f | |||
| 9ceba8b5bb | |||
| 2c0dbf95c7 | |||
| 860207a60b | |||
| 5c6460012a | |||
| be1d4081e0 | |||
| 83a94cacf3 | |||
| 0ce3790675 | |||
| 5854889eb5 | |||
| 4caaf74569 | |||
| fe889ca757 | |||
| 699c124b0e | |||
| 7d55c5f431 | |||
| c4fd74fc93 | |||
| 3775760734 | |||
| 643d12ff18 |
@@ -1,8 +0,0 @@
|
|||||||
# Changesets
|
|
||||||
|
|
||||||
Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works
|
|
||||||
with multi-package repos, or single-package repos to help you version and publish your code. You can
|
|
||||||
find the full documentation for it [in our repository](https://github.com/changesets/changesets)
|
|
||||||
|
|
||||||
We have a quick list of common questions to get you started engaging with this project in
|
|
||||||
[our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md)
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "https://unpkg.com/@changesets/config/schema.json",
|
|
||||||
"changelog": "@changesets/cli/changelog",
|
|
||||||
"commit": false,
|
|
||||||
"fixed": [],
|
|
||||||
"linked": [],
|
|
||||||
"access": "restricted",
|
|
||||||
"baseBranch": "main",
|
|
||||||
"updateInternalDependencies": "patch",
|
|
||||||
"ignore": []
|
|
||||||
}
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
---
|
|
||||||
"lst_v3": patch
|
|
||||||
---
|
|
||||||
|
|
||||||
build stuff
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"mode": "pre",
|
|
||||||
"tag": "alpha",
|
|
||||||
"initialVersions": {
|
|
||||||
"lst_v3": "1.0.1"
|
|
||||||
},
|
|
||||||
"changesets": [
|
|
||||||
"neat-years-unite",
|
|
||||||
"soft-onions-appear"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
---
|
|
||||||
"lst_v3": patch
|
|
||||||
---
|
|
||||||
|
|
||||||
external url added for docker
|
|
||||||
@@ -9,4 +9,4 @@ builds
|
|||||||
testFiles
|
testFiles
|
||||||
nssm.exe
|
nssm.exe
|
||||||
postgresql-17.9-2-windows-x64.exe
|
postgresql-17.9-2-windows-x64.exe
|
||||||
VSCodeUserSetup-x64-1.112.0.msi
|
VSCodeSetup-x64-1.120.0.msi
|
||||||
66
.env-example
66
.env-example
@@ -1,32 +1,60 @@
|
|||||||
|
NODE_ENV=development
|
||||||
# Server
|
# Server
|
||||||
PORT=3000
|
PORT=3000
|
||||||
URL=http://localhost:3000
|
URL=http://localhost:3000
|
||||||
|
SERVER_IP=10.75.2.38
|
||||||
|
TIMEZONE=America/New_York
|
||||||
|
TCP_PORT=2222
|
||||||
|
|
||||||
# authentication
|
# Better auth Secret
|
||||||
BETTER_AUTH_SECRET=""
|
BETTER_AUTH_SECRET=
|
||||||
RESET_EXPIRY_SECONDS=3600
|
RESET_EXPIRY_SECONDS=3600
|
||||||
|
|
||||||
# logging
|
# logging
|
||||||
LOG_LEVEL=debug
|
LOG_LEVEL=
|
||||||
|
|
||||||
# prodServer
|
# SMTP password
|
||||||
PROD_SERVER=usmcd1vms036
|
SMTP_PASSWORD=
|
||||||
PROD_PLANT_TOKEN=test3
|
|
||||||
PROD_USER=alplaprod
|
# opendock
|
||||||
PROD_PASSWORD=password
|
OPENDOCK_URL=https://neutron.opendock.com
|
||||||
|
OPENDOCK_PASSWORD=
|
||||||
|
DEFAULT_DOCK=
|
||||||
|
DEFAULT_LOAD_TYPE=
|
||||||
|
DEFAULT_CARRIER=
|
||||||
|
|
||||||
|
# prodServer when ruining on an actual prod server use localhost this way we don't go out and back in.
|
||||||
|
PROD_SERVER=
|
||||||
|
PROD_PLANT_TOKEN=
|
||||||
|
PROD_USER=
|
||||||
|
PROD_PASSWORD=
|
||||||
|
|
||||||
|
# Tech user for alplaprod api
|
||||||
|
TEC_API_KEY=
|
||||||
|
|
||||||
|
# AD STUFF
|
||||||
|
# this is mainly used for purchase stuff to reference reqs
|
||||||
|
LDAP_URL=
|
||||||
|
|
||||||
# postgres connection
|
# postgres connection
|
||||||
DATABASE_HOST=localhost
|
DATABASE_HOST=localhost
|
||||||
DATABASE_PORT=5433
|
DATABASE_PORT=5432
|
||||||
DATABASE_USER=user
|
DATABASE_USER=
|
||||||
DATABASE_PASSWORD=password
|
DATABASE_PASSWORD=
|
||||||
DATABASE_DB=lst_dev
|
DATABASE_DB=
|
||||||
|
|
||||||
# how is the app running server or client when in client mode you must provide the server
|
# Gp connection
|
||||||
APP_RUNNING_IN=server
|
GP_USER=
|
||||||
SERVER_NAME=localhost
|
GP_PASSWORD=
|
||||||
|
|
||||||
#dev stuff
|
# how often to check for new/updated queries in min
|
||||||
GITEA_TOKEN=""
|
QUERY_TIME_TYPE=m #valid options are m, h
|
||||||
EMAIL_USER=""
|
QUERY_CHECK=1
|
||||||
EMAIL_PASSWORD=""
|
|
||||||
|
|
||||||
|
# Oauth setup
|
||||||
|
PROVIDER=""
|
||||||
|
CLIENT_ID=""
|
||||||
|
CLIENT_SECRET=""
|
||||||
|
CLIENT_SCOPES="openid profile email groups"
|
||||||
|
DISCOVERY_URL=""
|
||||||
|
|||||||
66
.gitea/ISSUE_TEMPLATE/bug_report_frontend.md
Normal file
66
.gitea/ISSUE_TEMPLATE/bug_report_frontend.md
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
---
|
||||||
|
name: Bug Report - Frontend
|
||||||
|
about: Report something that is broken or not working correctly
|
||||||
|
title: "[BUG - Frontend] "
|
||||||
|
ref: "main"
|
||||||
|
labels:
|
||||||
|
|
||||||
|
- bug
|
||||||
|
- frontend
|
||||||
|
---
|
||||||
|
|
||||||
|
# Summary
|
||||||
|
|
||||||
|
Briefly explain the issue.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Steps To Reproduce
|
||||||
|
|
||||||
|
1. Go to ...
|
||||||
|
2. Click ...
|
||||||
|
3. Scan ...
|
||||||
|
4. Error occurs ...
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Expected Behavior
|
||||||
|
|
||||||
|
What should have happened?
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Actual Behavior
|
||||||
|
|
||||||
|
What actually happened instead?
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Severity
|
||||||
|
|
||||||
|
- [ ] Low
|
||||||
|
- [ ] Medium
|
||||||
|
- [ ] High
|
||||||
|
- [ ] Critical
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Environment
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
- Production
|
||||||
|
- Development
|
||||||
|
- Zebra Scanner
|
||||||
|
- Mobile Device
|
||||||
|
- Windows Server
|
||||||
|
- Docker
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Logs / Screenshots
|
||||||
|
|
||||||
|
Paste logs or upload screenshots here.
|
||||||
|
|
||||||
|
```txt
|
||||||
|
Paste logs here
|
||||||
66
.gitea/ISSUE_TEMPLATE/bug_report_mobile.md
Normal file
66
.gitea/ISSUE_TEMPLATE/bug_report_mobile.md
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
---
|
||||||
|
name: Bug Report - Mobile
|
||||||
|
about: Report something that is broken or not working correctly
|
||||||
|
title: "[BUG - Mobile] "
|
||||||
|
ref: "main"
|
||||||
|
labels:
|
||||||
|
|
||||||
|
- bug
|
||||||
|
- mobile
|
||||||
|
---
|
||||||
|
|
||||||
|
# Summary
|
||||||
|
|
||||||
|
Briefly explain the issue.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Steps To Reproduce
|
||||||
|
|
||||||
|
1. Go to ...
|
||||||
|
2. Click ...
|
||||||
|
3. Scan ...
|
||||||
|
4. Error occurs ...
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Expected Behavior
|
||||||
|
|
||||||
|
What should have happened?
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Actual Behavior
|
||||||
|
|
||||||
|
What actually happened instead?
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Severity
|
||||||
|
|
||||||
|
- [ ] Low
|
||||||
|
- [ ] Medium
|
||||||
|
- [ ] High
|
||||||
|
- [ ] Critical
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Environment
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
- Production
|
||||||
|
- Development
|
||||||
|
- Zebra Scanner
|
||||||
|
- Mobile Device
|
||||||
|
- Windows Server
|
||||||
|
- Docker
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Logs / Screenshots
|
||||||
|
|
||||||
|
Paste logs or upload screenshots here.
|
||||||
|
|
||||||
|
```txt
|
||||||
|
Paste logs here
|
||||||
67
.gitea/ISSUE_TEMPLATE/bug_report_server.md
Normal file
67
.gitea/ISSUE_TEMPLATE/bug_report_server.md
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
---
|
||||||
|
name: Bug Report - Server
|
||||||
|
about: Report something that is broken or not working correctly
|
||||||
|
title: "[BUG - Server] "
|
||||||
|
ref: "main"
|
||||||
|
labels:
|
||||||
|
|
||||||
|
- bug
|
||||||
|
- server
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Summary
|
||||||
|
|
||||||
|
Briefly explain the issue.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Steps To Reproduce
|
||||||
|
|
||||||
|
1. Go to ...
|
||||||
|
2. Click ...
|
||||||
|
3. Scan ...
|
||||||
|
4. Error occurs ...
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Expected Behavior
|
||||||
|
|
||||||
|
What should have happened?
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Actual Behavior
|
||||||
|
|
||||||
|
What actually happened instead?
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Severity
|
||||||
|
|
||||||
|
- [ ] Low
|
||||||
|
- [ ] Medium
|
||||||
|
- [ ] High
|
||||||
|
- [ ] Critical
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Environment
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
- Production
|
||||||
|
- Development
|
||||||
|
- Zebra Scanner
|
||||||
|
- Mobile Device
|
||||||
|
- Windows Server
|
||||||
|
- Docker
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Logs / Screenshots
|
||||||
|
|
||||||
|
Paste logs or upload screenshots here.
|
||||||
|
|
||||||
|
```txt
|
||||||
|
Paste logs here
|
||||||
1
.gitea/ISSUE_TEMPLATE/config.yaml
Normal file
1
.gitea/ISSUE_TEMPLATE/config.yaml
Normal file
@@ -0,0 +1 @@
|
|||||||
|
blank_issues_enabled: false
|
||||||
48
.gitea/ISSUE_TEMPLATE/enhancement_frontend.md
Normal file
48
.gitea/ISSUE_TEMPLATE/enhancement_frontend.md
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
---
|
||||||
|
name: Enhancement - Frontend
|
||||||
|
about: Improve or refine an existing feature
|
||||||
|
title: "[ENHANCEMENT - Frontend] "
|
||||||
|
ref: "main"
|
||||||
|
labels:
|
||||||
|
|
||||||
|
- enhancement
|
||||||
|
- frontend
|
||||||
|
---
|
||||||
|
|
||||||
|
# Existing Feature
|
||||||
|
|
||||||
|
What current feature or workflow is being improved?
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
- Notifications
|
||||||
|
- Scanner Login
|
||||||
|
- Release Monitor
|
||||||
|
- Printing
|
||||||
|
- Auth
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Proposed Improvement
|
||||||
|
|
||||||
|
Describe the improvement.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Expected Benefit
|
||||||
|
|
||||||
|
Why would this improvement help?
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Impact
|
||||||
|
|
||||||
|
- [ ] Small
|
||||||
|
- [ ] Medium
|
||||||
|
- [ ] Large
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Additional Notes
|
||||||
|
|
||||||
|
Anything else worth mentioning.
|
||||||
48
.gitea/ISSUE_TEMPLATE/enhancement_mobile.md
Normal file
48
.gitea/ISSUE_TEMPLATE/enhancement_mobile.md
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
---
|
||||||
|
name: Enhancement - Mobile
|
||||||
|
about: Improve or refine an existing feature
|
||||||
|
title: "[ENHANCEMENT - Mobile] "
|
||||||
|
ref: "main"
|
||||||
|
labels:
|
||||||
|
|
||||||
|
- enhancement
|
||||||
|
- mobile
|
||||||
|
---
|
||||||
|
|
||||||
|
# Existing Feature
|
||||||
|
|
||||||
|
What current feature or workflow is being improved?
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
- Notifications
|
||||||
|
- Scanner Login
|
||||||
|
- Release Monitor
|
||||||
|
- Printing
|
||||||
|
- Auth
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Proposed Improvement
|
||||||
|
|
||||||
|
Describe the improvement.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Expected Benefit
|
||||||
|
|
||||||
|
Why would this improvement help?
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Impact
|
||||||
|
|
||||||
|
- [ ] Small
|
||||||
|
- [ ] Medium
|
||||||
|
- [ ] Large
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Additional Notes
|
||||||
|
|
||||||
|
Anything else worth mentioning.
|
||||||
48
.gitea/ISSUE_TEMPLATE/enhancement_server.md
Normal file
48
.gitea/ISSUE_TEMPLATE/enhancement_server.md
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
---
|
||||||
|
name: Enhancement - Server
|
||||||
|
about: Improve or refine an existing feature
|
||||||
|
title: "[ENHANCEMENT - Server] "
|
||||||
|
ref: "main"
|
||||||
|
labels:
|
||||||
|
|
||||||
|
- enhancement
|
||||||
|
- server
|
||||||
|
---
|
||||||
|
|
||||||
|
# Existing Feature
|
||||||
|
|
||||||
|
What current feature or workflow is being improved?
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
- Notifications
|
||||||
|
- Scanner Login
|
||||||
|
- Release Monitor
|
||||||
|
- Printing
|
||||||
|
- Auth
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Proposed Improvement
|
||||||
|
|
||||||
|
Describe the improvement.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Expected Benefit
|
||||||
|
|
||||||
|
Why would this improvement help?
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Impact
|
||||||
|
|
||||||
|
- [ ] Small
|
||||||
|
- [ ] Medium
|
||||||
|
- [ ] Large
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Additional Notes
|
||||||
|
|
||||||
|
Anything else worth mentioning.
|
||||||
41
.gitea/ISSUE_TEMPLATE/feature_request_frontend.md
Normal file
41
.gitea/ISSUE_TEMPLATE/feature_request_frontend.md
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
---
|
||||||
|
name: Feature Request - Frontend
|
||||||
|
about: Suggest a brand new feature or module
|
||||||
|
title: "[FEATURE - Frontend] "
|
||||||
|
ref: "main"
|
||||||
|
labels:
|
||||||
|
|
||||||
|
- feature
|
||||||
|
- frontend
|
||||||
|
---
|
||||||
|
|
||||||
|
# Problem Statement
|
||||||
|
|
||||||
|
What problem are you trying to solve?
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Proposed Solution
|
||||||
|
|
||||||
|
Describe the feature you would like added.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Alternatives Considered
|
||||||
|
|
||||||
|
Any other ideas, workarounds, or approaches?
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Priority
|
||||||
|
|
||||||
|
- [ ] Nice to Have
|
||||||
|
- [ ] Medium Priority
|
||||||
|
- [ ] High Priority
|
||||||
|
- [ ] Critical
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Additional Context
|
||||||
|
|
||||||
|
Add mockups, screenshots, examples, or notes here.
|
||||||
41
.gitea/ISSUE_TEMPLATE/feature_request_mobile.md
Normal file
41
.gitea/ISSUE_TEMPLATE/feature_request_mobile.md
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
---
|
||||||
|
name: Feature Request - Mobile
|
||||||
|
about: Suggest a brand new feature or module
|
||||||
|
title: "[FEATURE - Mobile] "
|
||||||
|
ref: "main"
|
||||||
|
labels:
|
||||||
|
|
||||||
|
- feature
|
||||||
|
- mobile
|
||||||
|
---
|
||||||
|
|
||||||
|
# Problem Statement
|
||||||
|
|
||||||
|
What problem are you trying to solve?
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Proposed Solution
|
||||||
|
|
||||||
|
Describe the feature you would like added.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Alternatives Considered
|
||||||
|
|
||||||
|
Any other ideas, workarounds, or approaches?
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Priority
|
||||||
|
|
||||||
|
- [ ] Nice to Have
|
||||||
|
- [ ] Medium Priority
|
||||||
|
- [ ] High Priority
|
||||||
|
- [ ] Critical
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Additional Context
|
||||||
|
|
||||||
|
Add mockups, screenshots, examples, or notes here.
|
||||||
41
.gitea/ISSUE_TEMPLATE/feature_request_server.md
Normal file
41
.gitea/ISSUE_TEMPLATE/feature_request_server.md
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
---
|
||||||
|
name: Feature Request - Server
|
||||||
|
about: Suggest a brand new feature or module
|
||||||
|
title: "[FEATURE - Server] "
|
||||||
|
ref: "main"
|
||||||
|
labels:
|
||||||
|
|
||||||
|
- feature
|
||||||
|
- server
|
||||||
|
---
|
||||||
|
|
||||||
|
# Problem Statement
|
||||||
|
|
||||||
|
What problem are you trying to solve?
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Proposed Solution
|
||||||
|
|
||||||
|
Describe the feature you would like added.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Alternatives Considered
|
||||||
|
|
||||||
|
Any other ideas, workarounds, or approaches?
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Priority
|
||||||
|
|
||||||
|
- [ ] Nice to Have
|
||||||
|
- [ ] Medium Priority
|
||||||
|
- [ ] High Priority
|
||||||
|
- [ ] Critical
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Additional Context
|
||||||
|
|
||||||
|
Add mockups, screenshots, examples, or notes here.
|
||||||
@@ -12,20 +12,20 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Checkout (local)
|
- name: Checkout (local)
|
||||||
run: |
|
run: |
|
||||||
git clone https://git.tuffraid.net/cowch/lst_v3.git .
|
git clone http://10.75.9.150:3100/cowch/lst_v3.git .
|
||||||
git checkout ${{ gitea.sha }}
|
git checkout ${{ gitea.sha }}
|
||||||
|
|
||||||
- name: Login to registry
|
- name: Login to registry
|
||||||
run: echo "${{ secrets.PASSWORD }}" | docker login git.tuffraid.net -u "cowch" --password-stdin
|
run: echo "${{ secrets.PASSWORD }}" | docker login 10.75.9.150:3100 -u "cowch" --password-stdin
|
||||||
|
|
||||||
- name: Build image
|
- name: Build image
|
||||||
run: |
|
run: |
|
||||||
docker build \
|
docker build \
|
||||||
-t git.tuffraid.net/cowch/lst_v3:latest \
|
-t 10.75.9.150:3100/cowch/lst_v3:latest \
|
||||||
-t git.tuffraid.net/cowch/lst_v3:${{ gitea.sha }} \
|
-t 10.75.9.150:3100/cowch/lst_v3:${{ gitea.sha }} \
|
||||||
.
|
.
|
||||||
|
|
||||||
- name: Push
|
- name: Push
|
||||||
run: |
|
run: |
|
||||||
docker push git.tuffraid.net/cowch/lst_v3:latest
|
docker push 10.75.9.150:3100/cowch/lst_v3:latest
|
||||||
docker push git.tuffraid.net/cowch/lst_v3:${{ gitea.sha }}
|
docker push 10.75.9.150:3100/cowch/lst_v3:${{ gitea.sha }}
|
||||||
229
.gitea/workflows/release.yml
Normal file
229
.gitea/workflows/release.yml
Normal file
@@ -0,0 +1,229 @@
|
|||||||
|
name: Release and Build Image
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- "v*"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
release:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
env:
|
||||||
|
# Internal/origin Gitea URL. Do NOT use the Cloudflare fronted URL here.
|
||||||
|
# Examples:
|
||||||
|
# http://gitea.internal.lan:3000
|
||||||
|
# https://gitea-origin.yourdomain.local
|
||||||
|
GITEA_INTERNAL_URL: "http://10.75.9.150:3100" #"https://git.tuffraid.net"
|
||||||
|
|
||||||
|
# Internal/origin registry host. Usually same host as above, but without protocol.
|
||||||
|
# Example:
|
||||||
|
# gitea.internal:3000
|
||||||
|
REGISTRY_HOST: "10.75.9.150:3100" #"git.tuffraid.net"
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Check out repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Prepare release metadata
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
TAG="${GITHUB_REF_NAME:-${GITHUB_REF##refs/tags/}}"
|
||||||
|
VERSION="${TAG#v}"
|
||||||
|
IMAGE_NAME="${REGISTRY_HOST}/${{ gitea.repository }}"
|
||||||
|
|
||||||
|
echo "TAG=$TAG" >> "$GITHUB_ENV"
|
||||||
|
echo "VERSION=$VERSION" >> "$GITHUB_ENV"
|
||||||
|
echo "IMAGE_NAME=$IMAGE_NAME" >> "$GITHUB_ENV"
|
||||||
|
|
||||||
|
if [[ "$TAG" == *-* ]]; then
|
||||||
|
echo "PRERELEASE=true" >> "$GITHUB_ENV"
|
||||||
|
else
|
||||||
|
echo "PRERELEASE=false" >> "$GITHUB_ENV"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Resolved TAG=$TAG"
|
||||||
|
echo "Resolved VERSION=$VERSION"
|
||||||
|
echo "Resolved IMAGE_NAME=$IMAGE_NAME"
|
||||||
|
|
||||||
|
- name: Log in to Gitea container registry
|
||||||
|
shell: bash
|
||||||
|
env:
|
||||||
|
REGISTRY_USERNAME: ${{ secrets.REGISTRY_USERNAME }}
|
||||||
|
REGISTRY_TOKEN: ${{ secrets.RELEASE_TOKEN }}
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
echo "$REGISTRY_TOKEN" | docker login "$REGISTRY_HOST" -u "$REGISTRY_USERNAME" --password-stdin
|
||||||
|
|
||||||
|
- name: Build Docker image
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
docker build \
|
||||||
|
-t "$IMAGE_NAME:$TAG" \
|
||||||
|
-t "$IMAGE_NAME:latest" \
|
||||||
|
.
|
||||||
|
|
||||||
|
- name: Push version tag
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
docker push "$IMAGE_NAME:$TAG"
|
||||||
|
|
||||||
|
- name: Push latest tag
|
||||||
|
if: ${{ !contains(env.TAG, '-') }}
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
docker push "$IMAGE_NAME:latest"
|
||||||
|
|
||||||
|
- name: Push prerelease channel tag
|
||||||
|
if: ${{ contains(env.TAG, '-') }}
|
||||||
|
shell: bash
|
||||||
|
env:
|
||||||
|
TAG: ${{ env.TAG }}
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
CHANNEL="${TAG#*-}"
|
||||||
|
CHANNEL="${CHANNEL%%.*}"
|
||||||
|
|
||||||
|
echo "Resolved prerelease channel: $CHANNEL"
|
||||||
|
|
||||||
|
docker tag "$IMAGE_NAME:$TAG" "$IMAGE_NAME:$CHANNEL"
|
||||||
|
docker push "$IMAGE_NAME:$CHANNEL"
|
||||||
|
|
||||||
|
- name: Extract matching CHANGELOG section
|
||||||
|
shell: bash
|
||||||
|
env:
|
||||||
|
VERSION: ${{ env.VERSION }}
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
python3 - <<'PY'
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
version = os.environ["VERSION"]
|
||||||
|
changelog_path = Path("CHANGELOG.md")
|
||||||
|
|
||||||
|
if not changelog_path.exists():
|
||||||
|
Path("release_body.md").write_text(f"Release {version}\n", encoding="utf-8")
|
||||||
|
raise SystemExit(0)
|
||||||
|
|
||||||
|
text = changelog_path.read_text(encoding="utf-8")
|
||||||
|
|
||||||
|
# Matches headings like:
|
||||||
|
# ## [0.1.0]
|
||||||
|
# ## 0.1.0
|
||||||
|
# ## [0.1.0-alpha.1]
|
||||||
|
pattern = re.compile(
|
||||||
|
rf"^##\s+\[?{re.escape(version)}\]?[^\n]*\n(.*?)(?=^##\s+\[?[^\n]+|\Z)",
|
||||||
|
re.MULTILINE | re.DOTALL,
|
||||||
|
)
|
||||||
|
|
||||||
|
match = pattern.search(text)
|
||||||
|
if match:
|
||||||
|
body = match.group(1).strip()
|
||||||
|
else:
|
||||||
|
body = f"Release {version}"
|
||||||
|
|
||||||
|
if not body:
|
||||||
|
body = f"Release {version}"
|
||||||
|
|
||||||
|
Path("release_body.md").write_text(body + "\n", encoding="utf-8")
|
||||||
|
print("----- release_body.md -----")
|
||||||
|
print(body)
|
||||||
|
print("---------------------------")
|
||||||
|
PY
|
||||||
|
|
||||||
|
- name: Create Gitea release
|
||||||
|
shell: bash
|
||||||
|
env:
|
||||||
|
RELEASE_TOKEN: ${{ secrets.RELEASE_TOKEN }}
|
||||||
|
GITEA_REPOSITORY: ${{ gitea.repository }}
|
||||||
|
GITEA_INTERNAL_URL: ${{ env.GITEA_INTERNAL_URL }}
|
||||||
|
TAG: ${{ env.TAG }}
|
||||||
|
PRERELEASE: ${{ env.PRERELEASE }}
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
python3 - <<'PY'
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import urllib.request
|
||||||
|
import urllib.error
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
tag = os.environ["TAG"]
|
||||||
|
prerelease = os.environ["PRERELEASE"].lower() == "true"
|
||||||
|
server_url = os.environ["GITEA_INTERNAL_URL"].rstrip("/")
|
||||||
|
repo = os.environ["GITEA_REPOSITORY"]
|
||||||
|
token = os.environ["RELEASE_TOKEN"]
|
||||||
|
|
||||||
|
body = Path("release_body.md").read_text(encoding="utf-8").strip()
|
||||||
|
|
||||||
|
# Check if the release already exists for this tag
|
||||||
|
get_url = f"{server_url}/api/v1/repos/{repo}/releases/tags/{tag}"
|
||||||
|
get_req = urllib.request.Request(
|
||||||
|
get_url,
|
||||||
|
method="GET",
|
||||||
|
headers={
|
||||||
|
"Authorization": f"token {token}",
|
||||||
|
"Accept": "application/json",
|
||||||
|
"User-Agent": "lst-release-workflow/1.0",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
existing_release = None
|
||||||
|
try:
|
||||||
|
with urllib.request.urlopen(get_req) as resp:
|
||||||
|
existing_release = json.loads(resp.read().decode("utf-8"))
|
||||||
|
except urllib.error.HTTPError as e:
|
||||||
|
if e.code != 404:
|
||||||
|
details = e.read().decode("utf-8", errors="replace")
|
||||||
|
print("Failed checking existing release:")
|
||||||
|
print(details)
|
||||||
|
raise
|
||||||
|
|
||||||
|
payload = {
|
||||||
|
"tag_name": tag,
|
||||||
|
"name": tag,
|
||||||
|
"body": body,
|
||||||
|
"draft": False,
|
||||||
|
"prerelease": prerelease,
|
||||||
|
}
|
||||||
|
|
||||||
|
data = json.dumps(payload).encode("utf-8")
|
||||||
|
|
||||||
|
if existing_release:
|
||||||
|
release_id = existing_release["id"]
|
||||||
|
url = f"{server_url}/api/v1/repos/{repo}/releases/{release_id}"
|
||||||
|
method = "PATCH"
|
||||||
|
print(f"Release already exists for tag {tag}, updating release id {release_id}")
|
||||||
|
else:
|
||||||
|
url = f"{server_url}/api/v1/repos/{repo}/releases"
|
||||||
|
method = "POST"
|
||||||
|
print(f"No release exists for tag {tag}, creating a new one")
|
||||||
|
|
||||||
|
req = urllib.request.Request(
|
||||||
|
url,
|
||||||
|
data=data,
|
||||||
|
method=method,
|
||||||
|
headers={
|
||||||
|
"Authorization": f"token {token}",
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"Accept": "application/json",
|
||||||
|
"User-Agent": "lst-release-workflow/1.0",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
with urllib.request.urlopen(req) as resp:
|
||||||
|
print(resp.read().decode("utf-8"))
|
||||||
|
except urllib.error.HTTPError as e:
|
||||||
|
details = e.read().decode("utf-8", errors="replace")
|
||||||
|
print("Release create/update failed:")
|
||||||
|
print(details)
|
||||||
|
raise
|
||||||
|
PY
|
||||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -4,11 +4,14 @@ builds
|
|||||||
.includes
|
.includes
|
||||||
.buildNumber
|
.buildNumber
|
||||||
temp
|
temp
|
||||||
|
brunoApi
|
||||||
|
downloads
|
||||||
.scriptCreds
|
.scriptCreds
|
||||||
node-v24.14.0-x64.msi
|
node-v24.14.0-x64.msi
|
||||||
postgresql-17.9-2-windows-x64.exe
|
postgresql-17.9-2-windows-x64.exe
|
||||||
VSCodeUserSetup-x64-1.112.0.exe
|
VSCodeSetup-x64-1.120.0.exe
|
||||||
nssm.exe
|
nssm.exe
|
||||||
|
frontend/.tanstack
|
||||||
|
|
||||||
# Logs
|
# Logs
|
||||||
logs
|
logs
|
||||||
@@ -147,3 +150,4 @@ dist
|
|||||||
.yarn/install-state.gz
|
.yarn/install-state.gz
|
||||||
.pnp.*
|
.pnp.*
|
||||||
|
|
||||||
|
frontend/.tanstack/tmp/2249110e-da91fb0b1b87b6c4cc3e2c2cd25037fd
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
{ "type": "ci", "hidden": false, "section": "📈 Project changes" },
|
{ "type": "ci", "hidden": false, "section": "📈 Project changes" },
|
||||||
{ "type": "build", "hidden": false, "section": "📈 Project Builds" }
|
{ "type": "build", "hidden": false, "section": "📈 Project Builds" }
|
||||||
],
|
],
|
||||||
"commitUrlFormat": "https://git.tuffraid.net/cowch/lst/commits/{{hash}}",
|
"commitUrlFormat": "https://git.tuffraid.net/cowch/lst_v3/commits/{{hash}}",
|
||||||
"compareUrlFormat": "https://git.tuffraid.net/cowch/lst/compare/{{previousTag}}...{{currentTag}}",
|
"compareUrlFormat": "https://git.tuffraid.net/cowch/lst_v3/compare/{{previousTag}}...{{currentTag}}",
|
||||||
"header": "# All Changes to LST can be found below.\n"
|
"header": "# All Changes to LST can be found below.\n"
|
||||||
}
|
}
|
||||||
8
.vscode/settings.json
vendored
8
.vscode/settings.json
vendored
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"editor.defaultFormatter": "biomejs.biome",
|
"editor.defaultFormatter": "biomejs.biome",
|
||||||
"workbench.colorTheme": "Default Dark+",
|
"workbench.colorTheme": "Dark+",
|
||||||
"terminal.integrated.env.windows": {},
|
"terminal.integrated.env.windows": {},
|
||||||
"editor.formatOnSave": true,
|
"editor.formatOnSave": true,
|
||||||
"typescript.preferences.importModuleSpecifier": "relative",
|
"typescript.preferences.importModuleSpecifier": "relative",
|
||||||
@@ -54,8 +54,10 @@
|
|||||||
"alpla",
|
"alpla",
|
||||||
"alplamart",
|
"alplamart",
|
||||||
"alplaprod",
|
"alplaprod",
|
||||||
|
"alplapurchase",
|
||||||
"bookin",
|
"bookin",
|
||||||
"Datamart",
|
"Datamart",
|
||||||
|
"dotenvx",
|
||||||
"dyco",
|
"dyco",
|
||||||
"intiallally",
|
"intiallally",
|
||||||
"manadatory",
|
"manadatory",
|
||||||
@@ -63,12 +65,14 @@
|
|||||||
"onnotice",
|
"onnotice",
|
||||||
"opendock",
|
"opendock",
|
||||||
"opendocks",
|
"opendocks",
|
||||||
|
"palletizer",
|
||||||
"ppoo",
|
"ppoo",
|
||||||
"preseed",
|
"preseed",
|
||||||
"prodlabels",
|
"prodlabels",
|
||||||
"prolink",
|
"prolink",
|
||||||
"Skelly",
|
"Skelly",
|
||||||
"trycatch"
|
"trycatch",
|
||||||
|
"whse"
|
||||||
],
|
],
|
||||||
"gitea.token": "8456def90e1c651a761a8711763d6ef225d6b2db",
|
"gitea.token": "8456def90e1c651a761a8711763d6ef225d6b2db",
|
||||||
"gitea.instanceURL": "https://git.tuffraid.net",
|
"gitea.instanceURL": "https://git.tuffraid.net",
|
||||||
|
|||||||
480
CHANGELOG.md
480
CHANGELOG.md
@@ -1,14 +1,478 @@
|
|||||||
# lst_v3
|
# All Changes to LST can be found below.
|
||||||
|
|
||||||
## 1.0.2-alpha.0
|
## [0.1.0-alpha.3](https://git.tuffraid.net/cowch/lst_v3/compare/v0.1.0-alpha.2...v0.1.0-alpha.3) (2026-06-10)
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- build stuff
|
### 🌟 Enhancements
|
||||||
- external url added for docker
|
|
||||||
|
|
||||||
## 1.0.1
|
* **eom:** migrated eom endpoints from old version validated working ([e909e8d](https://git.tuffraid.net/cowch/lst_v3/commits/e909e8deecb54a3e4c39789609b0aa7435b9e08a))
|
||||||
|
* **mobile:** added auto download of latest ([6d0fb8a](https://git.tuffraid.net/cowch/lst_v3/commits/6d0fb8aee45c8b5c56ccd7d8a010e1dc803408bf))
|
||||||
|
* **mobile:** dock door scanning backend added ([fe0b157](https://git.tuffraid.net/cowch/lst_v3/commits/fe0b1573f3ba6fd220f181088b994588c52af139)), closes [#12](https://git.tuffraid.net/cowch/lst_v3/issues/12)
|
||||||
|
* **opendock:** added delete button in the article tab ([4464cea](https://git.tuffraid.net/cowch/lst_v3/commits/4464cea022ba48744b884b83fef0fc3f3421dea5)), closes [#23](https://git.tuffraid.net/cowch/lst_v3/issues/23)
|
||||||
|
* **opendock:** added in a new article link system ([bb7931d](https://git.tuffraid.net/cowch/lst_v3/commits/bb7931d6c8d0f5ce4065955491e9ee1247b5e92d))
|
||||||
|
* **warehousing:** ppoo monitoring added ([8b07694](https://git.tuffraid.net/cowch/lst_v3/commits/8b076949a7f8e723bc87619f729082d2c1991b2d)), closes [#13](https://git.tuffraid.net/cowch/lst_v3/issues/13)
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- cf18e94: core stuff
|
### 🐛 Bug fixes
|
||||||
|
|
||||||
|
* **app:** type in the templates... they all looked the same ([2a648f6](https://git.tuffraid.net/cowch/lst_v3/commits/2a648f63064a300b0a2888bae3322b857af6a238))
|
||||||
|
* **dock door scanning:** correction to how the data is posted ([2f49573](https://git.tuffraid.net/cowch/lst_v3/commits/2f495739e653c462eff7f10ff6343d9572f25563))
|
||||||
|
* **dockscanner:** removed console log ([4249e90](https://git.tuffraid.net/cowch/lst_v3/commits/4249e90307ba1a1992753803e1dc3ab7dd7ac95e))
|
||||||
|
* **eom:** removed un needed imports ([05c553e](https://git.tuffraid.net/cowch/lst_v3/commits/05c553e9279c6e8384d61073781bf915733b0ab5))
|
||||||
|
* **logistics:** historical checks for no data errors when feature is activeed ([4f848bb](https://git.tuffraid.net/cowch/lst_v3/commits/4f848bb649f350c9d370daa09c6fc48f7b76e2e2))
|
||||||
|
* **mobile users:** corrected and endpoint that prevented us from change the pin number ([347edb7](https://git.tuffraid.net/cowch/lst_v3/commits/347edb7078fb4ce959975cd968a6f026bacc98bf))
|
||||||
|
* **mobile:** scan log incorrect user ref ([9c0ef1f](https://git.tuffraid.net/cowch/lst_v3/commits/9c0ef1f5dfa13e8f7e1f72d47d0d5842b3da3c87))
|
||||||
|
* **mobile:** temp removed the autodownload as its causing issues ([3ef0f23](https://git.tuffraid.net/cowch/lst_v3/commits/3ef0f230ddbaef4c3d738cc531e7afee25b210dd))
|
||||||
|
* **mobile:** ui over lapping ([db28635](https://git.tuffraid.net/cowch/lst_v3/commits/db28635c8c260d0f378e109755d32201acdb2328)), closes [#25](https://git.tuffraid.net/cowch/lst_v3/issues/25)
|
||||||
|
* **notifications:** missed api converstion in the front end for updating ([5281118](https://git.tuffraid.net/cowch/lst_v3/commits/52811185965cb0fe4c9b42e73c447b67e917a5ca))
|
||||||
|
* **opendock:** changed the header of delete to be properly named ([865f280](https://git.tuffraid.net/cowch/lst_v3/commits/865f280cfaf82a7126ca4b57d658aed50cf19a73)), closes [#23](https://git.tuffraid.net/cowch/lst_v3/issues/23)
|
||||||
|
* **opendock:** correction to article link success on delete ([a717260](https://git.tuffraid.net/cowch/lst_v3/commits/a717260b8d5cf72534b055721de2b79901506875)), closes [#23](https://git.tuffraid.net/cowch/lst_v3/issues/23)
|
||||||
|
* **opendock:** ref wrong field oops ([f635415](https://git.tuffraid.net/cowch/lst_v3/commits/f635415b751e11d0e7beb557d18b83915155428a))
|
||||||
|
* **scanner:** corrected teh endpoint to delete the user if needed ([6dd1a5b](https://git.tuffraid.net/cowch/lst_v3/commits/6dd1a5b332a853f0e2c7226ac1f64e403ef752d7))
|
||||||
|
|
||||||
|
|
||||||
|
### 📚 Documentation
|
||||||
|
|
||||||
|
* **app:** updated last updated to the readme to show current progress ([78f7b8a](https://git.tuffraid.net/cowch/lst_v3/commits/78f7b8a179d078fc1b2740fd2bf4af71a3df8292))
|
||||||
|
* **app:** updated readme ([1a3d8a7](https://git.tuffraid.net/cowch/lst_v3/commits/1a3d8a7ddcb7a2e51da4b77968ffdefb7644a0b1))
|
||||||
|
|
||||||
|
|
||||||
|
### 🛠️ Code Refactor
|
||||||
|
|
||||||
|
* **backend:** dock door scanning socket and perms ([f8335f5](https://git.tuffraid.net/cowch/lst_v3/commits/f8335f5217c339a7dc09883fa8ba9c60aa4c35b1))
|
||||||
|
* **datamart:** if added to query will include plant token now ([7d722c4](https://git.tuffraid.net/cowch/lst_v3/commits/7d722c4aac16bc1d2cfbfbc70539783f34003f77))
|
||||||
|
* **db:** added in notifications vs pulling from the db makes it easier on the system ([706ab8b](https://git.tuffraid.net/cowch/lst_v3/commits/706ab8b448aafb81da94a76fbf8d3d400cba616b))
|
||||||
|
* **db:** added timezone check in so it comes over correct based on the backend timezone ([2558b2e](https://git.tuffraid.net/cowch/lst_v3/commits/2558b2e5bb68be7a3f46de09bb509c99423adeb6))
|
||||||
|
* **dock door scanning:** fixes and final writes for the intial trial went smooth ([86e1237](https://git.tuffraid.net/cowch/lst_v3/commits/86e1237509b81722dee7b42762d0bfced8d26fa3))
|
||||||
|
* **dock scanner:** more work on dock scanner and semi finished ([6eaae0f](https://git.tuffraid.net/cowch/lst_v3/commits/6eaae0f5378e39c4002dadd8325833698dd960e7))
|
||||||
|
* **dockscanning:** more work on the dock door scanning ([7671172](https://git.tuffraid.net/cowch/lst_v3/commits/7671172d975355b5d245a482b481726b49153578)), closes [#12](https://git.tuffraid.net/cowch/lst_v3/issues/12)
|
||||||
|
* **logger:** included error in the stack version so we dont have to remove it all ([da87e2e](https://git.tuffraid.net/cowch/lst_v3/commits/da87e2e1d322a45ca9a7b500d77499fe4c7b999e))
|
||||||
|
* **logs:** refactored to show the submodule and stack as well to make it more easy to watch ([45a0dee](https://git.tuffraid.net/cowch/lst_v3/commits/45a0dee9caae14df639439b905fa81b340791197))
|
||||||
|
* **logs:** socket io setup to be properly logging ([9ff428f](https://git.tuffraid.net/cowch/lst_v3/commits/9ff428f5ea1051e62521632947092f74eb93944d))
|
||||||
|
* **mobile:** intial addin of dockdoor scanning on mobile ([2a35381](https://git.tuffraid.net/cowch/lst_v3/commits/2a35381fe400fc46e6f10c4e72c3ab9ac435e0e5))
|
||||||
|
* **mobile:** moved logout to the tab bar ([bcdf956](https://git.tuffraid.net/cowch/lst_v3/commits/bcdf9566bcf721a085f46ce3befe783eb7b91949))
|
||||||
|
* **mobile:** new error found and added in ([7bbdd4e](https://git.tuffraid.net/cowch/lst_v3/commits/7bbdd4e5552889434aff81ed10ea7c64174f7d34))
|
||||||
|
* **mobile:** setup - added button to go home as it caused confustion ([c15ee07](https://git.tuffraid.net/cowch/lst_v3/commits/c15ee070e7057ad8a5e3d42f51c230d680db9e21))
|
||||||
|
* **new role:** added in warehouse role ([55418e2](https://git.tuffraid.net/cowch/lst_v3/commits/55418e2f098193b0891129a19532608dce2abd9c))
|
||||||
|
* **opend dock:** added in default dock so it uses the default setup as a backup ([8fc3129](https://git.tuffraid.net/cowch/lst_v3/commits/8fc3129f7d687d45dc242aca9a6e71f43d0352ab))
|
||||||
|
* **opendock:** added in check for really using the article link ([cfc497c](https://git.tuffraid.net/cowch/lst_v3/commits/cfc497c1f2254dc12a6e7bb15cfc0dce2a64c1e6))
|
||||||
|
* **opendock:** added in proper complete and ignore of picksheets ([3ad84da](https://git.tuffraid.net/cowch/lst_v3/commits/3ad84dab71ceea081efc824e97d075c6f33807ad))
|
||||||
|
* **opendock:** added some new goodies to the app to help manage releases ([c0a7d4a](https://git.tuffraid.net/cowch/lst_v3/commits/c0a7d4a1252e3f14240d14288a3ac4add44a6463))
|
||||||
|
* **opendock:** changed the article to look at the label to match all plants ([a2d9a6c](https://git.tuffraid.net/cowch/lst_v3/commits/a2d9a6c127cae3b6dc5beb1a996558ab1383fa8c))
|
||||||
|
* **opendock:** changed the subModule for better logging ([7c4c5f9](https://git.tuffraid.net/cowch/lst_v3/commits/7c4c5f980a15e13e2a43a8583a652622370ec1dd))
|
||||||
|
* **opendock:** if load type is drop we will not do anything unless its already scanned ([5a7046b](https://git.tuffraid.net/cowch/lst_v3/commits/5a7046bacd9323627a37c89d7a9a329383e8b1be))
|
||||||
|
* **server builds:** added in proper logging will still need fine tuned though ([15c939e](https://git.tuffraid.net/cowch/lst_v3/commits/15c939ebe8b808b3a9a46a4c1e62fb0488d3649b))
|
||||||
|
* **servers:** all remaining servers added ([5f2148f](https://git.tuffraid.net/cowch/lst_v3/commits/5f2148f5f0f7688cf71e1dad99d4a5f6c34241dd))
|
||||||
|
* **socket.io:** complete rewrite to manage dynamic rooms and seeding better ([9440b44](https://git.tuffraid.net/cowch/lst_v3/commits/9440b44f3bb1842d0e6ffc479d340ae9c5b84656))
|
||||||
|
* **socketio:** changes that if we are in dock room to constant reseed the room ([8fcb2c6](https://git.tuffraid.net/cowch/lst_v3/commits/8fcb2c66ed25c746dc1d63c5290e11fe6a56328e))
|
||||||
|
* **times:** added a better view for times as we save all db as there respective timezone ([40edbc3](https://git.tuffraid.net/cowch/lst_v3/commits/40edbc3eae9000afea447f81ec127884712b38da))
|
||||||
|
* **warehouse:** fixed ppoo check to match the new changes ([2e460c7](https://git.tuffraid.net/cowch/lst_v3/commits/2e460c7f8aeb7de1d641c42357001704a4d8d8b2))
|
||||||
|
|
||||||
|
|
||||||
|
### 📝 Testing Code
|
||||||
|
|
||||||
|
* **test:** added in vitest to start doing more testing before deploying ([e6b92ae](https://git.tuffraid.net/cowch/lst_v3/commits/e6b92aeb109394691dd2a7d548ddadd02769a159))
|
||||||
|
|
||||||
|
|
||||||
|
### 📈 Project changes
|
||||||
|
|
||||||
|
* **app:** added more labels to the templates ([69a9a81](https://git.tuffraid.net/cowch/lst_v3/commits/69a9a81a889aca010ec8835ffab7084f7cd4a9b0))
|
||||||
|
* **app:** changed the issues templates to be easier to add to the part of the app needed ([188fdd0](https://git.tuffraid.net/cowch/lst_v3/commits/188fdd0eb7c6bedf52ae1dcc9109a0fdbe969a21))
|
||||||
|
|
||||||
|
## [0.1.0-alpha.2](https://git.tuffraid.net/cowch/lst_v3/compare/v0.1.0-alpha.1...v0.1.0-alpha.2) (2026-05-23)
|
||||||
|
|
||||||
|
|
||||||
|
### ⚠ BREAKING CHANGES
|
||||||
|
|
||||||
|
* **build:** gives a rabbit hole error
|
||||||
|
|
||||||
|
### 🌟 Enhancements
|
||||||
|
|
||||||
|
* **opendock:** added in new article link setup for fine tuning how od works ([3892111](https://git.tuffraid.net/cowch/lst_v3/commits/389211186f00cb8a6fdd5de092a944fa7e5898aa))
|
||||||
|
* **opendock:** scheduing updates ([1840ac5](https://git.tuffraid.net/cowch/lst_v3/commits/1840ac5e580c726c452216480b6e14e7c52a0f35)), closes [#23](https://git.tuffraid.net/cowch/lst_v3/issues/23)
|
||||||
|
|
||||||
|
|
||||||
|
### 🐛 Bug fixes
|
||||||
|
|
||||||
|
* **build:** crashes when files changed :( ([1f6637c](https://git.tuffraid.net/cowch/lst_v3/commits/1f6637c512dcd465c5000f8d1baaa8e76766edc1)), closes [#24](https://git.tuffraid.net/cowch/lst_v3/issues/24)
|
||||||
|
* **docs:** wrong location for images ([057a570](https://git.tuffraid.net/cowch/lst_v3/commits/057a570e43a8e1763652d98244c90999c3fccd42))
|
||||||
|
* **mobile:** correction to axios helper ([ecfbda9](https://git.tuffraid.net/cowch/lst_v3/commits/ecfbda9036f3d68c93e9c1d81021efa8093f18e2))
|
||||||
|
* **sql queries:** disable job would error so now we will check if it exists before trying to kill it ([636daae](https://git.tuffraid.net/cowch/lst_v3/commits/636daaed0adeda908e7e850a4f5bb20d7bbef861))
|
||||||
|
|
||||||
|
|
||||||
|
### 📚 Documentation
|
||||||
|
|
||||||
|
* **mobile:** updated imgs to be a little smaller ([3a27fd8](https://git.tuffraid.net/cowch/lst_v3/commits/3a27fd8542c3fa4ad5520532c2f10c6e3eaa951c))
|
||||||
|
|
||||||
|
|
||||||
|
### 🛠️ Code Refactor
|
||||||
|
|
||||||
|
* **mobile:** added missing error to the scanner ([52974aa](https://git.tuffraid.net/cowch/lst_v3/commits/52974aa0b4f21431777b773200a57f185b4babd2))
|
||||||
|
* **opendock:** changes to how we do the intergration scheduling ([cd67c4d](https://git.tuffraid.net/cowch/lst_v3/commits/cd67c4de80b6f0244afc639a7360e9dc2ba97a21)), closes [#23](https://git.tuffraid.net/cowch/lst_v3/issues/23)
|
||||||
|
|
||||||
|
|
||||||
|
### 📈 Project changes
|
||||||
|
|
||||||
|
* **docker:** changes to the ignore file ([71c8306](https://git.tuffraid.net/cowch/lst_v3/commits/71c83062cb644796ebbfd845084ac6c019206faa))
|
||||||
|
|
||||||
|
## [0.1.0-alpha.1](https://git.tuffraid.net/cowch/lst_v3/compare/v0.1.0-alpha.0...v0.1.0-alpha.1) (2026-05-19)
|
||||||
|
|
||||||
|
|
||||||
|
### 🐛 Bug fixes
|
||||||
|
|
||||||
|
* **notifications:** reprinting ([c8931c7](https://git.tuffraid.net/cowch/lst_v3/commits/c8931c7249b8f532b5dd37df3271da98f14ee710)), closes [#20](https://git.tuffraid.net/cowch/lst_v3/issues/20)
|
||||||
|
* **settings:** failed build due it dormant import ([a7bb364](https://git.tuffraid.net/cowch/lst_v3/commits/a7bb364a2fd49d96b6195aca0cd58ba57c58f3a6))
|
||||||
|
|
||||||
|
|
||||||
|
### 🛠️ Code Refactor
|
||||||
|
|
||||||
|
* **servers:** changed activeity around and trying to make use of it ([514a44b](https://git.tuffraid.net/cowch/lst_v3/commits/514a44b6de3efe8dd8b308d98bdbc82e31ed8427))
|
||||||
|
* **users:** lots of auth stuff added to make it more easy to manage users ([047cc7c](https://git.tuffraid.net/cowch/lst_v3/commits/047cc7cdf036c39a89a0b87ab59dda8328efe0c0))
|
||||||
|
|
||||||
|
|
||||||
|
### 📈 Project changes
|
||||||
|
|
||||||
|
* **app:** added in chokidar to monitor folders ([8dc4d70](https://git.tuffraid.net/cowch/lst_v3/commits/8dc4d70e2827f0a40d2f54886fd757c8a2dc5ac4))
|
||||||
|
|
||||||
|
## [0.1.0-alpha.0](https://git.tuffraid.net/cowch/lst_v3/compare/v0.0.2-alpha.10...v0.1.0-alpha.0) (2026-05-14)
|
||||||
|
|
||||||
|
|
||||||
|
### ⚠ BREAKING CHANGES
|
||||||
|
|
||||||
|
* **app:** moved teh middleware to call the api hits to the main app and removed from
|
||||||
|
everywhere else
|
||||||
|
|
||||||
|
### 🌟 Enhancements
|
||||||
|
|
||||||
|
* **notification:** migrated sql cleanup ([3e66c39](https://git.tuffraid.net/cowch/lst_v3/commits/3e66c3920d65cee7a0a788f3910c1ddf09a07805))
|
||||||
|
* **scan users:** added in the place to add the new scanner users in ([ce9d8ea](https://git.tuffraid.net/cowch/lst_v3/commits/ce9d8eaaf5bcb8f53ea4bdc191347df8d589fdfa))
|
||||||
|
|
||||||
|
|
||||||
|
### 🐛 Bug fixes
|
||||||
|
|
||||||
|
* **anaylistics:** changes to the daily section so it populates correctly now ([f5bae2c](https://git.tuffraid.net/cowch/lst_v3/commits/f5bae2c0c24b85423c5c421164d94d58159ff70a))
|
||||||
|
* **anaylitics:** unique values were missing causing a weird crash ([13718fe](https://git.tuffraid.net/cowch/lst_v3/commits/13718fe70293c039bd1d9bf8cf395852e6ea6c21))
|
||||||
|
* **app:** emit.maxlistener issue ([7c31b43](https://git.tuffraid.net/cowch/lst_v3/commits/7c31b43a4a313237fa63c0c9bbc3690b74f63a6f)), closes [#18](https://git.tuffraid.net/cowch/lst_v3/issues/18)
|
||||||
|
* **app:** required auth was in wrong spot caused entire app to want you logged in ([d2a9e1d](https://git.tuffraid.net/cowch/lst_v3/commits/d2a9e1d1107ea05f13725e9528bc6ab1566c8efb))
|
||||||
|
* **notification subs:** made it so only acitve show ([2616acf](https://git.tuffraid.net/cowch/lst_v3/commits/2616acf106530f5c5ee04d1b79033795cf06b42d)), closes [#14](https://git.tuffraid.net/cowch/lst_v3/issues/14)
|
||||||
|
* **scanner:** changed to not crash on logging ([0de2579](https://git.tuffraid.net/cowch/lst_v3/commits/0de25799420f38a293ee9acc70eb36e3287145c4)), closes [#19](https://git.tuffraid.net/cowch/lst_v3/issues/19)
|
||||||
|
* **scanner:** fixes to be more clear that you need to scan a command to start ([0575879](https://git.tuffraid.net/cowch/lst_v3/commits/05758791be7a50e90b5da05d4977e618c311f654)), closes [#16](https://git.tuffraid.net/cowch/lst_v3/issues/16)
|
||||||
|
* **scanner:** logut out corrections ([85e96f5](https://git.tuffraid.net/cowch/lst_v3/commits/85e96f5ed13a81fd466c6bbff31c539244750838)), closes [#17](https://git.tuffraid.net/cowch/lst_v3/issues/17)
|
||||||
|
* **table:** skelly table causing hydration error ([1bbf5c2](https://git.tuffraid.net/cowch/lst_v3/commits/1bbf5c2a4955107a36ace05595886d19cc8e64f4))
|
||||||
|
|
||||||
|
|
||||||
|
### 📝 Chore
|
||||||
|
|
||||||
|
* **mobile:** removed console log that shouldnt be there ([9631736](https://git.tuffraid.net/cowch/lst_v3/commits/9631736e263ed00189f8118f686690cab25f09d3))
|
||||||
|
|
||||||
|
|
||||||
|
### 📚 Documentation
|
||||||
|
|
||||||
|
* **scanner:** added in instructions on how to update the scanner ([b0c7277](https://git.tuffraid.net/cowch/lst_v3/commits/b0c7277a6cdb5becec3a994ea1d5cc2d7b0326aa))
|
||||||
|
* **scanner:** added in westbend and dayton commands to scan for updates ([eb9d77c](https://git.tuffraid.net/cowch/lst_v3/commits/eb9d77c3d4767fd961759662ef44c3e09e00946b))
|
||||||
|
|
||||||
|
|
||||||
|
### 🛠️ Code Refactor
|
||||||
|
|
||||||
|
* **api:** changes to call a helper api to quit and redirect if needed ([c64392f](https://git.tuffraid.net/cowch/lst_v3/commits/c64392f45769108aa4134c7fd865f3d4bc664179))
|
||||||
|
* **app:** changed ways we get data so we can have better reasons why app no worky ([30ff7b7](https://git.tuffraid.net/cowch/lst_v3/commits/30ff7b71d9d159ced263a5330d70d53b97393157))
|
||||||
|
* **mobile:** scanner response ([a9c6925](https://git.tuffraid.net/cowch/lst_v3/commits/a9c69250bd3272ad682751e41b671c119cb678f1)), closes [#16](https://git.tuffraid.net/cowch/lst_v3/issues/16)
|
||||||
|
* **scanner:** logging - version of app ([d61be61](https://git.tuffraid.net/cowch/lst_v3/commits/d61be61f4433a2be2678d724f4724301931614c9))
|
||||||
|
* **scanner:** more scanner admin stuff ([eb950d2](https://git.tuffraid.net/cowch/lst_v3/commits/eb950d2c29f692b806d5cc4ab7014bd59a726a8d))
|
||||||
|
* **scanner:** removed 69 as an option lol ([e7af3d1](https://git.tuffraid.net/cowch/lst_v3/commits/e7af3d11824b42915cf6789f9c508a727511d678))
|
||||||
|
* **servers:** server name now links to the actual server:port ([ebf1060](https://git.tuffraid.net/cowch/lst_v3/commits/ebf1060475d37627b371bc6c79507cdde411600b))
|
||||||
|
* **users:** some user refactoring and configuring ([342a97f](https://git.tuffraid.net/cowch/lst_v3/commits/342a97f6b1054443b9126186d2c7872fbd8586da))
|
||||||
|
|
||||||
|
|
||||||
|
### 📈 Project changes
|
||||||
|
|
||||||
|
* **mobile:** added in ehs config to make it more easy for users to update the scanner app on the fly ([dc95e50](https://git.tuffraid.net/cowch/lst_v3/commits/dc95e50a8412b4fbc629fd44fcb5c77295583ca8))
|
||||||
|
* **notification:** removal of more console logs that shouldnt be here ([51026e3](https://git.tuffraid.net/cowch/lst_v3/commits/51026e3e2cce4d7f696d26aae305b3fd221f5bb1))
|
||||||
|
* **servives:** helpers moved around ([e9e73c8](https://git.tuffraid.net/cowch/lst_v3/commits/e9e73c829c2e5726650c0ac7ffa6a9055dbc982b))
|
||||||
|
* **updateserver:** changes to actually add the new env stuff ([bcb7773](https://git.tuffraid.net/cowch/lst_v3/commits/bcb7773007894ac2f85fe2a0b47faf14c7b474ad))
|
||||||
|
|
||||||
|
## [0.0.2-alpha.10](https://git.tuffraid.net/cowch/lst_v3/compare/v0.0.2-alpha.9...v0.0.2-alpha.10) (2026-05-08)
|
||||||
|
|
||||||
|
|
||||||
|
### 🌟 Enhancements
|
||||||
|
|
||||||
|
* **analytics:** added in backend anaylitics ([9edafc9](https://git.tuffraid.net/cowch/lst_v3/commits/9edafc9d2810f339d197c10dfc6a037b3352d81f))
|
||||||
|
* **api hits:** added in api hits for monitoring ([2721bb2](https://git.tuffraid.net/cowch/lst_v3/commits/2721bb2a3bf1f829591d26a0716f74c4f7fc0c79))
|
||||||
|
* **scanner:** added in lanechecks ([87803ee](https://git.tuffraid.net/cowch/lst_v3/commits/87803eed43069b73de3f66e6524bb45da9c46334))
|
||||||
|
|
||||||
|
|
||||||
|
### 🐛 Bug fixes
|
||||||
|
|
||||||
|
* **scan user:** typo ([d8869b1](https://git.tuffraid.net/cowch/lst_v3/commits/d8869b103b80e4208b3928a370a9524ef33d25cd))
|
||||||
|
* **schema:** typo in add_date ([7d7d991](https://git.tuffraid.net/cowch/lst_v3/commits/7d7d9911223905d6767b87d2471b6607a90f1ea7))
|
||||||
|
* **spelling:** corrected the spelling on the file ([0459cd7](https://git.tuffraid.net/cowch/lst_v3/commits/0459cd788aaad6ac54a67e23f798ce5e5a437394))
|
||||||
|
|
||||||
|
|
||||||
|
### 📝 Chore
|
||||||
|
|
||||||
|
* **file:** name changes.. spelled wrong ([5fa70da](https://git.tuffraid.net/cowch/lst_v3/commits/5fa70da90ca290ee45088e9c8eb06ba48a6677af))
|
||||||
|
* **server:** removed a console log that shouldnt be there ([1dba774](https://git.tuffraid.net/cowch/lst_v3/commits/1dba774abc54bf20850c3f26d49926e86d59712d))
|
||||||
|
|
||||||
|
|
||||||
|
### 🛠️ Code Refactor
|
||||||
|
|
||||||
|
* **analyitics:** finished analyitics as a base ([4424c74](https://git.tuffraid.net/cowch/lst_v3/commits/4424c742d24dc230b2bc1782e33535184c378cf0))
|
||||||
|
* **scan:** bump in build and style update ([505d7ce](https://git.tuffraid.net/cowch/lst_v3/commits/505d7cea5d2f52fc4a3ec1edff1878be703c4034))
|
||||||
|
* **scanner:** added toasts in to make it look better ([edb3668](https://git.tuffraid.net/cowch/lst_v3/commits/edb366854825f4c24ab5d77cf88759465d067f00))
|
||||||
|
|
||||||
|
|
||||||
|
### 📝 Testing Code
|
||||||
|
|
||||||
|
* **scanusers:** added in scan users as test ([1ff5e50](https://git.tuffraid.net/cowch/lst_v3/commits/1ff5e5032f9c8bf81f972dc99d6c86ba8d3936c6))
|
||||||
|
|
||||||
|
|
||||||
|
### 📈 Project changes
|
||||||
|
|
||||||
|
* **template:** bug in getting the template to work correctly ([e9b0101](https://git.tuffraid.net/cowch/lst_v3/commits/e9b01010954624aed738cd6e4b82fccbba195cc4))
|
||||||
|
* **templates:** added in templates for the repo to make it more easy to manage and add in new ideas ([ca885fb](https://git.tuffraid.net/cowch/lst_v3/commits/ca885fb01a3c8bc22694c2e05269c43fcd4de70e))
|
||||||
|
* **templates:** force useage ([6d8499b](https://git.tuffraid.net/cowch/lst_v3/commits/6d8499bfb85f7b9131b1ec7b31a17c4256d0f0cf))
|
||||||
|
|
||||||
|
## [0.0.2-alpha.9](https://git.tuffraid.net/cowch/lst_v3/compare/v0.0.2-alpha.8...v0.0.2-alpha.9) (2026-05-06)
|
||||||
|
|
||||||
|
|
||||||
|
### 🛠️ Code Refactor
|
||||||
|
|
||||||
|
* **mobile:** valildation of server after each scan ([4ca74de](https://git.tuffraid.net/cowch/lst_v3/commits/4ca74de2795cea7244e38697d16afe2822164ed6))
|
||||||
|
* **scanner:** added in running number ([a38e2e0](https://git.tuffraid.net/cowch/lst_v3/commits/a38e2e033977b725538e9a9046098d94194d549e))
|
||||||
|
* **scanner:** finished login stuff for current routes ([1241253](https://git.tuffraid.net/cowch/lst_v3/commits/12412536d10981013053c39d156c6c9cb0babd11))
|
||||||
|
|
||||||
|
|
||||||
|
### 📝 Testing Code
|
||||||
|
|
||||||
|
* **scanner:** lane check ([d99449d](https://git.tuffraid.net/cowch/lst_v3/commits/d99449ddc4e2777c1b0fe9189ba0a7c01fe1dd8f))
|
||||||
|
|
||||||
|
|
||||||
|
### 📈 Project Builds
|
||||||
|
|
||||||
|
* **builds:** changed to ip as its on the same server ([3552ca3](https://git.tuffraid.net/cowch/lst_v3/commits/3552ca31f9f7b3bcbe557a145e7eb154bfdae79c))
|
||||||
|
* **release:** bypass cloudflare upload limit ([b578f05](https://git.tuffraid.net/cowch/lst_v3/commits/b578f05d6482f9b6f30febeee6ab0b708a70f68b))
|
||||||
|
|
||||||
|
## [0.0.2-alpha.8](https://git.tuffraid.net/cowch/lst_v3/compare/v0.0.2-alpha.7...v0.0.2-alpha.8) (2026-05-06)
|
||||||
|
|
||||||
|
|
||||||
|
### 🌟 Enhancements
|
||||||
|
|
||||||
|
* **mobile:** auth added in ([ba30281](https://git.tuffraid.net/cowch/lst_v3/commits/ba30281e59040513a036fb7413e372457d04a7c8))
|
||||||
|
|
||||||
|
## [0.0.2-alpha.7](https://git.tuffraid.net/cowch/lst_v3/compare/v0.0.2-alpha.6...v0.0.2-alpha.7) (2026-05-06)
|
||||||
|
|
||||||
|
|
||||||
|
### 🌟 Enhancements
|
||||||
|
|
||||||
|
* **intial auth:** intial auth setup for the scanner ([cd13360](https://git.tuffraid.net/cowch/lst_v3/commits/cd13360cfb931daca50fd7b111e1c8f8ab09a909))
|
||||||
|
* **mobile:** new route for the ehs launcher ([649ae1e](https://git.tuffraid.net/cowch/lst_v3/commits/649ae1ee9f245a9b5d308ea8a636357bf72b1e34))
|
||||||
|
* **mobile:** shadcn like and tailwind added to make things look yummy ([7d2f048](https://git.tuffraid.net/cowch/lst_v3/commits/7d2f048932b77269568149de34351840b75486e2))
|
||||||
|
* **mobile:** update notifications and more error handling added ([30ffd84](https://git.tuffraid.net/cowch/lst_v3/commits/30ffd843c725da79ed035e2d9564f60a6babcda8))
|
||||||
|
* **scanner:** more work on the scanner and can now scan to prod no lst right now ([77b4533](https://git.tuffraid.net/cowch/lst_v3/commits/77b4533dea8314fd4fb81a597995cabd041fe188))
|
||||||
|
* **servers:** added iowa ebm ([8446dbc](https://git.tuffraid.net/cowch/lst_v3/commits/8446dbc955462235b9df35c501354761661e4f6a))
|
||||||
|
|
||||||
|
|
||||||
|
### 🐛 Bug fixes
|
||||||
|
|
||||||
|
* **mobile:** typo for version checking ([0b7318f](https://git.tuffraid.net/cowch/lst_v3/commits/0b7318f8566d15414edd3cd67c89fa5346058ab0))
|
||||||
|
|
||||||
|
|
||||||
|
### 🛠️ Code Refactor
|
||||||
|
|
||||||
|
* **docker compose:** changed to have the correct url that will be used as this is for auth ([4e0cf8c](https://git.tuffraid.net/cowch/lst_v3/commits/4e0cf8c54c4dfd68edba7e733518846a47c55064))
|
||||||
|
* **gp connection:** added in gp ip into env if not there use static name for dns ([36995e9](https://git.tuffraid.net/cowch/lst_v3/commits/36995e9fb42cfa1b72c096b8860866d70b86e70c))
|
||||||
|
* **mobile:** more look and feel work ([bb6155c](https://git.tuffraid.net/cowch/lst_v3/commits/bb6155c9692220542a52664848abf0b9eee91a43))
|
||||||
|
* **mobile:** moved the versioning lookup at at the mobile folder plus renamed ([bddc9ac](https://git.tuffraid.net/cowch/lst_v3/commits/bddc9aca0d2da2b2f53dec1250276d7a076a8601))
|
||||||
|
* **scanner:** format changes ([518c0a8](https://git.tuffraid.net/cowch/lst_v3/commits/518c0a8c19a4bff0b757bbd06ca5460d3565d8bd))
|
||||||
|
|
||||||
|
|
||||||
|
### 📈 Project Builds
|
||||||
|
|
||||||
|
* **scripts:** changing how the relase works so it purposly builds before it trys to release ([83a542d](https://git.tuffraid.net/cowch/lst_v3/commits/83a542d1b7beafe394949c001917f2b25056fac2))
|
||||||
|
|
||||||
|
## [0.0.2-alpha.6](https://git.tuffraid.net/cowch/lst_v3/compare/v0.0.2-alpha.1...v0.0.2-alpha.6) (2026-04-23)
|
||||||
|
|
||||||
|
## [0.0.2-alpha.1](https://git.tuffraid.net/cowch/lst_v3/compare/v0.0.2-alpha.0...v0.0.2-alpha.1) (2026-04-23)
|
||||||
|
|
||||||
|
## [0.0.2-alpha.0](https://git.tuffraid.net/cowch/lst_v3/compare/v0.0.1...v0.0.2-alpha.0) (2026-04-23)
|
||||||
|
|
||||||
|
## [0.0.1](https://git.tuffraid.net/cowch/lst_v3/compare/v0.0.1-alpha.5...v0.0.1) (2026-04-23)
|
||||||
|
|
||||||
|
|
||||||
|
### 🐛 Bug fixes
|
||||||
|
|
||||||
|
* **frontend:** lingering import crashed us ([781025d](https://git.tuffraid.net/cowch/lst_v3/commits/781025dca00e9dd4b2ad9b283be944ed91bbc1e5))
|
||||||
|
|
||||||
|
|
||||||
|
### 📝 Chore
|
||||||
|
|
||||||
|
* **doc remove:** removed a doc and put it in the real area for docs ([a593bb2](https://git.tuffraid.net/cowch/lst_v3/commits/a593bb2baafd0166a178b80cd76dd8862f240e11))
|
||||||
|
|
||||||
|
## [0.0.1-alpha.5](https://git.tuffraid.net/cowch/lst_v3/compare/v0.0.1-alpha.4...v0.0.1-alpha.5) (2026-04-23)
|
||||||
|
|
||||||
|
|
||||||
|
### 🌟 Enhancements
|
||||||
|
|
||||||
|
* **admin:** moved server build/update to full app ([cb00add](https://git.tuffraid.net/cowch/lst_v3/commits/cb00addee96b3ecccf2694f85cb7882cac9c7e3d))
|
||||||
|
* **lstmobile:** intial scanner setup kinda working ([3734d9d](https://git.tuffraid.net/cowch/lst_v3/commits/3734d9daac143ad8fb4404c59990bc4f546f365b))
|
||||||
|
* **oidc:** added in so we could use an oidc to login as well :D ([f7276ca](https://git.tuffraid.net/cowch/lst_v3/commits/f7276ca2d722e30da65bbead23dc9bd57df25aa7))
|
||||||
|
* **servers:** added marked tree in to the mix ([4d53af0](https://git.tuffraid.net/cowch/lst_v3/commits/4d53af033876d81e0d38c148c15cb0af6f3d5bf0))
|
||||||
|
|
||||||
|
|
||||||
|
### 🐛 Bug fixes
|
||||||
|
|
||||||
|
* **datamart:** fixes to correct how we handle activations of new features and legacy queries ([b832d7a](https://git.tuffraid.net/cowch/lst_v3/commits/b832d7aa1ecd063be1bbb7e969617fc7a6376ffa))
|
||||||
|
* **datamart:** if we do not have 2.0 warehousing activate we need to use legacy ([5b1c885](https://git.tuffraid.net/cowch/lst_v3/commits/5b1c88546ff9a42dc572450fe05ad68015edb627))
|
||||||
|
* **gp:** weird issue with db username and password ([d6328ab](https://git.tuffraid.net/cowch/lst_v3/commits/d6328ab764c3626aef99727b873003384951d299))
|
||||||
|
* **inventory:** changes to accruatly adjust the query and check the feature set ([32517d0](https://git.tuffraid.net/cowch/lst_v3/commits/32517d0c98c42a0f0f60135b4a9951c4090ccd58))
|
||||||
|
* **logistics:** historical issue where it was being really weird ([cfbc156](https://git.tuffraid.net/cowch/lst_v3/commits/cfbc1565172f7c2e27f0a1593fe8e99b00d91bb7))
|
||||||
|
* **logistics:** purchasing monitoring was going off every 5th min instead of every 5 min ([3639c1b](https://git.tuffraid.net/cowch/lst_v3/commits/3639c1b77c597a94816bfedd0892f0c8980c6403))
|
||||||
|
* **ocp:** fixes to make sure we always hav printer.data as an array or dont do anything ([fb3cd85](https://git.tuffraid.net/cowch/lst_v3/commits/fb3cd85b411315cac0abd22d050ee88929754833))
|
||||||
|
* **psi:** refactor psi queries ([a1eeade](https://git.tuffraid.net/cowch/lst_v3/commits/a1eeadeec438f7c5c6d31f190fee5c22f83dc6b0))
|
||||||
|
|
||||||
|
|
||||||
|
### 📝 Chore
|
||||||
|
|
||||||
|
* **clean:** removed bruno api a proper api doc will be added to lst later ([f716de1](https://git.tuffraid.net/cowch/lst_v3/commits/f716de1a58a4a4c02d9a0a375444ceecea4a018b))
|
||||||
|
* **scripts:** added in a helper to remove old stuff ([de5df2b](https://git.tuffraid.net/cowch/lst_v3/commits/de5df2b00b1c6fe7c53d6ea075b4cf7e0fb845f9))
|
||||||
|
|
||||||
|
|
||||||
|
### 🛠️ Code Refactor
|
||||||
|
|
||||||
|
* **scanner:** more basic work to get the scanner just running ([82f8369](https://git.tuffraid.net/cowch/lst_v3/commits/82f8369640b2b0ff63dd640dc0aa0609a42c7dda))
|
||||||
|
* **servers:** added mcd and stp1 ([88cef2a](https://git.tuffraid.net/cowch/lst_v3/commits/88cef2a56c390b692866658ce519e59ffeaf4c17))
|
||||||
|
* **server:** server updates can now only be done from a dev pc ([7962463](https://git.tuffraid.net/cowch/lst_v3/commits/7962463927c4c5d2e12db9a0dd536b0f29fc65b2))
|
||||||
|
* **sql:** changed sql connection to ip:port ([a6d53f0](https://git.tuffraid.net/cowch/lst_v3/commits/a6d53f0266f1edc3f3946cd1f07d893c8a98d9c7))
|
||||||
|
|
||||||
|
## [0.0.1-alpha.4](https://git.tuffraid.net/cowch/lst_v3/compare/v0.0.1-alpha.3...v0.0.1-alpha.4) (2026-04-15)
|
||||||
|
|
||||||
|
|
||||||
|
### 🌟 Enhancements
|
||||||
|
|
||||||
|
* **datamart:** migrations completed remaining is the deactivation that will be ran by anylitics ([eccaf17](https://git.tuffraid.net/cowch/lst_v3/commits/eccaf17332fb1c63b8d6bbea6f668c3bb42d44b7))
|
||||||
|
* **datamart:** psi data has been added :D ([e0d0ac2](https://git.tuffraid.net/cowch/lst_v3/commits/e0d0ac20773159373495d65023587b76b47df34f))
|
||||||
|
* **migrate:** quality alert migrated ([b0e5fd7](https://git.tuffraid.net/cowch/lst_v3/commits/b0e5fd79998d551d4f155d58416157a324498fbd))
|
||||||
|
* **ocp:** printer sync and logging logic added ([80189ba](https://git.tuffraid.net/cowch/lst_v3/commits/80189baf906224da43ec1b9b7521153d2a49e059))
|
||||||
|
* **tcp crud:** tcp server start, stop, restart endpoints + status check ([6307037](https://git.tuffraid.net/cowch/lst_v3/commits/6307037985162bc6b49f9f711132853296f43eee))
|
||||||
|
|
||||||
|
|
||||||
|
### 🐛 Bug fixes
|
||||||
|
|
||||||
|
* **datamart:** error when running build and crashed everything ([52a6c82](https://git.tuffraid.net/cowch/lst_v3/commits/52a6c821f4632e4b5b51e0528a0d620e2e0deffc))
|
||||||
|
|
||||||
|
|
||||||
|
### 📚 Documentation
|
||||||
|
|
||||||
|
* **docs:** removed docusorus as all docs will be inside lst now to better assist users ([6ba905a](https://git.tuffraid.net/cowch/lst_v3/commits/6ba905a887dbd8f306d71fed75bb34c71fee74c9))
|
||||||
|
* **env example:** updated the file ([ca3425d](https://git.tuffraid.net/cowch/lst_v3/commits/ca3425d327757120c2cc876fff28e8668c76838d))
|
||||||
|
* **notifcations:** docs for intro, notifcations, reprint added ([87f7387](https://git.tuffraid.net/cowch/lst_v3/commits/87f738702a935279a248d471541cdd9d49330565))
|
||||||
|
|
||||||
|
|
||||||
|
### 🛠️ Code Refactor
|
||||||
|
|
||||||
|
* **agent:** changed to have the test servers on there own push for better testing ([3bf024c](https://git.tuffraid.net/cowch/lst_v3/commits/3bf024cfc97d2841130d54d1a7c5cb5f09f0f598))
|
||||||
|
* **connection:** corrected the connection to the old system ([38a0b65](https://git.tuffraid.net/cowch/lst_v3/commits/38a0b65e9450c65b8300a10058a8f0357400f4e6))
|
||||||
|
* **logging:** when notify is true send the error to systemAdmins ([79e653e](https://git.tuffraid.net/cowch/lst_v3/commits/79e653efa3bcb2941ccee06b28378e709e085ec0))
|
||||||
|
* **notification:** blocking added ([9a0ef8e](https://git.tuffraid.net/cowch/lst_v3/commits/9a0ef8e51a36e3ab45b601b977f1b5cf35d56947))
|
||||||
|
* **puchase:** changes how the error handling works so a better email can be sent ([9d39c13](https://git.tuffraid.net/cowch/lst_v3/commits/9d39c13510974b5ada2a6f6c2448da3f1b755a5c))
|
||||||
|
* **reprint:** new query added to deactivate the old notifcation so no chance of duplicates ([c9eb59e](https://git.tuffraid.net/cowch/lst_v3/commits/c9eb59e2ad9847418ac55cb8a4a91c013f6c97bb))
|
||||||
|
* **server:** added in serverCrash email ([dcb3f2d](https://git.tuffraid.net/cowch/lst_v3/commits/dcb3f2dd1382986639b722778fad113392533b28))
|
||||||
|
* **services:** added in examples for migration stuff ([fc6dc82](https://git.tuffraid.net/cowch/lst_v3/commits/fc6dc82d8458a9928050dd3770778d6a6e1eea7f))
|
||||||
|
* **sql:** corrections to the way we reconnect so the app can error out and be reactivated later ([f33587a](https://git.tuffraid.net/cowch/lst_v3/commits/f33587a3d9a72ca72806635fac9d1214bb1452f1))
|
||||||
|
* **templates:** corrections for new notify process on critcal errors ([07ebf88](https://git.tuffraid.net/cowch/lst_v3/commits/07ebf88806b93b9320f8f9d36b867572dd9a9580))
|
||||||
|
|
||||||
|
|
||||||
|
### 📈 Project changes
|
||||||
|
|
||||||
|
* **agent:** added in jeff city ([e47ea9e](https://git.tuffraid.net/cowch/lst_v3/commits/e47ea9ec52a6ebaf5a8f67a7e8bd2c73da6186fb))
|
||||||
|
* **agent:** added in sherman ([4b6061c](https://git.tuffraid.net/cowch/lst_v3/commits/4b6061c478cbeba7c845dc1c8a015b9998721456))
|
||||||
|
* **service:** changes to the script to allow running the powershell on execution palicy restrictions ([84909bf](https://git.tuffraid.net/cowch/lst_v3/commits/84909bfcf85b91d085ea9dca78be00482b7fd231))
|
||||||
|
|
||||||
|
## [0.0.1-alpha.3](https://git.tuffraid.net/cowch/lst_v3/compare/v0.0.1-alpha.2...v0.0.1-alpha.3) (2026-04-10)
|
||||||
|
|
||||||
|
|
||||||
|
### 🌟 Enhancements
|
||||||
|
|
||||||
|
* **puchase hist:** finished up purhcase historical / gp updates ([a691dc2](https://git.tuffraid.net/cowch/lst_v3/commits/a691dc276e8650c669409241f73d7b2d7a1f9176))
|
||||||
|
|
||||||
|
|
||||||
|
### 🛠️ Code Refactor
|
||||||
|
|
||||||
|
* **gp connect:** gp connect as was added to long live services ([635635b](https://git.tuffraid.net/cowch/lst_v3/commits/635635b356e1262e1c0b063408fe2209e6a8d4ec))
|
||||||
|
* **reprints:** changes the module and submodule around to be more accurate ([97f93a1](https://git.tuffraid.net/cowch/lst_v3/commits/97f93a1830761437118863372108df810ce9977a))
|
||||||
|
* **send email:** changes the error message to show the true message in the error ([995b1dd](https://git.tuffraid.net/cowch/lst_v3/commits/995b1dda7cdfebf4367d301ccac38fd339fab6dd))
|
||||||
|
|
||||||
|
## [0.0.1-alpha.2](https://git.tuffraid.net/cowch/lst_v3/compare/v0.0.1-alpha.1...v0.0.1-alpha.2) (2026-04-08)
|
||||||
|
|
||||||
|
|
||||||
|
### 📈 Project Builds
|
||||||
|
|
||||||
|
* **release:** docker and release corrections ([103ae77](https://git.tuffraid.net/cowch/lst_v3/commits/103ae77e9f82fc008a8ae143b6feccc3ce802f8c))
|
||||||
|
|
||||||
|
## [0.0.1-alpha.1](https://git.tuffraid.net/cowch/lst_v3/compare/v0.0.1-alpha.0...v0.0.1-alpha.1) (2026-04-08)
|
||||||
|
|
||||||
|
|
||||||
|
* **notifcaion:** style changes to the notificaion card and started the table ([7d6c2db](https://git.tuffraid.net/cowch/lst_v3/commits/7d6c2db89cae1f137f126f5814dccd373f7ccb76))
|
||||||
|
|
||||||
|
|
||||||
|
### 🌟 Enhancements
|
||||||
|
|
||||||
|
* **notification:** base notifcaiton sub and admin compelted ([5865ac3](https://git.tuffraid.net/cowch/lst_v3/commits/5865ac3b99d60005c4245740369b0e0789c8fbbd))
|
||||||
|
* **notification:** reprint added ([a17787e](https://git.tuffraid.net/cowch/lst_v3/commits/a17787e85217f1fa4a5e5389e29c33ec09c286c5))
|
||||||
|
* **puchase history:** purhcase history changed to long running no notification ([34b0aba](https://git.tuffraid.net/cowch/lst_v3/commits/34b0abac36f645d0fe5f508881ddbef81ff04b7c))
|
||||||
|
* **purchase:** historical data capture for alpla purchase ([42861cc](https://git.tuffraid.net/cowch/lst_v3/commits/42861cc69e8d4aba5a9670aaed55417efda2b505))
|
||||||
|
* **user notifications:** added the ability for users to sub to notifications and add multi email ([637de85](https://git.tuffraid.net/cowch/lst_v3/commits/637de857f99499a41f7175181523f5d809d95d7e))
|
||||||
|
|
||||||
|
|
||||||
|
### 🐛 Bug fixes
|
||||||
|
|
||||||
|
* **build:** issue with how i wrote the release token ([fe889ca](https://git.tuffraid.net/cowch/lst_v3/commits/fe889ca75731af08c42ec714b7f2abf17cd1ee40))
|
||||||
|
* **build:** type in how we pushed the header over ([83a94ca](https://git.tuffraid.net/cowch/lst_v3/commits/83a94cacf3fc87287cdc0c0cc861b339e72e4b83))
|
||||||
|
* **build:** typo ([860207a](https://git.tuffraid.net/cowch/lst_v3/commits/860207a60b6e04b15736cba631be6c7eab74d020))
|
||||||
|
* **i suck:** more learning experance ([9ceba8b](https://git.tuffraid.net/cowch/lst_v3/commits/9ceba8b5bba17959f27b16b28f50a83c044863fb))
|
||||||
|
* **lala:** something here ([17aed6c](https://git.tuffraid.net/cowch/lst_v3/commits/17aed6cb89f8220570f6c66f78dba6bb202c1aaa))
|
||||||
|
* **release:** typo that caused errors ([76747cf](https://git.tuffraid.net/cowch/lst_v3/commits/76747cf91738bd0d0530afcf7b4f51f0db11ca98))
|
||||||
|
* **typo:** more dam typos ([079478f](https://git.tuffraid.net/cowch/lst_v3/commits/079478f93217dea31c9a1e8ffed85d2381a6977d))
|
||||||
|
* **wrelease:** forgot to save ([3775760](https://git.tuffraid.net/cowch/lst_v3/commits/377576073449e95d315defb913dc317759cc3f43))
|
||||||
|
|
||||||
|
|
||||||
|
### 📝 Chore
|
||||||
|
|
||||||
|
* **release:** 0.1.0-alpha.10 ([98e408c](https://git.tuffraid.net/cowch/lst_v3/commits/98e408cb8577da18e24821b55474198439434f3e))
|
||||||
|
* **release:** 0.1.0-alpha.11 ([d6d5b45](https://git.tuffraid.net/cowch/lst_v3/commits/d6d5b451cd9aeba642ef94654ca20f4acd0b827c))
|
||||||
|
* **release:** 0.1.0-alpha.12 ([1ad789b](https://git.tuffraid.net/cowch/lst_v3/commits/1ad789b2b91a20a2f5a8dc9e6f39af2e19ec9cdc))
|
||||||
|
* **release:** 0.1.0-alpha.9 ([8f59bba](https://git.tuffraid.net/cowch/lst_v3/commits/8f59bba614a8eaa3105bb56f0db36013d5e68485))
|
||||||
|
* **release:** version packages ([fb2c560](https://git.tuffraid.net/cowch/lst_v3/commits/fb2c5609aa12ea7823783c364d5bd029c48a64bd))
|
||||||
|
* **release:** version packages ([b02b93b](https://git.tuffraid.net/cowch/lst_v3/commits/b02b93b83f488fbcee6d24db080ad0d1fe1c5f59))
|
||||||
|
* **release:** version packages ([2c0dbf9](https://git.tuffraid.net/cowch/lst_v3/commits/2c0dbf95c7b8dfd2c98b476d3f44bc8929668c88))
|
||||||
|
* **release:** version packages ([5c64600](https://git.tuffraid.net/cowch/lst_v3/commits/5c6460012aa70d336fbc9702240b4f19262a6b41))
|
||||||
|
* **release:** version packages ([0ce3790](https://git.tuffraid.net/cowch/lst_v3/commits/0ce3790675bc408762eafe76cbd5ab496fd06e73))
|
||||||
|
* **release:** version packages ([4caaf74](https://git.tuffraid.net/cowch/lst_v3/commits/4caaf745693d4df847aefd3721ac5d0ae792114a))
|
||||||
|
* **release:** version packages ([699c124](https://git.tuffraid.net/cowch/lst_v3/commits/699c124b0efba8282e436210619504bda8878e90))
|
||||||
|
* **release:** version packages ([c4fd74f](https://git.tuffraid.net/cowch/lst_v3/commits/c4fd74fc93226cffd9e39602f507a05cd8ea628b))
|
||||||
|
|
||||||
|
|
||||||
|
### 📚 Documentation
|
||||||
|
|
||||||
|
* **readme:** updated progress data ([92ba3ef](https://git.tuffraid.net/cowch/lst_v3/commits/92ba3ef5121afd0d82d4f40a5a985e1fdc081011))
|
||||||
|
* **sop:** added more info ([be1d408](https://git.tuffraid.net/cowch/lst_v3/commits/be1d4081e07b0982b355a270b7850a852a4398f5))
|
||||||
|
|
||||||
|
|
||||||
|
### 🛠️ Code Refactor
|
||||||
|
|
||||||
|
* **build:** added in more info to the relase section ([5854889](https://git.tuffraid.net/cowch/lst_v3/commits/5854889eb5398feebda50a5d256ce7aec39ce112))
|
||||||
|
* **build:** changes to auto release when we cahnge version ([643d12f](https://git.tuffraid.net/cowch/lst_v3/commits/643d12ff182827e724e1569a583bd625a0d1dd0c))
|
||||||
|
* **build:** changes to the way we do release so it builds as well ([7d55c5f](https://git.tuffraid.net/cowch/lst_v3/commits/7d55c5f43173edb48d8709adcb972b7d8fbc3ebd))
|
||||||
|
* **changelog:** reverted back to commit-chagnelog, like more than changeset for solo dev ([ed052df](https://git.tuffraid.net/cowch/lst_v3/commits/ed052dff3c81a7064660a7d25685e0505065252c))
|
||||||
|
* **notification:** reprint - removed a console log as it shouldnt bc there ([5f3d683](https://git.tuffraid.net/cowch/lst_v3/commits/5f3d683a13c831229674166cced699e373131316))
|
||||||
|
* **notification:** select menu looks propper now ([74262be](https://git.tuffraid.net/cowch/lst_v3/commits/74262beb6596ddc971971cc9214a2688accf3a8e))
|
||||||
|
* **opendock refactor on how releases are posted:** this was a bug maybe just a better refactory ([0880298](https://git.tuffraid.net/cowch/lst_v3/commits/0880298cf53d83e487c706e73854e0874ae2d9da))
|
||||||
|
* **queries:** changed dev version to be 1500ms vs 5000ms ([f3b8dd9](https://git.tuffraid.net/cowch/lst_v3/commits/f3b8dd94e5ebae0cc4dd0a2689a19051942e94b8))
|
||||||
|
* **release:** changes to only have the changelog in the release ([6e85991](https://git.tuffraid.net/cowch/lst_v3/commits/6e8599106298ed13febd069d6fda8b354efb5b7b))
|
||||||
|
* **userprofile:** changes to have the table be blank and say nothing subscribed ([3ecf5fb](https://git.tuffraid.net/cowch/lst_v3/commits/3ecf5fb916d5dc1b1ffb224e2142d94f7a9cb126))
|
||||||
|
|
||||||
|
|
||||||
|
### 📈 Project Builds
|
||||||
|
|
||||||
|
* **agent:** added westbend into the flow ([28c226d](https://git.tuffraid.net/cowch/lst_v3/commits/28c226ddbc37ab85cd6a9a6aec091def3e5623d6))
|
||||||
|
* **changelog:** reset the change log after all crap testing ([0059b9b](https://git.tuffraid.net/cowch/lst_v3/commits/0059b9b850c9647695a3fecaf5927c2e3ee7b192))
|
||||||
|
|||||||
33
README.md
33
README.md
@@ -7,7 +7,7 @@
|
|||||||
Quick summary of current rewrite/migration goal.
|
Quick summary of current rewrite/migration goal.
|
||||||
|
|
||||||
- **Phase:** Backend rewrite
|
- **Phase:** Backend rewrite
|
||||||
- **Last updated:** 2024-05-01
|
- **Last updated:** 2026-05-27
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -16,21 +16,21 @@ Quick summary of current rewrite/migration goal.
|
|||||||
| Feature | Description | Status |
|
| Feature | Description | Status |
|
||||||
|----------|--------------|--------|
|
|----------|--------------|--------|
|
||||||
| User Authentication | ~~Login~~, ~~Signup~~, API Key | 🟨 In Progress |
|
| User Authentication | ~~Login~~, ~~Signup~~, API Key | 🟨 In Progress |
|
||||||
| User Profile | Edit profile, upload avatar | ⏳ Not Started |
|
| User Profile | ~~Edit profile~~, upload avatar | 🟨 In Progress |
|
||||||
| User Admin | Edit user, create user, remove user, alplaprod user integration | ⏳ Not Started |
|
| User Admin | ~~Edit user~~, ~~create user~~, remove user, alplaprod user integration | ⏳ Not Started |
|
||||||
| Notifications | Subscribe, Create, Update, Remove, Manual Trigger | ⏳ Not Started |
|
| Notifications | ~~Subscribe~~, ~~Create~~, ~~Update~~, ~~~~Remove~~, Manual Trigger | 🟨 In Progress |
|
||||||
| Datamart | Create, Update, Run, Deactivate | 🔧 In Progress |
|
| Datamart | ~~Create~~, ~~Update~~, ~~Run~~, Deactivate | 🟨 In Progress |
|
||||||
| Frontend | Analytics and charts | ⏳ Not Started |
|
| Frontend | Analytics and charts | ⏳ Not Started |
|
||||||
| Docs | Instructions and trouble shooting | ⏳ Not Started |
|
| Docs | Instructions and trouble shooting | ⏳ Not Started |
|
||||||
| One Click Print | Get printers, monitor printers, label process, material process, Special processes | ⏳ Not Started |
|
| One Click Print | ~~Get printers~~, monitor printers, label process, material process, Special processes | 🟨 In Progress |
|
||||||
| Silo Adjustments | Create, History, Comments | ⏳ Not Started |
|
| Silo Adjustments | Create, History, Comments | ⏳ Not Started |
|
||||||
| Demand Management | Orders, Forecast, Special Mappings, Create trucks, Load Trucks (tablet scanning) | ⏳ Not Started |
|
| Demand Management | Orders, Forecast, Special Mappings, Create trucks, Load Trucks (tablet scanning) | ⏳ Not Started |
|
||||||
| Open Docks | Integrations | ⏳ Not Started |
|
| Open Docks | Integrations | 🟨 In Progress |
|
||||||
| Transport Insight | Integrations | ⏳ Not Started |
|
| Transport Insight | Integrations | ⏳ Not Started |
|
||||||
| Quality Request Tool | Add Pallet, Monitor for moved, status changes, alerts | ⏳ Not Started |
|
| Quality Request Tool | Add Pallet, Monitor for moved, status changes, alerts | ⏳ Not Started |
|
||||||
| Logistics | Consume material, return and print, label info, relocate | ⏳ Not Started |
|
| Logistics | Consume material, return and print, label info, relocate | ⏳ Not Started |
|
||||||
| EOM | Endpoints, Report Pull for finance | ⏳ Not Started |
|
| EOM | ~~Endpoints~~, Report Pull for finance, SSRS report | 🟨 In Progress |
|
||||||
| OCME | Custom integration | ⏳ Not Started |
|
| ~~OCME~~ | ~~Custom integration~~ | Canceled |
|
||||||
| API Migration | Moving to new REST endpoints | 🔧 In Progress |
|
| API Migration | Moving to new REST endpoints | 🔧 In Progress |
|
||||||
| System | Tests,Builds, Updates, Remote Logging, DB Backups, Alerting | ⏳ Not Started |
|
| System | Tests,Builds, Updates, Remote Logging, DB Backups, Alerting | ⏳ Not Started |
|
||||||
|
|
||||||
@@ -44,7 +44,16 @@ _Status legend:_
|
|||||||
How to run the current version of the app.
|
How to run the current version of the app.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/youruser/yourrepo.git
|
git clone https://git.tuffraid.net/cowch/lst_v3.git
|
||||||
cd yourrepo
|
cd lst_v3
|
||||||
npm install
|
npm install
|
||||||
npm run dev
|
```
|
||||||
|
|
||||||
|
Rename the .env-example to .env
|
||||||
|
|
||||||
|
Update all the fields
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run dev:db:migrate
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
38
backend/admin/admin.build.ts
Normal file
38
backend/admin/admin.build.ts
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
/**
|
||||||
|
* To be able to run this we need to set our dev pc in the .env.
|
||||||
|
* if its empty just ignore it. this will just be the double catch
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Router } from "express";
|
||||||
|
import { build, building } from "../utils/build.utils.js";
|
||||||
|
import { apiReturn } from "../utils/returnHelper.utils.js";
|
||||||
|
|
||||||
|
const router = Router();
|
||||||
|
|
||||||
|
router.post("/release", async (_, res) => {
|
||||||
|
if (!building) {
|
||||||
|
build();
|
||||||
|
|
||||||
|
return apiReturn(res, {
|
||||||
|
success: true,
|
||||||
|
level: "info",
|
||||||
|
module: "admin",
|
||||||
|
subModule: "build",
|
||||||
|
message: `The build has been triggered see logs for progress of the current build.`,
|
||||||
|
data: [],
|
||||||
|
status: 200,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return apiReturn(res, {
|
||||||
|
success: false,
|
||||||
|
level: "error",
|
||||||
|
module: "admin",
|
||||||
|
subModule: "build",
|
||||||
|
message: `There is a build in progress already please check the logs for on going progress.`,
|
||||||
|
data: [],
|
||||||
|
status: 200,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default router;
|
||||||
16
backend/admin/admin.routes.ts
Normal file
16
backend/admin/admin.routes.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import type { Express } from "express";
|
||||||
|
import { requireAuth } from "../middleware/auth.middleware.js";
|
||||||
|
|
||||||
|
import build from "./admin.build.js";
|
||||||
|
import update from "./admin.updateServer.js";
|
||||||
|
import users from "./admin.users.js";
|
||||||
|
|
||||||
|
export const setupAdminRoutes = (baseUrl: string, app: Express) => {
|
||||||
|
//stats will be like this as we dont need to change this
|
||||||
|
|
||||||
|
app.use(`${baseUrl}/api/admin/build`, requireAuth, build);
|
||||||
|
app.use(`${baseUrl}/api/admin/build`, requireAuth, update);
|
||||||
|
app.use(`${baseUrl}/api/admin/user`, requireAuth, users);
|
||||||
|
|
||||||
|
// all other system should be under /api/system/*
|
||||||
|
};
|
||||||
86
backend/admin/admin.updateServer.ts
Normal file
86
backend/admin/admin.updateServer.ts
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
/**
|
||||||
|
* To be able to run this we need to set our dev pc in the .env.
|
||||||
|
* if its empty just ignore it. this will just be the double catch
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Router } from "express";
|
||||||
|
import z from "zod";
|
||||||
|
import { building } from "../utils/build.utils.js";
|
||||||
|
import { runUpdate, updating } from "../utils/deployApp.js";
|
||||||
|
import { apiReturn } from "../utils/returnHelper.utils.js";
|
||||||
|
|
||||||
|
const updateServer = z.object({
|
||||||
|
server: z.string(),
|
||||||
|
destination: z.string(),
|
||||||
|
token: z.string().min(5, "Plant tokens should be at least 5 characters long"),
|
||||||
|
});
|
||||||
|
|
||||||
|
const router = Router();
|
||||||
|
|
||||||
|
type Update = {
|
||||||
|
success: boolean;
|
||||||
|
message: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
router.post("/updateServer", async (req, res) => {
|
||||||
|
try {
|
||||||
|
const validated = updateServer.parse(req.body);
|
||||||
|
|
||||||
|
if (!updating && !building) {
|
||||||
|
const update = (await runUpdate({
|
||||||
|
server: validated.server,
|
||||||
|
destination: validated.destination,
|
||||||
|
token: validated.token,
|
||||||
|
})) as Update;
|
||||||
|
return apiReturn(res, {
|
||||||
|
success: update.success,
|
||||||
|
level: update.success ? "info" : "error",
|
||||||
|
module: "admin",
|
||||||
|
subModule: "update",
|
||||||
|
message: update.message,
|
||||||
|
data: [],
|
||||||
|
status: 200,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return apiReturn(res, {
|
||||||
|
success: false,
|
||||||
|
level: "error",
|
||||||
|
module: "admin",
|
||||||
|
subModule: "update",
|
||||||
|
message: `${validated.server}: ${validated.token} is already being updated, or is currently building the app.`,
|
||||||
|
data: [],
|
||||||
|
status: 200,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
if (err instanceof z.ZodError) {
|
||||||
|
const flattened = z.flattenError(err);
|
||||||
|
// return res.status(400).json({
|
||||||
|
// error: "Validation failed",
|
||||||
|
// details: flattened,
|
||||||
|
// });
|
||||||
|
|
||||||
|
return apiReturn(res, {
|
||||||
|
success: false,
|
||||||
|
level: "error", //connect.success ? "info" : "error",
|
||||||
|
module: "routes",
|
||||||
|
subModule: "auth",
|
||||||
|
message: "Validation failed",
|
||||||
|
data: [flattened.fieldErrors],
|
||||||
|
status: 400, //connect.success ? 200 : 400,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return apiReturn(res, {
|
||||||
|
success: false,
|
||||||
|
level: "error", //connect.success ? "info" : "error",
|
||||||
|
module: "routes",
|
||||||
|
subModule: "auth",
|
||||||
|
message: "Internal Server Error creating user",
|
||||||
|
data: [err],
|
||||||
|
status: 400, //connect.success ? 200 : 400,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default router;
|
||||||
46
backend/admin/admin.users.ts
Normal file
46
backend/admin/admin.users.ts
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
/**
|
||||||
|
* To be able to run this we need to set our dev pc in the .env.
|
||||||
|
* if its empty just ignore it. this will just be the double catch
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { fromNodeHeaders } from "better-auth/node";
|
||||||
|
import { Router } from "express";
|
||||||
|
import { auth } from "../utils/auth.utils.js";
|
||||||
|
import { apiReturn } from "../utils/returnHelper.utils.js";
|
||||||
|
|
||||||
|
const r = Router();
|
||||||
|
|
||||||
|
r.get("/", async (req, res) => {
|
||||||
|
const { users } = await auth.api.listUsers({
|
||||||
|
query: {
|
||||||
|
limit: 50,
|
||||||
|
},
|
||||||
|
headers: fromNodeHeaders(req.headers),
|
||||||
|
});
|
||||||
|
|
||||||
|
// console.log(error);
|
||||||
|
|
||||||
|
// if (error) {
|
||||||
|
// return apiReturn(res, {
|
||||||
|
// success: false,
|
||||||
|
// level: "info",
|
||||||
|
// module: "admin",
|
||||||
|
// subModule: "user",
|
||||||
|
// message: `There was an error getting the users.`,
|
||||||
|
// data: users,
|
||||||
|
// status: 400,
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
|
||||||
|
return apiReturn(res, {
|
||||||
|
success: true,
|
||||||
|
level: "info",
|
||||||
|
module: "admin",
|
||||||
|
subModule: "users",
|
||||||
|
message: `Current active users.`,
|
||||||
|
data: users,
|
||||||
|
status: 200,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
export default r;
|
||||||
@@ -3,7 +3,9 @@ import { fileURLToPath } from "node:url";
|
|||||||
import { toNodeHandler } from "better-auth/node";
|
import { toNodeHandler } from "better-auth/node";
|
||||||
import express from "express";
|
import express from "express";
|
||||||
import morgan from "morgan";
|
import morgan from "morgan";
|
||||||
|
import { umamiConfig } from "./configs/umami.config.js";
|
||||||
import { createLogger } from "./logger/logger.controller.js";
|
import { createLogger } from "./logger/logger.controller.js";
|
||||||
|
import { routeHitMiddleware } from "./middleware/routeHit.middleware.js";
|
||||||
import { setupRoutes } from "./routeHandler.routes.js";
|
import { setupRoutes } from "./routeHandler.routes.js";
|
||||||
import { auth } from "./utils/auth.utils.js";
|
import { auth } from "./utils/auth.utils.js";
|
||||||
import { lstCors } from "./utils/cors.utils.js";
|
import { lstCors } from "./utils/cors.utils.js";
|
||||||
@@ -26,19 +28,38 @@ const createApp = async () => {
|
|||||||
const __dirname = dirname(__filename);
|
const __dirname = dirname(__filename);
|
||||||
|
|
||||||
// well leave this active so we can monitor it to validate
|
// well leave this active so we can monitor it to validate
|
||||||
app.use(morgan("tiny"));
|
app.use(morgan("dev"));
|
||||||
app.set("trust proxy", true);
|
app.set("trust proxy", true);
|
||||||
app.use(lstCors());
|
app.use(lstCors());
|
||||||
|
app.use(routeHitMiddleware);
|
||||||
app.all(`${baseUrl}/api/auth/*splat`, toNodeHandler(auth));
|
app.all(`${baseUrl}/api/auth/*splat`, toNodeHandler(auth));
|
||||||
app.use(express.json());
|
app.use(express.json());
|
||||||
|
|
||||||
|
app.get(`${baseUrl}/api/lst-config.js`, (_, res) => {
|
||||||
|
res.type("application/javascript");
|
||||||
|
res.setHeader("Cache-Control", "no-store");
|
||||||
|
|
||||||
|
res.send(`
|
||||||
|
window.LST_CONFIG = {
|
||||||
|
appName: ${JSON.stringify(umamiConfig.appName ?? "LST")},
|
||||||
|
site: ${JSON.stringify(umamiConfig.site ?? "unknown")},
|
||||||
|
server: ${JSON.stringify(umamiConfig.server ?? "unknown")},
|
||||||
|
appVersion: ${JSON.stringify(umamiConfig.appVersion ?? "dev")},
|
||||||
|
umamiHost: ${JSON.stringify(umamiConfig.umamiHost ?? "")},
|
||||||
|
umamiWebsiteId: ${JSON.stringify(umamiConfig.umamiWebsiteId ?? "")},
|
||||||
|
timezone: ${JSON.stringify(process.env.TIMEZONE ?? "America/Chicago")}
|
||||||
|
};
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
setupRoutes(baseUrl, app);
|
setupRoutes(baseUrl, app);
|
||||||
|
|
||||||
app.use(
|
app.use(
|
||||||
baseUrl + "/app",
|
`${baseUrl}/app`,
|
||||||
express.static(join(__dirname, "../frontend/dist")),
|
express.static(join(__dirname, "../frontend/dist")),
|
||||||
);
|
);
|
||||||
|
|
||||||
app.get(baseUrl + "/app/*splat", (_, res) => {
|
app.get(`${baseUrl}/app/*splat`, (_, res) => {
|
||||||
res.sendFile(join(__dirname, "../frontend/dist/index.html"));
|
res.sendFile(join(__dirname, "../frontend/dist/index.html"));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
import type { Express } from "express";
|
import type { Express } from "express";
|
||||||
|
|
||||||
import login from "./login.route.js";
|
import login from "./login.route.js";
|
||||||
import register from "./register.route.js";
|
import register from "./register.route.js";
|
||||||
|
|
||||||
export const setupAuthRoutes = (baseUrl: string, app: Express) => {
|
export const setupAuthRoutes = (baseUrl: string, app: Express) => {
|
||||||
//setup all the routes
|
//setup all the routes
|
||||||
|
|
||||||
app.use(`${baseUrl}/api/authentication/login`, login);
|
app.use(`${baseUrl}/api/authentication/login`, login);
|
||||||
app.use(`${baseUrl}/api/authentication/register`, register);
|
app.use(`${baseUrl}/api/authentication/register`, register);
|
||||||
};
|
};
|
||||||
|
|||||||
29
backend/configs/gpSql.config.ts
Normal file
29
backend/configs/gpSql.config.ts
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import type sql from "mssql";
|
||||||
|
|
||||||
|
// TODO : Remove this later and get it onto the env
|
||||||
|
const username = "gpviewer";
|
||||||
|
const password = "gp$$ViewOnly!";
|
||||||
|
|
||||||
|
const port = process.env.SQL_PORT
|
||||||
|
? Number.parseInt(process.env.SQL_PORT, 10)
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
export const gpSqlConfig: sql.config = {
|
||||||
|
server: `${process.env.GP_SERVER ?? "USMCD1VMS011"}`,
|
||||||
|
port: port,
|
||||||
|
database: `ALPLA`,
|
||||||
|
user: username,
|
||||||
|
password: password,
|
||||||
|
options: {
|
||||||
|
encrypt: true,
|
||||||
|
trustServerCertificate: true,
|
||||||
|
},
|
||||||
|
requestTimeout: 90000, // how long until we kill the query and fail it
|
||||||
|
pool: {
|
||||||
|
max: 20, // Maximum number of connections in the pool
|
||||||
|
min: 0, // Minimum number of connections in the pool
|
||||||
|
idleTimeoutMillis: 10000, // How long a connection is allowed to be idle before being released
|
||||||
|
reapIntervalMillis: 1000, // how often to check for idle resources to destroy
|
||||||
|
acquireTimeoutMillis: 100000, // How long until a complete timeout happens
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -1,7 +1,13 @@
|
|||||||
import type sql from "mssql";
|
import type sql from "mssql";
|
||||||
|
|
||||||
|
const port = process.env.SQL_PORT
|
||||||
|
? Number.parseInt(process.env.SQL_PORT, 10)
|
||||||
|
: undefined;
|
||||||
|
|
||||||
export const prodSqlConfig: sql.config = {
|
export const prodSqlConfig: sql.config = {
|
||||||
server: `${process.env.PROD_SERVER}`,
|
server: `${process.env.PROD_SERVER}`,
|
||||||
database: `AlplaPROD_${process.env.PROD_PLANT_TOKEN}_cus`,
|
database: `AlplaPROD_${process.env.PROD_PLANT_TOKEN}_cus`,
|
||||||
|
port: port,
|
||||||
user: process.env.PROD_USER,
|
user: process.env.PROD_USER,
|
||||||
password: process.env.PROD_PASSWORD,
|
password: process.env.PROD_PASSWORD,
|
||||||
options: {
|
options: {
|
||||||
|
|||||||
@@ -9,8 +9,6 @@ import os from "node:os";
|
|||||||
import { apiReference } from "@scalar/express-api-reference";
|
import { apiReference } from "@scalar/express-api-reference";
|
||||||
// const port = 3000;
|
// const port = 3000;
|
||||||
import type { OpenAPIV3_1 } from "openapi-types";
|
import type { OpenAPIV3_1 } from "openapi-types";
|
||||||
import { cronerActiveJobs } from "../scaler/cronerActiveJobs.spec.js";
|
|
||||||
import { cronerStatusChange } from "../scaler/cronerStatusChange.spec.js";
|
|
||||||
import { prodLoginSpec } from "../scaler/login.spec.js";
|
import { prodLoginSpec } from "../scaler/login.spec.js";
|
||||||
import { openDockApt } from "../scaler/opendockGetRelease.spec.js";
|
import { openDockApt } from "../scaler/opendockGetRelease.spec.js";
|
||||||
import { prodRestartSpec } from "../scaler/prodSqlRestart.spec.js";
|
import { prodRestartSpec } from "../scaler/prodSqlRestart.spec.js";
|
||||||
@@ -125,8 +123,6 @@ export const setupApiDocsRoutes = (baseUrl: string, app: Express) => {
|
|||||||
...prodLoginSpec,
|
...prodLoginSpec,
|
||||||
...prodRegisterSpec,
|
...prodRegisterSpec,
|
||||||
//...mergedDatamart,
|
//...mergedDatamart,
|
||||||
...cronerActiveJobs,
|
|
||||||
...cronerStatusChange,
|
|
||||||
...openDockApt,
|
...openDockApt,
|
||||||
|
|
||||||
// Add more specs here as you build features
|
// Add more specs here as you build features
|
||||||
|
|||||||
21
backend/configs/umami.config.ts
Normal file
21
backend/configs/umami.config.ts
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
export type UmamiRuntimeConfig = {
|
||||||
|
appName: string;
|
||||||
|
site: string;
|
||||||
|
server: string;
|
||||||
|
appVersion: string;
|
||||||
|
umamiHost: string;
|
||||||
|
umamiWebsiteId: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const umamiConfig: UmamiRuntimeConfig = {
|
||||||
|
appName: process.env.APP_NAME ?? "LST",
|
||||||
|
site: process.env.URL ?? "unknown",
|
||||||
|
server: process.env.PROD_PLANT_TOKEN ?? "unknown", // could also be server name based on our setup.
|
||||||
|
appVersion: process.env.NODE_ENV ?? "dev",
|
||||||
|
umamiHost: process.env.UMAMI_HOST ?? "",
|
||||||
|
umamiWebsiteId: process.env.UMAMI_WEBSITE_ID ?? "",
|
||||||
|
};
|
||||||
|
|
||||||
|
export function isUmamiEnabled() {
|
||||||
|
return Boolean(umamiConfig.umamiHost && umamiConfig.umamiWebsiteId);
|
||||||
|
}
|
||||||
@@ -13,6 +13,10 @@
|
|||||||
*
|
*
|
||||||
* when a criteria is password over we will handle it by counting how many were passed up to 3 then deal with each one respectively
|
* when a criteria is password over we will handle it by counting how many were passed up to 3 then deal with each one respectively
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { and, between, inArray, notInArray } from "drizzle-orm";
|
||||||
|
import { db } from "../db/db.controller.js";
|
||||||
|
import { invHistoricalData } from "../db/schema/historicalInv.schema.js";
|
||||||
import { prodQuery } from "../prodSql/prodSqlQuery.controller.js";
|
import { prodQuery } from "../prodSql/prodSqlQuery.controller.js";
|
||||||
import {
|
import {
|
||||||
type SqlQuery,
|
type SqlQuery,
|
||||||
@@ -22,37 +26,126 @@ import { returnFunc } from "../utils/returnHelper.utils.js";
|
|||||||
import { tryCatch } from "../utils/trycatch.utils.js";
|
import { tryCatch } from "../utils/trycatch.utils.js";
|
||||||
import { datamartData } from "./datamartData.utlis.js";
|
import { datamartData } from "./datamartData.utlis.js";
|
||||||
|
|
||||||
type Options = {
|
|
||||||
name: string;
|
|
||||||
value: string;
|
|
||||||
};
|
|
||||||
type Data = {
|
type Data = {
|
||||||
name: string;
|
name: string;
|
||||||
options: Options;
|
options: any;
|
||||||
optionsRequired?: boolean;
|
optionsRequired?: boolean;
|
||||||
howManyOptionsRequired?: number;
|
howManyOptionsRequired?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const lstDbRun = async (data: Data) => {
|
||||||
|
if (data.options) {
|
||||||
|
if (data.name === "psiInventory") {
|
||||||
|
const ids = data.options.articles.split(",").map((id: any) => id.trim());
|
||||||
|
|
||||||
|
const whse = data.options.whseToInclude
|
||||||
|
? data.options.whseToInclude
|
||||||
|
.split(",")
|
||||||
|
.map((w: any) => w.trim())
|
||||||
|
.filter(Boolean)
|
||||||
|
: [];
|
||||||
|
|
||||||
|
const locations = data.options.exludeLanes
|
||||||
|
? data.options.exludeLanes
|
||||||
|
.split(",")
|
||||||
|
.map((l: any) => l.trim())
|
||||||
|
.filter(Boolean)
|
||||||
|
: [];
|
||||||
|
|
||||||
|
const conditions = [
|
||||||
|
inArray(invHistoricalData.article, ids),
|
||||||
|
between(
|
||||||
|
invHistoricalData.histDate,
|
||||||
|
data.options.startDate,
|
||||||
|
data.options.endDate,
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
|
// only add the warehouse condition if there are any whse values
|
||||||
|
if (whse.length > 0) {
|
||||||
|
conditions.push(inArray(invHistoricalData.whseId, whse));
|
||||||
|
}
|
||||||
|
|
||||||
|
// locations we dont want in the system
|
||||||
|
if (locations.length > 0) {
|
||||||
|
conditions.push(notInArray(invHistoricalData.location, locations));
|
||||||
|
}
|
||||||
|
|
||||||
|
return await db
|
||||||
|
.select()
|
||||||
|
.from(invHistoricalData)
|
||||||
|
.where(and(...conditions));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
};
|
||||||
export const runDatamartQuery = async (data: Data) => {
|
export const runDatamartQuery = async (data: Data) => {
|
||||||
// search the query db for the query by name
|
// search the query db for the query by name
|
||||||
const sqlQuery = sqlQuerySelector(`${data.name}`) as SqlQuery;
|
const considerLstDBRuns = ["psiInventory"];
|
||||||
|
|
||||||
|
if (considerLstDBRuns.includes(data.name)) {
|
||||||
|
const lstDB = await lstDbRun(data);
|
||||||
|
|
||||||
|
return returnFunc({
|
||||||
|
success: true,
|
||||||
|
level: "info",
|
||||||
|
module: "datamart",
|
||||||
|
subModule: "lstDBrn",
|
||||||
|
message: `Data for: ${data.name}`,
|
||||||
|
data: lstDB,
|
||||||
|
notify: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const featureQ = sqlQuerySelector(`featureCheck`) as SqlQuery;
|
||||||
|
|
||||||
|
const { data: fd, error: fe } = await tryCatch(
|
||||||
|
prodQuery(featureQ.query, `Running feature check`),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (fe) {
|
||||||
|
return returnFunc({
|
||||||
|
success: false,
|
||||||
|
level: "error",
|
||||||
|
module: "datamart",
|
||||||
|
subModule: "query",
|
||||||
|
message: `feature check failed`,
|
||||||
|
data: fe as any,
|
||||||
|
notify: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// for queries that will need to be ran on legacy until we get the plant updated need to go in here
|
||||||
|
const doubleQueries = ["inventory"];
|
||||||
|
let queryFile = "";
|
||||||
|
|
||||||
|
if (doubleQueries.includes(data.name)) {
|
||||||
|
queryFile = `datamart.${
|
||||||
|
fd.data[0].activated > 0 ? data.name : `legacy.${data.name}`
|
||||||
|
}`;
|
||||||
|
} else {
|
||||||
|
queryFile = `datamart.${data.name}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const sqlQuery = sqlQuerySelector(queryFile) as SqlQuery;
|
||||||
|
// checking if warehousing is as it will start to effect a lot of queries for plants that are not on 2.
|
||||||
|
|
||||||
const getDataMartInfo = datamartData.filter((x) => x.endpoint === data.name);
|
const getDataMartInfo = datamartData.filter((x) => x.endpoint === data.name);
|
||||||
|
|
||||||
// const optionsMissing =
|
// const optionsMissing =
|
||||||
// !data.options || Object.keys(data.options).length === 0;
|
// !data.options || Object.keys(data.options).length === 0;
|
||||||
|
|
||||||
const optionCount =
|
const isValid =
|
||||||
Object.keys(data.options).length ===
|
Object.keys(data.options ?? {}).length >=
|
||||||
getDataMartInfo[0]?.howManyOptionsRequired;
|
(getDataMartInfo[0]?.howManyOptionsRequired ?? 0);
|
||||||
|
|
||||||
if (getDataMartInfo[0]?.optionsRequired && !optionCount) {
|
if (getDataMartInfo[0]?.optionsRequired && !isValid) {
|
||||||
return returnFunc({
|
return returnFunc({
|
||||||
success: false,
|
success: false,
|
||||||
level: "error",
|
level: "error",
|
||||||
module: "datamart",
|
module: "datamart",
|
||||||
subModule: "query",
|
subModule: "query",
|
||||||
message: `This query is required to have the ${getDataMartInfo[0]?.howManyOptionsRequired} options set in order use it.`,
|
message: `This query is required to have ${getDataMartInfo[0]?.howManyOptionsRequired} option(s) set in order use it, please add in your option(s) data and try again.`,
|
||||||
data: [getDataMartInfo[0].options],
|
data: [getDataMartInfo[0].options],
|
||||||
notify: false,
|
notify: false,
|
||||||
});
|
});
|
||||||
@@ -75,10 +168,130 @@ export const runDatamartQuery = async (data: Data) => {
|
|||||||
|
|
||||||
// split the criteria by "," then and then update the query
|
// split the criteria by "," then and then update the query
|
||||||
if (data.options) {
|
if (data.options) {
|
||||||
Object.entries(data.options ?? {}).forEach(([key, value]) => {
|
switch (data.name) {
|
||||||
const pattern = new RegExp(`\\[${key.trim()}\\]`, "g");
|
case "activeArticles":
|
||||||
datamartQuery = datamartQuery.replace(pattern, String(value).trim());
|
break;
|
||||||
});
|
case "deliveryByDateRange":
|
||||||
|
datamartQuery = datamartQuery
|
||||||
|
.replace("[startDate]", `${data.options.startDate}`)
|
||||||
|
.replace("[endDate]", `${data.options.endDate}`)
|
||||||
|
.replace(
|
||||||
|
"--and r.ArticleHumanReadableId in ([articles]) ",
|
||||||
|
data.options.articles
|
||||||
|
? `and r.ArticleHumanReadableId in (${data.options.articles})`
|
||||||
|
: "--and r.ArticleHumanReadableId in ([articles]) ",
|
||||||
|
)
|
||||||
|
.replace(
|
||||||
|
"and DeliveredQuantity > 0",
|
||||||
|
data.options.all
|
||||||
|
? "--and DeliveredQuantity > 0"
|
||||||
|
: "and DeliveredQuantity > 0",
|
||||||
|
);
|
||||||
|
|
||||||
|
break;
|
||||||
|
case "customerInventory":
|
||||||
|
datamartQuery = datamartQuery
|
||||||
|
.replace(
|
||||||
|
"--and IdAdressen",
|
||||||
|
`and IdAdressen in (${data.options.customer})`,
|
||||||
|
)
|
||||||
|
.replace(
|
||||||
|
"--and x.IdWarenlager in (0)",
|
||||||
|
`${data.options.whseToInclude ? `and x.IdWarenlager in (${data.options.whseToInclude})` : `--and x.IdWarenlager in (0)`}`,
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case "openOrders":
|
||||||
|
datamartQuery = datamartQuery
|
||||||
|
.replace("[startDay]", `${data.options.startDay}`)
|
||||||
|
.replace("[endDay]", `${data.options.endDay}`);
|
||||||
|
break;
|
||||||
|
case "inventory":
|
||||||
|
datamartQuery = datamartQuery
|
||||||
|
.replaceAll(
|
||||||
|
"--,l.RunningNumber",
|
||||||
|
`${data.options.includeRunningNumbers ? `,l.RunningNumber` : `--,l.RunningNumber`}`,
|
||||||
|
)
|
||||||
|
.replaceAll(
|
||||||
|
"--,l.MachineLocation,l.MachineName,l.ProductionLotRunningNumber as lot",
|
||||||
|
`${data.options.lots ? `,l.MachineLocation,l.MachineName,l.ProductionLotRunningNumber as lot` : `--,l.MachineLocation,l.MachineName,l.ProductionLotRunningNumber as lot`}`,
|
||||||
|
)
|
||||||
|
.replaceAll(
|
||||||
|
"--,l.MachineLocation,l.MachineName,l.ProductionLotRunningNumber",
|
||||||
|
`${data.options.lots ? `,l.MachineLocation,l.MachineName,l.ProductionLotRunningNumber` : `--,l.MachineLocation,l.MachineName,l.ProductionLotRunningNumber`}`,
|
||||||
|
)
|
||||||
|
.replaceAll(
|
||||||
|
"--,l.WarehouseDescription,l.LaneDescription",
|
||||||
|
`${data.options.locations ? `,l.WarehouseDescription,l.LaneDescription` : `--,l.WarehouseDescription,l.LaneDescription`}`,
|
||||||
|
);
|
||||||
|
|
||||||
|
break;
|
||||||
|
case "fakeEDIUpdate":
|
||||||
|
datamartQuery = datamartQuery.replace(
|
||||||
|
"--AND h.CustomerHumanReadableId in (0)",
|
||||||
|
`${data.options.address ? `AND h.CustomerHumanReadableId in (${data.options.address})` : `--AND h.CustomerHumanReadableId in (0)`}`,
|
||||||
|
);
|
||||||
|
|
||||||
|
break;
|
||||||
|
case "forecast":
|
||||||
|
datamartQuery = datamartQuery.replace(
|
||||||
|
"where DeliveryAddressHumanReadableId in ([customers])",
|
||||||
|
data.options.customers
|
||||||
|
? `where DeliveryAddressHumanReadableId in (${data.options.customers})`
|
||||||
|
: "--where DeliveryAddressHumanReadableId in ([customers])",
|
||||||
|
);
|
||||||
|
|
||||||
|
break;
|
||||||
|
case "activeArticles2":
|
||||||
|
datamartQuery = datamartQuery.replace(
|
||||||
|
"and a.HumanReadableId in ([articles])",
|
||||||
|
data.options.articles
|
||||||
|
? `and a.HumanReadableId in (${data.options.articles})`
|
||||||
|
: "--and a.HumanReadableId in ([articles])",
|
||||||
|
);
|
||||||
|
|
||||||
|
break;
|
||||||
|
case "psiDeliveryData":
|
||||||
|
datamartQuery = datamartQuery
|
||||||
|
.replace("[startDate]", `${data.options.startDate}`)
|
||||||
|
.replace("[endDate]", `${data.options.endDate}`)
|
||||||
|
.replace(
|
||||||
|
"[articles]",
|
||||||
|
data.options.articles ? `${data.options.articles}` : "[articles]",
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case "productionData":
|
||||||
|
datamartQuery = datamartQuery
|
||||||
|
.replace("[startDate]", `${data.options.startDate}`)
|
||||||
|
.replace("[endDate]", `${data.options.endDate}`)
|
||||||
|
.replace(
|
||||||
|
"and ArticleHumanReadableId in ([articles])",
|
||||||
|
data.options.articles
|
||||||
|
? `and ArticleHumanReadableId in (${data.options.articles})`
|
||||||
|
: "--and ArticleHumanReadableId in ([articles])",
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case "psiPlanningData":
|
||||||
|
datamartQuery = datamartQuery
|
||||||
|
.replace("[startDate]", `${data.options.startDate}`)
|
||||||
|
.replace("[endDate]", `${data.options.endDate}`)
|
||||||
|
.replace(
|
||||||
|
"and pl.ArticleHumanReadableId IN ([articles]) ",
|
||||||
|
data.options.articles
|
||||||
|
? `and pl.ArticleHumanReadableId IN (${data.options.articles})`
|
||||||
|
: "--and pl.ArticleHumanReadableId IN ([articles])",
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return returnFunc({
|
||||||
|
success: false,
|
||||||
|
level: "error",
|
||||||
|
module: "datamart",
|
||||||
|
subModule: "query",
|
||||||
|
message: `${data.name} encountered an error as it might not exist in LST please contact support if this continues to happen`,
|
||||||
|
data: [sqlQuery.message],
|
||||||
|
notify: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const { data: queryRun, error } = await tryCatch(
|
const { data: queryRun, error } = await tryCatch(
|
||||||
@@ -114,8 +327,13 @@ export const runDatamartQuery = async (data: Data) => {
|
|||||||
level: "info",
|
level: "info",
|
||||||
module: "datamart",
|
module: "datamart",
|
||||||
subModule: "query",
|
subModule: "query",
|
||||||
message: `Data for: ${data.name}`,
|
message: `Data for: ${data.name} ${data.options.includePlantToken ? "including plant token" : ""}`,
|
||||||
data: queryRun.data,
|
// if includePlantToken was passed we should map this into the data
|
||||||
|
data: data.options.includePlantToken
|
||||||
|
? queryRun.data.map((i) => {
|
||||||
|
return { plantToken: process.env.PROD_PLANT_TOKEN, ...i };
|
||||||
|
})
|
||||||
|
: queryRun.data,
|
||||||
notify: false,
|
notify: false,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -10,14 +10,50 @@ export const datamartData = [
|
|||||||
name: "Active articles",
|
name: "Active articles",
|
||||||
endpoint: "activeArticles",
|
endpoint: "activeArticles",
|
||||||
description: "returns all active articles for the server with custom data",
|
description: "returns all active articles for the server with custom data",
|
||||||
options: "", // set as a string and each item will be seperated by a , this way we can split it later in the excel file.
|
options: "",
|
||||||
optionsRequired: false,
|
optionsRequired: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Delivery by date range",
|
name: "Delivery by date range",
|
||||||
endpoint: "deliveryByDateRange",
|
endpoint: "deliveryByDateRange",
|
||||||
description: `Returns all Deliverys in selected date range IE: 1/1/${new Date(Date.now()).getFullYear()} to 1/31/${new Date(Date.now()).getFullYear()}`,
|
description: `Returns all Deliveries in selected date range IE: 1/1/${new Date(Date.now()).getFullYear()} to 1/31/${new Date(Date.now()).getFullYear()}`,
|
||||||
options: "startDate,endDate", // set as a string and each item will be seperated by a , this way we can split it later in the excel file.
|
options: "startDate,endDate",
|
||||||
|
optionsRequired: true,
|
||||||
|
howManyOptionsRequired: 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Get Customer Inventory",
|
||||||
|
endpoint: "customerInventory",
|
||||||
|
description: `Returns specific customer inventory based on there address ID, IE: 8,12,145. \nWith option to include specific warehousesIds, IE 36,41,5. \nNOTES: *leaving warehouse blank will just pull everything for the customer, Inventory dose not include PPOO or INV`,
|
||||||
|
options: "customer,whseToInclude",
|
||||||
|
optionsRequired: true,
|
||||||
|
howManyOptionsRequired: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Get open order",
|
||||||
|
endpoint: "openOrders",
|
||||||
|
description: `Returns open orders based on day count sent over, IE: startDay 15 days in the past endDay 5 days in the future, can be left empty for this default days`,
|
||||||
|
options: "startDay,endDay",
|
||||||
|
optionsRequired: true,
|
||||||
|
howManyOptionsRequired: 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Get inventory",
|
||||||
|
endpoint: "inventory",
|
||||||
|
description: `Returns all inventory, excludes inv location. adding an x in one of the options will enable it.`,
|
||||||
|
options: "includeRunningNumbers,locations,lots",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Fake EDI Update",
|
||||||
|
endpoint: "fakeEDIUpdate",
|
||||||
|
description: `Returns all open orders to correct and resubmit via lst demand mgt, leaving blank will get everything putting an address only returns the specified address. \nNOTE: only orders that were created via edi will populate here.`,
|
||||||
|
options: "address",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Production Data",
|
||||||
|
endpoint: "productionData",
|
||||||
|
description: `Returns all production data from the date range with the option to have 1 to many avs to search by.`,
|
||||||
|
options: "startDate,endDate,articles",
|
||||||
optionsRequired: true,
|
optionsRequired: true,
|
||||||
howManyOptionsRequired: 2,
|
howManyOptionsRequired: 2,
|
||||||
},
|
},
|
||||||
|
|||||||
68
backend/datamart/getDatamart.route.test.ts
Normal file
68
backend/datamart/getDatamart.route.test.ts
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
import express from "express";
|
||||||
|
import request from "supertest";
|
||||||
|
import { describe, expect, it, vi } from "vitest";
|
||||||
|
|
||||||
|
vi.mock("../db/db.controller.js", () => ({
|
||||||
|
db: {},
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.mock("../logger/logger.controller.js", () => ({
|
||||||
|
createLogger: () => ({
|
||||||
|
info: vi.fn(),
|
||||||
|
error: vi.fn(),
|
||||||
|
warn: vi.fn(),
|
||||||
|
debug: vi.fn(),
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.mock("./datamart.controller.js", () => ({
|
||||||
|
runDatamartQuery: vi.fn(async ({ name, options }) => ({
|
||||||
|
success: true,
|
||||||
|
message: `Ran ${name}`,
|
||||||
|
data: {
|
||||||
|
name,
|
||||||
|
options,
|
||||||
|
},
|
||||||
|
})),
|
||||||
|
}));
|
||||||
|
|
||||||
|
import { runDatamartQuery } from "./datamart.controller.js";
|
||||||
|
import getDatamartRoute from "./getDatamart.route.js";
|
||||||
|
|
||||||
|
function createTestApp() {
|
||||||
|
const app = express();
|
||||||
|
|
||||||
|
app.use(express.json());
|
||||||
|
app.use("/datamart", getDatamartRoute);
|
||||||
|
|
||||||
|
return app;
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("GET /datamart/:name", () => {
|
||||||
|
it("runs a datamart query by name and returns api response", async () => {
|
||||||
|
const app = createTestApp();
|
||||||
|
|
||||||
|
const res = await request(app).get("/datamart/orders").query({
|
||||||
|
value: "123",
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(res.status).toBe(200);
|
||||||
|
|
||||||
|
expect(runDatamartQuery).toHaveBeenCalledWith({
|
||||||
|
name: "orders",
|
||||||
|
options: {
|
||||||
|
value: "123",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(res.body.success).toBe(true);
|
||||||
|
expect(res.body.module).toBe("datamart");
|
||||||
|
expect(res.body.subModule).toBe("query");
|
||||||
|
expect(res.body.data).toEqual({
|
||||||
|
name: "orders",
|
||||||
|
options: {
|
||||||
|
value: "123",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import { Router } from "express";
|
import { Router } from "express";
|
||||||
|
import * as XLSX from "xlsx";
|
||||||
import { apiReturn } from "../utils/returnHelper.utils.js";
|
import { apiReturn } from "../utils/returnHelper.utils.js";
|
||||||
import { runDatamartQuery } from "./datamart.controller.js";
|
import { runDatamartQuery } from "./datamart.controller.js";
|
||||||
|
|
||||||
@@ -7,13 +8,73 @@ const r = Router();
|
|||||||
type Options = {
|
type Options = {
|
||||||
name: string;
|
name: string;
|
||||||
value: string;
|
value: string;
|
||||||
|
format: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
r.get("/:name", async (req, res) => {
|
r.get("/:name", async (req, res) => {
|
||||||
const { name } = req.params;
|
const { name } = req.params;
|
||||||
const options = req.query as Options;
|
const options = { ...req.query } as Options;
|
||||||
|
|
||||||
const dataRan = await runDatamartQuery({ name, options });
|
const dataRan = await runDatamartQuery({ name, options });
|
||||||
|
|
||||||
|
if (!dataRan.success) {
|
||||||
|
return apiReturn(res, {
|
||||||
|
success: false,
|
||||||
|
level: "error",
|
||||||
|
module: "datamart",
|
||||||
|
subModule: "query",
|
||||||
|
message: dataRan.message,
|
||||||
|
status: 500,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// XLSX Export
|
||||||
|
if (options.format?.toLowerCase() === "xlsx") {
|
||||||
|
const wb = XLSX.utils.book_new();
|
||||||
|
|
||||||
|
const ws = XLSX.utils.json_to_sheet(dataRan.data);
|
||||||
|
|
||||||
|
XLSX.utils.book_append_sheet(wb, ws, name);
|
||||||
|
|
||||||
|
const buffer = XLSX.write(wb, {
|
||||||
|
type: "buffer",
|
||||||
|
bookType: "xlsx",
|
||||||
|
});
|
||||||
|
|
||||||
|
res.setHeader(
|
||||||
|
"Content-Type",
|
||||||
|
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||||
|
);
|
||||||
|
|
||||||
|
res.setHeader("Content-Disposition", `attachment; filename="${name}.xlsx"`);
|
||||||
|
|
||||||
|
return res.send(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// CSV Export
|
||||||
|
if (options.format?.toLowerCase() === "csv") {
|
||||||
|
const rows = dataRan.data as any;
|
||||||
|
|
||||||
|
if (!rows.length) {
|
||||||
|
return res.status(200).send("");
|
||||||
|
}
|
||||||
|
|
||||||
|
const headers = Object.keys(rows[0]);
|
||||||
|
|
||||||
|
const csv = [
|
||||||
|
headers.join(","),
|
||||||
|
...rows.map((row: any) =>
|
||||||
|
headers
|
||||||
|
.map((h) => `"${String(row[h] ?? "").replace(/"/g, '""')}"`)
|
||||||
|
.join(","),
|
||||||
|
),
|
||||||
|
].join("\r\n");
|
||||||
|
|
||||||
|
res.setHeader("Content-Type", "text/csv");
|
||||||
|
res.setHeader("Content-Disposition", `attachment; filename="${name}.csv"`);
|
||||||
|
|
||||||
|
return res.send(csv);
|
||||||
|
}
|
||||||
|
|
||||||
return apiReturn(res, {
|
return apiReturn(res, {
|
||||||
success: dataRan.success,
|
success: dataRan.success,
|
||||||
level: "info",
|
level: "info",
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
import { drizzle } from "drizzle-orm/postgres-js";
|
import { drizzle } from "drizzle-orm/postgres-js";
|
||||||
import postgres from "postgres";
|
import postgres from "postgres";
|
||||||
|
import * as dockScans from "./schema/dockdoor.scans.schema.js";
|
||||||
|
import * as logs from "./schema/logs.schema.js";
|
||||||
|
import * as opendockAVCheck from "./schema/opendock_articleSetup.js";
|
||||||
|
import * as scanUserSchema from "./schema/scanUsers.js";
|
||||||
|
import * as settingsSchema from "./schema/settings.schema.js";
|
||||||
|
|
||||||
const dbURL = `postgres://${process.env.DATABASE_USER}:${process.env.DATABASE_PASSWORD}@${process.env.DATABASE_HOST}:${process.env.DATABASE_PORT}/${process.env.DATABASE_DB}`;
|
const dbURL = `postgres://${process.env.DATABASE_USER}:${process.env.DATABASE_PASSWORD}@${process.env.DATABASE_HOST}:${process.env.DATABASE_PORT}/${process.env.DATABASE_DB}`;
|
||||||
|
|
||||||
@@ -13,4 +18,14 @@ const queryClient = postgres(dbURL, {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export const db = drizzle({ client: queryClient });
|
//export const db = drizzle({ client: queryClient });
|
||||||
|
|
||||||
|
export const db = drizzle(queryClient, {
|
||||||
|
schema: {
|
||||||
|
...scanUserSchema,
|
||||||
|
...settingsSchema,
|
||||||
|
...opendockAVCheck,
|
||||||
|
...logs,
|
||||||
|
...dockScans,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|||||||
59
backend/db/db.listener.ts
Normal file
59
backend/db/db.listener.ts
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
import postgres from "postgres";
|
||||||
|
import { createLogger } from "../logger/logger.controller.js";
|
||||||
|
import { handleDbNotification } from "./db.router.js";
|
||||||
|
|
||||||
|
const log = createLogger({
|
||||||
|
module: "db",
|
||||||
|
subModule: "notifications",
|
||||||
|
});
|
||||||
|
|
||||||
|
const CHANNELS = [
|
||||||
|
"logs_inserted",
|
||||||
|
// "labels_inserted",
|
||||||
|
// "dock_scans_inserted",
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
type DbNotificationChannel = (typeof CHANNELS)[number];
|
||||||
|
|
||||||
|
type DbNotificationPayload = {
|
||||||
|
table: string;
|
||||||
|
action: "INSERT" | "UPDATE" | "DELETE";
|
||||||
|
id: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function startDbNotificationListener() {
|
||||||
|
const sql = postgres({
|
||||||
|
host: `${process.env.DATABASE_HOST}`,
|
||||||
|
port: Number(process.env.DATABASE_PORT),
|
||||||
|
database: `${process.env.DATABASE_DB}`,
|
||||||
|
username: process.env.DATABASE_USER,
|
||||||
|
password: process.env.DATABASE_PASSWORD,
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const channel of CHANNELS) {
|
||||||
|
await sql.listen(channel, async (rawPayload) => {
|
||||||
|
await processNotification(channel, rawPayload);
|
||||||
|
});
|
||||||
|
|
||||||
|
log.info({ stack: { channel } }, `Listening for ${channel}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function processNotification(
|
||||||
|
channel: DbNotificationChannel,
|
||||||
|
rawPayload: string,
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
const payload = JSON.parse(rawPayload) as DbNotificationPayload;
|
||||||
|
|
||||||
|
await handleDbNotification({
|
||||||
|
channel,
|
||||||
|
payload,
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
log.error(
|
||||||
|
{ stack: { channel, rawPayload, e }, notify: true },
|
||||||
|
"Failed processing DB notification",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
41
backend/db/db.router.ts
Normal file
41
backend/db/db.router.ts
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import { handleDockScanInsertedNotification } from "../dockdoorScanning/dockdoor.socket.notifications.js";
|
||||||
|
import { createLogger } from "../logger/logger.controller.js";
|
||||||
|
import { handleLogInsertedNotification } from "../logger/logger.socket.notifications.js";
|
||||||
|
|
||||||
|
const log = createLogger({
|
||||||
|
module: "db",
|
||||||
|
subModule: "notifications-router",
|
||||||
|
});
|
||||||
|
|
||||||
|
type DbNotification = {
|
||||||
|
channel: string;
|
||||||
|
payload: {
|
||||||
|
table: string;
|
||||||
|
action: "INSERT" | "UPDATE" | "DELETE";
|
||||||
|
id: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function handleDbNotification(notification: DbNotification) {
|
||||||
|
const { channel, payload } = notification;
|
||||||
|
|
||||||
|
switch (channel) {
|
||||||
|
case "logs_inserted":
|
||||||
|
await handleLogInsertedNotification(payload.id);
|
||||||
|
return;
|
||||||
|
|
||||||
|
// case "labels_inserted":
|
||||||
|
// await handleLabelInsertedNotification(payload.id);
|
||||||
|
// return;
|
||||||
|
|
||||||
|
case "dock_scan_inserted":
|
||||||
|
await handleDockScanInsertedNotification(payload.id);
|
||||||
|
return;
|
||||||
|
|
||||||
|
default:
|
||||||
|
log.warn(
|
||||||
|
{ stack: notification },
|
||||||
|
`Unhandled DB notification channel: ${channel}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
99
backend/db/db.setupNotifications.ts
Normal file
99
backend/db/db.setupNotifications.ts
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
import { sql } from "drizzle-orm";
|
||||||
|
import { db } from "../db/db.controller.js";
|
||||||
|
import { createLogger } from "../logger/logger.controller.js";
|
||||||
|
|
||||||
|
const log = createLogger({
|
||||||
|
module: "db",
|
||||||
|
subModule: "notifications",
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates/updates Postgres notification functions + triggers.
|
||||||
|
*
|
||||||
|
* Safe to run on every app startup.
|
||||||
|
* CREATE OR REPLACE updates the function.
|
||||||
|
* DROP TRIGGER IF EXISTS prevents duplicate triggers.
|
||||||
|
*/
|
||||||
|
export async function setupDbNotifications() {
|
||||||
|
log.info({}, "Setting up DB notifications");
|
||||||
|
|
||||||
|
await setupLogsNotifications();
|
||||||
|
await setupDockScansNotifications();
|
||||||
|
|
||||||
|
log.info({}, "DB notifications setup complete");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logs notification setup.
|
||||||
|
*
|
||||||
|
* Flow:
|
||||||
|
* 1. app inserts into logs table
|
||||||
|
* 2. trigger runs after insert
|
||||||
|
* 3. Postgres sends NOTIFY logs_inserted with the new log id
|
||||||
|
* 4. Node listener receives id and fetches/emits full row
|
||||||
|
*/
|
||||||
|
async function setupLogsNotifications() {
|
||||||
|
await db.execute(sql`
|
||||||
|
CREATE OR REPLACE FUNCTION notify_logs_inserted()
|
||||||
|
RETURNS trigger AS $$
|
||||||
|
BEGIN
|
||||||
|
PERFORM pg_notify(
|
||||||
|
'logs_inserted',
|
||||||
|
json_build_object(
|
||||||
|
'table', TG_TABLE_NAME,
|
||||||
|
'action', TG_OP,
|
||||||
|
'id', NEW.id
|
||||||
|
)::text
|
||||||
|
);
|
||||||
|
|
||||||
|
RETURN NEW;
|
||||||
|
END;
|
||||||
|
$$ LANGUAGE plpgsql;
|
||||||
|
`);
|
||||||
|
|
||||||
|
await db.execute(sql`
|
||||||
|
DROP TRIGGER IF EXISTS logs_inserted_notify_trigger ON logs;
|
||||||
|
`);
|
||||||
|
|
||||||
|
await db.execute(sql`
|
||||||
|
CREATE TRIGGER logs_inserted_notify_trigger
|
||||||
|
AFTER INSERT ON logs
|
||||||
|
FOR EACH ROW
|
||||||
|
EXECUTE FUNCTION notify_logs_inserted();
|
||||||
|
`);
|
||||||
|
|
||||||
|
log.info({}, "Logs DB notification trigger ready");
|
||||||
|
}
|
||||||
|
|
||||||
|
async function setupDockScansNotifications() {
|
||||||
|
await db.execute(sql`
|
||||||
|
CREATE OR REPLACE FUNCTION notify_dock_scan_inserted()
|
||||||
|
RETURNS trigger AS $$
|
||||||
|
BEGIN
|
||||||
|
PERFORM pg_notify(
|
||||||
|
'dock_scan_inserted',
|
||||||
|
json_build_object(
|
||||||
|
'table', TG_TABLE_NAME,
|
||||||
|
'action', TG_OP,
|
||||||
|
'id', NEW.id
|
||||||
|
)::text
|
||||||
|
);
|
||||||
|
|
||||||
|
RETURN NEW;
|
||||||
|
END;
|
||||||
|
$$ LANGUAGE plpgsql;
|
||||||
|
`);
|
||||||
|
|
||||||
|
await db.execute(sql`
|
||||||
|
DROP TRIGGER IF EXISTS dock_scan_inserted_notify_trigger ON dock_door_scans;
|
||||||
|
`);
|
||||||
|
|
||||||
|
await db.execute(sql`
|
||||||
|
CREATE TRIGGER dock_scan_inserted_notify_trigger
|
||||||
|
AFTER INSERT ON dock_door_scans
|
||||||
|
FOR EACH ROW
|
||||||
|
EXECUTE FUNCTION notify_dock_scan_inserted();
|
||||||
|
`);
|
||||||
|
|
||||||
|
log.info({}, "Dock scan DB notification trigger ready");
|
||||||
|
}
|
||||||
21
backend/db/db.socketSeed.ts
Normal file
21
backend/db/db.socketSeed.ts
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import { db } from "./db.controller.js";
|
||||||
|
|
||||||
|
export const getRecentLogs = ({
|
||||||
|
module,
|
||||||
|
submodule,
|
||||||
|
limit = 200,
|
||||||
|
}: {
|
||||||
|
module?: string | undefined;
|
||||||
|
submodule?: string | undefined;
|
||||||
|
limit?: number | undefined;
|
||||||
|
}) => {
|
||||||
|
return db.query.logs.findMany({
|
||||||
|
where: (logs, { and, eq }) =>
|
||||||
|
and(
|
||||||
|
module ? eq(logs.module, module) : undefined,
|
||||||
|
submodule ? eq(logs.subModule, submodule) : undefined,
|
||||||
|
),
|
||||||
|
orderBy: (logs, { desc }) => [desc(logs.createdAt)],
|
||||||
|
limit,
|
||||||
|
});
|
||||||
|
};
|
||||||
39
backend/db/schema/alplapurchase.schema.ts
Normal file
39
backend/db/schema/alplapurchase.schema.ts
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import {
|
||||||
|
integer,
|
||||||
|
jsonb,
|
||||||
|
pgTable,
|
||||||
|
text,
|
||||||
|
timestamp,
|
||||||
|
uuid,
|
||||||
|
} from "drizzle-orm/pg-core";
|
||||||
|
import { createInsertSchema, createSelectSchema } from "drizzle-zod";
|
||||||
|
import type { z } from "zod";
|
||||||
|
|
||||||
|
export const alplaPurchaseHistory = pgTable("alpla_purchase_history", {
|
||||||
|
id: uuid("id").defaultRandom().primaryKey(),
|
||||||
|
apo: integer("apo"),
|
||||||
|
revision: integer("revision"),
|
||||||
|
confirmed: integer("confirmed"),
|
||||||
|
status: integer("status"),
|
||||||
|
statusText: text("status_text"),
|
||||||
|
journalNum: integer("journal_num"),
|
||||||
|
add_date: timestamp("add_date", { withTimezone: true }).defaultNow(),
|
||||||
|
add_user: text("add_user"),
|
||||||
|
upd_user: text("upd_user"),
|
||||||
|
upd_date: timestamp("upd_date", { withTimezone: true }).defaultNow(),
|
||||||
|
remark: text("remark"),
|
||||||
|
approvedStatus: text("approved_status").default("new"),
|
||||||
|
position: jsonb("position").default([]),
|
||||||
|
createdAt: timestamp("created_at", { withTimezone: true }).defaultNow(),
|
||||||
|
updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const alplaPurchaseHistorySchema =
|
||||||
|
createSelectSchema(alplaPurchaseHistory);
|
||||||
|
export const newAlplaPurchaseHistorySchema =
|
||||||
|
createInsertSchema(alplaPurchaseHistory);
|
||||||
|
|
||||||
|
export type AlplaPurchaseHistory = z.infer<typeof alplaPurchaseHistorySchema>;
|
||||||
|
export type NewAlplaPurchaseHistory = z.infer<
|
||||||
|
typeof newAlplaPurchaseHistorySchema
|
||||||
|
>;
|
||||||
23
backend/db/schema/analytics.schema.ts
Normal file
23
backend/db/schema/analytics.schema.ts
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import { integer, pgTable, text, timestamp, uuid } from "drizzle-orm/pg-core";
|
||||||
|
|
||||||
|
export const analytics = pgTable("analytics", {
|
||||||
|
id: uuid("id").defaultRandom().primaryKey(),
|
||||||
|
|
||||||
|
createdAt: timestamp("created_at", { withTimezone: true })
|
||||||
|
.defaultNow()
|
||||||
|
.notNull(),
|
||||||
|
|
||||||
|
method: text("method").notNull(),
|
||||||
|
routePattern: text("route_pattern").notNull(),
|
||||||
|
actualPath: text("actual_path").notNull(),
|
||||||
|
|
||||||
|
statusCode: integer("status_code").notNull(),
|
||||||
|
durationMs: integer("duration_ms").notNull(),
|
||||||
|
|
||||||
|
module: text("module"),
|
||||||
|
userId: text("user_id"),
|
||||||
|
userEmail: text("user_email"),
|
||||||
|
|
||||||
|
ipAddress: text("ip_address"),
|
||||||
|
userAgent: text("user_agent"),
|
||||||
|
});
|
||||||
@@ -16,13 +16,13 @@ export const jobAuditLog = pgTable(
|
|||||||
id: uuid("id").defaultRandom().primaryKey(),
|
id: uuid("id").defaultRandom().primaryKey(),
|
||||||
jobName: text("job_name"),
|
jobName: text("job_name"),
|
||||||
startedAt: timestamp("start_at"),
|
startedAt: timestamp("start_at"),
|
||||||
finishedAt: timestamp("finished_at"),
|
finishedAt: timestamp("finished_at", { withTimezone: true }),
|
||||||
durationMs: integer("duration_ms"),
|
durationMs: integer("duration_ms"),
|
||||||
status: text("status"), //success | error
|
status: text("status"), //success | error
|
||||||
errorMessage: text("error_message"),
|
errorMessage: text("error_message"),
|
||||||
errorStack: text("error_stack"),
|
errorStack: text("error_stack"),
|
||||||
metadata: jsonb("meta_data"),
|
metadata: jsonb("meta_data"),
|
||||||
createdAt: timestamp("created_at").defaultNow(),
|
createdAt: timestamp("created_at", { withTimezone: true }).defaultNow(),
|
||||||
},
|
},
|
||||||
(table) => {
|
(table) => {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export const user = pgTable("user", {
|
|||||||
emailVerified: boolean("email_verified").default(false).notNull(),
|
emailVerified: boolean("email_verified").default(false).notNull(),
|
||||||
image: text("image"),
|
image: text("image"),
|
||||||
createdAt: timestamp("created_at").defaultNow().notNull(),
|
createdAt: timestamp("created_at").defaultNow().notNull(),
|
||||||
updatedAt: timestamp("updated_at")
|
updatedAt: timestamp("updated_at", { withTimezone: true })
|
||||||
.defaultNow()
|
.defaultNow()
|
||||||
.$onUpdate(() => /* @__PURE__ */ new Date())
|
.$onUpdate(() => /* @__PURE__ */ new Date())
|
||||||
.notNull(),
|
.notNull(),
|
||||||
|
|||||||
10
backend/db/schema/buildHistory.schema.ts
Normal file
10
backend/db/schema/buildHistory.schema.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import { integer, pgTable, text, timestamp, uuid } from "drizzle-orm/pg-core";
|
||||||
|
|
||||||
|
export const deploymentHistory = pgTable("deployment_history", {
|
||||||
|
id: uuid("id").defaultRandom().primaryKey(),
|
||||||
|
serverId: uuid("server_id"),
|
||||||
|
buildNumber: integer("build_number").notNull(),
|
||||||
|
status: text("status").notNull(), // started, success, failed
|
||||||
|
message: text("message"),
|
||||||
|
createdAt: timestamp("created_at", { withTimezone: true }).defaultNow(),
|
||||||
|
});
|
||||||
49
backend/db/schema/dailyAnalytics.schema.ts
Normal file
49
backend/db/schema/dailyAnalytics.schema.ts
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
import {
|
||||||
|
date,
|
||||||
|
integer,
|
||||||
|
pgTable,
|
||||||
|
text,
|
||||||
|
timestamp,
|
||||||
|
unique,
|
||||||
|
uuid,
|
||||||
|
} from "drizzle-orm/pg-core";
|
||||||
|
|
||||||
|
export const analyticsDaily = pgTable(
|
||||||
|
"analytics_daily",
|
||||||
|
{
|
||||||
|
id: uuid("id").defaultRandom().primaryKey(),
|
||||||
|
|
||||||
|
businessDate: date("business_date", { mode: "string" }).notNull(),
|
||||||
|
|
||||||
|
method: text("method").notNull(),
|
||||||
|
routePattern: text("route_pattern").notNull(),
|
||||||
|
module: text("module").notNull(),
|
||||||
|
|
||||||
|
totalHits: integer("total_hits").notNull(),
|
||||||
|
uniqueUsers: integer("unique_users").notNull(),
|
||||||
|
|
||||||
|
successCount: integer("success_count").notNull(),
|
||||||
|
errorCount: integer("error_count").notNull(),
|
||||||
|
|
||||||
|
avgDurationMs: integer("avg_duration_ms").notNull(),
|
||||||
|
maxDurationMs: integer("max_duration_ms").notNull(),
|
||||||
|
|
||||||
|
firstHitAt: timestamp("first_hit_at", { withTimezone: true }).notNull(),
|
||||||
|
lastHitAt: timestamp("last_hit_at", { withTimezone: true }).notNull(),
|
||||||
|
|
||||||
|
createdAt: timestamp("created_at", { withTimezone: true })
|
||||||
|
.defaultNow()
|
||||||
|
.notNull(),
|
||||||
|
updatedAt: timestamp("updated_at", { withTimezone: true })
|
||||||
|
.defaultNow()
|
||||||
|
.notNull(),
|
||||||
|
},
|
||||||
|
(table) => [
|
||||||
|
unique("analytics_daily_business_route_unique").on(
|
||||||
|
table.businessDate,
|
||||||
|
table.method,
|
||||||
|
table.routePattern,
|
||||||
|
table.module,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
@@ -18,9 +18,9 @@ export const datamart = pgTable("datamart", {
|
|||||||
active: boolean("active").default(true),
|
active: boolean("active").default(true),
|
||||||
options: text("options").default(""),
|
options: text("options").default(""),
|
||||||
public: boolean("public_access").default(false),
|
public: boolean("public_access").default(false),
|
||||||
add_date: timestamp("add_date").defaultNow(),
|
add_date: timestamp("add_date", { withTimezone: true }).defaultNow(),
|
||||||
add_user: text("add_user").default("lst-system"),
|
add_user: text("add_user").default("lst-system"),
|
||||||
upd_date: timestamp("upd_date").defaultNow(),
|
upd_date: timestamp("upd_date", { withTimezone: true }).defaultNow(),
|
||||||
upd_user: text("upd_user").default("lst-system"),
|
upd_user: text("upd_user").default("lst-system"),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
23
backend/db/schema/dockdoor.scans.schema.ts
Normal file
23
backend/db/schema/dockdoor.scans.schema.ts
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import { pgTable, text, timestamp, uuid } from "drizzle-orm/pg-core";
|
||||||
|
import { createInsertSchema, createSelectSchema } from "drizzle-zod";
|
||||||
|
import type { z } from "zod";
|
||||||
|
|
||||||
|
export const dockDoorScans = pgTable("dock_door_scans", {
|
||||||
|
id: uuid("id").defaultRandom().primaryKey(),
|
||||||
|
dockId: text("dock_id").notNull(),
|
||||||
|
loadingOrder: text("loading_order").notNull(),
|
||||||
|
loadingUnit: text("loading_Unit"), // can be running number or sscc depending on where it came from
|
||||||
|
loadingUnitStatus: text("loading_unit_status").default("loaded"), // TODO: add enums on the status of each load.
|
||||||
|
message: text("message"), // the response it gave when scanning
|
||||||
|
status: text("status").default("active"), // TODO: add in enums for this
|
||||||
|
add_date: timestamp("add_date", { withTimezone: true }).defaultNow(),
|
||||||
|
add_user: text("add_user").default("lst-system"),
|
||||||
|
upd_date: timestamp("upd_date", { withTimezone: true }).defaultNow(),
|
||||||
|
upd_user: text("upd_user").default("lst-system"),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const dockDoorScansSchema = createSelectSchema(dockDoorScans);
|
||||||
|
export const newDockDoorScansSchema = createInsertSchema(dockDoorScans);
|
||||||
|
|
||||||
|
export type DockDoorScans = z.infer<typeof dockDoorScansSchema>;
|
||||||
|
export type NewDockDoorScans = z.infer<typeof newDockDoorScansSchema>;
|
||||||
22
backend/db/schema/dockdoor.schema.ts
Normal file
22
backend/db/schema/dockdoor.schema.ts
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import { boolean, pgTable, text, timestamp, uuid } from "drizzle-orm/pg-core";
|
||||||
|
import { createInsertSchema, createSelectSchema } from "drizzle-zod";
|
||||||
|
import type { z } from "zod";
|
||||||
|
|
||||||
|
export const dockDoorScanners = pgTable("dock_door_scanners", {
|
||||||
|
id: uuid("id").defaultRandom().primaryKey(),
|
||||||
|
ip: text("ip").notNull(),
|
||||||
|
name: text("name").unique(),
|
||||||
|
dockId: text("dock_id"),
|
||||||
|
active: boolean("active").default(true),
|
||||||
|
currentLoadingOrder: text("current_loading_order").default(""),
|
||||||
|
add_date: timestamp("add_date", { withTimezone: true }).defaultNow(),
|
||||||
|
add_user: text("add_user").default("lst-system"),
|
||||||
|
upd_date: timestamp("upd_date", { withTimezone: true }).defaultNow(),
|
||||||
|
upd_user: text("upd_user").default("lst-system"),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const dockDoorScannersSchema = createSelectSchema(dockDoorScanners);
|
||||||
|
export const newDockDoorScannersSchema = createInsertSchema(dockDoorScanners);
|
||||||
|
|
||||||
|
export type DockDoorScanners = z.infer<typeof dockDoorScannersSchema>;
|
||||||
|
export type NewDockDoorScanners = z.infer<typeof newDockDoorScannersSchema>;
|
||||||
22
backend/db/schema/forecastImports.schema.ts
Normal file
22
backend/db/schema/forecastImports.schema.ts
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import { jsonb, pgTable, text, timestamp, uuid } from "drizzle-orm/pg-core";
|
||||||
|
import { createInsertSchema, createSelectSchema } from "drizzle-zod";
|
||||||
|
import type { z } from "zod";
|
||||||
|
|
||||||
|
export const forecastImport = pgTable("forecast_import", {
|
||||||
|
id: uuid("id").defaultRandom().primaryKey(),
|
||||||
|
receivingPlantId: text("receiving_plant_id").notNull(),
|
||||||
|
documentName: text("documentName"),
|
||||||
|
sender: text("sender"),
|
||||||
|
customerId: text("customer_id"),
|
||||||
|
rawData: jsonb("raw_data").default([]),
|
||||||
|
add_date: timestamp("add_date", { withTimezone: true }).defaultNow(),
|
||||||
|
add_user: text("add_user").default("lst-system"),
|
||||||
|
upd_date: timestamp("upd_date", { withTimezone: true }).defaultNow(),
|
||||||
|
upd_user: text("upd_user").default("lst-system"),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const forecastImportSchema = createSelectSchema(forecastImport);
|
||||||
|
export const newForecastImportSchema = createInsertSchema(forecastImport);
|
||||||
|
|
||||||
|
export type ForecastImport = z.infer<typeof forecastImportSchema>;
|
||||||
|
export type NeworecastImport = z.infer<typeof newForecastImportSchema>;
|
||||||
30
backend/db/schema/historicalInv.schema.ts
Normal file
30
backend/db/schema/historicalInv.schema.ts
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
import { date, pgTable, text, timestamp, uuid } from "drizzle-orm/pg-core";
|
||||||
|
import { createInsertSchema, createSelectSchema } from "drizzle-zod";
|
||||||
|
import type z from "zod";
|
||||||
|
|
||||||
|
export const invHistoricalData = pgTable("inv_historical_data", {
|
||||||
|
inv: uuid("id").defaultRandom().primaryKey(),
|
||||||
|
histDate: date("hist_date").notNull(), // this date should always be yesterday when we post it.
|
||||||
|
plantToken: text("plant_token"),
|
||||||
|
article: text("article").notNull(),
|
||||||
|
articleDescription: text("article_description").notNull(),
|
||||||
|
materialType: text("material_type"),
|
||||||
|
total_QTY: text("total_QTY"),
|
||||||
|
available_QTY: text("available_QTY"),
|
||||||
|
coa_QTY: text("coa_QTY"),
|
||||||
|
held_QTY: text("held_QTY"),
|
||||||
|
consignment_QTY: text("consignment_qty"),
|
||||||
|
lot_Number: text("lot_number"),
|
||||||
|
locationId: text("location_id"),
|
||||||
|
location: text("location"),
|
||||||
|
whseId: text("whse_id").default(""),
|
||||||
|
whseName: text("whse_name").default("missing whseName"),
|
||||||
|
upd_user: text("upd_user").default("lst-system"),
|
||||||
|
upd_date: timestamp("upd_date", { withTimezone: true }).defaultNow(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const invHistoricalDataSchema = createSelectSchema(invHistoricalData);
|
||||||
|
export const newInvHistoricalDataSchema = createInsertSchema(invHistoricalData);
|
||||||
|
|
||||||
|
export type InvHistoricalData = z.infer<typeof invHistoricalDataSchema>;
|
||||||
|
export type NewInvHistoricalData = z.infer<typeof newInvHistoricalDataSchema>;
|
||||||
@@ -18,7 +18,7 @@ export const logs = pgTable("logs", {
|
|||||||
stack: jsonb("stack").default([]),
|
stack: jsonb("stack").default([]),
|
||||||
checked: boolean("checked").default(false),
|
checked: boolean("checked").default(false),
|
||||||
hostname: text("hostname"),
|
hostname: text("hostname"),
|
||||||
createdAt: timestamp("created_at").defaultNow(),
|
createdAt: timestamp("created_at", { withTimezone: true }).defaultNow(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const logSchema = createSelectSchema(logs);
|
export const logSchema = createSelectSchema(logs);
|
||||||
|
|||||||
@@ -1,25 +0,0 @@
|
|||||||
import {
|
|
||||||
integer,
|
|
||||||
jsonb,
|
|
||||||
pgTable,
|
|
||||||
text,
|
|
||||||
timestamp,
|
|
||||||
uuid,
|
|
||||||
} from "drizzle-orm/pg-core";
|
|
||||||
import { createInsertSchema, createSelectSchema } from "drizzle-zod";
|
|
||||||
import type { z } from "zod";
|
|
||||||
|
|
||||||
export const opendockApt = pgTable("opendock_apt", {
|
|
||||||
id: uuid("id").defaultRandom().primaryKey(),
|
|
||||||
release: integer("release").unique(),
|
|
||||||
openDockAptId: text("open_dock_apt_id").notNull(),
|
|
||||||
appointment: jsonb("appointment").default([]),
|
|
||||||
upd_date: timestamp("upd_date").defaultNow(),
|
|
||||||
createdAt: timestamp("created_at").defaultNow(),
|
|
||||||
});
|
|
||||||
|
|
||||||
export const opendockAptSchema = createSelectSchema(opendockApt);
|
|
||||||
export const newOpendockAptSchema = createInsertSchema(opendockApt);
|
|
||||||
|
|
||||||
export type OpendockApt = z.infer<typeof opendockAptSchema>;
|
|
||||||
export type NewOpendockApt = z.infer<typeof newOpendockAptSchema>;
|
|
||||||
39
backend/db/schema/opendock_apt.schema.ts
Normal file
39
backend/db/schema/opendock_apt.schema.ts
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import {
|
||||||
|
index,
|
||||||
|
integer,
|
||||||
|
jsonb,
|
||||||
|
pgTable,
|
||||||
|
text,
|
||||||
|
timestamp,
|
||||||
|
uuid,
|
||||||
|
} from "drizzle-orm/pg-core";
|
||||||
|
import { createInsertSchema, createSelectSchema } from "drizzle-zod";
|
||||||
|
import type { z } from "zod";
|
||||||
|
|
||||||
|
export const opendockApt = pgTable(
|
||||||
|
"opendock_apt",
|
||||||
|
{
|
||||||
|
id: uuid("id").defaultRandom().primaryKey(),
|
||||||
|
release: integer("release").notNull().unique("opendock_apt_release_unique"),
|
||||||
|
openDockAptId: text("open_dock_apt_id").notNull(),
|
||||||
|
status: text("status").default("active"),
|
||||||
|
appointment: jsonb("appointment").notNull().default([]),
|
||||||
|
upd_date: timestamp("upd_date", { withTimezone: true })
|
||||||
|
.notNull()
|
||||||
|
.defaultNow(),
|
||||||
|
createdAt: timestamp("created_at", { withTimezone: true })
|
||||||
|
.notNull()
|
||||||
|
.defaultNow(),
|
||||||
|
},
|
||||||
|
(table) => ({
|
||||||
|
openDockAptIdIdx: index("opendock_apt_opendock_id_idx").on(
|
||||||
|
table.openDockAptId,
|
||||||
|
),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
export const opendockAptSchema = createSelectSchema(opendockApt);
|
||||||
|
export const newOpendockAptSchema = createInsertSchema(opendockApt);
|
||||||
|
|
||||||
|
export type OpendockApt = z.infer<typeof opendockAptSchema>;
|
||||||
|
export type NewOpendockApt = z.infer<typeof newOpendockAptSchema>;
|
||||||
50
backend/db/schema/opendock_articleSetup.ts
Normal file
50
backend/db/schema/opendock_articleSetup.ts
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
import {
|
||||||
|
integer,
|
||||||
|
pgEnum,
|
||||||
|
pgTable,
|
||||||
|
text,
|
||||||
|
timestamp,
|
||||||
|
unique,
|
||||||
|
uuid,
|
||||||
|
} from "drizzle-orm/pg-core";
|
||||||
|
import { createInsertSchema, createSelectSchema } from "drizzle-zod";
|
||||||
|
import type { z } from "zod";
|
||||||
|
|
||||||
|
export const loadTypeEnum = pgEnum("load_type", ["drop", "live"]);
|
||||||
|
|
||||||
|
export const opendockArticleSetup = pgTable(
|
||||||
|
"opendock_article_setup",
|
||||||
|
{
|
||||||
|
id: uuid("id").defaultRandom().primaryKey(),
|
||||||
|
av: integer("av").notNull(),
|
||||||
|
description: text("description").notNull(),
|
||||||
|
customer: text("customer").notNull(), // customer should be a concat of the ID - Desc
|
||||||
|
customerDescription: text("customer_description").notNull(),
|
||||||
|
loadType: loadTypeEnum("load_type").notNull().default("drop"),
|
||||||
|
dock: text("dock").notNull(),
|
||||||
|
upd_date: timestamp("upd_date", { withTimezone: true })
|
||||||
|
.notNull()
|
||||||
|
.defaultNow(),
|
||||||
|
upd_user: text("upd_user").notNull().default("lst-system"),
|
||||||
|
createdAt: timestamp("created_at", { withTimezone: true })
|
||||||
|
.notNull()
|
||||||
|
.defaultNow(),
|
||||||
|
add_user: text("add_user").notNull().default("lst-system"),
|
||||||
|
},
|
||||||
|
(table) => ({
|
||||||
|
uniqueAvCustomer: unique("uq_opendock_article_setup_av_customer").on(
|
||||||
|
table.av,
|
||||||
|
table.customer,
|
||||||
|
),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
export const opendockArticleSetupSchema =
|
||||||
|
createSelectSchema(opendockArticleSetup);
|
||||||
|
export const newOpendockArticleSetupSchema =
|
||||||
|
createInsertSchema(opendockArticleSetup);
|
||||||
|
|
||||||
|
export type OpendockArticleSetup = z.infer<typeof opendockArticleSetupSchema>;
|
||||||
|
export type NewOpendockArticleSetup = z.infer<
|
||||||
|
typeof newOpendockArticleSetupSchema
|
||||||
|
>;
|
||||||
25
backend/db/schema/opendock_docks.ts
Normal file
25
backend/db/schema/opendock_docks.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { pgTable, text, timestamp, uuid } from "drizzle-orm/pg-core";
|
||||||
|
import { createInsertSchema, createSelectSchema } from "drizzle-zod";
|
||||||
|
import type { z } from "zod";
|
||||||
|
|
||||||
|
export const opendockDockSetup = pgTable("opendock_dock_setup", {
|
||||||
|
id: uuid("id").defaultRandom().primaryKey(),
|
||||||
|
name: text("name").notNull(),
|
||||||
|
dockID: text("dock_id").notNull(),
|
||||||
|
upd_date: timestamp("upd_date", { withTimezone: true })
|
||||||
|
.notNull()
|
||||||
|
.defaultNow(),
|
||||||
|
upd_user: text("upd_user").notNull().default("lst-system"),
|
||||||
|
createdAt: timestamp("created_at", { withTimezone: true })
|
||||||
|
.notNull()
|
||||||
|
.defaultNow(),
|
||||||
|
add_user: text("add_user").notNull().default("lst-system"),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const opendockDockSetupSchema = createSelectSchema(opendockDockSetup);
|
||||||
|
export const newOpendockDockSetupSchema = createInsertSchema(opendockDockSetup);
|
||||||
|
|
||||||
|
export type OpendockArticleSetup = z.infer<typeof opendockDockSetupSchema>;
|
||||||
|
export type NewOpendockArticleSetup = z.infer<
|
||||||
|
typeof newOpendockDockSetupSchema
|
||||||
|
>;
|
||||||
@@ -1,6 +1,11 @@
|
|||||||
import { integer, pgTable, text } from "drizzle-orm/pg-core";
|
import { integer, pgTable, text, timestamp } from "drizzle-orm/pg-core";
|
||||||
|
|
||||||
export const opendockApt = pgTable("printer_log", {
|
export const printerLog = pgTable("printer_log", {
|
||||||
id: integer().primaryKey().generatedAlwaysAsIdentity(),
|
id: integer().primaryKey().generatedAlwaysAsIdentity(),
|
||||||
name: text("name").notNull(),
|
name: text("name"),
|
||||||
|
ip: text("ip"),
|
||||||
|
printerSN: text("printer_sn"),
|
||||||
|
condition: text("condition").notNull(),
|
||||||
|
message: text("message"),
|
||||||
|
createdAt: timestamp("created_at", { withTimezone: true }).defaultNow(),
|
||||||
});
|
});
|
||||||
|
|||||||
44
backend/db/schema/printers.schema.ts
Normal file
44
backend/db/schema/printers.schema.ts
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
import {
|
||||||
|
boolean,
|
||||||
|
integer,
|
||||||
|
jsonb,
|
||||||
|
pgTable,
|
||||||
|
text,
|
||||||
|
timestamp,
|
||||||
|
uniqueIndex,
|
||||||
|
uuid,
|
||||||
|
} from "drizzle-orm/pg-core";
|
||||||
|
import { createInsertSchema, createSelectSchema } from "drizzle-zod";
|
||||||
|
import type z from "zod";
|
||||||
|
|
||||||
|
export const printerData = pgTable(
|
||||||
|
"printer_data",
|
||||||
|
{
|
||||||
|
id: uuid("id").defaultRandom().primaryKey(),
|
||||||
|
humanReadableId: text("humanReadable_id").unique().notNull(),
|
||||||
|
name: text("name").notNull(),
|
||||||
|
ipAddress: text("ipAddress"),
|
||||||
|
port: integer("port"),
|
||||||
|
status: text("status"),
|
||||||
|
statusText: text("statusText"),
|
||||||
|
printerSN: text("printer_sn"),
|
||||||
|
lastTimePrinted: timestamp("last_time_printed").notNull().defaultNow(),
|
||||||
|
assigned: boolean("assigned").default(false),
|
||||||
|
remark: text("remark"),
|
||||||
|
printDelay: integer("printDelay").default(90),
|
||||||
|
processes: jsonb("processes").default([]),
|
||||||
|
printDelayOverride: boolean("print_delay_override").default(false), // this will be more for if we have the lot time active but want to over ride this single line for some reason
|
||||||
|
add_Date: timestamp("add_Date", { withTimezone: true }).defaultNow(),
|
||||||
|
upd_date: timestamp("upd_date", { withTimezone: true }).defaultNow(),
|
||||||
|
},
|
||||||
|
(table) => [
|
||||||
|
//uniqueIndex("emailUniqueIndex").on(sql`lower(${table.email})`),
|
||||||
|
uniqueIndex("printer_id").on(table.humanReadableId),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
export const printerSchema = createSelectSchema(printerData);
|
||||||
|
export const newPrinterSchema = createInsertSchema(printerData);
|
||||||
|
|
||||||
|
export type Printer = z.infer<typeof printerSchema>;
|
||||||
|
export type NewPrinter = z.infer<typeof newPrinterSchema>;
|
||||||
48
backend/db/schema/scanUsers.ts
Normal file
48
backend/db/schema/scanUsers.ts
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
import {
|
||||||
|
boolean,
|
||||||
|
jsonb,
|
||||||
|
pgEnum,
|
||||||
|
pgTable,
|
||||||
|
text,
|
||||||
|
timestamp,
|
||||||
|
unique,
|
||||||
|
uuid,
|
||||||
|
} from "drizzle-orm/pg-core";
|
||||||
|
import { createInsertSchema, createSelectSchema } from "drizzle-zod";
|
||||||
|
import type z from "zod";
|
||||||
|
|
||||||
|
export const mobileRoleEnum = pgEnum("mobile_role", [
|
||||||
|
"user",
|
||||||
|
"lead",
|
||||||
|
"manager",
|
||||||
|
"admin",
|
||||||
|
]);
|
||||||
|
|
||||||
|
export const scanUser = pgTable(
|
||||||
|
"scan_users",
|
||||||
|
{
|
||||||
|
id: uuid("id").defaultRandom().primaryKey(),
|
||||||
|
name: text("name").notNull(), // the user that will be using the scanner
|
||||||
|
scannerId: text("scanner_id").unique().notNull(),
|
||||||
|
pinNumber: text("pin_number").unique().notNull(),
|
||||||
|
pinHash: text("pin_hash").notNull(),
|
||||||
|
excludedCommand: jsonb("excluded_commands").default([]),
|
||||||
|
role: mobileRoleEnum("role").notNull().default("user"),
|
||||||
|
active: boolean("active").default(true),
|
||||||
|
lastScan: timestamp("last_scan").defaultNow(),
|
||||||
|
add_Date: timestamp("add_Date", { withTimezone: true }).defaultNow(),
|
||||||
|
upd_date: timestamp("upd_date", { withTimezone: true }).defaultNow(),
|
||||||
|
},
|
||||||
|
(table) => ({
|
||||||
|
userNotificationUnique: unique("scan_user_unique").on(
|
||||||
|
table.scannerId,
|
||||||
|
table.pinNumber,
|
||||||
|
),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
export const scanUserSchema = createSelectSchema(scanUser);
|
||||||
|
export const newsSanUserSchema = createInsertSchema(scanUser);
|
||||||
|
|
||||||
|
export type ScanUser = z.infer<typeof scanUserSchema>;
|
||||||
|
export type NewScanUser = z.infer<typeof newsSanUserSchema>;
|
||||||
23
backend/db/schema/scanlog.schema.ts
Normal file
23
backend/db/schema/scanlog.schema.ts
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import { jsonb, pgTable, text, timestamp, uuid } from "drizzle-orm/pg-core";
|
||||||
|
import { createInsertSchema, createSelectSchema } from "drizzle-zod";
|
||||||
|
import type z from "zod";
|
||||||
|
|
||||||
|
export const scanLog = pgTable("scan_log", {
|
||||||
|
id: uuid("id").defaultRandom().primaryKey(),
|
||||||
|
user: text("user"),
|
||||||
|
scannerId: text("scanner_id"),
|
||||||
|
message: text("message").notNull(),
|
||||||
|
prompt: text("prompt"),
|
||||||
|
commandDescription: text("command_description"),
|
||||||
|
runningNumber: text("running_number").default("0"),
|
||||||
|
status: text("status"),
|
||||||
|
scannerVersion: text("scanner_version").default("0"),
|
||||||
|
lines: jsonb("lines").default([]),
|
||||||
|
add_Date: timestamp("add_date", { withTimezone: true }).defaultNow(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const scanLogSchema = createSelectSchema(scanLog);
|
||||||
|
export const newScanLogSchema = createInsertSchema(scanLog);
|
||||||
|
|
||||||
|
export type Printer = z.infer<typeof scanLogSchema>;
|
||||||
|
export type NewPrinter = z.infer<typeof newScanLogSchema>;
|
||||||
40
backend/db/schema/serverData.schema.ts
Normal file
40
backend/db/schema/serverData.schema.ts
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
import {
|
||||||
|
boolean,
|
||||||
|
integer,
|
||||||
|
pgTable,
|
||||||
|
text,
|
||||||
|
timestamp,
|
||||||
|
uuid,
|
||||||
|
} from "drizzle-orm/pg-core";
|
||||||
|
import { createInsertSchema, createSelectSchema } from "drizzle-zod";
|
||||||
|
import type z from "zod";
|
||||||
|
|
||||||
|
export const serverData = pgTable(
|
||||||
|
"server_data",
|
||||||
|
{
|
||||||
|
server_id: uuid("id").defaultRandom().primaryKey(),
|
||||||
|
name: text("name").notNull(),
|
||||||
|
server: text("server"),
|
||||||
|
plantToken: text("plant_token").notNull().unique(),
|
||||||
|
idAddress: text("id_address"),
|
||||||
|
greatPlainsPlantCode: text("great_plains_plant_code"),
|
||||||
|
contactEmail: text("contact_email"),
|
||||||
|
contactPhone: text("contact_phone"),
|
||||||
|
active: boolean("active").default(true),
|
||||||
|
serverLoc: text("server_loc"),
|
||||||
|
lastUpdated: timestamp("last_updated", { withTimezone: true }).defaultNow(),
|
||||||
|
buildNumber: integer("build_number"),
|
||||||
|
isUpgrading: boolean("is_upgrading").default(false),
|
||||||
|
},
|
||||||
|
|
||||||
|
// (table) => [
|
||||||
|
// // uniqueIndex('emailUniqueIndex').on(sql`lower(${table.email})`),
|
||||||
|
// uniqueIndex("plant_token").on(table.plantToken),
|
||||||
|
// ],
|
||||||
|
);
|
||||||
|
|
||||||
|
export const serverDataSchema = createSelectSchema(serverData);
|
||||||
|
export const newServerDataSchema = createInsertSchema(serverData);
|
||||||
|
|
||||||
|
export type ServerDataSchema = z.infer<typeof serverDataSchema>;
|
||||||
|
export type NewServerData = z.infer<typeof newServerDataSchema>;
|
||||||
@@ -32,13 +32,13 @@ export const settings = pgTable(
|
|||||||
settingType: settingType(),
|
settingType: settingType(),
|
||||||
seedVersion: integer("seed_version").default(1), // this is intended for if we want to update the settings.
|
seedVersion: integer("seed_version").default(1), // this is intended for if we want to update the settings.
|
||||||
add_User: text("add_User").default("LST_System").notNull(),
|
add_User: text("add_User").default("LST_System").notNull(),
|
||||||
add_Date: timestamp("add_Date").defaultNow(),
|
add_Date: timestamp("add_Date", { withTimezone: true }).defaultNow(),
|
||||||
upd_user: text("upd_User").default("LST_System").notNull(),
|
upd_user: text("upd_User").default("LST_System").notNull(),
|
||||||
upd_date: timestamp("upd_date").defaultNow(),
|
upd_date: timestamp("upd_date", { withTimezone: true }).defaultNow(),
|
||||||
},
|
},
|
||||||
(table) => [
|
(table) => [
|
||||||
// uniqueIndex('emailUniqueIndex').on(sql`lower(${table.email})`),
|
// uniqueIndex('emailUniqueIndex').on(sql`lower(${table.email})`),
|
||||||
uniqueIndex("name").on(table.name),
|
uniqueIndex("settings_name_unique").on(table.name),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,27 @@
|
|||||||
import type { InferSelectModel } from "drizzle-orm";
|
import {
|
||||||
import { integer, pgTable, text, timestamp } from "drizzle-orm/pg-core";
|
boolean,
|
||||||
|
integer,
|
||||||
|
jsonb,
|
||||||
|
pgTable,
|
||||||
|
text,
|
||||||
|
timestamp,
|
||||||
|
} from "drizzle-orm/pg-core";
|
||||||
|
import { createInsertSchema, createSelectSchema } from "drizzle-zod";
|
||||||
|
import type z from "zod";
|
||||||
|
|
||||||
export const serverStats = pgTable("stats", {
|
export const appStats = pgTable("app_stats", {
|
||||||
id: text("id").primaryKey().default("serverStats"),
|
id: text("id").primaryKey().default("primary"),
|
||||||
build: integer("build").notNull().default(1),
|
currentBuild: integer("current_build").notNull().default(1),
|
||||||
lastUpdate: timestamp("last_update").defaultNow(),
|
lastBuildAt: timestamp("last_build_at", { withTimezone: true }),
|
||||||
|
lastDeployAt: timestamp("last_deploy_at", { withTimezone: true }),
|
||||||
|
building: boolean("building").notNull().default(false),
|
||||||
|
updating: boolean("updating").notNull().default(false),
|
||||||
|
lastUpdated: timestamp("last_updated", { withTimezone: true }).defaultNow(),
|
||||||
|
meta: jsonb("meta").$type<Record<string, unknown>>().default({}),
|
||||||
});
|
});
|
||||||
|
|
||||||
export type ServerStats = InferSelectModel<typeof serverStats>;
|
export const appStatsSchema = createSelectSchema(appStats);
|
||||||
|
export const newAppStatsSchema = createInsertSchema(appStats, {});
|
||||||
|
|
||||||
|
export type AppStats = z.infer<typeof appStatsSchema>;
|
||||||
|
export type NewAppStats = z.infer<typeof newAppStatsSchema>;
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
import { addDays, subDays } from "date-fns";
|
||||||
|
import { format } from "date-fns-tz";
|
||||||
|
import { Router } from "express";
|
||||||
|
import { runProdApi } from "../utils/prodEndpoint.utils.js";
|
||||||
|
import { apiReturn } from "../utils/returnHelper.utils.js";
|
||||||
|
|
||||||
|
const r = Router();
|
||||||
|
|
||||||
|
r.get("/", async (_, res) => {
|
||||||
|
const orders = await runProdApi({
|
||||||
|
method: "post",
|
||||||
|
endpoint: "/public/v1.0/OutboundDeliveries/LoadingOrders/Search",
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
loadingDateFrom: format(subDays(new Date(Date.now()), 3), "yyyy-MM-dd"),
|
||||||
|
loadingDateTo: format(addDays(new Date(Date.now()), 3), "yyyy-MM-dd"),
|
||||||
|
states: [
|
||||||
|
1, // planned
|
||||||
|
2, // loading
|
||||||
|
],
|
||||||
|
//isCommissioned: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
return apiReturn(res, {
|
||||||
|
success: true,
|
||||||
|
level: "info",
|
||||||
|
module: "dockdoor",
|
||||||
|
subModule: "current Active loading orders",
|
||||||
|
message: `Current active loading loaders.`,
|
||||||
|
data: orders?.data ?? [],
|
||||||
|
status: 200,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
export default r;
|
||||||
181
backend/dockdoorScanning/dockdoor.closeLoadingOrder.route.ts
Normal file
181
backend/dockdoorScanning/dockdoor.closeLoadingOrder.route.ts
Normal file
@@ -0,0 +1,181 @@
|
|||||||
|
import { eq, sql } from "drizzle-orm";
|
||||||
|
import { Router } from "express";
|
||||||
|
import z from "zod";
|
||||||
|
import { db } from "../db/db.controller.js";
|
||||||
|
import { dockDoorScans } from "../db/schema/dockdoor.scans.schema.js";
|
||||||
|
import { dockDoorScanners } from "../db/schema/dockdoor.schema.js";
|
||||||
|
import { runProdApi } from "../utils/prodEndpoint.utils.js";
|
||||||
|
import { apiReturn } from "../utils/returnHelper.utils.js";
|
||||||
|
import { tryCatch } from "../utils/trycatch.utils.js";
|
||||||
|
|
||||||
|
const r = Router();
|
||||||
|
|
||||||
|
const endLoading = z.object({
|
||||||
|
loadingOrder: z.string(),
|
||||||
|
dockId: z.string(),
|
||||||
|
});
|
||||||
|
|
||||||
|
r.post("/", async (req, res) => {
|
||||||
|
if (req.body.clear) {
|
||||||
|
// just clear the loading order and clear out all the pallets to keep it clean.
|
||||||
|
await tryCatch(
|
||||||
|
db
|
||||||
|
.update(dockDoorScans)
|
||||||
|
.set({
|
||||||
|
status: "completed",
|
||||||
|
upd_date: sql`NOW()`,
|
||||||
|
upd_user: req.user?.username ?? "lst-dock-system",
|
||||||
|
})
|
||||||
|
.where(
|
||||||
|
req.body.loadingOrder
|
||||||
|
? eq(dockDoorScanners.currentLoadingOrder, req.body.loadingOrder)
|
||||||
|
: undefined,
|
||||||
|
)
|
||||||
|
.returning(),
|
||||||
|
);
|
||||||
|
|
||||||
|
const { data, error } = await tryCatch(
|
||||||
|
db
|
||||||
|
.update(dockDoorScanners)
|
||||||
|
.set({
|
||||||
|
currentLoadingOrder: "",
|
||||||
|
upd_date: sql`NOW()`,
|
||||||
|
upd_user: req.user?.username ?? "lst-dock-system",
|
||||||
|
})
|
||||||
|
.where(eq(dockDoorScanners.dockId, req.body.dockId))
|
||||||
|
.returning(),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
return apiReturn(res, {
|
||||||
|
success: false,
|
||||||
|
level: "error",
|
||||||
|
module: "dockdoor",
|
||||||
|
subModule: "loadingOrder",
|
||||||
|
message: `Failed to updating the dock.`,
|
||||||
|
data: (error as any) ?? [],
|
||||||
|
status: 400,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return apiReturn(res, {
|
||||||
|
success: true,
|
||||||
|
level: "info",
|
||||||
|
module: "dockdoor",
|
||||||
|
subModule: "loadingOrder",
|
||||||
|
message: `Loading order: ${req.body.loadingOrder} was just cleared out do to the process being completed in some other means. \nThis includes any scanned pallets as well.`,
|
||||||
|
data: data ?? [],
|
||||||
|
status: 200,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const validated = endLoading.parse(req.body);
|
||||||
|
|
||||||
|
const orders = (await runProdApi({
|
||||||
|
method: "post",
|
||||||
|
endpoint: `/public/v1.0/OutboundDeliveries/LoadingOrders/${req.body.loadingOrder}/Finish`,
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
printDeliveryDocuments: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})) as any;
|
||||||
|
|
||||||
|
if (orders?.data.errors) {
|
||||||
|
//console.log(orders.data.errors);
|
||||||
|
|
||||||
|
// if (orders.data.errors[0].code === 20) {
|
||||||
|
// await db
|
||||||
|
// .update(dockDoorScanners)
|
||||||
|
// .set({
|
||||||
|
// currentLoadingOrder: "",
|
||||||
|
// upd_date: sql`NOW()`,
|
||||||
|
// upd_user: req.user?.username ?? "lst-dock-system",
|
||||||
|
// })
|
||||||
|
// .where(eq(dockDoorScanners.dockId, validated.dockId));
|
||||||
|
|
||||||
|
// return apiReturn(res, {
|
||||||
|
// success: false,
|
||||||
|
// level: "warn",
|
||||||
|
// module: "dockdoor",
|
||||||
|
// subModule: "loadingOrder",
|
||||||
|
// message: `Loading order: ${validated.loadingOrder} cleared, It was probable completed by a scanner.. or user...`,
|
||||||
|
// data: (orders.data.errors as any) ?? [],
|
||||||
|
// status: 200,
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
return apiReturn(res, {
|
||||||
|
success: false,
|
||||||
|
level: "error",
|
||||||
|
module: "dockdoor",
|
||||||
|
subModule: "loadingOrder",
|
||||||
|
message: `Failed to finish the order: ${orders.data.errors[0].message}`,
|
||||||
|
data: (orders.data.errors as any) ?? [],
|
||||||
|
status: 400,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
await tryCatch(
|
||||||
|
db
|
||||||
|
.update(dockDoorScans)
|
||||||
|
.set({
|
||||||
|
status: "completed",
|
||||||
|
upd_date: sql`NOW()`,
|
||||||
|
upd_user: req.user?.username ?? "lst-dock-system",
|
||||||
|
})
|
||||||
|
.where(
|
||||||
|
eq(dockDoorScans.loadingOrder, validated.loadingOrder.toString()),
|
||||||
|
)
|
||||||
|
.returning(),
|
||||||
|
);
|
||||||
|
|
||||||
|
const { data, error } = await tryCatch(
|
||||||
|
db
|
||||||
|
.update(dockDoorScanners)
|
||||||
|
.set({
|
||||||
|
currentLoadingOrder: "",
|
||||||
|
upd_date: sql`NOW()`,
|
||||||
|
upd_user: req.user?.username ?? "lst-dock-system",
|
||||||
|
})
|
||||||
|
.where(eq(dockDoorScanners.dockId, validated.dockId))
|
||||||
|
.returning(),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
return apiReturn(res, {
|
||||||
|
success: false,
|
||||||
|
level: "error",
|
||||||
|
module: "dockdoor",
|
||||||
|
subModule: "loadingOrder",
|
||||||
|
message: `Failed to updating the dock.`,
|
||||||
|
data: (error as any) ?? [],
|
||||||
|
status: 400,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return apiReturn(res, {
|
||||||
|
success: orders.data.errors ? false : true,
|
||||||
|
level: orders.data.errors ? "error" : "info",
|
||||||
|
module: "dockdoor",
|
||||||
|
subModule: "loadingOrder",
|
||||||
|
message: orders.data.errors
|
||||||
|
? `Loading order was cleared but encountered an error: \n${orders.data.errors[0].message} \nPossible reason for this is the loading order was completed via scanner or other means.`
|
||||||
|
: `Loading order ${validated.loadingOrder} was just closed.`,
|
||||||
|
data: data ?? [],
|
||||||
|
status: orders.data.errors ? 400 : 200,
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
return apiReturn(res, {
|
||||||
|
success: false,
|
||||||
|
level: "error",
|
||||||
|
module: "dockdoor",
|
||||||
|
subModule: "loadingOrder",
|
||||||
|
message: `Failed to Close loading order.`,
|
||||||
|
data: (error as any) ?? [],
|
||||||
|
status: 400,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default r;
|
||||||
25
backend/dockdoorScanning/dockdoor.loadUnits.route.ts
Normal file
25
backend/dockdoorScanning/dockdoor.loadUnits.route.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { Router } from "express";
|
||||||
|
|
||||||
|
import { apiReturn } from "../utils/returnHelper.utils.js";
|
||||||
|
import loadUnit from "./dockdoor.loadUnits.js";
|
||||||
|
|
||||||
|
const r = Router();
|
||||||
|
|
||||||
|
r.post("/", async (req, res) => {
|
||||||
|
const unit = await loadUnit({
|
||||||
|
dockId: req.body.dockId,
|
||||||
|
runningNo: req.body.runningNo,
|
||||||
|
});
|
||||||
|
|
||||||
|
return apiReturn(res, {
|
||||||
|
success: unit.success,
|
||||||
|
level: "info",
|
||||||
|
module: "dockdoor",
|
||||||
|
subModule: "loadingUnit",
|
||||||
|
message: unit.message,
|
||||||
|
data: unit?.data ?? [],
|
||||||
|
status: unit.success ? 200 : 400,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
export default r;
|
||||||
201
backend/dockdoorScanning/dockdoor.loadUnits.ts
Normal file
201
backend/dockdoorScanning/dockdoor.loadUnits.ts
Normal file
@@ -0,0 +1,201 @@
|
|||||||
|
// sends the units from the dock door scanner here.
|
||||||
|
|
||||||
|
import { eq } from "drizzle-orm";
|
||||||
|
import { db } from "../db/db.controller.js";
|
||||||
|
import { dockDoorScans } from "../db/schema/dockdoor.scans.schema.js";
|
||||||
|
import { dockDoorScanners } from "../db/schema/dockdoor.schema.js";
|
||||||
|
import { createLogger } from "../logger/logger.controller.js";
|
||||||
|
import { emitToRoom } from "../socket.io/roomEmitter.socket.js";
|
||||||
|
import { runProdApi } from "../utils/prodEndpoint.utils.js";
|
||||||
|
import { returnFunc } from "../utils/returnHelper.utils.js";
|
||||||
|
|
||||||
|
// validate we are active
|
||||||
|
|
||||||
|
type Data = {
|
||||||
|
dockId?: string;
|
||||||
|
sscc?: string;
|
||||||
|
runningNo?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const postScan = async (data: any) => {
|
||||||
|
try {
|
||||||
|
const newScan = (await db
|
||||||
|
.insert(dockDoorScans)
|
||||||
|
.values({
|
||||||
|
dockId: data.dockId,
|
||||||
|
loadingOrder: data.loadingOrder,
|
||||||
|
loadingUnit: data.loadingUnit.sscc ?? data.loadingUnit.runningNo, // can be running number or sscc depending on where it came from
|
||||||
|
loadingUnitStatus: data.loadingUnitStatus, // TODO: add enums on the status of each load.
|
||||||
|
message: data.message, // the response it gave when scanning
|
||||||
|
status: data.status ?? undefined,
|
||||||
|
})
|
||||||
|
.returning()) as any;
|
||||||
|
|
||||||
|
emitToRoom(`dockDoorLoading:${data.dockId}`, newScan[0]);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("Error: ", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const loadUnit = async (data: Data) => {
|
||||||
|
const log = createLogger({ module: "dockdoor", subModule: "loadunit" });
|
||||||
|
log.info({ stack: data }, "Data Passed over from the scanner.");
|
||||||
|
// are we even active at this time?
|
||||||
|
const dockDoorActive = await db.query.settings.findFirst({
|
||||||
|
where: (u, { eq }) => eq(u.name, "dockDoorScanning"),
|
||||||
|
});
|
||||||
|
|
||||||
|
const unitToScan = data.sscc
|
||||||
|
? { sscc: data.sscc !== "noread" ? data.sscc?.slice(2) : data.sscc }
|
||||||
|
: { runningNo: Number(data.runningNo) };
|
||||||
|
|
||||||
|
const dock = await db
|
||||||
|
.select()
|
||||||
|
.from(dockDoorScanners)
|
||||||
|
.where(eq(dockDoorScanners.dockId, data.dockId as string));
|
||||||
|
|
||||||
|
if (!dockDoorActive?.active) {
|
||||||
|
return returnFunc({
|
||||||
|
success: false,
|
||||||
|
level: "error",
|
||||||
|
module: "dockdoor",
|
||||||
|
subModule: "loadunit",
|
||||||
|
message: "Dock door scanning feature is not active.",
|
||||||
|
data: [],
|
||||||
|
notify: false,
|
||||||
|
room: "",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if we currently have a loading order attached to the dock door.
|
||||||
|
|
||||||
|
if (dock[0]?.currentLoadingOrder === "") {
|
||||||
|
postScan({
|
||||||
|
dockId: data.dockId,
|
||||||
|
loadingOrder: dock[0]?.currentLoadingOrder,
|
||||||
|
loadingUnit: unitToScan,
|
||||||
|
loadingUnitStatus: "notLoaded",
|
||||||
|
message:
|
||||||
|
"There are know current active loading orders please start one and try again.",
|
||||||
|
status: "error",
|
||||||
|
});
|
||||||
|
return returnFunc({
|
||||||
|
success: true,
|
||||||
|
level: "error",
|
||||||
|
module: "dockdoor",
|
||||||
|
subModule: "loadingOrders",
|
||||||
|
message:
|
||||||
|
"There are know current active loading orders please start one and try again.",
|
||||||
|
data: [],
|
||||||
|
notify: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// check if its a valids an sscc
|
||||||
|
|
||||||
|
if (data.sscc === "noread") {
|
||||||
|
postScan({
|
||||||
|
dockId: data.dockId,
|
||||||
|
loadingOrder: dock[0]?.currentLoadingOrder,
|
||||||
|
loadingUnit: unitToScan,
|
||||||
|
loadingUnitStatus: "noread",
|
||||||
|
message:
|
||||||
|
"Failed to load the unit to the truck, there was no pallet read.",
|
||||||
|
status: "error",
|
||||||
|
});
|
||||||
|
|
||||||
|
return returnFunc({
|
||||||
|
success: false,
|
||||||
|
level: "error",
|
||||||
|
module: "dockdoor",
|
||||||
|
subModule: "loadUnit",
|
||||||
|
message:
|
||||||
|
"Failed to load the unit to the truck, there was no pallet read.",
|
||||||
|
data: [],
|
||||||
|
notify: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: pallet validation, check if we are on hold, then check if we have been in the staging warehouse for more than x time.
|
||||||
|
|
||||||
|
// if on hold stop the scan and send a bad read with the reason its on hold and what its on hold for, including coa.
|
||||||
|
|
||||||
|
// if precheck is active then check if we have a warehouse, then check if the pallet was in the warehouse for greater than the define min, all fails send a warning and still do the scan
|
||||||
|
|
||||||
|
// add the loading units
|
||||||
|
try {
|
||||||
|
const prod = (await runProdApi({
|
||||||
|
method: "post",
|
||||||
|
endpoint: `/public/v1.0/OutboundDeliveries/LoadingOrders/${dock[0]?.currentLoadingOrder}/LoadUnit`,
|
||||||
|
data: [unitToScan],
|
||||||
|
})) as any;
|
||||||
|
|
||||||
|
//emitToRoom(`dockDoorLoading:${data.dockId}`, prod?.data ?? []);
|
||||||
|
|
||||||
|
if (!prod?.success) {
|
||||||
|
postScan({
|
||||||
|
dockId: data.dockId,
|
||||||
|
loadingOrder: dock[0]?.currentLoadingOrder,
|
||||||
|
loadingUnit: unitToScan,
|
||||||
|
loadingUnitStatus: "notLoaded",
|
||||||
|
message: prod?.data.errors[0].message,
|
||||||
|
status: "error",
|
||||||
|
});
|
||||||
|
|
||||||
|
return returnFunc({
|
||||||
|
success: false,
|
||||||
|
level: "error",
|
||||||
|
module: "dockdoor",
|
||||||
|
subModule: "loadUnit",
|
||||||
|
message: `Unit encountered an error while loading`,
|
||||||
|
data: prod?.data.errors[0] as any,
|
||||||
|
notify: false,
|
||||||
|
//room: `dockDoorLoading:${data.dockId}`,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const emitData = {
|
||||||
|
message: `The unit ${prod.data.message.messageParams.runningNo} was loaded.`,
|
||||||
|
data: prod.data,
|
||||||
|
code: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
postScan({
|
||||||
|
dockId: data.dockId,
|
||||||
|
loadingOrder: dock[0]?.currentLoadingOrder,
|
||||||
|
loadingUnit: unitToScan,
|
||||||
|
loadingUnitStatus: "loaded",
|
||||||
|
message: emitData.message,
|
||||||
|
});
|
||||||
|
|
||||||
|
return returnFunc({
|
||||||
|
success: true,
|
||||||
|
level: "info",
|
||||||
|
module: "dockdoor",
|
||||||
|
subModule: "loadUnit",
|
||||||
|
message: `Unit added to loading order`,
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
message: `The unit ${prod.data.message.messageParams.runningNo} was loaded.`,
|
||||||
|
data: prod.data,
|
||||||
|
code: 0,
|
||||||
|
},
|
||||||
|
] as any,
|
||||||
|
notify: false,
|
||||||
|
//room: `dockDoorLoading:${data.dockId}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
return returnFunc({
|
||||||
|
success: true,
|
||||||
|
level: "error",
|
||||||
|
module: "dockdoor",
|
||||||
|
subModule: "loadUnit",
|
||||||
|
message: `Failed to load unit`,
|
||||||
|
data: error as any,
|
||||||
|
notify: false,
|
||||||
|
room: `dockDoorLoading:${data.dockId}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default loadUnit;
|
||||||
48
backend/dockdoorScanning/dockdoor.routes.ts
Normal file
48
backend/dockdoorScanning/dockdoor.routes.ts
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
import type { Express } from "express";
|
||||||
|
import { featureCheck } from "../middleware/featureActive.middleware.js";
|
||||||
|
import activeLoadingOrders from "./dockdoor.activeLoadingOrders.route.js";
|
||||||
|
import closeLoadingOrder from "./dockdoor.closeLoadingOrder.route.js";
|
||||||
|
import load from "./dockdoor.loadUnits.route.js";
|
||||||
|
import startLoad from "./dockdoor.startLoad.route.js";
|
||||||
|
import prodDocks from "./dockdoors.docks.route.js";
|
||||||
|
import docks from "./dockdoors.route.js";
|
||||||
|
|
||||||
|
export const setupDockDoorRoutes = (baseUrl: string, app: Express) => {
|
||||||
|
//stats will be like this as we dont need to change this
|
||||||
|
|
||||||
|
app.use(
|
||||||
|
`${baseUrl}/api/dockDoor/scanners`,
|
||||||
|
featureCheck("dockDoorScanning"),
|
||||||
|
|
||||||
|
docks,
|
||||||
|
);
|
||||||
|
|
||||||
|
app.use(
|
||||||
|
`${baseUrl}/api/dockDoor/finishOrder`,
|
||||||
|
featureCheck("dockDoorScanning"),
|
||||||
|
closeLoadingOrder,
|
||||||
|
);
|
||||||
|
app.use(
|
||||||
|
`${baseUrl}/api/dockDoor/activeLoadingOrders`,
|
||||||
|
featureCheck("dockDoorScanning"),
|
||||||
|
activeLoadingOrders,
|
||||||
|
);
|
||||||
|
app.use(
|
||||||
|
`${baseUrl}/api/dockDoor/startLoad`,
|
||||||
|
featureCheck("dockDoorScanning"),
|
||||||
|
startLoad,
|
||||||
|
);
|
||||||
|
app.use(
|
||||||
|
`${baseUrl}/api/dockDoor/docks`,
|
||||||
|
featureCheck("dockDoorScanning"),
|
||||||
|
prodDocks,
|
||||||
|
);
|
||||||
|
|
||||||
|
app.use(
|
||||||
|
`${baseUrl}/api/dockDoor/loadUnit`,
|
||||||
|
featureCheck("dockDoorScanning"),
|
||||||
|
load,
|
||||||
|
);
|
||||||
|
|
||||||
|
// all other system should be under /api/system/*
|
||||||
|
};
|
||||||
17
backend/dockdoorScanning/dockdoor.socket.notifications.ts
Normal file
17
backend/dockdoorScanning/dockdoor.socket.notifications.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import { eq } from "drizzle-orm";
|
||||||
|
import { db } from "../db/db.controller.js";
|
||||||
|
import { logs } from "../db/schema/logs.schema.js";
|
||||||
|
import { emitToRoom } from "../socket.io/roomEmitter.socket.js";
|
||||||
|
|
||||||
|
export async function handleDockScanInsertedNotification(id: string) {
|
||||||
|
const row = await db.query.dockDoorScans.findFirst({
|
||||||
|
where: eq(logs.id, id),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!row) return;
|
||||||
|
|
||||||
|
// send only to the current dock door
|
||||||
|
if (row.dockId) {
|
||||||
|
emitToRoom(`dockDoorLoading:${row.dockId}`, row);
|
||||||
|
}
|
||||||
|
}
|
||||||
22
backend/dockdoorScanning/dockdoor.socket.seed.ts
Normal file
22
backend/dockdoorScanning/dockdoor.socket.seed.ts
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import { db } from "../db/db.controller.js";
|
||||||
|
|
||||||
|
export const getRecentDockScans = ({
|
||||||
|
loadingOrder,
|
||||||
|
limit = 200,
|
||||||
|
}: {
|
||||||
|
loadingOrder: string;
|
||||||
|
limit?: number | undefined;
|
||||||
|
}) => {
|
||||||
|
return db.query.dockDoorScans.findMany({
|
||||||
|
//where: (scans, { eq }) => eq(scans.status, "active"),
|
||||||
|
where: (scans, { and, eq }) =>
|
||||||
|
and(
|
||||||
|
eq(scans.status, "active"),
|
||||||
|
loadingOrder
|
||||||
|
? eq(scans.loadingOrder, loadingOrder)
|
||||||
|
: eq(scans.loadingOrder, "0"),
|
||||||
|
),
|
||||||
|
orderBy: (scans, { desc }) => [desc(scans.upd_date)],
|
||||||
|
limit,
|
||||||
|
});
|
||||||
|
};
|
||||||
66
backend/dockdoorScanning/dockdoor.startLoad.route.ts
Normal file
66
backend/dockdoorScanning/dockdoor.startLoad.route.ts
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
import { eq, sql } from "drizzle-orm";
|
||||||
|
import { Router } from "express";
|
||||||
|
import z from "zod";
|
||||||
|
import { db } from "../db/db.controller.js";
|
||||||
|
import { dockDoorScanners } from "../db/schema/dockdoor.schema.js";
|
||||||
|
import { apiReturn } from "../utils/returnHelper.utils.js";
|
||||||
|
import { tryCatch } from "../utils/trycatch.utils.js";
|
||||||
|
|
||||||
|
const r = Router();
|
||||||
|
|
||||||
|
const startLoading = z.object({
|
||||||
|
loadingOrder: z.string(),
|
||||||
|
dockId: z.string(),
|
||||||
|
});
|
||||||
|
|
||||||
|
r.post("/", async (req, res) => {
|
||||||
|
try {
|
||||||
|
const validated = startLoading.parse(req.body);
|
||||||
|
|
||||||
|
const { data, error } = await tryCatch(
|
||||||
|
db
|
||||||
|
.update(dockDoorScanners)
|
||||||
|
.set({
|
||||||
|
currentLoadingOrder: validated.loadingOrder,
|
||||||
|
upd_date: sql`NOW()`,
|
||||||
|
upd_user: req.user?.username,
|
||||||
|
})
|
||||||
|
.where(eq(dockDoorScanners.dockId, validated.dockId))
|
||||||
|
.returning(),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
return apiReturn(res, {
|
||||||
|
success: false,
|
||||||
|
level: "error",
|
||||||
|
module: "dockdoor",
|
||||||
|
subModule: "loadingOrder",
|
||||||
|
message: `Failed to updating the dock.`,
|
||||||
|
data: (error as any) ?? [],
|
||||||
|
status: 400,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return apiReturn(res, {
|
||||||
|
success: true,
|
||||||
|
level: "info",
|
||||||
|
module: "dockdoor",
|
||||||
|
subModule: "loadingOrder",
|
||||||
|
message: `Loading order ${validated.loadingOrder} was just added to dockId ${validated.dockId}.`,
|
||||||
|
data: data ?? [],
|
||||||
|
status: 200,
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
return apiReturn(res, {
|
||||||
|
success: false,
|
||||||
|
level: "error",
|
||||||
|
module: "dockdoor",
|
||||||
|
subModule: "loadingOrder",
|
||||||
|
message: `Failed to start loading order.`,
|
||||||
|
data: (error as any) ?? [],
|
||||||
|
status: 400,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default r;
|
||||||
54
backend/dockdoorScanning/dockdoors.docks.route.ts
Normal file
54
backend/dockdoorScanning/dockdoors.docks.route.ts
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
import { Router } from "express";
|
||||||
|
import { prodQuery } from "../prodSql/prodSqlQuery.controller.js";
|
||||||
|
import {
|
||||||
|
type SqlQuery,
|
||||||
|
sqlQuerySelector,
|
||||||
|
} from "../prodSql/prodSqlQuerySelector.utils.js";
|
||||||
|
import { apiReturn } from "../utils/returnHelper.utils.js";
|
||||||
|
import { tryCatch } from "../utils/trycatch.utils.js";
|
||||||
|
|
||||||
|
const r = Router();
|
||||||
|
|
||||||
|
r.get("/", async (_, res) => {
|
||||||
|
const activeDocks = sqlQuerySelector(`outbound.docks`) as SqlQuery;
|
||||||
|
|
||||||
|
if (!activeDocks.success) {
|
||||||
|
return apiReturn(res, {
|
||||||
|
success: false,
|
||||||
|
level: "error",
|
||||||
|
module: "dockdoor",
|
||||||
|
subModule: "docks",
|
||||||
|
message: `There was an error getting the docks query.`,
|
||||||
|
data: [],
|
||||||
|
status: 400,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const { data, error } = await tryCatch(
|
||||||
|
prodQuery(activeDocks.query, "Current Active Docks"),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
return apiReturn(res, {
|
||||||
|
success: false,
|
||||||
|
level: "error",
|
||||||
|
module: "dockdoor",
|
||||||
|
subModule: "newDock",
|
||||||
|
message: `There was an error getting the docks.`,
|
||||||
|
data: (error as any) ?? ([] as any),
|
||||||
|
status: 400,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return apiReturn(res, {
|
||||||
|
success: true,
|
||||||
|
level: "info",
|
||||||
|
module: "dockdoor",
|
||||||
|
subModule: "docks",
|
||||||
|
message: `Current active docks.`,
|
||||||
|
data: (data.data as any) ?? ([] as any),
|
||||||
|
status: 200,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
export default r;
|
||||||
76
backend/dockdoorScanning/dockdoors.route.ts
Normal file
76
backend/dockdoorScanning/dockdoors.route.ts
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
import { Router } from "express";
|
||||||
|
import z from "zod";
|
||||||
|
import { db } from "../db/db.controller.js";
|
||||||
|
import { dockDoorScanners } from "../db/schema/dockdoor.schema.js";
|
||||||
|
import { requireAuth } from "../middleware/auth.middleware.js";
|
||||||
|
import { apiReturn } from "../utils/returnHelper.utils.js";
|
||||||
|
|
||||||
|
const r = Router();
|
||||||
|
|
||||||
|
const newDockScanner = z.object({
|
||||||
|
ip: z.string(),
|
||||||
|
name: z.string(),
|
||||||
|
dockId: z.string(),
|
||||||
|
});
|
||||||
|
|
||||||
|
r.get("/", async (_, res) => {
|
||||||
|
try {
|
||||||
|
const docks = await db
|
||||||
|
.select()
|
||||||
|
.from(dockDoorScanners)
|
||||||
|
.orderBy(dockDoorScanners.name);
|
||||||
|
|
||||||
|
return apiReturn(res, {
|
||||||
|
success: true,
|
||||||
|
level: "info",
|
||||||
|
module: "dockdoor",
|
||||||
|
subModule: "lane check",
|
||||||
|
message: `All dock Doors.`,
|
||||||
|
data: docks ?? [],
|
||||||
|
status: 200,
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
return apiReturn(res, {
|
||||||
|
success: false,
|
||||||
|
level: "error",
|
||||||
|
module: "dockdoor",
|
||||||
|
subModule: "newDock",
|
||||||
|
message: `There was an error adding in the new dock.`,
|
||||||
|
data: error ?? ([] as any),
|
||||||
|
status: 200,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
r.post("/", requireAuth, async (req, res) => {
|
||||||
|
try {
|
||||||
|
const validated = newDockScanner.parse(req.body);
|
||||||
|
|
||||||
|
const newDock = await db
|
||||||
|
.insert(dockDoorScanners)
|
||||||
|
.values(validated)
|
||||||
|
.returning();
|
||||||
|
|
||||||
|
return apiReturn(res, {
|
||||||
|
success: true,
|
||||||
|
level: "info",
|
||||||
|
module: "dockdoor",
|
||||||
|
subModule: "lane check",
|
||||||
|
message: `${validated.name} was just added.`,
|
||||||
|
data: newDock ?? [],
|
||||||
|
status: 200,
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
return apiReturn(res, {
|
||||||
|
success: false,
|
||||||
|
level: "error",
|
||||||
|
module: "dockdoor",
|
||||||
|
subModule: "newDock",
|
||||||
|
message: `There was an error adding in the new dock.`,
|
||||||
|
data: error ?? ([] as any),
|
||||||
|
status: 200,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default r;
|
||||||
107
backend/eom/eom.gpdata.route.ts
Normal file
107
backend/eom/eom.gpdata.route.ts
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
import { formatInTimeZone } from "date-fns-tz";
|
||||||
|
import { Router } from "express";
|
||||||
|
import { gpQuery } from "../gpSql/gpSqlQuery.controller.js";
|
||||||
|
import {
|
||||||
|
type SqlGPQuery,
|
||||||
|
sqlGpQuerySelector,
|
||||||
|
} from "../gpSql/gpSqlQuerySelector.utils.js";
|
||||||
|
import { apiReturn } from "../utils/returnHelper.utils.js";
|
||||||
|
import { tryCatch } from "../utils/trycatch.utils.js";
|
||||||
|
|
||||||
|
const r = Router();
|
||||||
|
|
||||||
|
r.get("/", async (req, res) => {
|
||||||
|
const { startDate, endDate, glCode, includePlantToken } = req.query;
|
||||||
|
|
||||||
|
if (
|
||||||
|
!startDate ||
|
||||||
|
startDate === "" ||
|
||||||
|
!endDate ||
|
||||||
|
endDate === "" ||
|
||||||
|
!glCode ||
|
||||||
|
glCode === ""
|
||||||
|
) {
|
||||||
|
return apiReturn(res, {
|
||||||
|
success: false,
|
||||||
|
level: "error",
|
||||||
|
module: "eom",
|
||||||
|
subModule: "GpData",
|
||||||
|
message:
|
||||||
|
"The start date, end date, and gl code are required to run this query.",
|
||||||
|
data: [],
|
||||||
|
status: 400,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const sqlQuery = sqlGpQuerySelector(`gp.eom.data`) as SqlGPQuery;
|
||||||
|
|
||||||
|
if (!sqlQuery.success) {
|
||||||
|
return apiReturn(res, {
|
||||||
|
success: false,
|
||||||
|
level: "error",
|
||||||
|
module: "eom",
|
||||||
|
subModule: "GpData",
|
||||||
|
message:
|
||||||
|
"Failed to get GpData sql file please, please contact support if this continues.",
|
||||||
|
data: [],
|
||||||
|
status: 400,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const { data, error } = await tryCatch(
|
||||||
|
gpQuery(
|
||||||
|
sqlQuery.query
|
||||||
|
.replace("[startDate]", startDate as string)
|
||||||
|
.replace("[endDate]", endDate as string)
|
||||||
|
.replace("[gpCode]", glCode as string),
|
||||||
|
"Eom GpData data",
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
return apiReturn(res, {
|
||||||
|
success: false,
|
||||||
|
level: "error",
|
||||||
|
module: "eom",
|
||||||
|
subModule: "GpData",
|
||||||
|
message: `Error getting GpData data info`,
|
||||||
|
data: error as any,
|
||||||
|
notify: false,
|
||||||
|
status: 400,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return apiReturn(res, {
|
||||||
|
success: data.success,
|
||||||
|
level: data.success ? "info" : "error",
|
||||||
|
module: "eom",
|
||||||
|
subModule: "GpData",
|
||||||
|
message: data.message,
|
||||||
|
data:
|
||||||
|
includePlantToken === "true" && data.success
|
||||||
|
? data.data.map((i) => {
|
||||||
|
return {
|
||||||
|
plantToken: process.env.PROD_PLANT_TOKEN,
|
||||||
|
...i,
|
||||||
|
Date_Received: formatInTimeZone(
|
||||||
|
i.Date_Received,
|
||||||
|
"etc/utc",
|
||||||
|
"M/d/yyyy",
|
||||||
|
),
|
||||||
|
};
|
||||||
|
})
|
||||||
|
: data.data.map((i) => {
|
||||||
|
return {
|
||||||
|
...i,
|
||||||
|
Date_Received: formatInTimeZone(
|
||||||
|
i.Date_Received,
|
||||||
|
"etc/utc",
|
||||||
|
"M/d/yyyy",
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
notify: false,
|
||||||
|
status: data.success ? 200 : 400,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
export default r;
|
||||||
9
backend/eom/eom.history.controller.ts
Normal file
9
backend/eom/eom.history.controller.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
/**
|
||||||
|
* The flow that will trigger all the history functions to run and store the data each day and clean up as needed
|
||||||
|
*
|
||||||
|
* if we are on usmcd1vms036 we will run a get request to all servers in the db so we can store that data as well.
|
||||||
|
*
|
||||||
|
* this will be stored in both. the vms036 functions will store in a bigger server so it can be pulled faster and in ssrs
|
||||||
|
*/
|
||||||
|
|
||||||
|
export const eomHistory = async () => {};
|
||||||
61
backend/eom/eom.historyInv.route.ts
Normal file
61
backend/eom/eom.historyInv.route.ts
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
import { format } from "date-fns";
|
||||||
|
import { eq } from "drizzle-orm";
|
||||||
|
import { Router } from "express";
|
||||||
|
import { db } from "../db/db.controller.js";
|
||||||
|
import { invHistoricalData } from "../db/schema/historicalInv.schema.js";
|
||||||
|
import { apiReturn } from "../utils/returnHelper.utils.js";
|
||||||
|
import { tryCatch } from "../utils/trycatch.utils.js";
|
||||||
|
|
||||||
|
const r = Router();
|
||||||
|
|
||||||
|
r.get("/", async (req, res) => {
|
||||||
|
// the params we are wanting to add in. min required will be the month so we dont pass everything over
|
||||||
|
const { date } = req.query;
|
||||||
|
|
||||||
|
if (!date || date === "") {
|
||||||
|
return apiReturn(res, {
|
||||||
|
success: false,
|
||||||
|
level: "error",
|
||||||
|
module: "eom",
|
||||||
|
subModule: "historical inv",
|
||||||
|
message:
|
||||||
|
"The day of the month is required to be included in order to pass.",
|
||||||
|
data: [],
|
||||||
|
status: 400,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the date passed over.
|
||||||
|
const { data, error } = await tryCatch(
|
||||||
|
db
|
||||||
|
.select()
|
||||||
|
.from(invHistoricalData)
|
||||||
|
.where(
|
||||||
|
eq(invHistoricalData.histDate, format(date as string, "yyyy-MM-dd")),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
return apiReturn(res, {
|
||||||
|
success: false,
|
||||||
|
level: "error",
|
||||||
|
module: "eom",
|
||||||
|
subModule: "historical inv",
|
||||||
|
message:
|
||||||
|
"There was an error getting the historical data from the server.",
|
||||||
|
data: error as any,
|
||||||
|
status: 400,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return apiReturn(res, {
|
||||||
|
success: true,
|
||||||
|
level: "info",
|
||||||
|
module: "eom",
|
||||||
|
subModule: "historical inv",
|
||||||
|
message: "Eom Historical Inv Data",
|
||||||
|
data: data,
|
||||||
|
status: 200,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
export default r;
|
||||||
77
backend/eom/eom.lastPurchasePrice.route.ts
Normal file
77
backend/eom/eom.lastPurchasePrice.route.ts
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
import { Router } from "express";
|
||||||
|
import { prodQuery } from "../prodSql/prodSqlQuery.controller.js";
|
||||||
|
import {
|
||||||
|
type SqlQuery,
|
||||||
|
sqlQuerySelector,
|
||||||
|
} from "../prodSql/prodSqlQuerySelector.utils.js";
|
||||||
|
import { apiReturn } from "../utils/returnHelper.utils.js";
|
||||||
|
import { tryCatch } from "../utils/trycatch.utils.js";
|
||||||
|
|
||||||
|
const r = Router();
|
||||||
|
|
||||||
|
r.get("/", async (req, res) => {
|
||||||
|
const { includePlantToken } = req.query;
|
||||||
|
|
||||||
|
const sqlQuery = sqlQuerySelector(`eom.lastPurchasePrice`) as SqlQuery;
|
||||||
|
|
||||||
|
if (!sqlQuery.success) {
|
||||||
|
return apiReturn(res, {
|
||||||
|
success: false,
|
||||||
|
level: "error",
|
||||||
|
module: "eom",
|
||||||
|
subModule: "lastPurchasePrice",
|
||||||
|
message:
|
||||||
|
"Failed to get last sales price sql file please, please contact support if this continues.",
|
||||||
|
data: [],
|
||||||
|
status: 400,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const { data, error } = await tryCatch(
|
||||||
|
prodQuery(
|
||||||
|
sqlQuery.query,
|
||||||
|
|
||||||
|
"Eom last purchase price data",
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
return apiReturn(res, {
|
||||||
|
success: false,
|
||||||
|
level: "error",
|
||||||
|
module: "eom",
|
||||||
|
subModule: "lastPurchasePrice",
|
||||||
|
message: `Error getting last purchase Price data info`,
|
||||||
|
data: error as any,
|
||||||
|
notify: false,
|
||||||
|
status: 400,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return apiReturn(res, {
|
||||||
|
success: data.success,
|
||||||
|
level: data.success ? "info" : "error",
|
||||||
|
module: "eom",
|
||||||
|
subModule: "lastPurchasePrice",
|
||||||
|
message: data.message,
|
||||||
|
data:
|
||||||
|
includePlantToken === "true" && data.success
|
||||||
|
? data.data.map((i) => {
|
||||||
|
return {
|
||||||
|
plantToken: process.env.PROD_PLANT_TOKEN,
|
||||||
|
...i,
|
||||||
|
//validDate: formatInTimeZone(i.validDate, "etc/utc", "M/d/yyyy"),
|
||||||
|
};
|
||||||
|
})
|
||||||
|
: data.data.map((i) => {
|
||||||
|
return {
|
||||||
|
...i,
|
||||||
|
//validDate: formatInTimeZone(i.validDate, "etc/utc", "M/d/yyyy"),
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
notify: false,
|
||||||
|
status: data.success ? 200 : 400,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
export default r;
|
||||||
89
backend/eom/eom.lastSalesPrice.route.ts
Normal file
89
backend/eom/eom.lastSalesPrice.route.ts
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
import { formatInTimeZone } from "date-fns-tz";
|
||||||
|
import { Router } from "express";
|
||||||
|
import { prodQuery } from "../prodSql/prodSqlQuery.controller.js";
|
||||||
|
import {
|
||||||
|
type SqlQuery,
|
||||||
|
sqlQuerySelector,
|
||||||
|
} from "../prodSql/prodSqlQuerySelector.utils.js";
|
||||||
|
import { apiReturn } from "../utils/returnHelper.utils.js";
|
||||||
|
import { tryCatch } from "../utils/trycatch.utils.js";
|
||||||
|
|
||||||
|
const r = Router();
|
||||||
|
|
||||||
|
r.get("/", async (req, res) => {
|
||||||
|
const { date, includePlantToken } = req.query;
|
||||||
|
|
||||||
|
if (!date || date === "") {
|
||||||
|
return apiReturn(res, {
|
||||||
|
success: false,
|
||||||
|
level: "error",
|
||||||
|
module: "eom",
|
||||||
|
subModule: "lastSalesPrice",
|
||||||
|
message: "A date is required to run this query.",
|
||||||
|
data: [],
|
||||||
|
status: 400,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const sqlQuery = sqlQuerySelector(`eom.lastSalesPrice`) as SqlQuery;
|
||||||
|
|
||||||
|
if (!sqlQuery.success) {
|
||||||
|
return apiReturn(res, {
|
||||||
|
success: false,
|
||||||
|
level: "error",
|
||||||
|
module: "eom",
|
||||||
|
subModule: "lastSalesPrice",
|
||||||
|
message:
|
||||||
|
"Failed to get last sales price sql file please, please contact support if this continues.",
|
||||||
|
data: [],
|
||||||
|
status: 400,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const { data, error } = await tryCatch(
|
||||||
|
prodQuery(
|
||||||
|
sqlQuery.query.replace("[date]", date as string),
|
||||||
|
|
||||||
|
"Eom last sales price data",
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
return apiReturn(res, {
|
||||||
|
success: false,
|
||||||
|
level: "error",
|
||||||
|
module: "eom",
|
||||||
|
subModule: "lastSalesPrice",
|
||||||
|
message: `Error getting last Sales Price data info`,
|
||||||
|
data: error as any,
|
||||||
|
notify: false,
|
||||||
|
status: 400,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return apiReturn(res, {
|
||||||
|
success: data.success,
|
||||||
|
level: data.success ? "info" : "error",
|
||||||
|
module: "eom",
|
||||||
|
subModule: "lastSalesPrice",
|
||||||
|
message: data.message,
|
||||||
|
data:
|
||||||
|
includePlantToken === "true" && data.success
|
||||||
|
? data.data.map((i) => {
|
||||||
|
return {
|
||||||
|
plantToken: process.env.PROD_PLANT_TOKEN,
|
||||||
|
...i,
|
||||||
|
validDate: formatInTimeZone(i.validDate, "etc/utc", "M/d/yyyy"),
|
||||||
|
};
|
||||||
|
})
|
||||||
|
: data.data.map((i) => {
|
||||||
|
return {
|
||||||
|
...i,
|
||||||
|
validDate: formatInTimeZone(i.validDate, "etc/utc", "M/d/yyyy"),
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
notify: false,
|
||||||
|
status: data.success ? 200 : 400,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
export default r;
|
||||||
97
backend/eom/eom.productionConsumption.route.ts
Normal file
97
backend/eom/eom.productionConsumption.route.ts
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
import { Router } from "express";
|
||||||
|
import { prodQuery } from "../prodSql/prodSqlQuery.controller.js";
|
||||||
|
import {
|
||||||
|
type SqlQuery,
|
||||||
|
sqlQuerySelector,
|
||||||
|
} from "../prodSql/prodSqlQuerySelector.utils.js";
|
||||||
|
import { apiReturn } from "../utils/returnHelper.utils.js";
|
||||||
|
import { tryCatch } from "../utils/trycatch.utils.js";
|
||||||
|
|
||||||
|
const r = Router();
|
||||||
|
|
||||||
|
r.get("/", async (req, res) => {
|
||||||
|
const { startDate, endDate, includePlantToken } = req.query;
|
||||||
|
|
||||||
|
if (!startDate || startDate === "" || !endDate || endDate === "") {
|
||||||
|
return apiReturn(res, {
|
||||||
|
success: false,
|
||||||
|
level: "error",
|
||||||
|
module: "eom",
|
||||||
|
subModule: "productionConsumption",
|
||||||
|
message: "The start date and end date are required to run this query.",
|
||||||
|
data: [],
|
||||||
|
status: 400,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const sqlQuery = sqlQuerySelector(`eom.productionConsumption`) as SqlQuery;
|
||||||
|
|
||||||
|
if (!sqlQuery.success) {
|
||||||
|
return apiReturn(res, {
|
||||||
|
success: false,
|
||||||
|
level: "error",
|
||||||
|
module: "eom",
|
||||||
|
subModule: "productionConsumption",
|
||||||
|
message:
|
||||||
|
"Failed to get production consumption sql file please, please contact support if this continues.",
|
||||||
|
data: [],
|
||||||
|
status: 400,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const { data, error } = await tryCatch(
|
||||||
|
prodQuery(
|
||||||
|
sqlQuery.query
|
||||||
|
.replace("[startDate]", startDate as string)
|
||||||
|
.replace("[endDate]", endDate as string),
|
||||||
|
"Eom production consumption data",
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
return apiReturn(res, {
|
||||||
|
success: false,
|
||||||
|
level: "error",
|
||||||
|
module: "eom",
|
||||||
|
subModule: "productionConsumption",
|
||||||
|
message: `Error getting production consumption data info`,
|
||||||
|
data: error as any,
|
||||||
|
notify: false,
|
||||||
|
status: 400,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return apiReturn(res, {
|
||||||
|
success: data.success,
|
||||||
|
level: data.success ? "info" : "error",
|
||||||
|
module: "eom",
|
||||||
|
subModule: "productionConsumption",
|
||||||
|
message: data.message,
|
||||||
|
data:
|
||||||
|
includePlantToken === "true" && data.success
|
||||||
|
? data.data.map((i) => {
|
||||||
|
return {
|
||||||
|
plantToken: process.env.PROD_PLANT_TOKEN,
|
||||||
|
...i,
|
||||||
|
// Prod_Date: formatInTimeZone(
|
||||||
|
// i.Prod_Date,
|
||||||
|
// "etc/utc",
|
||||||
|
// "M/d/yyyy",
|
||||||
|
// ),
|
||||||
|
};
|
||||||
|
})
|
||||||
|
: data.data.map((i) => {
|
||||||
|
return {
|
||||||
|
...i,
|
||||||
|
// Prod_Date: formatInTimeZone(
|
||||||
|
// i.Prod_Date,
|
||||||
|
// "etc/utc",
|
||||||
|
// "M/d/yyyy",
|
||||||
|
// ),
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
notify: false,
|
||||||
|
status: data.success ? 200 : 400,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
export default r;
|
||||||
98
backend/eom/eom.purchased.route.ts
Normal file
98
backend/eom/eom.purchased.route.ts
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
import { formatInTimeZone } from "date-fns-tz";
|
||||||
|
import { Router } from "express";
|
||||||
|
import { prodQuery } from "../prodSql/prodSqlQuery.controller.js";
|
||||||
|
import {
|
||||||
|
type SqlQuery,
|
||||||
|
sqlQuerySelector,
|
||||||
|
} from "../prodSql/prodSqlQuerySelector.utils.js";
|
||||||
|
import { apiReturn } from "../utils/returnHelper.utils.js";
|
||||||
|
import { tryCatch } from "../utils/trycatch.utils.js";
|
||||||
|
|
||||||
|
const r = Router();
|
||||||
|
|
||||||
|
r.get("/", async (req, res) => {
|
||||||
|
const { startDate, endDate, includePlantToken } = req.query;
|
||||||
|
|
||||||
|
if (!startDate || startDate === "" || !endDate || endDate === "") {
|
||||||
|
return apiReturn(res, {
|
||||||
|
success: false,
|
||||||
|
level: "error",
|
||||||
|
module: "eom",
|
||||||
|
subModule: "purchased",
|
||||||
|
message: "The start date and end date are required to run this query.",
|
||||||
|
data: [],
|
||||||
|
status: 400,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const sqlQuery = sqlQuerySelector(`eom.purchased`) as SqlQuery;
|
||||||
|
|
||||||
|
if (!sqlQuery.success) {
|
||||||
|
return apiReturn(res, {
|
||||||
|
success: false,
|
||||||
|
level: "error",
|
||||||
|
module: "eom",
|
||||||
|
subModule: "purchased",
|
||||||
|
message:
|
||||||
|
"Failed to get purchased sql file please, please contact support if this continues.",
|
||||||
|
data: [],
|
||||||
|
status: 400,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const { data, error } = await tryCatch(
|
||||||
|
prodQuery(
|
||||||
|
sqlQuery.query
|
||||||
|
.replace("[startDate]", startDate as string)
|
||||||
|
.replace("[endDate]", endDate as string),
|
||||||
|
"Eom purchased data",
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
return apiReturn(res, {
|
||||||
|
success: false,
|
||||||
|
level: "error",
|
||||||
|
module: "eom",
|
||||||
|
subModule: "purchased",
|
||||||
|
message: `Error getting purchased data info`,
|
||||||
|
data: error as any,
|
||||||
|
notify: false,
|
||||||
|
status: 400,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return apiReturn(res, {
|
||||||
|
success: data.success,
|
||||||
|
level: data.success ? "info" : "error",
|
||||||
|
module: "eom",
|
||||||
|
subModule: "purchased",
|
||||||
|
message: data.message,
|
||||||
|
data:
|
||||||
|
includePlantToken === "true" && data.success
|
||||||
|
? data.data.map((i) => {
|
||||||
|
return {
|
||||||
|
plantToken: process.env.PROD_PLANT_TOKEN,
|
||||||
|
...i,
|
||||||
|
Received_Date: formatInTimeZone(
|
||||||
|
i.Received_Date,
|
||||||
|
"etc/utc",
|
||||||
|
"M/d/yyyy",
|
||||||
|
),
|
||||||
|
};
|
||||||
|
})
|
||||||
|
: data.data.map((i) => {
|
||||||
|
return {
|
||||||
|
...i,
|
||||||
|
Received_Date: formatInTimeZone(
|
||||||
|
i.Received_Date,
|
||||||
|
"etc/utc",
|
||||||
|
"M/d/yyyy",
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
notify: false,
|
||||||
|
status: data.success ? 200 : 400,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
export default r;
|
||||||
98
backend/eom/eom.regrind.route.ts
Normal file
98
backend/eom/eom.regrind.route.ts
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
import { formatInTimeZone } from "date-fns-tz";
|
||||||
|
import { Router } from "express";
|
||||||
|
import { prodQuery } from "../prodSql/prodSqlQuery.controller.js";
|
||||||
|
import {
|
||||||
|
type SqlQuery,
|
||||||
|
sqlQuerySelector,
|
||||||
|
} from "../prodSql/prodSqlQuerySelector.utils.js";
|
||||||
|
import { apiReturn } from "../utils/returnHelper.utils.js";
|
||||||
|
import { tryCatch } from "../utils/trycatch.utils.js";
|
||||||
|
|
||||||
|
const r = Router();
|
||||||
|
|
||||||
|
r.get("/", async (req, res) => {
|
||||||
|
const { startDate, endDate, includePlantToken } = req.query;
|
||||||
|
|
||||||
|
if (!startDate || startDate === "" || !endDate || endDate === "") {
|
||||||
|
return apiReturn(res, {
|
||||||
|
success: false,
|
||||||
|
level: "error",
|
||||||
|
module: "eom",
|
||||||
|
subModule: "regrind",
|
||||||
|
message: "The start date and end date are required to run this query.",
|
||||||
|
data: [],
|
||||||
|
status: 400,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const sqlQuery = sqlQuerySelector(`eom.regrind`) as SqlQuery;
|
||||||
|
|
||||||
|
if (!sqlQuery.success) {
|
||||||
|
return apiReturn(res, {
|
||||||
|
success: false,
|
||||||
|
level: "error",
|
||||||
|
module: "eom",
|
||||||
|
subModule: "regrind",
|
||||||
|
message:
|
||||||
|
"Failed to get regrind sql file please, please contact support if this continues.",
|
||||||
|
data: [],
|
||||||
|
status: 400,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const { data, error } = await tryCatch(
|
||||||
|
prodQuery(
|
||||||
|
sqlQuery.query
|
||||||
|
.replace("[startDate]", startDate as string)
|
||||||
|
.replace("[endDate]", endDate as string),
|
||||||
|
"Eom regrind data",
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
return apiReturn(res, {
|
||||||
|
success: false,
|
||||||
|
level: "error",
|
||||||
|
module: "eom",
|
||||||
|
subModule: "regrind",
|
||||||
|
message: `Error getting regrind data info`,
|
||||||
|
data: error as any,
|
||||||
|
notify: false,
|
||||||
|
status: 400,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return apiReturn(res, {
|
||||||
|
success: data.success,
|
||||||
|
level: data.success ? "info" : "error",
|
||||||
|
module: "eom",
|
||||||
|
subModule: "regrind",
|
||||||
|
message: data.message,
|
||||||
|
data:
|
||||||
|
includePlantToken === "true" && data.success
|
||||||
|
? data.data.map((i) => {
|
||||||
|
return {
|
||||||
|
plantToken: process.env.PROD_PLANT_TOKEN,
|
||||||
|
...i,
|
||||||
|
Buchungsdatum: formatInTimeZone(
|
||||||
|
i.Buchungsdatum,
|
||||||
|
"etc/utc",
|
||||||
|
"M/d/yyyy",
|
||||||
|
),
|
||||||
|
};
|
||||||
|
})
|
||||||
|
: data.data.map((i) => {
|
||||||
|
return {
|
||||||
|
...i,
|
||||||
|
Buchungsdatum: formatInTimeZone(
|
||||||
|
i.Buchungsdatum,
|
||||||
|
"etc/utc",
|
||||||
|
"M/d/yyyy",
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
notify: false,
|
||||||
|
status: data.success ? 200 : 400,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
export default r;
|
||||||
35
backend/eom/eom.routes.ts
Normal file
35
backend/eom/eom.routes.ts
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import type { Express } from "express";
|
||||||
|
import { featureCheck } from "../middleware/featureActive.middleware.js";
|
||||||
|
import gpData from "./eom.gpdata.route.js";
|
||||||
|
import historyInv from "./eom.historyInv.route.js";
|
||||||
|
import lastPurchasePrice from "./eom.lastPurchasePrice.route.js";
|
||||||
|
import lastSalesPrice from "./eom.lastSalesPrice.route.js";
|
||||||
|
import productionConsumption from "./eom.productionConsumption.route.js";
|
||||||
|
import purchased from "./eom.purchased.route.js";
|
||||||
|
import regrind from "./eom.regrind.route.js";
|
||||||
|
import soldItems from "./eom.soldItems.route.js";
|
||||||
|
|
||||||
|
export const setupEomRoutes = (baseUrl: string, app: Express) => {
|
||||||
|
//stats will be like this as we dont need to change this
|
||||||
|
|
||||||
|
app.use(`${baseUrl}/api/eom/historyInv`, featureCheck("eom"), historyInv);
|
||||||
|
app.use(`${baseUrl}/api/eom/purchased`, featureCheck("eom"), purchased);
|
||||||
|
app.use(
|
||||||
|
`${baseUrl}/api/eom/lastSalesPrice`,
|
||||||
|
featureCheck("eom"),
|
||||||
|
lastSalesPrice,
|
||||||
|
);
|
||||||
|
app.use(
|
||||||
|
`${baseUrl}/api/eom/lastPurchasePrice`,
|
||||||
|
featureCheck("eom"),
|
||||||
|
lastPurchasePrice,
|
||||||
|
);
|
||||||
|
app.use(
|
||||||
|
`${baseUrl}/api/eom/productionConsumption`,
|
||||||
|
featureCheck("eom"),
|
||||||
|
productionConsumption,
|
||||||
|
);
|
||||||
|
app.use(`${baseUrl}/api/eom/regrind`, featureCheck("eom"), regrind);
|
||||||
|
app.use(`${baseUrl}/api/eom/soldItems`, featureCheck("eom"), soldItems);
|
||||||
|
app.use(`${baseUrl}/api/eom/gpData`, featureCheck("eom"), gpData);
|
||||||
|
};
|
||||||
98
backend/eom/eom.soldItems.route.ts
Normal file
98
backend/eom/eom.soldItems.route.ts
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
import { formatInTimeZone } from "date-fns-tz";
|
||||||
|
import { Router } from "express";
|
||||||
|
import { prodQuery } from "../prodSql/prodSqlQuery.controller.js";
|
||||||
|
import {
|
||||||
|
type SqlQuery,
|
||||||
|
sqlQuerySelector,
|
||||||
|
} from "../prodSql/prodSqlQuerySelector.utils.js";
|
||||||
|
import { apiReturn } from "../utils/returnHelper.utils.js";
|
||||||
|
import { tryCatch } from "../utils/trycatch.utils.js";
|
||||||
|
|
||||||
|
const r = Router();
|
||||||
|
|
||||||
|
r.get("/", async (req, res) => {
|
||||||
|
const { startDate, endDate, includePlantToken } = req.query;
|
||||||
|
|
||||||
|
if (!startDate || startDate === "" || !endDate || endDate === "") {
|
||||||
|
return apiReturn(res, {
|
||||||
|
success: false,
|
||||||
|
level: "error",
|
||||||
|
module: "eom",
|
||||||
|
subModule: "soldItems",
|
||||||
|
message: "The start date and end date are required to run this query.",
|
||||||
|
data: [],
|
||||||
|
status: 400,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const sqlQuery = sqlQuerySelector(`eom.soldItems`) as SqlQuery;
|
||||||
|
|
||||||
|
if (!sqlQuery.success) {
|
||||||
|
return apiReturn(res, {
|
||||||
|
success: false,
|
||||||
|
level: "error",
|
||||||
|
module: "eom",
|
||||||
|
subModule: "soldItems",
|
||||||
|
message:
|
||||||
|
"Failed to get soldItems sql file please, please contact support if this continues.",
|
||||||
|
data: [],
|
||||||
|
status: 400,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const { data, error } = await tryCatch(
|
||||||
|
prodQuery(
|
||||||
|
sqlQuery.query
|
||||||
|
.replace("[startDate]", startDate as string)
|
||||||
|
.replace("[endDate]", endDate as string),
|
||||||
|
"Eom soldItems data",
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
return apiReturn(res, {
|
||||||
|
success: false,
|
||||||
|
level: "error",
|
||||||
|
module: "eom",
|
||||||
|
subModule: "soldItems",
|
||||||
|
message: `Error getting soldItems data info`,
|
||||||
|
data: error as any,
|
||||||
|
notify: false,
|
||||||
|
status: 400,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return apiReturn(res, {
|
||||||
|
success: data.success,
|
||||||
|
level: data.success ? "info" : "error",
|
||||||
|
module: "eom",
|
||||||
|
subModule: "soldItems",
|
||||||
|
message: data.message,
|
||||||
|
data:
|
||||||
|
includePlantToken === "true" && data.success
|
||||||
|
? data.data.map((i) => {
|
||||||
|
return {
|
||||||
|
plantToken: process.env.PROD_PLANT_TOKEN,
|
||||||
|
...i,
|
||||||
|
DeliveryDate: formatInTimeZone(
|
||||||
|
i.DeliveryDate,
|
||||||
|
"etc/utc",
|
||||||
|
"M/d/yyyy",
|
||||||
|
),
|
||||||
|
};
|
||||||
|
})
|
||||||
|
: data.data.map((i) => {
|
||||||
|
return {
|
||||||
|
...i,
|
||||||
|
DeliveryDate: formatInTimeZone(
|
||||||
|
i.DeliveryDate,
|
||||||
|
"etc/utc",
|
||||||
|
"M/d/yyyy",
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
notify: false,
|
||||||
|
status: data.success ? 200 : 400,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
export default r;
|
||||||
17
backend/gpSql/gpSql.routes.ts
Normal file
17
backend/gpSql/gpSql.routes.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import { type Express, Router } from "express";
|
||||||
|
import { requireAuth } from "../middleware/auth.middleware.js";
|
||||||
|
|
||||||
|
import restart from "./gpSqlRestart.route.js";
|
||||||
|
import start from "./gpSqlStart.route.js";
|
||||||
|
import stop from "./gpSqlStop.route.js";
|
||||||
|
export const setupGPSqlRoutes = (baseUrl: string, app: Express) => {
|
||||||
|
//setup all the routes
|
||||||
|
// Apply auth to entire router
|
||||||
|
const router = Router();
|
||||||
|
|
||||||
|
router.use(start);
|
||||||
|
router.use(stop);
|
||||||
|
router.use(restart);
|
||||||
|
|
||||||
|
app.use(`${baseUrl}/api/system/gpSql`, requireAuth, router);
|
||||||
|
};
|
||||||
153
backend/gpSql/gpSqlConnection.controller.ts
Normal file
153
backend/gpSql/gpSqlConnection.controller.ts
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
import sql from "mssql";
|
||||||
|
import { gpSqlConfig } from "../configs/gpSql.config.js";
|
||||||
|
import { createLogger } from "../logger/logger.controller.js";
|
||||||
|
import { checkHostnamePort } from "../utils/checkHost.utils.js";
|
||||||
|
import { returnFunc } from "../utils/returnHelper.utils.js";
|
||||||
|
|
||||||
|
export let pool2: sql.ConnectionPool;
|
||||||
|
export let connected: boolean = false;
|
||||||
|
export let reconnecting = false;
|
||||||
|
// start the delay out as 2 seconds
|
||||||
|
let delayStart = 2000;
|
||||||
|
let attempt = 0;
|
||||||
|
const maxAttempts = 10;
|
||||||
|
|
||||||
|
export const connectGPSql = async () => {
|
||||||
|
const serverUp = await checkHostnamePort(
|
||||||
|
`${process.env.GP_SERVER ?? "usmcd1vms011"}:1433`,
|
||||||
|
);
|
||||||
|
if (!serverUp) {
|
||||||
|
// we will try to reconnect
|
||||||
|
connected = false;
|
||||||
|
reconnectToSql;
|
||||||
|
return returnFunc({
|
||||||
|
success: false,
|
||||||
|
level: "error",
|
||||||
|
module: "system",
|
||||||
|
subModule: "db",
|
||||||
|
message: "GP server is offline or unreachable.",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we are trying to click restart from the api for some reason we want to kick back and say no
|
||||||
|
if (connected) {
|
||||||
|
return returnFunc({
|
||||||
|
success: false,
|
||||||
|
level: "error",
|
||||||
|
module: "system",
|
||||||
|
subModule: "db",
|
||||||
|
message: "The Sql server is already connected.",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// try to connect to the sql server
|
||||||
|
try {
|
||||||
|
pool2 = new sql.ConnectionPool(gpSqlConfig);
|
||||||
|
await pool2.connect();
|
||||||
|
connected = true;
|
||||||
|
return returnFunc({
|
||||||
|
success: true,
|
||||||
|
level: "info",
|
||||||
|
module: "system",
|
||||||
|
subModule: "db",
|
||||||
|
message: `${gpSqlConfig.server} is connected to ${gpSqlConfig.database}`,
|
||||||
|
data: [],
|
||||||
|
notify: false,
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
reconnectToSql;
|
||||||
|
return returnFunc({
|
||||||
|
success: false,
|
||||||
|
level: "error",
|
||||||
|
module: "system",
|
||||||
|
subModule: "db",
|
||||||
|
message: "Failed to connect to the gp sql server.",
|
||||||
|
data: [error],
|
||||||
|
notify: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const closePool = async () => {
|
||||||
|
if (!connected) {
|
||||||
|
return returnFunc({
|
||||||
|
success: false,
|
||||||
|
level: "error",
|
||||||
|
module: "system",
|
||||||
|
subModule: "db",
|
||||||
|
message: "There is no connection to the prod server currently.",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await pool2.close();
|
||||||
|
connected = false;
|
||||||
|
return returnFunc({
|
||||||
|
success: true,
|
||||||
|
level: "info",
|
||||||
|
module: "system",
|
||||||
|
subModule: "db",
|
||||||
|
message: "The sql connection has been closed.",
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
connected = false;
|
||||||
|
|
||||||
|
return returnFunc({
|
||||||
|
success: false,
|
||||||
|
level: "error",
|
||||||
|
module: "system",
|
||||||
|
subModule: "db",
|
||||||
|
message: "There was an error closing the sql connection",
|
||||||
|
data: [error],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
export const reconnectToSql = async () => {
|
||||||
|
const log = createLogger({
|
||||||
|
module: "system",
|
||||||
|
subModule: "db",
|
||||||
|
});
|
||||||
|
if (reconnecting) return;
|
||||||
|
|
||||||
|
//set reconnecting to true while we try to reconnect
|
||||||
|
reconnecting = true;
|
||||||
|
|
||||||
|
while (!connected && attempt < maxAttempts) {
|
||||||
|
attempt++;
|
||||||
|
log.info(
|
||||||
|
`Reconnect attempt ${attempt}/${maxAttempts} in ${delayStart / 1000}s ...`,
|
||||||
|
);
|
||||||
|
|
||||||
|
await new Promise((res) => setTimeout(res, delayStart));
|
||||||
|
|
||||||
|
const serverUp = await checkHostnamePort(
|
||||||
|
`${process.env.GP_SERVER ?? "usmcd1vms011"}:1433`,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!serverUp) {
|
||||||
|
delayStart = Math.min(delayStart * 2, 30000); // exponential backoff until up to 30000
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
pool2 = await sql.connect(gpSqlConfig);
|
||||||
|
reconnecting = false;
|
||||||
|
connected = true;
|
||||||
|
log.info(`${gpSqlConfig.server} is connected to ${gpSqlConfig.database}`);
|
||||||
|
} catch (error) {
|
||||||
|
delayStart = Math.min(delayStart * 2, 30000);
|
||||||
|
log.error({ error }, "Failed to reconnect to the prod sql server.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!connected && attempt >= maxAttempts) {
|
||||||
|
log.error(
|
||||||
|
{ notify: true },
|
||||||
|
"Max reconnect attempts reached on the prodSql server. Stopping retries.",
|
||||||
|
);
|
||||||
|
|
||||||
|
reconnecting = false;
|
||||||
|
// TODO: exit alert someone here
|
||||||
|
}
|
||||||
|
};
|
||||||
78
backend/gpSql/gpSqlQuery.controller.ts
Normal file
78
backend/gpSql/gpSqlQuery.controller.ts
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
import { returnFunc } from "../utils/returnHelper.utils.js";
|
||||||
|
import { connected, pool2 } from "./gpSqlConnection.controller.js";
|
||||||
|
|
||||||
|
interface SqlError extends Error {
|
||||||
|
code?: string;
|
||||||
|
originalError?: {
|
||||||
|
info?: { message?: string };
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run a prod query
|
||||||
|
* just pass over the query as a string and the name of the query.
|
||||||
|
* Query should be like below.
|
||||||
|
* * select * from AlplaPROD_test1.dbo.table
|
||||||
|
* You must use test1 always as it will be changed via query
|
||||||
|
*/
|
||||||
|
export const gpQuery = async (queryToRun: string, name: string) => {
|
||||||
|
if (!connected) {
|
||||||
|
return returnFunc({
|
||||||
|
success: false,
|
||||||
|
level: "error",
|
||||||
|
module: "system",
|
||||||
|
subModule: "gpSql",
|
||||||
|
message: `${process.env.PROD_PLANT_TOKEN} is offline or attempting to reconnect`,
|
||||||
|
data: [],
|
||||||
|
notify: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
//change to the correct server
|
||||||
|
const query = queryToRun.replaceAll(
|
||||||
|
"test1",
|
||||||
|
`${process.env.PROD_PLANT_TOKEN}`,
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await pool2.request().query(query);
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
message: `Query results for: ${name}`,
|
||||||
|
data: result.recordset ?? [],
|
||||||
|
};
|
||||||
|
} catch (error: unknown) {
|
||||||
|
const err = error as SqlError;
|
||||||
|
if (err.code === "ETIMEOUT") {
|
||||||
|
return returnFunc({
|
||||||
|
success: false,
|
||||||
|
module: "system",
|
||||||
|
subModule: "gpSql",
|
||||||
|
level: "error",
|
||||||
|
message: `${name} did not run due to a timeout.`,
|
||||||
|
notify: false,
|
||||||
|
data: [],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (err.code === "EREQUEST") {
|
||||||
|
return returnFunc({
|
||||||
|
success: false,
|
||||||
|
module: "system",
|
||||||
|
subModule: "gpSql",
|
||||||
|
level: "error",
|
||||||
|
message: `${name} encountered an error ${err.originalError?.info?.message || "undefined error"}`,
|
||||||
|
data: [],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return returnFunc({
|
||||||
|
success: false,
|
||||||
|
module: "system",
|
||||||
|
subModule: "gpSql",
|
||||||
|
level: "error",
|
||||||
|
message: `${name} encountered an unknown error.`,
|
||||||
|
data: [],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
29
backend/gpSql/gpSqlQuerySelector.utils.ts
Normal file
29
backend/gpSql/gpSqlQuerySelector.utils.ts
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import { readFileSync } from "node:fs";
|
||||||
|
|
||||||
|
export type SqlGPQuery = {
|
||||||
|
query: string;
|
||||||
|
success: boolean;
|
||||||
|
message: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const sqlGpQuerySelector = (name: string) => {
|
||||||
|
try {
|
||||||
|
const queryFile = readFileSync(
|
||||||
|
new URL(`../gpSql/queries/${name}.sql`, import.meta.url),
|
||||||
|
"utf8",
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
message: `Query for: ${name}`,
|
||||||
|
query: queryFile,
|
||||||
|
};
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
message:
|
||||||
|
"Error getting the query file, please make sure you have the correct name.",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
23
backend/gpSql/gpSqlRestart.route.ts
Normal file
23
backend/gpSql/gpSqlRestart.route.ts
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import { Router } from "express";
|
||||||
|
import { apiReturn } from "../utils/returnHelper.utils.js";
|
||||||
|
import { closePool, connectGPSql } from "./gpSqlConnection.controller.js";
|
||||||
|
|
||||||
|
const r = Router();
|
||||||
|
|
||||||
|
r.post("/restart", async (_, res) => {
|
||||||
|
await closePool();
|
||||||
|
|
||||||
|
await new Promise((r) => setTimeout(r, 2000));
|
||||||
|
|
||||||
|
const connect = await connectGPSql();
|
||||||
|
apiReturn(res, {
|
||||||
|
success: connect.success,
|
||||||
|
level: connect.success ? "info" : "error",
|
||||||
|
module: "routes",
|
||||||
|
subModule: "prodSql",
|
||||||
|
message: "Sql Server has been restarted",
|
||||||
|
data: connect.data,
|
||||||
|
status: connect.success ? 200 : 400,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
export default r;
|
||||||
20
backend/gpSql/gpSqlStart.route.ts
Normal file
20
backend/gpSql/gpSqlStart.route.ts
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import { Router } from "express";
|
||||||
|
import { apiReturn } from "../utils/returnHelper.utils.js";
|
||||||
|
import { connectGPSql } from "./gpSqlConnection.controller.js";
|
||||||
|
|
||||||
|
const r = Router();
|
||||||
|
|
||||||
|
r.post("/start", async (_, res) => {
|
||||||
|
const connect = await connectGPSql();
|
||||||
|
apiReturn(res, {
|
||||||
|
success: connect.success,
|
||||||
|
level: connect.success ? "info" : "error",
|
||||||
|
module: "routes",
|
||||||
|
subModule: "prodSql",
|
||||||
|
message: connect.message,
|
||||||
|
data: connect.data,
|
||||||
|
status: connect.success ? 200 : 400,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
export default r;
|
||||||
20
backend/gpSql/gpSqlStop.route.ts
Normal file
20
backend/gpSql/gpSqlStop.route.ts
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import { Router } from "express";
|
||||||
|
import { apiReturn } from "../utils/returnHelper.utils.js";
|
||||||
|
import { closePool } from "./gpSqlConnection.controller.js";
|
||||||
|
|
||||||
|
const r = Router();
|
||||||
|
|
||||||
|
r.post("/stop", async (_, res) => {
|
||||||
|
const connect = await closePool();
|
||||||
|
apiReturn(res, {
|
||||||
|
success: connect.success,
|
||||||
|
level: connect.success ? "info" : "error",
|
||||||
|
module: "routes",
|
||||||
|
subModule: "prodSql",
|
||||||
|
message: connect.message,
|
||||||
|
data: connect.data,
|
||||||
|
status: connect.success ? 200 : 400,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
export default r;
|
||||||
16
backend/gpSql/queries/gp.eom.data.sql
Normal file
16
backend/gpSql/queries/gp.eom.data.sql
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
select * from (
|
||||||
|
select
|
||||||
|
case when x.POPRCTNM is null then p.POPRCTNM else p.POPRCTNM end as RCT_Num,
|
||||||
|
PONUMBER PO,
|
||||||
|
p.VENDORID Supplier,
|
||||||
|
ITEMNMBR Item,
|
||||||
|
QTYSHPPD shipped,
|
||||||
|
UOFM Type,
|
||||||
|
TRXLOCTN Location,
|
||||||
|
case when CONVERT(DATE, x.receiptdate) is null then convert(date, p.DATERECD) else CONVERT(DATE, x.receiptdate) end as Date_Received
|
||||||
|
from ALPLA.dbo.pop10500 (nolock) as p
|
||||||
|
left join
|
||||||
|
ALPLA.dbo.POP10300 as x on p.POPRCTNM = x.POPRCTNM
|
||||||
|
WHERE TRXLOCTN LIKE '[gpCode]%' and p.POPTYPE = 1) a
|
||||||
|
|
||||||
|
where Date_Received BETWEEN '[startDate]' AND '[endDate]'
|
||||||
39
backend/gpSql/queries/reqCheck.sql
Normal file
39
backend/gpSql/queries/reqCheck.sql
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
USE [ALPLA]
|
||||||
|
|
||||||
|
SELECT Distinct r.[POPRequisitionNumber] as req,
|
||||||
|
r.[ApprovalStatus] as approvalStatus,
|
||||||
|
r.[Requested By] requestedBy,
|
||||||
|
format(t.[Created Date], 'yyyy-MM-dd') as createdAt,
|
||||||
|
format(r.[Requisition Date], 'MM/dd/yyyy') as expectedDate,
|
||||||
|
r.[Requisition Amount] as glAccount,
|
||||||
|
case when r.[Account Segment 2] is null or r.[Account Segment 2] = '' then '999' else cast(r.[Account Segment 2] as varchar) end as plant
|
||||||
|
,t.Status as status
|
||||||
|
,t.[Document Status] as docStatus
|
||||||
|
,t.[Workflow Status] as reqState
|
||||||
|
,CASE
|
||||||
|
WHEN [Workflow Status] = 'Completed'
|
||||||
|
THEN 'Pending APO convertion'
|
||||||
|
WHEN [Workflow Status] = 'Pending User Action'
|
||||||
|
AND r.[ApprovalStatus] = 'Pending Approval'
|
||||||
|
THEN 'Pending plant approver'
|
||||||
|
WHEN [Workflow Status] = ''
|
||||||
|
AND r.[ApprovalStatus] = 'Not Submitted'
|
||||||
|
THEN 'Req not submited'
|
||||||
|
ELSE 'Unknown reason'
|
||||||
|
END AS approvedStatus
|
||||||
|
|
||||||
|
FROM [dbo].[PORequisitions] r (nolock)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
left join
|
||||||
|
[dbo].[PurchaseRequisitions] as t (nolock) on
|
||||||
|
t.[Requisition Number] = r.[POPRequisitionNumber]
|
||||||
|
|
||||||
|
|
||||||
|
--where ApprovalStatus = 'Pending Approval'
|
||||||
|
--and [Account Segment 2] = 80
|
||||||
|
|
||||||
|
where r.POPRequisitionNumber in ([reqsToCheck])
|
||||||
|
|
||||||
|
Order By r.POPRequisitionNumber
|
||||||
@@ -3,8 +3,8 @@ import { Writable } from "node:stream";
|
|||||||
import pino, { type Logger } from "pino";
|
import pino, { type Logger } from "pino";
|
||||||
import { db } from "../db/db.controller.js";
|
import { db } from "../db/db.controller.js";
|
||||||
import { logs } from "../db/schema/logs.schema.js";
|
import { logs } from "../db/schema/logs.schema.js";
|
||||||
import { emitToRoom } from "../socket.io/roomEmitter.socket.js";
|
|
||||||
import { tryCatch } from "../utils/trycatch.utils.js";
|
import { tryCatch } from "../utils/trycatch.utils.js";
|
||||||
|
import { notifySystemIssue } from "./logger.notify.js";
|
||||||
//import build from "pino-abstract-transport";
|
//import build from "pino-abstract-transport";
|
||||||
|
|
||||||
export const logLevel = process.env.LOG_LEVEL || "info";
|
export const logLevel = process.env.LOG_LEVEL || "info";
|
||||||
@@ -36,7 +36,7 @@ const dbStream = new Writable({
|
|||||||
subModule: obj?.subModule?.toLowerCase(),
|
subModule: obj?.subModule?.toLowerCase(),
|
||||||
hostname: obj?.hostname?.toLowerCase(),
|
hostname: obj?.hostname?.toLowerCase(),
|
||||||
message: obj.msg,
|
message: obj.msg,
|
||||||
stack: obj?.stack,
|
stack: obj?.stack || obj?.error, // this will add in the error or stack depending on how we pass it.
|
||||||
})
|
})
|
||||||
.returning(),
|
.returning(),
|
||||||
);
|
);
|
||||||
@@ -45,10 +45,14 @@ const dbStream = new Writable({
|
|||||||
console.error(res.error);
|
console.error(res.error);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (obj.room) {
|
if (obj.notify) {
|
||||||
emitToRoom(obj.room, res.data ? res.data[0] : obj);
|
notifySystemIssue(obj);
|
||||||
}
|
}
|
||||||
emitToRoom("logs", res.data ? res.data[0] : obj);
|
|
||||||
|
// if (obj.room) {
|
||||||
|
// emitToRoom(obj.room, res.data ? res.data[0] : obj);
|
||||||
|
// }
|
||||||
|
// emitToRoom("logs", res.data ? res.data[0] : obj);
|
||||||
callback();
|
callback();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("DB log insert error:", err);
|
console.error("DB log insert error:", err);
|
||||||
|
|||||||
44
backend/logger/logger.notify.ts
Normal file
44
backend/logger/logger.notify.ts
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
/**
|
||||||
|
* For all logging that has notify set to true well send an email to the system admins, if we have a discord webhook set well send it there as well
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { eq } from "drizzle-orm";
|
||||||
|
import { db } from "../db/db.controller.js";
|
||||||
|
import { user } from "../db/schema/auth.schema.js";
|
||||||
|
import { sendEmail } from "../utils/sendEmail.utils.js";
|
||||||
|
|
||||||
|
type NotifyData = {
|
||||||
|
module: string;
|
||||||
|
submodule: string;
|
||||||
|
hostname: string;
|
||||||
|
msg: string;
|
||||||
|
stack: unknown[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const notifySystemIssue = async (data: NotifyData) => {
|
||||||
|
// build the email out
|
||||||
|
|
||||||
|
const formattedError = Array.isArray(data.stack)
|
||||||
|
? data.stack.map((e: any) => e.error || e)
|
||||||
|
: data.stack;
|
||||||
|
|
||||||
|
const sysAdmin = await db
|
||||||
|
.select()
|
||||||
|
.from(user)
|
||||||
|
.where(eq(user.role, "systemAdmin"));
|
||||||
|
|
||||||
|
await sendEmail({
|
||||||
|
email: sysAdmin.map((r) => r.email).join("; ") ?? "cowchmonkey@gmail.com", // change to pull in system admin emails
|
||||||
|
subject: `${data.hostname} has encountered a critical issue.`,
|
||||||
|
template: "serverCritialIssue",
|
||||||
|
context: {
|
||||||
|
plant: data.hostname,
|
||||||
|
module: data.module,
|
||||||
|
subModule: data.submodule,
|
||||||
|
message: data.msg,
|
||||||
|
error: JSON.stringify(formattedError, null, 2),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// TODO: add discord
|
||||||
|
};
|
||||||
24
backend/logger/logger.socket.notifications.ts
Normal file
24
backend/logger/logger.socket.notifications.ts
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import { eq } from "drizzle-orm";
|
||||||
|
import { db } from "../db/db.controller.js";
|
||||||
|
import { logs } from "../db/schema/logs.schema.js";
|
||||||
|
import { emitToRoom } from "../socket.io/roomEmitter.socket.js";
|
||||||
|
|
||||||
|
export async function handleLogInsertedNotification(id: string) {
|
||||||
|
const row = await db.query.logs.findFirst({
|
||||||
|
where: eq(logs.id, id),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!row) return;
|
||||||
|
|
||||||
|
// More targeted rooms.
|
||||||
|
if (row.module) {
|
||||||
|
emitToRoom(`logs:${row.module}`, row);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (row.subModule) {
|
||||||
|
emitToRoom(`logs:${row.subModule}`, row);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Everyone listening to all logs.
|
||||||
|
emitToRoom("logs", row);
|
||||||
|
}
|
||||||
92
backend/logistics/logistics.dm.forecast.map.abbott.ts
Normal file
92
backend/logistics/logistics.dm.forecast.map.abbott.ts
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
import XLSX from "xlsx";
|
||||||
|
import { excelDateStuff } from "../utils/excelToDate.utils.js";
|
||||||
|
import { postData } from "./logistics.dm.postData.js";
|
||||||
|
|
||||||
|
export const abbottForecast = async (sheet: any, user: any) => {
|
||||||
|
/*
|
||||||
|
This is the forecast but will only be triggered when the actual sheet is passed over from the orders in. this is being done this way due to the truck list being sent over as well.
|
||||||
|
*/
|
||||||
|
const customerId = 8;
|
||||||
|
const posting: any = [];
|
||||||
|
|
||||||
|
const customHeaders = [
|
||||||
|
"date",
|
||||||
|
"time",
|
||||||
|
"newton8oz",
|
||||||
|
"newton10oz",
|
||||||
|
"E",
|
||||||
|
"F",
|
||||||
|
"fDate",
|
||||||
|
"f8ozqty",
|
||||||
|
"I",
|
||||||
|
"J",
|
||||||
|
"K",
|
||||||
|
"L",
|
||||||
|
"M",
|
||||||
|
"f10ozqty",
|
||||||
|
];
|
||||||
|
const forecastData = XLSX.utils.sheet_to_json(sheet, {
|
||||||
|
range: 5, // Start at row 5 (index 4)
|
||||||
|
header: customHeaders,
|
||||||
|
defval: "", // Default value for empty cells
|
||||||
|
});
|
||||||
|
|
||||||
|
for (let i = 1; i < forecastData.length; i++) {
|
||||||
|
const row: any = forecastData[i];
|
||||||
|
//console.log(row);
|
||||||
|
//if (row.fDate == undefined) continue;
|
||||||
|
|
||||||
|
if (row.fDate !== "") {
|
||||||
|
const date = isNaN(row.fDate)
|
||||||
|
? new Date(row.fDate)
|
||||||
|
: excelDateStuff(row.fDate);
|
||||||
|
// for 8oz do
|
||||||
|
if (row.f8ozqty > 0) {
|
||||||
|
posting.push({
|
||||||
|
customerArticleNo: "45300DA",
|
||||||
|
quantity: row.f8ozqty,
|
||||||
|
requirementDate: date,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (row.f10ozqty > 0) {
|
||||||
|
posting.push({
|
||||||
|
customerArticleNo: "43836DA",
|
||||||
|
quantity: row.f10ozqty,
|
||||||
|
requirementDate: date,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// the predefined data that will never change
|
||||||
|
const predefinedObject = {
|
||||||
|
receivingPlantId: process.env.PROD_PLANT_TOKEN ?? "test1",
|
||||||
|
documentName: `ForecastFromLST-${new Date(Date.now()).toLocaleString(
|
||||||
|
"en-US",
|
||||||
|
)}`,
|
||||||
|
sender: user.username || "lst-system",
|
||||||
|
customerId: customerId,
|
||||||
|
positions: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
// add the new forecast to the predefined data
|
||||||
|
const updatedPredefinedObject = {
|
||||||
|
...predefinedObject,
|
||||||
|
positions: [...predefinedObject.positions, ...posting],
|
||||||
|
};
|
||||||
|
|
||||||
|
const forecast: any = await postData(
|
||||||
|
{
|
||||||
|
type: "forecast",
|
||||||
|
endpoint: "/public/v1.0/DemandManagement/DELFOR",
|
||||||
|
data: updatedPredefinedObject as any,
|
||||||
|
},
|
||||||
|
user,
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: forecast.success,
|
||||||
|
message: forecast.message,
|
||||||
|
data: forecast.data,
|
||||||
|
};
|
||||||
|
};
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user