Compare commits
522 Commits
feature/IO
...
feature/IO
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4fdc241b9c | ||
|
|
3919efaa77 | ||
|
|
d915c123db | ||
|
|
d8420f472c | ||
|
|
34d93c4de0 | ||
|
|
1c400cd456 | ||
|
|
a3cf97fcab | ||
|
|
1a9dc7a377 | ||
|
|
806daebd3f | ||
|
|
dfd8845864 | ||
|
|
170108b339 | ||
|
|
9f1f58a9c7 | ||
|
|
4288e2d986 | ||
|
|
4b289388bf | ||
|
|
ed0136090c | ||
|
|
0d70545b98 | ||
|
|
2252091b53 | ||
|
|
386531884b | ||
|
|
afbe328aa7 | ||
|
|
b7c0fba48b | ||
|
|
68635b1629 | ||
|
|
3bd0058dc8 | ||
|
|
270a512585 | ||
|
|
ec2519eae4 | ||
|
|
d7f52d864a | ||
|
|
5cb2b3940e | ||
|
|
b5efaa944d | ||
|
|
bbcfc420d2 | ||
|
|
8ebf7baa71 | ||
|
|
742d2b5ff2 | ||
|
|
f96fefbfdc | ||
|
|
6570d38719 | ||
|
|
547b58f05a | ||
|
|
f299c685e2 | ||
|
|
59075ee610 | ||
|
|
432aa9f1e1 | ||
|
|
c5bed4f36d | ||
|
|
151c9c0add | ||
|
|
334306e3c9 | ||
|
|
0716920dfc | ||
|
|
07119e4e7e | ||
|
|
2a7606836c | ||
|
|
7dcdd64a17 | ||
|
|
f26b045727 | ||
|
|
79c966f9e4 | ||
|
|
8eff1dfc4c | ||
|
|
65f960db00 | ||
|
|
f14dc497cf | ||
|
|
89339e1033 | ||
|
|
74da3ec1ca | ||
|
|
7e99a51495 | ||
|
|
146a61c9cf | ||
|
|
5250d97935 | ||
|
|
18dce9795d | ||
|
|
8d170a5bb4 | ||
|
|
7add2bb12e | ||
|
|
8670f386dc | ||
|
|
1f62108e57 | ||
|
|
8715ef4f24 | ||
|
|
85b137f0d6 | ||
|
|
14ebb280a3 | ||
|
|
f1953eef29 | ||
|
|
55842faedd | ||
|
|
9c50de85de | ||
|
|
e6df079431 | ||
|
|
36ce547579 | ||
|
|
ae5fef435a | ||
|
|
fe55701079 | ||
|
|
87b567e990 | ||
|
|
c2b3905c8e | ||
|
|
32072f1d6c | ||
|
|
e84d3bf53a | ||
|
|
67a7e4b865 | ||
|
|
3d7da0b28a | ||
|
|
9320587595 | ||
|
|
7bceba7ed5 | ||
|
|
0db72cd9e4 | ||
|
|
53fc5e361f | ||
|
|
54af163ddf | ||
|
|
c0d756fa38 | ||
|
|
54e673176c | ||
|
|
fc16190ec4 | ||
|
|
cf7f4f1b46 | ||
|
|
756fc07193 | ||
|
|
189c60a6d1 | ||
|
|
ba95a636cf | ||
|
|
2478fedf1a | ||
|
|
a718f2a012 | ||
|
|
bc6c889afc | ||
|
|
33bcf250d0 | ||
|
|
bad32e069b | ||
|
|
9acf20d4f3 | ||
|
|
26f1ee0d89 | ||
|
|
9c86be8250 | ||
|
|
4a06f9a686 | ||
|
|
98700f54b4 | ||
|
|
9a37cb5cb8 | ||
|
|
673d0bb7c5 | ||
|
|
04c7bc445b | ||
|
|
41782fe120 | ||
|
|
71a5722b0e | ||
|
|
4507135a1b | ||
|
|
4e8e25a336 | ||
|
|
3c2d3156cb | ||
|
|
0dac15391f | ||
|
|
106534b59b | ||
|
|
19c0553746 | ||
|
|
5f1475d2ec | ||
|
|
84f4d5956a | ||
|
|
4330ddd926 | ||
|
|
0711210512 | ||
|
|
458ec76835 | ||
|
|
682ea860fb | ||
|
|
99977934e7 | ||
|
|
3e05b21c90 | ||
|
|
4e1dd52bea | ||
|
|
f6bcc743d8 | ||
|
|
7f2479f597 | ||
|
|
66b97be9d2 | ||
|
|
7472285641 | ||
|
|
d747594e39 | ||
|
|
67ff9f30c6 | ||
|
|
a27092dbcc | ||
|
|
ca41bff446 | ||
|
|
cf8280590c | ||
|
|
b649ca1f00 | ||
|
|
b441301007 | ||
|
|
2e93238b5c | ||
|
|
ce4fe84536 | ||
|
|
9b7c0af025 | ||
|
|
dfdaf36ed1 | ||
|
|
57909ebc61 | ||
|
|
cc8d1b3793 | ||
|
|
a33bfedbb8 | ||
|
|
d1090670a8 | ||
|
|
f4bcad1b06 | ||
|
|
eb359d83c5 | ||
|
|
ae13e9e36a | ||
|
|
4a62ac2a11 | ||
|
|
6656708c8e | ||
|
|
bfe3fb2ed0 | ||
|
|
ec21521281 | ||
|
|
016a62b6d5 | ||
|
|
3e226b50ab | ||
|
|
ab84cb5ada | ||
|
|
7825aa4122 | ||
|
|
e71e671d7d | ||
|
|
8d43fbfcd9 | ||
|
|
47a01628d3 | ||
|
|
c008660023 | ||
|
|
5da34cbeac | ||
|
|
5b29aec14b | ||
|
|
ca6aa682f6 | ||
|
|
eb3786cebf | ||
|
|
53843e22a4 | ||
|
|
e1693674ca | ||
|
|
2d2190e4fa | ||
|
|
02fd8097a8 | ||
|
|
fcfbc85683 | ||
|
|
802dd696f4 | ||
|
|
df6b0fae21 | ||
|
|
69723a8bf4 | ||
|
|
76d907987a | ||
|
|
241caa6132 | ||
|
|
13b93f6d9d | ||
|
|
77555366f4 | ||
|
|
f3e8132bfc | ||
|
|
7510385cf4 | ||
|
|
e05b72615e | ||
|
|
60a0222dd0 | ||
|
|
9114abd3ef | ||
|
|
b3f69d4088 | ||
|
|
8796c8fe06 | ||
|
|
b9a6a98fee | ||
|
|
acc9145d52 | ||
|
|
f9c8f53474 | ||
|
|
f7fc0e6a6d | ||
|
|
ffebbe3b2a | ||
|
|
25b8c1b1eb | ||
|
|
17f8625108 | ||
|
|
e3c21f0373 | ||
|
|
859ff00277 | ||
|
|
e7c3be5231 | ||
|
|
85e3c5a433 | ||
|
|
d8d5cde3f1 | ||
|
|
6efa08fee3 | ||
|
|
636c13373c | ||
|
|
004e96517f | ||
|
|
957265b1c8 | ||
|
|
3659fbec84 | ||
|
|
05f1a9b280 | ||
|
|
34b4baac3d | ||
|
|
5884d5eba0 | ||
|
|
6b4709b76b | ||
|
|
4dd2137006 | ||
|
|
03315836a6 | ||
|
|
8807e282f4 | ||
|
|
c394974dc8 | ||
|
|
28386a4234 | ||
|
|
6f16c47d4f | ||
|
|
25062e37f4 | ||
|
|
2b1f8e4335 | ||
|
|
02690b6796 | ||
|
|
3897281015 | ||
|
|
8d0e5b93ed | ||
|
|
ef146032df | ||
|
|
1dc025fb36 | ||
|
|
30c0c84b93 | ||
|
|
f703ba2cf9 | ||
|
|
4bd139f93b | ||
|
|
f59787add0 | ||
|
|
20c304a2db | ||
|
|
53526d9c80 | ||
|
|
68b4bc66ff | ||
|
|
805149daea | ||
|
|
b2529207e1 | ||
|
|
dc05e4e166 | ||
|
|
d2fe9b0590 | ||
|
|
4513acc640 | ||
|
|
612e359d4c | ||
|
|
c8fb1ce302 | ||
|
|
b29d8e1912 | ||
|
|
f1aa7944a3 | ||
|
|
74b6c2b6b5 | ||
|
|
4313cf471f | ||
|
|
fce8039dad | ||
|
|
085ae141ae | ||
|
|
fbc9ccc018 | ||
|
|
f2ede519d7 | ||
|
|
21c53473d3 | ||
|
|
fbc622fa04 | ||
|
|
6319fd20fa | ||
|
|
eff4f82ad7 | ||
|
|
c998e4901f | ||
|
|
06c35a4ff8 | ||
|
|
57327332c9 | ||
|
|
a2144ccb61 | ||
|
|
aa478fc510 | ||
|
|
659a0bc0fd | ||
|
|
f1ef28e544 | ||
|
|
64851047bf | ||
|
|
171c0b2b5a | ||
|
|
73fac34ef4 | ||
|
|
5ca34105ef | ||
|
|
fd4820336f | ||
|
|
fcfa1a9be8 | ||
|
|
41849644f3 | ||
|
|
46840266ee | ||
|
|
f36fb06dd6 | ||
|
|
af4c4a4fa3 | ||
|
|
fef680fb99 | ||
|
|
91a45a621a | ||
|
|
fe9512ca9c | ||
|
|
d6045a9334 | ||
|
|
dcc29f23d4 | ||
|
|
ff318599f5 | ||
|
|
d1ba90408d | ||
|
|
536f8d9cb9 | ||
|
|
6a9e871b08 | ||
|
|
d163d145d0 | ||
|
|
638a9fc76b | ||
|
|
fdf0506976 | ||
|
|
53e3b3fa03 | ||
|
|
56c1b6f992 | ||
|
|
22f9a7ee3d | ||
|
|
82a0d287d6 | ||
|
|
cfcad472fd | ||
|
|
1a622f1b2c | ||
|
|
4de604ef7c | ||
|
|
87e3adf579 | ||
|
|
aa7a4ccdd0 | ||
|
|
ec2c26ca69 | ||
|
|
28dc10f5a1 | ||
|
|
b2d615b9c1 | ||
|
|
1e40a22762 | ||
|
|
d1407162d9 | ||
|
|
7a1984d037 | ||
|
|
9bcc449f20 | ||
|
|
bc7d0ef171 | ||
|
|
3e9b046476 | ||
|
|
e0ccd62c82 | ||
|
|
fd0970aef2 | ||
|
|
b673bcae7a | ||
|
|
c9d8fc3072 | ||
|
|
0bcf67a5f5 | ||
|
|
3e48753329 | ||
|
|
96441dbb33 | ||
|
|
6a0b63b185 | ||
|
|
9dea43fd34 | ||
|
|
465980dd8c | ||
|
|
03d04fd8d1 | ||
|
|
7d18c9b160 | ||
|
|
63673548a0 | ||
|
|
c097f98959 | ||
|
|
29b74a8c0e | ||
|
|
213d4ad928 | ||
|
|
2658626c7e | ||
|
|
710b3b00f5 | ||
|
|
d041d03fbf | ||
|
|
24b90e9888 | ||
|
|
763b199646 | ||
|
|
026ce853e2 | ||
|
|
17905fa844 | ||
|
|
74a0b78a71 | ||
|
|
797a423702 | ||
|
|
2fce8c9644 | ||
|
|
9cd39c1c3e | ||
|
|
720b7f891b | ||
|
|
4b83e798ac | ||
|
|
468f93f3df | ||
|
|
78d9dd5acb | ||
|
|
6ef7d4653d | ||
|
|
645eb637f2 | ||
|
|
268fdce5ac | ||
|
|
f9f3a05a43 | ||
|
|
8a92919b2e | ||
|
|
dbcd675300 | ||
|
|
4628af0e43 | ||
|
|
b1fedf5904 | ||
|
|
6264a2f45c | ||
|
|
94e47d14ad | ||
|
|
45ec03e615 | ||
|
|
9319f492dd | ||
|
|
8f04c5a12c | ||
|
|
570d36b695 | ||
|
|
436a41405d | ||
|
|
b5332458ec | ||
|
|
a2150009db | ||
|
|
e1c785322f | ||
|
|
c1d71720ab | ||
|
|
89ff7740e2 | ||
|
|
4e69fe819e | ||
|
|
b36697054e | ||
|
|
17149fe853 | ||
|
|
a8cc3fa190 | ||
|
|
10fceb7ddf | ||
|
|
6c1a0cff8d | ||
|
|
d92d2cca9a | ||
|
|
ea54820bc0 | ||
|
|
62a800a2c0 | ||
|
|
9c408d8bf5 | ||
|
|
45ad09c100 | ||
|
|
dd5cafcd42 | ||
|
|
15c837f745 | ||
|
|
eb48b56f47 | ||
|
|
a879e99e77 | ||
|
|
ddd816e7ca | ||
|
|
3f43ff05d0 | ||
|
|
d646e5f285 | ||
|
|
c10517a11b | ||
|
|
d4dee21383 | ||
|
|
79da904767 | ||
|
|
0821797044 | ||
|
|
ad7ff62b56 | ||
|
|
ba683a2e8a | ||
|
|
6b66b76f84 | ||
|
|
f148d7d0d0 | ||
|
|
e19e3865e7 | ||
|
|
c3fe763261 | ||
|
|
3c7b16412b | ||
|
|
5209c12b89 | ||
|
|
bf7aa17f65 | ||
|
|
a454c57bc9 | ||
|
|
cd6e0dcde3 | ||
|
|
a2822f5592 | ||
|
|
ca129fa4a0 | ||
|
|
cbe0c78553 | ||
|
|
2e763f1dd5 | ||
|
|
b8942c320e | ||
|
|
eee135f4ef | ||
|
|
de92b2d47e | ||
|
|
5d7384aa8b | ||
|
|
35f062d4e0 | ||
|
|
01328ba33c | ||
|
|
7caa138184 | ||
|
|
1b8c3f3081 | ||
|
|
3e4f36fb6f | ||
|
|
aedcb973f5 | ||
|
|
26dc720929 | ||
|
|
b5cc1c06df | ||
|
|
bf18e687da | ||
|
|
786c790307 | ||
|
|
e69e844568 | ||
|
|
86ff7cbc69 | ||
|
|
2b5268fb77 | ||
|
|
eebe7edba8 | ||
|
|
1a5c74dc79 | ||
|
|
3e8660bb61 | ||
|
|
997aed4ab3 | ||
|
|
fbfdbc903c | ||
|
|
be62ab5ff9 | ||
|
|
85497eb815 | ||
|
|
f64ea058b9 | ||
|
|
96485ba252 | ||
|
|
a9bdcbcdd4 | ||
|
|
1d7f1cccba | ||
|
|
5724d0129c | ||
|
|
9e64fdc985 | ||
|
|
fd579fc509 | ||
|
|
6e21b1bdf6 | ||
|
|
b002477c0d | ||
|
|
27f1447469 | ||
|
|
ff153cdd81 | ||
|
|
fe16329443 | ||
|
|
744e1cf2be | ||
|
|
ab2cf8c8c7 | ||
|
|
a7ad18fae2 | ||
|
|
fbd001b797 | ||
|
|
4610be6f15 | ||
|
|
d5e34b649f | ||
|
|
d7c23297ab | ||
|
|
8bfa879485 | ||
|
|
ea774ff22b | ||
|
|
88101b0252 | ||
|
|
f6e095e0a6 | ||
|
|
60ec76701d | ||
|
|
6b52723ba9 | ||
|
|
d7ec2e717c | ||
|
|
910c2a0f9b | ||
|
|
6c93e600c4 | ||
|
|
e70edaec7c | ||
|
|
acaba96e3b | ||
|
|
7b49a94edd | ||
|
|
12d1613b04 | ||
|
|
35fd74d3fe | ||
|
|
df878672fc | ||
|
|
076115253f | ||
|
|
8dfcda6c5e | ||
|
|
6f58528de2 | ||
|
|
756d97a9cb | ||
|
|
3defe7201f | ||
|
|
c0887dbeb9 | ||
|
|
b8f0ff217f | ||
|
|
cc805781e3 | ||
|
|
9531eca7a7 | ||
|
|
d7a1d5bbd2 | ||
|
|
c214168dcd | ||
|
|
4ce75ead52 | ||
|
|
6de7ec00fe | ||
|
|
90ea2cd699 | ||
|
|
800552210b | ||
|
|
8325e2d9cf | ||
|
|
80abea56b4 | ||
|
|
480f081c40 | ||
|
|
9529335c96 | ||
|
|
dd085be5c7 | ||
|
|
cc3c1242f5 | ||
|
|
4334b3f419 | ||
|
|
94353bb342 | ||
|
|
5aad7acdd5 | ||
|
|
b2f4a5539c | ||
|
|
cd4f7ffb9c | ||
|
|
400dc79ed6 | ||
|
|
bd7fbfff37 | ||
|
|
52a25fc720 | ||
|
|
1dfb309223 | ||
|
|
29c9fb37a1 | ||
|
|
41d6f0a4bc | ||
|
|
af70c80e09 | ||
|
|
384153d914 | ||
|
|
511ac5068b | ||
|
|
b02d4e0fdd | ||
|
|
67e490ab53 | ||
|
|
27bf8d9ed6 | ||
|
|
b84cde1633 | ||
|
|
3350f7bd56 | ||
|
|
582ad03e05 | ||
|
|
babdfe4cc5 | ||
|
|
8a7a94dd70 | ||
|
|
2654519277 | ||
|
|
02eddcbbf4 | ||
|
|
b967bb6d4e | ||
|
|
f60870a087 | ||
|
|
39aa21d985 | ||
|
|
512bb5e013 | ||
|
|
7581b8634e | ||
|
|
78f041a34f | ||
|
|
2c456cbf03 | ||
|
|
da76021802 | ||
|
|
6de06e084b | ||
|
|
cb49c91983 | ||
|
|
b0df5fa91c | ||
|
|
0652404334 | ||
|
|
bd7d8068df | ||
|
|
4dd868130c | ||
|
|
71860cf899 | ||
|
|
3512905264 | ||
|
|
2c072a9e7a | ||
|
|
fee5bee569 | ||
|
|
0a1cdbdfe3 | ||
|
|
8af79989ff | ||
|
|
5d2bdc7ee1 | ||
|
|
255d65e47d | ||
|
|
f0805e0a79 | ||
|
|
c875ade35c | ||
|
|
31b4f4e561 | ||
|
|
9b485bfe45 | ||
|
|
7510419836 | ||
|
|
470f178cde | ||
|
|
2c1844fb13 | ||
|
|
bd59e40761 | ||
|
|
0652114013 | ||
|
|
3150647ff6 | ||
|
|
e2258bb91f | ||
|
|
3dd4b3dd77 | ||
|
|
d2f7585ea5 | ||
|
|
de102d9898 | ||
|
|
4dc3bc1532 | ||
|
|
50da8cfbc6 | ||
|
|
1af511be2f | ||
|
|
14b38604a3 | ||
|
|
40c7b706aa | ||
|
|
68f4237e15 | ||
|
|
88c03ce655 | ||
|
|
d5b1496898 | ||
|
|
3486e16d4e | ||
|
|
3641363d3d | ||
|
|
fde13436c9 | ||
|
|
b9a9f07d7b | ||
|
|
f4473d11a8 | ||
|
|
965af6da5f | ||
|
|
fb5c5561e9 |
@@ -8,13 +8,13 @@ orbs:
|
||||
jobs:
|
||||
api-deploy:
|
||||
docker:
|
||||
- image: "cimg/base:stable"
|
||||
- image: cimg/node:18.18.2
|
||||
steps:
|
||||
- checkout
|
||||
- eb/setup
|
||||
- run:
|
||||
command: |
|
||||
eb init imex-online-production-api -r ca-central-1 -p "Node.js 16 running on 64bit Amazon Linux 2"
|
||||
eb init imex-online-production-api -r ca-central-1 -p "Node.js 18 running on 64bit Amazon Linux 2"
|
||||
eb status --verbose
|
||||
eb deploy
|
||||
eb status
|
||||
@@ -77,7 +77,7 @@ jobs:
|
||||
- eb/setup
|
||||
- run:
|
||||
command: |
|
||||
eb init romeonline-productionapi -r us-east-2 -p "Node.js 16 running on 64bit Amazon Linux 2"
|
||||
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
|
||||
@@ -132,6 +132,57 @@ jobs:
|
||||
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
|
||||
|
||||
working_directory: ~/repo/client
|
||||
|
||||
steps:
|
||||
- checkout:
|
||||
path: ~/repo
|
||||
|
||||
- restore_cache:
|
||||
name: Restore Yarn Package Cache
|
||||
keys:
|
||||
- yarn-packages-{{ checksum "yarn.lock" }}
|
||||
- run:
|
||||
name: Install Dependencies
|
||||
command: yarn install --frozen-lockfile --cache-folder ~/.cache/yarn
|
||||
- save_cache:
|
||||
name: Save Yarn Package Cache
|
||||
key: yarn-packages-{{ checksum "yarn.lock" }}
|
||||
paths:
|
||||
- ~/.cache/yarn
|
||||
|
||||
- run: yarn run build:test
|
||||
|
||||
- aws-s3/sync:
|
||||
from: build
|
||||
to: "s3://rome-online-test/"
|
||||
- jira/notify
|
||||
|
||||
|
||||
test-hasura-migrate:
|
||||
docker:
|
||||
- image: cimg/node:16.15.0
|
||||
@@ -250,6 +301,15 @@ workflows:
|
||||
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:
|
||||
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -117,4 +117,6 @@ logs/oAuthClient-log.log
|
||||
|
||||
.node-persist/**
|
||||
|
||||
/*.env.*
|
||||
/*.env.*
|
||||
.idea/*
|
||||
.idea
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,14 +1,14 @@
|
||||
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_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_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='BN2GcDPjipR5MTEosO5dT4CfQ3cmrdBIsI4juoOQrRijn_5aRiHlwj1mlq0W145mOusx6xynEKl_tvYJhpCc9lo'
|
||||
REACT_APP_FIREBASE_PUBLIC_VAPID_KEY='BP1B7ZTYpn-KMt6nOxlld6aS8Skt3Q7ZLEqP0hAvGHxG4UojPYiXZ6kPlzZkUC5jH-EcWXomTLtmadAIxurfcHo'
|
||||
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.test.romeonline.io/
|
||||
REACT_APP_REPORTS_SERVER_URL=https://reports.test.romeonline.io
|
||||
REACT_APP_IS_TEST=true
|
||||
REACT_APP_SPLIT_API=ts615lqgnmk84thn72uk18uu5pgce6e0l4rc
|
||||
24522
client/package-lock.json
generated
Normal file
24522
client/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@
|
||||
"@apollo/client": "^3.7.9",
|
||||
"@asseinfo/react-kanban": "^2.2.0",
|
||||
"@craco/craco": "^7.0.0",
|
||||
"@fingerprintjs/fingerprintjs": "^3.3.3",
|
||||
"@fingerprintjs/fingerprintjs": "^3.4.2",
|
||||
"@jsreport/browser-client": "^3.1.0",
|
||||
"@sentry/react": "^7.40.0",
|
||||
"@sentry/tracing": "^7.40.0",
|
||||
@@ -89,8 +89,8 @@
|
||||
"analyze": "source-map-explorer 'build/static/js/*.js'",
|
||||
"start": "craco start",
|
||||
"build": "REACT_APP_GIT_SHA=`git rev-parse --short HEAD` craco build",
|
||||
"build:test": "env-cmd -f .env.test yarn run build",
|
||||
"build-deploy:test": "yarn run build:test && s3cmd sync build/* s3://imex-online-test && echo '🚀 TESTING Deployed!'",
|
||||
"build:test": "env-cmd -f .env.test npm run build",
|
||||
"build-deploy:test": "npm run build:test && s3cmd sync build/* s3://imex-online-test && echo '🚀 TESTING Deployed!'",
|
||||
"buildcra": "REACT_APP_GIT_SHA=`git rev-parse --short HEAD` craco build",
|
||||
"test": "cypress open",
|
||||
"eject": "react-scripts eject",
|
||||
|
||||
@@ -7,26 +7,76 @@
|
||||
<meta name="theme-color" content="#002366" />
|
||||
<meta name="description" content="Rome Online" />
|
||||
<!-- <link rel="apple-touch-icon" href="logo192.png" /> -->
|
||||
<link rel="apple-touch-icon" href="logo192.png" />
|
||||
<script type="text/javascript">
|
||||
var $zoho = $zoho || {};
|
||||
$zoho.salesiq = $zoho.salesiq || {
|
||||
widgetcode:
|
||||
"2ee4b2212fbdb380fb1e5e612f1e2dd7fe52032bee013140e27458e960add8e65b3cc65a44e7ecddabee40ced28dcfbd",
|
||||
values: {},
|
||||
ready: function () {},
|
||||
};
|
||||
var d = document;
|
||||
s = d.createElement("script");
|
||||
s.type = "text/javascript";
|
||||
s.id = "zsiqscript";
|
||||
s.defer = true;
|
||||
s.src = "https://salesiq.zoho.com/widget";
|
||||
t = d.getElementsByTagName("script")[0];
|
||||
t.parentNode.insertBefore(s, t);
|
||||
d.write("<div id='zsiqwidget'></div>");
|
||||
</script>
|
||||
|
||||
<!--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>
|
||||
!(function () {
|
||||
"use strict";
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 376 B |
@@ -148,6 +148,10 @@
|
||||
background: #e7f3ff !important;
|
||||
}
|
||||
|
||||
.ant-table-tbody > tr.ant-table-row-selected > td {
|
||||
background: #e6f7ff !important;
|
||||
}
|
||||
|
||||
.job-line-manual {
|
||||
color: tomato;
|
||||
font-style: italic;
|
||||
|
||||
BIN
client/src/assets/RomeOnline.png
Normal file
BIN
client/src/assets/RomeOnline.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 26 KiB |
BIN
client/src/assets/RomeOnlineBlue.png
Normal file
BIN
client/src/assets/RomeOnlineBlue.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 27 KiB |
@@ -15,6 +15,7 @@ import { createStructuredSelector } from "reselect";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import ExportLogsCountDisplay from "../export-logs-count-display/export-logs-count-display.component";
|
||||
import BillMarkSelectedExported from "../payable-mark-selected-exported/payable-mark-selected-exported.component";
|
||||
import {pageLimit} from "../../utils/config";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
@@ -210,7 +211,7 @@ export function AccountingPayablesTableComponent({
|
||||
<Table
|
||||
loading={loading}
|
||||
dataSource={dataSource}
|
||||
pagination={{ position: "top", pageSize: 50 }}
|
||||
pagination={{ position: "top", pageSize: pageLimit }}
|
||||
columns={columns}
|
||||
rowKey="id"
|
||||
onChange={handleTableChange}
|
||||
|
||||
@@ -15,6 +15,7 @@ import PaymentExportButton from "../payment-export-button/payment-export-button.
|
||||
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,
|
||||
@@ -209,7 +210,7 @@ export function AccountingPayablesTableComponent({
|
||||
<Table
|
||||
loading={loading}
|
||||
dataSource={dataSource}
|
||||
pagination={{ position: "top", pageSize: 50 }}
|
||||
pagination={{ position: "top", pageSize: pageLimit }}
|
||||
columns={columns}
|
||||
rowKey="id"
|
||||
onChange={handleTableChange}
|
||||
|
||||
@@ -4,6 +4,7 @@ import { alphaSort } from "../../utils/sorters";
|
||||
import { DateTimeFormatter } from "../../utils/DateFormatter";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import AuditTrailValuesComponent from "../audit-trail-values/audit-trail-values.component";
|
||||
import {pageLimit} from "../../utils/config";
|
||||
|
||||
export default function AuditTrailListComponent({ loading, data }) {
|
||||
const [state, setState] = useState({
|
||||
@@ -74,7 +75,7 @@ export default function AuditTrailListComponent({ loading, data }) {
|
||||
<Table
|
||||
{...formItemLayout}
|
||||
loading={loading}
|
||||
pagination={{ position: "top", defaultPageSize: 25 }}
|
||||
pagination={{ position: "top", defaultPageSize: pageLimit }}
|
||||
columns={columns}
|
||||
rowKey="id"
|
||||
dataSource={data}
|
||||
|
||||
@@ -3,6 +3,7 @@ import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { DateTimeFormatter } from "../../utils/DateFormatter";
|
||||
import { alphaSort } from "../../utils/sorters";
|
||||
import {pageLimit} from "../../utils/config";
|
||||
|
||||
export default function EmailAuditTrailListComponent({ loading, data }) {
|
||||
const [state, setState] = useState({
|
||||
@@ -53,7 +54,7 @@ export default function EmailAuditTrailListComponent({ loading, data }) {
|
||||
<Table
|
||||
{...formItemLayout}
|
||||
loading={loading}
|
||||
pagination={{ position: "top", defaultPageSize: 25 }}
|
||||
pagination={{ position: "top", defaultPageSize: pageLimit }}
|
||||
columns={columns}
|
||||
rowKey="id"
|
||||
dataSource={data}
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
import { useApolloClient, useMutation } from "@apollo/client";
|
||||
import { Button, Checkbox, Form, Modal, notification, Space } from "antd";
|
||||
import { useTreatments } from "@splitsoftware/splitio-react";
|
||||
import { Button, Checkbox, Form, Modal, Space, notification } from "antd";
|
||||
import _ from "lodash";
|
||||
import React, { useEffect, useState, useMemo } from "react";
|
||||
import React, { useEffect, useMemo, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { INSERT_NEW_BILL } from "../../graphql/bills.queries";
|
||||
import { UPDATE_INVENTORY_LINES } from "../../graphql/inventory.queries";
|
||||
import { UPDATE_JOB_LINE } from "../../graphql/jobs-lines.queries";
|
||||
import {
|
||||
QUERY_JOB_LBR_ADJUSTMENTS,
|
||||
UPDATE_JOB,
|
||||
} from "../../graphql/jobs.queries";
|
||||
import { MUTATION_MARK_RETURN_RECEIVED } from "../../graphql/parts-orders.queries";
|
||||
import { UPDATE_INVENTORY_LINES } from "../../graphql/inventory.queries";
|
||||
import { insertAuditTrail } from "../../redux/application/application.actions";
|
||||
import { toggleModalVisible } from "../../redux/modals/modals.actions";
|
||||
import { selectBillEnterModal } from "../../redux/modals/modals.selectors";
|
||||
@@ -20,15 +21,15 @@ import {
|
||||
selectBodyshop,
|
||||
selectCurrentUser,
|
||||
} from "../../redux/user/user.selectors";
|
||||
import confirmDialog from "../../utils/asyncConfirm";
|
||||
import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
||||
import BillFormContainer from "../bill-form/bill-form.container";
|
||||
import { CalculateBillTotal } from "../bill-form/bill-form.totals.utility";
|
||||
import { handleUpload } from "../documents-upload/documents-upload.utility";
|
||||
import { handleUpload as handleLocalUpload } from "../documents-local-upload/documents-local-upload.utility";
|
||||
import useLocalStorage from "../../utils/useLocalStorage";
|
||||
import { GenerateDocument } from "../../utils/RenderTemplate";
|
||||
import { TemplateList } from "../../utils/TemplateConstants";
|
||||
import confirmDialog from "../../utils/asyncConfirm";
|
||||
import useLocalStorage from "../../utils/useLocalStorage";
|
||||
import BillFormContainer from "../bill-form/bill-form.container";
|
||||
import { CalculateBillTotal } from "../bill-form/bill-form.totals.utility";
|
||||
import { handleUpload as handleLocalUpload } from "../documents-local-upload/documents-local-upload.utility";
|
||||
import { handleUpload } from "../documents-upload/documents-upload.utility";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
billEnterModal: selectBillEnterModal,
|
||||
@@ -37,8 +38,8 @@ const mapStateToProps = createStructuredSelector({
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
toggleModalVisible: () => dispatch(toggleModalVisible("billEnter")),
|
||||
insertAuditTrail: ({ jobid, operation }) =>
|
||||
dispatch(insertAuditTrail({ jobid, operation })),
|
||||
insertAuditTrail: ({ jobid, billid, operation }) =>
|
||||
dispatch(insertAuditTrail({ jobid, billid, operation })),
|
||||
});
|
||||
|
||||
const Templates = TemplateList("job_special");
|
||||
@@ -63,9 +64,19 @@ 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:
|
||||
@@ -98,6 +109,7 @@ function BillEnterModalContainer({
|
||||
} = values;
|
||||
|
||||
let adjustmentsToInsert = {};
|
||||
let payrollAdjustmentsToInsert = [];
|
||||
|
||||
const r1 = await insertBill({
|
||||
variables: {
|
||||
@@ -113,28 +125,72 @@ function BillEnterModalContainer({
|
||||
lbr_adjustment,
|
||||
location: lineLocation,
|
||||
part_type,
|
||||
create_ppc,
|
||||
original_actual_price,
|
||||
...restI
|
||||
} = i;
|
||||
|
||||
if (deductedfromlbr) {
|
||||
adjustmentsToInsert[lbr_adjustment.mod_lbr_ty] =
|
||||
(adjustmentsToInsert[lbr_adjustment.mod_lbr_ty] || 0) -
|
||||
restI.actual_price / lbr_adjustment.rate;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
...restI,
|
||||
deductedfromlbr: deductedfromlbr,
|
||||
lbr_adjustment,
|
||||
joblineid: i.joblineid === "noline" ? null : i.joblineid,
|
||||
applicable_taxes: {
|
||||
federal:
|
||||
(i.applicable_taxes && i.applicable_taxes.federal) ||
|
||||
false,
|
||||
state:
|
||||
(i.applicable_taxes && i.applicable_taxes.state) ||
|
||||
false,
|
||||
local:
|
||||
(i.applicable_taxes && i.applicable_taxes.local) ||
|
||||
false,
|
||||
},
|
||||
};
|
||||
}),
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
refetchQueries: ["QUERY_PARTS_BILLS_BY_JOBID"],
|
||||
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.
|
||||
@@ -243,6 +299,14 @@ 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,
|
||||
}
|
||||
: {}),
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -305,16 +369,18 @@ function BillEnterModalContainer({
|
||||
insertAuditTrail({
|
||||
jobid: values.jobid,
|
||||
billid: billId,
|
||||
operation: AuditTrailMapping.billposted(remainingValues.invoice_number),
|
||||
operation: AuditTrailMapping.billposted(
|
||||
r1.data.insert_bills.returning[0].invoice_number
|
||||
),
|
||||
});
|
||||
|
||||
if (enterAgain) {
|
||||
form.resetFields();
|
||||
form.resetFields();
|
||||
// form.resetFields();
|
||||
form.setFieldsValue({
|
||||
...formValues,
|
||||
billlines: [],
|
||||
});
|
||||
form.resetFields();
|
||||
} else {
|
||||
toggleModalVisible();
|
||||
}
|
||||
@@ -389,6 +455,9 @@ function BillEnterModalContainer({
|
||||
setEnterAgain(false);
|
||||
}}
|
||||
>
|
||||
<button onClick={() => console.log(form.getFieldsValue("billlines"))}>
|
||||
get billlines
|
||||
</button>
|
||||
<BillFormContainer
|
||||
form={form}
|
||||
disableInvNumber={billEnterModal.context.disableInvNumber}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import Icon, { UploadOutlined } from "@ant-design/icons";
|
||||
import { useApolloClient } from "@apollo/client";
|
||||
import { MdOpenInNew } from "react-icons/md";
|
||||
import { useTreatments } from "@splitsoftware/splitio-react";
|
||||
import {
|
||||
Alert,
|
||||
Divider,
|
||||
@@ -12,14 +12,17 @@ import {
|
||||
Switch,
|
||||
Upload,
|
||||
} from "antd";
|
||||
import moment from "moment";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { MdOpenInNew } from "react-icons/md";
|
||||
import { connect } from "react-redux";
|
||||
import { Link } from "react-router-dom";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { CHECK_BILL_INVOICE_NUMBER } from "../../graphql/bills.queries";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import AlertComponent from "../alert/alert.component";
|
||||
import BillFormLinesExtended from "../bill-form-lines-extended/bill-form-lines-extended.component";
|
||||
import FormDatePicker from "../form-date-picker/form-date-picker.component";
|
||||
import FormFieldsChanged from "../form-fields-changed-alert/form-fields-changed-alert.component";
|
||||
import CurrencyInput from "../form-items-formatted/currency-form-item.component";
|
||||
@@ -28,8 +31,6 @@ import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
||||
import VendorSearchSelect from "../vendor-search-select/vendor-search-select.component";
|
||||
import BillFormLines from "./bill-form.lines.component";
|
||||
import { CalculateBillTotal } from "./bill-form.totals.utility";
|
||||
import { useTreatments } from "@splitsoftware/splitio-react";
|
||||
import BillFormLinesExtended from "../bill-form-lines-extended/bill-form-lines-extended.component";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
@@ -49,6 +50,7 @@ export function BillFormComponent({
|
||||
job,
|
||||
loadOutstandingReturns,
|
||||
loadInventory,
|
||||
preferredMake,
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const client = useApolloClient();
|
||||
@@ -58,6 +60,11 @@ export function BillFormComponent({
|
||||
{},
|
||||
bodyshop.imexshopid
|
||||
);
|
||||
const { ClosingPeriod } = useTreatments(
|
||||
["ClosingPeriod"],
|
||||
{},
|
||||
bodyshop.imexshopid
|
||||
);
|
||||
|
||||
const handleVendorSelect = (props, opt) => {
|
||||
setDiscount(opt.discount);
|
||||
@@ -179,6 +186,7 @@ export function BillFormComponent({
|
||||
<VendorSearchSelect
|
||||
disabled={disabled}
|
||||
options={vendorAutoCompleteOptions}
|
||||
preferredMake={preferredMake}
|
||||
onSelect={handleVendorSelect}
|
||||
/>
|
||||
</Form.Item>
|
||||
@@ -259,6 +267,37 @@ export function BillFormComponent({
|
||||
required: true,
|
||||
//message: t("general.validation.required"),
|
||||
},
|
||||
({ getFieldValue }) => ({
|
||||
validator(rule, value) {
|
||||
if (
|
||||
ClosingPeriod.treatment === "on" &&
|
||||
bodyshop.accountingconfig.ClosingPeriod
|
||||
) {
|
||||
if (
|
||||
moment(value)
|
||||
.startOf("day")
|
||||
.isSameOrAfter(
|
||||
moment(
|
||||
bodyshop.accountingconfig.ClosingPeriod[0]
|
||||
).startOf("day")
|
||||
) &&
|
||||
moment(value)
|
||||
.startOf("day")
|
||||
.isSameOrBefore(
|
||||
moment(
|
||||
bodyshop.accountingconfig.ClosingPeriod[1]
|
||||
).endOf("day")
|
||||
)
|
||||
) {
|
||||
return Promise.resolve();
|
||||
} else {
|
||||
return Promise.reject(t("bills.validation.closingperiod"));
|
||||
}
|
||||
} else {
|
||||
return Promise.resolve();
|
||||
}
|
||||
},
|
||||
}),
|
||||
]}
|
||||
>
|
||||
<FormDatePicker disabled={disabled} />
|
||||
@@ -327,13 +366,15 @@ 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")}
|
||||
@@ -341,13 +382,15 @@ 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>
|
||||
{
|
||||
// <Form.Item
|
||||
// span={3}
|
||||
// label={t("bills.fields.local_tax_rate")}
|
||||
// name="local_tax_rate"
|
||||
// >
|
||||
// <CurrencyInput min={0} />
|
||||
// </Form.Item>
|
||||
}
|
||||
<Form.Item shouldUpdate span={15}>
|
||||
{() => {
|
||||
const values = form.getFieldsValue([
|
||||
@@ -373,21 +416,25 @@ 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()}
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import { useLazyQuery, useQuery } from "@apollo/client";
|
||||
import { useTreatments } from "@splitsoftware/splitio-react";
|
||||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { QUERY_OUTSTANDING_INVENTORY } from "../../graphql/inventory.queries";
|
||||
import { GET_JOB_LINES_TO_ENTER_BILL } from "../../graphql/jobs-lines.queries";
|
||||
import { QUERY_UNRECEIVED_LINES } from "../../graphql/parts-orders.queries";
|
||||
import { SEARCH_VENDOR_AUTOCOMPLETE } from "../../graphql/vendors.queries";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import BillFormComponent from "./bill-form.component";
|
||||
import BillCmdReturnsTableComponent from "../bill-cm-returns-table/bill-cm-returns-table.component";
|
||||
import { QUERY_UNRECEIVED_LINES } from "../../graphql/parts-orders.queries";
|
||||
import BillInventoryTable from "../bill-inventory-table/bill-inventory-table.component";
|
||||
import { QUERY_OUTSTANDING_INVENTORY } from "../../graphql/inventory.queries";
|
||||
import { useTreatments } from "@splitsoftware/splitio-react";
|
||||
import BillFormComponent from "./bill-form.component";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
@@ -59,6 +59,7 @@ export function BillFormContainer({
|
||||
disableInvNumber={disableInvNumber}
|
||||
loadOutstandingReturns={loadOutstandingReturns}
|
||||
loadInventory={loadInventory}
|
||||
preferredMake={lineData ? lineData.jobs_by_pk.v_make_desc : null}
|
||||
/>
|
||||
{!billEdit && (
|
||||
<BillCmdReturnsTableComponent
|
||||
|
||||
@@ -2,6 +2,7 @@ import { DeleteFilled, DollarCircleFilled } from "@ant-design/icons";
|
||||
import { useTreatments } from "@splitsoftware/splitio-react";
|
||||
import {
|
||||
Button,
|
||||
Checkbox,
|
||||
Form,
|
||||
Input,
|
||||
InputNumber,
|
||||
@@ -46,6 +47,13 @@ export function BillEnterModalLinesComponent({
|
||||
{},
|
||||
bodyshop && bodyshop.imexshopid
|
||||
);
|
||||
|
||||
const { Enhanced_Payroll } = useTreatments(
|
||||
["Enhanced_Payroll"],
|
||||
{},
|
||||
bodyshop.imexshopid
|
||||
);
|
||||
|
||||
const columns = (remove) => {
|
||||
return [
|
||||
{
|
||||
@@ -94,6 +102,7 @@ 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"
|
||||
@@ -220,6 +229,43 @@ 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"),
|
||||
@@ -361,7 +407,7 @@ export function BillEnterModalLinesComponent({
|
||||
},
|
||||
formInput: (record, index) => <Switch disabled={disabled} />,
|
||||
additional: (record, index) => (
|
||||
<Form.Item shouldUpdate style={{ display: "inline-block" }}>
|
||||
<Form.Item shouldUpdate noStyle style={{ display: "inline-block" }}>
|
||||
{() => {
|
||||
const price = getFieldValue([
|
||||
"billlines",
|
||||
@@ -376,12 +422,31 @@ 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,
|
||||
@@ -435,22 +500,44 @@ export function BillEnterModalLinesComponent({
|
||||
</Select.Option>
|
||||
</Select>
|
||||
</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>
|
||||
{price &&
|
||||
adjustmentRate &&
|
||||
`${(price / adjustmentRate).toFixed(1)} hrs`}
|
||||
{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>
|
||||
</div>
|
||||
);
|
||||
return <></>;
|
||||
@@ -458,21 +545,21 @@ 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: true,
|
||||
// name: [field.name, "applicable_taxes", "federal"],
|
||||
// };
|
||||
// },
|
||||
// formInput: (record, index) => <Switch disabled={disabled} />,
|
||||
// },
|
||||
{
|
||||
title: t("billlines.fields.state_tax_applicable"),
|
||||
dataIndex: "applicable_taxes.state",
|
||||
@@ -487,20 +574,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"),
|
||||
|
||||
@@ -625,7 +712,7 @@ const EditableCell = ({
|
||||
if (additional)
|
||||
return (
|
||||
<td {...restProps}>
|
||||
<Space size="small">
|
||||
<div size="small">
|
||||
<Form.Item
|
||||
name={dataIndex}
|
||||
labelCol={{ span: 0 }}
|
||||
@@ -634,7 +721,7 @@ const EditableCell = ({
|
||||
{(formInput && formInput(record, record.name)) || children}
|
||||
</Form.Item>
|
||||
{additional && additional(record, record.name)}
|
||||
</Space>
|
||||
</div>
|
||||
</td>
|
||||
);
|
||||
if (wrapper)
|
||||
|
||||
@@ -63,6 +63,12 @@ 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)}`
|
||||
|
||||
@@ -1,21 +1,40 @@
|
||||
import React, { useCallback, useEffect } from "react";
|
||||
import { DeleteFilled } from "@ant-design/icons";
|
||||
import { useLazyQuery, useMutation } from "@apollo/client";
|
||||
import {
|
||||
Button,
|
||||
Card,
|
||||
Col,
|
||||
Form,
|
||||
Input,
|
||||
Row,
|
||||
Space,
|
||||
Spin,
|
||||
Statistic,
|
||||
notification,
|
||||
} from "antd";
|
||||
import axios from "axios";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Button, Card, Form, Input, InputNumber, Row, Select } from "antd";
|
||||
import moment from "moment";
|
||||
import { useMutation, useQuery } from "@apollo/client";
|
||||
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
||||
import JobSearchSelectComponent from "../job-search-select/job-search-select.component";
|
||||
import { INSERT_NEW_PAYMENT } from "../../graphql/payments.queries";
|
||||
import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import {
|
||||
INSERT_PAYMENT_RESPONSE,
|
||||
QUERY_RO_AND_OWNER_BY_JOB_PK,
|
||||
QUERY_RO_AND_OWNER_BY_JOB_PKS,
|
||||
} from "../../graphql/payment_response.queries";
|
||||
import DataLabel from "../data-label/data-label.component";
|
||||
import { INSERT_NEW_PAYMENT } from "../../graphql/payments.queries";
|
||||
import { insertAuditTrail } from "../../redux/application/application.actions";
|
||||
import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
||||
import { connect } from "react-redux";
|
||||
import { toggleModalVisible } from "../../redux/modals/modals.actions";
|
||||
import { selectCardPayment } from "../../redux/modals/modals.selectors";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
||||
import CurrencyFormItemComponent from "../form-items-formatted/currency-form-item.component";
|
||||
import JobSearchSelectComponent from "../job-search-select/job-search-select.component";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
cardPaymentModal: selectCardPayment,
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
insertAuditTrail: ({ jobid, operation }) =>
|
||||
@@ -25,253 +44,331 @@ const mapDispatchToProps = (dispatch) => ({
|
||||
|
||||
const CardPaymentModalComponent = ({
|
||||
bodyshop,
|
||||
context,
|
||||
cardPaymentModal,
|
||||
toggleModalVisible,
|
||||
insertAuditTrail,
|
||||
}) => {
|
||||
const { context } = cardPaymentModal;
|
||||
|
||||
const [form] = Form.useForm();
|
||||
const amount = Form.useWatch("amount", form);
|
||||
const payer = Form.useWatch("payer", form);
|
||||
const jobid = Form.useWatch("jobid", form);
|
||||
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [insertPayment] = useMutation(INSERT_NEW_PAYMENT);
|
||||
const [insertPaymentResponse] = useMutation(INSERT_PAYMENT_RESPONSE);
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { data, refetch } = useQuery(QUERY_RO_AND_OWNER_BY_JOB_PK, {
|
||||
variables: { jobid: context?.jobid ?? "" },
|
||||
});
|
||||
const [, { data, refetch, queryLoading }] = useLazyQuery(
|
||||
QUERY_RO_AND_OWNER_BY_JOB_PKS,
|
||||
{
|
||||
variables: { jobids: [context.jobid] },
|
||||
skip: true,
|
||||
}
|
||||
);
|
||||
|
||||
const nonApproval = useCallback(
|
||||
async (response) => {
|
||||
console.log("🚀 ~ file: card-payment-modal.component..jsx:61 ~ data:", data);
|
||||
//Initialize the intellipay window.
|
||||
const SetIntellipayCallbackFunctions = () => {
|
||||
console.log("*** Set IntelliPay callback functions.");
|
||||
window.intellipay.runOnClose(() => {
|
||||
//window.intellipay.initialize();
|
||||
});
|
||||
|
||||
window.intellipay.runOnApproval(async function (response) {
|
||||
console.warn("*** Running On Approval Script ***");
|
||||
form.setFieldValue("paymentResponse", response);
|
||||
form.submit();
|
||||
});
|
||||
|
||||
window.intellipay.runOnNonApproval(async function (response) {
|
||||
// Mutate unsuccessful payment
|
||||
|
||||
const { payments } = form.getFieldsValue();
|
||||
|
||||
await insertPaymentResponse({
|
||||
variables: {
|
||||
paymentResponse: {
|
||||
amount: response.amount,
|
||||
paymentResponse: payments.map((payment) => ({
|
||||
amount: payment.amount,
|
||||
bodyshopid: bodyshop.id,
|
||||
jobid: jobid || context.jobid,
|
||||
jobid: payment.jobid,
|
||||
declinereason: response.declinereason,
|
||||
ext_paymentid: response.paymentid.toString(),
|
||||
successful: false,
|
||||
response,
|
||||
},
|
||||
})),
|
||||
},
|
||||
});
|
||||
|
||||
// Insert failed payment to audit trail
|
||||
insertAuditTrail({
|
||||
jobid: jobid || context?.jobid,
|
||||
operation: AuditTrailMapping.failedpayment(),
|
||||
});
|
||||
},
|
||||
[bodyshop, context, insertAuditTrail, insertPaymentResponse, jobid]
|
||||
);
|
||||
|
||||
const initIntellipayFunctions = useCallback(() => {
|
||||
if (window.intellipay !== undefined && typeof jobid !== "undefined") {
|
||||
console.log("intellipay init functions");
|
||||
|
||||
window.intellipay.runOnClose(() => {
|
||||
window.intellipay.initialize();
|
||||
});
|
||||
|
||||
window.intellipay.runOnApproval(async function (response) {
|
||||
form.setFieldValue("paymentResponse", response);
|
||||
form.submit();
|
||||
|
||||
toggleModalVisible();
|
||||
});
|
||||
|
||||
window.intellipay.runOnNonApproval(nonApproval);
|
||||
}
|
||||
}, [form, jobid, nonApproval, toggleModalVisible]);
|
||||
|
||||
const initJobId = useCallback(() => {
|
||||
if (context?.jobid) {
|
||||
form.setFieldValue("jobid", context.jobid);
|
||||
}
|
||||
|
||||
form.setFieldValue("payer", t("payments.labels.customer"));
|
||||
}, [context?.jobid, form, t]);
|
||||
|
||||
useEffect(() => {
|
||||
initJobId();
|
||||
|
||||
axios
|
||||
.post("/intellipay/lightbox_credentials", { bodyshop })
|
||||
.then((response) => {
|
||||
var rg = document.createRange();
|
||||
let node = rg.createContextualFragment(response.data);
|
||||
|
||||
document.documentElement.appendChild(node);
|
||||
window.intellipay.initialize();
|
||||
|
||||
initIntellipayFunctions();
|
||||
});
|
||||
|
||||
function handleEvents(...props) {
|
||||
const operation = props[0].data.operation;
|
||||
|
||||
if (operation === "updateform") {
|
||||
props[0].stopImmediatePropagation();
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener("message", handleEvents, false);
|
||||
|
||||
return () => window.removeEventListener("message", handleEvents, false);
|
||||
}, [bodyshop, initJobId, initIntellipayFunctions]);
|
||||
payments.forEach((payment) =>
|
||||
insertAuditTrail({
|
||||
jobid: payment.jobid,
|
||||
operation: AuditTrailMapping.failedpayment(),
|
||||
})
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
const handleFinish = async (values) => {
|
||||
const paymentResult = await insertPayment({
|
||||
variables: {
|
||||
paymentInput: {
|
||||
amount: values.amount,
|
||||
transactionid: values.paymentResponse.receiptelements.transid,
|
||||
payer: values.payer,
|
||||
type: values.paymentResponse.cardType,
|
||||
jobid: values.jobid,
|
||||
date: moment(Date.now()),
|
||||
},
|
||||
},
|
||||
update(cache, { data }) {
|
||||
cache.modify({
|
||||
id: cache.identify({ id: jobid, __typename: "jobs" }),
|
||||
fields: {
|
||||
payments(payments) {
|
||||
return [...data.insert_payments.returning, ...payments];
|
||||
},
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
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,
|
||||
|
||||
await insertPaymentResponse({
|
||||
variables: {
|
||||
paymentResponse: {
|
||||
amount: values.amount,
|
||||
bodyshopid: bodyshop.id,
|
||||
paymentid: paymentResult.data.insert_payments.returning[0].id,
|
||||
jobid: values.jobid,
|
||||
declinereason: values.paymentResponse.declinereason,
|
||||
ext_paymentid: values.paymentResponse.paymentid.toString(),
|
||||
successful: true,
|
||||
response: values.paymentResponse,
|
||||
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();
|
||||
} catch (error) {
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await axios.post("/intellipay/lightbox_credentials", {
|
||||
bodyshop,
|
||||
refresh: !!window.intellipay,
|
||||
});
|
||||
|
||||
if (window.intellipay) {
|
||||
// eslint-disable-next-line no-eval
|
||||
eval(response.data);
|
||||
SetIntellipayCallbackFunctions();
|
||||
window.intellipay.autoOpen();
|
||||
} else {
|
||||
var rg = document.createRange();
|
||||
let node = rg.createContextualFragment(response.data);
|
||||
document.documentElement.appendChild(node);
|
||||
SetIntellipayCallbackFunctions();
|
||||
window.intellipay.isAutoOpen = true;
|
||||
window.intellipay.initialize();
|
||||
}
|
||||
} catch (error) {
|
||||
notification.open({
|
||||
type: "error",
|
||||
message: t("job_payments.notifications.error.openingip"),
|
||||
});
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Card title="Card Payment">
|
||||
<Form onFinish={handleFinish} form={form}>
|
||||
<LayoutFormRow grow>
|
||||
<Form.Item
|
||||
name="jobid"
|
||||
label={t("bills.fields.ro_number")}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
// message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<JobSearchSelectComponent
|
||||
disabled={context?.jobid}
|
||||
notExported={false}
|
||||
clm_no
|
||||
onChange={(e) => {
|
||||
refetch({ jobid: e });
|
||||
}}
|
||||
/>
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
|
||||
{/* Lighbox Input amount needs to be hidden */}
|
||||
<Input
|
||||
className="ipayfield"
|
||||
data-ipayname="amount"
|
||||
type="hidden"
|
||||
value={amount}
|
||||
hidden
|
||||
/>
|
||||
<Input
|
||||
className="ipayfield"
|
||||
data-ipayname="account"
|
||||
type="hidden"
|
||||
value={data?.jobs_by_pk.ro_number}
|
||||
hidden
|
||||
/>
|
||||
<Input
|
||||
className="ipayfield"
|
||||
data-ipayname="email"
|
||||
type="hidden"
|
||||
value={data?.jobs_by_pk.owner.ownr_ea}
|
||||
hidden
|
||||
/>
|
||||
{/* Lightbox payment response when it is completed */}
|
||||
<Form.Item name="paymentResponse" hidden>
|
||||
<Input type="hidden" value={amount} />
|
||||
</Form.Item>
|
||||
|
||||
<LayoutFormRow grow>
|
||||
<Form.Item
|
||||
label={t("payments.fields.payer")}
|
||||
name="payer"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
// message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Select>
|
||||
<Select.Option value={t("payments.labels.customer")}>
|
||||
{t("payments.labels.customer")}
|
||||
</Select.Option>
|
||||
<Select.Option value={t("payments.labels.insurance")}>
|
||||
{t("payments.labels.insurance")}
|
||||
</Select.Option>
|
||||
</Select>
|
||||
</Form.Item>
|
||||
<Spin spinning={loading}>
|
||||
<Form
|
||||
onFinish={handleFinish}
|
||||
form={form}
|
||||
layout="vertical"
|
||||
initialValues={{
|
||||
payments: context.jobid ? [{ jobid: context.jobid }] : [],
|
||||
}}
|
||||
>
|
||||
<Form.List name={["payments"]}>
|
||||
{(fields, { add, remove, move }) => {
|
||||
return (
|
||||
<div>
|
||||
{fields.map((field, index) => (
|
||||
<Form.Item key={field.key}>
|
||||
<Row gutter={[16, 16]}>
|
||||
<Col span={16}>
|
||||
<Form.Item
|
||||
key={`${index}jobid`}
|
||||
label={t("jobs.fields.ro_number")}
|
||||
name={[field.name, "jobid"]}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
//message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<JobSearchSelectComponent
|
||||
notExported={false}
|
||||
clm_no
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={6}>
|
||||
<Form.Item
|
||||
key={`${index}amount`}
|
||||
label={t("payments.fields.amount")}
|
||||
name={[field.name, "amount"]}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
//message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<CurrencyFormItemComponent />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={2}>
|
||||
<DeleteFilled
|
||||
style={{ margin: "1rem" }}
|
||||
onClick={() => {
|
||||
remove(field.name);
|
||||
}}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
</Form.Item>
|
||||
))}
|
||||
<Form.Item>
|
||||
<Button
|
||||
type="dashed"
|
||||
onClick={() => {
|
||||
add();
|
||||
}}
|
||||
style={{ width: "100%" }}
|
||||
>
|
||||
{t("general.actions.add")}
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
</Form.List>
|
||||
|
||||
<Form.Item
|
||||
label="Amount"
|
||||
name="amount"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
// message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
shouldUpdate={(prevValues, curValues) =>
|
||||
prevValues.payments?.map((p) => p?.jobid).join() !==
|
||||
curValues.payments?.map((p) => p?.jobid).join()
|
||||
}
|
||||
>
|
||||
<InputNumber />
|
||||
{() => {
|
||||
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
|
||||
className="ipayfield"
|
||||
data-ipayname="account"
|
||||
//type="hidden"
|
||||
value={
|
||||
payments && data && data.jobs.length > 0
|
||||
? data.jobs.map((j) => j.ro_number).join(", ")
|
||||
: null
|
||||
}
|
||||
hidden
|
||||
/>
|
||||
<Input
|
||||
className="ipayfield"
|
||||
data-ipayname="email"
|
||||
// type="hidden"
|
||||
value={
|
||||
payments && data && data.jobs.length > 0
|
||||
? data.jobs.filter((j) => j.ownr_ea)[0]?.ownr_ea
|
||||
: null
|
||||
}
|
||||
hidden
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}}
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
shouldUpdate={(prevValues, curValues) =>
|
||||
prevValues.payments?.map((p) => p?.amount).join() !==
|
||||
curValues.payments?.map((p) => p?.amount).join()
|
||||
}
|
||||
>
|
||||
{() => {
|
||||
const { payments } = form.getFieldsValue();
|
||||
const totalAmountToCharge = payments?.reduce((acc, val) => {
|
||||
return acc + (val?.amount || 0);
|
||||
}, 0);
|
||||
|
||||
return (
|
||||
<Space style={{ float: "right" }}>
|
||||
<Statistic
|
||||
title="Amount To Charge"
|
||||
value={totalAmountToCharge}
|
||||
precision={2}
|
||||
/>
|
||||
<Input
|
||||
className="ipayfield"
|
||||
data-ipayname="amount"
|
||||
//type="hidden"
|
||||
value={totalAmountToCharge?.toFixed(2)}
|
||||
hidden
|
||||
/>
|
||||
<Button
|
||||
type="primary"
|
||||
// data-ipayname="submit"
|
||||
className="ipayfield"
|
||||
loading={queryLoading || loading}
|
||||
disabled={!(totalAmountToCharge > 0)}
|
||||
onClick={handleIntelliPayCharge}
|
||||
>
|
||||
{t("job_payments.buttons.proceedtopayment")}
|
||||
</Button>
|
||||
</Space>
|
||||
);
|
||||
}}
|
||||
</Form.Item>
|
||||
|
||||
<Row justify="space-around">
|
||||
<Button
|
||||
type="primary"
|
||||
data-ipayname="submit"
|
||||
className="ipayfield"
|
||||
disabled={!amount || !payer || !jobid}
|
||||
>
|
||||
{t("job_payments.buttons.proceedtopayment")}
|
||||
</Button>
|
||||
{context?.balance && (
|
||||
<DataLabel
|
||||
valueStyle={{
|
||||
color: context?.balance.getAmount() !== 0 ? "red" : "green",
|
||||
}}
|
||||
label={t("payments.labels.balance")}
|
||||
>
|
||||
{context?.balance.toFormat()}
|
||||
</DataLabel>
|
||||
)}
|
||||
</Row>
|
||||
</LayoutFormRow>
|
||||
</Form>
|
||||
{/* Lightbox payment response when it is completed */}
|
||||
<Form.Item name="paymentResponse" hidden>
|
||||
<Input type="hidden" />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Spin>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
export default connect(null, mapDispatchToProps)(CardPaymentModalComponent);
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(CardPaymentModalComponent);
|
||||
|
||||
@@ -22,7 +22,7 @@ function CardPaymentModalContainer({
|
||||
toggleModalVisible,
|
||||
bodyshop,
|
||||
}) {
|
||||
const { context, visible } = cardPaymentModal;
|
||||
const { visible } = cardPaymentModal;
|
||||
const { t } = useTranslation();
|
||||
|
||||
const handleCancel = () => {
|
||||
@@ -35,7 +35,7 @@ function CardPaymentModalContainer({
|
||||
|
||||
return (
|
||||
<Modal
|
||||
visible={visible}
|
||||
open={visible}
|
||||
onOk={handleOK}
|
||||
onCancel={handleCancel}
|
||||
footer={[
|
||||
@@ -43,10 +43,10 @@ function CardPaymentModalContainer({
|
||||
{t("job_payments.buttons.goback")}
|
||||
</Button>,
|
||||
]}
|
||||
width="60%"
|
||||
width="80%"
|
||||
destroyOnClose
|
||||
>
|
||||
<CardPaymentModalComponent bodyshop={bodyshop} context={context} />
|
||||
<CardPaymentModalComponent />
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -8,15 +8,23 @@ import PhoneNumberFormatter from "../../utils/PhoneFormatter";
|
||||
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import { searchingForConversation } from "../../redux/messaging/messaging.selectors";
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
searchingForConversation: searchingForConversation,
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
openChatByPhone: (phone) => dispatch(openChatByPhone(phone)),
|
||||
});
|
||||
|
||||
export function ChatOpenButton({ bodyshop, phone, jobid, openChatByPhone }) {
|
||||
export function ChatOpenButton({
|
||||
bodyshop,
|
||||
searchingForConversation,
|
||||
phone,
|
||||
jobid,
|
||||
openChatByPhone,
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
if (!phone) return <></>;
|
||||
|
||||
@@ -29,7 +37,7 @@ export function ChatOpenButton({ bodyshop, phone, jobid, openChatByPhone }) {
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
const p = parsePhoneNumber(phone, "CA");
|
||||
|
||||
if (searchingForConversation) return; //This is to prevent finding the same thing twice.
|
||||
if (p && p.isValid()) {
|
||||
openChatByPhone({ phone_num: p.formatInternational(), jobid: jobid });
|
||||
} else {
|
||||
|
||||
@@ -132,7 +132,7 @@ export function ChatPopupComponent({
|
||||
onClick={() => toggleChatVisible()}
|
||||
style={{ cursor: "pointer" }}
|
||||
>
|
||||
<MessageOutlined />
|
||||
<MessageOutlined className="chat-popup-info-icon" />
|
||||
<strong>{t("messaging.labels.messaging")}</strong>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -13,6 +13,9 @@
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
.chat-popup-info-icon {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
@media only screen and (min-width: 992px) {
|
||||
.chat-popup {
|
||||
|
||||
@@ -59,6 +59,14 @@ export default function ContractsCarsComponent({
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "model" && state.sortedInfo.order,
|
||||
},
|
||||
{
|
||||
title: t("courtesycars.fields.color"),
|
||||
dataIndex: "color",
|
||||
key: "color",
|
||||
sorter: (a, b) => alphaSort(a.color, b.color),
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "color" && state.sortedInfo.order,
|
||||
},
|
||||
{
|
||||
title: t("courtesycars.fields.plate"),
|
||||
dataIndex: "plate",
|
||||
@@ -93,6 +101,9 @@ export default function ContractsCarsComponent({
|
||||
(cc.model || "")
|
||||
.toLowerCase()
|
||||
.includes(state.search.toLowerCase()) ||
|
||||
(cc.color || "")
|
||||
.toLowerCase()
|
||||
.includes(state.search.toLowerCase()) ||
|
||||
(cc.plate || "").toLowerCase().includes(state.search.toLowerCase())
|
||||
);
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import { useTranslation } from "react-i18next";
|
||||
import { alphaSort } from "../../utils/sorters";
|
||||
import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component";
|
||||
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
|
||||
import {pageLimit} from "../../utils/config";
|
||||
|
||||
export default function ContractsJobsComponent({
|
||||
loading,
|
||||
@@ -175,7 +176,7 @@ export default function ContractsJobsComponent({
|
||||
loading={loading}
|
||||
pagination={{
|
||||
position: "top",
|
||||
defaultPageSize: 10,
|
||||
defaultPageSize: pageLimit,
|
||||
defaultCurrent: defaultCurrent,
|
||||
}}
|
||||
columns={columns}
|
||||
|
||||
@@ -2,10 +2,10 @@ import { useQuery } from "@apollo/client";
|
||||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { QUERY_ALL_ACTIVE_JOBS } from "../../graphql/jobs.queries";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import AlertComponent from "../alert/alert.component";
|
||||
import ContractJobsComponent from "./contract-jobs.component";
|
||||
import { QUERY_ALL_ACTIVE_JOBS } from "../../graphql/jobs.queries";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
//currentUser: selectCurrentUser
|
||||
@@ -15,6 +15,7 @@ export function ContractJobsContainer({ selectedJobState, bodyshop }) {
|
||||
const { loading, error, data } = useQuery(QUERY_ALL_ACTIVE_JOBS, {
|
||||
variables: {
|
||||
statuses: bodyshop.md_ro_statuses.active_statuses || ["Open"],
|
||||
isConverted: true,
|
||||
},
|
||||
fetchPolicy: "network-only",
|
||||
nextFetchPolicy: "network-only",
|
||||
|
||||
@@ -4,15 +4,16 @@ import queryString from "query-string";
|
||||
import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Link, useHistory, useLocation } from "react-router-dom";
|
||||
import { setModalContext } from "../../redux/modals/modals.actions";
|
||||
import { DateTimeFormatter } from "../../utils/DateFormatter";
|
||||
import { alphaSort } from "../../utils/sorters";
|
||||
import ContractsFindModalContainer from "../contracts-find-modal/contracts-find-modal.container";
|
||||
import { setModalContext } from "../../redux/modals/modals.actions";
|
||||
|
||||
import moment from "moment";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import moment from "moment";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import {pageLimit} from "../../utils/config";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
@@ -209,7 +210,7 @@ export function ContractsList({
|
||||
}}
|
||||
pagination={{
|
||||
position: "top",
|
||||
pageSize: 25,
|
||||
pageSize: pageLimit,
|
||||
current: parseInt(page || 1),
|
||||
total: total,
|
||||
}}
|
||||
|
||||
@@ -5,6 +5,7 @@ import { useTranslation } from "react-i18next";
|
||||
import { Link, useHistory, useLocation } from "react-router-dom";
|
||||
import { DateFormatter } from "../../utils/DateFormatter";
|
||||
import { alphaSort } from "../../utils/sorters";
|
||||
import {pageLimit} from "../../utils/config";
|
||||
|
||||
export default function CourtesyCarContractListComponent({
|
||||
contracts,
|
||||
@@ -89,7 +90,7 @@ export default function CourtesyCarContractListComponent({
|
||||
scroll={{ x: true }}
|
||||
pagination={{
|
||||
position: "top",
|
||||
pageSize: 25,
|
||||
pageSize: pageLimit,
|
||||
current: parseInt(page || 1),
|
||||
total: totalContracts,
|
||||
}}
|
||||
|
||||
@@ -34,6 +34,18 @@ export default function CourtesyCarCreateFormComponent({ form, saveLoading }) {
|
||||
|
||||
{/* <FormFieldsChanged form={form} /> */}
|
||||
<LayoutFormRow header={t("courtesycars.labels.vehicle")}>
|
||||
<Form.Item
|
||||
label={t("courtesycars.fields.year")}
|
||||
name="year"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
//message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("courtesycars.fields.make")}
|
||||
name="make"
|
||||
@@ -58,18 +70,6 @@ export default function CourtesyCarCreateFormComponent({ form, saveLoading }) {
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("courtesycars.fields.year")}
|
||||
name="year"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
//message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("courtesycars.fields.plate")}
|
||||
name="plate"
|
||||
@@ -118,7 +118,7 @@ export default function CourtesyCarCreateFormComponent({ form, saveLoading }) {
|
||||
},
|
||||
]}
|
||||
>
|
||||
<InputNumber precision={0} />
|
||||
<InputNumber min={0} precision={0} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("courtesycars.fields.fleetnumber")}
|
||||
@@ -213,49 +213,23 @@ export default function CourtesyCarCreateFormComponent({ form, saveLoading }) {
|
||||
>
|
||||
<CourtesyCarStatus />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("courtesycars.fields.nextservicekm")}
|
||||
name="nextservicekm"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
//message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<InputNumber />
|
||||
</Form.Item>
|
||||
<div>
|
||||
<Form.Item
|
||||
label={t("courtesycars.fields.nextservicedate")}
|
||||
name="nextservicedate"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
//message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
label={t("courtesycars.fields.nextservicekm")}
|
||||
name="nextservicekm"
|
||||
>
|
||||
<FormDatePicker />
|
||||
<InputNumber min={0} precision={0} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
shouldUpdate={(p, c) =>
|
||||
p.mileage !== c.mileage ||
|
||||
p.nextservicedate !== c.nextservicedate ||
|
||||
p.nextservicekm !== c.nextservicekm
|
||||
p.mileage !== c.mileage || p.nextservicekm !== c.nextservicekm
|
||||
}
|
||||
>
|
||||
{() => {
|
||||
const nextservicedate = form.getFieldValue("nextservicedate");
|
||||
const nextservicekm = form.getFieldValue("nextservicekm");
|
||||
|
||||
const mileageOver =
|
||||
nextservicekm <= form.getFieldValue("mileage");
|
||||
|
||||
const dueForService =
|
||||
nextservicedate && moment(nextservicedate).isBefore(moment());
|
||||
|
||||
if (mileageOver || dueForService)
|
||||
nextservicekm && nextservicekm <= form.getFieldValue("mileage");
|
||||
if (mileageOver)
|
||||
return (
|
||||
<Space direction="vertical" style={{ color: "tomato" }}>
|
||||
<span>
|
||||
@@ -263,6 +237,36 @@ export default function CourtesyCarCreateFormComponent({ form, saveLoading }) {
|
||||
{t("contracts.labels.cardueforservice")}
|
||||
</span>
|
||||
<span>{`${nextservicekm} km`}</span>
|
||||
</Space>
|
||||
);
|
||||
|
||||
return <></>;
|
||||
}}
|
||||
</Form.Item>
|
||||
</div>
|
||||
<div>
|
||||
<Form.Item
|
||||
label={t("courtesycars.fields.nextservicedate")}
|
||||
name="nextservicedate"
|
||||
>
|
||||
<FormDatePicker />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
shouldUpdate={(p, c) => p.nextservicedate !== c.nextservicedate}
|
||||
>
|
||||
{() => {
|
||||
const nextservicedate = form.getFieldValue("nextservicedate");
|
||||
const dueForService =
|
||||
nextservicedate &&
|
||||
moment(nextservicedate).endOf("day").isSameOrBefore(moment());
|
||||
|
||||
if (dueForService)
|
||||
return (
|
||||
<Space direction="vertical" style={{ color: "tomato" }}>
|
||||
<span>
|
||||
<WarningFilled style={{ marginRight: ".3rem" }} />
|
||||
{t("contracts.labels.cardueforservice")}
|
||||
</span>
|
||||
<span>
|
||||
<DateFormatter>{nextservicedate}</DateFormatter>
|
||||
</span>
|
||||
@@ -283,12 +287,6 @@ export default function CourtesyCarCreateFormComponent({ form, saveLoading }) {
|
||||
<Form.Item
|
||||
label={t("courtesycars.fields.registrationexpires")}
|
||||
name="registrationexpires"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
//message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<FormDatePicker />
|
||||
</Form.Item>
|
||||
@@ -300,7 +298,8 @@ export default function CourtesyCarCreateFormComponent({ form, saveLoading }) {
|
||||
{() => {
|
||||
const expires = form.getFieldValue("registrationexpires");
|
||||
|
||||
const dateover = expires && moment(expires).isBefore(moment());
|
||||
const dateover =
|
||||
expires && moment(expires).endOf("day").isBefore(moment());
|
||||
|
||||
if (dateover)
|
||||
return (
|
||||
@@ -330,14 +329,13 @@ export default function CourtesyCarCreateFormComponent({ form, saveLoading }) {
|
||||
<FormDatePicker />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
shouldUpdate={(p, c) =>
|
||||
p.insuranceexpires !== c.insuranceexpires
|
||||
}
|
||||
shouldUpdate={(p, c) => p.insuranceexpires !== c.insuranceexpires}
|
||||
>
|
||||
{() => {
|
||||
const expires = form.getFieldValue("insuranceexpires");
|
||||
|
||||
const dateover = expires && moment(expires).isBefore(moment());
|
||||
const dateover =
|
||||
expires && moment(expires).endOf("day").isBefore(moment());
|
||||
|
||||
if (dateover)
|
||||
return (
|
||||
|
||||
@@ -9,15 +9,15 @@ import {
|
||||
Table,
|
||||
Tooltip,
|
||||
} from "antd";
|
||||
import moment from "moment";
|
||||
import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Link } from "react-router-dom";
|
||||
import { DateTimeFormatter } from "../../utils/DateFormatter";
|
||||
import { alphaSort } from "../../utils/sorters";
|
||||
import { OwnerNameDisplayFunction } from "../owner-name-display/owner-name-display.component";
|
||||
import moment from "moment";
|
||||
import { GenerateDocument } from "../../utils/RenderTemplate";
|
||||
import { TemplateList } from "../../utils/TemplateConstants";
|
||||
import { alphaSort } from "../../utils/sorters";
|
||||
import { OwnerNameDisplayFunction } from "../owner-name-display/owner-name-display.component";
|
||||
export default function CourtesyCarsList({ loading, courtesycars, refetch }) {
|
||||
const [state, setState] = useState({
|
||||
sortedInfo: {},
|
||||
@@ -115,6 +115,14 @@ export default function CourtesyCarsList({ loading, courtesycars, refetch }) {
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "model" && state.sortedInfo.order,
|
||||
},
|
||||
{
|
||||
title: t("courtesycars.fields.color"),
|
||||
dataIndex: "color",
|
||||
key: "color",
|
||||
sorter: (a, b) => alphaSort(a.color, b.color),
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "color" && state.sortedInfo.order,
|
||||
},
|
||||
{
|
||||
title: t("courtesycars.fields.plate"),
|
||||
dataIndex: "plate",
|
||||
@@ -166,6 +174,7 @@ export default function CourtesyCarsList({ loading, courtesycars, refetch }) {
|
||||
(c.year || "").toLowerCase().includes(searchText.toLowerCase()) ||
|
||||
(c.make || "").toLowerCase().includes(searchText.toLowerCase()) ||
|
||||
(c.model || "").toLowerCase().includes(searchText.toLowerCase()) ||
|
||||
(c.plate || "").toLowerCase().includes(searchText.toLowerCase()) ||
|
||||
(t(c.status) || "").toLowerCase().includes(searchText.toLowerCase())
|
||||
)
|
||||
: courtesycars;
|
||||
|
||||
@@ -7,6 +7,7 @@ import { Link, useHistory, useLocation } from "react-router-dom";
|
||||
import { DateFormatter } from "../../utils/DateFormatter";
|
||||
import { alphaSort } from "../../utils/sorters";
|
||||
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
|
||||
import {pageLimit} from "../../utils/config";
|
||||
|
||||
export default function CsiResponseListPaginated({
|
||||
refetch,
|
||||
@@ -106,7 +107,7 @@ export default function CsiResponseListPaginated({
|
||||
loading={loading}
|
||||
pagination={{
|
||||
position: "top",
|
||||
pageSize: 25,
|
||||
pageSize: pageLimit,
|
||||
current: parseInt(page || 1),
|
||||
total: total,
|
||||
}}
|
||||
|
||||
@@ -6,6 +6,7 @@ import { alphaSort } from "../../../utils/sorters";
|
||||
import LoadingSkeleton from "../../loading-skeleton/loading-skeleton.component";
|
||||
import Dinero from "dinero.js";
|
||||
import DashboardRefreshRequired from "../refresh-required.component";
|
||||
import {pageLimit} from "../../../utils/config";
|
||||
|
||||
export default function DashboardMonthlyJobCosting({ data, ...cardProps }) {
|
||||
const { t } = useTranslation();
|
||||
@@ -118,7 +119,7 @@ export default function DashboardMonthlyJobCosting({ data, ...cardProps }) {
|
||||
<div style={{ height: "100%" }}>
|
||||
<Table
|
||||
onChange={handleTableChange}
|
||||
pagination={{ position: "top", defaultPageSize: 50 }}
|
||||
pagination={{ position: "top", defaultPageSize: pageLimit }}
|
||||
columns={columns}
|
||||
scroll={{ x: true, y: "calc(100% - 4em)" }}
|
||||
rowKey="id"
|
||||
|
||||
@@ -0,0 +1,245 @@
|
||||
import {
|
||||
BranchesOutlined,
|
||||
ExclamationCircleFilled,
|
||||
PauseCircleOutlined,
|
||||
} from "@ant-design/icons";
|
||||
import { Card, Space, Table, Tooltip } from "antd";
|
||||
import moment from "moment";
|
||||
import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Link } from "react-router-dom";
|
||||
import ChatOpenButton from "../../chat-open-button/chat-open-button.component";
|
||||
import OwnerNameDisplay from "../../owner-name-display/owner-name-display.component";
|
||||
import DashboardRefreshRequired from "../refresh-required.component";
|
||||
import {pageLimit} from "../../../utils/config";
|
||||
|
||||
export default function DashboardScheduledInToday({ data, ...cardProps }) {
|
||||
const { t } = useTranslation();
|
||||
const [state, setState] = useState({
|
||||
sortedInfo: {},
|
||||
});
|
||||
if (!data) return null;
|
||||
if (!data.scheduled_in_today)
|
||||
return <DashboardRefreshRequired {...cardProps} />;
|
||||
|
||||
const appt = []; // Flatten Data
|
||||
data.scheduled_in_today.forEach((item) => {
|
||||
if (item.job) {
|
||||
var i = {
|
||||
canceled: item.canceled,
|
||||
id: item.id,
|
||||
alt_transport: item.job.alt_transport,
|
||||
clm_no: item.job.clm_no,
|
||||
jobid: item.job.jobid,
|
||||
ins_co_nm: item.job.ins_co_nm,
|
||||
iouparent: item.job.iouparent,
|
||||
ownerid: item.job.ownerid,
|
||||
ownr_co_nm: item.job.ownr_co_nm,
|
||||
ownr_ea: item.job.ownr_ea,
|
||||
ownr_fn: item.job.ownr_fn,
|
||||
ownr_ln: item.job.ownr_ln,
|
||||
ownr_ph1: item.job.ownr_ph1,
|
||||
ownr_ph2: item.job.ownr_ph2,
|
||||
production_vars: item.job.production_vars,
|
||||
ro_number: item.job.ro_number,
|
||||
suspended: item.job.suspended,
|
||||
v_make_desc: item.job.v_make_desc,
|
||||
v_model_desc: item.job.v_model_desc,
|
||||
v_model_yr: item.job.v_model_yr,
|
||||
v_vin: item.job.v_vin,
|
||||
vehicleid: item.job.vehicleid,
|
||||
note: item.note,
|
||||
start: moment(item.start).format("hh:mm a"),
|
||||
title: item.title,
|
||||
};
|
||||
appt.push(i);
|
||||
}
|
||||
});
|
||||
appt.sort(function (a, b) {
|
||||
return new moment(a.start) - new moment(b.start);
|
||||
});
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: t("jobs.fields.ro_number"),
|
||||
dataIndex: "ro_number",
|
||||
key: "ro_number",
|
||||
render: (text, record) => (
|
||||
<Link
|
||||
to={"/manage/jobs/" + record.jobid}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<Space>
|
||||
{record.ro_number || t("general.labels.na")}
|
||||
{record.production_vars && record.production_vars.alert ? (
|
||||
<ExclamationCircleFilled className="production-alert" />
|
||||
) : null}
|
||||
{record.suspended && (
|
||||
<PauseCircleOutlined style={{ color: "orangered" }} />
|
||||
)}
|
||||
{record.iouparent && (
|
||||
<Tooltip title={t("jobs.labels.iou")}>
|
||||
<BranchesOutlined style={{ color: "orangered" }} />
|
||||
</Tooltip>
|
||||
)}
|
||||
</Space>
|
||||
</Link>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t("jobs.fields.owner"),
|
||||
dataIndex: "owner",
|
||||
key: "owner",
|
||||
ellipsis: true,
|
||||
responsive: ["md"],
|
||||
render: (text, record) => {
|
||||
return record.ownerid ? (
|
||||
<Link
|
||||
to={"/manage/owners/" + record.ownerid}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<OwnerNameDisplay ownerObject={record} />
|
||||
</Link>
|
||||
) : (
|
||||
<span>
|
||||
<OwnerNameDisplay ownerObject={record} />
|
||||
</span>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: t("jobs.fields.ownr_ph1"),
|
||||
dataIndex: "ownr_ph1",
|
||||
key: "ownr_ph1",
|
||||
ellipsis: true,
|
||||
responsive: ["md"],
|
||||
render: (text, record) => (
|
||||
<ChatOpenButton phone={record.ownr_ph1} jobid={record.jobid} />
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t("jobs.fields.ownr_ph2"),
|
||||
dataIndex: "ownr_ph2",
|
||||
key: "ownr_ph2",
|
||||
ellipsis: true,
|
||||
responsive: ["md"],
|
||||
render: (text, record) => (
|
||||
<ChatOpenButton phone={record.ownr_ph2} jobid={record.jobid} />
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t("jobs.fields.ownr_ea"),
|
||||
dataIndex: "ownr_ea",
|
||||
key: "ownr_ea",
|
||||
ellipsis: true,
|
||||
responsive: ["md"],
|
||||
render: (text, record) => (
|
||||
<ChatOpenButton phone={record.ownr_ea} jobid={record.jobid} />
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t("jobs.fields.vehicle"),
|
||||
dataIndex: "vehicle",
|
||||
key: "vehicle",
|
||||
ellipsis: true,
|
||||
render: (text, record) => {
|
||||
return record.vehicleid ? (
|
||||
<Link
|
||||
to={"/manage/vehicles/" + record.vehicleid}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
{`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${
|
||||
record.v_model_desc || ""
|
||||
}`}
|
||||
</Link>
|
||||
) : (
|
||||
<span>{`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${
|
||||
record.v_model_desc || ""
|
||||
}`}</span>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: t("jobs.fields.ins_co_nm"),
|
||||
dataIndex: "ins_co_nm",
|
||||
key: "ins_co_nm",
|
||||
ellipsis: true,
|
||||
responsive: ["md"],
|
||||
},
|
||||
{
|
||||
title: t("appointments.fields.time"),
|
||||
dataIndex: "start",
|
||||
key: "start",
|
||||
ellipsis: true,
|
||||
responsive: ["md"],
|
||||
},
|
||||
{
|
||||
title: t("appointments.fields.alt_transport"),
|
||||
dataIndex: "alt_transport",
|
||||
key: "alt_transport",
|
||||
ellipsis: true,
|
||||
responsive: ["md"],
|
||||
},
|
||||
];
|
||||
|
||||
const handleTableChange = (sorter) => {
|
||||
setState({ ...state, sortedInfo: sorter });
|
||||
};
|
||||
|
||||
return (
|
||||
<Card
|
||||
title={t("dashboard.titles.scheduledintoday", {
|
||||
date: moment().startOf("day").format("MM/DD/YYYY"),
|
||||
})}
|
||||
{...cardProps}
|
||||
>
|
||||
<div style={{ height: "100%" }}>
|
||||
<Table
|
||||
onChange={handleTableChange}
|
||||
pagination={{ position: "top", defaultPageSize: pageLimit }}
|
||||
columns={columns}
|
||||
scroll={{ x: true, y: "calc(100% - 2em)" }}
|
||||
rowKey="id"
|
||||
style={{ height: "85%" }}
|
||||
dataSource={appt}
|
||||
/>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
export const DashboardScheduledInTodayGql = `
|
||||
scheduled_in_today: appointments(where: {start: {_gte: "${moment()
|
||||
.startOf("day")
|
||||
.toISOString()}", _lte: "${moment()
|
||||
.endOf("day")
|
||||
.toISOString()}"}, canceled: {_eq: false}, block: {_neq: true}}) {
|
||||
canceled
|
||||
id
|
||||
job {
|
||||
alt_transport
|
||||
clm_no
|
||||
jobid: id
|
||||
ins_co_nm
|
||||
iouparent
|
||||
ownerid
|
||||
ownr_co_nm
|
||||
ownr_ea
|
||||
ownr_fn
|
||||
ownr_ln
|
||||
ownr_ph1
|
||||
ownr_ph2
|
||||
production_vars
|
||||
ro_number
|
||||
suspended
|
||||
v_make_desc
|
||||
v_model_desc
|
||||
v_model_yr
|
||||
v_vin
|
||||
vehicleid
|
||||
}
|
||||
note
|
||||
start
|
||||
title
|
||||
}
|
||||
`;
|
||||
@@ -0,0 +1,211 @@
|
||||
import {
|
||||
BranchesOutlined,
|
||||
ExclamationCircleFilled,
|
||||
PauseCircleOutlined,
|
||||
} from "@ant-design/icons";
|
||||
import { Card, Space, Table, Tooltip } from "antd";
|
||||
import moment from "moment";
|
||||
import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Link } from "react-router-dom";
|
||||
import ChatOpenButton from "../../chat-open-button/chat-open-button.component";
|
||||
import OwnerNameDisplay from "../../owner-name-display/owner-name-display.component";
|
||||
import DashboardRefreshRequired from "../refresh-required.component";
|
||||
import {pageLimit} from "../../../utils/config";
|
||||
|
||||
export default function DashboardScheduledOutToday({ data, ...cardProps }) {
|
||||
const { t } = useTranslation();
|
||||
const [state, setState] = useState({
|
||||
sortedInfo: {},
|
||||
});
|
||||
if (!data) return null;
|
||||
if (!data.scheduled_out_today)
|
||||
return <DashboardRefreshRequired {...cardProps} />;
|
||||
|
||||
data.scheduled_out_today.forEach((item) => {
|
||||
item.scheduled_completion= moment(item.scheduled_completion).format("hh:mm a")
|
||||
});
|
||||
data.scheduled_out_today.sort(function (a, b) {
|
||||
return new Date(a.scheduled_completion) - new Date(b.scheduled_completion);
|
||||
});
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: t("jobs.fields.ro_number"),
|
||||
dataIndex: "ro_number",
|
||||
key: "ro_number",
|
||||
render: (text, record) => (
|
||||
<Link
|
||||
to={"/manage/jobs/" + record.jobid}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<Space>
|
||||
{record.ro_number || t("general.labels.na")}
|
||||
{record.production_vars && record.production_vars.alert ? (
|
||||
<ExclamationCircleFilled className="production-alert" />
|
||||
) : null}
|
||||
{record.suspended && (
|
||||
<PauseCircleOutlined style={{ color: "orangered" }} />
|
||||
)}
|
||||
{record.iouparent && (
|
||||
<Tooltip title={t("jobs.labels.iou")}>
|
||||
<BranchesOutlined style={{ color: "orangered" }} />
|
||||
</Tooltip>
|
||||
)}
|
||||
</Space>
|
||||
</Link>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t("jobs.fields.owner"),
|
||||
dataIndex: "owner",
|
||||
key: "owner",
|
||||
ellipsis: true,
|
||||
responsive: ["md"],
|
||||
render: (text, record) => {
|
||||
return record.ownerid ? (
|
||||
<Link
|
||||
to={"/manage/owners/" + record.ownerid}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<OwnerNameDisplay ownerObject={record} />
|
||||
</Link>
|
||||
) : (
|
||||
<span>
|
||||
<OwnerNameDisplay ownerObject={record} />
|
||||
</span>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: t("jobs.fields.ownr_ph1"),
|
||||
dataIndex: "ownr_ph1",
|
||||
key: "ownr_ph1",
|
||||
ellipsis: true,
|
||||
responsive: ["md"],
|
||||
render: (text, record) => (
|
||||
<ChatOpenButton phone={record.ownr_ph1} jobid={record.jobid} />
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t("jobs.fields.ownr_ph2"),
|
||||
dataIndex: "ownr_ph2",
|
||||
key: "ownr_ph2",
|
||||
ellipsis: true,
|
||||
responsive: ["md"],
|
||||
render: (text, record) => (
|
||||
<ChatOpenButton phone={record.ownr_ph2} jobid={record.jobid} />
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t("jobs.fields.ownr_ea"),
|
||||
dataIndex: "ownr_ea",
|
||||
key: "ownr_ea",
|
||||
ellipsis: true,
|
||||
responsive: ["md"],
|
||||
render: (text, record) => (
|
||||
<ChatOpenButton phone={record.ownr_ea} jobid={record.jobid} />
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t("jobs.fields.vehicle"),
|
||||
dataIndex: "vehicle",
|
||||
key: "vehicle",
|
||||
ellipsis: true,
|
||||
render: (text, record) => {
|
||||
return record.vehicleid ? (
|
||||
<Link
|
||||
to={"/manage/vehicles/" + record.vehicleid}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
{`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${
|
||||
record.v_model_desc || ""
|
||||
}`}
|
||||
</Link>
|
||||
) : (
|
||||
<span>{`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${
|
||||
record.v_model_desc || ""
|
||||
}`}</span>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: t("jobs.fields.ins_co_nm"),
|
||||
dataIndex: "ins_co_nm",
|
||||
key: "ins_co_nm",
|
||||
ellipsis: true,
|
||||
responsive: ["md"],
|
||||
},
|
||||
{
|
||||
title: t("jobs.fields.scheduled_completion"),
|
||||
dataIndex: "scheduled_completion",
|
||||
key: "scheduled_completion",
|
||||
ellipsis: true,
|
||||
responsive: ["md"],
|
||||
},
|
||||
{
|
||||
title: t("appointments.fields.alt_transport"),
|
||||
dataIndex: "alt_transport",
|
||||
key: "alt_transport",
|
||||
ellipsis: true,
|
||||
responsive: ["md"],
|
||||
},
|
||||
];
|
||||
|
||||
const handleTableChange = (sorter) => {
|
||||
setState({ ...state, sortedInfo: sorter });
|
||||
};
|
||||
|
||||
return (
|
||||
<Card
|
||||
title={t("dashboard.titles.scheduledouttoday", {
|
||||
date: moment().startOf("day").format("MM/DD/YYYY"),
|
||||
})}
|
||||
{...cardProps}
|
||||
>
|
||||
<div style={{ height: "100%" }}>
|
||||
<Table
|
||||
onChange={handleTableChange}
|
||||
pagination={{ position: "top", defaultPageSize: pageLimit }}
|
||||
columns={columns}
|
||||
scroll={{ x: true, y: "calc(100% - 2em)" }}
|
||||
rowKey="id"
|
||||
style={{ height: "85%" }}
|
||||
dataSource={data.scheduled_out_today}
|
||||
/>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
export const DashboardScheduledOutTodayGql = `
|
||||
scheduled_out_today: jobs(where: {
|
||||
date_invoiced: {_is_null: true},
|
||||
ro_number: {_is_null: false},
|
||||
voided: {_eq: false},
|
||||
scheduled_completion: {_gte: "${moment().startOf("day").toISOString()}",
|
||||
_lte: "${moment().endOf("day").toISOString()}"}}) {
|
||||
alt_transport
|
||||
clm_no
|
||||
jobid: id
|
||||
ins_co_nm
|
||||
iouparent
|
||||
ownerid
|
||||
ownr_co_nm
|
||||
ownr_ea
|
||||
ownr_fn
|
||||
ownr_ln
|
||||
ownr_ph1
|
||||
ownr_ph2
|
||||
production_vars
|
||||
ro_number
|
||||
scheduled_completion
|
||||
suspended
|
||||
v_make_desc
|
||||
v_model_desc
|
||||
v_model_yr
|
||||
v_vin
|
||||
vehicleid
|
||||
|
||||
}
|
||||
`;
|
||||
@@ -1,6 +1,6 @@
|
||||
import Icon, { SyncOutlined } from "@ant-design/icons";
|
||||
import { gql, useMutation, useQuery } from "@apollo/client";
|
||||
import { Button, Dropdown, Menu, notification, PageHeader, Space } from "antd";
|
||||
import { Button, Dropdown, Menu, PageHeader, Space, notification } from "antd";
|
||||
import i18next from "i18next";
|
||||
import _ from "lodash";
|
||||
import moment from "moment";
|
||||
@@ -37,6 +37,12 @@ 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,
|
||||
} from "../dashboard-components/scheduled-in-today/scheduled-in-today.component";
|
||||
import DashboardScheduledOutToday, {
|
||||
DashboardScheduledOutTodayGql,
|
||||
} from "../dashboard-components/scheduled-out-today/scheduled-out-today.component";
|
||||
import "./dashboard-grid.styles.scss";
|
||||
import { GenerateDashboardData } from "./dashboard-grid.utils";
|
||||
|
||||
@@ -268,6 +274,28 @@ const componentList = {
|
||||
w: 2,
|
||||
h: 2,
|
||||
},
|
||||
ScheduleInToday: {
|
||||
label: i18next.t("dashboard.titles.scheduledintoday", {
|
||||
date: moment().startOf("day").format("MM/DD/YYYY"),
|
||||
}),
|
||||
component: DashboardScheduledInToday,
|
||||
gqlFragment: DashboardScheduledInTodayGql,
|
||||
minW: 10,
|
||||
minH: 2,
|
||||
w: 10,
|
||||
h: 2,
|
||||
},
|
||||
ScheduleOutToday: {
|
||||
label: i18next.t("dashboard.titles.scheduledouttoday", {
|
||||
date: moment().startOf("day").format("MM/DD/YYYY"),
|
||||
}),
|
||||
component: DashboardScheduledOutToday,
|
||||
gqlFragment: DashboardScheduledOutTodayGql,
|
||||
minW: 10,
|
||||
minH: 2,
|
||||
w: 10,
|
||||
h: 2,
|
||||
},
|
||||
};
|
||||
|
||||
const createDashboardQuery = (state) => {
|
||||
@@ -283,8 +311,12 @@ const createDashboardQuery = (state) => {
|
||||
monthly_sales: jobs(where: {_and: [
|
||||
{ voided: {_eq: false}},
|
||||
{date_invoiced: {_gte: "${moment()
|
||||
.startOf("month").startOf('day').toISOString()}"}}, {date_invoiced: {_lte: "${moment()
|
||||
.endOf("month").endOf('day').toISOString()}"}}]}) {
|
||||
.startOf("month")
|
||||
.startOf("day")
|
||||
.toISOString()}"}}, {date_invoiced: {_lte: "${moment()
|
||||
.endOf("month")
|
||||
.endOf("day")
|
||||
.toISOString()}"}}]}) {
|
||||
id
|
||||
ro_number
|
||||
date_invoiced
|
||||
|
||||
@@ -18,7 +18,7 @@ export default function DataLabel({
|
||||
<div {...props} style={{ display: "flex" }}>
|
||||
<div
|
||||
style={{
|
||||
flex: 2,
|
||||
// flex: 2,
|
||||
marginRight: ".2rem",
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -5,6 +5,7 @@ import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import {pageLimit} from "../../utils/config";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
//currentUser: selectCurrentUser
|
||||
@@ -117,7 +118,7 @@ export function DmsAllocationsSummaryAp({ socket, bodyshop, billids, title }) {
|
||||
}
|
||||
>
|
||||
<Table
|
||||
pagination={{ position: "top", defaultPageSize: 50 }}
|
||||
pagination={{ position: "top", defaultPageSize: pageLimit }}
|
||||
columns={columns}
|
||||
rowKey={(record) => `${record.InvoiceNumber}${record.Account}`}
|
||||
dataSource={allocationsSummary}
|
||||
|
||||
@@ -6,6 +6,7 @@ import { createStructuredSelector } from "reselect";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import Dinero from "dinero.js";
|
||||
import { SyncOutlined } from "@ant-design/icons";
|
||||
import {pageLimit} from "../../utils/config";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
//currentUser: selectCurrentUser
|
||||
@@ -94,7 +95,7 @@ export function DmsAllocationsSummary({ socket, bodyshop, jobId, title }) {
|
||||
<Alert type="warning" message={t("jobs.labels.dms.disablebillwip")} />
|
||||
)}
|
||||
<Table
|
||||
pagination={{ position: "top", defaultPageSize: 50 }}
|
||||
pagination={{ position: "top", defaultPageSize: pageLimit }}
|
||||
columns={columns}
|
||||
rowKey="center"
|
||||
dataSource={allocationsSummary}
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
Select,
|
||||
Space,
|
||||
Statistic,
|
||||
Switch,
|
||||
Typography,
|
||||
} from "antd";
|
||||
import Dinero from "dinero.js";
|
||||
@@ -183,6 +184,20 @@ export function DmsPostForm({ bodyshop, socket, job, logsRef }) {
|
||||
<Space>
|
||||
<DmsCdkMakes form={form} socket={socket} job={job} />
|
||||
<DmsCdkMakesRefetch />
|
||||
<Form.Item
|
||||
name="dms_unsold"
|
||||
label={t("jobs.fields.dms.dms_unsold")}
|
||||
initialValue={false}
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="dms_model_override"
|
||||
label={t("jobs.fields.dms.dms_model_override")}
|
||||
initialValue={false}
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
</Space>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -109,7 +109,7 @@ export function EmailOverlayContainer({
|
||||
]
|
||||
: []),
|
||||
],
|
||||
media: selectedMedia.filter((m) => m.isSelected).map((m) => m.src),
|
||||
media: selectedMedia.filter((m) => m.isSelected).map((m) => m.fullsize),
|
||||
//attachments,
|
||||
});
|
||||
notification["success"]({ message: t("emails.successes.sent") });
|
||||
|
||||
@@ -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**
|
||||
|
||||
// ----
|
||||
|
||||
@@ -65,8 +65,17 @@ export function FormDatePicker({
|
||||
});
|
||||
}
|
||||
|
||||
if (_a.isValid() && onChange)
|
||||
onChange(isDateOnly ? _a.format("YYYY-MM-DD") : _a);
|
||||
if (_a.isValid() && onChange) {
|
||||
if (onlyFuture) {
|
||||
if (moment().subtract(1, "day").isBefore(_a)) {
|
||||
onChange(isDateOnly ? _a.format("YYYY-MM-DD") : _a);
|
||||
} else {
|
||||
onChange(isDateOnly ? moment().format("YYYY-MM-DD") : moment());
|
||||
}
|
||||
} else {
|
||||
onChange(isDateOnly ? _a.format("YYYY-MM-DD") : _a);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import React, { forwardRef } from "react";
|
||||
//import DatePicker from "react-datepicker";
|
||||
//import "react-datepicker/src/stylesheets/datepicker.scss";
|
||||
import FormDatePicker from "../form-date-picker/form-date-picker.component";
|
||||
import { TimePicker } from "antd";
|
||||
import moment from "moment";
|
||||
import FormDatePicker from "../form-date-picker/form-date-picker.component";
|
||||
//To be used as a form element only.
|
||||
|
||||
const DateTimePicker = (
|
||||
@@ -26,20 +26,21 @@ const DateTimePicker = (
|
||||
value={value}
|
||||
onBlur={onBlur}
|
||||
onChange={onChange}
|
||||
onlyFuture={onlyFuture}
|
||||
isDateOnly={false}
|
||||
/>
|
||||
|
||||
<TimePicker
|
||||
value={value ? moment(value) : null}
|
||||
{...(onlyFuture && {
|
||||
disabledDate: (d) => moment().isAfter(d),
|
||||
})}
|
||||
onChange={onChange}
|
||||
showSecond={false}
|
||||
minuteStep={15}
|
||||
onBlur={onBlur}
|
||||
format="hh:mm a"
|
||||
{...restProps}
|
||||
value={value ? moment(value) : null}
|
||||
{...(onlyFuture && {
|
||||
disabledDate: (d) => moment().isAfter(d),
|
||||
})}
|
||||
onChange={onChange}
|
||||
showSecond={false}
|
||||
minuteStep={15}
|
||||
onBlur={onBlur}
|
||||
format="hh:mm a"
|
||||
{...restProps}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,9 +1,26 @@
|
||||
import Dinero from "dinero.js";
|
||||
import React, { forwardRef } from "react";
|
||||
|
||||
const ReadOnlyFormItem = ({ value, type = "text", onChange }, ref) => {
|
||||
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
|
||||
) => {
|
||||
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":
|
||||
@@ -14,4 +31,8 @@ const ReadOnlyFormItem = ({ value, type = "text", onChange }, ref) => {
|
||||
return <div>{value}</div>;
|
||||
}
|
||||
};
|
||||
export default forwardRef(ReadOnlyFormItem);
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(forwardRef(ReadOnlyFormItem));
|
||||
|
||||
@@ -54,6 +54,7 @@ export default function GlobalSearchOs() {
|
||||
job.v_make_desc || ""
|
||||
} ${job.v_model_desc || ""}`}</span>
|
||||
<span>{`${job.clm_no || ""}`}</span>
|
||||
<span>{`${job.plate_no || ""}`}</span>
|
||||
</Space>
|
||||
</Link>
|
||||
),
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import { useTreatments } from "@splitsoftware/splitio-react";
|
||||
import Icon, {
|
||||
BankFilled,
|
||||
BarChartOutlined,
|
||||
CarFilled,
|
||||
ClockCircleFilled,
|
||||
CheckCircleOutlined,
|
||||
ClockCircleFilled,
|
||||
DashboardFilled,
|
||||
DollarCircleFilled,
|
||||
ExportOutlined,
|
||||
@@ -26,6 +25,7 @@ import Icon, {
|
||||
UnorderedListOutlined,
|
||||
UserOutlined,
|
||||
} from "@ant-design/icons";
|
||||
import { useTreatments } from "@splitsoftware/splitio-react";
|
||||
import { Layout, Menu } from "antd";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
@@ -97,6 +97,11 @@ function Header({
|
||||
{},
|
||||
bodyshop && bodyshop.imexshopid
|
||||
);
|
||||
const { ImEXPay } = useTreatments(
|
||||
["ImEXPay"],
|
||||
{},
|
||||
bodyshop && bodyshop.imexshopid
|
||||
);
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -243,19 +248,20 @@ function Header({
|
||||
>
|
||||
{t("menus.header.enterpayment")}
|
||||
</Menu.Item>
|
||||
{/* TODO: Enter Card Payment */}
|
||||
<Menu.Item
|
||||
key="entercardpayments"
|
||||
onClick={() => {
|
||||
setCardPaymentContext({
|
||||
actions: {},
|
||||
context: null,
|
||||
});
|
||||
}}
|
||||
icon={<Icon component={FaCreditCard} />}
|
||||
>
|
||||
{t("menus.header.entercardpayment")}
|
||||
</Menu.Item>
|
||||
{ImEXPay.treatment === "on" && (
|
||||
<Menu.Item
|
||||
key="entercardpayments"
|
||||
onClick={() => {
|
||||
setCardPaymentContext({
|
||||
actions: {},
|
||||
context: {},
|
||||
});
|
||||
}}
|
||||
icon={<Icon component={FaCreditCard} />}
|
||||
>
|
||||
{t("menus.header.entercardpayment")}
|
||||
</Menu.Item>
|
||||
)}
|
||||
<Menu.Divider key="div5" />
|
||||
<Menu.Item key="timetickets" icon={<FieldTimeOutlined />}>
|
||||
<Link to="/manage/timetickets">
|
||||
@@ -275,7 +281,11 @@ function Header({
|
||||
onClick={() => {
|
||||
setTimeTicketContext({
|
||||
actions: {},
|
||||
context: {},
|
||||
context: {
|
||||
created_by: currentUser.displayName
|
||||
? currentUser.email.concat(" | ", currentUser.displayName)
|
||||
: currentUser.email,
|
||||
},
|
||||
});
|
||||
}}
|
||||
>
|
||||
@@ -379,20 +389,12 @@ function Header({
|
||||
<Menu.Item
|
||||
key="help"
|
||||
onClick={() => {
|
||||
window.open("https://help.imex.online/", "_blank");
|
||||
window.open("https://rometech.com/", "_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>
|
||||
|
||||
@@ -11,6 +11,7 @@ import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||
import InventoryBillRo from "../inventory-bill-ro/inventory-bill-ro.component";
|
||||
import InventoryLineDelete from "../inventory-line-delete/inventory-line-delete.component";
|
||||
import {pageLimit} from "../../utils/config";
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
//currentUser: selectCurrentUser
|
||||
bodyshop: selectBodyshop,
|
||||
@@ -213,7 +214,7 @@ export function JobsList({
|
||||
loading={loading}
|
||||
pagination={{
|
||||
position: "top",
|
||||
pageSize: 25,
|
||||
pageSize: pageLimit,
|
||||
current: parseInt(page || 1),
|
||||
total: total,
|
||||
}}
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
} from "../../redux/application/application.actions";
|
||||
import AlertComponent from "../alert/alert.component";
|
||||
import InventoryListPaginated from "./inventory-list.component";
|
||||
import {pageLimit} from "../../utils/config";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
//bodyshop: selectBodyshop,
|
||||
@@ -32,8 +33,8 @@ export function InventoryList({ setBreadcrumbs, setSelectedHeader }) {
|
||||
nextFetchPolicy: "network-only",
|
||||
variables: {
|
||||
search: search || "",
|
||||
offset: page ? (page - 1) * 25 : 0,
|
||||
limit: 25,
|
||||
offset: page ? (page - 1) * pageLimit : 0,
|
||||
limit: pageLimit,
|
||||
consumedIsNull: showall === "true" ? null : true,
|
||||
order: [
|
||||
{
|
||||
|
||||
@@ -5,6 +5,7 @@ import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { SEARCH_VENDOR_AUTOCOMPLETE_WITH_ADDR } from "../../graphql/vendors.queries";
|
||||
import { selectTechnician } from "../../redux/tech/tech.selectors";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import { GenerateDocument } from "../../utils/RenderTemplate";
|
||||
import { TemplateList } from "../../utils/TemplateConstants";
|
||||
@@ -13,13 +14,14 @@ import VendorSearchSelect from "../vendor-search-select/vendor-search-select.com
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
technician: selectTechnician,
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||
});
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(Jobd3RdPartyModal);
|
||||
|
||||
export function Jobd3RdPartyModal({ bodyshop, jobId, job }) {
|
||||
export function Jobd3RdPartyModal({ bodyshop, jobId, job, technician }) {
|
||||
const [isModalVisible, setIsModalVisible] = useState(false);
|
||||
const { t } = useTranslation();
|
||||
const [form] = Form.useForm();
|
||||
@@ -212,7 +214,9 @@ export function Jobd3RdPartyModal({ bodyshop, jobId, job }) {
|
||||
]}
|
||||
>
|
||||
<Radio.Group>
|
||||
<Radio value={"e"}>{t("parts_orders.labels.email")}</Radio>
|
||||
{!technician ? (
|
||||
<Radio value={"e"}>{t("parts_orders.labels.email")}</Radio>
|
||||
) : null}
|
||||
<Radio value={"p"}>{t("parts_orders.labels.print")}</Radio>
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
Divider,
|
||||
Dropdown,
|
||||
Form,
|
||||
Input,
|
||||
Menu,
|
||||
notification,
|
||||
Popover,
|
||||
@@ -29,11 +30,13 @@ import { GenerateDocument } from "../../utils/RenderTemplate";
|
||||
import { TemplateList } from "../../utils/TemplateConstants";
|
||||
import ChatOpenButton from "../chat-open-button/chat-open-button.component";
|
||||
import DataLabel from "../data-label/data-label.component";
|
||||
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
|
||||
import ScheduleManualEvent from "../schedule-manual-event/schedule-manual-event.component";
|
||||
import ScheduleAtChange from "./job-at-change.component";
|
||||
import ScheduleEventColor from "./schedule-event.color.component";
|
||||
import ScheduleEventNote from "./schedule-event.note.component";
|
||||
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
|
||||
import { useMutation } from "@apollo/client";
|
||||
import { UPDATE_APPOINTMENT } from "../../graphql/appointments.queries";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
@@ -58,16 +61,44 @@ export function ScheduleEventComponent({
|
||||
const [visible, setVisible] = useState(false);
|
||||
const history = useHistory();
|
||||
const searchParams = queryString.parse(useLocation().search);
|
||||
|
||||
const [updateAppointment] = useMutation(UPDATE_APPOINTMENT);
|
||||
const [title, setTitle] = useState(event.title);
|
||||
const blockContent = (
|
||||
<div>
|
||||
<Space direction="vertical" wrap>
|
||||
<Input
|
||||
value={title}
|
||||
onChange={(e) => setTitle(e.currentTarget.value)}
|
||||
onBlur={async () => {
|
||||
await updateAppointment({
|
||||
variables: {
|
||||
appid: event.id,
|
||||
app: {
|
||||
title: title,
|
||||
},
|
||||
},
|
||||
optimisticResponse: {
|
||||
update_appointments: {
|
||||
__typename: "appointments_mutation_response",
|
||||
returning: [
|
||||
{
|
||||
...event,
|
||||
title: title,
|
||||
__typename: "appointments",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
|
||||
<Button
|
||||
onClick={() => handleCancel({ id: event.id })}
|
||||
disabled={event.arrived}
|
||||
>
|
||||
{t("appointments.actions.cancel")}
|
||||
{t("appointments.actions.unblock")}
|
||||
</Button>
|
||||
</div>
|
||||
</Space>
|
||||
);
|
||||
|
||||
const popoverContent = (
|
||||
@@ -208,46 +239,56 @@ export function ScheduleEventComponent({
|
||||
<Button>{t("appointments.actions.sendreminder")}</Button>
|
||||
</Dropdown>
|
||||
) : null}
|
||||
<Popover
|
||||
trigger="click"
|
||||
disabled={event.arrived}
|
||||
content={
|
||||
<Form
|
||||
layout="vertical"
|
||||
onFinish={({ lost_sale_reason }) => {
|
||||
handleCancel({ id: event.id, lost_sale_reason });
|
||||
}}
|
||||
>
|
||||
<Form.Item
|
||||
name="lost_sale_reason"
|
||||
label={t("jobs.fields.lost_sale_reason")}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
//message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Select
|
||||
options={bodyshop.md_lost_sale_reasons.map((lsr) => ({
|
||||
label: lsr,
|
||||
value: lsr,
|
||||
}))}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Button htmlType="submit">
|
||||
{t("appointments.actions.cancel")}
|
||||
</Button>
|
||||
</Form>
|
||||
}
|
||||
>
|
||||
{event.arrived ? (
|
||||
<Button
|
||||
// onClick={() => handleCancel(event.id)}
|
||||
disabled={event.arrived}
|
||||
>
|
||||
{t("appointments.actions.cancel")}
|
||||
</Button>
|
||||
</Popover>
|
||||
) : (
|
||||
<Popover
|
||||
trigger="click"
|
||||
disabled={event.arrived}
|
||||
content={
|
||||
<Form
|
||||
layout="vertical"
|
||||
onFinish={({ lost_sale_reason }) => {
|
||||
handleCancel({ id: event.id, lost_sale_reason });
|
||||
}}
|
||||
>
|
||||
<Form.Item
|
||||
name="lost_sale_reason"
|
||||
label={t("jobs.fields.lost_sale_reason")}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
//message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Select
|
||||
options={bodyshop.md_lost_sale_reasons.map((lsr) => ({
|
||||
label: lsr,
|
||||
value: lsr,
|
||||
}))}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Button htmlType="submit">
|
||||
{t("appointments.actions.cancel")}
|
||||
</Button>
|
||||
</Form>
|
||||
}
|
||||
>
|
||||
<Button
|
||||
// onClick={() => handleCancel(event.id)}
|
||||
disabled={event.arrived}
|
||||
>
|
||||
{t("appointments.actions.cancel")}
|
||||
</Button>
|
||||
</Popover>
|
||||
)}
|
||||
|
||||
{event.isintake ? (
|
||||
<Button
|
||||
disabled={event.arrived}
|
||||
|
||||
@@ -2,12 +2,16 @@ import { useMutation } from "@apollo/client";
|
||||
import { notification } from "antd";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||
import { CANCEL_APPOINTMENT_BY_ID } from "../../graphql/appointments.queries";
|
||||
import { UPDATE_JOB } from "../../graphql/jobs.queries";
|
||||
import { insertAuditTrail } from "../../redux/application/application.actions";
|
||||
import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
||||
import ScheduleEventComponent from "./schedule-event.component";
|
||||
|
||||
export default function ScheduleEventContainer({ bodyshop, event, refetch }) {
|
||||
const dispatch = useDispatch();
|
||||
const { t } = useTranslation();
|
||||
const [cancelAppointment] = useMutation(CANCEL_APPOINTMENT_BY_ID);
|
||||
const [updateJob] = useMutation(UPDATE_JOB);
|
||||
@@ -34,16 +38,24 @@ export default function ScheduleEventContainer({ bodyshop, event, refetch }) {
|
||||
const jobUpdate = await updateJob({
|
||||
variables: {
|
||||
jobId: event.job.id,
|
||||
|
||||
job: {
|
||||
date_scheduled: null,
|
||||
scheduled_in: null,
|
||||
scheduled_completion: null,
|
||||
lost_sale_reason,
|
||||
date_lost_sale: new Date(),
|
||||
status: bodyshop.md_ro_statuses.default_imported,
|
||||
},
|
||||
},
|
||||
});
|
||||
if (!jobUpdate.errors) {
|
||||
dispatch(
|
||||
insertAuditTrail({
|
||||
jobid: event.job.id,
|
||||
operation: AuditTrailMapping.appointmentcancel(lost_sale_reason),
|
||||
})
|
||||
);
|
||||
}
|
||||
if (!!jobUpdate.errors) {
|
||||
notification["error"]({
|
||||
message: t("jobs.errors.updating", {
|
||||
|
||||
@@ -87,7 +87,8 @@ 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.towing))
|
||||
.add(Dinero(totals.additional.additionalCosts));
|
||||
|
||||
const discrepancy = totalPartsSublet.subtract(billTotals);
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useMutation } from "@apollo/client";
|
||||
import { Button, Card, Form, Input, notification, Switch } from "antd";
|
||||
import moment from "moment-business-days";
|
||||
import queryString from "query-string";
|
||||
import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
@@ -12,16 +13,15 @@ import {
|
||||
MARK_LATEST_APPOINTMENT_ARRIVED,
|
||||
} from "../../../../graphql/appointments.queries";
|
||||
import { UPDATE_JOB } from "../../../../graphql/jobs.queries";
|
||||
import { UPDATE_OWNER } from "../../../../graphql/owners.queries";
|
||||
import { insertAuditTrail } from "../../../../redux/application/application.actions";
|
||||
import {
|
||||
selectBodyshop,
|
||||
selectCurrentUser,
|
||||
} from "../../../../redux/user/user.selectors";
|
||||
import AuditTrailMapping from "../../../../utils/AuditTrailMappings";
|
||||
import ConfigFormComponents from "../../../config-form-components/config-form-components.component";
|
||||
import DateTimePicker from "../../../form-date-time-picker/form-date-time-picker.component";
|
||||
import moment from "moment-business-days";
|
||||
import { insertAuditTrail } from "../../../../redux/application/application.actions";
|
||||
import AuditTrailMapping from "../../../../utils/AuditTrailMappings";
|
||||
import { UPDATE_OWNER } from "../../../../graphql/owners.queries";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
@@ -230,6 +230,7 @@ export function JobChecklistForm({
|
||||
)),
|
||||
scheduled_delivery:
|
||||
job.scheduled_delivery && moment(job.scheduled_delivery),
|
||||
production_vars: job.production_vars,
|
||||
}),
|
||||
...(type === "deliver" && {
|
||||
removeFromProduction: true,
|
||||
|
||||
@@ -3,6 +3,7 @@ import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { alphaSort } from "../../utils/sorters";
|
||||
import Dinero from "dinero.js";
|
||||
import {pageLimit} from "../../utils/config";
|
||||
export default function JobCostingPartsTable({ data, summaryData }) {
|
||||
const [searchText, setSearchText] = useState("");
|
||||
const [state, setState] = useState({
|
||||
@@ -98,7 +99,7 @@ export default function JobCostingPartsTable({ data, summaryData }) {
|
||||
x: "50%", //y: "40rem"
|
||||
}}
|
||||
onChange={handleTableChange}
|
||||
pagination={{ position: "top", defaultPageSize: 50 }}
|
||||
pagination={{ position: "top", defaultPageSize: pageLimit }}
|
||||
columns={columns}
|
||||
rowKey="id"
|
||||
dataSource={filteredData}
|
||||
|
||||
@@ -8,7 +8,18 @@ import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||
import { DateFormatter } from "../../utils/DateFormatter";
|
||||
import AlertComponent from "../alert/alert.component";
|
||||
|
||||
export default function JobLinesExpander({ jobline, jobid }) {
|
||||
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)(JobLinesExpander);
|
||||
|
||||
export function JobLinesExpander({ jobline, jobid, bodyshop }) {
|
||||
const { t } = useTranslation();
|
||||
const { loading, error, data } = useQuery(GET_JOB_LINE_ORDERS, {
|
||||
fetchPolicy: "network-only",
|
||||
@@ -23,7 +34,7 @@ export default function JobLinesExpander({ jobline, jobid }) {
|
||||
|
||||
return (
|
||||
<Row>
|
||||
<Col md={24} lg={12}>
|
||||
<Col md={24} lg={8}>
|
||||
<Typography.Title level={4}>
|
||||
{t("parts_orders.labels.parts_orders")}
|
||||
</Typography.Title>
|
||||
@@ -49,7 +60,7 @@ export default function JobLinesExpander({ jobline, jobid }) {
|
||||
)}
|
||||
</Timeline>
|
||||
</Col>
|
||||
<Col md={24} lg={12}>
|
||||
<Col md={24} lg={8}>
|
||||
<Typography.Title level={4}>{t("bills.labels.bills")}</Typography.Title>
|
||||
<Timeline>
|
||||
{data.billlines.length > 0 ? (
|
||||
@@ -71,7 +82,7 @@ export default function JobLinesExpander({ jobline, jobid }) {
|
||||
</Col>
|
||||
<Col span={4}>
|
||||
<span>
|
||||
{`${t("billlines.fields.actual_cost")}: `}
|
||||
{`${t("billlines.fields.actual_cost")}: `}
|
||||
<CurrencyFormatter>{line.actual_cost}</CurrencyFormatter>
|
||||
</span>
|
||||
</Col>
|
||||
@@ -89,6 +100,37 @@ export default function JobLinesExpander({ jobline, jobid }) {
|
||||
)}
|
||||
</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>
|
||||
</Col>
|
||||
</Row>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -45,6 +45,10 @@ import JobSendPartPriceChangeComponent from "../job-send-parts-price-change/job-
|
||||
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,
|
||||
@@ -76,8 +80,16 @@ export function JobLinesComponent({
|
||||
setBillEnterContext,
|
||||
}) {
|
||||
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: {},
|
||||
@@ -121,10 +133,21 @@ export function JobLinesComponent({
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "oem_partno" && state.sortedInfo.order,
|
||||
ellipsis: true,
|
||||
render: (text, record) =>
|
||||
`${record.oem_partno || ""} ${
|
||||
record.alt_partno ? `(${record.alt_partno})` : ""
|
||||
}`.trim(),
|
||||
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>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t("joblines.fields.op_code_desc"),
|
||||
@@ -273,6 +296,23 @@ 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",
|
||||
@@ -388,10 +428,14 @@ export function JobLinesComponent({
|
||||
const markedTypes = [e.key];
|
||||
if (e.key === "PAN") markedTypes.push("PAP");
|
||||
if (e.key === "PAS") markedTypes.push("PASL");
|
||||
setSelectedLines(
|
||||
setSelectedLines((selectedLines) =>
|
||||
_.uniq([
|
||||
...selectedLines,
|
||||
...jobLines.filter((item) => markedTypes.includes(item.part_type)),
|
||||
...jobLines.filter(
|
||||
(item) =>
|
||||
markedTypes.includes(item.part_type) ||
|
||||
markedTypes.includes(item.mod_lbr_ty)
|
||||
),
|
||||
])
|
||||
);
|
||||
}
|
||||
@@ -404,6 +448,21 @@ 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>
|
||||
);
|
||||
@@ -427,6 +486,18 @@ 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) ||
|
||||
@@ -595,8 +666,17 @@ export function JobLinesComponent({
|
||||
onSelectAll: (selected, selectedRows, changeRows) => {
|
||||
setSelectedLines(selectedRows);
|
||||
},
|
||||
onSelect: (record, selected, selectedRows, nativeEvent) =>
|
||||
setSelectedLines(selectedRows),
|
||||
onSelect: (record, selected, selectedRows, nativeEvent) => {
|
||||
if (selected) {
|
||||
setSelectedLines((selectedLines) =>
|
||||
_.uniqBy([...selectedLines, record], "id")
|
||||
);
|
||||
} else {
|
||||
setSelectedLines((selectedLines) =>
|
||||
selectedLines.filter((l) => l.id !== record.id)
|
||||
);
|
||||
}
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
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,6 +1,6 @@
|
||||
import { notification, Select } from "antd";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useMutation } from "@apollo/client";
|
||||
import { notification, Select, Space } from "antd";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
@@ -56,8 +56,10 @@ export function JobLineLocationPopup({ bodyshop, jobline, disabled }) {
|
||||
<LoadingSpinner loading={loading}>
|
||||
<Select
|
||||
autoFocus
|
||||
allowClear
|
||||
dropdownMatchSelectWidth={100}
|
||||
value={location}
|
||||
onClear={() => setLocation(null)}
|
||||
onSelect={handleChange}
|
||||
onBlur={handleSave}
|
||||
>
|
||||
@@ -75,7 +77,10 @@ export function JobLineLocationPopup({ bodyshop, jobline, disabled }) {
|
||||
style={{ width: "100%", minHeight: "2rem", cursor: "pointer" }}
|
||||
onClick={() => !disabled && setEditing(true)}
|
||||
>
|
||||
{jobline.location}
|
||||
<Space wrap>
|
||||
{jobline.location}
|
||||
{jobline.parts_dispatch_lines?.length > 0 && "-Disp"}
|
||||
</Space>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
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);
|
||||
@@ -8,7 +8,7 @@ export default function JobLinesBillRefernece({ jobline }) {
|
||||
return (
|
||||
<div style={{ color: subletRequired && "tomato" }}>
|
||||
{subletRequired && <WarningFilled />}
|
||||
{`${(billLine.actual_price * billLine.quantity).toFixed(2)} (${
|
||||
{`${billLine.actual_price.toFixed(2)} x ${billLine.quantity} (${
|
||||
billLine.bill.vendor.name
|
||||
} #${billLine.bill.invoice_number})`}
|
||||
</div>
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
import { Button, Card, Space, Table } from "antd";
|
||||
import { EditFilled } from "@ant-design/icons";
|
||||
import { Button, Card, Space, Table } from "antd";
|
||||
import Dinero from "dinero.js";
|
||||
import React, { useMemo, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { selectJobReadOnly } from "../../redux/application/application.selectors";
|
||||
import {
|
||||
openChatByPhone,
|
||||
setMessage,
|
||||
} from "../../redux/messaging/messaging.actions";
|
||||
import { setModalContext } from "../../redux/modals/modals.actions";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||
import { DateFormatter } from "../../utils/DateFormatter";
|
||||
import { alphaSort, dateSort } from "../../utils/sorters";
|
||||
import { TemplateList } from "../../utils/TemplateConstants";
|
||||
import { alphaSort, dateSort } from "../../utils/sorters";
|
||||
import DataLabel from "../data-label/data-label.component";
|
||||
import PrintWrapperComponent from "../print-wrapper/print-wrapper.component";
|
||||
import PaymentExpandedRowComponent from "../payment-expanded-row/payment-expanded-row.component";
|
||||
import {
|
||||
setMessage,
|
||||
openChatByPhone,
|
||||
} from "../../redux/messaging/messaging.actions";
|
||||
import { parsePhoneNumber } from "libphonenumber-js";
|
||||
import axios from "axios";
|
||||
import PaymentsGenerateLink from "../payments-generate-link/payments-generate-link.component";
|
||||
import PrintWrapperComponent from "../print-wrapper/print-wrapper.component";
|
||||
import { useTreatments } from "@splitsoftware/splitio-react";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
@@ -46,12 +46,17 @@ export function JobPayments({
|
||||
setCardPaymentContext,
|
||||
refetch,
|
||||
}) {
|
||||
const { ImEXPay } = useTreatments(
|
||||
["ImEXPay"],
|
||||
{},
|
||||
bodyshop && bodyshop.imexshopid
|
||||
);
|
||||
|
||||
const { t } = useTranslation();
|
||||
const [state, setState] = useState({
|
||||
sortedInfo: {},
|
||||
filteredInfo: {},
|
||||
});
|
||||
const [generatingURL, setGeneratingtURL] = useState(false);
|
||||
|
||||
const columns = [
|
||||
{
|
||||
@@ -165,39 +170,21 @@ export function JobPayments({
|
||||
title={t("payments.labels.title")}
|
||||
extra={
|
||||
<Space wrap>
|
||||
<Button
|
||||
disabled={!job.converted}
|
||||
loading={generatingURL}
|
||||
onClick={async () => {
|
||||
const p = parsePhoneNumber(job.ownr_ph1, "CA");
|
||||
setGeneratingtURL(true);
|
||||
const response = await axios.post(
|
||||
"/intellipay/generate_payment_url",
|
||||
{
|
||||
bodyshop,
|
||||
amount: balance.getAmount(),
|
||||
account: job.ro_number,
|
||||
{ImEXPay.treatment === "on" && (
|
||||
<>
|
||||
<Button
|
||||
onClick={() =>
|
||||
setCardPaymentContext({
|
||||
actions: { refetch },
|
||||
context: { jobid: job.id, balance },
|
||||
})
|
||||
}
|
||||
);
|
||||
setGeneratingtURL(false);
|
||||
|
||||
console.log("SMS", response);
|
||||
|
||||
openChatByPhone({
|
||||
phone_num: p.formatInternational(),
|
||||
jobid: job.id,
|
||||
});
|
||||
setMessage(
|
||||
t("appointments.labels.smspaymentreminder", {
|
||||
shopname: bodyshop.shopname,
|
||||
amount: balance.toFormat(),
|
||||
payment_link: response.data.shorUrl,
|
||||
})
|
||||
);
|
||||
}}
|
||||
>
|
||||
{t("menus.header.paymentremindersms")}
|
||||
</Button>
|
||||
>
|
||||
{t("menus.header.entercardpayment")}
|
||||
</Button>
|
||||
<PaymentsGenerateLink job={job} />
|
||||
</>
|
||||
)}
|
||||
<Button
|
||||
disabled={!job.converted}
|
||||
onClick={() =>
|
||||
@@ -209,16 +196,7 @@ export function JobPayments({
|
||||
>
|
||||
{t("menus.header.enterpayment")}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() =>
|
||||
setCardPaymentContext({
|
||||
actions: { refetch },
|
||||
context: { jobid: job.id, balance },
|
||||
})
|
||||
}
|
||||
>
|
||||
{t("menus.header.entercardpayment")}
|
||||
</Button>
|
||||
|
||||
<DataLabel
|
||||
valueStyle={{ color: balance.getAmount() !== 0 ? "red" : "green" }}
|
||||
label={t("payments.labels.balance")}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
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;
|
||||
}
|
||||
@@ -19,7 +19,8 @@ export default function JobReconciliationModalComponent({ job, bills }) {
|
||||
|
||||
const jobLineData = job.joblines.filter(
|
||||
(j) =>
|
||||
(j.part_type !== null && j.part_type !== "PAE") ||
|
||||
(j.part_type !== "PAE" && j.act_price !== 0 && j.part_qty !== 0) ||
|
||||
j.misc_amt !== 0 ||
|
||||
(j.line_desc &&
|
||||
j.line_desc.toLowerCase().includes("towing") &&
|
||||
j.lbr_op === "OP13") ||
|
||||
|
||||
@@ -33,7 +33,9 @@ const JobSearchSelect = (
|
||||
useLazyQuery(SEARCH_JOBS_BY_ID_FOR_AUTOCOMPLETE);
|
||||
|
||||
const executeSearch = (v) => {
|
||||
if (v && v !== "") callSearch(v);
|
||||
console.log(v);
|
||||
if (v && v.variables?.search !== "" && v.variables.search.length >= 2)
|
||||
callSearch(v);
|
||||
};
|
||||
const debouncedExecuteSearch = _.debounce(executeSearch, 500);
|
||||
|
||||
|
||||
@@ -123,10 +123,10 @@ 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 !== undefined &&
|
||||
t("jobs.labels.threshhold", {
|
||||
amount: job.materials.mapa.cal_maxdlr,
|
||||
amount: job.materials.MAPA.cal_maxdlr,
|
||||
})}
|
||||
</Space>
|
||||
</Table.Summary.Cell>
|
||||
@@ -147,10 +147,10 @@ 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 !== undefined &&
|
||||
t("jobs.labels.threshhold", {
|
||||
amount: job.materials.mash.cal_maxdlr,
|
||||
amount: job.materials.MASH.cal_maxdlr,
|
||||
})}
|
||||
</Space>
|
||||
</Table.Summary.Cell>
|
||||
|
||||
@@ -28,26 +28,109 @@ export function JobTotalsTableTotals({ bodyshop, job }) {
|
||||
total: job.job_totals.totals.subtotal,
|
||||
bold: true,
|
||||
},
|
||||
{
|
||||
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"
|
||||
|
||||
...(job.job_totals.totals.us_sales_tax_breakdown
|
||||
? [
|
||||
{
|
||||
key: t("jobs.fields.ca_bc_pvrt"),
|
||||
total: job.job_totals.additional.pvrt,
|
||||
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.labels.federal_tax_amt"),
|
||||
total: job.job_totals.totals.federal_tax,
|
||||
},
|
||||
{
|
||||
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.total_repairs"),
|
||||
total: job.job_totals.totals.total_repairs,
|
||||
@@ -57,10 +140,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,
|
||||
@@ -81,7 +164,7 @@ export function JobTotalsTableTotals({ bodyshop, job }) {
|
||||
bold: true,
|
||||
},
|
||||
];
|
||||
}, [job.job_totals, t, bodyshop.region_config]);
|
||||
}, [job.job_totals, job.cieca_pft, t, bodyshop.md_responsibility_centers]);
|
||||
|
||||
const columns = [
|
||||
{
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
import FormFieldsChanged from "../form-fields-changed-alert/form-fields-changed-alert.component";
|
||||
import { useMutation } from "@apollo/client";
|
||||
import { Button, Form, notification } from "antd";
|
||||
import moment from "moment";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { UPDATE_JOB } from "../../graphql/jobs.queries";
|
||||
import DateTimePicker from "../form-date-time-picker/form-date-time-picker.component";
|
||||
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
||||
import moment from "moment";
|
||||
import FormDatePicker from "../form-date-picker/form-date-picker.component";
|
||||
import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
||||
import FormDatePicker from "../form-date-picker/form-date-picker.component";
|
||||
import DateTimePicker from "../form-date-time-picker/form-date-time-picker.component";
|
||||
import FormFieldsChanged from "../form-fields-changed-alert/form-fields-changed-alert.component";
|
||||
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
||||
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { insertAuditTrail } from "../../redux/application/application.actions";
|
||||
import { DateTimeFormat } from "./../../utils/DateFormatter";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
//currentUser: selectCurrentUser
|
||||
@@ -38,8 +39,8 @@ export function JobsAdminDatesChange({ insertAuditTrail, job }) {
|
||||
setLoading(true);
|
||||
const result = await updateJob({
|
||||
variables: { jobId: job.id, job: values },
|
||||
refetchQueries: ['GET_JOB_BY_PK'],
|
||||
awaitRefetchQueries:true
|
||||
refetchQueries: ["GET_JOB_BY_PK"],
|
||||
awaitRefetchQueries: true,
|
||||
});
|
||||
|
||||
const changedAuditFields = form.getFieldsValue(
|
||||
@@ -53,7 +54,7 @@ export function JobsAdminDatesChange({ insertAuditTrail, job }) {
|
||||
operation: AuditTrailMapping.admin_jobfieldchange(
|
||||
key,
|
||||
changedAuditFields[key] instanceof moment
|
||||
? moment(changedAuditFields[key]).format("MM/DD/YYYY hh:mm a")
|
||||
? DateTimeFormat(changedAuditFields[key])
|
||||
: changedAuditFields[key]
|
||||
),
|
||||
});
|
||||
@@ -126,7 +127,10 @@ export function JobsAdminDatesChange({ insertAuditTrail, job }) {
|
||||
<Form.Item label={t("jobs.fields.actual_in")} name="actual_in">
|
||||
<DateTimePicker />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.date_repairstarted")} name="date_repairstarted">
|
||||
<Form.Item
|
||||
label={t("jobs.fields.date_repairstarted")}
|
||||
name="date_repairstarted"
|
||||
>
|
||||
<DateTimePicker />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
@@ -173,6 +177,15 @@ export function JobsAdminDatesChange({ insertAuditTrail, job }) {
|
||||
>
|
||||
<DateTimePicker />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.date_void")} name="date_void">
|
||||
<DateTimePicker />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.date_lost_sale")}
|
||||
name="date_lost_sale"
|
||||
>
|
||||
<DateTimePicker />
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
</Form>
|
||||
|
||||
|
||||
@@ -1,19 +1,18 @@
|
||||
import { useMutation } from "@apollo/client";
|
||||
import { gql, useMutation } from "@apollo/client";
|
||||
import { Button, notification } from "antd";
|
||||
import { gql } from "@apollo/client";
|
||||
import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import moment from "moment";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { INSERT_EXPORT_LOG } from "../../graphql/accounting.queries";
|
||||
import { insertAuditTrail } from "../../redux/application/application.actions";
|
||||
import {
|
||||
selectBodyshop,
|
||||
selectCurrentUser,
|
||||
} from "../../redux/user/user.selectors";
|
||||
import moment from "moment";
|
||||
import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
||||
import { insertAuditTrail } from "../../redux/application/application.actions";
|
||||
import { INSERT_EXPORT_LOG } from "../../graphql/accounting.queries";
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
currentUser: selectCurrentUser,
|
||||
@@ -150,6 +149,10 @@ export function JobAdminMarkReexport({
|
||||
|
||||
if (!result.errors) {
|
||||
notification["success"]({ message: t("jobs.successes.save") });
|
||||
insertAuditTrail({
|
||||
jobid: job.id,
|
||||
operation: AuditTrailMapping.admin_jobuninvoice(),
|
||||
});
|
||||
} else {
|
||||
notification["error"]({
|
||||
message: t("jobs.errors.saving", {
|
||||
|
||||
@@ -33,8 +33,9 @@ export function JobsAdminUnvoid({
|
||||
mutation UNVOID_JOB($jobId: uuid!) {
|
||||
update_jobs_by_pk(pk_columns: {id: $jobId}, _set: {voided: false, status: "${
|
||||
bodyshop.md_ro_statuses.default_imported
|
||||
}"}) {
|
||||
}", date_void: null}) {
|
||||
id
|
||||
date_void
|
||||
voided
|
||||
status
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { GET_ALL_JOBLINES_BY_PK } from "../../graphql/jobs-lines.queries";
|
||||
import { gql } from "@apollo/client";
|
||||
import _ from "lodash";
|
||||
import { GET_ALL_JOBLINES_BY_PK } from "../../graphql/jobs-lines.queries";
|
||||
|
||||
export const GetSupplementDelta = async (client, jobId, newLines) => {
|
||||
const {
|
||||
@@ -50,6 +50,7 @@ export const GetSupplementDelta = async (client, jobId, newLines) => {
|
||||
.reduce((acc, value, idx) => {
|
||||
return acc + generateRemoveQuery(value, idx);
|
||||
}, "");
|
||||
//console.log(insertQueries, updateQueries, removeQueries);
|
||||
|
||||
if ((insertQueries + updateQueries + removeQueries).trim() === "") {
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
@@ -114,7 +114,7 @@ const headerFields = [
|
||||
"ins_ct_ph",
|
||||
"ins_ct_phx",
|
||||
"loss_cat",
|
||||
//ad2
|
||||
//AD2
|
||||
"clmt_ln",
|
||||
"clmt_fn",
|
||||
"clmt_title",
|
||||
@@ -219,7 +219,16 @@ const headerFields = [
|
||||
"loc_title",
|
||||
"loc_ph",
|
||||
"loc_phx",
|
||||
"loc_ea"
|
||||
"loc_ea",
|
||||
//VEH
|
||||
"plate_no",
|
||||
"plate_st",
|
||||
"v_vin",
|
||||
"v_model_yr",
|
||||
"v_make_desc",
|
||||
"v_model_desc",
|
||||
"v_options",
|
||||
"v_color",
|
||||
];
|
||||
|
||||
export default headerFields;
|
||||
|
||||
@@ -6,8 +6,9 @@ import {
|
||||
useQuery,
|
||||
} from "@apollo/client";
|
||||
import { useTreatments } from "@splitsoftware/splitio-react";
|
||||
import { Col, notification, Row } from "antd";
|
||||
import { Button, Col, Row, notification } from "antd";
|
||||
import Axios from "axios";
|
||||
import _ from "lodash";
|
||||
import moment from "moment";
|
||||
import queryString from "query-string";
|
||||
import React, { useCallback, useEffect, useState } from "react";
|
||||
@@ -29,7 +30,6 @@ import {
|
||||
selectBodyshop,
|
||||
selectCurrentUser,
|
||||
} from "../../redux/user/user.selectors";
|
||||
import confirmDialog from "../../utils/asyncConfirm";
|
||||
import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
||||
import CriticalPartsScan from "../../utils/criticalPartsScan";
|
||||
import AlertComponent from "../alert/alert.component";
|
||||
@@ -40,7 +40,6 @@ import OwnerFindModalContainer from "../owner-find-modal/owner-find-modal.contai
|
||||
import { GetSupplementDelta } from "./jobs-available-supplement.estlines.util";
|
||||
import HeaderFields from "./jobs-available-supplement.headerfields";
|
||||
import JobsAvailableTableComponent from "./jobs-available-table.component";
|
||||
import _ from "lodash";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
@@ -73,6 +72,8 @@ export function JobsAvailableContainer({
|
||||
|
||||
const [selectedJob, setSelectedJob] = useState(null);
|
||||
const [selectedOwner, setSelectedOwner] = useState(null);
|
||||
const [partsQueueToggle, setPartsQueueToggle] = useState(bodyshop.md_functionality_toggles.parts_queue_toggle);
|
||||
|
||||
const [insertLoading, setInsertLoading] = useState(false);
|
||||
|
||||
const [insertNote] = useMutation(INSERT_NEW_NOTE);
|
||||
@@ -90,13 +91,15 @@ export function JobsAvailableContainer({
|
||||
const modalSearchState = useState("");
|
||||
|
||||
//Import Scenario
|
||||
const onOwnerFindModalOk = async () => {
|
||||
const onOwnerFindModalOk = async (lazyData) => {
|
||||
logImEXEvent("job_import_new");
|
||||
|
||||
setOwnerModalVisible(false);
|
||||
setInsertLoading(true);
|
||||
|
||||
const estData = replaceEmpty(estDataRaw.data.available_jobs_by_pk);
|
||||
setInsertLoading(true);
|
||||
const estData = replaceEmpty(
|
||||
lazyData?.available_jobs_by_pk || estDataRaw.data.available_jobs_by_pk
|
||||
);
|
||||
|
||||
if (!(estData && estData.est_data)) {
|
||||
//We don't have the right data. Error!
|
||||
@@ -124,7 +127,7 @@ export function JobsAvailableContainer({
|
||||
|
||||
let existingVehicles;
|
||||
if (estData.est_data.v_vin) {
|
||||
//There's vehicle data, need to double check the VIN.
|
||||
//There's vehicle data, need to double-check the VIN.
|
||||
existingVehicles = await client.query({
|
||||
query: SEARCH_VEHICLE_BY_VIN,
|
||||
variables: {
|
||||
@@ -139,6 +142,7 @@ export function JobsAvailableContainer({
|
||||
// owner_owing: Dinero(newTotals.totals.custPayable.total).toFormat("0.00"),
|
||||
// job_totals: newTotals,
|
||||
date_open: moment(),
|
||||
status: bodyshop.md_ro_statuses.default_imported,
|
||||
notes: {
|
||||
data: {
|
||||
created_by: currentUser.email,
|
||||
@@ -146,7 +150,7 @@ export function JobsAvailableContainer({
|
||||
text: t("jobs.labels.importnote"),
|
||||
},
|
||||
},
|
||||
queued_for_parts: true,
|
||||
queued_for_parts: partsQueueToggle,
|
||||
...(existingVehicles && existingVehicles.data.vehicles.length > 0
|
||||
? { vehicleid: existingVehicles.data.vehicles[0].id, vehicle: null }
|
||||
: {}),
|
||||
@@ -160,50 +164,57 @@ export function JobsAvailableContainer({
|
||||
delete newJob.vehicle;
|
||||
}
|
||||
|
||||
insertNewJob({
|
||||
variables: {
|
||||
job: newJob,
|
||||
},
|
||||
})
|
||||
.then((r) => {
|
||||
Axios.post("/job/totalsssu", {
|
||||
id: r.data.insert_jobs.returning[0].id,
|
||||
});
|
||||
if (typeof newJob.kmin === "string") {
|
||||
newJob.kmin = null;
|
||||
}
|
||||
|
||||
if (CriticalPartsScanning.treatment === "on") {
|
||||
CriticalPartsScan(r.data.insert_jobs.returning[0].id);
|
||||
}
|
||||
notification["success"]({
|
||||
message: t("jobs.successes.created"),
|
||||
onClick: () => {
|
||||
history.push(`/manage/jobs/${r.data.insert_jobs.returning[0].id}`);
|
||||
},
|
||||
});
|
||||
//Job has been inserted. Clean up the available jobs record.
|
||||
try {
|
||||
const r = await insertNewJob({
|
||||
variables: {
|
||||
job: newJob,
|
||||
},
|
||||
});
|
||||
await Axios.post("/job/totalsssu", {
|
||||
id: r.data.insert_jobs.returning[0].id,
|
||||
});
|
||||
|
||||
insertAuditTrail({
|
||||
jobid: r.data.insert_jobs.returning[0].id,
|
||||
operation: AuditTrailMapping.jobimported(),
|
||||
});
|
||||
if (CriticalPartsScanning.treatment === "on") {
|
||||
CriticalPartsScan(r.data.insert_jobs.returning[0].id);
|
||||
}
|
||||
notification["success"]({
|
||||
message: t("jobs.successes.created"),
|
||||
onClick: () => {
|
||||
history.push(`/manage/jobs/${r.data.insert_jobs.returning[0].id}`);
|
||||
},
|
||||
});
|
||||
//Job has been inserted. Clean up the available jobs record.
|
||||
|
||||
deleteJob({
|
||||
variables: { id: estData.id },
|
||||
}).then((r) => {
|
||||
refetch();
|
||||
setInsertLoading(false);
|
||||
});
|
||||
})
|
||||
.catch((r) => {
|
||||
//error while inserting
|
||||
notification["error"]({
|
||||
message: t("jobs.errors.creating", { error: r.message }),
|
||||
});
|
||||
insertAuditTrail({
|
||||
jobid: r.data.insert_jobs.returning[0].id,
|
||||
operation: AuditTrailMapping.jobimported(),
|
||||
});
|
||||
|
||||
await deleteJob({
|
||||
variables: { id: estData.id },
|
||||
}).then((r) => {
|
||||
refetch();
|
||||
setInsertLoading(false);
|
||||
});
|
||||
|
||||
setPartsQueueToggle(bodyshop.md_functionality_toggles.parts_queue_toggle);
|
||||
} catch (r) {
|
||||
//error while inserting
|
||||
notification["error"]({
|
||||
message: t("jobs.errors.creating", { error: r.message }),
|
||||
});
|
||||
refetch();
|
||||
setInsertLoading(false);
|
||||
setPartsQueueToggle(bodyshop.md_functionality_toggles.parts_queue_toggle);
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
//Suplement scenario
|
||||
//Supplement scenario
|
||||
const onJobFindModalOk = async () => {
|
||||
logImEXEvent("job_import_supplement");
|
||||
|
||||
@@ -256,11 +267,14 @@ export function JobsAvailableContainer({
|
||||
// "0.00"
|
||||
// ),
|
||||
// job_totals: newTotals,
|
||||
// queued_for_parts: true,
|
||||
queued_for_parts: partsQueueToggle,
|
||||
},
|
||||
},
|
||||
});
|
||||
if (CriticalPartsScanning.treatment === "on") {
|
||||
|
||||
setPartsQueueToggle(bodyshop.md_functionality_toggles.parts_queue_toggle);
|
||||
|
||||
if (CriticalPartsScanning.treatment === "on") {
|
||||
CriticalPartsScan(updateResult.data.update_jobs.returning[0].id);
|
||||
}
|
||||
if (updateResult.errors) {
|
||||
@@ -335,12 +349,14 @@ export function JobsAvailableContainer({
|
||||
const onOwnerModalCancel = () => {
|
||||
setOwnerModalVisible(false);
|
||||
setSelectedOwner(null);
|
||||
setPartsQueueToggle(bodyshop.md_functionality_toggles.parts_queue_toggle);
|
||||
};
|
||||
|
||||
const onJobModalCancel = () => {
|
||||
setJobModalVisible(false);
|
||||
modalSearchState[1]("");
|
||||
setSelectedJob(null);
|
||||
setPartsQueueToggle(bodyshop.md_functionality_toggles.parts_queue_toggle);
|
||||
};
|
||||
|
||||
const addJobAsNew = (record) => {
|
||||
@@ -361,6 +377,8 @@ export function JobsAvailableContainer({
|
||||
}, [addJobAsSupp, availableJobId, clm_no]);
|
||||
|
||||
if (error) return <AlertComponent type="error" message={error.message} />;
|
||||
|
||||
|
||||
return (
|
||||
<LoadingSpinner
|
||||
loading={insertLoading}
|
||||
@@ -370,11 +388,14 @@ export function JobsAvailableContainer({
|
||||
loading={estDataRaw.loading}
|
||||
error={estDataRaw.error}
|
||||
owner={owner}
|
||||
partsQueueToggle={partsQueueToggle}
|
||||
setPartsQueueToggle={setPartsQueueToggle}
|
||||
selectedOwner={selectedOwner}
|
||||
setSelectedOwner={setSelectedOwner}
|
||||
visible={ownerModalVisible}
|
||||
onOk={onOwnerFindModalOk}
|
||||
onCancel={onOwnerModalCancel}
|
||||
|
||||
/>
|
||||
<JobsFindModalContainer
|
||||
loading={estDataRaw.loading}
|
||||
@@ -386,7 +407,28 @@ export function JobsAvailableContainer({
|
||||
onOk={onJobFindModalOk}
|
||||
onCancel={onJobModalCancel}
|
||||
modalSearchState={modalSearchState}
|
||||
partsQueueToggle={partsQueueToggle}
|
||||
setPartsQueueToggle={setPartsQueueToggle}
|
||||
/>
|
||||
{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
|
||||
@@ -418,98 +460,104 @@ 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(
|
||||
// `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;
|
||||
// }
|
||||
// }
|
||||
|
||||
//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?.PAM) {
|
||||
estData.parts_tax_rates.PAM = estData.parts_tax_rates.PAC;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
// //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;
|
||||
// }
|
||||
// }
|
||||
|
||||
//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.
|
||||
@@ -537,8 +585,7 @@ async function ResolveCCCLineIssues(estData, bodyshop) {
|
||||
//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.misc_amt;
|
||||
line.part_type = "PAS";
|
||||
line.act_price = line.act_price + line.misc_amt;
|
||||
line.tax_part = !!line.misc_tax;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -36,6 +36,8 @@ 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;
|
||||
}
|
||||
|
||||
@@ -4,22 +4,35 @@ import axios from "axios";
|
||||
import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { useHistory } from "react-router-dom";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { auth } from "../../firebase/firebase.utils";
|
||||
import { auth, logImEXEvent } from "../../firebase/firebase.utils";
|
||||
import { INSERT_EXPORT_LOG } from "../../graphql/accounting.queries";
|
||||
import { UPDATE_JOB } from "../../graphql/jobs.queries";
|
||||
import {
|
||||
selectBodyshop,
|
||||
selectCurrentUser,
|
||||
} from "../../redux/user/user.selectors";
|
||||
import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||
import { INSERT_EXPORT_LOG } from "../../graphql/accounting.queries";
|
||||
import { useHistory } from "react-router-dom";
|
||||
import client from "../../utils/GraphQLClient";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
currentUser: selectCurrentUser,
|
||||
});
|
||||
|
||||
function updateJobCache(items) {
|
||||
client.cache.modify({
|
||||
id: "ROOT_QUERY",
|
||||
fields: {
|
||||
jobs(existingJobs = []) {
|
||||
return existingJobs.filter(
|
||||
(jobRef) => jobRef.__ref.includes(items) === false
|
||||
);
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function JobsCloseExportButton({
|
||||
bodyshop,
|
||||
currentUser,
|
||||
@@ -101,6 +114,9 @@ export function JobsCloseExportButton({
|
||||
|
||||
//Check to see if any of them failed. If they didn't don't execute the update.
|
||||
const failedTransactions = PartnerResponse.data.filter((r) => !r.success);
|
||||
const successfulTransactions = PartnerResponse.data.filter(
|
||||
(r) => r.success
|
||||
);
|
||||
if (failedTransactions.length > 0) {
|
||||
//Uh oh. At least one was no good.
|
||||
failedTransactions.forEach((ft) => {
|
||||
@@ -159,12 +175,15 @@ export function JobsCloseExportButton({
|
||||
},
|
||||
});
|
||||
|
||||
if (!jobUpdateResponse.errors) {
|
||||
if (!!!jobUpdateResponse.errors) {
|
||||
notification.open({
|
||||
type: "success",
|
||||
key: "jobsuccessexport",
|
||||
message: t("jobs.successes.exported"),
|
||||
});
|
||||
updateJobCache(
|
||||
jobUpdateResponse.data.update_jobs.returning.map((job) => job.id)
|
||||
);
|
||||
} else {
|
||||
notification["error"]({
|
||||
message: t("jobs.errors.exporting", {
|
||||
@@ -173,13 +192,31 @@ export function JobsCloseExportButton({
|
||||
});
|
||||
}
|
||||
}
|
||||
if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo && successfulTransactions.length > 0) {
|
||||
notification.open({
|
||||
type: "success",
|
||||
key: "jobsuccessexport",
|
||||
message: t("jobs.successes.exported"),
|
||||
});
|
||||
updateJobCache([
|
||||
...new Set(
|
||||
successfulTransactions.map(
|
||||
(st) =>
|
||||
st[
|
||||
bodyshop.accountingconfig && bodyshop.accountingconfig.qbo
|
||||
? "jobid"
|
||||
: "id"
|
||||
]
|
||||
)
|
||||
),
|
||||
]);
|
||||
}
|
||||
if (setSelectedJobs) {
|
||||
setSelectedJobs((selectedJobs) => {
|
||||
return selectedJobs.filter((i) => i !== jobId);
|
||||
});
|
||||
}
|
||||
}
|
||||
if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) refetch();
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
|
||||
@@ -108,7 +108,7 @@ export function JobsConvertButton({
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Select>
|
||||
<Select showSearch>
|
||||
{bodyshop.md_ins_cos.map((s, i) => (
|
||||
<Select.Option key={i} value={s.name}>
|
||||
{s.name}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Collapse, Form, Input, InputNumber, Select, Switch } from "antd";
|
||||
import { Collapse, Form, Input, Select, Switch } from "antd";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
@@ -12,6 +12,12 @@ import FormItemPhone, {
|
||||
} from "../form-items-formatted/phone-form-item.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({
|
||||
@@ -258,26 +264,28 @@ 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 />
|
||||
@@ -356,6 +364,10 @@ export function JobsCreateJobsInfo({ bodyshop, form, selected }) {
|
||||
required={selected && true}
|
||||
form={form}
|
||||
/>
|
||||
<JobsDetailRatesLabor form={form} />
|
||||
<JobsDetailRatesMaterials form={form} />
|
||||
<JobsDetailRatesOther form={form} />
|
||||
<JobsDetailRatesTaxes form={form} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -141,6 +141,17 @@ export function JobsDetailDatesComponent({ jobRO, job, bodyshop }) {
|
||||
<Form.Item label={t("jobs.fields.date_exported")} name="date_exported">
|
||||
<DateTimePicker disabled={true || jobRO} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label={t("jobs.fields.date_void")} name="date_void">
|
||||
<DateTimePicker disabled={true || jobRO} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label={t("jobs.fields.date_lost_sale")}
|
||||
name="date_lost_sale"
|
||||
>
|
||||
<DateTimePicker disabled={true || jobRO} />
|
||||
</Form.Item>
|
||||
</FormRow>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -5,10 +5,10 @@ import {
|
||||
Dropdown,
|
||||
Form,
|
||||
Menu,
|
||||
notification,
|
||||
Popconfirm,
|
||||
Popover,
|
||||
Select,
|
||||
notification,
|
||||
} from "antd";
|
||||
import React, { useMemo } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
@@ -18,18 +18,20 @@ import { createStructuredSelector } from "reselect";
|
||||
import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||
import { CANCEL_APPOINTMENTS_BY_JOB_ID } from "../../graphql/appointments.queries";
|
||||
import { DELETE_JOB, UPDATE_JOB, VOID_JOB } from "../../graphql/jobs.queries";
|
||||
import { insertAuditTrail } from "../../redux/application/application.actions";
|
||||
import { selectJobReadOnly } from "../../redux/application/application.selectors";
|
||||
import { setModalContext } from "../../redux/modals/modals.actions";
|
||||
import {
|
||||
selectBodyshop,
|
||||
selectCurrentUser,
|
||||
} from "../../redux/user/user.selectors";
|
||||
import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
||||
import RbacWrapper from "../rbac-wrapper/rbac-wrapper.component";
|
||||
import JobsDetailHeaderActionsAddevent from "./jobs-detail-header-actions.addevent";
|
||||
import AddToProduction from "./jobs-detail-header-actions.addtoproduction.util";
|
||||
import JobsDetaiLheaderCsi from "./jobs-detail-header-actions.csi.component";
|
||||
import DuplicateJob from "./jobs-detail-header-actions.duplicate.util";
|
||||
import JobsDetailHeaderActionsExportcustdataComponent from "./jobs-detail-header-actions.exportcustdata.component";
|
||||
import RbacWrapper from "../rbac-wrapper/rbac-wrapper.component";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
@@ -52,6 +54,8 @@ const mapDispatchToProps = (dispatch) => ({
|
||||
dispatch(setModalContext({ context: context, modal: "timeTicketTask" })),
|
||||
setCardPaymentContext: (context) =>
|
||||
dispatch(setModalContext({ context: context, modal: "cardPayment" })),
|
||||
insertAuditTrail: ({ jobid, operation }) =>
|
||||
dispatch(insertAuditTrail({ jobid, operation })),
|
||||
});
|
||||
|
||||
export function JobsDetailHeaderActions({
|
||||
@@ -67,6 +71,7 @@ export function JobsDetailHeaderActions({
|
||||
setTimeTicketContext,
|
||||
setTimeTicketTaskContext,
|
||||
setCardPaymentContext,
|
||||
insertAuditTrail,
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const client = useApolloClient();
|
||||
@@ -143,63 +148,73 @@ export function JobsDetailHeaderActions({
|
||||
<Menu.Item
|
||||
disabled={job.status !== bodyshop.md_ro_statuses.default_scheduled}
|
||||
>
|
||||
<Popover
|
||||
trigger="click"
|
||||
disabled={job.status !== bodyshop.md_ro_statuses.default_scheduled}
|
||||
content={
|
||||
<Form
|
||||
layout="vertical"
|
||||
onFinish={async ({ lost_sale_reason }) => {
|
||||
const jobUpdate = await cancelAllAppointments({
|
||||
variables: {
|
||||
jobid: job.id,
|
||||
job: {
|
||||
date_scheduled: null,
|
||||
scheduled_in: null,
|
||||
scheduled_completion: null,
|
||||
lost_sale_reason,
|
||||
status: bodyshop.md_ro_statuses.default_imported,
|
||||
{job.status !== bodyshop.md_ro_statuses.default_scheduled ? (
|
||||
t("menus.jobsactions.cancelallappointments")
|
||||
) : (
|
||||
<Popover
|
||||
trigger="click"
|
||||
disabled={job.status !== bodyshop.md_ro_statuses.default_scheduled}
|
||||
content={
|
||||
<Form
|
||||
layout="vertical"
|
||||
onFinish={async ({ lost_sale_reason }) => {
|
||||
const jobUpdate = await cancelAllAppointments({
|
||||
variables: {
|
||||
jobid: job.id,
|
||||
job: {
|
||||
date_scheduled: null,
|
||||
scheduled_in: null,
|
||||
scheduled_completion: null,
|
||||
lost_sale_reason,
|
||||
date_lost_sale: new Date(),
|
||||
status: bodyshop.md_ro_statuses.default_imported,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
if (!jobUpdate.errors) {
|
||||
notification["success"]({
|
||||
message: t("appointments.successes.canceled"),
|
||||
});
|
||||
return;
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Form.Item
|
||||
name="lost_sale_reason"
|
||||
label={t("jobs.fields.lost_sale_reason")}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
//message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
if (!jobUpdate.errors) {
|
||||
notification["success"]({
|
||||
message: t("appointments.successes.canceled"),
|
||||
});
|
||||
insertAuditTrail({
|
||||
jobid: job.id,
|
||||
operation:
|
||||
AuditTrailMapping.appointmentcancel(lost_sale_reason),
|
||||
});
|
||||
return;
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Select
|
||||
options={bodyshop.md_lost_sale_reasons.map((lsr) => ({
|
||||
label: lsr,
|
||||
value: lsr,
|
||||
}))}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Button
|
||||
htmlType="submit"
|
||||
disabled={
|
||||
job.status !== bodyshop.md_ro_statuses.default_scheduled
|
||||
}
|
||||
>
|
||||
{t("appointments.actions.cancel")}
|
||||
</Button>
|
||||
</Form>
|
||||
}
|
||||
>
|
||||
{t("menus.jobsactions.cancelallappointments")}
|
||||
</Popover>
|
||||
<Form.Item
|
||||
name="lost_sale_reason"
|
||||
label={t("jobs.fields.lost_sale_reason")}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
//message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Select
|
||||
options={bodyshop.md_lost_sale_reasons.map((lsr) => ({
|
||||
label: lsr,
|
||||
value: lsr,
|
||||
}))}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Button
|
||||
htmlType="submit"
|
||||
disabled={
|
||||
job.status !== bodyshop.md_ro_statuses.default_scheduled
|
||||
}
|
||||
>
|
||||
{t("appointments.actions.cancel")}
|
||||
</Button>
|
||||
</Form>
|
||||
}
|
||||
>
|
||||
{t("menus.jobsactions.cancelallappointments")}
|
||||
</Popover>
|
||||
)}
|
||||
</Menu.Item>
|
||||
<Menu.Item
|
||||
disabled={
|
||||
@@ -245,7 +260,12 @@ export function JobsDetailHeaderActions({
|
||||
|
||||
setTimeTicketContext({
|
||||
actions: {},
|
||||
context: { jobId: job.id },
|
||||
context: {
|
||||
jobId: job.id,
|
||||
created_by: currentUser.displayName
|
||||
? currentUser.email.concat(" | ", currentUser.displayName)
|
||||
: currentUser.email,
|
||||
},
|
||||
});
|
||||
}}
|
||||
>
|
||||
@@ -516,6 +536,7 @@ export function JobsDetailHeaderActions({
|
||||
scheduled_in: null,
|
||||
scheduled_completion: null,
|
||||
inproduction: false,
|
||||
date_void: new Date(),
|
||||
},
|
||||
note: [
|
||||
{
|
||||
|
||||
@@ -27,6 +27,8 @@ export default async function DuplicateJob(
|
||||
delete existingJob.id;
|
||||
delete existingJob.createdat;
|
||||
delete existingJob.updatedat;
|
||||
delete existingJob.cieca_stl;
|
||||
delete existingJob.cieca_ttl;
|
||||
|
||||
const newJob = {
|
||||
...existingJob,
|
||||
@@ -81,6 +83,8 @@ export async function CreateIouForJob(
|
||||
delete existingJob.id;
|
||||
delete existingJob.createdat;
|
||||
delete existingJob.updatedat;
|
||||
delete existingJob.cieca_stl;
|
||||
delete existingJob.cieca_ttl;
|
||||
|
||||
const newJob = {
|
||||
...existingJob,
|
||||
|
||||
@@ -5,9 +5,13 @@ 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);
|
||||
@@ -48,6 +52,7 @@ const adjSpan = {
|
||||
};
|
||||
|
||||
export function JobsDetailLaborContainer({
|
||||
bodyshop,
|
||||
jobRO,
|
||||
job,
|
||||
jobId,
|
||||
@@ -58,6 +63,12 @@ export function JobsDetailLaborContainer({
|
||||
techConsole,
|
||||
adjustments,
|
||||
}) {
|
||||
const { Enhanced_Payroll } = useTreatments(
|
||||
["Enhanced_Payroll"],
|
||||
{},
|
||||
bodyshop.imexshopid
|
||||
);
|
||||
|
||||
return (
|
||||
<Row gutter={[16, 16]}>
|
||||
<Col {...ticketSpan}>
|
||||
@@ -70,14 +81,28 @@ export function JobsDetailLaborContainer({
|
||||
jobId={jobId}
|
||||
/>
|
||||
</Col>
|
||||
<Col {...adjSpan}>
|
||||
<LaborAllocationsTableComponent
|
||||
jobId={jobId}
|
||||
joblines={joblines}
|
||||
timetickets={timetickets}
|
||||
adjustments={adjustments}
|
||||
/>
|
||||
</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>
|
||||
)}
|
||||
</Row>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -6,12 +6,14 @@ 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>
|
||||
@@ -43,6 +45,13 @@ export default function JobsDetailPliComponent({
|
||||
billsQuery={billsQuery}
|
||||
/>
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
<PartsDispatchTable
|
||||
job={job}
|
||||
handleOnRowClick={handlePartsDispatchOnRowClick}
|
||||
billsQuery={billsQuery}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -39,12 +39,24 @@ export default function JobsDetailPliContainer({ job }) {
|
||||
}
|
||||
};
|
||||
|
||||
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) });
|
||||
}
|
||||
};
|
||||
return (
|
||||
<JobsDetailPliComponent
|
||||
job={job}
|
||||
billsQuery={billsQuery}
|
||||
handleBillOnRowClick={handleBillOnRowClick}
|
||||
handlePartsOrderOnRowClick={handlePartsOrderOnRowClick}
|
||||
handlePartsDispatchOnRowClick={handlePartsDispatchOnRowClick}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,25 +1,21 @@
|
||||
import {
|
||||
Divider,
|
||||
Form,
|
||||
Input,
|
||||
InputNumber,
|
||||
Select,
|
||||
Space,
|
||||
Switch,
|
||||
Tooltip,
|
||||
} from "antd";
|
||||
import { Divider, Form, Input, 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,
|
||||
@@ -84,14 +80,7 @@ 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"
|
||||
@@ -120,41 +109,7 @@ 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"
|
||||
@@ -242,7 +197,15 @@ 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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,427 @@
|
||||
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);
|
||||
@@ -0,0 +1,145 @@
|
||||
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);
|
||||
@@ -0,0 +1,104 @@
|
||||
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);
|
||||
@@ -68,11 +68,51 @@ export function JobsDetailRatesParts({
|
||||
},
|
||||
]}
|
||||
>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||
<InputNumber
|
||||
min={0}
|
||||
max={100}
|
||||
precision={4}
|
||||
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
|
||||
@@ -118,11 +158,51 @@ export function JobsDetailRatesParts({
|
||||
},
|
||||
]}
|
||||
>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||
<InputNumber
|
||||
min={0}
|
||||
max={100}
|
||||
precision={4}
|
||||
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
|
||||
@@ -168,11 +248,51 @@ export function JobsDetailRatesParts({
|
||||
},
|
||||
]}
|
||||
>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||
<InputNumber
|
||||
min={0}
|
||||
max={100}
|
||||
precision={4}
|
||||
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
|
||||
@@ -218,11 +338,51 @@ export function JobsDetailRatesParts({
|
||||
},
|
||||
]}
|
||||
>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||
<InputNumber
|
||||
min={0}
|
||||
max={100}
|
||||
precision={4}
|
||||
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
|
||||
@@ -268,11 +428,51 @@ export function JobsDetailRatesParts({
|
||||
},
|
||||
]}
|
||||
>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||
<InputNumber
|
||||
min={0}
|
||||
max={100}
|
||||
precision={4}
|
||||
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
|
||||
@@ -318,11 +518,51 @@ export function JobsDetailRatesParts({
|
||||
},
|
||||
]}
|
||||
>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||
<InputNumber
|
||||
min={0}
|
||||
max={100}
|
||||
precision={4}
|
||||
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
|
||||
@@ -368,11 +608,51 @@ export function JobsDetailRatesParts({
|
||||
},
|
||||
]}
|
||||
>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||
<InputNumber
|
||||
min={0}
|
||||
max={100}
|
||||
precision={4}
|
||||
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
|
||||
@@ -418,11 +698,51 @@ export function JobsDetailRatesParts({
|
||||
},
|
||||
]}
|
||||
>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||
<InputNumber
|
||||
min={0}
|
||||
max={100}
|
||||
precision={4}
|
||||
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
|
||||
@@ -468,11 +788,51 @@ export function JobsDetailRatesParts({
|
||||
},
|
||||
]}
|
||||
>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||
<InputNumber
|
||||
min={0}
|
||||
max={100}
|
||||
precision={4}
|
||||
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
|
||||
@@ -518,11 +878,51 @@ export function JobsDetailRatesParts({
|
||||
},
|
||||
]}
|
||||
>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||
<InputNumber
|
||||
min={0}
|
||||
max={100}
|
||||
precision={4}
|
||||
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
|
||||
@@ -568,11 +968,51 @@ export function JobsDetailRatesParts({
|
||||
},
|
||||
]}
|
||||
>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||
<InputNumber
|
||||
min={0}
|
||||
max={100}
|
||||
precision={4}
|
||||
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
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
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>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { useEffect } from "react";
|
||||
import { Gallery } from "react-grid-gallery";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { GenerateThumbUrl } from "./job-documents.utility";
|
||||
import { GenerateSrcUrl, GenerateThumbUrl } from "./job-documents.utility";
|
||||
|
||||
function JobsDocumentGalleryExternal({
|
||||
data,
|
||||
@@ -15,7 +15,7 @@ function JobsDocumentGalleryExternal({
|
||||
let documents = data.reduce((acc, value) => {
|
||||
if (value.type.startsWith("image")) {
|
||||
acc.push({
|
||||
//src: GenerateSrcUrl(value),
|
||||
fullsize: GenerateSrcUrl(value),
|
||||
src: GenerateThumbUrl(value),
|
||||
thumbnailHeight: 225,
|
||||
thumbnailWidth: 225,
|
||||
|
||||
@@ -52,7 +52,7 @@ function JobDocumentsLocalGalleryExternal({
|
||||
val.type.mime &&
|
||||
val.type.mime.startsWith("image")
|
||||
) {
|
||||
acc.push({ ...val, src: val.thumbnail });
|
||||
acc.push({ ...val, src: val.thumbnail, fullsize: val.src });
|
||||
}
|
||||
return acc;
|
||||
}, [])
|
||||
|
||||
@@ -13,12 +13,26 @@ import {
|
||||
selectBodyshop,
|
||||
selectCurrentUser,
|
||||
} from "../../redux/user/user.selectors";
|
||||
import client from "../../utils/GraphQLClient";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
currentUser: selectCurrentUser,
|
||||
});
|
||||
|
||||
function updateJobCache(items) {
|
||||
client.cache.modify({
|
||||
id: "ROOT_QUERY",
|
||||
fields: {
|
||||
jobs(existingJobs = []) {
|
||||
return existingJobs.filter(
|
||||
(jobRef) => jobRef.__ref.includes(items) === false
|
||||
);
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function JobsExportAllButton({
|
||||
bodyshop,
|
||||
currentUser,
|
||||
@@ -96,7 +110,9 @@ export function JobsExportAllButton({
|
||||
Object.keys(groupedData).map(async (key) => {
|
||||
//Check to see if any of them failed. If they didn't don't execute the update.
|
||||
const failedTransactions = groupedData[key].filter((r) => !r.success);
|
||||
|
||||
const successfulTransactions = groupedData[key].filter(
|
||||
(r) => r.success
|
||||
);
|
||||
if (failedTransactions.length > 0) {
|
||||
//Uh oh. At least one was no good.
|
||||
failedTransactions.forEach((ft) => {
|
||||
@@ -155,12 +171,17 @@ export function JobsExportAllButton({
|
||||
},
|
||||
});
|
||||
|
||||
if (!jobUpdateResponse.errors) {
|
||||
if (!!!jobUpdateResponse.errors) {
|
||||
notification.open({
|
||||
type: "success",
|
||||
key: "jobsuccessexport",
|
||||
message: t("jobs.successes.exported"),
|
||||
});
|
||||
updateJobCache(
|
||||
jobUpdateResponse.data.update_jobs.returning.map(
|
||||
(job) => job.id
|
||||
)
|
||||
);
|
||||
} else {
|
||||
notification["error"]({
|
||||
message: t("jobs.errors.exporting", {
|
||||
@@ -169,14 +190,31 @@ export function JobsExportAllButton({
|
||||
});
|
||||
}
|
||||
}
|
||||
if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo && successfulTransactions.length > 0) {
|
||||
notification.open({
|
||||
type: "success",
|
||||
key: "jobsuccessexport",
|
||||
message: t("jobs.successes.exported"),
|
||||
});
|
||||
updateJobCache([
|
||||
...new Set(
|
||||
successfulTransactions.map(
|
||||
(st) =>
|
||||
st[
|
||||
bodyshop.accountingconfig && bodyshop.accountingconfig.qbo
|
||||
? "jobid"
|
||||
: "id"
|
||||
]
|
||||
)
|
||||
),
|
||||
]);
|
||||
}
|
||||
}
|
||||
})
|
||||
);
|
||||
if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) refetch();
|
||||
|
||||
if (!!completedCallback) completedCallback([]);
|
||||
if (!!loadingCallback) loadingCallback(false);
|
||||
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
|
||||
@@ -14,6 +14,8 @@ export default function JobsFindModalComponent({
|
||||
importOptionsState,
|
||||
modalSearchState,
|
||||
jobsListRefetch,
|
||||
partsQueueToggle,
|
||||
setPartsQueueToggle,
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const [modalSearch, setModalSearch] = modalSearchState;
|
||||
@@ -199,6 +201,12 @@ export default function JobsFindModalComponent({
|
||||
>
|
||||
{t("jobs.labels.override_header")}
|
||||
</Checkbox>
|
||||
<Checkbox
|
||||
checked={partsQueueToggle}
|
||||
onChange={(e) => setPartsQueueToggle(e.target.checked)}
|
||||
>
|
||||
{t("bodyshop.fields.md_functionality_toggles.parts_queue_toggle")}
|
||||
</Checkbox>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -24,6 +24,8 @@ export default connect(
|
||||
setSelectedJob,
|
||||
importOptionsState,
|
||||
modalSearchState,
|
||||
partsQueueToggle,
|
||||
setPartsQueueToggle,
|
||||
...modalProps
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
@@ -91,6 +93,8 @@ export default connect(
|
||||
jobsListRefetch={jobsList.refetch}
|
||||
jobsList={jobsData}
|
||||
modalSearchState={modalSearchState}
|
||||
partsQueueToggle={partsQueueToggle}
|
||||
setPartsQueueToggle={setPartsQueueToggle}
|
||||
/>
|
||||
) : null}
|
||||
</Modal>
|
||||
|
||||
@@ -12,6 +12,7 @@ import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||
import StartChatButton from "../chat-open-button/chat-open-button.component";
|
||||
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
|
||||
import {pageLimit} from "../../utils/config";
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
//currentUser: selectCurrentUser
|
||||
bodyshop: selectBodyshop,
|
||||
@@ -33,17 +34,14 @@ 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),
|
||||
sortOrder: sortcolumn === "ro_number" && sortorder,
|
||||
|
||||
render: (text, record) => (
|
||||
<Link to={"/manage/jobs/" + record.id}>
|
||||
{record.ro_number || t("general.labels.na")}
|
||||
</Link>
|
||||
),
|
||||
},
|
||||
|
||||
{
|
||||
title: t("jobs.fields.owner"),
|
||||
dataIndex: "ownr_ln",
|
||||
@@ -125,7 +123,6 @@ export function JobsList({ bodyshop, refetch, loading, jobs, total }) {
|
||||
title: t("vehicles.fields.plate_no"),
|
||||
dataIndex: "plate_no",
|
||||
key: "plate_no",
|
||||
|
||||
ellipsis: true,
|
||||
sorter: true, //(a, b) => alphaSort(a.plate_no, b.plate_no),
|
||||
sortOrder: sortcolumn === "plate_no" && sortorder,
|
||||
@@ -137,7 +134,6 @@ export function JobsList({ bodyshop, refetch, loading, jobs, total }) {
|
||||
title: t("jobs.fields.clm_no"),
|
||||
dataIndex: "clm_no",
|
||||
key: "clm_no",
|
||||
|
||||
ellipsis: true,
|
||||
sorter: true, //(a, b) => alphaSort(a.clm_no, b.clm_no),
|
||||
sortOrder: sortcolumn === "clm_no" && sortorder,
|
||||
@@ -259,11 +255,11 @@ export function JobsList({ bodyshop, refetch, loading, jobs, total }) {
|
||||
pagination={
|
||||
search?.search
|
||||
? {
|
||||
pageSize: 25,
|
||||
pageSize: pageLimit,
|
||||
showSizeChanger: false,
|
||||
}
|
||||
: {
|
||||
pageSize: 25,
|
||||
pageSize: pageLimit,
|
||||
current: parseInt(page || 1),
|
||||
total: total,
|
||||
showSizeChanger: false,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import {
|
||||
SyncOutlined,
|
||||
ExclamationCircleFilled,
|
||||
PauseCircleOutlined,
|
||||
BranchesOutlined,
|
||||
SyncOutlined,
|
||||
ExclamationCircleFilled,
|
||||
PauseCircleOutlined,
|
||||
BranchesOutlined,
|
||||
} from "@ant-design/icons";
|
||||
import { useQuery } from "@apollo/client";
|
||||
import { Button, Card, Grid, Input, Space, Table, Tooltip } from "antd";
|
||||
@@ -22,374 +22,374 @@ import ChatOpenButton from "../chat-open-button/chat-open-button.component";
|
||||
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
|
||||
export function JobsList({ bodyshop }) {
|
||||
const searchParams = queryString.parse(useLocation().search);
|
||||
const { selected } = searchParams;
|
||||
const selectedBreakpoint = Object.entries(Grid.useBreakpoint())
|
||||
.filter((screen) => !!screen[1])
|
||||
.slice(-1)[0];
|
||||
const { loading, error, data, refetch } = useQuery(QUERY_ALL_ACTIVE_JOBS, {
|
||||
variables: {
|
||||
statuses: bodyshop.md_ro_statuses.active_statuses || ["Open", "Open*"],
|
||||
},
|
||||
fetchPolicy: "network-only",
|
||||
nextFetchPolicy: "network-only",
|
||||
});
|
||||
const searchParams = queryString.parse(useLocation().search);
|
||||
const { selected } = searchParams;
|
||||
const selectedBreakpoint = Object.entries(Grid.useBreakpoint())
|
||||
.filter((screen) => !!screen[1])
|
||||
.slice(-1)[0];
|
||||
const { loading, error, data, refetch } = useQuery(QUERY_ALL_ACTIVE_JOBS, {
|
||||
variables: {
|
||||
statuses: bodyshop.md_ro_statuses.active_statuses || ["Open", "Open*"],
|
||||
},
|
||||
fetchPolicy: "network-only",
|
||||
nextFetchPolicy: "network-only",
|
||||
});
|
||||
|
||||
const [state, setState] = useState({
|
||||
sortedInfo: {},
|
||||
filteredInfo: { text: "" },
|
||||
});
|
||||
const [state, setState] = useState({
|
||||
sortedInfo: {},
|
||||
filteredInfo: { text: "" },
|
||||
});
|
||||
|
||||
const { t } = useTranslation();
|
||||
const history = useHistory();
|
||||
const [searchText, setSearchText] = useState("");
|
||||
const { t } = useTranslation();
|
||||
const history = useHistory();
|
||||
const [searchText, setSearchText] = useState("");
|
||||
|
||||
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||
|
||||
const jobs = data
|
||||
? searchText === ""
|
||||
? data.jobs
|
||||
: data.jobs.filter(
|
||||
(j) =>
|
||||
(j.ro_number || "")
|
||||
.toString()
|
||||
.toLowerCase()
|
||||
.includes(searchText.toLowerCase()) ||
|
||||
(j.ownr_co_nm || "")
|
||||
.toLowerCase()
|
||||
.includes(searchText.toLowerCase()) ||
|
||||
(j.comments || "")
|
||||
.toLowerCase()
|
||||
.includes(searchText.toLowerCase()) ||
|
||||
(j.ownr_fn || "")
|
||||
.toLowerCase()
|
||||
.includes(searchText.toLowerCase()) ||
|
||||
(j.ownr_ln || "")
|
||||
.toLowerCase()
|
||||
.includes(searchText.toLowerCase()) ||
|
||||
(j.clm_no || "").toLowerCase().includes(searchText.toLowerCase()) ||
|
||||
(j.plate_no || "")
|
||||
.toLowerCase()
|
||||
.includes(searchText.toLowerCase()) ||
|
||||
(j.v_model_desc || "")
|
||||
.toLowerCase()
|
||||
.includes(searchText.toLowerCase()) ||
|
||||
(j.est_ct_fn || "")
|
||||
.toLowerCase()
|
||||
.includes(searchText.toLowerCase()) ||
|
||||
(j.est_ct_ln || "")
|
||||
.toLowerCase()
|
||||
.includes(searchText.toLowerCase()) ||
|
||||
(j.v_make_desc || "")
|
||||
.toLowerCase()
|
||||
.includes(searchText.toLowerCase())
|
||||
)
|
||||
: [];
|
||||
const jobs = data
|
||||
? searchText === ""
|
||||
? data.jobs
|
||||
: data.jobs.filter(
|
||||
(j) =>
|
||||
(j.ro_number || "")
|
||||
.toString()
|
||||
.toLowerCase()
|
||||
.includes(searchText.toLowerCase()) ||
|
||||
(j.ownr_co_nm || "")
|
||||
.toLowerCase()
|
||||
.includes(searchText.toLowerCase()) ||
|
||||
(j.comments || "")
|
||||
.toLowerCase()
|
||||
.includes(searchText.toLowerCase()) ||
|
||||
(j.ownr_fn || "")
|
||||
.toLowerCase()
|
||||
.includes(searchText.toLowerCase()) ||
|
||||
(j.ownr_ln || "")
|
||||
.toLowerCase()
|
||||
.includes(searchText.toLowerCase()) ||
|
||||
(j.clm_no || "").toLowerCase().includes(searchText.toLowerCase()) ||
|
||||
(j.plate_no || "")
|
||||
.toLowerCase()
|
||||
.includes(searchText.toLowerCase()) ||
|
||||
(j.v_model_desc || "")
|
||||
.toLowerCase()
|
||||
.includes(searchText.toLowerCase()) ||
|
||||
(j.est_ct_fn || "")
|
||||
.toLowerCase()
|
||||
.includes(searchText.toLowerCase()) ||
|
||||
(j.est_ct_ln || "")
|
||||
.toLowerCase()
|
||||
.includes(searchText.toLowerCase()) ||
|
||||
(j.v_make_desc || "")
|
||||
.toLowerCase()
|
||||
.includes(searchText.toLowerCase())
|
||||
)
|
||||
: [];
|
||||
|
||||
const handleTableChange = (pagination, filters, sorter) => {
|
||||
setState({ ...state, filteredInfo: filters, sortedInfo: sorter });
|
||||
};
|
||||
const handleTableChange = (pagination, filters, sorter) => {
|
||||
setState({ ...state, filteredInfo: filters, sortedInfo: sorter });
|
||||
};
|
||||
|
||||
const handleOnRowClick = (record) => {
|
||||
if (record) {
|
||||
if (record.id) {
|
||||
history.push({
|
||||
search: queryString.stringify({
|
||||
...searchParams,
|
||||
selected: record.id,
|
||||
}),
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
const handleOnRowClick = (record) => {
|
||||
if (record) {
|
||||
if (record.id) {
|
||||
history.push({
|
||||
search: queryString.stringify({
|
||||
...searchParams,
|
||||
selected: record.id,
|
||||
}),
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: t("jobs.fields.ro_number"),
|
||||
dataIndex: "ro_number",
|
||||
key: "ro_number",
|
||||
sorter: (a, b) =>
|
||||
parseInt((a.ro_number || "0").replace(/\D/g, "")) -
|
||||
parseInt((b.ro_number || "0").replace(/\D/g, "")),
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "ro_number" && state.sortedInfo.order,
|
||||
const columns = [
|
||||
{
|
||||
title: t("jobs.fields.ro_number"),
|
||||
dataIndex: "ro_number",
|
||||
key: "ro_number",
|
||||
sorter: (a, b) =>
|
||||
parseInt((a.ro_number || "0").replace(/\D/g, "")) -
|
||||
parseInt((b.ro_number || "0").replace(/\D/g, "")),
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "ro_number" && state.sortedInfo.order,
|
||||
|
||||
render: (text, record) => (
|
||||
<Link
|
||||
to={"/manage/jobs/" + record.id}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<Space>
|
||||
{record.ro_number || t("general.labels.na")}
|
||||
{record.production_vars && record.production_vars.alert ? (
|
||||
<ExclamationCircleFilled className="production-alert" />
|
||||
) : null}
|
||||
{record.suspended && (
|
||||
<PauseCircleOutlined style={{ color: "orangered" }} />
|
||||
)}
|
||||
{record.iouparent && (
|
||||
<Tooltip title={t("jobs.labels.iou")}>
|
||||
<BranchesOutlined style={{ color: "orangered" }} />
|
||||
</Tooltip>
|
||||
)}
|
||||
</Space>
|
||||
</Link>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t("jobs.fields.owner"),
|
||||
dataIndex: "owner",
|
||||
key: "owner",
|
||||
ellipsis: true,
|
||||
render: (text, record) => (
|
||||
<Link
|
||||
to={"/manage/jobs/" + record.id}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<Space>
|
||||
{record.ro_number || t("general.labels.na")}
|
||||
{record.production_vars && record.production_vars.alert ? (
|
||||
<ExclamationCircleFilled className="production-alert" />
|
||||
) : null}
|
||||
{record.suspended && (
|
||||
<PauseCircleOutlined style={{ color: "orangered" }} />
|
||||
)}
|
||||
{record.iouparent && (
|
||||
<Tooltip title={t("jobs.labels.iou")}>
|
||||
<BranchesOutlined style={{ color: "orangered" }} />
|
||||
</Tooltip>
|
||||
)}
|
||||
</Space>
|
||||
</Link>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t("jobs.fields.owner"),
|
||||
dataIndex: "owner",
|
||||
key: "owner",
|
||||
ellipsis: true,
|
||||
|
||||
responsive: ["md"],
|
||||
sorter: (a, b) => alphaSort(a.ownr_ln, b.ownr_ln),
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "owner" && state.sortedInfo.order,
|
||||
render: (text, record) => {
|
||||
return record.ownerid ? (
|
||||
<Link
|
||||
to={"/manage/owners/" + record.ownerid}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<OwnerNameDisplay ownerObject={record} />
|
||||
</Link>
|
||||
) : (
|
||||
<span>
|
||||
responsive: ["md"],
|
||||
sorter: (a, b) => alphaSort(a.ownr_ln, b.ownr_ln),
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "owner" && state.sortedInfo.order,
|
||||
render: (text, record) => {
|
||||
return record.ownerid ? (
|
||||
<Link
|
||||
to={"/manage/owners/" + record.ownerid}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<OwnerNameDisplay ownerObject={record} />
|
||||
</Link>
|
||||
) : (
|
||||
<span>
|
||||
<OwnerNameDisplay ownerObject={record} />
|
||||
</span>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: t("jobs.fields.ownr_ph1"),
|
||||
dataIndex: "ownr_ph1",
|
||||
key: "ownr_ph1",
|
||||
ellipsis: true,
|
||||
responsive: ["md"],
|
||||
render: (text, record) => (
|
||||
<ChatOpenButton phone={record.ownr_ph1} jobid={record.id} />
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t("jobs.fields.ownr_ph2"),
|
||||
dataIndex: "ownr_ph2",
|
||||
key: "ownr_ph2",
|
||||
ellipsis: true,
|
||||
responsive: ["md"],
|
||||
render: (text, record) => (
|
||||
<ChatOpenButton phone={record.ownr_ph2} jobid={record.id} />
|
||||
),
|
||||
},
|
||||
|
||||
{
|
||||
title: t("jobs.fields.status"),
|
||||
dataIndex: "status",
|
||||
key: "status",
|
||||
ellipsis: true,
|
||||
|
||||
sorter: (a, b) => alphaSort(a.status, b.status),
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "status" && state.sortedInfo.order,
|
||||
filters:
|
||||
(jobs &&
|
||||
jobs
|
||||
.map((j) => j.status)
|
||||
.filter(onlyUnique)
|
||||
.map((s) => {
|
||||
return {
|
||||
text: s || "No Status*",
|
||||
value: [s],
|
||||
};
|
||||
})) ||
|
||||
[],
|
||||
onFilter: (value, record) => value.includes(record.status),
|
||||
},
|
||||
|
||||
{
|
||||
title: t("jobs.fields.vehicle"),
|
||||
dataIndex: "vehicle",
|
||||
key: "vehicle",
|
||||
ellipsis: true,
|
||||
render: (text, record) => {
|
||||
return record.vehicleid ? (
|
||||
<Link
|
||||
to={"/manage/vehicles/" + record.vehicleid}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
{`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${
|
||||
record.v_model_desc || ""
|
||||
}`}
|
||||
</Link>
|
||||
) : (
|
||||
<span>{`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${
|
||||
record.v_model_desc || ""
|
||||
}`}</span>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: t("vehicles.fields.plate_no"),
|
||||
dataIndex: "plate_no",
|
||||
key: "plate_no",
|
||||
ellipsis: true,
|
||||
|
||||
responsive: ["md"],
|
||||
sorter: (a, b) => alphaSort(a.plate_no, b.plate_no),
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "plate_no" && state.sortedInfo.order,
|
||||
},
|
||||
{
|
||||
title: t("jobs.fields.clm_no"),
|
||||
dataIndex: "clm_no",
|
||||
key: "clm_no",
|
||||
ellipsis: true,
|
||||
responsive: ["md"],
|
||||
sorter: (a, b) => alphaSort(a.clm_no, b.clm_no),
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "clm_no" && state.sortedInfo.order,
|
||||
render: (text, record) =>
|
||||
`${record.clm_no || ""}${
|
||||
record.po_number ? ` (PO: ${record.po_number})` : ""
|
||||
}`,
|
||||
},
|
||||
{
|
||||
title: t("jobs.fields.ins_co_nm"),
|
||||
dataIndex: "ins_co_nm",
|
||||
key: "ins_co_nm",
|
||||
ellipsis: true,
|
||||
filters:
|
||||
(jobs &&
|
||||
jobs
|
||||
.map((j) => j.ins_co_nm)
|
||||
.filter(onlyUnique)
|
||||
.map((s) => {
|
||||
return {
|
||||
text: s,
|
||||
value: [s],
|
||||
};
|
||||
})) ||
|
||||
[],
|
||||
onFilter: (value, record) => value.includes(record.ins_co_nm),
|
||||
responsive: ["md"],
|
||||
},
|
||||
{
|
||||
title: t("jobs.fields.clm_total"),
|
||||
dataIndex: "clm_total",
|
||||
key: "clm_total",
|
||||
responsive: ["md"],
|
||||
ellipsis: true,
|
||||
|
||||
sorter: (a, b) => a.clm_total - b.clm_total,
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "clm_total" && state.sortedInfo.order,
|
||||
render: (text, record) => (
|
||||
<CurrencyFormatter>{record.clm_total}</CurrencyFormatter>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t("jobs.labels.estimator"),
|
||||
dataIndex: "jobs.labels.estimator",
|
||||
key: "jobs.labels.estimator",
|
||||
ellipsis: true,
|
||||
responsive: ["xl"],
|
||||
filterSearch: true,
|
||||
filters:
|
||||
(jobs &&
|
||||
jobs
|
||||
.map((j) => `${j.est_ct_fn || ""} ${j.est_ct_ln || ""}`.trim())
|
||||
.filter(onlyUnique)
|
||||
.map((s) => {
|
||||
return {
|
||||
text: s || "N/A",
|
||||
value: [s],
|
||||
};
|
||||
})) ||
|
||||
[],
|
||||
onFilter: (value, record) =>
|
||||
value.includes(
|
||||
`${record.est_ct_fn || ""} ${record.est_ct_ln || ""}`.trim()
|
||||
),
|
||||
render: (text, record) =>
|
||||
`${record.est_ct_fn || ""} ${record.est_ct_ln || ""}`.trim(),
|
||||
},
|
||||
{
|
||||
title: t("jobs.fields.comment"),
|
||||
dataIndex: "comment",
|
||||
key: "comment",
|
||||
ellipsis: true,
|
||||
responsive: ["md"],
|
||||
},
|
||||
// {
|
||||
// title: t("jobs.fields.owner_owing"),
|
||||
// dataIndex: "owner_owing",
|
||||
// key: "owner_owing",
|
||||
// responsive: ["md"],
|
||||
// render: (text, record) => (
|
||||
// <CurrencyFormatter>{record.owner_owing}</CurrencyFormatter>
|
||||
// ),
|
||||
// },
|
||||
];
|
||||
|
||||
const scrollMapper = {
|
||||
xs: true,
|
||||
sm: true,
|
||||
md: true,
|
||||
lg: "100%",
|
||||
xl: "100%",
|
||||
xxl: "100%",
|
||||
};
|
||||
|
||||
return (
|
||||
<Card
|
||||
title={t("titles.bc.jobs-active")}
|
||||
extra={
|
||||
<Space wrap>
|
||||
<Button onClick={() => refetch()}>
|
||||
<SyncOutlined />
|
||||
</Button>
|
||||
<Input.Search
|
||||
placeholder={t("general.labels.search")}
|
||||
onChange={(e) => {
|
||||
setSearchText(e.target.value);
|
||||
}}
|
||||
value={searchText}
|
||||
enterButton
|
||||
/>
|
||||
</Space>
|
||||
}
|
||||
>
|
||||
<Table
|
||||
loading={loading}
|
||||
pagination={{ defaultPageSize: 50 }}
|
||||
columns={columns}
|
||||
rowKey="id"
|
||||
dataSource={jobs}
|
||||
scroll={{
|
||||
x: selectedBreakpoint ? scrollMapper[selectedBreakpoint[0]] : "100%",
|
||||
}}
|
||||
rowSelection={{
|
||||
onSelect: (record) => {
|
||||
handleOnRowClick(record);
|
||||
},
|
||||
selectedRowKeys: [selected],
|
||||
type: "radio",
|
||||
}}
|
||||
onChange={handleTableChange}
|
||||
onRow={(record, rowIndex) => {
|
||||
return {
|
||||
onClick: (event) => {
|
||||
handleOnRowClick(record);
|
||||
);
|
||||
},
|
||||
};
|
||||
}}
|
||||
/>
|
||||
</Card>
|
||||
);
|
||||
},
|
||||
{
|
||||
title: t("jobs.fields.ownr_ph1"),
|
||||
dataIndex: "ownr_ph1",
|
||||
key: "ownr_ph1",
|
||||
ellipsis: true,
|
||||
responsive: ["md"],
|
||||
render: (text, record) => (
|
||||
<ChatOpenButton phone={record.ownr_ph1} jobid={record.id} />
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t("jobs.fields.ownr_ph2"),
|
||||
dataIndex: "ownr_ph2",
|
||||
key: "ownr_ph2",
|
||||
ellipsis: true,
|
||||
responsive: ["md"],
|
||||
render: (text, record) => (
|
||||
<ChatOpenButton phone={record.ownr_ph2} jobid={record.id} />
|
||||
),
|
||||
},
|
||||
|
||||
{
|
||||
title: t("jobs.fields.status"),
|
||||
dataIndex: "status",
|
||||
key: "status",
|
||||
ellipsis: true,
|
||||
|
||||
sorter: (a, b) => alphaSort(a.status, b.status),
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "status" && state.sortedInfo.order,
|
||||
filters:
|
||||
(jobs &&
|
||||
jobs
|
||||
.map((j) => j.status)
|
||||
.filter(onlyUnique)
|
||||
.map((s) => {
|
||||
return {
|
||||
text: s || "No Status*",
|
||||
value: [s],
|
||||
};
|
||||
})) ||
|
||||
[],
|
||||
onFilter: (value, record) => value.includes(record.status),
|
||||
},
|
||||
|
||||
{
|
||||
title: t("jobs.fields.vehicle"),
|
||||
dataIndex: "vehicle",
|
||||
key: "vehicle",
|
||||
ellipsis: true,
|
||||
render: (text, record) => {
|
||||
return record.vehicleid ? (
|
||||
<Link
|
||||
to={"/manage/vehicles/" + record.vehicleid}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
{`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${
|
||||
record.v_model_desc || ""
|
||||
}`}
|
||||
</Link>
|
||||
) : (
|
||||
<span>{`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${
|
||||
record.v_model_desc || ""
|
||||
}`}</span>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: t("vehicles.fields.plate_no"),
|
||||
dataIndex: "plate_no",
|
||||
key: "plate_no",
|
||||
ellipsis: true,
|
||||
|
||||
responsive: ["md"],
|
||||
sorter: (a, b) => alphaSort(a.plate_no, b.plate_no),
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "plate_no" && state.sortedInfo.order,
|
||||
},
|
||||
{
|
||||
title: t("jobs.fields.clm_no"),
|
||||
dataIndex: "clm_no",
|
||||
key: "clm_no",
|
||||
ellipsis: true,
|
||||
responsive: ["md"],
|
||||
sorter: (a, b) => alphaSort(a.clm_no, b.clm_no),
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "clm_no" && state.sortedInfo.order,
|
||||
render: (text, record) =>
|
||||
`${record.clm_no || ""}${
|
||||
record.po_number ? ` (PO: ${record.po_number})` : ""
|
||||
}`,
|
||||
},
|
||||
{
|
||||
title: t("jobs.fields.ins_co_nm"),
|
||||
dataIndex: "ins_co_nm",
|
||||
key: "ins_co_nm",
|
||||
ellipsis: true,
|
||||
filters:
|
||||
(jobs &&
|
||||
jobs
|
||||
.map((j) => j.ins_co_nm)
|
||||
.filter(onlyUnique)
|
||||
.map((s) => {
|
||||
return {
|
||||
text: s,
|
||||
value: [s],
|
||||
};
|
||||
})) ||
|
||||
[],
|
||||
onFilter: (value, record) => value.includes(record.ins_co_nm),
|
||||
responsive: ["md"],
|
||||
},
|
||||
{
|
||||
title: t("jobs.fields.clm_total"),
|
||||
dataIndex: "clm_total",
|
||||
key: "clm_total",
|
||||
responsive: ["md"],
|
||||
ellipsis: true,
|
||||
|
||||
sorter: (a, b) => a.clm_total - b.clm_total,
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "clm_total" && state.sortedInfo.order,
|
||||
render: (text, record) => (
|
||||
<CurrencyFormatter>{record.clm_total}</CurrencyFormatter>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t("jobs.labels.estimator"),
|
||||
dataIndex: "jobs.labels.estimator",
|
||||
key: "jobs.labels.estimator",
|
||||
ellipsis: true,
|
||||
responsive: ["xl"],
|
||||
filterSearch: true,
|
||||
filters:
|
||||
(jobs &&
|
||||
jobs
|
||||
.map((j) => `${j.est_ct_fn || ""} ${j.est_ct_ln || ""}`.trim())
|
||||
.filter(onlyUnique)
|
||||
.map((s) => {
|
||||
return {
|
||||
text: s || "N/A",
|
||||
value: [s],
|
||||
};
|
||||
})) ||
|
||||
[],
|
||||
onFilter: (value, record) =>
|
||||
value.includes(
|
||||
`${record.est_ct_fn || ""} ${record.est_ct_ln || ""}`.trim()
|
||||
),
|
||||
render: (text, record) =>
|
||||
`${record.est_ct_fn || ""} ${record.est_ct_ln || ""}`.trim(),
|
||||
},
|
||||
{
|
||||
title: t("jobs.fields.comment"),
|
||||
dataIndex: "comment",
|
||||
key: "comment",
|
||||
ellipsis: true,
|
||||
responsive: ["md"],
|
||||
},
|
||||
// {
|
||||
// title: t("jobs.fields.owner_owing"),
|
||||
// dataIndex: "owner_owing",
|
||||
// key: "owner_owing",
|
||||
// responsive: ["md"],
|
||||
// render: (text, record) => (
|
||||
// <CurrencyFormatter>{record.owner_owing}</CurrencyFormatter>
|
||||
// ),
|
||||
// },
|
||||
];
|
||||
|
||||
const scrollMapper = {
|
||||
xs: true,
|
||||
sm: true,
|
||||
md: true,
|
||||
lg: "100%",
|
||||
xl: "100%",
|
||||
xxl: "100%",
|
||||
};
|
||||
|
||||
return (
|
||||
<Card
|
||||
title={t("titles.bc.jobs-active")}
|
||||
extra={
|
||||
<Space wrap>
|
||||
<Button onClick={() => refetch()}>
|
||||
<SyncOutlined />
|
||||
</Button>
|
||||
<Input.Search
|
||||
placeholder={t("general.labels.search")}
|
||||
onChange={(e) => {
|
||||
setSearchText(e.target.value);
|
||||
}}
|
||||
value={searchText}
|
||||
enterButton
|
||||
/>
|
||||
</Space>
|
||||
}
|
||||
>
|
||||
<Table
|
||||
loading={loading}
|
||||
pagination={{ defaultPageSize: 50 }}
|
||||
columns={columns}
|
||||
rowKey="id"
|
||||
dataSource={jobs}
|
||||
scroll={{
|
||||
x: selectedBreakpoint ? scrollMapper[selectedBreakpoint[0]] : "100%",
|
||||
}}
|
||||
rowSelection={{
|
||||
onSelect: (record) => {
|
||||
handleOnRowClick(record);
|
||||
},
|
||||
selectedRowKeys: [selected],
|
||||
type: "radio",
|
||||
}}
|
||||
onChange={handleTableChange}
|
||||
onRow={(record, rowIndex) => {
|
||||
return {
|
||||
onClick: (event) => {
|
||||
handleOnRowClick(record);
|
||||
},
|
||||
};
|
||||
}}
|
||||
/>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, null)(JobsList);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user