Compare commits
298 Commits
edc874c302
...
migration
| Author | SHA1 | Date | |
|---|---|---|---|
| 8943407f27 | |||
| 0bbe411db0 | |||
| 99b3ad633c | |||
| c892348d19 | |||
| 360549aaf4 | |||
| bdc1e72fc1 | |||
| 4cae96b1ee | |||
| 1cde8ab2e6 | |||
| 03e8378213 | |||
| 275c93dc79 | |||
| fbb8c21d5c | |||
| dce93d3de2 | |||
| faf4e9f9ab | |||
| a9783a7d35 | |||
| e9ca6dbbb2 | |||
| e996f99400 | |||
| 3b939ff2d3 | |||
| a7ff88025e | |||
| 00899a5b77 | |||
| 99ecf52218 | |||
| a6920d7cd8 | |||
| 788efdf4b3 | |||
| a2a17beeeb | |||
| 6c793c66a4 | |||
| 6918a5a7d4 | |||
| ddd9b0e372 | |||
| 41d921756b | |||
| d86ca7d3bf | |||
| 932a72ba88 | |||
| b9c6d0ba57 | |||
| 592e4c8cd7 | |||
| b772666905 | |||
| a6edcb71bf | |||
| 76d929abea | |||
| da17defe82 | |||
| 669f5ca33f | |||
| b649dca4c9 | |||
| 04b8d041f9 | |||
| 71bc7c29bc | |||
| 79f1f8f91b | |||
| b677bc1498 | |||
| a53915ad8c | |||
| b3ce767b32 | |||
| 6caa5984e7 | |||
| 415d2e4a1d | |||
| 271cdbdbfa | |||
| 796a8dccd2 | |||
| e03e92c18d | |||
| dd2e5d04ae | |||
| 708b57b926 | |||
| 0444da8bbc | |||
| 6d49fae16d | |||
| be8a44f2dc | |||
| a3994a1f69 | |||
| 00eaf62d4c | |||
| 6851777faf | |||
| f9e97fc224 | |||
| 4e3c5e4191 | |||
| 172fd1d5c2 | |||
| 1094de2ebd | |||
| 7462b3d90b | |||
| 2045c13ef2 | |||
| 5fa9e83d5b | |||
| f48c944bd0 | |||
| dddd1d422e | |||
| 8133443ec9 | |||
| 28c7d30c1a | |||
| 57966ac9de | |||
| 6910550de7 | |||
| 1f8b8a7248 | |||
| cfed981928 | |||
| 0efe74d4b1 | |||
| c35db2e209 | |||
| e1cdeb740b | |||
| 3c9e75dc3c | |||
| 64b5b0d248 | |||
| 668eae9719 | |||
| 7cc3778506 | |||
| af47c1582e | |||
| 081572c421 | |||
| c6f6ef6262 | |||
| 34849e15d1 | |||
| 9bd66942f5 | |||
| 795da35141 | |||
| 468f933168 | |||
| 8c296c5b78 | |||
| 53ed2c4e6a | |||
| 0a6ddea8c0 | |||
| df423192bf | |||
| 2f908398bc | |||
| bf8203bbee | |||
| e92eccf785 | |||
| f4f3de49ca | |||
| 788d6367a3 | |||
| 24a97afe06 | |||
| 37f82a9710 | |||
| 369d16018c | |||
| 68901a857a | |||
| 171763184c | |||
| b7de2a8dbe | |||
| e78083f496 | |||
| 3f3b64bf2b | |||
| 123bb127e8 | |||
| 8d6ead3aa1 | |||
| 3148aa79d7 | |||
| 4486fe2436 | |||
| 0ba338d480 | |||
| 846ac479b1 | |||
| 73d38ba3fe | |||
| 27586e923a | |||
| 662a951b98 | |||
| 0c54cecbd4 | |||
| bcf5378966 | |||
| c5668e6cf1 | |||
| 213814b868 | |||
| 7d5b0c46c1 | |||
| 595e22e8e9 | |||
| a6f18554b8 | |||
| 88f61c8eaa | |||
| a183279268 | |||
| 5156c8bf7b | |||
| 4669ff95dc | |||
| ed93992165 | |||
| f16e2bf53b | |||
| a84998438c | |||
| 83469105f0 | |||
| 835ae58f04 | |||
| 1498a19121 | |||
| ca96849991 | |||
| 27b37f9849 | |||
| 7f81a2e09a | |||
| e14abd3644 | |||
| 2ff7b9baf9 | |||
| 8145dc800d | |||
| 6ccf500e5e | |||
| 103171c924 | |||
| 2eb6fa7794 | |||
| 397f1da595 | |||
| 8d63f7f6b0 | |||
| 52345bc94c | |||
| a8a1c1d7fb | |||
| 83ff2641f3 | |||
| 7c48f608bc | |||
| 1802b9ba4e | |||
| 67a12ccc5c | |||
| 15e2a65cbb | |||
| 9e5577e6bb | |||
| c52e2a8671 | |||
| 8f76d6998c | |||
| e209686d3c | |||
| 803c963f96 | |||
| 078f35626b | |||
| 2288884829 | |||
| 69fc7418c9 | |||
| a0179a41ba | |||
| a36552fd9b | |||
| c7bb12822b | |||
| 98a5ca7bb8 | |||
| 74ac2864c9 | |||
| 1f93eca561 | |||
| 1f44750346 | |||
| 0d17fef1a1 | |||
| f574645b44 | |||
| 998e84f564 | |||
| 3e51ebc18b | |||
| 8129dbb787 | |||
| b5561635dd | |||
| ff340161d6 | |||
| 39d0768d50 | |||
| d40ed30f55 | |||
| 8594d72d2c | |||
| 3e680a5a50 | |||
| e1113c49b3 | |||
| e964bb70ae | |||
| f01b6941ba | |||
| e78f3ffdb5 | |||
| 713df0b615 | |||
| f2615fbcea | |||
| 4f7d6fe4af | |||
| 96a3fd451b | |||
| d1befadd1c | |||
| fa99eb41dd | |||
| 11cc3662cf | |||
| d9a2f90056 | |||
| 23ee0468ad | |||
| 83ba84cf6f | |||
| 96d2a25081 | |||
| d54f85cf3d | |||
| 371870cbe5 | |||
| dd31763fdd | |||
| c9a64e2e7c | |||
| 733603fffa | |||
| bc92f8be0e | |||
| 9ced82b154 | |||
| d9bc68d6ac | |||
| 248e4cfeef | |||
| 9707990211 | |||
| 60672ba751 | |||
| 3b8190b080 | |||
| c1577ff432 | |||
| 40b382ca62 | |||
| 1ec3de0f02 | |||
| f5c035081c | |||
| 885e0ce348 | |||
| 7a59da77f2 | |||
| 82fb85ba0d | |||
| 0d263f6941 | |||
| 1762c70859 | |||
| c196b1d612 | |||
| 8926bfde00 | |||
| 256405b5a5 | |||
| eecf35fc9c | |||
| 700b18160c | |||
| 2dd6947bd6 | |||
| fe8ab62e94 | |||
| a0d476042b | |||
| 8e99a2e5e5 | |||
| 4b9f96f3b0 | |||
| 390107a1fb | |||
| e3a4ef7a80 | |||
| c224e30b8d | |||
| 16b710dc91 | |||
| a9e788f438 | |||
| 3aa4191b0f | |||
| 260d513ee3 | |||
| e9f729d5e2 | |||
| e43ac65d04 | |||
| 5199a9ce69 | |||
| 5ce78222b9 | |||
| 904de61e5a | |||
| c1ed482073 | |||
| f57426cf13 | |||
| 7079b151da | |||
| b87cd8dd7c | |||
| 531b343d17 | |||
| e2b549714c | |||
| e6f3a9b4d0 | |||
| 33295dc3c3 | |||
| 49fd9d65c9 | |||
| 0510f13885 | |||
| 588f264854 | |||
| 8afa4176ef | |||
| 611183fc19 | |||
| c31856ac3e | |||
| 61f0b7f06b | |||
| a7f8e39bac | |||
| 4923b3c698 | |||
| 28f34a6a31 | |||
| b81675e445 | |||
| c6d80dbc8a | |||
| bc54b365ea | |||
| 41308788fd | |||
| d6232cb358 | |||
| d89a336fb1 | |||
| e84ae42c83 | |||
| 6ec8f0863b | |||
| 4f231b343c | |||
| 454039e60c | |||
| c7c148fede | |||
| e30adc6612 | |||
| f7b4de8130 | |||
| 6584b37cb0 | |||
| 6a082d9737 | |||
| 58711becf2 | |||
| f757085fc2 | |||
| 8f5e1cb094 | |||
| 7497ee454c | |||
| c0ede8714c | |||
| 1a6a05112b | |||
| 97c6631eab | |||
| 1ae506939c | |||
| f0294942a7 | |||
| e323607b39 | |||
| df0851414e | |||
| a9b860a34d | |||
| ead35b4360 | |||
| d6b1a75afa | |||
| 1b661a6685 | |||
| f597142ee3 | |||
| 18780d872c | |||
| ba02ee42c5 | |||
| f573f4f721 | |||
| 1d6bf7215b | |||
| e616574fa6 | |||
| 08109f500f | |||
| ab8102afbf | |||
| e13b5a4283 | |||
| 7a4dea9d87 | |||
| a19ac539e7 | |||
| 1c910d80ea | |||
| 56e0f3eb47 | |||
| e50e9af839 | |||
| e34f273acb | |||
| 2c5f8a3a8c | |||
| 9e4729690a | |||
| 26ab6ecd8d | |||
| 05fa774d68 | |||
| b1acd97232 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -156,3 +156,5 @@ dist
|
||||
backend-0.1.3.zip
|
||||
BulkForecastTemplate
|
||||
BulkOrdersTemplate
|
||||
check.json
|
||||
server/services/ocp/controller/materials/materialcheck.bak
|
||||
|
||||
8
.includes
Normal file
8
.includes
Normal file
@@ -0,0 +1,8 @@
|
||||
database
|
||||
dist
|
||||
frontend/dist
|
||||
CHANGELOG.md
|
||||
drizzle.config.ts
|
||||
package.json
|
||||
package-lock.json
|
||||
README.md
|
||||
180
CHANGELOG.md
180
CHANGELOG.md
@@ -1,5 +1,185 @@
|
||||
# All CHanges to LST can be found below.
|
||||
|
||||
## [2.27.0](https://git.tuffraid.net/cowch/lstV2/compare/v2.26.0...v2.27.0) (2025-09-10)
|
||||
|
||||
|
||||
### 🌟 Enhancements
|
||||
|
||||
* **eom:** lastSales, lastPurch added to be pulled with new template ([7cc3778](https://git.tuffraid.net/cowch/lstV2/commits/7cc3778506fc92392ca8431aee0edb203861e10d))
|
||||
|
||||
|
||||
### 🛠️ Code Refactor
|
||||
|
||||
* **cosume:** changes to allow non logged in users to use this function ([271cdbd](https://git.tuffraid.net/cowch/lstV2/commits/271cdbdbfa2478ecf56e9b01a4474508acacda2e))
|
||||
* **mm query:** changes to the query to see pkg and materials properly and not duplicates ([57966ac](https://git.tuffraid.net/cowch/lstV2/commits/57966ac9de72543014451b7ccd75539296ccaa51))
|
||||
* **rfid:** changes to show all tags vs only 3 if there are more ([b677bc1](https://git.tuffraid.net/cowch/lstV2/commits/b677bc14981faff30c91f6ffe4602319dd3c6016))
|
||||
* **silo card:** changes to allow viewers to see and able to attach and detach ([796a8dc](https://git.tuffraid.net/cowch/lstV2/commits/796a8dccd2807890abdff7c8dacf8b2246eb265e))
|
||||
* **sql:** articles added in UOM ([cfed981](https://git.tuffraid.net/cowch/lstV2/commits/cfed981928a56389e09ef428c43ceabc1caec28e))
|
||||
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
* **article check:** corrected the query to not have a specfic plant in it ([79f1f8f](https://git.tuffraid.net/cowch/lstV2/commits/79f1f8f91ba33533de7f4a7cc91503cfd8dd4ce5))
|
||||
* **bookin:** corrected the bookin in error response ([1f8b8a7](https://git.tuffraid.net/cowch/lstV2/commits/1f8b8a7248c11c7e264c8c5ae7c042c5a0878c46))
|
||||
* **bookin:** corrections to only show the message on error vs the json ([b3ce767](https://git.tuffraid.net/cowch/lstV2/commits/b3ce767b323c990c0ccf35ad6c2c67136a27272e))
|
||||
* **db:** changes to the user so it deletes correctly ([932a72b](https://git.tuffraid.net/cowch/lstV2/commits/932a72ba884673471f0056e721cc3f2c8e34b4f3))
|
||||
* **eomservice:** changes to stop a crash incase the sql returned nothing due to start up ([6caa598](https://git.tuffraid.net/cowch/lstV2/commits/6caa5984e7a3e7b48b119c176835663ffec71151))
|
||||
* **frontend:** typos ([c6f6ef6](https://git.tuffraid.net/cowch/lstV2/commits/c6f6ef626295f452cdf26f6776b74cfb3b1a10f5))
|
||||
* **label query:** fixes to only pull in active layouts ([99ecf52](https://git.tuffraid.net/cowch/lstV2/commits/99ecf52218556e048ba9262e74f9b3d020dea31d))
|
||||
* **main material check:** corrections to properly ignore pkg during color checks ([6910550](https://git.tuffraid.net/cowch/lstV2/commits/6910550de769dce04b1045f96ab19cf7b8d1ef8c))
|
||||
* **material check:** split manual material out of the mm to properly catch it ([a53915a](https://git.tuffraid.net/cowch/lstV2/commits/a53915ad8cbec5bd8d6ba4643c460ad0162249e2))
|
||||
* **materials:** more fixes to try and please all plants to use this version and not call me ([00899a5](https://git.tuffraid.net/cowch/lstV2/commits/00899a5b778eab792b350a0b47589de1524d91c8))
|
||||
* **register:** changes to not give everyone system admin ([415d2e4](https://git.tuffraid.net/cowch/lstV2/commits/415d2e4a1d851cc46ac64ffc814a280a02293bbc))
|
||||
|
||||
## [2.26.0](https://git.tuffraid.net/cowch/lstV2/compare/v2.25.0...v2.26.0) (2025-08-28)
|
||||
|
||||
|
||||
### 🛠️ Code Refactor
|
||||
|
||||
* **labeling:** moved bookin fails inside bookin as it could be off ([df42319](https://git.tuffraid.net/cowch/lstV2/commits/df423192bfc5e2389872147e92b7d22e648a2927))
|
||||
* **labeling:** removed the wrong import ([788d636](https://git.tuffraid.net/cowch/lstV2/commits/788d6367a380a81722a76bd6adbe18831d88eeb5))
|
||||
* **materials:** changes for permissions on material consume ([3148aa7](https://git.tuffraid.net/cowch/lstV2/commits/3148aa79d73823499824cc0601fcabec97bb4f9d))
|
||||
* **materials:** changes to allow exact and eom transfers ([0a6ddea](https://git.tuffraid.net/cowch/lstV2/commits/0a6ddea8c0d2773aba00266df7e2839879d10cb1))
|
||||
* **notifcations:** changed hour to min in ti intergrations ([73d38ba](https://git.tuffraid.net/cowch/lstV2/commits/73d38ba3fe7ab4d5ca3a4a1e1c99fdd7bf5e92dc))
|
||||
* **tms intergration:** corrected how we added gl coding ([37f82a9](https://git.tuffraid.net/cowch/lstV2/commits/37f82a9710cf20e6d0d056893c9da43c70b9e619))
|
||||
|
||||
|
||||
### 🌟 Enhancements
|
||||
|
||||
* **materials:** added in a bigger window on eom transfer lots ([53ed2c4](https://git.tuffraid.net/cowch/lstV2/commits/53ed2c4e6a0d3fafafbcb655b5d06cff6324363d))
|
||||
* **ocp:** zechetti 1 added in ([27586e9](https://git.tuffraid.net/cowch/lstV2/commits/27586e923a106f2b8dd804e9c8292edd5d009cf0))
|
||||
* **rfid:** new check to remove tags that have been at a line longer than 6 hours ([0ba338d](https://git.tuffraid.net/cowch/lstV2/commits/0ba338d48037f7def74196ca3f41de5807e2cb31))
|
||||
* **tms:** a clean up function was added to remove releases added as blockers older than 45d ([662a951](https://git.tuffraid.net/cowch/lstV2/commits/662a951b9871d5dfc21f01a76ba23be77b475757))
|
||||
|
||||
|
||||
### 📝 Testing Code
|
||||
|
||||
* **forklifts:** forklift starting process ([8c296c5](https://git.tuffraid.net/cowch/lstV2/commits/8c296c5b783f42f85c30a955d6e9d540c50dcfbe))
|
||||
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
* **bookin:** corrected the error received from the book in fail ([369d160](https://git.tuffraid.net/cowch/lstV2/commits/369d16018c8be9fdb936afbc3749b3f9fcea58c8))
|
||||
* **dm:** corrected the remark section so its properly sent over ([68901a8](https://git.tuffraid.net/cowch/lstV2/commits/68901a857ae5161f024da9cdefc0a981a4907205))
|
||||
* **eom stats:** corrections to the eom inv stuff to be proper tiems ([468f933](https://git.tuffraid.net/cowch/lstV2/commits/468f933168cc40be0c1b159c31948a2c97600390))
|
||||
* **fake edi:** removed console log ([24a97af](https://git.tuffraid.net/cowch/lstV2/commits/24a97afe064d5c44872e24e6e3299ed5f2977a78))
|
||||
* **material check:** alt mm causing issues and utilizing an 80% to just be ok ([f4f3de4](https://git.tuffraid.net/cowch/lstV2/commits/f4f3de49cae277ffa158116d1802495b27fa8e75))
|
||||
* **ocp:** zechetti type correction to include the printer name ([4486fe2](https://git.tuffraid.net/cowch/lstV2/commits/4486fe24362b4811d7cff0467c7f2f85e0c9e3c4))
|
||||
* **produser:** changes to include DM ([8d6ead3](https://git.tuffraid.net/cowch/lstV2/commits/8d6ead3aa11aed95ffa3537fbe72cfa9bbceb380))
|
||||
* **transferlots:** missed adding this ([846ac47](https://git.tuffraid.net/cowch/lstV2/commits/846ac479b1bf211a7891ae362525ea14580ff0cc))
|
||||
|
||||
## [2.25.0](https://git.tuffraid.net/cowch/lstV2/compare/v2.24.1...v2.25.0) (2025-08-21)
|
||||
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
* **dm page:** correction to the insturcitons ([a36552f](https://git.tuffraid.net/cowch/lstV2/commits/a36552fd9b9c77f8ecee8b36f45e613383841f95))
|
||||
* **dyco:** correction to disable the camera if ocme is off ([c7bb128](https://git.tuffraid.net/cowch/lstV2/commits/c7bb12822b13c0c1c929d2c8a9ab150cd0feeff2))
|
||||
* **gotransport:** error handling so we dont get spammed with errors ([2eb6fa7](https://git.tuffraid.net/cowch/lstV2/commits/2eb6fa77946d5f8572ffc5baa47da602482bce2b))
|
||||
* **https fixes:** made it so the settings can be grabbed via https ([803c963](https://git.tuffraid.net/cowch/lstV2/commits/803c963f964f26095b2aa6a7d0a60e03615d4c17))
|
||||
* **inv cards:** correction to properly display the names ([2288884](https://git.tuffraid.net/cowch/lstV2/commits/22888848291aa3df4ebbdd224656731fbb305fa7))
|
||||
* **inv query:** error in improper placed , in the query ([397f1da](https://git.tuffraid.net/cowch/lstV2/commits/397f1da595fd8a1e1c2a630a3650eb8715604c82))
|
||||
* **mainmaterial check:** if the machine dose not require mm to be staged properly ignore ([0d17fef](https://git.tuffraid.net/cowch/lstV2/commits/0d17fef1a1c75dd0f27988fd2e3527b508b915cb))
|
||||
|
||||
|
||||
### 🛠️ Code Refactor
|
||||
|
||||
* **migration progress:** moved to start looking at the go backedn ([a0179a4](https://git.tuffraid.net/cowch/lstV2/commits/a0179a41bac93d2a7320802e9d70aa966ed79ae4))
|
||||
* **migrations:** not needed but we have it and needed to correct the settings ([2ff7b9b](https://git.tuffraid.net/cowch/lstV2/commits/2ff7b9baf9ca288f8a33bec3ab1a2ba331ace6b9))
|
||||
* **notifications:** refactored the cron job system so we can utilize outside the service ([103171c](https://git.tuffraid.net/cowch/lstV2/commits/103171c924a9de78b0a7600abb455fdd6f4bfea1))
|
||||
* **siloadjustment:** refactored to get the settings from the state vs direct from db ([8145dc8](https://git.tuffraid.net/cowch/lstV2/commits/8145dc800dced31860a926c80eca72cb39433b29))
|
||||
|
||||
|
||||
### 🌟 Enhancements
|
||||
|
||||
* **dm:** changes to have a default time if nothing is passed in the excel ([9e5577e](https://git.tuffraid.net/cowch/lstV2/commits/9e5577e6bb4ff3b6c4004288e177fbab322a4b44))
|
||||
* **eom:** added in hostorical data and deletion for data over 45 days ([52345bc](https://git.tuffraid.net/cowch/lstV2/commits/52345bc94c9e8abc82150fb371a9ba0d0757f16a))
|
||||
* **migration start:** this starts the migration of all settings to look at the go backend vs this ([998e84f](https://git.tuffraid.net/cowch/lstV2/commits/998e84f5648148c9a94df7177a3d311e16bf4614))
|
||||
* **ocp:** materials contorls and transfer to next lot logic ([88f61c8](https://git.tuffraid.net/cowch/lstV2/commits/88f61c8eaa32a581094b04b5e18c654040dbeb92))
|
||||
* **prodrole:** added in planner role ([6ccf500](https://git.tuffraid.net/cowch/lstV2/commits/6ccf500e5eb82125f7bcd3d764282a4b8a750be9))
|
||||
* **psi:** psi querys added and av grab right now ([8d63f7f](https://git.tuffraid.net/cowch/lstV2/commits/8d63f7f6b0981d604b628ff2f710b8eb41d32837))
|
||||
|
||||
### [2.24.1](https://git.tuffraid.net/cowch/lstV2/compare/v2.24.0...v2.24.1) (2025-07-25)
|
||||
|
||||
|
||||
### 🛠️ Code Refactor
|
||||
|
||||
* **serverdata:** moved stp1 to d drive ([f57426c](https://git.tuffraid.net/cowch/lstV2/commits/f57426cf13b11b357a1436317f592286084e0fb1))
|
||||
* **zip builds:** removed the c# wrapper as it wont be used on this build anymore ([e3a4ef7](https://git.tuffraid.net/cowch/lstV2/commits/e3a4ef7a804390cf1e381766baf7baf60b873a57))
|
||||
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
* **create labels:** better logging to know where the error came from ([d40ed30](https://git.tuffraid.net/cowch/lstV2/commits/d40ed30f55b1fb1a3c7a4ecde6dc40c535f3c0ed))
|
||||
* **mm check:** correction to know if a machine is required to have mm or not ([39d0768](https://git.tuffraid.net/cowch/lstV2/commits/39d0768d503c415b5d1ca845db2a292613f4fbed))
|
||||
* **submodules:** correction to accessing when changes made to roles ([3e680a5](https://git.tuffraid.net/cowch/lstV2/commits/3e680a5a503761a4a358b4a62e07c4aa545a6f10))
|
||||
|
||||
## [2.24.0](https://git.tuffraid.net/cowch/lstV2/compare/v2.23.0...v2.24.0) (2025-07-15)
|
||||
|
||||
|
||||
### 📝 Chore
|
||||
|
||||
* **updated packages:** all pacakges were just updated ([4a02bbc](https://git.tuffraid.net/cowch/lstV2/commits/4a02bbc3686f6ed150f1f22146c924aacf1f6b1b))
|
||||
|
||||
|
||||
### 📝 Testing Code
|
||||
|
||||
* **material returns:** added type check ([d6d19f8](https://git.tuffraid.net/cowch/lstV2/commits/d6d19f8e5b81d34652c9b7421cb934a81ef03136))
|
||||
|
||||
|
||||
### 🌟 Enhancements
|
||||
|
||||
* **default accounts:** added in a default account run this will create a bunch of system admins ([b9ff0a4](https://git.tuffraid.net/cowch/lstV2/commits/b9ff0a4138843e191351f2bcec75e8305a78d6f2))
|
||||
* **labeling:** ratios reset for labeling implemeneted ([61f0b7f](https://git.tuffraid.net/cowch/lstV2/commits/61f0b7f06b8f0a811387dc3f17fcacb00804d146))
|
||||
* **labelratio:** new feature to monitor label ratio from auto and manual ([4130878](https://git.tuffraid.net/cowch/lstV2/commits/41308788fdc5e918450db0721199bd4ac7da17e7))
|
||||
* **logistics:** get sscc based on runnning number ([7df512a](https://git.tuffraid.net/cowch/lstV2/commits/7df512acaa3a2b3cc30131d61563580c124d4f05))
|
||||
* **notifcations:** pallets booked as waste brought back in via cycle count ([095d724](https://git.tuffraid.net/cowch/lstV2/commits/095d724e65f211253ccdb075a2afb3c5ad267932))
|
||||
* **notify:** shortage bookings based on time and article type ([c5bd5a7](https://git.tuffraid.net/cowch/lstV2/commits/c5bd5a7c0a074ac1b94fa5435992f2b9a8e65132))
|
||||
* **prodroles:** added in quality manager ([4abecd0](https://git.tuffraid.net/cowch/lstV2/commits/4abecd045556c98b42cd6ac132958a32a3279a54))
|
||||
* **prodroles:** new cto role added ([ab8102a](https://git.tuffraid.net/cowch/lstV2/commits/ab8102afbfc62ba41de38c021ab7c0a78e972a90))
|
||||
* **readers:** added in good and bad reads to monitor eff ([c0ede87](https://git.tuffraid.net/cowch/lstV2/commits/c0ede8714cd99e9b92b4169b9eaf90de3e1d46ab))
|
||||
* **rfid:** added in manual trigger for rfid reader at the wrapper ([7497ee4](https://git.tuffraid.net/cowch/lstV2/commits/7497ee454c792fabe2c5bd0427f63e725b4a781e))
|
||||
* **rfid:** front end view of the readers and there status ([f7b4de8](https://git.tuffraid.net/cowch/lstV2/commits/f7b4de813051f1bd28fea98c6473a156024b1a1c))
|
||||
* **rfid:** more rfid topics while monitoring the system run ([6a082d9](https://git.tuffraid.net/cowch/lstV2/commits/6a082d97375be872ecec33c2045d37c8368d790a))
|
||||
* **silo attached detach:** added in silo attach detach setup in the silo card ([2ac4813](https://git.tuffraid.net/cowch/lstV2/commits/2ac48138cba961fc09853ab16b4bb2f1dc2972f0))
|
||||
|
||||
|
||||
### 🛠️ Code Refactor
|
||||
|
||||
* **consume material:** changes to use api vs prod user ([bc54b36](https://git.tuffraid.net/cowch/lstV2/commits/bc54b365ea9472170a1d22364ac3d9b635175386))
|
||||
* **datamart:** changes to customer inventory to include specific whse ([3073df3](https://git.tuffraid.net/cowch/lstV2/commits/3073df342ff2ede979d2e7f4b2bb16dacb99c093))
|
||||
* **dbscheme:** refactoring from opening and saving ([c155e89](https://git.tuffraid.net/cowch/lstV2/commits/c155e89bc7e62a4beb09b4978445075c088d0133))
|
||||
* **dyco:** change the plc check time from 5 > 8 ([e50e9af](https://git.tuffraid.net/cowch/lstV2/commits/e50e9af839caf9aca8bf71419433bee41702a656))
|
||||
* **dyco:** pickup changed to let spam again so we dont miss anything ([49fd9d6](https://git.tuffraid.net/cowch/lstV2/commits/49fd9d65c9cf23c1524ec2d4c16628cb742a6077))
|
||||
* **finance query:** changes to include article type ([26ceb95](https://git.tuffraid.net/cowch/lstV2/commits/26ceb95daefa1615dbc4e41f68763283c1ae91a3))
|
||||
* **lim:** shipping hours change ([7ed9db1](https://git.tuffraid.net/cowch/lstV2/commits/7ed9db1bdb2c5a1fee867d1501fc909f47e8ddaf))
|
||||
* **mm check:** added in a check that if we dont need mm we return mm checked ([58711be](https://git.tuffraid.net/cowch/lstV2/commits/58711becf2eec518ebca9a0239c2b97f23b38372))
|
||||
* **ocp page:** changes to the layout so it can better be moved for the tiles ([f757085](https://git.tuffraid.net/cowch/lstV2/commits/f757085fc2936c929fbfce19e9784ab14654193d))
|
||||
* **printers:** addedin daily check for printers used to only be on a restart ([588f264](https://git.tuffraid.net/cowch/lstV2/commits/588f264854a5898f633114032398608985e4371c))
|
||||
* **rfid:** ratio resets implemeneted ([a7f8e39](https://git.tuffraid.net/cowch/lstV2/commits/a7f8e39bac684b94a68ed484987b9c6c1693e3ec))
|
||||
* **rfid:** reduced teh rssi peak -50 > -40 ([a19ac53](https://git.tuffraid.net/cowch/lstV2/commits/a19ac539e7e65741858730e2089517756962c558))
|
||||
* **server data:** changes to bet and stp1 ([bd5bad3](https://git.tuffraid.net/cowch/lstV2/commits/bd5bad3cba587f53c8c6e31555c6433620c7a474))
|
||||
* **serverdb:** format changes ([77d9e5d](https://git.tuffraid.net/cowch/lstV2/commits/77d9e5d095a1f31fcbdd094b2dd39b89ddabce87))
|
||||
* **wrapper card:** changes to include the rfid and remove the manual trigger as no one used it ([8f5e1cb](https://git.tuffraid.net/cowch/lstV2/commits/8f5e1cb0948354d13b7def478226e3e82b377829))
|
||||
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
* **dashboard:** some more rending fixes due to some strange behaivers ([f4dd572](https://git.tuffraid.net/cowch/lstV2/commits/f4dd572a8f8889c5938145ef9d74621da27ae206))
|
||||
* **db:** lastRead column was named incorrectly ([1a6a051](https://git.tuffraid.net/cowch/lstV2/commits/1a6a05112b869358b3f2e87e688670ab95eb27a7))
|
||||
* **detach silo:** when pressing cancel the detach funciton would not work ([05fa774](https://git.tuffraid.net/cowch/lstV2/commits/05fa774d68c5bb006dd7fb317138bbc630a1cf55))
|
||||
* **dm:** missing api key call, and better logging on the post orders section ([531b343](https://git.tuffraid.net/cowch/lstV2/commits/531b343d17a0706bb18f8c7fc047422c5348fb92))
|
||||
* **label ratio:** missed a manual spot that was not considered ([e6f3a9b](https://git.tuffraid.net/cowch/lstV2/commits/e6f3a9b4d07765264d788a261a7b10c731d19987))
|
||||
* **logs:** time crap ([e13b5a4](https://git.tuffraid.net/cowch/lstV2/commits/e13b5a42836f337607f2fc0fd359c3f8cfdf4443))
|
||||
* **ocp page:** server info error added check to make sure was there ([3cafbd9](https://git.tuffraid.net/cowch/lstV2/commits/3cafbd92d0d1fd6dbdb8d92cfa811b9eaa7c60d0))
|
||||
* **renderfixes:** some strange rendering fixes that should have been caught long time ago ([3b56a5e](https://git.tuffraid.net/cowch/lstV2/commits/3b56a5e3e2d37858be4c4ce08f33abbf31f141d0))
|
||||
* **rfid reader:** ratio to be fixed 2 digits so it dose not span across the entire page ([c6d80db](https://git.tuffraid.net/cowch/lstV2/commits/c6d80dbc8a0dcb5a8b219913c9dec1065208012e))
|
||||
* **rfid:** corrections to the manual tag to no fail on manual when a tag already exists but failed ([33295dc](https://git.tuffraid.net/cowch/lstV2/commits/33295dc3c39569d818d4c7245390891e34080c9d))
|
||||
* **rfid:** existing tags were trying to be inserted and instead of updating failed corrected ([1c910d8](https://git.tuffraid.net/cowch/lstV2/commits/1c910d80ead6b9196dd4181534c6044a007e81b5))
|
||||
* **rfid:** lots of changes to the wrapper area ([7a4dea9](https://git.tuffraid.net/cowch/lstV2/commits/7a4dea9d87fa15def544d810b8da422acd142e6c))
|
||||
* **rfid:** typos in station 3 logging ([56e0f3e](https://git.tuffraid.net/cowch/lstV2/commits/56e0f3eb47a1c34b9b07121bc6189e793e08eea5))
|
||||
* **settings page:** error with the store not starting as fast as the page ([edc874c](https://git.tuffraid.net/cowch/lstV2/commits/edc874c302a8693b5d7a3abdb98cd95f44134e49))
|
||||
|
||||
## [2.23.0](https://git.tuffraid.net/cowch/lstV2/compare/v2.22.0...v2.23.0) (2025-06-17)
|
||||
|
||||
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
# THIS VERSION IS NO LONGER BEING UPDATED PLEASE GO TO THE NEW REPO LINK BELOW
|
||||
|
||||
[NEW LST REPO](https://git.tuffraid.net/cowch/lst)
|
||||
|
||||
# lstV2
|
||||
|
||||
Logistics Support Tool V2
|
||||
|
||||
1
database/migrations/0068_last_caretaker.sql
Normal file
1
database/migrations/0068_last_caretaker.sql
Normal file
@@ -0,0 +1 @@
|
||||
ALTER TABLE "rfidTags" RENAME COLUMN "timeStamp" TO "lastRead";
|
||||
2
database/migrations/0069_chemical_maximus.sql
Normal file
2
database/migrations/0069_chemical_maximus.sql
Normal file
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE "rfidReaders" ADD COLUMN "goodReads" integer DEFAULT 0;--> statement-breakpoint
|
||||
ALTER TABLE "rfidReaders" ADD COLUMN "badReads" integer DEFAULT 0;
|
||||
10
database/migrations/0070_brief_mephisto.sql
Normal file
10
database/migrations/0070_brief_mephisto.sql
Normal file
@@ -0,0 +1,10 @@
|
||||
CREATE TABLE "labelRatio" (
|
||||
" ratio_id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"name" text DEFAULT 'labels',
|
||||
"autoLabel" integer DEFAULT 0,
|
||||
"manualLabel" integer DEFAULT 0,
|
||||
"lastReset" timestamp DEFAULT now()
|
||||
);
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE "invHistoricalData" ADD COLUMN "lot_number" text;--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX "labelname" ON "labelRatio" USING btree ("name");
|
||||
2
database/migrations/0071_fantastic_old_lace.sql
Normal file
2
database/migrations/0071_fantastic_old_lace.sql
Normal file
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE "invHistoricalData" ALTER COLUMN "upd_user" SET DEFAULT 'lst';--> statement-breakpoint
|
||||
ALTER TABLE "invHistoricalData" ALTER COLUMN "upd_date" SET DEFAULT now();
|
||||
3
database/migrations/0072_round_black_knight.sql
Normal file
3
database/migrations/0072_round_black_knight.sql
Normal file
@@ -0,0 +1,3 @@
|
||||
ALTER TABLE "userRoles" DROP CONSTRAINT "userRoles_user_id_users_user_id_fk";
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE "userRoles" ADD CONSTRAINT "userRoles_user_id_users_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("user_id") ON DELETE cascade ON UPDATE no action;
|
||||
2166
database/migrations/meta/0068_snapshot.json
Normal file
2166
database/migrations/meta/0068_snapshot.json
Normal file
File diff suppressed because it is too large
Load Diff
2180
database/migrations/meta/0069_snapshot.json
Normal file
2180
database/migrations/meta/0069_snapshot.json
Normal file
File diff suppressed because it is too large
Load Diff
2250
database/migrations/meta/0070_snapshot.json
Normal file
2250
database/migrations/meta/0070_snapshot.json
Normal file
File diff suppressed because it is too large
Load Diff
2252
database/migrations/meta/0071_snapshot.json
Normal file
2252
database/migrations/meta/0071_snapshot.json
Normal file
File diff suppressed because it is too large
Load Diff
2252
database/migrations/meta/0072_snapshot.json
Normal file
2252
database/migrations/meta/0072_snapshot.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -477,6 +477,41 @@
|
||||
"when": 1749744049457,
|
||||
"tag": "0067_shallow_trish_tilby",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 68,
|
||||
"version": "7",
|
||||
"when": 1752195741709,
|
||||
"tag": "0068_last_caretaker",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 69,
|
||||
"version": "7",
|
||||
"when": 1752195894698,
|
||||
"tag": "0069_chemical_maximus",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 70,
|
||||
"version": "7",
|
||||
"when": 1754767718941,
|
||||
"tag": "0070_brief_mephisto",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 71,
|
||||
"version": "7",
|
||||
"when": 1754768521841,
|
||||
"tag": "0071_fantastic_old_lace",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 72,
|
||||
"version": "7",
|
||||
"when": 1757167736042,
|
||||
"tag": "0072_round_black_knight",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
0
database/schema/forkliftHours.ts
Normal file
0
database/schema/forkliftHours.ts
Normal file
0
database/schema/forkliftLeases.ts
Normal file
0
database/schema/forkliftLeases.ts
Normal file
0
database/schema/forklifts.ts
Normal file
0
database/schema/forklifts.ts
Normal file
@@ -12,7 +12,7 @@ export const invHistoricalData = pgTable(
|
||||
"invHistoricalData",
|
||||
{
|
||||
inv_id: uuid("inv_id").defaultRandom().primaryKey(),
|
||||
histDate: date("histDate").notNull(), // what month are we running in should just be the first of current month
|
||||
histDate: date("histDate").notNull(), // this date should always be yesterday when we post it.
|
||||
plantToken: text("plantToken"),
|
||||
article: text("article").notNull(),
|
||||
articleDescription: text("articleDescription").notNull(),
|
||||
@@ -21,10 +21,11 @@ export const invHistoricalData = pgTable(
|
||||
avaliable_QTY: text("avaliable_QTY"),
|
||||
coa_QTY: text("coa_QTY"),
|
||||
held_QTY: text("held_QTY"),
|
||||
lot_Number: text("lot_number"),
|
||||
consignment: text("consignment"),
|
||||
location: text("location"),
|
||||
upd_user: text("upd_user"),
|
||||
upd_date: timestamp("upd_date"),
|
||||
upd_user: text("upd_user").default("lst"),
|
||||
upd_date: timestamp("upd_date").defaultNow(),
|
||||
}
|
||||
// (table) => [
|
||||
// // uniqueIndex('emailUniqueIndex').on(sql`lower(${table.email})`),
|
||||
|
||||
32
database/schema/ratios.ts
Normal file
32
database/schema/ratios.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import {
|
||||
integer,
|
||||
jsonb,
|
||||
pgTable,
|
||||
text,
|
||||
timestamp,
|
||||
uniqueIndex,
|
||||
uuid,
|
||||
} from "drizzle-orm/pg-core";
|
||||
import { createSelectSchema } from "drizzle-zod";
|
||||
|
||||
export const labelRatio = pgTable(
|
||||
"labelRatio",
|
||||
{
|
||||
ratio_id: uuid(" ratio_id").defaultRandom().primaryKey(),
|
||||
name: text("name").default("labels"),
|
||||
autoLabel: integer("autoLabel").default(0),
|
||||
manualLabel: integer("manualLabel").default(0),
|
||||
lastReset: timestamp().defaultNow(),
|
||||
},
|
||||
(table) => [
|
||||
// uniqueIndex('emailUniqueIndex').on(sql`lower(${table.email})`),
|
||||
uniqueIndex("labelname").on(table.name),
|
||||
]
|
||||
);
|
||||
|
||||
// Schema for inserting a user - can be used to validate API requests
|
||||
// export const insertRolesSchema = createInsertSchema(roles, {
|
||||
// name: z.string().min(3, {message: "Role name must be more than 3 letters"}),
|
||||
// });
|
||||
// Schema for selecting a Expenses - can be used to validate API responses
|
||||
export const selectRolesSchema = createSelectSchema(labelRatio);
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
uuid,
|
||||
uniqueIndex,
|
||||
jsonb,
|
||||
integer,
|
||||
} from "drizzle-orm/pg-core";
|
||||
import { createInsertSchema, createSelectSchema } from "drizzle-zod";
|
||||
import { z } from "zod";
|
||||
@@ -23,6 +24,8 @@ export const rfidReaders = pgTable(
|
||||
lastTriggerGood: boolean("lastTiggerGood").default(true),
|
||||
active: boolean("active").default(true),
|
||||
lastTagScanned: text("lastTagScanned"),
|
||||
goodReads: integer("goodReads").default(0),
|
||||
badReads: integer("badReads").default(0),
|
||||
},
|
||||
(table) => [
|
||||
// uniqueIndex('emailUniqueIndex').on(sql`lower(${table.email})`),
|
||||
|
||||
@@ -16,7 +16,7 @@ export const rfidTags = pgTable(
|
||||
rfidTag_id: uuid("rfidTag_id").defaultRandom().primaryKey(),
|
||||
tagHex: text("tagHex"),
|
||||
tag: text("tag"),
|
||||
lastRead: timestamp("timeStamp").defaultNow(),
|
||||
lastRead: timestamp("lastRead").defaultNow(), // cahnge this and hope we dont loose the data.
|
||||
counts: jsonb("counts").default([]), // example [{area: Line3.2, count: 1}, {area: line3.1, count: 6}]
|
||||
lastareaIn: text("lastareaIn").notNull(),
|
||||
runningNumber: integer("runningNumber"),
|
||||
|
||||
@@ -27,7 +27,7 @@ export const userRoles = pgTable(
|
||||
{
|
||||
user_id: uuid("user_id")
|
||||
.notNull()
|
||||
.references(() => users.user_id),
|
||||
.references(() => users.user_id, { onDelete: "cascade" }),
|
||||
role_id: uuid("role_id")
|
||||
.notNull()
|
||||
.references(() => roles.role_id),
|
||||
|
||||
48
frontend/package-lock.json
generated
48
frontend/package-lock.json
generated
@@ -21,6 +21,7 @@
|
||||
"@radix-ui/react-select": "^2.2.5",
|
||||
"@radix-ui/react-separator": "^1.1.7",
|
||||
"@radix-ui/react-slot": "^1.2.3",
|
||||
"@radix-ui/react-switch": "^1.2.6",
|
||||
"@radix-ui/react-tabs": "^1.1.12",
|
||||
"@radix-ui/react-tooltip": "^1.2.7",
|
||||
"@react-pdf/renderer": "^4.3.0",
|
||||
@@ -2072,6 +2073,41 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-switch": {
|
||||
"version": "1.2.6",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-switch/-/react-switch-1.2.6.tgz",
|
||||
"integrity": "sha512-bByzr1+ep1zk4VubeEVViV592vu2lHE2BZY5OnzehZqOOgogN80+mNtCqPkhn2gklJqOpxWgPoYTSnhBCqpOXQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/primitive": "1.1.3",
|
||||
"@radix-ui/react-compose-refs": "1.1.2",
|
||||
"@radix-ui/react-context": "1.1.2",
|
||||
"@radix-ui/react-primitive": "2.1.3",
|
||||
"@radix-ui/react-use-controllable-state": "1.2.2",
|
||||
"@radix-ui/react-use-previous": "1.1.1",
|
||||
"@radix-ui/react-use-size": "1.1.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-switch/node_modules/@radix-ui/primitive": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.3.tgz",
|
||||
"integrity": "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@radix-ui/react-tabs": {
|
||||
"version": "1.1.12",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.12.tgz",
|
||||
@@ -5316,18 +5352,6 @@
|
||||
"node": ">= 4"
|
||||
}
|
||||
},
|
||||
"node_modules/immer": {
|
||||
"version": "10.1.1",
|
||||
"resolved": "https://registry.npmjs.org/immer/-/immer-10.1.1.tgz",
|
||||
"integrity": "sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/immer"
|
||||
}
|
||||
},
|
||||
"node_modules/import-fresh": {
|
||||
"version": "3.3.1",
|
||||
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
"@radix-ui/react-select": "^2.2.5",
|
||||
"@radix-ui/react-separator": "^1.1.7",
|
||||
"@radix-ui/react-slot": "^1.2.3",
|
||||
"@radix-ui/react-switch": "^1.2.6",
|
||||
"@radix-ui/react-tabs": "^1.1.12",
|
||||
"@radix-ui/react-tooltip": "^1.2.7",
|
||||
"@react-pdf/renderer": "^4.3.0",
|
||||
|
||||
@@ -15,6 +15,7 @@ import { ChangeSubModule } from "./SubModuleForm";
|
||||
|
||||
import { Skeleton } from "@/components/ui/skeleton";
|
||||
import { getSubModules } from "@/utils/querys/admin/subModules";
|
||||
import { useEffect } from "react";
|
||||
|
||||
export type Settings = {
|
||||
settings_id?: string;
|
||||
@@ -28,14 +29,34 @@ export default function SubModulePage() {
|
||||
const { modules } = useModuleStore();
|
||||
const router = useRouter();
|
||||
|
||||
const adminModule = modules.filter((n) => n.name === "admin");
|
||||
const userLevel =
|
||||
user?.roles.filter((r) => r.module_id === adminModule[0].module_id) ||
|
||||
[];
|
||||
// const adminModule = modules.filter((n) => n.name === "admin");
|
||||
// const userLevel =
|
||||
// user?.roles.filter((r) => r.module_id === adminModule[0].module_id) ||
|
||||
// [];
|
||||
|
||||
if (!adminModule[0].roles.includes(userLevel[0]?.role)) {
|
||||
router.navigate({ to: "/" });
|
||||
}
|
||||
// if (!adminModule[0]?.roles.includes(userLevel[0]?.role)) {
|
||||
// //router.navigate({ to: "/" });
|
||||
// }
|
||||
|
||||
useEffect(() => {
|
||||
if (!user || modules.length === 0) return;
|
||||
|
||||
const adminModule = modules.find((n) => n.name === "admin");
|
||||
if (!adminModule) {
|
||||
console.log("no module loaded");
|
||||
//router.navigate({ to: "/" });
|
||||
return;
|
||||
}
|
||||
|
||||
const userLevel =
|
||||
user?.roles?.filter((r) => r.module_id === adminModule.module_id) ||
|
||||
[];
|
||||
|
||||
if (!adminModule.roles?.includes(userLevel[0]?.role)) {
|
||||
console.log("Something failed");
|
||||
//router.navigate({ to: "/" });
|
||||
}
|
||||
}, [modules, user, router]);
|
||||
|
||||
const { data, isError, error, isLoading } = useQuery(
|
||||
getSubModules(token ?? "")
|
||||
|
||||
@@ -1,131 +1,9 @@
|
||||
import {useSessionStore} from "@/lib/store/sessionStore";
|
||||
import {LstCard} from "../extendedUI/LstCard";
|
||||
import {Tabs, TabsContent, TabsList, TabsTrigger} from "../ui/tabs";
|
||||
import {useModuleStore} from "@/lib/store/useModuleStore";
|
||||
import {Table, TableBody, TableCell, TableHead, TableHeader, TableRow} from "../ui/table";
|
||||
import {Skeleton} from "../ui/skeleton";
|
||||
|
||||
import {Link, useRouter} from "@tanstack/react-router";
|
||||
import {Popover, PopoverContent, PopoverTrigger} from "../ui/popover";
|
||||
import {Button} from "../ui/button";
|
||||
import {cn} from "@/lib/utils";
|
||||
import {CalendarIcon} from "lucide-react";
|
||||
import {format, startOfMonth} from "date-fns";
|
||||
import {Calendar} from "../ui/calendar";
|
||||
import {useState} from "react";
|
||||
import {toast} from "sonner";
|
||||
import KFP from "./KFP";
|
||||
import MaterialCheck from "./materialCheck/MaterialCheck";
|
||||
|
||||
export default function EomPage() {
|
||||
const {modules} = useModuleStore();
|
||||
const {user} = useSessionStore();
|
||||
const router = useRouter();
|
||||
const [date, setDate] = useState<Date>();
|
||||
|
||||
if (!user) {
|
||||
router.navigate({to: "/"});
|
||||
}
|
||||
const eomMod = modules.filter((m) => m.name === "eom");
|
||||
// the users current role for eom is?
|
||||
const role: any = user?.roles.filter((r) => r.module_id === eomMod[0].module_id) || "";
|
||||
|
||||
const tabs = [
|
||||
{key: "kfp", label: "Key Figures", roles: ["admin", "systemAdmin"], content: <KFP />},
|
||||
{key: "fg", label: "Finished Goods", roles: ["admin", "systemAdmin"], content: <DummyContent />},
|
||||
{key: "mm", label: "Main Material", roles: ["admin", "systemAdmin"], content: <DummyContent />},
|
||||
{key: "mb", label: "Master Batch", roles: ["admin", "systemAdmin"], content: <DummyContent />},
|
||||
{key: "ab", label: "Additive", roles: ["admin", "systemAdmin"], content: <DummyContent />},
|
||||
{key: "pp", label: "Purchased Preforms", roles: ["admin", "systemAdmin"], content: <DummyContent />},
|
||||
{key: "pre", label: "Preforms", roles: ["admin", "systemAdmin"], content: <DummyContent />},
|
||||
{key: "pkg", label: "Packaging", roles: ["admin", "systemAdmin"], content: <DummyContent />},
|
||||
{key: "ui", label: "Undefined Items", roles: ["admin"], content: <DummyContent />},
|
||||
];
|
||||
return (
|
||||
<div className="m-2 w-screen">
|
||||
<div className="mb-2 flex flex-row">
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
variant={"outline"}
|
||||
className={cn(
|
||||
"w-[280px] justify-start text-left font-normal",
|
||||
!date && "text-muted-foreground"
|
||||
)}
|
||||
>
|
||||
<CalendarIcon className="mr-2 h-4 w-4" />
|
||||
{date ? format(date, "PPP") : <span>Pick a date</span>}
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-auto p-0">
|
||||
<Calendar mode="single" selected={date} onSelect={setDate} initialFocus />
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
<div className="ml-2">
|
||||
<Button onClick={() => toast.success(`Getting data for ${startOfMonth(date!)}-${date}`)}>
|
||||
<span className="text-sm">Update Data</span>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Tabs defaultValue="mm">
|
||||
<TabsList>
|
||||
{tabs.map((tab) => {
|
||||
if (tab.roles.includes(role[0].role))
|
||||
return <TabsTrigger value={tab.key}>{tab.label}</TabsTrigger>;
|
||||
})}
|
||||
</TabsList>
|
||||
{tabs.map((tab) => {
|
||||
if (tab.roles.includes(role[0].role))
|
||||
return <TabsContent value={tab.key}>{tab.content}</TabsContent>;
|
||||
})}
|
||||
</Tabs>
|
||||
<MaterialCheck />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function DummyContent() {
|
||||
return (
|
||||
<LstCard className="w-5/6">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>Av</TableHead>
|
||||
<TableHead>Description</TableHead>
|
||||
<TableHead>Material Type</TableHead>
|
||||
<TableHead>Waste</TableHead>
|
||||
<TableHead>Loss / Gain $$</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
|
||||
<TableBody>
|
||||
{Array(10)
|
||||
.fill(0)
|
||||
.map((_, i) => (
|
||||
<TableRow key={i}>
|
||||
<TableCell className="font-medium m-2">
|
||||
<Link to="/article/$av" params={{av: `${i}`}}>
|
||||
{i}
|
||||
</Link>
|
||||
</TableCell>
|
||||
<TableCell className="font-medium">
|
||||
<Skeleton className="h-4" />
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Skeleton className="h-4" />
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Skeleton className="h-4" />
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Skeleton className="h-4" />
|
||||
</TableCell>
|
||||
{/* <TableCell>
|
||||
<Skeleton className="h-4" />
|
||||
</TableCell> */}
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</LstCard>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
export default function MaterialCheck() {
|
||||
return <div>MaterialCheck</div>;
|
||||
}
|
||||
211
frontend/src/components/eom/materialsData/MaterialData.tsx
Normal file
211
frontend/src/components/eom/materialsData/MaterialData.tsx
Normal file
@@ -0,0 +1,211 @@
|
||||
import { LstCard } from "@/components/extendedUI/LstCard";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Calendar } from "@/components/ui/calendar";
|
||||
import { Popover, PopoverTrigger } from "@/components/ui/popover";
|
||||
import { Skeleton } from "@/components/ui/skeleton";
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from "@/components/ui/table";
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||
import { useSessionStore } from "@/lib/store/sessionStore";
|
||||
import { useModuleStore } from "@/lib/store/useModuleStore";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { PopoverContent } from "@radix-ui/react-popover";
|
||||
import { Link, useRouter } from "@tanstack/react-router";
|
||||
import { startOfMonth } from "date-fns";
|
||||
import { format } from "date-fns-tz";
|
||||
import { CalendarIcon } from "lucide-react";
|
||||
import { useState } from "react";
|
||||
import { toast } from "sonner";
|
||||
import KFP from "../KFP";
|
||||
|
||||
export default function MaterialData() {
|
||||
const { modules } = useModuleStore();
|
||||
const { user } = useSessionStore();
|
||||
const router = useRouter();
|
||||
const [date, setDate] = useState<Date>();
|
||||
|
||||
if (!user) {
|
||||
router.navigate({ to: "/" });
|
||||
}
|
||||
const eomMod = modules.filter((m) => m.name === "eom");
|
||||
// the users current role for eom is?
|
||||
const role: any =
|
||||
user?.roles.filter((r) => r.module_id === eomMod[0].module_id) || "";
|
||||
|
||||
const tabs = [
|
||||
{
|
||||
key: "kfp",
|
||||
label: "Key Figures",
|
||||
roles: ["admin", "systemAdmin"],
|
||||
content: <KFP />,
|
||||
},
|
||||
{
|
||||
key: "fg",
|
||||
label: "Finished Goods",
|
||||
roles: ["admin", "systemAdmin"],
|
||||
content: <DummyContent />,
|
||||
},
|
||||
{
|
||||
key: "mm",
|
||||
label: "Main Material",
|
||||
roles: ["admin", "systemAdmin"],
|
||||
content: <DummyContent />,
|
||||
},
|
||||
{
|
||||
key: "mb",
|
||||
label: "Master Batch",
|
||||
roles: ["admin", "systemAdmin"],
|
||||
content: <DummyContent />,
|
||||
},
|
||||
{
|
||||
key: "ab",
|
||||
label: "Additive",
|
||||
roles: ["admin", "systemAdmin"],
|
||||
content: <DummyContent />,
|
||||
},
|
||||
{
|
||||
key: "pp",
|
||||
label: "Purchased Preforms",
|
||||
roles: ["admin", "systemAdmin"],
|
||||
content: <DummyContent />,
|
||||
},
|
||||
{
|
||||
key: "pre",
|
||||
label: "Preforms",
|
||||
roles: ["admin", "systemAdmin"],
|
||||
content: <DummyContent />,
|
||||
},
|
||||
{
|
||||
key: "pkg",
|
||||
label: "Packaging",
|
||||
roles: ["admin", "systemAdmin"],
|
||||
content: <DummyContent />,
|
||||
},
|
||||
{
|
||||
key: "ui",
|
||||
label: "Undefined Items",
|
||||
roles: ["admin"],
|
||||
content: <DummyContent />,
|
||||
},
|
||||
];
|
||||
return (
|
||||
<div className="m-2 w-screen">
|
||||
<div className="mb-2 flex flex-row">
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
variant={"outline"}
|
||||
className={cn(
|
||||
"w-[280px] justify-start text-left font-normal",
|
||||
!date && "text-muted-foreground"
|
||||
)}
|
||||
>
|
||||
<CalendarIcon className="mr-2 h-4 w-4" />
|
||||
{date ? (
|
||||
format(date, "PPP")
|
||||
) : (
|
||||
<span>Pick a date</span>
|
||||
)}
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-auto p-0">
|
||||
<Calendar
|
||||
mode="single"
|
||||
selected={date}
|
||||
onSelect={setDate}
|
||||
initialFocus
|
||||
/>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
<div className="ml-2">
|
||||
<Button
|
||||
onClick={() =>
|
||||
toast.success(
|
||||
`Getting data for ${startOfMonth(date!)}-${date}`
|
||||
)
|
||||
}
|
||||
>
|
||||
<span className="text-sm">Update Data</span>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Tabs defaultValue="mm">
|
||||
<TabsList>
|
||||
{tabs.map((tab) => {
|
||||
if (tab.roles.includes(role[0].role))
|
||||
return (
|
||||
<TabsTrigger value={tab.key}>
|
||||
{tab.label}
|
||||
</TabsTrigger>
|
||||
);
|
||||
})}
|
||||
</TabsList>
|
||||
{tabs.map((tab) => {
|
||||
if (tab.roles.includes(role[0].role))
|
||||
return (
|
||||
<TabsContent value={tab.key}>
|
||||
{tab.content}
|
||||
</TabsContent>
|
||||
);
|
||||
})}
|
||||
</Tabs>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function DummyContent() {
|
||||
return (
|
||||
<LstCard className="w-5/6">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>Av</TableHead>
|
||||
<TableHead>Description</TableHead>
|
||||
<TableHead>Material Type</TableHead>
|
||||
<TableHead>Waste</TableHead>
|
||||
<TableHead>Loss / Gain $$</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
|
||||
<TableBody>
|
||||
{Array(10)
|
||||
.fill(0)
|
||||
.map((_, i) => (
|
||||
<TableRow key={i}>
|
||||
<TableCell className="font-medium m-2">
|
||||
<Link
|
||||
to="/article/$av"
|
||||
params={{ av: `${i}` }}
|
||||
>
|
||||
{i}
|
||||
</Link>
|
||||
</TableCell>
|
||||
<TableCell className="font-medium">
|
||||
<Skeleton className="h-4" />
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Skeleton className="h-4" />
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Skeleton className="h-4" />
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Skeleton className="h-4" />
|
||||
</TableCell>
|
||||
{/* <TableCell>
|
||||
<Skeleton className="h-4" />
|
||||
</TableCell> */}
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</LstCard>
|
||||
);
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import {Printer} from "lucide-react";
|
||||
import { Printer, Tag } from "lucide-react";
|
||||
import {
|
||||
SidebarGroup,
|
||||
SidebarGroupContent,
|
||||
@@ -7,21 +7,36 @@ import {
|
||||
SidebarMenuButton,
|
||||
SidebarMenuItem,
|
||||
} from "../../ui/sidebar";
|
||||
import {hasPageAccess} from "@/utils/userAccess";
|
||||
import {User} from "@/types/users";
|
||||
import { hasPageAccess } from "@/utils/userAccess";
|
||||
import { User } from "@/types/users";
|
||||
|
||||
const items = [
|
||||
{
|
||||
title: "One Click Print",
|
||||
url: "/ocp",
|
||||
icon: Printer,
|
||||
role: ["viewer"],
|
||||
module: "ocp",
|
||||
active: true,
|
||||
},
|
||||
];
|
||||
|
||||
export function ProductionSideBar({user, moduleID}: {user: User | null; moduleID: string}) {
|
||||
export function ProductionSideBar({
|
||||
user,
|
||||
moduleID,
|
||||
}: {
|
||||
user: User | null;
|
||||
moduleID: string;
|
||||
}) {
|
||||
const url: string = window.location.host.split(":")[0];
|
||||
const items = [
|
||||
{
|
||||
title: "One Click Print",
|
||||
url: "/ocp",
|
||||
icon: Printer,
|
||||
role: ["viewer"],
|
||||
module: "ocp",
|
||||
active: true,
|
||||
},
|
||||
{
|
||||
title: "Rfid Readers",
|
||||
url: "/rfid",
|
||||
icon: Tag,
|
||||
role: ["viewer"],
|
||||
module: "production",
|
||||
active:
|
||||
url === "usday1vms006" || url === "localhost" ? true : false,
|
||||
},
|
||||
];
|
||||
return (
|
||||
<SidebarGroup>
|
||||
<SidebarGroupLabel>Production</SidebarGroupLabel>
|
||||
@@ -30,14 +45,15 @@ export function ProductionSideBar({user, moduleID}: {user: User | null; moduleID
|
||||
{items.map((item) => (
|
||||
<SidebarMenuItem key={item.title}>
|
||||
<>
|
||||
{hasPageAccess(user, item.role, moduleID) && item.active && (
|
||||
<SidebarMenuButton asChild>
|
||||
<a href={item.url}>
|
||||
<item.icon />
|
||||
<span>{item.title}</span>
|
||||
</a>
|
||||
</SidebarMenuButton>
|
||||
)}
|
||||
{hasPageAccess(user, item.role, moduleID) &&
|
||||
item.active && (
|
||||
<SidebarMenuButton asChild>
|
||||
<a href={item.url}>
|
||||
<item.icon />
|
||||
<span>{item.title}</span>
|
||||
</a>
|
||||
</SidebarMenuButton>
|
||||
)}
|
||||
</>
|
||||
</SidebarMenuItem>
|
||||
))}
|
||||
|
||||
@@ -6,7 +6,10 @@ import Barcode from "react-barcode";
|
||||
import { BarcodePDFExport } from "./BarcodeExport";
|
||||
import { BulkBarcodePDFExport } from "./BulkExport";
|
||||
|
||||
const commoncmd = [{ name: "Relocate", commandId: 33 }];
|
||||
const commoncmd = [
|
||||
{ name: "Relocate", commandId: 33 },
|
||||
//-{ name: "Stock in", commandId: 22 },
|
||||
];
|
||||
export default function CommonCommands() {
|
||||
const [checked, setChecked] = useState([]);
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ export default function DmPage() {
|
||||
<ul className="list-disc mr-2">
|
||||
<li>
|
||||
Download the standard template if you have not yet done
|
||||
so, top right click standard, then template.
|
||||
so, Above click Standard Order Template.
|
||||
</li>
|
||||
<li>
|
||||
Add in the orders like you see in the example below.
|
||||
@@ -37,8 +37,8 @@ export default function DmPage() {
|
||||
customerReleaseNumber. Quatity and dates can change.
|
||||
</li>
|
||||
<li>
|
||||
Once you have all the orders enters click the upload
|
||||
button on the top right
|
||||
Once you have all the orders entered, click Standard
|
||||
Order Import
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
@@ -1,26 +1,30 @@
|
||||
import {LstCard} from "@/components/extendedUI/LstCard";
|
||||
import {Button} from "@/components/ui/button";
|
||||
import {CardHeader} from "@/components/ui/card";
|
||||
import {Input} from "@/components/ui/input";
|
||||
import {Label} from "@/components/ui/label";
|
||||
import {useSessionStore} from "@/lib/store/sessionStore";
|
||||
import axios from "axios";
|
||||
import {useState} from "react";
|
||||
import { LstCard } from "@/components/extendedUI/LstCard";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { CardHeader } from "@/components/ui/card";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
|
||||
import {useForm} from "react-hook-form";
|
||||
import {toast} from "sonner";
|
||||
import axios from "axios";
|
||||
import { useState } from "react";
|
||||
|
||||
import { useForm } from "react-hook-form";
|
||||
import { toast } from "sonner";
|
||||
|
||||
export default function ConsumeMaterial() {
|
||||
const {register: register1, handleSubmit: handleSubmit1, reset} = useForm();
|
||||
const {
|
||||
register: register1,
|
||||
handleSubmit: handleSubmit1,
|
||||
reset,
|
||||
} = useForm();
|
||||
const [submitting, setSubmitting] = useState(false);
|
||||
const {token} = useSessionStore();
|
||||
|
||||
const handleConsume = async (data: any) => {
|
||||
setSubmitting(true);
|
||||
try {
|
||||
const result = await axios.post(`/api/logistics/consume`, data, {
|
||||
headers: {Authorization: `Bearer ${token}`},
|
||||
});
|
||||
// const result = await axios.post(`/api/logistics/consume`, data, {
|
||||
// headers: {Authorization: `Bearer ${token}`},
|
||||
// });
|
||||
const result = await axios.post(`/api/logistics/consume`, data);
|
||||
if (result.data.success) {
|
||||
toast.success(result.data.message);
|
||||
setSubmitting(false);
|
||||
@@ -33,12 +37,14 @@ export default function ConsumeMaterial() {
|
||||
toast.error(result.data.message);
|
||||
}
|
||||
} catch (error: any) {
|
||||
//console.log(error);
|
||||
console.log(error);
|
||||
setSubmitting(false);
|
||||
if (error.status === 401) {
|
||||
toast.error("Unauthorized to do this task.");
|
||||
} else {
|
||||
toast.error("Unexpected error if this continues please constact an admin.");
|
||||
toast.error(
|
||||
"Unexpected error if this continues please constact an admin."
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -53,7 +59,9 @@ export default function ConsumeMaterial() {
|
||||
<LstCard>
|
||||
<form onSubmit={handleSubmit1(handleConsume)}>
|
||||
<div className="m-2">
|
||||
<Label htmlFor="runningNr">Enter unit running number</Label>
|
||||
<Label htmlFor="runningNr">
|
||||
Enter unit running number
|
||||
</Label>
|
||||
<Input
|
||||
className="mt-2"
|
||||
//defaultValue="634"
|
||||
@@ -62,7 +70,9 @@ export default function ConsumeMaterial() {
|
||||
/>
|
||||
</div>
|
||||
<div className="m-2">
|
||||
<Label htmlFor="lotNum">Enter lot number</Label>
|
||||
<Label htmlFor="lotNum">
|
||||
Enter lot number
|
||||
</Label>
|
||||
<Input
|
||||
className="mt-2"
|
||||
//defaultValue="634"
|
||||
@@ -71,7 +81,12 @@ export default function ConsumeMaterial() {
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Button className="m-2" color="primary" type="submit" disabled={submitting}>
|
||||
<Button
|
||||
className="m-2"
|
||||
color="primary"
|
||||
type="submit"
|
||||
disabled={submitting}
|
||||
>
|
||||
Consume materal
|
||||
</Button>
|
||||
</form>
|
||||
@@ -81,12 +96,19 @@ export default function ConsumeMaterial() {
|
||||
<LstCard>
|
||||
<div className="w-96 p-1">
|
||||
<ol>
|
||||
<li>1. Enter the running number of the material you would like to consume</li>
|
||||
<li>2. Enter the lot number you will be consuming to</li>
|
||||
<li>
|
||||
1. Enter the running number of the
|
||||
material you would like to consume
|
||||
</li>
|
||||
<li>
|
||||
2. Enter the lot number you will be
|
||||
consuming to
|
||||
</li>
|
||||
<li>3. Press consume material</li>
|
||||
</ol>
|
||||
<p className="text-pretty w-96">
|
||||
*This process is only for barcoded material, if it is set to auto consume you will
|
||||
*This process is only for barcoded material,
|
||||
if it is set to auto consume you will
|
||||
encounter and error.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,478 @@
|
||||
import { LstCard } from "@/components/extendedUI/LstCard";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { CardContent, CardHeader } from "@/components/ui/card";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Switch } from "@/components/ui/switch";
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||
import { useAppForm } from "@/utils/formStuff";
|
||||
import axios from "axios";
|
||||
import { useState } from "react";
|
||||
import { toast } from "sonner";
|
||||
import { Info } from "lucide-react";
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
TooltipTrigger,
|
||||
} from "@/components/ui/tooltip";
|
||||
import { useSettingStore } from "@/lib/store/useSettings";
|
||||
|
||||
export default function TransferToNextLot() {
|
||||
const [gaylordFilled, setGaylordFilled] = useState([0]);
|
||||
const [actualAmount, setActualAmount] = useState(0);
|
||||
const [tab, setTab] = useState("esitmate");
|
||||
const [typeSwitch, setTypeSwitch] = useState(false);
|
||||
const { settings } = useSettingStore();
|
||||
|
||||
const server = settings.filter((n: any) => n.name === "dbServer");
|
||||
const form = useAppForm({
|
||||
defaultValues: {
|
||||
runningNumber: "",
|
||||
lotNumber: "",
|
||||
originalAmount: "",
|
||||
amount: "",
|
||||
},
|
||||
onSubmit: async ({ value }) => {
|
||||
//console.log(transferData);
|
||||
//toast.success("603468: qty: 361, was transfered to lot:24897");
|
||||
try {
|
||||
const res = await axios.post("/api/ocp/materiallottransfer", {
|
||||
runningNumber: Number(value.runningNumber),
|
||||
lotNumber: Number(value.lotNumber),
|
||||
originalAmount: Number(value.originalAmount),
|
||||
level: Number(
|
||||
gaylordFilled.length === 1
|
||||
? 0.25
|
||||
: gaylordFilled.length === 2
|
||||
? 0.5
|
||||
: gaylordFilled.length === 3
|
||||
? 0.75
|
||||
: gaylordFilled.length === 4 && 0.95
|
||||
),
|
||||
amount: actualAmount,
|
||||
type: typeSwitch ? "eom" : "lot",
|
||||
});
|
||||
|
||||
if (res.data.success) {
|
||||
toast.success(`${res.data.message}`);
|
||||
form.reset();
|
||||
setGaylordFilled([0]);
|
||||
setActualAmount(0);
|
||||
}
|
||||
//console.log(res.data);
|
||||
|
||||
if (!res.data.success) {
|
||||
toast.error(res.data.message);
|
||||
}
|
||||
} catch (error) {
|
||||
if (error) {
|
||||
console.log(error);
|
||||
//toast.error(error)
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
return (
|
||||
<div>
|
||||
<LstCard>
|
||||
<CardHeader>
|
||||
<p className="text-center text-lg">
|
||||
Material Transfer to Next lot
|
||||
</p>
|
||||
</CardHeader>
|
||||
<div>
|
||||
<div className="flex flex-wrap m-2 gap-2">
|
||||
<div className="flex gap-2">
|
||||
<div>
|
||||
<LstCard className="">
|
||||
<Tabs
|
||||
defaultValue={tab}
|
||||
onValueChange={setTab}
|
||||
>
|
||||
<TabsList>
|
||||
<TabsTrigger value="esitmate">
|
||||
Estimate Amount
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="actual">
|
||||
Actual Amount
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
<TabsContent value="esitmate">
|
||||
<div className="grid columns-1">
|
||||
<button
|
||||
className={`box-border h-16 w-96 border-3 ${
|
||||
gaylordFilled.includes(
|
||||
4
|
||||
)
|
||||
? " bg-green-500"
|
||||
: ""
|
||||
}`}
|
||||
onClick={() =>
|
||||
setGaylordFilled([
|
||||
1, 2, 3, 4,
|
||||
])
|
||||
}
|
||||
>
|
||||
<p className="text-center">
|
||||
Almost full - 95%
|
||||
</p>
|
||||
</button>
|
||||
<button
|
||||
className={`box-border h-16 w-96 border-3 ${
|
||||
gaylordFilled.includes(
|
||||
3
|
||||
)
|
||||
? " bg-green-500"
|
||||
: ""
|
||||
}`}
|
||||
onClick={() =>
|
||||
setGaylordFilled([
|
||||
1, 2, 3,
|
||||
])
|
||||
}
|
||||
>
|
||||
<p className="text-center">
|
||||
About full - 75%
|
||||
</p>
|
||||
</button>
|
||||
<button
|
||||
className={`box-border h-16 w-96 border-3 ${
|
||||
gaylordFilled.includes(
|
||||
2
|
||||
)
|
||||
? " bg-green-500"
|
||||
: ""
|
||||
}`}
|
||||
onClick={() =>
|
||||
setGaylordFilled([1, 2])
|
||||
}
|
||||
>
|
||||
<p className="text-center">
|
||||
Half full - 50%
|
||||
</p>
|
||||
</button>
|
||||
<button
|
||||
className={`box-border h-16 w-96 border-3 ${
|
||||
gaylordFilled.includes(
|
||||
1
|
||||
)
|
||||
? " bg-green-500"
|
||||
: ""
|
||||
}`}
|
||||
onClick={() =>
|
||||
setGaylordFilled(() => [
|
||||
1,
|
||||
])
|
||||
}
|
||||
>
|
||||
<p className="text-center">
|
||||
Almost empty - 25%
|
||||
</p>
|
||||
</button>
|
||||
</div>
|
||||
<div className="flex justify-end pr-1">
|
||||
<Button
|
||||
onClick={() =>
|
||||
setGaylordFilled([0])
|
||||
}
|
||||
>
|
||||
Reset Gaylord
|
||||
</Button>
|
||||
</div>
|
||||
</TabsContent>
|
||||
<TabsContent
|
||||
value="actual"
|
||||
className="w-96"
|
||||
>
|
||||
<CardHeader>
|
||||
<p>
|
||||
Enter the total amount of
|
||||
the cage/gaylord
|
||||
</p>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<Input
|
||||
type="number"
|
||||
//placeholder="35"
|
||||
onChange={(e) =>
|
||||
setActualAmount(
|
||||
Number(
|
||||
e.target.value
|
||||
)
|
||||
)
|
||||
}
|
||||
/>
|
||||
</CardContent>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</LstCard>
|
||||
</div>
|
||||
<div>
|
||||
<div className="w-96">
|
||||
<LstCard>
|
||||
<form
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
form.handleSubmit();
|
||||
}}
|
||||
>
|
||||
<div className="mt-3 p-2">
|
||||
<form.AppField
|
||||
name="runningNumber"
|
||||
children={(field) => (
|
||||
<field.InputField
|
||||
label="Running Number"
|
||||
inputType="number"
|
||||
required={true}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<div className="mt-3 p-2">
|
||||
<form.AppField
|
||||
name="lotNumber"
|
||||
children={(field) => (
|
||||
<field.InputField
|
||||
label="Lot Number"
|
||||
inputType="number"
|
||||
required={true}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
{tab !== "actual" && (
|
||||
<div className="mt-3 p-2">
|
||||
<form.AppField
|
||||
name="originalAmount"
|
||||
children={(
|
||||
field
|
||||
) => (
|
||||
<field.InputField
|
||||
label="Orignal Quantity"
|
||||
inputType="number"
|
||||
required={
|
||||
true
|
||||
}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex justify-between p-2">
|
||||
<div className="flex items-center space-x-2">
|
||||
<Switch
|
||||
checked={typeSwitch}
|
||||
onCheckedChange={
|
||||
setTypeSwitch
|
||||
}
|
||||
/>
|
||||
<span>
|
||||
{typeSwitch ? (
|
||||
<div className="flex items-center space-x-2">
|
||||
<span>
|
||||
"EOM
|
||||
Transfer"
|
||||
</span>
|
||||
<Tooltip>
|
||||
<TooltipTrigger>
|
||||
<Info className="h-[16px] w-[16px]" />
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>
|
||||
Click
|
||||
the
|
||||
toggle
|
||||
if
|
||||
you
|
||||
will
|
||||
be
|
||||
transfering
|
||||
at
|
||||
EOM,
|
||||
NOTE:
|
||||
This
|
||||
will
|
||||
trigger
|
||||
the
|
||||
delayed
|
||||
transfer.
|
||||
</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex items-center space-x-2">
|
||||
<span>
|
||||
"Lot
|
||||
Transfer"
|
||||
</span>
|
||||
<Tooltip>
|
||||
<TooltipTrigger>
|
||||
<Info className="h-[16px] w-[16px]" />
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>
|
||||
Click
|
||||
the
|
||||
toggle
|
||||
if
|
||||
you
|
||||
will
|
||||
be
|
||||
transfering
|
||||
at
|
||||
EOM,
|
||||
NOTE:
|
||||
This
|
||||
will
|
||||
trigger
|
||||
the
|
||||
delayed
|
||||
transfer.
|
||||
</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</div>
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<form.AppForm>
|
||||
<form.SubmitButton>
|
||||
Transfer To Lot
|
||||
</form.SubmitButton>
|
||||
</form.AppForm>
|
||||
</div>
|
||||
</form>
|
||||
</LstCard>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<LstCard className="p-2">
|
||||
<CardHeader>
|
||||
<p className="text-center text-lg">
|
||||
Moving material to the next lot.
|
||||
</p>
|
||||
</CardHeader>
|
||||
{tab !== "actual" ? (
|
||||
<div>
|
||||
<ol>
|
||||
<li>
|
||||
1. Grab the gaylord running
|
||||
number from the gaylord at the
|
||||
line/next to the tschritter
|
||||
</li>
|
||||
<li>
|
||||
2. Grab the next lot number you
|
||||
are going to be running (or the
|
||||
one that state no Main material
|
||||
prepared)
|
||||
</li>
|
||||
<li>
|
||||
3. Enter the total gaylord
|
||||
weight (this is how much the
|
||||
gaylord weighed when it came in
|
||||
from the supplier.)
|
||||
</li>
|
||||
<li>
|
||||
4. *Click the level of the
|
||||
gaylord (this is just an
|
||||
estimate to move to the next
|
||||
lot.)
|
||||
</li>
|
||||
<li>
|
||||
5. type in running number on the
|
||||
gaylord.
|
||||
</li>
|
||||
<li>
|
||||
6. Type in the new lot number.
|
||||
</li>
|
||||
<li>7. Press "Transfer To Lot"</li>
|
||||
</ol>
|
||||
<br></br>
|
||||
<p>
|
||||
* to reduce the time needed to get
|
||||
the lot going we will use an
|
||||
estimate of how full the gaylord is.
|
||||
</p>
|
||||
<p>
|
||||
NOTE: This is not the return
|
||||
process, this process will just get
|
||||
the gaylord to the next lot.
|
||||
</p>
|
||||
<br />
|
||||
{settings.length > 0 && (
|
||||
<p>
|
||||
For more in depth instructions
|
||||
please{" "}
|
||||
<a
|
||||
href={`https://${server[0].value}.alpla.net/lst/d/docs/ocp/ocp#tranfer-partial-estimated-quantity-to-the-next-lot`}
|
||||
target="_blank"
|
||||
>
|
||||
<em>CLICK HERE</em>
|
||||
</a>
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<div>
|
||||
<ol>
|
||||
<li>
|
||||
1. Grab the gaylord running
|
||||
number from the gaylord at the
|
||||
line/next to the tschritter
|
||||
</li>
|
||||
<li>
|
||||
2. Grab the next lot number you
|
||||
are going to be running (or the
|
||||
one that state no Main material
|
||||
prepared)
|
||||
</li>
|
||||
<li>
|
||||
3. Take the gaylord to the scale
|
||||
and weight it
|
||||
</li>
|
||||
<li>
|
||||
4. Enter the weight of the
|
||||
gaylord minus the tar weight.
|
||||
</li>
|
||||
<li>
|
||||
5. type in running number on the
|
||||
gaylord.
|
||||
</li>
|
||||
<li>
|
||||
6. Type in the new lot number.
|
||||
</li>
|
||||
<li>7. Press "Transfer To Lot"</li>
|
||||
</ol>
|
||||
<br></br>
|
||||
|
||||
<p>
|
||||
NOTE: This is not the return
|
||||
process, this process will just get
|
||||
the gaylord to the next lot.
|
||||
</p>
|
||||
<br />
|
||||
{settings.length > 0 && (
|
||||
<p>
|
||||
For more in depth instructions
|
||||
please{" "}
|
||||
<a
|
||||
href={`https://${server[0].value}.alpla.net/lst/d/docs/ocp/ocp#tranfer-partial-estimated-quantity-to-the-next-lot`}
|
||||
target="_blank"
|
||||
>
|
||||
<em>CLICK HERE</em>
|
||||
</a>
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</LstCard>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</LstCard>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
31
frontend/src/components/logistics/rfid/ManualTrigger.tsx
Normal file
31
frontend/src/components/logistics/rfid/ManualTrigger.tsx
Normal file
@@ -0,0 +1,31 @@
|
||||
import { Button } from "@/components/ui/button";
|
||||
import axios from "axios";
|
||||
|
||||
import { toast } from "sonner";
|
||||
|
||||
export default function ManualTrigger() {
|
||||
const rfidReaderTrigger = async () => {
|
||||
try {
|
||||
const res = await axios.post("/api/rfid/manualtrigger/wrapper1");
|
||||
|
||||
if (res.data.success) {
|
||||
toast.success(res.data.message);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!res.data.success) {
|
||||
toast.error(res.data.message);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
|
||||
//stoast.success(error.data.message);
|
||||
}
|
||||
};
|
||||
return (
|
||||
<div>
|
||||
<Button onClick={rfidReaderTrigger}>Wrapper 1 RFID</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -40,7 +40,6 @@ export function AttachSilo(props: any) {
|
||||
machineId: "",
|
||||
},
|
||||
onSubmit: async ({ value }) => {
|
||||
console.log(value);
|
||||
try {
|
||||
const res = await axios.post(
|
||||
"/api/logistics/attachsilo",
|
||||
|
||||
@@ -48,6 +48,7 @@ export function DetachSilo(props: any) {
|
||||
);
|
||||
|
||||
if (res.status === 200) {
|
||||
console.log(res.data.data);
|
||||
toast.success(res.data.message);
|
||||
|
||||
refetch();
|
||||
@@ -132,7 +133,12 @@ export function DetachSilo(props: any) {
|
||||
</div>
|
||||
<DialogFooter>
|
||||
<DialogClose asChild>
|
||||
<Button variant="outline">Cancel</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => setOpen(!open)}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
</DialogClose>
|
||||
<form.AppForm>
|
||||
<form.SubmitButton>Detach</form.SubmitButton>
|
||||
|
||||
@@ -21,13 +21,28 @@ import { toast } from "sonner";
|
||||
import ChartData from "./ChartData";
|
||||
import { AttachSilo } from "./AttachSilo";
|
||||
import { DetachSilo } from "./DetachSilo";
|
||||
import { useSessionStore } from "@/lib/store/sessionStore";
|
||||
import { useModuleStore } from "@/lib/store/useModuleStore";
|
||||
import { useGetUserRoles } from "@/lib/store/useGetRoles";
|
||||
|
||||
export default function SiloCard(data: any) {
|
||||
const token = localStorage.getItem("auth_token");
|
||||
const [submitting, setSubmitting] = useState(false);
|
||||
const { refetch } = useQuery(getStockSilo());
|
||||
const { user } = useSessionStore();
|
||||
const { userRoles } = useGetUserRoles();
|
||||
const { modules } = useModuleStore();
|
||||
const silo = data.silo;
|
||||
|
||||
// roles that can do the silo adjustments
|
||||
const roles = ["systemAdmin", "technician", "admin", "manager"];
|
||||
|
||||
const module = modules.filter((n) => n.name === "logistics");
|
||||
|
||||
const accessRoles = userRoles.filter(
|
||||
(n) => n.module_id === module[0]?.module_id
|
||||
) as any;
|
||||
|
||||
const form = useForm({
|
||||
defaultValues: {
|
||||
newLevel: "",
|
||||
@@ -46,7 +61,7 @@ export default function SiloCard(data: any) {
|
||||
dataToSubmit,
|
||||
{ headers: { Authorization: `Bearer ${token}` } }
|
||||
);
|
||||
console.log(res.data);
|
||||
//console.log(res.data);
|
||||
|
||||
if (res.data.success) {
|
||||
toast.success(res.data.message);
|
||||
@@ -70,6 +85,8 @@ export default function SiloCard(data: any) {
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
console.log(accessRoles);
|
||||
return (
|
||||
<LstCard>
|
||||
<div className="flex flex-row">
|
||||
@@ -109,82 +126,98 @@ export default function SiloCard(data: any) {
|
||||
</ul>
|
||||
</div>
|
||||
) : (
|
||||
<form
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}}
|
||||
>
|
||||
<form.Field
|
||||
name="newLevel"
|
||||
validators={{
|
||||
// We can choose between form-wide and field-specific validators
|
||||
onChange: ({ value }) =>
|
||||
value.length > 1
|
||||
? undefined
|
||||
: "You must enter a value greate than 1",
|
||||
}}
|
||||
children={(field) => {
|
||||
return (
|
||||
<div className="m-2 min-w-48 max-w-96 p-2">
|
||||
<div className="flex flex-row">
|
||||
<Label htmlFor="newLevel">
|
||||
New level
|
||||
</Label>
|
||||
<div>
|
||||
<Disclaimer />
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-row">
|
||||
<Input
|
||||
name={field.name}
|
||||
value={
|
||||
field.state.value
|
||||
}
|
||||
onBlur={
|
||||
field.handleBlur
|
||||
}
|
||||
type="decimal"
|
||||
onChange={(e) =>
|
||||
field.handleChange(
|
||||
e.target.value
|
||||
)
|
||||
}
|
||||
/>
|
||||
<Button
|
||||
className="ml-1"
|
||||
variant="outline"
|
||||
type="submit"
|
||||
onClick={
|
||||
form.handleSubmit
|
||||
}
|
||||
disabled={submitting}
|
||||
>
|
||||
{submitting ? (
|
||||
<span className="w-24">
|
||||
Submitting...
|
||||
</span>
|
||||
) : (
|
||||
<span className="w-24">
|
||||
Submit
|
||||
</span>
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
<>
|
||||
{user &&
|
||||
roles.includes(accessRoles[0]?.role) && (
|
||||
<form
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}}
|
||||
>
|
||||
<form.Field
|
||||
name="newLevel"
|
||||
validators={{
|
||||
// We can choose between form-wide and field-specific validators
|
||||
onChange: ({ value }) =>
|
||||
value.length > 1
|
||||
? undefined
|
||||
: "You must enter a value greate than 1",
|
||||
}}
|
||||
children={(field) => {
|
||||
return (
|
||||
<div className="m-2 min-w-48 max-w-96 p-2">
|
||||
<div className="flex flex-row">
|
||||
<Label htmlFor="newLevel">
|
||||
New level
|
||||
</Label>
|
||||
<div>
|
||||
<Disclaimer />
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-row">
|
||||
<Input
|
||||
name={
|
||||
field.name
|
||||
}
|
||||
value={
|
||||
field
|
||||
.state
|
||||
.value
|
||||
}
|
||||
onBlur={
|
||||
field.handleBlur
|
||||
}
|
||||
type="decimal"
|
||||
onChange={(
|
||||
e
|
||||
) =>
|
||||
field.handleChange(
|
||||
e
|
||||
.target
|
||||
.value
|
||||
)
|
||||
}
|
||||
/>
|
||||
<Button
|
||||
className="ml-1"
|
||||
variant="outline"
|
||||
type="submit"
|
||||
onClick={
|
||||
form.handleSubmit
|
||||
}
|
||||
disabled={
|
||||
submitting
|
||||
}
|
||||
>
|
||||
{submitting ? (
|
||||
<span className="w-24">
|
||||
Submitting...
|
||||
</span>
|
||||
) : (
|
||||
<span className="w-24">
|
||||
Submit
|
||||
</span>
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{field.state.meta.errors
|
||||
.length ? (
|
||||
<em>
|
||||
{field.state.meta.errors.join(
|
||||
","
|
||||
)}
|
||||
</em>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</form>
|
||||
{field.state.meta
|
||||
.errors
|
||||
.length ? (
|
||||
<em>
|
||||
{field.state.meta.errors.join(
|
||||
","
|
||||
)}
|
||||
</em>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</form>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</LstCard>
|
||||
|
||||
@@ -16,8 +16,7 @@ import { getOcmeInfo } from "@/utils/querys/production/getOcmeInfo";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { format } from "date-fns";
|
||||
import { Trash } from "lucide-react";
|
||||
import ManuallyEnterRn from "./ManuallyEnterRn";
|
||||
import { Separator } from "../ui/separator";
|
||||
import ManualTrigger from "../logistics/rfid/ManualTrigger";
|
||||
|
||||
const currentPallets = [
|
||||
{ key: "line", label: "Line" },
|
||||
@@ -160,11 +159,12 @@ export default function WrapperManualTrigger() {
|
||||
|
||||
<div>
|
||||
<hr />
|
||||
<p className="text-center mb-3">Manual Trigger</p>
|
||||
{/* <p className="text-center mb-3">Manual Trigger</p>
|
||||
<ManuallyEnterRn />
|
||||
<Separator className="m-1" />
|
||||
<div className="flex flex-row justify-between">
|
||||
<Separator className="m-1" /> */}
|
||||
<div className="flex flex-row justify-between m-2">
|
||||
<Button onClick={cameraTrigger}>Camera</Button>
|
||||
<ManualTrigger />
|
||||
</div>
|
||||
</div>
|
||||
</LstCard>
|
||||
|
||||
27
frontend/src/components/production/ocp/LabelRatio.tsx
Normal file
27
frontend/src/components/production/ocp/LabelRatio.tsx
Normal file
@@ -0,0 +1,27 @@
|
||||
import { getlabelRatio } from "@/utils/querys/production/labelRatio";
|
||||
import { labelRatioColumns } from "@/utils/tableData/production/labelRatio/labelRatioColumns";
|
||||
import { LabelRatioTable } from "@/utils/tableData/production/labelRatio/labelRatioData";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
|
||||
export default function LabelRatio() {
|
||||
const { data, isError, isLoading } = useQuery(getlabelRatio());
|
||||
|
||||
const ratioData = data ? data : [];
|
||||
if (isError) {
|
||||
return <div>Error</div>;
|
||||
}
|
||||
|
||||
if (isLoading) {
|
||||
return <div>Loading</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="m-2">
|
||||
<LabelRatioTable
|
||||
columns={labelRatioColumns}
|
||||
data={ratioData}
|
||||
//style={style}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -243,7 +243,8 @@ export default function ManualPrintForm() {
|
||||
<Textarea
|
||||
//label="Comments"
|
||||
placeholder="add more info as needed."
|
||||
{...register("additionalComments")}
|
||||
{...(register("additionalComments"),
|
||||
{ required: true, minLength: 10 })}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
ResizablePanel,
|
||||
ResizablePanelGroup,
|
||||
} from "@/components/ui/resizable-panels";
|
||||
import LabelRatio from "./LabelRatio";
|
||||
|
||||
export default function OCPPage() {
|
||||
const { settings } = useSettingStore();
|
||||
@@ -64,18 +65,32 @@ export default function OCPPage() {
|
||||
</ResizablePanelGroup>
|
||||
</ResizablePanel>
|
||||
<ResizableHandle />
|
||||
<ResizablePanel defaultSize={25}>
|
||||
<ResizablePanel className="min-h-[200px] min-w-[200px] max-w-[450px]">
|
||||
<ResizablePanelGroup
|
||||
direction="vertical"
|
||||
autoSaveId="ocpPage"
|
||||
autoSaveId="ocpPage_vert"
|
||||
>
|
||||
{server[0].value === "usday1vms006" && (
|
||||
<ResizablePanel className="max-h-[300px]">
|
||||
<ResizablePanel
|
||||
style={{
|
||||
overflow: "auto",
|
||||
scrollbarWidth: "none",
|
||||
}}
|
||||
defaultSize={50}
|
||||
className="min-h-[200px]"
|
||||
>
|
||||
<WrapperManualTrigger />
|
||||
</ResizablePanel>
|
||||
)}
|
||||
{server[0]?.value === "localhost" && (
|
||||
<ResizablePanel className="max-h-[300px]">
|
||||
<ResizablePanel
|
||||
className="min-h-[200px]"
|
||||
style={{
|
||||
overflow: "auto",
|
||||
scrollbarWidth: "none",
|
||||
}}
|
||||
defaultSize={50}
|
||||
>
|
||||
<WrapperManualTrigger />
|
||||
</ResizablePanel>
|
||||
)}
|
||||
@@ -83,6 +98,7 @@ export default function OCPPage() {
|
||||
<ResizableHandle />
|
||||
<ResizablePanel>
|
||||
<PrinterStatus />
|
||||
<LabelRatio />
|
||||
</ResizablePanel>
|
||||
</ResizablePanelGroup>
|
||||
</ResizablePanel>
|
||||
|
||||
28
frontend/src/components/rfid/RfidPage.tsx
Normal file
28
frontend/src/components/rfid/RfidPage.tsx
Normal file
@@ -0,0 +1,28 @@
|
||||
import { getReaders } from "@/utils/querys/rfid/getReaders";
|
||||
import { readerColumns } from "@/utils/tableData/rfid/readers/readerColumns";
|
||||
import { ReaderTable } from "@/utils/tableData/rfid/readers/readerData";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
|
||||
export default function RfidPage() {
|
||||
const { data, isError, isLoading } = useQuery(getReaders());
|
||||
|
||||
if (isError) {
|
||||
return <div>Error</div>;
|
||||
}
|
||||
|
||||
if (isLoading) {
|
||||
return <div>Loading</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="m-2">
|
||||
<ReaderTable
|
||||
columns={readerColumns}
|
||||
data={data.data.sort((a: any, b: any) =>
|
||||
a.reader.localeCompare(b.reader)
|
||||
)}
|
||||
//style={style}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
29
frontend/src/components/ui/switch.tsx
Normal file
29
frontend/src/components/ui/switch.tsx
Normal file
@@ -0,0 +1,29 @@
|
||||
import * as React from "react"
|
||||
import * as SwitchPrimitive from "@radix-ui/react-switch"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
function Switch({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof SwitchPrimitive.Root>) {
|
||||
return (
|
||||
<SwitchPrimitive.Root
|
||||
data-slot="switch"
|
||||
className={cn(
|
||||
"peer data-[state=checked]:bg-primary data-[state=unchecked]:bg-input focus-visible:border-ring focus-visible:ring-ring/50 dark:data-[state=unchecked]:bg-input/80 inline-flex h-[1.15rem] w-8 shrink-0 items-center rounded-full border border-transparent shadow-xs transition-all outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<SwitchPrimitive.Thumb
|
||||
data-slot="switch-thumb"
|
||||
className={cn(
|
||||
"bg-background dark:data-[state=unchecked]:bg-foreground dark:data-[state=checked]:bg-primary-foreground pointer-events-none block size-4 rounded-full ring-0 transition-transform data-[state=checked]:translate-x-[calc(100%-2px)] data-[state=unchecked]:translate-x-0"
|
||||
)}
|
||||
/>
|
||||
</SwitchPrimitive.Root>
|
||||
)
|
||||
}
|
||||
|
||||
export { Switch }
|
||||
@@ -17,6 +17,7 @@ import { Route as EomRouteImport } from './routes/_eom'
|
||||
import { Route as AuthRouteImport } from './routes/_auth'
|
||||
import { Route as AdminRouteImport } from './routes/_admin'
|
||||
import { Route as IndexRouteImport } from './routes/index'
|
||||
import { Route as RfidIndexRouteImport } from './routes/rfid/index'
|
||||
import { Route as OcpIndexRouteImport } from './routes/ocp/index'
|
||||
import { Route as EomEomRouteImport } from './routes/_eom/eom'
|
||||
import { Route as AuthProfileRouteImport } from './routes/_auth/profile'
|
||||
@@ -78,6 +79,11 @@ const IndexRoute = IndexRouteImport.update({
|
||||
path: '/',
|
||||
getParentRoute: () => rootRouteImport,
|
||||
} as any)
|
||||
const RfidIndexRoute = RfidIndexRouteImport.update({
|
||||
id: '/rfid/',
|
||||
path: '/rfid/',
|
||||
getParentRoute: () => rootRouteImport,
|
||||
} as any)
|
||||
const OcpIndexRoute = OcpIndexRouteImport.update({
|
||||
id: '/ocp/',
|
||||
path: '/ocp/',
|
||||
@@ -220,6 +226,7 @@ export interface FileRoutesByFullPath {
|
||||
'/profile': typeof AuthProfileRoute
|
||||
'/eom': typeof EomEomRoute
|
||||
'/ocp': typeof OcpIndexRoute
|
||||
'/rfid': typeof RfidIndexRoute
|
||||
'/siloAdjustments/$hist': typeof logisticsSiloAdjustmentsHistRoute
|
||||
'/article/$av': typeof EomArticleAvRoute
|
||||
'/barcodegen': typeof logisticsBarcodegenIndexRoute
|
||||
@@ -250,6 +257,7 @@ export interface FileRoutesByTo {
|
||||
'/profile': typeof AuthProfileRoute
|
||||
'/eom': typeof EomEomRoute
|
||||
'/ocp': typeof OcpIndexRoute
|
||||
'/rfid': typeof RfidIndexRoute
|
||||
'/siloAdjustments/$hist': typeof logisticsSiloAdjustmentsHistRoute
|
||||
'/article/$av': typeof EomArticleAvRoute
|
||||
'/barcodegen': typeof logisticsBarcodegenIndexRoute
|
||||
@@ -284,6 +292,7 @@ export interface FileRoutesById {
|
||||
'/_auth/profile': typeof AuthProfileRoute
|
||||
'/_eom/eom': typeof EomEomRoute
|
||||
'/ocp/': typeof OcpIndexRoute
|
||||
'/rfid/': typeof RfidIndexRoute
|
||||
'/(logistics)/siloAdjustments/$hist': typeof logisticsSiloAdjustmentsHistRoute
|
||||
'/_eom/article/$av': typeof EomArticleAvRoute
|
||||
'/(logistics)/barcodegen/': typeof logisticsBarcodegenIndexRoute
|
||||
@@ -316,6 +325,7 @@ export interface FileRouteTypes {
|
||||
| '/profile'
|
||||
| '/eom'
|
||||
| '/ocp'
|
||||
| '/rfid'
|
||||
| '/siloAdjustments/$hist'
|
||||
| '/article/$av'
|
||||
| '/barcodegen'
|
||||
@@ -346,6 +356,7 @@ export interface FileRouteTypes {
|
||||
| '/profile'
|
||||
| '/eom'
|
||||
| '/ocp'
|
||||
| '/rfid'
|
||||
| '/siloAdjustments/$hist'
|
||||
| '/article/$av'
|
||||
| '/barcodegen'
|
||||
@@ -379,6 +390,7 @@ export interface FileRouteTypes {
|
||||
| '/_auth/profile'
|
||||
| '/_eom/eom'
|
||||
| '/ocp/'
|
||||
| '/rfid/'
|
||||
| '/(logistics)/siloAdjustments/$hist'
|
||||
| '/_eom/article/$av'
|
||||
| '/(logistics)/barcodegen/'
|
||||
@@ -404,6 +416,7 @@ export interface RootRouteChildren {
|
||||
RegisterRoute: typeof RegisterRoute
|
||||
userPasswordChangeRoute: typeof userPasswordChangeRoute
|
||||
OcpIndexRoute: typeof OcpIndexRoute
|
||||
RfidIndexRoute: typeof RfidIndexRoute
|
||||
logisticsSiloAdjustmentsHistRoute: typeof logisticsSiloAdjustmentsHistRoute
|
||||
logisticsBarcodegenIndexRoute: typeof logisticsBarcodegenIndexRoute
|
||||
logisticsDmIndexRoute: typeof logisticsDmIndexRoute
|
||||
@@ -475,6 +488,13 @@ declare module '@tanstack/react-router' {
|
||||
preLoaderRoute: typeof IndexRouteImport
|
||||
parentRoute: typeof rootRouteImport
|
||||
}
|
||||
'/rfid/': {
|
||||
id: '/rfid/'
|
||||
path: '/rfid'
|
||||
fullPath: '/rfid'
|
||||
preLoaderRoute: typeof RfidIndexRouteImport
|
||||
parentRoute: typeof rootRouteImport
|
||||
}
|
||||
'/ocp/': {
|
||||
id: '/ocp/'
|
||||
path: '/ocp'
|
||||
@@ -694,6 +714,7 @@ const rootRouteChildren: RootRouteChildren = {
|
||||
RegisterRoute: RegisterRoute,
|
||||
userPasswordChangeRoute: userPasswordChangeRoute,
|
||||
OcpIndexRoute: OcpIndexRoute,
|
||||
RfidIndexRoute: RfidIndexRoute,
|
||||
logisticsSiloAdjustmentsHistRoute: logisticsSiloAdjustmentsHistRoute,
|
||||
logisticsBarcodegenIndexRoute: logisticsBarcodegenIndexRoute,
|
||||
logisticsDmIndexRoute: logisticsDmIndexRoute,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import ConsumeMaterial from "@/components/logistics/materialHelper/consumption/ConsumeMaterial";
|
||||
import PreformReturn from "@/components/logistics/materialHelper/consumption/MaterialReturn";
|
||||
import TransferToNextLot from "@/components/logistics/materialHelper/consumption/TransferToNextLot";
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
|
||||
export const Route = createFileRoute(
|
||||
@@ -21,10 +22,21 @@ export const Route = createFileRoute(
|
||||
|
||||
function RouteComponent() {
|
||||
const url: string = window.location.host.split(":")[0];
|
||||
const auth = localStorage.getItem("auth_token");
|
||||
return (
|
||||
<div>
|
||||
<ConsumeMaterial />
|
||||
{url === "localhost" && <PreformReturn />}
|
||||
<div className="flex flex-wrap">
|
||||
{auth ? (
|
||||
<>
|
||||
<ConsumeMaterial />
|
||||
{url === "localhost" && <PreformReturn />}
|
||||
<TransferToNextLot />
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<ConsumeMaterial />
|
||||
<TransferToNextLot />
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ import { useLogout } from "@/hooks/useLogout";
|
||||
import ExportInventoryData from "@/components/logistics/warehouse/ExportInventoryData";
|
||||
import { AddCards } from "@/components/dashboard/AddCards";
|
||||
import DMButtons from "@/components/logistics/dm/DMButtons";
|
||||
import { useSettingStore } from "@/lib/store/useSettings";
|
||||
//import { AddCards } from "@/components/dashboard/AddCards";
|
||||
|
||||
// same as the layout
|
||||
@@ -38,6 +39,9 @@ export const Route = createRootRoute({
|
||||
const { user } = useSessionStore();
|
||||
const logout = useLogout();
|
||||
const location = useLocation();
|
||||
const { settings } = useSettingStore();
|
||||
|
||||
const server = settings.filter((n: any) => n.name === "dbServer");
|
||||
|
||||
return (
|
||||
<div className="overflow-hidden">
|
||||
@@ -61,6 +65,17 @@ export const Route = createRootRoute({
|
||||
<div className="m-1">
|
||||
<ModeToggle />
|
||||
</div>
|
||||
<div className="mr-1 ml-1">
|
||||
{settings.length > 0 && (
|
||||
<a
|
||||
href={`https://${server[0].value}.alpla.net/lst/d`}
|
||||
target="_blank"
|
||||
>
|
||||
LST - Docs
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{session ? (
|
||||
<div className="m-1">
|
||||
<DropdownMenu>
|
||||
|
||||
@@ -33,13 +33,12 @@ function RouteComponent() {
|
||||
<span className="font-bold">
|
||||
Authentication Notice:
|
||||
</span>
|
||||
To interact with the Alpla prod through this
|
||||
application, you must use your{" "}
|
||||
<span className="font-semibold">
|
||||
Windows login credentials
|
||||
<span>
|
||||
The username, email, and password are
|
||||
only for LST you <em>DO NOT</em>Need to
|
||||
use Windows username if you do not wish
|
||||
to.
|
||||
</span>
|
||||
. These credentials are used solely for
|
||||
authentication purposes.
|
||||
</li>
|
||||
{/* <li>
|
||||
<span className="font-bold">
|
||||
|
||||
14
frontend/src/routes/rfid/index.tsx
Normal file
14
frontend/src/routes/rfid/index.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
import RfidPage from "@/components/rfid/RfidPage";
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
|
||||
export const Route = createFileRoute("/rfid/")({
|
||||
component: RouteComponent,
|
||||
});
|
||||
|
||||
function RouteComponent() {
|
||||
return (
|
||||
<div>
|
||||
<RfidPage />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
20
frontend/src/utils/querys/production/labelRatio.tsx
Normal file
20
frontend/src/utils/querys/production/labelRatio.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
import { queryOptions } from "@tanstack/react-query";
|
||||
import axios from "axios";
|
||||
|
||||
export function getlabelRatio() {
|
||||
return queryOptions({
|
||||
queryKey: ["labelRatio"],
|
||||
queryFn: () => fetchSettings(),
|
||||
|
||||
//staleTime: 1000,
|
||||
refetchInterval: 2 * 2000,
|
||||
refetchOnWindowFocus: true,
|
||||
});
|
||||
}
|
||||
|
||||
const fetchSettings = async () => {
|
||||
const { data } = await axios.get(`/api/ocp/labelratio`);
|
||||
// if we are not localhost ignore the devDir setting.
|
||||
//const url: string = window.location.host.split(":")[0];
|
||||
return data.data ?? [];
|
||||
};
|
||||
20
frontend/src/utils/querys/rfid/getReaders.tsx
Normal file
20
frontend/src/utils/querys/rfid/getReaders.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
import { queryOptions } from "@tanstack/react-query";
|
||||
import axios from "axios";
|
||||
|
||||
export function getReaders() {
|
||||
return queryOptions({
|
||||
queryKey: ["getReaders"],
|
||||
queryFn: () => fetchReaders(),
|
||||
//enabled:
|
||||
staleTime: 1000,
|
||||
refetchInterval: 60 * 1000,
|
||||
refetchOnWindowFocus: true,
|
||||
});
|
||||
}
|
||||
|
||||
const fetchReaders = async () => {
|
||||
const { data } = await axios.get(`/api/rfid/getreaders`);
|
||||
// if we are not localhost ignore the devDir setting.
|
||||
//const url: string = window.location.host.split(":")[0];
|
||||
return data.data ?? [];
|
||||
};
|
||||
@@ -66,6 +66,7 @@ export function InvTable<TData, TValue>({
|
||||
});
|
||||
//console.log(table.getState().sorting);
|
||||
//console.log(parseInt(style.height.replace("px", "")) - 50);
|
||||
console.log(info);
|
||||
return (
|
||||
<LstCard
|
||||
className="p-3"
|
||||
@@ -79,8 +80,9 @@ export function InvTable<TData, TValue>({
|
||||
<div>
|
||||
<div className="flex flex-row justify-between">
|
||||
<p className="text-center text-pretty">
|
||||
{info.type} {data.length > 0 ? "lanes" : "lane"} older
|
||||
than: {info.age}, needing to be completed
|
||||
{info.rowType} {data.length > 0 ? "lanes" : "lane"}{" "}
|
||||
older than: {info.age}, {data.length} needing to be
|
||||
completed
|
||||
</p>
|
||||
</div>
|
||||
<ScrollArea className="h-72 rounded-md border m-2">
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
//import { fixTime } from "@/utils/fixTime";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { getReaders } from "@/utils/querys/rfid/getReaders";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { ColumnDef } from "@tanstack/react-table";
|
||||
import axios from "axios";
|
||||
import { useState } from "react";
|
||||
import { toast } from "sonner";
|
||||
|
||||
// This type is used to define the shape of our data.
|
||||
// You can use a Zod schema here if you want.
|
||||
export type Adjustmnets = {
|
||||
ratio_id: string;
|
||||
name: string;
|
||||
autoLabel: number;
|
||||
manualLabel: string;
|
||||
};
|
||||
|
||||
export const labelRatioColumns: ColumnDef<Adjustmnets>[] = [
|
||||
// {
|
||||
// accessorKey: "line",
|
||||
// header: () => <div className="text-left">Line</div>,
|
||||
// },
|
||||
{
|
||||
accessorKey: "autoLabel",
|
||||
header: "Auto Labels",
|
||||
},
|
||||
{
|
||||
accessorKey: "manualLabel",
|
||||
header: "Manual Labels",
|
||||
},
|
||||
{
|
||||
accessorKey: "goodRatio",
|
||||
header: "Ratio",
|
||||
cell: ({ row }) => {
|
||||
const goodRatio =
|
||||
(parseInt(row.getValue("autoLabel")) /
|
||||
(parseInt(row.getValue("autoLabel")) +
|
||||
parseInt(row.getValue("manualLabel")))) *
|
||||
100;
|
||||
return (
|
||||
<div className="text-center font-medium">
|
||||
{isNaN(goodRatio) ? 0 : goodRatio.toFixed(2)}%
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: "reset",
|
||||
header: "Reset Reads",
|
||||
cell: () => {
|
||||
const { refetch } = useQuery(getReaders());
|
||||
const [readerReset, setReaderReset] = useState(false);
|
||||
const resetReads = async () => {
|
||||
setReaderReset(true);
|
||||
try {
|
||||
const res = await axios.post("/api/ocp/resetlabelratio", {
|
||||
reader: name,
|
||||
});
|
||||
|
||||
if (res.status === 200) {
|
||||
toast.success(res.data.message);
|
||||
setReaderReset(false);
|
||||
} else {
|
||||
toast.error(res.data.message);
|
||||
setReaderReset(false);
|
||||
}
|
||||
} catch (error: any) {
|
||||
toast.error(error.data.message);
|
||||
setReaderReset(false);
|
||||
}
|
||||
refetch();
|
||||
};
|
||||
return (
|
||||
<div className="text-left font-medium">
|
||||
<Button
|
||||
className="h-4"
|
||||
onClick={resetReads}
|
||||
disabled={readerReset}
|
||||
>
|
||||
Reset
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,124 @@
|
||||
import {
|
||||
ColumnDef,
|
||||
flexRender,
|
||||
getCoreRowModel,
|
||||
useReactTable,
|
||||
getPaginationRowModel,
|
||||
} from "@tanstack/react-table";
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from "@/components/ui/table";
|
||||
|
||||
import { useState } from "react";
|
||||
|
||||
import { ScrollArea } from "@/components/ui/scroll-area";
|
||||
import { LstCard } from "@/components/extendedUI/LstCard";
|
||||
|
||||
interface DataTableProps<TData, TValue> {
|
||||
columns: ColumnDef<TData, TValue>[];
|
||||
data: TData[];
|
||||
//style: any;
|
||||
}
|
||||
|
||||
export function LabelRatioTable<TData, TValue>({
|
||||
columns,
|
||||
data,
|
||||
//style,
|
||||
}: DataTableProps<TData, TValue>) {
|
||||
const [pagination, setPagination] = useState({
|
||||
pageIndex: 0, //initial page index
|
||||
pageSize: 5, //default page size
|
||||
});
|
||||
const table = useReactTable({
|
||||
data,
|
||||
columns,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
getPaginationRowModel: getPaginationRowModel(),
|
||||
onPaginationChange: setPagination,
|
||||
state: {
|
||||
//...
|
||||
pagination,
|
||||
},
|
||||
});
|
||||
|
||||
//console.log(parseInt(style.height.replace("px", "")) - 50);
|
||||
return (
|
||||
<LstCard>
|
||||
<div>
|
||||
<div className="flex flex-row justify-between">
|
||||
{data.length === 0 ? (
|
||||
<span className="text-center">
|
||||
No labels printed since last reset.
|
||||
</span>
|
||||
) : (
|
||||
<span>Label Ratio</span>
|
||||
)}
|
||||
</div>
|
||||
<ScrollArea className="max-h-32 rounded-md border m-2">
|
||||
<Table
|
||||
// style={{
|
||||
// width: `${parseInt(style.width.replace("px", "")) - 50}px`,
|
||||
// height: `${parseInt(style.height.replace("px", "")) - 200}px`,
|
||||
// cursor: "move",
|
||||
// }}
|
||||
>
|
||||
<TableHeader>
|
||||
{table.getHeaderGroups().map((headerGroup) => (
|
||||
<TableRow key={headerGroup.id}>
|
||||
{headerGroup.headers.map((header) => {
|
||||
return (
|
||||
<TableHead key={header.id}>
|
||||
{header.isPlaceholder
|
||||
? null
|
||||
: flexRender(
|
||||
header.column
|
||||
.columnDef.header,
|
||||
header.getContext()
|
||||
)}
|
||||
</TableHead>
|
||||
);
|
||||
})}
|
||||
</TableRow>
|
||||
))}
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{table.getRowModel().rows?.length ? (
|
||||
table.getRowModel().rows.map((row) => (
|
||||
<TableRow
|
||||
key={row.id}
|
||||
data-state={
|
||||
row.getIsSelected() && "selected"
|
||||
}
|
||||
>
|
||||
{row.getVisibleCells().map((cell) => (
|
||||
<TableCell key={cell.id}>
|
||||
{flexRender(
|
||||
cell.column.columnDef.cell,
|
||||
cell.getContext()
|
||||
)}
|
||||
</TableCell>
|
||||
))}
|
||||
</TableRow>
|
||||
))
|
||||
) : (
|
||||
<TableRow>
|
||||
<TableCell
|
||||
colSpan={columns.length}
|
||||
className="h-24 text-center"
|
||||
>
|
||||
No labels.
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</ScrollArea>
|
||||
</div>
|
||||
</LstCard>
|
||||
);
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { fixTime } from "@/utils/fixTime";
|
||||
import { ColumnDef } from "@tanstack/react-table";
|
||||
import { format } from "date-fns-tz";
|
||||
import { Trash } from "lucide-react";
|
||||
|
||||
// This type is used to define the shape of our data.
|
||||
@@ -38,9 +38,11 @@ export const ocpColumns = (
|
||||
header: "Error Date",
|
||||
cell: ({ row }) => {
|
||||
if (row.getValue("created_at")) {
|
||||
const correctDate = fixTime(row.getValue("created_at"));
|
||||
const correctDate: any = row.getValue("created_at");
|
||||
const strippedDate = correctDate.replace("Z", ""); // Remove Z
|
||||
const formattedDate = format(strippedDate, "MM/dd/yyyy HH:mm");
|
||||
return (
|
||||
<div className="text-left font-medium">{correctDate}</div>
|
||||
<div className="text-left font-medium">{formattedDate}</div>
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
148
frontend/src/utils/tableData/rfid/readers/readerColumns.tsx
Normal file
148
frontend/src/utils/tableData/rfid/readers/readerColumns.tsx
Normal file
@@ -0,0 +1,148 @@
|
||||
//import { fixTime } from "@/utils/fixTime";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { getReaders } from "@/utils/querys/rfid/getReaders";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { ColumnDef } from "@tanstack/react-table";
|
||||
import axios from "axios";
|
||||
import { format } from "date-fns-tz";
|
||||
import { useState } from "react";
|
||||
import { toast } from "sonner";
|
||||
|
||||
// This type is used to define the shape of our data.
|
||||
// You can use a Zod schema here if you want.
|
||||
export type Readers = {
|
||||
rfidReader_id: string;
|
||||
reader: string;
|
||||
readerIP: string;
|
||||
lastHeartBeat: string;
|
||||
lastTrigger: string;
|
||||
lastTriggerGood: boolean;
|
||||
active: boolean;
|
||||
lastTagScanned: string;
|
||||
goodReads: number;
|
||||
badReads: number;
|
||||
totalReads: number;
|
||||
goodRatio: number;
|
||||
};
|
||||
|
||||
export const readerColumns: ColumnDef<Readers>[] = [
|
||||
{
|
||||
accessorKey: "reader",
|
||||
header: () => <div className="text-left">Name</div>,
|
||||
},
|
||||
{
|
||||
accessorKey: "lastHeartBeat",
|
||||
header: "Last HeartBeat",
|
||||
cell: ({ row }) => {
|
||||
if (row.getValue("lastHeartBeat")) {
|
||||
const correctDate: any = row.getValue("lastHeartBeat");
|
||||
const strippedDate = correctDate.replace("Z", ""); // Remove Z
|
||||
const formattedDate = format(strippedDate, "MM/dd/yyyy HH:mm");
|
||||
return (
|
||||
<div className="text-left font-medium">{formattedDate}</div>
|
||||
);
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: "lastTrigger",
|
||||
header: "Last Trigger",
|
||||
cell: ({ row }) => {
|
||||
if (row.getValue("lastTrigger")) {
|
||||
const correctDate: any = row.getValue("lastTrigger");
|
||||
const strippedDate = correctDate.replace("Z", ""); // Remove Z
|
||||
const formattedDate = format(strippedDate, "MM/dd/yyyy HH:mm");
|
||||
return (
|
||||
<div className="text-left font-medium">{formattedDate}</div>
|
||||
);
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: "lastTriggerGood",
|
||||
header: "Last Trigger Status",
|
||||
},
|
||||
{
|
||||
accessorKey: "lastTagScanned",
|
||||
header: "Last Scanned Tag",
|
||||
},
|
||||
{
|
||||
accessorKey: "goodReads",
|
||||
header: "Total Good Reads",
|
||||
},
|
||||
{
|
||||
accessorKey: "badReads",
|
||||
header: "Total Bad Reads",
|
||||
},
|
||||
{
|
||||
accessorKey: "totalReads",
|
||||
header: "Total Reads",
|
||||
cell: ({ row }) => {
|
||||
const total =
|
||||
parseInt(row.getValue("goodReads")) +
|
||||
parseInt(row.getValue("badReads"));
|
||||
return <div className="text-left font-medium">{total}</div>;
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: "goodRatio",
|
||||
header: "Good Ratio",
|
||||
cell: ({ row }) => {
|
||||
const goodRatio =
|
||||
(parseInt(row.getValue("goodReads")) /
|
||||
(parseInt(row.getValue("goodReads")) +
|
||||
parseInt(row.getValue("badReads")))) *
|
||||
100;
|
||||
return (
|
||||
<div className="text-left font-medium">
|
||||
{isNaN(goodRatio) ? 0 : goodRatio.toFixed(2)}%
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: "reset",
|
||||
header: "Reset Reads",
|
||||
cell: ({ row }) => {
|
||||
const { refetch } = useQuery(getReaders());
|
||||
const [readerReset, setReaderReset] = useState(false);
|
||||
// const goodRatio =
|
||||
// (parseInt(row.getValue("goodReads")) /
|
||||
// (parseInt(row.getValue("goodReads")) +
|
||||
// parseInt(row.getValue("badReads")))) *
|
||||
// 100;
|
||||
const name = row.getValue("reader");
|
||||
const resetReads = async () => {
|
||||
setReaderReset(true);
|
||||
try {
|
||||
const res = await axios.post("/api/rfid/resetRatio", {
|
||||
reader: name,
|
||||
});
|
||||
|
||||
if (res.status === 200) {
|
||||
toast.success(res.data.message);
|
||||
setReaderReset(false);
|
||||
} else {
|
||||
toast.error(res.data.message);
|
||||
setReaderReset(false);
|
||||
}
|
||||
} catch (error: any) {
|
||||
toast.error(error.data.message);
|
||||
setReaderReset(false);
|
||||
}
|
||||
refetch();
|
||||
};
|
||||
return (
|
||||
<div className="text-left font-medium">
|
||||
<Button
|
||||
className="h-4"
|
||||
onClick={resetReads}
|
||||
disabled={readerReset}
|
||||
>
|
||||
Reset Reads
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
];
|
||||
174
frontend/src/utils/tableData/rfid/readers/readerData.tsx
Normal file
174
frontend/src/utils/tableData/rfid/readers/readerData.tsx
Normal file
@@ -0,0 +1,174 @@
|
||||
import {
|
||||
ColumnDef,
|
||||
flexRender,
|
||||
getCoreRowModel,
|
||||
useReactTable,
|
||||
getPaginationRowModel,
|
||||
} from "@tanstack/react-table";
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from "@/components/ui/table";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { useState } from "react";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectGroup,
|
||||
SelectItem,
|
||||
SelectLabel,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/components/ui/select";
|
||||
import { ScrollArea } from "@/components/ui/scroll-area";
|
||||
import { LstCard } from "@/components/extendedUI/LstCard";
|
||||
|
||||
interface DataTableProps<TData, TValue> {
|
||||
columns: ColumnDef<TData, TValue>[];
|
||||
data: TData[];
|
||||
//style: any;
|
||||
}
|
||||
|
||||
export function ReaderTable<TData, TValue>({
|
||||
columns,
|
||||
data,
|
||||
//style,
|
||||
}: DataTableProps<TData, TValue>) {
|
||||
const [pagination, setPagination] = useState({
|
||||
pageIndex: 0, //initial page index
|
||||
pageSize: 10, //default page size
|
||||
});
|
||||
const table = useReactTable({
|
||||
data,
|
||||
columns,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
getPaginationRowModel: getPaginationRowModel(),
|
||||
onPaginationChange: setPagination,
|
||||
state: {
|
||||
//...
|
||||
pagination,
|
||||
},
|
||||
});
|
||||
|
||||
//console.log(parseInt(style.height.replace("px", "")) - 50);
|
||||
return (
|
||||
<LstCard>
|
||||
<div>
|
||||
<div className="flex flex-row justify-between">
|
||||
{data.length === 0 ? (
|
||||
<span>No readers</span>
|
||||
) : (
|
||||
<span>Current reader Info</span>
|
||||
)}
|
||||
<Select
|
||||
value={pagination.pageSize.toString()}
|
||||
onValueChange={(e) =>
|
||||
setPagination({
|
||||
...pagination,
|
||||
pageSize: parseInt(e),
|
||||
})
|
||||
}
|
||||
>
|
||||
<SelectTrigger className="w-[180px]">
|
||||
<SelectValue
|
||||
//id={field.name}
|
||||
placeholder="Select Page"
|
||||
defaultValue={10}
|
||||
/>
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectGroup>
|
||||
<SelectLabel>Page Size</SelectLabel>
|
||||
<SelectItem value="5">5</SelectItem>
|
||||
<SelectItem value="10">10</SelectItem>
|
||||
<SelectItem value="50">50</SelectItem>
|
||||
<SelectItem value="100">100</SelectItem>
|
||||
</SelectGroup>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<ScrollArea className="h-96 rounded-md border m-2">
|
||||
<Table
|
||||
// style={{
|
||||
// width: `${parseInt(style.width.replace("px", "")) - 50}px`,
|
||||
// height: `${parseInt(style.height.replace("px", "")) - 200}px`,
|
||||
// cursor: "move",
|
||||
// }}
|
||||
>
|
||||
<TableHeader>
|
||||
{table.getHeaderGroups().map((headerGroup) => (
|
||||
<TableRow key={headerGroup.id}>
|
||||
{headerGroup.headers.map((header) => {
|
||||
return (
|
||||
<TableHead key={header.id}>
|
||||
{header.isPlaceholder
|
||||
? null
|
||||
: flexRender(
|
||||
header.column
|
||||
.columnDef.header,
|
||||
header.getContext()
|
||||
)}
|
||||
</TableHead>
|
||||
);
|
||||
})}
|
||||
</TableRow>
|
||||
))}
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{table.getRowModel().rows?.length ? (
|
||||
table.getRowModel().rows.map((row) => (
|
||||
<TableRow
|
||||
key={row.id}
|
||||
data-state={
|
||||
row.getIsSelected() && "selected"
|
||||
}
|
||||
>
|
||||
{row.getVisibleCells().map((cell) => (
|
||||
<TableCell key={cell.id}>
|
||||
{flexRender(
|
||||
cell.column.columnDef.cell,
|
||||
cell.getContext()
|
||||
)}
|
||||
</TableCell>
|
||||
))}
|
||||
</TableRow>
|
||||
))
|
||||
) : (
|
||||
<TableRow>
|
||||
<TableCell
|
||||
colSpan={columns.length}
|
||||
className="h-24 text-center"
|
||||
>
|
||||
No labels.
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</ScrollArea>
|
||||
</div>
|
||||
<div className="flex items-center justify-end space-x-2">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => table.previousPage()}
|
||||
disabled={!table.getCanPreviousPage()}
|
||||
>
|
||||
Previous
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => table.nextPage()}
|
||||
disabled={!table.getCanNextPage()}
|
||||
>
|
||||
Next
|
||||
</Button>
|
||||
</div>
|
||||
</LstCard>
|
||||
);
|
||||
}
|
||||
164
package-lock.json
generated
164
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "lstv2",
|
||||
"version": "2.23.0",
|
||||
"version": "2.27.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "lstv2",
|
||||
"version": "2.23.0",
|
||||
"version": "2.27.0",
|
||||
"dependencies": {
|
||||
"@dotenvx/dotenvx": "^1.45.1",
|
||||
"@hono/node-server": "^1.14.4",
|
||||
@@ -29,6 +29,7 @@
|
||||
"fast-xml-parser": "^5.2.5",
|
||||
"fs-extra": "^11.3.0",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"morgan": "^1.10.1",
|
||||
"mssql": "^11.0.1",
|
||||
"nodemailer": "^7.0.3",
|
||||
"nodemailer-express-handlebars": "^7.0.0",
|
||||
@@ -1548,14 +1549,6 @@
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/@petamoriken/float16": {
|
||||
"version": "3.9.2",
|
||||
"resolved": "https://registry.npmjs.org/@petamoriken/float16/-/float16-3.9.2.tgz",
|
||||
"integrity": "sha512-VgffxawQde93xKxT3qap3OH+meZf7VaSB5Sqd4Rqc+FP5alWbpOyan/7tRbOAvynjpG3GpdtAuGU/NdhQpmrog==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@scalar/core": {
|
||||
"version": "0.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@scalar/core/-/core-0.3.3.tgz",
|
||||
@@ -2118,6 +2111,24 @@
|
||||
],
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/basic-auth": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz",
|
||||
"integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"safe-buffer": "5.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/basic-auth/node_modules/safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/bcryptjs": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-3.0.2.tgz",
|
||||
@@ -3379,6 +3390,15 @@
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/depd": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
|
||||
"integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/detect-file": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz",
|
||||
@@ -3716,6 +3736,12 @@
|
||||
"node": ">=16"
|
||||
}
|
||||
},
|
||||
"node_modules/ee-first": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
||||
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/emoji-regex": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||
@@ -4227,70 +4253,6 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/gel": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/gel/-/gel-2.0.1.tgz",
|
||||
"integrity": "sha512-gfem3IGvqKqXwEq7XseBogyaRwGsQGuE7Cw/yQsjLGdgiyqX92G1xENPCE0ltunPGcsJIa6XBOTx/PK169mOqw==",
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@petamoriken/float16": "^3.8.7",
|
||||
"debug": "^4.3.4",
|
||||
"env-paths": "^3.0.0",
|
||||
"semver": "^7.6.2",
|
||||
"shell-quote": "^1.8.1",
|
||||
"which": "^4.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"gel": "dist/cli.mjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/gel/node_modules/env-paths": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/env-paths/-/env-paths-3.0.0.tgz",
|
||||
"integrity": "sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/gel/node_modules/isexe": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz",
|
||||
"integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==",
|
||||
"license": "ISC",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
}
|
||||
},
|
||||
"node_modules/gel/node_modules/which": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz",
|
||||
"integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==",
|
||||
"license": "ISC",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"isexe": "^3.1.1"
|
||||
},
|
||||
"bin": {
|
||||
"node-which": "bin/which.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^16.13.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/get-caller-file": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
|
||||
@@ -6080,6 +6042,37 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/morgan": {
|
||||
"version": "1.10.1",
|
||||
"resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.1.tgz",
|
||||
"integrity": "sha512-223dMRJtI/l25dJKWpgij2cMtywuG/WiUKXdvwfbhGKBhy1puASqXwFzmWZ7+K73vUPoR7SS2Qz2cI/g9MKw0A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"basic-auth": "~2.0.1",
|
||||
"debug": "2.6.9",
|
||||
"depd": "~2.0.0",
|
||||
"on-finished": "~2.3.0",
|
||||
"on-headers": "~1.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/morgan/node_modules/debug": {
|
||||
"version": "2.6.9",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ms": "2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/morgan/node_modules/ms": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/ms": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||
@@ -6236,6 +6229,27 @@
|
||||
"node": ">=14.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/on-finished": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
|
||||
"integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ee-first": "1.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/on-headers": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz",
|
||||
"integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/once": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||
@@ -7396,7 +7410,7 @@
|
||||
"version": "1.8.2",
|
||||
"resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.2.tgz",
|
||||
"integrity": "sha512-AzqKpGKjrj7EM6rKVQEPpB288oCfnrEIuyoT9cyF4nmGa7V8Zk6f7RRqYisX8X9m+Q7bd632aZW4ky7EhbQztA==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
|
||||
11
package.json
11
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "lstv2",
|
||||
"version": "2.23.0",
|
||||
"version": "2.27.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "concurrently -n \"server,frontend\" -c \"#007755,#2f6da3\" \"npm run dev:server\" \"cd frontend && npm run dev\"",
|
||||
@@ -8,7 +8,7 @@
|
||||
"dev:frontend": "cd frontend && npm run dev",
|
||||
"dev:dbgen": " drizzle-kit generate --config=drizzle-dev.config.ts",
|
||||
"dev:dbmigrate": " drizzle-kit migrate --config=drizzle-dev.config.ts",
|
||||
"build": "npm run build:server && npm run build:frontend && npm run build:iisNet && npm run zipServer && npm run dev",
|
||||
"build": "npm run build:server && npm run build:frontend && npm run zipServer",
|
||||
"build:server": "rimraf dist && tsc --build && npm run copy:scripts && xcopy server\\services\\notifications\\utils\\views\\ dist\\server\\services\\notifications\\utils\\views\\ /E /I /Y",
|
||||
"build:frontend": "cd frontend && npm run build",
|
||||
"build:iisNet": "rimraf dotnetwrapper\\bin && xcopy frontend\\dist dotnetwrapper\\wwwroot /E /I /Y && cd dotnetwrapper && dotnet publish lst-wrapper.csproj --configuration Release --output ../prodBuild",
|
||||
@@ -21,8 +21,8 @@
|
||||
"db:dev": "npm run build && npm run db:generate && npm run db:migrate",
|
||||
"deploy": "standard-version --conventional-commits && npm run build",
|
||||
"zipServer": "dotenvx run -f .env -- tsx server/scripts/zipUpBuild.ts \"C:\\Users\\matthes01\\Documents\\lstv2\"",
|
||||
"v1Build": "cd C:\\Users\\matthes01\\Documents\\logisticsSupportTool && npm run oldBuilder",
|
||||
"scriptBuild": "powershell -ExecutionPolicy Bypass -File server/scripts/build.ps1 -dir \"C:\\Users\\matthes01\\Documents\\lstv2\"",
|
||||
"newBuild": "npm run build:server && npm run build:frontend && npm run zipServer && npm run copyToNew",
|
||||
"copyToNew": "powershell -ExecutionPolicy Bypass -File server/scripts/copyToLst.ps1 -dir \"C:\\Users\\matthes01\\Documents\\lstv2\"",
|
||||
"removeOld": "rimraf dist && rimraf frontend/dist",
|
||||
"prodBuild": "npm run v1Build && npm run build && npm run zipServer && npm run dev",
|
||||
"commit": "cz",
|
||||
@@ -36,7 +36,7 @@
|
||||
}
|
||||
},
|
||||
"admConfig": {
|
||||
"build": 432,
|
||||
"build": 661,
|
||||
"oldBuild": "backend-0.1.3.zip"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -78,6 +78,7 @@
|
||||
"fast-xml-parser": "^5.2.5",
|
||||
"fs-extra": "^11.3.0",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"morgan": "^1.10.1",
|
||||
"mssql": "^11.0.1",
|
||||
"nodemailer": "^7.0.3",
|
||||
"nodemailer-express-handlebars": "^7.0.0",
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
import { eq } from "drizzle-orm";
|
||||
import { db } from "../../database/dbclient.js";
|
||||
import { settings } from "../../database/schema/settings.js";
|
||||
import { query } from "../services/sqlServer/prodSqlServer.js";
|
||||
import { plantInfo } from "../services/sqlServer/querys/dataMart/plantInfo.js";
|
||||
import { createLog } from "../services/logger/logger.js";
|
||||
import { getSettings } from "../services/server/controller/settings/getSettings.js";
|
||||
|
||||
export const createSSCC = async (runningNumber: number) => {
|
||||
// get the token
|
||||
const plantToken = await db
|
||||
.select()
|
||||
.from(settings)
|
||||
.where(eq(settings.name, "plantToken"));
|
||||
|
||||
let serverSettings = (await getSettings()) as any;
|
||||
const plantToken = serverSettings?.filter(
|
||||
(n: any) => n.name === "plantToken"
|
||||
);
|
||||
// const plantToken = await db
|
||||
// .select()
|
||||
// .from(settings)
|
||||
// .where(eq(settings.name, "plantToken"));
|
||||
let global: any = []; // get from plant address in basis enter the entire string here.
|
||||
|
||||
try {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { eq } from "drizzle-orm";
|
||||
import { db } from "../../database/dbclient.js";
|
||||
import { settings } from "../../database/schema/settings.js";
|
||||
|
||||
import { getSettings } from "../services/server/controller/settings/getSettings.js";
|
||||
|
||||
// create the test server stuff
|
||||
const testServers = [
|
||||
@@ -12,19 +13,22 @@ const testServers = [
|
||||
export const prodEndpointCreation = async (endpoint: string) => {
|
||||
let url = "";
|
||||
//get the plant token
|
||||
const plantToken = await db
|
||||
.select()
|
||||
.from(settings)
|
||||
.where(eq(settings.name, "plantToken"));
|
||||
let serverSettings = await getSettings();
|
||||
const plantToken = serverSettings?.filter((n) => n.name === "plantToken");
|
||||
// await db
|
||||
// .select()
|
||||
// .from(settings)
|
||||
// .where(eq(settings.name, "plantToken"));
|
||||
|
||||
// check if we are a test server
|
||||
const testServer = testServers.some(
|
||||
(server) => server.token === plantToken[0]?.value
|
||||
);
|
||||
const server = await db
|
||||
.select()
|
||||
.from(settings)
|
||||
.where(eq(settings.name, "dbServer"));
|
||||
const server = serverSettings?.filter((n) => n.name === "dbServer");
|
||||
// await db
|
||||
// .select()
|
||||
// .from(settings)
|
||||
// .where(eq(settings.name, "dbServer"));
|
||||
|
||||
if (testServer) {
|
||||
//filter out what testserver we are
|
||||
|
||||
@@ -1,23 +1,43 @@
|
||||
import type { Context } from "hono";
|
||||
import { createLog } from "../../services/logger/logger.js";
|
||||
|
||||
export type ReturnRes<T> =
|
||||
| { success: true; message: string; data: T }
|
||||
| { success: false; message: string; error: any };
|
||||
export function returnRes<T>(
|
||||
success: true,
|
||||
message: string,
|
||||
service: string,
|
||||
user: string,
|
||||
level: "info" | "error",
|
||||
data: T
|
||||
): { success: true; message: string; data: T };
|
||||
|
||||
export const returnRes = <T>(
|
||||
success: boolean,
|
||||
message: string,
|
||||
data: T | null = null
|
||||
): ReturnRes<T> => {
|
||||
/**
|
||||
* just a simple return to reduce the typing and make sure we are always consitant with our returns.
|
||||
*
|
||||
* data can be an error as well.
|
||||
*/
|
||||
return success
|
||||
? { success, message, data: data as T }
|
||||
: { success, message, error: data ?? "An unknown error occurred" };
|
||||
};
|
||||
export function returnRes<T>(
|
||||
success: false,
|
||||
message: string,
|
||||
service: string,
|
||||
user: string,
|
||||
level: "info" | "error",
|
||||
data?: T
|
||||
): { success: false; message: string; error: T | string };
|
||||
|
||||
export function returnRes<T>(
|
||||
success: boolean,
|
||||
message: string,
|
||||
service: string,
|
||||
user: string,
|
||||
level: "info" | "error",
|
||||
data?: T
|
||||
) {
|
||||
createLog(level, user, service, message);
|
||||
|
||||
if (success) {
|
||||
return { success: true, message, data: data as T };
|
||||
} else {
|
||||
return {
|
||||
success: false,
|
||||
message,
|
||||
error: data ?? "An unknown error occurred",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// export const returnApi = (c:Context,success: boolean, message: string, data?: any, code: number)=>{
|
||||
// /**
|
||||
|
||||
@@ -29,6 +29,11 @@ import eom from "./services/eom/eomService.js";
|
||||
import dataMart from "./services/dataMart/dataMartService.js";
|
||||
import qualityRequest from "./services/quality/qualityService.js";
|
||||
import produser from "./services/prodUser/prodUser.js";
|
||||
import {
|
||||
getSettings,
|
||||
serverSettings,
|
||||
} from "./services/server/controller/settings/getSettings.js";
|
||||
import type { Settings } from "./types/settings.js";
|
||||
|
||||
// create the main prodlogin here
|
||||
const username = "lst_user";
|
||||
@@ -36,15 +41,16 @@ const password = "Alpla$$Prod";
|
||||
export const lstAuth = btoa(`${username}:${password}`);
|
||||
|
||||
// checking to make sure we have the settings intialized
|
||||
const { data: settingsData, error: settingError } = await tryCatch(
|
||||
db.select().from(settings)
|
||||
);
|
||||
// const { data: settingsData, error: settingError } = await tryCatch(
|
||||
// db.select().from(settings)
|
||||
// );
|
||||
|
||||
if (settingError) {
|
||||
throw Error("Error getting settings from the db. critical error.");
|
||||
}
|
||||
// if (settingError) {
|
||||
// throw Error("Error getting settings from the db. critical error.");
|
||||
// }
|
||||
|
||||
const serverIntialized: any = await getSettings();
|
||||
|
||||
const serverIntialized: any = settingsData;
|
||||
export const installed =
|
||||
serverIntialized.length === 0 && process.env.NODE_ENV !== "development"
|
||||
? false
|
||||
@@ -211,7 +217,8 @@ serve(
|
||||
/**
|
||||
* Only for ocme until we get them switched over to the single port setup.
|
||||
*/
|
||||
const setting = await db.select().from(settings);
|
||||
// const setting = await db.select().from(settings);
|
||||
const setting = serverSettings;
|
||||
const isActive = setting.filter((n) => n.name === "ocmeService");
|
||||
if (ocmeport && isActive[0]?.value === "1") {
|
||||
serve(
|
||||
|
||||
61
server/scripts/copyToLst.ps1
Normal file
61
server/scripts/copyToLst.ps1
Normal file
@@ -0,0 +1,61 @@
|
||||
param(
|
||||
[string]$IncludesFile = ".includes",
|
||||
[string]$Destination = "C:\Users\matthes01\Documents\lst\lstV2",
|
||||
[string]$BaseDir = "C:\Users\matthes01\Documents\lst"
|
||||
)
|
||||
|
||||
# .\copy-includes.ps1 will run with defaults
|
||||
# .\copy-includes.ps1 -IncludesFile ".\mylist.txt" -Destination "D:\build\lstV2" will override defaults
|
||||
|
||||
if (-Not (Test-Path $IncludesFile)) {
|
||||
Write-Error "Includes file not found: $IncludesFile"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Ensure destination exists
|
||||
if (!(Test-Path -Path $Destination)) {
|
||||
New-Item -ItemType Directory -Path $Destination | Out-Null
|
||||
Write-Host "Folder created: $Destination"
|
||||
}
|
||||
|
||||
# Empty the destination folder
|
||||
Get-ChildItem -Path $Destination -Recurse -Force | Remove-Item -Recurse -Force
|
||||
|
||||
# If BaseDir wasn’t explicitly passed in, use IncludesFile directory
|
||||
if (-not $PSBoundParameters.ContainsKey('BaseDir')) {
|
||||
$BaseDir = Split-Path -Parent (Resolve-Path $IncludesFile)
|
||||
}
|
||||
|
||||
# Read includes list (ignore blank lines & comments)
|
||||
$items = Get-Content $IncludesFile |
|
||||
ForEach-Object { $_.Trim() } |
|
||||
Where-Object { $_ -and -not $_.StartsWith("#") }
|
||||
|
||||
foreach ($item in $items) {
|
||||
if ([System.IO.Path]::IsPathRooted($item)) {
|
||||
# Absolute path (rare case)
|
||||
$sourcePath = $item
|
||||
$relative = Split-Path $item -Leaf # just take folder/file name
|
||||
} else {
|
||||
# Relative to BaseDir
|
||||
$sourcePath = Join-Path $BaseDir $item
|
||||
$relative = $item # keep full relative path e.g. "frontend\dist"
|
||||
}
|
||||
|
||||
if (-Not (Test-Path $sourcePath)) {
|
||||
Write-Warning "Skipping missing path: $sourcePath"
|
||||
continue
|
||||
}
|
||||
|
||||
# Destination path should preserve the relative structure
|
||||
$targetPath = Join-Path $Destination $relative
|
||||
|
||||
# Ensure the parent folder exists
|
||||
$targetDir = Split-Path $targetPath -Parent
|
||||
if (-not (Test-Path $targetDir)) {
|
||||
New-Item -ItemType Directory -Path $targetDir -Force | Out-Null
|
||||
}
|
||||
|
||||
Write-Host "Copying $sourcePath -> $targetPath" -ForegroundColor Cyan
|
||||
Copy-Item -Path $sourcePath -Destination $targetPath -Recurse -Force
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import { db } from "../../database/dbclient.js";
|
||||
import { serverData } from "../../database/schema/serverData.js";
|
||||
import { eq, sql } from "drizzle-orm";
|
||||
import { createLog } from "../services/logger/logger.js";
|
||||
import { serverSettings } from "../services/server/controller/settings/getSettings.js";
|
||||
|
||||
type UpdateServerResponse = {
|
||||
success: boolean;
|
||||
@@ -48,6 +49,7 @@ export const updateServer = async (
|
||||
};
|
||||
}
|
||||
|
||||
console.log(serverInfo);
|
||||
const scriptPath = `${process.env.DEVFOLDER}\\server\\scripts\\update.ps1 `;
|
||||
const args = [
|
||||
"-NoProfile",
|
||||
|
||||
@@ -38,6 +38,7 @@ const ignoreList = [
|
||||
//misc files
|
||||
"jsTesting",
|
||||
"dotnetwrapper",
|
||||
"prodBuild",
|
||||
];
|
||||
|
||||
const shouldIgnore = (itemPath: any) => {
|
||||
|
||||
@@ -37,7 +37,7 @@ export const registerUser = async (
|
||||
.values({ username, email, password })
|
||||
.returning({ user: users.username, email: users.email });
|
||||
|
||||
if (usercount.length <= 1) {
|
||||
if (usercount.length === 0) {
|
||||
createLog(
|
||||
"info",
|
||||
"auth",
|
||||
|
||||
@@ -12,13 +12,15 @@ export const getAllUsersRoles = async () => {
|
||||
const { data, error } = await tryCatch(db.select().from(userRoles));
|
||||
|
||||
if (error) {
|
||||
returnRes(
|
||||
return returnRes(
|
||||
false,
|
||||
"auth",
|
||||
"auth",
|
||||
"There was an error getting users",
|
||||
"error",
|
||||
new Error("No user exists.")
|
||||
);
|
||||
}
|
||||
|
||||
returnRes(true, "All users.", data);
|
||||
return { success: true, message: "All users", data };
|
||||
return returnRes(true, "auth", "auth", "All users.", "info", data);
|
||||
};
|
||||
|
||||
@@ -32,7 +32,10 @@ export const getAllUsers = async () => {
|
||||
if (error) {
|
||||
returnRes(
|
||||
false,
|
||||
"auth",
|
||||
"auth",
|
||||
"There was an error getting users",
|
||||
"error",
|
||||
new Error("No user exists.")
|
||||
);
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import { createPassword } from "../../utils/createPassword.js";
|
||||
import { createLog } from "../../../logger/logger.js";
|
||||
import { sendEmail } from "../../../notifications/controller/sendMail.js";
|
||||
import { settings } from "../../../../../database/schema/settings.js";
|
||||
import { getSettings } from "../../../server/controller/settings/getSettings.js";
|
||||
|
||||
export const updateUserADM = async (userData: User) => {
|
||||
/**
|
||||
@@ -40,8 +41,8 @@ export const updateUserADM = async (userData: User) => {
|
||||
};
|
||||
}
|
||||
|
||||
const { data: s, error: se } = await tryCatch(db.select().from(settings));
|
||||
|
||||
//const { data: s, error: se } = await tryCatch(db.select().from(settings));
|
||||
const { data: s, error: se } = await tryCatch(getSettings());
|
||||
if (se) {
|
||||
return {
|
||||
success: false,
|
||||
|
||||
@@ -7,16 +7,16 @@ import { deliveryByDateRange } from "../../sqlServer/querys/dataMart/deleveryByD
|
||||
import { addDays, format } from "date-fns";
|
||||
|
||||
export const getDeliveryByDateRange = async (data: any | null) => {
|
||||
const { data: plantToken, error: plantError } = await tryCatch(
|
||||
db.select().from(settings).where(eq(settings.name, "plantToken"))
|
||||
);
|
||||
if (plantError) {
|
||||
return {
|
||||
success: false,
|
||||
message: "Error getting Settings",
|
||||
data: plantError,
|
||||
};
|
||||
}
|
||||
// const { data: plantToken, error: plantError } = await tryCatch(
|
||||
// db.select().from(settings).where(eq(settings.name, "plantToken"))
|
||||
// );
|
||||
// if (plantError) {
|
||||
// return {
|
||||
// success: false,
|
||||
// message: "Error getting Settings",
|
||||
// data: plantError,
|
||||
// };
|
||||
// }
|
||||
let deliverys: any = [];
|
||||
|
||||
let updatedQuery = deliveryByDateRange;
|
||||
|
||||
@@ -4,18 +4,21 @@ import { settings } from "../../../../database/schema/settings.js";
|
||||
import { tryCatch } from "../../../globalUtils/tryCatch.js";
|
||||
import { query } from "../../sqlServer/prodSqlServer.js";
|
||||
import { openOrders } from "../../sqlServer/querys/dataMart/openOrders.js";
|
||||
import { serverSettings } from "../../server/controller/settings/getSettings.js";
|
||||
|
||||
export const getOpenOrders = async (data: any | null) => {
|
||||
const { data: plantToken, error: plantError } = await tryCatch(
|
||||
db.select().from(settings).where(eq(settings.name, "plantToken"))
|
||||
);
|
||||
if (plantError) {
|
||||
return {
|
||||
success: false,
|
||||
message: "Error getting Settings",
|
||||
data: plantError,
|
||||
};
|
||||
}
|
||||
// const { data: plantToken, error: plantError } = await tryCatch(
|
||||
// db.select().from(settings).where(eq(settings.name, "plantToken"))
|
||||
// );
|
||||
// if (plantError) {
|
||||
// return {
|
||||
// success: false,
|
||||
// message: "Error getting Settings",
|
||||
// data: plantError,
|
||||
// };
|
||||
// }
|
||||
|
||||
const plantToken = serverSettings.filter((n) => n.name === "plantToken");
|
||||
let orders: any = [];
|
||||
|
||||
let updatedQuery = openOrders;
|
||||
|
||||
47
server/services/dataMart/controller/psiGetArticleData.ts
Normal file
47
server/services/dataMart/controller/psiGetArticleData.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import { tryCatch } from "../../../globalUtils/tryCatch.js";
|
||||
import { createLog } from "../../logger/logger.js";
|
||||
import { query } from "../../sqlServer/prodSqlServer.js";
|
||||
import { articleInfo } from "../../sqlServer/querys/psiReport/articleData.js";
|
||||
|
||||
// type ArticleData = {
|
||||
// id: string
|
||||
// }
|
||||
export const getGetPSIArticleData = async (avs: string) => {
|
||||
let articles: any = [];
|
||||
|
||||
if (!avs) {
|
||||
return {
|
||||
success: false,
|
||||
message: `Missing av's please send at least one over`,
|
||||
data: [],
|
||||
};
|
||||
}
|
||||
|
||||
const { data, error } = (await tryCatch(
|
||||
query(articleInfo.replace("[articles]", avs), "PSI article info")
|
||||
)) as any;
|
||||
|
||||
if (error) {
|
||||
createLog(
|
||||
"error",
|
||||
"datamart",
|
||||
"datamart",
|
||||
`There was an error getting the article info: ${JSON.stringify(
|
||||
error
|
||||
)}`
|
||||
);
|
||||
return {
|
||||
success: false,
|
||||
messsage: `There was an error getting the article info`,
|
||||
data: error,
|
||||
};
|
||||
}
|
||||
|
||||
articles = data.data;
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: "PSI Article Data",
|
||||
data: articles,
|
||||
};
|
||||
};
|
||||
63
server/services/dataMart/controller/psiGetInventory.ts
Normal file
63
server/services/dataMart/controller/psiGetInventory.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
import { and, between, inArray, sql } from "drizzle-orm";
|
||||
import { db } from "../../../../database/dbclient.js";
|
||||
import { invHistoricalData } from "../../../../database/schema/historicalINV.js";
|
||||
import { tryCatch } from "../../../globalUtils/tryCatch.js";
|
||||
import { createLog } from "../../logger/logger.js";
|
||||
|
||||
// type ArticleData = {
|
||||
// id: string
|
||||
// }
|
||||
export const psiGetInventory = async (
|
||||
avs: string,
|
||||
startDate: string,
|
||||
endDate: string
|
||||
) => {
|
||||
let articles: any = [];
|
||||
|
||||
if (!avs) {
|
||||
return {
|
||||
success: false,
|
||||
message: `Missing av's please send at least one over`,
|
||||
data: [],
|
||||
};
|
||||
}
|
||||
|
||||
const ids = avs.split(",").map((id) => id.trim());
|
||||
|
||||
const { data, error } = (await tryCatch(
|
||||
db
|
||||
.select()
|
||||
.from(invHistoricalData)
|
||||
.where(
|
||||
and(
|
||||
inArray(invHistoricalData.article, ids),
|
||||
between(invHistoricalData.histDate, startDate, endDate)
|
||||
)
|
||||
)
|
||||
//.limit(100)
|
||||
)) as any;
|
||||
|
||||
if (error) {
|
||||
createLog(
|
||||
"error",
|
||||
"datamart",
|
||||
"datamart",
|
||||
`There was an error getting the planning info: ${JSON.stringify(
|
||||
error
|
||||
)}`
|
||||
);
|
||||
return {
|
||||
success: false,
|
||||
messsage: `There was an error getting the planning info`,
|
||||
data: error,
|
||||
};
|
||||
}
|
||||
|
||||
articles = data;
|
||||
console.log(articles.length);
|
||||
return {
|
||||
success: true,
|
||||
message: "PSI planning Data",
|
||||
data: articles,
|
||||
};
|
||||
};
|
||||
63
server/services/dataMart/controller/psiGetPlanningData.ts
Normal file
63
server/services/dataMart/controller/psiGetPlanningData.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
import { tryCatch } from "../../../globalUtils/tryCatch.js";
|
||||
import { createLog } from "../../logger/logger.js";
|
||||
import { query } from "../../sqlServer/prodSqlServer.js";
|
||||
import { planningNumbersByAVDate } from "../../sqlServer/querys/psiReport/planningNumbersByAv.js";
|
||||
|
||||
// type ArticleData = {
|
||||
// id: string
|
||||
// }
|
||||
export const psiGetPlanningData = async (
|
||||
avs: string,
|
||||
startDate: string,
|
||||
endDate: string
|
||||
) => {
|
||||
let articles: any = [];
|
||||
|
||||
if (!avs) {
|
||||
return {
|
||||
success: false,
|
||||
message: `Missing av's please send at least one over`,
|
||||
data: [],
|
||||
};
|
||||
}
|
||||
|
||||
const { data, error } = (await tryCatch(
|
||||
query(
|
||||
planningNumbersByAVDate
|
||||
.replace("[articles]", avs)
|
||||
.replace("[startDate]", startDate)
|
||||
.replace("[endDate]", endDate),
|
||||
"PSI planning info"
|
||||
)
|
||||
)) as any;
|
||||
|
||||
if (error) {
|
||||
createLog(
|
||||
"error",
|
||||
"datamart",
|
||||
"datamart",
|
||||
`There was an error getting the planning info: ${JSON.stringify(
|
||||
error
|
||||
)}`
|
||||
);
|
||||
return {
|
||||
success: false,
|
||||
messsage: `There was an error getting the planning info`,
|
||||
data: error,
|
||||
};
|
||||
}
|
||||
|
||||
articles = data.data;
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: "PSI planning Data",
|
||||
data: articles.map((n: any) => {
|
||||
if (n.PalDay) {
|
||||
return { ...n, PalDay: n.PalDay.toFixed(2) };
|
||||
}
|
||||
|
||||
return n;
|
||||
}),
|
||||
};
|
||||
};
|
||||
63
server/services/dataMart/controller/psiGetProductionData.ts
Normal file
63
server/services/dataMart/controller/psiGetProductionData.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
import { tryCatch } from "../../../globalUtils/tryCatch.js";
|
||||
import { createLog } from "../../logger/logger.js";
|
||||
import { query } from "../../sqlServer/prodSqlServer.js";
|
||||
import { productionNumbers } from "../../sqlServer/querys/psiReport/prodcuctionNumbers.js";
|
||||
|
||||
// type ArticleData = {
|
||||
// id: string
|
||||
// }
|
||||
export const psiGetProductionData = async (
|
||||
avs: string,
|
||||
startDate: string,
|
||||
endDate: string
|
||||
) => {
|
||||
let articles: any = [];
|
||||
|
||||
if (!avs) {
|
||||
return {
|
||||
success: false,
|
||||
message: `Missing av's please send at least one over`,
|
||||
data: [],
|
||||
};
|
||||
}
|
||||
|
||||
const { data, error } = (await tryCatch(
|
||||
query(
|
||||
productionNumbers
|
||||
.replace("[articles]", avs)
|
||||
.replace("[startDate]", startDate)
|
||||
.replace("[endDate]", endDate),
|
||||
"PSI production info"
|
||||
)
|
||||
)) as any;
|
||||
|
||||
if (error) {
|
||||
createLog(
|
||||
"error",
|
||||
"datamart",
|
||||
"datamart",
|
||||
`There was an error getting the planning info: ${JSON.stringify(
|
||||
error
|
||||
)}`
|
||||
);
|
||||
return {
|
||||
success: false,
|
||||
messsage: `There was an error getting the planning info`,
|
||||
data: error,
|
||||
};
|
||||
}
|
||||
|
||||
articles = data.data;
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: "PSI planning Data",
|
||||
data: articles.map((n: any) => {
|
||||
if (n.PalDay) {
|
||||
return { ...n, PalDay: n.PalDay.toFixed(2) };
|
||||
}
|
||||
|
||||
return n;
|
||||
}),
|
||||
};
|
||||
};
|
||||
@@ -9,6 +9,10 @@ import fakeEDI from "./route/fakeEDI.js";
|
||||
import addressCorrections from "./route/getCityStateData.js";
|
||||
import fifoIndex from "./route/getFifoIndex.js";
|
||||
import financeAudit from "./route/getFinanceAudit.js";
|
||||
import psiArticleData from "./route/getPsiArticleData.js";
|
||||
import psiPlanningData from "./route/getPsiPlanningData.js";
|
||||
import psiProductionData from "./route/getPsiProductionData.js";
|
||||
import psiInventory from "./route/getPsiinventory.js";
|
||||
|
||||
const app = new OpenAPIHono();
|
||||
|
||||
@@ -23,6 +27,10 @@ const routes = [
|
||||
addressCorrections,
|
||||
fifoIndex,
|
||||
financeAudit,
|
||||
psiArticleData,
|
||||
psiPlanningData,
|
||||
psiProductionData,
|
||||
psiInventory,
|
||||
] as const;
|
||||
|
||||
const appRoutes = routes.forEach((route) => {
|
||||
|
||||
61
server/services/dataMart/route/getPsiArticleData.ts
Normal file
61
server/services/dataMart/route/getPsiArticleData.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
|
||||
import { responses } from "../../../globalUtils/routeDefs/responses.js";
|
||||
import { tryCatch } from "../../../globalUtils/tryCatch.js";
|
||||
import { getDeliveryByDateRange } from "../controller/getDeliveryByDateRange.js";
|
||||
import { apiHit } from "../../../globalUtils/apiHits.js";
|
||||
import { getGetPSIArticleData } from "../controller/psiGetArticleData.js";
|
||||
|
||||
const app = new OpenAPIHono({ strict: false });
|
||||
const Body = z.object({
|
||||
includeRunnningNumbers: z.string().openapi({ example: "x" }),
|
||||
});
|
||||
app.openapi(
|
||||
createRoute({
|
||||
tags: ["dataMart"],
|
||||
summary: "Returns the psiarticleData.",
|
||||
method: "get",
|
||||
path: "/psiarticledata",
|
||||
request: {
|
||||
body: {
|
||||
content: {
|
||||
"application/json": { schema: Body },
|
||||
},
|
||||
},
|
||||
},
|
||||
responses: responses(),
|
||||
}),
|
||||
async (c) => {
|
||||
const articles: any = c.req.queries();
|
||||
|
||||
// make sure we have a vaid user being accessed thats really logged in
|
||||
apiHit(c, { endpoint: "/psiarticledata" });
|
||||
//console.log(articles["avs"][0]);
|
||||
const { data, error } = await tryCatch(
|
||||
getGetPSIArticleData(articles ? articles["avs"][0] : null)
|
||||
);
|
||||
|
||||
if (error) {
|
||||
console.log(error);
|
||||
return c.json(
|
||||
{
|
||||
success: false,
|
||||
message: "There was an error getting the articles.",
|
||||
data: error,
|
||||
},
|
||||
400
|
||||
);
|
||||
}
|
||||
|
||||
//console.log(data);
|
||||
|
||||
return c.json(
|
||||
{
|
||||
success: data.success,
|
||||
message: data.message,
|
||||
data: data.data,
|
||||
},
|
||||
data.success ? 200 : 400
|
||||
);
|
||||
}
|
||||
);
|
||||
export default app;
|
||||
64
server/services/dataMart/route/getPsiPlanningData.ts
Normal file
64
server/services/dataMart/route/getPsiPlanningData.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
|
||||
import { responses } from "../../../globalUtils/routeDefs/responses.js";
|
||||
import { tryCatch } from "../../../globalUtils/tryCatch.js";
|
||||
import { apiHit } from "../../../globalUtils/apiHits.js";
|
||||
import { psiGetPlanningData } from "../controller/psiGetPlanningData.js";
|
||||
|
||||
const app = new OpenAPIHono({ strict: false });
|
||||
const Body = z.object({
|
||||
includeRunnningNumbers: z.string().openapi({ example: "x" }),
|
||||
});
|
||||
app.openapi(
|
||||
createRoute({
|
||||
tags: ["dataMart"],
|
||||
summary: "Returns the psiarticleData.",
|
||||
method: "get",
|
||||
path: "/psiplanningdata",
|
||||
request: {
|
||||
body: {
|
||||
content: {
|
||||
"application/json": { schema: Body },
|
||||
},
|
||||
},
|
||||
},
|
||||
responses: responses(),
|
||||
}),
|
||||
async (c) => {
|
||||
const q: any = c.req.queries();
|
||||
|
||||
// make sure we have a vaid user being accessed thats really logged in
|
||||
apiHit(c, { endpoint: "/psiplanningdata" });
|
||||
//console.log(articles["avs"][0]);
|
||||
const { data, error } = await tryCatch(
|
||||
psiGetPlanningData(
|
||||
q["avs"] ? q["avs"][0] : null,
|
||||
q["startDate"] ? q["startDate"][0] : null,
|
||||
q["endDate"] ? q["endDate"][0] : null
|
||||
)
|
||||
);
|
||||
|
||||
if (error) {
|
||||
console.log(error);
|
||||
return c.json(
|
||||
{
|
||||
success: false,
|
||||
message: "There was an error getting the planning.",
|
||||
data: error,
|
||||
},
|
||||
400
|
||||
);
|
||||
}
|
||||
|
||||
//console.log(data);
|
||||
|
||||
return c.json(
|
||||
{
|
||||
success: data.success,
|
||||
message: data.message,
|
||||
data: data.data,
|
||||
},
|
||||
data.success ? 200 : 400
|
||||
);
|
||||
}
|
||||
);
|
||||
export default app;
|
||||
64
server/services/dataMart/route/getPsiProductionData.ts
Normal file
64
server/services/dataMart/route/getPsiProductionData.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
|
||||
import { responses } from "../../../globalUtils/routeDefs/responses.js";
|
||||
import { tryCatch } from "../../../globalUtils/tryCatch.js";
|
||||
import { apiHit } from "../../../globalUtils/apiHits.js";
|
||||
import { psiGetProductionData } from "../controller/psiGetProductionData.js";
|
||||
|
||||
const app = new OpenAPIHono({ strict: false });
|
||||
const Body = z.object({
|
||||
includeRunnningNumbers: z.string().openapi({ example: "x" }),
|
||||
});
|
||||
app.openapi(
|
||||
createRoute({
|
||||
tags: ["dataMart"],
|
||||
summary: "Returns the psiproductiondata.",
|
||||
method: "get",
|
||||
path: "/psiproductiondata",
|
||||
request: {
|
||||
body: {
|
||||
content: {
|
||||
"application/json": { schema: Body },
|
||||
},
|
||||
},
|
||||
},
|
||||
responses: responses(),
|
||||
}),
|
||||
async (c) => {
|
||||
const q: any = c.req.queries();
|
||||
|
||||
// make sure we have a vaid user being accessed thats really logged in
|
||||
apiHit(c, { endpoint: "/psiproductiondata" });
|
||||
//console.log(articles["avs"][0]);
|
||||
const { data, error } = await tryCatch(
|
||||
psiGetProductionData(
|
||||
q["avs"] ? q["avs"][0] : null,
|
||||
q["startDate"] ? q["startDate"][0] : null,
|
||||
q["endDate"] ? q["endDate"][0] : null
|
||||
)
|
||||
);
|
||||
|
||||
if (error) {
|
||||
console.log(error);
|
||||
return c.json(
|
||||
{
|
||||
success: false,
|
||||
message: "There was an error getting the production.",
|
||||
data: error,
|
||||
},
|
||||
400
|
||||
);
|
||||
}
|
||||
|
||||
//console.log(data);
|
||||
|
||||
return c.json(
|
||||
{
|
||||
success: data.success,
|
||||
message: data.message,
|
||||
data: data.data,
|
||||
},
|
||||
data.success ? 200 : 400
|
||||
);
|
||||
}
|
||||
);
|
||||
export default app;
|
||||
64
server/services/dataMart/route/getPsiinventory.ts
Normal file
64
server/services/dataMart/route/getPsiinventory.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
|
||||
import { responses } from "../../../globalUtils/routeDefs/responses.js";
|
||||
import { tryCatch } from "../../../globalUtils/tryCatch.js";
|
||||
import { apiHit } from "../../../globalUtils/apiHits.js";
|
||||
import { psiGetInventory } from "../controller/psiGetInventory.js";
|
||||
|
||||
const app = new OpenAPIHono({ strict: false });
|
||||
const Body = z.object({
|
||||
includeRunnningNumbers: z.string().openapi({ example: "x" }),
|
||||
});
|
||||
app.openapi(
|
||||
createRoute({
|
||||
tags: ["dataMart"],
|
||||
summary: "Returns the getPsiinventory.",
|
||||
method: "get",
|
||||
path: "/getpsiinventory",
|
||||
request: {
|
||||
body: {
|
||||
content: {
|
||||
"application/json": { schema: Body },
|
||||
},
|
||||
},
|
||||
},
|
||||
responses: responses(),
|
||||
}),
|
||||
async (c) => {
|
||||
const q: any = c.req.queries();
|
||||
|
||||
// make sure we have a vaid user being accessed thats really logged in
|
||||
apiHit(c, { endpoint: "/getpsiinventory" });
|
||||
//console.log(articles["avs"][0]);
|
||||
const { data, error } = await tryCatch(
|
||||
psiGetInventory(
|
||||
q["avs"] ? q["avs"][0] : null,
|
||||
q["startDate"] ? q["startDate"][0] : null,
|
||||
q["endDate"] ? q["endDate"][0] : null
|
||||
)
|
||||
);
|
||||
|
||||
if (error) {
|
||||
console.log(error);
|
||||
return c.json(
|
||||
{
|
||||
success: false,
|
||||
message: "There was an error getting the production.",
|
||||
data: error,
|
||||
},
|
||||
400
|
||||
);
|
||||
}
|
||||
|
||||
//console.log(data);
|
||||
|
||||
return c.json(
|
||||
{
|
||||
success: data.success,
|
||||
message: data.message,
|
||||
data: data.data,
|
||||
},
|
||||
data.success ? 200 : 400
|
||||
);
|
||||
}
|
||||
);
|
||||
export default app;
|
||||
32
server/services/eom/controller/getHistoricalInvByDate.ts
Normal file
32
server/services/eom/controller/getHistoricalInvByDate.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import { eq } from "drizzle-orm";
|
||||
import { db } from "../../../../database/dbclient.js";
|
||||
import { invHistoricalData } from "../../../../database/schema/historicalINV.js";
|
||||
import { tryCatch } from "../../../globalUtils/tryCatch.js";
|
||||
import { format } from "date-fns";
|
||||
|
||||
export const historicalInvByDate = async (date: string) => {
|
||||
const histDate = new Date(date);
|
||||
|
||||
const { data, error } = (await tryCatch(
|
||||
db
|
||||
.select()
|
||||
.from(invHistoricalData)
|
||||
.where(
|
||||
eq(invHistoricalData.histDate, format(histDate, "yyyy-MM-dd"))
|
||||
)
|
||||
)) as any;
|
||||
|
||||
if (error) {
|
||||
return {
|
||||
success: false,
|
||||
message: "There was an error with getting the inventory",
|
||||
data: error,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: `Historical inventory for ${date}`,
|
||||
data: data,
|
||||
};
|
||||
};
|
||||
24
server/services/eom/controller/getLastPurchasesPrice.ts
Normal file
24
server/services/eom/controller/getLastPurchasesPrice.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { tryCatch } from "../../../globalUtils/tryCatch.js";
|
||||
import { format } from "date-fns";
|
||||
import { query } from "../../sqlServer/prodSqlServer.js";
|
||||
import { lastPurchasePrice } from "../../sqlServer/querys/eom/lstPurchasePrice.js";
|
||||
|
||||
export const lastPurchase = async () => {
|
||||
const { data, error } = (await tryCatch(
|
||||
query(lastPurchasePrice, "Last purchase price")
|
||||
)) as any;
|
||||
|
||||
if (error) {
|
||||
return {
|
||||
success: false,
|
||||
message: "Error getting the last purchase price",
|
||||
data: error,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: `Last purchase price for all av in the last 5 years`,
|
||||
data: data.data,
|
||||
};
|
||||
};
|
||||
23
server/services/eom/controller/getLastestSalesPrice.ts
Normal file
23
server/services/eom/controller/getLastestSalesPrice.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { tryCatch } from "../../../globalUtils/tryCatch.js";
|
||||
import { query } from "../../sqlServer/prodSqlServer.js";
|
||||
import { lastSalesPriceCheck } from "../../sqlServer/querys/eom/lastSalesprice.js";
|
||||
|
||||
export const lastSales = async (date: string) => {
|
||||
const { data, error } = (await tryCatch(
|
||||
query(lastSalesPriceCheck.replace("[date]", date), "Last sales price")
|
||||
)) as any;
|
||||
|
||||
if (error) {
|
||||
return {
|
||||
success: false,
|
||||
message: "Error getting the last sales price",
|
||||
data: error,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: `Last sales price for all av in the last 5 years`,
|
||||
data: data.data,
|
||||
};
|
||||
};
|
||||
@@ -1,15 +0,0 @@
|
||||
// import {prisma} from "database";
|
||||
// import {createLog} from "logging";
|
||||
|
||||
// export const deleteHistory = async (date: string) => {
|
||||
// // delete the inventory if it equals this date
|
||||
// try {
|
||||
// const remove = await prisma.$executeRaw`
|
||||
// DELETE FROM historyInventory
|
||||
// WHERE histDate < ${date}
|
||||
// `;
|
||||
// createLog("general/eom", "info", `${remove} were just remove from the historical inventory for date: ${date}`);
|
||||
// } catch (error) {
|
||||
// createLog("general/eom", "error", `Removing historical inventory error: ${error}`);
|
||||
// }
|
||||
// };
|
||||
@@ -4,10 +4,48 @@ const app = new OpenAPIHono();
|
||||
|
||||
import stats from "./route/stats.js";
|
||||
import history from "./route/invHistory.js";
|
||||
const routes = [stats, history] as const;
|
||||
import { createJob } from "../notifications/utils/processNotifications.js";
|
||||
import { historicalInvIMmport } from "./utils/historicalInv.js";
|
||||
import { tryCatch } from "../../globalUtils/tryCatch.js";
|
||||
import { query } from "../sqlServer/prodSqlServer.js";
|
||||
import { shiftChange } from "../sqlServer/querys/misc/shiftChange.js";
|
||||
import { createLog } from "../logger/logger.js";
|
||||
import lastPurch from "./route/getLastPurchPrice.js";
|
||||
import lastSales from "./route/getLastSalesPrice.js";
|
||||
|
||||
const routes = [stats, history, lastPurch, lastSales] as const;
|
||||
|
||||
const appRoutes = routes.forEach((route) => {
|
||||
app.route("/eom", route);
|
||||
});
|
||||
|
||||
setTimeout(async () => {
|
||||
const { data: shift, error: shiftError } = (await tryCatch(
|
||||
query(shiftChange, "shift change from material.")
|
||||
)) as any;
|
||||
|
||||
if (shiftError) {
|
||||
createLog(
|
||||
"error",
|
||||
"eom",
|
||||
"eom",
|
||||
"There was an error getting the shift times will use fallback times"
|
||||
);
|
||||
}
|
||||
|
||||
// shift split
|
||||
const shiftTimeSplit = shift?.data[0]?.shiftChange.split(":");
|
||||
|
||||
const cronSetup = `${
|
||||
shiftTimeSplit?.length > 0 ? `${parseInt(shiftTimeSplit[1])}` : "0"
|
||||
} ${
|
||||
shiftTimeSplit?.length > 0 ? `${parseInt(shiftTimeSplit[0])}` : "7"
|
||||
} * * *`;
|
||||
|
||||
//console.log(cronSetup);
|
||||
createJob("eom_historical_inv", cronSetup, historicalInvIMmport);
|
||||
}, 5 * 1000);
|
||||
// the time we want to run the hostircal data should be the same time the historical data run on the server
|
||||
// getting this from the shift time
|
||||
|
||||
export default app;
|
||||
|
||||
41
server/services/eom/route/getLastPurchPrice.ts
Normal file
41
server/services/eom/route/getLastPurchPrice.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
|
||||
import { apiHit } from "../../../globalUtils/apiHits.js";
|
||||
import { responses } from "../../../globalUtils/routeDefs/responses.js";
|
||||
|
||||
import { lastPurchase } from "../controller/getLastPurchasesPrice.js";
|
||||
|
||||
const app = new OpenAPIHono({ strict: false });
|
||||
|
||||
app.openapi(
|
||||
createRoute({
|
||||
tags: ["eom"],
|
||||
summary: "Returns last sales price.",
|
||||
method: "get",
|
||||
path: "/lastpurchprice",
|
||||
responses: responses(),
|
||||
}),
|
||||
async (c) => {
|
||||
//const body = await c.req.json();
|
||||
// make sure we have a vaid user being accessed thats really logged in
|
||||
|
||||
apiHit(c, { endpoint: "/lastpurchprice" });
|
||||
try {
|
||||
const res = await lastPurchase();
|
||||
|
||||
return c.json(
|
||||
{ success: res.success, message: res.message, data: res.data },
|
||||
200
|
||||
);
|
||||
} catch (error) {
|
||||
return c.json(
|
||||
{
|
||||
success: false,
|
||||
message: "There was an error posting the eom stat.",
|
||||
data: error,
|
||||
},
|
||||
400
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
export default app;
|
||||
43
server/services/eom/route/getLastSalesPrice.ts
Normal file
43
server/services/eom/route/getLastSalesPrice.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
|
||||
import { apiHit } from "../../../globalUtils/apiHits.js";
|
||||
import { responses } from "../../../globalUtils/routeDefs/responses.js";
|
||||
|
||||
import { lastPurchase } from "../controller/getLastPurchasesPrice.js";
|
||||
import { lastSales } from "../controller/getLastestSalesPrice.js";
|
||||
|
||||
const app = new OpenAPIHono({ strict: false });
|
||||
|
||||
app.openapi(
|
||||
createRoute({
|
||||
tags: ["eom"],
|
||||
summary: "Returns last sales price.",
|
||||
method: "get",
|
||||
path: "/lastsalesprice",
|
||||
responses: responses(),
|
||||
}),
|
||||
async (c) => {
|
||||
//const body = await c.req.json();
|
||||
const month: string = c.req.query("month") ?? "";
|
||||
// make sure we have a vaid user being accessed thats really logged in
|
||||
|
||||
apiHit(c, { endpoint: "/lastsalesprice" });
|
||||
try {
|
||||
const res = await lastSales(month);
|
||||
|
||||
return c.json(
|
||||
{ success: res.success, message: res.message, data: res.data },
|
||||
200
|
||||
);
|
||||
} catch (error) {
|
||||
return c.json(
|
||||
{
|
||||
success: false,
|
||||
message: "There was an error posting the eom stat.",
|
||||
data: error,
|
||||
},
|
||||
400
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
export default app;
|
||||
@@ -1,6 +1,7 @@
|
||||
import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
|
||||
import { apiHit } from "../../../globalUtils/apiHits.js";
|
||||
import { responses } from "../../../globalUtils/routeDefs/responses.js";
|
||||
import { historicalInvByDate } from "../controller/getHistoricalInvByDate.js";
|
||||
|
||||
const app = new OpenAPIHono({ strict: false });
|
||||
const EomStat = z.object({
|
||||
@@ -12,20 +13,24 @@ const EomStat = z.object({
|
||||
app.openapi(
|
||||
createRoute({
|
||||
tags: ["eom"],
|
||||
summary: "Gets the correct eom history.",
|
||||
method: "post",
|
||||
summary: "Gets History Data by date.",
|
||||
method: "get",
|
||||
path: "/histinv",
|
||||
request: {
|
||||
params: EomStat,
|
||||
},
|
||||
responses: responses(),
|
||||
}),
|
||||
async (c) => {
|
||||
//const body = await c.req.json();
|
||||
// make sure we have a vaid user being accessed thats really logged in
|
||||
const month: string = c.req.query("month") ?? "";
|
||||
|
||||
apiHit(c, { endpoint: "/histinv" });
|
||||
try {
|
||||
return c.json({ success: true, message: "", data: [] }, 200);
|
||||
const res = await historicalInvByDate(month);
|
||||
|
||||
return c.json(
|
||||
{ success: res.success, message: res.message, data: res.data },
|
||||
200
|
||||
);
|
||||
} catch (error) {
|
||||
return c.json(
|
||||
{
|
||||
|
||||
114
server/services/eom/utils/historicalInv.ts
Normal file
114
server/services/eom/utils/historicalInv.ts
Normal file
@@ -0,0 +1,114 @@
|
||||
import { sql } from "drizzle-orm";
|
||||
import { db } from "../../../../database/dbclient.js";
|
||||
import { invHistoricalData } from "../../../../database/schema/historicalINV.js";
|
||||
import { tryCatch } from "../../../globalUtils/tryCatch.js";
|
||||
import { createLog } from "../../logger/logger.js";
|
||||
import { query } from "../../sqlServer/prodSqlServer.js";
|
||||
import { totalInvNoRn } from "../../sqlServer/querys/dataMart/totalINV.js";
|
||||
import { format } from "date-fns-tz";
|
||||
import { serverSettings } from "../../server/controller/settings/getSettings.js";
|
||||
import { deleteHistory } from "./removeHistorical.js";
|
||||
import { activeArticle } from "../../sqlServer/querys/dataMart/article.js";
|
||||
|
||||
export const historicalInvIMmport = async () => {
|
||||
const plantToken = serverSettings.filter((n) => n.name === "plantToken");
|
||||
const { data, error } = (await tryCatch(
|
||||
db.select().from(invHistoricalData)
|
||||
)) as any;
|
||||
|
||||
if (error) {
|
||||
createLog(
|
||||
"error",
|
||||
"eom",
|
||||
"eom",
|
||||
`There was an error getting the historical data`
|
||||
);
|
||||
}
|
||||
// check if we have data already for today this way we dont duplicate anything.
|
||||
const today = new Date();
|
||||
today.setDate(today.getDate() - 1);
|
||||
|
||||
const dateCheck = data?.filter(
|
||||
(i: any) => i.histDate === format(today, "yyyy-MM-dd")
|
||||
);
|
||||
|
||||
if (dateCheck.length === 0) {
|
||||
// get the historical data from the sql
|
||||
const { data: inv, error: invError } = (await tryCatch(
|
||||
query(totalInvNoRn, "eom historical data")
|
||||
)) as any;
|
||||
|
||||
if (invError) {
|
||||
createLog(
|
||||
"error",
|
||||
"eom",
|
||||
"eom",
|
||||
`There was an error getting the sql data`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (inv.data.length === 0) {
|
||||
createLog("error", "eom", "eom", inv.message);
|
||||
return;
|
||||
}
|
||||
|
||||
const { data: articles, error: avError } = (await tryCatch(
|
||||
query(activeArticle, "Get active articles")
|
||||
)) as any;
|
||||
|
||||
const av = articles.data.length > 0 ? articles.data : ([] as any);
|
||||
|
||||
const importInv = inv.data ? inv.data : [];
|
||||
const eomImportData = importInv.map((i: any) => {
|
||||
return {
|
||||
histDate: sql`(NOW() - INTERVAL '1 day')::date`,
|
||||
plantToken: plantToken[0].value,
|
||||
article: i.av,
|
||||
articleDescription: i.Alias,
|
||||
materialType:
|
||||
av.filter((a: any) => a.IdArtikelvarianten === i.av)
|
||||
.length > 0
|
||||
? av.filter(
|
||||
(a: any) => a.IdArtikelvarianten === i.av
|
||||
)[0]?.TypeOfMaterial
|
||||
: "Item not defined",
|
||||
total_QTY: i.Total_PalletQTY,
|
||||
avaliable_QTY: i.Avaliable_PalletQTY,
|
||||
coa_QTY: i.COA_QTY,
|
||||
held_QTY: i.Held_QTY,
|
||||
consignment: i.Consigment,
|
||||
lot_Number: i.lot,
|
||||
};
|
||||
});
|
||||
|
||||
const { data: dataImport, error: errorImport } = await tryCatch(
|
||||
db.insert(invHistoricalData).values(eomImportData)
|
||||
);
|
||||
|
||||
if (errorImport) {
|
||||
createLog(
|
||||
"error",
|
||||
"eom",
|
||||
"eom",
|
||||
`There was an error importing all the inventory data.`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (dataImport) {
|
||||
createLog(
|
||||
"info",
|
||||
"eom",
|
||||
"eom",
|
||||
`All data was imported succefully.`
|
||||
);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
createLog("info", "eom", "eom", `Yesterdays Data already in..`);
|
||||
}
|
||||
|
||||
// do the check to delete old data
|
||||
deleteHistory();
|
||||
};
|
||||
51
server/services/eom/utils/removeHistorical.ts
Normal file
51
server/services/eom/utils/removeHistorical.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
// import {prisma} from "database";
|
||||
// import {createLog} from "logging";
|
||||
|
||||
import { lte, sql } from "drizzle-orm";
|
||||
import { db } from "../../../../database/dbclient.js";
|
||||
import { invHistoricalData } from "../../../../database/schema/historicalINV.js";
|
||||
import { tryCatch } from "../../../globalUtils/tryCatch.js";
|
||||
import { createLog } from "../../logger/logger.js";
|
||||
|
||||
// export const deleteHistory = async (date: string) => {
|
||||
// // delete the inventory if it equals this date
|
||||
// try {
|
||||
// const remove = await prisma.$executeRaw`
|
||||
// DELETE FROM historyInventory
|
||||
// WHERE histDate < ${date}
|
||||
// `;
|
||||
// createLog("general/eom", "info", `${remove} were just remove from the historical inventory for date: ${date}`);
|
||||
// } catch (error) {
|
||||
// createLog("general/eom", "error", `Removing historical inventory error: ${error}`);
|
||||
// }
|
||||
// };
|
||||
|
||||
export const deleteHistory = async () => {
|
||||
const { data, error } = await tryCatch(
|
||||
db
|
||||
.delete(invHistoricalData)
|
||||
.where(
|
||||
lte(
|
||||
invHistoricalData.histDate,
|
||||
sql`(NOW() - INTERVAL '365 day')::date`
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
if (error) {
|
||||
createLog(
|
||||
"error",
|
||||
"eom",
|
||||
"eom",
|
||||
"There was an error deleting the historical data."
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
createLog(
|
||||
"info",
|
||||
"eom",
|
||||
"eom",
|
||||
"Data older than 45 days has been deleted."
|
||||
);
|
||||
};
|
||||
@@ -1,11 +1,13 @@
|
||||
import {OpenAPIHono} from "@hono/zod-openapi";
|
||||
import {apiReference} from "@scalar/hono-api-reference";
|
||||
import {settings} from "../../../../database/schema/settings.js";
|
||||
import {db} from "../../../../database/dbclient.js";
|
||||
import {eq} from "drizzle-orm";
|
||||
import { OpenAPIHono } from "@hono/zod-openapi";
|
||||
import { apiReference } from "@scalar/hono-api-reference";
|
||||
import { settings } from "../../../../database/schema/settings.js";
|
||||
import { db } from "../../../../database/dbclient.js";
|
||||
import { eq } from "drizzle-orm";
|
||||
import { getSettings } from "../../server/controller/settings/getSettings.js";
|
||||
|
||||
const app = new OpenAPIHono();
|
||||
const plantToken = await db.select().from(settings).where(eq(settings.name, "plantToken"));
|
||||
const serverSettings = await getSettings();
|
||||
const plantToken = serverSettings.filter((n) => n.name === "plantToken") as any; //await db.select().from(settings).where(eq(settings.name, "plantToken"));
|
||||
|
||||
let pToken = plantToken[0]?.value;
|
||||
const testServers = ["test1", "test2", "test3"];
|
||||
@@ -17,7 +19,7 @@ app.get(
|
||||
apiReference({
|
||||
theme: "kepler",
|
||||
layout: "classic",
|
||||
defaultHttpClient: {targetKey: "node", clientKey: "axios"},
|
||||
defaultHttpClient: { targetKey: "node", clientKey: "axios" },
|
||||
pageTitle: "Lst API Reference",
|
||||
hiddenClients: [
|
||||
"libcurl",
|
||||
|
||||
50
server/services/logger/goTransport.ts
Normal file
50
server/services/logger/goTransport.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
|
||||
import axios from "axios";
|
||||
import { pino } from "pino";
|
||||
import build from "pino-abstract-transport";
|
||||
import { tryCatch } from "../../globalUtils/tryCatch.js";
|
||||
|
||||
const pinoLogLevels: any = {
|
||||
10: "trace",
|
||||
20: "debug",
|
||||
30: "info",
|
||||
40: "warn",
|
||||
50: "error",
|
||||
60: "fatal",
|
||||
};
|
||||
|
||||
export default async function buildGoTransport() {
|
||||
try {
|
||||
return build(async function (source) {
|
||||
for await (let obj of source) {
|
||||
// Insert log entry into the PostgreSQL database using Drizzle ORM
|
||||
|
||||
// convert to the name to make it more easy to find later :P
|
||||
const levelName = pinoLogLevels[obj.level] || "unknown";
|
||||
// await db.insert(logs).values({
|
||||
// level: levelName,
|
||||
// username: obj?.username.toLowerCase(),
|
||||
// service: obj?.service.toLowerCase(),
|
||||
// message: obj.msg,
|
||||
// });
|
||||
const { data, error } = (await tryCatch(
|
||||
axios.post(`${process.env.LST_BASE_URL}/api/v1/log`, {
|
||||
service: obj?.service.toLowerCase(),
|
||||
level: levelName,
|
||||
message: obj.msg,
|
||||
})
|
||||
)) as any;
|
||||
|
||||
if (error) {
|
||||
console.log(
|
||||
"The go server must be offline so we cant post the new logs."
|
||||
);
|
||||
}
|
||||
|
||||
// console.log(`Go log level: ${levelName}`);
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
console.error("Error inserting log into database:", err);
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,14 @@ const transport = pino.transport({
|
||||
{
|
||||
target: "./dbTransport.js",
|
||||
},
|
||||
// Only log to Go if LST_USE_GO=true
|
||||
...(process.env.LST_USE_GO === "true"
|
||||
? [
|
||||
{
|
||||
target: "./goTransport.js", // New transport for Go
|
||||
},
|
||||
]
|
||||
: []),
|
||||
],
|
||||
});
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import {OpenAPIHono} from "@hono/zod-openapi";
|
||||
import { OpenAPIHono } from "@hono/zod-openapi";
|
||||
|
||||
// routes
|
||||
import clearLog from "./routes/clearLog.js";
|
||||
import {db} from "../../../database/dbclient.js";
|
||||
import {settings} from "../../../database/schema/settings.js";
|
||||
import {logCleanup} from "./controller/logCleanup.js";
|
||||
import { db } from "../../../database/dbclient.js";
|
||||
import { settings } from "../../../database/schema/settings.js";
|
||||
import { logCleanup } from "./controller/logCleanup.js";
|
||||
import createNewLog from "./routes/createLog.js";
|
||||
import getLogs from "./routes/getLogs.js";
|
||||
import stream from "./routes/streamLogs.js";
|
||||
@@ -12,14 +12,17 @@ import stream from "./routes/streamLogs.js";
|
||||
const app = new OpenAPIHono();
|
||||
|
||||
const routes = [clearLog, createNewLog, getLogs, stream] as const;
|
||||
const setting = await db.select().from(settings);
|
||||
//const setting = await db.select().from(settings);
|
||||
|
||||
const appRoutes = routes.forEach((route) => {
|
||||
app.route("/logger", route);
|
||||
});
|
||||
|
||||
app.all("/logger/*", (c) => {
|
||||
return c.json({success: false, message: "You have encounters a log route that dose not exist."});
|
||||
return c.json({
|
||||
success: false,
|
||||
message: "You have encounters a log route that dose not exist.",
|
||||
});
|
||||
});
|
||||
|
||||
// run the clean up job ones on server restart/crash/update and then once a date
|
||||
|
||||
@@ -7,6 +7,8 @@ import { postForecast } from "../postForecast.js";
|
||||
import { query } from "../../../../../sqlServer/prodSqlServer.js";
|
||||
import { activeArticle } from "../../../../../sqlServer/querys/dataMart/article.js";
|
||||
import { addDays } from "date-fns";
|
||||
import { sendEmail } from "../../../../../notifications/controller/sendMail.js";
|
||||
import { createLog } from "../../../../../logger/logger.js";
|
||||
|
||||
let customerID = 4;
|
||||
export const lorealForecast = async (data: any, user: any) => {
|
||||
@@ -111,6 +113,9 @@ export const lorealForecast = async (data: any, user: any) => {
|
||||
);
|
||||
|
||||
if (activeAV.length === 0) {
|
||||
if (typeof forcast.customerArticleNo === "number") {
|
||||
missingSku.push(forcast);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -181,6 +186,9 @@ export const lorealForecast = async (data: any, user: any) => {
|
||||
);
|
||||
|
||||
if (activeAV.length === 0) {
|
||||
if (typeof forcast.customerArticleNo === "number") {
|
||||
missingSku.push(forcast);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -190,6 +198,56 @@ export const lorealForecast = async (data: any, user: any) => {
|
||||
|
||||
//console.log(comForecast);
|
||||
|
||||
// email the for the missing ones
|
||||
const missedGrouped = Object.values(
|
||||
missingSku.reduce((acc: any, item: any) => {
|
||||
const key = item.customerArticleNo;
|
||||
|
||||
if (!acc[key]) {
|
||||
// first time we see this customer
|
||||
acc[key] = item;
|
||||
} else {
|
||||
// compare dates and keep the earliest
|
||||
if (
|
||||
new Date(item.requirementDate) <
|
||||
new Date(acc[key].requirementDate)
|
||||
) {
|
||||
acc[key] = item;
|
||||
}
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, {})
|
||||
);
|
||||
|
||||
const emailSetup = {
|
||||
email: "Blake.matthes@alpla.com; Stuart.Gladney@alpla.com; Harold.Mccalister@alpla.com; Jenn.Osbourn@alpla.com",
|
||||
subject:
|
||||
missedGrouped.length > 0
|
||||
? `Alert! There are ${missedGrouped.length}, missing skus.`
|
||||
: `Alert! There is a missing SKU.`,
|
||||
template: "missingLorealSkus",
|
||||
context: {
|
||||
items: missedGrouped,
|
||||
},
|
||||
};
|
||||
|
||||
const { data: sentEmail, error: sendEmailError } = await tryCatch(
|
||||
sendEmail(emailSetup)
|
||||
);
|
||||
if (sendEmailError) {
|
||||
createLog(
|
||||
"error",
|
||||
"blocking",
|
||||
"notify",
|
||||
"Failed to send email, will try again on next interval"
|
||||
);
|
||||
return {
|
||||
success: false,
|
||||
message: "Failed to send email, will try again on next interval",
|
||||
};
|
||||
}
|
||||
|
||||
// if the customerarticle number is not matching just ignore it
|
||||
const predefinedObject = {
|
||||
receivingPlantId: plantToken[0].value,
|
||||
|
||||
@@ -14,7 +14,8 @@ export const postForecast = async (data: any, user: any) => {
|
||||
url: endpoint,
|
||||
method: "POST",
|
||||
headers: {
|
||||
Authorization: `Basic ${user.prod}`,
|
||||
"X-API-Key": process.env.TEC_API_KEY || "",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
// if a body is sent over it would be like below
|
||||
data: data,
|
||||
|
||||
@@ -157,7 +157,7 @@ export const standardOrders = async (data: any, user: any) => {
|
||||
deliveryDate: excelDateStuff(o.DeliveryDate),
|
||||
customerLineItemNo: o.CustomerLineNumber, // this is how it is currently sent over from abbott
|
||||
customerReleaseNo: o.CustomerRealeaseNumber, // same as above
|
||||
remark: o.remark === "" ? null : o.remark,
|
||||
remark: o.Remark === "" ? null : o.Remark,
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -169,7 +169,7 @@ export const standardOrders = async (data: any, user: any) => {
|
||||
orders: [...predefinedObject.orders, ...nOrder],
|
||||
};
|
||||
|
||||
//console.log(updatedPredefinedObject);
|
||||
//console.log(updatedPredefinedObject.orders[0]);
|
||||
|
||||
// post the orders to the server
|
||||
const posting: any = await postOrders(updatedPredefinedObject, user);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import axios from "axios";
|
||||
import { prodEndpointCreation } from "../../../../../globalUtils/createUrl.js";
|
||||
import { createLog } from "../../../../logger/logger.js";
|
||||
|
||||
export const postOrders = async (data: any, user: any) => {
|
||||
let endpoint = await prodEndpointCreation(
|
||||
@@ -11,7 +12,8 @@ export const postOrders = async (data: any, user: any) => {
|
||||
url: endpoint,
|
||||
method: "POST",
|
||||
headers: {
|
||||
Authorization: `Basic ${user.prod}`,
|
||||
"X-API-Key": process.env.TEC_API_KEY || "",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
// if a body is sent over it would be like below
|
||||
data: data,
|
||||
@@ -19,6 +21,12 @@ export const postOrders = async (data: any, user: any) => {
|
||||
|
||||
//console.log(results.status);
|
||||
if (results.data.errors) {
|
||||
createLog(
|
||||
"error",
|
||||
user.username,
|
||||
"logisitcs",
|
||||
results.data.errors[0].message
|
||||
);
|
||||
return {
|
||||
success: true,
|
||||
message: "Error processing orders",
|
||||
@@ -27,6 +35,12 @@ export const postOrders = async (data: any, user: any) => {
|
||||
}
|
||||
|
||||
if (results.status === 200) {
|
||||
createLog(
|
||||
"info",
|
||||
user.username,
|
||||
"logisitcs",
|
||||
"Orders were processed please check 2.0 for validation and errors"
|
||||
);
|
||||
return {
|
||||
success: true,
|
||||
message: "Success on posting orders",
|
||||
@@ -37,6 +51,7 @@ export const postOrders = async (data: any, user: any) => {
|
||||
//console.log(`There is an error`, error);
|
||||
if (error) {
|
||||
//console.log(error.response.data);
|
||||
createLog("error", user.username, "logisitcs", error.response.data);
|
||||
return {
|
||||
success: false,
|
||||
message: "There was an error processing data",
|
||||
|
||||
@@ -13,10 +13,12 @@ type Data = {
|
||||
runningNr: string;
|
||||
lotNum: number;
|
||||
};
|
||||
export const consumeMaterial = async (data: Data, prod: any) => {
|
||||
export const consumeMaterial = async (data: Data) => {
|
||||
const { runningNr, lotNum } = data;
|
||||
// replace the rn
|
||||
|
||||
console.log(data);
|
||||
|
||||
const rnReplace = labelData.replaceAll("[rn]", runningNr);
|
||||
|
||||
let barcode;
|
||||
@@ -26,12 +28,7 @@ export const consumeMaterial = async (data: Data, prod: any) => {
|
||||
barcode = r?.data;
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
createLog(
|
||||
"error",
|
||||
prod.user.username,
|
||||
"logistics",
|
||||
`Error getting barcode: ${error}`
|
||||
);
|
||||
createLog("error", "", "logistics", `Error getting barcode: ${error}`);
|
||||
}
|
||||
|
||||
if (barcode.length === 0) {
|
||||
@@ -54,8 +51,8 @@ export const consumeMaterial = async (data: Data, prod: any) => {
|
||||
try {
|
||||
const results = await axios.post(url, consumeSomething, {
|
||||
headers: {
|
||||
"X-API-Key": process.env.TEC_API_KEY || "",
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Basic ${prod.user.prod}`,
|
||||
},
|
||||
});
|
||||
//console.log(results);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user