Compare commits

...

129 Commits

Author SHA1 Message Date
4d0ac713d3 fix logout page 2025-10-09 16:09:56 +05:30
gowthaman
109acd33f7 add header as same as crm app 2025-08-04 13:45:10 +05:30
gowthaman
b5db27fb09 some avs changes 2025-07-17 11:16:12 +05:30
gowthaman
7b39e64ae5 handle limit 2024-08-15 16:27:21 +05:30
gowthaman
bb169d6cc8 add expense limit config 2024-08-15 15:36:42 +05:30
gowthaman
8abe607c26 make script work 2024-08-15 15:29:13 +05:30
gowthaman
d19d322753 order by 2024-08-12 17:22:18 +05:30
gowthaman
7bab40e4fa if empty string, then remove from filter 2024-08-12 12:04:37 +05:30
gowthaman
c0b41eb040 add date in excel 2024-08-12 09:11:52 +05:30
gowthaman
d7097733e0 add mdn/mrn filter 2024-08-12 09:10:24 +05:30
gowthaman
dcf8d176b7 fix exccel qty 2024-08-02 17:34:27 +05:30
gowthaman
f111f4833a fix outward excel 2024-08-01 10:45:56 +05:30
gowthaman
59e9dd6e67 fix outward excel 2024-08-01 10:44:04 +05:30
gowthaman
66a5a20c0c add config for print 2024-07-30 11:18:24 +05:30
gowthaman
e4cbee9c16 add config for print 2024-07-30 11:11:47 +05:30
gowthaman
bbb8ae36b5 add po to download of inward and fix date format 2024-07-12 09:46:52 +05:30
gowthaman
08e99a6182 Merge remote-tracking branch 'origin/master' 2024-07-09 14:12:32 +05:30
gowthaman
7ff4de4deb handle partially issued qty 2024-07-09 14:12:11 +05:30
gowthaman
0bd70d3cf4 fix edit of outward and plant name 2024-07-04 18:50:41 +05:30
gowthaman
a7538ebc6d some changes 2024-07-02 15:05:36 +05:30
gowthaman
7a1d2fcf91 remove 2024-06-28 14:10:52 +05:30
gowthaman
0247d68b50 fix edit of outward and plant name 2024-06-26 12:24:14 +05:30
gowthaman
062365ed51 partial issue of job card 2024-06-25 10:38:59 +05:30
gowthaman
17d5422c43 po excel 2024-06-17 16:40:08 +05:30
gowthaman
0025d50677 remove log 2024-06-17 16:19:37 +05:30
gowthaman
954c9088e3 initial stock configuration 2024-06-15 17:54:59 +05:30
gowthaman
9aafe5a95c handle job 2024-06-15 10:37:52 +05:30
gowthaman
fc43e84302 po track 2024-06-11 17:46:02 +05:30
gowthaman
1e7db629ae keep track of PO 2024-06-11 17:02:16 +05:30
gowthaman
0d203a880f fix excel 2024-06-11 08:30:29 +05:30
gowthaman
68107ca7f0 remove locationgroup 2024-06-11 08:28:53 +05:30
gowthaman
2ee82a2eed add liter 2024-06-04 12:51:01 +05:30
gowthaman
75495afd73 add liter 2024-06-04 12:45:46 +05:30
gowthaman
9528134eda handle po filter in incoming 2024-06-01 13:53:49 +05:30
gowthaman
a9961027d1 add vendor po fix and edits 2024-05-31 16:47:45 +05:30
gowthaman
444648178a add missing fields 2024-05-29 12:13:06 +05:30
gowthaman
36bf1695ca add a job card 2024-05-28 14:03:53 +05:30
gowthaman
916cd7b275 add plant to out 2024-05-28 13:28:27 +05:30
gowthaman
ae539ae285 add plant to out 2024-05-28 13:14:30 +05:30
gowthaman
36c11fabc1 some more permission related things 2024-05-27 21:46:11 +05:30
gowthaman
915094e49f some more permission related things 2024-05-27 20:04:04 +05:30
gowthaman
c8d8458f8c fix save of config message 2024-05-27 19:03:09 +05:30
gowthaman
b27f2feadb fix plantIs save 2024-05-27 15:46:25 +05:30
gowthaman
7d3af46a22 filter by po qty and price 2024-05-27 15:30:24 +05:30
gowthaman
f576d33ec9 some re-arrange of menu 2024-05-27 14:34:32 +05:30
gowthaman
4e80a3d9f2 some changes 2024-05-27 12:52:14 +05:30
gowthaman
06637c21ee some changes for expense 2024-05-24 11:14:30 +05:30
gowthaman
0de3eb7142 add expense and up kotlin 2024-05-23 19:24:30 +05:30
gowthaman
470893165a verify keys before doing anything 2024-05-23 13:52:13 +05:30
gowthaman
8b3af1adc0 remove stuff 2024-05-23 13:41:33 +05:30
gowthaman
dad5844b27 reduce padding 2024-05-23 13:40:50 +05:30
gowthaman
d51c8c7547 use sequence per tenant 2024-05-23 13:40:28 +05:30
gowthaman
0c9313b77c remove unused stuff 2024-05-23 13:27:09 +05:30
gowthaman
ce622a118f move around classes 2024-05-23 12:53:52 +05:30
gowthaman
ed1e7546a7 add an all plant filter 2024-05-16 17:30:19 +05:30
gowthaman
cad5a647f1 plant wise filter 2024-05-16 17:27:05 +05:30
gowthaman
e68b24b557 plant wise filter 2024-05-16 17:15:00 +05:30
gowthaman
bcdf15a034 supress log 2024-05-10 11:47:30 +05:30
gowthaman
943f83a33f supress some log and add a log 2024-05-10 11:45:49 +05:30
gowthaman
3d20b09464 refractor 2024-05-09 14:19:26 +05:30
gowthaman
fc402e0232 readd 2024-05-09 14:18:18 +05:30
gowthaman
a889b48bd1 fix product edit 2024-05-09 14:16:09 +05:30
gowthaman
1e02deb1b1 fix product edit 2024-05-09 13:56:59 +05:30
gowthaman
7d99a11e14 fix product edit 2024-05-09 13:52:23 +05:30
gowthaman
711983f312 fix product edit 2024-05-09 13:48:44 +05:30
gowthaman
1c1a25ade9 set object mapper for ebean 2024-05-09 12:24:25 +05:30
gowthaman
ad7b3effcc fix plant search 2024-05-09 06:56:46 +05:30
gowthaman
d8d2caddfb fix logout 2024-05-08 14:30:29 +05:30
gowthaman.b
75c79ba02b keep track of history 2024-05-08 14:23:35 +05:30
gowthaman.b
18afa76d56 keep track of history 2024-05-08 14:20:49 +05:30
gowthaman.b
c4985f9690 address logout 2024-05-08 14:03:08 +05:30
gowthaman.b
0a0e627b4a update db 2024-05-08 13:56:04 +05:30
gowthaman.b
34a0c5f5bb supress logging 2024-05-08 13:30:56 +05:30
gowthaman.b
ef181bce1a keep track of auth token in db 2024-05-08 13:27:10 +05:30
gowthaman
182ad004fb filter as per unloading site 2024-05-08 10:50:12 +05:30
gowthaman
c80a347bef some changes 2024-05-08 07:39:40 +05:30
gowthaman
dac0fcb85b add a random id 2024-04-30 16:38:48 +05:30
gowthaman
9fb657a0fb add a random id 2024-04-30 16:38:36 +05:30
gowthaman
f2b05b229d handle prefixes 2024-04-30 12:21:57 +05:30
gowthaman
fb4f1a0995 handle prefixes 2024-04-30 11:08:21 +05:30
gowthaman
d17ea1da6a handle hasvendor/crm 2024-04-30 09:58:45 +05:30
gowthaman
712543ec3c add display, but no edit yet 2024-04-30 01:05:54 +05:30
gowthaman
ecf31b1b74 fix dropdown 2024-04-29 23:54:08 +05:30
gowthaman
ae11f05aba add vendor address list 2024-04-29 22:43:34 +05:30
gowthaman
4d7f5ccdfb more changes to inward inventory 2024-04-29 22:38:00 +05:30
gowthaman
d28a3215d1 add gst% 2024-04-29 19:49:53 +05:30
gowthaman
387e203bc8 format 2024-04-29 18:44:02 +05:30
gowthaman
82c66d4b3a sync plants 2024-04-29 18:39:56 +05:30
gowthaman
c96656086e up kotlin and change props 2024-04-29 18:25:06 +05:30
gowthaman
ade8901649 up kotlin and change props 2024-04-29 18:23:28 +05:30
gowthaman.b
3f4c53c6cc add some more cols 2024-04-28 19:30:30 +05:30
gowthaman.b
bb51bb448f disable some tasks 2024-04-28 19:14:56 +05:30
gowthaman.b
7ef110eaf7 increment incoming inventory count 2024-04-28 19:12:01 +05:30
gowthaman.b
a1751b8918 change product id type 2024-04-28 19:06:22 +05:30
gowthaman.b
3b5a0379cb remove the plantIds fetch 2024-03-20 15:12:18 +05:30
gowthaman.b
09bc25346d xls update 2024-03-20 15:09:20 +05:30
gowthaman.b
9aadfbbd24 xls update 2024-03-20 14:58:54 +05:30
gowthaman.b
38c6a32114 add locationGroup to tenant 2024-03-20 14:14:52 +05:30
gowthaman.b
932657c2b2 add plant integ 2024-03-20 14:04:26 +05:30
gowthaman.b
ce1f5f9b04 add plant integ 2024-03-20 14:01:22 +05:30
gowthaman.b
fc361e3133 save file changes 2024-03-19 17:31:19 +05:30
141d8961e4 add fleet 2024-03-18 16:07:01 +05:30
32d4ea28f5 add fleet 2024-03-12 17:56:11 +05:30
67b7c5aa9a add payment, invoice 2024-03-07 18:01:23 +05:30
be03724217 add payment, invoce 2024-03-06 18:31:03 +05:30
d9dcda0724 add payments, invoice 2024-03-06 15:59:14 +05:30
211e55a373 Merge branch 'arsalan'
# Conflicts:
#	src/main/kotlin/com/restapi/controllers/Excel.kt
#	src/main/kotlin/com/restapi/controllers/Filters.kt
#	src/main/kotlin/com/restapi/domain/models.kt
2024-03-05 10:29:30 +05:30
5144456721 fixes 2024-03-05 10:11:02 +05:30
d475d3828b Add inventory 2024-03-01 18:12:17 +05:30
f8263e8a35 add conf 2024-02-29 11:06:03 +05:30
bd46d5ae52 fix prod api 2024-02-28 15:48:19 +05:30
9633983dec fixes 2024-02-28 14:40:14 +05:30
9a60105ed3 add vendor module 2024-02-21 17:22:45 +05:30
gowthaman.b
338f86a5f5 some updates 2024-02-09 14:49:25 +05:30
fc94c7aacc delete api.log 2024-02-09 08:24:59 +00:00
6ca803d7f2 fix updates 2024-02-09 13:04:38 +05:30
gowthaman.b
40933a2713 some updates 2024-02-08 17:50:08 +05:30
gowthaman.b
d8a4483c3c add shadow plaugin 2024-02-08 15:42:18 +05:30
gowthaman.b
db7bdffe33 add shadow plaugin 2024-02-08 15:38:36 +05:30
e94928ae7f Merge branch 'master' of https://git.basuvaraj.com/gowthaman/readymixerp_modules_api
# Conflicts:
#	api.http
#	src/main/kotlin/com/restapi/Main.kt
#	src/main/kotlin/com/restapi/controllers/Entities.kt
2024-02-08 15:14:18 +05:30
4e069e0300 fixes 2024-02-08 13:01:03 +05:30
7e8f70a7c2 UI 2024-02-05 09:42:06 +05:30
4688b92314 ExcelToDb complete 2024-01-24 18:33:01 +05:30
471c043218 merge complete 2024-01-24 17:49:30 +05:30
d8ab337fde Merge remote-tracking branch 'origin/master'
# Conflicts:
#	build.gradle.kts
#	src/main/kotlin/com/restapi/Main.kt
#	src/main/kotlin/com/restapi/controllers/Excel.kt
#	src/main/kotlin/com/restapi/controllers/Filters.kt
2024-01-24 17:46:49 +05:30
b5c28dd288 add fixes 2024-01-24 17:40:20 +05:30
3cd5c4c471 add excel templates fix apis 2024-01-24 10:01:25 +05:30
2fecc4b3fd add excel import and export 2024-01-22 18:32:24 +05:30
0b75681236 add filters, excel 2024-01-19 18:27:44 +05:30
124 changed files with 4912 additions and 1192 deletions

9
.gitignore vendored
View File

@ -43,4 +43,13 @@ bin/
application.yaml application.yaml
initial-data.sql initial-data.sql
app.yaml app.yaml
app.properties
*.env.json *.env.json
### API Logs ###
api.log
api.2024*
### Excel FIles ###
./excel
./.kotlin

17
.idea/dataSources.xml generated
View File

@ -1,24 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="DataSourceManagerImpl" format="xml" multifile-model="true"> <component name="DataSourceManagerImpl" format="xml" multifile-model="true">
<data-source source="LOCAL" name="uiapp@192.168.64.6" uuid="21bbb410-6e9c-47c3-8eda-511e7856f454"> <data-source source="LOCAL" name="postgres@localhost" uuid="ee0f00df-3ab2-482e-bd30-2c4401540952">
<driver-ref>postgresql</driver-ref> <driver-ref>postgresql</driver-ref>
<synchronize>true</synchronize> <synchronize>true</synchronize>
<jdbc-driver>org.postgresql.Driver</jdbc-driver> <jdbc-driver>org.postgresql.Driver</jdbc-driver>
<jdbc-url>jdbc:postgresql://192.168.64.6:5432/uiapp</jdbc-url> <jdbc-url>jdbc:postgresql://localhost:5432/postgres</jdbc-url>
<jdbc-additional-properties>
<property name="com.intellij.clouds.kubernetes.db.host.port" />
<property name="com.intellij.clouds.kubernetes.db.enabled" value="false" />
<property name="com.intellij.clouds.kubernetes.db.resource.type" value="Deployment" />
<property name="com.intellij.clouds.kubernetes.db.container.port" />
</jdbc-additional-properties>
<working-dir>$ProjectFileDir$</working-dir>
</data-source>
<data-source source="LOCAL" name="arsalan_rmc_module_app@10.10.10.211" uuid="572880af-561c-4a5a-90bd-e024c09c674b">
<driver-ref>postgresql</driver-ref>
<synchronize>true</synchronize>
<jdbc-driver>org.postgresql.Driver</jdbc-driver>
<jdbc-url>jdbc:postgresql://10.10.10.211:5432/arsalan_rmc_module_app</jdbc-url>
<jdbc-additional-properties> <jdbc-additional-properties>
<property name="com.intellij.clouds.kubernetes.db.host.port" /> <property name="com.intellij.clouds.kubernetes.db.host.port" />
<property name="com.intellij.clouds.kubernetes.db.enabled" value="false" /> <property name="com.intellij.clouds.kubernetes.db.enabled" value="false" />

6
.idea/data_source_mapping.xml generated Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DataSourcePerFileMappings">
<file url="file://$APPLICATION_CONFIG_DIR$/consoles/db/ee0f00df-3ab2-482e-bd30-2c4401540952/console.sql" value="ee0f00df-3ab2-482e-bd30-2c4401540952" />
</component>
</project>

View File

@ -2,6 +2,6 @@
<project version="4"> <project version="4">
<component name="ebeanPlugin11"> <component name="ebeanPlugin11">
<option name="enabled" value="true" /> <option name="enabled" value="true" />
<option name="agentPath" value="$APPLICATION_PLUGINS_DIR$/ebean-idea/lib/ebean-agent-13.22.0.jar" /> <option name="agentPath" value="$APPLICATION_PLUGINS_DIR$/ebean-idea/lib/ebean-agent-15.12.0.jar" />
</component> </component>
</project> </project>

2
.idea/kotlinc.xml generated
View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="KotlinJpsPluginSettings"> <component name="KotlinJpsPluginSettings">
<option name="version" value="1.9.22" /> <option name="version" value="2.0.0" />
</component> </component>
</project> </project>

3
.idea/misc.xml generated
View File

@ -10,9 +10,8 @@
<option value="$PROJECT_DIR$/../txn_workflow/pom.xml" /> <option value="$PROJECT_DIR$/../txn_workflow/pom.xml" />
</list> </list>
</option> </option>
<option name="workspaceImportForciblyTurnedOn" value="true" />
</component> </component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="17" project-jdk-type="JavaSDK"> <component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="zulu-17" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" /> <output url="file://$PROJECT_DIR$/out" />
</component> </component>
</project> </project>

1
.idea/vcs.xml generated
View File

@ -2,5 +2,6 @@
<project version="4"> <project version="4">
<component name="VcsDirectoryMappings"> <component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" /> <mapping directory="$PROJECT_DIR$" vcs="Git" />
<mapping directory="$PROJECT_DIR$/../rmc_modules_app" vcs="Git" />
</component> </component>
</project> </project>

BIN
Products_Template.xls Normal file

Binary file not shown.

298
api.http
View File

@ -7,8 +7,7 @@ Authorization: {{auth-token}}
"data": { "data": {
"number": "KA01HD6677", "number": "KA01HD6677",
"owner": "gowthaman" "owner": "gowthaman"
}, }
"uniqueIdentifier": ""
} }
### create row ### create row
@ -52,18 +51,20 @@ GET http://localhost:9001/api/log/log-0000000001
Authorization: Bearer {{auth-token}} Authorization: Bearer {{auth-token}}
### get row ### get row
GET http://localhost:9001/api/vehicle/KA01HD6667 GET http://localhost:9001/api/vehicle/KA01HD6677
Authorization: Bearer {{auth-token}} Authorization: Bearer {{auth-token}}
### query row
POST http://localhost:9001/api/vehicle/query ### search row
POST http://localhost:9001/api/vehicle/search
Content-Type: application/json Content-Type: application/json
Authorization: {{set-auth-token}} Authorization: {{auth-token}}
{ {
"sql": "select sys_pk, tenant_id, deleted_on, deleted_by, deleted, version, created_at, modified_at, created_by, modified_by, data, tags, comments, unique_identifier, entity_name from data_model where data ->> 'number' = :number", "dateRange": ["2024-05-01", "2024-05-24"],
"params": { "params": {
"number": "KA03HD6064" "number": "KA01HD6677",
"owner": "gowthaman"
} }
} }
@ -93,16 +94,16 @@ Authorization: {{auth-token}}
DELETE http://localhost:9001/api/vehicle/KA01HD6667 DELETE http://localhost:9001/api/vehicle/KA01HD6667
Authorization: {{auth-token}} Authorization: {{auth-token}}
### get products ### get po for id
GET http://localhost:9001/api/vendor/product GET http://localhost:9001/api/vendor/product
Authorization: {{auth-token}} Authorization: {{auth-token}}
### create excel for products ### get product for id
POST http://localhost:9001/api/vendor/product/product-excel GET http://localhost:9001/api/vendor/product/7
Authorization: {{auth-token}} Authorization: {{auth-token}}
### get ### get row
GET http://localhost:9001/api/vendor/product/product-import GET http://localhost:9001/api/vendor/product/7
Authorization: Bearer {{auth-token}} Authorization: Bearer {{auth-token}}
### create product ### create product
@ -132,3 +133,274 @@ Authorization: {{auth-token}}
### delete a row ### delete a row
DELETE http://localhost:9001/api/vendor/product/2 DELETE http://localhost:9001/api/vendor/product/2
Authorization: {{auth-token}} Authorization: {{auth-token}}
### create vendor
POST http://localhost:9001/api/vendor/
Content-Type: application/json
Authorization: {{auth-token}}
{
"name": "arsalan",
"msme": "1234",
"gstNumber": "GST123",
"address": "Bangalore",
"rating": 2,
"contacts": [
{
"name": "contact1",
"email": "abc@cyz.com",
"mobile": "01234567890"
}
]
}
### create batch vendor
POST http://localhost:9001/api/vendor/batch
Content-Type: application/json
Authorization: {{auth-token}}
[
{
"name": "john",
"description": "5678",
"hsnCode": "5678",
"gstNumber": "GST567",
"address": "Mumbai",
"rating": 4,
"contacts": [
{
"name": "contact1",
"email": "xyz@abc.com",
"phone": "9876543210"
}
]
},
{
"name": "emma",
"description": "7890",
"hsnCode": "7890",
"gstNumber": "GST789",
"address": "Delhi",
"rating": 3,
"contacts": [
{
"name": "contact2",
"email": "def@uvw.com",
"phone": "8765432109"
}
]
},
{
"name": "alex",
"description": "2345",
"hsnCode": "2345",
"gstNumber": "GST234",
"address": "Chennai",
"rating": 5,
"contacts": [
{
"name": "contact3",
"email": "ghi@rst.com",
"phone": "7654321098"
}
]
}
]
### GET ALL VENDORS
POST http://localhost:9001/api/vendor/getAll
Content-Type: application/json
Authorization: {{auth-token}}
{
"common": {
"sortAsc": true
},
"vendorFilters": {
"nameLike": "a"
}
}
### create batch pos
POST http://localhost:9001/api/vendor/po/batch
Content-Type: application/json
Authorization: {{auth-token}}
[
{
"products": [
{
"productId": "1232",
"productName": "chair",
"unitPrice": 34.2,
"quantity": 10,
"description": "wooden chair"
}
],
"referenceQuotation": "12323",
"totalAmount": 342,
"poNum": "1",
"poDate": "2024-01-10",
"validTill": "2024-02-10",
"tnc": ["tnc1", "tnc2"]
},
{
"products": [
{
"productId": "5678",
"productName": "table",
"unitPrice": 45.5,
"quantity": 5,
"description": "glass table"
}
],
"referenceQuotation": "56789",
"totalAmount": 227.5,
"poNum": "2",
"poDate": "2024-10-25",
"validTill": "2024-10-25",
"tnc": ["tnc3", "tnc4"]
},
{
"products": [
{
"productId": "91011",
"productName": "lamp",
"unitPrice": 15.75,
"quantity": 20,
"description": "floor lamp"
}
],
"referenceQuotation": "9101112",
"totalAmount": 315,
"poNum": "3",
"poDate": "2024-10-25",
"validTill": "2024-12-25",
"tnc": ["tnc5", "tnc6"]
}
]
### GET ALL POS
POST http://localhost:9001/api/vendor/po/getAll
Content-Type: application/json
Authorization: {{auth-token}}
{
"common" : {},
"poFilters": {}
}
### CREATE QUOTES
POST http://localhost:9001/api/vendor/quote/batch
Content-Type: application/json
Authorization: {{auth-token}}
[
{
"products": [
{
"productId": "1232",
"productName": "chair",
"unitPrice": 34.2,
"quantity": 10,
"description": "wooden chair"
}
],
"reqForQuoteNum": "12323",
"totalAmount": 342,
"quoteNum": "1",
"quoteDate": "2024-10-24",
"validTill": "2024-11-24",
"tnc": ["tnc1", "tnc2"]
},
{
"products": [
{
"productId": "5678",
"productName": "table",
"unitPrice": 45.5,
"quantity": 5,
"description": "glass table"
}
],
"reqForQuoteNum": "56789",
"totalAmount": 227.5,
"quoteNum": "2",
"quoteDate": "2024-10-25",
"validTill": "2024-11-25",
"tnc": ["tnc3", "tnc4"]
},
{
"products": [
{
"productId": "91011",
"productName": "lamp",
"unitPrice": 15.75,
"quantity": 20,
"description": "floor lamp"
}
],
"reqForQuoteNum": "9101112",
"totalAmount": 315,
"quoteNum": "3",
"quoteDate": "2024-10-25",
"validTill": "2024-12-25",
"tnc": ["tnc5", "tnc6"]
}
]
### GET ALL QUOTES
POST http://localhost:9001/api/vendor/quote/getAll
Content-Type: application/json
Authorization: {{auth-token}}
{
"common" : {},
"quoteFilters": {}
}
### GET ALL PRODUCTS
POST http://localhost:9001/api/vendor/product/getAll
Content-Type: application/json
Authorization: {{auth-token}}
{
"common" : {},
"quoteFilters": {}
}
### GET NEXT PO SEQ NUMBER
GET http://localhost:9001/api/vendor/po/next
Content-Type: application/json
Authorization: {{auth-token}}
### GET NEXT QUOTE SEW NUMBER
GET http://localhost:9001/api/vendor/quote/next
Content-Type: application/json
Authorization: {{auth-token}}
### GET NEXT INCOMING SEW NUMBER
GET http://localhost:9001/api/vendor/incoming/next
Content-Type: application/json
Authorization: {{auth-token}}
###
POST http://localhost:9001/api/expense/execute
Content-Type: application/json
Authorization: {{auth-token}}
{
"script": "vehicle.kts",
"fn": "execute",
"params": {
"a": "1",
"b": 2,
"c": {
"c1": 1,
"c2": "2024-04-20"
}
}
}

View File

@ -1,4 +1,4 @@
#DO Not edit this file, copy to your HOME Directory and then rename it to app.yaml and then edit it #DO Not edit this file, copy to your HOME or Current Directory and then rename it to app.yaml and then edit it
app: app:
db: db:
pass: postgres pass: postgres
@ -18,6 +18,8 @@ app:
client: rmc client: rmc
scripts: scripts:
path: /Users/gowthaman.b/IdeaProjects/rmc_modules_api/src/main/resources/scripts path: /Users/gowthaman.b/IdeaProjects/rmc_modules_api/src/main/resources/scripts
integration:
rmc: https://www.readymixerp.com
security: security:
enforce_role_restriction: 'true' enforce_role_restriction: 'true'
private_key: |- private_key: |-

View File

@ -1,8 +1,11 @@
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
plugins { plugins {
kotlin("jvm") version "1.9.22" kotlin("jvm") version "2.0.0"
kotlin("kapt") version "1.9.22" kotlin("kapt") version "2.0.0"
id("idea") id("idea")
id("io.ebean") version "13.23.2" id("io.ebean") version "13.23.2"
id("com.github.johnrengelman.shadow") version "8.1.1"
application application
} }
@ -15,33 +18,40 @@ repositories {
dependencies { dependencies {
testImplementation(kotlin("test")) testImplementation(kotlin("test"))
implementation("net.jodah:expiringmap:0.5.11")
implementation("javax.xml.bind:jaxb-api:2.3.1") implementation("javax.xml.bind:jaxb-api:2.3.1")
implementation("com.sun.xml.bind:jaxb-impl:2.3.9") implementation("com.sun.xml.bind:jaxb-impl:2.3.9")
implementation("io.javalin:javalin:5.6.3") implementation("io.javalin:javalin:5.6.3")
implementation("io.ebean:ebean:13.23.2") implementation("io.ebean:ebean:13.23.2")
implementation("io.ebean:ebean-querybean:13.23.2") implementation("io.ebean:ebean-querybean:13.23.2")
implementation("org.postgresql:postgresql:42.6.0") implementation("org.postgresql:postgresql:42.7.3")
implementation("io.ebean:ebean-migration:13.11.1") implementation("io.ebean:ebean-migration:13.11.1")
implementation("io.ebean:ebean-ddl-generator:13.23.2") implementation("io.ebean:ebean-ddl-generator:13.23.2")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.15.+") implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.15.+")
implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.15.+") implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.15.+")
implementation("ch.qos.logback:logback-core:1.4.12") implementation("ch.qos.logback:logback-core:1.5.6")
implementation("ch.qos.logback:logback-classic:1.4.12") implementation("ch.qos.logback:logback-classic:1.4.12")
implementation("org.bitbucket.b_c:jose4j:0.9.3") implementation("org.bitbucket.b_c:jose4j:0.9.4")
implementation("redis.clients:jedis:5.0.2") implementation("redis.clients:jedis:5.0.2")
implementation("org.jetbrains.kotlin:kotlin-scripting-jsr223:1.9.22") implementation("org.bouncycastle:bcprov-jdk18on:1.78.1")
implementation("org.jetbrains.kotlin:kotlin-script-runtime:1.9.22") implementation("org.bouncycastle:bcpkix-jdk18on:1.78.1")
implementation("org.bouncycastle:bcprov-jdk18on:1.76")
implementation("org.bouncycastle:bcpkix-jdk18on:1.76")
implementation("org.yaml:snakeyaml:2.2") implementation("org.yaml:snakeyaml:2.2")
implementation("io.minio:minio:8.5.7") implementation("io.minio:minio:8.5.11")
implementation("org.apache.httpcomponents:httpclient:4.5.14") implementation("org.apache.httpcomponents:httpclient:4.5.14")
implementation("org.apache.poi:poi:5.2.3") implementation("org.apache.poi:poi:5.2.3")
implementation("org.apache.poi:poi-ooxml:5.2.3") implementation("org.apache.poi:poi-ooxml:5.2.3")
api ("net.cactusthorn.config:config-core:0.81") implementation("com.google.code.gson:gson:2.8.9")
api ("net.cactusthorn.config:config-yaml:0.81") api("net.cactusthorn.config:config-core:0.81")
api("net.cactusthorn.config:config-yaml:0.81")
kapt("net.cactusthorn.config:config-compiler:0.81") kapt("net.cactusthorn.config:config-compiler:0.81")
kapt("io.ebean:kotlin-querybean-generator:13.23.2") kapt("io.ebean:kotlin-querybean-generator:13.23.2")
implementation("org.graalvm.polyglot:polyglot:24.0.2")
implementation("org.graalvm.polyglot:js:24.0.2")
implementation(kotlin("script-runtime"))
implementation(kotlin("scripting-jsr223"))
//implementation("org.jetbrains.kotlin:kotlin-scripting-jsr223:1.9.20")
//implementation("org.jetbrains.kotlin:kotlin-script-runtime:1.9.20")
} }
tasks.test { tasks.test {
@ -52,6 +62,30 @@ kotlin {
jvmToolchain(17) jvmToolchain(17)
} }
tasks {
named<ShadowJar>("shadowJar") {
archiveBaseName.set("rest-api")
mergeServiceFiles()
manifest {
attributes(mapOf("Main-Class" to "com.restapi.MainKt"))
}
isZip64 = true
}
}
tasks.distTar {
enabled = false
}
tasks.distZip {
enabled = false
}
tasks.shadowDistTar {
enabled = false
}
tasks.shadowDistZip {
enabled = false
}
application { application {
mainClass.set("com.restapi.MainKt") mainClass.set("com.restapi.MainKt")
} }

BIN
excel/Fleets.xls Normal file

Binary file not shown.

BIN
excel/IncomingInventory.xls Normal file

Binary file not shown.

BIN
excel/Invoices.xls Normal file

Binary file not shown.

BIN
excel/OutgoingInventory.xls Normal file

Binary file not shown.

BIN
excel/Payments.xls Normal file

Binary file not shown.

BIN
excel/Pos.xls Normal file

Binary file not shown.

BIN
excel/Products.xls Normal file

Binary file not shown.

BIN
excel/Quotes.xls Normal file

Binary file not shown.

BIN
excel/ReminderLogs.xls Normal file

Binary file not shown.

BIN
excel/VendorList.xls Normal file

Binary file not shown.

Binary file not shown.

View File

@ -1,5 +1,7 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists

41
gradlew vendored
View File

@ -55,7 +55,7 @@
# Darwin, MinGW, and NonStop. # Darwin, MinGW, and NonStop.
# #
# (3) This script is generated from the Groovy template # (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project. # within the Gradle project.
# #
# You can find Gradle at https://github.com/gradle/gradle/. # You can find Gradle at https://github.com/gradle/gradle/.
@ -80,13 +80,11 @@ do
esac esac
done done
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit # This is normally unused
# shellcheck disable=SC2034
APP_NAME="Gradle"
APP_BASE_NAME=${0##*/} APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value. # Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum MAX_FD=maximum
@ -133,22 +131,29 @@ location of your Java installation."
fi fi
else else
JAVACMD=java JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. if ! command -v java >/dev/null 2>&1
then
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the Please set the JAVA_HOME variable in your environment to match the
location of your Java installation." location of your Java installation."
fi
fi fi
# Increase the maximum file descriptors if we can. # Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #( case $MAX_FD in #(
max*) max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
MAX_FD=$( ulimit -H -n ) || MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit" warn "Could not query maximum file descriptor limit"
esac esac
case $MAX_FD in #( case $MAX_FD in #(
'' | soft) :;; #( '' | soft) :;; #(
*) *)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
ulimit -n "$MAX_FD" || ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD" warn "Could not set maximum file descriptor limit to $MAX_FD"
esac esac
@ -193,11 +198,15 @@ if "$cygwin" || "$msys" ; then
done done
fi fi
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
# shell script including quotes and variable substitutions, so put them in DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded. # Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.
set -- \ set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \ "-Dorg.gradle.appname=$APP_BASE_NAME" \
@ -205,6 +214,12 @@ set -- \
org.gradle.wrapper.GradleWrapperMain \ org.gradle.wrapper.GradleWrapperMain \
"$@" "$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# Use "xargs" to parse quoted args. # Use "xargs" to parse quoted args.
# #
# With -n1 it outputs one arg per line, with the quotes and backslashes removed. # With -n1 it outputs one arg per line, with the quotes and backslashes removed.

