Compare commits
1262 Commits
@bigcapita
...
nestjs-dto
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
66a2261e50 | ||
|
|
c51347d3ec | ||
|
|
b7a3c42074 | ||
|
|
83c9392b74 | ||
|
|
24bf3dd06d | ||
|
|
2b3f98d8fe | ||
|
|
4e64a9eadb | ||
|
|
0823bfc4e9 | ||
|
|
99fe5a6b0d | ||
|
|
ce058b9416 | ||
|
|
4de1ef71ca | ||
|
|
ecb80b2cf2 | ||
|
|
aef208b9d8 | ||
|
|
c096135d9f | ||
|
|
0c9d961272 | ||
|
|
c10cad4256 | ||
|
|
9ebd967fe7 | ||
|
|
a42143a996 | ||
|
|
7506c2f37f | ||
|
|
3c8b7c92fe | ||
|
|
f78d6efe27 | ||
|
|
401b3dc111 | ||
|
|
c9d752d102 | ||
|
|
4f6ad2b293 | ||
|
|
1d53063e09 | ||
|
|
51de3631fc | ||
|
|
b9755ff01c | ||
|
|
5bfff51093 | ||
|
|
1bcee9293c | ||
|
|
c953c48c39 | ||
|
|
ab49113d5a | ||
|
|
d851e5b646 | ||
|
|
e8f1fedf35 | ||
|
|
04c25bd31a | ||
|
|
6287f8b6e3 | ||
|
|
4febc4e502 | ||
|
|
443fbdd89e | ||
|
|
55fcc908ef | ||
|
|
f068218a16 | ||
|
|
842a862b87 | ||
|
|
1ed77dd5ed | ||
|
|
e47ca98171 | ||
|
|
503d0016ea | ||
|
|
0a2ac4ee56 | ||
|
|
8eb23d3a6f | ||
|
|
18017d25d5 | ||
|
|
f11b09cd87 | ||
|
|
ed81d4c1e0 | ||
|
|
88f66f1c1c | ||
|
|
ab717b96ac | ||
|
|
caff6ce47c | ||
|
|
682be715ae | ||
|
|
85946d3161 | ||
|
|
173610d0fa | ||
|
|
f20f07a42f | ||
|
|
6251831741 | ||
|
|
1cfddf2b4d | ||
|
|
6461a2318f | ||
|
|
92d98ce1d3 | ||
|
|
ef22b9ddaf | ||
|
|
4c42515613 | ||
|
|
2eb56e5850 | ||
|
|
136cc907bb | ||
|
|
fd65ee9428 | ||
|
|
08de50e2b1 | ||
|
|
197d173db9 | ||
|
|
cf496909a5 | ||
|
|
67ae7ad037 | ||
|
|
40b7daa2e3 | ||
|
|
b7d0b6c24a | ||
|
|
95bb4fc8e3 | ||
|
|
5c0bb52b59 | ||
|
|
36851d3209 | ||
|
|
9eee0b384d | ||
|
|
9539003cac | ||
|
|
2017539032 | ||
|
|
c4692d1716 | ||
|
|
7b81d0c8e5 | ||
|
|
9a5110aa38 | ||
|
|
2e1c57438c | ||
|
|
b46f2a91c3 | ||
|
|
8e36aab529 | ||
|
|
9eec60ea22 | ||
|
|
6550e88af3 | ||
|
|
dfc5674088 | ||
|
|
6dd854178d | ||
|
|
520d053b36 | ||
|
|
108d286f62 | ||
|
|
271c46ea3b | ||
|
|
7bcd578c11 | ||
|
|
936800600b | ||
|
|
e7e7a95aa1 | ||
|
|
081fdebee0 | ||
|
|
4ab20ac76a | ||
|
|
72818759a5 | ||
|
|
270b421a6c | ||
|
|
ddaea20d16 | ||
|
|
7e82080cb7 | ||
|
|
3bf5f4be86 | ||
|
|
6f870ea1e1 | ||
|
|
ee284196eb | ||
|
|
52362a43ab | ||
|
|
fdfb766587 | ||
|
|
1773df1858 | ||
|
|
abf92ac83f | ||
|
|
385d84d654 | ||
|
|
2bf58d9cb4 | ||
|
|
ba176394c8 | ||
|
|
1869ba216f | ||
|
|
b72f85b394 | ||
|
|
8bacf3a001 | ||
|
|
505c4b28a5 | ||
|
|
3ad34ba56f | ||
|
|
a819d6c1ba | ||
|
|
1b15261adb | ||
|
|
4938db704e | ||
|
|
3191076762 | ||
|
|
b046edf337 | ||
|
|
515a984714 | ||
|
|
77bbf6828d | ||
|
|
caf235e2b5 | ||
|
|
9f9b75cd31 | ||
|
|
736cedd63d | ||
|
|
cd84872a61 | ||
|
|
a6932d76f3 | ||
|
|
336171081e | ||
|
|
8a12caf48d | ||
|
|
cb8fd68d46 | ||
|
|
dc52f784b6 | ||
|
|
330192c042 | ||
|
|
1f32a7c59a | ||
|
|
83dfaa00fd | ||
|
|
93bf6d9d3d | ||
|
|
bfff56c470 | ||
|
|
87e9cd64e8 | ||
|
|
0a112c5655 | ||
|
|
70211980aa | ||
|
|
2ba31148ca | ||
|
|
1906d9f3f5 | ||
|
|
d640dc1f40 | ||
|
|
8cd1b36a02 | ||
|
|
5a8d9cc7e8 | ||
|
|
6323e2ffec | ||
|
|
7af2e7ccbc | ||
|
|
baf4c691d6 | ||
|
|
c633fa8522 | ||
|
|
1d54947764 | ||
|
|
477da0e7c0 | ||
|
|
b9963aa241 | ||
|
|
994c441bb8 | ||
|
|
0a5115fc20 | ||
|
|
11d7a40326 | ||
|
|
46719ef361 | ||
|
|
14ae978bde | ||
|
|
beec09788e | ||
|
|
391dc77071 | ||
|
|
38f2004e56 | ||
|
|
5a5eac246b | ||
|
|
a7bafd4f62 | ||
|
|
a25ab39647 | ||
|
|
7dd09e2903 | ||
|
|
6ab461a212 | ||
|
|
fabc88c81a | ||
|
|
3a19518440 | ||
|
|
56b5e3469e | ||
|
|
542763ddf5 | ||
|
|
1010d97a92 | ||
|
|
d5dacaa988 | ||
|
|
154ade9647 | ||
|
|
5b75fa9286 | ||
|
|
05cf94940e | ||
|
|
03b0d2519b | ||
|
|
000c3e40e1 | ||
|
|
ffb06f5194 | ||
|
|
73ab92e693 | ||
|
|
dd1392cdc8 | ||
|
|
17b3bbe1d8 | ||
|
|
e02ad1e795 | ||
|
|
df8391201f | ||
|
|
aa4aaeb612 | ||
|
|
09b98664c5 | ||
|
|
be46147d9a | ||
|
|
1fe7d58c8c | ||
|
|
4f57782be4 | ||
|
|
7b5f0d3930 | ||
|
|
831fb9180c | ||
|
|
2594e37dc7 | ||
|
|
ca44d6346d | ||
|
|
df41de7239 | ||
|
|
459bf4cd55 | ||
|
|
3537a05ea2 | ||
|
|
da47418f17 | ||
|
|
d5b0546301 | ||
|
|
b6f3c0145f | ||
|
|
c5c85bdfbe | ||
|
|
63a95df534 | ||
|
|
6103f1e4c7 | ||
|
|
3e591beb03 | ||
|
|
c6db54175f | ||
|
|
f7bf24acb3 | ||
|
|
ddffe630ff | ||
|
|
2c54092591 | ||
|
|
7e65f3f642 | ||
|
|
7df6aa4110 | ||
|
|
7df316aa56 | ||
|
|
53ab40a075 | ||
|
|
d115ebde12 | ||
|
|
0e99ac88d3 | ||
|
|
05f4b49b58 | ||
|
|
5d6f901d33 | ||
|
|
908bbb9fa6 | ||
|
|
6c1870be8f | ||
|
|
19080a67ab | ||
|
|
f5834c72c6 | ||
|
|
7ee3392d3e | ||
|
|
c58822fd6c | ||
|
|
ba8091d697 | ||
|
|
d668d60ed5 | ||
|
|
a34b7a2106 | ||
|
|
6f12127095 | ||
|
|
b4e5bbf376 | ||
|
|
a11530d190 | ||
|
|
43d4425da5 | ||
|
|
4c1909cb73 | ||
|
|
a7b6b7a03e | ||
|
|
1f46275bde | ||
|
|
aa7e5d4ae9 | ||
|
|
bb482df3ce | ||
|
|
f878786646 | ||
|
|
652851a1a9 | ||
|
|
850f4956cb | ||
|
|
94223b6ebf | ||
|
|
e9d34e19ad | ||
|
|
107532fe26 | ||
|
|
c32aff82ee | ||
|
|
de8a867d33 | ||
|
|
17a8aba23f | ||
|
|
04b601626b | ||
|
|
802775c118 | ||
|
|
b6baa80134 | ||
|
|
b2d0f2ed3c | ||
|
|
d23f33bae4 | ||
|
|
22ea557337 | ||
|
|
b3ebbb429c | ||
|
|
51218797af | ||
|
|
2d18a6573e | ||
|
|
2646ad5bc4 | ||
|
|
51aec8d8b3 | ||
|
|
638bd95d6f | ||
|
|
f2fcc3a649 | ||
|
|
48795748d8 | ||
|
|
6ba54a994a | ||
|
|
6687db4085 | ||
|
|
ba1d9b3f28 | ||
|
|
51905825fd | ||
|
|
bd5e33855a | ||
|
|
f7fbc0e31c | ||
|
|
cb06fa342c | ||
|
|
581229053a | ||
|
|
209da69b8f | ||
|
|
d09aebcebb | ||
|
|
0cc80bc179 | ||
|
|
79dcc592bc | ||
|
|
687111851a | ||
|
|
dbbaa387bd | ||
|
|
470bfd32f7 | ||
|
|
5fddd080fd | ||
|
|
e10c530b4b | ||
|
|
12189f018d | ||
|
|
0111b0e6ff | ||
|
|
0930b0428d | ||
|
|
289f40014e | ||
|
|
01cc0568f9 | ||
|
|
42ee8ed9fa | ||
|
|
1dae65cb74 | ||
|
|
ce40d67ea2 | ||
|
|
26088a71ee | ||
|
|
cadf6b81a0 | ||
|
|
728b4cacd9 | ||
|
|
b4d3ac2f96 | ||
|
|
bc8e440814 | ||
|
|
4c0f9a0aef | ||
|
|
c321d90575 | ||
|
|
03e6372f14 | ||
|
|
c0481f67ad | ||
|
|
b7f316d25a | ||
|
|
dffd818396 | ||
|
|
bbc19df6b4 | ||
|
|
c8c2786893 | ||
|
|
d79f26f1b5 | ||
|
|
32ba6f9a6c | ||
|
|
ccbb399685 | ||
|
|
f381d433ec | ||
|
|
2ee49f7496 | ||
|
|
bb299aa595 | ||
|
|
a66867463e | ||
|
|
de50b89e5c | ||
|
|
c4ee143354 | ||
|
|
a2227016e5 | ||
|
|
4d6f65b179 | ||
|
|
758ebbe261 | ||
|
|
279890e922 | ||
|
|
44fae36b82 | ||
|
|
fc2fac80af | ||
|
|
5ad9a9654b | ||
|
|
8a4034cc5d | ||
|
|
5649657bf0 | ||
|
|
c929a7cb27 | ||
|
|
eeedb789a9 | ||
|
|
321af8c271 | ||
|
|
fd4d86e797 | ||
|
|
49988e27a2 | ||
|
|
8c94ee5982 | ||
|
|
2e73a34fef | ||
|
|
ea7f987fe3 | ||
|
|
d55503f0c7 | ||
|
|
f59b8166b6 | ||
|
|
2c735d7edf | ||
|
|
5b5ab9fe1e | ||
|
|
28ac9b2d90 | ||
|
|
3300a6a499 | ||
|
|
152a22baa0 | ||
|
|
e873198238 | ||
|
|
68a0db91ee | ||
|
|
ddea7be24a | ||
|
|
b7b86bb0c5 | ||
|
|
817ef906dc | ||
|
|
863c7693fa | ||
|
|
cf78255220 | ||
|
|
f9aa6abdd7 | ||
|
|
0a5e40a0d8 | ||
|
|
4aa445fe35 | ||
|
|
1a1095c99b | ||
|
|
d92b46aa7b | ||
|
|
682d40cbf8 | ||
|
|
b62f3b3fa6 | ||
|
|
e76d3b15ce | ||
|
|
9edfb83221 | ||
|
|
bbdfe00c7a | ||
|
|
e3942551cd | ||
|
|
a0c1a21983 | ||
|
|
3358ce58bc | ||
|
|
3cd54653a8 | ||
|
|
6cad929738 | ||
|
|
184648040c | ||
|
|
df9d277e66 | ||
|
|
75ec315de2 | ||
|
|
c89b2367e6 | ||
|
|
bca5b3481c | ||
|
|
59996e7a40 | ||
|
|
af5726c48c | ||
|
|
90f08c5d51 | ||
|
|
a0a9f4a768 | ||
|
|
2649f1c326 | ||
|
|
c5ff1e4d4a | ||
|
|
c74c8e896a | ||
|
|
55fdc47ff0 | ||
|
|
126eb221d0 | ||
|
|
3c7e22be43 | ||
|
|
b23112bc92 | ||
|
|
cbc60b3c73 | ||
|
|
6caa1311fd | ||
|
|
cd0bbd11c3 | ||
|
|
2a944f8507 | ||
|
|
8a2754d9ce | ||
|
|
ace75f2dfa | ||
|
|
7ceb785c1b | ||
|
|
904a52f5a1 | ||
|
|
04fe65b176 | ||
|
|
7ac6e0d349 | ||
|
|
4ec3586173 | ||
|
|
4b6ab7035e | ||
|
|
3fe7babe00 | ||
|
|
f21570982e | ||
|
|
ad8fe52b84 | ||
|
|
15ce6ac710 | ||
|
|
783387dce6 | ||
|
|
863c7ad99f | ||
|
|
776b69475c | ||
|
|
6b6027a588 | ||
|
|
d465ee15bd | ||
|
|
be2049ca6e | ||
|
|
9b63c176cd | ||
|
|
e506a7ba35 | ||
|
|
2191ad0d40 | ||
|
|
ca162206a3 | ||
|
|
c5d7a2bfd8 | ||
|
|
b9506424d1 | ||
|
|
46a145ae58 | ||
|
|
e4044ef563 | ||
|
|
1cc71eb368 | ||
|
|
323b95de7b | ||
|
|
b0658be041 | ||
|
|
b222d56148 | ||
|
|
946872204b | ||
|
|
2f9adfd908 | ||
|
|
1c8e19378f | ||
|
|
37fd4a1fdb | ||
|
|
7aed3d9c8c | ||
|
|
b125e3e58b | ||
|
|
65788e344a | ||
|
|
abc242d117 | ||
|
|
6dd4968327 | ||
|
|
70bba4a6ed | ||
|
|
1570995021 | ||
|
|
8109236e72 | ||
|
|
9ba651decb | ||
|
|
eb5fdbf4ee | ||
|
|
9827a84857 | ||
|
|
3308133736 | ||
|
|
3129c76c30 | ||
|
|
c0a4c965f0 | ||
|
|
d16c57b63b | ||
|
|
e04f5d26a3 | ||
|
|
ad74007d58 | ||
|
|
6271d6c268 | ||
|
|
7756b5b304 | ||
|
|
8de8695b25 | ||
|
|
11c56c75a4 | ||
|
|
f5a1d68c52 | ||
|
|
0ae7a25c27 | ||
|
|
16eaacd4bc | ||
|
|
809973730f | ||
|
|
2ebb4595a8 | ||
|
|
77f628509c | ||
|
|
d2cd32a735 | ||
|
|
4665f529e6 | ||
|
|
df706d2573 | ||
|
|
5270e99de8 | ||
|
|
eb48f66f6e | ||
|
|
5e7cff0eb7 | ||
|
|
34e781b4a2 | ||
|
|
5f40d50852 | ||
|
|
bb0d91a9cb | ||
|
|
2c790427fa | ||
|
|
4f59b27d70 | ||
|
|
94c08f0b9e | ||
|
|
ef4beaa564 | ||
|
|
2b42215381 | ||
|
|
18d6ec7b59 | ||
|
|
430cf19533 | ||
|
|
542e61dbfc | ||
|
|
8566422ce3 | ||
|
|
9517b4e279 | ||
|
|
70551bee30 | ||
|
|
d690c6a3fe | ||
|
|
28319c2cdc | ||
|
|
df0f73f338 | ||
|
|
411ac55986 | ||
|
|
12226d469a | ||
|
|
632c4629de | ||
|
|
a7df23cebc | ||
|
|
ef74e250f1 | ||
|
|
c0769662bd | ||
|
|
5b6270a184 | ||
|
|
4541d28b68 | ||
|
|
716dec799a | ||
|
|
77a1e35ff4 | ||
|
|
317adfa0de | ||
|
|
f0dfc3d1b0 | ||
|
|
67904f52af | ||
|
|
f644ed6708 | ||
|
|
dc18bde6be | ||
|
|
132c1dfdbe | ||
|
|
162b92ce84 | ||
|
|
9247745ab0 | ||
|
|
c5c0342c7b | ||
|
|
f5e9485a12 | ||
|
|
a183666df6 | ||
|
|
e6bad27771 | ||
|
|
6d24474162 | ||
|
|
5962b990c4 | ||
|
|
9f21f649f0 | ||
|
|
d3d2112b8a | ||
|
|
3795322a65 | ||
|
|
fe5cd5a8ea | ||
|
|
c032a5db16 | ||
|
|
3fcb6fefde | ||
|
|
16f5cb713d | ||
|
|
f7a7925028 | ||
|
|
66fb0c9fa3 | ||
|
|
85acc85f17 | ||
|
|
c76ce09191 | ||
|
|
e3532098b2 | ||
|
|
b6783eb22e | ||
|
|
4ef00ab122 | ||
|
|
3dd827417a | ||
|
|
cbacd02aa2 | ||
|
|
3b7e0fb78a | ||
|
|
9add716395 | ||
|
|
083ea28a1f | ||
|
|
1b51742c36 | ||
|
|
0c6f23e770 | ||
|
|
37a8ca4e97 | ||
|
|
795303c3a8 | ||
|
|
63ba3f0898 | ||
|
|
62594efa00 | ||
|
|
9ac0dcbdc3 | ||
|
|
df588dc4ce | ||
|
|
d54f14a87a | ||
|
|
a4d4be54c1 | ||
|
|
ddd17e74b5 | ||
|
|
81c0761fbe | ||
|
|
0812e3087e | ||
|
|
0dd05493b2 | ||
|
|
bfb3909d26 | ||
|
|
266902026e | ||
|
|
791c4a4e9e | ||
|
|
2cfa9123b8 | ||
|
|
bb9614aafb | ||
|
|
302564a56e | ||
|
|
dcfc231d4d | ||
|
|
64080ed678 | ||
|
|
263935d91e | ||
|
|
7f5ffb8da1 | ||
|
|
f07d25edbe | ||
|
|
7c07d6b5ff | ||
|
|
e433f4ad68 | ||
|
|
a79b9caff6 | ||
|
|
2227cead66 | ||
|
|
410c4ea3e2 | ||
|
|
f92acbcbe0 | ||
|
|
c986585cd9 | ||
|
|
250f0a30ef | ||
|
|
84b5e1adc1 | ||
|
|
2ab28370db | ||
|
|
a88a525326 | ||
|
|
fce8e2c5a4 | ||
|
|
095608266c | ||
|
|
0ec8aaa330 | ||
|
|
9fcb3ef77d | ||
|
|
dc61c57daf | ||
|
|
af284f3f6d | ||
|
|
c43123db76 | ||
|
|
ebbcab3926 | ||
|
|
a235f573c0 | ||
|
|
84a0b8f495 | ||
|
|
b87321c897 | ||
|
|
fc6ebfea5c | ||
|
|
c9fe6d9b37 | ||
|
|
161d60393a | ||
|
|
79413fa85e | ||
|
|
58552c6c94 | ||
|
|
2072e35cfa | ||
|
|
1eaac9d691 | ||
|
|
a916e8a0cb | ||
|
|
a56f560036 | ||
|
|
0fb886936c | ||
|
|
670136916f | ||
|
|
768297f137 | ||
|
|
ef505a0a62 | ||
|
|
42d40620ec | ||
|
|
ee2d8d3065 | ||
|
|
959ef7a691 | ||
|
|
60f03f534b | ||
|
|
e44ebb700a | ||
|
|
8e94c7a755 | ||
|
|
3a2ca36c07 | ||
|
|
88ece74c8a | ||
|
|
67a8610328 | ||
|
|
278d61ce61 | ||
|
|
e72d6ad6b8 | ||
|
|
bf66b31679 | ||
|
|
b4d426d2e8 | ||
|
|
7f7dd270e7 | ||
|
|
f6bad8fe30 | ||
|
|
820b363f79 | ||
|
|
07740a51ef | ||
|
|
d15fb6fe19 | ||
|
|
5749ccec81 | ||
|
|
4a99f6c0cf | ||
|
|
59f480f9d5 | ||
|
|
6cb9c919b5 | ||
|
|
1062b65b5b | ||
|
|
bf3a70cabd | ||
|
|
f46cd28f87 | ||
|
|
dffcfe50aa | ||
|
|
fac55efbc7 | ||
|
|
8b90ce5f6c | ||
|
|
705b8da053 | ||
|
|
4a05ccc692 | ||
|
|
3200d65d90 | ||
|
|
3f23038227 | ||
|
|
408c807fc2 | ||
|
|
d29079a8c5 | ||
|
|
cca596b4a9 | ||
|
|
b768f18294 | ||
|
|
fed620505d | ||
|
|
a008aea3f3 | ||
|
|
25297bc191 | ||
|
|
1989887b25 | ||
|
|
e4fb126d39 | ||
|
|
5fcb2d9cc9 | ||
|
|
06ea631732 | ||
|
|
2f21107a43 | ||
|
|
fb8118bea8 | ||
|
|
4ba1c0aa22 | ||
|
|
169f115fa0 | ||
|
|
61ab2b78d9 | ||
|
|
93732430fc | ||
|
|
0215206220 | ||
|
|
3c8956fedf | ||
|
|
4477ada1ad | ||
|
|
fde9ccc5ca | ||
|
|
bbbd96f159 | ||
|
|
9f4de8115f | ||
|
|
d9f241a2f8 | ||
|
|
ee96dc68cc | ||
|
|
b12f090d13 | ||
|
|
f6ce761a27 | ||
|
|
8c89e04f54 | ||
|
|
f97b127a69 | ||
|
|
5c1fa8f5cd | ||
|
|
64c4d7b5a4 | ||
|
|
ea2fad648b | ||
|
|
9b8b51cb91 | ||
|
|
09b7e74d65 | ||
|
|
7137e06d99 | ||
|
|
fc29b765f7 | ||
|
|
2946475f89 | ||
|
|
d2193fdac0 | ||
|
|
ec2b7e332e | ||
|
|
a3704df6dd | ||
|
|
c3c784e52c | ||
|
|
bbcf695a6c | ||
|
|
038d4dd5a7 | ||
|
|
961e4b99e8 | ||
|
|
9991eebaaf | ||
|
|
cd90fede54 | ||
|
|
a2d28648bd | ||
|
|
3097d05eda | ||
|
|
ff94d8d9b2 | ||
|
|
79cc09fad9 | ||
|
|
c1b29c3f23 | ||
|
|
cf4bb3007e | ||
|
|
193a86cf30 | ||
|
|
7a81f14eb2 | ||
|
|
14d1f0bd1d | ||
|
|
82f8648c59 | ||
|
|
c928940d32 | ||
|
|
0a78d56015 | ||
|
|
1a5716873e | ||
|
|
01b7c86ab9 | ||
|
|
0ca209b195 | ||
|
|
be6f6e3c73 | ||
|
|
cb016be78c | ||
|
|
fc085f2328 | ||
|
|
9a34f3e283 | ||
|
|
7054e862d5 | ||
|
|
faa81abee4 | ||
|
|
6d01f2a323 | ||
|
|
72678bb936 | ||
|
|
df8b68fda6 | ||
|
|
9ae5644af9 | ||
|
|
e8830c5911 | ||
|
|
7699889bd6 | ||
|
|
35a061d188 | ||
|
|
c7c021c969 | ||
|
|
be8352654e | ||
|
|
fb58ab8cc1 | ||
|
|
8da89ebe8b | ||
|
|
d43d46ebec | ||
|
|
ac3a514795 | ||
|
|
f67c63a4fa | ||
|
|
0025dcf8d4 | ||
|
|
81995dc94f | ||
|
|
3fcc70c1d8 | ||
|
|
a986c7a250 | ||
|
|
37e25a8061 | ||
|
|
cfba628465 | ||
|
|
3d200f4d7d | ||
|
|
f21b01b1d6 | ||
|
|
3cbcfac333 | ||
|
|
cc21e1856f | ||
|
|
efd0e1e225 | ||
|
|
521b083ed7 | ||
|
|
a09fe26df7 | ||
|
|
c7a85c4cf8 | ||
|
|
f6350d3d61 | ||
|
|
64c0732e5f | ||
|
|
8e99a31455 | ||
|
|
6d0d0689e1 | ||
|
|
9836129e49 | ||
|
|
86631ea8c3 | ||
|
|
475ccd4903 | ||
|
|
8608144ec1 | ||
|
|
f9cf6d325a | ||
|
|
fc0240c692 | ||
|
|
b84675325f | ||
|
|
647bed5c67 | ||
|
|
00f5bb1d73 | ||
|
|
208800b411 | ||
|
|
5e12a4cea4 | ||
|
|
fdf3e34f1c | ||
|
|
d74337fb94 | ||
|
|
8cab012324 | ||
|
|
940b4f9175 | ||
|
|
5d0dd1fe3f | ||
|
|
ded4e2bb59 | ||
|
|
219e6fb466 | ||
|
|
7147e230de | ||
|
|
5ce11f192f | ||
|
|
71e865e9b7 | ||
|
|
590506f183 | ||
|
|
bed281a637 | ||
|
|
47dd767b3a | ||
|
|
8623b69991 | ||
|
|
a1ddc81dac | ||
|
|
832cdacebf | ||
|
|
9f979080b6 | ||
|
|
7f7301b31e | ||
|
|
6affbedef4 | ||
|
|
ba7f32c1bf | ||
|
|
305ce29ebb | ||
|
|
4cd0405078 | ||
|
|
783102449f | ||
|
|
ae617b2e1d | ||
|
|
9090d0a7b2 | ||
|
|
ffc55fa81b | ||
|
|
07c57ed539 | ||
|
|
788150f80d | ||
|
|
c4e77e4e3b | ||
|
|
c09384e49b | ||
|
|
4490c2d4b4 | ||
|
|
e11f1a95f6 | ||
|
|
b91273eee4 | ||
|
|
b5d570417b | ||
|
|
acd3265e35 | ||
|
|
894c899847 | ||
|
|
f6d4ec504f | ||
|
|
1a01461f5d | ||
|
|
f5e18fc1fe | ||
|
|
f64cd32985 | ||
|
|
89552d7ee2 | ||
|
|
4345623ea9 | ||
|
|
f457759e39 | ||
|
|
14d5e82b4a | ||
|
|
333b6f5a4b | ||
|
|
1660df20af | ||
|
|
14a9c4ba28 | ||
|
|
383be111fa | ||
|
|
7720b1cc34 | ||
|
|
db634cbb79 | ||
|
|
53f37f4f48 | ||
|
|
0a7b522b87 | ||
|
|
9e6500ac79 | ||
|
|
b93cb546f4 | ||
|
|
6d17f9cbeb | ||
|
|
998e6de211 | ||
|
|
6fb02f9869 | ||
|
|
449390143d | ||
|
|
51471ed000 | ||
|
|
fe214b1b2d | ||
|
|
6b6b73b77c | ||
|
|
d805703c08 | ||
|
|
c2815afbe3 | ||
|
|
fa7e6b1fca | ||
|
|
107a6f793b | ||
|
|
67d155759e | ||
|
|
7e2e87256f | ||
|
|
df7790d7c1 | ||
|
|
72128a72c4 | ||
|
|
eb3f23554f | ||
|
|
69ddf43b3e | ||
|
|
249eadaeaa | ||
|
|
59168bc691 | ||
|
|
81b26c6f13 | ||
|
|
da435d85d9 | ||
|
|
533006b90e | ||
|
|
d096e49d45 | ||
|
|
73acdb6240 | ||
|
|
38d4122d11 | ||
|
|
24a77c81b3 | ||
|
|
7f41b4280e | ||
|
|
aa89653967 | ||
|
|
b80bc95fa5 | ||
|
|
9a5befbee7 | ||
|
|
b7487f19d3 | ||
|
|
cd9039fe16 | ||
|
|
87f60f7461 | ||
|
|
202179ec0b | ||
|
|
168883a933 | ||
|
|
f62ec83e29 | ||
|
|
eff8b41720 | ||
|
|
632cc3d72e | ||
|
|
aefdaac68d | ||
|
|
b8a0a5509d | ||
|
|
a5eb42edaf | ||
|
|
67b519db61 | ||
|
|
91730d204e | ||
|
|
8a09de9771 | ||
|
|
50861940a8 | ||
|
|
55caf037cd | ||
|
|
c95eec565d | ||
|
|
79616cf1eb | ||
|
|
c27458ebcc | ||
|
|
da0fab9a58 | ||
|
|
5bbcb7913d | ||
|
|
48ff93b6ab | ||
|
|
f816e7f25c | ||
|
|
3cd66ba4d6 | ||
|
|
5d5d4a1972 | ||
|
|
b01528c06b | ||
|
|
cb1f587637 | ||
|
|
978ce6c441 | ||
|
|
fab22c9820 | ||
|
|
7edf268e75 | ||
|
|
87bf29f28c | ||
|
|
d305c7ad32 | ||
|
|
7a9c7209bc | ||
|
|
d2d37820f5 | ||
|
|
1889969191 | ||
|
|
8c2888fcd8 | ||
|
|
47879d04b2 | ||
|
|
f1f52ce972 | ||
|
|
dad8aeaff1 | ||
|
|
5aae45c8a8 | ||
|
|
3e437a041c | ||
|
|
e783cfeafa | ||
|
|
5dde7f5584 | ||
|
|
8e0911ec85 | ||
|
|
66d2d6a612 | ||
|
|
8dc2b18707 | ||
|
|
589b29bbdd | ||
|
|
ca403872b3 | ||
|
|
738a84bb4b | ||
|
|
b37002bea6 | ||
|
|
b6deb842ff | ||
|
|
d3230767dd | ||
|
|
6c4b0cdac5 | ||
|
|
0b5cee070a | ||
|
|
906835c396 | ||
|
|
7b4afd3859 | ||
|
|
590715037b | ||
|
|
1e53a8e85e | ||
|
|
2ad77103ac | ||
|
|
c1fc70863b | ||
|
|
125dff8376 | ||
|
|
84da7b7df5 | ||
|
|
4c82f6f8ad | ||
|
|
0d7aad5448 | ||
|
|
74b74a2722 | ||
|
|
3a0a0db8a7 | ||
|
|
265ea9ca48 | ||
|
|
cfd37f8894 | ||
|
|
d1caa5c5ce | ||
|
|
d998d716b7 | ||
|
|
031ccc4a0b | ||
|
|
e4f61823b3 | ||
|
|
1cbc1c056f | ||
|
|
4d4ef54c56 | ||
|
|
f7fcfefc78 | ||
|
|
858f347fd4 | ||
|
|
4d73b59cf3 | ||
|
|
bc67f0cca8 | ||
|
|
ef2d1ff141 | ||
|
|
dc4cdb2a8f | ||
|
|
8862810706 | ||
|
|
3dadbeac4d | ||
|
|
494d2c1fe0 | ||
|
|
d27562bd43 | ||
|
|
8b99e0938d | ||
|
|
94192bfc29 | ||
|
|
708a4dda9e | ||
|
|
10fcf94c92 | ||
|
|
fc9995c4da | ||
|
|
7dc769004d | ||
|
|
5dbfd36415 | ||
|
|
044f11ff74 | ||
|
|
6afe1a09c6 | ||
|
|
909a70e2c5 | ||
|
|
84dd0fa86b | ||
|
|
a4719fe15b | ||
|
|
fd915b503f | ||
|
|
bbba54c08e | ||
|
|
f241e2bede | ||
|
|
175bc243f3 | ||
|
|
7c06c8bb8a | ||
|
|
8fd930caac | ||
|
|
e175307da4 | ||
|
|
b1bf932e88 | ||
|
|
aa897212ab | ||
|
|
890903e08b | ||
|
|
16b2a33cf6 | ||
|
|
382d4ab028 | ||
|
|
85f26e1079 | ||
|
|
1b237323f6 | ||
|
|
f15fecde54 | ||
|
|
79be4266bb | ||
|
|
08ad117331 | ||
|
|
958f78e7a4 | ||
|
|
ba77351e44 | ||
|
|
09a15966f0 | ||
|
|
7ff36e8c4f | ||
|
|
8ed24748ec | ||
|
|
6a6dcadaf9 | ||
|
|
308a4f62ae | ||
|
|
6e50de1d28 | ||
|
|
ceb133e29a | ||
|
|
e7871e34a9 | ||
|
|
cfdbcea9c0 | ||
|
|
fcd61c6159 | ||
|
|
2244cc6116 | ||
|
|
15dbc4137c | ||
|
|
8f904fae3a | ||
|
|
c8f31f33be | ||
|
|
dc5bdf0b66 | ||
|
|
fe41f7976d | ||
|
|
2c7da86a00 | ||
|
|
8c2b9fba29 | ||
|
|
7208b8fab5 | ||
|
|
0836fe14e0 | ||
|
|
1227111fae | ||
|
|
2ada57a2b4 | ||
|
|
e380c598d3 | ||
|
|
370a8a4b91 | ||
|
|
c9ba9500cc | ||
|
|
23d27cafc1 | ||
|
|
92e3d31360 | ||
|
|
8aefa7709c | ||
|
|
3020295841 | ||
|
|
7f31a48755 | ||
|
|
8c0ef61038 | ||
|
|
76bb82f2b4 | ||
|
|
4c0dc276dd | ||
|
|
a69c4b4067 | ||
|
|
d81e544e82 | ||
|
|
6eeda23559 | ||
|
|
cd046cbe27 | ||
|
|
9b7bc1e5b9 | ||
|
|
987341ed29 | ||
|
|
d7cad17f1b | ||
|
|
dd02ae471e | ||
|
|
a5bfb0b02b | ||
|
|
f4440c9a03 | ||
|
|
cb88c234d1 | ||
|
|
f6a0476fb4 | ||
|
|
63cdc45295 | ||
|
|
83a5010dc5 | ||
|
|
55aab76c9b | ||
|
|
495941f43a | ||
|
|
00a1e070c6 | ||
|
|
fab71d2b65 | ||
|
|
9504bb5ccd | ||
|
|
7e89966f20 | ||
|
|
8a96c41258 | ||
|
|
4a713980bf | ||
|
|
2a1cbf6ced | ||
|
|
9103b60653 | ||
|
|
b9fc0cdd9e | ||
|
|
4368c18479 | ||
|
|
7e5c6b6487 | ||
|
|
b7214044bb | ||
|
|
93cb3615c3 | ||
|
|
7abfa6a162 | ||
|
|
1372a1f0a8 | ||
|
|
484024ec28 | ||
|
|
46639c7b86 | ||
|
|
d10d1654c1 | ||
|
|
2f06070ecb | ||
|
|
deefdb9bfd | ||
|
|
3cc62d80de | ||
|
|
4962c5d4d3 | ||
|
|
571a332658 | ||
|
|
b44c318a5d | ||
|
|
bd9717f4dc | ||
|
|
f48aea8e5a | ||
|
|
0ac3a5dea9 | ||
|
|
56b40ad4cb | ||
|
|
9b6f934990 | ||
|
|
80e3522f8a | ||
|
|
7975643765 | ||
|
|
2ac7f86bdb | ||
|
|
956b9b6812 | ||
|
|
60248ec3f6 | ||
|
|
9d3f1541eb | ||
|
|
9b5f1a36ab | ||
|
|
8ee691e1ed | ||
|
|
f9cb14da9e | ||
|
|
5e87581f4e | ||
|
|
8ca9cf39da | ||
|
|
9001fea524 | ||
|
|
dea0d71732 | ||
|
|
c191c4bd26 | ||
|
|
47d82ce591 | ||
|
|
9321db2a3a | ||
|
|
e486333c96 | ||
|
|
a9748b23c0 | ||
|
|
693ae61141 | ||
|
|
9807ac04b0 | ||
|
|
bddfde4138 | ||
|
|
a39dcd00d5 | ||
|
|
4d616e9287 | ||
|
|
dc52fb1de5 | ||
|
|
21a1777424 | ||
|
|
16b721db91 | ||
|
|
079491823d | ||
|
|
f7a87a6e9c | ||
|
|
e0cdf42980 | ||
|
|
ee56653f4b | ||
|
|
2310b09778 | ||
|
|
0684e50ebd | ||
|
|
aaa8f39e50 | ||
|
|
af981ce630 | ||
|
|
a1f8417b5d | ||
|
|
086b060351 | ||
|
|
bbafdcd8bd | ||
|
|
dd9098bdc1 | ||
|
|
3851d34ba4 | ||
|
|
b9651f30d5 | ||
|
|
45b5fb4088 | ||
|
|
aa64bcf69b | ||
|
|
cbd867b334 | ||
|
|
1a8ca83786 | ||
|
|
80c14ba1a0 | ||
|
|
785045dbad | ||
|
|
291301c1e3 | ||
|
|
824e4e13d1 | ||
|
|
74da28b464 | ||
|
|
22a016b56e | ||
|
|
040f016273 | ||
|
|
8ab809fc71 | ||
|
|
f9e5028e0d | ||
|
|
7a3e121942 | ||
|
|
fc1d123c6b | ||
|
|
ad4e51d81d | ||
|
|
973d1832bd | ||
|
|
858e3541cb | ||
|
|
a5ab535d3b | ||
|
|
1d8cec5069 | ||
|
|
2baa667c5d | ||
|
|
aba06991d4 | ||
|
|
ff5730d8a7 | ||
|
|
a27c877321 | ||
|
|
c5063fc5b5 | ||
|
|
ab4c0ab7a7 | ||
|
|
084d9d3d10 | ||
|
|
daa1e3a6bd | ||
|
|
4270d66928 | ||
|
|
90b4f3ef6d | ||
|
|
1fc6445123 | ||
|
|
b1d5390bfc | ||
|
|
1ba26a3b85 | ||
|
|
2c98376162 | ||
|
|
b71c79fef5 | ||
|
|
2baf407814 | ||
|
|
83fbb7225d | ||
|
|
b9a00418fa | ||
|
|
62d3e386dd | ||
|
|
d87d674aba | ||
|
|
a17b4f6a8a | ||
|
|
db839137d0 | ||
|
|
b602f28696 | ||
|
|
68f2f4ee84 | ||
|
|
f23e8d98f6 | ||
|
|
9db03350e0 | ||
|
|
0273714a07 | ||
|
|
ea497bfdea | ||
|
|
685a6150e6 | ||
|
|
daf87a8ec7 | ||
|
|
5b4ddadcf6 | ||
|
|
ea8c5458ff | ||
|
|
0833baabda | ||
|
|
ab7eb40ea9 | ||
|
|
00e6c141ee | ||
|
|
0aea339c1c | ||
|
|
2cf75e0136 | ||
|
|
5a8c394ad7 | ||
|
|
71742c3480 | ||
|
|
b340776278 | ||
|
|
6ea870edb2 | ||
|
|
dd072f8585 | ||
|
|
ecfdba9634 | ||
|
|
5be3ca75f4 | ||
|
|
7239b0ec7e | ||
|
|
2d3544fe37 | ||
|
|
3faeca20a9 | ||
|
|
b713b6922b | ||
|
|
4e40009ba0 | ||
|
|
e09f8e4334 | ||
|
|
1752c32eec | ||
|
|
d16ec7cda9 | ||
|
|
f6f6bc31b6 | ||
|
|
c06a8d9ca3 | ||
|
|
3509026ad8 | ||
|
|
465bb66d6b | ||
|
|
06e78db49d | ||
|
|
d2e6cc0036 | ||
|
|
7690789031 | ||
|
|
27fed5f18a | ||
|
|
1fd8a53ed1 | ||
|
|
d67189587e | ||
|
|
d229378957 | ||
|
|
eb4491f44a | ||
|
|
09ad725a67 | ||
|
|
b11c531cf5 | ||
|
|
0868eeaa0e | ||
|
|
9395ef094a | ||
|
|
a8671a8d99 | ||
|
|
cd8f64dfdc | ||
|
|
17dbe9713b | ||
|
|
706a324121 | ||
|
|
ecaf8c99bb | ||
|
|
90bde20674 | ||
|
|
0c61f85707 | ||
|
|
0f678e61c5 | ||
|
|
374f1acf8a | ||
|
|
ada1428193 | ||
|
|
528d447443 | ||
|
|
d6d67b9a51 | ||
|
|
12740223a8 | ||
|
|
f7f77b12c9 | ||
|
|
a6db4fb6df | ||
|
|
b38020d397 | ||
|
|
d090d5a026 | ||
|
|
7b5287ee80 | ||
|
|
00d9bc537c | ||
|
|
c688190acc | ||
|
|
2e0b3d0d5e | ||
|
|
6d888060d3 | ||
|
|
299a943153 | ||
|
|
e0ddcb022a | ||
|
|
b940c6dd17 | ||
|
|
b43cd26ecc | ||
|
|
b9886cfac3 | ||
|
|
ba3ea93a2d | ||
|
|
6b77bda77f | ||
|
|
ba387e81f7 | ||
|
|
0414c090ea | ||
|
|
a52f3a933f | ||
|
|
d01eacf8d9 | ||
|
|
0da151faaa | ||
|
|
f93c8b46dc | ||
|
|
59c5c8979d | ||
|
|
74a07847a4 | ||
|
|
1740226294 | ||
|
|
1b20d1b073 | ||
|
|
ac7175d83b | ||
|
|
d72d8e60d6 | ||
|
|
de5920f910 | ||
|
|
475c4e9967 | ||
|
|
63708ae839 | ||
|
|
760dbc6cfc | ||
|
|
9d4e7cec9e | ||
|
|
21eb88ef53 | ||
|
|
c8e7a2c7d9 | ||
|
|
429159acf9 | ||
|
|
7eb84474a5 | ||
|
|
e42adcae63 | ||
|
|
abffdd1029 | ||
|
|
d052c23560 | ||
|
|
f02afd3c9f | ||
|
|
3df17390e2 | ||
|
|
df38f8893d | ||
|
|
8882bc677e | ||
|
|
f03d01113c | ||
|
|
8f431597d9 | ||
|
|
03bc78a068 | ||
|
|
ecf5d60db0 | ||
|
|
3a5fd2782a | ||
|
|
1d8416ebfe | ||
|
|
3672abe7a5 | ||
|
|
2753908b83 | ||
|
|
8495990ec2 | ||
|
|
471ce1b7af | ||
|
|
4a920176f4 | ||
|
|
74fd76ce77 | ||
|
|
c9f57d9a75 | ||
|
|
6c9810ea4c | ||
|
|
1e4b29f83c | ||
|
|
2b03ac7f16 | ||
|
|
2cb8c2932f | ||
|
|
2814d23f75 | ||
|
|
35fa2389e1 | ||
|
|
c18b450129 | ||
|
|
8db6e9f049 | ||
|
|
b63a2006d3 | ||
|
|
8c1ca503bd | ||
|
|
0ac79e661c | ||
|
|
66ba4d35aa | ||
|
|
46d25dff4c | ||
|
|
5ef99f2cb3 | ||
|
|
7aee76e461 | ||
|
|
79f3f1b63d | ||
|
|
26e104b9f1 | ||
|
|
c71836ec27 | ||
|
|
60b1bc9ed7 | ||
|
|
e6a3daa2c3 | ||
|
|
276ef1c907 | ||
|
|
83e48cce42 | ||
|
|
99ca683d13 | ||
|
|
5062d891e1 | ||
|
|
92d1fc670b | ||
|
|
ab7abfea35 | ||
|
|
0d15c16d40 | ||
|
|
2a85fe2f3c | ||
|
|
dc762567b5 | ||
|
|
a676e09537 | ||
|
|
c46948049c | ||
|
|
de1b7f132c | ||
|
|
90b552d5c9 | ||
|
|
39115b3000 | ||
|
|
3c8c3d8253 | ||
|
|
657400c671 | ||
|
|
6356cb5e63 | ||
|
|
13c6e7a62d | ||
|
|
b6d99b1d4b | ||
|
|
f0e15d43d3 | ||
|
|
50d5ddba8e | ||
|
|
d2c63878ed | ||
|
|
ef52eaf91a | ||
|
|
cd71900bdd | ||
|
|
34e97fc56b | ||
|
|
e6e119682b | ||
|
|
e096db2ab8 | ||
|
|
cdff641cdb | ||
|
|
d0126ffc2c | ||
|
|
fe043eb594 | ||
|
|
eb1ff7c151 | ||
|
|
a862ee9ccf | ||
|
|
6953f7c4a3 | ||
|
|
ad53ddb9dd | ||
|
|
cfd4540a65 | ||
|
|
217321380a | ||
|
|
33e5d1a979 | ||
|
|
e5bcb1c19a | ||
|
|
bf2dc844d2 | ||
|
|
c9d3a3cd7c | ||
|
|
33afa3af64 | ||
|
|
fb70c94465 | ||
|
|
0f39cfb3af | ||
|
|
7fc1320834 | ||
|
|
6091bc73cf | ||
|
|
30b5f8120a | ||
|
|
5a958cc9fa | ||
|
|
d15c5890ed | ||
|
|
151aff4c8e | ||
|
|
b167284c8e | ||
|
|
b75d44a3dd | ||
|
|
f0f0045353 | ||
|
|
50164873ce | ||
|
|
a2cbab0bc3 | ||
|
|
5027f9fe5d | ||
|
|
eeb67d4005 | ||
|
|
a3d9e8ef2b | ||
|
|
121d992b68 | ||
|
|
6634144d82 | ||
|
|
078a7ea51c | ||
|
|
e070ac72dd | ||
|
|
08ac5f4b01 | ||
|
|
e4a7f09dbc | ||
|
|
2c5537efad | ||
|
|
017908600e | ||
|
|
6307ca8935 | ||
|
|
9531730d7a | ||
|
|
1ed1c9ea1d | ||
|
|
d40de4d22b | ||
|
|
ee62e3e1c2 | ||
|
|
5df454dd30 | ||
|
|
07628ddc37 | ||
|
|
69afa07e3b | ||
|
|
b1a043f699 |
@@ -60,6 +60,114 @@
|
||||
"contributions": [
|
||||
"bug"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "kochie",
|
||||
"name": "Robert Koch",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/10809884?v=4",
|
||||
"profile": "https://me.kochie.io",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "cschuijt",
|
||||
"name": "Casper Schuijt",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/5460015?v=4",
|
||||
"profile": "http://cschuijt.nl",
|
||||
"contributions": [
|
||||
"bug"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "ANasouf",
|
||||
"name": "ANasouf",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/19536487?v=4",
|
||||
"profile": "https://github.com/ANasouf",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "xprnio",
|
||||
"name": "Ragnar Laud",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/3042904?v=4",
|
||||
"profile": "https://ragnarlaud.dev",
|
||||
"contributions": [
|
||||
"bug"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "asenawritescode",
|
||||
"name": "Asena",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/67445192?v=4",
|
||||
"profile": "https://github.com/asenawritescode",
|
||||
"contributions": [
|
||||
"bug"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "benpsnyder",
|
||||
"name": "Ben Snyder",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/707567?v=4",
|
||||
"profile": "https://snyder.tech",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "cloudsbird",
|
||||
"name": "Vederis Leunardus",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/13505006?v=4",
|
||||
"profile": "http://vederis.id",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "ccantrell72",
|
||||
"name": "Chris Cantrell",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/104120598?v=4",
|
||||
"profile": "http://www.pivoten.com",
|
||||
"contributions": [
|
||||
"bug"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "oleynikd",
|
||||
"name": "Denis",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/3976868?v=4",
|
||||
"profile": "https://github.com/oleynikd",
|
||||
"contributions": [
|
||||
"bug"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "mittalsam98",
|
||||
"name": "Sachin Mittal",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/42431274?v=4",
|
||||
"profile": "https://myself.vercel.app/",
|
||||
"contributions": [
|
||||
"bug"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Champetaman",
|
||||
"name": "Camilo Oviedo",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/64604272?v=4",
|
||||
"profile": "https://www.camilooviedo.com/",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "nklmantey",
|
||||
"name": "Mantey",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/90279429?v=4",
|
||||
"profile": "https://nklmantey.com/",
|
||||
"contributions": [
|
||||
"bug"
|
||||
]
|
||||
}
|
||||
],
|
||||
"contributorsPerLine": 7,
|
||||
|
||||
57
.env.example
57
.env.example
@@ -1,3 +1,6 @@
|
||||
# App
|
||||
APP_JWT_SECRET=123123
|
||||
|
||||
# Mail
|
||||
MAIL_HOST=
|
||||
MAIL_USERNAME=
|
||||
@@ -8,7 +11,7 @@ MAIL_FROM_NAME=
|
||||
MAIL_FROM_ADDRESS=
|
||||
|
||||
# Database
|
||||
DB_HOST=mysql
|
||||
DB_HOST=localhost
|
||||
DB_USER=bigcapital
|
||||
DB_PASSWORD=bigcapital
|
||||
DB_ROOT_PASSWORD=root
|
||||
@@ -48,5 +51,55 @@ SIGNUP_DISABLED=false
|
||||
SIGNUP_ALLOWED_DOMAINS=
|
||||
SIGNUP_ALLOWED_EMAILS=
|
||||
|
||||
# Sign-up Email Confirmation
|
||||
SIGNUP_EMAIL_CONFIRMATION=false
|
||||
|
||||
# API rate limit (points,duration,block duration).
|
||||
API_RATE_LIMIT=120,60,600
|
||||
API_RATE_LIMIT=120,60,600
|
||||
|
||||
# Gotenberg API for PDF printing - (production).
|
||||
GOTENBERG_URL=http://gotenberg:3000
|
||||
GOTENBERG_DOCS_URL=http://server:3000/public/
|
||||
|
||||
# Gotenberg API - (development)
|
||||
# GOTENBERG_URL=http://localhost:9000
|
||||
# GOTENBERG_DOCS_URL=http://host.docker.internal:3000/public/
|
||||
|
||||
# Exchange Rate Service
|
||||
EXCHANGE_RATE_SERVICE=open-exchange-rate
|
||||
|
||||
# Open Exchange Rate
|
||||
OPEN_EXCHANGE_RATE_APP_ID=
|
||||
|
||||
# The Plaid environment to use ('sandbox' or 'development').
|
||||
# https://plaid.com/docs/#api-host
|
||||
PLAID_ENV=sandbox
|
||||
|
||||
# Your Plaid keys, which can be found in the Plaid Dashboard.
|
||||
# https://dashboard.plaid.com/account/keys
|
||||
PLAID_CLIENT_ID=
|
||||
PLAID_SECRET=
|
||||
PLAID_LINK_WEBHOOK=
|
||||
|
||||
# https://docs.lemonsqueezy.com/guides/developer-guide/getting-started#create-an-api-key
|
||||
LEMONSQUEEZY_API_KEY=
|
||||
LEMONSQUEEZY_STORE_ID=
|
||||
LEMONSQUEEZY_WEBHOOK_SECRET=
|
||||
|
||||
# S3 documents and attachments
|
||||
S3_REGION=US
|
||||
S3_ACCESS_KEY_ID=
|
||||
S3_SECRET_ACCESS_KEY=
|
||||
S3_ENDPOINT=
|
||||
S3_BUCKET=
|
||||
|
||||
# PostHog
|
||||
POSTHOG_API_KEY=
|
||||
POSTHOG_HOST=
|
||||
|
||||
# Stripe Payment
|
||||
STRIPE_PAYMENT_SECRET_KEY=
|
||||
STRIPE_PAYMENT_PUBLISHABLE_KEY=
|
||||
STRIPE_PAYMENT_CLIENT_ID=
|
||||
STRIPE_PAYMENT_WEBHOOKS_SECRET=
|
||||
STRIPE_PAYMENT_REDIRECT_URL=
|
||||
94
.github/workflows/build-deploy-container.yml
vendored
94
.github/workflows/build-deploy-container.yml
vendored
@@ -6,43 +6,66 @@ on:
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
REGISTRY: ghcr.io
|
||||
WEBAPP_IMAGE_NAME: bigcapital/bigcapital-webapp
|
||||
SERVER_IMAGE_NAME: bigcapital/bigcapital-server
|
||||
WEBAPP_IMAGE_NAME: bigcapitalhq/webapp
|
||||
SERVER_IMAGE_NAME: bigcapitalhq/server
|
||||
|
||||
jobs:
|
||||
build-publish-webapp:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
name: Build and deploy webapp container
|
||||
runs-on: ubuntu-latest
|
||||
environment: production
|
||||
steps:
|
||||
- name: Prepare
|
||||
run: |
|
||||
platform=${{ matrix.platform }}
|
||||
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
# Login to Container registry.
|
||||
- name: Log in to the Container registry
|
||||
uses: docker/login-action@v1
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GH_TOKEN }}
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
|
||||
- name: Extract metadata (tags, labels) for Docker
|
||||
id: meta
|
||||
uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38
|
||||
with:
|
||||
images: ${{ env.REGISTRY }}/${{ env.WEBAPP_IMAGE_NAME }}
|
||||
images: ${{ env.WEBAPP_IMAGE_NAME }}
|
||||
|
||||
# Builds and push the Docker image.
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@v2
|
||||
uses: docker/build-push-action@v5
|
||||
id: build
|
||||
with:
|
||||
context: .
|
||||
file: ./packages/webapp/Dockerfile
|
||||
push: true
|
||||
tags: ghcr.io/bigcapitalhq/webapp:latest
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
context: ./
|
||||
file: ./packages/webapp/Dockerfile
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
tags: bigcapitalhq/webapp:latest, bigcapitalhq/webapp:${{github.ref_name}}
|
||||
|
||||
- name: Export digest
|
||||
run: |
|
||||
mkdir -p /tmp/digests
|
||||
digest="${{ steps.build.outputs.digest }}"
|
||||
touch "/tmp/digests/${digest#sha256:}"
|
||||
|
||||
- name: Upload digest
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: digests-webapp
|
||||
path: /tmp/digests/*
|
||||
if-no-files-found: error
|
||||
retention-days: 1
|
||||
# Send notification to Slack channel.
|
||||
- name: Slack Notification built and published webapp container successfully.
|
||||
uses: rtCamp/action-slack-notify@v2
|
||||
@@ -53,29 +76,52 @@ jobs:
|
||||
name: Build and deploy server container
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Prepare
|
||||
run: |
|
||||
platform=${{ matrix.platform }}
|
||||
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
# Login to Container registry.
|
||||
- name: Log in to the Container registry
|
||||
uses: docker/login-action@v1
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GH_TOKEN }}
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
|
||||
# Builds and push the Docker image.
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@v2
|
||||
uses: docker/build-push-action@v5
|
||||
id: build
|
||||
with:
|
||||
context: ./
|
||||
file: ./packages/server/Dockerfile
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
tags: ghcr.io/bigcapitalhq/server:latest
|
||||
tags: bigcapitalhq/server:latest, bigcapitalhq/server:${{github.ref_name}}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
|
||||
- name: Export digest
|
||||
run: |
|
||||
mkdir -p /tmp/digests
|
||||
digest="${{ steps.build.outputs.digest }}"
|
||||
touch "/tmp/digests/${digest#sha256:}"
|
||||
|
||||
- name: Upload digest
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: digests-server
|
||||
path: /tmp/digests/*
|
||||
if-no-files-found: error
|
||||
retention-days: 1
|
||||
|
||||
# Send notification to Slack channel.
|
||||
- name: Slack Notification built and published server container successfully.
|
||||
uses: rtCamp/action-slack-notify@v2
|
||||
env:
|
||||
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
|
||||
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
|
||||
|
||||
127
.github/workflows/build-deploy-develop-container.yaml
vendored
Normal file
127
.github/workflows/build-deploy-develop-container.yaml
vendored
Normal file
@@ -0,0 +1,127 @@
|
||||
# This workflow will build a docker container, publish it to Github Registry.
|
||||
name: Build and Deploy Develop Docker Container
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- develop
|
||||
|
||||
env:
|
||||
WEBAPP_IMAGE_NAME: bigcapitalhq/webapp
|
||||
SERVER_IMAGE_NAME: bigcapitalhq/server
|
||||
|
||||
jobs:
|
||||
build-publish-webapp:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
name: Build and deploy webapp container
|
||||
runs-on: ubuntu-latest
|
||||
environment: production
|
||||
steps:
|
||||
- name: Prepare
|
||||
run: |
|
||||
platform=${{ matrix.platform }}
|
||||
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
# Login to Container registry.
|
||||
- name: Log in to the Container registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
|
||||
- name: Extract metadata (tags, labels) for Docker
|
||||
id: meta
|
||||
uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38
|
||||
with:
|
||||
images: ${{ env.WEBAPP_IMAGE_NAME }}
|
||||
|
||||
# Builds and push the Docker image.
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@v5
|
||||
id: build
|
||||
with:
|
||||
context: ./
|
||||
file: ./packages/webapp/Dockerfile
|
||||
platforms: linux/amd64
|
||||
push: true
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
tags: bigcapitalhq/webapp:develop
|
||||
|
||||
- name: Export digest
|
||||
run: |
|
||||
mkdir -p /tmp/digests
|
||||
digest="${{ steps.build.outputs.digest }}"
|
||||
touch "/tmp/digests/${digest#sha256:}"
|
||||
|
||||
- name: Upload digest
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: digests-webapp
|
||||
path: /tmp/digests/*
|
||||
if-no-files-found: error
|
||||
retention-days: 1
|
||||
# Send notification to Slack channel.
|
||||
- name: Slack Notification built and published webapp container successfully.
|
||||
uses: rtCamp/action-slack-notify@v2
|
||||
env:
|
||||
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
|
||||
|
||||
build-publish-server:
|
||||
name: Build and deploy server container
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Prepare
|
||||
run: |
|
||||
platform=${{ matrix.platform }}
|
||||
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
# Login to Container registry.
|
||||
- name: Log in to the Container registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
|
||||
# Builds and push the Docker image.
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@v5
|
||||
id: build
|
||||
with:
|
||||
context: ./
|
||||
file: ./packages/server/Dockerfile
|
||||
platforms: linux/amd64
|
||||
push: true
|
||||
tags: bigcapitalhq/server:develop
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
|
||||
- name: Export digest
|
||||
run: |
|
||||
mkdir -p /tmp/digests
|
||||
digest="${{ steps.build.outputs.digest }}"
|
||||
touch "/tmp/digests/${digest#sha256:}"
|
||||
|
||||
- name: Upload digest
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: digests-server
|
||||
path: /tmp/digests/*
|
||||
if-no-files-found: error
|
||||
retention-days: 1
|
||||
|
||||
# Send notification to Slack channel.
|
||||
- name: Slack Notification built and published server container successfully.
|
||||
uses: rtCamp/action-slack-notify@v2
|
||||
env:
|
||||
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
|
||||
4
.github/workflows/e2e.yml
vendored
4
.github/workflows/e2e.yml
vendored
@@ -8,14 +8,14 @@ on:
|
||||
- '**.ts'
|
||||
- '**.tsx'
|
||||
- '**/tsconfig.json'
|
||||
- 'yarn.lock'
|
||||
- 'pnpm-lock.yaml'
|
||||
- '.github/workflows/e2e.yml'
|
||||
pull_request:
|
||||
paths:
|
||||
- '**.ts'
|
||||
- '**.tsx'
|
||||
- '**/tsconfig.json'
|
||||
- 'yarn.lock'
|
||||
- 'pnpm-lock.yaml'
|
||||
- '.github/workflows/e2e.yml'
|
||||
|
||||
defaults:
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -6,4 +6,5 @@ node_modules/
|
||||
# Production env file
|
||||
.env
|
||||
|
||||
test-results/
|
||||
test-results/
|
||||
.qodo
|
||||
|
||||
22
.gitpod.yml
Normal file
22
.gitpod.yml
Normal file
@@ -0,0 +1,22 @@
|
||||
tasks:
|
||||
- name: Init
|
||||
init: |
|
||||
pnpm install &&
|
||||
cp .env.example .env &&
|
||||
docker-compose up -d &&
|
||||
pnpm run build:server &&
|
||||
node packages/server/build/commands.js system:migrate:latest
|
||||
command: |
|
||||
docker-compose up -d &&
|
||||
pnpm run dev
|
||||
|
||||
ports:
|
||||
- port: 4000
|
||||
visibility: public
|
||||
onOpen: open-preview
|
||||
- port: 3000
|
||||
visibility: public
|
||||
onOpen: ignore
|
||||
- port: 3306
|
||||
visibility: public
|
||||
onOpen: ignore
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env sh
|
||||
. "$(dirname -- "$0")/_/husky.sh"
|
||||
|
||||
yarn commitlint --edit
|
||||
pnpx commitlint --edit
|
||||
|
||||
415
CHANGELOG.md
415
CHANGELOG.md
@@ -2,11 +2,412 @@
|
||||
|
||||
All notable changes to Bigcapital server-side will be in this file.
|
||||
|
||||
# [0.10.1] - 25-09-2023
|
||||
# [0.22.0]
|
||||
|
||||
* feat: estimate, receipt, credit note mail preview by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/757
|
||||
* feat: Add discount to transactions by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/758
|
||||
* fix: update financial forms to use new formatted amount utilities and… by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/760
|
||||
* fix: total lines style by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/761
|
||||
* fix: discount & adjustment sale transactions bugs by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/762
|
||||
* fix: discount transactions GL entries by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/763
|
||||
|
||||
# [0.21.2]
|
||||
|
||||
* hotbug: upload attachments by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/755
|
||||
|
||||
# [0.21.1]
|
||||
|
||||
* fix: download invoice document on payment page by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/750
|
||||
* fix: attach branding template attrs to payment page by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/751
|
||||
* fix: make manual entries adjust decimal credit/debit amounts by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/754
|
||||
* feat: allow quantity of entries accept decimal value by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/753
|
||||
|
||||
# [0.21.0]
|
||||
|
||||
* fix: Credit and debit totals not balancing when decimal values are used by @nklmantey in https://github.com/bigcapitalhq/bigcapital/pull/722
|
||||
* docs: add nklmantey as a contributor for bug by @allcontributors in https://github.com/bigcapitalhq/bigcapital/pull/725
|
||||
* feat: track more services events by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/721
|
||||
* feat: Invoice mail receipt preview by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/723
|
||||
* fix: change the send mail button on invoice drawer by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/730
|
||||
* refactor: notification mail services by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/731
|
||||
* fix: attach payment link in sending invoice mail receipt by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/732
|
||||
* fix: send invoice drawer layout by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/733
|
||||
* fix: hook up cc and bcc fields to mail sender by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/734
|
||||
* fix: company logo does not show up in mail receipt preview by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/736
|
||||
* fix: change default invoice mail message by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/737
|
||||
* fix: typing invoice send mail fields by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/738
|
||||
* fix: clean up ivnoice mail receipt preview component by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/739
|
||||
* feat: add shared package to pdf templates to render in the server and… by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/735
|
||||
* feat: getting invoice preview on send mail view by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/740
|
||||
* fix: style SSR invoice paper template by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/741
|
||||
* fix: send invoice receipt addresses by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/742
|
||||
* fix: due invoice server invoice by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/744
|
||||
* fix: `BIG-265` forgot password text by @ibutiti in https://github.com/bigcapitalhq/bigcapital/pull/745
|
||||
* Crims on sv translation by @Crims-on in https://github.com/bigcapitalhq/bigcapital/pull/671
|
||||
* feat: Added Spanish language to the App 🇪🇸 by @angelosorno in https://github.com/bigcapitalhq/bigcapital/pull/530
|
||||
* fix: mail services by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/746
|
||||
* fix: company logo of the template by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/747
|
||||
* fix: monorepo dependencies scope by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/748
|
||||
|
||||
# [0.20.6]
|
||||
|
||||
* fix: Import category column of item resource by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/710
|
||||
* fix: Parse the uppercase values in importing by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/711
|
||||
* chore: Move i18nApply localization to the account transformer by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/713
|
||||
* fix: Sync Plaid credit card account type by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/714
|
||||
* fix: Sync account normal of cashflow GL entries by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/715
|
||||
* feat: Add quantity column to pdf templates by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/716
|
||||
* feat: Pre-line invoice statements by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/717
|
||||
* feat: Invoice number in downloaded pdf document by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/718
|
||||
* feat: Track events of pdf documents views by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/719
|
||||
* fix: Customer note does not appear in pdf document by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/720
|
||||
|
||||
# [0.20.5]
|
||||
|
||||
* fix: Disable tabs of the pdf customization if the first field not filed up by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/701
|
||||
* fix: Invoice form layout by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/705
|
||||
* refactor: Invoice, estimate, receipt, credit note and payment received date input fields by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/707
|
||||
* feat: Add customize templates button to edit forms by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/708
|
||||
* feat: Track account, invoice and item viewed events by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/709
|
||||
|
||||
# [0.20.4]
|
||||
|
||||
* fix: Delete company logo from the PDF template by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/699
|
||||
* fix: Set max width/height to company logo of pdf templates by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/700
|
||||
|
||||
# [0.20.3]
|
||||
|
||||
* feat: Assign default PDF template automatically by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/687
|
||||
* fix: pdf template addresses controlling by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/688
|
||||
* fix: Remove empty lines from address formats by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/690
|
||||
* fix: Pdf templates layout by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/691
|
||||
* feat: Download invoice pdf of the payment link by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/689
|
||||
* fix: Display country name by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/693
|
||||
* feat: Add shared packages to Docker container by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/694
|
||||
|
||||
# [0.20.2]
|
||||
|
||||
* feat: Assign default PDF template automatically by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/687
|
||||
* fix: pdf template addresses controlling by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/688
|
||||
* fix: Remove empty lines from address formats by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/690
|
||||
* fix: Pdf templates layout by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/691
|
||||
* feat: Download invoice pdf of the payment link by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/689
|
||||
* fix: Display country name by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/693
|
||||
* feat: Add shared packages to Docker container by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/694
|
||||
|
||||
# [0.20.1]
|
||||
|
||||
* fix: Getting uploaded object uri by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/684
|
||||
|
||||
# [0.20.0]
|
||||
|
||||
* feat: Customize pdf templates by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/667
|
||||
* feat: Onboard accounts to Stripe Connect by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/668
|
||||
* feat: Upload company logo to invoice templates by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/670
|
||||
* fix: Invoice pdf customize by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/672
|
||||
* fix: Invoice customize bugs by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/673
|
||||
* feat: Clean up payment links endpoints by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/674
|
||||
* feat: Hook up company logo to server-side pdf templates by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/675
|
||||
* feat: Company branding preferences by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/677
|
||||
* feat: Pdf templates customer/company addresses by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/678
|
||||
* fix: Listen to Stripe session completed event by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/679
|
||||
* feat: Track pdf templates Posthog events by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/680
|
||||
* fix: Branding customize content by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/681
|
||||
* feat: Listen to Stripe integration events by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/682
|
||||
* feat: Hook up customer/company address to invoice preview of payment page by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/683
|
||||
|
||||
# [0.19.17]
|
||||
|
||||
* fix: Un-categorize bank transactions by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/663
|
||||
|
||||
# [0.19.16]
|
||||
|
||||
* feat: Tracking more Posthog events by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/653
|
||||
* fix: Expense cannot accept credit card as payment account by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/654
|
||||
* fix: Suspense the lazy loaded components in banking pages by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/657
|
||||
* feat: Add help dropdown menu by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/656
|
||||
* feat: Bank pages layout breaking by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/658
|
||||
* feat: Datatable UI improvements by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/655
|
||||
* fix: Array cast of recognize function rule ids by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/660
|
||||
* fix: Payment made filling the form full amount field by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/661
|
||||
* feat: Tabular number of all money columns by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/659
|
||||
* refactor: The expense G/L writer by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/662
|
||||
|
||||
## [0.19.15] -
|
||||
|
||||
* fix: Bank transactions infinity scrolling by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/648
|
||||
* feat: Integrate multiple branches and warehouses to resource importing by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/645
|
||||
* fix: Integrate multiple branches with expense resource by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/649
|
||||
* feat: Cover more tracking events. by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/650
|
||||
* feat: Track banking service events by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/651
|
||||
|
||||
## [0.19.14]
|
||||
|
||||
* fix: Import bugs by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/643
|
||||
* fix: Set default index to transaction entries by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/644
|
||||
* feat(server): Events tracking using Posthog by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/646
|
||||
|
||||
## [0.19.13]
|
||||
|
||||
* fix: Subscription middleware by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/624
|
||||
* fix: Getting the sheet columns in import sheet by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/641
|
||||
|
||||
## [0.19.12]
|
||||
|
||||
* fix: Typo one-click demo page by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/640
|
||||
|
||||
## [0.19.11]
|
||||
|
||||
* fix: Avoid running the cost job in import preview by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/635
|
||||
* fix: Debounce scheduling calculating items cost by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/634
|
||||
* fix: Expand the resources export page size limitation by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/636
|
||||
* feat: Optimize loading perf. by splitting big chunks and lazy loading them by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/632
|
||||
* fix: Use standard ISO 8601 format for exported data by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/638
|
||||
* fix: Add customer type to customers resource by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/639
|
||||
|
||||
## [0.19.10]
|
||||
|
||||
* fix: Add subscription plans offer text by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/629
|
||||
|
||||
## [0.19.9]
|
||||
|
||||
* fix: Make webapp package env variables dynamic by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/628
|
||||
|
||||
## [v0.19.8]
|
||||
|
||||
* fix: Cannot import items income and cost accounts by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/617
|
||||
* fix: Some bank account details hidden by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/618
|
||||
* feat(ee): One-click demo account by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/616
|
||||
* feat: change banking service language by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/619
|
||||
* feat(banking): Filter uncategorized bank transactions by date by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/590
|
||||
* Fix: Syntax error caused error by @wolone in https://github.com/bigcapitalhq/bigcapital/pull/622
|
||||
* fix: Listen to payment webhooks by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/623
|
||||
* fix: Add prefix J-00001 to manual journals increments by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/625
|
||||
* fix: Disable sms service until Twilo integration by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/626
|
||||
* fix: Style tweaks in onboarding page by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/627
|
||||
|
||||
## [0.19.5] - 18-08-2024
|
||||
|
||||
* fix: Allow multi-lines to statements transactions by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/594
|
||||
* feat: Add amount comparators to amount bank rule field by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/595
|
||||
* fix: Transaction type and description do not show in general ledger. by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/596
|
||||
* fix: Refresh accounts and account transactions. by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/597
|
||||
* fix: Typo payments made by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/598
|
||||
* fix: Typo categories list by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/600
|
||||
* fix: Autofill the quick created customer/vendor by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/601
|
||||
* fix: Remove views tabs from receipts list by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/602
|
||||
* fix: Typo payment receive messages by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/599
|
||||
* fix: Enhance Dropzone visual of accept and reject modes by @Champetaman in https://github.com/bigcapitalhq/bigcapital/pull/603
|
||||
* fix: Matching bank transactions should create associate payment transactions by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/606
|
||||
* fix: Change Dropzone title and subtitle by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/607
|
||||
* fix: Inconsistance page size of paginated data tables by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/604
|
||||
* fix: Database connection lost error by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/611
|
||||
* fix: Language typos by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/613
|
||||
* Fix: Correctly display Date, Published At, and Created At in ExpenseDrawerHeader by @Champetaman in https://github.com/bigcapitalhq/bigcapital/pull/612
|
||||
* fix: Delete bank account with uncategorized transactions by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/614
|
||||
* feat: activate/inactivate account from drawer details by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/615
|
||||
|
||||
## [v0.19.4]
|
||||
|
||||
* feat: Import and export tax rates by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/591
|
||||
* feat: Un-categorize bank transactions in bulk by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/587
|
||||
* feat: Pending bank transactions by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/589
|
||||
* fix: Update `dev` Script in `package.json` to Use `cross-env` by @Champetaman in https://github.com/bigcapitalhq/bigcapital/pull/588
|
||||
* fix: Should not load branches on reconcile matching form if the branches not enabled by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/592
|
||||
* fix: Rounding the total amount the pending and matched transactions by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/593
|
||||
|
||||
## [v0.18.0] - 10-08-2024
|
||||
|
||||
* feat: Bank rules for automated categorization by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/511
|
||||
* feat: Categorize & match bank transaction by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/511
|
||||
* feat: Reconcile match transactions by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/522
|
||||
* fix: Issues in matching transactions by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/523
|
||||
* fix: Cashflow transactions types by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/524
|
||||
|
||||
## [v0.17.5] - 17-06-2024
|
||||
|
||||
* fix: Balance sheet and P/L nested accounts by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/501
|
||||
* fix: add space between buttons on floating actions bar by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/508
|
||||
* feat: Migrating to Envoy proxy instead of Nginx by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/509
|
||||
* fix: Disable email confirmation does not work with invited users by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/497
|
||||
* feat: Setting up the date format in the whole system dates by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/506
|
||||
|
||||
## [0.17.0] - 04-06-2024
|
||||
|
||||
### New
|
||||
|
||||
* feat: Upload and attach documents by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/461
|
||||
* feat: Export resource tables to pdf by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/460
|
||||
* feat: Build and deploy develop Docker container by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/476
|
||||
* feat: Internal docker virtual network by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/478
|
||||
|
||||
### Fixes
|
||||
|
||||
* fix: Skip send confirmation email if disabled by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/459
|
||||
* fix: Lemon Squeezy redirect to base url by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/479
|
||||
* fix: Organize Plaid env variables for development and sandbox envs by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/480
|
||||
* fix: Plaid syncs deposit imports as withdrawals by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/481
|
||||
* fix: Validate the s3 configures exist by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/482
|
||||
* fix: Run migrations only for initialized tenants by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/484
|
||||
|
||||
## [0.16.16] -
|
||||
|
||||
* feat: handle http exceptions by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/456
|
||||
* feat: add the missing Newrelic env vars to docker-compose.prod file by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/457
|
||||
* fix: add the signup email confirmation env var by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/458
|
||||
|
||||
## [0.16.14] -
|
||||
|
||||
* fix: Typo in setup wizard by @ccantrell72 in https://github.com/bigcapitalhq/bigcapital/pull/440
|
||||
* fix: Showing the real mail address on email confirmation view by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/445
|
||||
* fix: Auto-increment setting parsing by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/453
|
||||
|
||||
## [0.16.12] -
|
||||
|
||||
* feat: Create a manifest list for `webapp` Docker image and push it to DockerHub. by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/436
|
||||
* feat: Combine arm64 and amd64 in one Github action runner by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/437
|
||||
|
||||
## [0.16.11] - 06-05-2024
|
||||
|
||||
### improvements
|
||||
|
||||
* feat: Export resource data to csv, xlsx by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/430
|
||||
* feat: User email verification after signing-up. by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/426
|
||||
|
||||
### Fixes
|
||||
* feat(repo): upgrade to latest lerna v8 and pnpm v9 by @benpsnyder in https://github.com/bigcapitalhq/bigcapital/pull/414
|
||||
* feat: Update Docker Build-Push Action and Add ARM64 Support by @cloudsbird in https://github.com/bigcapitalhq/bigcapital/pull/412
|
||||
* feat: Pushing docker containers by version tag by @cloudsbird in https://github.com/bigcapitalhq/bigcapital/pull/421
|
||||
|
||||
## [0.16.10]
|
||||
|
||||
* fix: Running migration Docker container on Windows by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/432
|
||||
|
||||
## [0.16.9]
|
||||
|
||||
* feat: New Relic for tracking by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/429
|
||||
|
||||
## [0.16.8]
|
||||
|
||||
* feat: Ability to enable/disable the bank connect feature by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/423
|
||||
|
||||
## [0.16.6]
|
||||
|
||||
* hotfix: fix the subscription plan when subscribe on cloud by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/422
|
||||
|
||||
## [0.16.5]
|
||||
|
||||
IMPORTANT: If you upgraded to the v0.16 recently you should upgrade to v0.16.4 as soon as possible, because there're some breaking changes affected the sign-in and some users reported couldn't sign-in.
|
||||
|
||||
* feat: Seed free subscription to tenants that have no subscription. by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/410
|
||||
|
||||
## [0.16.3]
|
||||
|
||||
* feat: Integrate Lemon Squeezy payment by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/402
|
||||
* feat: optimize the onboarding subscription experience. by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/404
|
||||
* feat: subscription page content by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/405
|
||||
* feat: auto subscribe to free plan once signup on community version. by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/406
|
||||
* chore: add default value to env variable by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/407
|
||||
* fix: absolute storage imports path. by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/408
|
||||
|
||||
## [0.16.0]
|
||||
|
||||
* feat: add convert to invoice button on estimate drawer toolbar by @ANasouf in https://github.com/bigcapitalhq/bigcapital/pull/361
|
||||
* feat(webapp): add mark as delivered to action bar of invoice details … by @ANasouf in https://github.com/bigcapitalhq/bigcapital/pull/360
|
||||
* feat(webapp): Dialog to choose the bank service provider by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/378
|
||||
* feat: Categorize the bank synced transactions by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/377
|
||||
* feat: uncategorize the cashflow transaction by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/381
|
||||
* Import resources from csv/xlsx by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/382
|
||||
* feat(webapp): import resource UI by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/386
|
||||
* fix: import resources improvements by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/388
|
||||
* feat: add sample sheet to accounts and bank transactions by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/389
|
||||
* fix: show the unique row value in the import preview by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/392
|
||||
* feat: advanced parser for numeric and boolean import values by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/394
|
||||
* feat: validate the given imported sheet whether is empty by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/395
|
||||
* feat: linking relation with id in importing by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/393
|
||||
* feat: Aggregate rows import by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/396
|
||||
* feat: clean up the imported temp files by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/400
|
||||
* feat: add hints to import fields by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/401
|
||||
|
||||
## [0.15.0]
|
||||
|
||||
* feat: Printing financial reports by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/363
|
||||
* feat: Convert invoice status after sending mail notification by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/332
|
||||
* feat: Bigcapital <> Plaid Integration by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/346
|
||||
* fix: Broken transactions by vendor report by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/369
|
||||
* fix: Optimize the print style some financial reports by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/370
|
||||
|
||||
## [0.14.0] - 30-01-2024
|
||||
|
||||
* feat: purchases by items exporting by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/327
|
||||
* fix: expense amounts should not be rounded by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/339
|
||||
* feat: get latest exchange rate from third party services by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/340
|
||||
* fix(webapp): inconsistency in currency of universal search items by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/335
|
||||
* hotfix: editing sales and expense transactions don't reflect GL entries by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/342
|
||||
|
||||
## [0.13.3] - 22-01-2024
|
||||
|
||||
* hotfix(server): Unhandled thrown errors of services by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/329
|
||||
|
||||
## [0.13.2] - 21-01-2024
|
||||
|
||||
* feat: show customer / vendor balance. by @asenawritescode in https://github.com/bigcapitalhq/bigcapital/pull/311
|
||||
* feat: inventory valuation csv and xlsx export by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/308
|
||||
* feat: sales by items export csv & xlsx by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/310
|
||||
* fix(server): the invoice and payment receipt printing by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/315
|
||||
* fix: get cashflow transaction broken cause transaction type by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/318
|
||||
* fix: `AccountActivateAlert` import by @xprnio in https://github.com/bigcapitalhq/bigcapital/pull/322
|
||||
|
||||
## [0.13.1] - 15-01-2024
|
||||
|
||||
* feat(webapp): add approve/reject to action bar of estimate details dr… by @ANasouf in https://github.com/bigcapitalhq/bigcapital/pull/304
|
||||
* docs: add ANasouf as a contributor for code by @allcontributors in https://github.com/bigcapitalhq/bigcapital/pull/305
|
||||
* feat: Export general ledger & Journal to CSV and XLSX by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/303
|
||||
* feat: Auto re-calculate the items rate once changing the invoice exchange rate. by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/270
|
||||
|
||||
## [0.13.0] - 31-12-2023
|
||||
|
||||
* feat: Send an invoice mail the customer email by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/292
|
||||
* fix: Allow non-numeric postal codes by @cschuijt in https://github.com/bigcapitalhq/bigcapital/pull/294
|
||||
* docs: add cschuijt as a contributor for bug by @allcontributors in https://github.com/bigcapitalhq/bigcapital/pull/295
|
||||
|
||||
## [0.12.1] - 17-11-2023
|
||||
|
||||
* feat: Add default customer message and terms conditions to the transactions by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/291
|
||||
* fix: The currency code of transaction tax rate entry by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/293
|
||||
|
||||
## [0.12.0] - 04-11-2023
|
||||
|
||||
* feat: Export reports via CSV and XLSX by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/286
|
||||
* fix: Axios upgrade by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/288
|
||||
* fix(server): Allow decimal amount in sale/purchase transactions. by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/289
|
||||
* feat: Optimize invoice documents printing by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/280
|
||||
* chore(deps): bump axios from 0.20.0 to 1.6.0 in /packages/server by @dependabot in https://github.com/bigcapitalhq/bigcapital/pull/284
|
||||
* chore(deps): bump axios from 0.20.0 to 1.6.0 by @dependabot in https://github.com/bigcapitalhq/bigcapital/pull/283
|
||||
|
||||
## [0.11.0] - 28-10-2023
|
||||
|
||||
* feat: Migrate to pnpm by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/253
|
||||
* feat: Integrate tax rates to bills by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/260
|
||||
* feat: Assign default sell/purchase tax rates to items by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/261
|
||||
* chore(deps-dev): bump @babel/traverse from 7.23.0 to 7.23.2 in /packages/server by @dependabot in https://github.com/bigcapitalhq/bigcapital/pull/272
|
||||
* feat: Improve financial statements rows color by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/276
|
||||
* fix: Trial balance sheet adjusted balance by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/273
|
||||
* feat: Adds tax numbers to organization and customers by @kochie in https://github.com/bigcapitalhq/bigcapital/pull/269
|
||||
* docs: Add kochie as a contributor for code by @allcontributors in https://github.com/bigcapitalhq/bigcapital/pull/277
|
||||
* feat: Computed Net Income under Equity in Balance Sheet report. by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/271
|
||||
* fix: Change Dockerfile files with new pnpm by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/278
|
||||
|
||||
## [0.10.2] - 02-10-2023
|
||||
|
||||
fix(webapp): Disable tax rates from item entries editor table services do not support tax rates (https://github.com/bigcapitalhq/bigcapital/commit/69afa07e3ba45495a4cab3490c15f2b0c40c4790) by @abouolia
|
||||
fix(server): Add missing method in ItemEntry model (https://github.com/bigcapitalhq/bigcapital/commit/07628ddc37f46c98959ced0323f28752e0a98944) by @abouolia
|
||||
|
||||
## [0.10.1] - 25-09-2023
|
||||
|
||||
* Fix: Running tenants migration on Docker migration container by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/242
|
||||
|
||||
# [0.10.0] - 24-09-2023
|
||||
## [0.10.0] - 24-09-2023
|
||||
|
||||
* Added: Tax rates service by @abouolia @elforjani13 in https://github.com/bigcapitalhq/bigcapital/pull/204
|
||||
* Added: Sales Tax Liability Summary report by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/204
|
||||
@@ -16,7 +417,7 @@ All notable changes to Bigcapital server-side will be in this file.
|
||||
* chore(deps): bump word-wrap from 1.2.3 to 1.2.4 in /packages/webapp by @dependabot in https://github.com/bigcapitalhq/bigcapital/pull/199
|
||||
* chore(deps): bump mongoose from 5.13.15 to 5.13.20 by @dependabot in https://github.com/bigcapitalhq/bigcapital/pull/197
|
||||
|
||||
# [0.9.12] - 29-08-2023
|
||||
## [0.9.12] - 29-08-2023
|
||||
|
||||
* Refactor: split the services to multiple service classes. (by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/202)
|
||||
* Fix: create quick customer/vendor by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/206
|
||||
@@ -30,18 +431,18 @@ All notable changes to Bigcapital server-side will be in this file.
|
||||
* Fix: filter by customers, vendors and items in reports do not work by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/224
|
||||
https://github.com/bigcapitalhq/bigcapital/pull/225
|
||||
|
||||
# [0.9.11] - 23-07-2023
|
||||
## [0.9.11] - 23-07-2023
|
||||
|
||||
* added: Restart policy to docker compose files. by @suhaibaffan in https://github.com/bigcapitalhq/bigcapital/pull/198
|
||||
* fix: Expose and expand the rate limit to the env variables by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/195
|
||||
|
||||
# [0.9.10] - 18-07-2023
|
||||
## [0.9.10] - 18-07-2023
|
||||
|
||||
* feat(e2e): E2E onboarding process by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/176
|
||||
* fix(webapp): Show loading message of cost computing job on financial reports by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/196
|
||||
* fix(webapp): Change the currency code of sales and purchases transactions with foreign contacts.
|
||||
|
||||
# [0.9.9] - 28-06-2023
|
||||
## [0.9.9] - 28-06-2023
|
||||
|
||||
* refactor: Customer and vendor select component by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/171
|
||||
* chore: Move auto-increment components in separate files by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/170
|
||||
@@ -53,7 +454,7 @@ https://github.com/bigcapitalhq/bigcapital/pull/225
|
||||
* fix: No currency in amount field on money in/out dialogs by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/179
|
||||
* fix: No default branch for customer/vendor opening balance branch by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/182
|
||||
|
||||
# [0.9.8] - 19-06-2023
|
||||
## [0.9.8] - 19-06-2023
|
||||
|
||||
`bigcapitalhq/webapp`
|
||||
|
||||
|
||||
@@ -33,7 +33,8 @@ Contributions via pull requests are much appreciated. Once the approach is agree
|
||||
---
|
||||
|
||||
## Local Setup Prerequisites
|
||||
- The application currently supports **Node.js v14.x**. Please ensure that you are using this version of Node.js when developing. (use [nvm](https://github.com/nvm-sh/nvm#installing-and-updating) to switch between node versions)
|
||||
- The application currently supports **Node.js v18.x**.
|
||||
- `pnpm` packages manager, (from pnpm [guide](https://pnpm.io/installation) pick any installation method).
|
||||
|
||||
## Contribute to Backend
|
||||
|
||||
@@ -44,11 +45,10 @@ Contributions via pull requests are much appreciated. Once the approach is agree
|
||||
cp .env.example .env
|
||||
```
|
||||
|
||||
- Install all npm dependencies of the monorepo, you don't have to change directory to the `backend` package. just hit these command on root directory and it will install dependencies of all packages.
|
||||
- Install all npm dependencies of the monorepo, you don't have to change directory to the `backend` package. just hit the command on root directory and it will install dependencies of all packages.
|
||||
|
||||
```
|
||||
npm install
|
||||
npm run bootstrap
|
||||
pnpm install
|
||||
```
|
||||
|
||||
- Run all required docker containers in the development, we already configured all containers under `docker-compose.yml`.
|
||||
@@ -69,7 +69,7 @@ cefa73fe2881 bigcapital-redis "docker-entrypoint.s…" 7 seconds ago Up
|
||||
- There're some CLI commands we should run before running the server like databaase migration, so we need to build the `server` app first.
|
||||
|
||||
```
|
||||
npm run build:server
|
||||
pnpm run build:server
|
||||
```
|
||||
|
||||
- Run the database migration for system database.
|
||||
@@ -87,7 +87,7 @@ Batch 1 run: 6 migrations
|
||||
- Next, start the webapp application.
|
||||
|
||||
```
|
||||
npm run dev:server
|
||||
pnpm run dev:server
|
||||
```
|
||||
|
||||
**[`^top^`](#)**
|
||||
@@ -105,14 +105,13 @@ git clone https://github.com/bigcapital/bigcapital.git && cd bigcaptial
|
||||
- Install all npm dependencies of the monorepo, you don't have to change directory to the `frontend` package. just hit that command and will install all packages across all application.
|
||||
|
||||
```
|
||||
npm install
|
||||
npm run bootstrap
|
||||
pnpm install
|
||||
```
|
||||
|
||||
- Next, start the webapp application.
|
||||
|
||||
```
|
||||
npm run dev:webapp
|
||||
pnpm run dev:webapp
|
||||
```
|
||||
|
||||
**[`^top^`](#)**
|
||||
|
||||
60
README.md
60
README.md
@@ -1,6 +1,6 @@
|
||||
<p align="center">
|
||||
<p align="center">
|
||||
<a href="https://bigcapital.ly" target="_blank">
|
||||
<a href="https://bigcapital.app" target="_blank">
|
||||
<img src="https://raw.githubusercontent.com/abouolia/blog/main/public/bigcapital.svg" alt="Bigcapital" width="280" height="75">
|
||||
</a>
|
||||
</p>
|
||||
@@ -12,6 +12,9 @@
|
||||
<a href="https://github.com/bigcapitalhq/bigcapital/commits/develop">
|
||||
<img src="https://img.shields.io/github/commit-activity/m/bigcapitalhq/bigcapital/develop" />
|
||||
</a>
|
||||
<a href="https://hub.docker.com/u/bigcapitalhq">
|
||||
<img src="https://img.shields.io/docker/pulls/bigcapitalhq/webapp" />
|
||||
</a>
|
||||
<a href="https://discord.com/invite/c8nPBJafeb">
|
||||
<img src="https://img.shields.io/discord/1066514716752625725?label=Discord" alt="" />
|
||||
</a>
|
||||
@@ -25,8 +28,14 @@
|
||||
<img src="https://img.shields.io/twitter/follow/bigcapitalhq?style=social" alt="twitter" />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://my.bigcapital.app">Bigcapital Cloud</a>
|
||||
</p>
|
||||
</p>
|
||||
|
||||
> We are currently in the process of migrating all server-side API endpoints to NestJS to establish a more solid architecture. Some endpoints in development mode may be temporarily do not work during this stabilization phase. However, this migration doesn't affect the production Docker images, which remain on the latest stable version.
|
||||
|
||||
# What's Bigcapital?
|
||||
|
||||
Bigcapital is a smart and open-source accounting and inventory software, Bigcapital keeps all business finances in right place and automates accounting processes to give the business powerful and intelligent financial statements and reports to help in making decisions.
|
||||
@@ -37,9 +46,40 @@ Bigcapital is a smart and open-source accounting and inventory software, Bigcapi
|
||||
<img src="https://raw.githubusercontent.com/abouolia/blog/main/public/screenshot-3.png" width="270">
|
||||
</p>
|
||||
|
||||
# Getting Started
|
||||
|
||||
We've got serveral options on dev and prod depending on your need to get started quickly with Bigcapital.
|
||||
|
||||
## Self-hosted
|
||||
|
||||
Bigcapital is available open-source under AGPL license. You can host it on your own servers using Docker.
|
||||
|
||||
### Docker
|
||||
|
||||
To get started with self-hosted with Docker and Docker Compose, take a look at the [Docker guide](https://docs.bigcapital.app/deployment/docker).
|
||||
|
||||
## Development
|
||||
|
||||
### Local Setup
|
||||
|
||||
To get started locally, we have a [guide to help you](https://github.com/bigcapitalhq/bigcapital/blob/develop/CONTRIBUTING.md).
|
||||
|
||||
### Gitpod
|
||||
|
||||
- Click the Gitpod button below to open this project in development mode.
|
||||
- This will open and configure the workspace in your browser with all the necessary dependencies.
|
||||
|
||||
[](https://gitpod.io/new/#https://github.com/bigcapitalhq/bigcapital)
|
||||
|
||||
## Headless Accounting
|
||||
|
||||
You can integrate Bigcapital API with your system to organize your transactions in double-entry system to get the best financial reports.
|
||||
|
||||
[](https://www.postman.com/bigcapital/workspace/bigcapital-api)
|
||||
|
||||
# Resources
|
||||
|
||||
- [Documentation](https://docs.bigcapital.ly/) - Learn how to use.
|
||||
- [Documentation](https://docs.bigcapital.app/) - Learn how to use.
|
||||
- [Contribution](https://github.com/bigcapitalhq/bigcapital/blob/develop/CONTRIBUTING.md) - Welcome to any contributions.
|
||||
- [Discord](https://discord.com/invite/c8nPBJafeb) - Ask for help.
|
||||
- [Bug Tracker](https://github.com/bigcapitalhq/bigcapital/issues) - Notify us new bugs.
|
||||
@@ -80,6 +120,22 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://scheibling.se"><img src="https://avatars.githubusercontent.com/u/24367830?v=4?s=100" width="100px;" alt="Lars Scheibling"/><br /><sub><b>Lars Scheibling</b></sub></a><br /><a href="https://github.com/bigcapitalhq/bigcapital/issues?q=author%3Ascheibling" title="Bug reports">🐛</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/suhaibaffan"><img src="https://avatars.githubusercontent.com/u/18115937?v=4?s=100" width="100px;" alt="Suhaib Affan"/><br /><sub><b>Suhaib Affan</b></sub></a><br /><a href="https://github.com/bigcapitalhq/bigcapital/commits?author=suhaibaffan" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/KalliopiPliogka"><img src="https://avatars.githubusercontent.com/u/81677549?v=4?s=100" width="100px;" alt="Kalliopi Pliogka"/><br /><sub><b>Kalliopi Pliogka</b></sub></a><br /><a href="https://github.com/bigcapitalhq/bigcapital/issues?q=author%3AKalliopiPliogka" title="Bug reports">🐛</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://me.kochie.io"><img src="https://avatars.githubusercontent.com/u/10809884?v=4?s=100" width="100px;" alt="Robert Koch"/><br /><sub><b>Robert Koch</b></sub></a><br /><a href="https://github.com/bigcapitalhq/bigcapital/commits?author=kochie" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="http://cschuijt.nl"><img src="https://avatars.githubusercontent.com/u/5460015?v=4?s=100" width="100px;" alt="Casper Schuijt"/><br /><sub><b>Casper Schuijt</b></sub></a><br /><a href="https://github.com/bigcapitalhq/bigcapital/issues?q=author%3Acschuijt" title="Bug reports">🐛</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ANasouf"><img src="https://avatars.githubusercontent.com/u/19536487?v=4?s=100" width="100px;" alt="ANasouf"/><br /><sub><b>ANasouf</b></sub></a><br /><a href="https://github.com/bigcapitalhq/bigcapital/commits?author=ANasouf" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://ragnarlaud.dev"><img src="https://avatars.githubusercontent.com/u/3042904?v=4?s=100" width="100px;" alt="Ragnar Laud"/><br /><sub><b>Ragnar Laud</b></sub></a><br /><a href="https://github.com/bigcapitalhq/bigcapital/issues?q=author%3Axprnio" title="Bug reports">🐛</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/asenawritescode"><img src="https://avatars.githubusercontent.com/u/67445192?v=4?s=100" width="100px;" alt="Asena"/><br /><sub><b>Asena</b></sub></a><br /><a href="https://github.com/bigcapitalhq/bigcapital/issues?q=author%3Aasenawritescode" title="Bug reports">🐛</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://snyder.tech"><img src="https://avatars.githubusercontent.com/u/707567?v=4?s=100" width="100px;" alt="Ben Snyder"/><br /><sub><b>Ben Snyder</b></sub></a><br /><a href="https://github.com/bigcapitalhq/bigcapital/commits?author=benpsnyder" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="http://vederis.id"><img src="https://avatars.githubusercontent.com/u/13505006?v=4?s=100" width="100px;" alt="Vederis Leunardus"/><br /><sub><b>Vederis Leunardus</b></sub></a><br /><a href="https://github.com/bigcapitalhq/bigcapital/commits?author=cloudsbird" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="http://www.pivoten.com"><img src="https://avatars.githubusercontent.com/u/104120598?v=4?s=100" width="100px;" alt="Chris Cantrell"/><br /><sub><b>Chris Cantrell</b></sub></a><br /><a href="https://github.com/bigcapitalhq/bigcapital/issues?q=author%3Accantrell72" title="Bug reports">🐛</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/oleynikd"><img src="https://avatars.githubusercontent.com/u/3976868?v=4?s=100" width="100px;" alt="Denis"/><br /><sub><b>Denis</b></sub></a><br /><a href="https://github.com/bigcapitalhq/bigcapital/issues?q=author%3Aoleynikd" title="Bug reports">🐛</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://myself.vercel.app/"><img src="https://avatars.githubusercontent.com/u/42431274?v=4?s=100" width="100px;" alt="Sachin Mittal"/><br /><sub><b>Sachin Mittal</b></sub></a><br /><a href="https://github.com/bigcapitalhq/bigcapital/issues?q=author%3Amittalsam98" title="Bug reports">🐛</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://www.camilooviedo.com/"><img src="https://avatars.githubusercontent.com/u/64604272?v=4?s=100" width="100px;" alt="Camilo Oviedo"/><br /><sub><b>Camilo Oviedo</b></sub></a><br /><a href="https://github.com/bigcapitalhq/bigcapital/commits?author=Champetaman" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://nklmantey.com/"><img src="https://avatars.githubusercontent.com/u/90279429?v=4?s=100" width="100px;" alt="Mantey"/><br /><sub><b>Mantey</b></sub></a><br /><a href="https://github.com/bigcapitalhq/bigcapital/issues?q=author%3Anklmantey" title="Bug reports">🐛</a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -3,38 +3,33 @@
|
||||
version: '3.3'
|
||||
|
||||
services:
|
||||
nginx:
|
||||
container_name: bigcapital-nginx-gateway
|
||||
build:
|
||||
context: ./docker/nginx
|
||||
args:
|
||||
- SERVER_PROXY_PORT=3000
|
||||
- WEB_SSL=false
|
||||
- SELF_SIGNED=false
|
||||
volumes:
|
||||
- ./data/logs/nginx/:/var/log/nginx
|
||||
- ./docker/certbot/certs/:/var/certs
|
||||
proxy:
|
||||
image: envoyproxy/envoy:v1.30-latest
|
||||
depends_on:
|
||||
- server
|
||||
- webapp
|
||||
ports:
|
||||
- '${PUBLIC_PROXY_PORT:-80}:80'
|
||||
- '${PUBLIC_PROXY_SSL_PORT:-443}:443'
|
||||
tty: true
|
||||
depends_on:
|
||||
- server
|
||||
- webapp
|
||||
deploy:
|
||||
restart_policy:
|
||||
condition: unless-stopped
|
||||
volumes:
|
||||
- ./docker/envoy/envoy.yaml:/etc/envoy/envoy.yaml
|
||||
restart: on-failure
|
||||
networks:
|
||||
- bigcapital_network
|
||||
|
||||
webapp:
|
||||
container_name: bigcapital-webapp
|
||||
image: ghcr.io/bigcapitalhq/webapp:latest
|
||||
deploy:
|
||||
restart_policy:
|
||||
condition: unless-stopped
|
||||
image: bigcapitalhq/webapp:latest
|
||||
restart: on-failure
|
||||
networks:
|
||||
- bigcapital_network
|
||||
|
||||
server:
|
||||
container_name: bigcapital-server
|
||||
image: ghcr.io/bigcapitalhq/server:latest
|
||||
image: bigcapitalhq/server:latest
|
||||
expose:
|
||||
- '3000'
|
||||
links:
|
||||
- mysql
|
||||
- mongo
|
||||
@@ -43,9 +38,9 @@ services:
|
||||
- mysql
|
||||
- mongo
|
||||
- redis
|
||||
deploy:
|
||||
restart_policy:
|
||||
condition: unless-stopped
|
||||
restart: on-failure
|
||||
networks:
|
||||
- bigcapital_network
|
||||
environment:
|
||||
# Mail
|
||||
- MAIL_HOST=${MAIL_HOST}
|
||||
@@ -86,6 +81,48 @@ services:
|
||||
- SIGNUP_ALLOWED_DOMAINS=${SIGNUP_ALLOWED_DOMAINS}
|
||||
- SIGNUP_ALLOWED_EMAILS=${SIGNUP_ALLOWED_EMAILS}
|
||||
|
||||
# Sign-up email confirmation
|
||||
- SIGNUP_EMAIL_CONFIRMATION=${SIGNUP_EMAIL_CONFIRMATION}
|
||||
|
||||
# Gotenberg (Pdf generator)
|
||||
- GOTENBERG_URL=${GOTENBERG_URL}
|
||||
- GOTENBERG_DOCS_URL=${GOTENBERG_DOCS_URL}
|
||||
|
||||
# Exchange Rate
|
||||
- EXCHANGE_RATE_SERVICE=${EXCHANGE_RATE_SERVICE}
|
||||
- OPEN_EXCHANGE_RATE_APP_ID-${OPEN_EXCHANGE_RATE_APP_ID}
|
||||
|
||||
# Bank Sync
|
||||
- BANKING_CONNECT=${BANKING_CONNECT}
|
||||
|
||||
# Plaid
|
||||
- PLAID_ENV=${PLAID_ENV}
|
||||
- PLAID_CLIENT_ID=${PLAID_CLIENT_ID}
|
||||
- PLAID_SECRET=${PLAID_SECRET}
|
||||
- PLAID_LINK_WEBHOOK=${PLAID_LINK_WEBHOOK}
|
||||
|
||||
# Lemon Squeez
|
||||
- LEMONSQUEEZY_API_KEY=${LEMONSQUEEZY_API_KEY}
|
||||
- LEMONSQUEEZY_STORE_ID=${LEMONSQUEEZY_STORE_ID}
|
||||
- LEMONSQUEEZY_WEBHOOK_SECRET=${LEMONSQUEEZY_WEBHOOK_SECRET}
|
||||
- HOSTED_ON_BIGCAPITAL_CLOUD=${HOSTED_ON_BIGCAPITAL_CLOUD}
|
||||
|
||||
# New Relic matrics tracking.
|
||||
- NEW_RELIC_DISTRIBUTED_TRACING_ENABLED=${NEW_RELIC_DISTRIBUTED_TRACING_ENABLED}
|
||||
- NEW_RELIC_LOG=${NEW_RELIC_LOG}
|
||||
- NEW_RELIC_AI_MONITORING_ENABLED=${NEW_RELIC_AI_MONITORING_ENABLED}
|
||||
- NEW_RELIC_CUSTOM_INSIGHTS_EVENTS_MAX_SAMPLES_STORED=${NEW_RELIC_CUSTOM_INSIGHTS_EVENTS_MAX_SAMPLES_STORED}
|
||||
- NEW_RELIC_SPAN_EVENTS_MAX_SAMPLES_STORED=${NEW_RELIC_SPAN_EVENTS_MAX_SAMPLES_STORED}
|
||||
- NEW_RELIC_LICENSE_KEY=${NEW_RELIC_LICENSE_KEY}
|
||||
- NEW_RELIC_APP_NAME=${NEW_RELIC_APP_NAME}
|
||||
|
||||
# S3
|
||||
- S3_REGION=${S3_REGION}
|
||||
- S3_ACCESS_KEY_ID=${S3_ACCESS_KEY_ID}
|
||||
- S3_SECRET_ACCESS_KEY=${S3_SECRET_ACCESS_KEY}
|
||||
- S3_ENDPOINT=${S3_ENDPOINT}
|
||||
- S3_BUCKET=${S3_BUCKET}
|
||||
|
||||
database_migration:
|
||||
container_name: bigcapital-database-migration
|
||||
build:
|
||||
@@ -102,12 +139,12 @@ services:
|
||||
- TENANT_DB_NAME_PERFIX=${TENANT_DB_NAME_PERFIX}
|
||||
depends_on:
|
||||
- mysql
|
||||
networks:
|
||||
- bigcapital_network
|
||||
|
||||
mysql:
|
||||
container_name: bigcapital-mysql
|
||||
deploy:
|
||||
restart_policy:
|
||||
condition: unless-stopped
|
||||
restart: on-failure
|
||||
build:
|
||||
context: ./docker/mariadb
|
||||
environment:
|
||||
@@ -119,29 +156,38 @@ services:
|
||||
- mysql:/var/lib/mysql
|
||||
expose:
|
||||
- '3306'
|
||||
networks:
|
||||
- bigcapital_network
|
||||
|
||||
mongo:
|
||||
container_name: bigcapital-mongo
|
||||
deploy:
|
||||
restart_policy:
|
||||
condition: unless-stopped
|
||||
restart: on-failure
|
||||
build: ./docker/mongo
|
||||
expose:
|
||||
- '27017'
|
||||
volumes:
|
||||
- mongo:/var/lib/mongodb
|
||||
networks:
|
||||
- bigcapital_network
|
||||
|
||||
redis:
|
||||
container_name: bigcapital-redis
|
||||
deploy:
|
||||
restart_policy:
|
||||
condition: unless-stopped
|
||||
restart: on-failure
|
||||
build:
|
||||
context: ./docker/redis
|
||||
expose:
|
||||
- '6379'
|
||||
volumes:
|
||||
- redis:/data
|
||||
networks:
|
||||
- bigcapital_network
|
||||
|
||||
gotenberg:
|
||||
image: gotenberg/gotenberg:7
|
||||
expose:
|
||||
- '9000'
|
||||
networks:
|
||||
- bigcapital_network
|
||||
|
||||
# Volumes
|
||||
volumes:
|
||||
@@ -156,3 +202,8 @@ volumes:
|
||||
redis:
|
||||
name: bigcapital_prod_redis
|
||||
driver: local
|
||||
|
||||
# Networks
|
||||
networks:
|
||||
bigcapital_network:
|
||||
driver: bridge
|
||||
|
||||
@@ -41,12 +41,19 @@ services:
|
||||
context: ./docker/redis
|
||||
expose:
|
||||
- "6379"
|
||||
ports:
|
||||
- "6379:6379"
|
||||
volumes:
|
||||
- redis:/data
|
||||
deploy:
|
||||
restart_policy:
|
||||
condition: unless-stopped
|
||||
|
||||
gotenberg:
|
||||
image: gotenberg/gotenberg:7
|
||||
ports:
|
||||
- "9000:3000"
|
||||
|
||||
# Volumes
|
||||
volumes:
|
||||
mysql:
|
||||
|
||||
62
docker/envoy/envoy.yaml
Normal file
62
docker/envoy/envoy.yaml
Normal file
@@ -0,0 +1,62 @@
|
||||
static_resources:
|
||||
listeners:
|
||||
- name: listener_0
|
||||
address:
|
||||
socket_address:
|
||||
address: 0.0.0.0
|
||||
port_value: 80
|
||||
filter_chains:
|
||||
- filters:
|
||||
- name: envoy.filters.network.http_connection_manager
|
||||
typed_config:
|
||||
'@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
|
||||
stat_prefix: ingress_http
|
||||
route_config:
|
||||
name: local_route
|
||||
virtual_hosts:
|
||||
- name: backend
|
||||
domains: ['*']
|
||||
routes:
|
||||
- match:
|
||||
prefix: '/api'
|
||||
route:
|
||||
cluster: dynamic_server
|
||||
- match:
|
||||
prefix: '/'
|
||||
route:
|
||||
cluster: webapp
|
||||
http_filters:
|
||||
- name: envoy.filters.http.router
|
||||
typed_config:
|
||||
"@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
|
||||
|
||||
clusters:
|
||||
- name: dynamic_server
|
||||
connect_timeout: 0.25s
|
||||
type: STRICT_DNS
|
||||
dns_lookup_family: V4_ONLY
|
||||
lb_policy: ROUND_ROBIN
|
||||
load_assignment:
|
||||
cluster_name: dynamic_server
|
||||
endpoints:
|
||||
- lb_endpoints:
|
||||
- endpoint:
|
||||
address:
|
||||
socket_address:
|
||||
address: server
|
||||
port_value: 3000
|
||||
|
||||
- name: webapp
|
||||
connect_timeout: 0.25s
|
||||
type: STRICT_DNS
|
||||
dns_lookup_family: V4_ONLY
|
||||
lb_policy: ROUND_ROBIN
|
||||
load_assignment:
|
||||
cluster_name: webapp
|
||||
endpoints:
|
||||
- lb_endpoints:
|
||||
- endpoint:
|
||||
address:
|
||||
socket_address:
|
||||
address: webapp
|
||||
port_value: 80
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM ghcr.io/bigcapitalhq/server:latest as build
|
||||
FROM bigcapitalhq/server:latest as build
|
||||
|
||||
ARG DB_HOST= \
|
||||
DB_USER= \
|
||||
@@ -34,7 +34,5 @@ WORKDIR /app/packages/server
|
||||
|
||||
RUN git clone https://github.com/vishnubob/wait-for-it.git
|
||||
|
||||
ADD docker/migration/start.sh /
|
||||
RUN chmod +x /start.sh
|
||||
|
||||
CMD ["/start.sh"]
|
||||
# Once we listen the mysql port run the migration task.
|
||||
CMD ./wait-for-it/wait-for-it.sh mysql:3306 -- sh -c "node ./build/commands.js system:migrate:latest && node ./build/commands.js tenants:migrate:latest"
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
# Migrate the master system database.
|
||||
./wait-for-it/wait-for-it.sh mysql:3306 -- node ./build/commands.js system:migrate:latest
|
||||
|
||||
# Migrate all tenants.
|
||||
./wait-for-it/wait-for-it.sh mysql:3306 -- node ./build/commands.js tenants:migrate:latest
|
||||
@@ -1,21 +0,0 @@
|
||||
FROM nginx:1.11
|
||||
|
||||
RUN mkdir /etc/nginx/sites-available && rm /etc/nginx/conf.d/default.conf
|
||||
ADD nginx.conf /etc/nginx/
|
||||
|
||||
COPY scripts /root/scripts/
|
||||
COPY certs /etc/ssl/
|
||||
|
||||
COPY sites /etc/nginx/templates
|
||||
|
||||
ARG SERVER_PROXY_PORT=3000
|
||||
ARG WEB_SSL=false
|
||||
ARG SELF_SIGNED=false
|
||||
|
||||
ENV SERVER_PROXY_PORT=$SERVER_PROXY_PORT
|
||||
ENV WEB_SSL=$WEB_SSL
|
||||
ENV SELF_SIGNED=$SELF_SIGNED
|
||||
|
||||
RUN /bin/bash /root/scripts/build-nginx.sh
|
||||
|
||||
CMD nginx
|
||||
@@ -1,33 +0,0 @@
|
||||
user www-data;
|
||||
worker_processes auto;
|
||||
pid /run/nginx.pid;
|
||||
daemon off;
|
||||
|
||||
events {
|
||||
worker_connections 2048;
|
||||
use epoll;
|
||||
}
|
||||
|
||||
http {
|
||||
server_tokens off;
|
||||
sendfile on;
|
||||
tcp_nopush on;
|
||||
tcp_nodelay on;
|
||||
keepalive_timeout 15;
|
||||
types_hash_max_size 2048;
|
||||
client_max_body_size 20M;
|
||||
open_file_cache max=100;
|
||||
gzip on;
|
||||
gzip_disable "msie6";
|
||||
|
||||
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
|
||||
ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS';
|
||||
|
||||
include /etc/nginx/mime.types;
|
||||
default_type application/octet-stream;
|
||||
|
||||
include /etc/nginx/conf.d/*.conf;
|
||||
include /etc/nginx/sites-available/*;
|
||||
access_log /var/log/nginx/access.log;
|
||||
error_log /var/log/nginx/error.log;
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
for conf in /etc/nginx/templates/*.conf; do
|
||||
mv $conf "/etc/nginx/sites-available/"$(basename $conf) > /dev/null
|
||||
done
|
||||
|
||||
for template in /etc/nginx/templates/*.template; do
|
||||
envsubst < $template > "/etc/nginx/sites-available/"$(basename $template)".conf"
|
||||
done
|
||||
@@ -1,16 +0,0 @@
|
||||
server {
|
||||
listen 80 default_server;
|
||||
|
||||
location /api {
|
||||
proxy_pass http://server:${SERVER_PROXY_PORT};
|
||||
}
|
||||
|
||||
location / {
|
||||
proxy_pass http://webapp;
|
||||
}
|
||||
|
||||
location /.well-known/acme-challenge/ {
|
||||
root /var/www/letsencrypt/;
|
||||
log_not_found off;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM redis:4.0
|
||||
FROM redis:6.2.0
|
||||
|
||||
COPY redis.conf /usr/local/etc/redis/redis.conf
|
||||
|
||||
|
||||
24
launch.json
Normal file
24
launch.json
Normal file
@@ -0,0 +1,24 @@
|
||||
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"name": "Debug Nest Framework",
|
||||
"runtimeExecutable": "npm",
|
||||
"runtimeArgs": [
|
||||
"run",
|
||||
"start:debug",
|
||||
"--",
|
||||
"--inspect-brk"
|
||||
],
|
||||
"autoAttachChildProcesses": true,
|
||||
"restart": true,
|
||||
"sourceMaps": true,
|
||||
"stopOnEntry": false,
|
||||
"console": "integratedTerminal"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
{
|
||||
"$schema": "node_modules/lerna/schemas/lerna-schema.json",
|
||||
"useWorkspaces": true,
|
||||
"version": "0.9.6",
|
||||
"npmClient": "npm"
|
||||
"version": "independent",
|
||||
"npmClient": "pnpm",
|
||||
"packages": [
|
||||
"packages/*",
|
||||
"shared/*"
|
||||
]
|
||||
}
|
||||
|
||||
5730
package-lock.json
generated
5730
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
28
package.json
28
package.json
@@ -2,21 +2,19 @@
|
||||
"name": "bigcapital-monorepo",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"bootstrap": "lerna exec npm install",
|
||||
"dev": "lerna run dev",
|
||||
"build": "lerna run build",
|
||||
"dev:webapp": "lerna run dev --scope \"@bigcapital/webapp\"",
|
||||
"build:webapp": "lerna run build --scope \"@bigcapital/webapp\"",
|
||||
"dev:server": "lerna run dev --scope \"@bigcapital/server\"",
|
||||
"build:server": "lerna run build --scope \"@bigcapital/server\"",
|
||||
"serve:server": "lerna run serve --scope \"@bigcapital/server\"",
|
||||
"test:e2e": "playwright test",
|
||||
"dev:webapp": "lerna run dev --scope \"@bigcapital/webapp\" --scope \"@bigcapital/utils\" --scope \"@bigcapital/pdf-templates\"",
|
||||
"build:webapp": "lerna run build --scope \"@bigcapital/webapp\" --scope \"@bigcapital/utils\" --scope \"@bigcapital/pdf-templates\"",
|
||||
"dev:server": "lerna run dev --scope \"@bigcapital/server\" --scope \"@bigcapital/utils\" --scope \"@bigcapital/pdf-templates\" --scope \"@bigcapital/email-components\"",
|
||||
"build:server": "lerna run build --scope \"@bigcapital/server\" --scope \"@bigcapital/utils\" --scope \"@bigcapital/pdf-templates\" --scope \"@bigcapital/email-components\"",
|
||||
"serve:server": "lerna run serve --scope \"@bigcapital/server\" --scope \"@bigcapital/utils\"",
|
||||
"server2:start": "lerna run start:dev --scope \"@bigcapital/server2\"",
|
||||
"test:watch": "lerna run test:watch",
|
||||
"test:e2e": "lerna run test:e2e",
|
||||
"start:debug": "lerna run start:debug",
|
||||
"prepare": "husky install"
|
||||
},
|
||||
"workspaces": [
|
||||
"packages/*",
|
||||
"shared/*"
|
||||
],
|
||||
"devDependencies": {
|
||||
"@commitlint/cli": "^17.4.2",
|
||||
"@commitlint/config-conventional": "^17.4.2",
|
||||
@@ -24,14 +22,18 @@
|
||||
"@faker-js/faker": "^8.0.2",
|
||||
"@playwright/test": "^1.32.3",
|
||||
"husky": "^8.0.3",
|
||||
"lerna": "^6.4.1"
|
||||
"lerna": "^8.1.2",
|
||||
"pnpm": "^9.0.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": "14.x"
|
||||
"node": "16.x || 17.x || 18.x"
|
||||
},
|
||||
"husky": {
|
||||
"hooks": {
|
||||
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"tsup": "^8.3.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"presets": ["@babel/preset-env"],
|
||||
"retainLines": true,
|
||||
"plugins": [
|
||||
"@babel/plugin-transform-runtime",
|
||||
"@babel/plugin-syntax-dynamic-import"
|
||||
]
|
||||
}
|
||||
105
packages/server/.env.example
Normal file
105
packages/server/.env.example
Normal file
@@ -0,0 +1,105 @@
|
||||
# App
|
||||
APP_JWT_SECRET=123123
|
||||
|
||||
# Mail
|
||||
MAIL_HOST=
|
||||
MAIL_USERNAME=
|
||||
MAIL_PASSWORD=
|
||||
MAIL_PORT=
|
||||
MAIL_SECURE=
|
||||
MAIL_FROM_NAME=
|
||||
MAIL_FROM_ADDRESS=
|
||||
|
||||
# Database
|
||||
DB_HOST=localhost
|
||||
DB_USER=bigcapital
|
||||
DB_PASSWORD=bigcapital
|
||||
DB_ROOT_PASSWORD=root
|
||||
DB_CHARSET=utf8
|
||||
|
||||
# System database
|
||||
SYSTEM_DB_NAME=bigcapital_system
|
||||
# SYSTEM_DB_USER=
|
||||
# SYSTEM_DB_PASSWORD=
|
||||
# SYSTEM_DB_NAME=
|
||||
# SYSTEM_DB_CHARSET=
|
||||
|
||||
# Tenant databases
|
||||
TENANT_DB_NAME_PERFIX=bigcapital_tenant_
|
||||
# TENANT_DB_HOST=
|
||||
# TENANT_DB_USER=
|
||||
# TENANT_DB_PASSWORD=
|
||||
# TENANT_DB_CHARSET=
|
||||
|
||||
# Application
|
||||
BASE_URL=http://example.com
|
||||
JWT_SECRET=b0JDZW56RnV6aEthb0RGPXVEcUI
|
||||
|
||||
# Jobs MongoDB
|
||||
MONGODB_DATABASE_URL=mongodb://localhost/bigcapital
|
||||
|
||||
# App proxy
|
||||
PUBLIC_PROXY_PORT=80
|
||||
PUBLIC_PROXY_SSL_PORT=443
|
||||
|
||||
# Agendash
|
||||
AGENDASH_AUTH_USER=agendash
|
||||
AGENDASH_AUTH_PASSWORD=123123
|
||||
|
||||
# Sign-up restrictions
|
||||
SIGNUP_DISABLED=false
|
||||
SIGNUP_ALLOWED_DOMAINS=
|
||||
SIGNUP_ALLOWED_EMAILS=
|
||||
|
||||
# Sign-up Email Confirmation
|
||||
SIGNUP_EMAIL_CONFIRMATION=false
|
||||
|
||||
# API rate limit (points,duration,block duration).
|
||||
API_RATE_LIMIT=120,60,600
|
||||
|
||||
# Gotenberg API for PDF printing - (production).
|
||||
GOTENBERG_URL=http://gotenberg:3000
|
||||
GOTENBERG_DOCS_URL=http://server:3000/public/
|
||||
|
||||
# Gotenberg API - (development)
|
||||
# GOTENBERG_URL=http://localhost:9000
|
||||
# GOTENBERG_DOCS_URL=http://host.docker.internal:3000/public/
|
||||
|
||||
# Exchange Rate Service
|
||||
EXCHANGE_RATE_SERVICE=open-exchange-rate
|
||||
|
||||
# Open Exchange Rate
|
||||
OPEN_EXCHANGE_RATE_APP_ID=
|
||||
|
||||
# The Plaid environment to use ('sandbox' or 'development').
|
||||
# https://plaid.com/docs/#api-host
|
||||
PLAID_ENV=sandbox
|
||||
|
||||
# Your Plaid keys, which can be found in the Plaid Dashboard.
|
||||
# https://dashboard.plaid.com/account/keys
|
||||
PLAID_CLIENT_ID=
|
||||
PLAID_SECRET=
|
||||
PLAID_LINK_WEBHOOK=
|
||||
|
||||
# https://docs.lemonsqueezy.com/guides/developer-guide/getting-started#create-an-api-key
|
||||
LEMONSQUEEZY_API_KEY=
|
||||
LEMONSQUEEZY_STORE_ID=
|
||||
LEMONSQUEEZY_WEBHOOK_SECRET=
|
||||
|
||||
# S3 documents and attachments
|
||||
S3_REGION=US
|
||||
S3_ACCESS_KEY_ID=
|
||||
S3_SECRET_ACCESS_KEY=
|
||||
S3_ENDPOINT=
|
||||
S3_BUCKET=
|
||||
|
||||
# PostHog
|
||||
POSTHOG_API_KEY=
|
||||
POSTHOG_HOST=
|
||||
|
||||
# Stripe Payment
|
||||
STRIPE_PAYMENT_SECRET_KEY=
|
||||
STRIPE_PAYMENT_PUBLISHABLE_KEY=
|
||||
STRIPE_PAYMENT_CLIENT_ID=
|
||||
STRIPE_PAYMENT_WEBHOOKS_SECRET=
|
||||
STRIPE_PAYMENT_REDIRECT_URL=
|
||||
@@ -1,34 +1,25 @@
|
||||
module.exports = {
|
||||
env: {
|
||||
browser: true,
|
||||
es6: true,
|
||||
},
|
||||
extends: ['airbnb-base', 'airbnb-typescript'],
|
||||
parser: '@typescript-eslint/parser',
|
||||
parserOptions: {
|
||||
ecmaVersion: 2018,
|
||||
sourceType: 'module',
|
||||
project: 'tsconfig.json',
|
||||
tsconfigRootDir: './',
|
||||
tsconfigRootDir: __dirname,
|
||||
sourceType: 'module',
|
||||
},
|
||||
globals: {
|
||||
Atomics: 'readonly',
|
||||
SharedArrayBuffer: 'readonly',
|
||||
plugins: ['@typescript-eslint/eslint-plugin'],
|
||||
extends: [
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'plugin:prettier/recommended',
|
||||
],
|
||||
root: true,
|
||||
env: {
|
||||
node: true,
|
||||
jest: true,
|
||||
},
|
||||
plugins: ['import'],
|
||||
ignorePatterns: ['.eslintrc.js'],
|
||||
rules: {
|
||||
'import/no-unresolved': 'error',
|
||||
'import/prefer-default-export': 'off',
|
||||
},
|
||||
settings: {
|
||||
'import/parsers': {
|
||||
'@typescript-eslint/parser': ['.ts', '.tsx'],
|
||||
},
|
||||
'import/resolver': {
|
||||
typescript: {
|
||||
alwaysTryTypes: true,
|
||||
project: 'tsconfig.json',
|
||||
},
|
||||
},
|
||||
'@typescript-eslint/interface-name-prefix': 'off',
|
||||
'@typescript-eslint/explicit-function-return-type': 'off',
|
||||
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
},
|
||||
};
|
||||
|
||||
61
packages/server/.gitignore
vendored
61
packages/server/.gitignore
vendored
@@ -1,7 +1,56 @@
|
||||
/node_modules/
|
||||
/.env
|
||||
/storage
|
||||
package-lock.json
|
||||
stdout.log
|
||||
# compiled output
|
||||
/dist
|
||||
/build
|
||||
/node_modules
|
||||
/build
|
||||
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
pnpm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
|
||||
# Tests
|
||||
/coverage
|
||||
/.nyc_output
|
||||
|
||||
# IDEs and editors
|
||||
/.idea
|
||||
.project
|
||||
.classpath
|
||||
.c9/
|
||||
*.launch
|
||||
.settings/
|
||||
*.sublime-workspace
|
||||
|
||||
# IDE - VSCode
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
|
||||
# dotenv environment variable files
|
||||
.env
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
.env.local
|
||||
|
||||
# temp directory
|
||||
.temp
|
||||
.tmp
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
|
||||
4
packages/server/.prettierrc
Normal file
4
packages/server/.prettierrc
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"singleQuote": true,
|
||||
"trailingComma": "all"
|
||||
}
|
||||
1
packages/server/.todo
Normal file
1
packages/server/.todo
Normal file
@@ -0,0 +1 @@
|
||||
- Build authentication services.
|
||||
@@ -1,101 +0,0 @@
|
||||
FROM node:14.20-alpine as build
|
||||
|
||||
USER root
|
||||
|
||||
ARG MAIL_HOST= \
|
||||
MAIL_USERNAME= \
|
||||
MAIL_PASSWORD= \
|
||||
MAIL_PORT= \
|
||||
MAIL_SECURE= \
|
||||
MAIL_FROM_NAME= \
|
||||
MAIL_FROM_ADDRESS= \
|
||||
# Database
|
||||
DB_HOST= \
|
||||
DB_USER= \
|
||||
DB_PASSWORD= \
|
||||
DB_CHARSET= \
|
||||
# System database.
|
||||
SYSTEM_DB_NAME= \
|
||||
SYSTEM_DB_PASSWORD= \
|
||||
SYSTEM_DB_USER= \
|
||||
SYSTEM_DB_HOST= \
|
||||
SYSTEM_DB_CHARSET= \
|
||||
# Tenant databases.
|
||||
TENANT_DB_USER= \
|
||||
TENANT_DB_PASSWORD= \
|
||||
TENANT_DB_HOST= \
|
||||
TENANT_DB_NAME_PERFIX= \
|
||||
TENANT_DB_CHARSET= \
|
||||
# MongoDB
|
||||
MONGODB_DATABASE_URL= \
|
||||
# Authentication
|
||||
JWT_SECRET= \
|
||||
# Application
|
||||
BASE_URL= \
|
||||
# Agendash
|
||||
AGENDASH_AUTH_USER=agendash \
|
||||
AGENDASH_AUTH_PASSWORD=123123 \
|
||||
# Sign-up restriction
|
||||
SIGNUP_DISABLED= \
|
||||
SIGNUP_ALLOWED_DOMAINS= \
|
||||
SIGNUP_ALLOWED_EMAILS=
|
||||
|
||||
ENV MAIL_HOST=$MAIL_HOST \
|
||||
MAIL_USERNAME=$MAIL_USERNAME \
|
||||
MAIL_PASSWORD=$MAIL_PASSWORD \
|
||||
MAIL_PORT=$MAIL_PORT \
|
||||
MAIL_SECURE=$MAIL_SECURE \
|
||||
MAIL_FROM_NAME=$MAIL_FROM_NAME \
|
||||
MAIL_FROM_ADDRESS=$MAIL_FROM_ADDRESS \
|
||||
# Database
|
||||
DB_HOST=$DB_HOST \
|
||||
DB_USER=$DB_USER \
|
||||
DB_PASSWORD=$DB_PASSWORD \
|
||||
DB_CHARSET=$DB_CHARSET \
|
||||
# System database.
|
||||
SYSTEM_DB_HOST=$SYSTEM_DB_HOST \
|
||||
SYSTEM_DB_USER=$SYSTEM_DB_USER \
|
||||
SYSTEM_DB_PASSWORD=$SYSTEM_DB_PASSWORD \
|
||||
SYSTEM_DB_NAME=$SYSTEM_DB_NAME \
|
||||
SYSTEM_DB_CHARSET=$SYSTEM_DB_CHARSET \
|
||||
# Tenant databases.
|
||||
TENANT_DB_NAME_PERFIX=$TENANT_DB_NAME_PERFIX \
|
||||
TENANT_DB_HOST=$TENANT_DB_HOST \
|
||||
TENANT_DB_PASSWORD=$TENANT_DB_PASSWORD \
|
||||
TENANT_DB_USER=$TENANT_DB_USER \
|
||||
TENANT_DB_CHARSET=$TENANT_DB_CHARSET \
|
||||
# Authentication
|
||||
JWT_SECRET=$JWT_SECRET \
|
||||
# Agendash
|
||||
AGENDASH_AUTH_USER=$AGENDASH_AUTH_USER \
|
||||
AGENDASH_AUTH_PASSWORD=$AGENDASH_AUTH_PASSWORD \
|
||||
# MongoDB
|
||||
MONGODB_DATABASE_URL=$MONGODB_DATABASE_URL \
|
||||
# Application
|
||||
BASE_URL=$BASE_URL \
|
||||
# Sign-up restriction
|
||||
SIGNUP_DISABLED=$SIGNUP_DISABLED \
|
||||
SIGNUP_ALLOWED_DOMAINS=$SIGNUP_ALLOWED_DOMAINS \
|
||||
SIGNUP_ALLOWED_EMAILS=$SIGNUP_ALLOWED_EMAILS
|
||||
|
||||
# Create app directory.
|
||||
WORKDIR /app
|
||||
|
||||
RUN chown node:node /
|
||||
|
||||
# Copy application dependency manifests to the container image.
|
||||
COPY ./package*.json ./
|
||||
COPY ./packages/server/package*.json ./packages/server/
|
||||
|
||||
COPY ./lerna.json ./lerna.json
|
||||
|
||||
# Install app dependencies for production.
|
||||
RUN npm install
|
||||
RUN npm run bootstrap
|
||||
|
||||
COPY --chown=node:node ./packages/server ./packages/server
|
||||
|
||||
# # Creates a "dist" folder with the production build
|
||||
RUN npm run build:server --skip-nx-cache
|
||||
|
||||
CMD [ "node", "./packages/server/build/index.js" ]
|
||||
@@ -1 +1 @@
|
||||
# @bigcapital/server
|
||||
## @bigcapitalhq/server
|
||||
@@ -1,17 +0,0 @@
|
||||
const { knexSnakeCaseMappers } = require('objection');
|
||||
|
||||
module.exports = {
|
||||
client: 'mysql',
|
||||
connection: {
|
||||
host: '127.0.0.1',
|
||||
user: 'root',
|
||||
password: 'root',
|
||||
database: 'bigcapital_tenant_hqde5zqkylsho06',
|
||||
charset: 'utf8',
|
||||
},
|
||||
migrations: {
|
||||
directory: './src/database/migrations',
|
||||
},
|
||||
pool: { min: 0, max: 7 },
|
||||
...knexSnakeCaseMappers({ upperCase: true }),
|
||||
};
|
||||
11
packages/server/nest-cli.json
Normal file
11
packages/server/nest-cli.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/nest-cli",
|
||||
"collection": "@nestjs/schematics",
|
||||
"sourceRoot": "src",
|
||||
"compilerOptions": {
|
||||
"deleteOutDir": true,
|
||||
"assets": [
|
||||
{ "include": "i18n/**/*", "watchAssets": true }
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,147 +1,152 @@
|
||||
{
|
||||
"name": "@bigcapital/server",
|
||||
"version": "0.10.0",
|
||||
"name": "@bigcapital/server2",
|
||||
"version": "0.0.1",
|
||||
"description": "",
|
||||
"main": "src/server.ts",
|
||||
"author": "",
|
||||
"private": true,
|
||||
"license": "UNLICENSED",
|
||||
"scripts": {
|
||||
"inspect": "cross-env NODE_PATH=./src nodemon src/server.ts",
|
||||
"clear": "rimraf build",
|
||||
"dev": "cross-env NODE_ENV=development webpack --config scripts/webpack.config.js",
|
||||
"build:resources": "gulp --gulpfile=scripts/gulpfile.js styles styles-rtl",
|
||||
"build:app": "cross-env NODE_ENV=production webpack --config scripts/webpack.config.js",
|
||||
"build:commands": "cross-env NODE_ENV=production webpack --config scripts/webpack.cli.js",
|
||||
"build": "npm-run-all build:*",
|
||||
"serve": "node ./build/index.js",
|
||||
"lint:fix": "eslint --fix ./**/*.ts"
|
||||
},
|
||||
"author": "Ahmed Bouhuolia, <a.bouhuolia@gmail.com>",
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
"bigcapital": "./bin/bigcapital.js"
|
||||
"build": "nest build",
|
||||
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
|
||||
"start": "nest start",
|
||||
"start:dev": "nest start --watch",
|
||||
"start:debug": "nest start --debug --watch",
|
||||
"start:prod": "node dist/main",
|
||||
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
|
||||
"test": "jest",
|
||||
"test:watch": "jest --watch",
|
||||
"test:cov": "jest --coverage",
|
||||
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
|
||||
"test:e2e": "jest --config ./test/jest-e2e.json --watchAll"
|
||||
},
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-s3": "^3.576.0",
|
||||
"@aws-sdk/s3-request-presigner": "^3.583.0",
|
||||
"@bigcapital/email-components": "*",
|
||||
"@bigcapital/pdf-templates": "*",
|
||||
"@bigcapital/utils": "*",
|
||||
"@casl/ability": "^5.4.3",
|
||||
"@hapi/boom": "^7.4.3",
|
||||
"@types/i18n": "^0.8.7",
|
||||
"@types/knex": "^0.16.1",
|
||||
"@types/mathjs": "^6.0.12",
|
||||
"accepts": "^1.3.7",
|
||||
"@lemonsqueezy/lemonsqueezy.js": "^2.2.0",
|
||||
"@liaoliaots/nestjs-redis": "^10.0.0",
|
||||
"@nestjs/bull": "^10.2.1",
|
||||
"@nestjs/bullmq": "^10.2.2",
|
||||
"@nestjs/cache-manager": "^2.2.2",
|
||||
"@nestjs/common": "^10.0.0",
|
||||
"@nestjs/config": "^3.2.3",
|
||||
"@nestjs/core": "^10.0.0",
|
||||
"@nestjs/event-emitter": "^2.0.4",
|
||||
"@nestjs/jwt": "^10.2.0",
|
||||
"@nestjs/passport": "^11.0.5",
|
||||
"@nestjs/platform-express": "^10.0.0",
|
||||
"@nestjs/schedule": "^4.1.2",
|
||||
"@nestjs/swagger": "^7.4.2",
|
||||
"@nestjs/throttler": "^6.2.1",
|
||||
"@supercharge/promise-pool": "^3.2.0",
|
||||
"@types/multer": "^1.4.11",
|
||||
"@types/nodemailer": "^6.4.17",
|
||||
"@types/passport-local": "^1.0.38",
|
||||
"@types/ramda": "^0.30.2",
|
||||
"accounting": "^0.4.1",
|
||||
"agenda": "^4.2.1",
|
||||
"agendash": "^3.1.0",
|
||||
"app-root-path": "^3.0.0",
|
||||
"async": "^3.2.0",
|
||||
"axios": "^0.20.0",
|
||||
"babel-loader": "^9.1.2",
|
||||
"async-mutex": "^0.5.0",
|
||||
"axios": "^1.6.0",
|
||||
"bcrypt": "^5.1.1",
|
||||
"bcryptjs": "^2.4.3",
|
||||
"bluebird": "^3.7.2",
|
||||
"compression": "^1.7.4",
|
||||
"country-codes-list": "^1.6.8",
|
||||
"cpy": "^8.1.2",
|
||||
"cpy-cli": "^3.1.1",
|
||||
"crypto-random-string": "^3.2.0",
|
||||
"csurf": "^1.10.0",
|
||||
"deep-map": "^2.0.0",
|
||||
"deepdash": "^5.3.7",
|
||||
"dotenv": "^8.1.0",
|
||||
"errorhandler": "^1.5.1",
|
||||
"es6-weak-map": "^2.0.3",
|
||||
"esm": "^3.2.25",
|
||||
"event-dispatch": "^0.4.1",
|
||||
"eventemitter2": "^6.4.5",
|
||||
"express": "^4.17.1",
|
||||
"express-basic-auth": "^1.2.0",
|
||||
"express-boom": "^3.0.0",
|
||||
"express-fileupload": "^1.1.7-alpha.3",
|
||||
"express-oauth-server": "^2.0.0",
|
||||
"express-validator": "^6.12.2",
|
||||
"gulp": "^4.0.2",
|
||||
"gulp-sass": "^5.0.0",
|
||||
"helmet": "^3.21.0",
|
||||
"i18n": "^0.13.3",
|
||||
"bull": "^4.16.3",
|
||||
"bullmq": "^5.25.6",
|
||||
"cache-manager": "^6.1.1",
|
||||
"cache-manager-redis-store": "^3.0.1",
|
||||
"class-transformer": "^0.5.1",
|
||||
"class-validator": "^0.14.1",
|
||||
"deepdash": "^5.3.9",
|
||||
"express-validator": "^7.2.0",
|
||||
"form-data": "^4.0.0",
|
||||
"fp-ts": "^2.16.9",
|
||||
"ioredis": "^5.6.0",
|
||||
"is-my-json-valid": "^2.20.5",
|
||||
"js-money": "^0.6.3",
|
||||
"jsonwebtoken": "^8.5.1",
|
||||
"knex": "^0.95.15",
|
||||
"knex-cleaner": "^1.3.0",
|
||||
"knex-db-manager": "^0.6.1",
|
||||
"libphonenumber-js": "^1.9.6",
|
||||
"lodash": "^4.17.15",
|
||||
"knex": "^3.1.0",
|
||||
"lamda": "^0.4.1",
|
||||
"lodash": "^4.17.21",
|
||||
"lru-cache": "^6.0.0",
|
||||
"mathjs": "^9.4.0",
|
||||
"memory-cache": "^0.2.0",
|
||||
"moment": "^2.24.0",
|
||||
"mime-types": "^2.1.35",
|
||||
"moment": "^2.30.1",
|
||||
"moment-range": "^4.0.2",
|
||||
"mongoose": "^5.10.0",
|
||||
"mustache": "^3.0.3",
|
||||
"mysql": "^2.17.1",
|
||||
"mysql2": "^1.6.5",
|
||||
"node-cache": "^4.2.1",
|
||||
"moment-timezone": "^0.5.43",
|
||||
"multer": "1.4.5-lts.1",
|
||||
"multer-s3": "^3.0.1",
|
||||
"mysql": "^2.18.1",
|
||||
"mysql2": "^3.11.3",
|
||||
"nestjs-cls": "^5.2.0",
|
||||
"nestjs-i18n": "^10.4.9",
|
||||
"nestjs-redis": "^1.3.3",
|
||||
"nodemailer": "^6.3.0",
|
||||
"nodemon": "^1.19.1",
|
||||
"object-hash": "^2.0.3",
|
||||
"objection": "^3.0.0",
|
||||
"objection-filter": "^4.0.1",
|
||||
"objection-soft-delete": "^1.0.7",
|
||||
"objection-unique": "^1.2.2",
|
||||
"objection": "^3.1.5",
|
||||
"passport": "^0.7.0",
|
||||
"passport-jwt": "^4.0.1",
|
||||
"passport-local": "^1.0.0",
|
||||
"plaid": "^10.3.0",
|
||||
"pluralize": "^8.0.0",
|
||||
"posthog-node": "^4.3.2",
|
||||
"pug": "^3.0.2",
|
||||
"puppeteer": "^10.2.0",
|
||||
"qim": "0.0.52",
|
||||
"ramda": "^0.27.1",
|
||||
"rate-limiter-flexible": "^2.1.14",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"rtl-detect": "^1.0.4",
|
||||
"ts-transformer-keys": "^0.4.2",
|
||||
"tsyringe": "^4.3.0",
|
||||
"typedi": "^0.8.0",
|
||||
"ramda": "^0.30.1",
|
||||
"redis": "^4.7.0",
|
||||
"reflect-metadata": "^0.2.0",
|
||||
"remeda": "^2.19.2",
|
||||
"rxjs": "^7.8.1",
|
||||
"serialize-interceptor": "^1.1.7",
|
||||
"strategy": "^1.1.1",
|
||||
"stripe": "^16.10.0",
|
||||
"uniqid": "^5.2.0",
|
||||
"winston": "^3.2.1"
|
||||
"uuid": "^10.0.0",
|
||||
"xlsx": "^0.18.5",
|
||||
"yup": "^0.28.1",
|
||||
"zod": "^3.23.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/lodash": "^4.14.158",
|
||||
"@types/ramda": "^0.27.64",
|
||||
"@typescript-eslint/eslint-plugin": "^5.50.0",
|
||||
"@typescript-eslint/parser": "^5.50.0",
|
||||
"chai": "^4.2.0",
|
||||
"chai-http": "^4.3.0",
|
||||
"chai-things": "^0.2.0",
|
||||
"colorette": "^1.2.0",
|
||||
"commander": "^5.0.0",
|
||||
"cross-env": "^5.2.0",
|
||||
"eslint": "^8.33.0",
|
||||
"eslint-config-airbnb-base": "^15.0.0",
|
||||
"eslint-config-airbnb-typescript": "^17.0.0",
|
||||
"eslint-friendly-formatter": "^4.0.1",
|
||||
"eslint-import-resolver-typescript": "^3.5.3",
|
||||
"eslint-import-resolver-webpack": "^0.11.1",
|
||||
"eslint-loader": "^2.2.1",
|
||||
"eslint-plugin-import": "^2.27.5",
|
||||
"faker": "^4.1.0",
|
||||
"getopts": "^2.2.5",
|
||||
"gulp-postcss": "^9.0.0",
|
||||
"gulp-rename": "^2.0.0",
|
||||
"knex-factory": "0.0.6",
|
||||
"merge-stream": "^2.0.0",
|
||||
"mocha": "^5.2.0",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"nyc": "^14.1.1",
|
||||
"progress-bar-webpack-plugin": "^2.1.0",
|
||||
"regenerator-runtime": "^0.13.7",
|
||||
"rimraf": "^3.0.2",
|
||||
"rtlcss": "^3.3.0",
|
||||
"run-script-webpack-plugin": "^0.1.1",
|
||||
"sass": "^1.37.5",
|
||||
"sinon": "^7.4.2",
|
||||
"start-server-webpack-plugin": "^2.2.5",
|
||||
"ts-loader": "^9.4.2",
|
||||
"ts-node": "^9.0.0",
|
||||
"tsconfig-paths-webpack-plugin": "^4.0.0",
|
||||
"typescript": "^3.9.7",
|
||||
"webpack": "^5.75.0",
|
||||
"webpack-cli": "^4.10.0",
|
||||
"webpack-merge": "^5.8.0",
|
||||
"webpack-node-externals": "^3.0.0",
|
||||
"webpack-watch-changed": "^1.0.0"
|
||||
"@nestjs/cli": "^10.0.0",
|
||||
"@nestjs/schematics": "^10.0.0",
|
||||
"@nestjs/testing": "^10.0.0",
|
||||
"@types/express": "^5.0.0",
|
||||
"@types/jest": "^29.5.2",
|
||||
"@types/mathjs": "^6.0.12",
|
||||
"@types/node": "^20.3.1",
|
||||
"@types/supertest": "^6.0.0",
|
||||
"@types/yup": "^0.29.13",
|
||||
"@typescript-eslint/eslint-plugin": "^8.0.0",
|
||||
"@typescript-eslint/parser": "^8.0.0",
|
||||
"eslint": "^9.0.0",
|
||||
"eslint-config-prettier": "^9.0.0",
|
||||
"eslint-plugin-prettier": "^5.0.0",
|
||||
"jest": "^29.5.0",
|
||||
"mustache": "^3.0.3",
|
||||
"prettier": "^3.0.0",
|
||||
"source-map-support": "^0.5.21",
|
||||
"supertest": "^7.0.0",
|
||||
"ts-jest": "^29.1.0",
|
||||
"ts-loader": "^9.4.3",
|
||||
"ts-node": "^10.9.1",
|
||||
"tsconfig-paths": "^4.2.0",
|
||||
"typescript": "^5.1.3"
|
||||
},
|
||||
"jest": {
|
||||
"moduleFileExtensions": [
|
||||
"js",
|
||||
"json",
|
||||
"ts"
|
||||
],
|
||||
"rootDir": "src",
|
||||
"testRegex": ".*\\.spec\\.ts$",
|
||||
"transform": {
|
||||
"^.+\\.(t|j)s$": "ts-jest"
|
||||
},
|
||||
"collectCoverageFrom": [
|
||||
"**/*.(t|j)s"
|
||||
],
|
||||
"coverageDirectory": "../coverage",
|
||||
"testEnvironment": "node"
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,640 +0,0 @@
|
||||
{
|
||||
"Petty Cash": "العهدة",
|
||||
"Cash": "النقدية",
|
||||
"Bank": "المصرف",
|
||||
"Other Income": "إيرادات اخري",
|
||||
"Interest Income": "إيرادات الفوائد",
|
||||
"Depreciation Expense": "مصاريف الاهلاك",
|
||||
"Interest Expense": "مصروفات الفوائد",
|
||||
"Sales of Product Income": "مبيعات دخل المنتجات",
|
||||
"Inventory Asset": "المخزون",
|
||||
"Cost of Goods Sold (COGS)": "تكلفة البضائع المباعة (COGS)",
|
||||
"Cost of Goods Sold": "تكلفة البضاعة المباعة",
|
||||
"Accounts Payable": "الذمم الدائنة",
|
||||
"Other Expense": "مصاريف أخرى",
|
||||
"Payroll Expenses": "مصاريف المرتبات",
|
||||
"Fixed Asset": "أصول ثابتة",
|
||||
"Credit Card": "بطاقة إئتمان",
|
||||
"Non-Current Asset": "أصول غير متداولة",
|
||||
"Current Asset": "أصول متداولة",
|
||||
"Other Asset": "أصول اخري",
|
||||
"Long Term Liability": "التزامات طويلة الاجل",
|
||||
"Current Liability": "التزامات قصيرة الاجل",
|
||||
"Other Liability": "التزمات اخري",
|
||||
"Equity": "حقوق الملكية",
|
||||
"Expense": "مصروف",
|
||||
"Income": "إيراد",
|
||||
"Accounts Receivable (A/R)": "الذمم المدينة",
|
||||
"Accounts Receivable": "الذمم المدينة",
|
||||
"Accounts Payable (A/P)": "الذمم الدائنة",
|
||||
"Inactive": "غير نشط",
|
||||
"Other Current Asset": "أصول متداولة اخرى",
|
||||
"Tax Payable": "الضريبة المستحقة",
|
||||
"Other Current Liability": "التزامات قصيرة الأجر اخرى",
|
||||
"Non-Current Liability": "التزامات طويلة الأجر",
|
||||
"Assets": "أصول",
|
||||
"Liabilities": "الالتزمات",
|
||||
"Account name": "أسم الحساب",
|
||||
"Account type": "نوع الحساب",
|
||||
"Account normal": "حساب عادي",
|
||||
"Description": "وصف",
|
||||
"Account code": "رمز الحساب",
|
||||
"Currency": "عملة",
|
||||
"Balance": "توازن",
|
||||
"Active": "نشيط",
|
||||
"Created at": "أنشئت في",
|
||||
"fixed_asset": "أصل ثابت",
|
||||
"Journal": "قيد",
|
||||
"Reconciliation": "تسوية",
|
||||
"Credit": "دائن",
|
||||
"Debit": "مدين",
|
||||
"Interest": "فائدة",
|
||||
"Depreciation": "اهلاك",
|
||||
"Payroll": "كشف رواتب",
|
||||
"Type": "نوع",
|
||||
"Name": "الأسم",
|
||||
"Sellable": "قابل للبيع",
|
||||
"Purchasable": "قابل للشراء",
|
||||
"Sell price": "سعر البيع",
|
||||
"Cost price": "سعر الكلفة",
|
||||
"User": "المستخدم",
|
||||
"Category": "تصنيف",
|
||||
"Note": "ملحوظة",
|
||||
"Quantity on hand": "كمية في اليد",
|
||||
"Purchase description": "وصف الشراء",
|
||||
"Sell description": "وصف البيع",
|
||||
"Sell account": "حساب البيع",
|
||||
"Cost account": "حساب التكلفة",
|
||||
"Inventory account": "حساب المخزون",
|
||||
"Payment date": "تاريخ الدفع",
|
||||
"Payment account": "حساب الدفع",
|
||||
"Amount": "كمية",
|
||||
"Reference No.": "رقم المرجع.",
|
||||
"Published": "نشرت",
|
||||
"Journal number": "رقم القيد",
|
||||
"Status": "حالة",
|
||||
"Journal type": "نوع القيد",
|
||||
"Date": "تاريخ",
|
||||
"Asset": "أصل",
|
||||
"Liability": "التزام",
|
||||
"First-in first-out (FIFO)": "الوارد أولاً يصرف أولاً (FIFO)",
|
||||
"Last-in first-out (LIFO)": "الوارد أخيرًا يصرف أولاً (LIFO)",
|
||||
"Average rate": "المعدل المتوسط",
|
||||
"Total": "الإجمالي",
|
||||
"Transaction type": "نوع المعاملة",
|
||||
"Transaction #": "عملية #",
|
||||
"Running Value": "القيمة الجارية",
|
||||
"Running quantity": "الكمية الجارية",
|
||||
"Profit Margin": "هامش الربح",
|
||||
"Value": "القيمة",
|
||||
"Rate": "السعر",
|
||||
"OPERATING ACTIVITIES": "الأنشطة التشغيلية",
|
||||
"FINANCIAL ACTIVITIES": "الأنشطة التمويلية",
|
||||
"INVESTMENT ACTIVITIES": "الانشطة الاستثمارية",
|
||||
"Net income": "صافي الدخل",
|
||||
"Adjustments net income by operating activities.": "تسويات صافي الدخل من الأنشطة التشغيلية.",
|
||||
"Net cash provided by operating activities": "صافي التدفقات النقدية من أنشطة التشغيل",
|
||||
"Net cash provided by investing activities": "صافي التدفقات النقدية من أنشطة الاستثمار",
|
||||
"Net cash provided by financing activities": "صافي التدفقات النقدية من أنشطة التمويلية",
|
||||
"Cash at beginning of period": "التدفقات النقدية في بداية الفترة",
|
||||
"NET CASH INCREASE FOR PERIOD": "زيادة التدفقات النقدية للفترة",
|
||||
"CASH AT END OF PERIOD": "صافي التدفقات النقدية في نهاية الفترة",
|
||||
"Expenses": "مصاريف",
|
||||
"Services": "خدمات",
|
||||
"Inventory": "المخزون",
|
||||
"Non Inventory": "غير المخزون",
|
||||
"Draft": "مسودة",
|
||||
"Delivered": "تم التوصيل",
|
||||
"Overdue": "متأخر",
|
||||
"Partially paid": "المدفوعة جزئيا",
|
||||
"Paid": "مدفوع",
|
||||
"Opened": "افتتح",
|
||||
"Unpaid": "غير مدفوعة",
|
||||
"Approved": "وافق",
|
||||
"Rejected": "مرفوض",
|
||||
"Invoiced": "مفوترة",
|
||||
"Expired": "منتهي الصلاحية",
|
||||
"Closed": "مغلق",
|
||||
"Manual journal": "قيد اليدوي",
|
||||
"Owner contribution": "زيادة رأس المال",
|
||||
"Transfer to account": "تحويل إلى الحساب",
|
||||
"Transfer from account": "تحويل من الحساب",
|
||||
"Other income": "إيراد اخر",
|
||||
"Other expense": "مصاريف أخرى",
|
||||
"Owner drawing": "سحب رأس المال",
|
||||
"Inventory adjustment": "تسوية المخزون",
|
||||
"Customer opening balance": "الرصيد الافتتاحي للزبون",
|
||||
"Vendor opening balance": "رصيد افتتاحي للمورد",
|
||||
"Payment made": "سند الزبون",
|
||||
"Bill": "فاتورة الشراء",
|
||||
"Payment receive": "استلام الدفع",
|
||||
"Sale receipt": "إيصال البيع",
|
||||
"Sale invoice": "فاتورة البيع",
|
||||
"Quantity": "الكمية",
|
||||
"Bank Account": "حساب البنك",
|
||||
"Saving Bank Account": "حساب التوفير البنكي",
|
||||
"Undeposited Funds": "الأموال غير المودعة",
|
||||
"Computer Equipment": "معدات كمبيوتر",
|
||||
"Office Equipment": "معدات مكتبية",
|
||||
"Uncategorized Income": "الدخل غير مصنف",
|
||||
"Sales of Service Income": "دخل مبيعات الخدمات",
|
||||
"Bank Fees and Charges": "رسوم المصرفية",
|
||||
"Exchange Gain or Loss": "ربح أو خسارة فروقات الصرف",
|
||||
"Rent": "إيجار",
|
||||
"Office expenses": "مصاريف المكتب",
|
||||
"Other Expenses": "مصاريف اخري",
|
||||
"Drawings": "السحوبات",
|
||||
"Owner's Equity": "حقوق الملكية",
|
||||
"Opening Balance Equity": "الارصدة الافتتاحية ",
|
||||
"Retained Earnings": "الأرباح المحتجزة",
|
||||
"Sales Tax Payable": "ضريبة المبيعات المستحقة",
|
||||
"Revenue Received in Advance": "الإيرادات المقبوضة مقدما",
|
||||
"Opening Balance Liabilities": "رصيد الالتزامات الافتتاحي",
|
||||
"Loan": "اقراض",
|
||||
"Owner A Drawings": "مسحوبات المالك",
|
||||
"An account that holds valuation of products or goods that available for sale.": "حساب يحمل قيم مخزون البضاعة أو السلع المتاحة للبيع.",
|
||||
"Tracks the gain and losses of the exchange differences.": "يسجل مكاسب وخسائر فروق الصرف.",
|
||||
"Any bank fees levied is recorded into the bank fees and charges account. A bank account maintenance fee, transaction charges, a late payment fee are some examples.": "يتم تسجيل أي رسوم مصرفية يتم فرضها في حساب الرسوم والمصروفات البنكية. ومن الأمثلة على ذلك رسوم صيانة الحساب المصرفي ورسوم المعاملات ورسوم الدفع المتأخر.",
|
||||
"The income activities are not associated to the core business.": "لا ترتبط انشطة الدخل إلى الأعمال الأساسية.",
|
||||
"Cash and cash equivalents": "النقد والنقد المكافئ",
|
||||
"Inventories": "مخزون البضاعة",
|
||||
"Other current assets": "الأصول متداولة الأخرى",
|
||||
"Non-Current Assets": "أصول غير المتداولة",
|
||||
"Current Liabilties": "التزامات متداولة",
|
||||
"Long-Term Liabilities": "التزامات طويلة الاجل",
|
||||
"Non-Current Liabilities": "التزامات غير متداولة",
|
||||
"Liabilities and Equity": "التزامات وحقوق الملكية",
|
||||
"Closing balance": "الرصيد الختامي",
|
||||
"Opening balance": "الرصيد الفتاحي",
|
||||
"Total {{accountName}}": "إجمالي {{accountName}}",
|
||||
|
||||
"invoice.paper.invoice": "فاتورة",
|
||||
"invoice.paper.due_amount": "القيمة المستحقة",
|
||||
"invoice.paper.billed_to": "فاتورة إلي",
|
||||
"invoice.paper.invoice_date": "تاريخ الفاتورة",
|
||||
"invoice.paper.invoice_number": "رقم الفاتورة",
|
||||
"invoice.paper.due_date": "تاريخ الاستحقاق",
|
||||
"invoice.paper.conditions_title": "الشروط والأحكام",
|
||||
"invoice.paper.notes_title": "ملاحظات",
|
||||
"invoice.paper.total": "المجموع",
|
||||
"invoice.paper.balance_due": "مبلغ المستحق",
|
||||
"invoice.paper.payment_amount": "مبلغ المدفوع",
|
||||
"invoice.paper.invoice_amount": "قيمة الفاتورة",
|
||||
|
||||
"item_entry.paper.item_name": "اسم الصنف",
|
||||
"item_entry.paper.rate": "السعر",
|
||||
"item_entry.paper.quantity": "الكمية",
|
||||
"item_entry.paper.total": "إجمالي",
|
||||
|
||||
"estimate.paper.estimate": "عرض أسعار",
|
||||
"estimate.paper.billed_to": "عرض أسعار إلي",
|
||||
"estimate.paper.estimate_date": "تاريخ العرض",
|
||||
"estimate.paper.estimate_number": "رقم العرض",
|
||||
"estimate.paper.expiration_date": "تاريخ انتهاء الصلاحية",
|
||||
"estimate.paper.conditions_title": "الشروط والأحكام",
|
||||
"estimate.paper.notes_title": "ملاحظات",
|
||||
"estimate.paper.amount": "قيمة العرض",
|
||||
"estimate.paper.subtotal": "المجموع",
|
||||
"estimate.paper.total": "إجمالي",
|
||||
"estimate.paper.estimate_amount": "قيمة العرض",
|
||||
|
||||
"receipt.paper.receipt": "إيصال",
|
||||
"receipt.paper.billed_to": "الإيصال إلي",
|
||||
"receipt.paper.receipt_date": "تاريخ الإيصال",
|
||||
"receipt.paper.receipt_number": "رقم الإيصال",
|
||||
"receipt.paper.conditions_title": "الشروط والأحكام",
|
||||
"receipt.paper.notes_title": "ملاحظات",
|
||||
"receipt.paper.receipt_amount": "قيمة الإيصال",
|
||||
"receipt.paper.total": "إجمالي",
|
||||
"receipt.paper.payment_amount": "مبلغ المدفوع",
|
||||
"receipt.paper.balance_due": "مبلغ المستحق",
|
||||
"receipt.paper.statement": "البيان",
|
||||
"receipt.paper.notes": "ملاحظات",
|
||||
|
||||
"payment.paper.payment_receipt": "إيصال قبض",
|
||||
"payment.paper.amount_received": "القيمة المستلمه",
|
||||
"payment.paper.billed_to": "إيصال إلي",
|
||||
"payment.paper.payment_date": "تاريخ الدفع",
|
||||
"payment.paper.invoice_number": "رقم الفاتورة",
|
||||
"payment.paper.invoice_date": "تاريخ الفاتورة",
|
||||
"payment.paper.invoice_amount": "قيمة الفاتورة",
|
||||
"payment.paper.payment_amount": "قيمة الدفع",
|
||||
"payment.paper.balance_due": "المبلغ المستحق",
|
||||
"payment.paper.statement": "البيان",
|
||||
|
||||
"credit.paper.credit_note": "اشعار دائن",
|
||||
"credit.paper.amount": "قيمة الاشعار",
|
||||
"credit.paper.remaining": "رصيد المتبقي",
|
||||
"credit.paper.billed_to": "إيصال إلي",
|
||||
"credit.paper.credit_date": "تاريخ الاشعار",
|
||||
"credit.paper.terms_conditions": "الشروط والاحكام",
|
||||
"credit.paper.notes": "ملاحظات",
|
||||
"credit.paper.total": "إجمالي",
|
||||
"credit.paper.credits_used": "قيمة المستخدمه",
|
||||
"credit.paper.credits_remaining": "قيمة المتبقية",
|
||||
|
||||
"account.field.name": "إسم الحساب",
|
||||
"account.field.description": "الوصف",
|
||||
"account.field.slug": "Account slug",
|
||||
"account.field.code": "رقم الحساب",
|
||||
"account.field.root_type": "جذر الحساب",
|
||||
"account.field.normal": "طبيعة الحساب",
|
||||
"account.field.normal.credit": "دائن",
|
||||
"account.field.normal.debit": "مدين",
|
||||
"account.field.type": "نوع الحساب",
|
||||
"account.field.active": "Activity",
|
||||
"account.field.balance": "الرصيد",
|
||||
"account.field.created_at": "أنشئت في",
|
||||
"item.field.type": "نوع الصنف",
|
||||
"item.field.type.inventory": "مخزون",
|
||||
"item.field.type.service": "خدمة",
|
||||
"item.field.type.non-inventory": "غير مخزون",
|
||||
"item.field.name": "اسم الصنف",
|
||||
"item.field.code": "رمز الصنف",
|
||||
"item.field.sellable": "قابل للبيع",
|
||||
"item.field.purchasable": "قابل للشراء",
|
||||
"item.field.cost_price": "سعر التكلفة",
|
||||
"item.field.cost_account": "حساب التكلفة",
|
||||
"item.field.sell_account": "حساب البيع",
|
||||
"item.field.sell_description": "وصف البيع",
|
||||
"item.field.inventory_account": "حساب المخزون",
|
||||
"item.field.purchase_description": "وصف الشراء",
|
||||
"item.field.quantity_on_hand": "الكمية",
|
||||
"item.field.note": "ملاحظة",
|
||||
"item.field.category": "التصنيف",
|
||||
"item.field.active": "Active",
|
||||
"item.field.created_at": "أنشئت في",
|
||||
"item_category.field.name": "الاسم",
|
||||
"item_category.field.description": "الوصف",
|
||||
"item_category.field.count": "العدد",
|
||||
"item_category.field.created_at": "أنشئت في",
|
||||
"invoice.field.customer": "الزبون",
|
||||
"invoice.field.invoice_date": "تاريخ الفاتورة",
|
||||
"invoice.field.due_date": "تاريخ الاستحقاق",
|
||||
"invoice.field.invoice_no": "رقم الفاتورة",
|
||||
"invoice.field.reference_no": "رقم الإشاري",
|
||||
"invoice.field.invoice_message": "رسالة الفاتورة",
|
||||
"invoice.field.terms_conditions": "الشروط والأحكام",
|
||||
"invoice.field.amount": "القيمة",
|
||||
"invoice.field.payment_amount": "القيمة المدفوعة",
|
||||
"invoice.field.due_amount": "القيمة المستحقة",
|
||||
"invoice.field.status": "الحالة",
|
||||
"invoice.field.status.paid": "مدفوعة",
|
||||
"invoice.field.status.partially-paid": "المدفوعة جزئيا",
|
||||
"invoice.field.status.overdue": "متأخرة",
|
||||
"invoice.field.status.unpaid": "غير مدفوعة",
|
||||
"invoice.field.status.delivered": "تم تسليمها",
|
||||
"invoice.field.status.draft": "مسودة",
|
||||
"invoice.field.created_at": "أنشئت في",
|
||||
"estimate.field.amount": "القيمة",
|
||||
"estimate.field.estimate_number": "رقم العرض",
|
||||
"estimate.field.customer": "الزبون",
|
||||
"estimate.field.estimate_date": "تاريخ العرض",
|
||||
"estimate.field.expiration_date": "تاريخ انتهاء الصلاحية",
|
||||
"estimate.field.reference_no": "رقم الإشاري",
|
||||
"estimate.field.note": "ملاحظة",
|
||||
"estimate.field.terms_conditions": "الشروط والأحكام",
|
||||
"estimate.field.status": "الحالة",
|
||||
"estimate.field.status.delivered": "تم تسليمها",
|
||||
"estimate.field.status.rejected": "مرفوضة",
|
||||
"estimate.field.status.approved": "تم الموافقة",
|
||||
"estimate.field.status.draft": "مسودة",
|
||||
"estimate.field.created_at": "أنشئت في",
|
||||
"payment_receive.field.customer": "الزبون",
|
||||
"payment_receive.field.payment_date": "تاريخ الدفع",
|
||||
"payment_receive.field.amount": "القيمة",
|
||||
"payment_receive.field.reference_no": "رقم الإشاري",
|
||||
"payment_receive.field.deposit_account": "حساب الإيداع",
|
||||
"payment_receive.field.payment_receive_no": "رقم عملية الدفع",
|
||||
"payment_receive.field.statement": "البيان",
|
||||
"payment_receive.field.created_at": "أنشئت في",
|
||||
"bill_payment.field.vendor": "المورد",
|
||||
"bill_payment.field.amount": "القيمة",
|
||||
"bill_payment.field.due_amount": "قيمة المستحقة",
|
||||
"bill_payment.field.payment_account": "حساب الدفع",
|
||||
"bill_payment.field.payment_number": "قيمة الدفع",
|
||||
"bill_payment.field.payment_date": "تاريخ الدفع",
|
||||
"bill_payment.field.reference_no": "رقم الإشاري",
|
||||
"bill_payment.field.description": "الوصف",
|
||||
"bill_payment.field.created_at": "أنشئت في",
|
||||
"bill.field.vendor": "المورد",
|
||||
"bill.field.bill_number": "رقم الفاتورة",
|
||||
"bill.field.bill_date": "تاريخ الفاتورة",
|
||||
"bill.field.due_date": "تاريخ الاستحقاق",
|
||||
"bill.field.reference_no": "رقم الإشاري",
|
||||
"bill.field.status": "الحالة",
|
||||
"bill.field.status.paid": "مدفوعة",
|
||||
"bill.field.status.partially-paid": "مدفوعة جزئيا",
|
||||
"bill.field.status.unpaid": "غير مدفوعة",
|
||||
"bill.field.status.opened": "مفتوحة",
|
||||
"bill.field.status.draft": "مسودة",
|
||||
"bill.field.status.overdue": "متأخرة",
|
||||
"bill.field.amount": "القيمة",
|
||||
"bill.field.payment_amount": "قيم الدفع",
|
||||
"bill.field.note": "ملاحظة",
|
||||
"bill.field.created_at": "أنشئت في",
|
||||
"inventory_adjustment.field.date": "التاريخ",
|
||||
"inventory_adjustment.field.type": "النوع",
|
||||
"inventory_adjustment.field.type.increment": "زيادة",
|
||||
"inventory_adjustment.field.type.decrement": "نقصان",
|
||||
"inventory_adjustment.field.adjustment_account": "حساب التسوية",
|
||||
"inventory_adjustment.field.reason": "السبب",
|
||||
"inventory_adjustment.field.reference_no": "رقم الإشاري",
|
||||
"inventory_adjustment.field.description": "الوصف",
|
||||
"inventory_adjustment.field.published_at": "نشرت في",
|
||||
"inventory_adjustment.field.created_at": "أنشئت في",
|
||||
"expense.field.payment_date": "تاريخ الدفع",
|
||||
"expense.field.payment_account": "حساب الدفع",
|
||||
"expense.field.amount": "القيمة",
|
||||
"expense.field.reference_no": "رقم الإشاري",
|
||||
"expense.field.description": "الوصف",
|
||||
"expense.field.published": "Published",
|
||||
"expense.field.status": "الحالة",
|
||||
"expense.field.status.draft": "مسودة",
|
||||
"expense.field.status.published": "نشرت",
|
||||
"expense.field.created_at": "أنشئت في",
|
||||
"manual_journal.field.date": "التاريخ",
|
||||
"manual_journal.field.journal_number": "رقم القيد",
|
||||
"manual_journal.field.reference": "رقم الإشاري",
|
||||
"manual_journal.field.journal_type": "نوع القيد",
|
||||
"manual_journal.field.amount": "القيمة",
|
||||
"manual_journal.field.description": "الوصف",
|
||||
"manual_journal.field.status": "الحالة",
|
||||
"manual_journal.field.created_at": "أنشئت في",
|
||||
"receipt.field.amount": "القيمة",
|
||||
"receipt.field.deposit_account": "حساب الإيداع",
|
||||
"receipt.field.customer": "الزبون",
|
||||
"receipt.field.receipt_date": "تاريخ الإيصال",
|
||||
"receipt.field.receipt_number": "رقم الإيصال",
|
||||
"receipt.field.reference_no": "رقم الإشاري",
|
||||
"receipt.field.receipt_message": "رسالة الإيصال",
|
||||
"receipt.field.statement": "البيان",
|
||||
"receipt.field.created_at": "أنشئت في",
|
||||
"receipt.field.status": "الحالة",
|
||||
"receipt.field.status.draft": "مسودة",
|
||||
"receipt.field.status.closed": "مغلقة",
|
||||
"customer.field.first_name": "الاسم الأول",
|
||||
"customer.field.last_name": "الاسم الاخير",
|
||||
"customer.field.display_name": "اسم العرض",
|
||||
"customer.field.email": "بريد الالكتروني",
|
||||
"customer.field.work_phone": "هاتف عمل",
|
||||
"customer.field.personal_phone": "هاتف شخصي",
|
||||
"customer.field.company_name": "اسم الشركة",
|
||||
"customer.field.website": "موقع الكتروني",
|
||||
"customer.field.opening_balance_at": "الرصيد الافتتاحي في",
|
||||
"customer.field.opening_balance": "الرصيد الافتتاحي",
|
||||
"customer.field.created_at": "أنشئت في",
|
||||
"customer.field.balance": "الرصيد",
|
||||
"customer.field.status": "الحالة",
|
||||
"customer.field.currency": "العملة",
|
||||
"customer.field.status.active": "مفعل",
|
||||
"customer.field.status.inactive": "غير مفعل",
|
||||
"customer.field.status.overdue": "متأخر",
|
||||
"customer.field.status.unpaid": "غير دافع",
|
||||
"vendor.field.first_name": "الاسم الأول",
|
||||
"vendor.field.last_name": "الاسم الاخير",
|
||||
"vendor.field.display_name": "اسم العرض",
|
||||
"vendor.field.email": "بريد الالكتروني",
|
||||
"vendor.field.work_phone": "هاتف عمل",
|
||||
"vendor.field.personal_phone": "هاتف شخصي",
|
||||
"vendor.field.company_name": "اسم الشركة",
|
||||
"vendor.field.website": "موقع الكتروني",
|
||||
"vendor.field.opening_balance_at": "الرصيد الافتتاحي في",
|
||||
"vendor.field.opening_balance": "الرصيد الافتتاحي",
|
||||
"vendor.field.created_at": "أنشئت في",
|
||||
"vendor.field.balance": "الرصيد",
|
||||
"vendor.field.status": "الحالة",
|
||||
"vendor.field.currency": "العملة",
|
||||
"vendor.field.status.active": "مفعل",
|
||||
"vendor.field.status.inactive": "غير مفعل",
|
||||
"vendor.field.status.overdue": "متأخر",
|
||||
"vendor.field.status.unpaid": "غير دافع",
|
||||
"Invoice write-off": "شطب فاتورة",
|
||||
"transaction_type.credit_note": "اشعار دائن",
|
||||
"transaction_type.refund_credit_note": "استرجاع اموال اشعار دائن",
|
||||
"transaction_type.vendor_credit": "اشعار مدين",
|
||||
"transaction_type.refund_vendor_credit": "استرجاع اموال اشعار مدين",
|
||||
"transaction_type.landed_cost": "تحميل تكلفة",
|
||||
|
||||
"sms_notification.invoice_details.label": "تفاصيل فاتورة البيع ",
|
||||
"sms_notification.invoice_reminder.label": "تذكير بفاتورة البيع ",
|
||||
"sms_notification.receipt_details.label": "تفاصيل إيصال البيع ",
|
||||
"sms_notification.sale_estimate_details.label": "تفاصيل فاتورة عرض اسعار ",
|
||||
"sms_notification.payment_receive_details.label": "تفاصيل سند الزبون",
|
||||
"sms_notification.customer_balance.label": "رصيد الزبون",
|
||||
|
||||
"sms_notification.invoice_details.description": "سيتم إرسال إشعار عبر الرسائل القصيرة إلى العميل بمجرد إنشاء الفاتورة ونشرها أو عند إشعار العميل عبر رسالة نصية قصيرة بالفاتورة. ",
|
||||
"sms_notification.payment_receive.description": "سيتم إرسال إشعار رسالة شكر للدفع إلى العميل بمجرد إنشاء الدفعة ونشرها أو إشعار العميل بالدفع يدويًا. ",
|
||||
"sms_notification.receipt_details.description": "سيتم إرسال إشعار عبر الرسائل القصيرة إلى العميل بمجرد إنشاء ونشر الإيصال أو عند إشعار العميل بالإيصال يدويًا.",
|
||||
"sms_notification.customer_balance.description": "إرسال رسالة نصية قصيرة إشعار العملاء برصيدهم الحالي المستحق. ",
|
||||
"sms_notification.estimate_details.description": "سيتم إرسال إشعار عبر الرسائل القصيرة إلى عميلك بمجرد نشر العرض أو إشعار العميل بالعرض يدويًا.",
|
||||
"sms_notification.invoice_reminder.description": "سيتم ارسال إشعار SMS لتذكير الزبون بالدفع باكراً ، سواء ارسال بشكل تلقائي او يدوي.",
|
||||
|
||||
"sms_notification.customer_balance.default_message": "عزيزي {CustomerName} ، هذا تذكير بشأن رصيد الحالي المستحق {Balance} ، يُرجى الدفع في أقرب وقت ممكن. - {CompanyName}",
|
||||
"sms_notification.payment_receive.default_message": "مرحبًا {CustomerName} ، تم القبض بقيمة {Amount} للفاتورة - {InvoiceNumber}. نحن نتطلع إلى خدمتك مرة أخرى. شكرا لك. - {CompanyName}",
|
||||
"sms_notification.estimate.default_message": "مرحبًا , {CustomerName} ، تم أنشاء فاتورة عرض اسعار - {EstimateNumber} لك. يرجى إلقاء نظرة وقبوله للمضي قدما. بانتظار ردك. - {CompanyName}",
|
||||
|
||||
"sms_notification.invoice_details.default_message": "مرحبًا {CustomerName}, لديك مبلغ مستحق قدره {DueAmount} للفاتورة {InvoiceNumber}. - {CompanyName}",
|
||||
"sms_notification.receipt_details.default_message": "مرحبًا {CustomerName} ، لقد تم إنشاء إيصال - {ReceiptNumber} من أجلك. نتطلع إلى خدمتك مرة أخرى. شكرًا لك - {CompanyName}",
|
||||
"sms_notification.invoice_reminder.default_message": "عزيزي {CustomerName} ، يرجي سداد فاتورة - {InvoiceNumber} المستحقة. يرجى الدفع قبل تاريخ {DueDate}. شكرا لك. - {CompanyName}",
|
||||
|
||||
"module.sale_invoices.label": "فواتير البيع",
|
||||
"module.sale_receipts.label": "إيصالات البيع",
|
||||
"module.sale_estimates.label": "فاتورة عرض اسعار ",
|
||||
"module.payment_receives.label": "سندات الزبائن ",
|
||||
"module.customers.label": "العملاء",
|
||||
|
||||
"sms_notification.invoice.var.invoice_number": "يشير إلى رقم الفاتورة.",
|
||||
"sms_notification.invoice.var.reference_number": "يشير إلى رقم إشاري للفاتورة.",
|
||||
"sms_notification.invoice.var.customer_name": "يشير إلى اسم العميل الفاتورة",
|
||||
"sms_notification.invoice.var.due_amount": "يشير إلى مبلغ الفاتورة المستحق",
|
||||
"sms_notification.invoice.var.amount": "يشير إلى مبلغ الفاتورة.",
|
||||
"sms_notification.invoice.var.company_name": "يشير إلي اسم الشركة.",
|
||||
"sms_notification.invoice.var.due_date": "يشير إلي تاريخ استحقاق الفاتورة.",
|
||||
|
||||
"sms_notification.receipt.var.receipt_number": "يشير إلى رقم الإيصال.",
|
||||
"sms_notification.receipt.var.reference_number": "يشير إلى رقم الإشاري للإيصال.",
|
||||
"sms_notification.receipt.var.customer_name": "يشير إلى اسم العميل الإيصال.",
|
||||
"sms_notification.receipt.var.amount": "يشير إلى مبلغ الإيصال. ",
|
||||
"sms_notification.receipt.var.company_name": "يشير إلي اسم الشركة.",
|
||||
|
||||
"sms_notification.payment.var.payment_number": "يشير إلى رقم معاملة الدفع.",
|
||||
"sms_notification.payment.var.reference_number": "يشير إلى رقم الإشاري لعملية الدفع ",
|
||||
"sms_notification.payment.var.customer_name": "يشير إلى اسم العميل الدفع",
|
||||
"sms_notification.payment.var.amount": "يشير إلى مبلغ معاملة الدفع.",
|
||||
"sms_notification.payment.company_name": "يشير إلي اسم الشركة.",
|
||||
"sms_notification.payment.var.invoice_number": "يشير إلي رقم فاتورة التي تم دفعها.",
|
||||
|
||||
"sms_notification.estimate.var.estimate_number": "يشير إلى رقم فاتورة عرض اسعار.",
|
||||
"sms_notification.estimate.var.reference_number": "يشير إلى رقم الإشاري لفاتورة عرض اسعار.",
|
||||
"sms_notification.estimate.var.customer_name": "يشير إلى اسم العميل الفاتورة",
|
||||
"sms_notification.estimate.var.amount": "يشير إلى قيمة الفاتورة",
|
||||
"sms_notification.estimate.var.company_name": "يشير إلي اسم الشركة.",
|
||||
"sms_notification.estimate.var.expiration_date": "يشير إلي تاريخ الصلاحية الفاتورة.",
|
||||
"sms_notification.estimate.var.estimate_date": "يشير إلي تاريخ الفاتورة.",
|
||||
|
||||
"sms_notification.customer.var.customer_name": "يشير إلي اسم الزبون",
|
||||
"sms_notification.customer.var.balance": "يشير إلي رصيد زبون المستحق.",
|
||||
"sms_notification.customer.var.company_name": "يشير إلي اسم الشركة.",
|
||||
|
||||
"ability.accounts": "شجرة الحسابات",
|
||||
"ability.manual_journal": "القيود اليدوية",
|
||||
"ability.cashflow": "التدفقات النقدية",
|
||||
"ability.inventory_adjustment": "تسويات المخزون",
|
||||
"ability.customers": "الزبائن",
|
||||
"ability.vendors": "الموردين",
|
||||
"ability.sale_estimates": "فواتير عرض الاسعار",
|
||||
"ability.sale_invoices": "فواتير البيع",
|
||||
"ability.sale_receipts": "إيصالات البيع",
|
||||
"ability.expenses": "المصاريف",
|
||||
"ability.payments_receive": "سندات الزبائن",
|
||||
"ability.purchase_invoices": "فواتير الشراء",
|
||||
"ability.all_reports": "كل التقارير",
|
||||
"ability.payments_made": "سندات الموردين",
|
||||
"ability.preferences": "التفضيلات",
|
||||
"ability.mutate_system_preferences": "تعديل تفضيلات النظام.",
|
||||
|
||||
"ability.items": "الأصناف",
|
||||
"ability.view": "عرض",
|
||||
"ability.create": "إضافة",
|
||||
"ability.edit": "تعديل",
|
||||
"ability.delete": "حذف",
|
||||
"ability.transactions_locking": "إمكانية اغلاق المعاملات.",
|
||||
|
||||
"ability.balance_sheet_report": "ميزانية العمومية",
|
||||
"ability.profit_loss_sheet": "قائمة الدخل",
|
||||
"ability.journal": "اليومية العامة",
|
||||
"ability.general_ledger": "دفتر الأستاذ العام",
|
||||
"ability.cashflow_report": "تقرير التدفقات النقدية",
|
||||
"ability.AR_aging_summary_report": "ملخص اعمار الديون للذمم المدينة",
|
||||
"ability.AP_aging_summary_report": "ملخص اعمار الديون للذمم الدائنة",
|
||||
"ability.purchases_by_items": "المشتريات حسب المنتجات",
|
||||
"ability.sales_by_items_report": "المبيعات حسب المنتجات",
|
||||
"ability.customers_transactions_report": "معاملات الزبائن",
|
||||
"ability.vendors_transactions_report": "معاملات الموردين",
|
||||
"ability.customers_summary_balance_report": "ملخص أرصدة الزبائن",
|
||||
"ability.vendors_summary_balance_report": "ملخص أرصدة الموردين",
|
||||
"ability.inventory_valuation_summary": "ملخص تقييم المخزون",
|
||||
"ability.inventory_items_details": "تفاصيل منتج المخزون",
|
||||
|
||||
"vendor_credit.field.vendor": "المورد",
|
||||
"vendor_credit.field.amount": "القيمة",
|
||||
"vendor_credit.field.currency_code": "العملة",
|
||||
"vendor_credit.field.credit_date": "تاريخ الاشعار",
|
||||
"vendor_credit.field.credit_number": "رقم الاشعار",
|
||||
"vendor_credit.field.note": "ملاحظة",
|
||||
"vendor_credit.field.created_at": "أنشئت في",
|
||||
"vendor_credit.field.reference_no": "رقم الإشاري",
|
||||
|
||||
"vendor_credit.field.status": "الحالة",
|
||||
"vendor_credit.field.status.draft": "مسودة",
|
||||
"vendor_credit.field.status.published": "تم نشرها",
|
||||
"vendor_credit.field.status.open": "مفتوحة",
|
||||
"vendor_credit.field.status.closed": "مغلقة",
|
||||
|
||||
"credit_note.field.terms_conditions": "الشروط والاحكام",
|
||||
"credit_note.field.note": "ملاحظة",
|
||||
"credit_note.field.currency_code": "العملة",
|
||||
"credit_note.field.created_at": "أنشئت في",
|
||||
"credit_note.field.amount": "القيمة",
|
||||
"credit_note.field.credit_note_number": "رقم الاشعار",
|
||||
"credit_note.field.credit_note_date": "تاريخ الاشعار",
|
||||
"credit_note.field.customer": "الزبون",
|
||||
"credit_note.field.reference_no": "رقم الإشاري",
|
||||
|
||||
"credit_note.field.status": "الحالة",
|
||||
"credit_note.field.status.draft": "مسودة",
|
||||
"credit_note.field.status.published": "تم نشرها",
|
||||
"credit_note.field.status.open": "مفتوحة",
|
||||
"credit_note.field.status.closed": "مغلقة",
|
||||
|
||||
"transactions_locking.module.sales.label": "المبيعات",
|
||||
"transactions_locking.module.purchases.label": "المشتريات",
|
||||
"transactions_locking.module.financial.label": "المالية",
|
||||
"transactions_locking.module.all_transactions": "كل المعاملات",
|
||||
|
||||
"transactions_locking.module.sales.desc": "فواتير البيع ، والإيصالات ، والإشعارات الدائنة ، واستلام مدفوعات الزبائن ، والأرصدة الافتتاحية للزبائن.",
|
||||
"transactions_locking.module.purchases.desc": "فواتير الشراء ومدفوعات الموردين وإشعارات المدينة والأرصدة الافتتاحية للموردين.",
|
||||
"transactions_locking.module.financial.desc": "القيود اليدوية والمصروفات وتسويات المخزون.",
|
||||
|
||||
"inventory_adjustment.type.increment": "زيادة",
|
||||
"inventory_adjustment.type.decrement": "نقصان",
|
||||
|
||||
"customer.type.individual": "فرد",
|
||||
"customer.type.business": "اعمال",
|
||||
|
||||
"credit_note.view.draft": "مسودة",
|
||||
"credit_note.view.closed": "مغلقة",
|
||||
"credit_note.view.open": "مفتوحة",
|
||||
"credit_note.view.published": "نشرت",
|
||||
|
||||
"vendor_credit.view.draft": "مسودة",
|
||||
"vendor_credit.view.closed": "مغلقة",
|
||||
"vendor_credit.view.open": "مفتوحة",
|
||||
"vendor_credit.view.published": "نشرت",
|
||||
|
||||
"allocation_method.value.label": "القيمة",
|
||||
"allocation_method.quantity.label": "الكمية",
|
||||
|
||||
"balance_sheet.assets": "الأصول",
|
||||
"balance_sheet.current_asset": "الأصول المتداولة",
|
||||
"balance_sheet.cash_and_cash_equivalents": "النقدية وما يعادلها",
|
||||
"balance_sheet.accounts_receivable": "الذمم المدينة",
|
||||
"balance_sheet.inventory": "المخزون",
|
||||
"balance_sheet.other_current_assets": "اصول متداولة اخرى",
|
||||
"balance_sheet.fixed_asset": "الأصول الثابتة",
|
||||
"balance_sheet.non_current_assets": "الاصول غير المتداولة",
|
||||
"balance_sheet.liabilities_and_equity": "الالتزامات وحقوق الملكية",
|
||||
"balance_sheet.liabilities": "الإلتزامات",
|
||||
"balance_sheet.current_liabilties": "الالتزامات المتداولة",
|
||||
"balance_sheet.long_term_liabilities": "الالتزامات طويلة الاجل",
|
||||
"balance_sheet.non_current_liabilities": "الالتزامات غير المتداولة",
|
||||
"balance_sheet.equity": "حقوق الملكية",
|
||||
|
||||
"balance_sheet.account_name": "اسم الحساب",
|
||||
"balance_sheet.total": "إجمالي",
|
||||
"balance_sheet.percentage_of_column": "٪ التغير العمودي",
|
||||
"balance_sheet.percentage_of_row": "٪ التغير الأفقي",
|
||||
|
||||
"financial_sheet.previoud_period_date": "(ف.س) {{date}}",
|
||||
"fianncial_sheet.previous_period_change": "التغيرات (ف.س)",
|
||||
"financial_sheet.previous_period_percentage": "٪ التغير (ف.س)",
|
||||
|
||||
"financial_sheet.previous_year_date": "(س.س) {{date}}",
|
||||
"financial_sheet.previous_year_change": "التغيرات (س.س)",
|
||||
"financial_sheet.previous_year_percentage": "٪ التغير (س.س)",
|
||||
"financial_sheet.total_row": "إجمالي {{value}}",
|
||||
|
||||
"profit_loss_sheet.income": "الإيرادات",
|
||||
"profit_loss_sheet.cost_of_sales": "تكلفة المبيعات",
|
||||
"profit_loss_sheet.gross_profit": "إجمالي الدخل",
|
||||
"profit_loss_sheet.expenses": "المصروفات",
|
||||
"profit_loss_sheet.net_operating_income": "صافي الدخل التشغيلي",
|
||||
"profit_loss_sheet.other_income": "إيرادات اخري",
|
||||
"profit_loss_sheet.other_expenses": "مصاريف اخري",
|
||||
"profit_loss_sheet.net_income": "صافي الدخل",
|
||||
|
||||
"profit_loss_sheet.account_name": "اسم الحساب",
|
||||
"profit_loss_sheet.total": "إجمالي",
|
||||
|
||||
"profit_loss_sheet.percentage_of_income": "٪ التغير في الإيرادات",
|
||||
"profit_loss_sheet.percentage_of_expenses": "٪ التغير في المصاريف",
|
||||
"profit_loss_sheet.percentage_of_column": "٪ التغير العمودي",
|
||||
"profit_loss_sheet.percentage_of_row": "٪ التغير الأفقي",
|
||||
|
||||
"warehouses.primary_warehouse": "المستودع الرئيسي",
|
||||
"branches.head_branch": "الفرع الرئيسي",
|
||||
|
||||
"account.accounts_payable.currency": "الذمم الدائنة - {{currency}}",
|
||||
"account.accounts_receivable.currency": "الذمم المدينة - {{currency}}",
|
||||
|
||||
"role.admin.name": "الادارة",
|
||||
"role.admin.desc": "وصول غير مقيد لجميع الوحدات.",
|
||||
|
||||
"role.staff.name": "العاملين",
|
||||
"role.staff.desc": "الوصول إلى جميع الوحدات باستثناء التقارير والإعدادات والمحاسبة.",
|
||||
|
||||
"warehouse_transfer.view.draft.name": "مسودة",
|
||||
"warehouse_transfer.view.in_transit.name": "في النقل",
|
||||
"warehouse_transfer.view.transferred.name": "تم النقل"
|
||||
}
|
||||
@@ -1,641 +0,0 @@
|
||||
{
|
||||
"Petty Cash": "Petty Cash",
|
||||
"Cash": "Cash",
|
||||
"Bank": "Bank",
|
||||
"Other Income": "Other Income",
|
||||
"Interest Income": "Interest Income",
|
||||
"Depreciation Expense": "Depreciation Expense",
|
||||
"Interest Expense": "Interest Expense",
|
||||
"Sales of Product Income": "Sales of Product Income",
|
||||
"Inventory Asset": "Inventory Asset",
|
||||
"Cost of Goods Sold (COGS)": "Cost of Goods Sold (COGS)",
|
||||
"Cost of Goods Sold": "Cost of Goods Sold",
|
||||
"Accounts Payable": "Accounts Payable",
|
||||
"Other Expense": "Other Expense",
|
||||
"Payroll Expenses": "Payroll Expenses",
|
||||
"Fixed Asset": "Fixed Asset",
|
||||
"Credit Card": "Credit Card",
|
||||
"Non-Current Asset": "Non-Current Asset",
|
||||
"Current Asset": "Current Asset",
|
||||
"Other Asset": "Other Asset",
|
||||
"Long Term Liability": "Long Term Liability",
|
||||
"Current Liability": "Current Liability",
|
||||
"Other Liability": "Other Liability",
|
||||
"Equity": "Equity",
|
||||
"Expense": "Expense",
|
||||
"Income": "Income",
|
||||
"Accounts Receivable (A/R)": "Accounts Receivable (A/R)",
|
||||
"Accounts Receivable": "Accounts Receivable",
|
||||
"Accounts Payable (A/P)": "Accounts Payable (A/P)",
|
||||
"Inactive": "Inactive",
|
||||
"Other Current Asset": "Other Current Asset",
|
||||
"Tax Payable": "Tax Payable",
|
||||
"Other Current Liability": "Other Current Liability",
|
||||
"Non-Current Liability": "Non-Current Liability",
|
||||
"Assets": "Assets",
|
||||
"Liabilities": "Liabilities",
|
||||
"Account name": "Account name",
|
||||
"Account type": "Account type",
|
||||
"Account normal": "Account normal",
|
||||
"Description": "Description",
|
||||
"Account code": "Account code",
|
||||
"Currency": "Currency",
|
||||
"Balance": "Balance",
|
||||
"Active": "Active",
|
||||
"Created at": "Created at",
|
||||
"fixed_asset": "Fixed asset",
|
||||
"Journal": "Journal",
|
||||
"Reconciliation": "Reconciliation",
|
||||
"Credit": "Credit",
|
||||
"Debit": "Debit",
|
||||
"Interest": "Interest",
|
||||
"Depreciation": "Depreciation",
|
||||
"Payroll": "Payroll",
|
||||
"Type": "Type",
|
||||
"Name": "Name",
|
||||
"Sellable": "Sellable",
|
||||
"Purchasable": "Purchasable",
|
||||
"Sell price": "Sell price",
|
||||
"Cost price": "Cost price",
|
||||
"User": "User",
|
||||
"Category": "Category",
|
||||
"Note": "Note",
|
||||
"Quantity on hand": "Quantity on hand",
|
||||
"Quantity": "Quantity",
|
||||
"Purchase description": "Purchase description",
|
||||
"Sell description": "Sell description",
|
||||
"Sell account": "Sell account",
|
||||
"Cost account": "Cost account",
|
||||
"Inventory account": "Inventory account",
|
||||
"Payment date": "Payment date",
|
||||
"Payment account": "Payment account",
|
||||
"Amount": "Amount",
|
||||
"Reference No.": "Reference No.",
|
||||
"Journal number": "Journal number",
|
||||
"Status": "Status",
|
||||
"Journal type": "Journal type",
|
||||
"Date": "Date",
|
||||
"Asset": "Asset",
|
||||
"Liability": "Liability",
|
||||
"First-in first-out (FIFO)": "First-in first-out (FIFO)",
|
||||
"Last-in first-out (LIFO)": "Last-in first-out (LIFO)",
|
||||
"Average rate": "Average rate",
|
||||
"Total": "Total",
|
||||
"Transaction type": "Transaction type",
|
||||
"Transaction #": "Transaction #",
|
||||
"Running Value": "Running Value",
|
||||
"Running quantity": "Running quantity",
|
||||
"Profit Margin": "Profit Margin",
|
||||
"Value": "Value",
|
||||
"Rate": "Rate",
|
||||
"OPERATING ACTIVITIES": "OPERATING ACTIVITIES",
|
||||
"FINANCIAL ACTIVITIES": "FINANCIAL ACTIVITIES",
|
||||
"Net income": "Net income",
|
||||
"Adjustments net income by operating activities.": "Adjustments net income by operating activities.",
|
||||
"Net cash provided by operating activities": "Net cash provided by operating activities",
|
||||
"Net cash provided by investing activities": "Net cash provided by investing activities",
|
||||
"Net cash provided by financing activities": "Net cash provided by financing activities",
|
||||
"Cash at beginning of period": "Cash at beginning of period",
|
||||
"NET CASH INCREASE FOR PERIOD": "NET CASH INCREASE FOR PERIOD",
|
||||
"CASH AT END OF PERIOD": "CASH AT END OF PERIOD",
|
||||
"Expenses": "Expenses",
|
||||
"Services": "Services",
|
||||
"Inventory": "Inventory",
|
||||
"Non Inventory": "Non Inventory",
|
||||
"Draft": "Draft",
|
||||
"Published": "Published",
|
||||
"Delivered": "Delivered",
|
||||
"Overdue": "Overdue",
|
||||
"Partially paid": "Partially paid",
|
||||
"Paid": "Paid",
|
||||
"Opened": "Opened",
|
||||
"Unpaid": "Unpaid",
|
||||
"Approved": "Approved",
|
||||
"Rejected": "Rejected",
|
||||
"Invoiced": "Invoiced",
|
||||
"Expired": "Expired",
|
||||
"Closed": "Closed",
|
||||
"Manual journal": "Manual journal",
|
||||
"Owner contribution": "Owner contribution",
|
||||
"Transfer to account": "Transfer to account",
|
||||
"Transfer from account": "Transfer from account",
|
||||
"Other income": "Other income",
|
||||
"Other expense": "Other expense",
|
||||
"Owner drawing": "Owner drawing",
|
||||
"Inventory adjustment": "Inventory adjustment",
|
||||
"Customer opening balance": "Customer opening balance",
|
||||
"Vendor opening balance": "Vendor opening balance",
|
||||
"Payment made": "Payment made",
|
||||
"Bill": "Bill",
|
||||
"Payment receive": "Payment receive",
|
||||
"Sale receipt": "Sale receipt",
|
||||
"Sale invoice": "Sale invoice",
|
||||
"Bank Account": "Bank Account",
|
||||
"Saving Bank Account": "Saving Bank Account",
|
||||
"Undeposited Funds": "Undeposited Funds",
|
||||
"Computer Equipment": "Computer Equipment",
|
||||
"Office Equipment": "Office Equipment",
|
||||
"Uncategorized Income": "Uncategorized Income",
|
||||
"Sales of Service Income": "Sales of Service Income",
|
||||
"Bank Fees and Charges": "Bank Fees and Charges",
|
||||
"Exchange Gain or Loss": "Exchange Gain or Loss",
|
||||
"Rent": "Rent",
|
||||
"Office expenses": "Office expenses",
|
||||
"Other Expenses": "Other Expenses",
|
||||
"Drawings": "Drawings",
|
||||
"Owner's Equity": "Owner's Equity",
|
||||
"Opening Balance Equity": "Opening Balance Equity",
|
||||
"Retained Earnings": "Retained Earnings",
|
||||
"Sales Tax Payable": "Sales Tax Payable",
|
||||
"Revenue Received in Advance": "Revenue Received in Advance",
|
||||
"Opening Balance Liabilities": "Opening Balance Liabilities",
|
||||
"Loan": "Loan",
|
||||
"Owner A Drawings": "Owner A Drawings",
|
||||
"An account that holds valuation of products or goods that available for sale.": "An account that holds valuation of products or goods that available for sale.",
|
||||
"Tracks the gain and losses of the exchange differences.": "Tracks the gain and losses of the exchange differences.",
|
||||
"Any bank fees levied is recorded into the bank fees and charges account. A bank account maintenance fee, transaction charges, a late payment fee are some examples.": "Any bank fees levied is recorded into the bank fees and charges account. A bank account maintenance fee, transaction charges, a late payment fee are some examples.",
|
||||
"The income activities are not associated to the core business.": "The income activities are not associated to the core business.",
|
||||
"Cash and cash equivalents": "Cash and cash equivalents",
|
||||
"Inventories": "Inventories",
|
||||
"Other current assets": "Other current assets",
|
||||
"Non-Current Assets": "Non-Current Assets",
|
||||
"Current Liabilties": "Current Liabilties",
|
||||
"Long-Term Liabilities": "Long-Term Liabilities",
|
||||
"Non-Current Liabilities": "Non-Current Liabilities",
|
||||
"Liabilities and Equity": "Liabilities and Equity",
|
||||
"Closing balance": "Closing balance",
|
||||
"Opening Balance": "Opening balance",
|
||||
"Total {{accountName}}": "Total {{accountName}}",
|
||||
"invoice.paper.invoice": "Invoice",
|
||||
"invoice.paper.invoice_amount": "Invoice amount",
|
||||
"invoice.paper.due_amount": "Due amount",
|
||||
"invoice.paper.billed_to": "Billed to",
|
||||
"invoice.paper.invoice_date": "Invoice date",
|
||||
"invoice.paper.invoice_number": "Invoice No.",
|
||||
"invoice.paper.due_date": "Due date",
|
||||
"invoice.paper.conditions_title": "Conditions & terms",
|
||||
"invoice.paper.notes_title": "Notes",
|
||||
"invoice.paper.total": "Total",
|
||||
"invoice.paper.payment_amount": "Payment Amount",
|
||||
"invoice.paper.balance_due": "Balance Due",
|
||||
|
||||
"item_entry.paper.item_name": "Item name",
|
||||
"item_entry.paper.rate": "Rate",
|
||||
"item_entry.paper.quantity": "Quantity",
|
||||
"item_entry.paper.total": "Total",
|
||||
|
||||
"estimate.paper.estimate": "Estimate",
|
||||
"estimate.paper.estimate_amount": "Estimate amount",
|
||||
"estimate.paper.billed_to": "Billed to",
|
||||
"estimate.paper.estimate_date": "Estimate date",
|
||||
"estimate.paper.estimate_number": "Estimate number",
|
||||
"estimate.paper.expiration_date": "Expiration date",
|
||||
"estimate.paper.conditions_title": "Conditions & terms",
|
||||
"estimate.paper.notes_title": "Notes",
|
||||
"estimate.paper.amount": "Estimate amount",
|
||||
"estimate.paper.subtotal": "Subtotal",
|
||||
"estimate.paper.total": "Total",
|
||||
|
||||
"receipt.paper.receipt": "Receipt",
|
||||
"receipt.paper.billed_to": "Billed to",
|
||||
"receipt.paper.receipt_date": "Receipt date",
|
||||
"receipt.paper.receipt_number": "Receipt number",
|
||||
"receipt.paper.expiration_date": "Expiration date",
|
||||
"receipt.paper.conditions_title": "Conditions & terms",
|
||||
"receipt.paper.notes": "Notes",
|
||||
"receipt.paper.statement": "Statement",
|
||||
"receipt.paper.receipt_amount": "Receipt amount",
|
||||
"receipt.paper.total": "Total",
|
||||
"receipt.paper.balance_due": "Balance Due",
|
||||
"receipt.paper.payment_amount": "Payment Amount",
|
||||
|
||||
"credit.paper.credit_note": "Credit Note",
|
||||
"credit.paper.remaining": "Credit remaining",
|
||||
"credit.paper.amount": "Credit amount",
|
||||
"credit.paper.billed_to": "Bill to",
|
||||
"credit.paper.credit_date": "Credit date",
|
||||
"credit.paper.total": "Total",
|
||||
"credit.paper.credits_used": "Credits used",
|
||||
"credit.paper.credits_remaining": "Credits remaining",
|
||||
"credit.paper.conditions_title": "Conditions & terms",
|
||||
"credit.paper.notes": "Notes",
|
||||
|
||||
"payment.paper.payment_receipt": "Payment Receipt",
|
||||
"payment.paper.amount_received": "Amount received",
|
||||
"payment.paper.billed_to": "Billed to",
|
||||
"payment.paper.payment_date": "Payment date",
|
||||
"payment.paper.invoice_number": "Invoice number",
|
||||
"payment.paper.invoice_date": "Invoice date",
|
||||
"payment.paper.invoice_amount": "Invoice amount",
|
||||
"payment.paper.payment_amount": "Payment amount",
|
||||
"payment.paper.balance_due": "Balance Due",
|
||||
"payment.paper.statement": "Statement",
|
||||
|
||||
"account.field.name": "Account name",
|
||||
"account.field.description": "Description",
|
||||
"account.field.slug": "Account slug",
|
||||
"account.field.code": "Account code",
|
||||
"account.field.root_type": "Root type",
|
||||
"account.field.normal": "Account normal",
|
||||
"account.field.normal.credit": "Credit",
|
||||
"account.field.normal.debit": "Debit",
|
||||
"account.field.type": "Type",
|
||||
"account.field.active": "Activity",
|
||||
"account.field.balance": "Balance",
|
||||
"account.field.created_at": "Created at",
|
||||
"item.field.type": "Item type",
|
||||
"item.field.type.inventory": "Inventory",
|
||||
"item.field.type.service": "Service",
|
||||
"item.field.type.non-inventory": "Non inventory",
|
||||
"item.field.name": "Name",
|
||||
"item.field.code": "Code",
|
||||
"item.field.sellable": "Sellable",
|
||||
"item.field.purchasable": "Purchasable",
|
||||
"item.field.cost_price": "Cost price",
|
||||
"item.field.cost_account": "Cost account",
|
||||
"item.field.sell_account": "Sell account",
|
||||
"item.field.sell_description": "Sell description",
|
||||
"item.field.inventory_account": "Inventory account",
|
||||
"item.field.purchase_description": "Purchase description",
|
||||
"item.field.quantity_on_hand": "Quantity on hand",
|
||||
"item.field.note": "Note",
|
||||
"item.field.category": "Category",
|
||||
"item.field.active": "Active",
|
||||
"item.field.created_at": "Created at",
|
||||
"item_category.field.name": "Name",
|
||||
"item_category.field.description": "Description",
|
||||
"item_category.field.count": "Count",
|
||||
"item_category.field.created_at": "Created at",
|
||||
"invoice.field.customer": "Customer",
|
||||
"invoice.field.invoice_date": "Invoice date",
|
||||
"invoice.field.due_date": "Due date",
|
||||
"invoice.field.invoice_no": "Invoice No.",
|
||||
"invoice.field.reference_no": "Reference No.",
|
||||
"invoice.field.invoice_message": "Invoice message",
|
||||
"invoice.field.terms_conditions": "Terms & conditions",
|
||||
"invoice.field.amount": "Amount",
|
||||
"invoice.field.payment_amount": "Payment amount",
|
||||
"invoice.field.due_amount": "Due amount",
|
||||
"invoice.field.status": "Status",
|
||||
"invoice.field.status.paid": "Paid",
|
||||
"invoice.field.status.partially-paid": "Partially paid",
|
||||
"invoice.field.status.overdue": "Overdue",
|
||||
"invoice.field.status.unpaid": "Unpaid",
|
||||
"invoice.field.status.delivered": "Delivered",
|
||||
"invoice.field.status.draft": "Draft",
|
||||
"invoice.field.created_at": "Created at",
|
||||
"estimate.field.amount": "Amount",
|
||||
"estimate.field.estimate_number": "Estimate number",
|
||||
"estimate.field.customer": "Customer",
|
||||
"estimate.field.estimate_date": "Estimate date",
|
||||
"estimate.field.expiration_date": "Expiration date",
|
||||
"estimate.field.reference_no": "Reference No.",
|
||||
"estimate.field.note": "Note",
|
||||
"estimate.field.terms_conditions": "Terms & conditions",
|
||||
"estimate.field.status": "Status",
|
||||
"estimate.field.status.delivered": "Delivered",
|
||||
"estimate.field.status.rejected": "Rejected",
|
||||
"estimate.field.status.approved": "Approved",
|
||||
"estimate.field.status.draft": "Draft",
|
||||
"estimate.field.created_at": "Created at",
|
||||
"payment_receive.field.customer": "Customer",
|
||||
"payment_receive.field.payment_date": "Payment date",
|
||||
"payment_receive.field.amount": "Amount",
|
||||
"payment_receive.field.reference_no": "Reference No.",
|
||||
"payment_receive.field.deposit_account": "Deposit account",
|
||||
"payment_receive.field.payment_receive_no": "Payment receive No.",
|
||||
"payment_receive.field.statement": "Statement",
|
||||
"payment_receive.field.created_at": "Created at",
|
||||
"bill_payment.field.vendor": "Vendor",
|
||||
"bill_payment.field.amount": "Amount",
|
||||
"bill_payment.field.due_amount": "Due amount",
|
||||
"bill_payment.field.payment_account": "Payment account",
|
||||
"bill_payment.field.payment_number": "Payment number",
|
||||
"bill_payment.field.payment_date": "Payment date",
|
||||
"bill_payment.field.reference_no": "Reference No.",
|
||||
"bill_payment.field.description": "Description",
|
||||
"bill_payment.field.created_at": "Created at",
|
||||
"bill.field.vendor": "Vendor",
|
||||
"bill.field.bill_number": "Bill number",
|
||||
"bill.field.bill_date": "Bill date",
|
||||
"bill.field.due_date": "Due date",
|
||||
"bill.field.reference_no": "Reference No.",
|
||||
"bill.field.status": "Status",
|
||||
"bill.field.status.paid": "Paid",
|
||||
"bill.field.status.partially-paid": "Partially paid",
|
||||
"bill.field.status.unpaid": "Unpaid",
|
||||
"bill.field.status.opened": "Opened",
|
||||
"bill.field.status.draft": "Draft",
|
||||
"bill.field.status.overdue": "overdue",
|
||||
"bill.field.amount": "Amount",
|
||||
"bill.field.payment_amount": "Payment amount",
|
||||
"bill.field.note": "Note",
|
||||
"bill.field.created_at": "Created at",
|
||||
"inventory_adjustment.field.date": "Date",
|
||||
"inventory_adjustment.field.type": "Type",
|
||||
"inventory_adjustment.field.type.increment": "Increment",
|
||||
"inventory_adjustment.field.type.decrement": "Decrement",
|
||||
"inventory_adjustment.field.adjustment_account": "Adjustment account",
|
||||
"inventory_adjustment.field.reason": "Reason",
|
||||
"inventory_adjustment.field.reference_no": "Reference No.",
|
||||
"inventory_adjustment.field.description": "Description",
|
||||
"inventory_adjustment.field.published_at": "Published at",
|
||||
"inventory_adjustment.field.created_at": "Created at",
|
||||
"expense.field.payment_date": "Payment date",
|
||||
"expense.field.payment_account": "Payment account",
|
||||
"expense.field.amount": "Amount",
|
||||
"expense.field.reference_no": "Reference No.",
|
||||
"expense.field.description": "Description",
|
||||
"expense.field.published": "Published",
|
||||
"expense.field.status": "Status",
|
||||
"expense.field.status.draft": "Draft",
|
||||
"expense.field.status.published": "Published",
|
||||
"expense.field.created_at": "Created at",
|
||||
"manual_journal.field.date": "Date",
|
||||
"manual_journal.field.journal_number": "Journal number",
|
||||
"manual_journal.field.reference": "Reference No.",
|
||||
"manual_journal.field.journal_type": "Journal type",
|
||||
"manual_journal.field.amount": "Amount",
|
||||
"manual_journal.field.description": "Description",
|
||||
"manual_journal.field.status": "Status",
|
||||
"manual_journal.field.created_at": "Created at",
|
||||
"receipt.field.amount": "Amount",
|
||||
"receipt.field.deposit_account": "Deposit account",
|
||||
"receipt.field.customer": "Customer",
|
||||
"receipt.field.receipt_date": "Receipt date",
|
||||
"receipt.field.receipt_number": "Receipt number",
|
||||
"receipt.field.reference_no": "Reference No.",
|
||||
"receipt.field.receipt_message": "Receipt message",
|
||||
"receipt.field.statement": "Statement",
|
||||
"receipt.field.created_at": "Created at",
|
||||
"receipt.field.status": "Status",
|
||||
"receipt.field.status.draft": "Draft",
|
||||
"receipt.field.status.closed": "Closed",
|
||||
"customer.field.first_name": "First name",
|
||||
"customer.field.last_name": "Last name",
|
||||
"customer.field.display_name": "Display name",
|
||||
"customer.field.email": "Email",
|
||||
"customer.field.work_phone": "Work phone",
|
||||
"customer.field.personal_phone": "Personal phone",
|
||||
"customer.field.company_name": "Company name",
|
||||
"customer.field.website": "Website",
|
||||
"customer.field.opening_balance_at": "Opening balance at",
|
||||
"customer.field.opening_balance": "Opening balance",
|
||||
"customer.field.created_at": "Created at",
|
||||
"customer.field.balance": "Balance",
|
||||
"customer.field.status": "Status",
|
||||
"customer.field.currency": "Curreny",
|
||||
"customer.field.status.active": "Active",
|
||||
"customer.field.status.inactive": "Inactive",
|
||||
"customer.field.status.overdue": "Overdue",
|
||||
"customer.field.status.unpaid": "Unpaid",
|
||||
"vendor.field.first_name": "First name",
|
||||
"vendor.field.last_name": "Last name",
|
||||
"vendor.field.display_name": "Display name",
|
||||
"vendor.field.email": "Email",
|
||||
"vendor.field.work_phone": "Work phone",
|
||||
"vendor.field.personal_phone": "Personal phone",
|
||||
"vendor.field.company_name": "Company name",
|
||||
"vendor.field.website": "Website",
|
||||
"vendor.field.opening_balance_at": "Opening balance at",
|
||||
"vendor.field.opening_balance": "Opening balance",
|
||||
"vendor.field.created_at": "Created at",
|
||||
"vendor.field.balance": "Balance",
|
||||
"vendor.field.status": "Status",
|
||||
"vendor.field.currency": "Curreny",
|
||||
"vendor.field.status.active": "Active",
|
||||
"vendor.field.status.inactive": "Inactive",
|
||||
"vendor.field.status.overdue": "Overdue",
|
||||
"vendor.field.status.unpaid": "Unpaid",
|
||||
"Invoice write-off": "Invoice write-off",
|
||||
|
||||
"transaction_type.credit_note": "Credit note",
|
||||
"transaction_type.refund_credit_note": "Refund credit note",
|
||||
"transaction_type.vendor_credit": "Vendor credit",
|
||||
"transaction_type.refund_vendor_credit": "Refund vendor credit",
|
||||
"transaction_type.landed_cost": "Landed cost",
|
||||
|
||||
"sms_notification.invoice_details.label": "Sale invoice details",
|
||||
"sms_notification.invoice_reminder.label": "Sale invoice reminder",
|
||||
"sms_notification.receipt_details.label": "Sale receipt details",
|
||||
"sms_notification.sale_estimate_details.label": "Sale estimate details",
|
||||
"sms_notification.payment_receive_details.label": "Payment receive details",
|
||||
"sms_notification.customer_balance.label": "Customer balance",
|
||||
|
||||
"sms_notification.invoice_details.description": "SMS notification will be sent to your customer once invoice created and published or when notify customer via SMS about the invoice.",
|
||||
"sms_notification.payment_receive.description": "Payment thank you message notification will be sent to customer once the payment created and published or notify customer about payment manually.",
|
||||
"sms_notification.receipt_details.description": "SMS notification will be sent to your cusotmer once receipt created and published or when notify customer about the receipt manually.",
|
||||
"sms_notification.customer_balance.description": "Send SMS to notify customers about their current outstanding balance.",
|
||||
"sms_notification.estimate_details.description": "SMS notification will be sent to your customer once estimate publish or notify customer about estimate manually.",
|
||||
"sms_notification.invoice_reminder.description": "SMS notification will be sent to remind the customer to pay earliest, either automatically or manually.",
|
||||
|
||||
"sms_notification.customer_balance.default_message": "Dear {CustomerName}, This is reminder about your current outstanding balance of {Balance}, Please pay at the earliest. - {CompanyName}",
|
||||
"sms_notification.payment_receive.default_message": "'Hi, {CustomerName}, We have received your payment for the invoice - {InvoiceNumber}. We look forward to serving you again. Thank you. - {CompanyName}'",
|
||||
"sms_notification.estimate.default_message": "Hi, {CustomerName}, We have created an estimate - {EstimateNumber} for you. Please take a look and accept it to proceed further. Looking forward to hearing from you. - {CompanyName}",
|
||||
|
||||
"sms_notification.invoice_details.default_message": "Hi, {CustomerName}, You have an outstanding amount of {DueAmount} for the invoice {InvoiceNumber}. - {CompanyName}",
|
||||
"sms_notification.receipt_details.default_message": "Hi, {CustomerName}, We have created receipt - {ReceiptNumber} for you. we look forward to serveing you again. Thank your - {CompanyName}",
|
||||
"sms_notification.invoice_reminder.default_message": "Dear {CustomerName}, The payment towards the invoice - {InvoiceNumber} is due. Please pay before {DueDate}. Thank you. - {CompanyName}",
|
||||
|
||||
"module.sale_invoices.label": "Sale invoices",
|
||||
"module.sale_receipts.label": "Sale receipts",
|
||||
"module.sale_estimates.label": "Sale estimates",
|
||||
"module.payment_receives.label": "Payment receive",
|
||||
"module.customers.label": "Customers",
|
||||
|
||||
"sms_notification.invoice.var.invoice_number": "References to invoice number.",
|
||||
"sms_notification.invoice.var.reference_number": "References to invoice reference number.",
|
||||
"sms_notification.invoice.var.customer_name": "References to invoice customer name.",
|
||||
"sms_notification.invoice.var.due_amount": "References to invoice due amount.",
|
||||
"sms_notification.invoice.var.amount": "References to invoice amount.",
|
||||
"sms_notification.invoice.var.company_name": "References to company name.",
|
||||
"sms_notification.invoice.var.due_date": "References to invoice due date.",
|
||||
|
||||
"sms_notification.receipt.var.receipt_number": "References to receipt number.",
|
||||
"sms_notification.receipt.var.reference_number": "References to receipt reference number.",
|
||||
"sms_notification.receipt.var.customer_name": "References to receipt customer name.",
|
||||
"sms_notification.receipt.var.amount": "References to receipt amount.",
|
||||
"sms_notification.receipt.var.company_name": "References to company name.",
|
||||
|
||||
"sms_notification.payment.var.payment_number": "References to payment transaction number.",
|
||||
"sms_notification.payment.var.reference_number": "References to payment reference number",
|
||||
"sms_notification.payment.var.customer_name": "References to payment customer name.",
|
||||
"sms_notification.payment.var.amount": "References to payment transaction amount.",
|
||||
"sms_notification.payment.company_name": "References to company name",
|
||||
"sms_notification.payment.var.invoice_number": "Reference to payment invoice number.",
|
||||
|
||||
"sms_notification.estimate.var.estimate_number": "References to estimate number.",
|
||||
"sms_notification.estimate.var.reference_number": "References to estimate reference number.",
|
||||
"sms_notification.estimate.var.customer_name": "References to estimate customer name.",
|
||||
"sms_notification.estimate.var.amount": "References to estimate amount.",
|
||||
"sms_notification.estimate.var.company_name": "References to company name.",
|
||||
"sms_notification.estimate.var.expiration_date": "References to estimate expirtaion date.",
|
||||
"sms_notification.estimate.var.estimate_date": "References to estimate date.",
|
||||
|
||||
"sms_notification.customer.var.customer_name": "References to customer name.",
|
||||
"sms_notification.customer.var.balance": "References to customer outstanding balance.",
|
||||
"sms_notification.customer.var.company_name": "References to company name.",
|
||||
|
||||
"ability.accounts": "Chart of accounts",
|
||||
"ability.manual_journal": "Manual journals",
|
||||
"ability.cashflow": "Cash flow",
|
||||
"ability.inventory_adjustment": "Inventory adjustments",
|
||||
"ability.customers": "Customers",
|
||||
"ability.vendors": "vendors",
|
||||
"ability.sale_estimates": "Sale estimates",
|
||||
"ability.sale_invoices": "Sale invoices",
|
||||
"ability.sale_receipts": "Sale receipts",
|
||||
"ability.expenses": "Expenses",
|
||||
"ability.payments_receive": "Payments receive",
|
||||
"ability.purchase_invoices": "Purchase invoices",
|
||||
"ability.all_reports": "All reports",
|
||||
"ability.payments_made": "Payments made",
|
||||
"ability.preferences": "Preferences",
|
||||
"ability.mutate_system_preferences": "Mutate the system preferences.",
|
||||
|
||||
"ability.items": "Items",
|
||||
"ability.view": "View",
|
||||
"ability.create": "Create",
|
||||
"ability.edit": "Edit",
|
||||
"ability.delete": "Delete",
|
||||
"ability.transactions_locking": "Ability to transactions locking.",
|
||||
|
||||
"ability.balance_sheet_report": "Balance sheet.",
|
||||
"ability.profit_loss_sheet": "Profit/loss sheet",
|
||||
"ability.journal": "Journal",
|
||||
"ability.general_ledger": "General ledger",
|
||||
"ability.cashflow_report": "Cashflow",
|
||||
"ability.AR_aging_summary_report": "A/R aging summary",
|
||||
"ability.AP_aging_summary_report": "A/P aging summary",
|
||||
"ability.purchases_by_items": "Purchases by items",
|
||||
"ability.sales_by_items_report": "Sales by items",
|
||||
"ability.customers_transactions_report": "Customers transactions",
|
||||
"ability.vendors_transactions_report": "Vendors transactions",
|
||||
"ability.customers_summary_balance_report": "Customers summary balance",
|
||||
"ability.vendors_summary_balance_report": "Vendors summary balance",
|
||||
"ability.inventory_valuation_summary": "Inventory valuation summary",
|
||||
"ability.inventory_items_details": "Inventory items details",
|
||||
|
||||
"vendor_credit.field.vendor": "Vendor name",
|
||||
"vendor_credit.field.amount": "Amount",
|
||||
"vendor_credit.field.currency_code": "Currency code",
|
||||
"vendor_credit.field.credit_date": "Credit date",
|
||||
"vendor_credit.field.credit_number": "Credit number",
|
||||
"vendor_credit.field.note": "Note",
|
||||
"vendor_credit.field.created_at": "Created at",
|
||||
"vendor_credit.field.reference_no": "Reference No.",
|
||||
|
||||
"credit_note.field.terms_conditions": "Terms and conditions",
|
||||
"credit_note.field.note": "Note",
|
||||
"credit_note.field.currency_code": "Currency code",
|
||||
"credit_note.field.created_at": "Created at",
|
||||
"credit_note.field.amount": "Amount",
|
||||
"credit_note.field.credit_note_number": "Credit note number",
|
||||
"credit_note.field.credit_note_date": "Credit date",
|
||||
"credit_note.field.customer": "Customer",
|
||||
"credit_note.field.reference_no": "Reference No.",
|
||||
|
||||
"Credit note": "Credit note",
|
||||
"Vendor credit": "Vendor credit",
|
||||
"Refund credit note": "Refund credit note",
|
||||
"Refund vendor credit": "Refund vendor credit",
|
||||
"credit_note.field.status": "Status",
|
||||
"credit_note.field.status.draft": "Draft",
|
||||
"credit_note.field.status.published": "Published",
|
||||
"credit_note.field.status.open": "Open",
|
||||
"credit_note.field.status.closed": "Closed",
|
||||
|
||||
"transactions_locking.module.sales.label": "Sales",
|
||||
"transactions_locking.module.purchases.label": "Purchases",
|
||||
"transactions_locking.module.financial.label": "Financial",
|
||||
"transactions_locking.module.all_transactions": "All transactions",
|
||||
|
||||
"transactions_locking.module.sales.desc": "Sale invoices, Receipts, credit notes, customers payment receive and customers opening balances.",
|
||||
"transactions_locking.module.purchases.desc": "Purchase invoices, vendors payments, vendor credit notes and vendors opening balances.",
|
||||
"transactions_locking.module.financial.desc": "Manual journal, expenses and inventory adjustments.",
|
||||
|
||||
"inventory_adjustment.type.increment": "Increment",
|
||||
"inventory_adjustment.type.decrement": "Decrement",
|
||||
|
||||
"customer.type.individual": "Individual",
|
||||
"customer.type.business": "Business",
|
||||
|
||||
"credit_note.view.draft": "Draft",
|
||||
"credit_note.view.closed": "Closed",
|
||||
"credit_note.view.open": "Open",
|
||||
"credit_note.view.published": "Published",
|
||||
|
||||
"vendor_credit.view.draft": "Draft",
|
||||
"vendor_credit.view.closed": "Closed",
|
||||
"vendor_credit.view.open": "Open",
|
||||
"vendor_credit.view.published": "Published",
|
||||
|
||||
"allocation_method.value.label": "Value",
|
||||
"allocation_method.quantity.label": "Quantity",
|
||||
|
||||
"balance_sheet.assets": "Assets",
|
||||
"balance_sheet.current_asset": "Current Asset",
|
||||
"balance_sheet.cash_and_cash_equivalents": "Cash and cash equivalents",
|
||||
"balance_sheet.accounts_receivable": "Accounts Receivable",
|
||||
"balance_sheet.inventory": "Inventory",
|
||||
"balance_sheet.other_current_assets": "Other current assets",
|
||||
"balance_sheet.fixed_asset": "Fixed Asset",
|
||||
"balance_sheet.non_current_assets": "Non-Current Assets",
|
||||
"balance_sheet.liabilities_and_equity": "Liabilities and Equity",
|
||||
"balance_sheet.liabilities": "Liabilities",
|
||||
"balance_sheet.current_liabilties": "Current Liabilties",
|
||||
"balance_sheet.long_term_liabilities": "Long-Term Liabilities",
|
||||
"balance_sheet.non_current_liabilities": "Non-Current Liabilities",
|
||||
"balance_sheet.equity": "Equity",
|
||||
|
||||
"balance_sheet.account_name": "Account name",
|
||||
"balance_sheet.total": "Total",
|
||||
"balance_sheet.percentage_of_column": "% of Column",
|
||||
"balance_sheet.percentage_of_row": "% of Row",
|
||||
|
||||
"financial_sheet.previoud_period_date": "{{date}} (PP)",
|
||||
"fianncial_sheet.previous_period_change": "Change (PP)",
|
||||
"financial_sheet.previous_period_percentage": "% Change (PP)",
|
||||
|
||||
"financial_sheet.previous_year_date": "{{date}} (PY)",
|
||||
"financial_sheet.previous_year_change": "Change (PY)",
|
||||
"financial_sheet.previous_year_percentage": "% Change (PY)",
|
||||
"financial_sheet.total_row": "Total {{value}}",
|
||||
|
||||
"profit_loss_sheet.income": "Income",
|
||||
"profit_loss_sheet.cost_of_sales": "Cost of sales",
|
||||
"profit_loss_sheet.gross_profit": "GROSS PROFIT",
|
||||
"profit_loss_sheet.expenses": "Expenses",
|
||||
"profit_loss_sheet.net_operating_income": "NET OPERATING INCOME",
|
||||
"profit_loss_sheet.other_income": "Other income",
|
||||
"profit_loss_sheet.other_expenses": "Other expenses",
|
||||
"profit_loss_sheet.net_income": "NET INCOME",
|
||||
|
||||
"profit_loss_sheet.account_name": "Account name",
|
||||
"profit_loss_sheet.total": "Total",
|
||||
|
||||
"profit_loss_sheet.percentage_of_income": "% of Income",
|
||||
"profit_loss_sheet.percentage_of_expenses": "% of Expenses",
|
||||
"profit_loss_sheet.percentage_of_column": "% of Column",
|
||||
"profit_loss_sheet.percentage_of_row": "% of Row",
|
||||
|
||||
"contact_summary_balance.account_name": "Account name",
|
||||
"contact_summary_balance.total": "Total",
|
||||
"contact_summary_balance.percentage_column": "% of Column",
|
||||
|
||||
"warehouses.primary_warehouse": "Primary warehouse",
|
||||
"branches.head_branch": "Head Branch",
|
||||
|
||||
"account.accounts_payable.currency": "Accounts Payable (A/P) - {{currency}}",
|
||||
"account.accounts_receivable.currency": "Accounts Receivable (A/R) - {{currency}}",
|
||||
|
||||
"role.admin.name": "Admin",
|
||||
"role.admin.desc": "Unrestricted access to all modules.",
|
||||
|
||||
"role.staff.name": "Staff",
|
||||
"role.staff.desc": "Access to all modules except reports, settings and accountant.",
|
||||
|
||||
"warehouse_transfer.view.draft.name": "Draft",
|
||||
"warehouse_transfer.view.in_transit.name": "In Transit",
|
||||
"warehouse_transfer.view.transferred.name": "Transferred"
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
@import "./normalize.scss";
|
||||
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
th {
|
||||
text-align: inherit; // 2
|
||||
text-align: -webkit-match-parent; // 3
|
||||
}
|
||||
|
||||
thead,
|
||||
tbody,
|
||||
tfoot,
|
||||
tr,
|
||||
td,
|
||||
th {
|
||||
border-color: inherit;
|
||||
border-style: solid;
|
||||
border-width: 0;
|
||||
}
|
||||
|
||||
body{
|
||||
margin: 0;
|
||||
font-size: 1rem;
|
||||
font-weight: 400;
|
||||
line-height: 1.5;
|
||||
color: #212529;
|
||||
background-color: #fff;
|
||||
direction: ltr;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -1,19 +0,0 @@
|
||||
@import "../base.scss";
|
||||
@import "../fonts.scss";
|
||||
|
||||
body {
|
||||
background: #f8f9fa;
|
||||
text-align: left;
|
||||
-webkit-print-color-adjust: exact;
|
||||
|
||||
html[lang^='ar'] & {
|
||||
font-family: "Segoe UI";
|
||||
}
|
||||
html[lang^='en'] & {
|
||||
font-family: "Noto Sans";
|
||||
}
|
||||
|
||||
@media print {
|
||||
background: #fff;
|
||||
}
|
||||
}
|
||||
@@ -1,193 +0,0 @@
|
||||
@import "../layouts/paper-layout.scss";
|
||||
|
||||
.credit {
|
||||
text-align: left;
|
||||
padding: 45px 40px;
|
||||
|
||||
&__header {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
margin: 0 0 30px;
|
||||
|
||||
.organization {
|
||||
.title {
|
||||
margin: 0 0 4px;
|
||||
}
|
||||
|
||||
.creditNumber {
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.paper {
|
||||
|
||||
.title {
|
||||
font-weight: 400;
|
||||
text-transform: uppercase;
|
||||
margin: 0 0 2px;
|
||||
font-size: 32px;
|
||||
line-height: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
&__full-amount {
|
||||
margin-bottom: 18px;
|
||||
|
||||
.label {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.amount {
|
||||
font-size: 18px;
|
||||
font-weight: 800;
|
||||
}
|
||||
}
|
||||
|
||||
&__meta {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-bottom: 20px;
|
||||
font-size: 13px;
|
||||
|
||||
&-item {
|
||||
padding-right: 10px;
|
||||
font-weight: 400;
|
||||
margin-bottom: 10px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
.value {
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.label {
|
||||
color: #444;
|
||||
margin-bottom: 2px;
|
||||
width: 180px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__table {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
table {
|
||||
font-size: 12px;
|
||||
color: #000;
|
||||
text-align: left;
|
||||
border-spacing: 0;
|
||||
|
||||
thead th,
|
||||
tbody tr td {
|
||||
margin-bottom: 15px;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
thead th {
|
||||
font-weight: 400;
|
||||
border-bottom: none;
|
||||
padding: 8px;
|
||||
color: #fff;
|
||||
background-color: #333;
|
||||
}
|
||||
|
||||
tbody tr td {
|
||||
padding: 8px;
|
||||
border-bottom: 1px solid #cecbcb;
|
||||
}
|
||||
|
||||
thead tr th,
|
||||
tbody tr td {
|
||||
&.item {
|
||||
width: 45%;
|
||||
}
|
||||
|
||||
&.rate {
|
||||
width: 18%;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
&.quantity {
|
||||
width: 16%;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
&.total {
|
||||
width: 21%;
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
|
||||
.description {
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__table-after {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
&__table-total {
|
||||
margin-bottom: 20px;
|
||||
width: 50%;
|
||||
float: right;
|
||||
margin-left: auto;
|
||||
|
||||
table {
|
||||
border-spacing: 0;
|
||||
width: 100%;
|
||||
font-size: 12px;
|
||||
|
||||
tbody tr td {
|
||||
padding: 8px 10px 8px 0;
|
||||
border-top: 1px solid #d5d5d5;
|
||||
|
||||
&:last-child {
|
||||
width: 140px;
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
|
||||
tbody tr:first-child td {
|
||||
border-top: 0;
|
||||
}
|
||||
|
||||
tbody tr.payment-amount td:last-child {
|
||||
color: red
|
||||
}
|
||||
|
||||
tbody tr.blanace-due td {
|
||||
border-top: 3px double #666;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__footer {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
&__conditions,
|
||||
&__notes {
|
||||
|
||||
h3 {
|
||||
color: #666;
|
||||
font-size: 12px;
|
||||
margin-top: 0;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&__conditions+&__notes {
|
||||
margin-top: 20px;
|
||||
}
|
||||
}
|
||||
@@ -1,174 +0,0 @@
|
||||
@import "../layouts/paper-layout.scss";
|
||||
|
||||
.estimate {
|
||||
text-align: left;
|
||||
padding: 45px;
|
||||
|
||||
&__header {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
margin: 0 0 30px;
|
||||
|
||||
.organization {
|
||||
.title {
|
||||
margin: 0 0 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.paper {
|
||||
.title {
|
||||
font-weight: 400;
|
||||
text-transform: uppercase;
|
||||
margin: 0 0 2px;
|
||||
font-size: 32px;
|
||||
line-height: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__estimate-amount {
|
||||
margin-bottom: 18px;
|
||||
|
||||
.label {
|
||||
font-size: 12px;
|
||||
}
|
||||
.amount {
|
||||
font-size: 18px;
|
||||
font-weight: 800;
|
||||
}
|
||||
}
|
||||
&__meta {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-bottom: 20px;
|
||||
font-size: 13px;
|
||||
|
||||
&-item {
|
||||
padding-right: 10px;
|
||||
margin-bottom: 10px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
.value {
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.label {
|
||||
color: #444;
|
||||
margin-bottom: 2px;
|
||||
width: 180px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__table {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
table {
|
||||
font-size: 12px;
|
||||
color: #000;
|
||||
text-align: left;
|
||||
border-spacing: 0;
|
||||
|
||||
thead th,
|
||||
tbody tr td {
|
||||
margin-bottom: 15px;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
thead th {
|
||||
font-weight: 400;
|
||||
border-bottom: none;
|
||||
padding: 8px;
|
||||
color: #fff;
|
||||
background-color: #333;
|
||||
}
|
||||
|
||||
tbody tr td {
|
||||
padding: 8px;
|
||||
border-bottom: 1px solid #cecbcb;
|
||||
}
|
||||
|
||||
thead tr th,
|
||||
tbody tr td {
|
||||
&.item {
|
||||
width: 45%;
|
||||
}
|
||||
|
||||
&.rate {
|
||||
width: 18%;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
&.quantity {
|
||||
width: 16%;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
&.total {
|
||||
width: 21%;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.description {
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__table-after {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
&__table-total {
|
||||
margin-bottom: 20px;
|
||||
width: 50%;
|
||||
float: right;
|
||||
margin-left: auto;
|
||||
|
||||
table {
|
||||
border-spacing: 0;
|
||||
width: 100%;
|
||||
font-size: 12px;
|
||||
|
||||
tbody tr td {
|
||||
padding: 8px 10px 8px 0;
|
||||
border-top: 1px solid #d5d5d5;
|
||||
|
||||
&:last-child {
|
||||
width: 140px;
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
|
||||
tbody tr:first-child td {
|
||||
border-top: 0;
|
||||
}
|
||||
|
||||
tbody tr.total td {
|
||||
border-top: 3px double #666;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__footer{
|
||||
font-size: 12px;
|
||||
}
|
||||
&__conditions,
|
||||
&__notes {
|
||||
|
||||
h3 {
|
||||
color: #666;
|
||||
font-size: 12px;
|
||||
margin-top: 0;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
p {
|
||||
margin: 0 0 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,179 +0,0 @@
|
||||
@import "../layouts/paper-layout.scss";
|
||||
|
||||
.invoice {
|
||||
text-align: left;
|
||||
padding: 45px 40px;
|
||||
|
||||
&__header {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
margin: 0 0 30px;
|
||||
|
||||
.organization {
|
||||
.title {
|
||||
margin: 0 0 4px;
|
||||
}
|
||||
.invoiceNo {
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.paper {
|
||||
|
||||
.title {
|
||||
font-weight: 400;
|
||||
text-transform: uppercase;
|
||||
margin: 0 0 2px;
|
||||
font-size: 32px;
|
||||
line-height: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__meta {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-bottom: 20px;
|
||||
font-size: 13px;
|
||||
|
||||
&-item {
|
||||
padding-right: 10px;
|
||||
font-weight: 400;
|
||||
margin-bottom: 10px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
.value {
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.label {
|
||||
color: #444;
|
||||
margin-bottom: 2px;
|
||||
width: 180px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__table {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
table {
|
||||
font-size: 12px;
|
||||
color: #000;
|
||||
text-align: left;
|
||||
border-spacing: 0;
|
||||
|
||||
thead th,
|
||||
tbody tr td {
|
||||
margin-bottom: 15px;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
thead th {
|
||||
font-weight: 400;
|
||||
border-bottom: none;
|
||||
padding: 8px;
|
||||
color: #fff;
|
||||
background-color: #333;
|
||||
}
|
||||
tbody tr td {
|
||||
padding: 8px;
|
||||
border-bottom: 1px solid #cecbcb;
|
||||
}
|
||||
|
||||
thead tr th,
|
||||
tbody tr td {
|
||||
&.item {
|
||||
width: 45%;
|
||||
}
|
||||
&.rate {
|
||||
width: 18%;
|
||||
text-align: right;
|
||||
}
|
||||
&.quantity {
|
||||
width: 16%;
|
||||
text-align: right;
|
||||
}
|
||||
&.total {
|
||||
width: 21%;
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
.description {
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__table-after{
|
||||
display: flex;
|
||||
}
|
||||
&__table-total {
|
||||
margin-bottom: 20px;
|
||||
width: 50%;
|
||||
float: right;
|
||||
margin-left: auto;
|
||||
|
||||
table {
|
||||
border-spacing: 0;
|
||||
width: 100%;
|
||||
font-size: 12px;
|
||||
|
||||
tbody tr td {
|
||||
padding: 8px 10px 8px 0;
|
||||
border-top: 1px solid #d5d5d5;
|
||||
|
||||
&:last-child {
|
||||
width: 140px;
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
tbody tr:first-child td {
|
||||
border-top: 0;
|
||||
}
|
||||
tbody tr.payment-amount td:last-child {
|
||||
color: red
|
||||
}
|
||||
tbody tr.blanace-due td {
|
||||
border-top: 3px double #666;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__due-amount {
|
||||
margin-bottom: 18px;
|
||||
|
||||
.label {
|
||||
font-size: 12px;
|
||||
}
|
||||
.amount {
|
||||
font-size: 18px;
|
||||
font-weight: 800;
|
||||
}
|
||||
}
|
||||
|
||||
&__footer{
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
&__conditions,
|
||||
&__notes {
|
||||
|
||||
h3 {
|
||||
color: #666;
|
||||
font-size: 12px;
|
||||
margin-top: 0;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
p{
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
&__conditions + &__notes{
|
||||
margin-top: 20px;
|
||||
}
|
||||
}
|
||||
@@ -1,178 +0,0 @@
|
||||
@import "../layouts/paper-layout.scss";
|
||||
|
||||
.payment {
|
||||
text-align: left;
|
||||
padding: 45px 40px;
|
||||
|
||||
&__header {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
margin: 0 0 30px;
|
||||
|
||||
.organization {
|
||||
.title {
|
||||
margin: 0 0 4px;
|
||||
}
|
||||
.paymentNumber {
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.paper {
|
||||
|
||||
.title {
|
||||
font-weight: 400;
|
||||
text-transform: uppercase;
|
||||
margin: 0 0 2px;
|
||||
font-size: 32px;
|
||||
line-height: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__meta {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-bottom: 20px;
|
||||
font-size: 13px;
|
||||
|
||||
&-item {
|
||||
padding-right: 10px;
|
||||
font-weight: 400;
|
||||
margin-bottom: 10px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
.value {
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.label {
|
||||
color: #444;
|
||||
margin-bottom: 2px;
|
||||
width: 180px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__table {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
table {
|
||||
font-size: 12px;
|
||||
color: #000;
|
||||
text-align: left;
|
||||
border-spacing: 0;
|
||||
|
||||
thead th,
|
||||
tbody tr td {
|
||||
margin-bottom: 15px;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
thead th {
|
||||
font-weight: 400;
|
||||
border-bottom: none;
|
||||
padding: 8px;
|
||||
color: #fff;
|
||||
background-color: #333;
|
||||
}
|
||||
tbody tr td {
|
||||
padding: 8px;
|
||||
border-bottom: 1px solid #cecbcb;
|
||||
}
|
||||
|
||||
thead tr th,
|
||||
tbody tr td {
|
||||
&.item {
|
||||
width: 34%;
|
||||
}
|
||||
&.date {
|
||||
width: 22%;
|
||||
text-align: right;
|
||||
}
|
||||
&.invoiceAmount {
|
||||
width: 22%;
|
||||
text-align: right;
|
||||
}
|
||||
&.paymentAmount {
|
||||
width: 22%;
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
.description {
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__table-after{
|
||||
display: flex;
|
||||
}
|
||||
&__table-total {
|
||||
margin-bottom: 20px;
|
||||
width: 50%;
|
||||
float: right;
|
||||
margin-left: auto;
|
||||
|
||||
table {
|
||||
border-spacing: 0;
|
||||
width: 100%;
|
||||
font-size: 12px;
|
||||
|
||||
tbody tr td {
|
||||
padding: 8px 10px 8px 0;
|
||||
border-top: 1px solid #d5d5d5;
|
||||
|
||||
&:last-child {
|
||||
width: 140px;
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
tbody tr:first-child td {
|
||||
border-top: 0;
|
||||
}
|
||||
tbody tr.payment-amount td:last-child {
|
||||
color: red
|
||||
}
|
||||
tbody tr.blanace-due td {
|
||||
border-top: 3px double #666;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__received-amount {
|
||||
margin-bottom: 18px;
|
||||
|
||||
.label {
|
||||
font-size: 12px;
|
||||
}
|
||||
.amount {
|
||||
font-size: 18px;
|
||||
font-weight: 800;
|
||||
}
|
||||
}
|
||||
|
||||
&__footer{
|
||||
font-size: 12px;
|
||||
}
|
||||
&__conditions,
|
||||
&__notes {
|
||||
|
||||
h3 {
|
||||
color: #666;
|
||||
font-size: 12px;
|
||||
margin-top: 0;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
p{
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
&__conditions + &__notes{
|
||||
margin-top: 20px;
|
||||
}
|
||||
}
|
||||
@@ -1,185 +0,0 @@
|
||||
@import "../layouts/paper-layout.scss";
|
||||
|
||||
.receipt {
|
||||
text-align: left;
|
||||
padding: 45px;
|
||||
|
||||
&__header {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
margin: 0 0 30px;
|
||||
|
||||
.organization {
|
||||
|
||||
.title {
|
||||
margin: 0 0 4px;
|
||||
}
|
||||
|
||||
.receiptNumber {
|
||||
margin: 0 0 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.paper {
|
||||
|
||||
.title {
|
||||
font-weight: 400;
|
||||
text-transform: uppercase;
|
||||
margin: 0 0 2px;
|
||||
font-size: 32px;
|
||||
line-height: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
&__receipt-amount {
|
||||
margin-bottom: 18px;
|
||||
|
||||
.label {
|
||||
font-size: 12px;
|
||||
}
|
||||
.amount {
|
||||
font-size: 18px;
|
||||
font-weight: 800;
|
||||
}
|
||||
}
|
||||
|
||||
&__meta {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-bottom: 20px;
|
||||
font-size: 13px;
|
||||
|
||||
&-item {
|
||||
padding-right: 10px;
|
||||
margin-bottom: 10px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
.value {
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.label {
|
||||
color: #444;
|
||||
margin-bottom: 2px;
|
||||
width: 180px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__table {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
table {
|
||||
font-size: 12px;
|
||||
color: #000;
|
||||
text-align: left;
|
||||
border-spacing: 0;
|
||||
|
||||
thead th,
|
||||
tbody tr td {
|
||||
margin-bottom: 15px;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
thead th {
|
||||
font-weight: 400;
|
||||
border-bottom: none;
|
||||
padding: 8px;
|
||||
color: #fff;
|
||||
background-color: #333;
|
||||
}
|
||||
|
||||
tbody tr td {
|
||||
padding: 10px;
|
||||
border-bottom: 1px solid #cecbcb;
|
||||
}
|
||||
|
||||
thead tr th,
|
||||
tbody tr td {
|
||||
&.item {
|
||||
width: 45%;
|
||||
}
|
||||
|
||||
&.rate {
|
||||
width: 18%;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
&.quantity {
|
||||
width: 16%;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
&.total {
|
||||
width: 21%;
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__table-after {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
&__table-total {
|
||||
margin-bottom: 20px;
|
||||
width: 50%;
|
||||
float: right;
|
||||
margin-left: auto;
|
||||
|
||||
table {
|
||||
border-spacing: 0;
|
||||
width: 100%;
|
||||
font-size: 12px;
|
||||
|
||||
tbody tr td {
|
||||
padding: 8px 10px 8px 0;
|
||||
border-top: 1px solid #d5d5d5;
|
||||
|
||||
&:last-child {
|
||||
width: 140px;
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
|
||||
tbody tr:first-child td {
|
||||
border-top: 0;
|
||||
}
|
||||
|
||||
tbody tr.payment-amount td:last-child {
|
||||
color: red
|
||||
}
|
||||
|
||||
tbody tr.blanace-due td {
|
||||
border-top: 3px double #666;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
&__footer {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
&__conditions,
|
||||
&__notes {
|
||||
|
||||
h3 {
|
||||
color: #666;
|
||||
font-size: 12px;
|
||||
margin-top: 0;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0 0 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
379
packages/server/resources/scss/normalize.scss
vendored
379
packages/server/resources/scss/normalize.scss
vendored
@@ -1,379 +0,0 @@
|
||||
/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */
|
||||
|
||||
/* Document
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* 1. Correct the line height in all browsers.
|
||||
* 2. Prevent adjustments of font size after orientation changes in iOS.
|
||||
*/
|
||||
|
||||
html {
|
||||
line-height: 1.15;
|
||||
/* 1 */
|
||||
-webkit-text-size-adjust: 100%;
|
||||
/* 2 */
|
||||
}
|
||||
|
||||
/* Sections
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Remove the margin in all browsers.
|
||||
*/
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the `main` element consistently in IE.
|
||||
*/
|
||||
|
||||
main {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/**
|
||||
* Correct the font size and margin on `h1` elements within `section` and
|
||||
* `article` contexts in Chrome, Firefox, and Safari.
|
||||
*/
|
||||
|
||||
h1 {
|
||||
font-size: 2em;
|
||||
margin: 0.67em 0;
|
||||
}
|
||||
|
||||
/* Grouping content
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* 1. Add the correct box sizing in Firefox.
|
||||
* 2. Show the overflow in Edge and IE.
|
||||
*/
|
||||
|
||||
hr {
|
||||
box-sizing: content-box;
|
||||
/* 1 */
|
||||
height: 0;
|
||||
/* 1 */
|
||||
overflow: visible;
|
||||
/* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the inheritance and scaling of font size in all browsers.
|
||||
* 2. Correct the odd `em` font sizing in all browsers.
|
||||
*/
|
||||
|
||||
pre {
|
||||
font-family: monospace, monospace;
|
||||
/* 1 */
|
||||
font-size: 1em;
|
||||
/* 2 */
|
||||
}
|
||||
|
||||
/* Text-level semantics
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Remove the gray background on active links in IE 10.
|
||||
*/
|
||||
|
||||
a {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Remove the bottom border in Chrome 57-
|
||||
* 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
|
||||
*/
|
||||
|
||||
abbr[title] {
|
||||
border-bottom: none;
|
||||
/* 1 */
|
||||
text-decoration: underline;
|
||||
/* 2 */
|
||||
text-decoration: underline dotted;
|
||||
/* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct font weight in Chrome, Edge, and Safari.
|
||||
*/
|
||||
|
||||
b,
|
||||
strong {
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the inheritance and scaling of font size in all browsers.
|
||||
* 2. Correct the odd `em` font sizing in all browsers.
|
||||
*/
|
||||
|
||||
code,
|
||||
kbd,
|
||||
samp {
|
||||
font-family: monospace, monospace;
|
||||
/* 1 */
|
||||
font-size: 1em;
|
||||
/* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct font size in all browsers.
|
||||
*/
|
||||
|
||||
small {
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevent `sub` and `sup` elements from affecting the line height in
|
||||
* all browsers.
|
||||
*/
|
||||
|
||||
sub,
|
||||
sup {
|
||||
font-size: 75%;
|
||||
line-height: 0;
|
||||
position: relative;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
sub {
|
||||
bottom: -0.25em;
|
||||
}
|
||||
|
||||
sup {
|
||||
top: -0.5em;
|
||||
}
|
||||
|
||||
/* Embedded content
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Remove the border on images inside links in IE 10.
|
||||
*/
|
||||
|
||||
img {
|
||||
border-style: none;
|
||||
}
|
||||
|
||||
/* Forms
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* 1. Change the font styles in all browsers.
|
||||
* 2. Remove the margin in Firefox and Safari.
|
||||
*/
|
||||
|
||||
button,
|
||||
input,
|
||||
optgroup,
|
||||
select,
|
||||
textarea {
|
||||
font-family: inherit;
|
||||
/* 1 */
|
||||
font-size: 100%;
|
||||
/* 1 */
|
||||
line-height: 1.15;
|
||||
/* 1 */
|
||||
margin: 0;
|
||||
/* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the overflow in IE.
|
||||
* 1. Show the overflow in Edge.
|
||||
*/
|
||||
|
||||
button,
|
||||
input {
|
||||
/* 1 */
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the inheritance of text transform in Edge, Firefox, and IE.
|
||||
* 1. Remove the inheritance of text transform in Firefox.
|
||||
*/
|
||||
|
||||
button,
|
||||
select {
|
||||
/* 1 */
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* Correct the inability to style clickable types in iOS and Safari.
|
||||
*/
|
||||
|
||||
button,
|
||||
[type="button"],
|
||||
[type="reset"],
|
||||
[type="submit"] {
|
||||
-webkit-appearance: button;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the inner border and padding in Firefox.
|
||||
*/
|
||||
|
||||
button::-moz-focus-inner,
|
||||
[type="button"]::-moz-focus-inner,
|
||||
[type="reset"]::-moz-focus-inner,
|
||||
[type="submit"]::-moz-focus-inner {
|
||||
border-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore the focus styles unset by the previous rule.
|
||||
*/
|
||||
|
||||
button:-moz-focusring,
|
||||
[type="button"]:-moz-focusring,
|
||||
[type="reset"]:-moz-focusring,
|
||||
[type="submit"]:-moz-focusring {
|
||||
outline: 1px dotted ButtonText;
|
||||
}
|
||||
|
||||
/**
|
||||
* Correct the padding in Firefox.
|
||||
*/
|
||||
|
||||
fieldset {
|
||||
padding: 0.35em 0.75em 0.625em;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the text wrapping in Edge and IE.
|
||||
* 2. Correct the color inheritance from `fieldset` elements in IE.
|
||||
* 3. Remove the padding so developers are not caught out when they zero out
|
||||
* `fieldset` elements in all browsers.
|
||||
*/
|
||||
|
||||
legend {
|
||||
box-sizing: border-box;
|
||||
/* 1 */
|
||||
color: inherit;
|
||||
/* 2 */
|
||||
display: table;
|
||||
/* 1 */
|
||||
max-width: 100%;
|
||||
/* 1 */
|
||||
padding: 0;
|
||||
/* 3 */
|
||||
white-space: normal;
|
||||
/* 1 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct vertical alignment in Chrome, Firefox, and Opera.
|
||||
*/
|
||||
|
||||
progress {
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the default vertical scrollbar in IE 10+.
|
||||
*/
|
||||
|
||||
textarea {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Add the correct box sizing in IE 10.
|
||||
* 2. Remove the padding in IE 10.
|
||||
*/
|
||||
|
||||
[type="checkbox"],
|
||||
[type="radio"] {
|
||||
box-sizing: border-box;
|
||||
/* 1 */
|
||||
padding: 0;
|
||||
/* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Correct the cursor style of increment and decrement buttons in Chrome.
|
||||
*/
|
||||
|
||||
[type="number"]::-webkit-inner-spin-button,
|
||||
[type="number"]::-webkit-outer-spin-button {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the odd appearance in Chrome and Safari.
|
||||
* 2. Correct the outline style in Safari.
|
||||
*/
|
||||
|
||||
[type="search"] {
|
||||
-webkit-appearance: textfield;
|
||||
/* 1 */
|
||||
outline-offset: -2px;
|
||||
/* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the inner padding in Chrome and Safari on macOS.
|
||||
*/
|
||||
|
||||
[type="search"]::-webkit-search-decoration {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the inability to style clickable types in iOS and Safari.
|
||||
* 2. Change font properties to `inherit` in Safari.
|
||||
*/
|
||||
|
||||
::-webkit-file-upload-button {
|
||||
-webkit-appearance: button;
|
||||
/* 1 */
|
||||
font: inherit;
|
||||
/* 2 */
|
||||
}
|
||||
|
||||
/* Interactive
|
||||
========================================================================== */
|
||||
|
||||
/*
|
||||
* Add the correct display in Edge, IE 10+, and Firefox.
|
||||
*/
|
||||
|
||||
details {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add the correct display in all browsers.
|
||||
*/
|
||||
|
||||
summary {
|
||||
display: list-item;
|
||||
}
|
||||
|
||||
/* Misc
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Add the correct display in IE 10+.
|
||||
*/
|
||||
|
||||
template {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct display in IE 10.
|
||||
*/
|
||||
|
||||
[hidden] {
|
||||
display: none;
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
html(lang=locale)
|
||||
head
|
||||
title My Site - #{title}
|
||||
block head
|
||||
body
|
||||
div.paper-template
|
||||
block content
|
||||
@@ -1,81 +0,0 @@
|
||||
extends ../PaperTemplateLayout.pug
|
||||
|
||||
block head
|
||||
style
|
||||
if (isRtl)
|
||||
include ../../css/modules/credit-rtl.css
|
||||
else
|
||||
include ../../css/modules/credit.css
|
||||
|
||||
block content
|
||||
div.credit
|
||||
div.credit__header
|
||||
div.paper
|
||||
h1.title #{__('credit.paper.credit_note')}
|
||||
if creditNote.creditNoteNumber
|
||||
span.creditNoteNumber #{creditNote.creditNoteNumber}
|
||||
|
||||
div.organization
|
||||
h3.title #{organizationName}
|
||||
if organizationEmail
|
||||
span.email #{organizationEmail}
|
||||
|
||||
div.credit__full-amount
|
||||
div.label #{__('credit.paper.amount')}
|
||||
div.amount #{creditNote.formattedAmount}
|
||||
|
||||
div.credit__meta
|
||||
div.credit__meta-item.credit__meta-item--amount
|
||||
span.label #{__('credit.paper.remaining')}
|
||||
span.value #{creditNote.formattedCreditsRemaining}
|
||||
|
||||
div.credit__meta-item.credit__meta-item--billed-to
|
||||
span.label #{__("credit.paper.billed_to")}
|
||||
span.value #{creditNote.customer.displayName}
|
||||
|
||||
div.credit__meta-item.credit__meta-item--credit-date
|
||||
span.label #{__("credit.paper.credit_date")}
|
||||
span.value #{creditNote.formattedCreditNoteDate}
|
||||
|
||||
div.credit__table
|
||||
table
|
||||
thead
|
||||
tr
|
||||
th.item #{__("item_entry.paper.item_name")}
|
||||
th.rate #{__("item_entry.paper.rate")}
|
||||
th.quantity #{__("item_entry.paper.quantity")}
|
||||
th.total #{__("item_entry.paper.total")}
|
||||
tbody
|
||||
each entry in creditNote.entries
|
||||
tr
|
||||
td.item
|
||||
div.title=entry.item.name
|
||||
span.description=entry.description
|
||||
td.rate=entry.rate
|
||||
td.quantity=entry.quantity
|
||||
td.total=entry.amount
|
||||
|
||||
div.credit__table-after
|
||||
div.credit__table-total
|
||||
table
|
||||
tbody
|
||||
tr.total
|
||||
td #{__('credit.paper.total')}
|
||||
td #{creditNote.formattedAmount}
|
||||
tr.payment-amount
|
||||
td #{__('credit.paper.credits_used')}
|
||||
td #{creditNote.formattedCreditsUsed}
|
||||
tr.blanace-due
|
||||
td #{__('credit.paper.credits_remaining')}
|
||||
td #{creditNote.formattedCreditsRemaining}
|
||||
|
||||
div.credit__footer
|
||||
if creditNote.termsConditions
|
||||
div.credit__conditions
|
||||
h3 #{__("credit.paper.terms_conditions")}
|
||||
p #{creditNote.termsConditions}
|
||||
|
||||
if creditNote.note
|
||||
div.credit__notes
|
||||
h3 #{__("credit.paper.notes")}
|
||||
p #{creditNote.note}
|
||||
@@ -1,82 +0,0 @@
|
||||
extends ../PaperTemplateLayout.pug
|
||||
|
||||
block head
|
||||
style
|
||||
if (isRtl)
|
||||
include ../../css/modules/estimate-rtl.css
|
||||
else
|
||||
include ../../css/modules/estimate.css
|
||||
|
||||
block content
|
||||
div.estimate
|
||||
div.estimate__header
|
||||
div.paper
|
||||
h1.title #{__("estimate.paper.estimate")}
|
||||
span.email #{saleEstimate.estimateNumber}
|
||||
|
||||
div.organization
|
||||
h3.title #{organizationName}
|
||||
if organizationEmail
|
||||
span.email #{organizationEmail}
|
||||
|
||||
div.estimate__estimate-amount
|
||||
div.label #{__('estimate.paper.estimate_amount')}
|
||||
div.amount #{saleEstimate.formattedAmount}
|
||||
|
||||
div.estimate__meta
|
||||
if saleEstimate.estimateNumber
|
||||
div.estimate__meta-item.estimate__meta-item--estimate-number
|
||||
span.label #{__("estimate.paper.estimate_number")}
|
||||
span.value #{saleEstimate.estimateNumber}
|
||||
|
||||
div.estimate__meta-item.estimate__meta-item--billed-to
|
||||
span.label #{__("estimate.paper.billed_to")}
|
||||
span.value #{saleEstimate.customer.displayName}
|
||||
|
||||
div.estimate__meta-item.estimate__meta-item--estimate-date
|
||||
span.label #{__("estimate.paper.estimate_date")}
|
||||
span.value #{saleEstimate.formattedEstimateDate}
|
||||
|
||||
div.estimate__meta-item.estimate__meta-item--due-date
|
||||
span.label #{__("estimate.paper.expiration_date")}
|
||||
span.value #{saleEstimate.formattedExpirationDate}
|
||||
|
||||
div.estimate__table
|
||||
table
|
||||
thead
|
||||
tr
|
||||
th.item #{__("item_entry.paper.item_name")}
|
||||
th.rate #{__("item_entry.paper.rate")}
|
||||
th.quantity #{__("item_entry.paper.quantity")}
|
||||
th.total #{__("item_entry.paper.total")}
|
||||
tbody
|
||||
each entry in saleEstimate.entries
|
||||
tr
|
||||
td.item
|
||||
div.title=entry.item.name
|
||||
span.description=entry.description
|
||||
td.rate=entry.rate
|
||||
td.quantity=entry.quantity
|
||||
td.total=entry.amount
|
||||
|
||||
div.estimate__table-after
|
||||
div.estimate__table-total
|
||||
table
|
||||
tbody
|
||||
tr.subtotal
|
||||
td #{__('estimate.paper.subtotal')}
|
||||
td #{saleEstimate.formattedAmount}
|
||||
tr.total
|
||||
td #{__('estimate.paper.total')}
|
||||
td #{saleEstimate.formattedAmount}
|
||||
|
||||
div.estimate__footer
|
||||
if saleEstimate.termsConditions
|
||||
div.estimate__conditions
|
||||
h3 #{__("estimate.paper.conditions_title")}
|
||||
p #{saleEstimate.termsConditions}
|
||||
|
||||
if saleEstimate.note
|
||||
div.estimate__notes
|
||||
h3 #{__("estimate.paper.notes_title")}
|
||||
p #{saleEstimate.note}
|
||||
@@ -1,85 +0,0 @@
|
||||
extends ../PaperTemplateLayout.pug
|
||||
|
||||
block head
|
||||
style
|
||||
if (isRtl)
|
||||
include ../../css/modules/invoice-rtl.css
|
||||
else
|
||||
include ../../css/modules/invoice.css
|
||||
|
||||
block content
|
||||
div.invoice
|
||||
div.invoice__header
|
||||
div.paper
|
||||
h1.title #{__("invoice.paper.invoice")}
|
||||
if saleInvoice.invoiceNo
|
||||
span.invoiceNo #{saleInvoice.invoiceNo}
|
||||
|
||||
div.organization
|
||||
h3.title #{organizationName}
|
||||
if organizationEmail
|
||||
span.email #{organizationEmail}
|
||||
|
||||
div.invoice__due-amount
|
||||
div.label #{__('invoice.paper.invoice_amount')}
|
||||
div.amount #{saleInvoice.formattedAmount}
|
||||
|
||||
div.invoice__meta
|
||||
div.invoice__meta-item.invoice__meta-item--amount
|
||||
span.label #{__('invoice.paper.due_amount')}
|
||||
span.value #{saleInvoice.formattedDueAmount}
|
||||
|
||||
div.invoice__meta-item.invoice__meta-item--billed-to
|
||||
span.label #{__("invoice.paper.billed_to")}
|
||||
span.value #{saleInvoice.customer.displayName}
|
||||
|
||||
div.invoice__meta-item.invoice__meta-item--invoice-date
|
||||
span.label #{__("invoice.paper.invoice_date")}
|
||||
span.value #{saleInvoice.formattedInvoiceDate}
|
||||
|
||||
div.invoice__meta-item.invoice__meta-item--due-date
|
||||
span.label #{__("invoice.paper.due_date")}
|
||||
span.value #{saleInvoice.formattedDueDate}
|
||||
|
||||
div.invoice__table
|
||||
table
|
||||
thead
|
||||
tr
|
||||
th.item #{__("item_entry.paper.item_name")}
|
||||
th.rate #{__("item_entry.paper.rate")}
|
||||
th.quantity #{__("item_entry.paper.quantity")}
|
||||
th.total #{__("item_entry.paper.total")}
|
||||
tbody
|
||||
each entry in saleInvoice.entries
|
||||
tr
|
||||
td.item
|
||||
div.title=entry.item.name
|
||||
span.description=entry.description
|
||||
td.rate=entry.rate
|
||||
td.quantity=entry.quantity
|
||||
td.total=entry.amount
|
||||
|
||||
div.invoice__table-after
|
||||
div.invoice__table-total
|
||||
table
|
||||
tbody
|
||||
tr.total
|
||||
td #{__('invoice.paper.total')}
|
||||
td #{saleInvoice.formattedAmount}
|
||||
tr.payment-amount
|
||||
td #{__('invoice.paper.payment_amount')}
|
||||
td #{saleInvoice.formattedPaymentAmount}
|
||||
tr.blanace-due
|
||||
td #{__('invoice.paper.balance_due')}
|
||||
td #{saleInvoice.formattedDueAmount}
|
||||
|
||||
div.invoice__footer
|
||||
if saleInvoice.termsConditions
|
||||
div.invoice__conditions
|
||||
h3 #{__("invoice.paper.conditions_title")}
|
||||
p #{saleInvoice.termsConditions}
|
||||
|
||||
if saleInvoice.invoiceMessage
|
||||
div.invoice__notes
|
||||
h3 #{__("invoice.paper.notes_title")}
|
||||
p #{saleInvoice.invoiceMessage}
|
||||
@@ -1,67 +0,0 @@
|
||||
extends ../PaperTemplateLayout.pug
|
||||
|
||||
block head
|
||||
style
|
||||
if (isRtl)
|
||||
include ../../css/modules/payment-rtl.css
|
||||
else
|
||||
include ../../css/modules/payment.css
|
||||
|
||||
block content
|
||||
div.payment
|
||||
div.payment__header
|
||||
div.paper
|
||||
h1.title #{__("payment.paper.payment_receipt")}
|
||||
if paymentReceive.paymentReceiveNo
|
||||
span.paymentNumber #{paymentReceive.paymentReceiveNo}
|
||||
|
||||
div.organization
|
||||
h3.title #{organizationName}
|
||||
if organizationEmail
|
||||
span.email #{organizationEmail}
|
||||
|
||||
div.payment__received-amount
|
||||
div.label #{__('payment.paper.amount_received')}
|
||||
div.amount #{paymentReceive.formattedAmount}
|
||||
|
||||
div.payment__meta
|
||||
div.payment__meta-item.payment__meta-item--billed-to
|
||||
span.label #{__("payment.paper.billed_to")}
|
||||
span.value #{paymentReceive.customer.displayName}
|
||||
|
||||
div.payment__meta-item.payment__meta-item--payment-date
|
||||
span.label #{__("payment.paper.payment_date")}
|
||||
span.value #{paymentReceive.formattedPaymentDate}
|
||||
|
||||
div.payment__table
|
||||
table
|
||||
thead
|
||||
tr
|
||||
th.item #{__("payment.paper.invoice_number")}
|
||||
th.date #{__("payment.paper.invoice_date")}
|
||||
th.invoiceAmount #{__("payment.paper.invoice_amount")}
|
||||
th.paymentAmount #{__("payment.paper.payment_amount")}
|
||||
tbody
|
||||
each entry in paymentReceive.entries
|
||||
tr
|
||||
td.item=entry.invoice.invoiceNo
|
||||
td.date=entry.invoice.formattedInvoiceDate
|
||||
td.invoiceAmount=entry.invoice.formattedAmount
|
||||
td.paymentAmount=entry.invoice.formattedPaymentAmount
|
||||
|
||||
div.payment__table-after
|
||||
div.payment__table-total
|
||||
table
|
||||
tbody
|
||||
tr.payment-amount
|
||||
td #{__('payment.paper.payment_amount')}
|
||||
td #{paymentReceive.formattedAmount}
|
||||
tr.blanace-due
|
||||
td #{__('payment.paper.balance_due')}
|
||||
td #{paymentReceive.customer.closingBalance}
|
||||
|
||||
div.payment__footer
|
||||
if paymentReceive.statement
|
||||
div.payment__notes
|
||||
h3 #{__("payment.paper.statement")}
|
||||
p #{paymentReceive.statement}
|
||||
@@ -1,77 +0,0 @@
|
||||
extends ../PaperTemplateLayout.pug
|
||||
|
||||
block head
|
||||
style
|
||||
if (isRtl)
|
||||
include ../../css/modules/receipt-rtl.css
|
||||
else
|
||||
include ../../css/modules/receipt.css
|
||||
|
||||
block content
|
||||
div.receipt
|
||||
div.receipt__header
|
||||
div.paper
|
||||
h1.title #{__("receipt.paper.receipt")}
|
||||
span.receiptNumber #{saleReceipt.receiptNumber}
|
||||
|
||||
div.organization
|
||||
h3.title #{organizationName}
|
||||
|
||||
div.receipt__receipt-amount
|
||||
div.label #{__('receipt.paper.receipt_amount')}
|
||||
div.amount #{saleReceipt.formattedAmount}
|
||||
|
||||
div.receipt__meta
|
||||
div.receipt__meta-item.receipt__meta-item--billed-to
|
||||
span.label #{__("receipt.paper.billed_to")}
|
||||
span.value #{saleReceipt.customer.displayName}
|
||||
|
||||
div.receipt__meta-item.receipt__meta-item--invoice-date
|
||||
span.label #{__("receipt.paper.receipt_date")}
|
||||
span.value #{saleReceipt.formattedReceiptDate}
|
||||
|
||||
if saleReceipt.receiptNumber
|
||||
div.receipt__meta-item.receipt__meta-item--invoice-number
|
||||
span.label #{__("receipt.paper.receipt_number")}
|
||||
span.value #{saleReceipt.receiptNumber}
|
||||
|
||||
div.receipt__table
|
||||
table
|
||||
thead
|
||||
tr
|
||||
th.item #{__("item_entry.paper.item_name")}
|
||||
th.rate #{__("item_entry.paper.rate")}
|
||||
th.quantity #{__("item_entry.paper.quantity")}
|
||||
th.total #{__("item_entry.paper.total")}
|
||||
tbody
|
||||
each entry in saleReceipt.entries
|
||||
tr
|
||||
td.item=entry.item.name
|
||||
td.rate=entry.rate
|
||||
td.quantity=entry.quantity
|
||||
td.total=entry.amount
|
||||
|
||||
div.receipt__table-after
|
||||
div.receipt__table-total
|
||||
table
|
||||
tbody
|
||||
tr.total
|
||||
td #{__('receipt.paper.total')}
|
||||
td #{saleReceipt.formattedAmount}
|
||||
tr.payment-amount
|
||||
td #{__('receipt.paper.payment_amount')}
|
||||
td #{saleReceipt.formattedAmount}
|
||||
tr.blanace-due
|
||||
td #{__('receipt.paper.balance_due')}
|
||||
td #{'$0'}
|
||||
|
||||
div.receipt__footer
|
||||
if saleReceipt.statement
|
||||
div.receipt__conditions
|
||||
h3 #{__("receipt.paper.statement")}
|
||||
p #{saleReceipt.statement}
|
||||
|
||||
if saleReceipt.receiptMessage
|
||||
div.receipt__notes
|
||||
h3 #{__("receipt.paper.notes")}
|
||||
p #{saleReceipt.receiptMessage}
|
||||
@@ -1,145 +0,0 @@
|
||||
/**
|
||||
* # Gulp Configuration.
|
||||
* ------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
const RESOURCES_PATH = '../resources/';
|
||||
module.exports = {
|
||||
banner: [
|
||||
'/**',
|
||||
' * <%= pkg.name %> - <%= pkg.description %>',
|
||||
' * @version v<%= pkg.version %>',
|
||||
' * @link <%= pkg.homepage %>',
|
||||
' * @author <%= pkg.author %>',
|
||||
' * @license <%= pkg.license %>',
|
||||
'**/',
|
||||
'',
|
||||
].join('\n'),
|
||||
|
||||
// Browser Sync
|
||||
browsersync: {
|
||||
files: ['**/*', '!**.map', '!**.css'], // Exclude map files.
|
||||
notify: false, //
|
||||
open: true, // Set it to false if you don't like the broser window opening automatically.
|
||||
port: 8080, //
|
||||
proxy: 'localhost/customatic', //
|
||||
watchOptions: {
|
||||
debounceDelay: 2000, // This introduces a small delay when watching for file change events to avoid triggering too many reloads
|
||||
},
|
||||
snippetOptions: {
|
||||
whitelist: ['/wp-admin/admin-ajax.php'],
|
||||
blacklist: ['/wp-admin/**'],
|
||||
},
|
||||
},
|
||||
|
||||
// Style Related.
|
||||
style: {
|
||||
clean: ['style.css', 'style.min.css', 'style-rtl.css', 'style-rtl.min.css'],
|
||||
build: [
|
||||
{
|
||||
src: `${RESOURCES_PATH}/scss/modules/invoice.scss`,
|
||||
dest: `${RESOURCES_PATH}/css/modules`,
|
||||
// sourcemaps: true, // Allow to enable/disable sourcemaps or pass object to configure it.
|
||||
// minify: true, // Allow to enable/disable minify the source.
|
||||
},
|
||||
{
|
||||
src: `${RESOURCES_PATH}/scss/modules/estimate.scss`,
|
||||
dest: `${RESOURCES_PATH}/css/modules`,
|
||||
// sourcemaps: true, // Allow to enable/disable sourcemaps or pass object to configure it.
|
||||
// minify: true, // Allow to enable/disable minify the source.
|
||||
},
|
||||
{
|
||||
src: `${RESOURCES_PATH}/scss/modules/receipt.scss`,
|
||||
dest: `${RESOURCES_PATH}/css/modules`,
|
||||
// sourcemaps: true, // Allow to enable/disable sourcemaps or pass object to configure it.
|
||||
// minify: true, // Allow to enable/disable minify the source.
|
||||
},
|
||||
{
|
||||
src: `${RESOURCES_PATH}/scss/modules/credit.scss`,
|
||||
dest: `${RESOURCES_PATH}/css/modules`,
|
||||
// sourcemaps: true, // Allow to enable/disable sourcemaps or pass object to configure it.
|
||||
// minify: true, // Allow to enable/disable minify the source.
|
||||
},
|
||||
{
|
||||
src: `${RESOURCES_PATH}/scss/modules/payment.scss`,
|
||||
dest: `${RESOURCES_PATH}/css/modules`,
|
||||
// sourcemaps: true, // Allow to enable/disable sourcemaps or pass object to configure it.
|
||||
// minify: true, // Allow to enable/disable minify the source.
|
||||
},
|
||||
// {
|
||||
// src: './assets/sass/editor-style.scss',
|
||||
// dest: './assets/css',
|
||||
// sourcemaps: true,
|
||||
// minify: true,
|
||||
// },
|
||||
],
|
||||
// RTL builds.
|
||||
rtl: [
|
||||
{
|
||||
src: `${RESOURCES_PATH}/css/modules/invoice.css`,
|
||||
dest: `${RESOURCES_PATH}/css/modules`,
|
||||
},
|
||||
{
|
||||
src: `${RESOURCES_PATH}/css/modules/estimate.css`,
|
||||
dest: `${RESOURCES_PATH}/css/modules`,
|
||||
},
|
||||
{
|
||||
src: `${RESOURCES_PATH}/css/modules/receipt.css`,
|
||||
dest: `${RESOURCES_PATH}/css/modules`,
|
||||
},
|
||||
{
|
||||
src: `${RESOURCES_PATH}/css/modules/credit.css`,
|
||||
dest: `${RESOURCES_PATH}/css/modules`,
|
||||
},
|
||||
{
|
||||
src: `${RESOURCES_PATH}/css/modules/payment.css`,
|
||||
dest: `${RESOURCES_PATH}/css/modules`,
|
||||
},
|
||||
],
|
||||
|
||||
// Browsers you care about for auto-prefixing.
|
||||
autoprefixer: {
|
||||
browsers: [
|
||||
'Android 2.3',
|
||||
'Android >= 4',
|
||||
'Chrome >= 20',
|
||||
'Firefox >= 24',
|
||||
'Explorer >= 9',
|
||||
'iOS >= 6',
|
||||
'Opera >= 12',
|
||||
'Safari >= 6',
|
||||
],
|
||||
},
|
||||
|
||||
// SASS Configuration for all builds.
|
||||
sass: {
|
||||
errLogToConsole: true,
|
||||
// outputStyle: 'compact',
|
||||
},
|
||||
|
||||
// CSS MQ Packer configuration for all builds and style tasks.
|
||||
cssMqpacker: {},
|
||||
|
||||
// CSS nano configuration for all builds.
|
||||
cssnano: {},
|
||||
|
||||
// rtlcss configuration for all builds.
|
||||
rtlcss: {},
|
||||
},
|
||||
|
||||
// Clean specific files.
|
||||
clean: [
|
||||
'**/.DS_Store',
|
||||
'./assets/js/**/*.min.js',
|
||||
'**/*.map',
|
||||
'**/*.min.css',
|
||||
'assets/js/hypernews.js',
|
||||
],
|
||||
|
||||
// Watch related.
|
||||
watch: {
|
||||
css: ['./assets/sass/**/*'],
|
||||
js: ['assets/js/**/*.js', '!assets/js/**/*.min.js'],
|
||||
images: ['./assets/images/**/*'],
|
||||
},
|
||||
};
|
||||
@@ -1,50 +0,0 @@
|
||||
const gulp = require('gulp');
|
||||
const sass = require('sass');
|
||||
const gulpSass = require('gulp-sass')(sass); // Gulp pluign for Sass compilation.
|
||||
const mergeStream = require('merge-stream');
|
||||
|
||||
const rename = require('gulp-rename'); // Renames files E.g. style.css -> style.min.css
|
||||
|
||||
// Style related.
|
||||
const postcss = require('gulp-postcss'); // Transforming styles with JS plugins
|
||||
const rtlcss = require('rtlcss'); // Convert LTR CSS to RTL.
|
||||
|
||||
const config = require('./gulpConfig');
|
||||
|
||||
gulp.task('styles', () => {
|
||||
const builds = config.style.build.map((build) => {
|
||||
return gulp
|
||||
.src(build.src)
|
||||
.pipe(gulpSass(config.style.sass))
|
||||
.pipe(gulp.dest(build.dest));
|
||||
});
|
||||
return mergeStream(builds);
|
||||
});
|
||||
|
||||
/**
|
||||
* Task: `styles-rtl`
|
||||
*
|
||||
* This task does the following.
|
||||
* 1. Gets the source css files.
|
||||
* 2. Covert LTR CSS to RTL.
|
||||
* 3. Suffix all CSS files to `-rtl`.
|
||||
* 4. Reloads css files via browser sync stream.
|
||||
* 5. Combine matching media queries for `.min.css` version.
|
||||
* 6. Minify all CSS files.
|
||||
* 7. Reload minified css files via browser sync stream.
|
||||
*/
|
||||
gulp.task('styles-rtl', () => {
|
||||
const builds = config.style.rtl.map((build) => {
|
||||
return gulp
|
||||
.src(build.src)
|
||||
.pipe(
|
||||
postcss([
|
||||
rtlcss(config.style.rtlcss), // Convert LTR CSS to RTL.
|
||||
]),
|
||||
)
|
||||
.pipe(rename({ suffix: '-rtl' })) // Append "-rtl" to the filename.
|
||||
.pipe(gulp.dest(build.dest));
|
||||
});
|
||||
|
||||
return mergeStream(builds);
|
||||
});
|
||||
@@ -1,4 +0,0 @@
|
||||
|
||||
npm install
|
||||
npm run build
|
||||
npm run copy-i18n
|
||||
@@ -1,31 +0,0 @@
|
||||
MYSQL_USER="ratteb"
|
||||
MYSQL_DATABASE="ratteb"
|
||||
MYSQL_CONTAINER_NAME="ratteb_test"
|
||||
|
||||
MYSQL_ROOT_PASSWORD="root"
|
||||
MYSQL_PASSWORD="root"
|
||||
|
||||
echo "Start the testing MySql database..."
|
||||
|
||||
docker \
|
||||
run \
|
||||
--detach \
|
||||
--env MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD} \
|
||||
--env MYSQL_USER=${MYSQL_USER} \
|
||||
--env MYSQL_PASSWORD=${MYSQL_PASSWORD} \
|
||||
--env MYSQL_DATABASE=${MYSQL_DATABASE} \
|
||||
--name ${MYSQL_CONTAINER_NAME} \
|
||||
--publish 3306:3306 \
|
||||
--tmpfs /var/lib/mysql:rw \
|
||||
mysql:5.7;
|
||||
|
||||
echo "Sleeping for 10 seconds to allow time for the DB to be provisioned:"
|
||||
for i in `seq 1 10`;
|
||||
do
|
||||
echo "."
|
||||
sleep 1
|
||||
done
|
||||
|
||||
echo "Database '${MYSQL_DATABASE}' running."
|
||||
echo " Username: ${MYSQL_USER}"
|
||||
echo " Password: ${MYSQL_PASSWORD}"
|
||||
@@ -1,11 +0,0 @@
|
||||
const { getCommonWebpackOptions } = require('./webpack.common');
|
||||
|
||||
const inputEntry = './src/commands/index.ts';
|
||||
const outputDir = '../build';
|
||||
const outputFilename = 'commands.js';
|
||||
|
||||
module.exports = getCommonWebpackOptions({
|
||||
inputEntry,
|
||||
outputDir,
|
||||
outputFilename,
|
||||
});
|
||||
@@ -1,79 +0,0 @@
|
||||
const path = require('path');
|
||||
const { NormalModuleReplacementPlugin } = require('webpack');
|
||||
const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin');
|
||||
const { RunScriptWebpackPlugin } = require('run-script-webpack-plugin');
|
||||
const nodeExternals = require('webpack-node-externals');
|
||||
const ProgressBarPlugin = require('progress-bar-webpack-plugin');
|
||||
|
||||
const isDev = process.env.NODE_ENV === 'development';
|
||||
|
||||
exports.getCommonWebpackOptions = ({
|
||||
inputEntry,
|
||||
outputDir,
|
||||
outputFilename,
|
||||
}) => {
|
||||
const webpackOptions = {
|
||||
entry: ['regenerator-runtime/runtime', inputEntry],
|
||||
target: 'node',
|
||||
mode: isDev ? 'development' : 'production',
|
||||
watch: isDev,
|
||||
watchOptions: {
|
||||
aggregateTimeout: 200,
|
||||
poll: 1000,
|
||||
},
|
||||
output: {
|
||||
path: path.resolve(__dirname, outputDir),
|
||||
filename: outputFilename,
|
||||
},
|
||||
resolve: {
|
||||
extensions: ['.ts', '.tsx', '.js'],
|
||||
extensionAlias: {
|
||||
'.ts': ['.js', '.ts'],
|
||||
'.cts': ['.cjs', '.cts'],
|
||||
'.mts': ['.mjs', '.mts'],
|
||||
},
|
||||
plugins: [
|
||||
new TsconfigPathsPlugin({
|
||||
configFile: './tsconfig.json',
|
||||
extensions: ['.ts', '.tsx', '.js'],
|
||||
}),
|
||||
],
|
||||
},
|
||||
plugins: [
|
||||
// Ignore knex dynamic required dialects that we don't use
|
||||
new NormalModuleReplacementPlugin(
|
||||
/m[sy]sql2?|oracle(db)?|sqlite3|pg-(native|query)/,
|
||||
'noop2'
|
||||
),
|
||||
new ProgressBarPlugin(),
|
||||
],
|
||||
externals: [nodeExternals(), 'aws-sdk', 'prettier'],
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.([cm]?ts|tsx|js)$/,
|
||||
use: [
|
||||
{
|
||||
loader: 'ts-loader',
|
||||
options: {
|
||||
transpileOnly: true,
|
||||
configFile: 'tsconfig.json',
|
||||
},
|
||||
},
|
||||
],
|
||||
exclude: /(node_modules)/,
|
||||
},
|
||||
],
|
||||
},
|
||||
optimization: {
|
||||
minimize: false,
|
||||
},
|
||||
};
|
||||
|
||||
if (isDev) {
|
||||
webpackOptions.plugins.push(
|
||||
new RunScriptWebpackPlugin({ name: outputFilename })
|
||||
);
|
||||
}
|
||||
return webpackOptions;
|
||||
};
|
||||
@@ -1,11 +0,0 @@
|
||||
const { getCommonWebpackOptions } = require('./webpack.common');
|
||||
|
||||
const inputEntry = './src/server.ts';
|
||||
const outputDir = '../build';
|
||||
const outputFilename = 'index.js';
|
||||
|
||||
module.exports = getCommonWebpackOptions({
|
||||
inputEntry,
|
||||
outputDir,
|
||||
outputFilename,
|
||||
});
|
||||
@@ -1,52 +0,0 @@
|
||||
import { Router, Request, Response, NextFunction } from 'express';
|
||||
import { Service, Inject } from 'typedi';
|
||||
import BaseController from '@/api/controllers/BaseController';
|
||||
import AuthenticatedAccount from '@/services/AuthenticatedAccount';
|
||||
import TenancyMiddleware from '@/api/middleware/TenancyMiddleware';
|
||||
import AttachCurrentTenantUser from '@/api/middleware/AttachCurrentTenantUser';
|
||||
import JWTAuth from '@/api/middleware/jwtAuth';
|
||||
|
||||
@Service()
|
||||
export default class AccountController extends BaseController {
|
||||
@Inject()
|
||||
accountService: AuthenticatedAccount;
|
||||
|
||||
/**
|
||||
* Router constructor method.
|
||||
*/
|
||||
public router() {
|
||||
const router = Router();
|
||||
|
||||
// Should before build tenant database the user be authorized and
|
||||
// most important than that, should be subscribed to any plan.
|
||||
router.use(JWTAuth);
|
||||
router.use(AttachCurrentTenantUser);
|
||||
router.use(TenancyMiddleware);
|
||||
|
||||
router.get('/', this.getAccount);
|
||||
|
||||
return router;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new account.
|
||||
* @param {Request} req -
|
||||
* @param {Response} res -
|
||||
* @param {NextFunction} next -
|
||||
*/
|
||||
private getAccount = async (
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
) => {
|
||||
const { tenantId, user } = req;
|
||||
|
||||
try {
|
||||
const account = await this.accountService.getAccount(tenantId, user);
|
||||
|
||||
return res.status(200).send({ data: account });
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
import { Service, Inject } from 'typedi';
|
||||
import { Request, Response, Router, NextFunction } from 'express';
|
||||
import asyncMiddleware from '@/api/middleware/asyncMiddleware';
|
||||
import BaseController from '@/api/controllers/BaseController';
|
||||
import AccountsTypesService from '@/services/Accounts/AccountsTypesServices';
|
||||
|
||||
@Service()
|
||||
export default class AccountsTypesController extends BaseController {
|
||||
@Inject()
|
||||
accountsTypesService: AccountsTypesService;
|
||||
|
||||
/**
|
||||
* Router constructor.
|
||||
*/
|
||||
router() {
|
||||
const router = Router();
|
||||
|
||||
router.get('/', asyncMiddleware(this.getAccountTypesList.bind(this)));
|
||||
|
||||
return router;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve accounts types list.
|
||||
* @param {Request} req - Request.
|
||||
* @param {Response} res - Response.
|
||||
* @return {Response}
|
||||
*/
|
||||
getAccountTypesList(req: Request, res: Response, next: NextFunction) {
|
||||
const { tenantId } = req;
|
||||
|
||||
try {
|
||||
const accountTypes = this.accountsTypesService.getAccountsTypes(tenantId);
|
||||
|
||||
return res.status(200).send({
|
||||
account_types: this.transfromToResponse(accountTypes, ['label'], req),
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,528 +0,0 @@
|
||||
import { Router, Request, Response, NextFunction } from 'express';
|
||||
import { check, param, query } from 'express-validator';
|
||||
import { Service, Inject } from 'typedi';
|
||||
import asyncMiddleware from '@/api/middleware/asyncMiddleware';
|
||||
import BaseController from '@/api/controllers/BaseController';
|
||||
import {
|
||||
AbilitySubject,
|
||||
AccountAction,
|
||||
IAccountDTO,
|
||||
IAccountsStructureType,
|
||||
} from '@/interfaces';
|
||||
import { ServiceError } from '@/exceptions';
|
||||
import DynamicListingService from '@/services/DynamicListing/DynamicListService';
|
||||
import { DATATYPES_LENGTH } from '@/data/DataTypes';
|
||||
import CheckPolicies from '@/api/middleware/CheckPolicies';
|
||||
import { AccountsApplication } from '@/services/Accounts/AccountsApplication';
|
||||
import { MAX_ACCOUNTS_CHART_DEPTH } from 'services/Accounts/constants';
|
||||
|
||||
@Service()
|
||||
export default class AccountsController extends BaseController {
|
||||
@Inject()
|
||||
private accountsApplication: AccountsApplication;
|
||||
|
||||
@Inject()
|
||||
private dynamicListService: DynamicListingService;
|
||||
|
||||
/**
|
||||
* Router constructor method.
|
||||
*/
|
||||
router() {
|
||||
const router = Router();
|
||||
|
||||
router.get(
|
||||
'/transactions',
|
||||
CheckPolicies(AccountAction.VIEW, AbilitySubject.Account),
|
||||
[query('account_id').optional().isInt().toInt()],
|
||||
this.asyncMiddleware(this.accountTransactions.bind(this)),
|
||||
this.catchServiceErrors
|
||||
);
|
||||
router.post(
|
||||
'/:id/activate',
|
||||
CheckPolicies(AccountAction.EDIT, AbilitySubject.Account),
|
||||
[...this.accountParamSchema],
|
||||
asyncMiddleware(this.activateAccount.bind(this)),
|
||||
this.catchServiceErrors
|
||||
);
|
||||
router.post(
|
||||
'/:id/inactivate',
|
||||
CheckPolicies(AccountAction.EDIT, AbilitySubject.Account),
|
||||
[...this.accountParamSchema],
|
||||
asyncMiddleware(this.inactivateAccount.bind(this)),
|
||||
this.catchServiceErrors
|
||||
);
|
||||
router.post(
|
||||
'/:id',
|
||||
CheckPolicies(AccountAction.EDIT, AbilitySubject.Account),
|
||||
[...this.editAccountDTOSchema, ...this.accountParamSchema],
|
||||
this.validationResult,
|
||||
asyncMiddleware(this.editAccount.bind(this)),
|
||||
this.catchServiceErrors
|
||||
);
|
||||
router.post(
|
||||
'/',
|
||||
CheckPolicies(AccountAction.CREATE, AbilitySubject.Account),
|
||||
[...this.createAccountDTOSchema],
|
||||
this.validationResult,
|
||||
asyncMiddleware(this.newAccount.bind(this)),
|
||||
this.catchServiceErrors
|
||||
);
|
||||
router.get(
|
||||
'/:id',
|
||||
CheckPolicies(AccountAction.VIEW, AbilitySubject.Account),
|
||||
[...this.accountParamSchema],
|
||||
this.validationResult,
|
||||
asyncMiddleware(this.getAccount.bind(this)),
|
||||
this.catchServiceErrors
|
||||
);
|
||||
router.get(
|
||||
'/',
|
||||
CheckPolicies(AccountAction.VIEW, AbilitySubject.Account),
|
||||
[...this.accountsListSchema],
|
||||
this.validationResult,
|
||||
asyncMiddleware(this.getAccountsList.bind(this)),
|
||||
this.dynamicListService.handlerErrorsToResponse,
|
||||
this.catchServiceErrors
|
||||
);
|
||||
router.delete(
|
||||
'/:id',
|
||||
CheckPolicies(AccountAction.DELETE, AbilitySubject.Account),
|
||||
[...this.accountParamSchema],
|
||||
this.validationResult,
|
||||
asyncMiddleware(this.deleteAccount.bind(this)),
|
||||
this.catchServiceErrors
|
||||
);
|
||||
return router;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create account DTO Schema validation.
|
||||
*/
|
||||
get createAccountDTOSchema() {
|
||||
return [
|
||||
check('name')
|
||||
.exists()
|
||||
.isLength({ min: 3, max: DATATYPES_LENGTH.STRING })
|
||||
.trim()
|
||||
.escape(),
|
||||
check('code')
|
||||
.optional({ nullable: true })
|
||||
.isLength({ min: 3, max: 6 })
|
||||
.trim()
|
||||
.escape(),
|
||||
check('currency_code').optional(),
|
||||
check('account_type')
|
||||
.exists()
|
||||
.isLength({ min: 3, max: DATATYPES_LENGTH.STRING })
|
||||
.trim()
|
||||
.escape(),
|
||||
check('description')
|
||||
.optional({ nullable: true })
|
||||
.isLength({ max: DATATYPES_LENGTH.TEXT })
|
||||
.trim()
|
||||
.escape(),
|
||||
check('parent_account_id')
|
||||
.optional({ nullable: true })
|
||||
.isInt({ min: 0, max: DATATYPES_LENGTH.INT_10 })
|
||||
.toInt(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Account DTO Schema validation.
|
||||
*/
|
||||
get editAccountDTOSchema() {
|
||||
return [
|
||||
check('name')
|
||||
.exists()
|
||||
.isLength({ min: 3, max: DATATYPES_LENGTH.STRING })
|
||||
.trim()
|
||||
.escape(),
|
||||
check('code')
|
||||
.optional({ nullable: true })
|
||||
.isLength({ min: 3, max: 6 })
|
||||
.trim()
|
||||
.escape(),
|
||||
check('account_type')
|
||||
.exists()
|
||||
.isLength({ min: 3, max: DATATYPES_LENGTH.STRING })
|
||||
.trim()
|
||||
.escape(),
|
||||
check('description')
|
||||
.optional({ nullable: true })
|
||||
.isLength({ max: DATATYPES_LENGTH.TEXT })
|
||||
.trim()
|
||||
.escape(),
|
||||
check('parent_account_id')
|
||||
.optional({ nullable: true })
|
||||
.isInt({ min: 0, max: DATATYPES_LENGTH.INT_10 })
|
||||
.toInt(),
|
||||
];
|
||||
}
|
||||
|
||||
get accountParamSchema() {
|
||||
return [param('id').exists().isNumeric().toInt()];
|
||||
}
|
||||
|
||||
/**
|
||||
* Accounts list validation schema.
|
||||
*/
|
||||
get accountsListSchema() {
|
||||
return [
|
||||
query('view_slug').optional({ nullable: true }).isString().trim(),
|
||||
query('stringified_filter_roles').optional().isJSON(),
|
||||
|
||||
query('column_sort_by').optional(),
|
||||
query('sort_order').optional().isIn(['desc', 'asc']),
|
||||
|
||||
query('inactive_mode').optional().isBoolean().toBoolean(),
|
||||
query('search_keyword').optional({ nullable: true }).isString().trim(),
|
||||
|
||||
query('structure')
|
||||
.optional()
|
||||
.isString()
|
||||
.isIn([IAccountsStructureType.Tree, IAccountsStructureType.Flat]),
|
||||
];
|
||||
}
|
||||
|
||||
get closingAccountSchema() {
|
||||
return [
|
||||
check('to_account_id').exists().isNumeric().toInt(),
|
||||
check('delete_after_closing').exists().isBoolean(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new account.
|
||||
* @param {Request} req -
|
||||
* @param {Response} res -
|
||||
* @param {NextFunction} next -
|
||||
*/
|
||||
async newAccount(req: Request, res: Response, next: NextFunction) {
|
||||
const { tenantId } = req;
|
||||
const accountDTO: IAccountDTO = this.matchedBodyData(req);
|
||||
|
||||
try {
|
||||
const account = await this.accountsApplication.createAccount(
|
||||
tenantId,
|
||||
accountDTO
|
||||
);
|
||||
|
||||
return res.status(200).send({
|
||||
id: account.id,
|
||||
message: 'The account has been created successfully.',
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Edit account details.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @return {Response}
|
||||
*/
|
||||
async editAccount(req: Request, res: Response, next: NextFunction) {
|
||||
const { tenantId } = req;
|
||||
const { id: accountId } = req.params;
|
||||
const accountDTO: IAccountDTO = this.matchedBodyData(req);
|
||||
|
||||
try {
|
||||
const account = await this.accountsApplication.editAccount(
|
||||
tenantId,
|
||||
accountId,
|
||||
accountDTO
|
||||
);
|
||||
|
||||
return res.status(200).send({
|
||||
id: account.id,
|
||||
message: 'The account has been edited successfully',
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get details of the given account.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @return {Response}
|
||||
*/
|
||||
async getAccount(req: Request, res: Response, next: NextFunction) {
|
||||
const { tenantId } = req;
|
||||
const { id: accountId } = req.params;
|
||||
|
||||
try {
|
||||
const account = await this.accountsApplication.getAccount(
|
||||
tenantId,
|
||||
accountId
|
||||
);
|
||||
return res
|
||||
.status(200)
|
||||
.send({ account: this.transfromToResponse(account) });
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the given account.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @return {Response}
|
||||
*/
|
||||
async deleteAccount(req: Request, res: Response, next: NextFunction) {
|
||||
const { id: accountId } = req.params;
|
||||
const { tenantId } = req;
|
||||
|
||||
try {
|
||||
await this.accountsApplication.deleteAccount(tenantId, accountId);
|
||||
|
||||
return res.status(200).send({
|
||||
id: accountId,
|
||||
message: 'The deleted account has been deleted successfully.',
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Activate the given account.
|
||||
* @param {Response} res -
|
||||
* @param {Request} req -
|
||||
* @return {Response}
|
||||
*/
|
||||
async activateAccount(req: Request, res: Response, next: Function) {
|
||||
const { id: accountId } = req.params;
|
||||
const { tenantId } = req;
|
||||
|
||||
try {
|
||||
await this.accountsApplication.activateAccount(tenantId, accountId);
|
||||
|
||||
return res.status(200).send({
|
||||
id: accountId,
|
||||
message: 'The account has been activated successfully.',
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inactive the given account.
|
||||
* @param {Response} res -
|
||||
* @param {Request} req -
|
||||
* @return {Response}
|
||||
*/
|
||||
async inactivateAccount(req: Request, res: Response, next: Function) {
|
||||
const { id: accountId } = req.params;
|
||||
const { tenantId } = req;
|
||||
|
||||
try {
|
||||
await this.accountsApplication.inactivateAccount(tenantId, accountId);
|
||||
|
||||
return res.status(200).send({
|
||||
id: accountId,
|
||||
message: 'The account has been inactivated successfully.',
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve accounts datatable list.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {Response}
|
||||
*/
|
||||
public async getAccountsList(
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
) {
|
||||
const { tenantId } = req;
|
||||
|
||||
// Filter query.
|
||||
const filter = {
|
||||
sortOrder: 'desc',
|
||||
columnSortBy: 'created_at',
|
||||
inactiveMode: false,
|
||||
structure: IAccountsStructureType.Tree,
|
||||
...this.matchedQueryData(req),
|
||||
};
|
||||
|
||||
try {
|
||||
const { accounts, filterMeta } =
|
||||
await this.accountsApplication.getAccounts(tenantId, filter);
|
||||
|
||||
return res.status(200).send({
|
||||
accounts: this.transfromToResponse(accounts, 'accountTypeLabel', req),
|
||||
filter_meta: this.transfromToResponse(filterMeta),
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve accounts transactions list.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
* @returns {Response}
|
||||
*/
|
||||
async accountTransactions(req: Request, res: Response, next: NextFunction) {
|
||||
const { tenantId } = req;
|
||||
const transactionsFilter = this.matchedQueryData(req);
|
||||
|
||||
try {
|
||||
const transactions =
|
||||
await this.accountsApplication.getAccountsTransactions(
|
||||
tenantId,
|
||||
transactionsFilter
|
||||
);
|
||||
return res.status(200).send({
|
||||
transactions: this.transfromToResponse(transactions),
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms service errors to response.
|
||||
* @param {Error}
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {ServiceError} error
|
||||
*/
|
||||
private catchServiceErrors(
|
||||
error,
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
) {
|
||||
if (error instanceof ServiceError) {
|
||||
if (error.errorType === 'account_not_found') {
|
||||
return res.boom.notFound('The given account not found.', {
|
||||
errors: [{ type: 'ACCOUNT.NOT.FOUND', code: 100 }],
|
||||
});
|
||||
}
|
||||
if (error.errorType === 'account_name_not_unqiue') {
|
||||
return res.boom.badRequest('The given account not unique.', {
|
||||
errors: [{ type: 'ACCOUNT.NAME.NOT.UNIQUE', code: 150 }],
|
||||
});
|
||||
}
|
||||
if (error.errorType === 'account_type_not_found') {
|
||||
return res.boom.badRequest('The given account type not found.', {
|
||||
errors: [{ type: 'ACCOUNT_TYPE_NOT_FOUND', code: 200 }],
|
||||
});
|
||||
}
|
||||
if (error.errorType === 'account_type_not_allowed_to_changed') {
|
||||
return res.boom.badRequest(
|
||||
'Not allowed to change account type of the account.',
|
||||
{
|
||||
errors: [{ type: 'NOT.ALLOWED.TO.CHANGE.ACCOUNT.TYPE', code: 300 }],
|
||||
}
|
||||
);
|
||||
}
|
||||
if (error.errorType === 'parent_account_not_found') {
|
||||
return res.boom.badRequest('The parent account not found.', {
|
||||
errors: [{ type: 'PARENT_ACCOUNT_NOT_FOUND', code: 400 }],
|
||||
});
|
||||
}
|
||||
if (error.errorType === 'parent_has_different_type') {
|
||||
return res.boom.badRequest('The parent account has different type.', {
|
||||
errors: [
|
||||
{ type: 'PARENT.ACCOUNT.HAS.DIFFERENT.ACCOUNT.TYPE', code: 500 },
|
||||
],
|
||||
});
|
||||
}
|
||||
if (error.errorType === 'account_code_not_unique') {
|
||||
return res.boom.badRequest('The given account code is not unique.', {
|
||||
errors: [{ type: 'NOT_UNIQUE_CODE', code: 600 }],
|
||||
});
|
||||
}
|
||||
if (error.errorType === 'account_has_associated_transactions') {
|
||||
return res.boom.badRequest(
|
||||
'You could not delete account has associated transactions.',
|
||||
{
|
||||
errors: [
|
||||
{ type: 'ACCOUNT.HAS.ASSOCIATED.TRANSACTIONS', code: 800 },
|
||||
],
|
||||
}
|
||||
);
|
||||
}
|
||||
if (error.errorType === 'account_predefined') {
|
||||
return res.boom.badRequest('You could not delete predefined account', {
|
||||
errors: [{ type: 'ACCOUNT.PREDEFINED', code: 900 }],
|
||||
});
|
||||
}
|
||||
if (error.errorType === 'accounts_not_found') {
|
||||
return res.boom.notFound('Some of the given accounts not found.', {
|
||||
errors: [{ type: 'SOME.ACCOUNTS.NOT_FOUND', code: 1000 }],
|
||||
});
|
||||
}
|
||||
if (error.errorType === 'predefined_accounts') {
|
||||
return res.boom.badRequest(
|
||||
'Some of the given accounts are predefined.',
|
||||
{ errors: [{ type: 'ACCOUNTS_PREDEFINED', code: 1100 }] }
|
||||
);
|
||||
}
|
||||
if (error.errorType === 'close_account_and_to_account_not_same_type') {
|
||||
return res.boom.badRequest(
|
||||
'The close account has different root type with to account.',
|
||||
{
|
||||
errors: [
|
||||
{
|
||||
type: 'CLOSE_ACCOUNT_AND_TO_ACCOUNT_NOT_SAME_TYPE',
|
||||
code: 1200,
|
||||
},
|
||||
],
|
||||
}
|
||||
);
|
||||
}
|
||||
if (error.errorType === 'ACCOUNT_TYPE_NOT_SUPPORTS_MULTI_CURRENCY') {
|
||||
return res.boom.badRequest(
|
||||
'The given account type does not support multi-currency.',
|
||||
{
|
||||
errors: [
|
||||
{ type: 'ACCOUNT_TYPE_NOT_SUPPORTS_MULTI_CURRENCY', code: 1300 },
|
||||
],
|
||||
}
|
||||
);
|
||||
}
|
||||
if (error.errorType === 'ACCOUNT_CURRENCY_NOT_SAME_PARENT_ACCOUNT') {
|
||||
return res.boom.badRequest(
|
||||
'You could not add account has currency different on the parent account.',
|
||||
{
|
||||
errors: [
|
||||
{ type: 'ACCOUNT_CURRENCY_NOT_SAME_PARENT_ACCOUNT', code: 1400 },
|
||||
],
|
||||
}
|
||||
);
|
||||
}
|
||||
if (error.errorType === 'PARENT_ACCOUNT_EXCEEDED_THE_DEPTH_LEVEL') {
|
||||
return res.boom.badRequest(
|
||||
'The parent account exceeded the depth level of accounts chart.',
|
||||
{
|
||||
errors: [
|
||||
{
|
||||
type: 'PARENT_ACCOUNT_EXCEEDED_THE_DEPTH_LEVEL',
|
||||
code: 1500,
|
||||
data: {
|
||||
maxDepth: MAX_ACCOUNTS_CHART_DEPTH,
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
import { Router } from 'express';
|
||||
import basicAuth from 'express-basic-auth';
|
||||
import agendash from 'agendash';
|
||||
import { Container } from 'typedi';
|
||||
import config from '@/config';
|
||||
|
||||
export default class AgendashController {
|
||||
static router() {
|
||||
const router = Router();
|
||||
const agendaInstance = Container.get('agenda');
|
||||
|
||||
router.use(
|
||||
'/dash',
|
||||
basicAuth({
|
||||
users: {
|
||||
[config.agendash.user]: config.agendash.password,
|
||||
},
|
||||
challenge: true,
|
||||
}),
|
||||
agendash(agendaInstance)
|
||||
);
|
||||
return router;
|
||||
}
|
||||
}
|
||||
@@ -1,296 +0,0 @@
|
||||
import { Request, Response, Router } from 'express';
|
||||
import { check, ValidationChain } from 'express-validator';
|
||||
import { Service, Inject } from 'typedi';
|
||||
import BaseController from '@/api/controllers/BaseController';
|
||||
import asyncMiddleware from '@/api/middleware/asyncMiddleware';
|
||||
import { ILoginDTO, ISystemUser, IRegisterDTO } from '@/interfaces';
|
||||
import { ServiceError, ServiceErrors } from '@/exceptions';
|
||||
import { DATATYPES_LENGTH } from '@/data/DataTypes';
|
||||
import LoginThrottlerMiddleware from '@/api/middleware/LoginThrottlerMiddleware';
|
||||
import AuthenticationApplication from '@/services/Authentication/AuthApplication';
|
||||
|
||||
@Service()
|
||||
export default class AuthenticationController extends BaseController {
|
||||
@Inject()
|
||||
private authApplication: AuthenticationApplication;
|
||||
|
||||
/**
|
||||
* Constructor method.
|
||||
*/
|
||||
public router() {
|
||||
const router = Router();
|
||||
|
||||
router.post(
|
||||
'/login',
|
||||
this.loginSchema,
|
||||
this.validationResult,
|
||||
LoginThrottlerMiddleware,
|
||||
asyncMiddleware(this.login.bind(this)),
|
||||
this.handlerErrors
|
||||
);
|
||||
router.post(
|
||||
'/register',
|
||||
this.registerSchema,
|
||||
this.validationResult,
|
||||
asyncMiddleware(this.register.bind(this)),
|
||||
this.handlerErrors
|
||||
);
|
||||
router.post(
|
||||
'/send_reset_password',
|
||||
this.sendResetPasswordSchema,
|
||||
this.validationResult,
|
||||
asyncMiddleware(this.sendResetPassword.bind(this)),
|
||||
this.handlerErrors
|
||||
);
|
||||
router.post(
|
||||
'/reset/:token',
|
||||
this.resetPasswordSchema,
|
||||
this.validationResult,
|
||||
asyncMiddleware(this.resetPassword.bind(this)),
|
||||
this.handlerErrors
|
||||
);
|
||||
router.get('/meta', asyncMiddleware(this.getAuthMeta.bind(this)));
|
||||
return router;
|
||||
}
|
||||
|
||||
/**
|
||||
* Login validation schema.
|
||||
* @returns {ValidationChain[]}
|
||||
*/
|
||||
private get loginSchema(): ValidationChain[] {
|
||||
return [
|
||||
check('crediential').exists().isEmail(),
|
||||
check('password').exists().isLength({ min: 5 }),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Register validation schema.
|
||||
* @returns {ValidationChain[]}
|
||||
*/
|
||||
private get registerSchema(): ValidationChain[] {
|
||||
return [
|
||||
check('first_name')
|
||||
.exists()
|
||||
.isString()
|
||||
.trim()
|
||||
.escape()
|
||||
.isLength({ max: DATATYPES_LENGTH.STRING }),
|
||||
check('last_name')
|
||||
.exists()
|
||||
.isString()
|
||||
.trim()
|
||||
.escape()
|
||||
.isLength({ max: DATATYPES_LENGTH.STRING }),
|
||||
check('email')
|
||||
.exists()
|
||||
.isString()
|
||||
.isEmail()
|
||||
.trim()
|
||||
.escape()
|
||||
.isLength({ max: DATATYPES_LENGTH.STRING }),
|
||||
check('password')
|
||||
.exists()
|
||||
.isString()
|
||||
.isLength({ min: 6 })
|
||||
.trim()
|
||||
.escape()
|
||||
.isLength({ max: DATATYPES_LENGTH.STRING }),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset password schema.
|
||||
* @returns {ValidationChain[]}
|
||||
*/
|
||||
private get resetPasswordSchema(): ValidationChain[] {
|
||||
return [
|
||||
check('password')
|
||||
.exists()
|
||||
.isLength({ min: 6 })
|
||||
.custom((value, { req }) => {
|
||||
if (value !== req.body.confirm_password) {
|
||||
throw new Error("Passwords don't match");
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Send reset password validation schema.
|
||||
* @returns {ValidationChain[]}
|
||||
*/
|
||||
private get sendResetPasswordSchema(): ValidationChain[] {
|
||||
return [check('email').exists().isEmail().trim().escape()];
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle user login.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
*/
|
||||
private async login(req: Request, res: Response, next: Function): Response {
|
||||
const userDTO: ILoginDTO = this.matchedBodyData(req);
|
||||
|
||||
try {
|
||||
const { token, user, tenant } = await this.authApplication.signIn(
|
||||
userDTO.crediential,
|
||||
userDTO.password
|
||||
);
|
||||
return res.status(200).send({ token, user, tenant });
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Organization register handler.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
*/
|
||||
private async register(req: Request, res: Response, next: Function) {
|
||||
const registerDTO: IRegisterDTO = this.matchedBodyData(req);
|
||||
|
||||
try {
|
||||
await this.authApplication.signUp(registerDTO);
|
||||
|
||||
return res.status(200).send({
|
||||
type: 'success',
|
||||
code: 'REGISTER.SUCCESS',
|
||||
message: 'Register organization has been success.',
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send reset password handler
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
*/
|
||||
private async sendResetPassword(req: Request, res: Response, next: Function) {
|
||||
const { email } = this.matchedBodyData(req);
|
||||
|
||||
try {
|
||||
await this.authApplication.sendResetPassword(email);
|
||||
|
||||
return res.status(200).send({
|
||||
code: 'SEND_RESET_PASSWORD_SUCCESS',
|
||||
message: 'The reset password message has been sent successfully.',
|
||||
});
|
||||
} catch (error) {
|
||||
if (error instanceof ServiceError) {
|
||||
}
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset password handler
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
*/
|
||||
private async resetPassword(req: Request, res: Response, next: Function) {
|
||||
const { token } = req.params;
|
||||
const { password } = req.body;
|
||||
|
||||
try {
|
||||
await this.authApplication.resetPassword(token, password);
|
||||
|
||||
return res.status(200).send({
|
||||
type: 'RESET_PASSWORD_SUCCESS',
|
||||
message: 'The password has been reset successfully.',
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the authentication meta for SPA.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {Function} next
|
||||
* @returns {Response|void}
|
||||
*/
|
||||
private async getAuthMeta(req: Request, res: Response, next: Function) {
|
||||
try {
|
||||
const meta = await this.authApplication.getAuthMeta();
|
||||
|
||||
return res.status(200).send({ meta });
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the service errors.
|
||||
*/
|
||||
private handlerErrors(error, req: Request, res: Response, next: Function) {
|
||||
if (error instanceof ServiceError) {
|
||||
if (
|
||||
['INVALID_DETAILS', 'invalid_password'].indexOf(error.errorType) !== -1
|
||||
) {
|
||||
return res.boom.badRequest(null, {
|
||||
errors: [{ type: 'INVALID_DETAILS', code: 100 }],
|
||||
});
|
||||
}
|
||||
if (error.errorType === 'USER_INACTIVE') {
|
||||
return res.boom.badRequest(null, {
|
||||
errors: [{ type: 'USER_INACTIVE', code: 200 }],
|
||||
});
|
||||
}
|
||||
if (
|
||||
error.errorType === 'TOKEN_INVALID' ||
|
||||
error.errorType === 'TOKEN_EXPIRED'
|
||||
) {
|
||||
return res.boom.badRequest(null, {
|
||||
errors: [{ type: 'TOKEN_INVALID', code: 300 }],
|
||||
});
|
||||
}
|
||||
if (error.errorType === 'USER_NOT_FOUND') {
|
||||
return res.boom.badRequest(null, {
|
||||
errors: [{ type: 'USER_NOT_FOUND', code: 400 }],
|
||||
});
|
||||
}
|
||||
if (error.errorType === 'EMAIL_NOT_FOUND') {
|
||||
return res.status(400).send({
|
||||
errors: [{ type: 'EMAIL.NOT.REGISTERED', code: 500 }],
|
||||
});
|
||||
}
|
||||
if (error.errorType === 'EMAIL_EXISTS') {
|
||||
return res.status(400).send({
|
||||
errors: [{ type: 'EMAIL.EXISTS', code: 600 }],
|
||||
});
|
||||
}
|
||||
if (error.errorType === 'SIGNUP_RESTRICTED') {
|
||||
return res.status(400).send({
|
||||
errors: [
|
||||
{
|
||||
type: 'SIGNUP_RESTRICTED',
|
||||
message:
|
||||
'Sign-up is restricted no one can sign-up to the system.',
|
||||
code: 700,
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
if (error.errorType === 'SIGNUP_RESTRICTED_NOT_ALLOWED') {
|
||||
return res.status(400).send({
|
||||
errors: [
|
||||
{
|
||||
type: 'SIGNUP_RESTRICTED_NOT_ALLOWED',
|
||||
message:
|
||||
'Sign-up is restricted the given email address is not allowed to sign-up.',
|
||||
code: 710,
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
}
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
@@ -1,140 +0,0 @@
|
||||
import { Response, Request, NextFunction } from 'express';
|
||||
import { matchedData, validationResult } from 'express-validator';
|
||||
import accepts from 'accepts';
|
||||
import { isArray, drop, first, camelCase, snakeCase, omit, set, get } from 'lodash';
|
||||
import { mapKeysDeep } from 'utils';
|
||||
import asyncMiddleware from '@/api/middleware/asyncMiddleware';
|
||||
|
||||
export default class BaseController {
|
||||
/**
|
||||
* Converts plain object keys to cameCase style.
|
||||
* @param {Object} data
|
||||
*/
|
||||
protected dataToCamelCase(data) {
|
||||
return mapKeysDeep(data, (v, k) => camelCase(k));
|
||||
}
|
||||
|
||||
/**
|
||||
* Matches the body data from validation schema.
|
||||
* @param {Request} req
|
||||
* @param options
|
||||
*/
|
||||
protected matchedBodyData(req: Request, options: any = {}) {
|
||||
const data = matchedData(req, {
|
||||
locations: ['body'],
|
||||
includeOptionals: true,
|
||||
...omit(options, ['locations']), // override any propery except locations.
|
||||
});
|
||||
return this.dataToCamelCase(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Matches the query data from validation schema.
|
||||
* @param {Request} req
|
||||
*/
|
||||
protected matchedQueryData(req: Request) {
|
||||
const data = matchedData(req, {
|
||||
locations: ['query'],
|
||||
});
|
||||
return this.dataToCamelCase(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate validation schema middleware.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
*/
|
||||
protected validationResult(req: Request, res: Response, next: NextFunction) {
|
||||
const validationErrors = validationResult(req);
|
||||
|
||||
if (!validationErrors.isEmpty()) {
|
||||
return res.boom.badData(null, {
|
||||
code: 'validation_error',
|
||||
...validationErrors,
|
||||
});
|
||||
}
|
||||
next();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets localization to response object by the given path.
|
||||
* @param {Response} response -
|
||||
* @param {string} path -
|
||||
* @param {Request} req -
|
||||
*/
|
||||
private setLocalizationByPath(
|
||||
response: any,
|
||||
path: string,
|
||||
req: Request,
|
||||
) {
|
||||
const DOT = '.';
|
||||
|
||||
if (isArray(response)) {
|
||||
response.forEach((va) => {
|
||||
const currentPath = first(path.split(DOT));
|
||||
const value = get(va, currentPath);
|
||||
|
||||
if (isArray(value)) {
|
||||
const nextPath = drop(path.split(DOT)).join(DOT);
|
||||
this.setLocalizationByPath(value, nextPath, req);
|
||||
} else {
|
||||
set(va, path, req.__(value));
|
||||
}
|
||||
})
|
||||
} else {
|
||||
const value = get(response, path);
|
||||
set(response, path, req.__(value));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform the given data to response.
|
||||
* @param {any} data
|
||||
*/
|
||||
protected transfromToResponse(
|
||||
data: any,
|
||||
translatable?: string | string[],
|
||||
req?: Request
|
||||
) {
|
||||
const response = mapKeysDeep(data, (v, k) => snakeCase(k));
|
||||
|
||||
if (translatable) {
|
||||
const translatables = Array.isArray(translatable)
|
||||
? translatable
|
||||
: [translatable];
|
||||
|
||||
translatables.forEach((path) => {
|
||||
this.setLocalizationByPath(response, path, req);
|
||||
});
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Async middleware.
|
||||
* @param {function} callback
|
||||
*/
|
||||
protected asyncMiddleware(callback) {
|
||||
return asyncMiddleware(callback);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Request} req
|
||||
* @returns
|
||||
*/
|
||||
protected accepts(req) {
|
||||
return accepts(req);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Request} req
|
||||
* @param {string[]} types
|
||||
* @returns {string}
|
||||
*/
|
||||
protected acceptTypes(req: Request, types: string[]) {
|
||||
return this.accepts(req).types(types);
|
||||
}
|
||||
}
|
||||
@@ -1,335 +0,0 @@
|
||||
import { Service, Inject } from 'typedi';
|
||||
import { Request, Response, Router, NextFunction } from 'express';
|
||||
import { check, param } from 'express-validator';
|
||||
import BaseController from '@/api/controllers/BaseController';
|
||||
import { Features, ICreateBranchDTO, IEditBranchDTO } from '@/interfaces';
|
||||
import { BranchesApplication } from '@/services/Branches/BranchesApplication';
|
||||
import { ServiceError } from '@/exceptions';
|
||||
import { FeatureActivationGuard } from '@/api/middleware/FeatureActivationGuard';
|
||||
|
||||
@Service()
|
||||
export class BranchesController extends BaseController {
|
||||
@Inject()
|
||||
branchesApplication: BranchesApplication;
|
||||
|
||||
/**
|
||||
* Branches routes.
|
||||
* @returns {Router}
|
||||
*/
|
||||
router() {
|
||||
const router = Router();
|
||||
|
||||
router.post(
|
||||
'/activate',
|
||||
[],
|
||||
this.validationResult,
|
||||
this.asyncMiddleware(this.activateBranches),
|
||||
this.handlerServiceErrors
|
||||
);
|
||||
router.post(
|
||||
'/',
|
||||
FeatureActivationGuard(Features.BRANCHES),
|
||||
[
|
||||
check('name').exists(),
|
||||
check('code').optional({ nullable: true }),
|
||||
|
||||
check('address').optional({ nullable: true }),
|
||||
check('city').optional({ nullable: true }),
|
||||
check('country').optional({ nullable: true }),
|
||||
|
||||
check('phone_number').optional({ nullable: true }),
|
||||
check('email').optional({ nullable: true }).isEmail(),
|
||||
check('website').optional({ nullable: true }).isURL(),
|
||||
],
|
||||
this.validationResult,
|
||||
this.asyncMiddleware(this.createBranch),
|
||||
this.handlerServiceErrors
|
||||
);
|
||||
router.post(
|
||||
'/:id',
|
||||
FeatureActivationGuard(Features.BRANCHES),
|
||||
[
|
||||
param('id').exists().isInt().toInt(),
|
||||
check('name').exists(),
|
||||
check('code').optional({ nullable: true }),
|
||||
|
||||
check('address').optional({ nullable: true }),
|
||||
check('city').optional({ nullable: true }),
|
||||
check('country').optional({ nullable: true }),
|
||||
|
||||
check('phone_number').optional({ nullable: true }),
|
||||
check('email').optional({ nullable: true }).isEmail(),
|
||||
check('website').optional({ nullable: true }).isURL(),
|
||||
],
|
||||
this.validationResult,
|
||||
this.asyncMiddleware(this.editBranch),
|
||||
this.handlerServiceErrors
|
||||
);
|
||||
router.post(
|
||||
'/:id/mark-primary',
|
||||
FeatureActivationGuard(Features.BRANCHES),
|
||||
[],
|
||||
this.validationResult,
|
||||
this.asyncMiddleware(this.markBranchAsPrimary),
|
||||
this.handlerServiceErrors
|
||||
);
|
||||
router.delete(
|
||||
'/:id',
|
||||
FeatureActivationGuard(Features.BRANCHES),
|
||||
[param('id').exists().isInt().toInt()],
|
||||
this.validationResult,
|
||||
this.asyncMiddleware(this.deleteBranch),
|
||||
this.handlerServiceErrors
|
||||
);
|
||||
router.get(
|
||||
'/:id',
|
||||
FeatureActivationGuard(Features.BRANCHES),
|
||||
[param('id').exists().isInt().toInt()],
|
||||
this.validationResult,
|
||||
this.asyncMiddleware(this.getBranch),
|
||||
this.handlerServiceErrors
|
||||
);
|
||||
router.get(
|
||||
'/',
|
||||
FeatureActivationGuard(Features.BRANCHES),
|
||||
[],
|
||||
this.validationResult,
|
||||
this.asyncMiddleware(this.getBranches),
|
||||
this.handlerServiceErrors
|
||||
);
|
||||
return router;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new branch.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
* @returns {Response}
|
||||
*/
|
||||
public createBranch = async (
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
) => {
|
||||
const { tenantId } = req;
|
||||
const createBranchDTO: ICreateBranchDTO = this.matchedBodyData(req);
|
||||
|
||||
try {
|
||||
const branch = await this.branchesApplication.createBranch(
|
||||
tenantId,
|
||||
createBranchDTO
|
||||
);
|
||||
return res.status(200).send({
|
||||
id: branch.id,
|
||||
message: 'The branch has been created successfully.',
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Edits the given branch.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
* @returns {Response}
|
||||
*/
|
||||
public editBranch = async (
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
) => {
|
||||
const { tenantId } = req;
|
||||
const { id: branchId } = req.params;
|
||||
const editBranchDTO: IEditBranchDTO = this.matchedBodyData(req);
|
||||
|
||||
try {
|
||||
const branch = await this.branchesApplication.editBranch(
|
||||
tenantId,
|
||||
branchId,
|
||||
editBranchDTO
|
||||
);
|
||||
return res.status(200).send({
|
||||
id: branch.id,
|
||||
message: 'The branch has been edited successfully.',
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Deletes the given branch.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
* @returns {Response}
|
||||
*/
|
||||
public deleteBranch = async (
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
) => {
|
||||
const { tenantId } = req;
|
||||
const { id: branchId } = req.params;
|
||||
|
||||
try {
|
||||
await this.branchesApplication.deleteBranch(tenantId, branchId);
|
||||
|
||||
return res.status(200).send({
|
||||
id: branchId,
|
||||
message: 'The branch has been deleted successfully.',
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves specific branch.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
* @returns {Response}
|
||||
*/
|
||||
public getBranch = async (
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
) => {
|
||||
const { tenantId } = req;
|
||||
const { id: branchId } = req.params;
|
||||
|
||||
try {
|
||||
const branch = await this.branchesApplication.getBranch(
|
||||
tenantId,
|
||||
branchId
|
||||
);
|
||||
return res.status(200).send({ branch });
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves branches list.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
* @returns {Response}
|
||||
*/
|
||||
public getBranches = async (
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
) => {
|
||||
const { tenantId } = req;
|
||||
|
||||
try {
|
||||
const branches = await this.branchesApplication.getBranches(tenantId);
|
||||
|
||||
return res.status(200).send({ branches });
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Activates the multi-branches feature.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
* @returns {Response}
|
||||
*/
|
||||
public activateBranches = async (
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
) => {
|
||||
const { tenantId } = req;
|
||||
|
||||
try {
|
||||
await this.branchesApplication.activateBranches(tenantId);
|
||||
|
||||
return res.status(200).send({
|
||||
message: 'Multi-branches feature has been activated successfully.',
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Marks the given branch as primary.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
* @returns {Response}
|
||||
*/
|
||||
public markBranchAsPrimary = async (
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
) => {
|
||||
const { tenantId } = req;
|
||||
const { id: branchId } = req.params;
|
||||
|
||||
try {
|
||||
await this.branchesApplication.markBranchAsPrimary(tenantId, branchId);
|
||||
|
||||
return res.status(200).send({
|
||||
id: branchId,
|
||||
message: 'The branch has been marked as primary.',
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles service errors.
|
||||
* @param {Error} error
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
*/
|
||||
private handlerServiceErrors(
|
||||
error: Error,
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
) {
|
||||
if (error instanceof ServiceError) {
|
||||
if (error.errorType === 'BRANCH_NOT_FOUND') {
|
||||
return res.status(400).send({
|
||||
errors: [{ type: 'BRANCH_NOT_FOUND', code: 100 }],
|
||||
});
|
||||
}
|
||||
if (error.errorType === 'MUTLI_BRANCHES_ALREADY_ACTIVATED') {
|
||||
return res.status(400).send({
|
||||
errors: [{ type: 'MUTLI_BRANCHES_ALREADY_ACTIVATED', code: 100 }],
|
||||
});
|
||||
}
|
||||
if (error.errorType === 'COULD_NOT_DELETE_ONLY_BRANCH') {
|
||||
return res.status(400).send({
|
||||
errors: [{ type: 'COULD_NOT_DELETE_ONLY_BRANCH', code: 300 }],
|
||||
});
|
||||
}
|
||||
if (error.errorType === 'BRANCH_CODE_NOT_UNIQUE') {
|
||||
return res.status(400).send({
|
||||
errors: [{ type: 'BRANCH_CODE_NOT_UNIQUE', code: 400 }],
|
||||
});
|
||||
}
|
||||
if (error.errorType === 'BRANCH_HAS_ASSOCIATED_TRANSACTIONS') {
|
||||
return res.status(400).send({
|
||||
errors: [
|
||||
{ type: 'BRANCH_HAS_ASSOCIATED_TRANSACTIONS', code: 500 },
|
||||
],
|
||||
});
|
||||
}
|
||||
}
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
import { Service, Inject, Container } from 'typedi';
|
||||
import { Router } from 'express';
|
||||
import CommandCashflowTransaction from './NewCashflowTransaction';
|
||||
import DeleteCashflowTransaction from './DeleteCashflowTransaction';
|
||||
import GetCashflowTransaction from './GetCashflowTransaction';
|
||||
import GetCashflowAccounts from './GetCashflowAccounts';
|
||||
|
||||
@Service()
|
||||
export default class CashflowController {
|
||||
/**
|
||||
* Constructor method.
|
||||
*/
|
||||
router() {
|
||||
const router = Router();
|
||||
|
||||
router.use(Container.get(GetCashflowTransaction).router());
|
||||
router.use(Container.get(GetCashflowAccounts).router());
|
||||
router.use(Container.get(CommandCashflowTransaction).router());
|
||||
router.use(Container.get(DeleteCashflowTransaction).router());
|
||||
|
||||
return router;
|
||||
}
|
||||
}
|
||||
@@ -1,98 +0,0 @@
|
||||
import { Service, Inject } from 'typedi';
|
||||
import { Router, Request, Response, NextFunction } from 'express';
|
||||
import { param } from 'express-validator';
|
||||
import BaseController from '../BaseController';
|
||||
import { ServiceError } from '@/exceptions';
|
||||
import DeleteCashflowTransactionService from '../../../services/Cashflow/DeleteCashflowTransactionService';
|
||||
import CheckPolicies from '@/api/middleware/CheckPolicies';
|
||||
import { AbilitySubject, CashflowAction } from '@/interfaces';
|
||||
|
||||
@Service()
|
||||
export default class DeleteCashflowTransaction extends BaseController {
|
||||
@Inject()
|
||||
deleteCashflowService: DeleteCashflowTransactionService;
|
||||
|
||||
/**
|
||||
* Controller router.
|
||||
*/
|
||||
public router() {
|
||||
const router = Router();
|
||||
|
||||
router.delete(
|
||||
'/transactions/:transactionId',
|
||||
CheckPolicies(CashflowAction.Delete, AbilitySubject.Cashflow),
|
||||
[param('transactionId').exists().isInt().toInt()],
|
||||
this.asyncMiddleware(this.deleteCashflowTransaction),
|
||||
this.catchServiceErrors
|
||||
);
|
||||
return router;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the cashflow account transactions.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
*/
|
||||
private deleteCashflowTransaction = async (
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
) => {
|
||||
const { tenantId } = req;
|
||||
const { transactionId } = req.params;
|
||||
|
||||
try {
|
||||
const { oldCashflowTransaction } =
|
||||
await this.deleteCashflowService.deleteCashflowTransaction(
|
||||
tenantId,
|
||||
transactionId
|
||||
);
|
||||
|
||||
return res.status(200).send({
|
||||
id: oldCashflowTransaction.id,
|
||||
message: 'The cashflow transaction has been deleted successfully.',
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Catches the service errors.
|
||||
* @param error
|
||||
* @param req
|
||||
* @param res
|
||||
* @param next
|
||||
* @returns
|
||||
*/
|
||||
private catchServiceErrors(
|
||||
error,
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
) {
|
||||
if (error instanceof ServiceError) {
|
||||
if (error.errorType === 'CASHFLOW_TRANSACTION_NOT_FOUND') {
|
||||
return res.boom.badRequest(
|
||||
'The given cashflow transaction not found.',
|
||||
{
|
||||
errors: [{ type: 'CASHFLOW_TRANSACTION_NOT_FOUND', code: 100 }],
|
||||
}
|
||||
);
|
||||
}
|
||||
if (error.errorType === 'TRANSACTIONS_DATE_LOCKED') {
|
||||
return res.boom.badRequest(null, {
|
||||
errors: [
|
||||
{
|
||||
type: 'TRANSACTIONS_DATE_LOCKED',
|
||||
code: 4000,
|
||||
data: { ...error.payload },
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
}
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
import { Service, Inject } from 'typedi';
|
||||
import { Router, Request, Response, NextFunction } from 'express';
|
||||
import { param, query } from 'express-validator';
|
||||
import GetCashflowAccountsService from '@/services/Cashflow/GetCashflowAccountsService';
|
||||
import BaseController from '../BaseController';
|
||||
import GetCashflowTransactionsService from '@/services/Cashflow/GetCashflowTransactionsService';
|
||||
import { ServiceError } from '@/exceptions';
|
||||
import CheckPolicies from '@/api/middleware/CheckPolicies';
|
||||
import { AbilitySubject, CashflowAction } from '@/interfaces';
|
||||
|
||||
@Service()
|
||||
export default class GetCashflowAccounts extends BaseController {
|
||||
@Inject()
|
||||
getCashflowAccountsService: GetCashflowAccountsService;
|
||||
|
||||
@Inject()
|
||||
getCashflowTransactionsService: GetCashflowTransactionsService;
|
||||
|
||||
/**
|
||||
* Controller router.
|
||||
*/
|
||||
public router() {
|
||||
const router = Router();
|
||||
|
||||
router.get(
|
||||
'/accounts',
|
||||
CheckPolicies(CashflowAction.View, AbilitySubject.Cashflow),
|
||||
[
|
||||
query('stringified_filter_roles').optional().isJSON(),
|
||||
|
||||
query('column_sort_by').optional(),
|
||||
query('sort_order').optional().isIn(['desc', 'asc']),
|
||||
|
||||
query('inactive_mode').optional().isBoolean().toBoolean(),
|
||||
query('search_keyword').optional({ nullable: true }).isString().trim(),
|
||||
],
|
||||
this.asyncMiddleware(this.getCashflowAccounts),
|
||||
this.catchServiceErrors
|
||||
);
|
||||
return router;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the cashflow accounts.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
*/
|
||||
private getCashflowAccounts = async (
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
) => {
|
||||
const { tenantId } = req;
|
||||
// Filter query.
|
||||
const filter = {
|
||||
sortOrder: 'desc',
|
||||
columnSortBy: 'created_at',
|
||||
inactiveMode: false,
|
||||
...this.matchedQueryData(req),
|
||||
};
|
||||
|
||||
try {
|
||||
const cashflowAccounts =
|
||||
await this.getCashflowAccountsService.getCashflowAccounts(
|
||||
tenantId,
|
||||
filter
|
||||
);
|
||||
|
||||
return res.status(200).send({
|
||||
cashflow_accounts: this.transfromToResponse(cashflowAccounts),
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Catches the service errors.
|
||||
* @param {Error} error - Error.
|
||||
* @param {Request} req - Request.
|
||||
* @param {Response} res - Response.
|
||||
* @param {NextFunction} next -
|
||||
*/
|
||||
private catchServiceErrors(
|
||||
error,
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
) {
|
||||
if (error instanceof ServiceError) {
|
||||
}
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
@@ -1,98 +0,0 @@
|
||||
import { Service, Inject } from 'typedi';
|
||||
import { Router, Request, Response, NextFunction } from 'express';
|
||||
import { param } from 'express-validator';
|
||||
import BaseController from '../BaseController';
|
||||
import GetCashflowTransactionsService from '@/services/Cashflow/GetCashflowTransactionsService';
|
||||
import { ServiceError } from '@/exceptions';
|
||||
import CheckPolicies from '@/api/middleware/CheckPolicies';
|
||||
import { AbilitySubject, CashflowAction } from '@/interfaces';
|
||||
|
||||
@Service()
|
||||
export default class GetCashflowAccounts extends BaseController {
|
||||
@Inject()
|
||||
getCashflowTransactionsService: GetCashflowTransactionsService;
|
||||
|
||||
/**
|
||||
* Controller router.
|
||||
*/
|
||||
public router() {
|
||||
const router = Router();
|
||||
|
||||
router.get(
|
||||
'/transactions/:transactionId',
|
||||
CheckPolicies(CashflowAction.View, AbilitySubject.Cashflow),
|
||||
[param('transactionId').exists().isInt().toInt()],
|
||||
this.asyncMiddleware(this.getCashflowTransaction),
|
||||
this.catchServiceErrors
|
||||
);
|
||||
return router;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the cashflow account transactions.
|
||||
* @param {Request} req - Request object.
|
||||
* @param {Response} res - Response object.
|
||||
* @param {NextFunction} next
|
||||
*/
|
||||
private getCashflowTransaction = async (
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
) => {
|
||||
const { tenantId } = req;
|
||||
const { transactionId } = req.params;
|
||||
|
||||
try {
|
||||
const cashflowTransaction =
|
||||
await this.getCashflowTransactionsService.getCashflowTransaction(
|
||||
tenantId,
|
||||
transactionId
|
||||
);
|
||||
|
||||
return res.status(200).send({
|
||||
cashflow_transaction: this.transfromToResponse(cashflowTransaction),
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Catches the service errors.
|
||||
* @param {Error} error - Error.
|
||||
* @param {Request} req - Request.
|
||||
* @param {Response} res - Response.
|
||||
* @param {NextFunction} next -
|
||||
*/
|
||||
private catchServiceErrors(
|
||||
error,
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
) {
|
||||
if (error instanceof ServiceError) {
|
||||
if (error.errorType === 'CASHFLOW_TRANSACTION_NOT_FOUND') {
|
||||
return res.boom.badRequest(
|
||||
'The given cashflow tranasction not found.',
|
||||
{
|
||||
errors: [{ type: 'CASHFLOW_TRANSACTION_NOT_FOUND', code: 200 }],
|
||||
}
|
||||
);
|
||||
}
|
||||
if (error.errorType === 'ACCOUNT_ID_HAS_INVALID_TYPE') {
|
||||
return res.boom.badRequest(
|
||||
'The given cashflow account has invalid type.',
|
||||
{
|
||||
errors: [{ type: 'ACCOUNT_ID_HAS_INVALID_TYPE', code: 300 }],
|
||||
}
|
||||
);
|
||||
}
|
||||
if (error.errorType === 'ACCOUNT_NOT_FOUND') {
|
||||
return res.boom.badRequest('The given account not found.', {
|
||||
errors: [{ type: 'ACCOUNT_NOT_FOUND', code: 400 }],
|
||||
});
|
||||
}
|
||||
}
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
@@ -1,146 +0,0 @@
|
||||
import { Service, Inject } from 'typedi';
|
||||
import { check } from 'express-validator';
|
||||
import { Router, Request, Response, NextFunction } from 'express';
|
||||
import BaseController from '../BaseController';
|
||||
import { ServiceError } from '@/exceptions';
|
||||
import NewCashflowTransactionService from '@/services/Cashflow/NewCashflowTransactionService';
|
||||
import CheckPolicies from '@/api/middleware/CheckPolicies';
|
||||
import { AbilitySubject, CashflowAction } from '@/interfaces';
|
||||
|
||||
@Service()
|
||||
export default class NewCashflowTransactionController extends BaseController {
|
||||
@Inject()
|
||||
private newCashflowTranscationService: NewCashflowTransactionService;
|
||||
|
||||
/**
|
||||
* Router constructor.
|
||||
*/
|
||||
public router() {
|
||||
const router = Router();
|
||||
|
||||
router.post(
|
||||
'/transactions',
|
||||
CheckPolicies(CashflowAction.Create, AbilitySubject.Cashflow),
|
||||
this.newTransactionValidationSchema,
|
||||
this.validationResult,
|
||||
this.asyncMiddleware(this.newCashflowTransaction),
|
||||
this.catchServiceErrors
|
||||
);
|
||||
return router;
|
||||
}
|
||||
|
||||
/**
|
||||
* New cashflow transaction validation schema.
|
||||
*/
|
||||
get newTransactionValidationSchema() {
|
||||
return [
|
||||
check('date').exists().isISO8601().toDate(),
|
||||
check('reference_no').optional({ nullable: true }).trim().escape(),
|
||||
check('description')
|
||||
.optional({ nullable: true })
|
||||
.isLength({ min: 3 })
|
||||
.trim()
|
||||
.escape(),
|
||||
check('transaction_type').exists(),
|
||||
|
||||
check('amount').exists().isFloat().toFloat(),
|
||||
check('cashflow_account_id').exists().isInt().toInt(),
|
||||
check('credit_account_id').exists().isInt().toInt(),
|
||||
|
||||
check('exchange_rate').optional().isFloat({ gt: 0 }).toFloat(),
|
||||
|
||||
check('branch_id').optional({ nullable: true }).isNumeric().toInt(),
|
||||
|
||||
check('publish').default(false).isBoolean().toBoolean(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new cashflow transaction.
|
||||
* @param {Request} req -
|
||||
* @param {Response} res -
|
||||
* @param {NextFunction} next -
|
||||
*/
|
||||
private newCashflowTransaction = async (
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
) => {
|
||||
const { tenantId, userId } = req;
|
||||
const ownerContributionDTO = this.matchedBodyData(req);
|
||||
|
||||
try {
|
||||
const { cashflowTransaction } =
|
||||
await this.newCashflowTranscationService.newCashflowTransaction(
|
||||
tenantId,
|
||||
ownerContributionDTO,
|
||||
userId
|
||||
);
|
||||
|
||||
return res.status(200).send({
|
||||
id: cashflowTransaction.id,
|
||||
message: 'New cashflow transaction has been created successfully.',
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle the service errors.
|
||||
* @param error
|
||||
* @param req
|
||||
* @param res
|
||||
* @param next
|
||||
* @returns
|
||||
*/
|
||||
private catchServiceErrors(
|
||||
error,
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
) {
|
||||
if (error instanceof ServiceError) {
|
||||
if (error.errorType === 'CASHFLOW_ACCOUNTS_IDS_NOT_FOUND') {
|
||||
return res.boom.badRequest('Cashflow accounts ids not found.', {
|
||||
errors: [{ type: 'CASHFLOW_ACCOUNTS_IDS_NOT_FOUND', code: 100 }],
|
||||
});
|
||||
}
|
||||
if (error.errorType === 'CREDIT_ACCOUNTS_IDS_NOT_FOUND') {
|
||||
return res.boom.badRequest('Credit accounts ids not found.', {
|
||||
errors: [{ type: 'CREDIT_ACCOUNTS_IDS_NOT_FOUND', code: 200 }],
|
||||
});
|
||||
}
|
||||
if (error.errorType === 'CREDIT_ACCOUNTS_HAS_INVALID_TYPE') {
|
||||
return res.boom.badRequest('Cashflow .', {
|
||||
errors: [{ type: 'CREDIT_ACCOUNTS_HAS_INVALID_TYPE', code: 300 }],
|
||||
});
|
||||
}
|
||||
if (error.errorType === 'CASHFLOW_ACCOUNTS_HAS_INVALID_TYPE') {
|
||||
return res.boom.badRequest(
|
||||
'Cashflow accounts should be cash or bank type.',
|
||||
{
|
||||
errors: [{ type: 'CASHFLOW_ACCOUNTS_HAS_INVALID_TYPE', code: 300 }],
|
||||
}
|
||||
);
|
||||
}
|
||||
if (error.errorType === 'CASHFLOW_TRANSACTION_NOT_FOUND') {
|
||||
return res.boom.badRequest('Cashflow transaction not found.', {
|
||||
errors: [{ type: 'CASHFLOW_TRANSACTION_NOT_FOUND', code: 500 }],
|
||||
});
|
||||
}
|
||||
if (error.errorType === 'TRANSACTIONS_DATE_LOCKED') {
|
||||
return res.boom.badRequest(null, {
|
||||
errors: [
|
||||
{
|
||||
type: 'TRANSACTIONS_DATE_LOCKED',
|
||||
code: 4000,
|
||||
data: { ...error.payload },
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
}
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
@@ -1,404 +0,0 @@
|
||||
import { check, param, query, body, ValidationChain } from 'express-validator';
|
||||
import { Router, Request, Response, NextFunction } from 'express';
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { ServiceError } from '@/exceptions';
|
||||
import BaseController from '@/api/controllers/BaseController';
|
||||
import ContactsService from '@/services/Contacts/ContactsService';
|
||||
import DynamicListingService from '@/services/DynamicListing/DynamicListService';
|
||||
import { DATATYPES_LENGTH } from '@/data/DataTypes';
|
||||
|
||||
@Service()
|
||||
export default class ContactsController extends BaseController {
|
||||
@Inject()
|
||||
contactsService: ContactsService;
|
||||
|
||||
@Inject()
|
||||
dynamicListService: DynamicListingService;
|
||||
|
||||
/**
|
||||
* Express router.
|
||||
*/
|
||||
router() {
|
||||
const router = Router();
|
||||
|
||||
router.get(
|
||||
'/auto-complete',
|
||||
[...this.autocompleteQuerySchema],
|
||||
this.validationResult,
|
||||
this.asyncMiddleware(this.autocompleteContacts.bind(this)),
|
||||
this.dynamicListService.handlerErrorsToResponse
|
||||
);
|
||||
router.get(
|
||||
'/:id',
|
||||
[param('id').exists().isNumeric().toInt()],
|
||||
this.validationResult,
|
||||
this.asyncMiddleware(this.getContact.bind(this))
|
||||
);
|
||||
router.post(
|
||||
'/:id/inactivate',
|
||||
[param('id').exists().isNumeric().toInt()],
|
||||
this.validationResult,
|
||||
this.asyncMiddleware(this.inactivateContact.bind(this)),
|
||||
this.handlerServiceErrors
|
||||
);
|
||||
router.post(
|
||||
'/:id/activate',
|
||||
[param('id').exists().isNumeric().toInt()],
|
||||
this.validationResult,
|
||||
this.asyncMiddleware(this.activateContact.bind(this)),
|
||||
this.handlerServiceErrors
|
||||
);
|
||||
return router;
|
||||
}
|
||||
|
||||
/**
|
||||
* Auto-complete list query validation schema.
|
||||
*/
|
||||
get autocompleteQuerySchema() {
|
||||
return [
|
||||
query('column_sort_by').optional().trim().escape(),
|
||||
query('sort_order').optional().isIn(['desc', 'asc']),
|
||||
|
||||
query('stringified_filter_roles').optional().isJSON(),
|
||||
query('limit').optional().isNumeric().toInt(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve details of the given contact.
|
||||
* @param {Request} req -
|
||||
* @param {Response} res -
|
||||
* @param {NextFunction} next -
|
||||
*/
|
||||
async getContact(req: Request, res: Response, next: NextFunction) {
|
||||
const { tenantId } = req;
|
||||
const { id: contactId } = req.params;
|
||||
|
||||
try {
|
||||
const contact = await this.contactsService.getContact(
|
||||
tenantId,
|
||||
contactId
|
||||
);
|
||||
return res.status(200).send({
|
||||
customer: this.transfromToResponse(contact),
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve auto-complete contacts list.
|
||||
* @param {Request} req - Request object.
|
||||
* @param {Response} res - Response object.
|
||||
* @param {NextFunction} next
|
||||
*/
|
||||
async autocompleteContacts(req: Request, res: Response, next: NextFunction) {
|
||||
const { tenantId } = req;
|
||||
const filter = {
|
||||
filterRoles: [],
|
||||
sortOrder: 'asc',
|
||||
columnSortBy: 'display_name',
|
||||
limit: 10,
|
||||
...this.matchedQueryData(req),
|
||||
};
|
||||
try {
|
||||
const contacts = await this.contactsService.autocompleteContacts(
|
||||
tenantId,
|
||||
filter
|
||||
);
|
||||
return res.status(200).send({ contacts });
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {ValidationChain[]}
|
||||
*/
|
||||
get contactDTOSchema(): ValidationChain[] {
|
||||
return [
|
||||
check('salutation')
|
||||
.optional({ nullable: true })
|
||||
.isString()
|
||||
.trim()
|
||||
.escape()
|
||||
.isLength({ max: DATATYPES_LENGTH.STRING }),
|
||||
check('first_name')
|
||||
.optional({ nullable: true })
|
||||
.isString()
|
||||
.trim()
|
||||
.escape()
|
||||
.isLength({ max: DATATYPES_LENGTH.STRING }),
|
||||
check('last_name')
|
||||
.optional({ nullable: true })
|
||||
.isString()
|
||||
.trim()
|
||||
.escape()
|
||||
.isLength({ max: DATATYPES_LENGTH.STRING }),
|
||||
check('company_name')
|
||||
.optional({ nullable: true })
|
||||
.isString()
|
||||
.trim()
|
||||
.escape()
|
||||
.isLength({ max: DATATYPES_LENGTH.STRING }),
|
||||
|
||||
check('display_name')
|
||||
.exists()
|
||||
.isString()
|
||||
.trim()
|
||||
.escape()
|
||||
.isLength({ max: DATATYPES_LENGTH.STRING }),
|
||||
|
||||
check('email')
|
||||
.optional({ nullable: true })
|
||||
.isString()
|
||||
.normalizeEmail()
|
||||
.isEmail()
|
||||
.isLength({ max: DATATYPES_LENGTH.STRING }),
|
||||
check('website')
|
||||
.optional({ nullable: true })
|
||||
.isString()
|
||||
.trim()
|
||||
.isURL()
|
||||
.isLength({ max: DATATYPES_LENGTH.STRING }),
|
||||
check('work_phone')
|
||||
.optional({ nullable: true })
|
||||
.isString()
|
||||
.trim()
|
||||
.escape()
|
||||
.isLength({ max: DATATYPES_LENGTH.STRING }),
|
||||
check('personal_phone')
|
||||
.optional({ nullable: true })
|
||||
.isString()
|
||||
.trim()
|
||||
.escape()
|
||||
.isLength({ max: DATATYPES_LENGTH.STRING }),
|
||||
|
||||
check('billing_address_1')
|
||||
.optional({ nullable: true })
|
||||
.isString()
|
||||
.trim()
|
||||
.escape()
|
||||
.isLength({ max: DATATYPES_LENGTH.STRING }),
|
||||
check('billing_address_2')
|
||||
.optional({ nullable: true })
|
||||
.isString()
|
||||
.trim()
|
||||
.escape()
|
||||
.isLength({ max: DATATYPES_LENGTH.STRING }),
|
||||
check('billing_address_city')
|
||||
.optional({ nullable: true })
|
||||
.isString()
|
||||
.trim()
|
||||
.escape()
|
||||
.isLength({ max: DATATYPES_LENGTH.STRING }),
|
||||
check('billing_address_country')
|
||||
.optional({ nullable: true })
|
||||
.isString()
|
||||
.trim()
|
||||
.escape()
|
||||
.isLength({ max: DATATYPES_LENGTH.STRING }),
|
||||
check('billing_address_email')
|
||||
.optional({ nullable: true })
|
||||
.isString()
|
||||
.isEmail()
|
||||
.trim()
|
||||
.escape()
|
||||
.isLength({ max: DATATYPES_LENGTH.STRING }),
|
||||
check('billing_address_postcode')
|
||||
.optional({ nullable: true })
|
||||
.isString()
|
||||
.trim()
|
||||
.escape()
|
||||
.isLength({ max: DATATYPES_LENGTH.STRING }),
|
||||
check('billing_address_phone')
|
||||
.optional({ nullable: true })
|
||||
.isString()
|
||||
.trim()
|
||||
.escape()
|
||||
.isLength({ max: DATATYPES_LENGTH.STRING }),
|
||||
check('billing_address_state')
|
||||
.optional({ nullable: true })
|
||||
.isString()
|
||||
.trim()
|
||||
.escape()
|
||||
.isLength({ max: DATATYPES_LENGTH.STRING }),
|
||||
|
||||
check('shipping_address_1')
|
||||
.optional({ nullable: true })
|
||||
.isString()
|
||||
.trim()
|
||||
.escape()
|
||||
.isLength({ max: DATATYPES_LENGTH.STRING }),
|
||||
check('shipping_address_2')
|
||||
.optional({ nullable: true })
|
||||
.isString()
|
||||
.trim()
|
||||
.escape()
|
||||
.isLength({ max: DATATYPES_LENGTH.STRING }),
|
||||
check('shipping_address_city')
|
||||
.optional({ nullable: true })
|
||||
.isString()
|
||||
.trim()
|
||||
.escape()
|
||||
.isLength({ max: DATATYPES_LENGTH.STRING }),
|
||||
check('shipping_address_country')
|
||||
.optional({ nullable: true })
|
||||
.isString()
|
||||
.trim()
|
||||
.escape()
|
||||
.isLength({ max: DATATYPES_LENGTH.STRING }),
|
||||
check('shipping_address_email')
|
||||
.optional({ nullable: true })
|
||||
.isString()
|
||||
.isEmail()
|
||||
.trim()
|
||||
.escape()
|
||||
.isLength({ max: DATATYPES_LENGTH.STRING }),
|
||||
check('shipping_address_postcode')
|
||||
.optional({ nullable: true })
|
||||
.isString()
|
||||
.trim()
|
||||
.escape()
|
||||
.isLength({ max: DATATYPES_LENGTH.STRING }),
|
||||
check('shipping_address_phone')
|
||||
.optional({ nullable: true })
|
||||
.isString()
|
||||
.trim()
|
||||
.escape()
|
||||
.isLength({ max: DATATYPES_LENGTH.STRING }),
|
||||
check('shipping_address_state')
|
||||
.optional({ nullable: true })
|
||||
.isString()
|
||||
.trim()
|
||||
.escape()
|
||||
.isLength({ max: DATATYPES_LENGTH.STRING }),
|
||||
|
||||
check('note')
|
||||
.optional({ nullable: true })
|
||||
.isString()
|
||||
.trim()
|
||||
.escape()
|
||||
.isLength({ max: DATATYPES_LENGTH.TEXT }),
|
||||
check('active').optional().isBoolean().toBoolean(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Contact new DTO schema.
|
||||
* @returns {ValidationChain[]}
|
||||
*/
|
||||
get contactNewDTOSchema(): ValidationChain[] {
|
||||
return [
|
||||
check('opening_balance')
|
||||
.optional({ nullable: true })
|
||||
.isInt({ min: 0, max: DATATYPES_LENGTH.DECIMAL_13_3 })
|
||||
.toInt(),
|
||||
check('opening_balance_exchange_rate')
|
||||
.default(1)
|
||||
.isFloat({ gt: 0 })
|
||||
.toFloat(),
|
||||
body('opening_balance_at')
|
||||
.if(body('opening_balance').exists())
|
||||
.exists()
|
||||
.isISO8601(),
|
||||
check('opening_balance_branch_id')
|
||||
.optional({ nullable: true })
|
||||
.isNumeric()
|
||||
.toInt(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Contact edit DTO schema.
|
||||
* @returns {ValidationChain[]}
|
||||
*/
|
||||
get contactEditDTOSchema(): ValidationChain[] {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {ValidationChain[]}
|
||||
*/
|
||||
get specificContactSchema(): ValidationChain[] {
|
||||
return [param('id').exists().isNumeric().toInt()];
|
||||
}
|
||||
|
||||
/**
|
||||
* Activates the given contact.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
*/
|
||||
async activateContact(req: Request, res: Response, next: NextFunction) {
|
||||
const { tenantId } = req;
|
||||
const { id: contactId } = req.params;
|
||||
|
||||
try {
|
||||
await this.contactsService.activateContact(tenantId, contactId);
|
||||
|
||||
return res.status(200).send({
|
||||
id: contactId,
|
||||
message: 'The given contact activated successfully.',
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inactivate the given contact.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
*/
|
||||
async inactivateContact(req: Request, res: Response, next: NextFunction) {
|
||||
const { tenantId } = req;
|
||||
const { id: contactId } = req.params;
|
||||
|
||||
try {
|
||||
await this.contactsService.inactivateContact(tenantId, contactId);
|
||||
|
||||
return res.status(200).send({
|
||||
id: contactId,
|
||||
message: 'The given contact inactivated successfully.',
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles service errors.
|
||||
* @param {Error} error
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
*/
|
||||
private handlerServiceErrors(
|
||||
error: Error,
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
) {
|
||||
if (error instanceof ServiceError) {
|
||||
if (error.errorType === 'contact_not_found') {
|
||||
return res.boom.badRequest(null, {
|
||||
errors: [{ type: 'CONTACT.NOT.FOUND', code: 100 }],
|
||||
});
|
||||
}
|
||||
if (error.errorType === 'CONTACT_ALREADY_ACTIVE') {
|
||||
return res.boom.badRequest(null, {
|
||||
errors: [{ type: 'CONTACT_ALREADY_ACTIVE', code: 700 }],
|
||||
});
|
||||
}
|
||||
if (error.errorType === 'CONTACT_ALREADY_INACTIVE') {
|
||||
return res.boom.badRequest(null, {
|
||||
errors: [{ type: 'CONTACT_ALREADY_INACTIVE', code: 800 }],
|
||||
});
|
||||
}
|
||||
}
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
@@ -1,351 +0,0 @@
|
||||
import { Request, Response, Router, NextFunction } from 'express';
|
||||
import { Service, Inject } from 'typedi';
|
||||
import { check, query } from 'express-validator';
|
||||
import ContactsController from '@/api/controllers/Contacts/Contacts';
|
||||
import CustomersService from '@/services/Contacts/CustomersService';
|
||||
import { ServiceError } from '@/exceptions';
|
||||
import {
|
||||
ICustomerNewDTO,
|
||||
ICustomerEditDTO,
|
||||
AbilitySubject,
|
||||
CustomerAction,
|
||||
} from '@/interfaces';
|
||||
import asyncMiddleware from '@/api/middleware/asyncMiddleware';
|
||||
import DynamicListingService from '@/services/DynamicListing/DynamicListService';
|
||||
import CheckPolicies from '@/api/middleware/CheckPolicies';
|
||||
import { CustomersApplication } from '@/services/Contacts/Customers/CustomersApplication';
|
||||
|
||||
@Service()
|
||||
export default class CustomersController extends ContactsController {
|
||||
@Inject()
|
||||
private customersApplication: CustomersApplication;
|
||||
|
||||
@Inject()
|
||||
private dynamicListService: DynamicListingService;
|
||||
|
||||
/**
|
||||
* Express router.
|
||||
*/
|
||||
router() {
|
||||
const router = Router();
|
||||
|
||||
router.post(
|
||||
'/',
|
||||
CheckPolicies(CustomerAction.Create, AbilitySubject.Customer),
|
||||
[
|
||||
...this.contactDTOSchema,
|
||||
...this.contactNewDTOSchema,
|
||||
...this.customerDTOSchema,
|
||||
...this.createCustomerDTOSchema,
|
||||
],
|
||||
this.validationResult,
|
||||
asyncMiddleware(this.newCustomer.bind(this)),
|
||||
this.handlerServiceErrors
|
||||
);
|
||||
router.post(
|
||||
'/:id/opening_balance',
|
||||
CheckPolicies(CustomerAction.Edit, AbilitySubject.Customer),
|
||||
[
|
||||
...this.specificContactSchema,
|
||||
check('opening_balance').exists().isNumeric().toFloat(),
|
||||
check('opening_balance_at').optional().isISO8601(),
|
||||
check('opening_balance_exchange_rate')
|
||||
.default(1)
|
||||
.isFloat({ gt: 0 })
|
||||
.toFloat(),
|
||||
check('opening_balance_branch_id')
|
||||
.optional({ nullable: true })
|
||||
.isNumeric()
|
||||
.toInt(),
|
||||
],
|
||||
this.validationResult,
|
||||
asyncMiddleware(this.editOpeningBalanceCustomer.bind(this)),
|
||||
this.handlerServiceErrors
|
||||
);
|
||||
router.post(
|
||||
'/:id',
|
||||
CheckPolicies(CustomerAction.Edit, AbilitySubject.Customer),
|
||||
[
|
||||
...this.contactDTOSchema,
|
||||
...this.contactEditDTOSchema,
|
||||
...this.customerDTOSchema,
|
||||
],
|
||||
this.validationResult,
|
||||
asyncMiddleware(this.editCustomer.bind(this)),
|
||||
this.handlerServiceErrors
|
||||
);
|
||||
router.delete(
|
||||
'/:id',
|
||||
CheckPolicies(CustomerAction.Delete, AbilitySubject.Customer),
|
||||
[...this.specificContactSchema],
|
||||
this.validationResult,
|
||||
asyncMiddleware(this.deleteCustomer.bind(this)),
|
||||
this.handlerServiceErrors
|
||||
);
|
||||
router.get(
|
||||
'/',
|
||||
CheckPolicies(CustomerAction.View, AbilitySubject.Customer),
|
||||
[...this.validateListQuerySchema],
|
||||
this.validationResult,
|
||||
asyncMiddleware(this.getCustomersList.bind(this)),
|
||||
this.dynamicListService.handlerErrorsToResponse
|
||||
);
|
||||
router.get(
|
||||
'/:id',
|
||||
CheckPolicies(CustomerAction.View, AbilitySubject.Customer),
|
||||
[...this.specificContactSchema],
|
||||
this.validationResult,
|
||||
asyncMiddleware(this.getCustomer.bind(this)),
|
||||
this.handlerServiceErrors
|
||||
);
|
||||
return router;
|
||||
}
|
||||
|
||||
/**
|
||||
* Customer DTO schema.
|
||||
*/
|
||||
get customerDTOSchema() {
|
||||
return [
|
||||
check('customer_type')
|
||||
.exists()
|
||||
.isIn(['business', 'individual'])
|
||||
.trim()
|
||||
.escape(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Create customer DTO schema.
|
||||
*/
|
||||
get createCustomerDTOSchema() {
|
||||
return [
|
||||
check('currency_code')
|
||||
.optional({ nullable: true })
|
||||
.isString()
|
||||
.trim()
|
||||
.escape()
|
||||
.isLength({ max: 3 }),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* List param query schema.
|
||||
*/
|
||||
get validateListQuerySchema() {
|
||||
return [
|
||||
query('column_sort_by').optional().trim().escape(),
|
||||
query('sort_order').optional().isIn(['desc', 'asc']),
|
||||
|
||||
query('page').optional().isNumeric().toInt(),
|
||||
query('page_size').optional().isNumeric().toInt(),
|
||||
|
||||
query('view_slug').optional().isString().trim(),
|
||||
query('stringified_filter_roles').optional().isJSON(),
|
||||
|
||||
query('inactive_mode').optional().isBoolean().toBoolean(),
|
||||
query('search_keyword').optional({ nullable: true }).isString().trim(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new customer.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
*/
|
||||
async newCustomer(req: Request, res: Response, next: NextFunction) {
|
||||
const contactDTO: ICustomerNewDTO = this.matchedBodyData(req);
|
||||
const { tenantId, user } = req;
|
||||
|
||||
try {
|
||||
const contact = await this.customersApplication.createCustomer(
|
||||
tenantId,
|
||||
contactDTO,
|
||||
user
|
||||
);
|
||||
|
||||
return res.status(200).send({
|
||||
id: contact.id,
|
||||
message: 'The customer has been created successfully.',
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Edits the given customer details.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
*/
|
||||
async editCustomer(req: Request, res: Response, next: NextFunction) {
|
||||
const contactDTO: ICustomerEditDTO = this.matchedBodyData(req);
|
||||
const { tenantId, user } = req;
|
||||
const { id: contactId } = req.params;
|
||||
|
||||
try {
|
||||
await this.customersApplication.editCustomer(
|
||||
tenantId,
|
||||
contactId,
|
||||
contactDTO,
|
||||
user
|
||||
);
|
||||
|
||||
return res.status(200).send({
|
||||
id: contactId,
|
||||
message: 'The customer has been edited successfully.',
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the opening balance of the given customer.
|
||||
* @param {Request} req -
|
||||
* @param {Response} res -
|
||||
* @param {NextFunction} next -
|
||||
*/
|
||||
async editOpeningBalanceCustomer(
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
) {
|
||||
const { tenantId, user } = req;
|
||||
const { id: customerId } = req.params;
|
||||
const openingBalanceEditDTO = this.matchedBodyData(req);
|
||||
|
||||
try {
|
||||
await this.customersApplication.editOpeningBalance(
|
||||
tenantId,
|
||||
customerId,
|
||||
openingBalanceEditDTO
|
||||
);
|
||||
return res.status(200).send({
|
||||
id: customerId,
|
||||
message:
|
||||
'The opening balance of the given customer has been changed successfully.',
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve details of the given customer id.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
*/
|
||||
async getCustomer(req: Request, res: Response, next: NextFunction) {
|
||||
const { tenantId, user } = req;
|
||||
const { id: contactId } = req.params;
|
||||
|
||||
try {
|
||||
const customer = await this.customersApplication.getCustomer(
|
||||
tenantId,
|
||||
contactId,
|
||||
user
|
||||
);
|
||||
|
||||
return res.status(200).send({
|
||||
customer: this.transfromToResponse(customer),
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the given customer from the storage.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
*/
|
||||
async deleteCustomer(req: Request, res: Response, next: NextFunction) {
|
||||
const { tenantId, user } = req;
|
||||
const { id: contactId } = req.params;
|
||||
|
||||
try {
|
||||
await this.customersApplication.deleteCustomer(tenantId, contactId, user);
|
||||
|
||||
return res.status(200).send({
|
||||
id: contactId,
|
||||
message: 'The customer has been deleted successfully.',
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve customers paginated and filterable list.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
*/
|
||||
async getCustomersList(req: Request, res: Response, next: NextFunction) {
|
||||
const { tenantId } = req;
|
||||
|
||||
const filter = {
|
||||
inactiveMode: false,
|
||||
sortOrder: 'desc',
|
||||
columnSortBy: 'created_at',
|
||||
page: 1,
|
||||
pageSize: 12,
|
||||
...this.matchedQueryData(req),
|
||||
};
|
||||
|
||||
try {
|
||||
const { customers, pagination, filterMeta } =
|
||||
await this.customersApplication.getCustomers(tenantId, filter);
|
||||
|
||||
return res.status(200).send({
|
||||
customers: this.transfromToResponse(customers),
|
||||
pagination: this.transfromToResponse(pagination),
|
||||
filter_meta: this.transfromToResponse(filterMeta),
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles service errors.
|
||||
* @param {Error} error
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
*/
|
||||
private handlerServiceErrors(
|
||||
error: Error,
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
) {
|
||||
if (error instanceof ServiceError) {
|
||||
if (error.errorType === 'contact_not_found') {
|
||||
return res.boom.badRequest(null, {
|
||||
errors: [{ type: 'CUSTOMER.NOT.FOUND', code: 100 }],
|
||||
});
|
||||
}
|
||||
if (error.errorType === 'contacts_not_found') {
|
||||
return res.boom.badRequest(null, {
|
||||
errors: [{ type: 'CUSTOMERS.NOT.FOUND', code: 200 }],
|
||||
});
|
||||
}
|
||||
if (error.errorType === 'OPENING_BALANCE_DATE_REQUIRED') {
|
||||
return res.boom.badRequest(null, {
|
||||
errors: [{ type: 'OPENING_BALANCE_DATE_REQUIRED', code: 500 }],
|
||||
});
|
||||
}
|
||||
if (error.errorType === 'CUSTOMER_HAS_TRANSACTIONS') {
|
||||
return res.boom.badRequest(null, {
|
||||
errors: [{ type: 'CUSTOMER_HAS_TRANSACTIONS', code: 600 }],
|
||||
});
|
||||
}
|
||||
}
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
@@ -1,332 +0,0 @@
|
||||
import { Request, Response, Router, NextFunction } from 'express';
|
||||
import { Service, Inject } from 'typedi';
|
||||
import { body, query, ValidationChain, check } from 'express-validator';
|
||||
|
||||
import ContactsController from '@/api/controllers/Contacts/Contacts';
|
||||
import { ServiceError } from '@/exceptions';
|
||||
import {
|
||||
IVendorNewDTO,
|
||||
IVendorEditDTO,
|
||||
IVendorsFilter,
|
||||
AbilitySubject,
|
||||
VendorAction,
|
||||
} from '@/interfaces';
|
||||
import asyncMiddleware from '@/api/middleware/asyncMiddleware';
|
||||
import CheckPolicies from '@/api/middleware/CheckPolicies';
|
||||
import { VendorsApplication } from '@/services/Contacts/Vendors/VendorsApplication';
|
||||
|
||||
@Service()
|
||||
export default class VendorsController extends ContactsController {
|
||||
@Inject()
|
||||
private vendorsApplication: VendorsApplication;
|
||||
|
||||
/**
|
||||
* Express router.
|
||||
*/
|
||||
router() {
|
||||
const router = Router();
|
||||
|
||||
router.post(
|
||||
'/',
|
||||
CheckPolicies(VendorAction.Create, AbilitySubject.Vendor),
|
||||
[
|
||||
...this.contactDTOSchema,
|
||||
...this.contactNewDTOSchema,
|
||||
...this.vendorDTOSchema,
|
||||
],
|
||||
this.validationResult,
|
||||
asyncMiddleware(this.newVendor.bind(this)),
|
||||
this.handlerServiceErrors
|
||||
);
|
||||
router.post(
|
||||
'/:id/opening_balance',
|
||||
CheckPolicies(VendorAction.Edit, AbilitySubject.Vendor),
|
||||
[
|
||||
...this.specificContactSchema,
|
||||
check('opening_balance').exists().isNumeric().toFloat(),
|
||||
check('opening_balance_at').optional().isISO8601(),
|
||||
check('opening_balance_exchange_rate')
|
||||
.default(1)
|
||||
.isFloat({ gt: 0 })
|
||||
.toFloat(),
|
||||
check('opening_balance_branch_id')
|
||||
.optional({ nullable: true })
|
||||
.isNumeric()
|
||||
.toInt(),
|
||||
],
|
||||
this.validationResult,
|
||||
asyncMiddleware(this.editOpeningBalanceVendor.bind(this)),
|
||||
this.handlerServiceErrors
|
||||
);
|
||||
router.post(
|
||||
'/:id',
|
||||
CheckPolicies(VendorAction.Edit, AbilitySubject.Vendor),
|
||||
[
|
||||
...this.contactDTOSchema,
|
||||
...this.contactEditDTOSchema,
|
||||
...this.vendorDTOSchema,
|
||||
],
|
||||
this.validationResult,
|
||||
asyncMiddleware(this.editVendor.bind(this)),
|
||||
this.handlerServiceErrors
|
||||
);
|
||||
router.delete(
|
||||
'/:id',
|
||||
CheckPolicies(VendorAction.Delete, AbilitySubject.Vendor),
|
||||
[...this.specificContactSchema],
|
||||
this.validationResult,
|
||||
asyncMiddleware(this.deleteVendor.bind(this)),
|
||||
this.handlerServiceErrors
|
||||
);
|
||||
router.get(
|
||||
'/:id',
|
||||
CheckPolicies(VendorAction.View, AbilitySubject.Vendor),
|
||||
[...this.specificContactSchema],
|
||||
this.validationResult,
|
||||
asyncMiddleware(this.getVendor.bind(this)),
|
||||
this.handlerServiceErrors
|
||||
);
|
||||
router.get(
|
||||
'/',
|
||||
CheckPolicies(VendorAction.View, AbilitySubject.Vendor),
|
||||
[...this.vendorsListSchema],
|
||||
this.validationResult,
|
||||
asyncMiddleware(this.getVendorsList.bind(this))
|
||||
);
|
||||
return router;
|
||||
}
|
||||
|
||||
/**
|
||||
* Vendor DTO schema.
|
||||
* @returns {ValidationChain[]}
|
||||
*/
|
||||
get vendorDTOSchema(): ValidationChain[] {
|
||||
return [
|
||||
check('currency_code')
|
||||
.optional({ nullable: true })
|
||||
.isString()
|
||||
.trim()
|
||||
.escape()
|
||||
.isLength({ min: 3, max: 3 }),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Vendors datatable list validation schema.
|
||||
* @returns {ValidationChain[]}
|
||||
*/
|
||||
get vendorsListSchema() {
|
||||
return [
|
||||
query('view_slug').optional().isString().trim(),
|
||||
query('stringified_filter_roles').optional().isJSON(),
|
||||
|
||||
query('column_sort_by').optional(),
|
||||
query('sort_order').optional().isIn(['desc', 'asc']),
|
||||
|
||||
query('page').optional().isNumeric().toInt(),
|
||||
query('page_size').optional().isNumeric().toInt(),
|
||||
|
||||
query('inactive_mode').optional().isBoolean().toBoolean(),
|
||||
query('search_keyword').optional({ nullable: true }).isString().trim(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new vendor.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
*/
|
||||
async newVendor(req: Request, res: Response, next: NextFunction) {
|
||||
const contactDTO: IVendorNewDTO = this.matchedBodyData(req);
|
||||
const { tenantId, user } = req;
|
||||
|
||||
try {
|
||||
const vendor = await this.vendorsApplication.createVendor(
|
||||
tenantId,
|
||||
contactDTO,
|
||||
user
|
||||
);
|
||||
|
||||
return res.status(200).send({
|
||||
id: vendor.id,
|
||||
message: 'The vendor has been created successfully.',
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Edits the given vendor details.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
*/
|
||||
async editVendor(req: Request, res: Response, next: NextFunction) {
|
||||
const contactDTO: IVendorEditDTO = this.matchedBodyData(req);
|
||||
const { tenantId, user } = req;
|
||||
const { id: contactId } = req.params;
|
||||
|
||||
try {
|
||||
await this.vendorsApplication.editVendor(
|
||||
tenantId,
|
||||
contactId,
|
||||
contactDTO,
|
||||
user
|
||||
);
|
||||
|
||||
return res.status(200).send({
|
||||
id: contactId,
|
||||
message: 'The vendor has been edited successfully.',
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the opening balance of the given vendor.
|
||||
* @param {Request} req -
|
||||
* @param {Response} res -
|
||||
* @param {NextFunction} next -
|
||||
*/
|
||||
async editOpeningBalanceVendor(
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
) {
|
||||
const { tenantId, user } = req;
|
||||
const { id: vendorId } = req.params;
|
||||
const editOpeningBalanceDTO = this.matchedBodyData(req);
|
||||
|
||||
try {
|
||||
await this.vendorsApplication.editOpeningBalance(
|
||||
tenantId,
|
||||
vendorId,
|
||||
editOpeningBalanceDTO
|
||||
);
|
||||
return res.status(200).send({
|
||||
id: vendorId,
|
||||
message:
|
||||
'The opening balance of the given vendor has been changed successfully.',
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the given vendor from the storage.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
*/
|
||||
async deleteVendor(req: Request, res: Response, next: NextFunction) {
|
||||
const { tenantId, user } = req;
|
||||
const { id: contactId } = req.params;
|
||||
|
||||
try {
|
||||
await this.vendorsApplication.deleteVendor(tenantId, contactId, user);
|
||||
|
||||
return res.status(200).send({
|
||||
id: contactId,
|
||||
message: 'The vendor has been deleted successfully.',
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve details of the given vendor id.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
*/
|
||||
async getVendor(req: Request, res: Response, next: NextFunction) {
|
||||
const { tenantId, user } = req;
|
||||
const { id: vendorId } = req.params;
|
||||
|
||||
try {
|
||||
const vendor = await this.vendorsApplication.getVendor(
|
||||
tenantId,
|
||||
vendorId,
|
||||
user
|
||||
);
|
||||
return res.status(200).send(this.transfromToResponse({ vendor }));
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve vendors datatable list.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
*/
|
||||
async getVendorsList(req: Request, res: Response, next: NextFunction) {
|
||||
const { tenantId } = req;
|
||||
|
||||
const vendorsFilter: IVendorsFilter = {
|
||||
inactiveMode: false,
|
||||
sortOrder: 'desc',
|
||||
columnSortBy: 'created_at',
|
||||
page: 1,
|
||||
pageSize: 12,
|
||||
...this.matchedQueryData(req),
|
||||
};
|
||||
|
||||
try {
|
||||
const { vendors, pagination, filterMeta } =
|
||||
await this.vendorsApplication.getVendors(tenantId, vendorsFilter);
|
||||
|
||||
return res.status(200).send({
|
||||
vendors: this.transfromToResponse(vendors),
|
||||
pagination: this.transfromToResponse(pagination),
|
||||
filter_meta: this.transfromToResponse(filterMeta),
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle service errors.
|
||||
* @param {Error} error -
|
||||
* @param {Request} req -
|
||||
* @param {Response} res -
|
||||
* @param {NextFunction} next -
|
||||
*/
|
||||
private handlerServiceErrors(
|
||||
error,
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
) {
|
||||
if (error instanceof ServiceError) {
|
||||
if (error.errorType === 'contact_not_found') {
|
||||
return res.boom.badRequest(null, {
|
||||
errors: [{ type: 'VENDOR.NOT.FOUND', code: 100 }],
|
||||
});
|
||||
}
|
||||
if (error.errorType === 'contacts_not_found') {
|
||||
return res.boom.badRequest(null, {
|
||||
errors: [{ type: 'VENDORS.NOT.FOUND', code: 200 }],
|
||||
});
|
||||
}
|
||||
if (error.errorType === 'OPENING_BALANCE_DATE_REQUIRED') {
|
||||
return res.boom.badRequest(null, {
|
||||
errors: [{ type: 'OPENING_BALANCE_DATE_REQUIRED', code: 500 }],
|
||||
});
|
||||
}
|
||||
if (error.errorType === 'VENDOR_HAS_TRANSACTIONS') {
|
||||
return res.boom.badRequest(null, {
|
||||
errors: [{ type: 'VENDOR_HAS_TRANSACTIONS', code: 600 }],
|
||||
});
|
||||
}
|
||||
}
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
@@ -1,211 +0,0 @@
|
||||
import { Router, Request, Response, NextFunction } from 'express';
|
||||
import { check, param, query, ValidationChain } from 'express-validator';
|
||||
import asyncMiddleware from '@/api/middleware/asyncMiddleware';
|
||||
import BaseController from './BaseController';
|
||||
import CurrenciesService from '@/services/Currencies/CurrenciesService';
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { ServiceError } from '@/exceptions';
|
||||
|
||||
@Service()
|
||||
export default class CurrenciesController extends BaseController {
|
||||
@Inject()
|
||||
currenciesService: CurrenciesService;
|
||||
|
||||
/**
|
||||
* Router constructor.
|
||||
*/
|
||||
router() {
|
||||
const router = Router();
|
||||
|
||||
router.get(
|
||||
'/',
|
||||
[...this.listSchema],
|
||||
this.validationResult,
|
||||
asyncMiddleware(this.all.bind(this))
|
||||
);
|
||||
router.post(
|
||||
'/',
|
||||
[...this.currencyDTOSchemaValidation],
|
||||
this.validationResult,
|
||||
asyncMiddleware(this.newCurrency.bind(this)),
|
||||
this.handlerServiceError
|
||||
);
|
||||
router.post(
|
||||
'/:id',
|
||||
[...this.currencyIdParamSchema, ...this.currencyEditDTOSchemaValidation],
|
||||
this.validationResult,
|
||||
asyncMiddleware(this.editCurrency.bind(this)),
|
||||
this.handlerServiceError
|
||||
);
|
||||
router.delete(
|
||||
'/:currency_code',
|
||||
[...this.currencyParamSchema],
|
||||
this.validationResult,
|
||||
asyncMiddleware(this.deleteCurrency.bind(this)),
|
||||
this.handlerServiceError
|
||||
);
|
||||
return router;
|
||||
}
|
||||
|
||||
get currencyDTOSchemaValidation(): ValidationChain[] {
|
||||
return [
|
||||
check('currency_name').exists().trim(),
|
||||
check('currency_code').exists().trim(),
|
||||
check('currency_sign').exists().trim(),
|
||||
];
|
||||
}
|
||||
|
||||
get currencyEditDTOSchemaValidation(): ValidationChain[] {
|
||||
return [
|
||||
check('currency_name').exists().trim(),
|
||||
check('currency_sign').exists().trim(),
|
||||
];
|
||||
}
|
||||
|
||||
get currencyIdParamSchema(): ValidationChain[] {
|
||||
return [param('id').exists().isNumeric().toInt()];
|
||||
}
|
||||
|
||||
get currencyParamSchema(): ValidationChain[] {
|
||||
return [param('currency_code').exists().trim().escape()];
|
||||
}
|
||||
|
||||
get listSchema(): ValidationChain[] {
|
||||
return [
|
||||
query('page').optional().isNumeric().toInt(),
|
||||
query('page_size').optional().isNumeric().toInt(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve all registered currency details.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
*/
|
||||
async all(req: Request, res: Response, next: NextFunction) {
|
||||
const { tenantId } = req;
|
||||
|
||||
try {
|
||||
const currencies = await this.currenciesService.listCurrencies(tenantId);
|
||||
|
||||
return res.status(200).send({
|
||||
currencies: this.transfromToResponse(currencies),
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new currency on the storage.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
*/
|
||||
async newCurrency(req: Request, res: Response, next: Function) {
|
||||
const { tenantId } = req;
|
||||
const currencyDTO = this.matchedBodyData(req);
|
||||
|
||||
try {
|
||||
await this.currenciesService.newCurrency(tenantId, currencyDTO);
|
||||
|
||||
return res.status(200).send({
|
||||
currency_code: currencyDTO.currencyCode,
|
||||
message: 'The currency has been created successfully.',
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Edits details of the given currency.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
*/
|
||||
async deleteCurrency(req: Request, res: Response, next: Function) {
|
||||
const { tenantId } = req;
|
||||
const { currency_code: currencyCode } = req.params;
|
||||
|
||||
try {
|
||||
await this.currenciesService.deleteCurrency(tenantId, currencyCode);
|
||||
return res.status(200).send({
|
||||
currency_code: currencyCode,
|
||||
message: 'The currency has been deleted successfully.',
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the currency.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
*/
|
||||
async editCurrency(req: Request, res: Response, next: Function) {
|
||||
const { tenantId } = req;
|
||||
const { id: currencyId } = req.params;
|
||||
const editCurrencyDTO = this.matchedBodyData(req);
|
||||
|
||||
try {
|
||||
const currency = await this.currenciesService.editCurrency(
|
||||
tenantId,
|
||||
currencyId,
|
||||
editCurrencyDTO
|
||||
);
|
||||
return res.status(200).send({
|
||||
currency_code: currency.currencyCode,
|
||||
message: 'The currency has been edited successfully.',
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles currencies service error.
|
||||
* @param {Error} error
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
*/
|
||||
handlerServiceError(
|
||||
error: Error,
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
) {
|
||||
if (error instanceof ServiceError) {
|
||||
if (error.errorType === 'currency_not_found') {
|
||||
return res.boom.badRequest(null, {
|
||||
errors: [{ type: 'CURRENCY_NOT_FOUND', code: 100 }],
|
||||
});
|
||||
}
|
||||
if (error.errorType === 'currency_code_exists') {
|
||||
return res.boom.badRequest(null, {
|
||||
errors: [{
|
||||
type: 'CURRENCY_CODE_EXISTS',
|
||||
message: 'The given currency code is already exists.',
|
||||
code: 200,
|
||||
}],
|
||||
});
|
||||
}
|
||||
if (error.errorType === 'CANNOT_DELETE_BASE_CURRENCY') {
|
||||
return res.boom.badRequest(null, {
|
||||
errors: [
|
||||
{
|
||||
type: 'CANNOT_DELETE_BASE_CURRENCY',
|
||||
code: 300,
|
||||
message: 'Cannot delete the base currency.',
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
}
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { Router, Request, Response, NextFunction } from 'express';
|
||||
import DashboardService from '@/services/Dashboard/DashboardService';
|
||||
|
||||
@Service()
|
||||
export default class DashboardMetaController {
|
||||
@Inject()
|
||||
dashboardService: DashboardService;
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns
|
||||
*/
|
||||
router() {
|
||||
const router = Router();
|
||||
|
||||
router.get('/boot', this.getDashboardBoot);
|
||||
|
||||
return router;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the dashboard boot meta.
|
||||
* @param {Request} req -
|
||||
* @param {Response} res -
|
||||
* @param {NextFunction} next -
|
||||
*/
|
||||
getDashboardBoot = async (
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
) => {
|
||||
const authorizedUser = req.user;
|
||||
const { tenantId } = req;
|
||||
|
||||
try {
|
||||
const meta = await this.dashboardService.getBootMeta(
|
||||
tenantId,
|
||||
authorizedUser
|
||||
);
|
||||
|
||||
return res.status(200).send({ meta });
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1,220 +0,0 @@
|
||||
import { Service, Inject } from 'typedi';
|
||||
import { Router, Request, Response, NextFunction } from 'express';
|
||||
import { check, param, query } from 'express-validator';
|
||||
import asyncMiddleware from '@/api/middleware/asyncMiddleware';
|
||||
import BaseController from './BaseController';
|
||||
import { ServiceError } from '@/exceptions';
|
||||
import ExchangeRatesService from '@/services/ExchangeRates/ExchangeRatesService';
|
||||
import DynamicListingService from '@/services/DynamicListing/DynamicListService';
|
||||
|
||||
@Service()
|
||||
export default class ExchangeRatesController extends BaseController {
|
||||
@Inject()
|
||||
exchangeRatesService: ExchangeRatesService;
|
||||
|
||||
@Inject()
|
||||
dynamicListService: DynamicListingService;
|
||||
|
||||
/**
|
||||
* Constructor method.
|
||||
*/
|
||||
router() {
|
||||
const router = Router();
|
||||
|
||||
router.get(
|
||||
'/',
|
||||
[...this.exchangeRatesListSchema],
|
||||
this.validationResult,
|
||||
asyncMiddleware(this.exchangeRates.bind(this)),
|
||||
this.dynamicListService.handlerErrorsToResponse,
|
||||
this.handleServiceError,
|
||||
);
|
||||
router.post(
|
||||
'/',
|
||||
[...this.exchangeRateDTOSchema],
|
||||
this.validationResult,
|
||||
asyncMiddleware(this.addExchangeRate.bind(this)),
|
||||
this.handleServiceError
|
||||
);
|
||||
router.post(
|
||||
'/:id',
|
||||
[...this.exchangeRateEditDTOSchema, ...this.exchangeRateIdSchema],
|
||||
this.validationResult,
|
||||
asyncMiddleware(this.editExchangeRate.bind(this)),
|
||||
this.handleServiceError
|
||||
);
|
||||
router.delete(
|
||||
'/:id',
|
||||
[...this.exchangeRateIdSchema],
|
||||
this.validationResult,
|
||||
asyncMiddleware(this.deleteExchangeRate.bind(this)),
|
||||
this.handleServiceError
|
||||
);
|
||||
return router;
|
||||
}
|
||||
|
||||
get exchangeRatesListSchema() {
|
||||
return [
|
||||
query('page').optional().isNumeric().toInt(),
|
||||
query('page_size').optional().isNumeric().toInt(),
|
||||
|
||||
query('column_sort_by').optional(),
|
||||
query('sort_order').optional().isIn(['desc', 'asc']),
|
||||
];
|
||||
}
|
||||
|
||||
get exchangeRateDTOSchema() {
|
||||
return [
|
||||
check('exchange_rate').exists().isNumeric().toFloat(),
|
||||
check('currency_code').exists().trim().escape(),
|
||||
check('date').exists().isISO8601(),
|
||||
];
|
||||
}
|
||||
|
||||
get exchangeRateEditDTOSchema() {
|
||||
return [check('exchange_rate').exists().isNumeric().toFloat()];
|
||||
}
|
||||
|
||||
get exchangeRateIdSchema() {
|
||||
return [param('id').isNumeric().toInt()];
|
||||
}
|
||||
|
||||
get exchangeRatesIdsSchema() {
|
||||
return [
|
||||
query('ids').isArray({ min: 2 }),
|
||||
query('ids.*').isNumeric().toInt(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve exchange rates.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
*/
|
||||
async exchangeRates(req: Request, res: Response, next: NextFunction) {
|
||||
const { tenantId } = req;
|
||||
const filter = {
|
||||
page: 1,
|
||||
pageSize: 12,
|
||||
filterRoles: [],
|
||||
columnSortBy: 'created_at',
|
||||
sortOrder: 'asc',
|
||||
...this.matchedQueryData(req),
|
||||
};
|
||||
if (filter.stringifiedFilterRoles) {
|
||||
filter.filterRoles = JSON.parse(filter.stringifiedFilterRoles);
|
||||
}
|
||||
try {
|
||||
const exchangeRates = await this.exchangeRatesService.listExchangeRates(
|
||||
tenantId,
|
||||
filter
|
||||
);
|
||||
return res.status(200).send({ exchange_rates: exchangeRates });
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new exchange rate on the given date.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
*/
|
||||
async addExchangeRate(req: Request, res: Response, next: NextFunction) {
|
||||
const { tenantId } = req;
|
||||
const exchangeRateDTO = this.matchedBodyData(req);
|
||||
|
||||
try {
|
||||
const exchangeRate = await this.exchangeRatesService.newExchangeRate(
|
||||
tenantId,
|
||||
exchangeRateDTO
|
||||
);
|
||||
return res.status(200).send({ id: exchangeRate.id });
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Edit the given exchange rate.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
*/
|
||||
async editExchangeRate(req: Request, res: Response, next: NextFunction) {
|
||||
const { tenantId } = req;
|
||||
const { id: exchangeRateId } = req.params;
|
||||
const exchangeRateDTO = this.matchedBodyData(req);
|
||||
|
||||
try {
|
||||
const exchangeRate = await this.exchangeRatesService.editExchangeRate(
|
||||
tenantId,
|
||||
exchangeRateId,
|
||||
exchangeRateDTO
|
||||
);
|
||||
|
||||
return res.status(200).send({
|
||||
id: exchangeRateId,
|
||||
message: 'The exchange rate has been edited successfully.',
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the given exchange rate from the storage.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
*/
|
||||
async deleteExchangeRate(req: Request, res: Response, next: NextFunction) {
|
||||
const { tenantId } = req;
|
||||
const { id: exchangeRateId } = req.params;
|
||||
|
||||
try {
|
||||
await this.exchangeRatesService.deleteExchangeRate(
|
||||
tenantId,
|
||||
exchangeRateId
|
||||
);
|
||||
return res.status(200).send({ id: exchangeRateId });
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle service errors.
|
||||
* @param {Error} error
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
*/
|
||||
handleServiceError(
|
||||
error: Error,
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
) {
|
||||
if (error instanceof ServiceError) {
|
||||
if (error.errorType === 'EXCHANGE_RATE_NOT_FOUND') {
|
||||
return res.status(404).send({
|
||||
errors: [{ type: 'EXCHANGE.RATE.NOT.FOUND', code: 200 }],
|
||||
});
|
||||
}
|
||||
if (error.errorType === 'NOT_FOUND_EXCHANGE_RATES') {
|
||||
return res.status(400).send({
|
||||
errors: [{ type: 'EXCHANGE.RATES.IS.NOT.FOUND', code: 100 }],
|
||||
});
|
||||
}
|
||||
if (error.errorType === 'EXCHANGE_RATE_PERIOD_EXISTS') {
|
||||
return res.status(400).send({
|
||||
errors: [{ type: 'EXCHANGE.RATE.PERIOD.EXISTS', code: 300 }],
|
||||
});
|
||||
}
|
||||
}
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
@@ -1,456 +0,0 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { check, param, query } from 'express-validator';
|
||||
import { Router, Request, Response, NextFunction } from 'express';
|
||||
import asyncMiddleware from '@/api/middleware/asyncMiddleware';
|
||||
import BaseController from '@/api/controllers/BaseController';
|
||||
import {
|
||||
AbilitySubject,
|
||||
ExpenseAction,
|
||||
IExpenseCreateDTO,
|
||||
IExpenseEditDTO,
|
||||
} from '@/interfaces';
|
||||
import { ServiceError } from '@/exceptions';
|
||||
import DynamicListingService from '@/services/DynamicListing/DynamicListService';
|
||||
import { DATATYPES_LENGTH } from '@/data/DataTypes';
|
||||
import CheckPolicies from '@/api/middleware/CheckPolicies';
|
||||
import { ExpensesApplication } from '@/services/Expenses/ExpensesApplication';
|
||||
|
||||
@Service()
|
||||
export class ExpensesController extends BaseController {
|
||||
@Inject()
|
||||
private expensesApplication: ExpensesApplication;
|
||||
|
||||
@Inject()
|
||||
private dynamicListService: DynamicListingService;
|
||||
|
||||
/**
|
||||
* Express router.
|
||||
*/
|
||||
router() {
|
||||
const router = Router();
|
||||
|
||||
router.post(
|
||||
'/',
|
||||
CheckPolicies(ExpenseAction.Create, AbilitySubject.Expense),
|
||||
[...this.expenseDTOSchema],
|
||||
this.validationResult,
|
||||
asyncMiddleware(this.newExpense.bind(this)),
|
||||
this.catchServiceErrors
|
||||
);
|
||||
router.post(
|
||||
'/:id/publish',
|
||||
CheckPolicies(ExpenseAction.Edit, AbilitySubject.Expense),
|
||||
[...this.expenseParamSchema],
|
||||
this.validationResult,
|
||||
asyncMiddleware(this.publishExpense.bind(this)),
|
||||
this.catchServiceErrors
|
||||
);
|
||||
router.post(
|
||||
'/:id',
|
||||
CheckPolicies(ExpenseAction.Edit, AbilitySubject.Expense),
|
||||
[...this.editExpenseDTOSchema, ...this.expenseParamSchema],
|
||||
this.validationResult,
|
||||
asyncMiddleware(this.editExpense.bind(this)),
|
||||
this.catchServiceErrors
|
||||
);
|
||||
router.delete(
|
||||
'/:id',
|
||||
CheckPolicies(ExpenseAction.Delete, AbilitySubject.Expense),
|
||||
[...this.expenseParamSchema],
|
||||
this.validationResult,
|
||||
asyncMiddleware(this.deleteExpense.bind(this)),
|
||||
this.catchServiceErrors
|
||||
);
|
||||
router.get(
|
||||
'/',
|
||||
CheckPolicies(ExpenseAction.View, AbilitySubject.Expense),
|
||||
[...this.expensesListSchema],
|
||||
this.validationResult,
|
||||
asyncMiddleware(this.getExpensesList.bind(this)),
|
||||
this.dynamicListService.handlerErrorsToResponse,
|
||||
this.catchServiceErrors
|
||||
);
|
||||
router.get(
|
||||
'/:id',
|
||||
CheckPolicies(ExpenseAction.View, AbilitySubject.Expense),
|
||||
[this.expenseParamSchema],
|
||||
this.validationResult,
|
||||
asyncMiddleware(this.getExpense.bind(this)),
|
||||
this.catchServiceErrors
|
||||
);
|
||||
return router;
|
||||
}
|
||||
|
||||
/**
|
||||
* Expense DTO schema.
|
||||
*/
|
||||
get expenseDTOSchema() {
|
||||
return [
|
||||
check('reference_no')
|
||||
.optional({ nullable: true })
|
||||
.trim()
|
||||
.escape()
|
||||
.isLength({ max: DATATYPES_LENGTH.STRING }),
|
||||
check('payment_date').exists().isISO8601().toDate(),
|
||||
check('payment_account_id')
|
||||
.exists()
|
||||
.isInt({ max: DATATYPES_LENGTH.INT_10 })
|
||||
.toInt(),
|
||||
check('description')
|
||||
.optional({ nullable: true })
|
||||
.isString()
|
||||
.isLength({ max: DATATYPES_LENGTH.TEXT }),
|
||||
check('currency_code').optional().isString().isLength({ max: 3 }),
|
||||
check('exchange_rate').optional({ nullable: true }).isNumeric().toFloat(),
|
||||
check('publish').optional().isBoolean().toBoolean(),
|
||||
check('payee_id').optional({ nullable: true }).isNumeric().toInt(),
|
||||
|
||||
check('branch_id').optional({ nullable: true }).isNumeric().toInt(),
|
||||
|
||||
check('categories').exists().isArray({ min: 1 }),
|
||||
check('categories.*.index')
|
||||
.exists()
|
||||
.isInt({ max: DATATYPES_LENGTH.INT_10 })
|
||||
.toInt(),
|
||||
check('categories.*.expense_account_id')
|
||||
.exists()
|
||||
.isInt({ max: DATATYPES_LENGTH.INT_10 })
|
||||
.toInt(),
|
||||
check('categories.*.amount')
|
||||
.optional({ nullable: true })
|
||||
.isFloat({ max: DATATYPES_LENGTH.DECIMAL_13_3 }) // 13, 3
|
||||
.toFloat(),
|
||||
check('categories.*.description')
|
||||
.optional()
|
||||
.trim()
|
||||
.escape()
|
||||
.isLength({ max: DATATYPES_LENGTH.STRING }),
|
||||
check('categories.*.landed_cost').optional().isBoolean().toBoolean(),
|
||||
check('categories.*.project_id')
|
||||
.optional({ nullable: true })
|
||||
.isInt({ max: DATATYPES_LENGTH.INT_10 })
|
||||
.toInt(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Edit expense validation schema.
|
||||
*/
|
||||
get editExpenseDTOSchema() {
|
||||
return [
|
||||
check('reference_no')
|
||||
.optional({ nullable: true })
|
||||
.trim()
|
||||
.escape()
|
||||
.isLength({ max: DATATYPES_LENGTH.STRING }),
|
||||
check('payment_date').exists().isISO8601().toDate(),
|
||||
check('payment_account_id')
|
||||
.exists()
|
||||
.isInt({ max: DATATYPES_LENGTH.INT_10 })
|
||||
.toInt(),
|
||||
check('description')
|
||||
.optional({ nullable: true })
|
||||
.isString()
|
||||
.isLength({ max: DATATYPES_LENGTH.TEXT }),
|
||||
check('currency_code').optional().isString().isLength({ max: 3 }),
|
||||
check('exchange_rate').optional({ nullable: true }).isNumeric().toFloat(),
|
||||
check('publish').optional().isBoolean().toBoolean(),
|
||||
check('payee_id').optional({ nullable: true }).isNumeric().toInt(),
|
||||
|
||||
check('branch_id').optional({ nullable: true }).isNumeric().toInt(),
|
||||
|
||||
check('categories').exists().isArray({ min: 1 }),
|
||||
check('categories.*.id').optional().isNumeric().toInt(),
|
||||
check('categories.*.index')
|
||||
.exists()
|
||||
.isInt({ max: DATATYPES_LENGTH.INT_10 })
|
||||
.toInt(),
|
||||
check('categories.*.expense_account_id')
|
||||
.exists()
|
||||
.isInt({ max: DATATYPES_LENGTH.INT_10 })
|
||||
.toInt(),
|
||||
check('categories.*.amount')
|
||||
.optional({ nullable: true })
|
||||
.isFloat({ max: DATATYPES_LENGTH.DECIMAL_13_3 }) // 13, 3
|
||||
.toFloat(),
|
||||
check('categories.*.description')
|
||||
.optional()
|
||||
.trim()
|
||||
.escape()
|
||||
.isLength({ max: DATATYPES_LENGTH.STRING }),
|
||||
check('categories.*.landed_cost').optional().isBoolean().toBoolean(),
|
||||
check('categories.*.project_id')
|
||||
.optional({ nullable: true })
|
||||
.isInt({ max: DATATYPES_LENGTH.INT_10 })
|
||||
.toInt(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Expense param validation schema.
|
||||
*/
|
||||
get expenseParamSchema() {
|
||||
return [param('id').exists().isNumeric().toInt()];
|
||||
}
|
||||
|
||||
/**
|
||||
* Expenses list validation schema.
|
||||
*/
|
||||
get expensesListSchema() {
|
||||
return [
|
||||
query('view_slug').optional({ nullable: true }).isString().trim(),
|
||||
query('stringified_filter_roles').optional().isJSON(),
|
||||
|
||||
query('column_sort_by').optional(),
|
||||
query('sort_order').optional().isIn(['desc', 'asc']),
|
||||
|
||||
query('page').optional().isNumeric().toInt(),
|
||||
query('page_size').optional().isNumeric().toInt(),
|
||||
|
||||
query('search_keyword').optional({ nullable: true }).isString().trim(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new expense on
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
*/
|
||||
async newExpense(req: Request, res: Response, next: NextFunction) {
|
||||
const expenseDTO: IExpenseCreateDTO = this.matchedBodyData(req);
|
||||
const { tenantId, user } = req;
|
||||
|
||||
try {
|
||||
const expense = await this.expensesApplication.createExpense(
|
||||
tenantId,
|
||||
expenseDTO,
|
||||
user
|
||||
);
|
||||
return res.status(200).send({
|
||||
id: expense.id,
|
||||
message: 'The expense has been created successfully.',
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Edits details of the given expense.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
*/
|
||||
async editExpense(req: Request, res: Response, next: NextFunction) {
|
||||
const { id: expenseId } = req.params;
|
||||
const expenseDTO: IExpenseEditDTO = this.matchedBodyData(req);
|
||||
const { tenantId, user } = req;
|
||||
|
||||
try {
|
||||
await this.expensesApplication.editExpense(
|
||||
tenantId,
|
||||
expenseId,
|
||||
expenseDTO,
|
||||
user
|
||||
);
|
||||
return res.status(200).send({
|
||||
id: expenseId,
|
||||
message: 'The expense has been edited successfully.',
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the given expense.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
*/
|
||||
async deleteExpense(req: Request, res: Response, next: NextFunction) {
|
||||
const { tenantId, user } = req;
|
||||
const { id: expenseId } = req.params;
|
||||
|
||||
try {
|
||||
await this.expensesApplication.deleteExpense(tenantId, expenseId, user);
|
||||
|
||||
return res.status(200).send({
|
||||
id: expenseId,
|
||||
message: 'The expense has been deleted successfully.',
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Publishs the given expense.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
*/
|
||||
async publishExpense(req: Request, res: Response, next: NextFunction) {
|
||||
const { tenantId, user } = req;
|
||||
const { id: expenseId } = req.params;
|
||||
|
||||
try {
|
||||
await this.expensesApplication.publishExpense(tenantId, expenseId, user);
|
||||
|
||||
return res.status(200).send({
|
||||
id: expenseId,
|
||||
message: 'The expense has been published successfully',
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve expneses list.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
*/
|
||||
async getExpensesList(req: Request, res: Response, next: NextFunction) {
|
||||
const { tenantId } = req;
|
||||
const filter = {
|
||||
sortOrder: 'desc',
|
||||
columnSortBy: 'created_at',
|
||||
page: 1,
|
||||
pageSize: 12,
|
||||
...this.matchedQueryData(req),
|
||||
};
|
||||
|
||||
try {
|
||||
const { expenses, pagination, filterMeta } =
|
||||
await this.expensesApplication.getExpenses(tenantId, filter);
|
||||
|
||||
return res.status(200).send({
|
||||
expenses: this.transfromToResponse(expenses),
|
||||
pagination: this.transfromToResponse(pagination),
|
||||
filter_meta: this.transfromToResponse(filterMeta),
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve expense details.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
*/
|
||||
async getExpense(req: Request, res: Response, next: NextFunction) {
|
||||
const { tenantId } = req;
|
||||
const { id: expenseId } = req.params;
|
||||
|
||||
try {
|
||||
const expense = await this.expensesApplication.getExpense(
|
||||
tenantId,
|
||||
expenseId
|
||||
);
|
||||
return res.status(200).send(this.transfromToResponse({ expense }));
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform service errors to api response errors.
|
||||
* @param {Response} res
|
||||
* @param {ServiceError} error
|
||||
*/
|
||||
private catchServiceErrors(
|
||||
error: Error,
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
) {
|
||||
if (error instanceof ServiceError) {
|
||||
if (error.errorType === 'expense_not_found') {
|
||||
return res.boom.badRequest('Expense not found.', {
|
||||
errors: [{ type: 'EXPENSE_NOT_FOUND', code: 100 }],
|
||||
});
|
||||
}
|
||||
if (error.errorType === 'EXPENSES_NOT_FOUND') {
|
||||
return res.boom.badRequest('Expenses not found.', {
|
||||
errors: [{ type: 'EXPENSES_NOT_FOUND', code: 110 }],
|
||||
});
|
||||
}
|
||||
if (error.errorType === 'total_amount_equals_zero') {
|
||||
return res.boom.badRequest('Expense total should not equal zero.', {
|
||||
errors: [{ type: 'TOTAL.AMOUNT.EQUALS.ZERO', code: 200 }],
|
||||
});
|
||||
}
|
||||
if (error.errorType === 'payment_account_not_found') {
|
||||
return res.boom.badRequest('Payment account not found.', {
|
||||
errors: [{ type: 'PAYMENT.ACCOUNT.NOT.FOUND', code: 300 }],
|
||||
});
|
||||
}
|
||||
if (error.errorType === 'some_expenses_not_found') {
|
||||
return res.boom.badRequest('Some expense accounts not found.', {
|
||||
errors: [{ type: 'SOME.EXPENSE.ACCOUNTS.NOT.FOUND', code: 400 }],
|
||||
});
|
||||
}
|
||||
if (error.errorType === 'payment_account_has_invalid_type') {
|
||||
return res.boom.badRequest('Payment account has invalid type.', {
|
||||
errors: [{ type: 'PAYMENT.ACCOUNT.HAS.INVALID.TYPE', code: 500 }],
|
||||
});
|
||||
}
|
||||
if (error.errorType === 'expenses_account_has_invalid_type') {
|
||||
return res.boom.badRequest(null, {
|
||||
errors: [{ type: 'EXPENSES.ACCOUNT.HAS.INVALID.TYPE', code: 600 }],
|
||||
});
|
||||
}
|
||||
if (error.errorType === 'expense_already_published') {
|
||||
return res.boom.badRequest(null, {
|
||||
errors: [{ type: 'EXPENSE_ALREADY_PUBLISHED', code: 700 }],
|
||||
});
|
||||
}
|
||||
if (error.errorType === 'contact_not_found') {
|
||||
return res.boom.badRequest(null, {
|
||||
errors: [{ type: 'CONTACT_NOT_FOUND', code: 800 }],
|
||||
});
|
||||
}
|
||||
if (error.errorType === 'EXPENSE_HAS_ASSOCIATED_LANDED_COST') {
|
||||
return res.status(400).send({
|
||||
errors: [{ type: 'EXPENSE_HAS_ASSOCIATED_LANDED_COST', code: 900 }],
|
||||
});
|
||||
}
|
||||
if (error.errorType === 'ENTRIES_ALLOCATED_COST_COULD_NOT_DELETED') {
|
||||
return res.status(400).send({
|
||||
errors: [
|
||||
{ type: 'ENTRIES_ALLOCATED_COST_COULD_NOT_DELETED', code: 1000 },
|
||||
],
|
||||
});
|
||||
}
|
||||
if (
|
||||
error.errorType === 'LOCATED_COST_ENTRIES_SHOULD_BIGGE_THAN_NEW_ENTRIES'
|
||||
) {
|
||||
return res.status(400).send({
|
||||
errors: [
|
||||
{
|
||||
type: 'LOCATED_COST_ENTRIES_SHOULD_BIGGE_THAN_NEW_ENTRIES',
|
||||
code: 1100,
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
if (error.errorType === 'TRANSACTIONS_DATE_LOCKED') {
|
||||
return res.boom.badRequest(null, {
|
||||
errors: [
|
||||
{
|
||||
type: 'TRANSACTIONS_DATE_LOCKED',
|
||||
code: 4000,
|
||||
data: { ...error.payload },
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
}
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
|
||||
import { Router } from 'express';
|
||||
import { Container, Service } from 'typedi';
|
||||
import { ExpensesController } from './Expenses';
|
||||
|
||||
@Service()
|
||||
export default class ExpensesBaseController {
|
||||
router() {
|
||||
const router = Router();
|
||||
|
||||
router.use('/', Container.get(ExpensesController).router());
|
||||
|
||||
return router;
|
||||
}
|
||||
}
|
||||
@@ -1,112 +0,0 @@
|
||||
import { Router } from 'express';
|
||||
import { Container, Service } from 'typedi';
|
||||
|
||||
import BalanceSheetController from './FinancialStatements/BalanceSheet';
|
||||
import TrialBalanceSheetController from './FinancialStatements/TrialBalanceSheet';
|
||||
import GeneralLedgerController from './FinancialStatements/GeneralLedger';
|
||||
import JournalSheetController from './FinancialStatements/JournalSheet';
|
||||
import ProfitLossController from './FinancialStatements/ProfitLossSheet';
|
||||
import ARAgingSummary from './FinancialStatements/ARAgingSummary';
|
||||
import APAgingSummary from './FinancialStatements/APAgingSummary';
|
||||
import PurchasesByItemsController from './FinancialStatements/PurchasesByItem';
|
||||
import SalesByItemsController from './FinancialStatements/SalesByItems';
|
||||
import InventoryValuationController from './FinancialStatements/InventoryValuationSheet';
|
||||
import CustomerBalanceSummaryController from './FinancialStatements/CustomerBalanceSummary';
|
||||
import VendorBalanceSummaryController from './FinancialStatements/VendorBalanceSummary';
|
||||
import TransactionsByCustomers from './FinancialStatements/TransactionsByCustomers';
|
||||
import TransactionsByVendors from './FinancialStatements/TransactionsByVendors';
|
||||
import CashFlowStatementController from './FinancialStatements/CashFlow/CashFlow';
|
||||
import InventoryDetailsController from './FinancialStatements/InventoryDetails';
|
||||
import TransactionsByReferenceController from './FinancialStatements/TransactionsByReference';
|
||||
import CashflowAccountTransactions from './FinancialStatements/CashflowAccountTransactions';
|
||||
import ProjectProfitabilityController from './FinancialStatements/ProjectProfitabilitySummary';
|
||||
import SalesTaxLiabilitySummary from './FinancialStatements/SalesTaxLiabilitySummary';
|
||||
|
||||
@Service()
|
||||
export default class FinancialStatementsService {
|
||||
/**
|
||||
* Router constructor.
|
||||
*/
|
||||
router() {
|
||||
const router = Router();
|
||||
|
||||
router.use(
|
||||
'/balance_sheet',
|
||||
Container.get(BalanceSheetController).router()
|
||||
);
|
||||
router.use(
|
||||
'/profit_loss_sheet',
|
||||
Container.get(ProfitLossController).router()
|
||||
);
|
||||
router.use(
|
||||
'/general_ledger',
|
||||
Container.get(GeneralLedgerController).router()
|
||||
);
|
||||
router.use(
|
||||
'/trial_balance_sheet',
|
||||
Container.get(TrialBalanceSheetController).router()
|
||||
);
|
||||
router.use('/journal', Container.get(JournalSheetController).router());
|
||||
router.use(
|
||||
'/receivable_aging_summary',
|
||||
Container.get(ARAgingSummary).router()
|
||||
);
|
||||
router.use(
|
||||
'/payable_aging_summary',
|
||||
Container.get(APAgingSummary).router()
|
||||
);
|
||||
router.use(
|
||||
'/purchases-by-items',
|
||||
Container.get(PurchasesByItemsController).router()
|
||||
);
|
||||
router.use(
|
||||
'/sales-by-items',
|
||||
Container.get(SalesByItemsController).router()
|
||||
);
|
||||
router.use(
|
||||
'/inventory-valuation',
|
||||
Container.get(InventoryValuationController).router()
|
||||
);
|
||||
router.use(
|
||||
'/customer-balance-summary',
|
||||
Container.get(CustomerBalanceSummaryController).router()
|
||||
);
|
||||
router.use(
|
||||
'/vendor-balance-summary',
|
||||
Container.get(VendorBalanceSummaryController).router()
|
||||
);
|
||||
router.use(
|
||||
'/transactions-by-customers',
|
||||
Container.get(TransactionsByCustomers).router()
|
||||
);
|
||||
router.use(
|
||||
'/transactions-by-vendors',
|
||||
Container.get(TransactionsByVendors).router()
|
||||
);
|
||||
router.use(
|
||||
'/cash-flow',
|
||||
Container.get(CashFlowStatementController).router()
|
||||
);
|
||||
router.use(
|
||||
'/inventory-item-details',
|
||||
Container.get(InventoryDetailsController).router()
|
||||
);
|
||||
router.use(
|
||||
'/transactions-by-reference',
|
||||
Container.get(TransactionsByReferenceController).router()
|
||||
);
|
||||
router.use(
|
||||
'/cashflow-account-transactions',
|
||||
Container.get(CashflowAccountTransactions).router()
|
||||
);
|
||||
router.use(
|
||||
'/project-profitability-summary',
|
||||
Container.get(ProjectProfitabilityController).router()
|
||||
);
|
||||
router.use(
|
||||
'/sales-tax-liability-summary',
|
||||
Container.get(SalesTaxLiabilitySummary).router()
|
||||
);
|
||||
return router;
|
||||
}
|
||||
}
|
||||
@@ -1,93 +0,0 @@
|
||||
import { Router, Request, Response, NextFunction } from 'express';
|
||||
import { query } from 'express-validator';
|
||||
import { Inject } from 'typedi';
|
||||
import asyncMiddleware from '@/api/middleware/asyncMiddleware';
|
||||
import APAgingSummaryReportService from '@/services/FinancialStatements/AgingSummary/APAgingSummaryService';
|
||||
import BaseFinancialReportController from './BaseFinancialReportController';
|
||||
import { AbilitySubject, ReportsAction } from '@/interfaces';
|
||||
import CheckPolicies from '@/api/middleware/CheckPolicies';
|
||||
|
||||
export default class APAgingSummaryReportController extends BaseFinancialReportController {
|
||||
@Inject()
|
||||
APAgingSummaryService: APAgingSummaryReportService;
|
||||
|
||||
/**
|
||||
* Router constructor.
|
||||
*/
|
||||
router() {
|
||||
const router = Router();
|
||||
|
||||
router.get(
|
||||
'/',
|
||||
CheckPolicies(ReportsAction.READ_AP_AGING_SUMMARY, AbilitySubject.Report),
|
||||
this.validationSchema,
|
||||
asyncMiddleware(this.payableAgingSummary.bind(this))
|
||||
);
|
||||
return router;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validation schema.
|
||||
*/
|
||||
get validationSchema() {
|
||||
return [
|
||||
...this.sheetNumberFormatValidationSchema,
|
||||
query('as_date').optional().isISO8601(),
|
||||
|
||||
query('aging_days_before').default(30).isInt({ max: 500 }).toInt(),
|
||||
query('aging_periods').default(3).isInt({ max: 12 }).toInt(),
|
||||
|
||||
query('vendors_ids').optional().isArray({ min: 1 }),
|
||||
query('vendors_ids.*').isInt({ min: 1 }).toInt(),
|
||||
|
||||
query('none_zero').default(true).isBoolean().toBoolean(),
|
||||
|
||||
// Filtering by branches.
|
||||
query('branches_ids').optional().toArray().isArray({ min: 1 }),
|
||||
query('branches_ids.*').isNumeric().toInt(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve payable aging summary report.
|
||||
*/
|
||||
async payableAgingSummary(req: Request, res: Response, next: NextFunction) {
|
||||
const { tenantId, settings } = req;
|
||||
const filter = this.matchedQueryData(req);
|
||||
|
||||
try {
|
||||
const accept = this.accepts(req);
|
||||
const acceptType = accept.types(['json', 'application/json+table']);
|
||||
|
||||
switch (acceptType) {
|
||||
case 'application/json+table':
|
||||
const table = await this.APAgingSummaryService.APAgingSummaryTable(
|
||||
tenantId,
|
||||
filter
|
||||
);
|
||||
return res.status(200).send({
|
||||
table: {
|
||||
rows: table.rows,
|
||||
columns: table.columns,
|
||||
},
|
||||
meta: table.meta,
|
||||
query: table.query,
|
||||
});
|
||||
break;
|
||||
default:
|
||||
const { data, columns, query, meta } =
|
||||
await this.APAgingSummaryService.APAgingSummary(tenantId, filter);
|
||||
|
||||
return res.status(200).send({
|
||||
data: this.transfromToResponse(data),
|
||||
columns: this.transfromToResponse(columns),
|
||||
query: this.transfromToResponse(query),
|
||||
meta: this.transfromToResponse(meta),
|
||||
});
|
||||
break;
|
||||
}
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
import { Service, Inject } from 'typedi';
|
||||
import { Router, Request, Response } from 'express';
|
||||
import { query } from 'express-validator';
|
||||
import ARAgingSummaryService from '@/services/FinancialStatements/AgingSummary/ARAgingSummaryService';
|
||||
import BaseFinancialReportController from './BaseFinancialReportController';
|
||||
import { AbilitySubject, ReportsAction } from '@/interfaces';
|
||||
import CheckPolicies from '@/api/middleware/CheckPolicies';
|
||||
|
||||
@Service()
|
||||
export default class ARAgingSummaryReportController extends BaseFinancialReportController {
|
||||
@Inject()
|
||||
ARAgingSummaryService: ARAgingSummaryService;
|
||||
|
||||
/**
|
||||
* Router constructor.
|
||||
*/
|
||||
router() {
|
||||
const router = Router();
|
||||
|
||||
router.get(
|
||||
'/',
|
||||
CheckPolicies(ReportsAction.READ_AR_AGING_SUMMARY, AbilitySubject.Report),
|
||||
this.validationSchema,
|
||||
this.validationResult,
|
||||
this.asyncMiddleware(this.receivableAgingSummary.bind(this))
|
||||
);
|
||||
return router;
|
||||
}
|
||||
|
||||
/**
|
||||
* AR aging summary validation roles.
|
||||
*/
|
||||
get validationSchema() {
|
||||
return [
|
||||
...this.sheetNumberFormatValidationSchema,
|
||||
|
||||
query('as_date').optional().isISO8601(),
|
||||
|
||||
query('aging_days_before').default(30).isInt({ max: 500 }).toInt(),
|
||||
query('aging_periods').default(3).isInt({ max: 12 }).toInt(),
|
||||
|
||||
query('customers_ids').optional().isArray({ min: 1 }),
|
||||
query('customers_ids.*').isInt({ min: 1 }).toInt(),
|
||||
|
||||
query('none_zero').default(true).isBoolean().toBoolean(),
|
||||
|
||||
// Filtering by branches.
|
||||
query('branches_ids').optional().toArray().isArray({ min: 1 }),
|
||||
query('branches_ids.*').isNumeric().toInt(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve AR aging summary report.
|
||||
*/
|
||||
async receivableAgingSummary(req: Request, res: Response) {
|
||||
const { tenantId, settings } = req;
|
||||
const filter = this.matchedQueryData(req);
|
||||
|
||||
try {
|
||||
const accept = this.accepts(req);
|
||||
const acceptType = accept.types(['json', 'application/json+table']);
|
||||
|
||||
switch (acceptType) {
|
||||
case 'application/json+table':
|
||||
const table = await this.ARAgingSummaryService.ARAgingSummaryTable(
|
||||
tenantId,
|
||||
filter
|
||||
);
|
||||
return res.status(200).send({
|
||||
table: {
|
||||
rows: table.rows,
|
||||
columns: table.columns,
|
||||
},
|
||||
meta: table.meta,
|
||||
query: table.query,
|
||||
});
|
||||
break;
|
||||
default:
|
||||
const { data, columns, query, meta } =
|
||||
await this.ARAgingSummaryService.ARAgingSummary(tenantId, filter);
|
||||
|
||||
return res.status(200).send({
|
||||
data: this.transfromToResponse(data),
|
||||
columns: this.transfromToResponse(columns),
|
||||
query: this.transfromToResponse(query),
|
||||
meta: this.transfromToResponse(meta),
|
||||
});
|
||||
break;
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,126 +0,0 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { Router, Request, Response, NextFunction } from 'express';
|
||||
import { query, ValidationChain } from 'express-validator';
|
||||
import { castArray } from 'lodash';
|
||||
import asyncMiddleware from '@/api/middleware/asyncMiddleware';
|
||||
import BalanceSheetStatementService from '@/services/FinancialStatements/BalanceSheet/BalanceSheetService';
|
||||
import BaseFinancialReportController from './BaseFinancialReportController';
|
||||
import { AbilitySubject, ReportsAction } from '@/interfaces';
|
||||
import CheckPolicies from '@/api/middleware/CheckPolicies';
|
||||
import BalanceSheetTable from '@/services/FinancialStatements/BalanceSheet/BalanceSheetTable';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
|
||||
@Service()
|
||||
export default class BalanceSheetStatementController extends BaseFinancialReportController {
|
||||
@Inject()
|
||||
balanceSheetService: BalanceSheetStatementService;
|
||||
|
||||
@Inject()
|
||||
tenancy: HasTenancyService;
|
||||
|
||||
/**
|
||||
* Router constructor.
|
||||
*/
|
||||
router() {
|
||||
const router = Router();
|
||||
|
||||
router.get(
|
||||
'/',
|
||||
CheckPolicies(ReportsAction.READ_BALANCE_SHEET, AbilitySubject.Report),
|
||||
this.balanceSheetValidationSchema,
|
||||
this.validationResult,
|
||||
asyncMiddleware(this.balanceSheet.bind(this))
|
||||
);
|
||||
return router;
|
||||
}
|
||||
|
||||
/**
|
||||
* Balance sheet validation schecma.
|
||||
* @returns {ValidationChain[]}
|
||||
*/
|
||||
get balanceSheetValidationSchema(): ValidationChain[] {
|
||||
return [
|
||||
...this.sheetNumberFormatValidationSchema,
|
||||
query('accounting_method').optional().isIn(['cash', 'accrual']),
|
||||
|
||||
query('from_date').optional(),
|
||||
query('to_date').optional(),
|
||||
|
||||
query('display_columns_type').optional().isIn(['date_periods', 'total']),
|
||||
query('display_columns_by')
|
||||
.optional({ nullable: true, checkFalsy: true })
|
||||
.isIn(['year', 'month', 'week', 'day', 'quarter']),
|
||||
|
||||
query('account_ids').isArray().optional(),
|
||||
query('account_ids.*').isNumeric().toInt(),
|
||||
|
||||
query('none_zero').optional().isBoolean().toBoolean(),
|
||||
query('none_transactions').optional().isBoolean().toBoolean(),
|
||||
|
||||
// Percentage of column/row.
|
||||
query('percentage_of_column').optional().isBoolean().toBoolean(),
|
||||
query('percentage_of_row').optional().isBoolean().toBoolean(),
|
||||
|
||||
// Camparsion periods periods.
|
||||
query('previous_period').optional().isBoolean().toBoolean(),
|
||||
query('previous_period_amount_change').optional().isBoolean().toBoolean(),
|
||||
query('previous_period_percentage_change')
|
||||
.optional()
|
||||
.isBoolean()
|
||||
.toBoolean(),
|
||||
// Camparsion periods periods.
|
||||
query('previous_year').optional().isBoolean().toBoolean(),
|
||||
query('previous_year_amount_change').optional().isBoolean().toBoolean(),
|
||||
query('previous_year_percentage_change')
|
||||
.optional()
|
||||
.isBoolean()
|
||||
.toBoolean(),
|
||||
|
||||
// Filtering by branches.
|
||||
query('branches_ids').optional().toArray().isArray({ min: 1 }),
|
||||
query('branches_ids.*').isNumeric().toInt(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the balance sheet.
|
||||
*/
|
||||
async balanceSheet(req: Request, res: Response, next: NextFunction) {
|
||||
const { tenantId, settings } = req;
|
||||
const i18n = this.tenancy.i18n(tenantId);
|
||||
|
||||
let filter = this.matchedQueryData(req);
|
||||
|
||||
filter = {
|
||||
...filter,
|
||||
accountsIds: castArray(filter.accountsIds),
|
||||
};
|
||||
|
||||
try {
|
||||
const { data, columns, query, meta } =
|
||||
await this.balanceSheetService.balanceSheet(tenantId, filter);
|
||||
|
||||
const accept = this.accepts(req);
|
||||
const acceptType = accept.types(['json', 'application/json+table']);
|
||||
|
||||
const table = new BalanceSheetTable(data, query, i18n);
|
||||
|
||||
switch (acceptType) {
|
||||
case 'application/json+table':
|
||||
return res.status(200).send({
|
||||
table: {
|
||||
rows: table.tableRows(),
|
||||
columns: table.tableColumns(),
|
||||
},
|
||||
query,
|
||||
meta,
|
||||
});
|
||||
case 'json':
|
||||
default:
|
||||
return res.status(200).send({ data, columns, query, meta });
|
||||
}
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
import { query } from 'express-validator';
|
||||
import BaseController from "../BaseController";
|
||||
|
||||
export default class BaseFinancialReportController extends BaseController {
|
||||
|
||||
|
||||
get sheetNumberFormatValidationSchema() {
|
||||
return [
|
||||
query('number_format.precision')
|
||||
.optional()
|
||||
.isInt({ min: 0, max: 5 })
|
||||
.toInt(),
|
||||
query('number_format.divide_on_1000').optional().isBoolean().toBoolean(),
|
||||
query('number_format.show_zero').optional().isBoolean().toBoolean(),
|
||||
query('number_format.format_money')
|
||||
.optional()
|
||||
.isIn(['total', 'always', 'none'])
|
||||
.trim(),
|
||||
query('number_format.negative_format')
|
||||
.optional()
|
||||
.isIn(['parentheses', 'mines'])
|
||||
.trim()
|
||||
.escape(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -1,137 +0,0 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { query } from 'express-validator';
|
||||
import {
|
||||
NextFunction,
|
||||
Router,
|
||||
Request,
|
||||
Response,
|
||||
ValidationChain,
|
||||
} from 'express';
|
||||
import BaseFinancialReportController from '../BaseFinancialReportController';
|
||||
import CashFlowStatementService from '@/services/FinancialStatements/CashFlow/CashFlowService';
|
||||
import {
|
||||
ICashFlowStatementDOO,
|
||||
ICashFlowStatement,
|
||||
AbilitySubject,
|
||||
ReportsAction,
|
||||
} from '@/interfaces';
|
||||
import CashFlowTable from '@/services/FinancialStatements/CashFlow/CashFlowTable';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
import CheckPolicies from '@/api/middleware/CheckPolicies';
|
||||
|
||||
@Service()
|
||||
export default class CashFlowController extends BaseFinancialReportController {
|
||||
@Inject()
|
||||
cashFlowService: CashFlowStatementService;
|
||||
|
||||
@Inject()
|
||||
tenancy: HasTenancyService;
|
||||
|
||||
/**
|
||||
* Router constructor.
|
||||
*/
|
||||
router() {
|
||||
const router = Router();
|
||||
|
||||
router.get(
|
||||
'/',
|
||||
CheckPolicies(ReportsAction.READ_CASHFLOW, AbilitySubject.Report),
|
||||
this.cashflowValidationSchema,
|
||||
this.validationResult,
|
||||
this.asyncMiddleware(this.cashFlow.bind(this))
|
||||
);
|
||||
return router;
|
||||
}
|
||||
|
||||
/**
|
||||
* Balance sheet validation schecma.
|
||||
* @returns {ValidationChain[]}
|
||||
*/
|
||||
get cashflowValidationSchema(): ValidationChain[] {
|
||||
return [
|
||||
...this.sheetNumberFormatValidationSchema,
|
||||
query('from_date').optional(),
|
||||
query('to_date').optional(),
|
||||
|
||||
query('display_columns_type').optional().isIn(['date_periods', 'total']),
|
||||
query('display_columns_by')
|
||||
.optional({ nullable: true, checkFalsy: true })
|
||||
.isIn(['year', 'month', 'week', 'day', 'quarter']),
|
||||
|
||||
query('none_zero').optional().isBoolean().toBoolean(),
|
||||
query('none_transactions').optional().isBoolean().toBoolean(),
|
||||
|
||||
// Filtering by branches.
|
||||
query('branches_ids').optional().toArray().isArray({ min: 1 }),
|
||||
query('branches_ids.*').isNumeric().toInt(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the cashflow statment to json response.
|
||||
* @param {ICashFlowStatement} cashFlow -
|
||||
*/
|
||||
private transformJsonResponse(cashFlowDOO: ICashFlowStatementDOO) {
|
||||
const { data, query, meta } = cashFlowDOO;
|
||||
|
||||
return {
|
||||
data: this.transfromToResponse(data),
|
||||
query: this.transfromToResponse(query),
|
||||
meta: this.transfromToResponse(meta),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Transformes the report statement to table rows.
|
||||
* @param {ITransactionsByVendorsStatement} statement -
|
||||
*/
|
||||
private transformToTableRows(
|
||||
cashFlowDOO: ICashFlowStatementDOO,
|
||||
tenantId: number
|
||||
) {
|
||||
const i18n = this.tenancy.i18n(tenantId);
|
||||
const cashFlowTable = new CashFlowTable(cashFlowDOO, i18n);
|
||||
|
||||
return {
|
||||
table: {
|
||||
data: cashFlowTable.tableRows(),
|
||||
columns: cashFlowTable.tableColumns(),
|
||||
},
|
||||
query: this.transfromToResponse(cashFlowDOO.query),
|
||||
meta: this.transfromToResponse(cashFlowDOO.meta),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the cash flow statment.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
* @returns {Response}
|
||||
*/
|
||||
async cashFlow(req: Request, res: Response, next: NextFunction) {
|
||||
const { tenantId, settings } = req;
|
||||
const filter = {
|
||||
...this.matchedQueryData(req),
|
||||
};
|
||||
|
||||
try {
|
||||
const cashFlow = await this.cashFlowService.cashFlow(tenantId, filter);
|
||||
|
||||
const accept = this.accepts(req);
|
||||
const acceptType = accept.types(['json', 'application/json+table']);
|
||||
|
||||
switch (acceptType) {
|
||||
case 'application/json+table':
|
||||
return res
|
||||
.status(200)
|
||||
.send(this.transformToTableRows(cashFlow, tenantId));
|
||||
case 'json':
|
||||
default:
|
||||
return res.status(200).send(this.transformJsonResponse(cashFlow));
|
||||
}
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,168 +0,0 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { query } from 'express-validator';
|
||||
import {
|
||||
NextFunction,
|
||||
Router,
|
||||
Request,
|
||||
Response,
|
||||
ValidationChain,
|
||||
} from 'express';
|
||||
import BaseFinancialReportController from '../BaseFinancialReportController';
|
||||
import {
|
||||
AbilitySubject,
|
||||
ICashFlowStatementDOO,
|
||||
ReportsAction,
|
||||
} from '@/interfaces';
|
||||
import CashFlowTable from '@/services/FinancialStatements/CashFlow/CashFlowTable';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
import CashflowAccountTransactionsService from '@/services/FinancialStatements/CashflowAccountTransactions/CashflowAccountTransactionsService';
|
||||
import { ServiceError } from '@/exceptions';
|
||||
import CheckPolicies from '@/api/middleware/CheckPolicies';
|
||||
|
||||
@Service()
|
||||
export default class CashFlowAccountTransactionsController extends BaseFinancialReportController {
|
||||
@Inject()
|
||||
tenancy: HasTenancyService;
|
||||
|
||||
@Inject()
|
||||
cashflowAccountTransactions: CashflowAccountTransactionsService;
|
||||
|
||||
/**
|
||||
* Router constructor.
|
||||
*/
|
||||
router() {
|
||||
const router = Router();
|
||||
|
||||
router.get(
|
||||
'/',
|
||||
CheckPolicies(
|
||||
ReportsAction.READ_CASHFLOW_ACCOUNT_TRANSACTION,
|
||||
AbilitySubject.Report
|
||||
),
|
||||
this.validationSchema,
|
||||
this.validationResult,
|
||||
this.asyncMiddleware(this.cashFlow),
|
||||
this.catchServiceErrors
|
||||
);
|
||||
return router;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cashflow account transactions validation schecma.
|
||||
* @returns {ValidationChain[]}
|
||||
*/
|
||||
get validationSchema(): ValidationChain[] {
|
||||
return [
|
||||
query('account_id').exists().isInt().toInt(),
|
||||
|
||||
query('page').optional().isNumeric().toInt(),
|
||||
query('page_size').optional().isNumeric().toInt(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the cashflow account transactions statment to json response.
|
||||
* @param {ICashFlowStatement} cashFlow -
|
||||
*/
|
||||
private transformJsonResponse(casahflowAccountTransactions) {
|
||||
const { transactions, pagination } = casahflowAccountTransactions;
|
||||
|
||||
return {
|
||||
transactions: this.transfromToResponse(transactions),
|
||||
pagination: this.transfromToResponse(pagination),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Transformes the report statement to table rows.
|
||||
* @param {ITransactionsByVendorsStatement} statement -
|
||||
*/
|
||||
private transformToTableRows(
|
||||
cashFlowDOO: ICashFlowStatementDOO,
|
||||
tenantId: number
|
||||
) {
|
||||
const i18n = this.tenancy.i18n(tenantId);
|
||||
const cashFlowTable = new CashFlowTable(cashFlowDOO, i18n);
|
||||
|
||||
return {
|
||||
table: {
|
||||
data: cashFlowTable.tableRows(),
|
||||
columns: cashFlowTable.tableColumns(),
|
||||
},
|
||||
query: this.transfromToResponse(cashFlowDOO.query),
|
||||
meta: this.transfromToResponse(cashFlowDOO.meta),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the cash flow statment.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
* @returns {Response}
|
||||
*/
|
||||
private cashFlow = async (
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
) => {
|
||||
const { tenantId } = req;
|
||||
const query = this.matchedQueryData(req);
|
||||
|
||||
try {
|
||||
const cashFlowAccountTransactions =
|
||||
await this.cashflowAccountTransactions.cashflowAccountTransactions(
|
||||
tenantId,
|
||||
query
|
||||
);
|
||||
|
||||
const accept = this.accepts(req);
|
||||
const acceptType = accept.types(['json', 'application/json+table']);
|
||||
|
||||
switch (acceptType) {
|
||||
// case 'application/json+table':
|
||||
// return res
|
||||
// .status(200)
|
||||
// .send(this.transformToTableRows(cashFlow, tenantId));
|
||||
case 'json':
|
||||
default:
|
||||
return res
|
||||
.status(200)
|
||||
.send(this.transformJsonResponse(cashFlowAccountTransactions));
|
||||
}
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Catches the service errors.
|
||||
* @param {Error} error - Error.
|
||||
* @param {Request} req - Request.
|
||||
* @param {Response} res - Response.
|
||||
* @param {NextFunction} next -
|
||||
*/
|
||||
private catchServiceErrors(
|
||||
error,
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
) {
|
||||
if (error instanceof ServiceError) {
|
||||
if (error.errorType === 'ACCOUNT_ID_HAS_INVALID_TYPE') {
|
||||
return res.boom.badRequest(
|
||||
'The given account id should be cash, bank or credit card type.',
|
||||
{
|
||||
errors: [{ type: 'ACCOUNT_ID_HAS_INVALID_TYPE', code: 200 }],
|
||||
}
|
||||
);
|
||||
}
|
||||
if (error.errorType === 'account_not_found') {
|
||||
return res.boom.notFound('The given account not found.', {
|
||||
errors: [{ type: 'ACCOUNT.NOT.FOUND', code: 100 }],
|
||||
});
|
||||
}
|
||||
}
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
@@ -1,139 +0,0 @@
|
||||
import { Router, Request, Response, NextFunction } from 'express';
|
||||
import { query } from 'express-validator';
|
||||
import { Inject } from 'typedi';
|
||||
import {
|
||||
AbilitySubject,
|
||||
ICustomerBalanceSummaryStatement,
|
||||
ReportsAction,
|
||||
} from '@/interfaces';
|
||||
import asyncMiddleware from '@/api/middleware/asyncMiddleware';
|
||||
import CustomerBalanceSummary from '@/services/FinancialStatements/CustomerBalanceSummary/CustomerBalanceSummaryService';
|
||||
import BaseFinancialReportController from '../BaseFinancialReportController';
|
||||
import CustomerBalanceSummaryTableRows from '@/services/FinancialStatements/CustomerBalanceSummary/CustomerBalanceSummaryTableRows';
|
||||
import CheckPolicies from '@/api/middleware/CheckPolicies';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
|
||||
export default class CustomerBalanceSummaryReportController extends BaseFinancialReportController {
|
||||
@Inject()
|
||||
customerBalanceSummaryService: CustomerBalanceSummary;
|
||||
|
||||
@Inject()
|
||||
tenancy: HasTenancyService;
|
||||
|
||||
/**
|
||||
* Router constructor.
|
||||
*/
|
||||
router() {
|
||||
const router = Router();
|
||||
|
||||
router.get(
|
||||
'/',
|
||||
CheckPolicies(
|
||||
ReportsAction.READ_CUSTOMERS_SUMMARY_BALANCE,
|
||||
AbilitySubject.Report
|
||||
),
|
||||
this.validationSchema,
|
||||
this.validationResult,
|
||||
asyncMiddleware(this.customerBalanceSummary.bind(this))
|
||||
);
|
||||
return router;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validation schema.
|
||||
*/
|
||||
get validationSchema() {
|
||||
return [
|
||||
...this.sheetNumberFormatValidationSchema,
|
||||
|
||||
// As date.
|
||||
query('as_date').optional().isISO8601(),
|
||||
|
||||
// Percentages.
|
||||
query('percentage_column').optional().isBoolean().toBoolean(),
|
||||
|
||||
// Filters none-zero or none-transactions.
|
||||
query('none_zero').optional().isBoolean().toBoolean(),
|
||||
query('none_transactions').optional().isBoolean().toBoolean(),
|
||||
|
||||
// Customers ids.
|
||||
query('customers_ids').optional().isArray({ min: 1 }),
|
||||
query('customers_ids.*').exists().isInt().toInt(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Transformes the balance summary statement to table rows.
|
||||
* @param {ICustomerBalanceSummaryStatement} statement -
|
||||
*/
|
||||
private transformToTableRows(
|
||||
tenantId,
|
||||
{ data, query }: ICustomerBalanceSummaryStatement
|
||||
) {
|
||||
const i18n = this.tenancy.i18n(tenantId);
|
||||
const tableRows = new CustomerBalanceSummaryTableRows(data, query, i18n);
|
||||
|
||||
return {
|
||||
table: {
|
||||
columns: tableRows.tableColumns(),
|
||||
data: tableRows.tableRows(),
|
||||
},
|
||||
query: this.transfromToResponse(query),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Transformes the balance summary statement to raw json.
|
||||
* @param {ICustomerBalanceSummaryStatement} customerBalance -
|
||||
*/
|
||||
private transformToJsonResponse({
|
||||
data,
|
||||
columns,
|
||||
query,
|
||||
}: ICustomerBalanceSummaryStatement) {
|
||||
return {
|
||||
data: this.transfromToResponse(data),
|
||||
columns: this.transfromToResponse(columns),
|
||||
query: this.transfromToResponse(query),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve payable aging summary report.
|
||||
* @param {Request} req -
|
||||
* @param {Response} res -
|
||||
* @param {NextFunction} next -
|
||||
*/
|
||||
async customerBalanceSummary(
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
) {
|
||||
const { tenantId, settings } = req;
|
||||
const filter = this.matchedQueryData(req);
|
||||
|
||||
try {
|
||||
const customerBalanceSummary =
|
||||
await this.customerBalanceSummaryService.customerBalanceSummary(
|
||||
tenantId,
|
||||
filter
|
||||
);
|
||||
const accept = this.accepts(req);
|
||||
const acceptType = accept.types(['json', 'application/json+table']);
|
||||
|
||||
switch (acceptType) {
|
||||
case 'application/json+table':
|
||||
return res
|
||||
.status(200)
|
||||
.send(this.transformToTableRows(tenantId, customerBalanceSummary));
|
||||
case 'application/json':
|
||||
default:
|
||||
return res
|
||||
.status(200)
|
||||
.send(this.transformToJsonResponse(customerBalanceSummary));
|
||||
}
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
import { Router, Request, Response, NextFunction } from 'express';
|
||||
import { query, ValidationChain } from 'express-validator';
|
||||
import { Inject, Service } from 'typedi';
|
||||
import asyncMiddleware from '@/api/middleware/asyncMiddleware';
|
||||
import GeneralLedgerService from '@/services/FinancialStatements/GeneralLedger/GeneralLedgerService';
|
||||
import BaseFinancialReportController from './BaseFinancialReportController';
|
||||
import { AbilitySubject, ReportsAction } from '@/interfaces';
|
||||
import CheckPolicies from '@/api/middleware/CheckPolicies';
|
||||
|
||||
@Service()
|
||||
export default class GeneralLedgerReportController extends BaseFinancialReportController {
|
||||
@Inject()
|
||||
generalLedgetService: GeneralLedgerService;
|
||||
|
||||
/**
|
||||
* Router constructor.
|
||||
*/
|
||||
router() {
|
||||
const router = Router();
|
||||
|
||||
router.get(
|
||||
'/',
|
||||
CheckPolicies(ReportsAction.READ_GENERAL_LEDGET, AbilitySubject.Report),
|
||||
this.validationSchema,
|
||||
this.validationResult,
|
||||
asyncMiddleware(this.generalLedger.bind(this))
|
||||
);
|
||||
return router;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validation schema.
|
||||
*/
|
||||
get validationSchema(): ValidationChain[] {
|
||||
return [
|
||||
query('from_date').optional().isISO8601(),
|
||||
query('to_date').optional().isISO8601(),
|
||||
|
||||
query('basis').optional(),
|
||||
|
||||
query('number_format.no_cents').optional().isBoolean().toBoolean(),
|
||||
query('number_format.divide_1000').optional().isBoolean().toBoolean(),
|
||||
|
||||
query('none_transactions').default(true).isBoolean().toBoolean(),
|
||||
|
||||
query('accounts_ids').optional().isArray({ min: 1 }),
|
||||
query('accounts_ids.*').isInt().toInt(),
|
||||
|
||||
query('orderBy').optional().isIn(['created_at', 'name', 'code']),
|
||||
query('order').optional().isIn(['desc', 'asc']),
|
||||
|
||||
// Filtering by branches.
|
||||
query('branches_ids').optional().toArray().isArray({ min: 1 }),
|
||||
query('branches_ids.*').isNumeric().toInt(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the general ledger financial statement.
|
||||
* @param {Request} req -
|
||||
* @param {Response} res -
|
||||
*/
|
||||
async generalLedger(req: Request, res: Response, next: NextFunction) {
|
||||
const { tenantId, settings } = req;
|
||||
const filter = this.matchedQueryData(req);
|
||||
|
||||
try {
|
||||
const { data, query, meta } =
|
||||
await this.generalLedgetService.generalLedger(tenantId, filter);
|
||||
|
||||
return res.status(200).send({
|
||||
meta: this.transfromToResponse(meta),
|
||||
data: this.transfromToResponse(data),
|
||||
query: this.transfromToResponse(query),
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,148 +0,0 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { query } from 'express-validator';
|
||||
import {
|
||||
NextFunction,
|
||||
Router,
|
||||
Request,
|
||||
Response,
|
||||
ValidationChain,
|
||||
} from 'express';
|
||||
import BaseController from '@/api/controllers/BaseController';
|
||||
import InventoryDetailsService from '@/services/FinancialStatements/InventoryDetails/InventoryDetailsService';
|
||||
import InventoryDetailsTable from '@/services/FinancialStatements/InventoryDetails/InventoryDetailsTable';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
import { AbilitySubject, ReportsAction } from '@/interfaces';
|
||||
import CheckPolicies from '@/api/middleware/CheckPolicies';
|
||||
|
||||
@Service()
|
||||
export default class InventoryDetailsController extends BaseController {
|
||||
@Inject()
|
||||
inventoryDetailsService: InventoryDetailsService;
|
||||
|
||||
@Inject()
|
||||
tenancy: HasTenancyService;
|
||||
|
||||
/**
|
||||
* Router constructor.
|
||||
*/
|
||||
router() {
|
||||
const router = Router();
|
||||
|
||||
router.get(
|
||||
'/',
|
||||
CheckPolicies(
|
||||
ReportsAction.READ_INVENTORY_ITEM_DETAILS,
|
||||
AbilitySubject.Report
|
||||
),
|
||||
this.validationSchema,
|
||||
this.validationResult,
|
||||
this.asyncMiddleware(this.inventoryDetails.bind(this))
|
||||
);
|
||||
return router;
|
||||
}
|
||||
|
||||
/**
|
||||
* Balance sheet validation schecma.
|
||||
* @returns {ValidationChain[]}
|
||||
*/
|
||||
get validationSchema(): ValidationChain[] {
|
||||
return [
|
||||
query('number_format.precision')
|
||||
.optional()
|
||||
.isInt({ min: 0, max: 5 })
|
||||
.toInt(),
|
||||
query('number_format.divide_on_1000').optional().isBoolean().toBoolean(),
|
||||
query('number_format.negative_format')
|
||||
.optional()
|
||||
.isIn(['parentheses', 'mines'])
|
||||
.trim()
|
||||
.escape(),
|
||||
query('from_date').optional(),
|
||||
query('to_date').optional(),
|
||||
|
||||
query('none_zero').optional().isBoolean().toBoolean(),
|
||||
query('none_transactions').optional().isBoolean().toBoolean(),
|
||||
|
||||
query('items_ids').optional().isArray(),
|
||||
query('items_ids.*').optional().isInt().toInt(),
|
||||
|
||||
// Filtering by branches.
|
||||
query('branches_ids').optional().toArray().isArray({ min: 1 }),
|
||||
query('branches_ids.*').isNumeric().toInt(),
|
||||
|
||||
// Filtering by warehouses.
|
||||
query('warehouses_ids').optional().toArray().isArray({ min: 1 }),
|
||||
query('warehouses_ids.*').isNumeric().toInt(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the cashflow statment to json response.
|
||||
* @param {ICashFlowStatement} cashFlow -
|
||||
*/
|
||||
private transformJsonResponse(inventoryDetails) {
|
||||
const { data, query, meta } = inventoryDetails;
|
||||
|
||||
return {
|
||||
data: this.transfromToResponse(data),
|
||||
query: this.transfromToResponse(query),
|
||||
meta: this.transfromToResponse(meta),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Transformes the report statement to table rows.
|
||||
*/
|
||||
private transformToTableRows(inventoryDetails, tenantId: number) {
|
||||
const i18n = this.tenancy.i18n(tenantId);
|
||||
const inventoryDetailsTable = new InventoryDetailsTable(
|
||||
inventoryDetails,
|
||||
i18n
|
||||
);
|
||||
|
||||
return {
|
||||
table: {
|
||||
data: inventoryDetailsTable.tableData(),
|
||||
columns: inventoryDetailsTable.tableColumns(),
|
||||
},
|
||||
query: this.transfromToResponse(inventoryDetails.query),
|
||||
meta: this.transfromToResponse(inventoryDetails.meta),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the cash flow statment.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
* @returns {Response}
|
||||
*/
|
||||
async inventoryDetails(req: Request, res: Response, next: NextFunction) {
|
||||
const { tenantId, settings } = req;
|
||||
const filter = {
|
||||
...this.matchedQueryData(req),
|
||||
};
|
||||
|
||||
try {
|
||||
const inventoryDetails =
|
||||
await this.inventoryDetailsService.inventoryDetails(tenantId, filter);
|
||||
|
||||
const accept = this.accepts(req);
|
||||
const acceptType = accept.types(['json', 'application/json+table']);
|
||||
|
||||
switch (acceptType) {
|
||||
case 'application/json+table':
|
||||
return res
|
||||
.status(200)
|
||||
.send(this.transformToTableRows(inventoryDetails, tenantId));
|
||||
case 'json':
|
||||
default:
|
||||
return res
|
||||
.status(200)
|
||||
.send(this.transformJsonResponse(inventoryDetails));
|
||||
}
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user