Compare commits
234 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b667524a04 | ||
|
|
011cb1b349 | ||
|
|
9b2cdddb88 | ||
|
|
5a4c0e4f65 | ||
|
|
c71db5133d | ||
|
|
ee733434e6 | ||
|
|
c2050e7e26 | ||
|
|
5f9c756172 | ||
|
|
c0096a0dec | ||
|
|
41e40b9165 | ||
|
|
6528509288 | ||
|
|
1e566c82cd | ||
|
|
d45be60668 | ||
|
|
a18e40a63c | ||
|
|
62d5511337 | ||
|
|
ac37074666 | ||
|
|
c171847555 | ||
|
|
9467c57c8f | ||
|
|
87f591ce1b | ||
|
|
c3a950088e | ||
|
|
7e60e2c699 | ||
|
|
704e35e0b0 | ||
|
|
d616d204d0 | ||
|
|
8eae657bb3 | ||
|
|
492cee29e7 | ||
|
|
b4cd016008 | ||
|
|
e685ca5ad8 | ||
|
|
7595cbf11d | ||
|
|
786f2f212c | ||
|
|
0fa110cc51 | ||
|
|
bdfbe52244 | ||
|
|
0ca75298d8 | ||
|
|
1ffad4a8b0 | ||
|
|
cac7df66e1 | ||
|
|
76441e49e1 | ||
|
|
524385af7d | ||
|
|
cb50ca7897 | ||
|
|
a4f4f45251 | ||
|
|
acc91abc0c | ||
|
|
d6d6ced7a4 | ||
|
|
ded7393aec | ||
|
|
17928741e3 | ||
|
|
a6b825ffdf | ||
|
|
f2914868e2 | ||
|
|
561f0313f9 | ||
|
|
02a49efbea | ||
|
|
4fb9c37c0d | ||
|
|
b269d54f2c | ||
|
|
069d508528 | ||
|
|
d213c382c9 | ||
|
|
878e81dc8f | ||
|
|
deab90b326 | ||
|
|
a1f7e7b755 | ||
|
|
c8f8a86a98 | ||
|
|
1620b94a7b | ||
|
|
81f94eac6c | ||
|
|
ec7509670d | ||
|
|
8c74276b98 | ||
|
|
0acfd3c4b1 | ||
|
|
bfc4cb1ad9 | ||
|
|
ac1ad12ac4 | ||
|
|
1f42be2e54 | ||
|
|
30d344af6b | ||
|
|
3ca989fd8c | ||
|
|
ce2086a480 | ||
|
|
63f7106d2b | ||
|
|
e75d8d1874 | ||
|
|
07bf84ed69 | ||
|
|
dedc594b58 | ||
|
|
c2cc7b1e9e | ||
|
|
fe55eccbf9 | ||
|
|
88a71dd647 | ||
|
|
c3b395c99e | ||
|
|
02747f4144 | ||
|
|
33d5d9b462 | ||
|
|
b5a371d0cf | ||
|
|
05b1078e27 | ||
|
|
3b35f38ad5 | ||
|
|
1f5c1b9658 | ||
|
|
cc57eb40cf | ||
|
|
37196e65c3 | ||
|
|
b20c605c85 | ||
|
|
d8ac708536 | ||
|
|
8a32fe50f3 | ||
|
|
7897a490bd | ||
|
|
17d73fc6d7 | ||
|
|
7d1910086e | ||
|
|
817c41afb9 | ||
|
|
04315a9045 | ||
|
|
d0871ffe21 | ||
|
|
dca587d6e0 | ||
|
|
19ec4cb021 | ||
|
|
33518e5c25 | ||
|
|
346e82bdbc | ||
|
|
fd9575e5a5 | ||
|
|
af6bc0fb5a | ||
|
|
b34909b7f3 | ||
|
|
d0e289757f | ||
|
|
becc54f7b3 | ||
|
|
e3fabdac91 | ||
|
|
40621db556 | ||
|
|
c2e64a124d | ||
|
|
1fa83d124d | ||
|
|
cc734d3981 | ||
|
|
b41d69593d | ||
|
|
b7055aac84 | ||
|
|
09e505399c | ||
|
|
bd599cfea4 | ||
|
|
42f9f275c7 | ||
|
|
39b119b2e8 | ||
|
|
e8917f5736 | ||
|
|
5bc224206c | ||
|
|
190da863c2 | ||
|
|
2711b5fce5 | ||
|
|
8e9358cd6f | ||
|
|
0e31bbb789 | ||
|
|
3b635aeed3 | ||
|
|
1f896b1ede | ||
|
|
b1ca09bd4f | ||
|
|
f9ca36ec89 | ||
|
|
9d479d4b4d | ||
|
|
23b5b740cb | ||
|
|
8f9b05b974 | ||
|
|
d1132e7d45 | ||
|
|
960b0b4d09 | ||
|
|
f35ea026b8 | ||
|
|
b1fc2828c8 | ||
|
|
bc25c23982 | ||
|
|
2215c8439e | ||
|
|
f98c9e6f71 | ||
|
|
b7ee95059d | ||
|
|
28f2e8ad30 | ||
|
|
c27e206687 | ||
|
|
01fd253f1d | ||
|
|
e67bc0d953 | ||
|
|
3eab3e2fb6 | ||
|
|
f59d369529 | ||
|
|
3adf6b649b | ||
|
|
f8243aa2b3 | ||
|
|
eefac38d9c | ||
|
|
3c3f50d138 | ||
|
|
4f7e1b81ac | ||
|
|
249b74a597 | ||
|
|
806bdc4c70 | ||
|
|
a0572a0cec | ||
|
|
04cdf13e86 | ||
|
|
50349e91dc | ||
|
|
9998a8f154 | ||
|
|
9dcbcb2a43 | ||
|
|
5773f7a0f3 | ||
|
|
8614d88e71 | ||
|
|
fa5e26c52a | ||
|
|
90e1cbd390 | ||
|
|
947a3c6a88 | ||
|
|
a33662e6f0 | ||
|
|
6d1f04369e | ||
|
|
4a27726ef3 | ||
|
|
eedba97237 | ||
|
|
bcf095ed4f | ||
|
|
c3f9e268c7 | ||
|
|
c8442f0750 | ||
|
|
da5b446c30 | ||
|
|
e13b2bb969 | ||
|
|
e8969c4698 | ||
|
|
346d32a2bb | ||
|
|
6ba00a90be | ||
|
|
4293d20313 | ||
|
|
706c70c509 | ||
|
|
e872b1bf0a | ||
|
|
379fa060d8 | ||
|
|
a6258c6456 | ||
|
|
d6bf0a225b | ||
|
|
ec2b914e5e | ||
|
|
309a20148a | ||
|
|
013b56778b | ||
|
|
4fb491697c | ||
|
|
9dec4a3a61 | ||
|
|
189b4db90f | ||
|
|
c49fa1c527 | ||
|
|
53ef048f6f | ||
|
|
09da3f349b | ||
|
|
1cc7eed983 | ||
|
|
1c8f377212 | ||
|
|
e8f4f30cc4 | ||
|
|
c050947276 | ||
|
|
9bde1f820d | ||
|
|
393765640c | ||
|
|
69c2836425 | ||
|
|
ec9edf30eb | ||
|
|
6a812f9ea7 | ||
|
|
fa86254bfd | ||
|
|
58ab7afbb3 | ||
|
|
fa7d90d2a9 | ||
|
|
fbf9047974 | ||
|
|
3e9b795052 | ||
|
|
d29ffc21e5 | ||
|
|
959f7780e8 | ||
|
|
e47731702a | ||
|
|
8c4f395ff9 | ||
|
|
acbf75ff4d | ||
|
|
36e3828e2d | ||
|
|
281c9bbc2a | ||
|
|
455e79a6f7 | ||
|
|
bc73e75217 | ||
|
|
7b3f71701e | ||
|
|
383a383a9f | ||
|
|
7ee853ae15 | ||
|
|
3510d081cd | ||
|
|
471105828c | ||
|
|
1ec1f5d3a8 | ||
|
|
355de9efd0 | ||
|
|
2c14731ebd | ||
|
|
065d2d5688 | ||
|
|
879dea9fa7 | ||
|
|
3ceada30a3 | ||
|
|
d26eb81339 | ||
|
|
fcdcdbbf7f | ||
|
|
d482447dc9 | ||
|
|
647dfbfa06 | ||
|
|
f17899285f | ||
|
|
47f5641518 | ||
|
|
6e41a282ca | ||
|
|
abbd2263ee | ||
|
|
4b70c89e54 | ||
|
|
5e2c1aae4f | ||
|
|
fda06bb298 | ||
|
|
3dfd45ca0f | ||
|
|
86e748502c | ||
|
|
0f85250d40 | ||
|
|
f04bc59966 | ||
|
|
e6e24dae4b | ||
|
|
4ad32117c3 | ||
|
|
befb8009e0 | ||
|
|
41bec72390 |
@@ -59,139 +59,6 @@ jobs:
|
||||
to: "s3://imex-online-production/"
|
||||
arguments: "--exclude '*.map'"
|
||||
- jira/notify
|
||||
|
||||
rome-api-deploy:
|
||||
docker:
|
||||
- image: "cimg/base:stable"
|
||||
steps:
|
||||
- checkout
|
||||
- eb/setup
|
||||
- run:
|
||||
command: |
|
||||
eb init romeonline-productionapi -r us-east-2 -p "Node.js 18 running on 64bit Amazon Linux 2"
|
||||
eb status --verbose
|
||||
eb deploy
|
||||
eb status
|
||||
- jira/notify
|
||||
|
||||
rome-hasura-migrate:
|
||||
docker:
|
||||
- image: cimg/node:16.15.0
|
||||
parameters:
|
||||
secret:
|
||||
type: string
|
||||
default: $HASURA_PROD_SECRET
|
||||
working_directory: ~/repo/hasura
|
||||
steps:
|
||||
- checkout:
|
||||
path: ~/repo
|
||||
- run:
|
||||
name: Execute migration
|
||||
command: |
|
||||
npm install hasura-cli -g
|
||||
hasura migrate apply --endpoint https://db.romeonline.io/ --admin-secret << parameters.secret >>
|
||||
hasura metadata apply --endpoint https://db.romeonline.io/ --admin-secret << parameters.secret >>
|
||||
hasura metadata reload --endpoint https://db.romeonline.io/ --admin-secret << parameters.secret >>
|
||||
|
||||
rome-app-build:
|
||||
docker:
|
||||
- image: cimg/node:16.15.0
|
||||
resource_class: large
|
||||
working_directory: ~/repo/client
|
||||
|
||||
steps:
|
||||
- checkout:
|
||||
path: ~/repo
|
||||
- run:
|
||||
name: Install Dependencies
|
||||
command: npm i
|
||||
|
||||
- run: npm run build
|
||||
|
||||
- aws-s3/sync:
|
||||
from: build
|
||||
to: "s3://rome-online-production/"
|
||||
- jira/notify
|
||||
|
||||
test-rome-hasura-migrate:
|
||||
docker:
|
||||
- image: cimg/node:16.15.0
|
||||
parameters:
|
||||
secret:
|
||||
type: string
|
||||
default: $HASURA_ROME_TEST_SECRET
|
||||
working_directory: ~/repo/hasura
|
||||
steps:
|
||||
- checkout:
|
||||
path: ~/repo
|
||||
- run:
|
||||
name: Execute migration
|
||||
command: |
|
||||
npm install hasura-cli -g
|
||||
echo ${HASURA_TEST_SECRET}
|
||||
hasura migrate apply --endpoint https://db.test.romeonline.io/ --admin-secret << parameters.secret >>
|
||||
hasura metadata apply --endpoint https://db.test.romeonline.io/ --admin-secret << parameters.secret >>
|
||||
hasura metadata reload --endpoint https://db.test.romeonline.io/ --admin-secret << parameters.secret >>
|
||||
|
||||
test-rome-app-build:
|
||||
docker:
|
||||
- image: cimg/node:16.15.0
|
||||
resource_class: large
|
||||
working_directory: ~/repo/client
|
||||
|
||||
steps:
|
||||
- checkout:
|
||||
path: ~/repo
|
||||
- run:
|
||||
name: Install Dependencies
|
||||
command: npm i
|
||||
|
||||
- run: npm run build:test
|
||||
|
||||
- aws-s3/sync:
|
||||
from: build
|
||||
to: "s3://rome-online-test/"
|
||||
- jira/notify
|
||||
|
||||
app-beta-build:
|
||||
docker:
|
||||
- image: cimg/node:18.18.2
|
||||
resource_class: large
|
||||
working_directory: ~/repo/client
|
||||
|
||||
steps:
|
||||
- checkout:
|
||||
path: ~/repo
|
||||
- run:
|
||||
name: Install Dependencies
|
||||
command: npm i
|
||||
|
||||
- run: npm run build
|
||||
|
||||
- aws-s3/sync:
|
||||
from: build
|
||||
to: "s3://imex-online-beta/"
|
||||
- jira/notify
|
||||
|
||||
rome-app-beta-build:
|
||||
docker:
|
||||
- image: cimg/node:18.18.2
|
||||
resource_class: large
|
||||
working_directory: ~/repo/client
|
||||
|
||||
steps:
|
||||
- checkout:
|
||||
path: ~/repo
|
||||
- run:
|
||||
name: Install Dependencies
|
||||
command: npm i
|
||||
|
||||
- run: npm run build
|
||||
|
||||
- aws-s3/sync:
|
||||
from: build
|
||||
to: "s3://rome-online-production-beta/"
|
||||
- jira/notify
|
||||
|
||||
test-hasura-migrate:
|
||||
docker:
|
||||
@@ -234,49 +101,6 @@ jobs:
|
||||
arguments: "--exclude '*.map'"
|
||||
- jira/notify
|
||||
|
||||
test-app-beta-build:
|
||||
docker:
|
||||
- image: cimg/node:18.18.2
|
||||
resource_class: large
|
||||
working_directory: ~/repo/client
|
||||
|
||||
steps:
|
||||
- checkout:
|
||||
path: ~/repo
|
||||
|
||||
- run:
|
||||
name: Install Dependencies
|
||||
command: npm i
|
||||
|
||||
- run: npm run build:test
|
||||
|
||||
- aws-s3/sync:
|
||||
from: build
|
||||
to: "s3://imex-online-test-beta/"
|
||||
- jira/notify
|
||||
|
||||
rome-test-app-beta-build:
|
||||
docker:
|
||||
- image: cimg/node:18.18.2
|
||||
resource_class: large
|
||||
working_directory: ~/repo/client
|
||||
|
||||
steps:
|
||||
- checkout:
|
||||
path: ~/repo
|
||||
|
||||
- run:
|
||||
name: Install Dependencies
|
||||
command: npm i
|
||||
|
||||
- run: npm run build:test
|
||||
|
||||
- aws-s3/sync:
|
||||
from: build
|
||||
to: "s3://rome-online-test-beta/"
|
||||
- jira/notify
|
||||
|
||||
|
||||
admin-app-build:
|
||||
docker:
|
||||
- image: cimg/node:16.15.0
|
||||
@@ -310,66 +134,24 @@ jobs:
|
||||
workflows:
|
||||
deploy_and_build:
|
||||
jobs:
|
||||
- api-deploy:
|
||||
filters:
|
||||
branches:
|
||||
only: master
|
||||
- app-build:
|
||||
filters:
|
||||
branches:
|
||||
only: master
|
||||
- app-beta-build:
|
||||
filters:
|
||||
branches:
|
||||
only: master-beta
|
||||
- rome-app-beta-build:
|
||||
filters:
|
||||
branches:
|
||||
only: rome/master-beta
|
||||
- hasura-migrate:
|
||||
secret: ${HASURA_PROD_SECRET}
|
||||
filters:
|
||||
branches:
|
||||
only: master
|
||||
- rome-api-deploy:
|
||||
filters:
|
||||
branches:
|
||||
only: rome/master
|
||||
- rome-app-build:
|
||||
filters:
|
||||
branches:
|
||||
only: rome/master
|
||||
- rome-hasura-migrate:
|
||||
secret: ${HASURA_PROD_SECRET}
|
||||
filters:
|
||||
branches:
|
||||
only: rome/master
|
||||
- test-app-build:
|
||||
filters:
|
||||
branches:
|
||||
only: test
|
||||
- rome-test-app-beta-build:
|
||||
filters:
|
||||
branches:
|
||||
only: rome/test-beta
|
||||
- test-app-beta-build:
|
||||
filters:
|
||||
branches:
|
||||
only: test-beta
|
||||
- test-hasura-migrate:
|
||||
secret: ${HASURA_TEST_SECRET}
|
||||
filters:
|
||||
branches:
|
||||
only: test
|
||||
- test-rome-app-build:
|
||||
filters:
|
||||
branches:
|
||||
only: rome/test
|
||||
- test-rome-hasura-migrate:
|
||||
secret: ${HASURA_ROME_TEST_SECRET}
|
||||
filters:
|
||||
branches:
|
||||
only: rome/test
|
||||
#- admin-app-build:
|
||||
#filters:
|
||||
#branches:
|
||||
|
||||
@@ -59,7 +59,7 @@ Filters can make use of reflection to pre-fill select boxes, the following is an
|
||||
"type": "internal",
|
||||
"name": "special.job_statuses"
|
||||
}
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
in this example, a reflector with the type 'internal' (all types at the moment require this, and it is used for future functionality), with a name of `special.job_statuses`
|
||||
@@ -74,9 +74,13 @@ The following cases are available
|
||||
- `special.employees` - This will reflect the employees `bodyshop.employees`
|
||||
- `special.first_names` - This will reflect the first names `bodyshop.employees`
|
||||
- `special.last_names` - This will reflect the last names `bodyshop.employees`
|
||||
- `special.referral_sources` - This will reflect the referral sources `bodyshop.md_referral_sources
|
||||
- `special.referral_sources` - This will reflect the referral sources `bodyshop.md_referral_sources`
|
||||
- `special.class`- This will reflect the class `bodyshop.md_classes`
|
||||
-
|
||||
- `special.lost_sale_reasons` - This will reflect the lost sale reasons `bodyshop.md_lost_sale_reasons`
|
||||
- `special.alt_transports` - This will reflect the alternative transports `bodyshop.appt_alt_transport`
|
||||
- `special.payment_types` - This will reflect the payment types `bodyshop.md_payment_types`
|
||||
- `special.payment_payers` - This is a special case with a key value set of [Customer, Insurance]
|
||||
|
||||
### Path without brackets, multi level
|
||||
|
||||
`"name": "jobs.joblines.mod_lb_hrs",`
|
||||
@@ -156,6 +160,7 @@ query gendoc_hours_sold_detail_open($starttz: timestamptz!, $endtz: timestamptz!
|
||||
- Do not add the ability to filter things that are already filtered as part of the original query, this would be
|
||||
redundant and could cause issues.
|
||||
- Do not add the ability to filter on things like FK constraints, must like the above example.
|
||||
- *INHERITANCE CAVEAT* If you have a filters file on a parent report that has a child that you do not want the filters inherited from, you must place a blank filters file (valid json so {}) on the child report level. This will than fetch the child filters, which are empty and move along, versus inheriting the parent filters.
|
||||
|
||||
## Sorters
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,14 +1,13 @@
|
||||
REACT_APP_GRAPHQL_ENDPOINT=https://db.dev.bodyshop.app/v1/graphql
|
||||
REACT_APP_GRAPHQL_ENDPOINT_WS=wss://db.dev.bodyshop.app/v1/graphql
|
||||
REACT_APP_GA_CODE=231099835
|
||||
REACT_APP_FIREBASE_CONFIG={ "apiKey": "AIzaSyAuLQR9SV5LsVxjU8wh9hvFLdhcAHU6cxE", "authDomain": "rome-prod-1.firebaseapp.com", "projectId": "rome-prod-1", "storageBucket": "rome-prod-1.appspot.com", "messagingSenderId": "147786367145", "appId": "1:147786367145:web:9d4cba68071c3f29a8a9b8", "measurementId": "G-G8Z9DRHTZS"}
|
||||
REACT_APP_FIREBASE_CONFIG={"apiKey":"AIzaSyDPLT8GiDHDR1R4nI66Qi0BY1aYviDPioc","authDomain":"imex-dev.firebaseapp.com","databaseURL":"https://imex-dev.firebaseio.com","projectId":"imex-dev","storageBucket":"imex-dev.appspot.com","messagingSenderId":"759548147434","appId":"1:759548147434:web:e8239868a48ceb36700993","measurementId":"G-K5XRBVVB4S"}
|
||||
REACT_APP_CLOUDINARY_ENDPOINT_API=https://api.cloudinary.com/v1_1/io-test
|
||||
REACT_APP_CLOUDINARY_ENDPOINT=https://res.cloudinary.com/io-test
|
||||
REACT_APP_CLOUDINARY_API_KEY=957865933348715
|
||||
REACT_APP_CLOUDINARY_THUMB_TRANSFORMATIONS=c_fill,h_250,w_250
|
||||
REACT_APP_FIREBASE_PUBLIC_VAPID_KEY='BP1B7ZTYpn-KMt6nOxlld6aS8Skt3Q7ZLEqP0hAvGHxG4UojPYiXZ6kPlzZkUC5jH-EcWXomTLtmadAIxurfcHo'
|
||||
REACT_APP_FIREBASE_PUBLIC_VAPID_KEY='BG3tzU7L2BXlGZ_3VLK4PNaRceoEXEnmHfxcVbRMF5o5g05ejslhVPki9kBM9cBBT-08Ad9kN3HSpS6JmrWD6h4'
|
||||
REACT_APP_STRIPE_PUBLIC_KEY=pk_test_51GqB4TJl3nQjrZ0wCQWAxAhlNF8jKe0tipIa6ExBaxwJGitwvFsIZUEua4dUzaMIAuXp4qwYHXx7lgjyQSwP0Pe900vzm38C7g
|
||||
REACT_APP_AXIOS_BASE_API_URL=http://localhost:4000
|
||||
REACT_APP_REPORTS_SERVER_URL=https://reports3.test.imex.online
|
||||
REACT_APP_SPLIT_API=ts615lqgnmk84thn72uk18uu5pgce6e0l4rc
|
||||
REACT_APP_COUNTRY=USA
|
||||
REACT_APP_SPLIT_API=ts615lqgnmk84thn72uk18uu5pgce6e0l4rc
|
||||
@@ -1,13 +1,14 @@
|
||||
REACT_APP_GRAPHQL_ENDPOINT=https://db.romeonline.io/v1/graphql
|
||||
REACT_APP_GRAPHQL_ENDPOINT_WS=wss://db.romeonline.io/v1/graphql
|
||||
GENERATE_SOURCEMAP=true
|
||||
REACT_APP_GRAPHQL_ENDPOINT=https://db.imex.online/v1/graphql
|
||||
REACT_APP_GRAPHQL_ENDPOINT_WS=wss://db.imex.online/v1/graphql
|
||||
REACT_APP_GA_CODE=231103507
|
||||
REACT_APP_FIREBASE_CONFIG={ "apiKey": "AIzaSyAuLQR9SV5LsVxjU8wh9hvFLdhcAHU6cxE", "authDomain": "rome-prod-1.firebaseapp.com", "projectId": "rome-prod-1", "storageBucket": "rome-prod-1.appspot.com", "messagingSenderId": "147786367145", "appId": "1:147786367145:web:9d4cba68071c3f29a8a9b8", "measurementId": "G-G8Z9DRHTZS"}
|
||||
REACT_APP_FIREBASE_CONFIG={"apiKey":"AIzaSyDSezy-jGJreo7ulgpLdlpOwAOrgcaEkhU","authDomain":"imex-prod.firebaseapp.com","databaseURL":"https://imex-prod.firebaseio.com","projectId":"imex-prod","storageBucket":"imex-prod.appspot.com","messagingSenderId":"253497221485","appId":"1:253497221485:web:3c81c483b94db84b227a64","measurementId":"G-NTWBKG2L0M"}
|
||||
REACT_APP_CLOUDINARY_ENDPOINT_API=https://api.cloudinary.com/v1_1/bodyshop
|
||||
REACT_APP_CLOUDINARY_ENDPOINT=https://res.cloudinary.com/bodyshop
|
||||
REACT_APP_CLOUDINARY_API_KEY=473322739956866
|
||||
REACT_APP_CLOUDINARY_THUMB_TRANSFORMATIONS=c_fill,h_250,w_250
|
||||
REACT_APP_FIREBASE_PUBLIC_VAPID_KEY='BP1B7ZTYpn-KMt6nOxlld6aS8Skt3Q7ZLEqP0hAvGHxG4UojPYiXZ6kPlzZkUC5jH-EcWXomTLtmadAIxurfcHo'
|
||||
REACT_APP_FIREBASE_PUBLIC_VAPID_KEY='BMgZT1NZztW2DsJl8Mg2L04hgY9FzAg6b8fbzgNAfww2VDzH3VE63Ot9EaP_U7KWS2JT-7HPHaw0T_Tw_5vkZc8'
|
||||
REACT_APP_STRIPE_PUBLIC_KEY=pk_test_51GqB4TJl3nQjrZ0wCQWAxAhlNF8jKe0tipIa6ExBaxwJGitwvFsIZUEua4dUzaMIAuXp4qwYHXx7lgjyQSwP0Pe900vzm38C7g
|
||||
REACT_APP_AXIOS_BASE_API_URL=https://api.romeonline.io/
|
||||
REACT_APP_REPORTS_SERVER_URL=https://reports.romeonline.io
|
||||
REACT_APP_AXIOS_BASE_API_URL=https://api.imex.online/
|
||||
REACT_APP_REPORTS_SERVER_URL=https://reports.imex.online
|
||||
REACT_APP_SPLIT_API=et9pjkik6bn67he5evpmpr1agoo7gactphgk
|
||||
@@ -1,14 +1,14 @@
|
||||
REACT_APP_GRAPHQL_ENDPOINT=https://db.test.romeonline.io/v1/graphql
|
||||
REACT_APP_GRAPHQL_ENDPOINT_WS=wss://db.test.romeonline.io/v1/graphql
|
||||
REACT_APP_GA_CODE=231103507
|
||||
REACT_APP_FIREBASE_CONFIG={ "apiKey": "AIzaSyAuLQR9SV5LsVxjU8wh9hvFLdhcAHU6cxE", "authDomain": "rome-prod-1.firebaseapp.com", "projectId": "rome-prod-1", "storageBucket": "rome-prod-1.appspot.com", "messagingSenderId": "147786367145", "appId": "1:147786367145:web:9d4cba68071c3f29a8a9b8", "measurementId": "G-G8Z9DRHTZS"}
|
||||
REACT_APP_GRAPHQL_ENDPOINT=https://db.test.bodyshop.app/v1/graphql
|
||||
REACT_APP_GRAPHQL_ENDPOINT_WS=wss://db.test.bodyshop.app/v1/graphql
|
||||
REACT_APP_GA_CODE=231099835
|
||||
REACT_APP_FIREBASE_CONFIG={ "apiKey":"AIzaSyBw7_GTy7GtQyfkIRPVrWHEGKfcqeyXw0c", "authDomain":"imex-test.firebaseapp.com", "projectId":"imex-test", "storageBucket":"imex-test.appspot.com", "messagingSenderId":"991923618608", "appId":"1:991923618608:web:633437569cdad78299bef5", "measurementId":"G-TW0XLZEH18"}
|
||||
REACT_APP_CLOUDINARY_ENDPOINT_API=https://api.cloudinary.com/v1_1/bodyshop
|
||||
REACT_APP_CLOUDINARY_ENDPOINT=https://res.cloudinary.com/bodyshop
|
||||
REACT_APP_CLOUDINARY_API_KEY=473322739956866
|
||||
REACT_APP_CLOUDINARY_THUMB_TRANSFORMATIONS=c_fill,h_250,w_250
|
||||
REACT_APP_FIREBASE_PUBLIC_VAPID_KEY='BP1B7ZTYpn-KMt6nOxlld6aS8Skt3Q7ZLEqP0hAvGHxG4UojPYiXZ6kPlzZkUC5jH-EcWXomTLtmadAIxurfcHo'
|
||||
REACT_APP_FIREBASE_PUBLIC_VAPID_KEY='BN2GcDPjipR5MTEosO5dT4CfQ3cmrdBIsI4juoOQrRijn_5aRiHlwj1mlq0W145mOusx6xynEKl_tvYJhpCc9lo'
|
||||
REACT_APP_STRIPE_PUBLIC_KEY=pk_test_51GqB4TJl3nQjrZ0wCQWAxAhlNF8jKe0tipIa6ExBaxwJGitwvFsIZUEua4dUzaMIAuXp4qwYHXx7lgjyQSwP0Pe900vzm38C7g
|
||||
REACT_APP_AXIOS_BASE_API_URL=https://api.test.romeonline.io/
|
||||
REACT_APP_REPORTS_SERVER_URL=https://reports.test.romeonline.io
|
||||
REACT_APP_AXIOS_BASE_API_URL=https://api.test.imex.online/
|
||||
REACT_APP_REPORTS_SERVER_URL=https://reports3.test.imex.online
|
||||
REACT_APP_IS_TEST=true
|
||||
REACT_APP_SPLIT_API=ts615lqgnmk84thn72uk18uu5pgce6e0l4rc
|
||||
@@ -27,7 +27,7 @@ module.exports = {
|
||||
lessOptions: {
|
||||
modifyVars: {
|
||||
...(process.env.NODE_ENV === "development"
|
||||
? { "@primary-color": "#B22234" }
|
||||
? { "@primary-color": "#a51d1d" }
|
||||
: {
|
||||
//"@primary-color": "#1DA57A"
|
||||
}),
|
||||
|
||||
@@ -28,17 +28,6 @@ switch (this.location.hostname) {
|
||||
// measurementId: "${config.measurementId}",
|
||||
};
|
||||
break;
|
||||
case "romeonline.io":
|
||||
firebaseConfig = {
|
||||
apiKey: "AIzaSyAuLQR9SV5LsVxjU8wh9hvFLdhcAHU6cxE",
|
||||
authDomain: "rome-prod-1.firebaseapp.com",
|
||||
projectId: "rome-prod-1",
|
||||
storageBucket: "rome-prod-1.appspot.com",
|
||||
messagingSenderId: "147786367145",
|
||||
appId: "1:147786367145:web:9d4cba68071c3f29a8a9b8",
|
||||
measurementId: "G-G8Z9DRHTZS",
|
||||
};
|
||||
break;
|
||||
case "imex.online":
|
||||
default:
|
||||
firebaseConfig = {
|
||||
|
||||
@@ -2,81 +2,23 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="%PUBLIC_URL%/ro-favicon.png" />
|
||||
<link rel="icon" href="%PUBLIC_URL%/favicon.png" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#002366" />
|
||||
<meta name="description" content="Rome Online" />
|
||||
<meta name="description" content="ImEX Online" />
|
||||
<!-- <link rel="apple-touch-icon" href="logo192.png" /> -->
|
||||
|
||||
<!--Use the below code snippet to provide real time updates to the live chat plugin without the need of copying and paste each time to your website when changes are made via PBX-->
|
||||
|
||||
<call-us-selector phonesystem-url=https://rometech.east.3cx.us:5001 party="LiveChat528346"></call-us-selector>
|
||||
|
||||
<!--Incase you don't want real time updates to the live chat plugin when options are changed, use the below code snippet. Please note that each time you change the settings you will need to copy and paste the snippet code to your website-->
|
||||
|
||||
<!--<call-us
|
||||
|
||||
phonesystem-url=https://rometech.east.3cx.us:5001
|
||||
|
||||
style="position:fixed;font-size:16px;line-height:17px;z-index: 99999;right: 20px; bottom: 20px;"
|
||||
|
||||
id="wp-live-chat-by-3CX"
|
||||
|
||||
minimized="true"
|
||||
|
||||
animation-style="noanimation"
|
||||
|
||||
party="LiveChat528346"
|
||||
|
||||
minimized-style="bubbleright"
|
||||
|
||||
allow-call="true"
|
||||
|
||||
allow-video="false"
|
||||
|
||||
allow-soundnotifications="true"
|
||||
|
||||
enable-mute="true"
|
||||
|
||||
enable-onmobile="true"
|
||||
|
||||
offline-enabled="true"
|
||||
|
||||
enable="true"
|
||||
|
||||
ignore-queueownership="false"
|
||||
|
||||
authentication="both"
|
||||
|
||||
show-operator-actual-name="true"
|
||||
|
||||
aknowledge-received="true"
|
||||
|
||||
gdpr-enabled="false"
|
||||
|
||||
message-userinfo-format="name"
|
||||
|
||||
message-dateformat="both"
|
||||
|
||||
lang="browser"
|
||||
|
||||
button-icon-type="default"
|
||||
|
||||
greeting-visibility="none"
|
||||
|
||||
greeting-offline-visibility="none"
|
||||
|
||||
chat-delay="2000"
|
||||
|
||||
enable-direct-call="true"
|
||||
|
||||
enable-ga="false"
|
||||
|
||||
></call-us>-->
|
||||
|
||||
<script defer src=https://downloads-global.3cx.com/downloads/livechatandtalk/v1/callus.js id="tcx-callus-js" charset="utf-8"></script>
|
||||
|
||||
<link rel="apple-touch-icon" href="logo192.png" />
|
||||
<script type="text/javascript">
|
||||
window.$crisp = [];
|
||||
window.CRISP_WEBSITE_ID = "36724f62-2eb0-4b29-9cdd-9905fb99913e";
|
||||
(function () {
|
||||
d = document;
|
||||
s = d.createElement("script");
|
||||
s.src = "https://client.crisp.chat/l.js";
|
||||
s.async = 1;
|
||||
d.getElementsByTagName("head")[0].appendChild(s);
|
||||
})();
|
||||
</script>
|
||||
<script>
|
||||
!(function () {
|
||||
"use strict";
|
||||
@@ -135,7 +77,7 @@ enable-ga="false"
|
||||
work correctly both with client-side routing and a non-root public URL.
|
||||
Learn how to configure a non-root public URL by running `npm run build`.
|
||||
-->
|
||||
<title>Rome Online</title>
|
||||
<title>ImEX Online</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"short_name": "Rome Online",
|
||||
"name": "Rome Online",
|
||||
"short_name": "ImEX Online",
|
||||
"name": "ImEX Online",
|
||||
"description": "The ultimate bodyshop management system.",
|
||||
"icons": [
|
||||
{
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 376 B |
@@ -28,12 +28,6 @@ function AppContainer() {
|
||||
//componentSize="small"
|
||||
input={{ autoComplete: "new-password" }}
|
||||
locale={enLocale}
|
||||
theme={{
|
||||
token: {
|
||||
colorPrimary: "#326ade",
|
||||
colorInfo: "#326ade"
|
||||
},
|
||||
}}
|
||||
form={{
|
||||
validateMessages: {
|
||||
// eslint-disable-next-line no-template-curly-in-string
|
||||
|
||||
@@ -78,9 +78,9 @@ export function App({
|
||||
if (currentUser.authorized && bodyshop) {
|
||||
client.setAttribute("imexshopid", bodyshop.imexshopid);
|
||||
|
||||
LogRocket.init("rome-online/rome-online");
|
||||
if (client.getTreatment("LogRocket_Tracking") === "on") {
|
||||
LogRocket.init("rome-online/rome-online");
|
||||
console.log("LR Start");
|
||||
LogRocket.init("gvfvfw/bodyshopapp");
|
||||
}
|
||||
}
|
||||
}, [bodyshop, client, currentUser.authorized]);
|
||||
@@ -112,7 +112,7 @@ export function App({
|
||||
|
||||
return (
|
||||
<Switch>
|
||||
<Suspense fallback={<LoadingSpinner />}>
|
||||
<Suspense fallback={<LoadingSpinner message="ImEX Online" />}>
|
||||
<ErrorBoundary>
|
||||
<Route exact path="/" component={LandingPage} />
|
||||
</ErrorBoundary>
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 26 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 27 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 72 KiB |
@@ -1,21 +1,21 @@
|
||||
import { Input, Table, Checkbox, Card, Space } from "antd";
|
||||
import { Card, Checkbox, Input, Space, Table } from "antd";
|
||||
import queryString from "query-string";
|
||||
import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Link } from "react-router-dom";
|
||||
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||
import { alphaSort, dateSort } from "../../utils/sorters";
|
||||
import PayableExportButton from "../payable-export-button/payable-export-button.component";
|
||||
import PayableExportAll from "../payable-export-all-button/payable-export-all-button.component";
|
||||
import { DateFormatter } from "../../utils/DateFormatter";
|
||||
import queryString from "query-string";
|
||||
import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||
import QboAuthorizeComponent from "../qbo-authorize/qbo-authorize.component";
|
||||
import { connect } from "react-redux";
|
||||
import { Link } from "react-router-dom";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||
import { DateFormatter } from "../../utils/DateFormatter";
|
||||
import { pageLimit } from "../../utils/config";
|
||||
import { alphaSort, dateSort } from "../../utils/sorters";
|
||||
import ExportLogsCountDisplay from "../export-logs-count-display/export-logs-count-display.component";
|
||||
import PayableExportAll from "../payable-export-all-button/payable-export-all-button.component";
|
||||
import PayableExportButton from "../payable-export-button/payable-export-button.component";
|
||||
import BillMarkSelectedExported from "../payable-mark-selected-exported/payable-mark-selected-exported.component";
|
||||
import {pageLimit} from "../../utils/config";
|
||||
import QboAuthorizeComponent from "../qbo-authorize/qbo-authorize.component";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
@@ -138,7 +138,6 @@ export function AccountingPayablesTableComponent({
|
||||
title: t("exportlogs.labels.attempts"),
|
||||
dataIndex: "attempts",
|
||||
key: "attempts",
|
||||
|
||||
render: (text, record) => (
|
||||
<ExportLogsCountDisplay logs={record.exportlogs} />
|
||||
),
|
||||
@@ -147,8 +146,6 @@ export function AccountingPayablesTableComponent({
|
||||
title: t("general.labels.actions"),
|
||||
dataIndex: "actions",
|
||||
key: "actions",
|
||||
sorter: (a, b) => a.clm_total - b.clm_total,
|
||||
|
||||
render: (text, record) => (
|
||||
<PayableExportButton
|
||||
billId={record.id}
|
||||
|
||||
@@ -8,14 +8,16 @@ import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||
import { DateFormatter, DateTimeFormatter } from "../../utils/DateFormatter";
|
||||
import { pageLimit } from "../../utils/config";
|
||||
import { alphaSort, dateSort } from "../../utils/sorters";
|
||||
import ExportLogsCountDisplay from "../export-logs-count-display/export-logs-count-display.component";
|
||||
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
|
||||
import OwnerNameDisplay, {
|
||||
OwnerNameDisplayFunction,
|
||||
} from "../owner-name-display/owner-name-display.component";
|
||||
import PaymentExportButton from "../payment-export-button/payment-export-button.component";
|
||||
import PaymentMarkSelectedExported from "../payment-mark-selected-exported/payment-mark-selected-exported.component";
|
||||
import PaymentsExportAllButton from "../payments-export-all-button/payments-export-all-button.component";
|
||||
import QboAuthorizeComponent from "../qbo-authorize/qbo-authorize.component";
|
||||
import {pageLimit} from "../../utils/config";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
@@ -75,7 +77,11 @@ export function AccountingPayablesTableComponent({
|
||||
dataIndex: "owner",
|
||||
key: "owner",
|
||||
ellipsis: true,
|
||||
sorter: (a, b) => alphaSort(a.job.ownr_ln, b.job.ownr_ln),
|
||||
sorter: (a, b) =>
|
||||
alphaSort(
|
||||
OwnerNameDisplayFunction(a.job),
|
||||
OwnerNameDisplayFunction(b.job)
|
||||
),
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "owner" && state.sortedInfo.order,
|
||||
render: (text, record) => {
|
||||
@@ -94,6 +100,9 @@ export function AccountingPayablesTableComponent({
|
||||
title: t("payments.fields.amount"),
|
||||
dataIndex: "amount",
|
||||
key: "amount",
|
||||
sorter: (a, b) => a.amount - b.amount,
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "amount" && state.sortedInfo.order,
|
||||
render: (text, record) => (
|
||||
<CurrencyFormatter>{record.amount}</CurrencyFormatter>
|
||||
),
|
||||
@@ -112,18 +121,21 @@ export function AccountingPayablesTableComponent({
|
||||
title: t("payments.fields.created_at"),
|
||||
dataIndex: "created_at",
|
||||
key: "created_at",
|
||||
sorter: (a, b) => dateSort(a.created_at, b.created_at),
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "created_at" && state.sortedInfo.order,
|
||||
render: (text, record) => (
|
||||
<DateTimeFormatter>{record.created_at}</DateTimeFormatter>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t("payments.fields.exportedat"),
|
||||
dataIndex: "exportedat",
|
||||
key: "exportedat",
|
||||
render: (text, record) => (
|
||||
<DateTimeFormatter>{record.exportedat}</DateTimeFormatter>
|
||||
),
|
||||
},
|
||||
// {
|
||||
// title: t("payments.fields.exportedat"),
|
||||
// dataIndex: "exportedat",
|
||||
// key: "exportedat",
|
||||
// render: (text, record) => (
|
||||
// <DateTimeFormatter>{record.exportedat}</DateTimeFormatter>
|
||||
// ),
|
||||
// },
|
||||
{
|
||||
title: t("exportlogs.labels.attempts"),
|
||||
dataIndex: "attempts",
|
||||
@@ -137,8 +149,6 @@ export function AccountingPayablesTableComponent({
|
||||
title: t("general.labels.actions"),
|
||||
dataIndex: "actions",
|
||||
key: "actions",
|
||||
sorter: (a, b) => a.clm_total - b.clm_total,
|
||||
|
||||
render: (text, record) => (
|
||||
<PaymentExportButton
|
||||
paymentId={record.id}
|
||||
|
||||
@@ -4,17 +4,19 @@ import { useTranslation } from "react-i18next";
|
||||
import { Link } from "react-router-dom";
|
||||
import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||
import { alphaSort, dateSort } from "../../utils/sorters";
|
||||
import { alphaSort, dateSort, statusSort } from "../../utils/sorters";
|
||||
import JobExportButton from "../jobs-close-export-button/jobs-close-export-button.component";
|
||||
import JobsExportAllButton from "../jobs-export-all-button/jobs-export-all-button.component";
|
||||
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import QboAuthorizeComponent from "../qbo-authorize/qbo-authorize.component";
|
||||
import { DateFormatter } from "../../utils/DateFormatter";
|
||||
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
|
||||
import ExportLogsCountDisplay from "../export-logs-count-display/export-logs-count-display.component";
|
||||
import OwnerNameDisplay, {
|
||||
OwnerNameDisplayFunction,
|
||||
} from "../owner-name-display/owner-name-display.component";
|
||||
import QboAuthorizeComponent from "../qbo-authorize/qbo-authorize.component";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
@@ -63,7 +65,7 @@ export function AccountingReceivablesTableComponent({
|
||||
title: t("jobs.fields.status"),
|
||||
dataIndex: "status",
|
||||
key: "status",
|
||||
sorter: (a, b) => a.status - b.status,
|
||||
sorter: (a, b) => statusSort(a, b, bodyshop.md_ro_statuses.statuses),
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "status" && state.sortedInfo.order,
|
||||
},
|
||||
@@ -83,7 +85,8 @@ export function AccountingReceivablesTableComponent({
|
||||
title: t("jobs.fields.owner"),
|
||||
dataIndex: "owner",
|
||||
key: "owner",
|
||||
sorter: (a, b) => alphaSort(a.ownr_ln, b.ownr_ln),
|
||||
sorter: (a, b) =>
|
||||
alphaSort(OwnerNameDisplayFunction(a), OwnerNameDisplayFunction(b)),
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "owner" && state.sortedInfo.order,
|
||||
render: (text, record) => {
|
||||
@@ -103,6 +106,15 @@ export function AccountingReceivablesTableComponent({
|
||||
dataIndex: "vehicle",
|
||||
key: "vehicle",
|
||||
ellipsis: true,
|
||||
sorter: (a, b) =>
|
||||
alphaSort(
|
||||
`${a.v_model_yr || ""} ${a.v_make_desc || ""} ${
|
||||
a.v_model_desc || ""
|
||||
}`,
|
||||
`${b.v_model_yr || ""} ${b.v_make_desc || ""} ${b.v_model_desc || ""}`
|
||||
),
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "vehicle" && state.sortedInfo.order,
|
||||
render: (text, record) => {
|
||||
return record.vehicleid ? (
|
||||
<Link to={"/manage/vehicles/" + record.vehicleid}>
|
||||
|
||||
@@ -12,8 +12,8 @@ import RbacWrapper from "../rbac-wrapper/rbac-wrapper.component";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
insertAuditTrail: ({ jobid, operation }) =>
|
||||
dispatch(insertAuditTrail({ jobid, operation })),
|
||||
insertAuditTrail: ({ jobid, operation, type }) =>
|
||||
dispatch(insertAuditTrail({ jobid, operation, type })),
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(BillDeleteButton);
|
||||
@@ -51,6 +51,7 @@ export function BillDeleteButton({ bill, jobid, callback, insertAuditTrail }) {
|
||||
insertAuditTrail({
|
||||
jobid: jobid,
|
||||
operation: AuditTrailMapping.billdeleted(bill.invoice_number),
|
||||
type: "billdeleted",
|
||||
});
|
||||
|
||||
if (callback && typeof callback === "function") callback(bill.id);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useMutation, useQuery } from "@apollo/client";
|
||||
import { Button, Form, PageHeader, Popconfirm, Space } from "antd";
|
||||
import { Button, Divider, Form, PageHeader, Popconfirm, Space } from "antd";
|
||||
import moment from "moment";
|
||||
import queryString from "query-string";
|
||||
import React, { useState } from "react";
|
||||
@@ -33,8 +33,8 @@ const mapStateToProps = createStructuredSelector({
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setPartsOrderContext: (context) =>
|
||||
dispatch(setModalContext({ context: context, modal: "partsOrder" })),
|
||||
insertAuditTrail: ({ jobid, operation }) =>
|
||||
dispatch(insertAuditTrail({ jobid, operation })),
|
||||
insertAuditTrail: ({ jobid, operation, type }) =>
|
||||
dispatch(insertAuditTrail({ jobid, operation, type })),
|
||||
});
|
||||
|
||||
export default connect(
|
||||
@@ -150,6 +150,7 @@ export function BillDetailEditcontainer({
|
||||
jobid: bill.jobid,
|
||||
billid: search.billid,
|
||||
operation: AuditTrailMapping.billupdated(bill.invoice_number),
|
||||
type: "billupdated",
|
||||
});
|
||||
|
||||
await refetch();
|
||||
@@ -163,6 +164,7 @@ export function BillDetailEditcontainer({
|
||||
if (!search.billid) return <></>; //<div>{t("bills.labels.noneselected")}</div>;
|
||||
|
||||
const exported = data && data.bills_by_pk && data.bills_by_pk.exported;
|
||||
const isinhouse = data && data.bills_by_pk && data.bills_by_pk.isinhouse;
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -206,8 +208,8 @@ export function BillDetailEditcontainer({
|
||||
initialValues={transformData(data)}
|
||||
layout="vertical"
|
||||
>
|
||||
<BillFormContainer form={form} billEdit disabled={exported} />
|
||||
|
||||
<BillFormContainer form={form} billEdit disabled={exported} disableInHouse={isinhouse}/>
|
||||
<Divider orientation="left">{t("general.labels.media")}</Divider>
|
||||
{bodyshop.uselocalmediaserver ? (
|
||||
<JobsDocumentsLocalGallery
|
||||
job={{ id: data ? data.bills_by_pk.jobid : null }}
|
||||
|
||||
@@ -16,8 +16,8 @@ const mapStateToProps = createStructuredSelector({
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setPartsOrderContext: (context) =>
|
||||
dispatch(setModalContext({ context: context, modal: "partsOrder" })),
|
||||
insertAuditTrail: ({ jobid, operation }) =>
|
||||
dispatch(insertAuditTrail({ jobid, operation })),
|
||||
insertAuditTrail: ({ jobid, operation, type }) =>
|
||||
dispatch(insertAuditTrail({ jobid, operation, type })),
|
||||
});
|
||||
|
||||
export default connect(
|
||||
@@ -45,6 +45,7 @@ export function BillDetailEditReturn({
|
||||
actions: {},
|
||||
context: {
|
||||
jobId: data.bills_by_pk.jobid,
|
||||
job: data.bills_by_pk.job,
|
||||
vendorId: data.bills_by_pk.vendorid,
|
||||
returnFromBill: data.bills_by_pk.id,
|
||||
invoiceNumber: data.bills_by_pk.invoice_number,
|
||||
@@ -173,7 +174,11 @@ export function BillDetailEditReturn({
|
||||
</Form>
|
||||
</Modal>
|
||||
<Button
|
||||
disabled={data.bills_by_pk.is_credit_memo || disabled}
|
||||
disabled={
|
||||
data.bills_by_pk.is_credit_memo ||
|
||||
data.bills_by_pk.isinhouse ||
|
||||
disabled
|
||||
}
|
||||
onClick={() => {
|
||||
setVisible(true);
|
||||
}}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { useApolloClient, useMutation } from "@apollo/client";
|
||||
import { useTreatments } from "@splitsoftware/splitio-react";
|
||||
import { Button, Checkbox, Form, Modal, Space, notification } from "antd";
|
||||
import _ from "lodash";
|
||||
import React, { useEffect, useMemo, useState } from "react";
|
||||
@@ -38,8 +37,8 @@ const mapStateToProps = createStructuredSelector({
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
toggleModalVisible: () => dispatch(toggleModalVisible("billEnter")),
|
||||
insertAuditTrail: ({ jobid, billid, operation }) =>
|
||||
dispatch(insertAuditTrail({ jobid, billid, operation })),
|
||||
insertAuditTrail: ({ jobid, billid, operation, type }) =>
|
||||
dispatch(insertAuditTrail({ jobid, billid, operation, type })),
|
||||
});
|
||||
|
||||
const Templates = TemplateList("job_special");
|
||||
@@ -64,19 +63,9 @@ function BillEnterModalContainer({
|
||||
"enter_bill_generate_label",
|
||||
false
|
||||
);
|
||||
const { Enhanced_Payroll } = useTreatments(
|
||||
["Enhanced_Payroll"],
|
||||
{},
|
||||
bodyshop.imexshopid
|
||||
);
|
||||
const formValues = useMemo(() => {
|
||||
return {
|
||||
...billEnterModal.context.bill,
|
||||
//Added as a part of IO-2436 for capturing parts price changes.
|
||||
billlines: billEnterModal.context?.bill?.billlines?.map((line) => ({
|
||||
...line,
|
||||
original_actual_price: line.actual_price,
|
||||
})),
|
||||
jobid:
|
||||
(billEnterModal.context.job && billEnterModal.context.job.id) || null,
|
||||
federal_tax_rate:
|
||||
@@ -110,7 +99,6 @@ function BillEnterModalContainer({
|
||||
} = values;
|
||||
|
||||
let adjustmentsToInsert = {};
|
||||
let payrollAdjustmentsToInsert = [];
|
||||
|
||||
const r1 = await insertBill({
|
||||
variables: {
|
||||
@@ -126,33 +114,14 @@ function BillEnterModalContainer({
|
||||
lbr_adjustment,
|
||||
location: lineLocation,
|
||||
part_type,
|
||||
create_ppc,
|
||||
original_actual_price,
|
||||
...restI
|
||||
} = i;
|
||||
|
||||
if (Enhanced_Payroll.treatment === "on") {
|
||||
if (
|
||||
deductedfromlbr &&
|
||||
true //payroll is on
|
||||
) {
|
||||
payrollAdjustmentsToInsert.push({
|
||||
id: i.joblineid,
|
||||
convertedtolbr: true,
|
||||
convertedtolbr_data: {
|
||||
mod_lb_hrs: lbr_adjustment.mod_lb_hrs * -1,
|
||||
mod_lbr_ty: lbr_adjustment.mod_lbr_ty,
|
||||
},
|
||||
});
|
||||
}
|
||||
} else {
|
||||
if (deductedfromlbr) {
|
||||
adjustmentsToInsert[lbr_adjustment.mod_lbr_ty] =
|
||||
(adjustmentsToInsert[lbr_adjustment.mod_lbr_ty] || 0) -
|
||||
restI.actual_price / lbr_adjustment.rate;
|
||||
}
|
||||
if (deductedfromlbr) {
|
||||
adjustmentsToInsert[lbr_adjustment.mod_lbr_ty] =
|
||||
(adjustmentsToInsert[lbr_adjustment.mod_lbr_ty] || 0) -
|
||||
restI.actual_price / lbr_adjustment.rate;
|
||||
}
|
||||
|
||||
return {
|
||||
...restI,
|
||||
deductedfromlbr: deductedfromlbr,
|
||||
@@ -178,20 +147,6 @@ function BillEnterModalContainer({
|
||||
refetchQueries: ["QUERY_PARTS_BILLS_BY_JOBID", "GET_JOB_BY_PK"],
|
||||
});
|
||||
|
||||
await Promise.all(
|
||||
payrollAdjustmentsToInsert.map((li) => {
|
||||
return updateJobLines({
|
||||
variables: {
|
||||
lineId: li.id,
|
||||
line: {
|
||||
convertedtolbr: li.convertedtolbr,
|
||||
convertedtolbr_data: li.convertedtolbr_data,
|
||||
},
|
||||
},
|
||||
});
|
||||
})
|
||||
);
|
||||
|
||||
const adjKeys = Object.keys(adjustmentsToInsert);
|
||||
if (adjKeys.length > 0) {
|
||||
//Query the adjustments, merge, and update them.
|
||||
@@ -216,6 +171,7 @@ function BillEnterModalContainer({
|
||||
mod_lbr_ty: key,
|
||||
hours: adjustmentsToInsert[key].toFixed(1),
|
||||
}),
|
||||
type: "jobmodifylbradj",
|
||||
});
|
||||
});
|
||||
|
||||
@@ -300,14 +256,6 @@ function BillEnterModalContainer({
|
||||
location: li.location || location,
|
||||
status:
|
||||
bodyshop.md_order_statuses.default_received || "Received*",
|
||||
//Added parts price changes.
|
||||
...(li.create_ppc &&
|
||||
li.original_actual_price !== li.actual_price
|
||||
? {
|
||||
act_price_before_ppc: li.original_actual_price,
|
||||
act_price: li.actual_price,
|
||||
}
|
||||
: {}),
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -373,15 +321,16 @@ function BillEnterModalContainer({
|
||||
operation: AuditTrailMapping.billposted(
|
||||
r1.data.insert_bills.returning[0].invoice_number
|
||||
),
|
||||
type: "billposted",
|
||||
});
|
||||
|
||||
if (enterAgain) {
|
||||
// form.resetFields();
|
||||
form.resetFields();
|
||||
form.resetFields();
|
||||
form.setFieldsValue({
|
||||
...formValues,
|
||||
billlines: [],
|
||||
});
|
||||
form.resetFields();
|
||||
} else {
|
||||
toggleModalVisible();
|
||||
}
|
||||
|
||||
@@ -47,6 +47,7 @@ export function BillFormComponent({
|
||||
loadLines,
|
||||
billEdit,
|
||||
disableInvNumber,
|
||||
disableInHouse,
|
||||
job,
|
||||
loadOutstandingReturns,
|
||||
loadInventory,
|
||||
@@ -79,19 +80,19 @@ export function BillFormComponent({
|
||||
});
|
||||
};
|
||||
|
||||
// const handleFederalTaxExemptSwitchToggle = (checked) => {
|
||||
// // Early gate
|
||||
// if (!checked) return;
|
||||
// const values = form.getFieldsValue("billlines");
|
||||
// // Gate bill lines
|
||||
// if (!values?.billlines?.length) return;
|
||||
const handleFederalTaxExemptSwitchToggle = (checked) => {
|
||||
// Early gate
|
||||
if (!checked) return;
|
||||
const values = form.getFieldsValue("billlines");
|
||||
// Gate bill lines
|
||||
if (!values?.billlines?.length) return;
|
||||
|
||||
// const billlines = values.billlines.map((b) => {
|
||||
// b.applicable_taxes.federal = false;
|
||||
// return b;
|
||||
// });
|
||||
// form.setFieldsValue({ billlines });
|
||||
// };
|
||||
const billlines = values.billlines.map((b) => {
|
||||
b.applicable_taxes.federal = false;
|
||||
return b;
|
||||
});
|
||||
form.setFieldsValue({ billlines });
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (job) form.validateFields(["is_credit_memo"]);
|
||||
@@ -198,7 +199,7 @@ export function BillFormComponent({
|
||||
]}
|
||||
>
|
||||
<VendorSearchSelect
|
||||
disabled={disabled}
|
||||
disabled={disabled || disableInHouse}
|
||||
options={vendorAutoCompleteOptions}
|
||||
preferredMake={preferredMake}
|
||||
onSelect={handleVendorSelect}
|
||||
@@ -271,7 +272,7 @@ export function BillFormComponent({
|
||||
}),
|
||||
]}
|
||||
>
|
||||
<Input disabled={disabled || disableInvNumber} />
|
||||
<Input disabled={disabled || disableInvNumber || disableInHouse} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("bills.fields.date")}
|
||||
@@ -380,15 +381,13 @@ export function BillFormComponent({
|
||||
)}
|
||||
</LayoutFormRow>
|
||||
<LayoutFormRow>
|
||||
{
|
||||
// <Form.Item
|
||||
// span={3}
|
||||
// label={t("bills.fields.federal_tax_rate")}
|
||||
// name="federal_tax_rate"
|
||||
// >
|
||||
// <CurrencyInput min={0} disabled={disabled} />
|
||||
// </Form.Item>
|
||||
}
|
||||
<Form.Item
|
||||
span={3}
|
||||
label={t("bills.fields.federal_tax_rate")}
|
||||
name="federal_tax_rate"
|
||||
>
|
||||
<CurrencyInput min={0} disabled={disabled} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
span={3}
|
||||
label={t("bills.fields.state_tax_rate")}
|
||||
@@ -396,27 +395,22 @@ export function BillFormComponent({
|
||||
>
|
||||
<CurrencyInput min={0} disabled={disabled} />
|
||||
</Form.Item>
|
||||
{
|
||||
// <Form.Item
|
||||
// span={3}
|
||||
// label={t("bills.fields.local_tax_rate")}
|
||||
// name="local_tax_rate"
|
||||
// >
|
||||
// <CurrencyInput min={0} />
|
||||
// </Form.Item>
|
||||
}
|
||||
{
|
||||
//Removed as a part of the merge to Rome Online. Federal tax not applicable.
|
||||
// bodyshop.pbs_serialnumber || bodyshop.cdk_dealerid ? (
|
||||
// <Form.Item
|
||||
// span={2}
|
||||
// label={t("bills.labels.federal_tax_exempt")}
|
||||
// name="federal_tax_exempt"
|
||||
// >
|
||||
// <Switch onChange={handleFederalTaxExemptSwitchToggle} />
|
||||
// </Form.Item>
|
||||
// ) : null
|
||||
}
|
||||
<Form.Item
|
||||
span={3}
|
||||
label={t("bills.fields.local_tax_rate")}
|
||||
name="local_tax_rate"
|
||||
>
|
||||
<CurrencyInput min={0} />
|
||||
</Form.Item>
|
||||
{bodyshop.pbs_serialnumber || bodyshop.cdk_dealerid ? (
|
||||
<Form.Item
|
||||
span={2}
|
||||
label={t("bills.labels.federal_tax_exempt")}
|
||||
name="federal_tax_exempt"
|
||||
>
|
||||
<Switch onChange={handleFederalTaxExemptSwitchToggle} />
|
||||
</Form.Item>
|
||||
) : null}
|
||||
<Form.Item shouldUpdate span={13}>
|
||||
{() => {
|
||||
const values = form.getFieldsValue([
|
||||
@@ -442,25 +436,21 @@ export function BillFormComponent({
|
||||
value={totals.subtotal.toFormat()}
|
||||
precision={2}
|
||||
/>
|
||||
{
|
||||
// <Statistic
|
||||
// title={t("bills.labels.federal_tax")}
|
||||
// value={totals.federalTax.toFormat()}
|
||||
// precision={2}
|
||||
// />
|
||||
}
|
||||
<Statistic
|
||||
title={t("bills.labels.federal_tax")}
|
||||
value={totals.federalTax.toFormat()}
|
||||
precision={2}
|
||||
/>
|
||||
<Statistic
|
||||
title={t("bills.labels.state_tax")}
|
||||
value={totals.stateTax.toFormat()}
|
||||
precision={2}
|
||||
/>
|
||||
{
|
||||
// <Statistic
|
||||
// title={t("bills.labels.local_tax")}
|
||||
// value={totals.localTax.toFormat()}
|
||||
// precision={2}
|
||||
// />
|
||||
}
|
||||
<Statistic
|
||||
title={t("bills.labels.local_tax")}
|
||||
value={totals.localTax.toFormat()}
|
||||
precision={2}
|
||||
/>
|
||||
<Statistic
|
||||
title={t("bills.labels.entered_total")}
|
||||
value={totals.enteredTotal.toFormat()}
|
||||
@@ -515,10 +505,11 @@ export function BillFormComponent({
|
||||
billEdit={billEdit}
|
||||
/>
|
||||
)}
|
||||
|
||||
<Divider orientation="left" style={{ display: billEdit ? "none" : null }}>
|
||||
{t("documents.labels.upload")}
|
||||
</Divider>
|
||||
<Form.Item
|
||||
name="upload"
|
||||
label="Upload"
|
||||
style={{ display: billEdit ? "none" : null }}
|
||||
valuePropName="fileList"
|
||||
getValueFromEvent={(e) => {
|
||||
|
||||
@@ -22,6 +22,7 @@ export function BillFormContainer({
|
||||
billEdit,
|
||||
disabled,
|
||||
disableInvNumber,
|
||||
disableInHouse
|
||||
}) {
|
||||
const { Simple_Inventory } = useTreatments(
|
||||
["Simple_Inventory"],
|
||||
@@ -57,6 +58,7 @@ export function BillFormContainer({
|
||||
job={lineData ? lineData.jobs_by_pk : null}
|
||||
responsibilityCenters={bodyshop.md_responsibility_centers || null}
|
||||
disableInvNumber={disableInvNumber}
|
||||
disableInHouse={disableInHouse}
|
||||
loadOutstandingReturns={loadOutstandingReturns}
|
||||
loadInventory={loadInventory}
|
||||
preferredMake={lineData ? lineData.jobs_by_pk.v_make_desc : null}
|
||||
|
||||
@@ -2,7 +2,6 @@ import { DeleteFilled, DollarCircleFilled } from "@ant-design/icons";
|
||||
import { useTreatments } from "@splitsoftware/splitio-react";
|
||||
import {
|
||||
Button,
|
||||
Checkbox,
|
||||
Form,
|
||||
Input,
|
||||
InputNumber,
|
||||
@@ -47,13 +46,6 @@ export function BillEnterModalLinesComponent({
|
||||
{},
|
||||
bodyshop && bodyshop.imexshopid
|
||||
);
|
||||
|
||||
const { Enhanced_Payroll } = useTreatments(
|
||||
["Enhanced_Payroll"],
|
||||
{},
|
||||
bodyshop.imexshopid
|
||||
);
|
||||
|
||||
const columns = (remove) => {
|
||||
return [
|
||||
{
|
||||
@@ -102,7 +94,6 @@ export function BillEnterModalLinesComponent({
|
||||
line_desc: opt.line_desc,
|
||||
quantity: opt.part_qty || 1,
|
||||
actual_price: opt.cost,
|
||||
original_actual_price: opt.cost,
|
||||
cost_center: opt.part_type
|
||||
? bodyshop.pbs_serialnumber || bodyshop.cdk_dealerid
|
||||
? opt.part_type !== "PAE"
|
||||
@@ -229,43 +220,6 @@ export function BillEnterModalLinesComponent({
|
||||
}}
|
||||
/>
|
||||
),
|
||||
additional: (record, index) => (
|
||||
<Form.Item
|
||||
dependencies={["billlines", record.name, "actual_price"]}
|
||||
noStyle
|
||||
>
|
||||
{() => {
|
||||
const billLine = getFieldValue(["billlines", record.name]);
|
||||
const jobLine = lineData.find(
|
||||
(line) => line.id === billLine?.joblineid
|
||||
);
|
||||
|
||||
if (
|
||||
!billEdit &&
|
||||
billLine &&
|
||||
jobLine &&
|
||||
billLine?.actual_price !== jobLine?.act_price
|
||||
) {
|
||||
return (
|
||||
<Space size="small">
|
||||
<Form.Item
|
||||
noStyle
|
||||
label={t("joblines.fields.create_ppc")}
|
||||
key={`${index}ppc`}
|
||||
valuePropName="checked"
|
||||
name={[record.name, "create_ppc"]}
|
||||
>
|
||||
<Checkbox />
|
||||
</Form.Item>
|
||||
{t("joblines.fields.create_ppc")}
|
||||
</Space>
|
||||
);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}}
|
||||
</Form.Item>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t("billlines.fields.actual_cost"),
|
||||
@@ -407,7 +361,7 @@ export function BillEnterModalLinesComponent({
|
||||
},
|
||||
formInput: (record, index) => <Switch disabled={disabled} />,
|
||||
additional: (record, index) => (
|
||||
<Form.Item shouldUpdate noStyle style={{ display: "inline-block" }}>
|
||||
<Form.Item shouldUpdate style={{ display: "inline-block" }}>
|
||||
{() => {
|
||||
const price = getFieldValue([
|
||||
"billlines",
|
||||
@@ -422,31 +376,12 @@ export function BillEnterModalLinesComponent({
|
||||
"rate",
|
||||
]);
|
||||
|
||||
const billline = getFieldValue(["billlines", record.name]);
|
||||
|
||||
const jobline = lineData.find(
|
||||
(line) => line.id === billline?.joblineid
|
||||
);
|
||||
|
||||
const employeeTeamName = bodyshop.employee_teams.find(
|
||||
(team) => team.id === jobline?.assigned_team
|
||||
);
|
||||
|
||||
if (getFieldValue(["billlines", record.name, "deductedfromlbr"]))
|
||||
return (
|
||||
<div>
|
||||
<Space>
|
||||
{t("joblines.fields.assigned_team", {
|
||||
name: employeeTeamName?.name,
|
||||
})}
|
||||
{`${jobline.mod_lb_hrs} units/${t(
|
||||
`joblines.fields.lbr_types.${jobline.mod_lbr_ty}`
|
||||
)}`}
|
||||
</Space>
|
||||
<Form.Item
|
||||
label={t("joblines.fields.mod_lbr_ty")}
|
||||
key={`${index}modlbrty`}
|
||||
initialValue={jobline ? jobline.mod_lbr_ty : null}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
@@ -500,44 +435,22 @@ export function BillEnterModalLinesComponent({
|
||||
</Select.Option>
|
||||
</Select>
|
||||
</Form.Item>
|
||||
{Enhanced_Payroll.treatment === "on" ? (
|
||||
<Form.Item
|
||||
label={t("billlines.labels.mod_lbr_adjustment")}
|
||||
name={[record.name, "lbr_adjustment", "mod_lb_hrs"]}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
//message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<InputNumber
|
||||
precision={5}
|
||||
min={0.01}
|
||||
max={jobline ? jobline.mod_lb_hrs : 0}
|
||||
/>
|
||||
</Form.Item>
|
||||
) : (
|
||||
<Form.Item
|
||||
label={t("jobs.labels.adjustmentrate")}
|
||||
name={[record.name, "lbr_adjustment", "rate"]}
|
||||
initialValue={bodyshop.default_adjustment_rate}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
//message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<InputNumber precision={2} min={0.01} />
|
||||
</Form.Item>
|
||||
)}
|
||||
|
||||
<Space>
|
||||
{price &&
|
||||
adjustmentRate &&
|
||||
`${(price / adjustmentRate).toFixed(1)} hrs`}
|
||||
</Space>
|
||||
<Form.Item
|
||||
label={t("jobs.labels.adjustmentrate")}
|
||||
name={[record.name, "lbr_adjustment", "rate"]}
|
||||
initialValue={bodyshop.default_adjustment_rate}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
//message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<InputNumber precision={2} min={0.01} />
|
||||
</Form.Item>
|
||||
{price &&
|
||||
adjustmentRate &&
|
||||
`${(price / adjustmentRate).toFixed(1)} hrs`}
|
||||
</div>
|
||||
);
|
||||
return <></>;
|
||||
@@ -545,21 +458,22 @@ export function BillEnterModalLinesComponent({
|
||||
</Form.Item>
|
||||
),
|
||||
},
|
||||
// {
|
||||
// title: t("billlines.fields.federal_tax_applicable"),
|
||||
// dataIndex: "applicable_taxes.federal",
|
||||
// editable: true,
|
||||
{
|
||||
title: t("billlines.fields.federal_tax_applicable"),
|
||||
dataIndex: "applicable_taxes.federal",
|
||||
editable: true,
|
||||
|
||||
// formItemProps: (field) => {
|
||||
// return {
|
||||
// key: `${field.index}fedtax`,
|
||||
// valuePropName: "checked",
|
||||
// // initialValue: true,
|
||||
// name: [field.name, "applicable_taxes", "federal"],
|
||||
// };
|
||||
// },
|
||||
// formInput: (record, index) => <Switch disabled={disabled} />,
|
||||
// },
|
||||
formItemProps: (field) => {
|
||||
return {
|
||||
key: `${field.index}fedtax`,
|
||||
valuePropName: "checked",
|
||||
initialValue:
|
||||
form.getFieldValue("federal_tax_exempt") === true ? false : true,
|
||||
name: [field.name, "applicable_taxes", "federal"],
|
||||
};
|
||||
},
|
||||
formInput: (record, index) => <Switch disabled={disabled} />,
|
||||
},
|
||||
{
|
||||
title: t("billlines.fields.state_tax_applicable"),
|
||||
dataIndex: "applicable_taxes.state",
|
||||
@@ -574,20 +488,20 @@ export function BillEnterModalLinesComponent({
|
||||
},
|
||||
formInput: (record, index) => <Switch disabled={disabled} />,
|
||||
},
|
||||
// {
|
||||
// title: t("billlines.fields.local_tax_applicable"),
|
||||
// dataIndex: "applicable_taxes.local",
|
||||
// editable: true,
|
||||
{
|
||||
title: t("billlines.fields.local_tax_applicable"),
|
||||
dataIndex: "applicable_taxes.local",
|
||||
editable: true,
|
||||
|
||||
// formItemProps: (field) => {
|
||||
// return {
|
||||
// key: `${field.index}localtax`,
|
||||
// valuePropName: "checked",
|
||||
// name: [field.name, "applicable_taxes", "local"],
|
||||
// };
|
||||
// },
|
||||
// formInput: (record, index) => <Switch disabled={disabled} />,
|
||||
// },
|
||||
formItemProps: (field) => {
|
||||
return {
|
||||
key: `${field.index}localtax`,
|
||||
valuePropName: "checked",
|
||||
name: [field.name, "applicable_taxes", "local"],
|
||||
};
|
||||
},
|
||||
formInput: (record, index) => <Switch disabled={disabled} />,
|
||||
},
|
||||
{
|
||||
title: t("general.labels.actions"),
|
||||
|
||||
@@ -712,7 +626,7 @@ const EditableCell = ({
|
||||
if (additional)
|
||||
return (
|
||||
<td {...restProps}>
|
||||
<div size="small">
|
||||
<Space size="small">
|
||||
<Form.Item
|
||||
name={dataIndex}
|
||||
labelCol={{ span: 0 }}
|
||||
@@ -721,7 +635,7 @@ const EditableCell = ({
|
||||
{(formInput && formInput(record, record.name)) || children}
|
||||
</Form.Item>
|
||||
{additional && additional(record, record.name)}
|
||||
</div>
|
||||
</Space>
|
||||
</td>
|
||||
);
|
||||
if (wrapper)
|
||||
|
||||
@@ -19,14 +19,14 @@ export const CalculateBillTotal = (invoice) => {
|
||||
}).multiply(i.quantity || 1);
|
||||
|
||||
subtotal = subtotal.add(itemTotal);
|
||||
if (i.applicable_taxes?.federal) {
|
||||
if (i.applicable_taxes.federal) {
|
||||
federalTax = federalTax.add(
|
||||
itemTotal.percentage(federal_tax_rate || 0)
|
||||
);
|
||||
}
|
||||
if (i.applicable_taxes?.state)
|
||||
if (i.applicable_taxes.state)
|
||||
stateTax = stateTax.add(itemTotal.percentage(state_tax_rate || 0));
|
||||
if (i.applicable_taxes?.local)
|
||||
if (i.applicable_taxes.local)
|
||||
localTax = localTax.add(itemTotal.percentage(local_tax_rate || 0));
|
||||
}
|
||||
});
|
||||
|
||||
@@ -63,12 +63,6 @@ const BillLineSearchSelect = (
|
||||
item.oem_partno ? ` - ${item.oem_partno}` : ""
|
||||
}${item.alt_partno ? ` (${item.alt_partno})` : ""}`.trim()}
|
||||
</span>
|
||||
{item.act_price === 0 && item.mod_lb_hrs > 0 && (
|
||||
<span style={{ float: "right", paddingleft: "1rem" }}>
|
||||
{`${item.mod_lb_hrs} units`}
|
||||
</span>
|
||||
)}
|
||||
|
||||
<span style={{ float: "right", paddingleft: "1rem" }}>
|
||||
{item.act_price
|
||||
? `$${item.act_price && item.act_price.toFixed(2)}`
|
||||
|
||||
@@ -60,7 +60,7 @@ export function BillsListTableComponent({
|
||||
)}
|
||||
<BillDeleteButton bill={record} jobid={job.id} />
|
||||
<BillDetailEditReturnComponent
|
||||
data={{ bills_by_pk: { ...record, jobid: job.id } }}
|
||||
data={{ bills_by_pk: { ...record, jobid: job.id, job: job } }}
|
||||
disabled={
|
||||
record.is_credit_memo ||
|
||||
record.vendorid === bodyshop.inhousevendorid ||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import React, { useState } from "react";
|
||||
import { Button, Form, InputNumber, Popover } from "antd";
|
||||
import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { CalculatorFilled } from "@ant-design/icons";
|
||||
import { Button, Form, InputNumber, Popover, Space } from "antd";
|
||||
import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||
export default function CABCpvrtCalculator({ disabled, form }) {
|
||||
const [visibility, setVisibility] = useState(false);
|
||||
|
||||
@@ -26,10 +26,14 @@ export default function CABCpvrtCalculator({ disabled, form }) {
|
||||
<Form.Item name="days" label={t("jobs.labels.ca_bc_pvrt.days")}>
|
||||
<InputNumber precision={0} min={0} />
|
||||
</Form.Item>
|
||||
<Button type="primary" htmlType="submit">
|
||||
{t("general.actions.calculate")}
|
||||
</Button>
|
||||
<Button onClick={() => setVisibility(false)}>Close</Button>
|
||||
<div style={{ display: "flex", justifyContent: "flex-end" }}>
|
||||
<Space>
|
||||
<Button type="primary" htmlType="submit">
|
||||
{t("general.actions.calculate")}
|
||||
</Button>
|
||||
<Button onClick={() => setVisibility(false)}>Close</Button>
|
||||
</Space>
|
||||
</div>
|
||||
</Form>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -13,7 +13,6 @@ import {
|
||||
notification,
|
||||
} from "antd";
|
||||
import axios from "axios";
|
||||
import moment from "moment";
|
||||
import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
@@ -22,7 +21,6 @@ import {
|
||||
INSERT_PAYMENT_RESPONSE,
|
||||
QUERY_RO_AND_OWNER_BY_JOB_PKS,
|
||||
} from "../../graphql/payment_response.queries";
|
||||
import { INSERT_NEW_PAYMENT } from "../../graphql/payments.queries";
|
||||
import { insertAuditTrail } from "../../redux/application/application.actions";
|
||||
import { toggleModalVisible } from "../../redux/modals/modals.actions";
|
||||
import { selectCardPayment } from "../../redux/modals/modals.selectors";
|
||||
@@ -37,8 +35,8 @@ const mapStateToProps = createStructuredSelector({
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
insertAuditTrail: ({ jobid, operation }) =>
|
||||
dispatch(insertAuditTrail({ jobid, operation })),
|
||||
insertAuditTrail: ({ jobid, operation, type }) =>
|
||||
dispatch(insertAuditTrail({ jobid, operation, type })),
|
||||
toggleModalVisible: () => dispatch(toggleModalVisible("cardPayment")),
|
||||
});
|
||||
|
||||
@@ -48,12 +46,12 @@ const CardPaymentModalComponent = ({
|
||||
toggleModalVisible,
|
||||
insertAuditTrail,
|
||||
}) => {
|
||||
const { context } = cardPaymentModal;
|
||||
const { context, actions } = cardPaymentModal;
|
||||
|
||||
const [form] = Form.useForm();
|
||||
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [insertPayment] = useMutation(INSERT_NEW_PAYMENT);
|
||||
// const [insertPayment] = useMutation(INSERT_NEW_PAYMENT);
|
||||
const [insertPaymentResponse] = useMutation(INSERT_PAYMENT_RESPONSE);
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -65,7 +63,6 @@ const CardPaymentModalComponent = ({
|
||||
}
|
||||
);
|
||||
|
||||
console.log("🚀 ~ file: card-payment-modal.component..jsx:61 ~ data:", data);
|
||||
//Initialize the intellipay window.
|
||||
const SetIntellipayCallbackFunctions = () => {
|
||||
console.log("*** Set IntelliPay callback functions.");
|
||||
@@ -74,16 +71,20 @@ const CardPaymentModalComponent = ({
|
||||
});
|
||||
|
||||
window.intellipay.runOnApproval(async function (response) {
|
||||
console.warn("*** Running On Approval Script ***");
|
||||
form.setFieldValue("paymentResponse", response);
|
||||
form.submit();
|
||||
//2024-04-25: Nothing is going to happen here anymore. We'll completely rely on the callback.
|
||||
//Add a slight delay to allow the refetch to properly get the data.
|
||||
setTimeout(() => {
|
||||
if (actions && actions.refetch && typeof actions.refetch === "function")
|
||||
actions.refetch();
|
||||
setLoading(false);
|
||||
toggleModalVisible();
|
||||
}, 750);
|
||||
});
|
||||
|
||||
window.intellipay.runOnNonApproval(async function (response) {
|
||||
// Mutate unsuccessful payment
|
||||
|
||||
const { payments } = form.getFieldsValue();
|
||||
|
||||
await insertPaymentResponse({
|
||||
variables: {
|
||||
paymentResponse: payments.map((payment) => ({
|
||||
@@ -102,55 +103,15 @@ const CardPaymentModalComponent = ({
|
||||
insertAuditTrail({
|
||||
jobid: payment.jobid,
|
||||
operation: AuditTrailMapping.failedpayment(),
|
||||
type: "failedpayment",
|
||||
})
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
const handleFinish = async (values) => {
|
||||
try {
|
||||
await insertPayment({
|
||||
variables: {
|
||||
paymentInput: values.payments.map((payment) => ({
|
||||
amount: payment.amount,
|
||||
transactionid: (values.paymentResponse.paymentid || "").toString(),
|
||||
payer: t("payments.labels.customer"),
|
||||
type: values.paymentResponse.cardbrand,
|
||||
jobid: payment.jobid,
|
||||
date: moment(Date.now()),
|
||||
payment_responses: {
|
||||
data: [
|
||||
{
|
||||
amount: payment.amount,
|
||||
bodyshopid: bodyshop.id,
|
||||
|
||||
jobid: payment.jobid,
|
||||
declinereason: values.paymentResponse.declinereason,
|
||||
ext_paymentid: values.paymentResponse.paymentid.toString(),
|
||||
successful: true,
|
||||
response: values.paymentResponse,
|
||||
},
|
||||
],
|
||||
},
|
||||
})),
|
||||
},
|
||||
refetchQueries: ["GET_JOB_BY_PK"],
|
||||
});
|
||||
toggleModalVisible();
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
notification.open({
|
||||
type: "error",
|
||||
message: t("payments.errors.inserting", { error: error.message }),
|
||||
});
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleIntelliPayCharge = async () => {
|
||||
setLoading(true);
|
||||
|
||||
//Validate
|
||||
try {
|
||||
await form.validateFields();
|
||||
@@ -163,6 +124,7 @@ const CardPaymentModalComponent = ({
|
||||
const response = await axios.post("/intellipay/lightbox_credentials", {
|
||||
bodyshop,
|
||||
refresh: !!window.intellipay,
|
||||
paymentSplitMeta: form.getFieldsValue(),
|
||||
});
|
||||
|
||||
if (window.intellipay) {
|
||||
@@ -191,7 +153,6 @@ const CardPaymentModalComponent = ({
|
||||
<Card title="Card Payment">
|
||||
<Spin spinning={loading}>
|
||||
<Form
|
||||
onFinish={handleFinish}
|
||||
form={form}
|
||||
layout="vertical"
|
||||
initialValues={{
|
||||
@@ -272,23 +233,14 @@ const CardPaymentModalComponent = ({
|
||||
}
|
||||
>
|
||||
{() => {
|
||||
console.log("Updating the owner info section.");
|
||||
//If all of the job ids have been fileld in, then query and update the IP field.
|
||||
const { payments } = form.getFieldsValue();
|
||||
if (
|
||||
payments?.length > 0 &&
|
||||
payments?.filter((p) => p?.jobid).length === payments?.length
|
||||
) {
|
||||
console.log("**Calling refetch.");
|
||||
refetch({ jobids: payments.map((p) => p.jobid) });
|
||||
}
|
||||
console.log(
|
||||
"Acc info",
|
||||
data,
|
||||
payments && data && data.jobs.length > 0
|
||||
? data.jobs.map((j) => j.ro_number).join(", ")
|
||||
: null
|
||||
);
|
||||
return (
|
||||
<>
|
||||
<Input
|
||||
@@ -343,6 +295,13 @@ const CardPaymentModalComponent = ({
|
||||
value={totalAmountToCharge?.toFixed(2)}
|
||||
hidden
|
||||
/>
|
||||
<Input
|
||||
className="ipayfield"
|
||||
data-ipayname="comment"
|
||||
//type="hidden"
|
||||
value={btoa(JSON.stringify(payments))}
|
||||
hidden
|
||||
/>
|
||||
<Button
|
||||
type="primary"
|
||||
// data-ipayname="submit"
|
||||
@@ -357,11 +316,6 @@ const CardPaymentModalComponent = ({
|
||||
);
|
||||
}}
|
||||
</Form.Item>
|
||||
|
||||
{/* Lightbox payment response when it is completed */}
|
||||
<Form.Item name="paymentResponse" hidden>
|
||||
<Input type="hidden" />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Spin>
|
||||
</Card>
|
||||
|
||||
@@ -10,11 +10,15 @@ import CourtesyCarFuelSlider from "../courtesy-car-fuel-select/courtesy-car-fuel
|
||||
import CourtesyCarReadiness from "../courtesy-car-readiness-select/courtesy-car-readiness-select.component";
|
||||
import CourtesyCarStatus from "../courtesy-car-status-select/courtesy-car-status-select.component";
|
||||
import FormDatePicker from "../form-date-picker/form-date-picker.component";
|
||||
//import FormFieldsChanged from "../form-fields-changed-alert/form-fields-changed-alert.component";
|
||||
import FormFieldsChanged from "../form-fields-changed-alert/form-fields-changed-alert.component";
|
||||
import CurrencyInput from "../form-items-formatted/currency-form-item.component";
|
||||
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
||||
|
||||
export default function CourtesyCarCreateFormComponent({ form, saveLoading }) {
|
||||
export default function CourtesyCarCreateFormComponent({
|
||||
form,
|
||||
saveLoading,
|
||||
newCC,
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const client = useApolloClient();
|
||||
|
||||
@@ -33,7 +37,7 @@ export default function CourtesyCarCreateFormComponent({ form, saveLoading }) {
|
||||
}
|
||||
/>
|
||||
|
||||
{/* <FormFieldsChanged form={form} /> */}
|
||||
{newCC ? null : <FormFieldsChanged form={form} />}
|
||||
<LayoutFormRow header={t("courtesycars.labels.vehicle")}>
|
||||
<Form.Item
|
||||
label={t("courtesycars.fields.year")}
|
||||
|
||||
@@ -37,6 +37,9 @@ const CourtesyCarStatusComponent = ({ value, onChange }, ref) => {
|
||||
<Option value="courtesycars.status.leasereturn">
|
||||
{t("courtesycars.status.leasereturn")}
|
||||
</Option>
|
||||
<Option value="courtesycars.status.unavailable">
|
||||
{t("courtesycars.status.unavailable")}
|
||||
</Option>
|
||||
</Select>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -61,6 +61,10 @@ export default function CourtesyCarsList({ loading, courtesycars, refetch }) {
|
||||
text: t("courtesycars.status.in"),
|
||||
value: "courtesycars.status.in",
|
||||
},
|
||||
{
|
||||
text: t("courtesycars.status.inservice"),
|
||||
value: "courtesycars.status.inservice",
|
||||
},
|
||||
{
|
||||
text: t("courtesycars.status.out"),
|
||||
value: "courtesycars.status.out",
|
||||
@@ -73,8 +77,12 @@ export default function CourtesyCarsList({ loading, courtesycars, refetch }) {
|
||||
text: t("courtesycars.status.leasereturn"),
|
||||
value: "courtesycars.status.leasereturn",
|
||||
},
|
||||
{
|
||||
text: t("courtesycars.status.unavailable"),
|
||||
value: "courtesycars.status.unavailable",
|
||||
},
|
||||
],
|
||||
onFilter: (value, record) => value.includes(record.status),
|
||||
onFilter: (value, record) => record.status === value,
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "status" && state.sortedInfo.order,
|
||||
render: (text, record) => {
|
||||
@@ -178,7 +186,7 @@ export default function CourtesyCarsList({ loading, courtesycars, refetch }) {
|
||||
title: t("courtesycars.fields.fuel"),
|
||||
dataIndex: "fuel",
|
||||
key: "fuel",
|
||||
sorter: (a, b) => alphaSort(a.fuel, b.fuel),
|
||||
sorter: (a, b) => a.fuel - b.fuel,
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "fuel" && state.sortedInfo.order,
|
||||
render: (text, record) => {
|
||||
@@ -187,12 +195,14 @@ export default function CourtesyCarsList({ loading, courtesycars, refetch }) {
|
||||
return t("courtesycars.labels.fuel.full");
|
||||
case 88:
|
||||
return t("courtesycars.labels.fuel.78");
|
||||
case 75:
|
||||
return t("courtesycars.labels.fuel.34");
|
||||
case 63:
|
||||
return t("courtesycars.labels.fuel.58");
|
||||
case 50:
|
||||
return t("courtesycars.labels.fuel.12");
|
||||
case 38:
|
||||
return t("courtesycars.labels.fuel.34");
|
||||
return t("courtesycars.labels.fuel.38");
|
||||
case 25:
|
||||
return t("courtesycars.labels.fuel.14");
|
||||
case 13:
|
||||
|
||||
@@ -0,0 +1,169 @@
|
||||
import {Card, Table, Tag} from "antd";
|
||||
import LoadingSkeleton from "../../loading-skeleton/loading-skeleton.component";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import React, {useEffect, useState} from "react";
|
||||
import moment from "moment";
|
||||
import DashboardRefreshRequired from "../refresh-required.component";
|
||||
import axios from "axios";
|
||||
|
||||
const fortyFiveDaysAgo = () => moment().subtract(45, 'days').toLocaleString();
|
||||
|
||||
export default function JobLifecycleDashboardComponent({data, bodyshop, ...cardProps}) {
|
||||
const {t} = useTranslation();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [lifecycleData, setLifecycleData] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
async function getLifecycleData() {
|
||||
if (data && data.job_lifecycle) {
|
||||
setLoading(true);
|
||||
const response = await axios.post("/job/lifecycle", {
|
||||
jobids: data.job_lifecycle.map(x => x.id),
|
||||
statuses: bodyshop.md_ro_statuses
|
||||
});
|
||||
setLifecycleData(response.data.durations);
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
getLifecycleData().catch(e => {
|
||||
console.error(`Error in getLifecycleData: ${e}`);
|
||||
})
|
||||
}, [data, bodyshop]);
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: t('job_lifecycle.columns.status'),
|
||||
dataIndex: 'status',
|
||||
bgColor: 'red',
|
||||
key: 'status',
|
||||
render: (text, record) => {
|
||||
return <Tag color={record.color}>{record.status}</Tag>
|
||||
}
|
||||
},
|
||||
{
|
||||
title: t('job_lifecycle.columns.human_readable'),
|
||||
dataIndex: 'humanReadable',
|
||||
key: 'humanReadable',
|
||||
},
|
||||
{
|
||||
title: t('job_lifecycle.columns.status_count'),
|
||||
key: 'statusCount',
|
||||
render: (text, record) => {
|
||||
return lifecycleData.statusCounts[record.status];
|
||||
}
|
||||
},
|
||||
{
|
||||
title: t('job_lifecycle.columns.percentage'),
|
||||
dataIndex: 'percentage',
|
||||
key: 'percentage',
|
||||
render: (text, record) => {
|
||||
return record.percentage.toFixed(2) + '%';
|
||||
}
|
||||
},
|
||||
];
|
||||
|
||||
if (!data) return null;
|
||||
|
||||
if (!data.job_lifecycle || !lifecycleData) return <DashboardRefreshRequired {...cardProps} />;
|
||||
|
||||
const extra = `${t('job_lifecycle.content.calculated_based_on')} ${lifecycleData.jobs} ${t('job_lifecycle.content.jobs_in_since')} ${fortyFiveDaysAgo()}`
|
||||
|
||||
return (
|
||||
<Card title={t("job_lifecycle.titles.dashboard")} {...cardProps}>
|
||||
<LoadingSkeleton loading={loading}>
|
||||
<div style={{overflow: 'scroll', height: "100%"}}>
|
||||
<div id="bar-container" style={{
|
||||
display: 'flex',
|
||||
width: '100%',
|
||||
height: '100px',
|
||||
textAlign: 'center',
|
||||
borderRadius: '5px',
|
||||
borderWidth: '5px',
|
||||
borderStyle: 'solid',
|
||||
borderColor: '#f0f2f5',
|
||||
margin: 0,
|
||||
padding: 0
|
||||
}}>
|
||||
{lifecycleData.summations.map((key, index, array) => {
|
||||
const isFirst = index === 0;
|
||||
const isLast = index === array.length - 1;
|
||||
return (
|
||||
<div key={key.status} style={{
|
||||
overflow: 'hidden',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
margin: 0,
|
||||
padding: 0,
|
||||
|
||||
borderTop: '1px solid #f0f2f5',
|
||||
borderBottom: '1px solid #f0f2f5',
|
||||
borderLeft: isFirst ? '1px solid #f0f2f5' : undefined,
|
||||
borderRight: isLast ? '1px solid #f0f2f5' : undefined,
|
||||
|
||||
backgroundColor: key.color,
|
||||
width: `${key.percentage}%`
|
||||
}}
|
||||
aria-label={`${key.status} | ${key.roundedPercentage} | ${key.humanReadable}`}
|
||||
title={`${key.status} | ${key.roundedPercentage} | ${key.humanReadable}`}
|
||||
>
|
||||
|
||||
{key.percentage > 15 ?
|
||||
<>
|
||||
<div>{key.roundedPercentage}</div>
|
||||
<div style={{
|
||||
backgroundColor: '#f0f2f5',
|
||||
borderRadius: '5px',
|
||||
paddingRight: '2px',
|
||||
paddingLeft: '2px',
|
||||
fontSize: '0.8rem',
|
||||
}}>
|
||||
{key.status}
|
||||
</div>
|
||||
</>
|
||||
: null}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<Card extra={extra} type='inner' title={t('job_lifecycle.content.legend_title')}
|
||||
style={{marginTop: '10px'}}>
|
||||
<div>
|
||||
{lifecycleData.summations.map((key) => (
|
||||
<Tag color={key.color} style={{width: '13vh', padding: '4px', margin: '4px'}}>
|
||||
<div
|
||||
aria-label={`${key.status} | ${key.roundedPercentage} | ${key.humanReadable}`}
|
||||
title={`${key.status} | ${key.roundedPercentage} | ${key.humanReadable}`}
|
||||
style={{
|
||||
backgroundColor: '#f0f2f5',
|
||||
color: '#000',
|
||||
padding: '4px',
|
||||
textAlign: 'center'
|
||||
}}>
|
||||
{key.status} [{lifecycleData.statusCounts[key.status]}] ({key.roundedPercentage})
|
||||
</div>
|
||||
</Tag>
|
||||
))}
|
||||
</div>
|
||||
</Card>
|
||||
<Card style={{marginTop: "5px"}} type='inner' title={t("job_lifecycle.titles.top_durations")}>
|
||||
<Table size="small" pagination={false} columns={columns}
|
||||
dataSource={lifecycleData.summations.sort((a, b) => b.value - a.value).slice(0, 3)}/>
|
||||
</Card>
|
||||
</div>
|
||||
</LoadingSkeleton>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
export const JobLifecycleDashboardGQL = `
|
||||
job_lifecycle: jobs(where: {
|
||||
actual_in: {
|
||||
_gte: "${moment().subtract(45, 'days').toISOString()}"
|
||||
}
|
||||
}) {
|
||||
id
|
||||
actual_in
|
||||
} `;
|
||||
@@ -1,380 +1,391 @@
|
||||
import Icon, { SyncOutlined } from "@ant-design/icons";
|
||||
import { gql, useMutation, useQuery } from "@apollo/client";
|
||||
import { Button, Dropdown, Menu, PageHeader, Space, notification } from "antd";
|
||||
import Icon, {SyncOutlined} from "@ant-design/icons";
|
||||
import {gql, useMutation, useQuery} from "@apollo/client";
|
||||
import {Button, Dropdown, Menu, notification, PageHeader, Space} from "antd";
|
||||
import i18next from "i18next";
|
||||
import _ from "lodash";
|
||||
import moment from "moment";
|
||||
import React, { useState } from "react";
|
||||
import { Responsive, WidthProvider } from "react-grid-layout";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { MdClose } from "react-icons/md";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||
import { UPDATE_DASHBOARD_LAYOUT } from "../../graphql/user.queries";
|
||||
import {
|
||||
selectBodyshop,
|
||||
selectCurrentUser,
|
||||
} from "../../redux/user/user.selectors";
|
||||
import React, {useState} from "react";
|
||||
import {Responsive, WidthProvider} from "react-grid-layout";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {MdClose} from "react-icons/md";
|
||||
import {connect} from "react-redux";
|
||||
import {createStructuredSelector} from "reselect";
|
||||
import {logImEXEvent} from "../../firebase/firebase.utils";
|
||||
import {UPDATE_DASHBOARD_LAYOUT} from "../../graphql/user.queries";
|
||||
import {selectBodyshop, selectCurrentUser,} from "../../redux/user/user.selectors";
|
||||
import AlertComponent from "../alert/alert.component";
|
||||
import DashboardMonthlyEmployeeEfficiency, {
|
||||
DashboardMonthlyEmployeeEfficiencyGql,
|
||||
DashboardMonthlyEmployeeEfficiencyGql,
|
||||
} from "../dashboard-components/monthly-employee-efficiency/monthly-employee-efficiency.component";
|
||||
import DashboardMonthlyJobCosting from "../dashboard-components/monthly-job-costing/monthly-job-costing.component";
|
||||
import DashboardMonthlyLaborSales from "../dashboard-components/monthly-labor-sales/monthly-labor-sales.component";
|
||||
import DashboardMonthlyPartsSales from "../dashboard-components/monthly-parts-sales/monthly-parts-sales.component";
|
||||
import DashboardMonthlyRevenueGraph, {
|
||||
DashboardMonthlyRevenueGraphGql,
|
||||
DashboardMonthlyRevenueGraphGql,
|
||||
} from "../dashboard-components/monthly-revenue-graph/monthly-revenue-graph.component";
|
||||
import DashboardProjectedMonthlySales, {
|
||||
DashboardProjectedMonthlySalesGql,
|
||||
DashboardProjectedMonthlySalesGql,
|
||||
} from "../dashboard-components/pojected-monthly-sales/projected-monthly-sales.component";
|
||||
import DashboardTotalProductionDollars from "../dashboard-components/total-production-dollars/total-production-dollars.component";
|
||||
import DashboardTotalProductionDollars
|
||||
from "../dashboard-components/total-production-dollars/total-production-dollars.component";
|
||||
import DashboardTotalProductionHours, {
|
||||
DashboardTotalProductionHoursGql,
|
||||
DashboardTotalProductionHoursGql,
|
||||
} from "../dashboard-components/total-production-hours/total-production-hours.component";
|
||||
import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component";
|
||||
//Combination of the following:
|
||||
// /node_modules/react-grid-layout/css/styles.css
|
||||
// /node_modules/react-resizable/css/styles.css
|
||||
import DashboardScheduledInToday, {
|
||||
DashboardScheduledInTodayGql,
|
||||
DashboardScheduledInTodayGql,
|
||||
} from "../dashboard-components/scheduled-in-today/scheduled-in-today.component";
|
||||
import DashboardScheduledOutToday, {
|
||||
DashboardScheduledOutTodayGql,
|
||||
DashboardScheduledOutTodayGql,
|
||||
} from "../dashboard-components/scheduled-out-today/scheduled-out-today.component";
|
||||
import JobLifecycleDashboardComponent, {
|
||||
JobLifecycleDashboardGQL
|
||||
} from "../dashboard-components/job-lifecycle/job-lifecycle-dashboard.component";
|
||||
import "./dashboard-grid.styles.scss";
|
||||
import { GenerateDashboardData } from "./dashboard-grid.utils";
|
||||
import {GenerateDashboardData} from "./dashboard-grid.utils";
|
||||
|
||||
const ResponsiveReactGridLayout = WidthProvider(Responsive);
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
currentUser: selectCurrentUser,
|
||||
bodyshop: selectBodyshop,
|
||||
currentUser: selectCurrentUser,
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||
});
|
||||
|
||||
export function DashboardGridComponent({ currentUser, bodyshop }) {
|
||||
const { t } = useTranslation();
|
||||
const [state, setState] = useState({
|
||||
...(bodyshop.associations[0].user.dashboardlayout
|
||||
? bodyshop.associations[0].user.dashboardlayout
|
||||
: { items: [], layout: {}, layouts: [] }),
|
||||
});
|
||||
|
||||
const { loading, error, data, refetch } = useQuery(
|
||||
createDashboardQuery(state),
|
||||
{ fetchPolicy: "network-only", nextFetchPolicy: "network-only" }
|
||||
);
|
||||
|
||||
const [updateLayout] = useMutation(UPDATE_DASHBOARD_LAYOUT);
|
||||
|
||||
const handleLayoutChange = async (layout, layouts) => {
|
||||
logImEXEvent("dashboard_change_layout");
|
||||
|
||||
setState({ ...state, layout, layouts });
|
||||
|
||||
const result = await updateLayout({
|
||||
variables: {
|
||||
email: currentUser.email,
|
||||
layout: { ...state, layout, layouts },
|
||||
},
|
||||
export function DashboardGridComponent({currentUser, bodyshop}) {
|
||||
const {t} = useTranslation();
|
||||
const [state, setState] = useState({
|
||||
...(bodyshop.associations[0].user.dashboardlayout
|
||||
? bodyshop.associations[0].user.dashboardlayout
|
||||
: {items: [], layout: {}, layouts: []}),
|
||||
});
|
||||
if (!!result.errors) {
|
||||
notification["error"]({
|
||||
message: t("dashboard.errors.updatinglayout", {
|
||||
message: JSON.stringify(result.errors),
|
||||
}),
|
||||
});
|
||||
}
|
||||
};
|
||||
const handleRemoveComponent = (key) => {
|
||||
logImEXEvent("dashboard_remove_component", { name: key });
|
||||
const idxToRemove = state.items.findIndex((i) => i.i === key);
|
||||
|
||||
const items = _.cloneDeep(state.items);
|
||||
const {loading, error, data, refetch} = useQuery(
|
||||
createDashboardQuery(state),
|
||||
{fetchPolicy: "network-only", nextFetchPolicy: "network-only"}
|
||||
);
|
||||
|
||||
items.splice(idxToRemove, 1);
|
||||
setState({ ...state, items });
|
||||
};
|
||||
const [updateLayout] = useMutation(UPDATE_DASHBOARD_LAYOUT);
|
||||
|
||||
const handleAddComponent = (e) => {
|
||||
logImEXEvent("dashboard_add_component", { name: e });
|
||||
setState({
|
||||
...state,
|
||||
items: [
|
||||
...state.items,
|
||||
{
|
||||
i: e.key,
|
||||
x: (state.items.length * 2) % (state.cols || 12),
|
||||
y: 99, // puts it at the bottom
|
||||
w: componentList[e.key].w || 2,
|
||||
h: componentList[e.key].h || 2,
|
||||
},
|
||||
],
|
||||
});
|
||||
};
|
||||
const handleLayoutChange = async (layout, layouts) => {
|
||||
logImEXEvent("dashboard_change_layout");
|
||||
|
||||
const dashboarddata = React.useMemo(
|
||||
() => GenerateDashboardData(data),
|
||||
[data]
|
||||
);
|
||||
const existingLayoutKeys = state.items.map((i) => i.i);
|
||||
const addComponentOverlay = (
|
||||
<Menu onClick={handleAddComponent}>
|
||||
{Object.keys(componentList).map((key) => (
|
||||
<Menu.Item
|
||||
key={key}
|
||||
value={key}
|
||||
disabled={existingLayoutKeys.includes(key)}
|
||||
>
|
||||
{componentList[key].label}
|
||||
</Menu.Item>
|
||||
))}
|
||||
</Menu>
|
||||
);
|
||||
setState({...state, layout, layouts});
|
||||
|
||||
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<PageHeader
|
||||
extra={
|
||||
<Space>
|
||||
<Button onClick={() => refetch()}>
|
||||
<SyncOutlined />
|
||||
</Button>
|
||||
<Dropdown overlay={addComponentOverlay} trigger={["click"]}>
|
||||
<Button>{t("dashboard.actions.addcomponent")}</Button>
|
||||
</Dropdown>
|
||||
</Space>
|
||||
const result = await updateLayout({
|
||||
variables: {
|
||||
email: currentUser.email,
|
||||
layout: {...state, layout, layouts},
|
||||
},
|
||||
});
|
||||
if (!!result.errors) {
|
||||
notification["error"]({
|
||||
message: t("dashboard.errors.updatinglayout", {
|
||||
message: JSON.stringify(result.errors),
|
||||
}),
|
||||
});
|
||||
}
|
||||
/>
|
||||
};
|
||||
const handleRemoveComponent = (key) => {
|
||||
logImEXEvent("dashboard_remove_component", {name: key});
|
||||
const idxToRemove = state.items.findIndex((i) => i.i === key);
|
||||
|
||||
<ResponsiveReactGridLayout
|
||||
className="layout"
|
||||
breakpoints={{ lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0 }}
|
||||
cols={{ lg: 12, md: 10, sm: 6, xs: 4, xxs: 2 }}
|
||||
width="100%"
|
||||
layouts={state.layouts}
|
||||
onLayoutChange={handleLayoutChange}
|
||||
// onBreakpointChange={onBreakpointChange}
|
||||
>
|
||||
{state.items.map((item, index) => {
|
||||
const TheComponent = componentList[item.i].component;
|
||||
return (
|
||||
<div
|
||||
key={item.i}
|
||||
data-grid={{
|
||||
...item,
|
||||
minH: componentList[item.i].minH || 1,
|
||||
minW: componentList[item.i].minW || 1,
|
||||
}}
|
||||
const items = _.cloneDeep(state.items);
|
||||
|
||||
items.splice(idxToRemove, 1);
|
||||
setState({...state, items});
|
||||
};
|
||||
|
||||
const handleAddComponent = (e) => {
|
||||
logImEXEvent("dashboard_add_component", {name: e});
|
||||
setState({
|
||||
...state,
|
||||
items: [
|
||||
...state.items,
|
||||
{
|
||||
i: e.key,
|
||||
x: (state.items.length * 2) % (state.cols || 12),
|
||||
y: 99, // puts it at the bottom
|
||||
w: componentList[e.key].w || 2,
|
||||
h: componentList[e.key].h || 2,
|
||||
},
|
||||
],
|
||||
});
|
||||
};
|
||||
|
||||
const dashboarddata = React.useMemo(
|
||||
() => GenerateDashboardData(data),
|
||||
[data]
|
||||
);
|
||||
const existingLayoutKeys = state.items.map((i) => i.i);
|
||||
const addComponentOverlay = (
|
||||
<Menu onClick={handleAddComponent}>
|
||||
{Object.keys(componentList).map((key) => (
|
||||
<Menu.Item
|
||||
key={key}
|
||||
value={key}
|
||||
disabled={existingLayoutKeys.includes(key)}
|
||||
>
|
||||
{componentList[key].label}
|
||||
</Menu.Item>
|
||||
))}
|
||||
</Menu>
|
||||
);
|
||||
|
||||
if (error) return <AlertComponent message={error.message} type="error"/>;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<PageHeader
|
||||
extra={
|
||||
<Space>
|
||||
<Button onClick={() => refetch()}>
|
||||
<SyncOutlined/>
|
||||
</Button>
|
||||
<Dropdown overlay={addComponentOverlay} trigger={["click"]}>
|
||||
<Button>{t("dashboard.actions.addcomponent")}</Button>
|
||||
</Dropdown>
|
||||
</Space>
|
||||
}
|
||||
/>
|
||||
|
||||
<ResponsiveReactGridLayout
|
||||
className="layout"
|
||||
breakpoints={{lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0}}
|
||||
cols={{lg: 12, md: 10, sm: 6, xs: 4, xxs: 2}}
|
||||
width="100%"
|
||||
layouts={state.layouts}
|
||||
onLayoutChange={handleLayoutChange}
|
||||
// onBreakpointChange={onBreakpointChange}
|
||||
>
|
||||
<LoadingSkeleton loading={loading}>
|
||||
<Icon
|
||||
component={MdClose}
|
||||
key={item.i}
|
||||
style={{
|
||||
position: "absolute",
|
||||
zIndex: "2",
|
||||
right: ".25rem",
|
||||
top: ".25rem",
|
||||
cursor: "pointer",
|
||||
}}
|
||||
onClick={() => handleRemoveComponent(item.i)}
|
||||
/>
|
||||
<TheComponent className="dashboard-card" data={dashboarddata} />
|
||||
</LoadingSkeleton>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</ResponsiveReactGridLayout>
|
||||
</div>
|
||||
);
|
||||
{state.items.map((item, index) => {
|
||||
const TheComponent = componentList[item.i].component;
|
||||
return (
|
||||
<div
|
||||
key={item.i}
|
||||
data-grid={{
|
||||
...item,
|
||||
minH: componentList[item.i].minH || 1,
|
||||
minW: componentList[item.i].minW || 1,
|
||||
}}
|
||||
>
|
||||
<LoadingSkeleton loading={loading}>
|
||||
<Icon
|
||||
component={MdClose}
|
||||
key={item.i}
|
||||
style={{
|
||||
position: "absolute",
|
||||
zIndex: "2",
|
||||
right: ".25rem",
|
||||
top: ".25rem",
|
||||
cursor: "pointer",
|
||||
}}
|
||||
onClick={() => handleRemoveComponent(item.i)}
|
||||
/>
|
||||
<TheComponent className="dashboard-card" bodyshop={bodyshop} data={dashboarddata}/>
|
||||
</LoadingSkeleton>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</ResponsiveReactGridLayout>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(DashboardGridComponent);
|
||||
|
||||
const componentList = {
|
||||
ProductionDollars: {
|
||||
label: i18next.t("dashboard.titles.productiondollars"),
|
||||
component: DashboardTotalProductionDollars,
|
||||
gqlFragment: null,
|
||||
w: 1,
|
||||
h: 1,
|
||||
minW: 2,
|
||||
minH: 1,
|
||||
},
|
||||
ProductionHours: {
|
||||
label: i18next.t("dashboard.titles.productionhours"),
|
||||
component: DashboardTotalProductionHours,
|
||||
gqlFragment: DashboardTotalProductionHoursGql,
|
||||
w: 3,
|
||||
h: 1,
|
||||
minW: 3,
|
||||
minH: 1,
|
||||
},
|
||||
ProjectedMonthlySales: {
|
||||
label: i18next.t("dashboard.titles.projectedmonthlysales"),
|
||||
component: DashboardProjectedMonthlySales,
|
||||
gqlFragment: DashboardProjectedMonthlySalesGql,
|
||||
w: 2,
|
||||
h: 1,
|
||||
minW: 2,
|
||||
minH: 1,
|
||||
},
|
||||
MonthlyRevenueGraph: {
|
||||
label: i18next.t("dashboard.titles.monthlyrevenuegraph"),
|
||||
component: DashboardMonthlyRevenueGraph,
|
||||
gqlFragment: DashboardMonthlyRevenueGraphGql,
|
||||
w: 4,
|
||||
h: 2,
|
||||
minW: 4,
|
||||
minH: 2,
|
||||
},
|
||||
MonthlyJobCosting: {
|
||||
label: i18next.t("dashboard.titles.monthlyjobcosting"),
|
||||
component: DashboardMonthlyJobCosting,
|
||||
gqlFragment: null,
|
||||
minW: 6,
|
||||
minH: 3,
|
||||
w: 6,
|
||||
h: 3,
|
||||
},
|
||||
MonthlyPartsSales: {
|
||||
label: i18next.t("dashboard.titles.monthlypartssales"),
|
||||
component: DashboardMonthlyPartsSales,
|
||||
gqlFragment: null,
|
||||
minW: 2,
|
||||
minH: 2,
|
||||
w: 2,
|
||||
h: 2,
|
||||
},
|
||||
MonthlyLaborSales: {
|
||||
label: i18next.t("dashboard.titles.monthlylaborsales"),
|
||||
component: DashboardMonthlyLaborSales,
|
||||
gqlFragment: null,
|
||||
minW: 2,
|
||||
minH: 2,
|
||||
w: 2,
|
||||
h: 2,
|
||||
},
|
||||
MonthlyEmployeeEfficency: {
|
||||
label: i18next.t("dashboard.titles.monthlyemployeeefficiency"),
|
||||
component: DashboardMonthlyEmployeeEfficiency,
|
||||
gqlFragment: DashboardMonthlyEmployeeEfficiencyGql,
|
||||
minW: 2,
|
||||
minH: 2,
|
||||
w: 2,
|
||||
h: 2,
|
||||
},
|
||||
ScheduleInToday: {
|
||||
label: i18next.t("dashboard.titles.scheduledintoday"),
|
||||
component: DashboardScheduledInToday,
|
||||
gqlFragment: DashboardScheduledInTodayGql,
|
||||
minW: 6,
|
||||
minH: 2,
|
||||
w: 10,
|
||||
h: 3,
|
||||
},
|
||||
ScheduleOutToday: {
|
||||
label: i18next.t("dashboard.titles.scheduledouttoday"),
|
||||
component: DashboardScheduledOutToday,
|
||||
gqlFragment: DashboardScheduledOutTodayGql,
|
||||
minW: 6,
|
||||
minH: 2,
|
||||
w: 10,
|
||||
h: 3,
|
||||
},
|
||||
ProductionDollars: {
|
||||
label: i18next.t("dashboard.titles.productiondollars"),
|
||||
component: DashboardTotalProductionDollars,
|
||||
gqlFragment: null,
|
||||
w: 1,
|
||||
h: 1,
|
||||
minW: 2,
|
||||
minH: 1,
|
||||
},
|
||||
ProductionHours: {
|
||||
label: i18next.t("dashboard.titles.productionhours"),
|
||||
component: DashboardTotalProductionHours,
|
||||
gqlFragment: DashboardTotalProductionHoursGql,
|
||||
w: 3,
|
||||
h: 1,
|
||||
minW: 3,
|
||||
minH: 1,
|
||||
},
|
||||
ProjectedMonthlySales: {
|
||||
label: i18next.t("dashboard.titles.projectedmonthlysales"),
|
||||
component: DashboardProjectedMonthlySales,
|
||||
gqlFragment: DashboardProjectedMonthlySalesGql,
|
||||
w: 2,
|
||||
h: 1,
|
||||
minW: 2,
|
||||
minH: 1,
|
||||
},
|
||||
MonthlyRevenueGraph: {
|
||||
label: i18next.t("dashboard.titles.monthlyrevenuegraph"),
|
||||
component: DashboardMonthlyRevenueGraph,
|
||||
gqlFragment: DashboardMonthlyRevenueGraphGql,
|
||||
w: 4,
|
||||
h: 2,
|
||||
minW: 4,
|
||||
minH: 2,
|
||||
},
|
||||
MonthlyJobCosting: {
|
||||
label: i18next.t("dashboard.titles.monthlyjobcosting"),
|
||||
component: DashboardMonthlyJobCosting,
|
||||
gqlFragment: null,
|
||||
minW: 6,
|
||||
minH: 3,
|
||||
w: 6,
|
||||
h: 3,
|
||||
},
|
||||
MonthlyPartsSales: {
|
||||
label: i18next.t("dashboard.titles.monthlypartssales"),
|
||||
component: DashboardMonthlyPartsSales,
|
||||
gqlFragment: null,
|
||||
minW: 2,
|
||||
minH: 2,
|
||||
w: 2,
|
||||
h: 2,
|
||||
},
|
||||
MonthlyLaborSales: {
|
||||
label: i18next.t("dashboard.titles.monthlylaborsales"),
|
||||
component: DashboardMonthlyLaborSales,
|
||||
gqlFragment: null,
|
||||
minW: 2,
|
||||
minH: 2,
|
||||
w: 2,
|
||||
h: 2,
|
||||
},
|
||||
// Typo in Efficency should be Efficiency, but changing it would reset users dashboard settings
|
||||
MonthlyEmployeeEfficency: {
|
||||
label: i18next.t("dashboard.titles.monthlyemployeeefficiency"),
|
||||
component: DashboardMonthlyEmployeeEfficiency,
|
||||
gqlFragment: DashboardMonthlyEmployeeEfficiencyGql,
|
||||
minW: 2,
|
||||
minH: 2,
|
||||
w: 2,
|
||||
h: 2,
|
||||
},
|
||||
ScheduleInToday: {
|
||||
label: i18next.t("dashboard.titles.scheduledintoday"),
|
||||
component: DashboardScheduledInToday,
|
||||
gqlFragment: DashboardScheduledInTodayGql,
|
||||
minW: 6,
|
||||
minH: 2,
|
||||
w: 10,
|
||||
h: 3,
|
||||
},
|
||||
ScheduleOutToday: {
|
||||
label: i18next.t("dashboard.titles.scheduledouttoday"),
|
||||
component: DashboardScheduledOutToday,
|
||||
gqlFragment: DashboardScheduledOutTodayGql,
|
||||
minW: 6,
|
||||
minH: 2,
|
||||
w: 10,
|
||||
h: 3,
|
||||
},
|
||||
JobLifecycle: {
|
||||
label: i18next.t("dashboard.titles.joblifecycle"),
|
||||
component: JobLifecycleDashboardComponent,
|
||||
gqlFragment: JobLifecycleDashboardGQL,
|
||||
minW: 6,
|
||||
minH: 3,
|
||||
w: 6,
|
||||
h: 3,
|
||||
},
|
||||
};
|
||||
|
||||
const createDashboardQuery = (state) => {
|
||||
const componentBasedAdditions =
|
||||
state &&
|
||||
Array.isArray(state.layout) &&
|
||||
state.layout
|
||||
.map((item, index) => componentList[item.i].gqlFragment || "")
|
||||
.join("");
|
||||
return gql`
|
||||
query QUERY_DASHBOARD_DETAILS { ${componentBasedAdditions || ""}
|
||||
monthly_sales: jobs(where: {_and: [
|
||||
{ voided: {_eq: false}},
|
||||
{date_invoiced: {_gte: "${moment()
|
||||
const componentBasedAdditions =
|
||||
state &&
|
||||
Array.isArray(state.layout) &&
|
||||
state.layout
|
||||
.map((item, index) => componentList[item.i].gqlFragment || "")
|
||||
.join("");
|
||||
return gql`
|
||||
query QUERY_DASHBOARD_DETAILS { ${componentBasedAdditions || ""}
|
||||
monthly_sales: jobs(where: {_and: [
|
||||
{ voided: {_eq: false}},
|
||||
{date_invoiced: {_gte: "${moment()
|
||||
.startOf("month")
|
||||
.startOf("day")
|
||||
.toISOString()}"}}, {date_invoiced: {_lte: "${moment()
|
||||
.startOf("day")
|
||||
.toISOString()}"}}, {date_invoiced: {_lte: "${moment()
|
||||
.endOf("month")
|
||||
.endOf("day")
|
||||
.toISOString()}"}}]}) {
|
||||
id
|
||||
ro_number
|
||||
date_invoiced
|
||||
job_totals
|
||||
rate_la1
|
||||
rate_la2
|
||||
rate_la3
|
||||
rate_la4
|
||||
rate_laa
|
||||
rate_lab
|
||||
rate_lad
|
||||
rate_lae
|
||||
rate_laf
|
||||
rate_lag
|
||||
rate_lam
|
||||
rate_lar
|
||||
rate_las
|
||||
rate_lau
|
||||
rate_ma2s
|
||||
rate_ma2t
|
||||
rate_ma3s
|
||||
rate_mabl
|
||||
rate_macs
|
||||
rate_mahw
|
||||
rate_mapa
|
||||
rate_mash
|
||||
rate_matd
|
||||
joblines(where: { removed: { _eq: false } }) {
|
||||
id
|
||||
mod_lbr_ty
|
||||
mod_lb_hrs
|
||||
act_price
|
||||
part_qty
|
||||
part_type
|
||||
}
|
||||
}
|
||||
production_jobs: jobs(where: { inproduction: { _eq: true } }) {
|
||||
id
|
||||
ro_number
|
||||
ins_co_nm
|
||||
job_totals
|
||||
joblines(where: { removed: { _eq: false } }) {
|
||||
id
|
||||
mod_lbr_ty
|
||||
mod_lb_hrs
|
||||
act_price
|
||||
part_qty
|
||||
part_type
|
||||
}
|
||||
labhrs: joblines_aggregate(where: { mod_lbr_ty: { _neq: "LAR" }, removed: { _eq: false } }) {
|
||||
aggregate {
|
||||
sum {
|
||||
mod_lb_hrs
|
||||
}
|
||||
}
|
||||
}
|
||||
larhrs: joblines_aggregate(where: { mod_lbr_ty: { _eq: "LAR" }, removed: { _eq: false } }) {
|
||||
aggregate {
|
||||
sum {
|
||||
mod_lb_hrs
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}`;
|
||||
ro_number
|
||||
date_invoiced
|
||||
job_totals
|
||||
rate_la1
|
||||
rate_la2
|
||||
rate_la3
|
||||
rate_la4
|
||||
rate_laa
|
||||
rate_lab
|
||||
rate_lad
|
||||
rate_lae
|
||||
rate_laf
|
||||
rate_lag
|
||||
rate_lam
|
||||
rate_lar
|
||||
rate_las
|
||||
rate_lau
|
||||
rate_ma2s
|
||||
rate_ma2t
|
||||
rate_ma3s
|
||||
rate_mabl
|
||||
rate_macs
|
||||
rate_mahw
|
||||
rate_mapa
|
||||
rate_mash
|
||||
rate_matd
|
||||
joblines(where: { removed: { _eq: false } }) {
|
||||
id
|
||||
mod_lbr_ty
|
||||
mod_lb_hrs
|
||||
act_price
|
||||
part_qty
|
||||
part_type
|
||||
}
|
||||
}
|
||||
production_jobs: jobs(where: { inproduction: { _eq: true } }) {
|
||||
id
|
||||
ro_number
|
||||
ins_co_nm
|
||||
job_totals
|
||||
joblines(where: { removed: { _eq: false } }) {
|
||||
id
|
||||
mod_lbr_ty
|
||||
mod_lb_hrs
|
||||
act_price
|
||||
part_qty
|
||||
part_type
|
||||
}
|
||||
labhrs: joblines_aggregate(where: { mod_lbr_ty: { _neq: "LAR" }, removed: { _eq: false } }) {
|
||||
aggregate {
|
||||
sum {
|
||||
mod_lb_hrs
|
||||
}
|
||||
}
|
||||
}
|
||||
larhrs: joblines_aggregate(where: { mod_lbr_ty: { _eq: "LAR" }, removed: { _eq: false } }) {
|
||||
aggregate {
|
||||
sum {
|
||||
mod_lb_hrs
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}`;
|
||||
};
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { Select, Space, Tag } from "antd";
|
||||
import React from "react";
|
||||
import React, { forwardRef } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
const { Option } = Select;
|
||||
//To be used as a form element only.
|
||||
|
||||
const EmployeeSearchSelect = ({ options, ...props }) => {
|
||||
const EmployeeSearchSelect = ({ options, ...props }, ref) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
@@ -39,4 +39,4 @@ const EmployeeSearchSelect = ({ options, ...props }) => {
|
||||
</Select>
|
||||
);
|
||||
};
|
||||
export default EmployeeSearchSelect;
|
||||
export default forwardRef(EmployeeSearchSelect);
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
import { useQuery } from "@apollo/client";
|
||||
import { Select } from "antd";
|
||||
import React, { forwardRef } from "react";
|
||||
import { QUERY_TEAMS } from "../../graphql/employee_teams.queries";
|
||||
import AlertComponent from "../alert/alert.component";
|
||||
|
||||
//To be used as a form element only.
|
||||
|
||||
const EmployeeTeamSearchSelect = ({ ...props }, ref) => {
|
||||
const { loading, error, data } = useQuery(QUERY_TEAMS);
|
||||
|
||||
if (error) return <AlertComponent message={JSON.stringify(error)} />;
|
||||
return (
|
||||
<Select
|
||||
showSearch
|
||||
allowClear
|
||||
loading={loading}
|
||||
style={{
|
||||
width: 400,
|
||||
}}
|
||||
options={
|
||||
data
|
||||
? data.employee_teams.map((e) => ({
|
||||
value: JSON.stringify(e),
|
||||
label: e.name,
|
||||
}))
|
||||
: []
|
||||
}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
};
|
||||
export default forwardRef(EmployeeTeamSearchSelect);
|
||||
@@ -40,22 +40,22 @@ class ErrorBoundary extends React.Component {
|
||||
}
|
||||
|
||||
handleErrorSubmit = () => {
|
||||
// window.$crisp.push([
|
||||
// "do",
|
||||
// "message:send",
|
||||
// [
|
||||
// "text",
|
||||
// `I hit the following error: \n\n
|
||||
// ${this.state.error.message}\n\n
|
||||
// ${this.state.error.stack}\n\n
|
||||
// URL:${window.location} as ${this.props.currentUser.email} for ${
|
||||
// this.props.bodyshop && this.props.bodyshop.name
|
||||
// }
|
||||
// `,
|
||||
// ],
|
||||
// ]);
|
||||
window.$crisp.push([
|
||||
"do",
|
||||
"message:send",
|
||||
[
|
||||
"text",
|
||||
`I hit the following error: \n\n
|
||||
${this.state.error.message}\n\n
|
||||
${this.state.error.stack}\n\n
|
||||
URL:${window.location} as ${this.props.currentUser.email} for ${
|
||||
this.props.bodyshop && this.props.bodyshop.name
|
||||
}
|
||||
`,
|
||||
],
|
||||
]);
|
||||
|
||||
// window.$crisp.push(["do", "chat:open"]);
|
||||
window.$crisp.push(["do", "chat:open"]);
|
||||
// const errorDescription = `**Please add relevant details about what you were doing before you encountered this issue**
|
||||
|
||||
// ----
|
||||
|
||||
@@ -1,26 +1,9 @@
|
||||
import Dinero from "dinero.js";
|
||||
import React, { forwardRef } from "react";
|
||||
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||
});
|
||||
|
||||
const ReadOnlyFormItem = (
|
||||
{ bodyshop, value, type = "text", onChange },
|
||||
ref
|
||||
) => {
|
||||
const ReadOnlyFormItem = ({ value, type = "text", onChange }, ref) => {
|
||||
if (!value) return null;
|
||||
switch (type) {
|
||||
case "employee":
|
||||
const emp = bodyshop.employees.find((e) => e.id === value);
|
||||
return `${emp?.first_name} ${emp?.last_name}`;
|
||||
|
||||
case "text":
|
||||
return <div>{value}</div>;
|
||||
case "currency":
|
||||
@@ -31,8 +14,4 @@ const ReadOnlyFormItem = (
|
||||
return <div>{value}</div>;
|
||||
}
|
||||
};
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(forwardRef(ReadOnlyFormItem));
|
||||
export default forwardRef(ReadOnlyFormItem);
|
||||
|
||||
@@ -3,16 +3,13 @@ import axios from "axios";
|
||||
import _ from "lodash";
|
||||
import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Link, useHistory } from "react-router-dom";
|
||||
import { Link } from "react-router-dom";
|
||||
import PhoneNumberFormatter from "../../utils/PhoneFormatter";
|
||||
import OwnerNameDisplay, {
|
||||
OwnerNameDisplayFunction,
|
||||
} from "../owner-name-display/owner-name-display.component";
|
||||
import OwnerNameDisplay, { OwnerNameDisplayFunction } from "../owner-name-display/owner-name-display.component";
|
||||
import VehicleVinDisplay from "../vehicle-vin-display/vehicle-vin-display.component";
|
||||
|
||||
export default function GlobalSearchOs() {
|
||||
const { t } = useTranslation();
|
||||
const history = useHistory();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [data, setData] = useState(false);
|
||||
|
||||
@@ -21,7 +18,7 @@ export default function GlobalSearchOs() {
|
||||
try {
|
||||
setLoading(true);
|
||||
const searchData = await axios.post("/search", {
|
||||
search: v,
|
||||
search: v
|
||||
});
|
||||
|
||||
const resultsByType = {
|
||||
@@ -29,7 +26,7 @@ export default function GlobalSearchOs() {
|
||||
jobs: [],
|
||||
bills: [],
|
||||
owners: [],
|
||||
vehicles: [],
|
||||
vehicles: []
|
||||
};
|
||||
|
||||
searchData.data.hits.hits.forEach((hit) => {
|
||||
@@ -50,16 +47,14 @@ export default function GlobalSearchOs() {
|
||||
<span>
|
||||
<OwnerNameDisplay ownerObject={job} />
|
||||
</span>
|
||||
<span>{`${job.v_model_yr || ""} ${
|
||||
job.v_make_desc || ""
|
||||
} ${job.v_model_desc || ""}`}</span>
|
||||
<span>{`${job.v_model_yr || ""} ${job.v_make_desc || ""} ${job.v_model_desc || ""}`}</span>
|
||||
<span>{`${job.clm_no || ""}`}</span>
|
||||
<span>{`${job.plate_no || ""}`}</span>
|
||||
</Space>
|
||||
</Link>
|
||||
),
|
||||
)
|
||||
};
|
||||
}),
|
||||
})
|
||||
},
|
||||
{
|
||||
label: renderTitle(t("menus.header.search.owners")),
|
||||
@@ -69,53 +64,39 @@ export default function GlobalSearchOs() {
|
||||
value: OwnerNameDisplayFunction(owner),
|
||||
label: (
|
||||
<Link to={`/manage/owners/${owner.id}`}>
|
||||
<Space
|
||||
size="small"
|
||||
split={<Divider type="vertical" />}
|
||||
wrap
|
||||
>
|
||||
<Space size="small" split={<Divider type="vertical" />} wrap>
|
||||
<span>
|
||||
<OwnerNameDisplay ownerObject={owner} />
|
||||
</span>
|
||||
<PhoneNumberFormatter>
|
||||
{owner.ownr_ph1}
|
||||
</PhoneNumberFormatter>
|
||||
<PhoneNumberFormatter>
|
||||
{owner.ownr_ph2}
|
||||
</PhoneNumberFormatter>
|
||||
<PhoneNumberFormatter>{owner.ownr_ph1}</PhoneNumberFormatter>
|
||||
<PhoneNumberFormatter>{owner.ownr_ph2}</PhoneNumberFormatter>
|
||||
</Space>
|
||||
</Link>
|
||||
),
|
||||
)
|
||||
};
|
||||
}),
|
||||
})
|
||||
},
|
||||
{
|
||||
label: renderTitle(t("menus.header.search.vehicles")),
|
||||
options: resultsByType.vehicles.map((vehicle) => {
|
||||
return {
|
||||
key: vehicle.id,
|
||||
value: `${vehicle.v_model_yr || ""} ${
|
||||
vehicle.v_make_desc || ""
|
||||
} ${vehicle.v_model_desc || ""}`,
|
||||
value: `${vehicle.v_model_yr || ""} ${vehicle.v_make_desc || ""} ${vehicle.v_model_desc || ""}`,
|
||||
label: (
|
||||
<Link to={`/manage/vehicles/${vehicle.id}`}>
|
||||
<Space size="small" split={<Divider type="vertical" />}>
|
||||
<span>
|
||||
{`${vehicle.v_model_yr || ""} ${
|
||||
vehicle.v_make_desc || ""
|
||||
} ${vehicle.v_model_desc || ""}`}
|
||||
{`${vehicle.v_model_yr || ""} ${vehicle.v_make_desc || ""} ${vehicle.v_model_desc || ""}`}
|
||||
</span>
|
||||
<span>{vehicle.plate_no || ""}</span>
|
||||
<span>
|
||||
<VehicleVinDisplay>
|
||||
{vehicle.v_vin || ""}
|
||||
</VehicleVinDisplay>
|
||||
<VehicleVinDisplay>{vehicle.v_vin || ""}</VehicleVinDisplay>
|
||||
</span>
|
||||
</Space>
|
||||
</Link>
|
||||
),
|
||||
)
|
||||
};
|
||||
}),
|
||||
})
|
||||
},
|
||||
{
|
||||
label: renderTitle(t("menus.header.search.payments")),
|
||||
@@ -133,9 +114,9 @@ export default function GlobalSearchOs() {
|
||||
<span>{payment.transactionid || ""}</span>
|
||||
</Space>
|
||||
</Link>
|
||||
),
|
||||
)
|
||||
};
|
||||
}),
|
||||
})
|
||||
},
|
||||
{
|
||||
label: renderTitle(t("menus.header.search.bills")),
|
||||
@@ -151,10 +132,10 @@ export default function GlobalSearchOs() {
|
||||
<span>{bill.date}</span>
|
||||
</Space>
|
||||
</Link>
|
||||
),
|
||||
)
|
||||
};
|
||||
}),
|
||||
},
|
||||
})
|
||||
}
|
||||
// {
|
||||
// label: renderTitle(t("menus.header.search.phonebook")),
|
||||
// options: resultsByType.search_phonebook.map((pb) => {
|
||||
@@ -196,15 +177,7 @@ export default function GlobalSearchOs() {
|
||||
};
|
||||
|
||||
return (
|
||||
<AutoComplete
|
||||
options={data}
|
||||
onSearch={handleSearch}
|
||||
defaultActiveFirstOption
|
||||
onSelect={(val, opt) => {
|
||||
history.push(opt.label.props.to);
|
||||
}}
|
||||
onClear={() => setData([])}
|
||||
>
|
||||
<AutoComplete options={data} onSearch={handleSearch} defaultActiveFirstOption onClear={() => setData([])}>
|
||||
<Input.Search
|
||||
size="large"
|
||||
placeholder={t("general.labels.globalsearch")}
|
||||
|
||||
@@ -3,28 +3,19 @@ import { AutoComplete, Divider, Input, Space } from "antd";
|
||||
import _ from "lodash";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Link, useHistory } from "react-router-dom";
|
||||
import { Link } from "react-router-dom";
|
||||
import { GLOBAL_SEARCH_QUERY } from "../../graphql/search.queries";
|
||||
import PhoneNumberFormatter from "../../utils/PhoneFormatter";
|
||||
import AlertComponent from "../alert/alert.component";
|
||||
import OwnerNameDisplay, {
|
||||
OwnerNameDisplayFunction,
|
||||
} from "../owner-name-display/owner-name-display.component";
|
||||
import OwnerNameDisplay, { OwnerNameDisplayFunction } from "../owner-name-display/owner-name-display.component";
|
||||
import VehicleVinDisplay from "../vehicle-vin-display/vehicle-vin-display.component";
|
||||
|
||||
export default function GlobalSearch() {
|
||||
const { t } = useTranslation();
|
||||
const history = useHistory();
|
||||
const [callSearch, { loading, error, data }] =
|
||||
useLazyQuery(GLOBAL_SEARCH_QUERY);
|
||||
const [callSearch, { loading, error, data }] = useLazyQuery(GLOBAL_SEARCH_QUERY);
|
||||
|
||||
const executeSearch = (v) => {
|
||||
if (
|
||||
v &&
|
||||
v.variables.search &&
|
||||
v.variables.search !== "" &&
|
||||
v.variables.search.length >= 3
|
||||
)
|
||||
callSearch(v);
|
||||
if (v && v.variables.search && v.variables.search !== "" && v.variables.search.length >= 3) callSearch(v);
|
||||
};
|
||||
const debouncedExecuteSearch = _.debounce(executeSearch, 750);
|
||||
|
||||
@@ -53,15 +44,13 @@ export default function GlobalSearch() {
|
||||
<span>
|
||||
<OwnerNameDisplay ownerObject={job} />
|
||||
</span>
|
||||
<span>{`${job.v_model_yr || ""} ${job.v_make_desc || ""} ${
|
||||
job.v_model_desc || ""
|
||||
}`}</span>
|
||||
<span>{`${job.v_model_yr || ""} ${job.v_make_desc || ""} ${job.v_model_desc || ""}`}</span>
|
||||
<span>{`${job.clm_no || ""}`}</span>
|
||||
</Space>
|
||||
</Link>
|
||||
),
|
||||
)
|
||||
};
|
||||
}),
|
||||
})
|
||||
},
|
||||
{
|
||||
label: renderTitle(t("menus.header.search.owners")),
|
||||
@@ -75,45 +64,35 @@ export default function GlobalSearch() {
|
||||
<span>
|
||||
<OwnerNameDisplay ownerObject={owner} />
|
||||
</span>
|
||||
<PhoneNumberFormatter>
|
||||
{owner.ownr_ph1}
|
||||
</PhoneNumberFormatter>
|
||||
<PhoneNumberFormatter>
|
||||
{owner.ownr_ph2}
|
||||
</PhoneNumberFormatter>
|
||||
<PhoneNumberFormatter>{owner.ownr_ph1}</PhoneNumberFormatter>
|
||||
<PhoneNumberFormatter>{owner.ownr_ph2}</PhoneNumberFormatter>
|
||||
</Space>
|
||||
</Link>
|
||||
),
|
||||
)
|
||||
};
|
||||
}),
|
||||
})
|
||||
},
|
||||
{
|
||||
label: renderTitle(t("menus.header.search.vehicles")),
|
||||
options: data.search_vehicles.map((vehicle) => {
|
||||
return {
|
||||
key: vehicle.id,
|
||||
value: `${vehicle.v_model_yr || ""} ${
|
||||
vehicle.v_make_desc || ""
|
||||
} ${vehicle.v_model_desc || ""}`,
|
||||
value: `${vehicle.v_model_yr || ""} ${vehicle.v_make_desc || ""} ${vehicle.v_model_desc || ""}`,
|
||||
label: (
|
||||
<Link to={`/manage/vehicles/${vehicle.id}`}>
|
||||
<Space size="small" split={<Divider type="vertical" />}>
|
||||
<span>
|
||||
{`${vehicle.v_model_yr || ""} ${
|
||||
vehicle.v_make_desc || ""
|
||||
} ${vehicle.v_model_desc || ""}`}
|
||||
{`${vehicle.v_model_yr || ""} ${vehicle.v_make_desc || ""} ${vehicle.v_model_desc || ""}`}
|
||||
</span>
|
||||
<span>{vehicle.plate_no || ""}</span>
|
||||
<span>
|
||||
<VehicleVinDisplay>
|
||||
{vehicle.v_vin || ""}
|
||||
</VehicleVinDisplay>
|
||||
<VehicleVinDisplay>{vehicle.v_vin || ""}</VehicleVinDisplay>
|
||||
</span>
|
||||
</Space>
|
||||
</Link>
|
||||
),
|
||||
)
|
||||
};
|
||||
}),
|
||||
})
|
||||
},
|
||||
{
|
||||
label: renderTitle(t("menus.header.search.payments")),
|
||||
@@ -131,9 +110,9 @@ export default function GlobalSearch() {
|
||||
<span>{payment.transactionid || ""}</span>
|
||||
</Space>
|
||||
</Link>
|
||||
),
|
||||
)
|
||||
};
|
||||
}),
|
||||
})
|
||||
},
|
||||
{
|
||||
label: renderTitle(t("menus.header.search.bills")),
|
||||
@@ -149,46 +128,35 @@ export default function GlobalSearch() {
|
||||
<span>{bill.date}</span>
|
||||
</Space>
|
||||
</Link>
|
||||
),
|
||||
)
|
||||
};
|
||||
}),
|
||||
})
|
||||
},
|
||||
{
|
||||
label: renderTitle(t("menus.header.search.phonebook")),
|
||||
options: data.search_phonebook.map((pb) => {
|
||||
return {
|
||||
key: pb.id,
|
||||
value: `${pb.firstname || ""} ${pb.lastname || ""} ${
|
||||
pb.company || ""
|
||||
}`,
|
||||
value: `${pb.firstname || ""} ${pb.lastname || ""} ${pb.company || ""}`,
|
||||
label: (
|
||||
<Link to={`/manage/phonebook?phonebookentry=${pb.id}`}>
|
||||
<Space size="small" split={<Divider type="vertical" />}>
|
||||
<span>{`${pb.firstname || ""} ${pb.lastname || ""} ${
|
||||
pb.company || ""
|
||||
}`}</span>
|
||||
<span>{`${pb.firstname || ""} ${pb.lastname || ""} ${pb.company || ""}`}</span>
|
||||
<PhoneNumberFormatter>{pb.phone1}</PhoneNumberFormatter>
|
||||
<span>{pb.email}</span>
|
||||
</Space>
|
||||
</Link>
|
||||
),
|
||||
)
|
||||
};
|
||||
}),
|
||||
},
|
||||
})
|
||||
}
|
||||
]
|
||||
: [];
|
||||
|
||||
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||
|
||||
return (
|
||||
<AutoComplete
|
||||
options={options}
|
||||
onSearch={handleSearch}
|
||||
defaultActiveFirstOption
|
||||
onSelect={(val, opt) => {
|
||||
history.push(opt.label.props.to);
|
||||
}}
|
||||
>
|
||||
<AutoComplete options={options} onSearch={handleSearch} defaultActiveFirstOption>
|
||||
<Input.Search
|
||||
size="large"
|
||||
placeholder={t("general.labels.globalsearch")}
|
||||
|
||||
@@ -13,8 +13,7 @@ import Icon, {
|
||||
FileFilled,
|
||||
//GlobalOutlined,
|
||||
HomeFilled,
|
||||
ImportOutlined,
|
||||
// InfoCircleOutlined,
|
||||
ImportOutlined, InfoCircleOutlined,
|
||||
LineChartOutlined,
|
||||
PaperClipOutlined,
|
||||
PhoneOutlined,
|
||||
@@ -27,11 +26,8 @@ import Icon, {
|
||||
UserOutlined,
|
||||
} from "@ant-design/icons";
|
||||
import { useTreatments } from "@splitsoftware/splitio-react";
|
||||
import {
|
||||
Layout,
|
||||
Menu, // Switch, Tooltip
|
||||
} from "antd";
|
||||
import React from "react"; //, { useEffect, useState }
|
||||
import {Layout, Menu, Switch, Tooltip} from "antd";
|
||||
import React, {useEffect, useState} from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { BsKanban } from "react-icons/bs";
|
||||
import {
|
||||
@@ -56,7 +52,7 @@ import {
|
||||
selectBodyshop,
|
||||
selectCurrentUser,
|
||||
} from "../../redux/user/user.selectors";
|
||||
//import { handleBeta, setBeta, checkBeta } from "../../utils/handleBeta";
|
||||
import {handleBeta, setBeta, checkBeta} from "../../utils/handleBeta";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
currentUser: selectCurrentUser,
|
||||
@@ -107,20 +103,20 @@ function Header({
|
||||
{},
|
||||
bodyshop && bodyshop.imexshopid
|
||||
);
|
||||
//const [betaSwitch, setBetaSwitch] = useState(false);
|
||||
const [betaSwitch, setBetaSwitch] = useState(false);
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
// useEffect(() => {
|
||||
// const isBeta = checkBeta();
|
||||
// setBetaSwitch(isBeta);
|
||||
// }, []);
|
||||
useEffect(() => {
|
||||
const isBeta = checkBeta();
|
||||
setBetaSwitch(isBeta);
|
||||
}, []);
|
||||
|
||||
// const betaSwitchChange = (checked) => {
|
||||
// setBeta(checked);
|
||||
// setBetaSwitch(checked);
|
||||
// handleBeta();
|
||||
// };
|
||||
const betaSwitchChange = (checked) => {
|
||||
setBeta(checked);
|
||||
setBetaSwitch(checked);
|
||||
handleBeta();
|
||||
}
|
||||
|
||||
return (
|
||||
<Layout.Header>
|
||||
@@ -285,13 +281,6 @@ function Header({
|
||||
{t("menus.header.timetickets")}
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
{bodyshop?.md_tasks_presets?.use_approvals && (
|
||||
<Menu.Item key="ttapprovals" icon={<FieldTimeOutlined />}>
|
||||
<Link to="/manage/ttapprovals">
|
||||
{t("menus.header.ttapprovals")}
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
)}
|
||||
<Menu.Item
|
||||
key="entertimetickets"
|
||||
icon={<Icon component={GiPlayerTime} />}
|
||||
@@ -406,12 +395,20 @@ function Header({
|
||||
<Menu.Item
|
||||
key="help"
|
||||
onClick={() => {
|
||||
window.open("https://rometech.com/", "_blank");
|
||||
window.open("https://help.imex.online/", "_blank");
|
||||
}}
|
||||
icon={<Icon component={QuestionCircleFilled} />}
|
||||
>
|
||||
{t("menus.header.help")}
|
||||
</Menu.Item>
|
||||
<Menu.Item
|
||||
key="rescue"
|
||||
onClick={() => {
|
||||
window.open("https://imexrescue.com/", "_blank");
|
||||
}}
|
||||
>
|
||||
{t("menus.header.rescueme")}
|
||||
</Menu.Item>
|
||||
<Menu.Item key="shiftclock">
|
||||
<Link to="/manage/shiftclock">{t("menus.header.shiftclock")}</Link>
|
||||
</Menu.Item>
|
||||
@@ -447,18 +444,17 @@ function Header({
|
||||
</Menu.Item>
|
||||
))}
|
||||
</Menu.SubMenu>
|
||||
{
|
||||
// <Menu.Item style={{marginLeft: 'auto'}} key="profile">
|
||||
// <Tooltip title="A more modern ImEX Online is ready for you to try! You can switch back at any time.">
|
||||
// <InfoCircleOutlined/>
|
||||
// <span style={{marginRight: 8}}>Try the new ImEX Online</span>
|
||||
// <Switch
|
||||
// checked={betaSwitch}
|
||||
// onChange={betaSwitchChange}
|
||||
// />
|
||||
// </Tooltip>
|
||||
// </Menu.Item>
|
||||
}
|
||||
<Menu.Item style={{marginLeft: 'auto'}} key="profile">
|
||||
<Tooltip title="A more modern ImEX Online is ready for you to try! You can switch back at any time.">
|
||||
<InfoCircleOutlined/>
|
||||
<span style={{marginRight: 8}}>Try the new ImEX Online</span>
|
||||
<Switch
|
||||
checked={betaSwitch}
|
||||
onChange={betaSwitchChange}
|
||||
/>
|
||||
</Tooltip>
|
||||
</Menu.Item>
|
||||
|
||||
</Menu>
|
||||
</Layout.Header>
|
||||
);
|
||||
|
||||
@@ -53,6 +53,7 @@ export default function ScheduleEventContainer({ bodyshop, event, refetch }) {
|
||||
insertAuditTrail({
|
||||
jobid: event.job.id,
|
||||
operation: AuditTrailMapping.appointmentcancel(lost_sale_reason),
|
||||
type: "appointmentcancel",
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@@ -87,8 +87,7 @@ export default function JobBillsTotalComponent({
|
||||
const totalPartsSublet = Dinero(totals.parts.parts.total)
|
||||
.add(Dinero(totals.parts.sublets.total))
|
||||
.add(Dinero(totals.additional.shipping))
|
||||
.add(Dinero(totals.additional.towing))
|
||||
.add(Dinero(totals.additional.additionalCosts));
|
||||
.add(Dinero(totals.additional.towing));
|
||||
|
||||
const discrepancy = totalPartsSublet.subtract(billTotals);
|
||||
|
||||
|
||||
@@ -1,52 +1,47 @@
|
||||
import { Button, notification } from "antd";
|
||||
import Axios from "axios";
|
||||
import React, { useState } from "react";
|
||||
import { useMutation } from "@apollo/client";
|
||||
import { useTranslation } from "react-i18next";
|
||||
export default function JobCalculateTotals({ job, disabled, refetch }) {
|
||||
import { UPDATE_JOB } from "../../graphql/jobs.queries";
|
||||
import Dinero from "dinero.js";
|
||||
export default function JobCalculateTotals({ job, disabled }) {
|
||||
const { t } = useTranslation();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [updateJob] = useMutation(UPDATE_JOB);
|
||||
|
||||
const handleCalculate = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
setLoading(true);
|
||||
const newTotals = (
|
||||
await Axios.post("/job/totals", {
|
||||
job: job,
|
||||
})
|
||||
).data;
|
||||
|
||||
await Axios.post("/job/totalsssu", {
|
||||
id: job.id,
|
||||
});
|
||||
|
||||
if (refetch) refetch();
|
||||
// const result = await updateJob({
|
||||
// refetchQueries: ["GET_JOB_BY_PK"],
|
||||
// awaitRefetchQueries: true,
|
||||
// variables: {
|
||||
// jobId: job.id,
|
||||
// job: {
|
||||
// job_totals: newTotals,
|
||||
// clm_total: Dinero(newTotals.totals.total_repairs).toFormat("0.00"),
|
||||
// owner_owing: Dinero(newTotals.totals.custPayable.total).toFormat(
|
||||
// "0.00"
|
||||
// ),
|
||||
// },
|
||||
// },
|
||||
// });
|
||||
// if (!!!result.errors) {
|
||||
// notification["success"]({ message: t("jobs.successes.updated") });
|
||||
// } else {
|
||||
// notification["error"]({
|
||||
// message: t("jobs.errors.updating", {
|
||||
// error: JSON.stringify(result.errors),
|
||||
// }),
|
||||
// });
|
||||
// }
|
||||
} catch (error) {
|
||||
const result = await updateJob({
|
||||
refetchQueries: ["GET_JOB_BY_PK"],
|
||||
awaitRefetchQueries: true,
|
||||
variables: {
|
||||
jobId: job.id,
|
||||
job: {
|
||||
job_totals: newTotals,
|
||||
clm_total: Dinero(newTotals.totals.total_repairs).toFormat("0.00"),
|
||||
owner_owing: Dinero(newTotals.totals.custPayable.total).toFormat(
|
||||
"0.00"
|
||||
),
|
||||
},
|
||||
},
|
||||
});
|
||||
if (!!!result.errors) {
|
||||
notification["success"]({ message: t("jobs.successes.updated") });
|
||||
} else {
|
||||
notification["error"]({
|
||||
message: t("jobs.errors.updating", {
|
||||
error: JSON.stringify(error),
|
||||
error: JSON.stringify(result.errors),
|
||||
}),
|
||||
});
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -28,8 +28,8 @@ const mapStateToProps = createStructuredSelector({
|
||||
currentUser: selectCurrentUser,
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
insertAuditTrail: ({ jobid, operation }) =>
|
||||
dispatch(insertAuditTrail({ jobid, operation })),
|
||||
insertAuditTrail: ({ jobid, operation, type }) =>
|
||||
dispatch(insertAuditTrail({ jobid, operation, type })),
|
||||
});
|
||||
|
||||
export function JobChecklistForm({
|
||||
@@ -183,6 +183,7 @@ export function JobChecklistForm({
|
||||
(type === "intake" && bodyshop.md_ro_statuses.default_arrived) ||
|
||||
(type === "deliver" && bodyshop.md_ro_statuses.default_delivered)
|
||||
),
|
||||
type: "jobchecklist",
|
||||
});
|
||||
} else {
|
||||
notification["error"]({
|
||||
|
||||
@@ -32,9 +32,9 @@ const mapDispatchToProps = (dispatch) => ({
|
||||
});
|
||||
|
||||
const span = {
|
||||
lg: { span: 24 },
|
||||
xl: { span: 12 },
|
||||
xxl: { span: 8 },
|
||||
sm: { span: 24 },
|
||||
md: { span: 12 },
|
||||
lg: { span: 8 },
|
||||
};
|
||||
|
||||
export function JobDetailCards({ bodyshop, setPrintCenterContext }) {
|
||||
@@ -137,6 +137,12 @@ export function JobDetailCards({ bodyshop, setPrintCenterContext }) {
|
||||
data={data ? data.jobs_by_pk : null}
|
||||
/>
|
||||
</Col>
|
||||
<Col {...span}>
|
||||
<JobDetailCardsPartsComponent
|
||||
loading={loading}
|
||||
data={data ? data.jobs_by_pk : null}
|
||||
/>
|
||||
</Col>
|
||||
<Col {...span}>
|
||||
<JobDetailCardsNotesComponent
|
||||
loading={loading}
|
||||
@@ -157,12 +163,6 @@ export function JobDetailCards({ bodyshop, setPrintCenterContext }) {
|
||||
data={data ? data.jobs_by_pk : null}
|
||||
/>
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
<JobDetailCardsPartsComponent
|
||||
loading={loading}
|
||||
data={data ? data.jobs_by_pk : null}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
</Card>
|
||||
) : null}
|
||||
|
||||
@@ -1,119 +1,16 @@
|
||||
import { Table } from "antd";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import JobLineNotePopup from "../job-line-note-popup/job-line-note-popup.component";
|
||||
import PartsStatusPie from "../parts-status-pie/parts-status-pie.component";
|
||||
import CardTemplate from "./job-detail-cards.template.component";
|
||||
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { selectJobReadOnly } from "../../redux/application/application.selectors";
|
||||
import { onlyUnique } from "../../utils/arrayHelper";
|
||||
import { alphaSort } from "../../utils/sorters";
|
||||
import JobLineLocationPopup from "../job-line-location-popup/job-line-location-popup.component";
|
||||
import JobLineStatusPopup from "../job-line-status-popup/job-line-status-popup.component";
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
//currentUser: selectCurrentUser
|
||||
jobRO: selectJobReadOnly,
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||
});
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(JobDetailCardsPartsComponent);
|
||||
|
||||
export function JobDetailCardsPartsComponent({ loading, data, jobRO }) {
|
||||
export default function JobDetailCardsPartsComponent({ loading, data }) {
|
||||
const { t } = useTranslation();
|
||||
const { joblines_status } = data;
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: t("joblines.fields.line_desc"),
|
||||
dataIndex: "line_desc",
|
||||
fixed: "left",
|
||||
key: "line_desc",
|
||||
sorter: (a, b) => alphaSort(a.line_desc, b.line_desc),
|
||||
onCell: (record) => ({
|
||||
className: record.manual_line && "job-line-manual",
|
||||
style: {
|
||||
...(record.critical ? { boxShadow: " -.5em 0 0 #FFC107" } : {}),
|
||||
},
|
||||
}),
|
||||
width: "30%",
|
||||
ellipsis: true,
|
||||
},
|
||||
{
|
||||
title: t("joblines.fields.part_type"),
|
||||
dataIndex: "part_type",
|
||||
key: "part_type",
|
||||
width: "15%",
|
||||
sorter: (a, b) =>
|
||||
alphaSort(
|
||||
t(`joblines.fields.part_types.${a.part_type}`),
|
||||
t(`joblines.fields.part_types.${b.part_type}`)
|
||||
),
|
||||
render: (text, record) =>
|
||||
record.part_type
|
||||
? t(`joblines.fields.part_types.${record.part_type}`)
|
||||
: null,
|
||||
},
|
||||
{
|
||||
title: t("joblines.fields.part_qty"),
|
||||
dataIndex: "part_qty",
|
||||
key: "part_qty",
|
||||
width: "10%",
|
||||
},
|
||||
{
|
||||
title: t("joblines.fields.notes"),
|
||||
dataIndex: "notes",
|
||||
key: "notes",
|
||||
render: (text, record) => (
|
||||
<JobLineNotePopup disabled={jobRO} jobline={record} />
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t("joblines.fields.location"),
|
||||
dataIndex: "location",
|
||||
key: "location",
|
||||
sorter: (a, b) => alphaSort(a.location, b.location),
|
||||
render: (text, record) => (
|
||||
<JobLineLocationPopup jobline={record} disabled={jobRO} />
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t("joblines.fields.status"),
|
||||
dataIndex: "status",
|
||||
key: "status",
|
||||
sorter: (a, b) => alphaSort(a.status, b.status),
|
||||
filters:
|
||||
(data &&
|
||||
data.joblines
|
||||
?.map((l) => l.status)
|
||||
.filter(onlyUnique)
|
||||
.map((s) => {
|
||||
return {
|
||||
text: s || "No Status*",
|
||||
value: [s],
|
||||
};
|
||||
})) ||
|
||||
[],
|
||||
onFilter: (value, record) => value.includes(record.status),
|
||||
render: (text, record) => (
|
||||
<JobLineStatusPopup jobline={record} disabled={jobRO} />
|
||||
),
|
||||
},
|
||||
];
|
||||
return (
|
||||
<div>
|
||||
<CardTemplate loading={loading} title={t("jobs.labels.cards.parts")}>
|
||||
<PartsStatusPie joblines_status={joblines_status} />
|
||||
<Table
|
||||
key="id"
|
||||
columns={columns}
|
||||
dataSource={data ? data.joblines : []}
|
||||
/>
|
||||
</CardTemplate>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -18,10 +18,8 @@ export default function JobDetailCardsTotalsComponent({ loading, data }) {
|
||||
/>
|
||||
<Statistic
|
||||
className="imex-flex-row__margin-large"
|
||||
title={t("jobs.fields.ded_amt")}
|
||||
value={Dinero({
|
||||
amount: Math.round((data.ded_amt || 0) * 100),
|
||||
}).toFormat()}
|
||||
title={t("jobs.fields.customerowing")}
|
||||
value={Dinero(data.job_totals.totals.custPayable.total).toFormat()}
|
||||
/>
|
||||
<Statistic
|
||||
className="imex-flex-row__margin-large"
|
||||
|
||||
@@ -1,25 +1,26 @@
|
||||
import { useQuery } from "@apollo/client";
|
||||
import { Col, Divider, Row, Skeleton, Space, Timeline, Typography } from "antd";
|
||||
import { Col, Row, Skeleton, Timeline, Typography } from "antd";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { Link } from "react-router-dom";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { GET_JOB_LINE_ORDERS } from "../../graphql/jobs.queries";
|
||||
import { selectTechnician } from "../../redux/tech/tech.selectors.js";
|
||||
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||
import { DateFormatter } from "../../utils/DateFormatter";
|
||||
import AlertComponent from "../alert/alert.component";
|
||||
import BillDetailEditcontainer from "../bill-detail-edit/bill-detail-edit.container.jsx";
|
||||
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||
technician: selectTechnician,
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({});
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(JobLinesExpander);
|
||||
|
||||
export function JobLinesExpander({ jobline, jobid, bodyshop }) {
|
||||
export function JobLinesExpander({ jobline, jobid, technician }) {
|
||||
const { t } = useTranslation();
|
||||
const { loading, error, data } = useQuery(GET_JOB_LINE_ORDERS, {
|
||||
fetchPolicy: "network-only",
|
||||
@@ -34,7 +35,7 @@ export function JobLinesExpander({ jobline, jobid, bodyshop }) {
|
||||
|
||||
return (
|
||||
<Row>
|
||||
<Col md={24} lg={8}>
|
||||
<Col md={24} lg={12}>
|
||||
<Typography.Title level={4}>
|
||||
{t("parts_orders.labels.parts_orders")}
|
||||
</Typography.Title>
|
||||
@@ -42,15 +43,31 @@ export function JobLinesExpander({ jobline, jobid, bodyshop }) {
|
||||
{data.parts_order_lines.length > 0 ? (
|
||||
data.parts_order_lines.map((line) => (
|
||||
<Timeline.Item key={line.id}>
|
||||
<Space split={<Divider type="vertical" />} wrap>
|
||||
<Link
|
||||
to={`/manage/jobs/${jobid}?partsorderid=${line.parts_order.id}`}
|
||||
>
|
||||
{line.parts_order.order_number}
|
||||
</Link>
|
||||
<DateFormatter>{line.parts_order.order_date}</DateFormatter>
|
||||
{line.parts_order.vendor.name}
|
||||
</Space>
|
||||
<Row wrap>
|
||||
<Col span={4}>
|
||||
{!technician ? (
|
||||
<Link
|
||||
to={`/manage/jobs/${jobid}?partsorderid=${line.parts_order.id}`}
|
||||
>
|
||||
{line.parts_order.order_number}
|
||||
</Link>
|
||||
) : (
|
||||
`${line.parts_order.order_number}`
|
||||
)}
|
||||
</Col>
|
||||
<Col span={4}>
|
||||
<DateFormatter>{line.parts_order.order_date}</DateFormatter>
|
||||
</Col>
|
||||
<Col span={4}>{line.parts_order.vendor.name}</Col>
|
||||
{line.backordered_eta ? (
|
||||
<Col span={4}>
|
||||
<span>
|
||||
{`${t("parts_orders.fields.backordered_eta")}: `}
|
||||
<DateFormatter>{line.backordered_eta}</DateFormatter>
|
||||
</span>
|
||||
</Col>
|
||||
) : null}
|
||||
</Row>
|
||||
</Timeline.Item>
|
||||
))
|
||||
) : (
|
||||
@@ -60,19 +77,24 @@ export function JobLinesExpander({ jobline, jobid, bodyshop }) {
|
||||
)}
|
||||
</Timeline>
|
||||
</Col>
|
||||
<Col md={24} lg={8}>
|
||||
<Col md={24} lg={12}>
|
||||
<Typography.Title level={4}>{t("bills.labels.bills")}</Typography.Title>
|
||||
<BillDetailEditcontainer />
|
||||
<Timeline>
|
||||
{data.billlines.length > 0 ? (
|
||||
data.billlines.map((line) => (
|
||||
<Timeline.Item key={line.id}>
|
||||
<Row wrap>
|
||||
<Col span={4}>
|
||||
<Link
|
||||
to={`/manage/jobs/${jobid}?tab=partssublet&billid=${line.bill.id}`}
|
||||
>
|
||||
{line.bill.invoice_number}
|
||||
</Link>
|
||||
{!technician ? (
|
||||
<Link
|
||||
to={`/manage/jobs/${jobid}?tab=partssublet&billid=${line.bill.id}`}
|
||||
>
|
||||
{line.bill.invoice_number}
|
||||
</Link>
|
||||
) : (
|
||||
`${line.bill.invoice_number}`
|
||||
)}
|
||||
</Col>
|
||||
<Col span={4}>
|
||||
<span>
|
||||
@@ -94,40 +116,7 @@ export function JobLinesExpander({ jobline, jobid, bodyshop }) {
|
||||
</Timeline.Item>
|
||||
))
|
||||
) : (
|
||||
<Timeline.Item>
|
||||
{t("parts_orders.labels.notyetordered")}
|
||||
</Timeline.Item>
|
||||
)}
|
||||
</Timeline>
|
||||
</Col>
|
||||
<Col md={24} lg={8}>
|
||||
<Typography.Title level={4}>
|
||||
{t("parts_dispatch.labels.parts_dispatch")}
|
||||
</Typography.Title>
|
||||
<Timeline>
|
||||
{data.parts_dispatch_lines.length > 0 ? (
|
||||
data.parts_dispatch_lines.map((line) => (
|
||||
<Timeline.Item key={line.id}>
|
||||
<Space split={<Divider type="vertical" />} wrap>
|
||||
<Link to={`/manage/jobs/${jobid}?partsorderid=${line.id}`}>
|
||||
{line.parts_dispatch.number}
|
||||
</Link>
|
||||
{
|
||||
bodyshop.employees.find(
|
||||
(e) => e.id === line.parts_dispatch.employeeid
|
||||
)?.first_name
|
||||
}
|
||||
<Space>
|
||||
{t("parts_dispatch_lines.fields.accepted_at")}
|
||||
<DateFormatter>{line.accepted_at}</DateFormatter>
|
||||
</Space>
|
||||
</Space>
|
||||
</Timeline.Item>
|
||||
))
|
||||
) : (
|
||||
<Timeline.Item>
|
||||
{t("parts_orders.labels.notyetordered")}
|
||||
</Timeline.Item>
|
||||
<Timeline.Item>{t("bills.labels.nobilllines")}</Timeline.Item>
|
||||
)}
|
||||
</Timeline>
|
||||
</Col>
|
||||
|
||||
@@ -1,97 +0,0 @@
|
||||
import { useMutation } from "@apollo/client";
|
||||
import { Button, Form, notification, Popover, Tooltip } from "antd";
|
||||
import { t } from "i18next";
|
||||
import React, { useState } from "react";
|
||||
import { UPDATE_LINE_PPC } from "../../graphql/jobs-lines.queries";
|
||||
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||
import CurrencyFormItemComponent from "../form-items-formatted/currency-form-item.component";
|
||||
import JobLineConvertToLabor from "../job-line-convert-to-labor/job-line-convert-to-labor.component";
|
||||
import axios from "axios";
|
||||
export default function JobLinesPartPriceChange({ job, line, refetch }) {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [updatePartPrice] = useMutation(UPDATE_LINE_PPC);
|
||||
|
||||
const handleFinish = async (values) => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const result = await updatePartPrice({
|
||||
variables: {
|
||||
id: line.id,
|
||||
jobline: {
|
||||
act_price_before_ppc: line.act_price_before_ppc
|
||||
? line.act_price_before_ppc
|
||||
: line.act_price,
|
||||
act_price: values.act_price,
|
||||
},
|
||||
},
|
||||
});
|
||||
await axios.post("/job/totalsssu", {
|
||||
id: job.id,
|
||||
});
|
||||
if (result.errors) {
|
||||
notification.open({
|
||||
type: "error",
|
||||
message: t("joblines.errors.saving", {
|
||||
error: JSON.stringify(result.errors),
|
||||
}),
|
||||
});
|
||||
if (refetch) refetch();
|
||||
} else {
|
||||
notification.open({
|
||||
type: "success",
|
||||
message: t("joblines.successes.saved"),
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
notification.open({
|
||||
type: "error",
|
||||
message: t("joblines.errors.saving", { error: JSON.stringify(error) }),
|
||||
});
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const popcontent = (
|
||||
<Form layout="vertical" onFinish={handleFinish} initialValues={{ act_price: line.act_price }}>
|
||||
<Form.Item
|
||||
name="act_price"
|
||||
label={t("jobs.labels.act_price_ppc")}
|
||||
rules={[{ required: true }]}
|
||||
>
|
||||
<CurrencyFormItemComponent />
|
||||
</Form.Item>
|
||||
<Button loading={loading} htmlType="primary">
|
||||
{t("general.actions.save")}
|
||||
</Button>
|
||||
</Form>
|
||||
);
|
||||
|
||||
return (
|
||||
<JobLineConvertToLabor jobline={line} job={job}>
|
||||
<Popover trigger="click" disabled={line.manual_line} content={popcontent}>
|
||||
<CurrencyFormatter>
|
||||
{line.db_ref === "900510" || line.db_ref === "900511"
|
||||
? line.prt_dsmk_m
|
||||
: line.act_price}
|
||||
</CurrencyFormatter>
|
||||
{line.prt_dsmk_p && line.prt_dsmk_p !== 0 ? (
|
||||
<span style={{ marginLeft: ".2rem" }}>{`(${line.prt_dsmk_p}%)`}</span>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
{line.act_price_before_ppc && line.act_price_before_ppc !== 0 ? (
|
||||
<Tooltip title={t("jobs.labels.ppc")}>
|
||||
<span style={{ marginLeft: ".2rem", color: "tomato" }}>
|
||||
(
|
||||
<CurrencyFormatter>{line.act_price_before_ppc}</CurrencyFormatter>
|
||||
)
|
||||
</span>
|
||||
</Tooltip>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</Popover>
|
||||
</JobLineConvertToLabor>
|
||||
);
|
||||
}
|
||||
@@ -20,6 +20,8 @@ import {
|
||||
Tag,
|
||||
} from "antd";
|
||||
import axios from "axios";
|
||||
import _ from "lodash";
|
||||
import moment from "moment";
|
||||
import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
@@ -28,27 +30,19 @@ import { DELETE_JOB_LINE_BY_PK } from "../../graphql/jobs-lines.queries";
|
||||
import { selectJobReadOnly } from "../../redux/application/application.selectors";
|
||||
import { setModalContext } from "../../redux/modals/modals.actions";
|
||||
import { selectTechnician } from "../../redux/tech/tech.selectors";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import { onlyUnique } from "../../utils/arrayHelper";
|
||||
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||
import { alphaSort } from "../../utils/sorters";
|
||||
import JobCreateIOU from "../job-create-iou/job-create-iou.component";
|
||||
import JobLineConvertToLabor from "../job-line-convert-to-labor/job-line-convert-to-labor.component";
|
||||
import JobLineLocationPopup from "../job-line-location-popup/job-line-location-popup.component";
|
||||
import JobLineNotePopup from "../job-line-note-popup/job-line-note-popup.component";
|
||||
import JobLineStatusPopup from "../job-line-status-popup/job-line-status-popup.component";
|
||||
import JobLinesBillRefernece from "../job-lines-bill-reference/job-lines-bill-reference.component";
|
||||
// import AllocationsAssignmentContainer from "../allocations-assignment/allocations-assignment.container";
|
||||
// import AllocationsBulkAssignmentContainer from "../allocations-bulk-assignment/allocations-bulk-assignment.container";
|
||||
// import AllocationsEmployeeLabelContainer from "../allocations-employee-label/allocations-employee-label.container";
|
||||
import _ from "lodash";
|
||||
import moment from "moment";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import JobCreateIOU from "../job-create-iou/job-create-iou.component";
|
||||
import JobSendPartPriceChangeComponent from "../job-send-parts-price-change/job-send-parts-price-change.component";
|
||||
import PartsOrderDrawer from "../parts-order-list-table/parts-order-list-table-drawer.component";
|
||||
import PartsOrderModalContainer from "../parts-order-modal/parts-order-modal.container";
|
||||
import JobLinesExpander from "./job-lines-expander.component";
|
||||
import JobLinesPartPriceChange from "./job-lines-part-price-change.component";
|
||||
import JoblineTeamAssignment from "../job-line-team-assignment/job-line-team-assignmnent.component";
|
||||
import JobLineDispatchButton from "../job-line-dispatch-button/job-line-dispatch-button.component";
|
||||
import JobLineBulkAssignComponent from "../job-line-bulk-assign/job-line-bulk-assign.component";
|
||||
import { useTreatments } from "@splitsoftware/splitio-react";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
@@ -61,6 +55,8 @@ const mapDispatchToProps = (dispatch) => ({
|
||||
dispatch(setModalContext({ context: context, modal: "jobLineEdit" })),
|
||||
setPartsOrderContext: (context) =>
|
||||
dispatch(setModalContext({ context: context, modal: "partsOrder" })),
|
||||
setPartsReceiveContext: (context) =>
|
||||
dispatch(setModalContext({ context: context, modal: "partsReceive" })),
|
||||
setBillEnterContext: (context) =>
|
||||
dispatch(setModalContext({ context: context, modal: "billEnter" })),
|
||||
});
|
||||
@@ -70,6 +66,7 @@ export function JobLinesComponent({
|
||||
jobRO,
|
||||
technician,
|
||||
setPartsOrderContext,
|
||||
setPartsReceiveContext,
|
||||
loading,
|
||||
refetch,
|
||||
jobLines,
|
||||
@@ -78,18 +75,12 @@ export function JobLinesComponent({
|
||||
setJobLineEditContext,
|
||||
form,
|
||||
setBillEnterContext,
|
||||
billsQuery,
|
||||
handlePartsOrderOnRowClick,
|
||||
}) {
|
||||
const [deleteJobLine] = useMutation(DELETE_JOB_LINE_BY_PK);
|
||||
const { Enhanced_Payroll } = useTreatments(
|
||||
["Enhanced_Payroll"],
|
||||
{},
|
||||
bodyshop.imexshopid
|
||||
);
|
||||
|
||||
const [selectedLines, setSelectedLines] = useState([]);
|
||||
console.log(
|
||||
"🚀 ~ file: job-lines.component.jsx:89 ~ selectedLines:",
|
||||
selectedLines
|
||||
);
|
||||
const [state, setState] = useState({
|
||||
sortedInfo: {},
|
||||
filteredInfo: {},
|
||||
@@ -133,21 +124,10 @@ export function JobLinesComponent({
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "oem_partno" && state.sortedInfo.order,
|
||||
ellipsis: true,
|
||||
onCell: (record) => ({
|
||||
className: record.manual_line && "job-line-manual",
|
||||
style: {
|
||||
...(record.parts_dispatch_lines[0]?.accepted_at
|
||||
? { boxShadow: " -.5em 0 0 #FFC107" }
|
||||
: {}),
|
||||
},
|
||||
}),
|
||||
render: (text, record) => (
|
||||
<span class="ant-table-cell-content">
|
||||
{`${record.oem_partno || ""} ${
|
||||
record.alt_partno ? `(${record.alt_partno})` : ""
|
||||
}`.trim()}
|
||||
</span>
|
||||
),
|
||||
render: (text, record) =>
|
||||
`${record.oem_partno || ""} ${
|
||||
record.alt_partno ? `(${record.alt_partno})` : ""
|
||||
}`.trim(),
|
||||
},
|
||||
{
|
||||
title: t("joblines.fields.op_code_desc"),
|
||||
@@ -243,7 +223,20 @@ export function JobLinesComponent({
|
||||
state.sortedInfo.columnKey === "act_price" && state.sortedInfo.order,
|
||||
ellipsis: true,
|
||||
render: (text, record) => (
|
||||
<JobLinesPartPriceChange line={record} job={job} refetch={refetch} />
|
||||
<JobLineConvertToLabor jobline={record} job={job}>
|
||||
<CurrencyFormatter>
|
||||
{record.db_ref === "900510" || record.db_ref === "900511"
|
||||
? record.prt_dsmk_m
|
||||
: record.act_price}
|
||||
</CurrencyFormatter>
|
||||
{record.prt_dsmk_p && record.prt_dsmk_p !== 0 ? (
|
||||
<span
|
||||
style={{ marginLeft: ".2rem" }}
|
||||
>{`(${record.prt_dsmk_p}%)`}</span>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</JobLineConvertToLabor>
|
||||
),
|
||||
},
|
||||
{
|
||||
@@ -296,23 +289,6 @@ export function JobLinesComponent({
|
||||
state.sortedInfo.columnKey === "line_ind" && state.sortedInfo.order,
|
||||
responsive: ["md"],
|
||||
},
|
||||
...(Enhanced_Payroll.treatment === "on"
|
||||
? [
|
||||
{
|
||||
title: t("joblines.fields.assigned_team"),
|
||||
dataIndex: "assigned_team",
|
||||
key: "assigned_team",
|
||||
render: (text, record) => (
|
||||
<JoblineTeamAssignment
|
||||
disabled={jobRO}
|
||||
jobline={record}
|
||||
jobId={job.id}
|
||||
/>
|
||||
),
|
||||
},
|
||||
]
|
||||
: []),
|
||||
|
||||
{
|
||||
title: t("joblines.fields.notes"),
|
||||
dataIndex: "notes",
|
||||
@@ -368,7 +344,7 @@ export function JobLinesComponent({
|
||||
key: "actions",
|
||||
render: (text, record) => (
|
||||
<Space>
|
||||
{(record.manual_line || jobIsPrivate) && (
|
||||
{(record.manual_line || jobIsPrivate) && !technician && (
|
||||
<>
|
||||
<Button
|
||||
disabled={jobRO}
|
||||
@@ -431,11 +407,7 @@ export function JobLinesComponent({
|
||||
setSelectedLines((selectedLines) =>
|
||||
_.uniq([
|
||||
...selectedLines,
|
||||
...jobLines.filter(
|
||||
(item) =>
|
||||
markedTypes.includes(item.part_type) ||
|
||||
markedTypes.includes(item.mod_lbr_ty)
|
||||
),
|
||||
...jobLines.filter((item) => markedTypes.includes(item.part_type)),
|
||||
])
|
||||
);
|
||||
}
|
||||
@@ -448,21 +420,6 @@ export function JobLinesComponent({
|
||||
<Menu.Item key="PAL">{t("joblines.fields.part_types.PAL")}</Menu.Item>
|
||||
<Menu.Item key="PAS">{t("joblines.fields.part_types.PAS")}</Menu.Item>
|
||||
<Menu.Divider />
|
||||
<Menu.Item key="LAA">{t("joblines.fields.lbr_types.LAA")}</Menu.Item>
|
||||
<Menu.Item key="LAB">{t("joblines.fields.lbr_types.LAB")}</Menu.Item>
|
||||
<Menu.Item key="LAD">{t("joblines.fields.lbr_types.LAD")}</Menu.Item>
|
||||
<Menu.Item key="LAE">{t("joblines.fields.lbr_types.LAE")}</Menu.Item>
|
||||
<Menu.Item key="LAF">{t("joblines.fields.lbr_types.LAF")}</Menu.Item>
|
||||
<Menu.Item key="LAG">{t("joblines.fields.lbr_types.LAG")}</Menu.Item>
|
||||
<Menu.Item key="LAM">{t("joblines.fields.lbr_types.LAM")}</Menu.Item>
|
||||
<Menu.Item key="LAR">{t("joblines.fields.lbr_types.LAR")}</Menu.Item>
|
||||
<Menu.Item key="LAS">{t("joblines.fields.lbr_types.LAS")}</Menu.Item>
|
||||
<Menu.Item key="LAU">{t("joblines.fields.lbr_types.LAU")}</Menu.Item>
|
||||
<Menu.Item key="LA1">{t("joblines.fields.lbr_types.LA1")}</Menu.Item>
|
||||
<Menu.Item key="LA2">{t("joblines.fields.lbr_types.LA2")}</Menu.Item>
|
||||
<Menu.Item key="LA3">{t("joblines.fields.lbr_types.LA3")}</Menu.Item>
|
||||
<Menu.Item key="LA4">{t("joblines.fields.lbr_types.LA2")}</Menu.Item>
|
||||
<Menu.Divider />
|
||||
<Menu.Item key="clear">{t("general.labels.clear")}</Menu.Item>
|
||||
</Menu>
|
||||
);
|
||||
@@ -470,6 +427,14 @@ export function JobLinesComponent({
|
||||
return (
|
||||
<div>
|
||||
<PartsOrderModalContainer />
|
||||
{!technician && (
|
||||
<PartsOrderDrawer
|
||||
job={job}
|
||||
billsQuery={billsQuery}
|
||||
handleOnRowClick={handlePartsOrderOnRowClick}
|
||||
setPartsReceiveContext={setPartsReceiveContext}
|
||||
/>
|
||||
)}
|
||||
<PageHeader
|
||||
title={t("jobs.labels.estimatelines")}
|
||||
extra={
|
||||
@@ -486,18 +451,6 @@ export function JobLinesComponent({
|
||||
</Space>
|
||||
</Tag>
|
||||
)}
|
||||
<JobLineDispatchButton
|
||||
selectedLines={selectedLines}
|
||||
setSelectedLines={setSelectedLines}
|
||||
job={job}
|
||||
/>
|
||||
{Enhanced_Payroll.treatment === "on" && (
|
||||
<JobLineBulkAssignComponent
|
||||
selectedLines={selectedLines}
|
||||
setSelectedLines={setSelectedLines}
|
||||
job={job}
|
||||
/>
|
||||
)}
|
||||
<Button
|
||||
disabled={
|
||||
(job && !job.converted) ||
|
||||
@@ -506,6 +459,15 @@ export function JobLinesComponent({
|
||||
technician
|
||||
}
|
||||
onClick={() => {
|
||||
// setPartsOrderContext({
|
||||
// actions: { refetch: refetch },
|
||||
// context: {
|
||||
// jobId: job.id,
|
||||
// job: job,
|
||||
// linesToOrder: selectedLines,
|
||||
// },
|
||||
// });
|
||||
|
||||
setBillEnterContext({
|
||||
actions: { refetch: refetch },
|
||||
context: {
|
||||
@@ -612,9 +574,6 @@ export function JobLinesComponent({
|
||||
>
|
||||
{t("joblines.actions.new")}
|
||||
</Button>
|
||||
{bodyshop.region_config.toLowerCase().startsWith("us") && (
|
||||
<JobSendPartPriceChangeComponent job={job} />
|
||||
)}
|
||||
<JobCreateIOU job={job} selectedJobLines={selectedLines} />
|
||||
<Input.Search
|
||||
placeholder={t("general.labels.search")}
|
||||
|
||||
@@ -1,6 +1,15 @@
|
||||
import React, { useMemo, useState } from "react";
|
||||
import JobLinesComponent from "./job-lines.component";
|
||||
function JobLinesContainer({ job, joblines, refetch, form, ...rest }) {
|
||||
function JobLinesContainer({
|
||||
job,
|
||||
joblines,
|
||||
billsQuery,
|
||||
handleBillOnRowClick,
|
||||
handlePartsOrderOnRowClick,
|
||||
refetch,
|
||||
form,
|
||||
...rest
|
||||
}) {
|
||||
const [searchText, setSearchText] = useState("");
|
||||
|
||||
const jobLines = useMemo(() => {
|
||||
@@ -37,6 +46,9 @@ function JobLinesContainer({ job, joblines, refetch, form, ...rest }) {
|
||||
<JobLinesComponent
|
||||
refetch={refetch}
|
||||
jobLines={jobLines}
|
||||
billsQuery={billsQuery}
|
||||
handleBillOnRowClick={handleBillOnRowClick}
|
||||
handlePartsOrderOnRowClick={handlePartsOrderOnRowClick}
|
||||
setSearchText={setSearchText}
|
||||
job={job}
|
||||
form={form}
|
||||
|
||||
@@ -23,7 +23,6 @@ export function JobEmployeeAssignments({
|
||||
jobRO,
|
||||
body,
|
||||
refinish,
|
||||
|
||||
prep,
|
||||
csr,
|
||||
handleAdd,
|
||||
@@ -78,7 +77,7 @@ export function JobEmployeeAssignments({
|
||||
setVisibility(false);
|
||||
}}
|
||||
>
|
||||
Assign
|
||||
{t("allocations.actions.assign")}
|
||||
</Button>
|
||||
<Button onClick={() => setVisibility(false)}>Close</Button>
|
||||
</Space>
|
||||
|
||||
@@ -14,8 +14,8 @@ const mapStateToProps = createStructuredSelector({
|
||||
//currentUser: selectCurrentUser
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
insertAuditTrail: ({ jobid, operation }) =>
|
||||
dispatch(insertAuditTrail({ jobid, operation })),
|
||||
insertAuditTrail: ({ jobid, operation, type }) =>
|
||||
dispatch(insertAuditTrail({ jobid, operation, type })),
|
||||
});
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
@@ -43,12 +43,13 @@ export function JobEmployeeAssignmentsContainer({
|
||||
});
|
||||
if (refetch) refetch();
|
||||
|
||||
insertAuditTrail({
|
||||
jobid: job.id,
|
||||
operation: AuditTrailMapping.jobassignmentchange(operation, name),
|
||||
});
|
||||
|
||||
if (!!result.errors) {
|
||||
if (!!!result.errors) {
|
||||
insertAuditTrail({
|
||||
jobid: job.id,
|
||||
operation: AuditTrailMapping.jobassignmentchange(operation, name),
|
||||
type: "jobassignmentchange",
|
||||
});
|
||||
} else {
|
||||
notification["error"]({
|
||||
message: t("jobs.errors.assigning", {
|
||||
message: JSON.stringify(result.errors),
|
||||
@@ -66,17 +67,19 @@ export function JobEmployeeAssignmentsContainer({
|
||||
variables: { jobId: job.id, job: { [empAssignment]: null } },
|
||||
});
|
||||
|
||||
if (!!result.errors) {
|
||||
if (!!!result.errors) {
|
||||
insertAuditTrail({
|
||||
jobid: job.id,
|
||||
operation: AuditTrailMapping.jobassignmentremoved(operation),
|
||||
type: "jobassignmentremoved",
|
||||
});
|
||||
} else {
|
||||
notification["error"]({
|
||||
message: t("jobs.errors.assigning", {
|
||||
message: JSON.stringify(result.errors),
|
||||
}),
|
||||
});
|
||||
}
|
||||
insertAuditTrail({
|
||||
jobid: job.id,
|
||||
operation: AuditTrailMapping.jobassignmentremoved(operation),
|
||||
});
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,149 +0,0 @@
|
||||
import React, { useState } from "react";
|
||||
|
||||
import { useMutation } from "@apollo/client";
|
||||
import { Button, Form, Popover, Select, Space, notification } from "antd";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { UPDATE_LINE_BULK_ASSIGN } from "../../graphql/jobs-lines.queries";
|
||||
import { selectJobReadOnly } from "../../redux/application/application.selectors";
|
||||
import {
|
||||
selectBodyshop,
|
||||
selectCurrentUser,
|
||||
} from "../../redux/user/user.selectors";
|
||||
import { insertAuditTrail } from "../../redux/application/application.actions";
|
||||
import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
jobRO: selectJobReadOnly,
|
||||
currentUser: selectCurrentUser,
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
insertAuditTrail: ({ jobid, operation }) =>
|
||||
dispatch(insertAuditTrail({ jobid, operation })),
|
||||
});
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(JoblineBulkAssign);
|
||||
|
||||
export function JoblineBulkAssign({
|
||||
setSelectedLines,
|
||||
selectedLines,
|
||||
insertAuditTrail,
|
||||
bodyshop,
|
||||
jobRO,
|
||||
job,
|
||||
currentUser,
|
||||
}) {
|
||||
const [visible, setVisible] = useState(false);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [form] = Form.useForm();
|
||||
|
||||
const { t } = useTranslation();
|
||||
const [assignLines] = useMutation(UPDATE_LINE_BULK_ASSIGN);
|
||||
|
||||
const handleConvert = async (values) => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const result = await assignLines({
|
||||
variables: {
|
||||
jobline: {
|
||||
assigned_team: values.assigned_team,
|
||||
},
|
||||
ids: selectedLines.map((l) => l.id),
|
||||
},
|
||||
});
|
||||
if (result.errors) {
|
||||
notification.open({
|
||||
type: "error",
|
||||
message: t("parts_dispatch.errors.creating", {
|
||||
error: JSON.stringify(result.errors),
|
||||
}),
|
||||
});
|
||||
} else {
|
||||
//Insert the audit trail here.
|
||||
|
||||
const teamName = bodyshop.employee_teams.find(
|
||||
(et) => et.id === values.assigned_team
|
||||
)?.name;
|
||||
|
||||
const hours = selectedLines.reduce(
|
||||
(acc, val) => (acc += val.mod_lb_hrs),
|
||||
0
|
||||
);
|
||||
|
||||
insertAuditTrail({
|
||||
jobid: job.id,
|
||||
operation: AuditTrailMapping.assignedlinehours(
|
||||
teamName,
|
||||
hours.toFixed(1)
|
||||
),
|
||||
});
|
||||
setSelectedLines([]);
|
||||
setVisible(false);
|
||||
}
|
||||
} catch (error) {
|
||||
notification.open({
|
||||
type: "error",
|
||||
message: t("parts_dispatch.errors.creating", {
|
||||
error: error,
|
||||
}),
|
||||
});
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const popMenu = (
|
||||
<div>
|
||||
<Form layout="vertical" form={form} onFinish={handleConvert}>
|
||||
<Form.Item
|
||||
name={"assigned_team"}
|
||||
label={t("joblines.fields.assigned_team")}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
//message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Select
|
||||
showSearch
|
||||
style={{ width: 200 }}
|
||||
optionFilterProp="children"
|
||||
filterOption={(input, option) =>
|
||||
option.props.children
|
||||
.toLowerCase()
|
||||
.indexOf(input.toLowerCase()) >= 0
|
||||
}
|
||||
>
|
||||
{bodyshop.employee_teams.map((team) => (
|
||||
<Select.Option value={team.id} key={team.id} name={team.name}>
|
||||
{team.name}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
|
||||
<Space wrap>
|
||||
<Button type="danger" onClick={() => form.submit()} loading={loading}>
|
||||
{t("general.actions.save")}
|
||||
</Button>
|
||||
<Button onClick={() => setVisible(false)}>
|
||||
{t("general.actions.cancel")}
|
||||
</Button>
|
||||
</Space>
|
||||
</Form>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<Popover open={visible} content={popMenu}>
|
||||
<Button
|
||||
disabled={selectedLines.length === 0 || jobRO}
|
||||
loading={loading}
|
||||
onClick={() => setVisible(true)}
|
||||
>
|
||||
{t("joblines.actions.assign_team", { count: selectedLines.length })}
|
||||
</Button>
|
||||
</Popover>
|
||||
);
|
||||
}
|
||||
@@ -23,14 +23,18 @@ import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { UPDATE_JOB_LINE } from "../../graphql/jobs-lines.queries";
|
||||
import { insertAuditTrail } from "../../redux/application/application.actions";
|
||||
import { selectTechnician } from "../../redux/tech/tech.selectors";
|
||||
import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
//currentUser: selectCurrentUser
|
||||
technician: selectTechnician,
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
insertAuditTrail: ({ jobid, operation }) =>
|
||||
dispatch(insertAuditTrail({ jobid, operation })),
|
||||
insertAuditTrail: ({ jobid, operation, type }) =>
|
||||
dispatch(insertAuditTrail({ jobid, operation, type })),
|
||||
});
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
@@ -41,6 +45,7 @@ export function JobLineConvertToLabor({
|
||||
jobline,
|
||||
job,
|
||||
insertAuditTrail,
|
||||
technician,
|
||||
...otherBtnProps
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
@@ -113,6 +118,7 @@ export function JobLineConvertToLabor({
|
||||
hours: calculateAdjustment({ mod_lbr_ty, job, jobline }).toFixed(1),
|
||||
mod_lbr_ty,
|
||||
}),
|
||||
type: "jobmodifylbradj",
|
||||
});
|
||||
setLoading(false);
|
||||
setVisibility(false);
|
||||
@@ -221,7 +227,7 @@ export function JobLineConvertToLabor({
|
||||
return (
|
||||
<>
|
||||
{children}
|
||||
{jobline.act_price !== 0 && (
|
||||
{jobline.act_price !== 0 && !technician && (
|
||||
<Popover
|
||||
disabled={jobline.convertedtolbr}
|
||||
content={overlay}
|
||||
|
||||
@@ -1,167 +0,0 @@
|
||||
import React, { useState } from "react";
|
||||
|
||||
import { useMutation } from "@apollo/client";
|
||||
import { Button, Form, Popover, Select, Space, notification } from "antd";
|
||||
import moment from "moment";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { INSERT_PARTS_DISPATCH } from "../../graphql/parts-dispatch.queries";
|
||||
import { selectJobReadOnly } from "../../redux/application/application.selectors";
|
||||
import {
|
||||
selectBodyshop,
|
||||
selectCurrentUser,
|
||||
} from "../../redux/user/user.selectors";
|
||||
import { GenerateDocument } from "../../utils/RenderTemplate";
|
||||
import { TemplateList } from "../../utils/TemplateConstants";
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
jobRO: selectJobReadOnly,
|
||||
currentUser: selectCurrentUser,
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||
});
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(JobLineDispatchButton);
|
||||
|
||||
export function JobLineDispatchButton({
|
||||
setSelectedLines,
|
||||
selectedLines,
|
||||
|
||||
bodyshop,
|
||||
jobRO,
|
||||
job,
|
||||
currentUser,
|
||||
}) {
|
||||
const [visible, setVisible] = useState(false);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [form] = Form.useForm();
|
||||
const Templates = TemplateList("job_special", {
|
||||
ro_number: job.ro_number,
|
||||
});
|
||||
const { t } = useTranslation();
|
||||
const [dispatchLines] = useMutation(INSERT_PARTS_DISPATCH);
|
||||
|
||||
const handleConvert = async (values) => {
|
||||
try {
|
||||
setLoading(true);
|
||||
//THIS HAS NOT YET BEEN TESTED. START BY FINISHING THIS FUNCTION.
|
||||
const result = await dispatchLines({
|
||||
variables: {
|
||||
partsDispatch: {
|
||||
dispatched_at: moment(),
|
||||
employeeid: values.employeeid,
|
||||
jobid: job.id,
|
||||
dispatched_by: currentUser.email,
|
||||
parts_dispatch_lines: {
|
||||
data: selectedLines.map((l) => ({
|
||||
joblineid: l.id,
|
||||
quantity: l.part_qty,
|
||||
})),
|
||||
},
|
||||
},
|
||||
//joblineids: selectedLines.map((l) => l.id),
|
||||
},
|
||||
});
|
||||
if (result.errors) {
|
||||
notification.open({
|
||||
type: "error",
|
||||
message: t("parts_dispatch.errors.creating", {
|
||||
error: result.errors,
|
||||
}),
|
||||
});
|
||||
} else {
|
||||
setSelectedLines([]);
|
||||
await GenerateDocument(
|
||||
{
|
||||
name: Templates.parts_dispatch.key,
|
||||
variables: {
|
||||
id: result.data.insert_parts_dispatch_one.id,
|
||||
},
|
||||
},
|
||||
{},
|
||||
"p"
|
||||
);
|
||||
}
|
||||
setVisible(false);
|
||||
} catch (error) {
|
||||
notification.open({
|
||||
type: "error",
|
||||
message: t("parts_dispatch.errors.creating", {
|
||||
error: error,
|
||||
}),
|
||||
});
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const popMenu = (
|
||||
<div>
|
||||
<Form layout="vertical" form={form} onFinish={handleConvert}>
|
||||
<Form.Item
|
||||
name={"employeeid"}
|
||||
label={t("timetickets.fields.employee")}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
//message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Select
|
||||
showSearch
|
||||
style={{ width: 200 }}
|
||||
optionFilterProp="children"
|
||||
filterOption={(input, option) =>
|
||||
option.props.children
|
||||
.toLowerCase()
|
||||
.indexOf(input.toLowerCase()) >= 0
|
||||
}
|
||||
>
|
||||
{bodyshop.employees
|
||||
.filter((emp) => emp.active)
|
||||
.map((emp) => (
|
||||
<Select.Option
|
||||
value={emp.id}
|
||||
key={emp.id}
|
||||
name={`${emp.first_name} ${emp.last_name}`}
|
||||
>
|
||||
{`${emp.first_name} ${emp.last_name}`}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
|
||||
<Space wrap>
|
||||
<Button
|
||||
type="danger"
|
||||
onClick={() => form.submit()}
|
||||
loading={loading}
|
||||
disabled={selectedLines.length === 0}
|
||||
>
|
||||
{t("general.actions.save")}
|
||||
</Button>
|
||||
<Button onClick={() => setVisible(false)}>
|
||||
{t("general.actions.cancel")}
|
||||
</Button>
|
||||
</Space>
|
||||
</Form>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<Popover open={visible} content={popMenu}>
|
||||
<Button
|
||||
disabled={selectedLines.length === 0 || jobRO}
|
||||
loading={loading}
|
||||
onClick={() => setVisible(true)}
|
||||
>
|
||||
{t("joblines.actions.dispatchparts", { count: selectedLines.length })}
|
||||
</Button>
|
||||
</Popover>
|
||||
);
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useMutation } from "@apollo/client";
|
||||
import { notification, Select, Space } from "antd";
|
||||
import { notification, Select } from "antd";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
@@ -77,10 +77,7 @@ export function JobLineLocationPopup({ bodyshop, jobline, disabled }) {
|
||||
style={{ width: "100%", minHeight: "2rem", cursor: "pointer" }}
|
||||
onClick={() => !disabled && setEditing(true)}
|
||||
>
|
||||
<Space wrap>
|
||||
{jobline.location}
|
||||
{jobline.parts_dispatch_lines?.length > 0 && "-Disp"}
|
||||
</Space>
|
||||
{jobline.location}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,115 +0,0 @@
|
||||
import { notification, Select } from "antd";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useMutation } from "@apollo/client";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { UPDATE_JOB_LINE } from "../../graphql/jobs-lines.queries";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
|
||||
import { insertAuditTrail } from "../../redux/application/application.actions";
|
||||
import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
//currentUser: selectCurrentUser
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
insertAuditTrail: ({ jobid, operation }) =>
|
||||
dispatch(insertAuditTrail({ jobid, operation })),
|
||||
});
|
||||
|
||||
export function JoblineTeamAssignment({
|
||||
bodyshop,
|
||||
jobline,
|
||||
disabled,
|
||||
jobId,
|
||||
insertAuditTrail,
|
||||
}) {
|
||||
const [editing, setEditing] = useState(false);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [assignedTeam, setAssignedTeam] = useState(jobline.assigned_team);
|
||||
const [updateJob] = useMutation(UPDATE_JOB_LINE);
|
||||
const { t } = useTranslation();
|
||||
|
||||
useEffect(() => {
|
||||
if (editing) setAssignedTeam(jobline.assigned_team);
|
||||
}, [editing, jobline.assigned_team]);
|
||||
|
||||
const handleChange = (e) => {
|
||||
setAssignedTeam(e);
|
||||
};
|
||||
|
||||
const handleSave = async (e) => {
|
||||
setLoading(true);
|
||||
const result = await updateJob({
|
||||
variables: {
|
||||
lineId: jobline.id,
|
||||
line: { assigned_team: assignedTeam },
|
||||
},
|
||||
});
|
||||
|
||||
if (!!!result.errors) {
|
||||
notification["success"]({ message: t("joblines.successes.saved") });
|
||||
//insert the audit trail here.
|
||||
const teamName = bodyshop.employee_teams.find(
|
||||
(et) => et.id === assignedTeam
|
||||
)?.name;
|
||||
insertAuditTrail({
|
||||
jobid: jobId,
|
||||
operation: AuditTrailMapping.assignedlinehours(
|
||||
teamName,
|
||||
jobline.mod_lb_hrs
|
||||
),
|
||||
});
|
||||
} else {
|
||||
notification["error"]({
|
||||
message: t("joblines.errors.saving", {
|
||||
error: JSON.stringify(result.errors),
|
||||
}),
|
||||
});
|
||||
}
|
||||
setLoading(false);
|
||||
setEditing(false);
|
||||
};
|
||||
|
||||
if (editing)
|
||||
return (
|
||||
<div>
|
||||
<LoadingSpinner loading={loading}>
|
||||
<Select
|
||||
autoFocus
|
||||
allowClear
|
||||
dropdownMatchSelectWidth={100}
|
||||
value={assignedTeam}
|
||||
onSelect={handleChange}
|
||||
onBlur={handleSave}
|
||||
onClear={() => handleChange(null)}
|
||||
>
|
||||
{Object.values(bodyshop.employee_teams).map((s, idx) => (
|
||||
<Select.Option key={idx} value={s.id}>
|
||||
{s.name}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</LoadingSpinner>
|
||||
</div>
|
||||
);
|
||||
|
||||
const team = bodyshop.employee_teams.find(
|
||||
(tm) => tm.id === jobline.assigned_team
|
||||
);
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{ width: "100%", minHeight: "1rem", cursor: "pointer" }}
|
||||
onClick={() => !disabled && setEditing(true)}
|
||||
>
|
||||
{team?.name}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(JoblineTeamAssignment);
|
||||
@@ -1,18 +0,0 @@
|
||||
import { Alert } from "antd";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
export default function JobProfileDataWarning({ job }) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
let missingProfileInfo =
|
||||
Object.keys(job.cieca_pft).length === 0 ||
|
||||
Object.keys(job.cieca_pfl).length === 0 ||
|
||||
Object.keys(job.materials).length === 0;
|
||||
|
||||
if (missingProfileInfo)
|
||||
return (
|
||||
<Alert type="error" message={t("jobs.labels.missingprofileinfo")}></Alert>
|
||||
);
|
||||
return null;
|
||||
}
|
||||
@@ -82,7 +82,7 @@ export default function JobReconciliationBillsTable({
|
||||
state.sortedInfo.order,
|
||||
|
||||
render: (text, record) => (
|
||||
<Checkbox disabled checked={record.bill.is_credit_memo} />
|
||||
<Checkbox checked={record.bill.is_credit_memo} />
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
@@ -19,8 +19,7 @@ export default function JobReconciliationModalComponent({ job, bills }) {
|
||||
|
||||
const jobLineData = job.joblines.filter(
|
||||
(j) =>
|
||||
(j.part_type !== "PAE" && j.act_price !== 0 && j.part_qty !== 0) ||
|
||||
j.misc_amt !== 0 ||
|
||||
(j.part_type !== null && j.part_type !== "PAE") ||
|
||||
(j.line_desc &&
|
||||
j.line_desc.toLowerCase().includes("towing") &&
|
||||
j.lbr_op === "OP13") ||
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
import { Button, notification } from "antd";
|
||||
import axios from "axios";
|
||||
import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
export default function JobSendPartPriceChangeComponent({ job }) {
|
||||
const { t } = useTranslation();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const handleClick = async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const ppcData = await axios.post("/job/ppc", { jobid: job.id });
|
||||
await axios.post("http://localhost:1337/ppc/", ppcData.data);
|
||||
} catch (error) {
|
||||
notification.open({
|
||||
type: "error",
|
||||
message: t("jobs.errors.partspricechange", {
|
||||
error: JSON.stringify(error),
|
||||
}),
|
||||
});
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Button onClick={handleClick} loading={loading}>
|
||||
{t("jobs.actions.sendpartspricechange")}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
@@ -67,8 +67,7 @@ export function JobsTotalsTableComponent({ jobRO, currentUser, job }) {
|
||||
<JobTotalsTableTotals job={job} />
|
||||
</Card>
|
||||
</Col>
|
||||
{(currentUser.email.includes("@imex.") ||
|
||||
currentUser.email.includes("@rome.")) && (
|
||||
{currentUser.email.includes("@imex.") && (
|
||||
<Col span={24}>
|
||||
<Card title="DEVELOPMENT USE ONLY">
|
||||
<JobCalculateTotals job={job} disabled={jobRO} />
|
||||
|
||||
@@ -123,10 +123,11 @@ export default function JobTotalsTableLabor({ job }) {
|
||||
<Space>
|
||||
{t("jobs.labels.mapa")}
|
||||
{job.materials &&
|
||||
job.materials.MAPA &&
|
||||
job.materials.MAPA.cal_maxdlr !== undefined &&
|
||||
job.materials.mapa &&
|
||||
job.materials.mapa.cal_maxdlr &&
|
||||
job.materials.mapa.cal_maxdlr > 0 &&
|
||||
t("jobs.labels.threshhold", {
|
||||
amount: job.materials.MAPA.cal_maxdlr,
|
||||
amount: job.materials.mapa.cal_maxdlr,
|
||||
})}
|
||||
</Space>
|
||||
</Table.Summary.Cell>
|
||||
@@ -147,10 +148,11 @@ export default function JobTotalsTableLabor({ job }) {
|
||||
<Space wrap>
|
||||
{t("jobs.labels.mash")}
|
||||
{job.materials &&
|
||||
job.materials.MASH &&
|
||||
job.materials.MASH.cal_maxdlr !== undefined &&
|
||||
job.materials.mash &&
|
||||
job.materials.mash.cal_maxdlr &&
|
||||
job.materials.mash.cal_maxdlr > 0 &&
|
||||
t("jobs.labels.threshhold", {
|
||||
amount: job.materials.MASH.cal_maxdlr,
|
||||
amount: job.materials.mash.cal_maxdlr,
|
||||
})}
|
||||
</Space>
|
||||
</Table.Summary.Cell>
|
||||
|
||||
@@ -11,22 +11,6 @@ export default function JobTotalsTableParts({ job }) {
|
||||
filteredInfo: {},
|
||||
});
|
||||
|
||||
const insuranceAdjustments = useMemo(() => {
|
||||
if (!job.job_totals) return [];
|
||||
if (!job.job_totals?.parts?.adjustments) return [];
|
||||
const adjs = [];
|
||||
Object.keys(job.job_totals?.parts?.adjustments).forEach((key) => {
|
||||
if (Dinero(job.job_totals?.parts?.adjustments[key]).getAmount() !== 0) {
|
||||
adjs.push({
|
||||
id: key,
|
||||
amount: Dinero(job.job_totals.parts.adjustments[key]),
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return adjs;
|
||||
}, [job.job_totals]);
|
||||
|
||||
const data = useMemo(() => {
|
||||
return Object.keys(job.job_totals.parts.parts.list)
|
||||
.filter(
|
||||
@@ -90,11 +74,11 @@ export default function JobTotalsTableParts({ job }) {
|
||||
<Table.Summary.Cell>
|
||||
{t("jobs.labels.prt_dsmk_total")}
|
||||
</Table.Summary.Cell>
|
||||
|
||||
<Table.Summary.Cell align="right">
|
||||
{Dinero(job.job_totals.parts.parts.prt_dsmk_total).toFormat()}
|
||||
</Table.Summary.Cell>
|
||||
</Table.Summary.Row>
|
||||
|
||||
<Table.Summary.Row>
|
||||
<Table.Summary.Cell>
|
||||
<strong>{t("jobs.labels.partstotal")}</strong>
|
||||
@@ -106,24 +90,6 @@ export default function JobTotalsTableParts({ job }) {
|
||||
</strong>
|
||||
</Table.Summary.Cell>
|
||||
</Table.Summary.Row>
|
||||
{insuranceAdjustments.length > 0 && (
|
||||
<Table.Summary.Row>
|
||||
<Table.Summary.Cell colSpan={24}>
|
||||
{t("jobs.labels.profileadjustments")}
|
||||
</Table.Summary.Cell>
|
||||
</Table.Summary.Row>
|
||||
)}
|
||||
{insuranceAdjustments.map((adj, idx) => (
|
||||
<Table.Summary.Row key={idx}>
|
||||
<Table.Summary.Cell>
|
||||
{t(`jobs.fields.${adj.id.toLowerCase()}`)}
|
||||
</Table.Summary.Cell>
|
||||
|
||||
<Table.Summary.Cell align="right">
|
||||
{adj.amount.toFormat()}
|
||||
</Table.Summary.Cell>
|
||||
</Table.Summary.Row>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
|
||||
@@ -28,109 +28,26 @@ export function JobTotalsTableTotals({ bodyshop, job }) {
|
||||
total: job.job_totals.totals.subtotal,
|
||||
bold: true,
|
||||
},
|
||||
|
||||
...(job.job_totals.totals.us_sales_tax_breakdown
|
||||
{
|
||||
key: t("jobs.labels.local_tax_amt"),
|
||||
total: job.job_totals.totals.local_tax,
|
||||
},
|
||||
{
|
||||
key: t("jobs.labels.state_tax_amt"),
|
||||
total: job.job_totals.totals.state_tax,
|
||||
},
|
||||
...(bodyshop.region_config === "CA_BC"
|
||||
? [
|
||||
{
|
||||
key: `${
|
||||
bodyshop.md_responsibility_centers.taxes.tax_ty1?.tax_type1 ||
|
||||
"T1"
|
||||
} - ${[
|
||||
job.cieca_pft.ty1_rate1,
|
||||
job.cieca_pft.ty1_rate2,
|
||||
job.cieca_pft.ty1_rate3,
|
||||
job.cieca_pft.ty1_rate4,
|
||||
job.cieca_pft.ty1_rate5,
|
||||
]
|
||||
.filter((i) => i > 0)
|
||||
.join(", ")}%`,
|
||||
total: job.job_totals.totals.us_sales_tax_breakdown.ty1Tax,
|
||||
key: t("jobs.fields.ca_bc_pvrt"),
|
||||
total: job.job_totals.additional.pvrt,
|
||||
},
|
||||
{
|
||||
key: `${
|
||||
bodyshop.md_responsibility_centers.taxes.tax_ty2?.tax_type2 ||
|
||||
"T2"
|
||||
} - ${[
|
||||
job.cieca_pft.ty2_rate1,
|
||||
job.cieca_pft.ty2_rate2,
|
||||
job.cieca_pft.ty2_rate3,
|
||||
job.cieca_pft.ty2_rate4,
|
||||
job.cieca_pft.ty2_rate5,
|
||||
]
|
||||
.filter((i) => i > 0)
|
||||
.join(", ")}%`,
|
||||
total: job.job_totals.totals.us_sales_tax_breakdown.ty2Tax,
|
||||
},
|
||||
{
|
||||
key: `${
|
||||
bodyshop.md_responsibility_centers.taxes.tax_ty3?.tax_type3 ||
|
||||
"T3"
|
||||
} - ${[
|
||||
job.cieca_pft.ty3_rate1,
|
||||
job.cieca_pft.ty3_rate2,
|
||||
job.cieca_pft.ty3_rate3,
|
||||
job.cieca_pft.ty3_rate4,
|
||||
job.cieca_pft.ty3_rate5,
|
||||
]
|
||||
.filter((i) => i > 0)
|
||||
.join(", ")}%`,
|
||||
total: job.job_totals.totals.us_sales_tax_breakdown.ty3Tax,
|
||||
},
|
||||
{
|
||||
key: `${
|
||||
bodyshop.md_responsibility_centers.taxes.tax_ty4?.tax_type4 ||
|
||||
"T4"
|
||||
} - ${[
|
||||
job.cieca_pft.ty4_rate1,
|
||||
job.cieca_pft.ty4_rate2,
|
||||
job.cieca_pft.ty4_rate3,
|
||||
job.cieca_pft.ty4_rate4,
|
||||
job.cieca_pft.ty4_rate5,
|
||||
]
|
||||
.filter((i) => i > 0)
|
||||
.join(", ")}%`,
|
||||
total: job.job_totals.totals.us_sales_tax_breakdown.ty4Tax,
|
||||
},
|
||||
{
|
||||
key: `${
|
||||
bodyshop.md_responsibility_centers.taxes.tax_ty5?.tax_type5 ||
|
||||
"TT"
|
||||
} - ${[
|
||||
job.cieca_pft.ty5_rate1,
|
||||
job.cieca_pft.ty5_rate2,
|
||||
job.cieca_pft.ty5_rate3,
|
||||
job.cieca_pft.ty5_rate4,
|
||||
job.cieca_pft.ty5_rate5,
|
||||
]
|
||||
.filter((i) => i > 0)
|
||||
.join(", ")}%`,
|
||||
total: job.job_totals.totals.us_sales_tax_breakdown.ty5Tax,
|
||||
},
|
||||
{
|
||||
key: t("jobs.labels.total_sales_tax"),
|
||||
bold: true,
|
||||
total: Dinero(job.job_totals.totals.us_sales_tax_breakdown.ty1Tax)
|
||||
.add(
|
||||
Dinero(job.job_totals.totals.us_sales_tax_breakdown.ty2Tax)
|
||||
)
|
||||
.add(
|
||||
Dinero(job.job_totals.totals.us_sales_tax_breakdown.ty3Tax)
|
||||
)
|
||||
.add(
|
||||
Dinero(job.job_totals.totals.us_sales_tax_breakdown.ty4Tax)
|
||||
)
|
||||
.add(
|
||||
Dinero(job.job_totals.totals.us_sales_tax_breakdown.ty5Tax)
|
||||
).toJSON(),
|
||||
},
|
||||
].filter((item) => item.total.amount !== 0)
|
||||
: [
|
||||
{
|
||||
key: t("jobs.labels.state_tax_amt"),
|
||||
total: job.job_totals.totals.state_tax,
|
||||
},
|
||||
]),
|
||||
|
||||
]
|
||||
: []),
|
||||
{
|
||||
key: t("jobs.labels.federal_tax_amt"),
|
||||
total: job.job_totals.totals.federal_tax,
|
||||
},
|
||||
{
|
||||
key: t("jobs.labels.total_repairs"),
|
||||
total: job.job_totals.totals.total_repairs,
|
||||
@@ -140,10 +57,10 @@ export function JobTotalsTableTotals({ bodyshop, job }) {
|
||||
key: t("jobs.fields.ded_amt"),
|
||||
total: job.job_totals.totals.custPayable.deductible,
|
||||
},
|
||||
// {
|
||||
// key: t("jobs.fields.federal_tax_payable"),
|
||||
// total: job.job_totals.totals.custPayable.federal_tax,
|
||||
// },
|
||||
{
|
||||
key: t("jobs.fields.federal_tax_payable"),
|
||||
total: job.job_totals.totals.custPayable.federal_tax,
|
||||
},
|
||||
{
|
||||
key: t("jobs.fields.other_amount_payable"),
|
||||
total: job.job_totals.totals.custPayable.other_customer_amount,
|
||||
@@ -164,7 +81,7 @@ export function JobTotalsTableTotals({ bodyshop, job }) {
|
||||
bold: true,
|
||||
},
|
||||
];
|
||||
}, [job.job_totals, job.cieca_pft, t, bodyshop.md_responsibility_centers]);
|
||||
}, [job.job_totals, t, bodyshop.region_config]);
|
||||
|
||||
const columns = [
|
||||
{
|
||||
|
||||
@@ -14,8 +14,8 @@ const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
insertAuditTrail: ({ jobid, operation }) =>
|
||||
dispatch(insertAuditTrail({ jobid, operation })),
|
||||
insertAuditTrail: ({ jobid, operation, type }) =>
|
||||
dispatch(insertAuditTrail({ jobid, operation, type })),
|
||||
});
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(JobsAdminStatus);
|
||||
|
||||
@@ -32,6 +32,7 @@ export function JobsAdminStatus({ insertAuditTrail, bodyshop, job }) {
|
||||
insertAuditTrail({
|
||||
jobid: job.id,
|
||||
operation: AuditTrailMapping.admin_jobstatuschange(status),
|
||||
type: "admin_jobstatuschange",
|
||||
});
|
||||
// refetch();
|
||||
})
|
||||
|
||||
@@ -20,8 +20,8 @@ const mapStateToProps = createStructuredSelector({
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
insertAuditTrail: ({ jobid, operation }) =>
|
||||
dispatch(insertAuditTrail({ jobid, operation })),
|
||||
insertAuditTrail: ({ jobid, operation, type }) =>
|
||||
dispatch(insertAuditTrail({ jobid, operation, type })),
|
||||
});
|
||||
|
||||
export default connect(
|
||||
@@ -57,6 +57,7 @@ export function JobsAdminDatesChange({ insertAuditTrail, job }) {
|
||||
? DateTimeFormat(changedAuditFields[key])
|
||||
: changedAuditFields[key]
|
||||
),
|
||||
type: "admin_jobfieldchange",
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -23,8 +23,8 @@ const mapStateToProps = createStructuredSelector({
|
||||
currentUser: selectCurrentUser,
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
insertAuditTrail: ({ jobid, operation }) =>
|
||||
dispatch(insertAuditTrail({ jobid, operation })),
|
||||
insertAuditTrail: ({ jobid, operation, type }) =>
|
||||
dispatch(insertAuditTrail({ jobid, operation, type })),
|
||||
});
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
@@ -59,6 +59,7 @@ export function JobAdminMarkReexport({
|
||||
insertAuditTrail({
|
||||
jobid: job.id,
|
||||
operation: AuditTrailMapping.admin_jobmarkforreexport(),
|
||||
type: "admin_jobmarkforreexport",
|
||||
});
|
||||
} else {
|
||||
notification["error"]({
|
||||
@@ -99,6 +100,7 @@ export function JobAdminMarkReexport({
|
||||
insertAuditTrail({
|
||||
jobid: job.id,
|
||||
operation: AuditTrailMapping.admin_jobmarkexported(),
|
||||
type: "admin_jobmarkexported",
|
||||
});
|
||||
} else {
|
||||
notification["error"]({
|
||||
@@ -124,6 +126,7 @@ export function JobAdminMarkReexport({
|
||||
insertAuditTrail({
|
||||
jobid: job.id,
|
||||
operation: AuditTrailMapping.admin_jobuninvoice(),
|
||||
type: "admin_jobuninvoice",
|
||||
});
|
||||
} else {
|
||||
notification["error"]({
|
||||
|
||||
@@ -10,8 +10,8 @@ import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
insertAuditTrail: ({ jobid, operation }) =>
|
||||
dispatch(insertAuditTrail({ jobid, operation })),
|
||||
insertAuditTrail: ({ jobid, operation, type }) =>
|
||||
dispatch(insertAuditTrail({ jobid, operation, type })),
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(JobsAdminRemoveAR);
|
||||
@@ -34,6 +34,7 @@ export function JobsAdminRemoveAR({ insertAuditTrail, job }) {
|
||||
insertAuditTrail({
|
||||
jobid: job.id,
|
||||
operation: AuditTrailMapping.admin_job_remove_from_ar(value),
|
||||
type: "admin_job_remove_from_ar",
|
||||
});
|
||||
setSwitchValue(value);
|
||||
} else {
|
||||
|
||||
@@ -17,8 +17,8 @@ const mapStateToProps = createStructuredSelector({
|
||||
currentUser: selectCurrentUser,
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
insertAuditTrail: ({ jobid, operation }) =>
|
||||
dispatch(insertAuditTrail({ jobid, operation })),
|
||||
insertAuditTrail: ({ jobid, operation, type }) =>
|
||||
dispatch(insertAuditTrail({ jobid, operation, type })),
|
||||
});
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(JobsAdminUnvoid);
|
||||
|
||||
@@ -49,6 +49,7 @@ export function JobsAdminUnvoid({
|
||||
insertAuditTrail({
|
||||
jobid: job.id,
|
||||
operation: AuditTrailMapping.admin_jobunvoid(),
|
||||
type: "admin_jobunvoid",
|
||||
});
|
||||
} else {
|
||||
notification["error"]({
|
||||
|
||||
@@ -25,7 +25,7 @@ export const GetSupplementDelta = async (client, jobId, newLines) => {
|
||||
//Found a relevant matching line. Add it to lines to update.
|
||||
linesToUpdate.push({
|
||||
id: existingLines[matchingIndex].id,
|
||||
newData: { ...newLine, removed: false, act_price_before_ppc: null },
|
||||
newData: { ...newLine, removed: false },
|
||||
});
|
||||
|
||||
//Splice out item we found for performance.
|
||||
|
||||
@@ -6,9 +6,9 @@ import {
|
||||
useQuery,
|
||||
} from "@apollo/client";
|
||||
import { useTreatments } from "@splitsoftware/splitio-react";
|
||||
import { Button, Col, Row, notification } from "antd";
|
||||
import { Col, Row, notification } from "antd";
|
||||
import Axios from "axios";
|
||||
import _ from "lodash";
|
||||
import Dinero from "dinero.js";
|
||||
import moment from "moment-business-days";
|
||||
import queryString from "query-string";
|
||||
import React, { useCallback, useEffect, useState } from "react";
|
||||
@@ -31,7 +31,7 @@ import {
|
||||
selectCurrentUser,
|
||||
} from "../../redux/user/user.selectors";
|
||||
import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
||||
//import confirmDialog from "../../utils/asyncConfirm";
|
||||
import confirmDialog from "../../utils/asyncConfirm";
|
||||
import CriticalPartsScan from "../../utils/criticalPartsScan";
|
||||
import AlertComponent from "../alert/alert.component";
|
||||
import JobsAvailableScan from "../jobs-available-scan/jobs-available-scan.component";
|
||||
@@ -47,8 +47,8 @@ const mapStateToProps = createStructuredSelector({
|
||||
currentUser: selectCurrentUser,
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
insertAuditTrail: ({ jobid, operation }) =>
|
||||
dispatch(insertAuditTrail({ jobid, operation })),
|
||||
insertAuditTrail: ({ jobid, operation, type }) =>
|
||||
dispatch(insertAuditTrail({ jobid, operation, type })),
|
||||
});
|
||||
export function JobsAvailableContainer({
|
||||
bodyshop,
|
||||
@@ -100,15 +100,14 @@ export function JobsAvailableContainer({
|
||||
const modalSearchState = useState("");
|
||||
|
||||
//Import Scenario
|
||||
const onOwnerFindModalOk = async (lazyData) => {
|
||||
const onOwnerFindModalOk = async () => {
|
||||
logImEXEvent("job_import_new");
|
||||
|
||||
setOwnerModalVisible(false);
|
||||
|
||||
setInsertLoading(true);
|
||||
const estData = replaceEmpty(
|
||||
lazyData?.available_jobs_by_pk || estDataRaw.data.available_jobs_by_pk
|
||||
);
|
||||
|
||||
const estData = replaceEmpty(estDataRaw.data.available_jobs_by_pk);
|
||||
|
||||
if (!(estData && estData.est_data)) {
|
||||
//We don't have the right data. Error!
|
||||
@@ -118,21 +117,17 @@ export function JobsAvailableContainer({
|
||||
});
|
||||
return;
|
||||
}
|
||||
// if (process.env.REACT_APP_COUNTRY === "USA") {
|
||||
//Massage the CCC file set to remove duplicate UNQ_SEQ.
|
||||
await ResolveCCCLineIssues(estData.est_data, bodyshop);
|
||||
// } else {
|
||||
//IO-539 Check for Parts Rate on PAL for SGI use case.
|
||||
await CheckTaxRates(estData.est_data, bodyshop);
|
||||
// }
|
||||
// const newTotals = (
|
||||
// await Axios.post("/job/totals", {
|
||||
// job: {
|
||||
// ...estData.est_data,
|
||||
// joblines: estData.est_data.joblines.data,
|
||||
// },
|
||||
// })
|
||||
// ).data;
|
||||
|
||||
const newTotals = (
|
||||
await Axios.post("/job/totals", {
|
||||
job: {
|
||||
...estData.est_data,
|
||||
joblines: estData.est_data.joblines.data,
|
||||
},
|
||||
})
|
||||
).data;
|
||||
|
||||
let existingVehicles;
|
||||
if (estData.est_data.v_vin) {
|
||||
@@ -147,9 +142,9 @@ export function JobsAvailableContainer({
|
||||
|
||||
const newJob = {
|
||||
...estData.est_data,
|
||||
// clm_total: Dinero(newTotals.totals.total_repairs).toFormat("0.00"),
|
||||
// owner_owing: Dinero(newTotals.totals.custPayable.total).toFormat("0.00"),
|
||||
// job_totals: newTotals,
|
||||
clm_total: Dinero(newTotals.totals.total_repairs).toFormat("0.00"),
|
||||
owner_owing: Dinero(newTotals.totals.custPayable.total).toFormat("0.00"),
|
||||
job_totals: newTotals,
|
||||
date_open: moment(),
|
||||
status: bodyshop.md_ro_statuses.default_imported,
|
||||
notes: {
|
||||
@@ -173,23 +168,17 @@ export function JobsAvailableContainer({
|
||||
delete newJob.vehicle;
|
||||
}
|
||||
|
||||
if (typeof newJob.kmin === "string") {
|
||||
newJob.kmin = null;
|
||||
}
|
||||
|
||||
try {
|
||||
const r = await insertNewJob({
|
||||
variables: {
|
||||
job: newJob,
|
||||
},
|
||||
});
|
||||
await Axios.post("/job/totalsssu", {
|
||||
id: r.data.insert_jobs.returning[0].id,
|
||||
});
|
||||
|
||||
if (CriticalPartsScanning.treatment === "on") {
|
||||
CriticalPartsScan(r.data.insert_jobs.returning[0].id);
|
||||
}
|
||||
|
||||
notification["success"]({
|
||||
message: t("jobs.successes.created"),
|
||||
onClick: () => {
|
||||
@@ -201,9 +190,10 @@ export function JobsAvailableContainer({
|
||||
insertAuditTrail({
|
||||
jobid: r.data.insert_jobs.returning[0].id,
|
||||
operation: AuditTrailMapping.jobimported(),
|
||||
type: "jobimported",
|
||||
});
|
||||
|
||||
await deleteJob({
|
||||
deleteJob({
|
||||
variables: { id: estData.id },
|
||||
}).then((r) => {
|
||||
refetch();
|
||||
@@ -211,12 +201,12 @@ export function JobsAvailableContainer({
|
||||
});
|
||||
|
||||
setPartsQueueToggle(bodyshop.md_functionality_toggles.parts_queue_toggle);
|
||||
} catch (r) {
|
||||
} catch (err) {
|
||||
//error while inserting
|
||||
notification["error"]({
|
||||
message: t("jobs.errors.creating", { error: r.message }),
|
||||
message: t("jobs.errors.creating", { error: err.message }),
|
||||
});
|
||||
refetch().catch((err) => {
|
||||
refetch().catch((e) => {
|
||||
console.error(
|
||||
`Something went wrong in jobs available table container - ${
|
||||
err.message || ""
|
||||
@@ -225,7 +215,6 @@ export function JobsAvailableContainer({
|
||||
});
|
||||
setInsertLoading(false);
|
||||
setPartsQueueToggle(bodyshop.md_functionality_toggles.parts_queue_toggle);
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
@@ -249,7 +238,6 @@ export function JobsAvailableContainer({
|
||||
let supp = replaceEmpty({ ...estData.est_data });
|
||||
//IO-539 Check for Parts Rate on PAL for SGI use case.
|
||||
await CheckTaxRates(supp, bodyshop);
|
||||
await ResolveCCCLineIssues(supp, bodyshop);
|
||||
|
||||
if (updateSchComp.checked === true) {
|
||||
if (updateSchComp.automatic === true) {
|
||||
@@ -363,6 +351,7 @@ export function JobsAvailableContainer({
|
||||
insertAuditTrail({
|
||||
jobid: selectedJob,
|
||||
operation: AuditTrailMapping.jobsupplement(),
|
||||
type: "jobsupplement",
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -441,25 +430,6 @@ export function JobsAvailableContainer({
|
||||
updateSchComp={updateSchComp}
|
||||
setSchComp={setSchComp}
|
||||
/>
|
||||
{currentUser.email.includes("@rome.") ||
|
||||
currentUser.email.includes("@imex.") ? (
|
||||
<Button
|
||||
onClick={async () => {
|
||||
for (const record of data.available_jobs) {
|
||||
//Query the data
|
||||
console.log("Start Job", record.id);
|
||||
const { data } = await loadEstData({
|
||||
variables: { id: record.id },
|
||||
});
|
||||
console.log("Query has been awaited and is complete");
|
||||
await onOwnerFindModalOk(data);
|
||||
}
|
||||
}}
|
||||
>
|
||||
Add all jobs as new.
|
||||
</Button>
|
||||
) : null}
|
||||
|
||||
<Row gutter={[16, 16]}>
|
||||
<Col span={24}>
|
||||
<JobsAvailableTableComponent
|
||||
@@ -491,158 +461,115 @@ function replaceEmpty(someObj, replaceValue = null) {
|
||||
}
|
||||
|
||||
async function CheckTaxRates(estData, bodyshop) {
|
||||
// //LKQ Check
|
||||
// if (
|
||||
// !estData.parts_tax_rates?.PAL ||
|
||||
// estData.parts_tax_rates?.PAL?.prt_tax_rt === null ||
|
||||
// estData.parts_tax_rates?.PAL?.prt_tax_rt === 0
|
||||
// ) {
|
||||
// const res = await confirmDialog(
|
||||
// `Rome Online has detected that there is a missing tax rate for LKQ parts. Pressing OK will set the tax rate to ${bodyshop.bill_tax_rates.state_tax_rate}% and enable the rate. Pressing cancel will keep the tax rate as is.`
|
||||
// );
|
||||
// if (res) {
|
||||
// if (!estData.parts_tax_rates.PAL) {
|
||||
// estData.parts_tax_rates.PAL = {
|
||||
// prt_discp: 0,
|
||||
// prt_mktyp: true,
|
||||
// prt_mkupp: 0,
|
||||
// prt_type: "PAL",
|
||||
// };
|
||||
// }
|
||||
// estData.parts_tax_rates.PAL.prt_tax_rt =
|
||||
// bodyshop.bill_tax_rates.state_tax_rate / 100;
|
||||
// estData.parts_tax_rates.PAL.prt_tax_in = true;
|
||||
// }
|
||||
// }
|
||||
// //PAC Check
|
||||
// if (
|
||||
// !estData.parts_tax_rates?.PAC ||
|
||||
// estData.parts_tax_rates?.PAC?.prt_tax_rt === null ||
|
||||
// estData.parts_tax_rates?.PAC?.prt_tax_rt === 0
|
||||
// ) {
|
||||
// const res = await confirmDialog(
|
||||
// `Rome Online has detected that there is a missing tax rate for rechromed parts. Pressing OK will set the tax rate to ${bodyshop.bill_tax_rates.state_tax_rate}% and enable the rate. Pressing cancel will keep the tax rate as is.`
|
||||
// );
|
||||
// if (res) {
|
||||
// if (!estData.parts_tax_rates.PAC) {
|
||||
// estData.parts_tax_rates.PAC = {
|
||||
// prt_discp: 0,
|
||||
// prt_mktyp: true,
|
||||
// prt_mkupp: 0,
|
||||
// prt_type: "PAC",
|
||||
// };
|
||||
// }
|
||||
// estData.parts_tax_rates.PAC.prt_tax_rt =
|
||||
// bodyshop.bill_tax_rates.state_tax_rate / 100;
|
||||
// estData.parts_tax_rates.PAC.prt_tax_in = true;
|
||||
// }
|
||||
// }
|
||||
|
||||
//LKQ Check
|
||||
if (
|
||||
!estData.parts_tax_rates?.PAL ||
|
||||
estData.parts_tax_rates?.PAL?.prt_tax_rt === null ||
|
||||
estData.parts_tax_rates?.PAL?.prt_tax_rt === 0
|
||||
) {
|
||||
const res = await confirmDialog(
|
||||
`ImEX Online has detected that there is a missing tax rate for LKQ parts. Pressing OK will set the tax rate to ${bodyshop.bill_tax_rates.state_tax_rate}% and enable the rate. Pressing cancel will keep the tax rate as is.`
|
||||
);
|
||||
if (res) {
|
||||
if (!estData.parts_tax_rates.PAL) {
|
||||
estData.parts_tax_rates.PAL = {
|
||||
prt_discp: 0,
|
||||
prt_mktyp: true,
|
||||
prt_mkupp: 0,
|
||||
prt_type: "PAL",
|
||||
};
|
||||
}
|
||||
estData.parts_tax_rates.PAL.prt_tax_rt =
|
||||
bodyshop.bill_tax_rates.state_tax_rate / 100;
|
||||
estData.parts_tax_rates.PAL.prt_tax_in = true;
|
||||
}
|
||||
}
|
||||
//PAC Check
|
||||
if (
|
||||
!estData.parts_tax_rates?.PAC ||
|
||||
estData.parts_tax_rates?.PAC?.prt_tax_rt === null ||
|
||||
estData.parts_tax_rates?.PAC?.prt_tax_rt === 0
|
||||
) {
|
||||
const res = await confirmDialog(
|
||||
`ImEX Online has detected that there is a missing tax rate for rechromed parts. Pressing OK will set the tax rate to ${bodyshop.bill_tax_rates.state_tax_rate}% and enable the rate. Pressing cancel will keep the tax rate as is.`
|
||||
);
|
||||
if (res) {
|
||||
if (!estData.parts_tax_rates.PAC) {
|
||||
estData.parts_tax_rates.PAC = {
|
||||
prt_discp: 0,
|
||||
prt_mktyp: true,
|
||||
prt_mkupp: 0,
|
||||
prt_type: "PAC",
|
||||
};
|
||||
}
|
||||
estData.parts_tax_rates.PAC.prt_tax_rt =
|
||||
bodyshop.bill_tax_rates.state_tax_rate / 100;
|
||||
estData.parts_tax_rates.PAC.prt_tax_in = true;
|
||||
}
|
||||
}
|
||||
//PAM Check
|
||||
if (!estData.parts_tax_rates?.PAM) {
|
||||
estData.parts_tax_rates.PAM = estData.parts_tax_rates.PAC;
|
||||
if (
|
||||
!estData.parts_tax_rates?.PAM ||
|
||||
estData.parts_tax_rates?.PAM?.prt_tax_rt === null ||
|
||||
estData.parts_tax_rates?.PAM?.prt_tax_rt === 0
|
||||
) {
|
||||
const res = await confirmDialog(
|
||||
`ImEX Online has detected that there is a missing tax rate for remanufactured parts. Pressing OK will set the tax rate to ${bodyshop.bill_tax_rates.state_tax_rate}% and enable the rate. Pressing cancel will keep the tax rate as is.`
|
||||
);
|
||||
if (res) {
|
||||
if (!estData.parts_tax_rates.PAM) {
|
||||
estData.parts_tax_rates.PAM = {
|
||||
prt_discp: 0,
|
||||
prt_mktyp: true,
|
||||
prt_mkupp: 0,
|
||||
prt_type: "PAM",
|
||||
};
|
||||
}
|
||||
estData.parts_tax_rates.PAM.prt_tax_rt =
|
||||
bodyshop.bill_tax_rates.state_tax_rate / 100;
|
||||
estData.parts_tax_rates.PAM.prt_tax_in = true;
|
||||
}
|
||||
}
|
||||
|
||||
// //PAM Check
|
||||
// if (
|
||||
// !estData.parts_tax_rates?.PAM ||
|
||||
// estData.parts_tax_rates?.PAM?.prt_tax_rt === null ||
|
||||
// estData.parts_tax_rates?.PAM?.prt_tax_rt === 0
|
||||
// ) {
|
||||
// const res = await confirmDialog(
|
||||
// `Rome Online has detected that there is a missing tax rate for remanufactured parts. Pressing OK will set the tax rate to ${bodyshop.bill_tax_rates.state_tax_rate}% and enable the rate. Pressing cancel will keep the tax rate as is.`
|
||||
// );
|
||||
// if (res) {
|
||||
// if (!estData.parts_tax_rates.PAM) {
|
||||
// estData.parts_tax_rates.PAM = {
|
||||
// prt_discp: 0,
|
||||
// prt_mktyp: true,
|
||||
// prt_mkupp: 0,
|
||||
// prt_type: "PAM",
|
||||
// };
|
||||
// }
|
||||
// estData.parts_tax_rates.PAM.prt_tax_rt =
|
||||
// bodyshop.bill_tax_rates.state_tax_rate / 100;
|
||||
// estData.parts_tax_rates.PAM.prt_tax_in = true;
|
||||
// }
|
||||
// }
|
||||
|
||||
// if (
|
||||
// !estData.parts_tax_rates?.PAR ||
|
||||
// estData.parts_tax_rates?.PAR?.prt_tax_rt === null ||
|
||||
// estData.parts_tax_rates?.PAR?.prt_tax_rt === 0
|
||||
// ) {
|
||||
// const res = await confirmDialog(
|
||||
// `Rome Online has detected that there is a missing tax rate for recored parts. Pressing OK will set the tax rate to ${bodyshop.bill_tax_rates.state_tax_rate}% and enable the rate. Pressing cancel will keep the tax rate as is.`
|
||||
// );
|
||||
// if (res) {
|
||||
// if (!estData.parts_tax_rates.PAR) {
|
||||
// estData.parts_tax_rates.PAR = {
|
||||
// prt_discp: 0,
|
||||
// prt_mktyp: true,
|
||||
// prt_mkupp: 0,
|
||||
// prt_type: "PAR",
|
||||
// };
|
||||
// }
|
||||
// estData.parts_tax_rates.PAR.prt_tax_rt =
|
||||
// bodyshop.bill_tax_rates.state_tax_rate / 100;
|
||||
// estData.parts_tax_rates.PAR.prt_tax_in = true;
|
||||
// }
|
||||
// }
|
||||
if (
|
||||
!estData.parts_tax_rates?.PAR ||
|
||||
estData.parts_tax_rates?.PAR?.prt_tax_rt === null ||
|
||||
estData.parts_tax_rates?.PAR?.prt_tax_rt === 0
|
||||
) {
|
||||
const res = await confirmDialog(
|
||||
`ImEX Online has detected that there is a missing tax rate for recored parts. Pressing OK will set the tax rate to ${bodyshop.bill_tax_rates.state_tax_rate}% and enable the rate. Pressing cancel will keep the tax rate as is.`
|
||||
);
|
||||
if (res) {
|
||||
if (!estData.parts_tax_rates.PAR) {
|
||||
estData.parts_tax_rates.PAR = {
|
||||
prt_discp: 0,
|
||||
prt_mktyp: true,
|
||||
prt_mkupp: 0,
|
||||
prt_type: "PAR",
|
||||
};
|
||||
}
|
||||
estData.parts_tax_rates.PAR.prt_tax_rt =
|
||||
bodyshop.bill_tax_rates.state_tax_rate / 100;
|
||||
estData.parts_tax_rates.PAR.prt_tax_in = true;
|
||||
}
|
||||
}
|
||||
|
||||
//IO-1387 If a sublet line is NOT R&R, use the labor tax. If it is, use the sublet tax rate.
|
||||
//Currently limited to SK shops only.
|
||||
if (bodyshop.region_config === "CA_SK") {
|
||||
estData.joblines.data.forEach((jl, index) => {
|
||||
if (
|
||||
(jl.part_type === "PASL" || jl.part_type === "PAS") &&
|
||||
jl.lbr_op !== "OP11"
|
||||
) {
|
||||
estData.joblines.data[index].tax_part = jl.lbr_tax;
|
||||
}
|
||||
//if (bodyshop.region_config === "CA_SK") {
|
||||
estData.joblines.data.forEach((jl, index) => {
|
||||
if (
|
||||
(jl.part_type === "PASL" || jl.part_type === "PAS") &&
|
||||
jl.lbr_op !== "OP11"
|
||||
) {
|
||||
estData.joblines.data[index].tax_part = jl.lbr_tax;
|
||||
}
|
||||
|
||||
//Set markup lines and tax lines as taxable.
|
||||
//900510 is a mark up. 900510 is a discount.
|
||||
if (jl.db_ref === "900510") {
|
||||
estData.joblines.data[index].tax_part = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async function ResolveCCCLineIssues(estData, bodyshop) {
|
||||
//Find all misc amounts, populate them to the act price.
|
||||
//TODO Ensure that this doesnt get violated
|
||||
//This needs to be done before cleansing unq_seq since some misc prices could move over.
|
||||
estData.joblines.data.forEach((line) => {
|
||||
if (line.misc_amt && line.misc_amt !== 0) {
|
||||
line.act_price = line.act_price + line.misc_amt;
|
||||
line.tax_part = !!line.misc_tax;
|
||||
//Set markup lines and tax lines as taxable.
|
||||
//900510 is a mark up. 900510 is a discount.
|
||||
if (jl.db_ref === "900510") {
|
||||
estData.joblines.data[index].tax_part = true;
|
||||
}
|
||||
});
|
||||
|
||||
//Generate the list of duplicated UNQ_SEQ that will feed into the next section to scrub the lines.
|
||||
const unqSeqHash = _.groupBy(estData.joblines.data, "unq_seq");
|
||||
const duplicatedUnqSeq = Object.keys(unqSeqHash).filter(
|
||||
(key) => unqSeqHash[key].length > 1
|
||||
);
|
||||
|
||||
duplicatedUnqSeq.forEach((unq_seq) => {
|
||||
//Keys are strings, convert to int.
|
||||
const int_unq_seq = parseInt(unq_seq);
|
||||
|
||||
//When line splitting, the first line is always the non-refinish line. We will keep it as is.
|
||||
//We will cleanse the second line, which is always the next line.
|
||||
const nonRefLineIndex = estData.joblines.data.findIndex(
|
||||
(line) => line.unq_seq === int_unq_seq
|
||||
);
|
||||
estData.joblines.data[nonRefLineIndex + 1] = {
|
||||
...estData.joblines.data[nonRefLineIndex + 1],
|
||||
part_type: null,
|
||||
act_price: 0,
|
||||
db_price: 0,
|
||||
prt_dsmk_p: 0,
|
||||
prt_dsmk_m: 0,
|
||||
};
|
||||
});
|
||||
//}
|
||||
}
|
||||
|
||||
@@ -16,8 +16,8 @@ const mapStateToProps = createStructuredSelector({
|
||||
jobRO: selectJobReadOnly,
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
insertAuditTrail: ({ jobid, operation }) =>
|
||||
dispatch(insertAuditTrail({ jobid, operation })),
|
||||
insertAuditTrail: ({ jobid, operation, type }) =>
|
||||
dispatch(insertAuditTrail({ jobid, operation, type })),
|
||||
});
|
||||
|
||||
export function JobsChangeStatus({ job, bodyshop, jobRO, insertAuditTrail }) {
|
||||
@@ -35,6 +35,7 @@ export function JobsChangeStatus({ job, bodyshop, jobRO, insertAuditTrail }) {
|
||||
insertAuditTrail({
|
||||
jobid: job.id,
|
||||
operation: AuditTrailMapping.jobstatuschange(status),
|
||||
type: "jobstatuschange",
|
||||
});
|
||||
// refetch();
|
||||
})
|
||||
|
||||
@@ -36,8 +36,6 @@ export function JobsCloseAutoAllocate({ bodyshop, joblines, form, disabled }) {
|
||||
ret.profitcenter_part = defaults.profits["MAPA"];
|
||||
} else if (lineDesc.includes("ats amount")) {
|
||||
ret.profitcenter_part = defaults.profits["ATS"];
|
||||
} else if (jl.act_price > 0) {
|
||||
ret.profitcenter_part = defaults.profits["PAO"];
|
||||
} else {
|
||||
ret.profitcenter_part = null;
|
||||
}
|
||||
|
||||
@@ -23,8 +23,8 @@ const mapStateToProps = createStructuredSelector({
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
insertAuditTrail: ({ jobid, operation }) =>
|
||||
dispatch(insertAuditTrail({ jobid, operation })),
|
||||
insertAuditTrail: ({ jobid, operation, type }) =>
|
||||
dispatch(insertAuditTrail({ jobid, operation, type })),
|
||||
});
|
||||
|
||||
function updateJobCache(items) {
|
||||
@@ -192,6 +192,7 @@ export function JobsCloseExportButton({
|
||||
insertAuditTrail({
|
||||
jobid: jobId,
|
||||
operation: AuditTrailMapping.jobexported(),
|
||||
type: "jobexported",
|
||||
});
|
||||
updateJobCache(
|
||||
jobUpdateResponse.data.update_jobs.returning.map((job) => job.id)
|
||||
@@ -217,6 +218,7 @@ export function JobsCloseExportButton({
|
||||
insertAuditTrail({
|
||||
jobid: jobId,
|
||||
operation: AuditTrailMapping.jobexported(),
|
||||
type: "jobexported",
|
||||
});
|
||||
updateJobCache([
|
||||
...new Set(
|
||||
|
||||
@@ -25,8 +25,8 @@ const mapStateToProps = createStructuredSelector({
|
||||
jobRO: selectJobReadOnly,
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
insertAuditTrail: ({ jobid, operation }) =>
|
||||
dispatch(insertAuditTrail({ jobid, operation })),
|
||||
insertAuditTrail: ({ jobid, operation, type }) =>
|
||||
dispatch(insertAuditTrail({ jobid, operation, type })),
|
||||
});
|
||||
|
||||
export function JobsConvertButton({
|
||||
@@ -78,6 +78,7 @@ export function JobsConvertButton({
|
||||
operation: AuditTrailMapping.jobconverted(
|
||||
res.data.update_jobs.returning[0].ro_number
|
||||
),
|
||||
type: "jobconverted",
|
||||
});
|
||||
|
||||
setVisible(false);
|
||||
|
||||
@@ -1,4 +1,12 @@
|
||||
import { Collapse, Form, Input, Select, Switch } from "antd";
|
||||
import {
|
||||
Collapse,
|
||||
Form,
|
||||
Input,
|
||||
InputNumber,
|
||||
Select,
|
||||
Space,
|
||||
Switch,
|
||||
} from "antd";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
@@ -10,14 +18,10 @@ import FormItemEmail from "../form-items-formatted/email-form-item.component";
|
||||
import FormItemPhone, {
|
||||
PhoneItemFormatterValidation,
|
||||
} from "../form-items-formatted/phone-form-item.component";
|
||||
import JobsDetailChangeEstimator from "../jobs-detail-change-estimator/jobs-detail-change-estimator.component";
|
||||
import JobsDetailChangeFilehandler from "../jobs-detail-change-filehandler/jobs-detail-change-filehandler.component";
|
||||
import JobsDetailRatesChangeButton from "../jobs-detail-rates-change-button/jobs-detail-rates-change-button.component";
|
||||
import JobsDetailRatesParts from "../jobs-detail-rates/jobs-detail-rates.parts.component";
|
||||
|
||||
import JobsDetailRatesLabor from "../jobs-detail-rates/jobs-detail-rates.labor.component";
|
||||
import JobsDetailRatesMaterials from "../jobs-detail-rates/jobs-detail-rates.materials.component";
|
||||
import JobsDetailRatesOther from "../jobs-detail-rates/jobs-detail-rates.other.component";
|
||||
import JobsDetailRatesTaxes from "../jobs-detail-rates/jobs-detail-rates.taxes.component";
|
||||
|
||||
import JobsMarkPstExempt from "../jobs-mark-pst-exempt/jobs-mark-pst-exempt.component";
|
||||
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
@@ -31,6 +35,15 @@ const mapDispatchToProps = (dispatch) => ({
|
||||
export function JobsCreateJobsInfo({ bodyshop, form, selected }) {
|
||||
const { t } = useTranslation();
|
||||
const { getFieldValue } = form;
|
||||
|
||||
const handleInsCoChange = (value) => {
|
||||
const selectedCompany = bodyshop.md_ins_cos.find((s) => s.name === value);
|
||||
if (selectedCompany) {
|
||||
form.setFieldValue("ins_addr1", selectedCompany.street1);
|
||||
form.setFieldValue("ins_city", selectedCompany.city);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Collapse defaultActiveKey="insurance">
|
||||
@@ -40,26 +53,20 @@ export function JobsCreateJobsInfo({ bodyshop, form, selected }) {
|
||||
forceRender
|
||||
>
|
||||
<LayoutFormRow>
|
||||
<Form.Item label={t("jobs.fields.ins_co_id")} name="ins_co_id">
|
||||
<Form.Item label={t("jobs.fields.clm_no")} name="clm_no">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.policy_no")} name="policy_no">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.clm_no")} name="clm_no">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.regie_number")}
|
||||
name="regie_number"
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.loss_date")} name="loss_date">
|
||||
<FormDatePicker />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.ins_co_nm")} name="ins_co_nm">
|
||||
<Select>
|
||||
<Select onChange={handleInsCoChange}>
|
||||
{bodyshop.md_ins_cos.map((s) => (
|
||||
<Select.Option key={s.name} value={s.name}>
|
||||
{s.name}
|
||||
@@ -73,7 +80,15 @@ export function JobsCreateJobsInfo({ bodyshop, form, selected }) {
|
||||
<Form.Item label={t("jobs.fields.ins_city")} name="ins_city">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.ins_ct_ln")} name="ins_ct_ln">
|
||||
<Form.Item
|
||||
label={
|
||||
<Space>
|
||||
{t("jobs.fields.ins_ct_ln")}
|
||||
<JobsDetailChangeFilehandler form={form} />
|
||||
</Space>
|
||||
}
|
||||
name="ins_ct_ln"
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.ins_ct_fn")} name="ins_ct_fn">
|
||||
@@ -101,11 +116,24 @@ export function JobsCreateJobsInfo({ bodyshop, form, selected }) {
|
||||
>
|
||||
<FormItemEmail email={getFieldValue("ins_ea")} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label={t("jobs.fields.loss_date")} name="loss_date">
|
||||
<FormDatePicker />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.kmin")} name="kmin">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.est_co_nm")} name="est_co_nm">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.est_ct_fn")} name="est_ct_fn">
|
||||
<Form.Item
|
||||
label={
|
||||
<Space>
|
||||
{t("jobs.fields.est_ct_fn")}
|
||||
<JobsDetailChangeEstimator form={form} />
|
||||
</Space>
|
||||
}
|
||||
name="est_ct_fn"
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.est_ct_ln")} name="est_ct_ln">
|
||||
@@ -264,28 +292,26 @@ export function JobsCreateJobsInfo({ bodyshop, form, selected }) {
|
||||
<CurrencyInput />
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
{
|
||||
// <LayoutFormRow>
|
||||
// <Form.Item
|
||||
// label={t("jobs.fields.federal_tax_rate")}
|
||||
// name="federal_tax_rate"
|
||||
// >
|
||||
// <InputNumber min={0} max={1} precision={2} />
|
||||
// </Form.Item>
|
||||
// <Form.Item
|
||||
// label={t("jobs.fields.state_tax_rate")}
|
||||
// name="state_tax_rate"
|
||||
// >
|
||||
// <InputNumber min={0} max={1} precision={2} />
|
||||
// </Form.Item>
|
||||
// <Form.Item
|
||||
// label={t("jobs.fields.local_tax_rate")}
|
||||
// name="local_tax_rate"
|
||||
// >
|
||||
// <InputNumber min={0} max={1} precision={2} />
|
||||
// </Form.Item>
|
||||
// </LayoutFormRow>
|
||||
}
|
||||
<LayoutFormRow>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.federal_tax_rate")}
|
||||
name="federal_tax_rate"
|
||||
>
|
||||
<InputNumber min={0} max={1} precision={2} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.state_tax_rate")}
|
||||
name="state_tax_rate"
|
||||
>
|
||||
<InputNumber min={0} max={1} precision={2} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.local_tax_rate")}
|
||||
name="local_tax_rate"
|
||||
>
|
||||
<InputNumber min={0} max={1} precision={2} />
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
<LayoutFormRow>
|
||||
<Form.Item label={t("jobs.fields.rate_lab")} name="rate_lab">
|
||||
<CurrencyInput />
|
||||
@@ -364,10 +390,6 @@ export function JobsCreateJobsInfo({ bodyshop, form, selected }) {
|
||||
required={selected && true}
|
||||
form={form}
|
||||
/>
|
||||
<JobsDetailRatesLabor form={form} />
|
||||
<JobsDetailRatesMaterials form={form} />
|
||||
<JobsDetailRatesOther form={form} />
|
||||
<JobsDetailRatesTaxes form={form} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -37,6 +37,15 @@ const lossColDamage = { sm: { span: 24 }, md: { span: 6 }, lg: { span: 4 } };
|
||||
export function JobsDetailGeneral({ bodyshop, jobRO, job, form }) {
|
||||
const { getFieldValue } = form;
|
||||
const { t } = useTranslation();
|
||||
|
||||
const handleInsCoChange = (value) => {
|
||||
const selectedCompany = bodyshop.md_ins_cos.find((s) => s.name === value);
|
||||
if (selectedCompany) {
|
||||
form.setFieldValue("ins_addr1", selectedCompany.street1);
|
||||
form.setFieldValue("ins_city", selectedCompany.city);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<FormRow header={t("jobs.forms.claiminfo")}>
|
||||
@@ -71,7 +80,7 @@ export function JobsDetailGeneral({ bodyshop, jobRO, job, form }) {
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label={t("jobs.fields.ins_co_nm")} name="ins_co_nm">
|
||||
<Select disabled={jobRO}>
|
||||
<Select disabled={jobRO} onChange={handleInsCoChange}>
|
||||
{bodyshop.md_ins_cos.map((s) => (
|
||||
<Select.Option key={s.name} value={s.name}>
|
||||
{s.name}
|
||||
|
||||
@@ -29,6 +29,7 @@ export default function AddToProduction(
|
||||
insertAuditTrail({
|
||||
jobid: jobId,
|
||||
operation: AuditTrailMapping.jobinproductionchange(!remove),
|
||||
type: "jobinproductionchange",
|
||||
})
|
||||
);
|
||||
if (completionCallback) completionCallback();
|
||||
|
||||
@@ -50,12 +50,10 @@ const mapDispatchToProps = (dispatch) => ({
|
||||
dispatch(setModalContext({ context: context, modal: "jobCosting" })),
|
||||
setTimeTicketContext: (context) =>
|
||||
dispatch(setModalContext({ context: context, modal: "timeTicket" })),
|
||||
setTimeTicketTaskContext: (context) =>
|
||||
dispatch(setModalContext({ context: context, modal: "timeTicketTask" })),
|
||||
setCardPaymentContext: (context) =>
|
||||
dispatch(setModalContext({ context: context, modal: "cardPayment" })),
|
||||
insertAuditTrail: ({ jobid, operation }) =>
|
||||
dispatch(insertAuditTrail({ jobid, operation })),
|
||||
insertAuditTrail: ({ jobid, operation, type }) =>
|
||||
dispatch(insertAuditTrail({ jobid, operation, type })),
|
||||
});
|
||||
|
||||
export function JobsDetailHeaderActions({
|
||||
@@ -69,7 +67,6 @@ export function JobsDetailHeaderActions({
|
||||
setJobCostingContext,
|
||||
jobRO,
|
||||
setTimeTicketContext,
|
||||
setTimeTicketTaskContext,
|
||||
setCardPaymentContext,
|
||||
insertAuditTrail,
|
||||
}) {
|
||||
@@ -118,6 +115,7 @@ export function JobsDetailHeaderActions({
|
||||
? !job.production_vars.alert
|
||||
: true
|
||||
),
|
||||
type: "alertToggle",
|
||||
});
|
||||
};
|
||||
|
||||
@@ -137,6 +135,7 @@ export function JobsDetailHeaderActions({
|
||||
operation: AuditTrailMapping.jobsuspend(
|
||||
!!job.suspended ? !job.suspended : true
|
||||
),
|
||||
type: "jobsuspend",
|
||||
});
|
||||
};
|
||||
|
||||
@@ -193,6 +192,7 @@ export function JobsDetailHeaderActions({
|
||||
jobid: job.id,
|
||||
operation:
|
||||
AuditTrailMapping.appointmentcancel(lost_sale_reason),
|
||||
type: "appointmentcancel",
|
||||
});
|
||||
return;
|
||||
}
|
||||
@@ -285,24 +285,6 @@ export function JobsDetailHeaderActions({
|
||||
>
|
||||
{t("timetickets.actions.enter")}
|
||||
</Menu.Item>
|
||||
{bodyshop.md_tasks_presets.enable_tasks && (
|
||||
<Menu.Item
|
||||
key="claimtimetickettasks"
|
||||
disabled={
|
||||
!job.converted ||
|
||||
(!bodyshop.tt_allow_post_to_invoiced && job.date_invoiced)
|
||||
}
|
||||
onClick={() => {
|
||||
setTimeTicketTaskContext({
|
||||
actions: {},
|
||||
context: { jobid: job.id },
|
||||
});
|
||||
}}
|
||||
>
|
||||
{t("timetickets.actions.claimtasks")}
|
||||
</Menu.Item>
|
||||
)}
|
||||
|
||||
<Menu.Item
|
||||
key="enterpayments"
|
||||
disabled={!job.converted}
|
||||
@@ -322,7 +304,7 @@ export function JobsDetailHeaderActions({
|
||||
disabled={!job.converted}
|
||||
onClick={() => {
|
||||
setCardPaymentContext({
|
||||
actions: {},
|
||||
actions: { refetch },
|
||||
context: { jobid: job.id },
|
||||
});
|
||||
}}
|
||||
@@ -570,6 +552,7 @@ export function JobsDetailHeaderActions({
|
||||
insertAuditTrail({
|
||||
jobid: job.id,
|
||||
operation: AuditTrailMapping.jobvoid(),
|
||||
type: "jobvoid",
|
||||
});
|
||||
//go back to jobs list.
|
||||
history.push(`/manage/`);
|
||||
|
||||
@@ -4,7 +4,8 @@ import {
|
||||
PauseCircleOutlined,
|
||||
WarningFilled,
|
||||
} from "@ant-design/icons";
|
||||
import { Card, Col, Row, Space, Tag, Tooltip } from "antd";
|
||||
import { Card, Col, Divider, Row, Space, Tag, Tooltip } from "antd";
|
||||
import moment from "moment";
|
||||
import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
@@ -62,6 +63,13 @@ export function JobsDetailHeader({ job, bodyshop, disabled }) {
|
||||
${job.v_make_desc || ""}
|
||||
${job.v_model_desc || ""}`.trim();
|
||||
|
||||
const bodyHrs = job.joblines
|
||||
.filter((j) => j.mod_lbr_ty !== "LAR")
|
||||
.reduce((acc, val) => acc + val.mod_lb_hrs, 0);
|
||||
const refinishHrs = job.joblines
|
||||
.filter((line) => line.mod_lbr_ty === "LAR")
|
||||
.reduce((acc, val) => acc + val.mod_lb_hrs, 0);
|
||||
|
||||
const ownerTitle = OwnerNameDisplayFunction(job).trim();
|
||||
|
||||
return (
|
||||
@@ -93,7 +101,13 @@ export function JobsDetailHeader({ job, bodyshop, disabled }) {
|
||||
{job.status === bodyshop.md_ro_statuses.default_scheduled &&
|
||||
job.scheduled_in ? (
|
||||
<Tag>
|
||||
<DateTimeFormatter>{job.scheduled_in}</DateTimeFormatter>
|
||||
<Link
|
||||
to={`/manage/schedule?date=${moment(
|
||||
job.scheduled_in
|
||||
).format("YYYY-MM-DD")}`}
|
||||
>
|
||||
<DateTimeFormatter>{job.scheduled_in}</DateTimeFormatter>
|
||||
</Link>
|
||||
</Tag>
|
||||
) : null}
|
||||
</Space>
|
||||
@@ -211,6 +225,12 @@ export function JobsDetailHeader({ job, bodyshop, disabled }) {
|
||||
{job.owner?.tax_number || ""}
|
||||
</DataLabel>
|
||||
)}
|
||||
<DataLabel
|
||||
label={t("owners.fields.note")}
|
||||
valueStyle={{ overflow: "hidden", textOverflow: "ellipsis" }}
|
||||
>
|
||||
{job.owner?.note || ""}
|
||||
</DataLabel>
|
||||
</div>
|
||||
</Card>
|
||||
</Col>
|
||||
@@ -299,6 +319,11 @@ export function JobsDetailHeader({ job, bodyshop, disabled }) {
|
||||
>
|
||||
<div>
|
||||
<JobEmployeeAssignments job={job} />
|
||||
<Divider style={{ margin: ".5rem" }} />
|
||||
<DataLabel label={t("jobs.labels.labor_hrs")}>
|
||||
{bodyHrs.toFixed(1)} / {refinishHrs.toFixed(1)} /{" "}
|
||||
{(bodyHrs + refinishHrs).toFixed(1)}
|
||||
</DataLabel>
|
||||
</div>
|
||||
</Card>
|
||||
</Col>
|
||||
|
||||
@@ -5,13 +5,9 @@ import { createStructuredSelector } from "reselect";
|
||||
import { selectJobReadOnly } from "../../redux/application/application.selectors";
|
||||
import LaborAllocationsTableComponent from "../labor-allocations-table/labor-allocations-table.component";
|
||||
import TimeTicketList from "../time-ticket-list/time-ticket-list.component";
|
||||
import PayrollLaborAllocationsTable from "../labor-allocations-table/labor-allocations-table.payroll.component";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import { useTreatments } from "@splitsoftware/splitio-react";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
jobRO: selectJobReadOnly,
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps, null)(JobsDetailLaborContainer);
|
||||
@@ -52,7 +48,6 @@ const adjSpan = {
|
||||
};
|
||||
|
||||
export function JobsDetailLaborContainer({
|
||||
bodyshop,
|
||||
jobRO,
|
||||
job,
|
||||
jobId,
|
||||
@@ -63,12 +58,6 @@ export function JobsDetailLaborContainer({
|
||||
techConsole,
|
||||
adjustments,
|
||||
}) {
|
||||
const { Enhanced_Payroll } = useTreatments(
|
||||
["Enhanced_Payroll"],
|
||||
{},
|
||||
bodyshop.imexshopid
|
||||
);
|
||||
|
||||
return (
|
||||
<Row gutter={[16, 16]}>
|
||||
<Col {...ticketSpan}>
|
||||
@@ -81,28 +70,14 @@ export function JobsDetailLaborContainer({
|
||||
jobId={jobId}
|
||||
/>
|
||||
</Col>
|
||||
|
||||
{Enhanced_Payroll.treatment === "on" ? (
|
||||
<Col {...adjSpan}>
|
||||
<PayrollLaborAllocationsTable
|
||||
jobId={jobId}
|
||||
joblines={joblines}
|
||||
timetickets={timetickets}
|
||||
refetch={refetch}
|
||||
adjustments={adjustments}
|
||||
/>
|
||||
</Col>
|
||||
) : (
|
||||
<Col {...adjSpan}>
|
||||
<LaborAllocationsTableComponent
|
||||
jobId={jobId}
|
||||
joblines={joblines}
|
||||
timetickets={timetickets}
|
||||
refetch={refetch}
|
||||
adjustments={adjustments}
|
||||
/>
|
||||
</Col>
|
||||
)}
|
||||
<Col {...adjSpan}>
|
||||
<LaborAllocationsTableComponent
|
||||
jobId={jobId}
|
||||
joblines={joblines}
|
||||
timetickets={timetickets}
|
||||
adjustments={adjustments}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -6,14 +6,12 @@ import BillsListTable from "../bills-list-table/bills-list-table.component";
|
||||
import JobBillsTotal from "../job-bills-total/job-bills-total.component";
|
||||
import PartsOrderListTableComponent from "../parts-order-list-table/parts-order-list-table.component";
|
||||
import PartsOrderModal from "../parts-order-modal/parts-order-modal.container";
|
||||
import PartsDispatchTable from "../parts-dispatch-table/parts-dispatch-table.component";
|
||||
|
||||
export default function JobsDetailPliComponent({
|
||||
job,
|
||||
billsQuery,
|
||||
handleBillOnRowClick,
|
||||
handlePartsOrderOnRowClick,
|
||||
handlePartsDispatchOnRowClick,
|
||||
}) {
|
||||
return (
|
||||
<div>
|
||||
@@ -45,13 +43,6 @@ export default function JobsDetailPliComponent({
|
||||
billsQuery={billsQuery}
|
||||
/>
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
<PartsDispatchTable
|
||||
job={job}
|
||||
handleOnRowClick={handlePartsDispatchOnRowClick}
|
||||
billsQuery={billsQuery}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,62 +1,18 @@
|
||||
import { useQuery } from "@apollo/client";
|
||||
import queryString from "query-string";
|
||||
import React from "react";
|
||||
import { useHistory, useLocation } from "react-router-dom";
|
||||
import { QUERY_BILLS_BY_JOBID } from "../../graphql/bills.queries";
|
||||
import JobsDetailPliComponent from "./jobs-detail-pli.component";
|
||||
|
||||
export default function JobsDetailPliContainer({ job }) {
|
||||
const billsQuery = useQuery(QUERY_BILLS_BY_JOBID, {
|
||||
variables: { jobid: job.id },
|
||||
fetchPolicy: "network-only",
|
||||
nextFetchPolicy: "network-only",
|
||||
});
|
||||
|
||||
const search = queryString.parse(useLocation().search);
|
||||
const history = useHistory();
|
||||
|
||||
const handleBillOnRowClick = (record) => {
|
||||
if (record) {
|
||||
if (record.id) {
|
||||
search.billid = record.id;
|
||||
history.push({ search: queryString.stringify(search) });
|
||||
}
|
||||
} else {
|
||||
delete search.billid;
|
||||
history.push({ search: queryString.stringify(search) });
|
||||
}
|
||||
};
|
||||
|
||||
const handlePartsOrderOnRowClick = (record) => {
|
||||
if (record) {
|
||||
if (record.id) {
|
||||
search.partsorderid = record.id;
|
||||
history.push({ search: queryString.stringify(search) });
|
||||
}
|
||||
} else {
|
||||
delete search.partsorderid;
|
||||
history.push({ search: queryString.stringify(search) });
|
||||
}
|
||||
};
|
||||
|
||||
const handlePartsDispatchOnRowClick = (record) => {
|
||||
if (record) {
|
||||
if (record.id) {
|
||||
search.partsdispatchid = record.id;
|
||||
history.push({ search: queryString.stringify(search) });
|
||||
}
|
||||
} else {
|
||||
delete search.partsdispatchid;
|
||||
history.push({ search: queryString.stringify(search) });
|
||||
}
|
||||
};
|
||||
export default function JobsDetailPliContainer({
|
||||
job,
|
||||
billsQuery,
|
||||
handleBillOnRowClick,
|
||||
handlePartsOrderOnRowClick,
|
||||
}) {
|
||||
return (
|
||||
<JobsDetailPliComponent
|
||||
job={job}
|
||||
billsQuery={billsQuery}
|
||||
handleBillOnRowClick={handleBillOnRowClick}
|
||||
handlePartsOrderOnRowClick={handlePartsOrderOnRowClick}
|
||||
handlePartsDispatchOnRowClick={handlePartsDispatchOnRowClick}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,21 +1,25 @@
|
||||
import { Divider, Form, Input, Select, Space, Switch, Tooltip } from "antd";
|
||||
import {
|
||||
Divider,
|
||||
Form,
|
||||
Input,
|
||||
InputNumber,
|
||||
Select,
|
||||
Space,
|
||||
Switch,
|
||||
Tooltip,
|
||||
} from "antd";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { selectJobReadOnly } from "../../redux/application/application.selectors";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
|
||||
import CABCpvrtCalculator from "../ca-bc-pvrt-calculator/ca-bc-pvrt-calculator.component";
|
||||
import CurrencyInput from "../form-items-formatted/currency-form-item.component";
|
||||
import JobsDetailRatesChangeButton from "../jobs-detail-rates-change-button/jobs-detail-rates-change-button.component";
|
||||
import JobsMarkPstExempt from "../jobs-mark-pst-exempt/jobs-mark-pst-exempt.component";
|
||||
import FormRow from "../layout-form-row/layout-form-row.component";
|
||||
import JobsDetailRatesLabor from "./jobs-detail-rates.labor.component";
|
||||
import JobsDetailRatesMaterials from "./jobs-detail-rates.materials.component";
|
||||
import JobsDetailRatesOther from "./jobs-detail-rates.other.component";
|
||||
import JobsDetailRatesParts from "./jobs-detail-rates.parts.component";
|
||||
import JobsDetailRatesTaxes from "./jobs-detail-rates.taxes.component";
|
||||
import JobsDetailRatesProfileOVerride from "./jobs-detail-rates.profile-override.component";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
jobRO: selectJobReadOnly,
|
||||
@@ -80,7 +84,14 @@ export function JobsDetailRates({ jobRO, form, job, bodyshop }) {
|
||||
>
|
||||
<CurrencyInput disabled={jobRO || bodyshop.cdk_dealerid} />
|
||||
</Form.Item>
|
||||
|
||||
{bodyshop.region_config === "CA_BC" && (
|
||||
<Space align="center">
|
||||
<Form.Item label={t("jobs.fields.ca_bc_pvrt")} name="ca_bc_pvrt">
|
||||
<CurrencyInput disabled={jobRO} min={0} />
|
||||
</Form.Item>
|
||||
<CABCpvrtCalculator form={form} disabled={jobRO} />
|
||||
</Space>
|
||||
)}
|
||||
<Form.Item
|
||||
label={t("jobs.fields.auto_add_ats")}
|
||||
name="auto_add_ats"
|
||||
@@ -109,7 +120,41 @@ export function JobsDetailRates({ jobRO, form, job, bodyshop }) {
|
||||
}}
|
||||
</Form.Item>
|
||||
</FormRow>
|
||||
|
||||
<FormRow>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.federal_tax_rate")}
|
||||
name="federal_tax_rate"
|
||||
>
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.state_tax_rate")}
|
||||
name="state_tax_rate"
|
||||
>
|
||||
<InputNumber
|
||||
min={0}
|
||||
max={1}
|
||||
precision={2}
|
||||
disabled={jobRO}
|
||||
autoComplete="new-password"
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.local_tax_rate")}
|
||||
name="local_tax_rate"
|
||||
>
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
||||
</Form.Item>
|
||||
{bodyshop.region_config.toLowerCase().startsWith("ca") && (
|
||||
<Form.Item
|
||||
label={t("jobs.fields.ca_gst_registrant")}
|
||||
name="ca_gst_registrant"
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch disabled={jobRO} />
|
||||
</Form.Item>
|
||||
)}
|
||||
</FormRow>
|
||||
<Divider
|
||||
orientation="left"
|
||||
type="horizontal"
|
||||
@@ -197,15 +242,7 @@ export function JobsDetailRates({ jobRO, form, job, bodyshop }) {
|
||||
<CurrencyInput min={0} disabled={jobRO} />
|
||||
</Form.Item>
|
||||
</FormRow>
|
||||
<Divider orientation="left">Tax Profile</Divider>
|
||||
|
||||
<JobsDetailRatesProfileOVerride form={form} />
|
||||
|
||||
<JobsDetailRatesParts form={form} />
|
||||
<JobsDetailRatesLabor form={form} />
|
||||
<JobsDetailRatesMaterials form={form} />
|
||||
<JobsDetailRatesOther form={form} />
|
||||
<JobsDetailRatesTaxes form={form} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,427 +0,0 @@
|
||||
import { Collapse, Form, Switch } from "antd";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { selectJobReadOnly } from "../../redux/application/application.selectors";
|
||||
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
jobRO: selectJobReadOnly,
|
||||
});
|
||||
|
||||
export function JobsDetailRatesLabor({
|
||||
jobRO,
|
||||
expanded,
|
||||
required = true,
|
||||
form,
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<Collapse defaultActiveKey={expanded && "rates"}>
|
||||
<Collapse.Panel
|
||||
forceRender
|
||||
header={t("jobs.labels.cieca_pfl")}
|
||||
key="cieca_pfl"
|
||||
>
|
||||
<LayoutFormRow header={t("joblines.fields.lbr_types.LAB")}>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.cieca_pfl.lbr_tax_in")}
|
||||
name={["cieca_pfl", "LAB", "lbr_tax_in"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.cieca_pfl.lbr_tx_in1")}
|
||||
name={["cieca_pfl", "LAB", "lbr_tx_in1"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.cieca_pfl.lbr_tx_in2")}
|
||||
name={["cieca_pfl", "LAB", "lbr_tx_in2"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.cieca_pfl.lbr_tx_in3")}
|
||||
name={["cieca_pfl", "LAB", "lbr_tx_in3"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.cieca_pfl.lbr_tx_in4")}
|
||||
name={["cieca_pfl", "LAB", "lbr_tx_in4"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.cieca_pfl.lbr_tx_in5")}
|
||||
name={["cieca_pfl", "LAB", "lbr_tx_in5"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch disabled={jobRO} />
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
<LayoutFormRow header={t("joblines.fields.lbr_types.LAD")}>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.cieca_pfl.lbr_tax_in")}
|
||||
name={["cieca_pfl", "LAD", "lbr_tax_in"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.cieca_pfl.lbr_tx_in1")}
|
||||
name={["cieca_pfl", "LAD", "lbr_tx_in1"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.cieca_pfl.lbr_tx_in2")}
|
||||
name={["cieca_pfl", "LAD", "lbr_tx_in2"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.cieca_pfl.lbr_tx_in3")}
|
||||
name={["cieca_pfl", "LAD", "lbr_tx_in3"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.cieca_pfl.lbr_tx_in4")}
|
||||
name={["cieca_pfl", "LAD", "lbr_tx_in4"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.cieca_pfl.lbr_tx_in5")}
|
||||
name={["cieca_pfl", "LAD", "lbr_tx_in5"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch disabled={jobRO} />
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
<LayoutFormRow header={t("joblines.fields.lbr_types.LAE")}>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.cieca_pfl.lbr_tax_in")}
|
||||
name={["cieca_pfl", "LAE", "lbr_tax_in"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.cieca_pfl.lbr_tx_in1")}
|
||||
name={["cieca_pfl", "LAE", "lbr_tx_in1"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.cieca_pfl.lbr_tx_in2")}
|
||||
name={["cieca_pfl", "LAE", "lbr_tx_in2"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.cieca_pfl.lbr_tx_in3")}
|
||||
name={["cieca_pfl", "LAE", "lbr_tx_in3"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.cieca_pfl.lbr_tx_in4")}
|
||||
name={["cieca_pfl", "LAE", "lbr_tx_in4"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.cieca_pfl.lbr_tx_in5")}
|
||||
name={["cieca_pfl", "LAE", "lbr_tx_in5"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch disabled={jobRO} />
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
<LayoutFormRow header={t("joblines.fields.lbr_types.LAF")}>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.cieca_pfl.lbr_tax_in")}
|
||||
name={["cieca_pfl", "LAF", "lbr_tax_in"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.cieca_pfl.lbr_tx_in1")}
|
||||
name={["cieca_pfl", "LAF", "lbr_tx_in1"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.cieca_pfl.lbr_tx_in2")}
|
||||
name={["cieca_pfl", "LAF", "lbr_tx_in2"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.cieca_pfl.lbr_tx_in3")}
|
||||
name={["cieca_pfl", "LAF", "lbr_tx_in3"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.cieca_pfl.lbr_tx_in4")}
|
||||
name={["cieca_pfl", "LAF", "lbr_tx_in4"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.cieca_pfl.lbr_tx_in5")}
|
||||
name={["cieca_pfl", "LAF", "lbr_tx_in5"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch disabled={jobRO} />
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
<LayoutFormRow header={t("joblines.fields.lbr_types.LAG")}>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.cieca_pfl.lbr_tax_in")}
|
||||
name={["cieca_pfl", "LAG", "lbr_tax_in"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.cieca_pfl.lbr_tx_in1")}
|
||||
name={["cieca_pfl", "LAG", "lbr_tx_in1"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.cieca_pfl.lbr_tx_in2")}
|
||||
name={["cieca_pfl", "LAG", "lbr_tx_in2"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.cieca_pfl.lbr_tx_in3")}
|
||||
name={["cieca_pfl", "LAG", "lbr_tx_in3"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.cieca_pfl.lbr_tx_in4")}
|
||||
name={["cieca_pfl", "LAG", "lbr_tx_in4"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.cieca_pfl.lbr_tx_in5")}
|
||||
name={["cieca_pfl", "LAG", "lbr_tx_in5"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch disabled={jobRO} />
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
<LayoutFormRow header={t("joblines.fields.lbr_types.LAM")}>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.cieca_pfl.lbr_tax_in")}
|
||||
name={["cieca_pfl", "LAM", "lbr_tax_in"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.cieca_pfl.lbr_tx_in1")}
|
||||
name={["cieca_pfl", "LAM", "lbr_tx_in1"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.cieca_pfl.lbr_tx_in2")}
|
||||
name={["cieca_pfl", "LAM", "lbr_tx_in2"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.cieca_pfl.lbr_tx_in3")}
|
||||
name={["cieca_pfl", "LAM", "lbr_tx_in3"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.cieca_pfl.lbr_tx_in4")}
|
||||
name={["cieca_pfl", "LAM", "lbr_tx_in4"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.cieca_pfl.lbr_tx_in5")}
|
||||
name={["cieca_pfl", "LAM", "lbr_tx_in5"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch disabled={jobRO} />
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
<LayoutFormRow header={t("joblines.fields.lbr_types.LAR")}>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.cieca_pfl.lbr_tax_in")}
|
||||
name={["cieca_pfl", "LAR", "lbr_tax_in"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.cieca_pfl.lbr_tx_in1")}
|
||||
name={["cieca_pfl", "LAR", "lbr_tx_in1"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.cieca_pfl.lbr_tx_in2")}
|
||||
name={["cieca_pfl", "LAR", "lbr_tx_in2"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.cieca_pfl.lbr_tx_in3")}
|
||||
name={["cieca_pfl", "LAR", "lbr_tx_in3"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.cieca_pfl.lbr_tx_in4")}
|
||||
name={["cieca_pfl", "LAR", "lbr_tx_in4"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.cieca_pfl.lbr_tx_in5")}
|
||||
name={["cieca_pfl", "LAR", "lbr_tx_in5"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch disabled={jobRO} />
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
<LayoutFormRow header={t("joblines.fields.lbr_types.LAS")}>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.cieca_pfl.lbr_tax_in")}
|
||||
name={["cieca_pfl", "LAS", "lbr_tax_in"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.cieca_pfl.lbr_tx_in1")}
|
||||
name={["cieca_pfl", "LAS", "lbr_tx_in1"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.cieca_pfl.lbr_tx_in2")}
|
||||
name={["cieca_pfl", "LAS", "lbr_tx_in2"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.cieca_pfl.lbr_tx_in3")}
|
||||
name={["cieca_pfl", "LAS", "lbr_tx_in3"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.cieca_pfl.lbr_tx_in4")}
|
||||
name={["cieca_pfl", "LAS", "lbr_tx_in4"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.cieca_pfl.lbr_tx_in5")}
|
||||
name={["cieca_pfl", "LAS", "lbr_tx_in5"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch disabled={jobRO} />
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
<LayoutFormRow header={t("joblines.fields.lbr_types.LAU")}>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.cieca_pfl.lbr_tax_in")}
|
||||
name={["cieca_pfl", "LAU", "lbr_tax_in"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.cieca_pfl.lbr_tx_in1")}
|
||||
name={["cieca_pfl", "LAU", "lbr_tx_in1"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.cieca_pfl.lbr_tx_in2")}
|
||||
name={["cieca_pfl", "LAU", "lbr_tx_in2"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.cieca_pfl.lbr_tx_in3")}
|
||||
name={["cieca_pfl", "LAU", "lbr_tx_in3"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.cieca_pfl.lbr_tx_in4")}
|
||||
name={["cieca_pfl", "LAU", "lbr_tx_in4"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.cieca_pfl.lbr_tx_in5")}
|
||||
name={["cieca_pfl", "LAU", "lbr_tx_in5"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch disabled={jobRO} />
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
</Collapse.Panel>
|
||||
</Collapse>
|
||||
);
|
||||
}
|
||||
export default connect(mapStateToProps, null)(JobsDetailRatesLabor);
|
||||
@@ -1,145 +0,0 @@
|
||||
import { Collapse, Form, Input, InputNumber, Switch } from "antd";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { selectJobReadOnly } from "../../redux/application/application.selectors";
|
||||
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
jobRO: selectJobReadOnly,
|
||||
});
|
||||
|
||||
export function JobsDetailRatesMaterials({
|
||||
jobRO,
|
||||
expanded,
|
||||
required = true,
|
||||
form,
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<Collapse defaultActiveKey={expanded && "rates"}>
|
||||
<Collapse.Panel
|
||||
forceRender
|
||||
header={t("jobs.fields.materials.materials")}
|
||||
key="materials"
|
||||
>
|
||||
<LayoutFormRow header={t("jobs.fields.materials.MAPA")}>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.materials.cal_maxdlr")}
|
||||
name={["materials", "MAPA", "cal_maxdlr"]}
|
||||
>
|
||||
<InputNumber min={0} precision={2} disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.materials.cal_opcode")}
|
||||
name={["materials", "MAPA", "cal_opcode"]}
|
||||
>
|
||||
<Input disabled={jobRO} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label={t("jobs.fields.materials.tax_ind")}
|
||||
name={["materials", "MAPA", "tax_ind"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.materials.mat_tx_in1")}
|
||||
name={["materials", "MAPA", "mat_tx_in1"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.materials.mat_tx_in2")}
|
||||
name={["materials", "MAPA", "mat_tx_in2"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.materials.mat_tx_in3")}
|
||||
name={["materials", "MAPA", "mat_tx_in3"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.materials.mat_tx_in4")}
|
||||
name={["materials", "MAPA", "mat_tx_in4"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.materials.mat_tx_in5")}
|
||||
name={["materials", "MAPA", "mat_tx_in5"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
<LayoutFormRow header={t("jobs.fields.materials.MASH")}>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.materials.cal_maxdlr")}
|
||||
name={["materials", "MASH", "cal_maxdlr"]}
|
||||
>
|
||||
<InputNumber min={0} precision={2} disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.materials.cal_opcode")}
|
||||
name={["materials", "MASH", "cal_opcode"]}
|
||||
>
|
||||
<Input disabled={jobRO} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label={t("jobs.fields.materials.tax_ind")}
|
||||
name={["materials", "MASH", "tax_ind"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.materials.mat_tx_in1")}
|
||||
name={["materials", "MASH", "mat_tx_in1"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.materials.mat_tx_in2")}
|
||||
name={["materials", "MASH", "mat_tx_in2"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.materials.mat_tx_in3")}
|
||||
name={["materials", "MASH", "mat_tx_in3"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.materials.mat_tx_in4")}
|
||||
name={["materials", "MASH", "mat_tx_in4"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.materials.mat_tx_in5")}
|
||||
name={["materials", "MASH", "mat_tx_in5"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
</Collapse.Panel>
|
||||
</Collapse>
|
||||
);
|
||||
}
|
||||
export default connect(mapStateToProps, null)(JobsDetailRatesMaterials);
|
||||
@@ -1,104 +0,0 @@
|
||||
import { Collapse, Form, Switch } from "antd";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { selectJobReadOnly } from "../../redux/application/application.selectors";
|
||||
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
jobRO: selectJobReadOnly,
|
||||
});
|
||||
|
||||
export function JobsDetailRatesOther({
|
||||
jobRO,
|
||||
expanded,
|
||||
required = true,
|
||||
form,
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<Collapse defaultActiveKey={expanded && "rates"}>
|
||||
<Collapse.Panel
|
||||
forceRender
|
||||
header={t("jobs.labels.cieca_pfo")}
|
||||
key="cieca_pfo"
|
||||
>
|
||||
<LayoutFormRow noDivider>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.cieca_pfo.tow_t_in1")}
|
||||
name={["cieca_pfo", "tow_t_in1"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.cieca_pfo.tow_t_in2")}
|
||||
name={["cieca_pfo", "tow_t_in2"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.cieca_pfo.tow_t_in3")}
|
||||
name={["cieca_pfo", "tow_t_in3"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.cieca_pfo.tow_t_in4")}
|
||||
name={["cieca_pfo", "tow_t_in4"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.cieca_pfo.tow_t_in5")}
|
||||
name={["cieca_pfo", "tow_t_in5"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label={t("jobs.fields.cieca_pfo.stor_t_in1")}
|
||||
name={["cieca_pfo", "stor_t_in1"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.cieca_pfo.stor_t_in2")}
|
||||
name={["cieca_pfo", "stor_t_in2"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.cieca_pfo.stor_t_in3")}
|
||||
name={["cieca_pfo", "stor_t_in3"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.cieca_pfo.stor_t_in4")}
|
||||
name={["cieca_pfo", "stor_t_in4"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.cieca_pfo.stor_t_in5")}
|
||||
name={["cieca_pfo", "stor_t_in5"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
</Collapse.Panel>
|
||||
</Collapse>
|
||||
);
|
||||
}
|
||||
export default connect(mapStateToProps, null)(JobsDetailRatesOther);
|
||||
@@ -29,7 +29,7 @@ export function JobsDetailRatesParts({
|
||||
label={t("jobs.fields.parts_tax_rates.prt_discp")}
|
||||
name={["parts_tax_rates", "PAA", "prt_discp"]}
|
||||
>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
|
||||
@@ -42,7 +42,7 @@ export function JobsDetailRatesParts({
|
||||
label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
|
||||
name={["parts_tax_rates", "PAA", "prt_mkupp"]}
|
||||
>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
|
||||
@@ -68,58 +68,18 @@ export function JobsDetailRatesParts({
|
||||
},
|
||||
]}
|
||||
>
|
||||
<InputNumber
|
||||
min={0}
|
||||
max={100}
|
||||
precision={4}
|
||||
disabled={jobRO}
|
||||
/>
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
||||
</Form.Item>
|
||||
);
|
||||
}}
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tx_in1")}
|
||||
name={["parts_tax_rates", "PAA", "prt_tx_in1"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tx_in2")}
|
||||
name={["parts_tax_rates", "PAA", "prt_tx_in2"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tx_in3")}
|
||||
name={["parts_tax_rates", "PAA", "prt_tx_in3"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tx_in4")}
|
||||
name={["parts_tax_rates", "PAA", "prt_tx_in4"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tx_in5")}
|
||||
name={["parts_tax_rates", "PAA", "prt_tx_in5"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
<LayoutFormRow header={t("joblines.fields.part_types.PAC")}>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_discp")}
|
||||
name={["parts_tax_rates", "PAC", "prt_discp"]}
|
||||
>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
|
||||
@@ -132,7 +92,7 @@ export function JobsDetailRatesParts({
|
||||
label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
|
||||
name={["parts_tax_rates", "PAC", "prt_mkupp"]}
|
||||
>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
|
||||
@@ -158,58 +118,18 @@ export function JobsDetailRatesParts({
|
||||
},
|
||||
]}
|
||||
>
|
||||
<InputNumber
|
||||
min={0}
|
||||
max={100}
|
||||
precision={4}
|
||||
disabled={jobRO}
|
||||
/>
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
||||
</Form.Item>
|
||||
);
|
||||
}}
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tx_in1")}
|
||||
name={["parts_tax_rates", "PAC", "prt_tx_in1"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tx_in2")}
|
||||
name={["parts_tax_rates", "PAC", "prt_tx_in2"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tx_in3")}
|
||||
name={["parts_tax_rates", "PAC", "prt_tx_in3"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tx_in4")}
|
||||
name={["parts_tax_rates", "PAC", "prt_tx_in4"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tx_in5")}
|
||||
name={["parts_tax_rates", "PAC", "prt_tx_in5"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
<LayoutFormRow header={t("joblines.fields.part_types.PAL")}>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_discp")}
|
||||
name={["parts_tax_rates", "PAL", "prt_discp"]}
|
||||
>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
|
||||
@@ -222,7 +142,7 @@ export function JobsDetailRatesParts({
|
||||
label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
|
||||
name={["parts_tax_rates", "PAL", "prt_mkupp"]}
|
||||
>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
|
||||
@@ -248,58 +168,18 @@ export function JobsDetailRatesParts({
|
||||
},
|
||||
]}
|
||||
>
|
||||
<InputNumber
|
||||
min={0}
|
||||
max={100}
|
||||
precision={4}
|
||||
disabled={jobRO}
|
||||
/>
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
||||
</Form.Item>
|
||||
);
|
||||
}}
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tx_in1")}
|
||||
name={["parts_tax_rates", "PAL", "prt_tx_in1"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tx_in2")}
|
||||
name={["parts_tax_rates", "PAL", "prt_tx_in2"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tx_in3")}
|
||||
name={["parts_tax_rates", "PAL", "prt_tx_in3"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tx_in4")}
|
||||
name={["parts_tax_rates", "PAL", "prt_tx_in4"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tx_in5")}
|
||||
name={["parts_tax_rates", "PAL", "prt_tx_in5"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
<LayoutFormRow header={t("joblines.fields.part_types.PAG")}>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_discp")}
|
||||
name={["parts_tax_rates", "PAG", "prt_discp"]}
|
||||
>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
|
||||
@@ -312,7 +192,7 @@ export function JobsDetailRatesParts({
|
||||
label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
|
||||
name={["parts_tax_rates", "PAG", "prt_mkupp"]}
|
||||
>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
|
||||
@@ -338,58 +218,18 @@ export function JobsDetailRatesParts({
|
||||
},
|
||||
]}
|
||||
>
|
||||
<InputNumber
|
||||
min={0}
|
||||
max={100}
|
||||
precision={4}
|
||||
disabled={jobRO}
|
||||
/>
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
||||
</Form.Item>
|
||||
);
|
||||
}}
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tx_in1")}
|
||||
name={["parts_tax_rates", "PAG", "prt_tx_in1"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tx_in2")}
|
||||
name={["parts_tax_rates", "PAG", "prt_tx_in2"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tx_in3")}
|
||||
name={["parts_tax_rates", "PAG", "prt_tx_in3"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tx_in4")}
|
||||
name={["parts_tax_rates", "PAG", "prt_tx_in4"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tx_in5")}
|
||||
name={["parts_tax_rates", "PAG", "prt_tx_in5"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
<LayoutFormRow header={t("joblines.fields.part_types.PAM")}>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_discp")}
|
||||
name={["parts_tax_rates", "PAM", "prt_discp"]}
|
||||
>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
|
||||
@@ -402,7 +242,7 @@ export function JobsDetailRatesParts({
|
||||
label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
|
||||
name={["parts_tax_rates", "PAM", "prt_mkupp"]}
|
||||
>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
|
||||
@@ -428,58 +268,18 @@ export function JobsDetailRatesParts({
|
||||
},
|
||||
]}
|
||||
>
|
||||
<InputNumber
|
||||
min={0}
|
||||
max={100}
|
||||
precision={4}
|
||||
disabled={jobRO}
|
||||
/>
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
||||
</Form.Item>
|
||||
);
|
||||
}}
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tx_in1")}
|
||||
name={["parts_tax_rates", "PAM", "prt_tx_in1"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tx_in2")}
|
||||
name={["parts_tax_rates", "PAM", "prt_tx_in2"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tx_in3")}
|
||||
name={["parts_tax_rates", "PAM", "prt_tx_in3"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tx_in4")}
|
||||
name={["parts_tax_rates", "PAM", "prt_tx_in4"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tx_in5")}
|
||||
name={["parts_tax_rates", "PAM", "prt_tx_in5"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
<LayoutFormRow header={t("joblines.fields.part_types.PAN")}>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_discp")}
|
||||
name={["parts_tax_rates", "PAN", "prt_discp"]}
|
||||
>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
|
||||
@@ -492,7 +292,7 @@ export function JobsDetailRatesParts({
|
||||
label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
|
||||
name={["parts_tax_rates", "PAN", "prt_mkupp"]}
|
||||
>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
|
||||
@@ -518,58 +318,18 @@ export function JobsDetailRatesParts({
|
||||
},
|
||||
]}
|
||||
>
|
||||
<InputNumber
|
||||
min={0}
|
||||
max={100}
|
||||
precision={4}
|
||||
disabled={jobRO}
|
||||
/>
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
||||
</Form.Item>
|
||||
);
|
||||
}}
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tx_in1")}
|
||||
name={["parts_tax_rates", "PAN", "prt_tx_in1"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tx_in2")}
|
||||
name={["parts_tax_rates", "PAN", "prt_tx_in2"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tx_in3")}
|
||||
name={["parts_tax_rates", "PAN", "prt_tx_in3"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tx_in4")}
|
||||
name={["parts_tax_rates", "PAN", "prt_tx_in4"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tx_in5")}
|
||||
name={["parts_tax_rates", "PAN", "prt_tx_in5"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
<LayoutFormRow header={t("joblines.fields.part_types.PAO")}>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_discp")}
|
||||
name={["parts_tax_rates", "PAO", "prt_discp"]}
|
||||
>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
|
||||
@@ -582,7 +342,7 @@ export function JobsDetailRatesParts({
|
||||
label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
|
||||
name={["parts_tax_rates", "PAO", "prt_mkupp"]}
|
||||
>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
|
||||
@@ -608,58 +368,18 @@ export function JobsDetailRatesParts({
|
||||
},
|
||||
]}
|
||||
>
|
||||
<InputNumber
|
||||
min={0}
|
||||
max={100}
|
||||
precision={4}
|
||||
disabled={jobRO}
|
||||
/>
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
||||
</Form.Item>
|
||||
);
|
||||
}}
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tx_in1")}
|
||||
name={["parts_tax_rates", "PAO", "prt_tx_in1"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tx_in2")}
|
||||
name={["parts_tax_rates", "PAO", "prt_tx_in2"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tx_in3")}
|
||||
name={["parts_tax_rates", "PAO", "prt_tx_in3"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tx_in4")}
|
||||
name={["parts_tax_rates", "PAO", "prt_tx_in4"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tx_in5")}
|
||||
name={["parts_tax_rates", "PAO", "prt_tx_in5"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
<LayoutFormRow header={t("joblines.fields.part_types.PAP")}>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_discp")}
|
||||
name={["parts_tax_rates", "PAP", "prt_discp"]}
|
||||
>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
|
||||
@@ -672,7 +392,7 @@ export function JobsDetailRatesParts({
|
||||
label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
|
||||
name={["parts_tax_rates", "PAP", "prt_mkupp"]}
|
||||
>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
|
||||
@@ -698,58 +418,18 @@ export function JobsDetailRatesParts({
|
||||
},
|
||||
]}
|
||||
>
|
||||
<InputNumber
|
||||
min={0}
|
||||
max={100}
|
||||
precision={4}
|
||||
disabled={jobRO}
|
||||
/>
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
||||
</Form.Item>
|
||||
);
|
||||
}}
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tx_in1")}
|
||||
name={["parts_tax_rates", "PAP", "prt_tx_in1"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tx_in2")}
|
||||
name={["parts_tax_rates", "PAP", "prt_tx_in2"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tx_in3")}
|
||||
name={["parts_tax_rates", "PAP", "prt_tx_in3"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tx_in4")}
|
||||
name={["parts_tax_rates", "PAP", "prt_tx_in4"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tx_in5")}
|
||||
name={["parts_tax_rates", "PAP", "prt_tx_in5"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
<LayoutFormRow header={t("joblines.fields.part_types.PAR")}>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_discp")}
|
||||
name={["parts_tax_rates", "PAR", "prt_discp"]}
|
||||
>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
|
||||
@@ -762,7 +442,7 @@ export function JobsDetailRatesParts({
|
||||
label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
|
||||
name={["parts_tax_rates", "PAR", "prt_mkupp"]}
|
||||
>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
|
||||
@@ -788,58 +468,18 @@ export function JobsDetailRatesParts({
|
||||
},
|
||||
]}
|
||||
>
|
||||
<InputNumber
|
||||
min={0}
|
||||
max={100}
|
||||
precision={4}
|
||||
disabled={jobRO}
|
||||
/>
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
||||
</Form.Item>
|
||||
);
|
||||
}}
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tx_in1")}
|
||||
name={["parts_tax_rates", "PAR", "prt_tx_in1"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tx_in2")}
|
||||
name={["parts_tax_rates", "PAR", "prt_tx_in2"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tx_in3")}
|
||||
name={["parts_tax_rates", "PAR", "prt_tx_in3"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tx_in4")}
|
||||
name={["parts_tax_rates", "PAR", "prt_tx_in4"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tx_in5")}
|
||||
name={["parts_tax_rates", "PAR", "prt_tx_in5"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
<LayoutFormRow header={t("joblines.fields.part_types.PAS")}>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_discp")}
|
||||
name={["parts_tax_rates", "PAS", "prt_discp"]}
|
||||
>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
|
||||
@@ -852,7 +492,7 @@ export function JobsDetailRatesParts({
|
||||
label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
|
||||
name={["parts_tax_rates", "PAS", "prt_mkupp"]}
|
||||
>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
|
||||
@@ -878,58 +518,18 @@ export function JobsDetailRatesParts({
|
||||
},
|
||||
]}
|
||||
>
|
||||
<InputNumber
|
||||
min={0}
|
||||
max={100}
|
||||
precision={4}
|
||||
disabled={jobRO}
|
||||
/>
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
||||
</Form.Item>
|
||||
);
|
||||
}}
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tx_in1")}
|
||||
name={["parts_tax_rates", "PAS", "prt_tx_in1"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tx_in2")}
|
||||
name={["parts_tax_rates", "PAS", "prt_tx_in2"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tx_in3")}
|
||||
name={["parts_tax_rates", "PAS", "prt_tx_in3"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tx_in4")}
|
||||
name={["parts_tax_rates", "PAS", "prt_tx_in4"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tx_in5")}
|
||||
name={["parts_tax_rates", "PAS", "prt_tx_in5"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
<LayoutFormRow header={t("joblines.fields.part_types.PASL")}>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_discp")}
|
||||
name={["parts_tax_rates", "PASL", "prt_discp"]}
|
||||
>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
|
||||
@@ -942,7 +542,7 @@ export function JobsDetailRatesParts({
|
||||
label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
|
||||
name={["parts_tax_rates", "PASL", "prt_mkupp"]}
|
||||
>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
|
||||
@@ -968,58 +568,18 @@ export function JobsDetailRatesParts({
|
||||
},
|
||||
]}
|
||||
>
|
||||
<InputNumber
|
||||
min={0}
|
||||
max={100}
|
||||
precision={4}
|
||||
disabled={jobRO}
|
||||
/>
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
||||
</Form.Item>
|
||||
);
|
||||
}}
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tx_in1")}
|
||||
name={["parts_tax_rates", "PASL", "prt_tx_in1"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tx_in2")}
|
||||
name={["parts_tax_rates", "PASL", "prt_tx_in2"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tx_in3")}
|
||||
name={["parts_tax_rates", "PASL", "prt_tx_in3"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tx_in4")}
|
||||
name={["parts_tax_rates", "PASL", "prt_tx_in4"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tx_in5")}
|
||||
name={["parts_tax_rates", "PASL", "prt_tx_in5"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
<LayoutFormRow header={t("joblines.fields.part_types.CCDR")}>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_discp")}
|
||||
name={["parts_tax_rates", "CCDR", "prt_discp"]}
|
||||
>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
|
||||
@@ -1032,7 +592,7 @@ export function JobsDetailRatesParts({
|
||||
label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
|
||||
name={["parts_tax_rates", "CCDR", "prt_mkupp"]}
|
||||
>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
|
||||
@@ -1045,7 +605,7 @@ export function JobsDetailRatesParts({
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tax_rt")}
|
||||
name={["parts_tax_rates", "CCDR", "prt_tax_rt"]}
|
||||
>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
<LayoutFormRow header={t("joblines.fields.part_types.CCF")}>
|
||||
@@ -1053,7 +613,7 @@ export function JobsDetailRatesParts({
|
||||
label={t("jobs.fields.parts_tax_rates.prt_discp")}
|
||||
name={["parts_tax_rates", "CCF", "prt_discp"]}
|
||||
>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
|
||||
@@ -1066,7 +626,7 @@ export function JobsDetailRatesParts({
|
||||
label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
|
||||
name={["parts_tax_rates", "CCF", "prt_mkupp"]}
|
||||
>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
|
||||
@@ -1079,7 +639,7 @@ export function JobsDetailRatesParts({
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tax_rt")}
|
||||
name={["parts_tax_rates", "CCF", "prt_tax_rt"]}
|
||||
>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
<LayoutFormRow header={t("joblines.fields.part_types.CCM")}>
|
||||
@@ -1087,7 +647,7 @@ export function JobsDetailRatesParts({
|
||||
label={t("jobs.fields.parts_tax_rates.prt_discp")}
|
||||
name={["parts_tax_rates", "CCM", "prt_discp"]}
|
||||
>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
|
||||
@@ -1100,7 +660,7 @@ export function JobsDetailRatesParts({
|
||||
label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
|
||||
name={["parts_tax_rates", "CCM", "prt_mkupp"]}
|
||||
>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
|
||||
@@ -1113,7 +673,7 @@ export function JobsDetailRatesParts({
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tax_rt")}
|
||||
name={["parts_tax_rates", "CCM", "prt_tax_rt"]}
|
||||
>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
<LayoutFormRow header={t("joblines.fields.part_types.CCC")}>
|
||||
@@ -1121,7 +681,7 @@ export function JobsDetailRatesParts({
|
||||
label={t("jobs.fields.parts_tax_rates.prt_discp")}
|
||||
name={["parts_tax_rates", "CCC", "prt_discp"]}
|
||||
>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
|
||||
@@ -1134,7 +694,7 @@ export function JobsDetailRatesParts({
|
||||
label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
|
||||
name={["parts_tax_rates", "CCC", "prt_mkupp"]}
|
||||
>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
|
||||
@@ -1147,7 +707,7 @@ export function JobsDetailRatesParts({
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tax_rt")}
|
||||
name={["parts_tax_rates", "CCC", "prt_tax_rt"]}
|
||||
>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
<LayoutFormRow header={t("joblines.fields.part_types.CCD")}>
|
||||
@@ -1155,7 +715,7 @@ export function JobsDetailRatesParts({
|
||||
label={t("jobs.fields.parts_tax_rates.prt_discp")}
|
||||
name={["parts_tax_rates", "CCD", "prt_discp"]}
|
||||
>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
|
||||
@@ -1168,7 +728,7 @@ export function JobsDetailRatesParts({
|
||||
label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
|
||||
name={["parts_tax_rates", "CCD", "prt_mkupp"]}
|
||||
>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
|
||||
@@ -1181,39 +741,39 @@ export function JobsDetailRatesParts({
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tax_rt")}
|
||||
name={["parts_tax_rates", "CCD", "prt_tax_rt"]}
|
||||
>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
<LayoutFormRow>
|
||||
<Form.Item label={t("jobs.fields.tax_tow_rt")} name="tax_tow_rt">
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.tax_str_rt")} name="tax_str_rt">
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.tax_paint_mat_rt")}
|
||||
name="tax_paint_mat_rt"
|
||||
>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.tax_shop_mat_rt")}
|
||||
name="tax_shop_mat_rt"
|
||||
>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.tax_sub_rt")} name="tax_sub_rt">
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.tax_lbr_rt")} name="tax_lbr_rt">
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.tax_levies_rt")}
|
||||
name="tax_levies_rt"
|
||||
>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
</Collapse.Panel>
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
import { Button, Popconfirm } from "antd";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||
});
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(JobsDetailRatesProfileOVerride);
|
||||
|
||||
export function JobsDetailRatesProfileOVerride({ bodyshop, form }) {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<Popconfirm
|
||||
onConfirm={() => {
|
||||
form.setFieldsValue({
|
||||
cieca_pft: {
|
||||
...bodyshop.md_responsibility_centers.taxes.tax_ty1,
|
||||
...bodyshop.md_responsibility_centers.taxes.tax_ty2,
|
||||
...bodyshop.md_responsibility_centers.taxes.tax_ty3,
|
||||
...bodyshop.md_responsibility_centers.taxes.tax_ty4,
|
||||
...bodyshop.md_responsibility_centers.taxes.tax_ty5,
|
||||
},
|
||||
materials: bodyshop.md_responsibility_centers.cieca_pfm,
|
||||
cieca_pfl: bodyshop.md_responsibility_centers.cieca_pfl,
|
||||
parts_tax_rates: bodyshop.md_responsibility_centers.parts_tax_rates,
|
||||
});
|
||||
}}
|
||||
title={t("jobs.actions.taxprofileoverride_confirm")}
|
||||
>
|
||||
<Button type="link">{t("jobs.actions.taxprofileoverride")}</Button>
|
||||
</Popconfirm>
|
||||
);
|
||||
}
|
||||
@@ -1,155 +0,0 @@
|
||||
import { Collapse, Divider, Form, Input, InputNumber, Space } from "antd";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { selectJobReadOnly } from "../../redux/application/application.selectors";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
jobRO: selectJobReadOnly,
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
|
||||
export function JobsDetailRatesTaxes({
|
||||
jobRO,
|
||||
expanded,
|
||||
bodyshop,
|
||||
required = true,
|
||||
form,
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const formItems = [];
|
||||
for (let tyCounter = 1; tyCounter <= 5; tyCounter++) {
|
||||
const section = [];
|
||||
|
||||
section.push(
|
||||
TaxFormItems({
|
||||
typeNum: tyCounter,
|
||||
rootElements: true,
|
||||
bodyshop,
|
||||
jobRO,
|
||||
})
|
||||
);
|
||||
|
||||
for (let iterator = 1; iterator <= 5; iterator++) {
|
||||
section.push(
|
||||
TaxFormItems({
|
||||
typeNum: tyCounter,
|
||||
typeNumIterator: iterator,
|
||||
rootElements: false,
|
||||
jobRO,
|
||||
})
|
||||
);
|
||||
}
|
||||
formItems.push(Space({ children: section, wrap: true }));
|
||||
formItems.push(<Divider />);
|
||||
}
|
||||
return (
|
||||
<Collapse defaultActiveKey={expanded && "rates"}>
|
||||
<Collapse.Panel
|
||||
forceRender
|
||||
header={t("jobs.labels.cieca_pft")}
|
||||
key="cieca_pft"
|
||||
>
|
||||
{formItems}
|
||||
</Collapse.Panel>
|
||||
</Collapse>
|
||||
);
|
||||
}
|
||||
export default connect(mapStateToProps, null)(JobsDetailRatesTaxes);
|
||||
|
||||
function TaxFormItems({
|
||||
typeNum,
|
||||
typeNumIterator,
|
||||
rootElements,
|
||||
bodyshopjobRO,
|
||||
jobRO,
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
if (rootElements)
|
||||
return (
|
||||
<>
|
||||
<Form.Item
|
||||
label={t("bodyshop.fields.responsibilitycenter_tax_type", {
|
||||
typeNum,
|
||||
typeNumIterator,
|
||||
})}
|
||||
// rules={[
|
||||
// {
|
||||
// required: true,
|
||||
// //message: t("general.validation.required"),
|
||||
// },
|
||||
// ]}
|
||||
name={["cieca_pft", `tax_type${typeNum}`]}
|
||||
>
|
||||
<Input disabled={jobRO} />
|
||||
</Form.Item>
|
||||
</>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Form.Item
|
||||
label={t("bodyshop.fields.responsibilitycenter_tax_tier", {
|
||||
typeNum,
|
||||
typeNumIterator,
|
||||
})}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
//message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
name={["cieca_pft", `ty${typeNum}_tier${typeNumIterator}`]}
|
||||
>
|
||||
<InputNumber precision={0} min={0} disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("bodyshop.fields.responsibilitycenter_tax_thres", {
|
||||
typeNum,
|
||||
typeNumIterator,
|
||||
})}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
//message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
name={["cieca_pft", `ty${typeNum}_thres${typeNumIterator}`]}
|
||||
>
|
||||
<InputNumber min={0} precision={2} disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("bodyshop.fields.responsibilitycenter_tax_rate", {
|
||||
typeNum,
|
||||
typeNumIterator,
|
||||
})}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
//message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
name={["cieca_pft", `ty${typeNum}_rate${typeNumIterator}`]}
|
||||
>
|
||||
<InputNumber min={0} precision={2} disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("bodyshop.fields.responsibilitycenter_tax_sur", {
|
||||
typeNum,
|
||||
typeNumIterator,
|
||||
})}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
//message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
name={["cieca_pft", `ty${typeNum}_sur${typeNumIterator}`]}
|
||||
>
|
||||
<InputNumber min={0} precision={2} disabled={jobRO} />
|
||||
</Form.Item>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -23,8 +23,8 @@ const mapStateToProps = createStructuredSelector({
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
insertAuditTrail: ({ jobid, operation }) =>
|
||||
dispatch(insertAuditTrail({ jobid, operation })),
|
||||
insertAuditTrail: ({ jobid, operation, type }) =>
|
||||
dispatch(insertAuditTrail({ jobid, operation, type })),
|
||||
});
|
||||
|
||||
function updateJobCache(items) {
|
||||
@@ -189,6 +189,7 @@ export function JobsExportAllButton({
|
||||
insertAuditTrail({
|
||||
jobid: job.id,
|
||||
operation: AuditTrailMapping.jobexported(),
|
||||
type: "jobexported",
|
||||
});
|
||||
});
|
||||
updateJobCache(
|
||||
@@ -230,6 +231,7 @@ export function JobsExportAllButton({
|
||||
insertAuditTrail({
|
||||
jobid: successfulTransactionsSet[0],
|
||||
operation: AuditTrailMapping.jobexported(),
|
||||
type: "jobexported",
|
||||
});
|
||||
}
|
||||
updateJobCache(successfulTransactionsSet);
|
||||
|
||||
@@ -11,6 +11,7 @@ import { createStructuredSelector } from "reselect";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||
import { pageLimit } from "../../utils/config";
|
||||
import { alphaSort, statusSort } from "../../utils/sorters";
|
||||
import useLocalStorage from "../../utils/useLocalStorage";
|
||||
import StartChatButton from "../chat-open-button/chat-open-button.component";
|
||||
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
|
||||
@@ -36,7 +37,10 @@ export function JobsList({ bodyshop, refetch, loading, jobs, total }) {
|
||||
title: t("jobs.fields.ro_number"),
|
||||
dataIndex: "ro_number",
|
||||
key: "ro_number",
|
||||
sorter: true, //(a, b) => alphaSort(a.ro_number, b.ro_number),
|
||||
sorter: search?.search
|
||||
? (a, b) =>
|
||||
parseInt((a.ro_number || "0").replace(/\D/g, "")) - parseInt((b.ro_number || "0").replace(/\D/g, ""))
|
||||
: true,
|
||||
sortOrder: sortcolumn === "ro_number" && sortorder,
|
||||
render: (text, record) => (
|
||||
<Link to={"/manage/jobs/" + record.id}>
|
||||
@@ -50,7 +54,6 @@ export function JobsList({ bodyshop, refetch, loading, jobs, total }) {
|
||||
key: "ownr_ln",
|
||||
ellipsis: true,
|
||||
//sorter: true, // (a, b) => alphaSort(a.ownr_ln, b.ownr_ln),
|
||||
|
||||
//sortOrder: sortcolumn === "ownr_ln" && sortorder,
|
||||
render: (text, record) => {
|
||||
return record.ownerid ? (
|
||||
@@ -68,7 +71,6 @@ export function JobsList({ bodyshop, refetch, loading, jobs, total }) {
|
||||
title: t("jobs.fields.ownr_ph1"),
|
||||
dataIndex: "ownr_ph1",
|
||||
key: "ownr_ph1",
|
||||
|
||||
ellipsis: true,
|
||||
render: (text, record) => (
|
||||
<StartChatButton phone={record.ownr_ph1} jobid={record.id} />
|
||||
@@ -78,7 +80,6 @@ export function JobsList({ bodyshop, refetch, loading, jobs, total }) {
|
||||
title: t("jobs.fields.ownr_ph2"),
|
||||
dataIndex: "ownr_ph2",
|
||||
key: "ownr_ph2",
|
||||
|
||||
ellipsis: true,
|
||||
render: (text, record) => (
|
||||
<StartChatButton phone={record.ownr_ph2} jobid={record.id} />
|
||||
@@ -88,9 +89,8 @@ export function JobsList({ bodyshop, refetch, loading, jobs, total }) {
|
||||
title: t("jobs.fields.status"),
|
||||
dataIndex: "status",
|
||||
key: "status",
|
||||
|
||||
ellipsis: true,
|
||||
sorter: true, // (a, b) => alphaSort(a.status, b.status),
|
||||
sorter: search?.search ? (a, b) => statusSort(a.status, b.status, bodyshop.md_ro_statuses.active_statuses) : true,
|
||||
sortOrder: sortcolumn === "status" && sortorder,
|
||||
render: (text, record) => {
|
||||
return record.status || t("general.labels.na");
|
||||
@@ -106,7 +106,6 @@ export function JobsList({ bodyshop, refetch, loading, jobs, total }) {
|
||||
title: t("jobs.fields.vehicle"),
|
||||
dataIndex: "vehicle",
|
||||
key: "vehicle",
|
||||
|
||||
ellipsis: true,
|
||||
render: (text, record) => {
|
||||
return record.vehicleid ? (
|
||||
@@ -127,7 +126,7 @@ export function JobsList({ bodyshop, refetch, loading, jobs, total }) {
|
||||
dataIndex: "plate_no",
|
||||
key: "plate_no",
|
||||
ellipsis: true,
|
||||
sorter: true, //(a, b) => alphaSort(a.plate_no, b.plate_no),
|
||||
sorter: search?.search ? (a, b) => alphaSort(a.plate_no, b.plate_no) : true,
|
||||
sortOrder: sortcolumn === "plate_no" && sortorder,
|
||||
render: (text, record) => {
|
||||
return record.plate_no ? record.plate_no : "";
|
||||
@@ -138,7 +137,7 @@ export function JobsList({ bodyshop, refetch, loading, jobs, total }) {
|
||||
dataIndex: "clm_no",
|
||||
key: "clm_no",
|
||||
ellipsis: true,
|
||||
sorter: true, //(a, b) => alphaSort(a.clm_no, b.clm_no),
|
||||
sorter: search?.search ? (a, b) => alphaSort(a.clm_no, b.clm_no) : true,
|
||||
sortOrder: sortcolumn === "clm_no" && sortorder,
|
||||
render: (text, record) =>
|
||||
`${record.clm_no || ""}${
|
||||
@@ -156,7 +155,7 @@ export function JobsList({ bodyshop, refetch, loading, jobs, total }) {
|
||||
dataIndex: "clm_total",
|
||||
key: "clm_total",
|
||||
|
||||
sorter: true, //(a, b) => a.clm_total - b.clm_total,
|
||||
sorter: search?.search ? (a, b) => a.clm_total - b.clm_total : true,
|
||||
sortOrder: sortcolumn === "clm_total" && sortorder,
|
||||
render: (text, record) => {
|
||||
return record.clm_total ? (
|
||||
@@ -170,7 +169,6 @@ export function JobsList({ bodyshop, refetch, loading, jobs, total }) {
|
||||
title: t("jobs.fields.owner_owing"),
|
||||
dataIndex: "owner_owing",
|
||||
key: "owner_owing",
|
||||
|
||||
render: (text, record) => (
|
||||
<CurrencyFormatter>{record.owner_owing}</CurrencyFormatter>
|
||||
),
|
||||
|
||||
@@ -21,6 +21,7 @@ import useLocalStorage from "../../utils/useLocalStorage";
|
||||
import AlertComponent from "../alert/alert.component";
|
||||
import ChatOpenButton from "../chat-open-button/chat-open-button.component";
|
||||
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
|
||||
import { OwnerNameDisplayFunction } from "./../owner-name-display/owner-name-display.component";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
@@ -145,7 +146,8 @@ export function JobsList({ bodyshop }) {
|
||||
key: "owner",
|
||||
ellipsis: true,
|
||||
responsive: ["md"],
|
||||
sorter: (a, b) => alphaSort(a.ownr_ln, b.ownr_ln),
|
||||
sorter: (a, b) =>
|
||||
alphaSort(OwnerNameDisplayFunction(a), OwnerNameDisplayFunction(b)),
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "owner" && state.sortedInfo.order,
|
||||
render: (text, record) => {
|
||||
@@ -188,7 +190,8 @@ export function JobsList({ bodyshop }) {
|
||||
dataIndex: "status",
|
||||
key: "status",
|
||||
ellipsis: true,
|
||||
sorter: (a, b) => alphaSort(a.status, b.status),
|
||||
sorter: (a, b) =>
|
||||
statusSort(a.status, b.status, bodyshop.md_ro_statuses.active_statuses),
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "status" && state.sortedInfo.order,
|
||||
filteredValue: filter?.status || null,
|
||||
@@ -219,6 +222,15 @@ export function JobsList({ bodyshop }) {
|
||||
dataIndex: "vehicle",
|
||||
key: "vehicle",
|
||||
ellipsis: true,
|
||||
sorter: (a, b) =>
|
||||
alphaSort(
|
||||
`${a.v_model_yr || ""} ${a.v_make_desc || ""} ${
|
||||
a.v_model_desc || ""
|
||||
}`,
|
||||
`${b.v_model_yr || ""} ${b.v_make_desc || ""} ${b.v_model_desc || ""}`
|
||||
),
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "vehicle" && state.sortedInfo.order,
|
||||
render: (text, record) => {
|
||||
return record.vehicleid ? (
|
||||
<Link
|
||||
@@ -266,6 +278,9 @@ export function JobsList({ bodyshop }) {
|
||||
dataIndex: "ins_co_nm",
|
||||
key: "ins_co_nm",
|
||||
ellipsis: true,
|
||||
sorter: (a, b) => alphaSort(a.ins_co_nm, b.ins_co_nm),
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "ins_co_nm" && state.sortedInfo.order,
|
||||
filteredValue: filter?.ins_co_nm || null,
|
||||
filters:
|
||||
(jobs &&
|
||||
@@ -302,6 +317,13 @@ export function JobsList({ bodyshop }) {
|
||||
key: "estimator",
|
||||
ellipsis: true,
|
||||
responsive: ["xl"],
|
||||
sorter: (a, b) =>
|
||||
alphaSort(
|
||||
`${a.est_ct_fn || ""} ${a.est_ct_ln || ""}`.trim(),
|
||||
`${b.est_ct_fn || ""} ${b.est_ct_ln || ""}`.trim()
|
||||
),
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "estimator" && state.sortedInfo.order,
|
||||
filterSearch: true,
|
||||
filteredValue: filter?.estimator || null,
|
||||
filters:
|
||||
|
||||
@@ -19,8 +19,8 @@ const mapStateToProps = createStructuredSelector({
|
||||
//currentUser: selectCurrentUser
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
insertAuditTrail: ({ jobid, operation }) =>
|
||||
dispatch(insertAuditTrail({ jobid, operation })),
|
||||
insertAuditTrail: ({ jobid, operation, type }) =>
|
||||
dispatch(insertAuditTrail({ jobid, operation, type })),
|
||||
});
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(JobNotesContainer);
|
||||
|
||||
@@ -49,6 +49,7 @@ export function JobNotesContainer({ jobId, insertAuditTrail }) {
|
||||
insertAuditTrail({
|
||||
jobid: jobId,
|
||||
operation: AuditTrailMapping.jobnotedeleted(),
|
||||
type: "jobnotedeleted",
|
||||
});
|
||||
});
|
||||
setDeleteLoading(false);
|
||||
|
||||
@@ -20,8 +20,8 @@ const mapStateToProps = createStructuredSelector({
|
||||
//currentUser: selectCurrentUser
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
insertAuditTrail: ({ jobid, operation }) =>
|
||||
dispatch(insertAuditTrail({ jobid, operation })),
|
||||
insertAuditTrail: ({ jobid, operation, type }) =>
|
||||
dispatch(insertAuditTrail({ jobid, operation, type })),
|
||||
});
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
@@ -76,6 +76,7 @@ export function LaborAllocationsAdjustmentEdit({
|
||||
values.hours -
|
||||
((adjustments && adjustments[mod_lbr_ty]) || 0).toFixed(1),
|
||||
}),
|
||||
type: "jobmodifylbradj",
|
||||
});
|
||||
}
|
||||
setLoading(false);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user