15
gradlew.bat vendored
View File

@ -14,7 +14,7 @@
@rem limitations under the License. @rem limitations under the License.
@rem @rem
@if "%DEBUG%" == "" @echo off @if "%DEBUG%"=="" @echo off
@rem ########################################################################## @rem ##########################################################################
@rem @rem
@rem Gradle startup script for Windows @rem Gradle startup script for Windows
@ -25,7 +25,8 @@
if "%OS%"=="Windows_NT" setlocal if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0 set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=. if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0 set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME% set APP_HOME=%DIRNAME%
@ -40,7 +41,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1 %JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute if %ERRORLEVEL% equ 0 goto execute
echo. echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
@ -75,13 +76,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
:end :end
@rem End local scope for the variables with windows NT shell @rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd if %ERRORLEVEL% equ 0 goto mainEnd
:fail :fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code! rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 set EXIT_CODE=%ERRORLEVEL%
exit /b 1 if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd :mainEnd
if "%OS%"=="Windows_NT" endlocal if "%OS%"=="Windows_NT" endlocal

View File

@ -6,7 +6,8 @@ pluginManagement {
} }
plugins { plugins {
id("org.gradle.toolchains.foojay-resolver-convention") version "0.7.0" id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0"
id("de.fayard.refreshVersions") version "0.60.5"
} }
rootProject.name = "rest_api" rootProject.name = "rest_api"

View File

@ -42,7 +42,7 @@ class AppAccessManager : AccessManager {
Role.DbOps -> listOf("ROLE_DB_OPS") Role.DbOps -> listOf("ROLE_DB_OPS")
Role.Entity -> loadEntityActionRole(entity, action) Role.Entity -> loadEntityActionRole(entity, action)
is Role.Standard -> role.action.toList().map { "ROLE_${entity}_${it}" } is Role.Standard -> role.action.toList().map { "ROLE_${entity}_${it}" }
is Role.Explicit -> role.roles is Role.Explicit -> role.roles.toList() + listOf("ROLE_ADMIN")
}.map(String::uppercase) }.map(String::uppercase)
} }
@ -60,6 +60,15 @@ class AppAccessManager : AccessManager {
//if role is allowed, or enforcement is turned off or no roles are explicitly allowed //if role is allowed, or enforcement is turned off or no roles are explicitly allowed
handler.handle(ctx) handler.handle(ctx)
} else { } else {
logger.warn(
"entity - {}, action {}, user roles = {}, allowed = {}, isAllowed? {}, enforce? {}",
entity,
action,
currentRoles(),
allowedRoles,
isAllowed,
appConfig.enforceRoleRestriction()
)
ctx.status(HttpStatus.FORBIDDEN).result("user not allowed to do this") ctx.status(HttpStatus.FORBIDDEN).result("user not allowed to do this")
} }
} }

View File

@ -6,10 +6,9 @@ import com.restapi.config.AppConfig.Companion.appConfig
import com.restapi.config.Auth.validateAuthToken import com.restapi.config.Auth.validateAuthToken
import com.restapi.controllers.* import com.restapi.controllers.*
import com.restapi.domain.DataNotFoundException import com.restapi.domain.DataNotFoundException
import com.restapi.domain.Product
import com.restapi.domain.Session.a
import com.restapi.domain.Session.currentTenant import com.restapi.domain.Session.currentTenant
import com.restapi.domain.Session.currentUser import com.restapi.domain.Session.currentUser
import com.restapi.domain.Session.currentUserPlants
import com.restapi.domain.Session.objectMapper import com.restapi.domain.Session.objectMapper
import com.restapi.domain.Session.setAuthorizedUser import com.restapi.domain.Session.setAuthorizedUser
import com.restapi.domain.Session.signPayload import com.restapi.domain.Session.signPayload
@ -25,7 +24,6 @@ import io.javalin.http.util.RateLimitUtil
import io.javalin.json.JavalinJackson import io.javalin.json.JavalinJackson
import org.jose4j.jwt.consumer.InvalidJwtException import org.jose4j.jwt.consumer.InvalidJwtException
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import java.io.InputStream
import java.security.MessageDigest import java.security.MessageDigest
import java.time.LocalDateTime import java.time.LocalDateTime
import java.util.* import java.util.*
@ -39,21 +37,19 @@ fun main(args: Array<String>) {
val viewRole = Role.Standard(Action.VIEW) val viewRole = Role.Standard(Action.VIEW)
val createRole = Role.Standard(Action.CREATE) val createRole = Role.Standard(Action.CREATE)
val updateRole = Role.Standard(Action.UPDATE) val updateRole = Role.Standard(Action.UPDATE)
val approveOrRejectRole = Role.Standard(Action.APPROVE) val appAdmin = Role.Explicit("ROLE_ADMIN")
//todo, create roles in keycloak based on entity and actions //todo, create roles in keycloak based on entity and actions
//ratelimit based on IP Only //ratelimit based on IP Only
RateLimitUtil.keyFunction = { ctx -> ctx.header("X-Forwarded-For")?.split(",")?.get(0) ?: ctx.ip() } RateLimitUtil.keyFunction = { ctx -> ctx.header("X-Forwarded-For")?.split(",")?.get(0) ?: ctx.ip() }
Javalin Javalin.create { cfg ->
.create { cfg ->
cfg.http.generateEtags = true cfg.http.generateEtags = true
if (appConfig.corsEnabled()) { if (appConfig.corsEnabled()) {
cfg.plugins.enableCors { container -> cfg.plugins.enableCors { container ->
container.add { container.add {
it.allowHost( it.allowHost(
"http://localhost:5173", "http://localhost:5173", *appConfig.corsHosts().toTypedArray()
*appConfig.corsHosts().toTypedArray()
) )
} }
} }
@ -62,22 +58,20 @@ fun main(args: Array<String>) {
cfg.compression.gzipOnly() cfg.compression.gzipOnly()
cfg.jsonMapper(JavalinJackson(objectMapper)) cfg.jsonMapper(JavalinJackson(objectMapper))
cfg.accessManager(AppAccessManager()) cfg.accessManager(AppAccessManager())
} }.routes {
.routes {
path("/auth") { path("/auth") {
get("/endpoint", Auth::endPoint) get("/endpoint", Auth::endPoint)
get("/init", Auth::init) get("/init", Auth::init)
get("/code", Auth::code) get("/code", Auth::code)
get("/keys", Auth::keys) get("/keys", Auth::keys)
post("/logout", Auth::logout)
post("/refresh", Auth::refreshToken) post("/refresh", Auth::refreshToken)
} }
before("/api/*") { ctx -> before("/api/*") { ctx ->
NaiveRateLimit.requestPerTimeUnit( NaiveRateLimit.requestPerTimeUnit(
ctx, ctx, appConfig.rateLimit().getOrDefault(10000), TimeUnit.MINUTES
appConfig.rateLimit().getOrDefault(30),
TimeUnit.MINUTES
) )
val authToken = ctx.getAuthHeader() ?: throw UnauthorizedResponse() val authToken = ctx.getAuthHeader() ?: throw UnauthorizedResponse()
@ -87,9 +81,6 @@ fun main(args: Array<String>) {
setAuthorizedUser(validateAuthToken(authToken = authToken)) setAuthorizedUser(validateAuthToken(authToken = authToken))
if (appConfig.enforcePayloadEncryption()) {
//todo: decrypt the request from user
}
} }
after("/api/*") { after("/api/*") {
@ -101,13 +92,13 @@ fun main(args: Array<String>) {
it.header("X-Checksum", outEncoded) it.header("X-Checksum", outEncoded)
it.header("X-Signature", signPayload(outEncoded)) it.header("X-Signature", signPayload(outEncoded))
if (appConfig.enforcePayloadEncryption()) {
//todo: encrypt and send the response back to user
}
} }
path("/api") { path("/api") {
get("/plants") {
it.json(currentUserPlants())
}
post("/audit/{action}") { post("/audit/{action}") {
logger.warn("User ${currentUser()} of tenant ${currentTenant()} has performed ${it.pathParam("action")} @ ${LocalDateTime.now()}") logger.warn("User ${currentUser()} of tenant ${currentTenant()} has performed ${it.pathParam("action")} @ ${LocalDateTime.now()}")
it.json(mapOf("status" to true)) it.json(mapOf("status" to true))
@ -115,171 +106,210 @@ fun main(args: Array<String>) {
path("/vendor") { path("/vendor") {
path("/") { path("/") {
post("", Vendor::create, Roles(Role.Explicit(listOf("ROLE_VENDOR_CREATE", "ROLE_ADMIN")))) post("", VendorCtrl::create, Roles(Role.Explicit("ROLE_VENDOR_CREATE")))
get( post("/batch", VendorCtrl::createBatch, Roles(Role.Explicit("ROLE_VENDOR_CREATE")))
"", get("/{id}", VendorCtrl::get, Roles(Role.Explicit("ROLE_VENDOR_VIEW", "ROLE_VENDOR_CREATE")))
Vendor::get, post(
Roles(Role.Explicit(listOf("ROLE_VENDOR_VIEW", "ROLE_VENDOR_CREATE", "ROLE_ADMIN"))) "/getAll", VendorCtrl::getAll
) )
get( get(
"quotes/{id}", "quotes/{id}", VendorCtrl::getQuotes, Roles(Role.Explicit("ROLE_QUOTE_VIEW", "ROLE_QUOTE_CREATE", "ROLE_VENDOR_VIEW"))
Vendor::getQuotes, )
get("pos/{id}", VendorCtrl::getPos, Roles(Role.Explicit("ROLE_PO_VIEW", "ROLE_PO_CREATE`")))
put("/rate/{id}/{rating}", VendorCtrl::rate, Roles(Role.Explicit("ROLE_VENDOR_CREATE")))
put("/{id}", VendorCtrl::update, Roles(Role.Explicit("ROLE_VENDOR_CREATE")))
}
path("/incoming") {
get(
"/plants", IncomingInventoryCtrl::plantsForUser
)
put(
"/plants/{id}", IncomingInventoryCtrl::updatePlant,
Roles( Roles(
Role.Explicit("ROLE_INVENTORY_CREATE"),
Role.Explicit("ROLE_VENDOR_CREATE")
)
)
post("", IncomingInventoryCtrl::create, Roles(Role.Explicit("ROLE_INVENTORY_CREATE")))
get("/next", IncomingInventoryCtrl::getNextNum, Roles(Role.Explicit("ROLE_INVENTORY_CREATE")))
get(
"/{id}", IncomingInventoryCtrl::get, Roles(Role.Explicit("ROLE_INVENTORY_VIEW", "ROLE_INVENTORY_CREATE"))
)
put("/{id}", IncomingInventoryCtrl::update, Roles(Role.Explicit("ROLE_INVENTORY_CREATE")))
post(
"/getAll", IncomingInventoryCtrl::getAll, Roles(Role.Explicit("ROLE_INVENTORY_CREATE", "ROLE_INVENTORY_VIEW"))
)
}
path("/outgoing") {
post("", OutgoingInventoryCtrl::create, Roles(Role.Explicit("ROLE_INVENTORY_CREATE")))
get("/next", OutgoingInventoryCtrl::getNextNum, Roles(Role.Explicit("ROLE_INVENTORY_CREATE")))
get(
"/{id}", OutgoingInventoryCtrl::get, Roles(Role.Explicit("ROLE_INVENTORY_VIEW", "ROLE_INVENTORY_CREATE"))
)
put("/{id}", OutgoingInventoryCtrl::update, Roles(Role.Explicit("ROLE_INVENTORY_CREATE")))
post(
"/getAll", OutgoingInventoryCtrl::getAll, Roles(Role.Explicit("ROLE_INVENTORY_CREATE", "ROLE_INVENTORY_VIEW"))
)
}
path("/invoice") {
post("", InvoiceCtrl::create, Roles(Role.Explicit("ROLE_INVOICE_CREATE")))
get("/next", InvoiceCtrl::getNextNum, Roles(Role.Explicit("ROLE_INVOICE_CREATE")))
get(
"/{id}", InvoiceCtrl::get, Roles(Role.Explicit("ROLE_INVOICE_VIEW", "ROLE_INVOICE_CREATE"))
)
put("/{id}", InvoiceCtrl::update, Roles(Role.Explicit("ROLE_INVOICE_CREATE")))
post(
"/getAll", InvoiceCtrl::getAll, Roles(Role.Explicit("ROLE_INVOICE_CREATE", "ROLE_INVOICE_VIEW"))
)
}
path("/payment") {
post("", PaymentCtrl::create, Roles(Role.Explicit("ROLE_PAYMENT_CREATE")))
get(
"/{id}", PaymentCtrl::get, Roles(Role.Explicit("ROLE_PAYMENT_VIEW", "ROLE_PAYMENT_CREATE"))
)
put("/{id}", PaymentCtrl::update, Roles(Role.Explicit("ROLE_PAYMENT_CREATE")))
post(
"/getAll", PaymentCtrl::getAll, Roles(Role.Explicit("ROLE_PAYMENT_CREATE", "ROLE_PAYMENT_VIEW"))
)
delete("/{id}", PaymentCtrl::delete, Roles(Role.Explicit("ROLE_PAYMENT_CREATE")))
}
path("/fleet") {
post("", FleetCtrl::create, Roles(Role.Explicit("ROLE_FLEET_CREATE")))
get(
"/{id}", FleetCtrl::get, Roles(Role.Explicit("ROLE_FLEET_VIEW", "ROLE_FLEET_CREATE"))
)
put("/{id}", FleetCtrl::update, Roles(Role.Explicit("ROLE_FLEET_CREATE")))
post(
"/getAll", FleetCtrl::getAll, Roles(
Role.Explicit( Role.Explicit(
listOf( "ROLE_FLEET_CREATE",
"ROLE_ADMIN", "ROLE_FLEET_VIEW",
"ROLE_QUOTE_VIEW", "ROLE_EXPENSE_CREATE",
"ROLE_QUOTE_CREATE", "ROLE_EXPENSE_VIEW",
"ROLE_VENDOR_VIEW"
) )
) )
) )
delete("/{id}", FleetCtrl::delete, Roles(Role.Explicit("ROLE_FLEET_CREATE")))
}
path("/renewal") {
post("", RenewalCtrl::create, Roles(Role.Explicit("ROLE_FLEET_CREATE")))
get(
"/{id}", RenewalCtrl::get, Roles(Role.Explicit("ROLE_FLEET_VIEW", "ROLE_FLEET_CREATE"))
)
put("/{id}", RenewalCtrl::update, Roles(Role.Explicit("ROLE_FLEET_CREATE")))
post(
"/getAll", RenewalCtrl::getAll, Roles(Role.Explicit("ROLE_FLEET_CREATE", "ROLE_FLEET_VIEW"))
)
delete("/{id}", RenewalCtrl::delete, Roles(Role.Explicit("ROLE_FLEET_CREATE")))
}
path("/reminder") {
post("", ReminderCtrl::create, Roles(Role.Explicit("ROLE_REMINDER_CREATE")))
get(
"/{id}", ReminderCtrl::get, Roles(Role.Explicit("ROLE_REMINDER_VIEW", "ROLE_REMINDER_CREATE"))
)
put("/{id}", ReminderCtrl::update, Roles(Role.Explicit("ROLE_REMINDER_CREATE")))
post(
"/getAll", ReminderLogCtrl::getAll, Roles(Role.Explicit("ROLE_REMINDER_CREATE", "ROLE_REMINDER_VIEW"))
)
post(
"/done", ReminderLogCtrl::done, Roles(Role.Explicit("ROLE_REMAINDER_CREATE"))
) )
get( get(
"pos/{id}", "/getAll/{id}", ReminderCtrl::getAllByFleetId, Roles(Role.Explicit("ROLE_REMINDER_CREATE", "ROLE_REMINDER_VIEW"))
Vendor::getPos, )
Roles(Role.Explicit(listOf("ROLE_ADMIN", "ROLE_PO_VIEW", "ROLE_PO_CREATE`"))) delete(
"/{id}", ReminderCtrl::delete, Roles(Role.Explicit("ROLE_REMINDER_CREATE"))
)
}
path("/fleetType") {
post("", FleetTypeCtrl::create, Roles(Role.Explicit("ROLE_FLEET_CREATE")))
get(
"/{id}", FleetTypeCtrl::get, Roles(Role.Explicit("ROLE_FLEET_VIEW", "ROLE_FLEET_CREATE"))
)
put("/{id}", FleetTypeCtrl::update, Roles(Role.Explicit("ROLE_FLEET_CREATE")))
post(
"/getAll", FleetTypeCtrl::getAll, Roles(Role.Explicit("ROLE_FLEET_CREATE", "ROLE_FLEET_VIEW"))
) )
put("/rate/{id}/{rating}", Vendor::rate, Roles(Role.Explicit(listOf("ROLE_VENDOR_CREATE"))))
} }
path("/po") { path("/po") {
post("", PurchaseOrder::create, Roles(Role.Explicit(listOf("ROLE_PO_CREATE", "ROLE_ADMIN")))) get("/next", PurchaseOrderCtrl::getNextNum, Roles(Role.Explicit("ROLE_PO_CREATE")))
get( post("", PurchaseOrderCtrl::create, Roles(Role.Explicit("ROLE_PO_CREATE")))
"/{id}", post("/batch", PurchaseOrderCtrl::createBatch, Roles(Role.Explicit("ROLE_PO_CREATE")))
PurchaseOrder::get, post(
Roles(Role.Explicit(listOf("ROLE_PO_CREATE", "ROLE_PO_VIEW", "ROLE_QUOTE_CREATE"))) "/getAll", PurchaseOrderCtrl::getAll, Roles(Role.Explicit("ROLE_PO_CREATE", "ROLE_PO_VIEW", "ROLE_VENDOR_CREATE"))
)
put(
"/approve/{id}",
PurchaseOrder::approve,
Roles(Role.Explicit(listOf("ROLE_ADMIN", "ROLE_APPROVE")))
)
put(
"/reject/{id}",
PurchaseOrder::reject,
Roles(Role.Explicit(listOf("ROLE_ADMIN", "ROLE_APPROVE")))
) )
get( get(
"/refQuote/{id}", "/{id}", PurchaseOrderCtrl::get, Roles(Role.Explicit("ROLE_PO_CREATE", "ROLE_PO_VIEW", "ROLE_QUOTE_CREATE"))
PurchaseOrder::quoteReference,
Roles(Role.Explicit(listOf("ROLE_PO_CREATE", "ROLE_PO_VIEW")))
) )
put("/{id}", PurchaseOrderCtrl::update, Roles(Role.Explicit("ROLE_PO_CREATE")))
put("/approve/{id}", PurchaseOrderCtrl::approve, Roles(Role.Explicit()))
put("/reject/{id}", PurchaseOrderCtrl::reject, Roles(Role.Explicit()))
get("/refQuote/{id}", PurchaseOrderCtrl::quoteReference, Roles(Role.Explicit("ROLE_PO_CREATE")))
} }
path("/quote") { path("/quote") {
post("", Quotation::create, Roles(Role.Explicit(listOf("ROLE_QUOTE_CREATE", "ROLE_ADMIN")))) get("/next", QuotationCtrl::getNextNum, Roles(Role.Explicit("ROLE_QUOTE_CREATE")))
get( post("", QuotationCtrl::create, Roles(Role.Explicit("ROLE_QUOTE_CREATE")))
"/{id}", post("/batch", QuotationCtrl::createBatch, Roles(Role.Explicit("ROLE_QUOTE_CREATE")))
Quotation::get, post(
Roles( "/getAll", QuotationCtrl::getAll, Roles(Role.Explicit("ROLE_QUOTE_CREATE", "ROLE_QUOTE_VIEW"))
Role.Explicit(
listOf(
"ROLE_QUOTE_VIEW",
"ROLE_ADMIN",
"ROLE_PO_CREATE",
"ROLE_QUOTE_CREATE"
)
)
)
)
get(
"/po/{id}",
Quotation::generatePO,
Roles(Role.Explicit(listOf("ROLE_ADMIN", "ROLE_PO_CRETE")))
)
get(
"/rfq/{rfqNum}",
Quotation::reqForQuote,
Roles(Role.Explicit(listOf("ROLE_QUOTE_CREATE", "ROLE_QUOTE_VIEW")))
)
delete(
"/{id}",
Quotation::delete,
Roles(Role.Explicit(listOf("ROLE_QUOTE_CREATE", "ROLE_ADMIN")))
) )
get("/{id}", QuotationCtrl::get, Roles(Role.Explicit("ROLE_QUOTE_VIEW", "ROLE_QUOTE_CREATE")))
put("/{id}", QuotationCtrl::update, Roles(Role.Explicit("ROLE_QUOTE_CREATE")))
delete("/{id}", QuotationCtrl::delete, Roles(Role.Explicit("ROLE_QUOTE_CREATE")))
} }
path("/product") { path("/product") {
post("", ProductCtrl::create, Roles(Role.Explicit(listOf("ROLE_PRODUCT_CREATE", "ROLE_ADMIN")))) post("", ProductCtrl::create, Roles(Role.Explicit("ROLE_PRODUCT_CREATE")))
//get("/{hsnCode}", ProductCtrl::get, Roles(Role.Explicit(listOf("ROLE_PRODUCT_VIEW", "ROLE_ADMIN")))) put("/{id}", ProductCtrl::update, Roles(Role.Explicit("ROLE_PRODUCT_CREATE")))
put( delete("/{id}", ProductCtrl::delete, Roles(Role.Explicit("ROLE_PRODUCT_CREATE")))
"/{id}", patch("/{id}", ProductCtrl::patch, Roles(Role.Explicit("ROLE_PRODUCT_CREATE")))
ProductCtrl::update, post("/getPrice", ProductCtrl::getPrice, Roles(Role.Explicit("ROLE_PRODUCT_VIEW")))
Roles(Role.Explicit(listOf("ROLE_PRODUCT_UPDATE", "ROLE_ADMIN"))) post("/getAll", ProductCtrl::getAll, Roles(Role.Explicit("ROLE_PRODUCT_VIEW")))
) get("/{id}", ProductCtrl::get, Roles(Role.Explicit("ROLE_PRODUCT_VIEW")))
//patch("/{id}", ProductCtrl::patch, Roles(Role.Explicit(listOf("ROLE_PRODUCT_UPDATE", "ROLE_ADMIN"))))
delete(
"/{id}",
ProductCtrl::delete,
Roles(Role.Explicit(listOf("ROLE_PRODUCT_DELETE", "ROLE_ADMIN")))
)
get("", ProductCtrl::getAll, Roles(Role.Explicit(listOf("ROLE_PRODUCT_VIEW", "ROLE_ADMIN"))))
post("/product-excel", ProductCtrl::prodExcel)
get("/product-import") { ctx -> //ctx.json(ExcelRead())}
val fileItem = ctx.uploadedFiles("file")
if (fileItem != null) {
ctx.result("Data imported successfully!")
} else {
ctx.result("No file uploaded")
}
}
} }
path("/doc") { path("/doc") {
post("", Document::create, Roles(Role.Explicit(listOf("ROLE_DOC_CREATE", "ROLE_ADMIN")))) post("", DocumentCtrl::create, Roles(Role.Explicit("ROLE_DOC_CREATE")))
//why type and refid are clubbed ?? //why type and refid are clubbed ??
get( get(
"/{type}/{refId}", "/{type}/{refId}", DocumentCtrl::getWithRefId, Roles(Role.Explicit("ROLE_DOC_VIEW", "ROLE_PRODUCT_CREATE"))
Document::getWithRefId,
Roles(Role.Explicit(listOf("ROLE_DOC_VIEW", "ROLE_ADMIN", "ROLE_PRODUCT_CREATE")))
) )
get("/{id}", DocumentCtrl::get, Roles(Role.Explicit("ROLE_DOC_VIEW", "ROLE_PRODUCT_CREATE")))
get( get(
"/{id}", "/print/{id}", DocumentCtrl::print, Roles(Role.Explicit("ROLE_DOC_CREATE", "ROLE_DOC_VIEW"))
Document::get,
Roles(Role.Explicit(listOf("ROLE_DOC_VIEW", "ROLE_ADMIN", "ROLE_PRODUCT_CREATE")))
) )
get( delete("/{id}", DocumentCtrl::delete, Roles(Role.Explicit("ROLE_DOC_CREATE")))
"/print/{id}",
Document::print,
Roles(Role.Explicit(listOf("ROLE_DOC_CREATE", "ROLE_DOC_VIEW")))
)
delete("/{id}", Document::delete, Roles(Role.Explicit(listOf("ROLE_DOC_CREATE"))))
} }
path("/reqForQuote") { path("/reqForQuote") {
post("", RequestForQuote::create, Roles(Role.Explicit(listOf("ROLE_RFQ_CREATE")))) post(
get( "", RequestForQuote::create, Roles(Role.Explicit("ROLE_QUOTE_CREATE", "ROLE_PO_CREATE", "ROLE_RFQ_CREATE"))
"/{id}", )
RequestForQuote::get, get(
Roles(Role.Explicit(listOf("ROLE_RFQ_CREATE", "ROLE_RFQ_VIEW"))) "/{id}", RequestForQuote::get, Roles(Role.Explicit("ROLE_RFQ_CREATE", "ROLE_RFQ_VIEW", "ROLE_QUOTE_VIEW", "ROLE_PO_VIEW"))
)
put(
"/{id}", RequestForQuote::update, Roles(Role.Explicit("ROLE_QUOTE_CREATE", "ROLE_PO_CREATE", "ROLE_RFQ_CREATE"))
) )
put("/{id}", RequestForQuote::update, Roles(Role.Explicit(listOf("ROLE_RFQ_CREATE"))))
} }
} }
post("/script/database/{name}", Entities::executeStoredProcedure, Roles(adminRole, Role.DbOps))
post("/script/{file}/{name}", Entities::executeScript, Roles(adminRole, Role.DbOps))
get("/{entity}/{id}", Entities::view, Roles(adminRole, viewRole)) get("/{entity}", Entities::getAll)
post("/{entity}/query/{id}", Entities::sqlQueryById, Roles(adminRole, viewRole)) post("/{entity}/next", Entities::getNextSeqNo, Roles(adminRole, viewRole, appAdmin))
post("/{entity}/query", Entities::sqlQueryRaw, Roles(adminRole, viewRole)) get("/{entity}/{id}", Entities::view, Roles(adminRole, viewRole, appAdmin))
post("/{entity}", Entities::create, Roles(adminRole, createRole)) post("/{entity}/search", Entities::search, Roles(adminRole, viewRole, appAdmin))
post("/{entity}/execute", Entities::execute, Roles(adminRole, createRole, appAdmin))
post("/{entity}", Entities::create, Roles(adminRole, createRole, appAdmin))
put("/{entity}/approve/{id}", Entities::approve, Roles(adminRole, approveOrRejectRole)) put("/{entity}/{id}", Entities::update, Roles(adminRole, updateRole, appAdmin))
put("/{entity}/reject/{id}", Entities::reject, Roles(adminRole, approveOrRejectRole)) patch("/{entity}/{id}", Entities::patch, Roles(adminRole, updateRole, appAdmin))
put("/{entity}/{action}/{id}", Entities::action, Roles(adminRole, Role.Entity)) delete("/{entity}/{id}", Entities::delete, Roles(adminRole, Role.Standard(Action.DELETE), appAdmin))
put("/{entity}/{id}", Entities::update, Roles(adminRole, updateRole))
patch("/{entity}/{id}", Entities::patch, Roles(adminRole, updateRole))
delete("/{entity}/{id}", Entities::delete, Roles(adminRole, Role.Standard(Action.DELETE)))
} }
} }.exception(DuplicateKeyException::class.java, Exceptions.dupKeyExceptionHandler)
.exception(DuplicateKeyException::class.java, Exceptions.dupKeyExceptionHandler)
.exception(DataIntegrityException::class.java, Exceptions.dataIntegrityException) .exception(DataIntegrityException::class.java, Exceptions.dataIntegrityException)
.exception(DataNotFoundException::class.java, Exceptions.dataNotFoundException) .exception(DataNotFoundException::class.java, Exceptions.dataNotFoundException)
.exception(IllegalArgumentException::class.java, Exceptions.illegalArgumentException) .exception(IllegalArgumentException::class.java, Exceptions.illegalArgumentException)
.exception(JsonMappingException::class.java, Exceptions.jsonMappingException) .exception(JsonMappingException::class.java, Exceptions.jsonMappingException)
.exception(InvalidJwtException::class.java, Exceptions.invalidJwtException) .exception(InvalidJwtException::class.java, Exceptions.invalidJwtException).start(appConfig.portNumber())
.start(appConfig.portNumber()) }
}
private fun Context.getAuthHeader() = header("Authorization") private fun Context.getAuthHeader() = header("Authorization")?.replace("Bearer ", "")?.replace("Bearer: ", "")?.trim()
?.replace("Bearer ", "")
?.replace("Bearer: ", "")
?.trim()

View File

@ -8,7 +8,13 @@ import net.cactusthorn.config.core.loader.LoadStrategy
import java.util.Optional import java.util.Optional
@Config( @Config(
sources = ["file:~/app.yaml", "system:env"], sources = [
"file:./app.properties",
"file:~/app.properties",
"file:./app.yaml",
"file:~/app.yaml",
"system:env",
],
loadStrategy = LoadStrategy.FIRST_KEYCASEINSENSITIVE loadStrategy = LoadStrategy.FIRST_KEYCASEINSENSITIVE
) )
interface AppConfig { interface AppConfig {
@ -58,6 +64,10 @@ interface AppConfig {
@Key("app.iam.client_secret") @Key("app.iam.client_secret")
fun iamClientSecret(): Optional<String> fun iamClientSecret(): Optional<String>
@Key("app.integration.rmc")
@Default("https://www.readymixerp.com")
fun integrationRmc(): String
@Key("app.cache.redis_uri") @Key("app.cache.redis_uri")
fun redisUri(): Optional<String> fun redisUri(): Optional<String>

View File

@ -2,13 +2,20 @@ package com.restapi.config
import com.fasterxml.jackson.module.kotlin.readValue import com.fasterxml.jackson.module.kotlin.readValue
import com.restapi.config.AppConfig.Companion.appConfig import com.restapi.config.AppConfig.Companion.appConfig
import com.restapi.domain.AuthTokenCache
import com.restapi.domain.Plant
import com.restapi.domain.RefreshHistory
import com.restapi.domain.Session import com.restapi.domain.Session
import com.restapi.domain.Session.database
import com.restapi.domain.Session.objectMapper import com.restapi.domain.Session.objectMapper
import io.javalin.http.BadRequestResponse import io.javalin.http.BadRequestResponse
import io.javalin.http.ContentType import io.javalin.http.ContentType
import io.javalin.http.Context import io.javalin.http.Context
import io.javalin.http.UnauthorizedResponse import io.javalin.http.UnauthorizedResponse
import io.javalin.security.RouteRole import io.javalin.security.RouteRole
import org.apache.http.client.methods.HttpGet
import org.apache.http.impl.client.HttpClients
import org.apache.http.util.EntityUtils
import org.jose4j.jwk.HttpsJwks import org.jose4j.jwk.HttpsJwks
import org.jose4j.jwt.consumer.JwtConsumerBuilder import org.jose4j.jwt.consumer.JwtConsumerBuilder
import org.jose4j.keys.resolvers.HttpsJwksVerificationKeyResolver import org.jose4j.keys.resolvers.HttpsJwksVerificationKeyResolver
@ -21,12 +28,11 @@ import java.net.http.HttpResponse
import java.nio.charset.StandardCharsets import java.nio.charset.StandardCharsets
import java.time.LocalDateTime import java.time.LocalDateTime
import java.time.ZoneId import java.time.ZoneId
import java.time.format.DateTimeFormatter
import java.util.* import java.util.*
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
const val AUTH_TOKEN = "AUTH_TOKEN_V2"
private fun getFormDataAsString(formData: Map<String, String>): String { private fun getFormDataAsString(formData: Map<String, String>): String {
return formData.entries.joinToString("&") { return formData.entries.joinToString("&") {
@ -41,15 +47,12 @@ object Auth {
private val logger = LoggerFactory.getLogger("Auth") private val logger = LoggerFactory.getLogger("Auth")
private val authCache = ConcurrentHashMap<String, AuthEndpoint>() private val authCache = ConcurrentHashMap<String, AuthEndpoint>()
fun getAuthEndpoint(): AuthEndpoint { private fun getAuthEndpoint(): AuthEndpoint {
return authCache.computeIfAbsent("AUTH") { return authCache.computeIfAbsent("AUTH") {
val wellKnown = val wellKnown = "${appConfig.iamUrl()}/realms/${appConfig.iamRealm()}/.well-known/openid-configuration"
"${appConfig.iamUrl()}/realms/${appConfig.iamRealm()}/.well-known/openid-configuration"
val client = HttpClient.newHttpClient() val client = HttpClient.newHttpClient()
val req = HttpRequest.newBuilder() val req = HttpRequest.newBuilder().uri(URI.create(wellKnown)).GET().build()
.uri(URI.create(wellKnown))
.GET().build()
objectMapper.readValue<AuthEndpoint>( objectMapper.readValue<AuthEndpoint>(
client.send(req, HttpResponse.BodyHandlers.ofString()).body() client.send(req, HttpResponse.BodyHandlers.ofString()).body()
@ -58,19 +61,15 @@ object Auth {
} }
private val jwtConsumer = JwtConsumerBuilder() private val jwtConsumer =
.setRequireExpirationTime() JwtConsumerBuilder().setRequireExpirationTime().setAllowedClockSkewInSeconds(30).setRequireSubject()
.setAllowedClockSkewInSeconds(30)
.setRequireSubject()
.setExpectedAudience("account") .setExpectedAudience("account")
.setExpectedIssuer(getAuthEndpoint().issuer) .setExpectedIssuer(getAuthEndpoint().issuer)
.setVerificationKeyResolver(HttpsJwksVerificationKeyResolver(HttpsJwks(getAuthEndpoint().jwksUri))) .setVerificationKeyResolver(HttpsJwksVerificationKeyResolver(HttpsJwks(getAuthEndpoint().jwksUri))).build()
.build()
private val jwtConsumerSkipValidate = JwtConsumerBuilder() private val jwtConsumerSkipValidate =
.setSkipAllValidators() JwtConsumerBuilder().setSkipAllValidators()
.setVerificationKeyResolver(HttpsJwksVerificationKeyResolver(HttpsJwks(getAuthEndpoint().jwksUri))) .setVerificationKeyResolver(HttpsJwksVerificationKeyResolver(HttpsJwks(getAuthEndpoint().jwksUri))).build()
.build()
fun validateAuthToken(authToken: String, skipValidate: Boolean = false): AuthUser { fun validateAuthToken(authToken: String, skipValidate: Boolean = false): AuthUser {
@ -78,17 +77,52 @@ object Auth {
val jwtClaims = if (skipValidate) jwtConsumerSkipValidate.process(authToken) else jwtConsumer.process(authToken) val jwtClaims = if (skipValidate) jwtConsumerSkipValidate.process(authToken) else jwtConsumer.process(authToken)
val userId = jwtClaims.jwtClaims.claimsMap["preferred_username"] as String val userId = jwtClaims.jwtClaims.claimsMap["preferred_username"] as String
val tenant = jwtClaims.jwtClaims.claimsMap["tenant"] as String val tenant = jwtClaims.jwtClaims.claimsMap["tenant"] as String
val plantIds = jwtClaims.jwtClaims.claimsMap["plantIds"] as List<String>
val roles = ((jwtClaims.jwtClaims.claimsMap["realm_access"] as Map<String, Any>)["roles"]) as List<String> val roles = ((jwtClaims.jwtClaims.claimsMap["realm_access"] as Map<String, Any>)["roles"]) as List<String>
val date = Date(jwtClaims.jwtClaims.expirationTime.valueInMillis) val date = Date(jwtClaims.jwtClaims.expirationTime.valueInMillis)
try {
HttpClients.createDefault().use { h ->
//sync plant's from rmc to here, just name and id
for (plantId in plantIds) {
val existing = database.find(Plant::class.java).where().eq("plantId", plantId).findOne()
h.execute(HttpGet("${appConfig.integrationRmc()}/plant?id=${plantId}")).use { r ->
if (r.statusLine.statusCode == 200) {
val response = EntityUtils.toString(r.entity)
if (existing == null) {
database.save(Plant().apply {
this.plantId = plantId
this.plantName = response
this.plantOriginalName = response
})
} else {
existing.apply {
this.plantOriginalName = response
this.save()
}
}
}
}
}
}
} catch (e: Exception) {
logger.warn("Exception in syncing plants", e)
}
return AuthUser( return AuthUser(
userName = userId, userName = userId,
tenant = tenant, tenant = tenant,
roles = roles, roles = roles,
token = authToken, token = authToken,
expiry = LocalDateTime.from(date.toInstant().atZone(ZoneId.systemDefault())) expiry = LocalDateTime.from(date.toInstant().atZone(ZoneId.systemDefault())),
plantIds = plantIds
) )
} }
private val userToTenant = ConcurrentHashMap<String, String>()
fun keys(ctx: Context) { fun keys(ctx: Context) {
ctx.json(Session.jwk()) ctx.json(Session.jwk())
} }
@ -112,9 +146,7 @@ object Auth {
val ep = getAuthEndpoint().tokenEndpoint val ep = getAuthEndpoint().tokenEndpoint
val httpClient = HttpClient.newHttpClient() val httpClient = HttpClient.newHttpClient()
val req = HttpRequest.newBuilder() val req = HttpRequest.newBuilder().uri(URI.create(ep)).POST(
.uri(URI.create(ep))
.POST(
HttpRequest.BodyPublishers.ofString( HttpRequest.BodyPublishers.ofString(
getFormDataAsString( getFormDataAsString(
mapOf( mapOf(
@ -125,61 +157,85 @@ object Auth {
) )
) )
) )
) ).header("Content-Type", "application/x-www-form-urlencoded").build()
.header("Content-Type", "application/x-www-form-urlencoded")
.build()
val message = httpClient.send(req, HttpResponse.BodyHandlers.ofString()).body() val message = httpClient.send(req, HttpResponse.BodyHandlers.ofString()).body()
val atResponse = objectMapper.readValue<AuthTokenResponse>(message) val atResponse = objectMapper.readValue<AuthTokenResponse>(message)
val parsed = validateAuthToken(atResponse.accessToken) val parsed = validateAuthToken(atResponse.accessToken)
//keep track of this for renewal when asked by client
Session.redis.lpush( database.save(AuthTokenCache().apply {
"$AUTH_TOKEN${parsed.userName}", this.userId = parsed.userName
objectMapper.writeValueAsString( this.authToken = atResponse.accessToken
atResponse.copy( this.expiresAt = LocalDateTime.now().plusSeconds(atResponse.expiresIn.toLong())
createdAt = LocalDateTime.now() this.refreshToken = atResponse.refreshToken
) this.refreshExpiresAt = LocalDateTime.now().plusSeconds(atResponse.refreshExpiresIn.toLong())
) this.refreshHistory = arrayListOf()
) })
ctx.result(atResponse.accessToken).contentType(ContentType.TEXT_PLAIN) ctx.result(atResponse.accessToken).contentType(ContentType.TEXT_PLAIN)
} }
fun logout(ctx: Context) {
val authToken = ctx.header("Authorization")?.replace("Bearer ", "")?.replace("Bearer: ", "")?.trim()
?: return
val authUser = validateAuthToken(authToken, skipValidate = true)
logger.warn("User ${authUser.userName} is logging out")
database.updateAll(
database.find(AuthTokenCache::class.java)
.where()
.eq("authToken", authToken)
.findList()
.onEach {
it.loggedOut = true
})
ctx.json(mapOf("status" to true))
}
fun refreshToken(ctx: Context) { fun refreshToken(ctx: Context) {
//refresh authToken //refresh authToken
val authToken = ctx.header("Authorization") val authToken = ctx.header("Authorization")?.replace("Bearer ", "")?.replace("Bearer: ", "")?.trim()
?.replace("Bearer ", "") ?: throw UnauthorizedResponse()
?.replace("Bearer: ", "")
?.trim() ?: throw UnauthorizedResponse()
val authUser = validateAuthToken(authToken, skipValidate = true) val authUser = validateAuthToken(authToken, skipValidate = true)
val client = ctx.queryParam("client") ?: throw BadRequestResponse("client not sent") val client = ctx.queryParam("client") ?: throw BadRequestResponse("client not sent")
val redirectUri = ctx.queryParam("redirectUri") ?: throw BadRequestResponse("redirectUri not sent") val redirectUri = ctx.queryParam("redirectUri") ?: throw BadRequestResponse("redirectUri not sent")
val key = "$AUTH_TOKEN${authUser.userName}" val foundOldAt = database.find(AuthTokenCache::class.java)
val found = Session.redis.llen(key) .where()
logger.warn("for user ${authUser.userName}, found from redis, $key => $found entries") .eq("userId", authUser.userName)
val foundOldAt = (0..found) .eq("expired", false)
.mapNotNull { Session.redis.lindex(key, it) } .eq("loggedOut", false)
.map { objectMapper.readValue<AuthTokenResponse>(it) } .gt("refreshExpiresAt", LocalDateTime.now())
.firstOrNull { it.accessToken == authToken } .findList()
?: throw BadRequestResponse("authToken not found in cache") .onEach {
logger.warn("valid authToken for ${authUser.userName} is ${it.authToken.substring(0..10)}")
}
.firstOrNull {
it.authToken.equals(authToken, ignoreCase = true)
} ?: throw BadRequestResponse("we did not find an entry for this auth token $authToken")
val createdAt = foundOldAt.createdAt ?: throw BadRequestResponse("created at is missing") val createdAt = foundOldAt.createdAt
val expiresAt = createdAt.plusSeconds(foundOldAt.expiresIn + 0L) val expiresAt = foundOldAt.expiresAt
val rtExpiresAt = createdAt.plusSeconds(foundOldAt.refreshExpiresIn + 0L) val rtExpiresAt = foundOldAt.refreshExpiresAt
val now = LocalDateTime.now() val now = LocalDateTime.now()
logger.warn("can we refresh the token for ${authUser.userName}, created = $createdAt expires = $expiresAt, refresh Till = $rtExpiresAt") logger.warn("can we refresh the token for ${authUser.userName}, created = $createdAt expires = $expiresAt, refresh Till = $rtExpiresAt")
val authTokenValid = expiresAt.isAfter(now)
if (authTokenValid) {
ctx.result(authToken).contentType(ContentType.TEXT_PLAIN)
return
}
//we can refresh if at is expired, but we still have time for refresh //we can refresh if at is expired, but we still have time for refresh
if (expiresAt.isBefore(now) && now.isBefore(rtExpiresAt)) { val refreshTokenValid = rtExpiresAt.isAfter(now)
if (refreshTokenValid) {
logger.warn("We can refresh the token for ${authUser.userName}, expires = $expiresAt, refresh Till = $rtExpiresAt") logger.warn("We can refresh the token for ${authUser.userName}, expires = $expiresAt, refresh Till = $rtExpiresAt")
val ep = getAuthEndpoint().tokenEndpoint val ep = getAuthEndpoint().tokenEndpoint
val httpClient = HttpClient.newHttpClient() val httpClient = HttpClient.newHttpClient()
val req = HttpRequest.newBuilder() val req = HttpRequest.newBuilder().uri(URI.create(ep)).POST(
.uri(URI.create(ep))
.POST(
HttpRequest.BodyPublishers.ofString( HttpRequest.BodyPublishers.ofString(
getFormDataAsString( getFormDataAsString(
mapOf( mapOf(
@ -190,33 +246,33 @@ object Auth {
) )
) )
) )
) ).header("Content-Type", "application/x-www-form-urlencoded").build()
.header("Content-Type", "application/x-www-form-urlencoded")
.build()
val message = httpClient.send(req, HttpResponse.BodyHandlers.ofString()).body() val message = httpClient.send(req, HttpResponse.BodyHandlers.ofString()).body()
val atResponse = objectMapper.readValue<AuthTokenResponse>(message) val atResponse = objectMapper.readValue<AuthTokenResponse>(message)
val parsed = validateAuthToken(atResponse.accessToken)
Session.redis.lpush( foundOldAt.authToken = atResponse.accessToken
"AUTH_TOKEN_${parsed.userName}", foundOldAt.expiresAt = LocalDateTime.now().plusSeconds(atResponse.expiresIn.toLong())
objectMapper.writeValueAsString( foundOldAt.refreshExpiresAt = LocalDateTime.now().plusSeconds(atResponse.refreshExpiresIn.toLong())
atResponse.copy(createdAt = LocalDateTime.now()) foundOldAt.refreshToken = atResponse.refreshToken
) foundOldAt.refreshHistory = (foundOldAt.refreshHistory ?: arrayListOf()).apply {
) add(RefreshHistory().apply {
oldAt = authUser.token
oldExpiryAt = expiresAt.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
newAt = atResponse.accessToken
newExpiryAt = LocalDateTime.now().plusSeconds(atResponse.expiresIn.toLong()).format(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
this.createdAt = LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
})
}
database.update(foundOldAt)
ctx.result(atResponse.accessToken).contentType(ContentType.TEXT_PLAIN) ctx.result(atResponse.accessToken).contentType(ContentType.TEXT_PLAIN)
} else { } else {
//at is still valid //at is still valid
if (expiresAt.isAfter(now)) {
logger.warn("Still valid, the token for ${authUser.userName}, will expire at $expiresAt")
ctx.result(foundOldAt.accessToken).contentType(ContentType.TEXT_PLAIN)
} else {
//we have exceeded the refresh time, so we shall ask the user to login again //we have exceeded the refresh time, so we shall ask the user to login again
logger.warn("We can't refresh the token for ${authUser.userName}, as refresh-time [$rtExpiresAt] is expired") logger.warn("We can't refresh the token for ${authUser.userName}, as refresh-time [$rtExpiresAt] is expired")
throw UnauthorizedResponse() throw UnauthorizedResponse()
} }
} }
}
} }
@ -225,7 +281,8 @@ data class AuthUser(
val tenant: String, val tenant: String,
val roles: List<String>, val roles: List<String>,
val token: String, val token: String,
val expiry: LocalDateTime val expiry: LocalDateTime,
val plantIds: List<String>
) )
enum class Action { enum class Action {
@ -235,7 +292,7 @@ enum class Action {
sealed class Role { sealed class Role {
open class Standard(vararg val action: Action) : Role() open class Standard(vararg val action: Action) : Role()
data object Entity : Role() data object Entity : Role()
data class Explicit(val roles: List<String>) : Role() open class Explicit(vararg val roles: String) : Role()
data object DbOps : Role() data object DbOps : Role()
} }

View File

@ -0,0 +1,49 @@
package com.restapi.controllers
import com.restapi.domain.DocType
import com.restapi.domain.Document
import com.restapi.domain.Session
import io.javalin.http.Context
import io.javalin.http.HttpStatus
import io.javalin.http.NotFoundResponse
import io.javalin.http.bodyAsClass
object DocumentCtrl {
fun get(ctx: Context) {
val id = ctx.pathParam("id")
val doc = Session.database.find(Document::class.java, id) ?: throw NotFoundResponse("no doc found with id $id")
ctx.status(HttpStatus.OK)
ctx.json(doc)
}
fun create(ctx: Context) {
val doc = ctx.bodyAsClass<Document>()
Session.database.save(doc)
ctx.status(HttpStatus.CREATED)
ctx.json(doc)
}
fun print(ctx: Context) {
//would be handled in the frontend ??
}
fun delete(ctx: Context) {
val id = ctx.pathParam("id")
val doc = Session.database.find(Document::class.java, id) ?: throw NotFoundResponse("no document found with id $id")
Session.database.delete(doc)
ctx.status(HttpStatus.OK)
}
fun getWithRefId(ctx: Context) {
//fetches a particular doc (po, quote) with ref id
val refId = ctx.pathParam("refId")
val doc = Session.database.find(Document::class.java)
.where()
.eq("typeOfDoc", DocType.valueOf(ctx.pathParam("type")))
.eq("refIdOfDoc", refId)
?: throw NotFoundResponse("no doc found for refId $refId")
ctx.status(HttpStatus.OK)
ctx.json(doc)
}
}

View File

@ -6,108 +6,114 @@ import com.fasterxml.jackson.databind.JsonDeserializer
import com.fasterxml.jackson.databind.JsonNode import com.fasterxml.jackson.databind.JsonNode
import com.fasterxml.jackson.databind.annotation.JsonDeserialize import com.fasterxml.jackson.databind.annotation.JsonDeserialize
import com.restapi.domain.* import com.restapi.domain.*
import com.restapi.domain.Product import com.restapi.domain.Session.currentRoles
import com.restapi.domain.PurchaseOrder
import com.restapi.domain.Quotation
import com.restapi.domain.Session.currentUser import com.restapi.domain.Session.currentUser
import com.restapi.domain.Session.database import com.restapi.domain.Session.database
import com.restapi.domain.Session.findDataModelByEntityAndUniqId import com.restapi.domain.Session.findDataModelByEntityAndUniqId
import com.restapi.domain.Vendor import com.restapi.domain.Session.objectMapper
import com.restapi.integ.Scripting import com.restapi.integ.Scripting
import io.ebean.CallableSql
import io.ebean.RawSqlBuilder import io.ebean.RawSqlBuilder
import io.javalin.http.* import io.javalin.http.BadRequestResponse
import org.apache.poi.ss.usermodel.WorkbookFactory import io.javalin.http.Context
import io.javalin.http.HttpStatus
import io.javalin.http.bodyAsClass
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import java.io.File
import java.io.FileInputStream
import java.io.InputStream
import java.sql.Types
import java.time.LocalDate import java.time.LocalDate
import java.time.LocalDateTime import java.time.LocalDateTime
import java.time.LocalTime import java.time.LocalTime
import java.time.format.DateTimeFormatter import java.time.format.DateTimeFormatter
import java.time.format.DateTimeParseException
enum class QueryParamType { enum class QueryParamType {
STRING, NUMBER, DATETIME, DATE STRING, NUMBER, DATETIME, DATE
} }
data class RawQuery( @JsonDeserialize(using = QueryParamDeSerializer::class)
val sql: String,
val params: Map<String, QueryParam>
)
data class QueryById(
val params: List<QueryParam>
)
@JsonDeserialize(using = QueryByIdParamsDeSerializer::class)
sealed class QueryParam { sealed class QueryParam {
data class Simple(val simple: String) : QueryParam() data class StrParam(val str: String) : QueryParam()
data class Complex(val type: QueryParamType, val value: String) : QueryParam() { data class NumberParam(val nbr: Number) : QueryParam()
data class BooleanParam(val bool: Boolean) : QueryParam()
data class ComplexParam(val type: QueryParamType, val value: String) : QueryParam() {
fun getValueComplex(): Any { fun getValueComplex(): Any {
return when (type) { return when (type) {
QueryParamType.STRING -> value QueryParamType.STRING -> value
QueryParamType.NUMBER -> if (value.matches(Regex("\\d+"))) value.toLong() else value.toDouble() QueryParamType.NUMBER -> (if (value.matches(Regex("\\d+"))) value.toLongOrNull() else value.toDoubleOrNull())
QueryParamType.DATETIME -> LocalDateTime.parse( ?: throw IllegalArgumentException("$value is not a number")
QueryParamType.DATETIME -> try {
LocalDateTime.parse(
value, value,
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss") DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
) )
} catch (e: DateTimeParseException) {
throw IllegalArgumentException("unable to parse $value as datetime")
}
QueryParamType.DATE -> LocalDate.parse(value, DateTimeFormatter.ofPattern("yyyy-MM-dd")) QueryParamType.DATE -> try {
LocalDate.parse(value, DateTimeFormatter.ofPattern("yyyy-MM-dd"))
} catch (e: DateTimeParseException) {
throw IllegalArgumentException("unable to parse $value as date")
}
} }
} }
} }
fun getValue(): Any { fun getValue(): Any {
return when (this) { return when (this) {
is Complex -> getValueComplex() is ComplexParam -> getValueComplex()
is Simple -> simple is StrParam -> str
is NumberParam -> nbr
is BooleanParam -> bool
} }
} }
} }
class QueryByIdParamsDeSerializer : JsonDeserializer<QueryParam>() { class QueryParamDeSerializer : JsonDeserializer<QueryParam>() {
override fun deserialize(p: JsonParser, ctxt: DeserializationContext?): QueryParam { override fun deserialize(p: JsonParser, ctxt: DeserializationContext?): QueryParam {
val node = p.readValueAsTree<JsonNode>() val node = p.readValueAsTree<JsonNode>()
return if (node.isTextual) { return if (node.isTextual) {
QueryParam.Simple(node.asText()) QueryParam.StrParam(node.asText())
} else { } else if (node.isNumber) {
QueryParam.Complex( QueryParam.NumberParam(node.numberValue())
} else if (node.isBoolean) {
QueryParam.BooleanParam(node.booleanValue())
} else if (node.isObject) {
QueryParam.ComplexParam(
QueryParamType.valueOf(node.get("type").textValue()), QueryParamType.valueOf(node.get("type").textValue()),
node.get("value").textValue(), node.get("value").textValue(),
) )
} else {
throw BadRequestResponse("unable to find valid query param for $node")
} }
} }
} }
enum class ResultType {
INTEGER, DECIMAL, STRING, DATETIME, ARRAY, OBJECT
}
data class RejectAction(val reason: String)
data class StoredProcedure(val input: Map<String, Any>, val output: Map<String, ResultType> = hashMapOf())
object Entities { object Entities {
private val logger = LoggerFactory.getLogger("Entities") private val logger = LoggerFactory.getLogger("Entities")
private val OK = mapOf("status" to true)
fun delete(ctx: Context) { fun delete(ctx: Context) {
val e = database.findDataModelByEntityAndUniqId(ctx.pathParam("entity"), ctx.pathParam("id")) val e = database.findDataModelByEntityAndUniqId(ctx.pathParam("entity"), ctx.pathParam("id"))
e.deletedBy = Session.currentUser() e.deletedBy = Session.currentUser()
e.deletedOn = LocalDateTime.now() e.deletedOn = LocalDateTime.now()
e.update() e.update()
e.delete() e.delete()
ctx.json(OK)
} }
fun patch(ctx: Context) { fun patch(ctx: Context) {
val e = database.findDataModelByEntityAndUniqId(ctx.pathParam("entity"), ctx.pathParam("id")) val e = database.findDataModelByEntityAndUniqId(ctx.pathParam("entity"), ctx.pathParam("id"))
val pv = ctx.bodyAsClass<Map<String, Any>>() val pv = ctx.bodyAsClass<Map<String, Any>>()
verifyKeys(pv)
pv.forEach { (key, value) -> pv.forEach { (key, value) ->
e.data[key] = value; e.data[key] = value;
} }
e.update() e.update()
ctx.json(OK)
} }
fun update(ctx: Context) { fun update(ctx: Context) {
@ -115,121 +121,129 @@ object Entities {
val e = database.findDataModelByEntityAndUniqId(ctx.pathParam("entity"), ctx.pathParam("id")) val e = database.findDataModelByEntityAndUniqId(ctx.pathParam("entity"), ctx.pathParam("id"))
val newData = ctx.bodyAsClass<Map<String, Any>>() val newData = ctx.bodyAsClass<Map<String, Any>>()
verifyKeys(newData)
if (purgeExisting) { if (purgeExisting) {
e.data.clear(); e.data.clear();
} }
e.data.putAll(newData) e.data.putAll(newData)
e.update() e.update()
ctx.json(OK)
} }
fun action(ctx: Context) {} private fun verifyKeys(newData: Map<String, Any>) {
fun approve(ctx: Context) { newData.keys.forEach { key ->
approveOrReject(ctx, ApprovalStatus.APPROVED) if (!SafeStringDeserializer.isSafe(key)) throw IllegalArgumentException("$key is invalid from $newData ")
}
} }
fun reject(ctx: Context) {
approveOrReject(ctx, ApprovalStatus.REJECTED) fun search(ctx: Context) {
val sql = ctx.bodyAsClass<SearchParams>()
verifyKeys(sql.params)
val entity = ctx.pathParam("entity").lowercase()
val noCreatedFilter = currentRoles().contains("ROLE_ADMIN") || sql.createdBy.isNullOrEmpty()
val createdFilter = if (noCreatedFilter) "" else "and created_by = :cBy"
val searchJsonMap = sql.params.map { e -> Pair(e.key, e.value.getValue()) }
.filter {
val second = it.second
if (second is String) {
second.isNotEmpty()
} else {
true
} }
private fun approveOrReject(ctx: Context, rejected: ApprovalStatus) {
val e = database.findDataModelByEntityAndUniqId(ctx.pathParam("entity"), ctx.pathParam("id"))
val reject = ctx.bodyAsClass<RejectAction>()
e.approvalStatus = rejected
e.comments.add(Comments(text = reject.reason, by = currentUser()))
e.save()
} }
.toMap()
fun executeScript(ctx: Context) { logger.warn("convert ${sql.params} to $searchJsonMap")
val name = ctx.pathParam("name") val fl = database.find(DataModel::class.java)
val file = ctx.pathParam("file")
val params = ctx.bodyAsClass<Map<String, Any>>()
ctx.json(
Scripting.execute(file, name, params)
)
}
fun executeStoredProcedure(ctx: Context) {
val name = ctx.pathParam("name")
val sp = ctx.bodyAsClass<StoredProcedure>()
val inputParams = sp.input.entries.toList()
val outputParams = sp.output.entries.toList()
val placeholders = (0..inputParams.size + 1).joinToString(",") { "?" }
val sql = "{call $name($placeholders)}"
val cs: CallableSql = database.createCallableSql(sql)
inputParams.forEachIndexed { index, entry ->
cs.setParameter(index + 1, entry.value)
}
cs.setParameter(inputParams.size + 1, Session.currentTenant())
outputParams.forEachIndexed { idx, entry ->
when (entry.value) {
ResultType.INTEGER -> cs.registerOut(idx + 1, Types.INTEGER)
ResultType.DECIMAL -> cs.registerOut(idx + 1, Types.DOUBLE)
ResultType.STRING -> cs.registerOut(idx + 1, Types.VARCHAR)
ResultType.DATETIME -> cs.registerOut(idx + 1, Types.DATE)
ResultType.ARRAY -> cs.registerOut(idx + 1, Types.ARRAY)
ResultType.OBJECT -> cs.registerOut(idx + 1, Types.JAVA_OBJECT)
}
}
val done = database.execute(cs)
val output = outputParams.mapIndexed { index, entry ->
Pair(entry.key, cs.getObject(index + 1))
}.toMap()
ctx.json(
mapOf(
"done" to done,
"output" to output
)
)
}
fun sqlQueryRaw(ctx: Context) {
val sql = ctx.bodyAsClass<RawQuery>()
logger.warn("running sql ${sql.sql}, with params ${sql.params}")
ctx.json(
database.find(DataModel::class.java)
.setRawSql( .setRawSql(
RawSqlBuilder.parse(sql.sql).create() RawSqlBuilder.parse(
).apply { """
sql.params.forEach { (t, u) -> select sys_pk,
setParameter(t, u.getValue()) deleted_on,
} current_approval_level,
} required_approval_levels,
.findList() deleted,
version,
created_at,
modified_at,
deleted_by,
approval_status,
tags,
comments,
tenant_id,
unique_identifier,
entity_name,
data,
created_by,
modified_by
from data_model
where entity_name = :e
and created_at between :from and :to
and data @> cast(:search as jsonb)
and deleted = false
$createdFilter
order by ${sql.orderBy}
""".trimIndent()
).create()
) )
.setParameter("from", sql.dateRange.first())
} .setParameter("to", sql.dateRange.last().plusDays(1))
.setParameter("e", entity)
fun sqlQueryById(ctx: Context) { .setParameter("search", objectMapper.writeValueAsString(searchJsonMap))
val sql = ctx.bodyAsClass<QueryById>()
val sqlId = ctx.pathParam("id")
logger.warn("running sqlId $sqlId, with params ${sql.params}")
val entity = ctx.pathParam("entity")
val query = database.find(SqlModel::class.java)
.where()
.eq("entityName", entity)
.eq("sqlId", sqlId)
.findOne() ?: throw NotFoundResponse("sql not found for $entity, $sqlId")
ctx.json(
database.find(DataModel::class.java)
.setRawSql(RawSqlBuilder.parse(query.sql).create())
.apply { .apply {
sql.params.forEachIndexed { index, entry -> if (!noCreatedFilter) {
setParameter(index + 1, entry.getValue()) logger.warn("Set Created By Filter to ${currentUser()}")
setParameter("cBy", currentUser())
} }
} }
.findList() .findList()
)
logger.warn("Search jsonMap [$searchJsonMap] => ${fl.size} entries")
ctx.json(fl)
}
fun getAll(ctx: Context) {
val entity = ctx.pathParam("entity").uppercase()
val pageNo = ctx.queryParam("pageNo")?.toInt() ?: 1
val perPage = ctx.queryParam("perPage")?.toInt() ?: 100
val cnt = database.find(DataModel::class.java)
.where()
.eq("entityName", entity.lowercase())
.setFirstRow((pageNo - 1) * perPage)
.setMaxRows(perPage)
.orderBy("sysPk desc")
.findPagedList()
ctx.json(cnt)
}
fun getNextSeqNo(ctx: Context) {
val entity = ctx.pathParam("entity").uppercase()
val prefix = "$entity/"
val plantId = ctx.queryParam("plantId") ?: throw BadRequestResponse("plantId not sent")
val plant = database.find(Plant::class.java)
.where()
.eq("plantId", plantId)
.findOne() ?: throw BadRequestResponse("plant missing for $plantId")
val inventoryPrefix = plant.prefixes?.get(entity) ?: prefix
val cnt = (database.find(DataModel::class.java)
.where()
.eq("entityName", entity.lowercase())
.eq("data->>'plantId'", plantId)
.findCount() + 1)
.toString()
.padStart(6, '0')
val seq = SequenceNumber(inventoryPrefix + cnt)
ctx.json(seq).status(HttpStatus.OK)
} }
fun view(it: Context) { fun view(it: Context) {
@ -245,6 +259,25 @@ object Entities {
) )
} }
data class Execute(val script: String, val fn: String, val params: Map<String, Any>)
fun execute(ctx: Context) {
val entity = ctx.pathParam("entity")
val body = ctx.bodyAsClass<Execute>()
//may be approval flow is configured?
val setupEntity = database.find(EntityModel::class.java)
.where()
.eq("name", entity)
.findOne()
ctx.json(
Scripting.execute(body.script, body.fn, body.params, setupEntity)
)
}
fun create(ctx: Context) { fun create(ctx: Context) {
val entity = ctx.pathParam("entity") val entity = ctx.pathParam("entity")
@ -264,6 +297,7 @@ object Entities {
} }
this.approvalStatus = ApprovalStatus.APPROVED this.approvalStatus = ApprovalStatus.APPROVED
} }
verifyKeys(dataModel.data)
database.save( database.save(
dataModel.apply { dataModel.apply {
@ -324,36 +358,26 @@ object Entities {
} }
} }
} }
if (!setupEntity.preSaveScript.isNullOrEmpty()) {
val ok = Scripting.execute(setupEntity.preSaveScript!!, "preSave", this) as Boolean
if (!ok) {
throw BadRequestResponse("PreSave Failed")
}
}
if (setupEntity.approvalLevels > 0) {
this.approvalStatus = ApprovalStatus.PENDING
this.requiredApprovalLevels = setupEntity.approvalLevels
}
} }
} }
) )
if (setupEntity != null && !setupEntity.postSaveScript.isNullOrEmpty()) { if (setupEntity != null && !setupEntity.postSaveScript.isNullOrEmpty()) {
Scripting.execute(setupEntity.postSaveScript!!, "postSave", dataModel) Scripting.execute(setupEntity.postSaveScript!!, "postSave", dataModel, setupEntity)
} }
database.save( database.save(
AuditLog().apply { AuditLog().apply {
auditType = AuditType.CREATE this.auditType = AuditType.CREATE
this.entity = entity this.entity = entity
uniqueIdentifier = dataModel.uniqueIdentifier this.uniqueIdentifier = dataModel.uniqueIdentifier
this.data = dataModel.data this.data = dataModel.data
} }
) )
ctx.json(OK)
} }
private fun isValidDate(f: String) = try { private fun isValidDate(f: String) = try {
@ -378,251 +402,12 @@ object Entities {
} }
} }
object PurchaseOrder { data class SearchParams(
fun get(ctx :Context){ val params: Map<String, QueryParam> = mapOf(),
val id = ctx.pathParam("id") val createdBy: String?,
val po = database.find(PurchaseOrder::class.java, id) ?: throw NotFoundResponse("po not found for $id") val dateRange: List<LocalDate> = listOf(LocalDate.now().minusDays(7), LocalDate.now()),
val orderBy: String = "sysPk asc"
ctx.json(po)
}
fun create(ctx :Context){
val po = ctx.bodyAsClass<PurchaseOrder>()
database.save(po)
ctx.result("po created")
}
fun approve(ctx :Context){
val id = ctx.pathParam("id")
val po = database.find(PurchaseOrder::class.java, id) ?: throw NotFoundResponse("po not found for $id")
po.approvalStatus = ApprovalStatus.APPROVED
po.save()
ctx.result("po with id $id approved")
//reject all other pos pertaining to the same tx ??
}
fun reject(ctx :Context){
val id = ctx.pathParam("id")
val po = database.find(PurchaseOrder::class.java, id) ?: throw NotFoundResponse("po not found for $id")
po.approvalStatus = ApprovalStatus.REJECTED
po.save()
ctx.result("po with id $id rejected")
}
fun quoteReference(ctx :Context){
//gets the quote reference on which this po is based on
val id = ctx.pathParam("id")
val quote = database.find(Quotation::class.java)
.where()
.eq("referenceQuotation", id)
?: throw NotFoundResponse("reference quotation not found for po $id")
ctx.json(quote)
}
}
data class ProductSearch(
var isSort: String? = null
) )
object ProductCtrl { data class SequenceNumber(val number: String)
fun get(ctx :Context){
val hsnCode = ctx.pathParam("hsnCode")
val product = database.find(Product::class.java, hsnCode) ?: throw NotFoundResponse("Product not found for $hsnCode")
ctx.json(product)
}
fun getAll(ctx: Context){
val productList = Session.database.find(Product::class.java)
.findList()
//.sortedBy { it.hsnCode }
ctx.json(productList)
}
fun create(ctx :Context){
val product = ctx.bodyAsClass<Product>()
database.save(product)
}
fun delete(ctx: Context) {
val id = ctx.pathParam("id")
val product = database.delete(Product::class.java, id)
}
fun patch(ctx: Context) {
}
fun update(ctx: Context) {
}
fun prodExcel(it: Context) {
val product = database.find(Product::class.java).findList()
it.result(CreateExcel(product)).contentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
.header("Content-Disposition", "attachment; filename=\"product.xlsx\"")
}
data class ProductList(
val name: String,
val description: String,
val hsnCode: String,
val uom: String?,
)
fun excelToDb(it: Context){
val inputStream = FileInputStream("C:\\Users\\vinay\\IdeaProjects\\readymixerp_modules_api_git\\Untitled 1.xlsx")
val workbook = WorkbookFactory.create(inputStream)
val workSheet = workbook.getSheetAt(0)
val dataList = mutableListOf<ProductList>()
for (row in workSheet) {
val cell1Value = row.getCell(0).stringCellValue
val cell2Value = row.getCell(1).stringCellValue
val cell3Value = row.getCell(2).stringCellValue
val cell4Value = row.getCell(3).stringCellValue
val data = ProductList(cell1Value, cell2Value, cell3Value, cell4Value)
dataList.add(data)
}
database.saveAll(dataList)
}
}
object Quotation {
fun get(ctx :Context){
val id = ctx.pathParam("id")
val quote = database.find(Quotation::class.java, id) ?: throw NotFoundResponse("quote not found for $id")
ctx.json(quote)
}
fun create(ctx :Context){
val quote = ctx.bodyAsClass<Quotation>()
//we have to check if the quotation created date is below the expiry of rfq
val rfq = database.find(com.restapi.domain.ReqForQuote::class.java)
.where()
.eq("reqForQuoteNum", quote.reqForQuoteNum)
.findOne()
if(rfq != null){
//compare dates
if(quote.quoteDate!! <= rfq.openTill) {
//valid
database.save(quote)
ctx.result("quote created")
}else {
ctx.result("request for quote closed")
}
}else {
throw NotFoundResponse("request for quote not found for this quotation")
}
}
fun delete(ctx :Context){
val id = ctx.pathParam("id")
val quote = database.find(Quotation::class.java, id) ?: throw NotFoundResponse("quote not found for id $id")
quote.delete()
ctx.result("quote with $id deleted")
}
fun generatePO(ctx :Context){
//user should be redirected to a po form submission with prefilled values
//create a PO object with values from the quote and then send it as body to vendor/po/create ??
}
fun reqForQuote(ctx :Context){
val reqForQuoteNum = ctx.pathParam(("rfqNum"))
val rfq = database.find(RequestForQuote::class.java)
.where()
.eq("reqForQuoteNum", reqForQuoteNum)
?: throw NotFoundResponse("request for quote not found for this quotation")
ctx.json(rfq)
}
}
object Document {
fun get(ctx :Context){
val id = ctx.pathParam("id")
val doc = database.find(Document::class.java, id) ?: throw NotFoundResponse("no doc found with id $id")
ctx.json(doc)
}
fun create(ctx :Context){
val doc = ctx.bodyAsClass<Document>()
database.save(doc)
ctx.result("doc created")
}
fun print(ctx :Context){
//would be handled in the frontend ??
}
fun delete(ctx :Context){
val id = ctx.pathParam("id")
val doc = database.find(Document::class.java, id) ?: throw NotFoundResponse("no doc found with id $id")
//doc.delete()
ctx.result("document deleted")
}
fun getWithRefId(ctx :Context){
//fetches a particular doc (po, quote) with ref id
val refId = ctx.pathParam("refId")
val doc = database.find(Document::class.java)
.where()
.eq("refId", refId)
?: throw NotFoundResponse("no doc found for refId $refId")
ctx.json(doc)
}
}
object Vendor {
fun get(ctx :Context){
val id = ctx.pathParam("id")
val vendor = database.find(Vendor::class.java, id) ?: throw NotFoundResponse("no vendor found with id $id")
ctx.json(vendor)
}
fun create(ctx :Context){
val vendor = ctx.bodyAsClass<Vendor>()
database.save(vendor)
ctx.result("vendor created")
}
fun update(ctx :Context){
}
fun delete(ctx :Context){
}
fun getQuotes(ctx :Context){
val id = ctx.pathParam("id")
val quotes = database.find(Quotation::class.java)
.where()
.eq("vendor", id)
.findList()
ctx.json(quotes)
}
fun getPos(ctx :Context){
val id = ctx.pathParam("id")
val pos = database.find(PurchaseOrder::class.java)
.where()
.eq("vendor", id)
.findList()
ctx.json(pos)
}
fun rate(ctx :Context){
val id = ctx.pathParam("id")
val rating = ctx.pathParam("rating").toDouble()
val vendor = database.find(Vendor::class.java, id) ?: throw NotFoundResponse("vendor not found for id $id")
//could place some rating validation checks
vendor.rating = rating
vendor.save()
ctx.result("rating changed")
}
}
object RequestForQuote {
fun create(ctx :Context) {
val rfq = ctx.bodyAsClass<ReqForQuote>()
database.save(rfq)
//ctx.result("request for quote created")
//ctx.json(rfq)
//ctx.status(HttpStatus.CREATED)
//ctx.json("asss")
}
fun get(ctx :Context){
val id = ctx.pathParam("id")
val rfq = database.find(ReqForQuote::class.java, id) ?: throw NotFoundResponse("request for quote not found for id $id")
ctx.json(rfq)
}
fun update(ctx :Context){
//shuld we compare the new body fields with preexisting ones and prepare a sql query to update those fields??
}
}

View File

@ -1,151 +1,748 @@
package com.restapi.controllers package com.restapi.controllers
import com.fasterxml.jackson.databind.DeserializationFeature import com.restapi.domain.*
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule import com.restapi.domain.Session.currentUserPlants
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import com.restapi.domain.Session.database
import com.restapi.domain.Product import org.apache.poi.hssf.usermodel.DVConstraint
import org.apache.poi.ss.usermodel.Cell import org.apache.poi.hssf.usermodel.HSSFDataValidation
import org.apache.poi.ss.usermodel.CellType import org.apache.poi.hssf.usermodel.HSSFSheet
import org.apache.poi.ss.usermodel.Row import org.apache.poi.hssf.usermodel.HSSFWorkbook
import org.apache.poi.ss.usermodel.WorkbookFactory import org.apache.poi.ss.usermodel.*
import org.apache.poi.xssf.usermodel.XSSFWorkbook import org.apache.poi.ss.util.CellRangeAddressList
import java.io.ByteArrayInputStream import java.io.File
import java.io.ByteArrayOutputStream import java.io.FileOutputStream
import java.io.FileInputStream import java.text.SimpleDateFormat
import java.io.InputStream import java.time.LocalDate
import java.time.ZoneId
import java.time.format.DateTimeFormatter
import java.util.*
import java.util.concurrent.ConcurrentHashMap
fun CreateExcel(productList: List<Product>): InputStream { fun createHeaderRow(cols: List<String>, sh: HSSFSheet, wb: Workbook) {
val wb = XSSFWorkbook() val boldFont = wb.createFont()
boldFont.bold = true
val style = wb.createCellStyle()
style.setFont(boldFont)
style.locked = true
sh.createRow(0).apply {
cols.forEachIndexed { index, value ->
val cell = createCell(index)
cell.setCellValue(value)
cell.setCellStyle(style)
}
}
}
fun String.parseDate(format: String): Date? {
val locale = Locale.getDefault()
return try {
SimpleDateFormat(format, locale).parse(this)
} catch (e: Exception) {
null
}
}
fun dateFromCellHelper(cell: Cell): LocalDate? {
val date = when (cell.cellType) {
CellType.STRING -> cell.stringCellValue.parseDate("yyyy-MM-dd")
CellType.NUMERIC -> {
if (DateUtil.isCellDateFormatted(cell)) {
cell.getDateCellValue()
} else {
null
}
}
else -> null
}
return date?.toInstant()?.atZone(ZoneId.systemDefault())?.toLocalDate()
}
fun stringFromCellHelper(cell: Cell): String {
val string = when (cell.cellType) {
CellType.NUMERIC -> cell.numericCellValue.toString()
CellType.STRING -> cell.stringCellValue
else -> ""
}
return string
}
fun doubleFromCellHelper(cell: Cell): Double {
val double = when (cell.cellType) {
CellType.NUMERIC -> cell.numericCellValue
CellType.STRING -> cell.stringCellValue.toDoubleOrNull()
else -> 0.0
}
return double ?: 0.0
}
fun longIntFromCellHelper(cell: Cell): Long {
val long = when (cell.cellType) {
CellType.NUMERIC -> cell.numericCellValue.toLong()
CellType.STRING -> cell.stringCellValue.toLong()
else -> 0
}
return long
}
enum class FileType {
QUOTES, POS, VENDORS, PRODS, DOCS
}
enum class EnumFor {
UOM, DocType
}
fun saveExcelFileLocally(fileName: String, wb: Workbook) {
val path = "./excel"
val file = File(path, fileName)
if (!file.parentFile.exists()) {
file.parentFile.mkdirs()
}
val out = FileOutputStream(file)
wb.use {
it.write(out)
}
out.close()
}
fun TemplateExcelFile(fileType: FileType) {
when (fileType) {
FileType.QUOTES -> {
val headers: List<String> = listOf(
"Quotation Number",
"Date",
"Open Till",
"Product Id",
"Product Name",
"Product Unit Price",
"Quantity",
"Vendor Name",
"Vendor Address",
"RFQ Number",
"Total Amount",
"Terms and Conditions"
)
val wb = HSSFWorkbook()
val sh = wb.createSheet() val sh = wb.createSheet()
val rows: Row = sh.createRow(0) createHeaderRow(headers, sh, wb)
rows.createCell(0).setCellValue("Name") saveExcelFileLocally("Quotes_Template.xls", wb)
rows.createCell(1).setCellValue("Description")
rows.createCell(2).setCellValue("HSN")
rows.createCell(3).setCellValue("UOM")
var rowNum = 1
for (product in productList) {
val row: Row = sh.createRow(rowNum++)
row.createCell(0).setCellValue(product.name)
row.createCell(1).setCellValue(product.description)
row.createCell(2).setCellValue(product.hsnCode)
val uomCell: Cell = row.createCell(3)
uomCell.setCellValue(product.uom?.name ?: "")
} }
val baos = ByteArrayOutputStream() FileType.POS -> {
wb.write(baos) val headers: List<String> = listOf(
wb.close() "Number",
"Date",
"Open Till",
"Reference Quotation Number",
"Vendor Name",
"Vendor Address",
"Product Id",
"Product Name",
"Unit Price",
"Quantity",
"Total Amount",
"Terms and Conditions"
)
val wb = HSSFWorkbook()
val sh = wb.createSheet()
createHeaderRow(headers, sh, wb)
saveExcelFileLocally("Purchase_Order_Template.xls", wb)
}
return ByteArrayInputStream(baos.toByteArray()) FileType.VENDORS -> {
val headers: List<String> = listOf(
"Name", "MSME", "GST Number", "Address", "Rating", "Contact Name", "Contact Email", "Contact Mobile"
)
val wb = HSSFWorkbook()
val sh = wb.createSheet()
createHeaderRow(headers, sh, wb)
saveExcelFileLocally("Vendors_Template.xls", wb)
}
FileType.PRODS -> {
val headers: List<String> = listOf("Id", "Name", "Description", "HSN Code", "UOM")
val wb = HSSFWorkbook()
val sh = wb.createSheet()
createHeaderRow(headers, sh, wb)
val r0 = CellRangeAddressList(0, 1000, 4, 4)
val dv0 = HSSFDataValidation(
r0, DVConstraint.createExplicitListConstraint(arrayOf("LTR", "MTR", "NOS", "ALL"))
).apply {
suppressDropDownArrow = true
}
sh.addValidationData(dv0)
saveExcelFileLocally("Products_Template.xls", wb)
}
FileType.DOCS -> {
}
}
} }
data class validateExcel( fun exportQuotations(quotes: List<Quotation>) {
val name: String, val wb = HSSFWorkbook()
val description: String, val sh = wb.createSheet()
val hsnCode: String,
val ok: Boolean,
val err: String,
)
val app_common_om = jacksonObjectMapper().apply { val headers: List<String> = listOf(
registerModule(JavaTimeModule()) "Quotation Number",
configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) "Date",
"Open Till",
"Product Id",
"Product Name",
"Product Unit Price",
"Quantity",
"Vendor Name",
"Vendor Address",
"RFQ Number",
"Total AMount",
"Terms and Conditions"
)
createHeaderRow(headers, sh, wb)
var rowCnt = 1
for (quote in quotes) {
val prodCnt = quote.products.size
for (j in 0..<prodCnt) {
val row = sh.createRow(rowCnt++)
var i = 0;
row.createCell(i++).setCellValue(quote.quoteNum)
row.createCell(i++).setCellValue(quote.quoteDate)
row.createCell(i++).setCellValue(quote.validTill)
//6 would be repeated
row.createCell(i++).setCellValue(quote.products[j].productId.toString())
row.createCell(i++).setCellValue(quote.products[j].productName)
row.createCell(i++).setCellValue(quote.products[j].unitPrice)
row.createCell(i++).setCellValue(quote.products[j].quantity)
row.createCell(i++).setCellValue(quote.vendor?.name)
row.createCell(i++).setCellValue(quote.vendor?.address)
row.createCell(i++).setCellValue(quote.reqForQuoteNum)
row.createCell(i++).setCellValue(quote.totalAmount)
row.createCell(i++).setCellValue(quote.tnc?.joinToString(";"))
}
}
saveExcelFileLocally("Quotes.xls", wb)
} }
fun ExcelRead(): String{ fun exportVendors(vendors: List<Vendor>) {
val inputStream = FileInputStream("C:\\Users\\vinay\\IdeaProjects\\readymixerp_modules_api_git\\Untitled 1.xlsx") val wb = HSSFWorkbook()
val workbook = WorkbookFactory.create(inputStream) val sh = wb.createSheet()
val workSheet = workbook.getSheetAt(0)
var h = true
//Header check
if(workSheet.getRow(0).getCell(0).stringCellValue.equals("Name")) {
if (workSheet.getRow(0).getCell(1).stringCellValue.equals("Description")) {
if (workSheet.getRow(0).getCell(2).stringCellValue.equals("HSN")) {
if (workSheet.getRow(0).getCell(3).stringCellValue.equals("UOM")) {
h = false
}else return "Header UOM mismatch"
}else return "Header-HSN mismatch"
}else return "Header-Desc mismatch"
}else return "Header-Name mismatch"
val resp = arrayListOf<validateExcel>() val headers: List<String> = listOf(
"No.", "Name", "MSME", "GST Number", "Address", "Rating", "Contact Name", "Contact Email", "Contact Mobile"
)
createHeaderRow(headers, sh, wb)
if(h==false) { val totalCols = headers.size
workSheet.rowIterator().forEach { row -> var rowCnt = 1
for (vendor in vendors) {
val contactCnt = vendor.contacts.size
for (j in 0..<contactCnt) {
val row = sh.createRow(rowCnt++)
var i = 0
row.createCell(i++).setCellValue((rowCnt - 1).toString())
row.createCell(i++).setCellValue(vendor.name)
row.createCell(i++).setCellValue(vendor.msme)
row.createCell(i++).setCellValue(vendor.gstNumber)
row.createCell(i++).setCellValue(vendor.address)
row.createCell(i++).setCellValue(vendor.rating)
row.createCell(i++).setCellValue(vendor.contacts[j].name)
row.createCell(i++).setCellValue(vendor.contacts[j].email)
row.createCell(i++).setCellValue(vendor.contacts[j].mobile)
}
}
saveExcelFileLocally("VendorList.xls", wb)
}
fun exportProds(prods: List<Product>) {
val wb = HSSFWorkbook()
val sh = wb.createSheet()
val headers: List<String> = listOf("Id", "Name", "Description", "HSN Code", "UOM")
createHeaderRow(headers, sh, wb)
var rowCnt = 1
for (prod in prods) {
val row = sh.createRow(rowCnt++)
var i = 0
row.createCell(i++).setCellValue(prod.code.toString())
row.createCell(i++).setCellValue(prod.name)
row.createCell(i++).setCellValue(prod.description)
row.createCell(i++).setCellValue(prod.hsnCode)
row.createCell(i++).setCellValue(prod.uom?.name)
}
saveExcelFileLocally("Products.xls", wb)
}
fun exportPos(pos: List<PurchaseOrder>) {
val wb = HSSFWorkbook()
val sh = wb.createSheet()
val headers: List<String> = listOf(
"Number",
"Date",
"Open Till",
"Reference Quotation Number",
"Vendor Name",
"Vendor Address",
"Product Id",
"Product Name",
"Unit Price",
"Quantity",
"Loading Site",
"Unloading Plant",
"Total Amount",
"Terms and Conditions"
)
createHeaderRow(headers, sh, wb)
var rowCnt = 1
val vmap = ConcurrentHashMap<Long, Vendor>()
val plants = currentUserPlants()
for (po in pos) {
val prodCnt = po.products.size
for (j in 0..<prodCnt) {
val row = sh.createRow(rowCnt++)
var i = 0
row.createCell(i++).setCellValue(po.poNum)
row.createCell(i++).setCellValue(po.poDate)
row.createCell(i++).setCellValue(po.validTill)
row.createCell(i++).setCellValue(po.referenceQuotation)
val vendorId = po.vendor!!.sysPk
val vendor = vmap.computeIfAbsent(vendorId) {
database.find(Vendor::class.java, vendorId)!!
}
row.createCell(i++).setCellValue(vendor.name)
//6 would be repeated
row.createCell(i++).setCellValue(po.products[j].productId.toString())
row.createCell(i++).setCellValue(po.products[j].productName)
row.createCell(i++).setCellValue(po.products[j].unitPrice)
row.createCell(i++).setCellValue(po.products[j].quantity)
if (po.allLoadingSite) {
row.createCell(i++).setCellValue("All Site")
} else {
row.createCell(i++).setCellValue(po.loadingSiteId?.toString())
}
if (po.allUnloadingSite) {
row.createCell(i++).setCellValue("All Plants")
} else {
row.createCell(i++)
.setCellValue(po.unloadingPlantId)
}
row.createCell(i++).setCellValue(po.totalAmount)
row.createCell(i).setCellValue(po.tnc?.joinToString(";"))
}
}
saveExcelFileLocally("Pos.xls", wb)
}
fun exportIncomingInventory(tickets: List<IncomingInventory>) {
val wb = HSSFWorkbook()
val sh = wb.createSheet()
val headers: List<String> = listOf(
"MRN",
"Inward Date",
"Entry Date",
"Vendor Name",
"Vendor Bill Number",
"Vendor Bill Amount",
"Vehicle No",
"Loading Site",
"Unloading Site",
"Product Name",
"Unit Price",
"Quantity",
"PO Number",
"PO Date",
)
createHeaderRow(headers, sh, wb)
var rowCnt = 1
for (ticket in tickets) {
val products = ticket.products ?: continue
val prodCnt = products.size
for (j in 0..<prodCnt) {
val row = sh.createRow(rowCnt++)
var i = 0
row.createCell(i++).setCellValue(ticket.mrn)
row.createCell(i++).setCellValue(ticket.date?.format(DateTimeFormatter.ofPattern("dd/MM/yyyy ")) ?: "")
row.createCell(i++).setCellValue(ticket.createdAt?.format(DateTimeFormatter.ofPattern("dd/MM/yyyy ")) ?: "")
row.createCell(i++).setCellValue(ticket.vendor?.name)
row.createCell(i++).setCellValue(ticket.vendorBillNum)
row.createCell(i++).setCellValue(ticket.vendorBillAmount)
row.createCell(i++).setCellValue(ticket.vehicle)
row.createCell(i++).setCellValue(ticket.loading)
row.createCell(i++).setCellValue(ticket.unloading)
//6 would be repeated
val poProduct = products[j]
row.createCell(i++).setCellValue(poProduct.productName)
row.createCell(i++).setCellValue(poProduct.unitPrice)
row.createCell(i++).setCellValue(poProduct.quantity)
if (poProduct.poId != null) {
val po = database.find(PurchaseOrder::class.java, poProduct.poId)
row.createCell(i++).setCellValue(po?.poNum ?: "")
row.createCell(i++).setCellValue(po?.poDate?.format(DateTimeFormatter.ofPattern("dd/MM/yyyy")) ?: "")
} else {
row.createCell(i++).setCellValue("")
row.createCell(i++).setCellValue("")
}
}
}
saveExcelFileLocally("IncomingInventory.xls", wb)
}
fun exportOutgoingInventory(tickets: List<OutgoingInventory>) {
val wb = HSSFWorkbook()
val sh = wb.createSheet()
val headers: List<String> = listOf(
"Plant",
"MDN",
"Outward Date",
"Entry Date",
"Out Mode",
"Purpose",
"Person",
"Vehicle",
"Job Card",
"Product Name",
"Quantity",
)
createHeaderRow(headers, sh, wb)
var rowCnt = 1
for (ticket in tickets) {
val prodCnt = ticket.products?.size
for (j in 0..<prodCnt!!) {
val row = sh.createRow(rowCnt++)
var i = 0
row.createCell(i++).setCellValue(ticket.unloading)
row.createCell(i++).setCellValue(ticket.mdn)
row.createCell(i++).setCellValue(ticket.date?.format(DateTimeFormatter.ofPattern("dd/MM/yyyy")) ?: "")
row.createCell(i++).setCellValue(ticket.createdAt?.format(DateTimeFormatter.ofPattern("dd/MM/yyyy")) ?: "")
row.createCell(i++).setCellValue(ticket.outMode?.name ?: "")
row.createCell(i++).setCellValue(ticket.purpose)
row.createCell(i++).setCellValue(ticket.person)
row.createCell(i++).setCellValue(ticket.vehicle)
row.createCell(i++).setCellValue(ticket.jobCard)
//6 would be repeated
row.createCell(i++).setCellValue(ticket.products!![j].productName)
row.createCell(i++).setCellValue(ticket.products!![j].billQty)
}
}
saveExcelFileLocally("OutgoingInventory.xls", wb)
}
fun exportPayments(payments: List<Payment>) {
val wb = HSSFWorkbook()
val sh = wb.createSheet()
val headers: List<String> = listOf("Reference Number", "Vendor", "Amount Paid", "Amount Deducted", "Excess Amount")
createHeaderRow(headers, sh, wb)
var rowCnt = 1
for (pmt in payments) {
val row = sh.createRow(rowCnt++)
var i = 0
row.createCell(i++).setCellValue(pmt.refNumber)
row.createCell(i++).setCellValue(pmt.vendor?.name)
row.createCell(i++).setCellValue(pmt.amount)
pmt.amountDeducted?.let { row.createCell(i++).setCellValue(it) }
pmt.excessAmount?.let { row.createCell(i++).setCellValue(it) }
}
saveExcelFileLocally("Payments.xls", wb)
}
fun exportInvoices(invoices: List<Invoice>) {
val wb = HSSFWorkbook()
val sh = wb.createSheet()
val headers: List<String> = listOf(
"Number",
"Date",
"Reference PO",
"Status",
"Vendor Name",
"Vendor Address",
"Product Id",
"Product Name",
"Unit Price",
"Quantity",
"Total Amount"
)
createHeaderRow(headers, sh, wb)
var rowCnt = 1
for (invoice in invoices) {
val prodCnt = invoice.products?.size
for (j in 0..<prodCnt!!) {
val row = sh.createRow(rowCnt++)
var i = 0
row.createCell(i++).setCellValue(invoice.number)
row.createCell(i++).setCellValue(invoice.date)
row.createCell(i++).setCellValue(invoice.poNum)
row.createCell(i++).setCellValue(invoice.status.toString())
row.createCell(i++).setCellValue(invoice.vendor?.name)
row.createCell(i++).setCellValue(invoice.vendor?.address)
//6 would be repeated
row.createCell(i++).setCellValue(invoice.products?.get(j)?.productId?.toString() ?: "NA")
row.createCell(i++).setCellValue(invoice.products?.get(j)?.productName ?: "NA")
invoice.products?.get(j)?.let { row.createCell(i++).setCellValue(it.unitPrice) }
invoice.products?.get(j)?.let { row.createCell(i++).setCellValue(it.quantity) }
row.createCell(i++).setCellValue(invoice.totalAmount)
}
}
saveExcelFileLocally("Invoices.xls", wb)
}
fun exportFleets(fleets: List<Fleet>) {
val wb = HSSFWorkbook()
val sh = wb.createSheet()
val headers: List<String> = listOf("Type", "Reg. Number", "Reg. Date", "Model", "Make", "Driver", "Mileage")
createHeaderRow(headers, sh, wb)
var rowCnt = 1
for (flt in fleets) {
val row = sh.createRow(rowCnt++)
var i = 0
row.createCell(i++).setCellValue(flt.type)
row.createCell(i++).setCellValue(flt.regNumber)
row.createCell(i++).setCellValue(flt.regDate)
row.createCell(i++).setCellValue(flt.model)
row.createCell(i++).setCellValue(flt.make)
row.createCell(i++).setCellValue(flt.driver)
row.createCell(i++).setCellValue(flt.mileage)
}
saveExcelFileLocally("Fleets.xls", wb)
}
fun exportReminderLogs(logs: List<ReminderLog>) {
val wb = HSSFWorkbook()
val sh = wb.createSheet()
val headers: List<String> = listOf("Type", "Amount", "Frequency", "Reminder Date", "Acted Upon", "Vehicle")
createHeaderRow(headers, sh, wb)
var rowCnt = 1
for (log in logs) {
val row = sh.createRow(rowCnt++)
var i = 0
row.createCell(i++).setCellValue(log.reminderType)
row.createCell(i++).setCellValue(log.amount)
row.createCell(i++).setCellValue(log.reminder?.frequency.toString())
row.createCell(i++).setCellValue(log.reminderDate)
row.createCell(i++).setCellValue(log.actedUpon)
row.createCell(i++).setCellValue(log.reminder?.fleet?.regNumber)
}
saveExcelFileLocally("ReminderLogs.xls", wb)
}
fun main() {
//ImportFromExcel(FileType.QUOTES, "C:\\Users\\arsalan\\Downloads\\Book.xlsx")
TemplateExcelFile(FileType.PRODS)
}
fun ImportFromExcel(fileType: FileType, filePath: String) {
val wb = WorkbookFactory.create(File(filePath))
val sh = wb.getSheetAt(0)
when (fileType) {
FileType.QUOTES -> {
//Quote Number, ProductName, Product Quantity, Total Amount, RFQ Number, Quote Date, Valid Till, TNC[], Documents[]
val quotesMap: MutableMap<String, Quotation> = mutableMapOf()
val quotesList: List<Quotation> = mutableListOf()
sh.rowIterator().forEach { row ->
if (row == null) {
//reached eof
return@forEach
}
val quoteNumber = stringFromCellHelper(row.getCell(0))
val quoteDt = dateFromCellHelper(row.getCell(1))
val rfqNum = stringFromCellHelper(row.getCell(2))
val quoteValidTill = dateFromCellHelper(row.getCell(3))
val vendorName = stringFromCellHelper(row.getCell(4))
val vendorGstNum = stringFromCellHelper(row.getCell(5))
val vendorAddress = stringFromCellHelper(row.getCell(6))
val prodName = stringFromCellHelper(row.getCell(7))
val prodQuantity = doubleFromCellHelper(row.getCell(8))
val prodUnitPrice = doubleFromCellHelper(row.getCell(9))
val totalQuoteAmount = doubleFromCellHelper(row.getCell(10))
val prod = POProducts(0, prodName, prodUnitPrice, prodQuantity)
if (quotesMap.containsKey(quoteNumber)) {
//duplicated row
quotesMap.get(quoteNumber)?.products?.add(prod)
} else {
val v = Vendor()
v.apply {
name = vendorName
address = vendorAddress
gstNumber = vendorGstNum
}
val quote = Quotation()
quote.apply {
quoteNum = quoteNumber
quoteDate = quoteDt
reqForQuoteNum = rfqNum
validTill = quoteValidTill
products = mutableListOf(prod)
vendor = v
totalAmount = totalQuoteAmount
}
quotesMap.put(quoteNumber, quote)
}
}
//docs, tncs
// println("$quotesMap")
// quotesMap.forEach { (k, v) ->
// println("$v")
// }
}
FileType.POS -> {
//poNum, poDate, validTill, refQuoteNum, prodName, prodQuantity, totalAmount, products, vendorName, vendorGst, vendorAddress, tnc[]. docs[]
val poMap: MutableMap<String, PurchaseOrder> = mutableMapOf()
sh.rowIterator().forEach { row ->
if (row == null) return@forEach if (row == null) return@forEach
val poNum = stringFromCellHelper(row.getCell(0))
val poDate = dateFromCellHelper(row.getCell(1))
val refQuoteNum = stringFromCellHelper(row.getCell(2))
val poValidTill = dateFromCellHelper(row.getCell(3))
val prodName = stringFromCellHelper(row.getCell(4))
val prodQuantity = doubleFromCellHelper(row.getCell(5))
val vendorName = stringFromCellHelper(row.getCell(6))
val vendorGstNum = stringFromCellHelper(row.getCell(7))
val vendorAddress = stringFromCellHelper(row.getCell(8))
val totalPoAmount = doubleFromCellHelper(row.getCell(9))
//tncs, docs
if (h) { val prod = POProducts(
productId = 0,
val pName = row.getCell(0).run { productName = prodName,
when { unitPrice = 0.0,
this == null -> "" quantity = prodQuantity,
this.cellType == CellType.STRING -> this.stringCellValue billQty = prodQuantity,
else -> "" gstPct = 0.0,
taxableValue = 0.0,
totalValue = 0.0,
description = "",
uom = ""
)
if (poMap.containsKey(poNum)) {
//repeated row
poMap.get(poNum)?.products?.add(prod)
} else {
val vendor = Vendor()
vendor.name = vendorName
vendor.address = vendorAddress
vendor.gstNumber = vendorGstNum
val po = PurchaseOrder()
po.poNum = poNum
po.poDate = poDate
po.referenceQuotation = refQuoteNum
po.validTill = poValidTill
poMap[poNum] = po
}
} }
} }
val pDesc = row.getCell(1).run { FileType.VENDORS -> {
when { sh.rowIterator().forEach { row ->
this == null -> "" //name, msme, gstNum, addresss, rating, contacts
this.cellType == CellType.STRING -> this.stringCellValue if (row == null) return@forEach
else -> "" val name = stringFromCellHelper(row.getCell(0))
} val msme = stringFromCellHelper(row.getCell(1))
} val gstNum = stringFromCellHelper(row.getCell(2))
val pHsn = row.getCell(2).run { val address = stringFromCellHelper(row.getCell(3))
when { val rating = doubleFromCellHelper(row.getCell(4))
this == null -> ""
this.cellType == CellType.STRING -> this.stringCellValue //vendor object
else -> "" val vendor = Vendor()
vendor.name = name
vendor.address = address
vendor.msme = msme
vendor.gstNumber = gstNum
vendor.rating = rating
} }
} }
if (pName.isEmpty() && pDesc.isEmpty() && pHsn.isEmpty()) { FileType.PRODS -> {
return@forEach sh.rowIterator().forEach { row ->
} if (row == null) return@forEach
if (pName.isEmpty()) { //id, name, description, hsnCode, uom
resp.add( val prodId = stringFromCellHelper(row.getCell(0))
validateExcel( val prodName = stringFromCellHelper(row.getCell(1))
name = pName, val prodDesc = stringFromCellHelper(row.getCell(2))
description = pDesc, val prodHsnCode = stringFromCellHelper(row.getCell(3))
hsnCode = pHsn, val prodUom = stringFromCellHelper(row.getCell(4))
ok = false,
err = "Product name is required" //new prod object
) val prod = Product()
) prod.code = prodId
return@forEach prod.name = prodName
} prod.description = prodDesc
if (pDesc.isEmpty()) { prod.hsnCode = prodHsnCode
resp.add( prod.uom = when (prodUom) {
validateExcel( "nos" -> UOM.NOS
name = pName, "ltr" -> UOM.LTR
description = pDesc, "mtr" -> UOM.MTR
hsnCode = pHsn, else -> UOM.ALL
ok = false,
err = "Product description is required"
)
)
return@forEach
}
if (pHsn.isEmpty()) {
resp.add(
validateExcel(
name = pName,
description = pDesc,
hsnCode = pHsn,
ok = false,
err = "Product HSN is required"
)
)
return@forEach
} }
} }
h = true }
FileType.DOCS -> {
sh.rowIterator().forEach { row ->
//Document Name, Document Type, RefID, url
if (row == null) return@forEach
val docName = stringFromCellHelper(row.getCell(0))
val docType = stringFromCellHelper(row.getCell(1))
val refId = stringFromCellHelper(row.getCell(2))
val url = stringFromCellHelper(row.getCell(3))
//new doc object
val doc = Document()
doc.name = docName
doc.typeOfDoc = when (docType) {
"quote" -> DocType.QUOTE
"po" -> DocType.PO
"invoice" -> DocType.INVOICE
else -> DocType.ALL
}
doc.refIdOfDoc = refId.toLong()
doc.url = url
}
} }
} }
return app_common_om.writeValueAsString(resp)
} }

View File

@ -1,145 +1,328 @@
package com.restapi.controllers package com.restapi.controllers
import com.restapi.domain.* import com.restapi.domain.*
import com.restapi.domain.PurchaseOrder
import com.restapi.domain.Quotation
import java.time.LocalDate
import com.restapi.domain.Session.database import com.restapi.domain.Session.database
import io.ebean.ExpressionList
import net.jodah.expiringmap.ExpiringMap
import java.time.LocalDate
import java.util.concurrent.TimeUnit
//constants //constants
const val IGNORE = "%" const val IGNORE = "%"
val baseDate :LocalDate = LocalDate.MIN val baseDate: LocalDate = LocalDate.of(1500, 1, 1)
val maxDate :LocalDate = LocalDate.MAX val maxDate: LocalDate = LocalDate.of(3000, 1, 1)
const val RATING_MAX = 10.0 const val RATING_MAX = 10.0
const val RATING_MIN = 0.0 const val RATING_MIN = 0.0
//common filters would be used by most of the handlers //common filters would be used by most of the handlers
//require a list of vendor ids to be passed //require a list of vendor ids to be passed
data class CommonFilters ( data class CommonFilters(
val fromDate :LocalDate = baseDate, val from: LocalDate = baseDate,
val toDate :LocalDate = maxDate, val to: LocalDate = maxDate,
val vendor :List<Long>? = null, val vendor: List<Long>? = null,
val sortAsc :Boolean = true, val plant: List<Long>? = null,
val sortBy :String = IGNORE val sortAsc: Boolean = true,
val sortBy: String = IGNORE
) )
data class POFilters (
val poNumLike :String = IGNORE, interface CustomFilters {}
val totalAmountExceeds :Long = Long.MIN_VALUE,
val totalAmountLessThan :Long = Long.MAX_VALUE, data class POFilters(
val poNumLike: String = "",
val totalAmountExceeds: Long = Long.MIN_VALUE,
val totalAmountLessThan: Long = Long.MAX_VALUE,
val validAfter: LocalDate = baseDate, val validAfter: LocalDate = baseDate,
val validBefore: LocalDate = maxDate, val validBefore: LocalDate = maxDate,
val refQuotation :String = IGNORE, val refQuotation: String = IGNORE,
val onlyActive: Boolean = false
) : CustomFilters
data class ProductFilters(
val nameLike: String = IGNORE,
val hsnLike: String = IGNORE,
val uom: UOM = UOM.ALL,
) : CustomFilters
data class DocumentFilters(
val nameLike: String = IGNORE,
val typeOfDoc: DocType = DocType.ALL,
val docDateFrom: LocalDate = baseDate,
val docDataTo: LocalDate = maxDate,
) : CustomFilters
data class RFQFilters(
val validBefore: LocalDate = maxDate,
val validAfter: LocalDate = baseDate,
val reqForQuoteNumLike: String = IGNORE,
) : CustomFilters
data class QuoteFilters(
val quoteNumLike: String = IGNORE,
val validBefore: LocalDate = maxDate,
val validAfter: LocalDate = baseDate,
val totalAmountExceeds: Long = Long.MIN_VALUE,
val totalAmountLessThan: Long = Long.MAX_VALUE,
) : CustomFilters
data class VendorFilters(
val nameLike: String = IGNORE,
val msmeLike: String = IGNORE,
val gstNumLike: String = IGNORE,
val addressLike: String = IGNORE,
val ratingExceeds: Double = RATING_MIN,
val ratingLessThan: Double = RATING_MAX,
) : CustomFilters
data class IncomingInventoryFilters(
val mrnLike: String = IGNORE,
val vehicleLike: String = IGNORE,
val vendorBillAmountExceeds: Double = Double.MIN_VALUE,
val vendorBillAmountLessThan: Double = Double.MAX_VALUE
) : CustomFilters
data class OutgoingInventoryFilters(
val mdnLike: String = IGNORE,
val purposeLike: String = IGNORE,
val personLike: String = IGNORE,
val vehicleLike: String = IGNORE,
val outMode: OutMode = OutMode.ALL
) : CustomFilters
data class InvoiceFilters(
val numLike: String = IGNORE,
val poNumLike: String = IGNORE,
val status: InvoiceStatus = InvoiceStatus.ALL,
val totalAmountExceeds: Double = Double.MIN_VALUE,
val totalAmountLessThan: Double = Double.MAX_VALUE
) : CustomFilters
data class PaymentFilters(
val refNumberLike: String = IGNORE,
val amountExceeds: Double = Double.MIN_VALUE,
val amountLessThan: Double = Double.MAX_VALUE
) : CustomFilters
data class FleetFilters(
val fleetNameLike: String = IGNORE
) : CustomFilters
data class ReminderLogFilters(
val reminderType: String = IGNORE,
val actedUpon: Boolean? = null,
val fleetId: Long? = null
) )
data class ProductFilters (
val nameLike :String = IGNORE, fun <T> applyVendorHelper(q: io.ebean.ExpressionList<T>, vids: List<Long>?) {
val hsnLike :String = IGNORE,
val uom :UOM = UOM.ALL,
)
data class DocumentFilters (
val nameLike :String = IGNORE,
val typeOfDoc :DocType = DocType.ALL,
)
data class RFQFilters (
val validBefore :LocalDate = maxDate,
val validAfter :LocalDate = baseDate,
val reqForQuoteNumLike :String = IGNORE,
)
data class QuoteFilters (
val quoteNumLike :String = IGNORE,
val validBefore :LocalDate = baseDate,
val validAfter :LocalDate = maxDate,
val totalAmountExceeds :Long = Long.MIN_VALUE,
val totalAmountLessThan :Long = Long.MAX_VALUE,
)
data class VendorFilters (
val nameLike :String = IGNORE,
val msmeLike :String = IGNORE,
val gstNumLike :String = IGNORE,
val addressLike :String = IGNORE,
val ratingExceeds :Double = RATING_MIN,
val ratingLessThan :Double = RATING_MAX,
)
fun<T> applyVendorHelper(q :io.ebean.ExpressionList<T>, vids :List<Long>?) {
if (vids.isNullOrEmpty()) return if (vids.isNullOrEmpty()) return
q.apply { q.eq("vendor_sys_pk", vids[0])
q.`in`("vendor", vids) }
fun <T> applyPlantFilter(q: io.ebean.ExpressionList<T>, vids: List<Long>?) {
val v = vids?.filter { it > 0 } ?: emptyList()
if (v.isEmpty()) {
q.`in`("unloadingPlantId", Session.currentUserPlants().map { it.plantId })
} else {
q.`in`("unloadingPlantId", v.map { it.toString() })
} }
} }
fun<T> applySortHelper(q :io.ebean.ExpressionList<T>, sortBy :String, asc :Boolean) {
if(sortBy == IGNORE) return; fun <T> applySortHelper(q: io.ebean.ExpressionList<T>, sortBy: String, asc: Boolean) {
if (sortBy == IGNORE) return;
val order = if (asc) "ASC" else "DESC" val order = if (asc) "ASC" else "DESC"
q.orderBy("$sortBy $order") q.orderBy("$sortBy $order")
} }
fun<T> applyCommonFilters(q :io.ebean.ExpressionList<T>, commonFilters: CommonFilters) {
fun <T> applyFromToHelper(q: io.ebean.ExpressionList<T>, fromDate: LocalDate, toDate: LocalDate, colName: String) {
q.ge(colName, fromDate)
.lt(colName, toDate.plusDays(1))
}
fun <T> applyCommonFilters(q: io.ebean.ExpressionList<T>, commonFilters: CommonFilters) {
applyVendorHelper<T>(q, commonFilters.vendor) applyVendorHelper<T>(q, commonFilters.vendor)
applySortHelper<T>(q, commonFilters.sortBy, commonFilters.sortAsc) applySortHelper<T>(q, commonFilters.sortBy, commonFilters.sortAsc)
} }
fun searchQuotes(commonFilters: CommonFilters, quoteFilters: QuoteFilters) : List<Quotation> {
fun searchQuotes(commonFilters: CommonFilters, quoteFilters: QuoteFilters): List<Quotation> {
val q = database.find(Quotation::class.java) val q = database.find(Quotation::class.java)
.where() .where()
.between("quoteDate", commonFilters.fromDate, commonFilters.toDate) .ge("quoteDate", commonFilters.from)
.ilike("quoteNum", quoteFilters.quoteNumLike ) .le("quoteDate", commonFilters.to)
.ge("validTill",quoteFilters.validAfter) .ge("validTill", quoteFilters.validAfter)
.le("validTill", quoteFilters.validBefore) .le("validTill", quoteFilters.validBefore)
.le("totalAmount", quoteFilters.totalAmountLessThan)
.ge("totalAmount", quoteFilters.totalAmountExceeds) .ge("totalAmount", quoteFilters.totalAmountExceeds)
.apply { .le("totalAmount", quoteFilters.totalAmountLessThan)
if(!commonFilters.vendor?.isEmpty()!!){ .ilike("quoteNum", "%" + quoteFilters.quoteNumLike + "%")
commonFilters.vendor.let { this.`in`("vendor", it) } applyFromToHelper(q, commonFilters.from, commonFilters.to, "quoteDate")
}
}
applySortHelper(q, commonFilters.sortBy, commonFilters.sortAsc)
return q.findList()
}
fun searchVendors(commonFilters: CommonFilters, vendorFilters: VendorFilters) : List<Vendor> {
val q = database.find(Vendor::class.java)
.where()
.ge("rating", vendorFilters.ratingExceeds)
.le("rating", vendorFilters.ratingLessThan)
.ilike("name", vendorFilters.nameLike)
.ilike("msme", vendorFilters.msmeLike)
.ilike("gstNum", vendorFilters.gstNumLike)
.ilike("address", vendorFilters.addressLike)
applySortHelper(q, commonFilters.sortBy, commonFilters.sortAsc)
return q.findList()
}
fun searchDocs(commonFilters: CommonFilters, documentFilters: DocumentFilters) : List<Document> {
val q = database.find(Document::class.java)
.where()
.apply {
if(documentFilters.typeOfDoc != DocType.ALL){
this.eq("docType", documentFilters.typeOfDoc)
}
}
.ilike("name", documentFilters.nameLike )
applySortHelper(q, commonFilters.sortBy, commonFilters.sortAsc)
return q.findList()
}
fun searchPos(commonFilters: CommonFilters, poFilters: POFilters) : List<PurchaseOrder> {
val q = database.find(PurchaseOrder::class.java)
.where()
.between("totalAmount", poFilters.totalAmountExceeds, poFilters.totalAmountLessThan)
.between("validTill", poFilters.validAfter, poFilters.validBefore)
.ilike("poNum", poFilters.poNumLike )
.ilike("referenceQuotation", poFilters.refQuotation )
applyVendorHelper(q, commonFilters.vendor)
applySortHelper(q, commonFilters.sortBy, commonFilters.sortAsc)
return q.findList()
}
fun searchRFQ(commonFilters: CommonFilters, rfqFilters: RFQFilters) : List<ReqForQuote> {
val q = database.find(ReqForQuote::class.java)
.where()
.between("validTill", rfqFilters.validAfter, rfqFilters.validBefore)
.ilike("reqForQuoteNum", rfqFilters.reqForQuoteNumLike)
applyVendorHelper(q, commonFilters.vendor) applyVendorHelper(q, commonFilters.vendor)
applySortHelper(q, commonFilters.sortBy, commonFilters.sortAsc) applySortHelper(q, commonFilters.sortBy, commonFilters.sortAsc)
return q.findList() return q.findList()
} }
fun searchProduct(commonFilters: CommonFilters, productFilters: ProductFilters): List<Product> { fun searchVendors(commonFilters: CommonFilters, vendorFilters: VendorFilters): List<Vendor> {
val p = database.find(Product::class.java) val q = database.find(Vendor::class.java)
.where() .where()
.ilike("hsnCode", productFilters.hsnLike) .ilike("name", "%" + vendorFilters.nameLike + "%")
.ilike("Pname", productFilters.nameLike) .ilike("gstNumber", "%" + vendorFilters.gstNumLike + "%")
applySortHelper(p, commonFilters.sortBy, commonFilters.sortAsc) applySortHelper(q, commonFilters.sortBy, commonFilters.sortAsc)
return p.findList() return q.findList()
} }
private val productCache = ExpiringMap.builder()
.expiration(10, TimeUnit.SECONDS)
.build<String, List<Product>>()
fun searchProducts(commonFilters: CommonFilters, productFilters: ProductFilters): List<Product> {
return productCache.computeIfAbsent("$commonFilters:$productFilters:${Session.currentUser()}") {
val q = database.find(Product::class.java)
.where()
.ilike("name", "%" + productFilters.nameLike + "%")
.ilike("hsnCode", "%" + productFilters.hsnLike + "%")
if (productFilters.uom != UOM.ALL) {
q.eq("uom", productFilters.uom)
}
applySortHelper(q, commonFilters.sortBy, commonFilters.sortAsc)
q.findList()
}
}
fun searchDocs(commonFilters: CommonFilters, documentFilters: DocumentFilters): List<Document> {
val q = database.find(Document::class.java)
.where()
.apply {
if (documentFilters.typeOfDoc != DocType.ALL) {
this.eq("docType", documentFilters.typeOfDoc)
}
}
.ilike("name", "%" + documentFilters.nameLike + "%")
applySortHelper(q, commonFilters.sortBy, commonFilters.sortAsc)
applyVendorHelper(q, commonFilters.vendor)
return q.findList()
}
fun searchPos(commonFilters: CommonFilters, poFilters0: POFilters?): List<PurchaseOrder> {
val poFilters = poFilters0 ?: POFilters()
val q = database.find(PurchaseOrder::class.java)
.where()
.apply {
if (poFilters.poNumLike.isNotEmpty()) {
ilike("poNum", "%" + poFilters.poNumLike + "%")
}
}
if (poFilters.onlyActive) {
q.gt("validTill", LocalDate.now().minusDays(1))
}
applyFromToHelper(q, commonFilters.from, commonFilters.to, "poDate")
applyVendorHelper(q, commonFilters.vendor)
applySortHelper(q, commonFilters.sortBy, commonFilters.sortAsc)
return q.findList()
}
fun searchRFQ(commonFilters: CommonFilters, rfqFilters: RFQFilters): List<ReqForQuote> {
val q = database.find(ReqForQuote::class.java)
.where()
.ge("validTill", rfqFilters.validAfter)
.le("validTill", rfqFilters.validBefore)
.ilike("reqForQuoteNum", "%" + rfqFilters.reqForQuoteNumLike + "%")
applyVendorHelper(q, commonFilters.vendor)
applySortHelper(q, commonFilters.sortBy, commonFilters.sortAsc)
return q.findList()
}
fun searchIncomingInventory(
commonFilters: CommonFilters,
incomingInventoryFilters: IncomingInventoryFilters
): List<IncomingInventory> {
val q = database.find(IncomingInventory::class.java).where()
applyFromToHelper(q, commonFilters.from, commonFilters.to, "date")
applyVendorHelper(q, commonFilters.vendor)
applyPlantFilter(q, commonFilters.plant)
applySortHelper(q, commonFilters.sortBy, commonFilters.sortAsc)
applyInwardFilter(q, incomingInventoryFilters)
return q.findList()
}
fun applyInwardFilter(q: ExpressionList<IncomingInventory>, incomingInventoryFilters: IncomingInventoryFilters) {
if (incomingInventoryFilters.mrnLike.isNotEmpty() && incomingInventoryFilters.mrnLike != "%") {
q.eq("mrn", incomingInventoryFilters.mrnLike)
}
}
fun searchOutgoingInventory(
commonFilters: CommonFilters,
outgoingInventoryFilters: OutgoingInventoryFilters
): List<OutgoingInventory> {
val q = database.find(OutgoingInventory::class.java).where()
if (outgoingInventoryFilters.mdnLike.isNotEmpty() && outgoingInventoryFilters.mdnLike != "%") {
q.eq("mdn", outgoingInventoryFilters.mdnLike)
}
if (outgoingInventoryFilters.outMode != OutMode.ALL) {
q.eq("outMode", outgoingInventoryFilters.outMode)
}
applyPlantFilter(q, commonFilters.plant)
applyFromToHelper(q, commonFilters.from, commonFilters.to, "date")
applySortHelper(q, commonFilters.sortBy, commonFilters.sortAsc)
return q.findList()
}
fun searchInvoices(commonFilters: CommonFilters, invoiceFilters: InvoiceFilters): List<Invoice> {
val q = database.find(Invoice::class.java)
.where()
.ilike("number", "%" + invoiceFilters.numLike + "%")
if (invoiceFilters.status != InvoiceStatus.ALL) {
q.ne("status", invoiceFilters.status)
}
applyFromToHelper(q, commonFilters.from, commonFilters.to, "date")
applyVendorHelper(q, commonFilters.vendor)
applySortHelper(q, commonFilters.sortBy, commonFilters.sortAsc)
return q.findList()
}
fun searchPayments(commonFilters: CommonFilters, paymentFilters: PaymentFilters): List<Payment> {
val q = database.find(Payment::class.java)
.where()
.ilike("refNumber", "%" + paymentFilters.refNumberLike + "%")
// .ge("amount", paymentFilters.amountExceeds)
//.le("amount", paymentFilters.amountLessThan)
applyFromToHelper(q, commonFilters.from, commonFilters.to, "date")
applyVendorHelper(q, commonFilters.vendor)
applySortHelper(q, commonFilters.sortBy, commonFilters.sortAsc)
return q.findList()
}
fun searchFleets(filters: CommonFilters, fleetFilters: FleetFilters): List<Fleet> {
val q = database.find(Fleet::class.java)
.where()
.ilike("name", "%" + fleetFilters.fleetNameLike + "%")
// q.raw("(insurance_renewal_date >= ? and insurance_renewal_date <= ?)" +
// "or (pollution_renewal_date >= ? and pollution_renewal_date <= ?)" +
// "or (fitness_renewal_date >= ? and fitness_renewal_date <= ?)",
// filters.from, filters.to, filters.from, filters.to, filters.from, filters.to
// )
// applyFromToHelper(q, filters.from, filters.to, "insuranceRenewalDate")
// applyFromToHelper(q, filters.from, filters.to, "pollutionRenewalDate")
// applyFromToHelper(q, filters.from, filters.to, "fitnessRenewalDate")
return q.findList()
}
fun searchReminderLogs(filters: CommonFilters, reminderLogFilters: ReminderLogFilters): List<ReminderLog> {
val q = database.find(ReminderLog::class.java)
.where()
.ilike("reminder_type", reminderLogFilters.reminderType)
if (reminderLogFilters.actedUpon != null) {
q.eq("acted_upon", reminderLogFilters.actedUpon)
}
if (reminderLogFilters.fleetId != null) {
q.eq("fleet_sys_pk", reminderLogFilters.fleetId)
}
return q.findList()
}
//if date is null then fromtoheper drops that///

View File

@ -0,0 +1,287 @@
package com.restapi.controllers
import com.restapi.domain.*
import io.javalin.http.Context
import io.javalin.http.HttpStatus
import io.javalin.http.NotFoundResponse
import io.javalin.http.bodyAsClass
import java.io.FileInputStream
import java.time.LocalDate
object FleetCtrl {
fun create(ctx: Context) {
val fleet = ctx.bodyAsClass<Fleet>()
Session.database.save(fleet)
ctx.json(fleet).status(HttpStatus.CREATED)
}
fun get(ctx: Context) {
val id = ctx.pathParam("id").toLong()
val fleet = Session.database.find(Fleet::class.java, id)
?: throw NotFoundResponse("No fleet found with id $id")
ctx.json(fleet).status(HttpStatus.OK)
}
data class FLTF(val common: CommonFilters, val fleetFilters: FleetFilters)
fun getAll(ctx: Context) {
val filters = ctx.bodyAsClass<FLTF>()
val fleets = searchFleets(filters.common, filters.fleetFilters)
val excel = ctx.queryParam("excel")
if (excel !== null) {
exportFleets(fleets)
val inputStream = FileInputStream("./excel/Fleets.xls")
ctx.result(inputStream).status(HttpStatus.OK)
} else {
ctx.json(fleets).status(HttpStatus.OK)
}
}
fun update(ctx: Context) {
val id = ctx.pathParam("id").toLong()
val fleet =
Session.database.find(Fleet::class.java, id) ?: throw NotFoundResponse("fleet not found for $id")
val updatedFleet = ctx.bodyAsClass<Fleet>()
fleet.patchValues(updatedFleet)
fleet.update()
ctx.json(fleet).status(HttpStatus.OK)
}
fun delete(ctx: Context) {
}
}
object FleetTypeCtrl {
fun create(ctx: Context) {
val fleetType = ctx.bodyAsClass<FleetType>()
Session.database.save(fleetType)
ctx.json(fleetType).status(HttpStatus.CREATED)
}
fun get(ctx: Context) {
val id = ctx.pathParam("id").toLong()
val fleetType = Session.database.find(FleetType::class.java, id)
?: throw NotFoundResponse("No fleetType found with id $id")
ctx.json(fleetType).status(HttpStatus.OK)
}
fun getAll(ctx: Context) {
val fleetTypes = Session.database.find(FleetType::class.java).findList()
val excel = ctx.queryParam("excel")
if (excel !== null) {
// exportFleetTypes(fleetTypes)
val inputStream = FileInputStream("./excel/FleetTypes.xls")
ctx.result(inputStream).status(HttpStatus.OK)
} else {
ctx.json(fleetTypes).status(HttpStatus.OK)
}
}
fun update(ctx: Context) {
val id = ctx.pathParam("id").toLong()
val fleetType =
Session.database.find(FleetType::class.java, id) ?: throw NotFoundResponse("fleetType not found for $id")
val updatedFleetType = ctx.bodyAsClass<FleetType>()
fleetType.patchValues(updatedFleetType)
fleetType.update()
ctx.json(fleetType).status(HttpStatus.OK)
}
}
object RenewalCtrl {
fun create(ctx: Context) {
val renewal = ctx.bodyAsClass<Renewal>()
Session.database.save(renewal)
ctx.json(renewal).status(HttpStatus.CREATED)
}
fun get(ctx: Context) {
val id = ctx.pathParam("id").toLong()
val renewal = Session.database.find(Renewal::class.java, id)
?: throw NotFoundResponse("No renewal found with id $id")
ctx.json(renewal).status(HttpStatus.OK)
}
//data class RNLF(val common: CommonFilters, val renewalFilters: RenewalFilters)
fun getAll(ctx: Context) {
// val filters = ctx.bodyAsClass<FLTF>()
// val renewals = searchRenewals(filters.common, filters.renewalFilters)
val renewals = Session.database.find(Renewal::class.java).findList()
val excel = ctx.queryParam("excel")
if (excel !== null) {
// exportRenewals(renewals)
val inputStream = FileInputStream("./excel/Renewals.xls")
ctx.result(inputStream).status(HttpStatus.OK)
} else {
ctx.json(renewals).status(HttpStatus.OK)
}
}
fun update(ctx: Context) {
val id = ctx.pathParam("id").toLong()
val renewal =
Session.database.find(Renewal::class.java, id) ?: throw NotFoundResponse("renewal not found for $id")
val updatedRenewal = ctx.bodyAsClass<Renewal>()
renewal.patchValues(updatedRenewal)
renewal.update()
ctx.json(renewal).status(HttpStatus.OK)
}
fun delete(ctx: Context) {
}
}
object ReminderCtrl {
fun create(ctx: Context) {
val renewal = ctx.bodyAsClass<Reminder>()
Session.database.save(renewal)
val entry = ReminderLog()
entry.apply {
reminder = renewal
reminderDate = renewal.nextRenewalDate
reminderType = renewal.type
fleet = renewal.fleet
amount = 0.0
actedUpon = false
}
ReminderLogCtrl.create(entry)
ctx.json(renewal).status(HttpStatus.CREATED)
}
fun getAllByFleetId(ctx: Context) {
val fleetId = ctx.pathParam("id").toLong()
val history = Session.database.find(Reminder::class.java)
.where()
.eq("fleet_sys_pk", fleetId)
.findList()
ctx.json(history).status(HttpStatus.OK)
}
fun get(ctx: Context) {
val id = ctx.pathParam("id").toLong()
val renewal = Session.database.find(Reminder::class.java, id)
?: throw NotFoundResponse("No renewal found with id $id")
ctx.json(renewal).status(HttpStatus.OK)
}
//data class RNLF(val common: CommonFilters, val renewalFilters: ReminderFilters)
fun getAll(ctx: Context) {
// val filters = ctx.bodyAsClass<FLTF>()
// val renewals = searchReminders(filters.common, filters.renewalFilters)
val renewals = Session.database.find(Reminder::class.java).findList()
val excel = ctx.queryParam("excel")
if (excel !== null) {
// exportReminders(renewals)
val inputStream = FileInputStream("./excel/Reminders.xls")
ctx.result(inputStream).status(HttpStatus.OK)
} else {
ctx.json(renewals).status(HttpStatus.OK)
}
}
fun update(ctx: Context) {
val id = ctx.pathParam("id").toLong()
val renewal =
Session.database.find(Reminder::class.java, id) ?: throw NotFoundResponse("renewal not found for $id")
val updatedReminder = ctx.bodyAsClass<Reminder>()
renewal.patchValues(updatedReminder)
renewal.update()
ctx.json(renewal).status(HttpStatus.OK)
}
fun delete(ctx: Context) {
}
}
object ReminderLogCtrl {
fun create(entry: ReminderLog) {
// val renewal = ctx.bodyAsClass<ReminderLog>()
Session.database.save(entry)
return
//ctx.json(renewal).status(HttpStatus.CREATED)
}
data class Done(
val logId: Long,
val reminderId: Long,
val renewalDate: LocalDate,
val nextRenewalDate: LocalDate,
val amount: Double,
val documents: List<String>?
)
fun done(ctx: Context) {
val req = ctx.bodyAsClass<Done>()
val reminder =
Session.database.find(Reminder::class.java, req.reminderId) ?: throw NotFoundResponse("No Reminder found")
//update reminder
reminder.apply {
lastRenewalDate = req.renewalDate
nextRenewalDate = req.nextRenewalDate
}
reminder.update()
val entry = Session.database.find(ReminderLog::class.java, req.logId) ?: throw NotFoundResponse("Log not found")
entry.apply {
actedUpon = true
documents = req.documents
amount = req.amount
}
entry.update()
ctx.json(entry).status(HttpStatus.OK)
}
fun getAllByFleetId(ctx: Context) {
val fleetId = ctx.pathParam("id").toLong()
val history = Session.database.find(ReminderLog::class.java)
.where()
.eq("fleet_sys_pk", fleetId)
.findList()
ctx.json(history).status(HttpStatus.OK)
}
fun get(ctx: Context) {
val id = ctx.pathParam("id").toLong()
val renewal = Session.database.find(ReminderLog::class.java, id)
?: throw NotFoundResponse("No renewal found with id $id")
ctx.json(renewal).status(HttpStatus.OK)
}
data class LOGF(val common: CommonFilters, val reminderLogFilters: ReminderLogFilters)
fun getAll(ctx: Context) {
val filters = ctx.bodyAsClass<LOGF>()
val entries = searchReminderLogs(filters.common, filters.reminderLogFilters)
val excel = ctx.queryParam("excel")
if (excel !== null) {
exportReminderLogs(entries)
val inputStream = FileInputStream("./excel/ReminderLogs.xls")
ctx.result(inputStream).status(HttpStatus.OK)
} else {
ctx.json(entries).status(HttpStatus.OK)
}
}
fun update(ctx: Context) {
val id = ctx.pathParam("id").toLong()
val renewal =
Session.database.find(ReminderLog::class.java, id) ?: throw NotFoundResponse("renewal not found for $id")
val updatedReminderLog = ctx.bodyAsClass<ReminderLog>()
renewal.patchValues(updatedReminderLog)
renewal.update()
ctx.json(renewal).status(HttpStatus.OK)
}
fun delete(ctx: Context) {
}
}

View File

@ -0,0 +1,179 @@
package com.restapi.controllers
import com.restapi.domain.*
import com.restapi.domain.Session.database
import com.restapi.integ.logger
import io.javalin.http.*
import java.io.FileInputStream
object IncomingInventoryCtrl {
fun plantsForUser(ctx: Context) {
ctx.json(
Session.currentUserPlants()
)
}
fun updatePlant(ctx: Context) {
val p = ctx.bodyAsClass<Plant>()
database.find(Plant::class.java, ctx.pathParam("id"))?.let {
it.patch(p)
}
ctx.json(
Session.currentUserPlants()
)
}
fun create(ctx: Context) {
val ticket = ctx.bodyAsClass<IncomingInventory>()
database.save(ticket)
keepTrackOfPoQty(ticket)
ctx.json(ticket).status(HttpStatus.CREATED)
}
fun update(ctx: Context) {
val id = ctx.pathParam("id").toLong()
val ticket =
database.find(IncomingInventory::class.java, id) ?: throw NotFoundResponse("quote not found for $id")
val updatedTicket = ctx.bodyAsClass<IncomingInventory>()
ticket.patchValues(updatedTicket)
ticket.update()
keepTrackOfPoQty(ticket)
ctx.json(ticket).status(HttpStatus.OK)
}
private fun keepTrackOfPoQty(ticket: IncomingInventory) {
ticket.products?.forEach { incomingProduct ->
if (incomingProduct.poId != null) {
database.find(PurchaseOrder::class.java, incomingProduct.poId)?.let { po ->
logger.warn("found po for ${incomingProduct.poId} => ${po.poNum}")
po.products.forEach { poProduct ->
if (poProduct.productId == incomingProduct.productId) {
logger.warn("for ${ticket.mrn} , product ${poProduct.productId} add to po-qty ${incomingProduct.receivedQty}")
//remove existing entry for same mrn and product id
poProduct.receivedVoucher.removeIf {
it.productId == incomingProduct.productId
&& it.plantId == ticket.unloadingPlantId
&& it.ticket == ticket.mrn
}
poProduct.receivedVoucher.add(
ReceivedVoucher(
ticket = ticket.mrn!!,
productName = poProduct.productName,
productId = poProduct.productId,
qty = incomingProduct.receivedQty,
plantId = ticket.unloadingPlantId!!,
ticketDate = ticket.date!!
)
)
} else {
logger.warn("no match for ${poProduct.productId} and ${incomingProduct.productId}")
}
}
po.update()
}
} else {
logger.warn("poId is missing for $incomingProduct")
}
}
}
fun get(ctx: Context) {
val id = ctx.pathParam("id").toLong()
val ticket = database.find(IncomingInventory::class.java, id)
?: throw NotFoundResponse("No incoming inventory ticket found with id $id")
ctx.json(ticket).status(HttpStatus.OK)
}
data class IIF(val common: CommonFilters, val incomingInventoryFilters: IncomingInventoryFilters)
fun getAll(ctx: Context) {
val filters = ctx.bodyAsClass<IIF>()
val tickets = searchIncomingInventory(filters.common, filters.incomingInventoryFilters)
val excel = ctx.queryParam("excel")
if (excel !== null) {
exportIncomingInventory(tickets)
val inputStream = FileInputStream("./excel/IncomingInventory.xls")
ctx.result(inputStream).status(HttpStatus.OK)
} else {
ctx.json(tickets).status(HttpStatus.OK)
}
}
fun getNextNum(ctx: Context) {
val prefix = "MRN/"
val plantId = ctx.queryParam("plantId") ?: throw BadRequestResponse("plantId not sent")
val plant = database.find(Plant::class.java)
.where()
.eq("plantId", plantId)
.findOne() ?: throw BadRequestResponse("plant missing for $plantId")
val inventoryPrefix = plant.prefixes?.get("INBOUND") ?: prefix
val cnt = (database.find(IncomingInventory::class.java)
.where()
.eq("unloadingPlantId", plantId)
.findCount() + 1)
.toString()
.padStart(6, '0')
val seq = SequenceNumber(inventoryPrefix + cnt)
ctx.json(seq).status(HttpStatus.OK)
}
}
object OutgoingInventoryCtrl {
fun create(ctx: Context) {
val ticket = ctx.bodyAsClass<OutgoingInventory>()
database.save(ticket)
ctx.json(ticket).status(HttpStatus.CREATED)
}
fun update(ctx: Context) {
val id = ctx.pathParam("id").toLong()
val ticket =
database.find(OutgoingInventory::class.java, id) ?: throw NotFoundResponse("quote not found for $id")
val updatedTicket = ctx.bodyAsClass<OutgoingInventory>()
ticket.patchValues(updatedTicket)
ticket.update()
ctx.json(ticket).status(HttpStatus.OK)
}
fun get(ctx: Context) {
val id = ctx.pathParam("id").toLong()
val ticket = database.find(OutgoingInventory::class.java, id)
?: throw NotFoundResponse("No incoming inventory ticket found with id $id")
ctx.json(ticket).status(HttpStatus.OK)
}
data class OIF(val common: CommonFilters, val outgoingInventoryFilters: OutgoingInventoryFilters)
fun getAll(ctx: Context) {
val filters = ctx.bodyAsClass<OIF>()
val tickets = searchOutgoingInventory(filters.common, filters.outgoingInventoryFilters)
val excel = ctx.queryParam("excel")
if (excel !== null) {
exportOutgoingInventory(tickets)
val inputStream = FileInputStream("./excel/OutgoingInventory.xls")
ctx.result(inputStream).status(HttpStatus.OK)
} else {
ctx.json(tickets).status(HttpStatus.OK)
}
}
fun getNextNum(ctx: Context) {
val prefix = "MDN/"
val plantId = ctx.queryParam("plantId") ?: throw BadRequestResponse("plantId not sent")
val plant = database.find(Plant::class.java)
.where()
.eq("plantId", plantId)
.findOne() ?: throw BadRequestResponse("plant missing for $plantId")
val inventoryPrefix = plant.prefixes?.get("OUTBOUND") ?: prefix
val cnt = (database.find(OutgoingInventory::class.java)
.where()
.eq("unloadingPlantId", plantId)
.findCount() + 1)
.toString()
.padStart(6, '0')
val seq = SequenceNumber(inventoryPrefix + cnt)
ctx.json(seq).status(HttpStatus.OK)
}
}

View File

@ -0,0 +1,96 @@
package com.restapi.controllers
import com.restapi.domain.Invoice
import com.restapi.domain.InvoiceStatus
import com.restapi.domain.Payment
import com.restapi.domain.Session
import io.javalin.http.Context
import io.javalin.http.HttpStatus
import io.javalin.http.NotFoundResponse
import io.javalin.http.bodyAsClass
import java.io.FileInputStream
object PaymentCtrl {
fun create(ctx: Context) {
val pmt = ctx.bodyAsClass(Payment::class.java)
//update the status of invoices pertaining to payment.vendor
val vendors: List<Long>? = pmt.vendor?.let { listOf(it.sysPk) }
val invoices = searchInvoices(
CommonFilters(sortBy = "date", sortAsc = true, vendor = vendors),
InvoiceFilters(status = InvoiceStatus.PAID_FULL)
)
// println(invoices)
//pmt.invoicesAffected = mutableMapOf()
var totalAmount = pmt.amount
var totalDeduct = 0.0
for (invoice in invoices) {
val deduct = Math.min(totalAmount, invoice.totalAmount)
invoice.totalAmount -= deduct
totalDeduct += deduct
totalAmount -= deduct
if (invoice.totalAmount <= 0.0) {
invoice.status = InvoiceStatus.PAID_FULL
} else {
invoice.status = InvoiceStatus.PAID_SOME
}
//println(invoice)
Session.database.update(invoice)
pmt.invoicesAffected?.put(invoice.sysPk, deduct)
println(pmt.invoicesAffected)
if (totalAmount <= 0.0) break
}
pmt.amountDeducted = totalDeduct
pmt.excessAmount = pmt.amount - pmt.amountDeducted!!
Session.database.save(pmt)
ctx.json(pmt).status(HttpStatus.CREATED)
}
fun delete(ctx: Context) {
val id = ctx.pathParam("id").toLong()
val pmt = Session.database.find(Payment::class.java, id)
?: throw NotFoundResponse("No payment found for this id $id")
val invoiceDeductMap = pmt.invoicesAffected
for (entry in invoiceDeductMap?.entries!!.iterator()) {
val inv = Session.database.find(Invoice::class.java, entry.key)
?: throw NotFoundResponse("No invoice found for $entry.key")
inv.totalAmount += entry.value
inv.status = InvoiceStatus.PAID_SOME
Session.database.update(inv)
}
Session.database.delete(pmt)
ctx.json(pmt).status(HttpStatus.OK)
}
fun get(ctx: Context) {
val id = ctx.pathParam("id")
val pmt = Session.database.find(Payment::class.java, id)
?: throw NotFoundResponse("No payment found for this id $id")
ctx.json(pmt).status(HttpStatus.OK)
}
data class PMTF(val common: CommonFilters, val paymentFilters: PaymentFilters)
fun getAll(ctx: Context) {
val filters = ctx.bodyAsClass<PMTF>()
val payments = searchPayments(filters.common, filters.paymentFilters)
// println(payments)
val excel = ctx.queryParam("excel")
if (excel !== null) {
exportPayments(payments)
val inputStream = FileInputStream("./excel/Payments.xls")
ctx.result(inputStream).status(HttpStatus.OK)
} else {
ctx.json(payments).status(HttpStatus.OK)
}
}
fun update(ctx: Context) {
val id = ctx.pathParam("id").toLong()
val pmt =
Session.database.find(Payment::class.java, id) ?: throw NotFoundResponse("payment not found for $id")
val updatedPayment = ctx.bodyAsClass<Payment>()
pmt.patchValues(updatedPayment)
pmt.update()
ctx.json(pmt).status(HttpStatus.OK)
}
}

View File

@ -0,0 +1,120 @@
package com.restapi.controllers
import com.restapi.domain.Product
import com.restapi.domain.PurchaseOrder
import com.restapi.domain.Session
import com.restapi.domain.Vendor
import io.javalin.http.*
import java.io.FileInputStream
object ProductCtrl {
fun get(ctx: Context) {
val id = ctx.pathParam("id")
val product = Session.database.find(Product::class.java)
.where()
.eq("sys_pk", id.toLong())
.findOne()
?: throw NotFoundResponse("Product not found for $id")
ctx.json(product).status(HttpStatus.OK)
}
data class PF(val common: CommonFilters, val productFilters: ProductFilters)
data class GetPrice(val productId: Long, val vendor: Long, val po: Long?)
fun getPrice(ctx: Context) {
val gp = ctx.bodyAsClass<GetPrice>()
val vendor = Session.database.find(Vendor::class.java, gp.vendor) ?: throw BadRequestResponse("vendor not found for ${gp.vendor}")
val product = Session.database.find(Product::class.java, gp.productId) ?: throw BadRequestResponse("product not found for ${gp.productId}")
val poProduct = if (gp.po != null) {
Session.database.find(PurchaseOrder::class.java, gp.po)?.products?.firstOrNull { it.productId == product.sysPk }
} else {
Session.database.find(PurchaseOrder::class.java)
.where()
.eq("vendor", vendor)
.findList()
.flatMap { it.products }
.firstOrNull { it.productId == product.sysPk }
}
ctx.json(
poProduct ?: throw BadRequestResponse("price not found for this vendor and product and po")
)
}
fun getAll(ctx: Context) {
val filters = ctx.bodyAsClass<PF>()
val prods = searchProducts(filters.common, filters.productFilters)
val excel = ctx.queryParam("excel")
if (excel != null) {
exportProds(prods)
val inputStream = FileInputStream("./excel/Products.xls")
ctx.result(inputStream).status(HttpStatus.OK)
} else {
ctx.json(prods).status(HttpStatus.OK)
}
}
fun create(ctx: Context) {
val product = ctx.bodyAsClass<Product>()
Session.database.save(product)
ctx.json(product).status(HttpStatus.CREATED)
}
fun delete(ctx: Context) {
val id = ctx.pathParam("id")
val prod = Session.database.find(Product::class.java, id) ?: throw NotFoundResponse("no product found with id $id")
Session.database.delete(prod)
ctx.status(HttpStatus.OK)
}
fun patch(ctx: Context) {
val id = ctx.pathParam("id")
val patchValues = ctx.bodyAsClass<Map<String, Any>>()
Session.database.beginTransaction().use {
patchValues.entries.forEach { en ->
val key = en.key
val value = en.value
Session.database.sqlUpdate("update products set $key = ? where id = ?").apply {
setParameter(1, value)
setParameter(2, id)
execute()
}
}
it.commit()
}
}
fun update(ctx: Context) {
val id = ctx.pathParam("id").toLong()
val product = Session.database.find(Product::class.java, id) ?: throw NotFoundResponse("product not found for $id")
val updatedProduct = ctx.bodyAsClass<Product>()
product.patchValues(updatedProduct)
product.update()
ctx.json(product).status(HttpStatus.OK)
}
@JvmStatic
fun main(args: Array<String>) {
val patchValues = mapOf("name" to 1)
val id = 1;
Session.database.beginTransaction().use {
patchValues.entries.forEach { en ->
val key = en.key
val value = en.value
Session.database.sqlUpdate("update product set $key = ? where sys_pk = ?").apply {
setParameter(1, value)
setParameter(2, id)
execute()
}
}
it.commit()
}
}
}

View File

@ -0,0 +1,269 @@
package com.restapi.controllers
import com.restapi.domain.*
import io.javalin.http.Context
import io.javalin.http.HttpStatus
import io.javalin.http.NotFoundResponse
import io.javalin.http.bodyAsClass
import java.io.FileInputStream
object PurchaseOrderCtrl {
fun getNextNum(ctx: Context) {
val prefix = "PO/"
val cnt = (Session.database.find(PurchaseOrder::class.java)
.findCount() + 1)
.toString()
.padStart(6, '0')
val seq = SequenceNumber(prefix + cnt)
ctx.json(seq).status(HttpStatus.OK)
}
fun get(ctx: Context) {
val id = ctx.pathParam("id")
val po = Session.database.find(PurchaseOrder::class.java, id) ?: throw NotFoundResponse("po not found for $id")
ctx.json(po).status(HttpStatus.OK)
}
data class PoFilterQuery(val common: CommonFilters, val poFilters: POFilters)
fun getAll(ctx: Context) {
val filters = ctx.bodyAsClass<PoFilterQuery>()
val pos = searchPos(filters.common, filters.poFilters)
val excel = ctx.queryParam("excel")
if (excel != null) {
exportPos(pos)
val inputStream = FileInputStream("./excel/Pos.xls")
ctx.result(inputStream).status(HttpStatus.OK)
} else {
ctx.json(pos).status(HttpStatus.OK)
}
}
fun create(ctx: Context) {
val po = ctx.bodyAsClass<PurchaseOrder>()
val prods = po.products
if (prods.isEmpty()) {
ctx.json(mapOf("error" to "empty product list")).status(HttpStatus.BAD_REQUEST)
return
}
Session.database.save(po)
ctx.json(po).status(HttpStatus.CREATED)
}
fun createBatch(ctx: Context) {
val pos = ctx.bodyAsClass<List<PurchaseOrder>>()
val txn = Session.database.beginTransaction()
try {
txn.isBatchMode = true
for (po in pos) Session.database.save(po)
txn.commit()
ctx.status(HttpStatus.CREATED).result("POS Created")
} catch (e: Exception) {
txn.rollback()
ctx.status(HttpStatus.INTERNAL_SERVER_ERROR).result("Pos Creation failed" + e.message)
} finally {
txn.end()
}
ctx.result("pos batch created").status(HttpStatus.CREATED)
}
fun approve(ctx: Context) {
val id = ctx.pathParam("id")
val po = Session.database.find(PurchaseOrder::class.java, id) ?: throw NotFoundResponse("po not found for $id")
po.approvalStatus = ApprovalStatus.APPROVED
po.save()
ctx.json(po).status(HttpStatus.CREATED)
//reject all other pos pertaining to the same tx ??
}
fun reject(ctx: Context) {
val id = ctx.pathParam("id")
val po = Session.database.find(PurchaseOrder::class.java, id) ?: throw NotFoundResponse("po not found for $id")
po.apply {
approvalStatus = ApprovalStatus.REJECTED
save()
}
ctx.json(po).status(HttpStatus.CREATED)
}
fun quoteReference(ctx: Context) {
//gets the quote reference on which this po is based on
val id = ctx.pathParam("id")
val quote = Session.database.find(Quotation::class.java)
.where()
.eq("referenceQuotation", id)
?: throw NotFoundResponse("reference quotation not found for po $id")
ctx.json(quote)
}
fun update(ctx: Context) {
val id = ctx.pathParam("id").toLong()
val po = Session.database.find(PurchaseOrder::class.java, id) ?: throw NotFoundResponse("po not found for $id")
val updatedPo = ctx.bodyAsClass<PurchaseOrder>()
po.patchValues(updatedPo)
po.update()
ctx.json(po).status(HttpStatus.OK)
}
fun delete(ctx: Context) {
val id = ctx.pathParam("id")
val po = Session.database.find(PurchaseOrder::class.java, id) ?: throw NotFoundResponse("no po found with id $id")
Session.database.delete(po)
ctx.status(HttpStatus.OK)
}
}
object QuotationCtrl {
fun getNextNum(ctx: Context) {
val prefix = "QUOTE/"
val cnt = Session.database.find(Quotation::class.java)
.findCount()
.toString()
.padStart(6, '0')
val seq = SequenceNumber(prefix + cnt)
ctx.json(seq).status(HttpStatus.OK)
}
fun get(ctx: Context) {
val id = ctx.pathParam("id")
val quote = Session.database.find(Quotation::class.java, id) ?: throw NotFoundResponse("quote not found for $id")
ctx.status(HttpStatus.OK)
ctx.json(quote)
}
data class QF(val common: CommonFilters, val quoteFilters: QuoteFilters)
fun getAll(ctx: Context) {
val filters = ctx.bodyAsClass<QF>()
val excel: String? = ctx.queryParam("excel")
val quotes = searchQuotes(filters.common, filters.quoteFilters)
if (excel != null) {
exportQuotations(quotes)
val inputStream = FileInputStream("./excel/Quotes.xls")
ctx.result(inputStream).status(HttpStatus.OK)
} else {
ctx.json(quotes).status(HttpStatus.OK)
}
}
fun create(ctx: Context) {
val quote = ctx.bodyAsClass<Quotation>()
//validation
val prods = quote.products
if (prods.isEmpty()) {
ctx.json(mapOf("error" to "empty product list")).status(HttpStatus.BAD_REQUEST)
return
}
Session.database.save(quote)
ctx.json(quote).status(HttpStatus.CREATED)
}
fun createBatch(ctx: Context) {
val quotes = ctx.bodyAsClass<List<Quotation>>()
val txn = Session.database.beginTransaction()
try {
txn.isBatchMode = true
for (quote in quotes) Session.database.save(quote)
txn.commit()
ctx.status(HttpStatus.CREATED).result("Quotes Created")
} catch (e: Exception) {
txn.rollback()
ctx.status(HttpStatus.INTERNAL_SERVER_ERROR).result("Quotes Creation failed" + e.message)
} finally {
txn.end()
}
ctx.result("Quotes batch created").status(HttpStatus.CREATED)
}
fun delete(ctx: Context) {
val id = ctx.pathParam("id")
val quote = Session.database.find(Quotation::class.java, id) ?: throw NotFoundResponse("no quote found with id $id")
Session.database.delete(quote)
ctx.status(HttpStatus.OK)
}
fun update(ctx: Context) {
val id = ctx.pathParam("id").toLong()
val quote = Session.database.find(Quotation::class.java, id) ?: throw NotFoundResponse("quote not found for $id")
val updatedQuote = ctx.bodyAsClass<Quotation>()
quote.patchValues(updatedQuote)
quote.update()
ctx.json(quote).status(HttpStatus.OK)
}
}
object RequestForQuote {
fun create(ctx: Context) {
val rfq = ctx.bodyAsClass<ReqForQuote>()
Session.database.save(rfq)
//ctx.result("request for quote created")
//ctx.json(rfq)
ctx.status(HttpStatus.CREATED)
ctx.json("asss")
}
fun get(ctx: Context) {
val id = ctx.pathParam("id")
val rfq = Session.database.find(ReqForQuote::class.java, id)
?: throw NotFoundResponse("request for quote not found for id $id")
ctx.status(HttpStatus.OK)
ctx.json(rfq)
}
fun update(ctx: Context) {
//shuld we compare the new body fields with preexisting ones and prepare a sql query to update those fields??
}
}
object InvoiceCtrl {
fun create(ctx: Context) {
val invoice = ctx.bodyAsClass<Invoice>()
Session.database.save(invoice)
ctx.json(invoice).status(HttpStatus.CREATED)
}
fun get(ctx: Context) {
val id = ctx.pathParam("id").toLong()
val invoice = Session.database.find(Invoice::class.java, id)
?: throw NotFoundResponse("No invoice found with id $id")
ctx.json(invoice).status(HttpStatus.OK)
}
data class INVF(val common: CommonFilters, val invoiceFilters: InvoiceFilters)
fun getAll(ctx: Context) {
val filters = ctx.bodyAsClass<INVF>()
val invoices = searchInvoices(filters.common, filters.invoiceFilters)
val excel = ctx.queryParam("excel")
if (excel !== null) {
exportInvoices(invoices)
val inputStream = FileInputStream("./excel/Invoices.xls")
ctx.result(inputStream).status(HttpStatus.OK)
} else {
ctx.json(invoices).status(HttpStatus.OK)
}
}
fun update(ctx: Context) {
val id = ctx.pathParam("id").toLong()
val invoice =
Session.database.find(Invoice::class.java, id) ?: throw NotFoundResponse("invoice not found for $id")
val updatedPayment = ctx.bodyAsClass<Invoice>()
invoice.patchValues(updatedPayment)
invoice.update()
ctx.json(invoice).status(HttpStatus.OK)
}
fun getNextNum(ctx: Context) {
val prefix = "INV/"
val cnt = Session.database.find(Invoice::class.java)
.findCount()
.toString()
.padStart(6, '0')
val seq = SequenceNumber(prefix + cnt)
ctx.json(seq).status(HttpStatus.OK)
}
}

View File

@ -0,0 +1,96 @@
package com.restapi.controllers
import com.restapi.domain.PurchaseOrder
import com.restapi.domain.Quotation
import com.restapi.domain.Session
import com.restapi.domain.Vendor
import io.javalin.http.Context
import io.javalin.http.HttpStatus
import io.javalin.http.NotFoundResponse
import io.javalin.http.bodyAsClass
import org.slf4j.LoggerFactory
import java.io.FileInputStream
object VendorCtrl {
val logger = LoggerFactory.getLogger("Vendor")
fun get(ctx: Context) {
val id = ctx.pathParam("id").toLong()
val vendor = Session.database.find(Vendor::class.java, id) ?: throw NotFoundResponse("no vendor found with id $id")
ctx.status(HttpStatus.OK)
ctx.json(vendor)
}
data class VF(val common: CommonFilters, val vendorFilters: VendorFilters)
fun getAll(ctx: Context) {
val filters = ctx.bodyAsClass<VF>()
val excel: String? = ctx.queryParam("excel")
val vendors = searchVendors(filters.common, filters.vendorFilters)
if (excel !== null) {
exportVendors(vendors)
val inputStream = FileInputStream("./excel/VendorList.xls")
ctx.result(inputStream).status(HttpStatus.OK)
} else {
ctx.json(vendors).status(HttpStatus.OK)
}
}
fun createBatch(ctx: Context) {
val vendors = ctx.bodyAsClass<List<Vendor>>()
Session.database.saveAll(vendors)
}
fun create(ctx: Context) {
val vendor = ctx.bodyAsClass<Vendor>()
Session.database.save(vendor)
ctx.status(HttpStatus.CREATED)
ctx.json(vendor)
}
fun update(ctx: Context) {
val id = ctx.pathParam("id").toLong()
val vendor = Session.database.find(Vendor::class.java, id) ?: throw NotFoundResponse("vendor not found for $id")
val updatedVendor = ctx.bodyAsClass<Vendor>()
vendor.patchValues(updatedVendor)
vendor.update()
ctx.json(vendor).status(HttpStatus.OK)
}
fun delete(ctx: Context) {
val id = ctx.pathParam("id")
val vendor = Session.database.find(Vendor::class.java, id) ?: throw NotFoundResponse("no vendor found with id $id")
Session.database.delete(vendor)
ctx.status(HttpStatus.OK)
}
fun getQuotes(ctx: Context) {
val id = ctx.pathParam("id").toLong()
val quotes = Session.database.find(Quotation::class.java)
.where()
.eq("vendor", Session.database.find(Vendor::class.java, id) ?: throw NotFoundResponse("vendor not found for $id"))
.findList()
ctx.json(quotes).status(HttpStatus.OK)
}
fun getPos(ctx: Context) {
val id = ctx.pathParam("id").toLong()
val pos = Session.database.find(PurchaseOrder::class.java)
.where()
.eq("vendor", Session.database.find(Vendor::class.java, id) ?: throw NotFoundResponse("vendor not found for $id"))
.findList()
ctx.json(pos).status(HttpStatus.OK)
}
fun rate(ctx: Context) {
val id = ctx.pathParam("id")
val rating1 = ctx.pathParam("rating").toDouble()
Session.database.find(Vendor::class.java, id)?.let {
it.rating = rating1
it.save()
} ?: throw NotFoundResponse("vendor not found for id $id")
ctx.result("rating changed").status(HttpStatus.OK)
}
}

View File

@ -0,0 +1,113 @@
package com.restapi.domain
import io.ebean.annotation.DbArray
import io.ebean.annotation.DbJsonB
import java.time.LocalDate
import javax.persistence.Entity
import javax.persistence.ManyToOne
data class FR(val renewal: String, val date: LocalDate)
@Entity
open class Fleet : BaseTenantModel() {
fun patchValues(updated: Fleet) {
this.name = updated.name
this.type = updated.type
this.regNumber = updated.regNumber
this.model = updated.model
this.make = updated.make
this.driver = updated.driver
this.driverContact = updated.driverContact
this.mileage = updated.mileage
this.cost = updated.cost
this.insuranceRenewalDate = updated.insuranceRenewalDate
this.pollutionRenewalDate = updated.pollutionRenewalDate
this.fitnessRenewalDate = updated.fitnessRenewalDate
this.renewals = updated.renewals
this.regDate = updated.regDate
}
var name: String = ""
var type: String = ""
var regNumber: String = ""
var regDate: LocalDate? = null
var model: String = ""
var make: String = ""
var driver: String = ""
var driverContact: String = ""
var mileage: Double = 0.0
var cost: Double = 0.0
var insuranceRenewalDate: LocalDate? = null
var pollutionRenewalDate: LocalDate? = null
var fitnessRenewalDate: LocalDate? = null
@DbJsonB
var renewals: List<FR>? = null
}
@Entity
open class FleetType : BaseTenantModel() {
fun patchValues(updated: FleetType) {
this.name = updated.name
this.personIncharge = updated.personIncharge
}
var name: String = ""
var personIncharge: String = ""
}
@Entity
open class Renewal : BaseTenantModel() {
fun patchValues(updated: Renewal) {
this.name = updated.name
}
var name: String = ""
}
@Entity
open class Reminder : BaseTenantModel() {
fun patchValues(updated: Reminder) {
this.type = updated.type
this.nextRenewalDate = updated.nextRenewalDate
this.lastRenewalDate = updated.lastRenewalDate
this.frequency = updated.frequency
this.documents = updated.documents
}
var type: String = "Other"
var nextRenewalDate: LocalDate? = null
var lastRenewalDate: LocalDate? = null
var amount: Double = 0.0
var frequency: Int = 1
@DbArray
var documents: List<String>? = arrayListOf()
@ManyToOne
var fleet: Fleet? = null
}
@Entity
open class ReminderLog : BaseTenantModel() {
fun patchValues(updated: ReminderLog) {
this.fleet = updated.fleet
this.reminderDate = updated.reminderDate
this.reminderType = updated.reminderType
this.actedUpon = updated.actedUpon
this.documents = updated.documents
this.amount = updated.amount
}
@ManyToOne
var reminder: Reminder? = null
var reminderType: String = "Other"
var reminderDate: LocalDate? = null
@ManyToOne
var fleet: Fleet? = null
var actedUpon: Boolean = false
var amount: Double = 0.0
@DbArray
var documents: List<String>? = null
}

View File

@ -0,0 +1,76 @@
package com.restapi.domain
import io.ebean.annotation.DbJsonB
import java.time.LocalDate
import javax.persistence.Entity
import javax.persistence.EnumType
import javax.persistence.Enumerated
import javax.persistence.ManyToOne
@Entity
open class IncomingInventory : BaseTenantModel() {
fun patchValues(updated: IncomingInventory) {
this.date = updated.date
this.vendorBillNum = updated.vendorBillNum
this.vendorBillAmount = updated.vendorBillAmount
this.vehicle = updated.vehicle
this.products = updated.products
this.vendor = updated.vendor
this.loading = updated.loading
this.unloading = updated.unloading
this.unloadingPlantId = updated.unloadingPlantId
}
var mrn: String? = null
var date: LocalDate? = null
var vendorBillNum: String? = null
var vendorBillAmount: Double = 0.0
var vehicle: String = ""
var loading: String? = ""
var unloadingPlantId: String? = null
var unloading: String? = ""
@DbJsonB
var products: List<POProducts>? = null
@ManyToOne
var vendor: Vendor? = null
}
enum class OutMode {
PERSON, VEHICLE, ALL, JOB, OFFICE
}
@Entity
open class OutgoingInventory : BaseTenantModel() {
fun patchValues(updated: OutgoingInventory) {
this.date = updated.date
this.products = updated.products
this.purpose = updated.purpose
this.outMode = updated.outMode
this.person = updated.person
this.vehicle = updated.vehicle
this.jobCard = updated.jobCard
this.unloading = updated.unloading
this.unloadingPlantId = updated.unloadingPlantId
}
var mdn: String? = null
var date: LocalDate? = null
var unloadingPlantId: String? = null
var unloading: String? = ""
@DbJsonB
var products: List<POProducts>? = null
var purpose: String? = null
@Enumerated(EnumType.STRING)
var outMode: OutMode? = null
var person: String? = null
var jobCard: String? = null
var vehicle: String? = null
}

View File

@ -0,0 +1,42 @@
package com.restapi.domain
import javax.persistence.Column
import javax.persistence.Entity
import javax.persistence.EnumType
import javax.persistence.Enumerated
enum class UOM {
KG, CFT, TON, LTR, NUMBER, MTR, BOX, CUM, PACKET, ALL, NOS, LITER;
}
enum class TypeOfProduct {
RAW_MATERIAL
}
@Entity
open class Product : BaseTenantModel() {
fun patchValues(updatedProduct: Product) {
this.name = updatedProduct.name
this.description = updatedProduct.description
this.hsnCode = updatedProduct.hsnCode
this.uom = updatedProduct.uom
this.gstPct = updatedProduct.gstPct
this.code = updatedProduct.code
}
@Column(name = "id")
var code: String? = null
var name: String = ""
var description: String = ""
var hsnCode: String = ""
@Enumerated(EnumType.STRING)
var uom: UOM? = null
var gstPct: Double? = 0.0
@Enumerated(EnumType.STRING)
var type: TypeOfProduct? = null
}

View File

@ -0,0 +1,201 @@
package com.restapi.domain
import io.ebean.annotation.DbArray
import io.ebean.annotation.DbJsonB
import java.time.LocalDate
import java.util.*
import javax.persistence.Entity
import javax.persistence.EnumType
import javax.persistence.Enumerated
import javax.persistence.ManyToOne
enum class AddressType {
BILLING, SHIPPING
}
data class ContactPerson(
val id: String = UUID.randomUUID().toString(),
val name: String = "", val email: String = "", val mobile: String = ""
)
data class Address(
val id: String = UUID.randomUUID().toString(),
val type: AddressType = AddressType.BILLING,
val address: String = "",
val pincode: String = ""
)
@Entity
open class Vendor : BaseTenantModel() {
fun patchValues(updatedVendor: Vendor) {
this.name = updatedVendor.name
this.msme = updatedVendor.msme
this.gstNumber = updatedVendor.gstNumber
this.address = updatedVendor.address
this.rating = updatedVendor.rating
this.contacts = updatedVendor.contacts
this.outstanding = updatedVendor.outstanding
this.asOnWhichDate = updatedVendor.asOnWhichDate
this.addressList = updatedVendor.addressList
}
var name: String = ""
var msme: String = ""
var gstNumber: String = ""
var address: String = ""
var rating: Double = 0.0
var outstanding: Double? = 0.0
var asOnWhichDate: LocalDate? = null
@DbJsonB
var addressList: List<Address>? = mutableListOf()
@DbJsonB
var contacts: List<ContactPerson> = mutableListOf()
}
@Entity
open class PurchaseOrder : BaseTenantModel() {
fun patchValues(updatedPo: PurchaseOrder) {
this.poDate = updatedPo.poDate
this.validTill = updatedPo.validTill
this.tnc = updatedPo.tnc
this.products = updatedPo.products
this.vendor = updatedPo.vendor
this.documents = updatedPo.documents
this.totalAmount = updatedPo.totalAmount
this.allLoadingSite = updatedPo.allLoadingSite
this.allUnloadingSite = updatedPo.allUnloadingSite
this.unloadingPlantId = updatedPo.unloadingPlantId
this.loadingSiteId = updatedPo.loadingSiteId
}
@DbJsonB
var products: MutableList<POProducts> = mutableListOf()
@ManyToOne
var vendor: Vendor? = null
var referenceQuotation: String? = ""
var totalAmount: Double = 0.0
var poNum: String = ""
var poDate: LocalDate? = null
var validTill: LocalDate? = null
var allLoadingSite: Boolean = true
var allUnloadingSite: Boolean = true
var unloadingPlantId: String? = null
var loadingSiteId: UUID? = null
@DbArray
var tnc: List<String>? = arrayListOf()
@DbArray
var documents: List<String>? = arrayListOf()
}
@Entity
open class Quotation : BaseTenantModel() {
fun patchValues(updatedQuote: Quotation) {
this.quoteDate = updatedQuote.quoteDate
this.vendorQuoteNum = updatedQuote.vendorQuoteNum
this.validTill = updatedQuote.validTill
this.reqForQuoteNum = updatedQuote.reqForQuoteNum
this.tnc = updatedQuote.tnc
this.products = updatedQuote.products
this.vendor = updatedQuote.vendor
this.documents = updatedQuote.documents
this.totalAmount = updatedQuote.totalAmount
}
@DbJsonB
var products: MutableList<POProducts> = mutableListOf()
@ManyToOne
var vendor: Vendor? = null
var totalAmount: Double = 0.0
var reqForQuoteNum: String? = ""
var quoteNum: String = ""
var vendorQuoteNum: String? = ""
var quoteDate: LocalDate? = null
var validTill: LocalDate? = null
@DbArray
var tnc: List<String>? = arrayListOf()
@DbArray
var documents: List<String>? = arrayListOf()
var taxesIncluded: Boolean? = null
}
enum class RFQStatus {
DELIVERED, PO, QUOTE, CANCELLED
}
@Entity
open class ReqForQuote : BaseTenantModel() {
@DbArray
var potentialVendors: List<Long>? = null
@Enumerated(EnumType.STRING)
var status: RFQStatus? = null
@DbJsonB
var products: List<POProducts>? = null
var reqForQuoteNum: String? = null
var openTill: LocalDate? = null
}
@Entity
open class Invoice : BaseTenantModel() {
fun patchValues(updated: Invoice) {
this.date = updated.date
this.number = updated.number
this.totalAmount = updated.totalAmount
this.poNum = updated.poNum
this.products = updated.products
this.vendor = updated.vendor
this.status = updated.status
}
var number: String = ""
var date: LocalDate? = null
var totalAmount: Double = 0.0
var poNum: String? = null
@DbJsonB
var products: List<POProducts>? = null
@ManyToOne
var vendor: Vendor? = null
@Enumerated(EnumType.STRING)
var status: InvoiceStatus? = null
}
@Entity
open class Payment : BaseTenantModel() {
fun patchValues(updated: Payment) {
this.refNumber = updated.refNumber
this.amount = updated.amount
this.date = updated.date
this.remark = updated.remark
this.vendor = updated.vendor
}
var refNumber: String = ""
var amount: Double = 0.0
var date: LocalDate? = null
var remark: String? = null
var amountDeducted: Double? = null
var excessAmount: Double? = null
@DbJsonB
var invoicesAffected: MutableMap<Long, Double>? = mutableMapOf()
@ManyToOne
var vendor: Vendor? = null
}

View File

@ -30,7 +30,7 @@ import java.security.spec.PKCS8EncodedKeySpec
import java.security.spec.X509EncodedKeySpec import java.security.spec.X509EncodedKeySpec
import java.time.LocalDateTime import java.time.LocalDateTime
import java.util.* import java.util.*
import kotlin.collections.HashMap import java.util.concurrent.ConcurrentHashMap
import kotlin.jvm.optionals.getOrDefault import kotlin.jvm.optionals.getOrDefault
@ -39,7 +39,7 @@ object Session {
private val logger = LoggerFactory.getLogger("session") private val logger = LoggerFactory.getLogger("session")
private val currentUser = object : ThreadLocal<AuthUser>() { private val currentUser = object : ThreadLocal<AuthUser>() {
override fun initialValue(): AuthUser { override fun initialValue(): AuthUser {
return AuthUser("", "", emptyList(), "", LocalDateTime.now()) return AuthUser("", "", emptyList(), "", LocalDateTime.now(), emptyList())
} }
} }
@ -138,27 +138,12 @@ object Session {
} }
fun a(){ val objectMapper = jacksonObjectMapper().apply {
val a = HashMap<String,String>() configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
a.put("a", "b"); configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false)
a.put("a", "b"); findAndRegisterModules()
a.put("a", "b");
a.put("a", "b");
val b = HashMap<String,String>().apply {
put("a", "b");
put("a", "b");
put("a", "b");
put("a", "b");
} }
val c: String? = ""
val x = c?.get(1)
c?.apply {
//will work only when c is not null
}
}
private val sc = DatabaseConfig().apply { private val sc = DatabaseConfig().apply {
loadFromProperties(Properties().apply { loadFromProperties(Properties().apply {
setProperty("datasource.db.username", appConfig.dbUser()) setProperty("datasource.db.username", appConfig.dbUser())
@ -169,20 +154,27 @@ object Session {
tenantMode = TenantMode.PARTITION tenantMode = TenantMode.PARTITION
currentTenantProvider = CurrentTenantProvider { currentUser.get().tenant } currentTenantProvider = CurrentTenantProvider { currentUser.get().tenant }
currentUserProvider = CurrentUserProvider { currentUser.get().userName } currentUserProvider = CurrentUserProvider { currentUser.get().userName }
objectMapper = Session.objectMapper
} }
val database: Database = DatabaseFactory.create(sc) val database: Database = DatabaseFactory.create(sc)
val objectMapper = jacksonObjectMapper().apply {
configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false)
findAndRegisterModules()
}
fun currentUser() = currentUser.get().userName fun currentUser() = currentUser.get().userName
fun currentTenant() = currentUser.get().tenant fun currentTenant() = currentUser.get().tenant
fun currentRoles() = currentUser.get().roles fun currentRoles() = currentUser.get().roles
fun currentToken() = currentUser.get().token fun currentUserPlants() = currentUser.get().plantIds.mapNotNull {
fun jwk() = keypair.toParams(JsonWebKey.OutputControlLevel.PUBLIC_ONLY) database.find(Plant::class.java)
.where()
.eq("plantId", it)
.findOne()
?.apply {
if (this.prefixes == null || this.prefixes.isNullOrEmpty()) {
this.prefixes = mutableMapOf("INBOUND" to "MRN/")
}
}
}
fun jwk(): MutableMap<String, Any> = keypair.toParams(JsonWebKey.OutputControlLevel.PUBLIC_ONLY)
fun Database.findDataModelByEntityAndUniqId(entity: String, uniqId: String): DataModel { fun Database.findDataModelByEntityAndUniqId(entity: String, uniqId: String): DataModel {
return find(DataModel::class.java) return find(DataModel::class.java)
@ -192,20 +184,22 @@ object Session {
.findOne() ?: throw DataNotFoundException .findOne() ?: throw DataNotFoundException
} }
private fun seqName(entity: String) = "sequence_$entity" private fun seqName(entity: String) = "sequence_${entity}_${currentTenant()}"
private val seqCreationCache = ConcurrentHashMap<String, Int>()
fun creatSeq(entity: String): Int { fun creatSeq(entity: String): Int {
return database.sqlUpdate("CREATE SEQUENCE IF NOT EXISTS ${seqName(entity)} START 1;").execute() return seqCreationCache.computeIfAbsent(entity){
database.sqlUpdate("CREATE SEQUENCE IF NOT EXISTS ${seqName(entity)} START 1;").execute()
}
} }
fun nextUniqId(entity: String): String { fun nextUniqId(entity: String): String {
val s = database val s = database
.sqlQuery("SELECT nextval('${seqName(entity)}');") .sqlQuery("SELECT nextval('${seqName(entity)}');")
.findOne()?.getLong("nextval") ?: throw DataNotFoundException .findOne()?.getLong("nextval") ?: throw DataNotFoundException
return "$entity-${"$s".padStart(10, '0')}" return "$entity-${"$s".padStart(6, '0')}"
} }
val redis = JedisPooled(appConfig.redisUri().getOrDefault("redis://localhost:6739/0"))
} }
object DataNotFoundException : Exception() { object DataNotFoundException : Exception() {

View File

@ -8,7 +8,7 @@ object DBMigration {
private fun create(){ private fun create(){
val dbMigration: DbMigration = DbMigration.create() val dbMigration: DbMigration = DbMigration.create()
dbMigration.setPlatform(Platform.POSTGRES) dbMigration.setPlatform(Platform.POSTGRES)
//dbMigration.setGeneratePendingDrop("1.8")
dbMigration.generateMigration() dbMigration.generateMigration()
} }

View File

@ -9,11 +9,30 @@ import io.ebean.annotation.*
import io.ebean.annotation.Index import io.ebean.annotation.Index
import java.time.LocalDate import java.time.LocalDate
import java.time.LocalDateTime import java.time.LocalDateTime
import java.util.*
import javax.persistence.* import javax.persistence.*
data class Comments(val text: String = "", val by: String = "", val at: LocalDateTime = LocalDateTime.now()) data class Comments(val text: String = "", val by: String = "", val at: LocalDateTime = LocalDateTime.now())
data class POProducts(val productId: String = "", val unitPrice :Double = 0.0, val quantity: Double = 0.0, val description :String = "") data class ReceivedVoucher(val ticket: String, val productName: String, val productId: Long, val qty: Double, val plantId: String = "", val ticketDate: LocalDate = LocalDate.now())
data class POProducts(
val productId: Long = 0,
val productName: String = "",
val unitPrice: Double = 0.0,
val quantity: Double = 0.0,
val orderedQty: Double = 0.0,
val receivedQty: Double = 0.0,
val issuedQty: Double = 0.0,
val receivedVoucher: MutableList<ReceivedVoucher> = arrayListOf(),
val billQty: Double = 0.0,
val gstPct: Double = 0.0,
val taxableValue: Double = 0.0,
val totalValue: Double = 0.0,
val description: String = "",
val uom: String = "",
val poId: Long? = null,
val isAvailable: Boolean? = false
)
enum class ApprovalStatus { enum class ApprovalStatus {
PENDING, APPROVED, REJECTED PENDING, APPROVED, REJECTED
} }
@ -104,15 +123,13 @@ open class AuditLog : BaseTenantModel() {
@DbJsonB @DbJsonB
@Index( @Index(
definition = "create index audit_log_values_idx on audit_log using GIN (data)", definition = "create index audit_log_values_idx on audit_log using GIN (data)", platforms = [Platform.POSTGRES]
platforms = [Platform.POSTGRES]
) )
var data: Map<String, Any> = hashMapOf() var data: Map<String, Any> = hashMapOf()
@DbJsonB @DbJsonB
@Index( @Index(
definition = "create index audit_log_changes_idx on audit_log using GIN (changes)", definition = "create index audit_log_changes_idx on audit_log using GIN (changes)", platforms = [Platform.POSTGRES]
platforms = [Platform.POSTGRES]
) )
var changes: Map<String, Any> = hashMapOf() var changes: Map<String, Any> = hashMapOf()
} }
@ -168,18 +185,6 @@ enum class JobType {
SCRIPT, DB SCRIPT, DB
} }
@Entity
@Index(unique = true, name = "sql_unique_id", columnNames = ["entity_name", "sql_id", "tenant_id"])
open class SqlModel : BaseTenantModel() {
@JsonDeserialize(using = SafeStringDeserializer::class)
var sqlId: String = ""
var entityName: String = ""
@Column(columnDefinition = "text")
var sql: String = ""
}
@Entity @Entity
open class JobModel : BaseTenantModel() { open class JobModel : BaseTenantModel() {
@ -217,7 +222,6 @@ open class DataModel : BaseTenantModel() {
} }
@Entity @Entity
@Index(unique = true, name = "unique_session_id", columnNames = ["session_id"]) @Index(unique = true, name = "unique_session_id", columnNames = ["session_id"])
open class AnonSession : BaseTenantModel() { open class AnonSession : BaseTenantModel() {
@ -232,103 +236,100 @@ open class AnonSession : BaseTenantModel() {
} }
class SafeStringDeserializer : JsonDeserializer<String>() { class SafeStringDeserializer : JsonDeserializer<String>() {
companion object {
private val regex = Regex("^[a-zA-Z0-9\\-_\\.]+$") private val regex = Regex("^[a-zA-Z0-9\\-_\\.]+$")
fun isSafe(s: String) = regex.matches(s)
}
override fun deserialize(p: JsonParser, ctxt: DeserializationContext?): String { override fun deserialize(p: JsonParser, ctxt: DeserializationContext?): String {
val text = p.text val text = p.text
if (!regex.matches(text)) throw IllegalArgumentException() if (!isSafe(text)) throw IllegalArgumentException()
return text return text
} }
} }
data class ContactPerson(val name: String, val email: String, val mobile: String)
@Entity
open class Vendor :BaseTenantModel() {
var name :String = ""
var msme :String = ""
var gstNumber :String = ""
var address :String = ""
var rating :Double = 0.0
@DbJsonB
var contacts :List<ContactPerson> = mutableListOf()
}
@Entity
open class PurchaseOrder :BaseTenantModel() {
@DbJsonB
var products :MutableList<POProducts> = mutableListOf()
@ManyToOne
var vendor :Vendor? = null
var referenceQuotation :String = ""
var totalAmount :Int = 0
var poNum: String = ""
var poDate: LocalDate? = null
var validTill: LocalDate? = null
@DbArray
var tnc: List<String> = arrayListOf()
@DbArray
var documents: MutableList<Long> = arrayListOf()
}
enum class UOM { enum class DocType {
NOS, LTR, MTR, ALL
}
@Entity
open class Product :BaseTenantModel() {
var id: Int? = null
var name :String = ""
var description :String = ""
var hsnCode :String = ""
@Enumerated(EnumType.STRING)
var uom: UOM? = null
}
@Entity
open class Quotation :BaseTenantModel() {
@DbJsonB
var products :MutableList<POProducts> = mutableListOf()
@ManyToOne
var vendor :Vendor? = null
var totalAmount :Long = 0
var reqForQuoteNum: String = ""
var quoteNum: String = ""
var quoteDate: LocalDate? = null
var validTill: LocalDate? = null
@DbArray
var tnc: List<String> = arrayListOf()
@DbArray
var documents: MutableList<Long> = arrayListOf()
}
enum class DocType{
PO, QUOTE, INVOICE, ALL PO, QUOTE, INVOICE, ALL
} }
@Entity @Entity
open class Document :BaseTenantModel() { open class Document : BaseTenantModel() {
var name :String = "" var name: String = ""
@Enumerated(EnumType.STRING) @Enumerated(EnumType.STRING)
var typeOfDoc :DocType? = null var typeOfDoc: DocType? = null
var refId: Long? = null
var description :String = "" //could be quoteNum, PoNum, InvoiceNum
var url :String = "" var refIdOfDoc: Long? = null
var description: String = ""
var url: String = ""
var docDate: LocalDate? = null
} }
enum class RFQStatus{
DELIVERED, PO, QUOTE, CANCELLED enum class InvoiceStatus {
PAID_FULL, PAID_SOME, PAID_NONE, ALL
} }
@Entity @Entity
open class ReqForQuote :BaseTenantModel() { @Index(name = "plantid_idx", columnNames = ["plant_id"], unique = true)
@DbArray open class Plant : BaseModel() {
var potentialVendors :List<Long>? = null fun patch(p: Plant) {
@Enumerated(EnumType.STRING) this.plantName = p.plantName
var status :RFQStatus? = null this.prefixes?.apply {
// @DbArray this.clear()
// var docs :List<Document>? = null this.putAll(p.prefixes ?: emptyMap())
}
this.print?.apply {
this.clear()
this.putAll(p.print ?: emptyMap())
}
this.limits?.apply {
this.clear()
this.putAll(p.limits ?: emptyMap())
}
this.save()
}
var plantId: String = ""
var plantName: String = ""
var plantOriginalName: String? = ""
@DbJsonB @DbJsonB
var products :List<POProducts>? = null var print: MutableMap<String, String>? = mutableMapOf()
var reqForQuoteNum: String? = null @DbJsonB
var openTill: LocalDate? = null var prefixes: MutableMap<String, String>? = mutableMapOf()
@DbJsonB
var limits: MutableMap<String, String>? = mutableMapOf()
}
class RefreshHistory {
var oldAt: String = ""
var oldExpiryAt: String = ""
var newAt: String = ""
var newExpiryAt: String = ""
var createdAt: String = ""
}
@Entity
open class AuthTokenCache : BaseModel() {
@Column(columnDefinition = "text")
var authToken: String = ""
var issuedAt: LocalDateTime = LocalDateTime.now()
var expiresAt: LocalDateTime = LocalDateTime.now()
var refreshExpiresAt: LocalDateTime = LocalDateTime.now()
@Column(columnDefinition = "text")
var refreshToken: String = ""
var userId: String = ""
var expired: Boolean = false
var loggedOut: Boolean = false
@DbJsonB
var refreshHistory: MutableList<RefreshHistory>? = arrayListOf()
} }

View File

@ -2,6 +2,7 @@ package com.restapi.integ
import com.restapi.config.AppConfig.Companion.appConfig import com.restapi.config.AppConfig.Companion.appConfig
import com.restapi.domain.DataModel import com.restapi.domain.DataModel
import com.restapi.domain.EntityModel
import com.restapi.domain.Session.currentTenant import com.restapi.domain.Session.currentTenant
import com.restapi.domain.Session.currentUser import com.restapi.domain.Session.currentUser
import com.restapi.domain.Session.database import com.restapi.domain.Session.database
@ -21,11 +22,11 @@ object Scripting {
engine as Invocable engine as Invocable
} }
fun execute(scriptName: String, fnName: String, params: Map<String, Any>): Any { fun execute(scriptName: String, fnName: String, params: Map<String, Any>, entityModel: EntityModel?,): Any {
return getEngine(scriptName).invokeFunction(fnName, params, database, logger, currentUser(), currentTenant()) return getEngine(scriptName).invokeFunction(fnName, params, entityModel, database, logger, currentUser(), currentTenant())
} }
fun execute(scriptName: String, fnName: String, dataModel: DataModel): Any { fun execute(scriptName: String, fnName: String, dataModel: DataModel, entityModel: EntityModel?,): Any {
return getEngine(scriptName).invokeFunction(fnName, dataModel, database, logger, currentUser(), currentTenant()) return getEngine(scriptName).invokeFunction(fnName, dataModel, entityModel, database, logger, currentUser(), currentTenant())
} }
} }

View File

@ -0,0 +1,2 @@
-- apply alter tables
alter table incoming_inventory drop column vendor_bil_num;

View File

@ -0,0 +1,3 @@
-- apply alter tables
alter table incoming_inventory drop column mdn;
alter table outgoing_inventory drop column mrn;

View File

@ -0,0 +1,64 @@
-- apply changes
create table invoice (
sys_pk bigint generated by default as identity not null,
deleted_on timestamp,
current_approval_level integer default 0 not null,
required_approval_levels integer default 0 not null,
date date,
total_amount float not null,
vendor_sys_pk bigint,
deleted boolean default false not null,
version integer default 1 not null,
created_at timestamp default 'now()' not null,
modified_at timestamp default 'now()' not null,
deleted_by varchar(255),
approval_status varchar(8) default 'APPROVED' not null,
tags varchar[] default '{}' not null,
comments jsonb default '[]' not null,
tenant_id varchar(255) not null,
number varchar(255) not null,
po_num varchar(255),
products jsonb,
status varchar(9),
created_by varchar(255) not null,
modified_by varchar(255) not null,
constraint ck_invoice_approval_status check ( approval_status in ('PENDING','APPROVED','REJECTED')),
constraint ck_invoice_status check ( status in ('PAID_FULL','PAID_SOME','PAID_NONE','ALL')),
constraint pk_invoice primary key (sys_pk)
);
create table payment (
sys_pk bigint generated by default as identity not null,
deleted_on timestamp,
current_approval_level integer default 0 not null,
required_approval_levels integer default 0 not null,
amount float not null,
date date,
vendor_sys_pk bigint,
deleted boolean default false not null,
version integer default 1 not null,
created_at timestamp default 'now()' not null,
modified_at timestamp default 'now()' not null,
deleted_by varchar(255),
approval_status varchar(8) default 'APPROVED' not null,
tags varchar[] default '{}' not null,
comments jsonb default '[]' not null,
tenant_id varchar(255) not null,
ref_number varchar(255) not null,
remark varchar(255),
created_by varchar(255) not null,
modified_by varchar(255) not null,
constraint ck_payment_approval_status check ( approval_status in ('PENDING','APPROVED','REJECTED')),
constraint pk_payment primary key (sys_pk)
);
-- apply alter tables
alter table vendor add column if not exists outstanding float;
alter table vendor add column if not exists as_on_which_date date;
-- foreign keys and indices
create index ix_invoice_vendor_sys_pk on invoice (vendor_sys_pk);
alter table invoice add constraint fk_invoice_vendor_sys_pk foreign key (vendor_sys_pk) references vendor (sys_pk) on delete restrict on update restrict;
create index ix_payment_vendor_sys_pk on payment (vendor_sys_pk);
alter table payment add constraint fk_payment_vendor_sys_pk foreign key (vendor_sys_pk) references vendor (sys_pk) on delete restrict on update restrict;

View File

@ -0,0 +1,3 @@
-- apply alter tables
alter table payment add column if not exists excess_amount float;
alter table payment add column if not exists invoices_affected jsonb;

View File

@ -0,0 +1,2 @@
-- apply alter tables
alter table payment add column if not exists amount_deducted float;

View File

@ -0,0 +1,33 @@
-- apply changes
create table fleet (
sys_pk bigint generated by default as identity not null,
deleted_on timestamp,
current_approval_level integer default 0 not null,
required_approval_levels integer default 0 not null,
mileage float not null,
cost float not null,
insurance_renewal_date date,
pollution_renewal_date date,
fitness_renewal_date date,
deleted boolean default false not null,
version integer default 1 not null,
created_at timestamp default 'now()' not null,
modified_at timestamp default 'now()' not null,
deleted_by varchar(255),
approval_status varchar(8) default 'APPROVED' not null,
tags varchar[] default '{}' not null,
comments jsonb default '[]' not null,
tenant_id varchar(255) not null,
name varchar(255) not null,
type varchar(255) not null,
reg_number varchar(255) not null,
model varchar(255) not null,
make varchar(255) not null,
driver varchar(255) not null,
driver_contact varchar(255) not null,
created_by varchar(255) not null,
modified_by varchar(255) not null,
constraint ck_fleet_approval_status check ( approval_status in ('PENDING','APPROVED','REJECTED')),
constraint pk_fleet primary key (sys_pk)
);

View File

@ -0,0 +1,67 @@
-- apply changes
create table fleet_type (
sys_pk bigint generated by default as identity not null,
deleted_on timestamp,
current_approval_level integer default 0 not null,
required_approval_levels integer default 0 not null,
deleted boolean default false not null,
version integer default 1 not null,
created_at timestamp default 'now()' not null,
modified_at timestamp default 'now()' not null,
deleted_by varchar(255),
approval_status varchar(8) default 'APPROVED' not null,
tags varchar[] default '{}' not null,
comments jsonb default '[]' not null,
tenant_id varchar(255) not null,
name varchar(255) not null,
person_incharge varchar(255) not null,
created_by varchar(255) not null,
modified_by varchar(255) not null,
constraint ck_fleet_type_approval_status check ( approval_status in ('PENDING','APPROVED','REJECTED')),
constraint pk_fleet_type primary key (sys_pk)
);
create table renewal (
sys_pk bigint generated by default as identity not null,
deleted_on timestamp,
current_approval_level integer default 0 not null,
required_approval_levels integer default 0 not null,
deleted boolean default false not null,
version integer default 1 not null,
created_at timestamp default 'now()' not null,
modified_at timestamp default 'now()' not null,
deleted_by varchar(255),
approval_status varchar(8) default 'APPROVED' not null,
tags varchar[] default '{}' not null,
comments jsonb default '[]' not null,
tenant_id varchar(255) not null,
name varchar(255) not null,
created_by varchar(255) not null,
modified_by varchar(255) not null,
constraint ck_renewal_approval_status check ( approval_status in ('PENDING','APPROVED','REJECTED')),
constraint pk_renewal primary key (sys_pk)
);
create table vehicle (
sys_pk bigint generated by default as identity not null,
deleted_on timestamp,
current_approval_level integer default 0 not null,
required_approval_levels integer default 0 not null,
deleted boolean default false not null,
version integer default 1 not null,
created_at timestamp default 'now()' not null,
modified_at timestamp default 'now()' not null,
deleted_by varchar(255),
approval_status varchar(8) default 'APPROVED' not null,
tags varchar[] default '{}' not null,
comments jsonb default '[]' not null,
tenant_id varchar(255) not null,
name varchar(255) not null,
created_by varchar(255) not null,
modified_by varchar(255) not null,
constraint ck_vehicle_approval_status check ( approval_status in ('PENDING','APPROVED','REJECTED')),
constraint pk_vehicle primary key (sys_pk)
);
-- apply alter tables
alter table fleet add column if not exists renewals jsonb;

View File

@ -0,0 +1,30 @@
-- apply changes
create table fleet_renewal (
sys_pk bigint generated by default as identity not null,
deleted_on timestamp,
current_approval_level integer default 0 not null,
required_approval_levels integer default 0 not null,
date date,
last_date date,
frequency integer not null,
fleet_sys_pk bigint,
deleted boolean default false not null,
version integer default 1 not null,
created_at timestamp default 'now()' not null,
modified_at timestamp default 'now()' not null,
deleted_by varchar(255),
approval_status varchar(8) default 'APPROVED' not null,
tags varchar[] default '{}' not null,
comments jsonb default '[]' not null,
tenant_id varchar(255) not null,
name varchar(255) not null,
created_by varchar(255) not null,
modified_by varchar(255) not null,
constraint ck_fleet_renewal_approval_status check ( approval_status in ('PENDING','APPROVED','REJECTED')),
constraint pk_fleet_renewal primary key (sys_pk)
);
-- foreign keys and indices
create index ix_fleet_renewal_fleet_sys_pk on fleet_renewal (fleet_sys_pk);
alter table fleet_renewal add constraint fk_fleet_renewal_fleet_sys_pk foreign key (fleet_sys_pk) references fleet (sys_pk) on delete restrict on update restrict;

View File

@ -0,0 +1,2 @@
-- apply alter tables
alter table fleet_renewal add column if not exists documents varchar[];

View File

@ -0,0 +1,62 @@
-- drop dependencies
alter table if exists fleet_renewal drop constraint if exists fk_fleet_renewal_fleet_sys_pk;
-- apply changes
create table reminder (
sys_pk bigint generated by default as identity not null,
deleted_on timestamp,
current_approval_level integer default 0 not null,
required_approval_levels integer default 0 not null,
next_renewal_date date,
last_renewal_date date,
amount float not null,
frequency integer not null,
fleet_sys_pk bigint,
deleted boolean default false not null,
version integer default 1 not null,
created_at timestamp default 'now()' not null,
modified_at timestamp default 'now()' not null,
deleted_by varchar(255),
approval_status varchar(8) default 'APPROVED' not null,
tags varchar[] default '{}' not null,
comments jsonb default '[]' not null,
tenant_id varchar(255) not null,
type varchar(255) not null,
documents varchar[],
created_by varchar(255) not null,
modified_by varchar(255) not null,
constraint ck_reminder_approval_status check ( approval_status in ('PENDING','APPROVED','REJECTED')),
constraint pk_reminder primary key (sys_pk)
);
create table reminder_log (
sys_pk bigint generated by default as identity not null,
deleted_on timestamp,
current_approval_level integer default 0 not null,
required_approval_levels integer default 0 not null,
reminder_date date,
fleet_sys_pk bigint,
acted_upon boolean default false not null,
amount float not null,
deleted boolean default false not null,
version integer default 1 not null,
created_at timestamp default 'now()' not null,
modified_at timestamp default 'now()' not null,
deleted_by varchar(255),
approval_status varchar(8) default 'APPROVED' not null,
tags varchar[] default '{}' not null,
comments jsonb default '[]' not null,
tenant_id varchar(255) not null,
reminder_type varchar(255) not null,
created_by varchar(255) not null,
modified_by varchar(255) not null,
constraint ck_reminder_log_approval_status check ( approval_status in ('PENDING','APPROVED','REJECTED')),
constraint pk_reminder_log primary key (sys_pk)
);
-- foreign keys and indices
create index ix_reminder_fleet_sys_pk on reminder (fleet_sys_pk);
alter table reminder add constraint fk_reminder_fleet_sys_pk foreign key (fleet_sys_pk) references fleet (sys_pk) on delete restrict on update restrict;
create index ix_reminder_log_fleet_sys_pk on reminder_log (fleet_sys_pk);
alter table reminder_log add constraint fk_reminder_log_fleet_sys_pk foreign key (fleet_sys_pk) references fleet (sys_pk) on delete restrict on update restrict;

View File

@ -0,0 +1,2 @@
-- apply alter tables
alter table reminder_log add column if not exists documents varchar[];

View File

@ -0,0 +1,6 @@
-- apply alter tables
alter table reminder_log add column if not exists reminder_sys_pk bigint;
-- foreign keys and indices
create index ix_reminder_log_reminder_sys_pk on reminder_log (reminder_sys_pk);
alter table reminder_log add constraint fk_reminder_log_reminder_sys_pk foreign key (reminder_sys_pk) references reminder (sys_pk) on delete restrict on update restrict;

View File

@ -0,0 +1,2 @@
-- apply alter tables
alter table fleet add column if not exists reg_date date;

View File

@ -0,0 +1,23 @@
-- apply changes
create table plant (
sys_pk bigint generated by default as identity not null,
deleted_on timestamp,
current_approval_level integer default 0 not null,
required_approval_levels integer default 0 not null,
deleted boolean default false not null,
version integer default 1 not null,
created_at timestamp default 'now()' not null,
modified_at timestamp default 'now()' not null,
deleted_by varchar(255),
approval_status varchar(8) default 'APPROVED' not null,
tags varchar[] default '{}' not null,
comments jsonb default '[]' not null,
plant_id varchar(255) not null,
plant_name varchar(255) not null,
created_by varchar(255) not null,
modified_by varchar(255) not null,
constraint ck_plant_approval_status check ( approval_status in ('PENDING','APPROVED','REJECTED')),
constraint plantid_idx unique (plant_id),
constraint pk_plant primary key (sys_pk)
);

View File

@ -0,0 +1,2 @@
-- apply alter tables
alter table product alter column id type varchar(255) using id::varchar(255);

View File

@ -0,0 +1,3 @@
-- apply alter tables
alter table incoming_inventory add column if not exists loading varchar(255);
alter table incoming_inventory add column if not exists unloading varchar(255);

View File

@ -0,0 +1,7 @@
-- drop dependencies
alter table product drop constraint if exists ck_product_uom;
-- apply alter tables
alter table product alter column uom type varchar(6);
alter table product add column if not exists gst_pct float;
-- apply post alter
alter table product add constraint ck_product_uom check ( uom in ('KG','CFT','TON','LTR','NUMBER','MTR','BOX','CUM','PACKET','ALL','NOS'));

View File

@ -0,0 +1,2 @@
-- apply alter tables
alter table vendor add column if not exists address_list jsonb;

View File

@ -0,0 +1,2 @@
-- apply alter tables
alter table incoming_inventory add column if not exists unloading_plant_id varchar(255);

View File

@ -0,0 +1,2 @@
-- apply alter tables
alter table plant add column if not exists prefixes jsonb;

View File

@ -0,0 +1,43 @@
-- drop dependencies
alter table document drop constraint if exists ck_document_type_of_doc;
alter table product drop constraint if exists ck_product_uom;
-- apply changes
create table req_for_quote (
sys_pk bigint generated by default as identity not null,
deleted_on timestamp,
current_approval_level integer default 0 not null,
required_approval_levels integer default 0 not null,
potential_vendors bigint[],
open_till date,
deleted boolean default false not null,
version integer default 1 not null,
created_at timestamp default 'now()' not null,
modified_at timestamp default 'now()' not null,
deleted_by varchar(255),
approval_status varchar(8) default 'APPROVED' not null,
tags varchar[] default '{}' not null,
comments jsonb default '[]' not null,
tenant_id varchar(255) not null,
status varchar(9),
products jsonb,
req_for_quote_num varchar(255),
created_by varchar(255) not null,
modified_by varchar(255) not null,
constraint ck_req_for_quote_approval_status check ( approval_status in ('PENDING','APPROVED','REJECTED')),
constraint ck_req_for_quote_status check ( status in ('DELIVERED','PO','QUOTE','CANCELLED')),
constraint pk_req_for_quote primary key (sys_pk)
);
-- apply alter tables
alter table document alter column type_of_doc type varchar(7) using type_of_doc::varchar(7);
alter table document alter column type_of_doc drop not null;
alter table document add column if not exists ref_id varchar(255);
alter table document add column if not exists doc_date date;
alter table product add column if not exists id bigint;
alter table purchase_order alter column reference_quotation drop not null;
alter table purchase_order alter column total_amount type float using total_amount::float;
alter table quotation alter column total_amount type float using total_amount::float;
alter table quotation add column if not exists req_for_quote_num varchar(255);
-- apply post alter
alter table document add constraint ck_document_type_of_doc check ( type_of_doc in ('PO','QUOTE','INVOICE','ALL'));
alter table product add constraint ck_product_uom check ( uom in ('NOS','LTR','MTR','ALL'));

View File

@ -0,0 +1,28 @@
-- apply changes
create table auth_token_cache (
sys_pk bigint generated by default as identity not null,
deleted_on timestamp,
current_approval_level integer default 0 not null,
required_approval_levels integer default 0 not null,
auth_token text not null,
issued_at timestamp not null,
expires_at timestamp not null,
refresh_expires_at timestamp not null,
refresh_token text not null,
expired boolean default false not null,
logged_out boolean default false not null,
deleted boolean default false not null,
version integer default 1 not null,
created_at timestamp default 'now()' not null,
modified_at timestamp default 'now()' not null,
deleted_by varchar(255),
approval_status varchar(8) default 'APPROVED' not null,
tags varchar[] default '{}' not null,
comments jsonb default '[]' not null,
user_id varchar(255) not null,
created_by varchar(255) not null,
modified_by varchar(255) not null,
constraint ck_auth_token_cache_approval_status check ( approval_status in ('PENDING','APPROVED','REJECTED')),
constraint pk_auth_token_cache primary key (sys_pk)
);

View File

@ -0,0 +1,3 @@
-- apply alter tables
alter table auth_token_cache add column if not exists refresh_history jsonb;
alter table product add column if not exists id varchar(255);

View File

@ -0,0 +1,4 @@
-- apply alter tables
alter table outgoing_inventory add column if not exists unloading_plant_id varchar(255);
alter table outgoing_inventory add column if not exists unloading varchar(255);
alter table outgoing_inventory add column if not exists job_card varchar(255);

View File

@ -0,0 +1,5 @@
-- apply alter tables
alter table purchase_order add column if not exists all_loading_site boolean default true not null;
alter table purchase_order add column if not exists all_unloading_site boolean default true not null;
alter table purchase_order add column if not exists unloading_plant_id varchar(255);
alter table purchase_order add column if not exists loading_site_id uuid;

View File

@ -0,0 +1,7 @@
-- drop dependencies
alter table product drop constraint if exists ck_product_uom;
-- apply alter tables
alter table purchase_order alter column all_loading_site set default false;
alter table purchase_order alter column all_unloading_site set default false;
-- apply post alter
alter table product add constraint ck_product_uom check ( uom in ('KG','CFT','TON','LTR','NUMBER','MTR','BOX','CUM','PACKET','ALL','NOS','LITER'));

View File

@ -0,0 +1,4 @@
-- drop dependencies
alter table outgoing_inventory drop constraint if exists ck_outgoing_inventory_out_mode;
-- apply post alter
alter table outgoing_inventory add constraint ck_outgoing_inventory_out_mode check ( out_mode in ('PERSON','VEHICLE','ALL','JOB','OFFICE'));

View File

@ -0,0 +1,2 @@
-- apply alter tables
alter table plant add column if not exists plant_original_name varchar(255);

View File

@ -0,0 +1,2 @@
-- apply alter tables
alter table plant add column if not exists print jsonb;

View File

@ -0,0 +1,2 @@
-- apply alter tables
alter table plant add column if not exists limits jsonb;

View File

@ -0,0 +1,8 @@
-- apply alter tables
alter table purchase_order alter column tnc drop not null;
alter table purchase_order alter column documents type varchar[] using documents::varchar[];
alter table purchase_order alter column documents drop not null;
alter table quotation alter column tnc drop not null;
alter table quotation alter column documents type varchar[] using documents::varchar[];
alter table quotation alter column documents drop not null;
alter table quotation add column if not exists vendor_quote_num varchar(255);

View File

@ -0,0 +1,2 @@
-- apply alter tables
alter table document add column if not exists ref_id_of_doc bigint;

View File

@ -0,0 +1,64 @@
-- apply changes
create table incoming_inventory (
sys_pk bigint generated by default as identity not null,
deleted_on timestamp,
current_approval_level integer default 0 not null,
required_approval_levels integer default 0 not null,
date date,
vendor_bill_amount float not null,
vendor_sys_pk bigint,
deleted boolean default false not null,
version integer default 1 not null,
created_at timestamp default 'now()' not null,
modified_at timestamp default 'now()' not null,
deleted_by varchar(255),
approval_status varchar(8) default 'APPROVED' not null,
tags varchar[] default '{}' not null,
comments jsonb default '[]' not null,
tenant_id varchar(255) not null,
mdn varchar(255) not null,
vendor_bil_num varchar(255) not null,
vehicle varchar(255) not null,
products jsonb,
created_by varchar(255) not null,
modified_by varchar(255) not null,
constraint ck_incoming_inventory_approval_status check ( approval_status in ('PENDING','APPROVED','REJECTED')),
constraint pk_incoming_inventory primary key (sys_pk)
);
create table outgoing_inventory (
sys_pk bigint generated by default as identity not null,
deleted_on timestamp,
current_approval_level integer default 0 not null,
required_approval_levels integer default 0 not null,
date date,
deleted boolean default false not null,
version integer default 1 not null,
created_at timestamp default 'now()' not null,
modified_at timestamp default 'now()' not null,
deleted_by varchar(255),
approval_status varchar(8) default 'APPROVED' not null,
tags varchar[] default '{}' not null,
comments jsonb default '[]' not null,
tenant_id varchar(255) not null,
mrn varchar(255) not null,
purpose varchar(255),
out_mode varchar(7),
person varchar(255),
vehicle varchar(255),
created_by varchar(255) not null,
modified_by varchar(255) not null,
constraint ck_outgoing_inventory_approval_status check ( approval_status in ('PENDING','APPROVED','REJECTED')),
constraint ck_outgoing_inventory_out_mode check ( out_mode in ('PERSON','VEHICLE','ALL')),
constraint pk_outgoing_inventory primary key (sys_pk)
);
-- apply alter tables
alter table product add column if not exists type varchar(12);
alter table quotation add column if not exists taxes_included boolean;
-- apply post alter
alter table product add constraint ck_product_type check ( type in ('RAW_MATERIAL'));
-- foreign keys and indices
create index ix_incoming_inventory_vendor_sys_pk on incoming_inventory (vendor_sys_pk);
alter table incoming_inventory add constraint fk_incoming_inventory_vendor_sys_pk foreign key (vendor_sys_pk) references vendor (sys_pk) on delete restrict on update restrict;

View File

@ -0,0 +1,2 @@
-- apply alter tables
alter table incoming_inventory add column if not exists vendor_bill_num varchar(255);

View File

@ -0,0 +1,4 @@
-- apply alter tables
alter table incoming_inventory add column if not exists mrn varchar(255);
alter table outgoing_inventory add column if not exists mdn varchar(255);
alter table outgoing_inventory add column if not exists products jsonb;

View File

@ -0,0 +1,2 @@
-- apply alter tables
alter table document drop column ref_id;

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<migration xmlns="http://ebean-orm.github.io/xml/ns/dbmigration">
<changeSet type="apply" dropsFor="1.7">
<dropColumn columnName="vendor_bil_num" tableName="incoming_inventory"/>
</changeSet>
</migration>

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<migration xmlns="http://ebean-orm.github.io/xml/ns/dbmigration">
<changeSet type="apply" dropsFor="1.8">
<dropColumn columnName="mdn" tableName="incoming_inventory"/>
<dropColumn columnName="mrn" tableName="outgoing_inventory"/>
</changeSet>
</migration>

View File

@ -0,0 +1,55 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<migration xmlns="http://ebean-orm.github.io/xml/ns/dbmigration">
<changeSet type="apply">
<createTable name="invoice" pkName="pk_invoice">
<column name="sys_pk" type="bigint" primaryKey="true"/>
<column name="deleted_on" type="localdatetime"/>
<column name="deleted_by" type="varchar"/>
<column name="current_approval_level" type="integer" defaultValue="0" notnull="true"/>
<column name="required_approval_levels" type="integer" defaultValue="0" notnull="true"/>
<column name="approval_status" type="varchar(8)" defaultValue="'APPROVED'" notnull="true" checkConstraint="check ( approval_status in ('PENDING','APPROVED','REJECTED'))" checkConstraintName="ck_invoice_approval_status"/>
<column name="tags" type="varchar[]" defaultValue="'{}'" notnull="true"/>
<column name="comments" type="jsonb" defaultValue="'[]'" notnull="true"/>
<column name="tenant_id" type="varchar" notnull="true"/>
<column name="number" type="varchar" notnull="true"/>
<column name="date" type="date"/>
<column name="total_amount" type="double" notnull="true"/>
<column name="po_num" type="varchar"/>
<column name="products" type="jsonb"/>
<column name="vendor_sys_pk" type="bigint" references="vendor.sys_pk" foreignKeyName="fk_invoice_vendor_sys_pk" foreignKeyIndex="ix_invoice_vendor_sys_pk"/>
<column name="status" type="varchar(9)" checkConstraint="check ( status in ('PAID_FULL','PAID_SOME','PAID_NONE','ALL'))" checkConstraintName="ck_invoice_status"/>
<column name="deleted" type="boolean" defaultValue="false" notnull="true"/>
<column name="version" type="integer" defaultValue="1" notnull="true"/>
<column name="created_at" type="localdatetime" defaultValue="'now()'" notnull="true"/>
<column name="modified_at" type="localdatetime" defaultValue="'now()'" notnull="true"/>
<column name="created_by" type="varchar" notnull="true"/>
<column name="modified_by" type="varchar" notnull="true"/>
</createTable>
<createTable name="payment" pkName="pk_payment">
<column name="sys_pk" type="bigint" primaryKey="true"/>
<column name="deleted_on" type="localdatetime"/>
<column name="deleted_by" type="varchar"/>
<column name="current_approval_level" type="integer" defaultValue="0" notnull="true"/>
<column name="required_approval_levels" type="integer" defaultValue="0" notnull="true"/>
<column name="approval_status" type="varchar(8)" defaultValue="'APPROVED'" notnull="true" checkConstraint="check ( approval_status in ('PENDING','APPROVED','REJECTED'))" checkConstraintName="ck_payment_approval_status"/>
<column name="tags" type="varchar[]" defaultValue="'{}'" notnull="true"/>
<column name="comments" type="jsonb" defaultValue="'[]'" notnull="true"/>
<column name="tenant_id" type="varchar" notnull="true"/>
<column name="ref_number" type="varchar" notnull="true"/>
<column name="amount" type="double" notnull="true"/>
<column name="date" type="date"/>
<column name="remark" type="varchar"/>
<column name="vendor_sys_pk" type="bigint" references="vendor.sys_pk" foreignKeyName="fk_payment_vendor_sys_pk" foreignKeyIndex="ix_payment_vendor_sys_pk"/>
<column name="deleted" type="boolean" defaultValue="false" notnull="true"/>
<column name="version" type="integer" defaultValue="1" notnull="true"/>
<column name="created_at" type="localdatetime" defaultValue="'now()'" notnull="true"/>
<column name="modified_at" type="localdatetime" defaultValue="'now()'" notnull="true"/>
<column name="created_by" type="varchar" notnull="true"/>
<column name="modified_by" type="varchar" notnull="true"/>
</createTable>
<addColumn tableName="vendor">
<column name="outstanding" type="double"/>
<column name="as_on_which_date" type="date"/>
</addColumn>
</changeSet>
</migration>

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<migration xmlns="http://ebean-orm.github.io/xml/ns/dbmigration">
<changeSet type="apply">
<addColumn tableName="payment">
<column name="excess_amount" type="double"/>
<column name="invoices_affected" type="jsonb"/>
</addColumn>
</changeSet>
</migration>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<migration xmlns="http://ebean-orm.github.io/xml/ns/dbmigration">
<changeSet type="apply">
<addColumn tableName="payment">
<column name="amount_deducted" type="double"/>
</addColumn>
</changeSet>
</migration>

View File

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<migration xmlns="http://ebean-orm.github.io/xml/ns/dbmigration">
<changeSet type="apply">
<createTable name="fleet" pkName="pk_fleet">
<column name="sys_pk" type="bigint" primaryKey="true"/>
<column name="deleted_on" type="localdatetime"/>
<column name="deleted_by" type="varchar"/>
<column name="current_approval_level" type="integer" defaultValue="0" notnull="true"/>
<column name="required_approval_levels" type="integer" defaultValue="0" notnull="true"/>
<column name="approval_status" type="varchar(8)" defaultValue="'APPROVED'" notnull="true" checkConstraint="check ( approval_status in ('PENDING','APPROVED','REJECTED'))" checkConstraintName="ck_fleet_approval_status"/>
<column name="tags" type="varchar[]" defaultValue="'{}'" notnull="true"/>
<column name="comments" type="jsonb" defaultValue="'[]'" notnull="true"/>
<column name="tenant_id" type="varchar" notnull="true"/>
<column name="name" type="varchar" notnull="true"/>
<column name="type" type="varchar" notnull="true"/>
<column name="reg_number" type="varchar" notnull="true"/>
<column name="model" type="varchar" notnull="true"/>
<column name="make" type="varchar" notnull="true"/>
<column name="driver" type="varchar" notnull="true"/>
<column name="driver_contact" type="varchar" notnull="true"/>
<column name="mileage" type="double" notnull="true"/>
<column name="cost" type="double" notnull="true"/>
<column name="insurance_renewal_date" type="date"/>
<column name="pollution_renewal_date" type="date"/>
<column name="fitness_renewal_date" type="date"/>
<column name="deleted" type="boolean" defaultValue="false" notnull="true"/>
<column name="version" type="integer" defaultValue="1" notnull="true"/>
<column name="created_at" type="localdatetime" defaultValue="'now()'" notnull="true"/>
<column name="modified_at" type="localdatetime" defaultValue="'now()'" notnull="true"/>
<column name="created_by" type="varchar" notnull="true"/>
<column name="modified_by" type="varchar" notnull="true"/>
</createTable>
</changeSet>
</migration>

View File

@ -0,0 +1,63 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<migration xmlns="http://ebean-orm.github.io/xml/ns/dbmigration">
<changeSet type="apply">
<addColumn tableName="fleet">
<column name="renewals" type="jsonb"/>
</addColumn>
<createTable name="fleet_type" pkName="pk_fleet_type">
<column name="sys_pk" type="bigint" primaryKey="true"/>
<column name="deleted_on" type="localdatetime"/>
<column name="deleted_by" type="varchar"/>
<column name="current_approval_level" type="integer" defaultValue="0" notnull="true"/>
<column name="required_approval_levels" type="integer" defaultValue="0" notnull="true"/>
<column name="approval_status" type="varchar(8)" defaultValue="'APPROVED'" notnull="true" checkConstraint="check ( approval_status in ('PENDING','APPROVED','REJECTED'))" checkConstraintName="ck_fleet_type_approval_status"/>
<column name="tags" type="varchar[]" defaultValue="'{}'" notnull="true"/>
<column name="comments" type="jsonb" defaultValue="'[]'" notnull="true"/>
<column name="tenant_id" type="varchar" notnull="true"/>
<column name="name" type="varchar" notnull="true"/>
<column name="person_incharge" type="varchar" notnull="true"/>
<column name="deleted" type="boolean" defaultValue="false" notnull="true"/>
<column name="version" type="integer" defaultValue="1" notnull="true"/>
<column name="created_at" type="localdatetime" defaultValue="'now()'" notnull="true"/>
<column name="modified_at" type="localdatetime" defaultValue="'now()'" notnull="true"/>
<column name="created_by" type="varchar" notnull="true"/>
<column name="modified_by" type="varchar" notnull="true"/>
</createTable>
<createTable name="renewal" pkName="pk_renewal">
<column name="sys_pk" type="bigint" primaryKey="true"/>
<column name="deleted_on" type="localdatetime"/>
<column name="deleted_by" type="varchar"/>
<column name="current_approval_level" type="integer" defaultValue="0" notnull="true"/>
<column name="required_approval_levels" type="integer" defaultValue="0" notnull="true"/>
<column name="approval_status" type="varchar(8)" defaultValue="'APPROVED'" notnull="true" checkConstraint="check ( approval_status in ('PENDING','APPROVED','REJECTED'))" checkConstraintName="ck_renewal_approval_status"/>
<column name="tags" type="varchar[]" defaultValue="'{}'" notnull="true"/>
<column name="comments" type="jsonb" defaultValue="'[]'" notnull="true"/>
<column name="tenant_id" type="varchar" notnull="true"/>
<column name="name" type="varchar" notnull="true"/>
<column name="deleted" type="boolean" defaultValue="false" notnull="true"/>
<column name="version" type="integer" defaultValue="1" notnull="true"/>
<column name="created_at" type="localdatetime" defaultValue="'now()'" notnull="true"/>
<column name="modified_at" type="localdatetime" defaultValue="'now()'" notnull="true"/>
<column name="created_by" type="varchar" notnull="true"/>
<column name="modified_by" type="varchar" notnull="true"/>
</createTable>
<createTable name="vehicle" pkName="pk_vehicle">
<column name="sys_pk" type="bigint" primaryKey="true"/>
<column name="deleted_on" type="localdatetime"/>
<column name="deleted_by" type="varchar"/>
<column name="current_approval_level" type="integer" defaultValue="0" notnull="true"/>
<column name="required_approval_levels" type="integer" defaultValue="0" notnull="true"/>
<column name="approval_status" type="varchar(8)" defaultValue="'APPROVED'" notnull="true" checkConstraint="check ( approval_status in ('PENDING','APPROVED','REJECTED'))" checkConstraintName="ck_vehicle_approval_status"/>
<column name="tags" type="varchar[]" defaultValue="'{}'" notnull="true"/>
<column name="comments" type="jsonb" defaultValue="'[]'" notnull="true"/>
<column name="tenant_id" type="varchar" notnull="true"/>
<column name="name" type="varchar" notnull="true"/>
<column name="deleted" type="boolean" defaultValue="false" notnull="true"/>
<column name="version" type="integer" defaultValue="1" notnull="true"/>
<column name="created_at" type="localdatetime" defaultValue="'now()'" notnull="true"/>
<column name="modified_at" type="localdatetime" defaultValue="'now()'" notnull="true"/>
<column name="created_by" type="varchar" notnull="true"/>
<column name="modified_by" type="varchar" notnull="true"/>
</createTable>
</changeSet>
</migration>

View File

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<migration xmlns="http://ebean-orm.github.io/xml/ns/dbmigration">
<changeSet type="apply">
<createTable name="fleet_renewal" pkName="pk_fleet_renewal">
<column name="sys_pk" type="bigint" primaryKey="true"/>
<column name="deleted_on" type="localdatetime"/>
<column name="deleted_by" type="varchar"/>
<column name="current_approval_level" type="integer" defaultValue="0" notnull="true"/>
<column name="required_approval_levels" type="integer" defaultValue="0" notnull="true"/>
<column name="approval_status" type="varchar(8)" defaultValue="'APPROVED'" notnull="true" checkConstraint="check ( approval_status in ('PENDING','APPROVED','REJECTED'))" checkConstraintName="ck_fleet_renewal_approval_status"/>
<column name="tags" type="varchar[]" defaultValue="'{}'" notnull="true"/>
<column name="comments" type="jsonb" defaultValue="'[]'" notnull="true"/>
<column name="tenant_id" type="varchar" notnull="true"/>
<column name="name" type="varchar" notnull="true"/>
<column name="date" type="date"/>
<column name="last_date" type="date"/>
<column name="frequency" type="integer" notnull="true"/>
<column name="fleet_sys_pk" type="bigint" references="fleet.sys_pk" foreignKeyName="fk_fleet_renewal_fleet_sys_pk" foreignKeyIndex="ix_fleet_renewal_fleet_sys_pk"/>
<column name="deleted" type="boolean" defaultValue="false" notnull="true"/>
<column name="version" type="integer" defaultValue="1" notnull="true"/>
<column name="created_at" type="localdatetime" defaultValue="'now()'" notnull="true"/>
<column name="modified_at" type="localdatetime" defaultValue="'now()'" notnull="true"/>
<column name="created_by" type="varchar" notnull="true"/>
<column name="modified_by" type="varchar" notnull="true"/>
</createTable>
</changeSet>
</migration>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<migration xmlns="http://ebean-orm.github.io/xml/ns/dbmigration">
<changeSet type="apply">
<addColumn tableName="fleet_renewal">
<column name="documents" type="varchar[]"/>
</addColumn>
</changeSet>
</migration>

View File

@ -0,0 +1,55 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<migration xmlns="http://ebean-orm.github.io/xml/ns/dbmigration">
<changeSet type="apply">
<createTable name="reminder" pkName="pk_reminder">
<column name="sys_pk" type="bigint" primaryKey="true"/>
<column name="deleted_on" type="localdatetime"/>
<column name="deleted_by" type="varchar"/>
<column name="current_approval_level" type="integer" defaultValue="0" notnull="true"/>
<column name="required_approval_levels" type="integer" defaultValue="0" notnull="true"/>
<column name="approval_status" type="varchar(8)" defaultValue="'APPROVED'" notnull="true" checkConstraint="check ( approval_status in ('PENDING','APPROVED','REJECTED'))" checkConstraintName="ck_reminder_approval_status"/>
<column name="tags" type="varchar[]" defaultValue="'{}'" notnull="true"/>
<column name="comments" type="jsonb" defaultValue="'[]'" notnull="true"/>
<column name="tenant_id" type="varchar" notnull="true"/>
<column name="type" type="varchar" notnull="true"/>
<column name="next_renewal_date" type="date"/>
<column name="last_renewal_date" type="date"/>
<column name="amount" type="double" notnull="true"/>
<column name="frequency" type="integer" notnull="true"/>
<column name="documents" type="varchar[]"/>
<column name="fleet_sys_pk" type="bigint" references="fleet.sys_pk" foreignKeyName="fk_reminder_fleet_sys_pk" foreignKeyIndex="ix_reminder_fleet_sys_pk"/>
<column name="deleted" type="boolean" defaultValue="false" notnull="true"/>
<column name="version" type="integer" defaultValue="1" notnull="true"/>
<column name="created_at" type="localdatetime" defaultValue="'now()'" notnull="true"/>
<column name="modified_at" type="localdatetime" defaultValue="'now()'" notnull="true"/>
<column name="created_by" type="varchar" notnull="true"/>
<column name="modified_by" type="varchar" notnull="true"/>
</createTable>
<createTable name="reminder_log" pkName="pk_reminder_log">
<column name="sys_pk" type="bigint" primaryKey="true"/>
<column name="deleted_on" type="localdatetime"/>
<column name="deleted_by" type="varchar"/>
<column name="current_approval_level" type="integer" defaultValue="0" notnull="true"/>
<column name="required_approval_levels" type="integer" defaultValue="0" notnull="true"/>
<column name="approval_status" type="varchar(8)" defaultValue="'APPROVED'" notnull="true" checkConstraint="check ( approval_status in ('PENDING','APPROVED','REJECTED'))" checkConstraintName="ck_reminder_log_approval_status"/>
<column name="tags" type="varchar[]" defaultValue="'{}'" notnull="true"/>
<column name="comments" type="jsonb" defaultValue="'[]'" notnull="true"/>
<column name="tenant_id" type="varchar" notnull="true"/>
<column name="reminder_type" type="varchar" notnull="true"/>
<column name="reminder_date" type="date"/>
<column name="fleet_sys_pk" type="bigint" references="fleet.sys_pk" foreignKeyName="fk_reminder_log_fleet_sys_pk" foreignKeyIndex="ix_reminder_log_fleet_sys_pk"/>
<column name="acted_upon" type="boolean" defaultValue="false" notnull="true"/>
<column name="amount" type="double" notnull="true"/>
<column name="deleted" type="boolean" defaultValue="false" notnull="true"/>
<column name="version" type="integer" defaultValue="1" notnull="true"/>
<column name="created_at" type="localdatetime" defaultValue="'now()'" notnull="true"/>
<column name="modified_at" type="localdatetime" defaultValue="'now()'" notnull="true"/>
<column name="created_by" type="varchar" notnull="true"/>
<column name="modified_by" type="varchar" notnull="true"/>
</createTable>
<alterForeignKey name="fk_fleet_renewal_fleet_sys_pk" columnNames="DROP FOREIGN KEY" indexName="ix_fleet_renewal_fleet_sys_pk" tableName="fleet_renewal"/>
</changeSet>
<changeSet type="pendingDrops">
<dropTable name="fleet_renewal" sequenceCol="sys_pk"/>
</changeSet>
</migration>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<migration xmlns="http://ebean-orm.github.io/xml/ns/dbmigration">
<changeSet type="apply">
<addColumn tableName="reminder_log">
<column name="documents" type="varchar[]"/>
</addColumn>
</changeSet>
</migration>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<migration xmlns="http://ebean-orm.github.io/xml/ns/dbmigration">
<changeSet type="apply">
<addColumn tableName="reminder_log">
<column name="reminder_sys_pk" type="bigint" references="reminder.sys_pk" foreignKeyName="fk_reminder_log_reminder_sys_pk" foreignKeyIndex="ix_reminder_log_reminder_sys_pk"/>
</addColumn>
</changeSet>
</migration>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<migration xmlns="http://ebean-orm.github.io/xml/ns/dbmigration">
<changeSet type="apply">
<addColumn tableName="fleet">
<column name="reg_date" type="date"/>
</addColumn>
</changeSet>
</migration>

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<migration xmlns="http://ebean-orm.github.io/xml/ns/dbmigration">
<changeSet type="apply">
<createTable name="plant" pkName="pk_plant">
<column name="sys_pk" type="bigint" primaryKey="true"/>
<column name="deleted_on" type="localdatetime"/>
<column name="deleted_by" type="varchar"/>
<column name="current_approval_level" type="integer" defaultValue="0" notnull="true"/>
<column name="required_approval_levels" type="integer" defaultValue="0" notnull="true"/>
<column name="approval_status" type="varchar(8)" defaultValue="'APPROVED'" notnull="true" checkConstraint="check ( approval_status in ('PENDING','APPROVED','REJECTED'))" checkConstraintName="ck_plant_approval_status"/>
<column name="tags" type="varchar[]" defaultValue="'{}'" notnull="true"/>
<column name="comments" type="jsonb" defaultValue="'[]'" notnull="true"/>
<column name="plant_id" type="varchar" notnull="true"/>
<column name="plant_name" type="varchar" notnull="true"/>
<column name="deleted" type="boolean" defaultValue="false" notnull="true"/>
<column name="version" type="integer" defaultValue="1" notnull="true"/>
<column name="created_at" type="localdatetime" defaultValue="'now()'" notnull="true"/>
<column name="modified_at" type="localdatetime" defaultValue="'now()'" notnull="true"/>
<column name="created_by" type="varchar" notnull="true"/>
<column name="modified_by" type="varchar" notnull="true"/>
<uniqueConstraint name="plantid_idx" columnNames="plant_id" oneToOne="false" nullableColumns=""/>
</createTable>
</changeSet>
</migration>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<migration xmlns="http://ebean-orm.github.io/xml/ns/dbmigration">
<changeSet type="apply">
<alterColumn columnName="code" tableName="product" type="varchar" currentType="bigint" currentNotnull="false"/>
</changeSet>
</migration>

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<migration xmlns="http://ebean-orm.github.io/xml/ns/dbmigration">
<changeSet type="apply">
<addColumn tableName="incoming_inventory">
<column name="loading" type="varchar"/>
<column name="unloading" type="varchar"/>
</addColumn>
</changeSet>
</migration>

Some files were not shown because too many files have changed in this diff Show More