Compare commits
979 Commits
release/re
...
feature/ne
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d887a89988 | ||
|
|
cb02319453 | ||
|
|
7f5f65f07f | ||
|
|
c45200aaee | ||
|
|
d2351a67b0 | ||
|
|
52873021cb | ||
|
|
3f276f4177 | ||
|
|
f01a300aae | ||
|
|
8e11ef6e72 | ||
|
|
52c0df9b83 | ||
|
|
33b9c48e87 | ||
|
|
aa2f562ca8 | ||
|
|
77ce078e91 | ||
|
|
5450c9996f | ||
|
|
70f8aadd66 | ||
|
|
119083ac2b | ||
|
|
dd768e938c | ||
|
|
611aa0fe05 | ||
|
|
73564d574c | ||
|
|
ea5231adff | ||
|
|
2b3da48e9b | ||
|
|
2632c4a5a0 | ||
|
|
2420987f95 | ||
|
|
d80f3c4d5c | ||
|
|
5cb726bb2d | ||
|
|
f6b8fa5665 | ||
|
|
113d0abb4b | ||
|
|
3f3c494f7f | ||
|
|
3632a3f98f | ||
|
|
73402d1c5a | ||
|
|
4e41efd423 | ||
|
|
0b931f8255 | ||
|
|
b49135f5f9 | ||
|
|
ed6d28cc21 | ||
|
|
9857787d5a | ||
|
|
c6e62f65b6 | ||
|
|
6c0b034147 | ||
|
|
c0d520a47f | ||
|
|
de11c57800 | ||
|
|
d4ff4be32e | ||
|
|
48371b01cd | ||
|
|
310d8b33df | ||
|
|
6d61e45028 | ||
|
|
fff22a74b1 | ||
|
|
3588288434 | ||
|
|
7408cc00f9 | ||
|
|
9b3d1170b0 | ||
|
|
a774c74e6a | ||
|
|
1d53f47488 | ||
|
|
60cc49238c | ||
|
|
b415278997 | ||
|
|
ac32494944 | ||
|
|
8c81315c33 | ||
|
|
1d66c1dbea | ||
|
|
085d26669f | ||
|
|
bb7b72b540 | ||
|
|
ded0d06f86 | ||
|
|
fe08ee4173 | ||
|
|
25ee849796 | ||
|
|
bafcf35ac9 | ||
|
|
cc1455b22c | ||
|
|
1d38334c81 | ||
|
|
13d99b1bcc | ||
|
|
269be36d86 | ||
|
|
ebd76afb29 | ||
|
|
a1807e05db | ||
|
|
11866629c5 | ||
|
|
f8f5286568 | ||
|
|
36908f13d3 | ||
|
|
a7ed317b09 | ||
|
|
fe4346b802 | ||
|
|
23da273af1 | ||
|
|
68fdea4520 | ||
|
|
c5ac62060d | ||
|
|
350cf0a74b | ||
|
|
e204d693cf | ||
|
|
a6422330b6 | ||
|
|
230e87a9f1 | ||
|
|
e634e9726f | ||
|
|
e8d07abe05 | ||
|
|
808bbd7b22 | ||
|
|
fabcca1188 | ||
|
|
97c8b1dcc1 | ||
|
|
f99a35932f | ||
|
|
ce7dfdbd36 | ||
|
|
d53f3837c3 | ||
|
|
1cb646ec48 | ||
|
|
c73706555f | ||
|
|
21f961d893 | ||
|
|
08a682143f | ||
|
|
ebdeb95b0e | ||
|
|
0f26cac68d | ||
|
|
45c96276ff | ||
|
|
a2855f1f08 | ||
|
|
e8c006d495 | ||
|
|
9671efeced | ||
|
|
1e3fd6bc09 | ||
|
|
73aa4af118 | ||
|
|
38a8a298b5 | ||
|
|
bc0b8bdecb | ||
|
|
a8e9fe40ac | ||
|
|
5c5606749a | ||
|
|
8add4b5ad1 | ||
|
|
f579c8928c | ||
|
|
9410ca82db | ||
|
|
73c986cd50 | ||
|
|
f8a9a38778 | ||
|
|
9d723c5e00 | ||
|
|
722a044707 | ||
|
|
967effe8f1 | ||
|
|
5f5877699c | ||
|
|
4787cb451d | ||
|
|
811a79c7c1 | ||
|
|
153f599fe8 | ||
|
|
f6f9d14f0a | ||
|
|
018d51e25f | ||
|
|
ac03192119 | ||
|
|
b8d5d655ea | ||
|
|
00f986cdb5 | ||
|
|
5dc5c814c8 | ||
|
|
c64ede58fd | ||
|
|
6e17bb933f | ||
|
|
99aa161834 | ||
|
|
ed641c85b3 | ||
|
|
cf7756f51e | ||
|
|
a75269b3a3 | ||
|
|
6b184d7df3 | ||
|
|
daba7d5155 | ||
|
|
33a1837274 | ||
|
|
6b3e3aa49c | ||
|
|
a7b5fc898e | ||
|
|
9a9bfd198b | ||
|
|
2446065172 | ||
|
|
7565094077 | ||
|
|
ac2708404a | ||
|
|
63b9f495e9 | ||
|
|
012bb9fd54 | ||
|
|
7bcb54861b | ||
|
|
2f64a3a612 | ||
|
|
5365211f49 | ||
|
|
e38407c0a5 | ||
|
|
13ef106c0e | ||
|
|
433f3bd2dc | ||
|
|
ff072a32bb | ||
|
|
de7098a7d4 | ||
|
|
205bfd0538 | ||
|
|
37538141bf | ||
|
|
975ced08cb | ||
|
|
d646019c93 | ||
|
|
efa1e2b60f | ||
|
|
8bbc65ef88 | ||
|
|
e1121cc3b5 | ||
|
|
026bf17a68 | ||
|
|
fdcd825142 | ||
|
|
317f44d6c2 | ||
|
|
7d5c7bceb1 | ||
|
|
359fd40ed3 | ||
|
|
13a930b59c | ||
|
|
359a246793 | ||
|
|
2b658d7be0 | ||
|
|
cd9f485fc0 | ||
|
|
dda627f20a | ||
|
|
e8858559f6 | ||
|
|
d68a9249c6 | ||
|
|
eebb143778 | ||
|
|
a12eb12678 | ||
|
|
f5f67c52af | ||
|
|
3df2306e1b | ||
|
|
5dae0ea973 | ||
|
|
e7b18233f0 | ||
|
|
e55e42fe6b | ||
|
|
741f2ad8b2 | ||
|
|
1b87a81a5a | ||
|
|
7042eb0d7b | ||
|
|
28788e2a81 | ||
|
|
0f37b45b30 | ||
|
|
38c4aba45c | ||
|
|
3b686a8272 | ||
|
|
2798ce6183 | ||
|
|
e981d68805 | ||
|
|
cd3ab04728 | ||
|
|
d419efa3c3 | ||
|
|
8b4eb325ef | ||
|
|
330726007d | ||
|
|
4d7b1f557b | ||
|
|
1699f61d3f | ||
|
|
a0eec00749 | ||
|
|
f5365b4a3a | ||
|
|
8b9aa1c454 | ||
|
|
9a4d7e87ec | ||
|
|
6679c200e1 | ||
|
|
01df55ef35 | ||
|
|
d2b028ed4e | ||
|
|
de1b97bfb7 | ||
|
|
111c0f6dae | ||
|
|
11da893b17 | ||
|
|
b434329fc4 | ||
|
|
72998f04c6 | ||
|
|
23db164452 | ||
|
|
1f11d188c1 | ||
|
|
fa30e0c1da | ||
|
|
5106768330 | ||
|
|
6c0d9486fc | ||
|
|
211d74d9ff | ||
|
|
84db3a92be | ||
|
|
487a2936c3 | ||
|
|
aa9bdfe372 | ||
|
|
6134ac3b4c | ||
|
|
85c24e3d94 | ||
|
|
106945fba7 | ||
|
|
8ea72198ea | ||
|
|
41cd76ad14 | ||
|
|
e20510bc4f | ||
|
|
1d14ab27af | ||
|
|
7d6699fe7a | ||
|
|
fd55abec72 | ||
|
|
76597f6ccb | ||
|
|
d7779e969d | ||
|
|
9d28eccd1b | ||
|
|
27339afaae | ||
|
|
8dc3f2698b | ||
|
|
4fd7ad8d03 | ||
|
|
3bc33fe2fc | ||
|
|
c21319a127 | ||
|
|
02e21a8fde | ||
|
|
2cf578de65 | ||
|
|
9665cdfc84 | ||
|
|
bef7376827 | ||
|
|
e73e9d6960 | ||
|
|
892f5d0e70 | ||
|
|
61339da204 | ||
|
|
25db392864 | ||
|
|
745fd429c2 | ||
|
|
5c59a27a81 | ||
|
|
9bac3675f6 | ||
|
|
b4fbbd48fb | ||
|
|
3559d67c05 | ||
|
|
816623b1fe | ||
|
|
91b913a7bd | ||
|
|
ba8393a8c7 | ||
|
|
06d061165b | ||
|
|
18a7dbbff9 | ||
|
|
dc545e6d49 | ||
|
|
1467dbf88c | ||
|
|
e9caf78b81 | ||
|
|
aa1353cf99 | ||
|
|
14d49f2944 | ||
|
|
8560069b19 | ||
|
|
8dc23a638f | ||
|
|
537fbf7e14 | ||
|
|
5363557faf | ||
|
|
3e684a308c | ||
|
|
3c026004f3 | ||
|
|
651c8f266b | ||
|
|
a3bfbe66c4 | ||
|
|
563e40adc6 | ||
|
|
f88a784a87 | ||
|
|
7227e1e59c | ||
|
|
01df5e86cd | ||
|
|
41f853b6c9 | ||
|
|
906a0ea767 | ||
|
|
6162c731f2 | ||
|
|
4f1d518ef3 | ||
|
|
06eaebd098 | ||
|
|
8578a9a538 | ||
|
|
b9039ef466 | ||
|
|
a97f581c99 | ||
|
|
036179ec4d | ||
|
|
d33188dedf | ||
|
|
7033f95273 | ||
|
|
00689d76cc | ||
|
|
86d97ef331 | ||
|
|
05a183f46f | ||
|
|
50959d2e9c | ||
|
|
519ce0d5bb | ||
|
|
f06842cdcb | ||
|
|
d5a1caad10 | ||
|
|
e51fff4120 | ||
|
|
2fe8e0e356 | ||
|
|
bbe2e5792f | ||
|
|
a1a520ec39 | ||
|
|
1b714a9962 | ||
|
|
9d133096c7 | ||
|
|
69f3453134 | ||
|
|
ab10271b4d | ||
|
|
516a98e102 | ||
|
|
390213881a | ||
|
|
ac2eba4734 | ||
|
|
b64893436b | ||
|
|
ee148239d2 | ||
|
|
4cc03aee9b | ||
|
|
31a703952c | ||
|
|
69c63f708d | ||
|
|
ae08c5b56e | ||
|
|
dc76dcdc2f | ||
|
|
6a040d16ea | ||
|
|
30e8d04b70 | ||
|
|
2b3375ad41 | ||
|
|
8a059a5520 | ||
|
|
49a2f02b54 | ||
|
|
ef92f24d35 | ||
|
|
0207914592 | ||
|
|
5776c97744 | ||
|
|
180cdf2a08 | ||
|
|
47720948ff | ||
|
|
7814a2b622 | ||
|
|
06981fb894 | ||
|
|
19dbd9901d | ||
|
|
4985b0a85d | ||
|
|
39f99d6ce7 | ||
|
|
c8f25ea4b3 | ||
|
|
cab9e08066 | ||
|
|
2b83f82989 | ||
|
|
598e8fee9c | ||
|
|
6f3a788101 | ||
|
|
30ff349b37 | ||
|
|
e264306fed | ||
|
|
4018797384 | ||
|
|
f557cb8d10 | ||
|
|
a1efde6ef9 | ||
|
|
cf34e28e35 | ||
|
|
b7595fe4ec | ||
|
|
2551d82a99 | ||
|
|
0cedba8ea4 | ||
|
|
3f84972bfd | ||
|
|
d64a814251 | ||
|
|
86d5de4fa1 | ||
|
|
ed15958319 | ||
|
|
0bf9c752f6 | ||
|
|
e62eb060fb | ||
|
|
563b44adb1 | ||
|
|
a094946428 | ||
|
|
d28fd5d787 | ||
|
|
41c357d124 | ||
|
|
f94d52a4d9 | ||
|
|
74dc31b083 | ||
|
|
3b1fca529a | ||
|
|
843eeb8a89 | ||
|
|
12e81ec702 | ||
|
|
fe2af8a4ce | ||
|
|
f337a30aba | ||
|
|
7f3a8d877f | ||
|
|
5e6a7d0a39 | ||
|
|
5788a157c5 | ||
|
|
0b70447333 | ||
|
|
de8ba77bef | ||
|
|
1026c0edd0 | ||
|
|
f366b650d0 | ||
|
|
6054ce39f3 | ||
|
|
e1e9dc425b | ||
|
|
3a2a942f3b | ||
|
|
bf9d848f10 | ||
|
|
add7d404a0 | ||
|
|
7e6fa7b6c7 | ||
|
|
3fd17302f5 | ||
|
|
db9cee3e28 | ||
|
|
97a26dfa06 | ||
|
|
cebd895811 | ||
|
|
bb6c208d37 | ||
|
|
921f2c3503 | ||
|
|
1224e0e094 | ||
|
|
a571a870d3 | ||
|
|
a28d20b3bf | ||
|
|
d4e0d36166 | ||
|
|
39579aef10 | ||
|
|
1fe8fd6933 | ||
|
|
3b1caca62b | ||
|
|
3fe8c957be | ||
|
|
98923c1797 | ||
|
|
8661dd16c1 | ||
|
|
7157ea1587 | ||
|
|
a580a12d69 | ||
|
|
0d512e2e43 | ||
|
|
103069c554 | ||
|
|
f2482b5128 | ||
|
|
da38d24111 | ||
|
|
1f0ebe71c7 | ||
|
|
f897955f68 | ||
|
|
451e70b7c7 | ||
|
|
6c1c2ca0fc | ||
|
|
e7420365cf | ||
|
|
736a18518c | ||
|
|
41b287d559 | ||
|
|
5d8e0bda08 | ||
|
|
e9dbbaedf4 | ||
|
|
92ae6be3bd | ||
|
|
b00038aaed | ||
|
|
86c1880cdb | ||
|
|
cfd2dda4e6 | ||
|
|
c01e9a4d39 | ||
|
|
88ff4bbaa9 | ||
|
|
f62c70d354 | ||
|
|
22c9de86f6 | ||
|
|
b362f74336 | ||
|
|
81cfbfb540 | ||
|
|
668db4475f | ||
|
|
2af21bc4ee | ||
|
|
e6614a624f | ||
|
|
124bc8478e | ||
|
|
1a3a178ec6 | ||
|
|
cae5149fba | ||
|
|
5c4c036948 | ||
|
|
4003d6a652 | ||
|
|
0e8e81e4cb | ||
|
|
8cff3d8377 | ||
|
|
b132771c92 | ||
|
|
cbaf0d79c1 | ||
|
|
953d675dcf | ||
|
|
182eb83090 | ||
|
|
67a607b053 | ||
|
|
00a04af94f | ||
|
|
33d23c28b1 | ||
|
|
571fb29482 | ||
|
|
1dae20e055 | ||
|
|
6be9d44572 | ||
|
|
8a459e7e90 | ||
|
|
4c486e53aa | ||
|
|
19509a3ce2 | ||
|
|
4d424e90d3 | ||
|
|
9bbf613bcb | ||
|
|
cc40d479a9 | ||
|
|
f5ea0ec153 | ||
|
|
ea6c0f7d0b | ||
|
|
763009a3c0 | ||
|
|
cf34ac5bbc | ||
|
|
0faedd35d7 | ||
|
|
135b97d4f8 | ||
|
|
61f3aff768 | ||
|
|
9e369f0150 | ||
|
|
16d50f5bf2 | ||
|
|
80f0454996 | ||
|
|
9b9d811594 | ||
|
|
008ae20329 | ||
|
|
80b3b4b0f7 | ||
|
|
d64b480496 | ||
|
|
854ec8da4e | ||
|
|
d25b871854 | ||
|
|
37aafe7b30 | ||
|
|
26910f7b15 | ||
|
|
3ea2acd46e | ||
|
|
303462ba9c | ||
|
|
53fad50af6 | ||
|
|
1245323b9d | ||
|
|
4d8e86cff3 | ||
|
|
7bc7d2059c | ||
|
|
f9c209e85c | ||
|
|
6ae783387c | ||
|
|
7c0202afc6 | ||
|
|
9ad582612b | ||
|
|
d1db917432 | ||
|
|
847293385d | ||
|
|
4e8eccfd0b | ||
|
|
5cb5306471 | ||
|
|
9a5b45b969 | ||
|
|
7c82bbf3da | ||
|
|
9177acf277 | ||
|
|
93187d7f4a | ||
|
|
94b1551de2 | ||
|
|
1c713d3278 | ||
|
|
bdef93fca3 | ||
|
|
e9e55c72b9 | ||
|
|
919450aafb | ||
|
|
78f486ad7c | ||
|
|
ccdfcb5e50 | ||
|
|
9b275536e9 | ||
|
|
bf39ed10b4 | ||
|
|
0e8bda3369 | ||
|
|
87bb22857a | ||
|
|
9cd8d9d823 | ||
|
|
931732f75e | ||
|
|
35ace3b3eb | ||
|
|
4b4601ae85 | ||
|
|
46337855a4 | ||
|
|
43f8aec70a | ||
|
|
a21b4302d4 | ||
|
|
4adc3b700a | ||
|
|
42b5a32708 | ||
|
|
296e30e5fd | ||
|
|
90718cc989 | ||
|
|
857930e169 | ||
|
|
9b1b6e2b72 | ||
|
|
37217b0dd9 | ||
|
|
f138eab47a | ||
|
|
4e4c53927b | ||
|
|
fab1d77d64 | ||
|
|
f4f7087465 | ||
|
|
020da1d510 | ||
|
|
2956f13ee7 | ||
|
|
939f3d58f4 | ||
|
|
3d85b7863c | ||
|
|
54abb7a9f7 | ||
|
|
9ca9b337ef | ||
|
|
92c4bcb049 | ||
|
|
ce5cb17e31 | ||
|
|
6330929c04 | ||
|
|
79a753b609 | ||
|
|
30ebc62697 | ||
|
|
28b218a13b | ||
|
|
cd24430ea9 | ||
|
|
f4daf94f7b | ||
|
|
4acce194d9 | ||
|
|
761e8d4579 | ||
|
|
284c66ad86 | ||
|
|
6c7c10924a | ||
|
|
8f2d5129b1 | ||
|
|
2a067778bc | ||
|
|
299cc2f12f | ||
|
|
ce705abd20 | ||
|
|
e2bd36ca16 | ||
|
|
e2dbd41e8f | ||
|
|
050a61f334 | ||
|
|
0fa7c1b2d7 | ||
|
|
e1af6ba2f5 | ||
|
|
4a928735eb | ||
|
|
fdc2af4a39 | ||
|
|
70b3daad73 | ||
|
|
d8bade64e1 | ||
|
|
f779c492d8 | ||
|
|
a7b98f1e5e | ||
|
|
797eb9b159 | ||
|
|
c3e40fd3aa | ||
|
|
0ed07b024a | ||
|
|
22da51729b | ||
|
|
c3be2894c9 | ||
|
|
e08f968f1a | ||
|
|
351815ad91 | ||
|
|
25a90f3966 | ||
|
|
50bb50f5ab | ||
|
|
d1ff7c196d | ||
|
|
298d2391de | ||
|
|
49cbeee261 | ||
|
|
b104b358ea | ||
|
|
786fca2937 | ||
|
|
ca6d1f51df | ||
|
|
f324453351 | ||
|
|
d0f5aca707 | ||
|
|
f0cb047e8c | ||
|
|
b74bc62804 | ||
|
|
02467d5220 | ||
|
|
f18c0e3eaf | ||
|
|
c55f0b4c39 | ||
|
|
94f57629e5 | ||
|
|
2639a6ac6b | ||
|
|
52c1992814 | ||
|
|
b1fbf4344e | ||
|
|
a1b9f2dd3b | ||
|
|
441cfaffce | ||
|
|
75e1f5387d | ||
|
|
e4c845a9f6 | ||
|
|
d239509299 | ||
|
|
3f0fda7aac | ||
|
|
cc5452d14c | ||
|
|
5aff8c1737 | ||
|
|
a00f4af923 | ||
|
|
62a2aed7d9 | ||
|
|
e99f01104e | ||
|
|
457f0aa2ac | ||
|
|
1de4704046 | ||
|
|
0d65e67140 | ||
|
|
ccd01c9c75 | ||
|
|
84266364e3 | ||
|
|
0b127ff419 | ||
|
|
d4a01d9b8a | ||
|
|
ea88d8284a | ||
|
|
d2ec420f57 | ||
|
|
97d33adabd | ||
|
|
c11f3cc66c | ||
|
|
6b4a7e35a9 | ||
|
|
11100c9260 | ||
|
|
941c3ca43a | ||
|
|
05f9528ca9 | ||
|
|
b205945f65 | ||
|
|
c1dc28488a | ||
|
|
c6065653f0 | ||
|
|
fd0de5ae09 | ||
|
|
547d6608e6 | ||
|
|
dacb3cc076 | ||
|
|
282657fbca | ||
|
|
7e516c6f0b | ||
|
|
34e2a49bef | ||
|
|
ff4bdd2f2d | ||
|
|
b239fdf847 | ||
|
|
47feb4cf3c | ||
|
|
83ca18d7d5 | ||
|
|
11666b9ae1 | ||
|
|
63b732ff42 | ||
|
|
50d63c28d6 | ||
|
|
73b7ff7d1b | ||
|
|
ccdcd01372 | ||
|
|
198296f0f8 | ||
|
|
f122343e31 | ||
|
|
82fa39f1a3 | ||
|
|
841e1b4ce2 | ||
|
|
1ab62d7724 | ||
|
|
28a46d395b | ||
|
|
64c66f1272 | ||
|
|
f8d957486b | ||
|
|
1866607967 | ||
|
|
40efdf6f3b | ||
|
|
f910df3ce5 | ||
|
|
c99c96a9ea | ||
|
|
95cc557577 | ||
|
|
fa78389f47 | ||
|
|
f7381c43aa | ||
|
|
76c5db61da | ||
|
|
88ffc34a68 | ||
|
|
4f65b34aeb | ||
|
|
4a26d18b46 | ||
|
|
664ac8128a | ||
|
|
ce285818cf | ||
|
|
6f7811a843 | ||
|
|
54ed90e892 | ||
|
|
f8060113d9 | ||
|
|
48ab891991 | ||
|
|
7f384c3f56 | ||
|
|
01a2771dfb | ||
|
|
45b6214ddd | ||
|
|
c96ff3f5b4 | ||
|
|
d84758232e | ||
|
|
479953e082 | ||
|
|
7235f242b2 | ||
|
|
27881589d4 | ||
|
|
79ec3f2c91 | ||
|
|
715b4181be | ||
|
|
0ccda10c65 | ||
|
|
5b4376b5f9 | ||
|
|
1050f7616b | ||
|
|
3a9cc0c09c | ||
|
|
4d63b90373 | ||
|
|
43c4614650 | ||
|
|
eba9ccf083 | ||
|
|
947100a9d4 | ||
|
|
ed4924651a | ||
|
|
3cc2bb94fe | ||
|
|
8ecb3eb50a | ||
|
|
819d89c278 | ||
|
|
66d47c60bc | ||
|
|
0340563749 | ||
|
|
271e23a67f | ||
|
|
de42382f58 | ||
|
|
3728809d10 | ||
|
|
d4e2ace8a6 | ||
|
|
1d4188df7e | ||
|
|
a2323293ca | ||
|
|
29bd1c2968 | ||
|
|
6c35ec13fd | ||
|
|
83107b05e2 | ||
|
|
5fb7165306 | ||
|
|
c569b60d5c | ||
|
|
bd2748659a | ||
|
|
7fef8f4c77 | ||
|
|
3ee233c0bc | ||
|
|
d63081dffa | ||
|
|
0932ec95bf | ||
|
|
7361da0e5d | ||
|
|
2dbddf532c | ||
|
|
eb867e62d8 | ||
|
|
d75504960a | ||
|
|
93efbf742e | ||
|
|
7c3ac138b5 | ||
|
|
63444d3dd1 | ||
|
|
2fcad742b8 | ||
|
|
f341ef5e83 | ||
|
|
d488824e3d | ||
|
|
0c28700f75 | ||
|
|
48d97f3e57 | ||
|
|
ef17c9920c | ||
|
|
ef643a5a72 | ||
|
|
b7ff8d0bd5 | ||
|
|
1e721d68bf | ||
|
|
7454297dcd | ||
|
|
2e3a9a8b80 | ||
|
|
4e0a023c36 | ||
|
|
155c5eb329 | ||
|
|
45718ab925 | ||
|
|
562c8dda8d | ||
|
|
84a37a970f | ||
|
|
37cc02e97a | ||
|
|
7a8753408b | ||
|
|
eb853d0be9 | ||
|
|
52438733d7 | ||
|
|
b986dd8473 | ||
|
|
98fdb581fb | ||
|
|
ea7380288c | ||
|
|
9beecab21d | ||
|
|
2e6932e054 | ||
|
|
5ca9e028d8 | ||
|
|
64e2b71a34 | ||
|
|
8d52c94aed | ||
|
|
9d91a1c99f | ||
|
|
d6fdeafef6 | ||
|
|
371b3a9d65 | ||
|
|
36c1c7b170 | ||
|
|
38d3a7901d | ||
|
|
2223259d13 | ||
|
|
aba270182d | ||
|
|
c9378fc9fe | ||
|
|
378850682d | ||
|
|
02c35803da | ||
|
|
75b60c1ec8 | ||
|
|
2cd4424ba1 | ||
|
|
d52f0aed6e | ||
|
|
d197fc5b9e | ||
|
|
34280fe24b | ||
|
|
fd88a37aec | ||
|
|
0082a308fa | ||
|
|
b5f5826c80 | ||
|
|
5024a9254d | ||
|
|
2a14a630d5 | ||
|
|
ef8ea2599f | ||
|
|
e9ab4270a9 | ||
|
|
a99d651509 | ||
|
|
c54a5b36d4 | ||
|
|
ae844cf203 | ||
|
|
ec66ea76ba | ||
|
|
8643c41193 | ||
|
|
bed0483496 | ||
|
|
a648208656 | ||
|
|
98af6d0530 | ||
|
|
5af1953e04 | ||
|
|
b1a08be57b | ||
|
|
15981aadf1 | ||
|
|
e19cb52e70 | ||
|
|
5269d09d0e | ||
|
|
e7e64720c6 | ||
|
|
a6701a842e | ||
|
|
d697bd13a1 | ||
|
|
6e20a466ce | ||
|
|
dd5c9775fc | ||
|
|
82c274591c | ||
|
|
4f73f57ddf | ||
|
|
269141dfb2 | ||
|
|
cab0e1ce0b | ||
|
|
d4ff7ffaa9 | ||
|
|
d238d09cc3 | ||
|
|
4cee5213bb | ||
|
|
759e4a481d | ||
|
|
1213afe834 | ||
|
|
35fda7106a | ||
|
|
b8baa84270 | ||
|
|
6591e663b6 | ||
|
|
bad5c8d489 | ||
|
|
68f8c94332 | ||
|
|
ddfb233452 | ||
|
|
bbf62d2302 | ||
|
|
17b09c1f6d | ||
|
|
e0fc70ec60 | ||
|
|
4558fc0336 | ||
|
|
4609ec442a | ||
|
|
eb7d9e4168 | ||
|
|
8706e25b0d | ||
|
|
85fea64f49 | ||
|
|
f74215c9f6 | ||
|
|
cc8906980c | ||
|
|
56f1ba0f25 | ||
|
|
0990d73075 | ||
|
|
7d877a0cbb | ||
|
|
e13bad8483 | ||
|
|
22ebb1bbe1 | ||
|
|
bc888d7984 | ||
|
|
c92d4f7868 | ||
|
|
667a2a81fb | ||
|
|
4a22071f94 | ||
|
|
ec2075c874 | ||
|
|
ebc9716019 | ||
|
|
d067c5b8c4 | ||
|
|
72c9c9872e | ||
|
|
2b541fe041 | ||
|
|
f6e371f223 | ||
|
|
0aefe9e656 | ||
|
|
4f8f29e41a | ||
|
|
ffb32c7ce2 | ||
|
|
3cacca80e9 | ||
|
|
a3356fe195 | ||
|
|
3c70e1623e | ||
|
|
51628c081a | ||
|
|
18ebe6bcb4 | ||
|
|
63ac4f2b44 | ||
|
|
1b6ca170c0 | ||
|
|
2966260f51 | ||
|
|
c4069995e0 | ||
|
|
54920982a2 | ||
|
|
f947a3ed58 | ||
|
|
5e86d67316 | ||
|
|
7354a19696 | ||
|
|
78e3d77000 | ||
|
|
47ad2410cf | ||
|
|
fa2eb7b3cf | ||
|
|
0cee6ebd77 | ||
|
|
73f161ffa4 | ||
|
|
ff06d5c3be | ||
|
|
78cb177ac5 | ||
|
|
9601502360 | ||
|
|
739b7e600c | ||
|
|
27da7639b2 | ||
|
|
258e3bdb9f | ||
|
|
7f3e9927d5 | ||
|
|
927f726030 | ||
|
|
7576200fc4 | ||
|
|
4ffb9d636f | ||
|
|
24f0547343 | ||
|
|
402a323673 | ||
|
|
5e1685417f | ||
|
|
9dfe34ce9a | ||
|
|
190d6da217 | ||
|
|
8946b44280 | ||
|
|
f21d085462 | ||
|
|
43f53197c4 | ||
|
|
ac43be1262 | ||
|
|
8bf1c56efd | ||
|
|
d66791dc41 | ||
|
|
ce6b36a77e | ||
|
|
4acae01009 | ||
|
|
213b76a280 | ||
|
|
c675e1d67c | ||
|
|
8a9f6ab34f | ||
|
|
8aca6ce084 | ||
|
|
cb0c093006 | ||
|
|
1e7d7008b4 | ||
|
|
ef453dbc1e | ||
|
|
9f1e635c8d | ||
|
|
92037f3f04 | ||
|
|
4ba0566d27 | ||
|
|
5d870d53f6 | ||
|
|
6a674cdb5b | ||
|
|
fbce132173 | ||
|
|
98e13cba99 | ||
|
|
7ce2f6c797 | ||
|
|
7502792438 | ||
|
|
9efb13cf01 | ||
|
|
b14e263a2e | ||
|
|
6f41ef3047 | ||
|
|
de29f0c938 | ||
|
|
589a24f595 | ||
|
|
85186f94f5 | ||
|
|
ffc363a31c | ||
|
|
a047ad3bb6 | ||
|
|
cc3289c834 | ||
|
|
e1093a3c69 | ||
|
|
ba955ca658 | ||
|
|
946abc5cf7 | ||
|
|
57ea9ed8f4 | ||
|
|
f8a12b1fe3 | ||
|
|
722f6257ae | ||
|
|
ccbe311c58 | ||
|
|
0f6ca0876c | ||
|
|
bb60fd0339 | ||
|
|
eefbd5939b | ||
|
|
7c01196f17 | ||
|
|
4c58f4d7f3 | ||
|
|
c741587341 | ||
|
|
6ad2fa80f1 | ||
|
|
55694ae303 | ||
|
|
8423b12119 | ||
|
|
5a7ba7f87e | ||
|
|
d36da3df24 | ||
|
|
302a5f0cf1 | ||
|
|
b4fef421dd | ||
|
|
5f5e991d33 | ||
|
|
43a49242c5 | ||
|
|
2e5694cd1a | ||
|
|
3c123a07ea | ||
|
|
dec5ddd154 | ||
|
|
22864b9669 | ||
|
|
528fa483a2 | ||
|
|
b5a3815f1f | ||
|
|
1719e0394e | ||
|
|
eae902ee92 | ||
|
|
01c57a8691 | ||
|
|
2808005ce9 | ||
|
|
06c5f65df2 | ||
|
|
e0bad6ed40 | ||
|
|
35c8b9316e | ||
|
|
2ddfbb0dde | ||
|
|
988c4a460d | ||
|
|
c050e2f11a | ||
|
|
95e49c7b0f | ||
|
|
e46a13889e | ||
|
|
335151f6ca | ||
|
|
908e4a3df6 | ||
|
|
6b403f2c1e | ||
|
|
da5e322229 | ||
|
|
4f9ac16a07 | ||
|
|
4342844845 | ||
|
|
8bc68e1edd | ||
|
|
763d64a1ca | ||
|
|
8a10d18f37 | ||
|
|
181f490d47 | ||
|
|
322718d19a | ||
|
|
96eeb1cab7 | ||
|
|
d5c82e4a28 | ||
|
|
436adc5ff6 | ||
|
|
97a5d91fce | ||
|
|
9eee8557c2 | ||
|
|
4394dd023e | ||
|
|
d39bbe4f99 | ||
|
|
b188c8adfe | ||
|
|
ea6fd42b2e | ||
|
|
625499f758 | ||
|
|
0c2d6dd582 | ||
|
|
f6a9c97e7f | ||
|
|
e2d741937e | ||
|
|
9a3404f4b8 | ||
|
|
d000aa095f | ||
|
|
8945801db8 | ||
|
|
40f327ea17 | ||
|
|
44a2604dbd | ||
|
|
0ae7424f04 | ||
|
|
091af92e8d | ||
|
|
3ed338ed64 | ||
|
|
65781570e7 | ||
|
|
e1f7aeaa96 | ||
|
|
a0a580588c | ||
|
|
a2f2a21a4c | ||
|
|
1ddb446bc6 | ||
|
|
dcdc6851b6 | ||
|
|
ece4e56118 | ||
|
|
7cd104c047 | ||
|
|
80a7dd75c8 | ||
|
|
ec2ef752b3 | ||
|
|
57d5c57048 | ||
|
|
9b7c22c35e | ||
|
|
6711f6f6e6 | ||
|
|
7ac7077f8c | ||
|
|
ed20249988 | ||
|
|
dba8d23348 | ||
|
|
ab9062983c | ||
|
|
37493a4688 | ||
|
|
689d149edc | ||
|
|
9b9106c5da | ||
|
|
9a43af98c3 | ||
|
|
1eae92f2b2 | ||
|
|
4560bbc0ed | ||
|
|
2d5ef0aae4 | ||
|
|
096542c8ff | ||
|
|
d123ca5173 | ||
|
|
388929f207 | ||
|
|
29b9223a14 | ||
|
|
cf283377cd | ||
|
|
5f218e69d4 | ||
|
|
7f5fb165de | ||
|
|
eec79d7d3e | ||
|
|
00809d88f6 | ||
|
|
18b0cc6ea0 | ||
|
|
54d9902ae4 | ||
|
|
eff39a39e3 | ||
|
|
005a7bb68e | ||
|
|
afc1701150 | ||
|
|
72eada0639 | ||
|
|
979e8a47ab | ||
|
|
e119a364c1 | ||
|
|
c7b9bbb18d | ||
|
|
3cb3dce247 | ||
|
|
a7e1c89e66 | ||
|
|
ca1060ada5 | ||
|
|
69578ab054 | ||
|
|
511bed47ff | ||
|
|
26a0412649 | ||
|
|
4989ff773c | ||
|
|
04c0d68763 | ||
|
|
589035d42b | ||
|
|
5726d50106 | ||
|
|
6e9c326a52 | ||
|
|
fc1cdf4d0e | ||
|
|
45178ac38b | ||
|
|
09ee1c26b1 | ||
|
|
64180ca57f | ||
|
|
0c052a118c | ||
|
|
cb9fd43f11 | ||
|
|
1dc55c2770 | ||
|
|
0ca6d46a7b | ||
|
|
3624ef4755 | ||
|
|
f07a003f2c | ||
|
|
0ef46407dd | ||
|
|
f7f3c005f8 | ||
|
|
cae5e14587 | ||
|
|
9a5aa52801 | ||
|
|
1e172cb4e3 | ||
|
|
d187bb2bba |
@@ -3,8 +3,8 @@
|
|||||||
'name': "jikimo_account_process",
|
'name': "jikimo_account_process",
|
||||||
|
|
||||||
'summary': """
|
'summary': """
|
||||||
Short (1 phrase/line) summary of the module's purpose, used as
|
处理会计凭证生成重复名称报错问题
|
||||||
subtitle on modules listing or apps.openerp.com""",
|
""",
|
||||||
|
|
||||||
'description': """
|
'description': """
|
||||||
Long description of module's purpose
|
Long description of module's purpose
|
||||||
|
|||||||
@@ -6,6 +6,6 @@ import { patch } from "web.utils";
|
|||||||
patch(WebClient.prototype, "kolpolok_custom_title_and_favicon.WebClient", {
|
patch(WebClient.prototype, "kolpolok_custom_title_and_favicon.WebClient", {
|
||||||
setup() {
|
setup() {
|
||||||
this._super();
|
this._super();
|
||||||
this.title.setParts({ zopenerp: "JIKIMO" });
|
// this.title.setParts({ zopenerp: "JIKIMO" });
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -1,41 +1,99 @@
|
|||||||
.zoomed {
|
|
||||||
position: fixed !important;
|
.processing-capabilities-grid {
|
||||||
top: 50%;
|
display: grid;
|
||||||
left: 50%;
|
grid-template-columns: repeat(6, 1fr);
|
||||||
transform: translate(-50%, -50%) scale(10);
|
gap: 10px;
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.many2many_flex {
|
.grid-item {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.many2many_flex>div {
|
.item-content {
|
||||||
margin-right: 15px;
|
display: flex;
|
||||||
display: flex;
|
flex-direction: column;
|
||||||
flex-direction: column;
|
align-items: center;
|
||||||
align-items: center;
|
text-align: center;
|
||||||
|
}
|
||||||
|
/*控制图片大小*/
|
||||||
|
.item-icon {
|
||||||
|
width: 50px;
|
||||||
|
height: 50px;
|
||||||
|
margin-bottom: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.many2many_flex>div>:nth-child(2) {
|
.item-label {
|
||||||
position: relative;
|
font-size: 12px;
|
||||||
|
word-break: break-word;
|
||||||
}
|
}
|
||||||
|
|
||||||
.close {
|
@media (max-width: 1200px) {
|
||||||
width: 20px;
|
.processing-capabilities-grid {
|
||||||
height: 20px;
|
grid-template-columns: repeat(4, 1fr);
|
||||||
position: absolute;
|
}
|
||||||
top: -8.8px;
|
|
||||||
right: -8.8px;
|
|
||||||
color: #fff;
|
|
||||||
background-color: #000;
|
|
||||||
opacity: 0;
|
|
||||||
text-align: center;
|
|
||||||
line-height: 20px;
|
|
||||||
font-size: 18px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.img_close {
|
@media (max-width: 768px) {
|
||||||
opacity: 1;
|
.processing-capabilities-grid {
|
||||||
transform: scale(0.1);
|
grid-template-columns: repeat(3, 1fr);
|
||||||
cursor: pointer;
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 480px) {
|
||||||
|
.processing-capabilities-grid {
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.image-preview-container {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-color: rgba(0, 0, 0, 0.9);
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
z-index: 1000;
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-preview-container.show {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-preview {
|
||||||
|
max-width: 90%;
|
||||||
|
max-height: 90%;
|
||||||
|
object-fit: contain;
|
||||||
|
box-shadow: 0 0 20px rgba(255, 255, 255, 0.2);
|
||||||
|
border-radius: 5px;
|
||||||
|
transform: scale(0.9);
|
||||||
|
transition: transform 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-preview-container.show .image-preview {
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-preview-close {
|
||||||
|
position: absolute;
|
||||||
|
top: 20px;
|
||||||
|
right: 30px;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 40px;
|
||||||
|
font-weight: bold;
|
||||||
|
transition: 0.3s;
|
||||||
|
cursor: pointer;
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-preview-close:hover,
|
||||||
|
.image-preview-close:focus {
|
||||||
|
opacity: 1;
|
||||||
|
text-decoration: none;
|
||||||
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
@@ -4,35 +4,57 @@ import {Many2ManyCheckboxesField} from "@web/views/fields/many2many_checkboxes/m
|
|||||||
import {registry} from "@web/core/registry";
|
import {registry} from "@web/core/registry";
|
||||||
|
|
||||||
export class MyCustomWidget extends Many2ManyCheckboxesField {
|
export class MyCustomWidget extends Many2ManyCheckboxesField {
|
||||||
// 你可以重写或者添加一些方法和属性
|
|
||||||
// 例如,你可以重写setup方法来添加一些事件监听器或者初始化一些变量
|
|
||||||
setup() {
|
setup() {
|
||||||
super.setup(); // 调用父类的setup方法
|
super.setup();
|
||||||
// 你自己的代码
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onImageClick(event) {
|
onImageClick(event, src) {
|
||||||
// 放大图片逻辑
|
event.preventDefault();
|
||||||
// 获取图片元素
|
event.stopPropagation();
|
||||||
const img = event.target;
|
|
||||||
const close = img.nextSibling;
|
|
||||||
|
|
||||||
// 实现放大图片逻辑
|
// 创建预览框
|
||||||
// 比如使用 CSS 放大
|
const previewContainer = document.createElement('div');
|
||||||
img.parentElement.classList.add('zoomed');
|
previewContainer.className = 'image-preview-container';
|
||||||
close.classList.add('img_close');
|
|
||||||
}
|
|
||||||
|
|
||||||
onCloseClick(event) {
|
const previewImg = document.createElement('img');
|
||||||
const close = event.target;
|
previewImg.src = src;
|
||||||
const img = close.previousSibling;
|
previewImg.className = 'image-preview';
|
||||||
img.parentElement.classList.remove('zoomed');
|
// 设置放大的预览图片大小
|
||||||
close.classList.remove('img_close');
|
previewImg.style.width = '600px';
|
||||||
|
previewImg.style.height = 'auto'; // 保持宽高比
|
||||||
|
|
||||||
|
const closeButton = document.createElement('span');
|
||||||
|
closeButton.innerHTML = '×';
|
||||||
|
closeButton.className = 'image-preview-close';
|
||||||
|
|
||||||
|
previewContainer.appendChild(previewImg);
|
||||||
|
previewContainer.appendChild(closeButton);
|
||||||
|
document.body.appendChild(previewContainer);
|
||||||
|
|
||||||
|
// 添加关闭预览的事件监听器
|
||||||
|
const closePreview = () => {
|
||||||
|
previewContainer.classList.remove('show');
|
||||||
|
setTimeout(() => {
|
||||||
|
document.body.removeChild(previewContainer);
|
||||||
|
}, 300);
|
||||||
|
};
|
||||||
|
|
||||||
|
closeButton.addEventListener('click', closePreview);
|
||||||
|
|
||||||
|
// 点击预览框外部也可以关闭
|
||||||
|
previewContainer.addEventListener('click', (e) => {
|
||||||
|
if (e.target === previewContainer) {
|
||||||
|
closePreview();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 使用 setTimeout 来触发过渡效果
|
||||||
|
setTimeout(() => {
|
||||||
|
previewContainer.classList.add('show');
|
||||||
|
}, 10);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MyCustomWidget.template = "jikimo_frontend.MyCustomWidget";
|
MyCustomWidget.template = "jikimo_frontend.MyCustomWidget";
|
||||||
// MyCustomWidget.supportedTypes = ['many2many'];
|
|
||||||
|
|
||||||
registry.category("fields").add("custom_many2many_checkboxes", MyCustomWidget);
|
|
||||||
|
|
||||||
|
registry.category("fields").add("custom_many2many_checkboxes", MyCustomWidget);
|
||||||
@@ -2,27 +2,22 @@
|
|||||||
<templates xml:space="preserve">
|
<templates xml:space="preserve">
|
||||||
|
|
||||||
<t t-name="jikimo_frontend.MyCustomWidget" owl="1">
|
<t t-name="jikimo_frontend.MyCustomWidget" owl="1">
|
||||||
<div aria-atomic="true" class="many2many_flex">
|
<div aria-atomic="true" class="many2many_flex processing-capabilities-grid">
|
||||||
<t t-foreach="items" t-as="item" t-key="item[0]">
|
<t t-foreach="items" t-as="item" t-key="item[0]">
|
||||||
<div>
|
<div class="grid-item">
|
||||||
<CheckBox
|
<CheckBox
|
||||||
value="isSelected(item)"
|
value="isSelected(item)"
|
||||||
disabled="props.readonly"
|
disabled="props.readonly"
|
||||||
onChange="(ev) => this.onChange(item[0], ev)"
|
onChange="(ev) => this.onChange(item[0], ev)"
|
||||||
>
|
>
|
||||||
<t t-esc="item[1]"/>
|
<div class="item-content">
|
||||||
|
<img t-att-src="item[2]" class="item-icon" t-on-click="(ev) => this.onImageClick(ev, item[2])"/>
|
||||||
|
<span class="item-label"><t t-esc="item[1]"/></span>
|
||||||
|
</div>
|
||||||
</CheckBox>
|
</CheckBox>
|
||||||
<div t-on-dblclick="onImageClick">
|
|
||||||
<t>
|
|
||||||
<img t-att-src="item[2]" width="50" height="50"/>
|
|
||||||
<div class="close" t-on-click="onCloseClick">×</div>
|
|
||||||
</t>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
</t>
|
</t>
|
||||||
</div>
|
</div>
|
||||||
</t>
|
</t>
|
||||||
|
|
||||||
</templates>
|
</templates>
|
||||||
@@ -108,6 +108,10 @@ td.o_required_modifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.color_3 {
|
.color_3 {
|
||||||
|
background-color: #808080;
|
||||||
|
}
|
||||||
|
|
||||||
|
.color_4 {
|
||||||
background-color: rgb(255, 150, 0);
|
background-color: rgb(255, 150, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,9 +5,9 @@
|
|||||||
<!-- 修改页面头部图标及文字 -->
|
<!-- 修改页面头部图标及文字 -->
|
||||||
<template id="favicon_icon" inherit_id="web.layout" name="Web layout">
|
<template id="favicon_icon" inherit_id="web.layout" name="Web layout">
|
||||||
<!-- change the title with reliance partner -->
|
<!-- change the title with reliance partner -->
|
||||||
<xpath expr="//head//title" position="before">
|
<!-- <xpath expr="//head//title" position="before">
|
||||||
<title t-esc="'JIKIMO'"/>
|
<title t-esc="'JIKIMO'"/>
|
||||||
</xpath>
|
</xpath> -->
|
||||||
<!-- change the default favicon icon with -->
|
<!-- change the default favicon icon with -->
|
||||||
<xpath expr="//head//link[@rel='shortcut icon']" position="replace">
|
<xpath expr="//head//link[@rel='shortcut icon']" position="replace">
|
||||||
<link type="image/x-icon" rel="shortcut icon" href="/jikimo_frontend/static/src/img/jikimo-logo.ico"/>
|
<link type="image/x-icon" rel="shortcut icon" href="/jikimo_frontend/static/src/img/jikimo-logo.ico"/>
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
<!-- hide 登录页面 powerd by odoo 及管理数据库 -->
|
<!-- hide 登录页面 powerd by odoo 及管理数据库 -->
|
||||||
<template id="login_page_layout" inherit_id="web.login_layout" name="Login Page Layout">
|
<template id="login_page_layout" inherit_id="web.login_layout" name="Login Page Layout">
|
||||||
<xpath expr="//div[@class='card-body']//div[last()]" position="replace"></xpath>
|
<!-- <xpath expr="//div[@class='card-body']/div[last()]" position="replace"></xpath> -->
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- 隐藏odoo版本信息 -->
|
<!-- 隐藏odoo版本信息 -->
|
||||||
|
|||||||
143
jikimo_sale_multiple_supply_methods/__init__.py
Normal file
143
jikimo_sale_multiple_supply_methods/__init__.py
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from . import models
|
||||||
|
from . import controllers
|
||||||
|
|
||||||
|
from odoo import api, SUPERUSER_ID
|
||||||
|
|
||||||
|
def _data_install(cr, registry):
|
||||||
|
env = api.Environment(cr, SUPERUSER_ID, {})
|
||||||
|
# 获取所有需要设置的产品模板
|
||||||
|
env.ref('jikimo_sale_multiple_supply_methods.product_template_purchase').product_variant_id.write({'active': False, 'is_bfm': True})
|
||||||
|
env.ref('jikimo_sale_multiple_supply_methods.product_template_manual_processing').product_variant_id.write({'active': False, 'single_manufacturing': True, 'is_bfm': True})
|
||||||
|
env.ref('jikimo_sale_multiple_supply_methods.product_template_default').product_variant_id.write({'active': False, 'is_bfm': True})
|
||||||
|
env.ref('jikimo_sale_multiple_supply_methods.product_template_embryo_customer_provided').product_variant_id.write({'active': False})
|
||||||
|
env.ref('jikimo_sale_multiple_supply_methods.product_template_outsourcing').product_variant_id.write({'active': False, 'is_bfm': True})
|
||||||
|
env.ref('sf_dlm.product_embryo_sf_self_machining').product_tmpl_id.write({'categ_type': '坯料'})
|
||||||
|
env.ref('sf_dlm.product_template_sf').product_tmpl_id.write({'categ_type': '成品'})
|
||||||
|
env.ref('sf_dlm.product_embryo_sf_outsource').product_tmpl_id.write({'categ_type': '坯料'})
|
||||||
|
env.ref('sf_dlm.product_embryo_sf_purchase').product_tmpl_id.write({'categ_type': '坯料'})
|
||||||
|
|
||||||
|
# 为三步制造增加规则
|
||||||
|
warehouse = env['stock.warehouse'].search([('company_id', '=', env.company.id)], limit=1)
|
||||||
|
product_route_id = warehouse.pbm_route_id
|
||||||
|
# 创建规则:原料存货区 -> 制造前, 坯料存货区 -> 制造前, 制造后 -> 坯料存货区, 制造后 -> 成品存货区
|
||||||
|
raw_material_location_id = env['stock.location'].search([('name', '=', '坯料存货区')], limit=1)
|
||||||
|
picking_type_production = warehouse.pbm_type_id
|
||||||
|
picking_type_stock = warehouse.sam_type_id
|
||||||
|
material_location_id = env['stock.location'].search([('name', '=', '原料存货区')], limit=1)
|
||||||
|
# 为mto增加规则
|
||||||
|
mto_route_id = env.ref('stock.route_warehouse0_mto')
|
||||||
|
# 创建规则:原料存货区 -> 外包位置, 坯料存货区 -> 外包位置
|
||||||
|
subcontracting_location_id = env.company.subcontracting_location_id
|
||||||
|
picking_type_subcontracting = warehouse.subcontracting_resupply_type_id
|
||||||
|
# 为补给外包商增加规则
|
||||||
|
resupply_route_id = warehouse.subcontracting_route_id
|
||||||
|
|
||||||
|
rules_data = [
|
||||||
|
{
|
||||||
|
'name': 'WH: 原料存货区 → 制造前',
|
||||||
|
'location_src_id': material_location_id.id,
|
||||||
|
'location_dest_id': warehouse.pbm_loc_id.id,
|
||||||
|
'route_id': product_route_id.id,
|
||||||
|
'picking_type_id': picking_type_production.id,
|
||||||
|
'action': 'pull',
|
||||||
|
'sequence': 20,
|
||||||
|
'warehouse_id': warehouse.id,
|
||||||
|
'procure_method': 'mts_else_mto',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'name': 'WH: 坯料存货区 → 制造前',
|
||||||
|
'location_src_id': raw_material_location_id.id,
|
||||||
|
'location_dest_id': warehouse.pbm_loc_id.id,
|
||||||
|
'route_id': product_route_id.id,
|
||||||
|
'picking_type_id': picking_type_production.id,
|
||||||
|
'action': 'pull',
|
||||||
|
'sequence': 21,
|
||||||
|
'warehouse_id': warehouse.id,
|
||||||
|
'procure_method': 'mts_else_mto',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'name': 'WH: 制造后 → 坯料存货区',
|
||||||
|
'location_src_id': warehouse.sam_loc_id.id,
|
||||||
|
'location_dest_id': raw_material_location_id.id,
|
||||||
|
'route_id': product_route_id.id,
|
||||||
|
'picking_type_id': picking_type_stock.id,
|
||||||
|
'action': 'push',
|
||||||
|
'sequence': 23,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'name': 'WH: 制造后 → 成品存货区',
|
||||||
|
'location_src_id': warehouse.sam_loc_id.id,
|
||||||
|
'location_dest_id': env['stock.location'].search([('name', '=', '成品存货区')], limit=1).id,
|
||||||
|
'route_id': product_route_id.id,
|
||||||
|
'picking_type_id': picking_type_stock.id,
|
||||||
|
'action': 'push',
|
||||||
|
'sequence': 24,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'name': 'WH: 原料存货区 → 外包位置 (MTO)',
|
||||||
|
'location_src_id': material_location_id.id,
|
||||||
|
'location_dest_id': subcontracting_location_id.id,
|
||||||
|
'route_id': mto_route_id.id,
|
||||||
|
'picking_type_id': picking_type_subcontracting.id,
|
||||||
|
'action': 'pull',
|
||||||
|
'sequence': 24,
|
||||||
|
'warehouse_id': warehouse.id,
|
||||||
|
'procure_method': 'mts_else_mto',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'name': 'WH: 坯料存货区 → 外包位置 (MTO)',
|
||||||
|
'location_src_id': raw_material_location_id.id,
|
||||||
|
'location_dest_id': subcontracting_location_id.id,
|
||||||
|
'route_id': mto_route_id.id,
|
||||||
|
'picking_type_id': picking_type_subcontracting.id,
|
||||||
|
'action': 'pull',
|
||||||
|
'sequence': 25,
|
||||||
|
'warehouse_id': warehouse.id,
|
||||||
|
'procure_method': 'mts_else_mto',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'name': 'WH: 坯料存货区 → 外包位置',
|
||||||
|
'location_src_id': raw_material_location_id.id,
|
||||||
|
'location_dest_id': subcontracting_location_id.id,
|
||||||
|
'route_id': resupply_route_id.id,
|
||||||
|
'picking_type_id': picking_type_subcontracting.id,
|
||||||
|
'action': 'pull',
|
||||||
|
'sequence': 26,
|
||||||
|
'warehouse_id': warehouse.id,
|
||||||
|
'procure_method': 'make_to_stock',
|
||||||
|
}
|
||||||
|
]
|
||||||
|
# 遍历每个规则数据,执行创建或更新操作
|
||||||
|
for rule_data in rules_data:
|
||||||
|
_create_or_update_stock_rule(env, rule_data)
|
||||||
|
|
||||||
|
def _create_or_update_stock_rule(env, rule_data):
|
||||||
|
# 尝试查找现有的 stock.rule
|
||||||
|
existing_rule = env['stock.rule'].search([
|
||||||
|
('name', '=', rule_data['name']),
|
||||||
|
('route_id', '=', rule_data.get('route_id'))
|
||||||
|
], limit=1)
|
||||||
|
|
||||||
|
if existing_rule:
|
||||||
|
# 如果存在,更新现有记录
|
||||||
|
existing_rule.write(rule_data)
|
||||||
|
else:
|
||||||
|
# 如果不存在,创建新记录
|
||||||
|
env['stock.rule'].create(rule_data)
|
||||||
|
|
||||||
|
def _data_uninstall(cr, registry):
|
||||||
|
env = api.Environment(cr, SUPERUSER_ID, {})
|
||||||
|
warehouse = env['stock.warehouse'].search([('company_id', '=', env.company.id)], limit=1)
|
||||||
|
product_route_id = warehouse.pbm_route_id
|
||||||
|
resupply_route_id = warehouse.subcontracting_route_id
|
||||||
|
mto_route_id = env.ref('stock.route_warehouse0_mto')
|
||||||
|
# Fail unlink means that the route is used somewhere (e.g. route_id on stock.rule). In this case
|
||||||
|
# we don't try to do anything.
|
||||||
|
try:
|
||||||
|
with env.cr.savepoint():
|
||||||
|
env['stock.rule'].search([('name', 'in', ('WH: 原料存货区 → 制造前', 'WH: 坯料存货区 → 制造前', 'WH: 制造后 → 坯料存货区', 'WH: 制造后 → 成品存货区')), ('route_id', '=', product_route_id.id)]).unlink()
|
||||||
|
env['stock.rule'].search([('name', 'in', ('WH: 原料存货区 → 外包位置 (MTO)', 'WH: 坯料存货区 → 外包位置 (MTO)')), ('route_id', '=', mto_route_id.id)]).unlink()
|
||||||
|
env['stock.rule'].search([('name', '=', 'WH: 坯料存货区 → 外包位置'), ('route_id', '=', resupply_route_id.id)]).unlink()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
27
jikimo_sale_multiple_supply_methods/__manifest__.py
Normal file
27
jikimo_sale_multiple_supply_methods/__manifest__.py
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
{
|
||||||
|
'name': '机企猫 多供货方式',
|
||||||
|
'version': '16.0.1.0.0',
|
||||||
|
'summary': """ 报价单提供(自动化产线加工/人工线下加工/外购/委外加工)多种供货方式选择 """,
|
||||||
|
'author': 'fox',
|
||||||
|
'website': '',
|
||||||
|
'category': '',
|
||||||
|
'depends': ['sf_dlm', 'sale_stock', 'sf_sale'],
|
||||||
|
"data": [
|
||||||
|
'security/ir.model.access.csv',
|
||||||
|
'data/stock_routes.xml',
|
||||||
|
'data/product_data.xml',
|
||||||
|
'views/sale_order_views.xml',
|
||||||
|
# 'views/product_product_views.xml',
|
||||||
|
],'assets': {
|
||||||
|
# 'web.assets_backend': [
|
||||||
|
# 'jikimo_sale_multiple_supply_methods/static/src/**/*'
|
||||||
|
# ],
|
||||||
|
},
|
||||||
|
'post_init_hook': '_data_install',
|
||||||
|
'uninstall_hook': '_data_uninstall',
|
||||||
|
'application': True,
|
||||||
|
'installable': True,
|
||||||
|
'auto_install': False,
|
||||||
|
'license': 'LGPL-3',
|
||||||
|
}
|
||||||
@@ -1 +1,2 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from . import main
|
||||||
51
jikimo_sale_multiple_supply_methods/controllers/main.py
Normal file
51
jikimo_sale_multiple_supply_methods/controllers/main.py
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
import logging
|
||||||
|
import json
|
||||||
|
from odoo import http
|
||||||
|
from odoo.http import request
|
||||||
|
from odoo.addons.sf_bf_connect.controllers.controllers import Sf_Bf_Connect
|
||||||
|
|
||||||
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
class JikimoSaleRoutePicking(Sf_Bf_Connect):
|
||||||
|
|
||||||
|
@http.route('/api/bfm_process_order/list', type='http', auth='sf_token', methods=['GET', 'POST'], csrf=False,
|
||||||
|
cors="*")
|
||||||
|
def get_bfm_process_order_list(self, **kw):
|
||||||
|
"""
|
||||||
|
接收业务平台加工订单分配工厂时传送来的订单数据并生成销售订单和产品及坯料
|
||||||
|
:param kw:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
res = {'status': 1, 'factory_order_no': ''}
|
||||||
|
# _logger.info('get_bfm_process_order_list:%s' % kw['order_number'])
|
||||||
|
try:
|
||||||
|
product_id = request.env.ref('jikimo_sale_multiple_supply_methods.product_template_default').with_context(active_test=False).sudo().product_variant_id
|
||||||
|
_logger.info('product_id:%s' % product_id)
|
||||||
|
company_id = request.env.ref('base.main_company').sudo()
|
||||||
|
bfm_process_order_list = json.loads(kw['bfm_process_order_list'])
|
||||||
|
order_id = request.env['sale.order'].with_user(request.env.ref("base.user_admin")).sale_order_create(
|
||||||
|
company_id, kw['delivery_name'], kw['delivery_telephone'], kw['delivery_address'],
|
||||||
|
kw['delivery_end_date'], kw['payments_way'], kw['pay_way'], state='draft')
|
||||||
|
i = 1
|
||||||
|
# 给sale_order的default_code字段赋值
|
||||||
|
# aa = request.env['sale.order'].sudo().search([('name', '=', order_id.name)])
|
||||||
|
# _logger.info('get_bfm_process_or===================================:%s' % order_id.name)
|
||||||
|
order_id.default_code = kw['order_number']
|
||||||
|
if kw.get('logistics_way'):
|
||||||
|
order_id.logistics_way = kw['logistics_way']
|
||||||
|
for item in bfm_process_order_list:
|
||||||
|
if item.get('embryo_redundancy_id'):
|
||||||
|
item['embryo_redundancy'] = request.env['sf.embryo.redundancy'].sudo().search([('code', '=', item['embryo_redundancy_id'])], limit=1)
|
||||||
|
item['embryo_redundancy_id'] = item['embryo_redundancy'].id
|
||||||
|
product = request.env['product.template'].sudo().product_create(product_id, item, order_id,
|
||||||
|
kw['order_number'], i)
|
||||||
|
product.product_tmpl_id.is_customer_provided = True if item['embryo_redundancy_id'] else False
|
||||||
|
order_id.with_user(request.env.ref("base.user_admin")).sale_order_create_line(product, item)
|
||||||
|
i += 1
|
||||||
|
res['factory_order_no'] = order_id.name
|
||||||
|
except Exception as e:
|
||||||
|
_logger.info('get_bfm_process_order_list error:%s' % e)
|
||||||
|
res['status'] = -1
|
||||||
|
res['message'] = '工厂创建销售订单和产品失败,请联系管理员'
|
||||||
|
request.cr.rollback()
|
||||||
|
return json.JSONEncoder().encode(res)
|
||||||
98
jikimo_sale_multiple_supply_methods/data/product_data.xml
Normal file
98
jikimo_sale_multiple_supply_methods/data/product_data.xml
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<odoo>
|
||||||
|
<data noupdate="1">
|
||||||
|
<record id="product_template_manual_processing" model="product.template">
|
||||||
|
<field name="name">人工线下加工模板</field>
|
||||||
|
<field name="active" eval="False"/>
|
||||||
|
<field name="categ_id" ref="sf_dlm.product_category_finished_sf"/>
|
||||||
|
<field name="route_ids"
|
||||||
|
eval="[ref('stock.route_warehouse0_mto'), ref('mrp.route_warehouse0_manufacture')]"/>
|
||||||
|
<field name="invoice_policy">delivery</field>
|
||||||
|
<field name="detailed_type">product</field>
|
||||||
|
<field name="purchase_ok">false</field>
|
||||||
|
<field name="uom_id" ref="uom.product_uom_unit"/>
|
||||||
|
<field name="uom_po_id" ref="uom.product_uom_unit"/>
|
||||||
|
<field name="company_id" ref="base.main_company"/>
|
||||||
|
<field name="single_manufacturing">true</field>
|
||||||
|
<field name="tracking">serial</field>
|
||||||
|
<!-- <field name="categ_type">成品</field> -->
|
||||||
|
<field name="is_manual_processing">true</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_template_purchase" model="product.template">
|
||||||
|
<field name="name">成品外购模板</field>
|
||||||
|
<field name="active" eval="False"/>
|
||||||
|
<field name="categ_id" ref="sf_dlm.product_category_finished_sf"/>
|
||||||
|
<field name="route_ids"
|
||||||
|
eval="[ref('stock.route_warehouse0_mto'), ref('purchase_stock.route_warehouse0_buy')]"/>
|
||||||
|
<field name="tracking">serial</field>
|
||||||
|
<field name="detailed_type">product</field>
|
||||||
|
<field name="uom_id" ref="uom.product_uom_unit"/>
|
||||||
|
<field name="uom_po_id" ref="uom.product_uom_unit"/>
|
||||||
|
<field name="company_id" ref="base.main_company"/>
|
||||||
|
<!-- <field name="categ_type">成品</field> -->
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_template_outsourcing" model="product.template">
|
||||||
|
<field name="name">成品委外加工模板</field>
|
||||||
|
<field name="active" eval="False"/>
|
||||||
|
<field name="categ_id" ref="sf_dlm.product_category_finished_sf"/>
|
||||||
|
<field name="route_ids"
|
||||||
|
eval="[ref('stock.route_warehouse0_mto'), ref('purchase_stock.route_warehouse0_buy'), ref('mrp_subcontracting.route_resupply_subcontractor_mto')]"/>
|
||||||
|
<field name="tracking">serial</field>
|
||||||
|
<field name="detailed_type">product</field>
|
||||||
|
<field name="uom_id" ref="uom.product_uom_unit"/>
|
||||||
|
<field name="uom_po_id" ref="uom.product_uom_unit"/>
|
||||||
|
<field name="company_id" ref="base.main_company"/>
|
||||||
|
<!-- <field name="categ_type">成品</field> -->
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_template_default" model="product.template">
|
||||||
|
<field name="name">成品初始化模板</field>
|
||||||
|
<field name="active" eval="False"/>
|
||||||
|
<field name="categ_id" ref="sf_dlm.product_category_finished_sf"/>
|
||||||
|
<field name="route_ids" eval="[]"/>
|
||||||
|
<field name="tracking">serial</field>
|
||||||
|
<field name="detailed_type">product</field>
|
||||||
|
<field name="uom_id" ref="uom.product_uom_unit"/>
|
||||||
|
<field name="uom_po_id" ref="uom.product_uom_unit"/>
|
||||||
|
<field name="company_id" ref="base.main_company"/>
|
||||||
|
<!-- <field name="categ_type">成品</field> -->
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<!-- 供应商信息(业务平台),由于数据是python创建,只能指定ID -->
|
||||||
|
<record id="product_supplierinfo_bfm" model="product.supplierinfo">
|
||||||
|
<field name="partner_id" eval="91"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_template_embryo_customer_provided" model="product.template">
|
||||||
|
<field name="name">坯料客供料模板</field>
|
||||||
|
<field name="active" eval="False"/>
|
||||||
|
<field name="categ_id" ref="sf_dlm.product_category_embryo_sf"/>
|
||||||
|
<field name="route_ids" eval="[
|
||||||
|
ref('stock.route_warehouse0_mto'),
|
||||||
|
ref('mrp_subcontracting.route_resupply_subcontractor_mto'),
|
||||||
|
ref('jikimo_sale_multiple_supply_methods.route_material_processing')]"/>
|
||||||
|
<field name="sale_ok">false</field>
|
||||||
|
<field name="tracking">serial</field>
|
||||||
|
<field name="detailed_type">product</field>
|
||||||
|
<field name="uom_id" ref="uom.product_uom_unit"/>
|
||||||
|
<field name="uom_po_id" ref="uom.product_uom_unit"/>
|
||||||
|
<field name="company_id" ref="base.main_company"/>
|
||||||
|
<!-- <field name="categ_type">坯料</field> -->
|
||||||
|
<field name="seller_ids" eval="[ref('jikimo_sale_multiple_supply_methods.product_supplierinfo_bfm')]"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="sf_dlm.product_embryo_sf_self_machining" model="product.product">
|
||||||
|
<field name="is_manual_processing">true</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="sf_dlm.product_embryo_sf_self_machining" model="product.product">
|
||||||
|
<field name="route_ids" eval="[(4, ref('mrp_subcontracting.route_resupply_subcontractor_mto'))]"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="sf_dlm.product_embryo_sf_purchase" model="product.product">
|
||||||
|
<field name="route_ids" eval="[(4, ref('mrp_subcontracting.route_resupply_subcontractor_mto'))]"/>
|
||||||
|
</record>
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
32
jikimo_sale_multiple_supply_methods/data/stock_routes.xml
Normal file
32
jikimo_sale_multiple_supply_methods/data/stock_routes.xml
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<odoo>
|
||||||
|
<data noupdate="1">
|
||||||
|
<record id="route_material_processing" model="stock.route">
|
||||||
|
<field name="name">带料加工</field>
|
||||||
|
<field name="product_selectable">true</field>
|
||||||
|
<field name="warehouse_selectable">true</field>
|
||||||
|
<field name="warehouse_ids" eval="[ref('stock.warehouse0')]"/>
|
||||||
|
<field name="sequence">16</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="material_picking_in" model="stock.picking.type">
|
||||||
|
<field name="name">客供料入库</field>
|
||||||
|
<field name="code">incoming</field>
|
||||||
|
<field name="active">true</field>
|
||||||
|
<field name="company_id" ref="base.main_company"/>
|
||||||
|
<field name="sequence_code">DL</field>
|
||||||
|
<field name="warehouse_id" ref="stock.warehouse0"/>
|
||||||
|
<field name="default_location_src_id" ref="stock.stock_location_customers"/>
|
||||||
|
<field name="default_location_dest_id" eval="25"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="rule_material_receiving" model="stock.rule">
|
||||||
|
<field name="name">带料收货</field>
|
||||||
|
<field name="route_id" ref="route_material_processing"/>
|
||||||
|
<field name="location_dest_id" ref="stock.stock_location_company"/>
|
||||||
|
<field name="location_src_id" ref="stock.stock_location_customers"/>
|
||||||
|
<field name="picking_type_id" ref="material_picking_in"/>
|
||||||
|
<field name="action">pull</field>
|
||||||
|
</record>
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
4
jikimo_sale_multiple_supply_methods/models/__init__.py
Normal file
4
jikimo_sale_multiple_supply_methods/models/__init__.py
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from . import product_template
|
||||||
|
from . import sale_order
|
||||||
|
from . import mrp_bom
|
||||||
13
jikimo_sale_multiple_supply_methods/models/mrp_bom.py
Normal file
13
jikimo_sale_multiple_supply_methods/models/mrp_bom.py
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
from odoo import models, fields
|
||||||
|
|
||||||
|
class MrpBom(models.Model):
|
||||||
|
_inherit = 'mrp.bom'
|
||||||
|
|
||||||
|
# 业务平台分配工厂后在智能工厂先创建销售订单再创建该产品后再次进行创建bom
|
||||||
|
def bom_create(self, product, bom_type, product_type):
|
||||||
|
bom_id = super(MrpBom, self).bom_create(product, bom_type, product_type)
|
||||||
|
|
||||||
|
# 成品的供应商从模板中获取
|
||||||
|
if product_type == 'product':
|
||||||
|
bom_id.subcontractor_id = product.product_tmpl_id.seller_ids.partner_id.id
|
||||||
|
return bom_id
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
from odoo import models, fields, api
|
||||||
|
|
||||||
|
class ProductTemplate(models.Model):
|
||||||
|
_inherit = 'product.template'
|
||||||
|
|
||||||
|
is_manual_processing = fields.Boolean(string='人工线下加工')
|
||||||
|
is_customer_provided = fields.Boolean(string='客供料')
|
||||||
|
|
||||||
|
def copy_template(self, product_template_id):
|
||||||
|
if not isinstance(product_template_id, ProductTemplate):
|
||||||
|
raise ValueError('%s必须是ProductTemplate类型' % product_template_id)
|
||||||
|
|
||||||
|
self.route_ids = product_template_id.route_ids
|
||||||
|
self.categ_id = product_template_id.categ_id
|
||||||
|
self.invoice_policy = product_template_id.invoice_policy
|
||||||
|
self.detailed_type = product_template_id.detailed_type
|
||||||
|
self.purchase_ok = product_template_id.purchase_ok
|
||||||
|
self.uom_id = product_template_id.uom_id
|
||||||
|
self.uom_po_id = product_template_id.uom_po_id
|
||||||
|
self.company_id = product_template_id.company_id
|
||||||
|
self.single_manufacturing = product_template_id.single_manufacturing
|
||||||
|
self.tracking = product_template_id.tracking
|
||||||
|
self.is_bfm = product_template_id.is_bfm
|
||||||
|
self.is_manual_processing = product_template_id.is_manual_processing
|
||||||
|
# 复制 seller_ids
|
||||||
|
self.seller_ids = [(0, 0, {'partner_id': seller.partner_id.id, 'delay': 1.0}) for seller in product_template_id.seller_ids]
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
from datetime import datetime
|
||||||
|
from odoo import models
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import base64
|
||||||
|
import hashlib
|
||||||
|
|
||||||
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class QuickEasyOrder(models.Model):
|
||||||
|
_inherit = 'quick.easy.order'
|
||||||
|
|
||||||
|
def distribute_to_factory(self, obj):
|
||||||
|
"""
|
||||||
|
多供货方式,重写派单到工厂
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
_logger.info('---------派单到工厂-------')
|
||||||
|
res = {'bfm_process_order_list': []}
|
||||||
|
for item in obj:
|
||||||
|
attachment = item.upload_model_file[0]
|
||||||
|
base64_data = base64.b64encode(attachment.datas)
|
||||||
|
base64_datas = base64_data.decode('utf-8')
|
||||||
|
barcode = hashlib.sha1(base64_datas.encode('utf-8')).hexdigest()
|
||||||
|
# logging.info('model_file-size: %s' % len(item.model_file))
|
||||||
|
res['bfm_process_order_list'].append({
|
||||||
|
'model_long': item.model_length,
|
||||||
|
'model_width': item.model_width,
|
||||||
|
'model_height': item.model_height,
|
||||||
|
'model_volume': item.model_volume,
|
||||||
|
'model_machining_precision': item.machining_precision,
|
||||||
|
'model_name': attachment.name,
|
||||||
|
'model_data': base64_datas,
|
||||||
|
'model_file': base64.b64encode(item.model_file).decode('utf-8'),
|
||||||
|
'texture_code': item.material_id.materials_no,
|
||||||
|
'texture_type_code': item.material_model_id.materials_no,
|
||||||
|
# 'surface_process_code': self.env['jikimo.surface.process']._json_surface_process_code(item),
|
||||||
|
'process_parameters_code': self.env[
|
||||||
|
'sf.production.process.parameter']._json_production_process_item_code(
|
||||||
|
item),
|
||||||
|
'price': item.price,
|
||||||
|
'number': item.quantity,
|
||||||
|
'total_amount': item.price,
|
||||||
|
'remark': '',
|
||||||
|
'manual_quotation': True,
|
||||||
|
'barcode': barcode,
|
||||||
|
'part_number': item.part_drawing_number,
|
||||||
|
'machining_drawings_name': '',
|
||||||
|
'quality_standard_name': '',
|
||||||
|
'machining_drawings_mimetype': '',
|
||||||
|
'quality_standard_mimetype': '',
|
||||||
|
'machining_drawings': item.machining_drawings,
|
||||||
|
'quality_standard': '',
|
||||||
|
'part_name': '',
|
||||||
|
})
|
||||||
|
company_id = self.env.ref('base.main_company').sudo()
|
||||||
|
product_id = self.env.ref('jikimo_sale_multiple_supply_methods.product_template_default').sudo().with_context(active_test=False).product_variant_id
|
||||||
|
# user_id = request.env.ref('base.user_admin').sudo()
|
||||||
|
order_id = self.env['sale.order'].sale_order_create(company_id, 'XXXXX', 'XXXXX', 'XXXXX',
|
||||||
|
str(datetime.now()), '现结', '支付宝', state='draft')
|
||||||
|
order_id.default_code = obj.name
|
||||||
|
i = 1
|
||||||
|
for item in res['bfm_process_order_list']:
|
||||||
|
product = self.env['product.template'].sudo().product_create(product_id, item, order_id,
|
||||||
|
obj.name, i)
|
||||||
|
order_id.with_user(self.env.ref("base.user_admin")).sale_order_create_line(product, item)
|
||||||
|
return order_id
|
||||||
|
except Exception as e:
|
||||||
|
return UserError('工厂创建销售订单和产品失败,请联系管理员')
|
||||||
157
jikimo_sale_multiple_supply_methods/models/sale_order.py
Normal file
157
jikimo_sale_multiple_supply_methods/models/sale_order.py
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
import logging
|
||||||
|
import json
|
||||||
|
from odoo import models, fields, api
|
||||||
|
from odoo.exceptions import UserError
|
||||||
|
|
||||||
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
class SaleOrder(models.Model):
|
||||||
|
_inherit = 'sale.order'
|
||||||
|
|
||||||
|
state = fields.Selection([
|
||||||
|
('draft', "报价"),
|
||||||
|
('sent', "报价已发送"),
|
||||||
|
('supply method', "供货方式待确认"),
|
||||||
|
('sale', "销售订单"),
|
||||||
|
('done', "已锁定"),
|
||||||
|
('cancel', "已取消"),
|
||||||
|
])
|
||||||
|
|
||||||
|
def confirm_to_supply_method(self):
|
||||||
|
self.state = 'supply method'
|
||||||
|
|
||||||
|
def action_confirm(self):
|
||||||
|
# 判断是否所有产品都选择了供货方式
|
||||||
|
filter_line = self.order_line.filtered(lambda line: not line.supply_method)
|
||||||
|
if filter_line:
|
||||||
|
raise UserError('当前订单内(%s)产品未选择路线,请选择后重试' % ','.join(filter_line.mapped('product_id.name')))
|
||||||
|
|
||||||
|
for line in self.order_line:
|
||||||
|
bom_type = ''
|
||||||
|
# 根据供货方式修改成品模板
|
||||||
|
if line.supply_method == 'automation':
|
||||||
|
bom_type = 'normal'
|
||||||
|
product_template_id = self.env.ref('sf_dlm.product_template_sf').sudo().product_tmpl_id
|
||||||
|
elif line.supply_method == 'outsourcing':
|
||||||
|
bom_type = 'subcontract'
|
||||||
|
product_template_id = self.env.ref('jikimo_sale_multiple_supply_methods.product_template_outsourcing').sudo()
|
||||||
|
elif line.supply_method == 'purchase':
|
||||||
|
product_template_id = self.env.ref('jikimo_sale_multiple_supply_methods.product_template_purchase').sudo()
|
||||||
|
elif line.supply_method == 'manual':
|
||||||
|
bom_type = 'normal'
|
||||||
|
product_template_id = self.env.ref('jikimo_sale_multiple_supply_methods.product_template_manual_processing').sudo()
|
||||||
|
|
||||||
|
# 复制成品模板上的属性
|
||||||
|
line.product_id.product_tmpl_id.copy_template(product_template_id)
|
||||||
|
|
||||||
|
order_id = self
|
||||||
|
product = line.product_id
|
||||||
|
# 拼接方法需要的item结构
|
||||||
|
item = {
|
||||||
|
'texture_code': product.materials_id.materials_no,
|
||||||
|
'texture_type_code': product.materials_type_id.materials_no,
|
||||||
|
'model_long': product.length,
|
||||||
|
'model_width': product.width,
|
||||||
|
'model_height': product.height,
|
||||||
|
'price': product.list_price,
|
||||||
|
'embryo_redundancy_id': line.embryo_redundancy_id,
|
||||||
|
}
|
||||||
|
# 获取成品名结尾-n的n
|
||||||
|
product_seria = int(product.name.split('-')[-1])
|
||||||
|
# 成品供货方式为采购则不生成bom
|
||||||
|
if line.supply_method != 'purchase':
|
||||||
|
bom_data = self.env['mrp.bom'].with_user(self.env.ref("base.user_admin")).get_bom(product)
|
||||||
|
_logger.info('bom_data:%s' % bom_data)
|
||||||
|
if bom_data:
|
||||||
|
bom = self.env['mrp.bom'].with_user(self.env.ref("base.user_admin")).bom_create(product, 'normal', False)
|
||||||
|
bom.with_user(self.env.ref("base.user_admin")).bom_create_line_has(bom_data)
|
||||||
|
else:
|
||||||
|
# 当成品上带有客供料选项时,生成坯料时选择“客供料”路线
|
||||||
|
if line.embryo_redundancy_id:
|
||||||
|
# 将成品模板的内容复制到成品上
|
||||||
|
customer_provided_embryo = self.env.ref('jikimo_sale_multiple_supply_methods.product_template_embryo_customer_provided').sudo()
|
||||||
|
# 创建坯料,客供料的批量不需要创建bom
|
||||||
|
material_customer_provided_embryo = self.env['product.template'].sudo().no_bom_product_create(
|
||||||
|
customer_provided_embryo.with_context(active_test=False).product_variant_id,
|
||||||
|
item,
|
||||||
|
order_id, 'material_customer_provided', product_seria, product)
|
||||||
|
# 成品配置bom
|
||||||
|
product_bom_material_customer_provided = self.env['mrp.bom'].with_user(
|
||||||
|
self.env.ref("base.user_admin")).bom_create(
|
||||||
|
product, bom_type, 'product')
|
||||||
|
product_bom_material_customer_provided.with_user(self.env.ref("base.user_admin")).bom_create_line_has(
|
||||||
|
material_customer_provided_embryo)
|
||||||
|
elif line.product_id.materials_type_id.gain_way == '自加工':
|
||||||
|
self_machining_id = self.env.ref('sf_dlm.product_embryo_sf_self_machining').sudo()
|
||||||
|
# 创建坯料
|
||||||
|
self_machining_embryo = self.env['product.template'].sudo().no_bom_product_create(
|
||||||
|
self_machining_id,
|
||||||
|
item,
|
||||||
|
order_id, 'self_machining', product_seria, product)
|
||||||
|
# 创建坯料的bom
|
||||||
|
self_machining_bom = self.env['mrp.bom'].with_user(
|
||||||
|
self.env.ref("base.user_admin")).bom_create(
|
||||||
|
self_machining_embryo, 'normal', False)
|
||||||
|
# 创建坯料里bom的组件
|
||||||
|
self_machining_bom_line = self_machining_bom.with_user(
|
||||||
|
self.env.ref("base.user_admin")).bom_create_line(
|
||||||
|
self_machining_embryo)
|
||||||
|
if not self_machining_bom_line:
|
||||||
|
raise UserError('该订单模型的材料型号暂未有原材料,请先配置再进行分配')
|
||||||
|
# 产品配置bom
|
||||||
|
product_bom_self_machining = self.env['mrp.bom'].with_user(
|
||||||
|
self.env.ref("base.user_admin")).bom_create(
|
||||||
|
product, bom_type, 'product')
|
||||||
|
product_bom_self_machining.with_user(self.env.ref("base.user_admin")).bom_create_line_has(
|
||||||
|
self_machining_embryo)
|
||||||
|
elif line.product_id.materials_type_id.gain_way == '外协':
|
||||||
|
outsource_id = self.env.ref('sf_dlm.product_embryo_sf_outsource').sudo()
|
||||||
|
# 创建坯料
|
||||||
|
outsource_embryo = self.env['product.template'].sudo().no_bom_product_create(outsource_id,
|
||||||
|
item,
|
||||||
|
order_id,
|
||||||
|
'subcontract',
|
||||||
|
product_seria, product)
|
||||||
|
if outsource_embryo == -3:
|
||||||
|
raise UserError('该订单模型的材料型号暂未设置获取方式和供应商,请先配置再进行分配')
|
||||||
|
# 创建坯料的bom
|
||||||
|
outsource_bom = self.env['mrp.bom'].with_user(self.env.ref("base.user_admin")).bom_create(
|
||||||
|
outsource_embryo,
|
||||||
|
'subcontract', True)
|
||||||
|
# 创建坯料的bom的组件
|
||||||
|
outsource_bom_line = outsource_bom.with_user(
|
||||||
|
self.env.ref("base.user_admin")).bom_create_line(outsource_embryo)
|
||||||
|
if not outsource_bom_line:
|
||||||
|
raise UserError('该订单模型的材料型号暂未有原材料,请先配置再进行分配')
|
||||||
|
# 产品配置bom
|
||||||
|
product_bom_outsource = self.env['mrp.bom'].with_user(
|
||||||
|
self.env.ref("base.user_admin")).bom_create(product, bom_type, 'product')
|
||||||
|
product_bom_outsource.with_user(self.env.ref("base.user_admin")).bom_create_line_has(
|
||||||
|
outsource_embryo)
|
||||||
|
elif line.product_id.materials_type_id.gain_way == '采购':
|
||||||
|
purchase_id = self.env.ref('sf_dlm.product_embryo_sf_purchase').sudo()
|
||||||
|
purchase_embryo = self.env['product.template'].sudo().no_bom_product_create(purchase_id,
|
||||||
|
item,
|
||||||
|
order_id,
|
||||||
|
'purchase', product_seria,
|
||||||
|
product)
|
||||||
|
if purchase_embryo == -3:
|
||||||
|
raise UserError('该订单模型的材料型号暂未设置获取方式和供应商,请先配置再进行分配')
|
||||||
|
else:
|
||||||
|
# 产品配置bom
|
||||||
|
product_bom_purchase = self.env['mrp.bom'].with_user(
|
||||||
|
self.env.ref("base.user_admin")).bom_create(product, bom_type, 'product')
|
||||||
|
product_bom_purchase.with_user(self.env.ref("base.user_admin")).bom_create_line_has(
|
||||||
|
purchase_embryo)
|
||||||
|
return super(SaleOrder, self).action_confirm()
|
||||||
|
|
||||||
|
class SaleOrderLine(models.Model):
|
||||||
|
_inherit = 'sale.order.line'
|
||||||
|
|
||||||
|
# 供货方式
|
||||||
|
supply_method = fields.Selection([
|
||||||
|
('automation', "自动化产线加工"),
|
||||||
|
('manual', "人工线下加工"),
|
||||||
|
('purchase', "外购"),
|
||||||
|
('outsourcing', "委外加工"),
|
||||||
|
], string='供货方式')
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||||
|
access_sale_order_group_production_engineer,sale.order_group_production_engineer,sale.model_sale_order,sf_base.group_production_engineer,1,1,0,0
|
||||||
|
access_sale_order_line_group_production_engineer,sale_order_line_group_production_engineer,sale.model_sale_order_line,sf_base.group_production_engineer,1,1,0,0
|
||||||
|
access_product_product_group_production_engineer,product_product_group_production_engineer,product.model_product_product,sf_base.group_production_engineer,1,0,0,0
|
||||||
|
access_product_template_group_production_engineer,product_template_group_production_engineer,product.model_product_template,sf_base.group_production_engineer,1,0,0,0
|
||||||
|
access_stock_picking_group_production_engineer,stock_picking_group_production_engineer,stock.model_stock_picking,sf_base.group_production_engineer,1,0,0,0
|
||||||
|
access_stock_move_group_production_engineer,stock_move_group_production_engineer,stock.model_stock_move,sf_base.group_production_engineer,1,0,0,0
|
||||||
|
access_mrp_bom_group_production_engineer,mrp_bom_group_production_engineer,mrp.model_mrp_bom,sf_base.group_production_engineer,1,0,0,0
|
||||||
|
@@ -0,0 +1,16 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<odoo>
|
||||||
|
<!-- 由于该模块不能依赖sf_dlm_management, 该功能只能在sf_dlm_management中实现,并且依赖该模块-->
|
||||||
|
<record id="view_product_product_form_inherit_sf" model="ir.ui.view">
|
||||||
|
<field name="name">view.product.template.form.inherit.sf</field>
|
||||||
|
<field name="model">product.template</field>
|
||||||
|
<field name="inherit_id" ref="sf_dlm_management.view_sale_product_template_form_inherit_sf"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//field[@name='manual_quotation']" position="after">
|
||||||
|
<field name="is_customer_provided" attrs="{'invisible': [('categ_type', 'not in', ['成品', '坯料'])], 'readonly': True}" />
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
</odoo>
|
||||||
|
|
||||||
|
|
||||||
108
jikimo_sale_multiple_supply_methods/views/sale_order_views.xml
Normal file
108
jikimo_sale_multiple_supply_methods/views/sale_order_views.xml
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<odoo>
|
||||||
|
<record id="view_order_form_inherit_sf" model="ir.ui.view">
|
||||||
|
<field name="name">view.sale.order.form.inherit.sf</field>
|
||||||
|
<field name="inherit_id" ref="sale_stock.view_order_form_inherit_sale_stock_qty"/>
|
||||||
|
<field name="model">sale.order</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//page/field[@name='order_line']/form/group/group/div[@name='ordered_qty']/widget[@name='qty_at_date_widget']" position="replace">
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//page/field[@name='order_line']/tree/widget[@name='qty_at_date_widget']" position="replace">
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="view_order_form_inherit_supply_method" model="ir.ui.view">
|
||||||
|
<field name="name">view.sale.order.form.inherit.supply.method</field>
|
||||||
|
<field name="inherit_id" ref="sf_sale.view_sale_order_form_inherit_sf"/>
|
||||||
|
<field name="model">sale.order</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//header/button[@name='action_confirm'][last()]" position="attributes">
|
||||||
|
<attribute name="attrs">{'invisible': [('state', '!=', 'draft')]}</attribute>
|
||||||
|
<attribute name="name">confirm_to_supply_method</attribute>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//header/button[@name='confirm_to_supply_method']" position="before">
|
||||||
|
<button name="action_confirm" string="供货方式确认" type="object" attrs="{'invisible': [('state', '!=', 'supply method')]}" confirm="确认供货方式"/>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//header/field[@name='state']" position="attributes">
|
||||||
|
<attribute name="statusbar_visible">draft,sent,supply method,sale</attribute>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//page/field[@name='order_line']/tree/field[@name='remark']" position="before">
|
||||||
|
<field name="supply_method" attrs="{'invisible': [('state', '=', 'draft')], 'required': [('state', '=', 'supply method')]}" />
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//header/button[@name='action_cancel']" position="attributes">
|
||||||
|
<attribute name="attrs">{'invisible': [('state', '!=', 'draft')]}</attribute>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//header/button[@name='action_cancel']" position="attributes">
|
||||||
|
<attribute name="attrs">{'invisible': [('state', '!=', 'draft')]}</attribute>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//header/button[@name='action_quotation_send'][5]" position="attributes">
|
||||||
|
<attribute name="attrs">{'invisible': ['|','&',('check_status', '!=', 'approved'),('state', 'in', ['draft','cancel','supply method']),'&',('check_status', '=', 'approved'),('state', 'in', ['sale','cancel','supply method'])]}</attribute>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="jikimo_sale_order_view_search_inherit_quotation_supply_method" model="ir.ui.view">
|
||||||
|
<field name="name">jikimo.sale.order.search.inherit.quotation.supply.method</field>
|
||||||
|
<field name="model">sale.order</field>
|
||||||
|
<field name="mode">primary</field>
|
||||||
|
<field name="inherit_id" ref="sale.sale_order_view_search_inherit_quotation"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//filter[@name='my_quotation']" position="attributes">
|
||||||
|
<attribute name="domain">[('state', 'in', ('draft', 'sent')), ('user_id', '=', uid)]</attribute>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//filter[@name='draft']" position="after">
|
||||||
|
<filter string="供货方式待确认" name="supply_method" domain="[('state', '=', 'supply method')]"/>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="sale.action_quotations_with_onboarding" model="ir.actions.act_window">
|
||||||
|
<field name="search_view_id" ref="jikimo_sale_order_view_search_inherit_quotation_supply_method"/>
|
||||||
|
<field name="context">{'search_default_draft': 1}</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="action_quotations_supply_method" model="ir.actions.act_window">
|
||||||
|
<field name="name">报价单</field>
|
||||||
|
<field name="type">ir.actions.act_window</field>
|
||||||
|
<field name="res_model">sale.order</field>
|
||||||
|
<field name="view_mode">tree,kanban,form,calendar,pivot,graph,activity</field>
|
||||||
|
<field name="view_id" ref="sale.view_quotation_tree_with_onboarding"/>
|
||||||
|
<field name="search_view_id" ref="jikimo_sale_order_view_search_inherit_quotation_supply_method"/>
|
||||||
|
<field name="context">{'search_default_supply_method': 1}</field>
|
||||||
|
<field name="help" type="html">
|
||||||
|
<p class="o_view_nocontent_smiling_face">
|
||||||
|
Create a new quotation, the first step of a new sale!
|
||||||
|
</p><p>
|
||||||
|
Once the quotation is confirmed by the customer, it becomes a sales order.<br/> You will be able to create an invoice and collect the payment.
|
||||||
|
</p>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
<menuitem
|
||||||
|
id="sale.sale_menu_root"
|
||||||
|
groups="sf_base.group_production_engineer,sf_base.group_sale_director,sf_base.group_sale_salemanager"
|
||||||
|
/>
|
||||||
|
<!--供货路线专员菜单-->
|
||||||
|
<menuitem
|
||||||
|
id="sale_order_menu_quotations_supply_method"
|
||||||
|
name="报价"
|
||||||
|
action="action_quotations_supply_method"
|
||||||
|
parent="sale.sale_order_menu"
|
||||||
|
groups="sf_base.group_production_engineer"
|
||||||
|
sequence="2"/>
|
||||||
|
|
||||||
|
<record id="sale.menu_sale_order" model="ir.ui.menu">
|
||||||
|
<field name="groups_id" eval="[(4, ref('sf_base.group_production_engineer'))]"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="sale.report_sales_team" model="ir.ui.menu">
|
||||||
|
<field name="groups_id" eval="[(4, ref('sf_base.group_production_engineer'))]"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="sale.res_partner_menu" model="ir.ui.menu">
|
||||||
|
<field name="groups_id" eval="[(4, ref('sf_base.group_production_engineer'))]"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</odoo>
|
||||||
BIN
jikimo_system_order/static/description/系统工单.png
Normal file
BIN
jikimo_system_order/static/description/系统工单.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 533 B |
@@ -234,7 +234,7 @@
|
|||||||
</record>
|
</record>
|
||||||
|
|
||||||
|
|
||||||
<menuitem name="系统工单" id="work_order_1_list" web_icon="jikimo_system_order,static/description/icon.png"/>
|
<menuitem name="系统工单" id="work_order_1_list" web_icon="jikimo_system_order,static/description/系统工单.png"/>
|
||||||
<menuitem name="工单" id="work_order" parent="work_order_1_list" action="system_order"/>
|
<menuitem name="工单" id="work_order" parent="work_order_1_list" action="system_order"/>
|
||||||
<menuitem name="工单模板" id="work_order_template" parent="work_order_1_list" action="work_template" groups="jikimo_system_order.group_operations_permissions_rwc"/>
|
<menuitem name="工单模板" id="work_order_template" parent="work_order_1_list" action="work_template" groups="jikimo_system_order.group_operations_permissions_rwc"/>
|
||||||
<menuitem name="工单分类" id="work_order_type" parent="work_order_1_list" action="classify" groups="jikimo_system_order.group_operations_permissions_rwc"/>
|
<menuitem name="工单分类" id="work_order_type" parent="work_order_1_list" action="classify" groups="jikimo_system_order.group_operations_permissions_rwc"/>
|
||||||
|
|||||||
4
jikimo_workorder_exception/__init__.py
Normal file
4
jikimo_workorder_exception/__init__.py
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from . import models
|
||||||
|
from . import controllers
|
||||||
21
jikimo_workorder_exception/__manifest__.py
Normal file
21
jikimo_workorder_exception/__manifest__.py
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||||
|
{
|
||||||
|
'name': '机企猫 工单异常记录',
|
||||||
|
'version': '1.0',
|
||||||
|
'summary': '记录工单的异常日志',
|
||||||
|
'sequence': 1,
|
||||||
|
'category': 'sf',
|
||||||
|
'website': 'https://www.sf.jikimo.com',
|
||||||
|
'depends': ['sf_manufacturing', 'sf_mrs_connect'],
|
||||||
|
'data': [
|
||||||
|
'views/mrp_workorder_views.xml',
|
||||||
|
'security/ir.model.access.csv',
|
||||||
|
],
|
||||||
|
'demo': [
|
||||||
|
],
|
||||||
|
'license': 'LGPL-3',
|
||||||
|
'installable': True,
|
||||||
|
'application': False,
|
||||||
|
'auto_install': False,
|
||||||
|
}
|
||||||
1
jikimo_workorder_exception/controllers/__init__.py
Normal file
1
jikimo_workorder_exception/controllers/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
from . import main
|
||||||
89
jikimo_workorder_exception/controllers/main.py
Normal file
89
jikimo_workorder_exception/controllers/main.py
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
from odoo import http, fields
|
||||||
|
from odoo.http import request
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
|
from odoo.addons.sf_mrs_connect.controllers.controllers import Sf_Mrs_Connect
|
||||||
|
from odoo.addons.sf_manufacturing.controllers.controllers import Manufacturing_Connect
|
||||||
|
|
||||||
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
class WorkorderExceptionConroller(http.Controller):
|
||||||
|
|
||||||
|
@http.route('/AutoDeviceApi/BillError', type='json', auth='public', methods=['GET', 'POST'], csrf=False,
|
||||||
|
cors="*")
|
||||||
|
def workder_exception(self, **kw):
|
||||||
|
"""
|
||||||
|
记录工单异常
|
||||||
|
:param kw:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
_logger.info('workder_exception:%s' % kw)
|
||||||
|
try:
|
||||||
|
res = {'Succeed': True, 'ErrorCode': 0, 'Error': ''}
|
||||||
|
datas = request.httprequest.data
|
||||||
|
ret = json.loads(datas)
|
||||||
|
if not ret.get('RfidCode') or not ret.get('coding'):
|
||||||
|
res = {'Succeed': False, 'ErrorCode': 400, 'Error': '参数错误'}
|
||||||
|
return json.JSONEncoder().encode(res)
|
||||||
|
|
||||||
|
# 通过RfidCode获取就绪的CNC工单
|
||||||
|
workorder = request.env['mrp.workorder'].sudo().search([
|
||||||
|
('rfid_code', '=', ret['RfidCode']),
|
||||||
|
('routing_type', '=', 'CNC加工'),
|
||||||
|
])
|
||||||
|
if not workorder:
|
||||||
|
res = {'Succeed': False, 'ErrorCode': 401, 'Error': '无效的工单'}
|
||||||
|
return json.JSONEncoder().encode(res)
|
||||||
|
|
||||||
|
# 创建工单异常记录,关联工单
|
||||||
|
request.env['jikimo.workorder.exception'].sudo().create({
|
||||||
|
'workorder_id': workorder.id,
|
||||||
|
'exception_code': ret.get('coding'),
|
||||||
|
'exception_content': ret.get('Error', '')
|
||||||
|
})
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
res = {'Succeed': False, 'ErrorCode': 202, 'Error': e}
|
||||||
|
_logger.info('workder_exception error:%s' % e)
|
||||||
|
return json.JSONEncoder().encode(res)
|
||||||
|
|
||||||
|
|
||||||
|
class SfMrsConnectController(Sf_Mrs_Connect):
|
||||||
|
|
||||||
|
@http.route('/api/cnc_processing/create', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False,
|
||||||
|
cors="*")
|
||||||
|
def get_cnc_processing_create(self, **kw):
|
||||||
|
"""
|
||||||
|
更新工单异常记录【'YC001', 'YC004'】
|
||||||
|
"""
|
||||||
|
res = super(SfMrsConnectController, self).get_cnc_processing_create(**kw)
|
||||||
|
# 如果有未完成的YC0001、YC0004异常记录,则标记为完成
|
||||||
|
res = json.loads(res)
|
||||||
|
_logger.info('已进入工单异常:%s' % res)
|
||||||
|
if res.get('production_ids'):
|
||||||
|
try:
|
||||||
|
productions = request.env['mrp.production'].sudo().search([('id', 'in', res.get('production_ids'))])
|
||||||
|
if productions.workorder_ids:
|
||||||
|
productions.workorder_ids.handle_exception(['YC0001', 'YC0004'])
|
||||||
|
except Exception as e:
|
||||||
|
_logger.info('更新工单异常记录失败:%s' % e)
|
||||||
|
return json.JSONEncoder().encode(res)
|
||||||
|
|
||||||
|
class ManufactruingController(Manufacturing_Connect):
|
||||||
|
|
||||||
|
@http.route('/AutoDeviceApi/FeedBackStart', type='json', auth='none', methods=['GET', 'POST'], csrf=False,
|
||||||
|
cors="*")
|
||||||
|
def button_Work_START(self, **kw):
|
||||||
|
"""
|
||||||
|
更新工单异常记录【'YC0002', 'YC0003'】
|
||||||
|
"""
|
||||||
|
res = super(ManufactruingController, self).button_Work_START(**kw)
|
||||||
|
res = json.loads(res)
|
||||||
|
_logger.info('已进入工单异常:%s' % res)
|
||||||
|
if res.get('workorder_id'):
|
||||||
|
try:
|
||||||
|
workorder = request.env['mrp.workorder'].sudo().browse(int(res.get('workorder_id')))
|
||||||
|
workorder.handle_exception(['YC0002', 'YC0003'])
|
||||||
|
except Exception as e:
|
||||||
|
_logger.info('更新工单异常记录失败:%s' % e)
|
||||||
|
return json.JSONEncoder().encode(res)
|
||||||
3
jikimo_workorder_exception/models/__init__.py
Normal file
3
jikimo_workorder_exception/models/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from . import jikimo_workorder_exception
|
||||||
|
from . import mrp_workorder
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
from odoo import models, fields
|
||||||
|
|
||||||
|
|
||||||
|
class JikimoWorkorderException(models.Model):
|
||||||
|
_name = 'jikimo.workorder.exception'
|
||||||
|
_description = '工单异常记录'
|
||||||
|
_order = 'id desc'
|
||||||
|
|
||||||
|
workorder_id = fields.Many2one('mrp.workorder', string='工单')
|
||||||
|
exception_code = fields.Char('异常编码')
|
||||||
|
exception_content = fields.Char('反馈的异常/问题信息')
|
||||||
|
completion_time = fields.Datetime('处理完成时间')
|
||||||
|
state = fields.Selection([('pending', '进行中'), ('done', '已处理')], string='状态', default='pending')
|
||||||
|
|
||||||
40
jikimo_workorder_exception/models/mrp_workorder.py
Normal file
40
jikimo_workorder_exception/models/mrp_workorder.py
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
from odoo import models, fields
|
||||||
|
import logging
|
||||||
|
|
||||||
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
class MrpWorkorder(models.Model):
|
||||||
|
_inherit = 'mrp.workorder'
|
||||||
|
|
||||||
|
exception_ids = fields.One2many('jikimo.workorder.exception', 'workorder_id', string='工单异常记录')
|
||||||
|
|
||||||
|
def write(self, values):
|
||||||
|
if values.get('test_results') and self.exception_ids:
|
||||||
|
pending_exception = self.exception_ids.filtered(
|
||||||
|
lambda exc: exc.state == 'pending' and exc.exception_code == 'YC0005'
|
||||||
|
)
|
||||||
|
if pending_exception:
|
||||||
|
pending_exception.write({
|
||||||
|
'completion_time': fields.Datetime.now(),
|
||||||
|
'state': 'done'
|
||||||
|
})
|
||||||
|
return super(MrpWorkorder, self).write(values)
|
||||||
|
|
||||||
|
def handle_exception(self, exception_codes):
|
||||||
|
"""
|
||||||
|
处理异常
|
||||||
|
:param exception_codes: 需要处理的异常编码列表
|
||||||
|
"""
|
||||||
|
if not isinstance(exception_codes, list):
|
||||||
|
exception_codes = [exception_codes]
|
||||||
|
if self.exception_ids:
|
||||||
|
_logger.info('workorder.exception_ids:%s' % self.exception_ids)
|
||||||
|
pending_exception = self.exception_ids.filtered(
|
||||||
|
lambda exc: exc.state == 'pending' and exc.exception_code in exception_codes
|
||||||
|
)
|
||||||
|
_logger.info('pending_exception:%s' % pending_exception)
|
||||||
|
if pending_exception:
|
||||||
|
pending_exception.write({
|
||||||
|
'completion_time': fields.Datetime.now(),
|
||||||
|
'state': 'done'
|
||||||
|
})
|
||||||
5
jikimo_workorder_exception/security/ir.model.access.csv
Normal file
5
jikimo_workorder_exception/security/ir.model.access.csv
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink"
|
||||||
|
"access_jikimo_workorder_exception","access.jikimo.workorder.exception","model_jikimo_workorder_exception","mrp.group_mrp_user",1,1,1,0
|
||||||
|
"access_jikimo_workorder_exception_group_quality","access.jikimo.workorder.exception.group_quality","model_jikimo_workorder_exception","sf_base.group_quality",1,1,1,0
|
||||||
|
"access_jikimo_workorder_exception_group_quality_director","access.jikimo.workorder.exception.group_quality_director","model_jikimo_workorder_exception","sf_base.group_quality_director",1,1,1,0
|
||||||
|
|
||||||
|
2
jikimo_workorder_exception/tests/__init__.py
Normal file
2
jikimo_workorder_exception/tests/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
from . import common
|
||||||
|
from . import test_jikimo_workorder_exception
|
||||||
48
jikimo_workorder_exception/tests/common.py
Normal file
48
jikimo_workorder_exception/tests/common.py
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||||
|
from odoo import fields, Command
|
||||||
|
from odoo.tests.common import TransactionCase, HttpCase, tagged, Form
|
||||||
|
|
||||||
|
import json
|
||||||
|
import time
|
||||||
|
import base64
|
||||||
|
from lxml import etree
|
||||||
|
|
||||||
|
@tagged('post_install', '-at_install')
|
||||||
|
class TestJikimoWorkorderExceptionCommon(TransactionCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(TestJikimoWorkorderExceptionCommon, self).setUp()
|
||||||
|
# 获取名字为“1#自动生产线”的制造中心
|
||||||
|
workcenter = self.env['mrp.workcenter'].search([('name', '=', '1#自动生产线')], limit=1)
|
||||||
|
# 创建一个产品
|
||||||
|
product_product = self.env['product.product'].create({
|
||||||
|
'name': '测试产品',
|
||||||
|
'type': 'product',
|
||||||
|
})
|
||||||
|
uom_unit = self.env.ref('uom.product_uom_unit')
|
||||||
|
# 创建一个bom
|
||||||
|
self.bom = self.env['mrp.bom'].create({
|
||||||
|
'product_id': product_product.id,
|
||||||
|
'product_tmpl_id': product_product.product_tmpl_id.id,
|
||||||
|
'product_uom_id': uom_unit.id,
|
||||||
|
'product_qty': 1.0,
|
||||||
|
'type': 'normal',
|
||||||
|
})
|
||||||
|
# 创建一个制造订单
|
||||||
|
self.production = self.env['mrp.production'].create({
|
||||||
|
'name': 'Test Production',
|
||||||
|
'product_id': product_product.id,
|
||||||
|
'bom_id': self.bom.id,
|
||||||
|
'company_id': self.env.ref('base.main_company').id,
|
||||||
|
})
|
||||||
|
# 创建一个测试工单
|
||||||
|
self.workorder = self.env['mrp.workorder'].create({
|
||||||
|
'name': 'Test order',
|
||||||
|
'workcenter_id': workcenter.id,
|
||||||
|
'product_uom_id': self.bom.product_uom_id.id,
|
||||||
|
'production_id': self.production.id,
|
||||||
|
'duration_expected': 1.0,
|
||||||
|
'rfid_code': 'test-123456',
|
||||||
|
'routing_type': 'CNC加工'
|
||||||
|
})
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
import json
|
||||||
|
from datetime import datetime
|
||||||
|
from odoo.addons.jikimo_workorder_exception.tests.common import TestJikimoWorkorderExceptionCommon
|
||||||
|
|
||||||
|
class TestJikimoWorkorderException(TestJikimoWorkorderExceptionCommon):
|
||||||
|
|
||||||
|
def test_create_exception_record(self):
|
||||||
|
exception_record = self.env['jikimo.workorder.exception'].create({
|
||||||
|
'workorder_id': self.workorder.id,
|
||||||
|
'exception_code': 'YC0001',
|
||||||
|
'exception_content': '无CNC编程'
|
||||||
|
})
|
||||||
|
|
||||||
|
self.assertTrue(exception_record)
|
||||||
|
self.assertEqual(exception_record.exception_content, '无CNC编程')
|
||||||
|
self.assertEqual(exception_record.workorder_id.id, self.workorder.id)
|
||||||
|
self.assertEqual(exception_record.exception_code, 'YC0001')
|
||||||
|
|
||||||
|
def test_handle_exception(self):
|
||||||
|
exception_record = self.env['jikimo.workorder.exception'].create({
|
||||||
|
'workorder_id': self.workorder.id,
|
||||||
|
'exception_code': 'YC0001',
|
||||||
|
'exception_content': '无CNC编程'
|
||||||
|
})
|
||||||
|
self.workorder.handle_exception('YC0001')
|
||||||
|
self.assertEqual(exception_record.state, 'done')
|
||||||
|
# 判断完成时间是否为当前分钟
|
||||||
|
self.assertEqual(exception_record.completion_time.minute, datetime.now().minute)
|
||||||
|
|
||||||
|
def test_handle_exception_with_invalid_code(self):
|
||||||
|
exception_record = self.env['jikimo.workorder.exception'].create({
|
||||||
|
'workorder_id': self.workorder.id,
|
||||||
|
'exception_code': 'YC0001',
|
||||||
|
'exception_content': '无CNC编程'
|
||||||
|
})
|
||||||
|
self.workorder.handle_exception(['YC0002', 'YC0004'])
|
||||||
|
self.assertEqual(exception_record.state, 'pending')
|
||||||
|
self.assertEqual(exception_record.completion_time, False)
|
||||||
|
|
||||||
|
|
||||||
|
def test_handle_exception_with_test_results(self):
|
||||||
|
exception_record = self.env['jikimo.workorder.exception'].create({
|
||||||
|
'workorder_id': self.workorder.id,
|
||||||
|
'exception_code': 'YC0005',
|
||||||
|
'exception_content': '工单加工失败'
|
||||||
|
})
|
||||||
|
self.workorder.write({
|
||||||
|
'test_results': '返工',
|
||||||
|
'reason': 'cutter',
|
||||||
|
'detailed_reason': '刀坏了',
|
||||||
|
})
|
||||||
|
self.assertEqual(exception_record.state, 'done')
|
||||||
|
self.assertEqual(exception_record.completion_time.minute, datetime.now().minute)
|
||||||
23
jikimo_workorder_exception/views/mrp_workorder_views.xml
Normal file
23
jikimo_workorder_exception/views/mrp_workorder_views.xml
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<odoo>
|
||||||
|
<data>
|
||||||
|
<record id="jikimo_workorder_exception_form_view_inherit" model="ir.ui.view">
|
||||||
|
<field name="name">mrp.workorder.form</field>
|
||||||
|
<field name="model">mrp.workorder</field>
|
||||||
|
<field name="inherit_id" ref="mrp.mrp_production_workorder_form_view_inherit"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//notebook/page[last()]" position="after">
|
||||||
|
<page string="异常记录" name="workorder_exception" attrs="{'invisible': [('routing_type', '!=', 'CNC加工')]}">
|
||||||
|
<field name="exception_ids" nolabel="1" readonly="1">
|
||||||
|
<tree create="false" delete="false" edit="false">
|
||||||
|
<field name="exception_content" string="反馈的异常/问题信息"/>
|
||||||
|
<field name="create_date" string="时间"/>
|
||||||
|
<field name="completion_time"/>
|
||||||
|
</tree>
|
||||||
|
</field>
|
||||||
|
</page>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
4
jikimo_workorder_exception_notify/__init__.py
Normal file
4
jikimo_workorder_exception_notify/__init__.py
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from . import models
|
||||||
|
|
||||||
22
jikimo_workorder_exception_notify/__manifest__.py
Normal file
22
jikimo_workorder_exception_notify/__manifest__.py
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||||
|
{
|
||||||
|
'name': '机企猫 工单异常消息通知',
|
||||||
|
'version': '1.0',
|
||||||
|
'summary': '当产生工单异常时,发送消息通知',
|
||||||
|
'sequence': 1,
|
||||||
|
'category': 'sf',
|
||||||
|
'website': 'https://www.sf.jikimo.com',
|
||||||
|
'depends': ['jikimo_workorder_exception', 'jikimo_message_notify'],
|
||||||
|
'data': [
|
||||||
|
'data/bussiness_node.xml',
|
||||||
|
'data/template_data.xml',
|
||||||
|
# 'security/ir.model.access.csv',
|
||||||
|
],
|
||||||
|
'demo': [
|
||||||
|
],
|
||||||
|
'license': 'LGPL-3',
|
||||||
|
'installable': True,
|
||||||
|
'application': False,
|
||||||
|
'auto_install': False,
|
||||||
|
}
|
||||||
17
jikimo_workorder_exception_notify/data/bussiness_node.xml
Normal file
17
jikimo_workorder_exception_notify/data/bussiness_node.xml
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<?xml version="1.0" ?>
|
||||||
|
<odoo>
|
||||||
|
<data noupdate="1">
|
||||||
|
<record id="bussiness_no_functional_tool" model="jikimo.message.bussiness.node">
|
||||||
|
<field name="name">无功能刀具</field>
|
||||||
|
<field name="model">jikimo.workorder.exception</field>
|
||||||
|
</record>
|
||||||
|
<record id="bussiness_no_position_data" model="jikimo.message.bussiness.node">
|
||||||
|
<field name="name">无定位数据</field>
|
||||||
|
<field name="model">jikimo.workorder.exception</field>
|
||||||
|
</record>
|
||||||
|
<record id="bussiness_processing_failure" model="jikimo.message.bussiness.node">
|
||||||
|
<field name="name">加工失败</field>
|
||||||
|
<field name="model">jikimo.workorder.exception</field>
|
||||||
|
</record>
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
38
jikimo_workorder_exception_notify/data/template_data.xml
Normal file
38
jikimo_workorder_exception_notify/data/template_data.xml
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
<?xml version="1.0" ?>
|
||||||
|
<odoo>
|
||||||
|
<data noupdate="1">
|
||||||
|
<record id="template_no_function_tool" model="jikimo.message.template">
|
||||||
|
<field name="name">生产线无功能刀具提醒</field>
|
||||||
|
<field name="model_id" ref="jikimo_workorder_exception_notify.model_jikimo_workorder_exception"/>
|
||||||
|
<field name="model">jikimo.workorder.exception</field>
|
||||||
|
<field name="bussiness_node_id" ref="bussiness_no_functional_tool"/>
|
||||||
|
<field name="msgtype">markdown</field>
|
||||||
|
<field name="urgency">urgent</field>
|
||||||
|
<field name="content">### 生产线无功能刀具提醒
|
||||||
|
单号:工单[{{workorder_id.production_id.name}}]({{url}})
|
||||||
|
原因:生产线无加工程序要用的功能刀具</field>
|
||||||
|
</record>
|
||||||
|
<record id="template_no_position_data" model="jikimo.message.template">
|
||||||
|
<field name="name">工单无定位数据提醒</field>
|
||||||
|
<field name="model_id" ref="jikimo_workorder_exception_notify.model_jikimo_workorder_exception"/>
|
||||||
|
<field name="model">jikimo.workorder.exception</field>
|
||||||
|
<field name="bussiness_node_id" ref="bussiness_no_position_data"/>
|
||||||
|
<field name="msgtype">markdown</field>
|
||||||
|
<field name="urgency">urgent</field>
|
||||||
|
<field name="content">### 工单无定位数据提醒
|
||||||
|
单号:工单[{{workorder_id.production_id.name}}]({{url}})
|
||||||
|
原因:无装夹定位测量数据</field>
|
||||||
|
</record>
|
||||||
|
<record id="template_processing_failure" model="jikimo.message.template">
|
||||||
|
<field name="name">工单加工失败提醒</field>
|
||||||
|
<field name="model_id" ref="jikimo_workorder_exception_notify.model_jikimo_workorder_exception"/>
|
||||||
|
<field name="model">jikimo.workorder.exception</field>
|
||||||
|
<field name="bussiness_node_id" ref="bussiness_processing_failure"/>
|
||||||
|
<field name="msgtype">markdown</field>
|
||||||
|
<field name="urgency">urgent</field>
|
||||||
|
<field name="content">### 工单加工失败提醒
|
||||||
|
单号:工单[{{workorder_id.production_id.name}}]({{url}})
|
||||||
|
原因:加工失败,工件下产线处理</field>
|
||||||
|
</record>
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
3
jikimo_workorder_exception_notify/models/__init__.py
Normal file
3
jikimo_workorder_exception_notify/models/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from . import jikimo_message_template
|
||||||
|
from . import jikimo_workorder_exception
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
from odoo import models
|
||||||
|
|
||||||
|
|
||||||
|
class JikimoMessageTemplate(models.Model):
|
||||||
|
_inherit = "jikimo.message.template"
|
||||||
|
|
||||||
|
def _get_message_model(self):
|
||||||
|
res = super(JikimoMessageTemplate, self)._get_message_model()
|
||||||
|
res.append('jikimo.workorder.exception')
|
||||||
|
return res
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
from odoo import models, api
|
||||||
|
from odoo.addons.sf_base.commons.common import Common
|
||||||
|
import requests, logging
|
||||||
|
|
||||||
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class JikimoWorkorderException(models.Model):
|
||||||
|
_name = 'jikimo.workorder.exception'
|
||||||
|
_inherit = ['jikimo.workorder.exception', 'jikimo.message.dispatch']
|
||||||
|
|
||||||
|
@api.model_create_multi
|
||||||
|
def create(self, vals_list):
|
||||||
|
res = super(JikimoWorkorderException, self).create(vals_list)
|
||||||
|
# 根据异常编码发送消息提醒
|
||||||
|
try:
|
||||||
|
for rec in res:
|
||||||
|
if rec.exception_code == 'YC0001':
|
||||||
|
# 无CNC程序,调用cloud接口
|
||||||
|
data = {'name': rec.workorder_id.production_id.programming_no, 'exception_code': 'YC0001'}
|
||||||
|
configsettings = self.env['res.config.settings'].sudo().get_values()
|
||||||
|
config_header = Common.get_headers(self, configsettings['token'], configsettings['sf_secret_key'])
|
||||||
|
url = '/api/message/workorder_exception'
|
||||||
|
config_url = configsettings['sf_url'] + url
|
||||||
|
data['token'] = configsettings['token']
|
||||||
|
ret = requests.post(config_url, json=data, headers=config_header)
|
||||||
|
ret = ret.json()
|
||||||
|
_logger.info('无CNC程序异常消息推送接口:%s' % ret)
|
||||||
|
elif rec.exception_code == 'YC0002':
|
||||||
|
# 无功能刀具
|
||||||
|
rec.add_queue('无功能刀具')
|
||||||
|
elif rec.exception_code == 'YC0003':
|
||||||
|
# 无定位数据
|
||||||
|
rec.add_queue('无定位数据')
|
||||||
|
elif rec.exception_code == 'YC0004':
|
||||||
|
# 无FTP文件,调用cloud接口
|
||||||
|
data = {'name': rec.workorder_id.production_id.programming_no, 'exception_code': 'YC0004'}
|
||||||
|
configsettings = self.env['res.config.settings'].sudo().get_values()
|
||||||
|
config_header = Common.get_headers(self, configsettings['token'], configsettings['sf_secret_key'])
|
||||||
|
url = '/api/message/workorder_exception'
|
||||||
|
config_url = configsettings['sf_url'] + url
|
||||||
|
data['token'] = configsettings['token']
|
||||||
|
ret = requests.post(config_url, json=data, headers=config_header)
|
||||||
|
ret = ret.json()
|
||||||
|
_logger.info('无FTP文件异常消息推送接口:%s' % ret)
|
||||||
|
elif rec.exception_code == 'YC0005':
|
||||||
|
# 加工失败
|
||||||
|
rec.add_queue('加工失败')
|
||||||
|
except Exception as e:
|
||||||
|
_logger.error('异常编码发送消息提醒失败:%s' % e)
|
||||||
|
return res
|
||||||
|
|
||||||
|
def _get_message(self, message_queue_ids):
|
||||||
|
contents = super(JikimoWorkorderException, self)._get_message(message_queue_ids)
|
||||||
|
url = self.env['ir.config_parameter'].get_param('web.base.url')
|
||||||
|
action_id = self.env.ref('mrp.mrp_production_action').id
|
||||||
|
for index, content in enumerate(contents):
|
||||||
|
exception_id = self.env['jikimo.workorder.exception'].browse(message_queue_ids[index].res_id)
|
||||||
|
url = url + '/web#id=%s&view_type=form&action=%s' % (exception_id.workorder_id.production_id.id, action_id)
|
||||||
|
contents[index] = content.replace('{{url}}', url)
|
||||||
|
return contents
|
||||||
2
jikimo_workorder_exception_notify/tests/__init__.py
Normal file
2
jikimo_workorder_exception_notify/tests/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
from . import common
|
||||||
|
from . import test_jikimo_workorder_exception_notify
|
||||||
18
jikimo_workorder_exception_notify/tests/common.py
Normal file
18
jikimo_workorder_exception_notify/tests/common.py
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||||
|
from odoo import fields, Command
|
||||||
|
from odoo.tests.common import TransactionCase, HttpCase, tagged, Form
|
||||||
|
|
||||||
|
import json
|
||||||
|
import time
|
||||||
|
import base64
|
||||||
|
from lxml import etree
|
||||||
|
|
||||||
|
@tagged('post_install', '-at_install')
|
||||||
|
class TestJikimoWorkorderExceptionNotifyCommonNotify(TransactionCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(TestJikimoWorkorderExceptionNotifyCommonNotify, self).setUp()
|
||||||
|
# 获取最后一个工单
|
||||||
|
self.workorder = self.env['mrp.workorder'].search([], order='id desc', limit=1)
|
||||||
|
|
||||||
@@ -0,0 +1,113 @@
|
|||||||
|
import json
|
||||||
|
from datetime import datetime
|
||||||
|
from odoo.addons.jikimo_workorder_exception_notify.tests.common import TestJikimoWorkorderExceptionNotifyCommonNotify
|
||||||
|
|
||||||
|
class TestJikimoWorkorderExceptionNotify(TestJikimoWorkorderExceptionNotifyCommonNotify):
|
||||||
|
|
||||||
|
def test_create_message_template(self):
|
||||||
|
self.assertTrue(self.env['jikimo.message.template'].search([
|
||||||
|
('name', '=', '生产线无功能刀具提醒'),
|
||||||
|
('model', '=', 'jikimo.workorder.exception')
|
||||||
|
]))
|
||||||
|
self.assertTrue(self.env['jikimo.message.template'].search([
|
||||||
|
('name', '=', '工单无定位数据提醒'),
|
||||||
|
('model', '=', 'jikimo.workorder.exception')
|
||||||
|
]))
|
||||||
|
self.assertTrue(self.env['jikimo.message.template'].search([
|
||||||
|
('name', '=', '工单加工失败提醒'),
|
||||||
|
('model', '=', 'jikimo.workorder.exception')
|
||||||
|
]))
|
||||||
|
|
||||||
|
def test_create_message_queue_yc0001(self):
|
||||||
|
exception_record = self.env['jikimo.workorder.exception'].create({
|
||||||
|
'workorder_id': self.workorder.id,
|
||||||
|
'exception_code': 'YC0001',
|
||||||
|
'exception_content': '无CNC程序'
|
||||||
|
})
|
||||||
|
|
||||||
|
message_record = self.env['jikimo.message.queue'].search([
|
||||||
|
('res_id', '=', exception_record.id),
|
||||||
|
('model', '=', 'jikimo.workorder.exception'),
|
||||||
|
('message_status', '=', 'pending')
|
||||||
|
])
|
||||||
|
self.assertFalse(message_record)
|
||||||
|
|
||||||
|
def test_create_message_queue_yc0002(self):
|
||||||
|
exception_record = self.env['jikimo.workorder.exception'].create({
|
||||||
|
'workorder_id': self.workorder.id,
|
||||||
|
'exception_code': 'YC0002',
|
||||||
|
'exception_content': '无功能刀具'
|
||||||
|
})
|
||||||
|
|
||||||
|
bussiness_node = self.env['jikimo.message.bussiness.node'].search([
|
||||||
|
('name', '=', '无功能刀具'),
|
||||||
|
('model', '=', 'jikimo.workorder.exception')
|
||||||
|
])
|
||||||
|
|
||||||
|
message_template = self.env['jikimo.message.template'].search([
|
||||||
|
('bussiness_node_id', '=', bussiness_node.id),
|
||||||
|
('model', '=', 'jikimo.workorder.exception')
|
||||||
|
])
|
||||||
|
|
||||||
|
message_record = self.env['jikimo.message.queue'].search([
|
||||||
|
('res_id', '=', exception_record.id),
|
||||||
|
('model', '=', 'jikimo.workorder.exception'),
|
||||||
|
('message_status', '=', 'pending'),
|
||||||
|
('message_template_id', '=', message_template.id)
|
||||||
|
])
|
||||||
|
self.assertTrue(message_record)
|
||||||
|
|
||||||
|
def test_create_message_queue_yc0003(self):
|
||||||
|
exception_record = self.env['jikimo.workorder.exception'].create({
|
||||||
|
'workorder_id': self.workorder.id,
|
||||||
|
'exception_code': 'YC0003',
|
||||||
|
'exception_content': '无定位数据'
|
||||||
|
})
|
||||||
|
|
||||||
|
bussiness_node = self.env['jikimo.message.bussiness.node'].search([
|
||||||
|
('name', '=', '无定位数据'),
|
||||||
|
('model', '=', 'jikimo.workorder.exception')
|
||||||
|
])
|
||||||
|
|
||||||
|
message_template = self.env['jikimo.message.template'].search([
|
||||||
|
('bussiness_node_id', '=', bussiness_node.id),
|
||||||
|
('model', '=', 'jikimo.workorder.exception')
|
||||||
|
])
|
||||||
|
|
||||||
|
message_record = self.env['jikimo.message.queue'].search([
|
||||||
|
('res_id', '=', exception_record.id),
|
||||||
|
('model', '=', 'jikimo.workorder.exception'),
|
||||||
|
('message_status', '=', 'pending'),
|
||||||
|
('message_template_id', '=', message_template.id)
|
||||||
|
])
|
||||||
|
self.assertTrue(message_record)
|
||||||
|
|
||||||
|
def test_create_message_queue_yc0004(self):
|
||||||
|
exception_record = self.env['jikimo.workorder.exception'].create({
|
||||||
|
'workorder_id': self.workorder.id,
|
||||||
|
'exception_code': 'YC0004',
|
||||||
|
'exception_content': '无CNC程序'
|
||||||
|
})
|
||||||
|
|
||||||
|
message_record = self.env['jikimo.message.queue'].search([
|
||||||
|
('res_id', '=', exception_record.id),
|
||||||
|
('model', '=', 'jikimo.workorder.exception'),
|
||||||
|
('message_status', '=', 'pending')
|
||||||
|
])
|
||||||
|
self.assertFalse(message_record)
|
||||||
|
|
||||||
|
def test_get_message(self):
|
||||||
|
exception_record = self.env['jikimo.workorder.exception'].create({
|
||||||
|
'workorder_id': self.workorder.id,
|
||||||
|
'exception_code': 'YC0002',
|
||||||
|
'exception_content': '无功能刀具'
|
||||||
|
})
|
||||||
|
message_queue_ids = self.env['jikimo.message.queue'].search([
|
||||||
|
('res_id', '=', exception_record.id),
|
||||||
|
('model', '=', 'jikimo.workorder.exception'),
|
||||||
|
('message_status', '=', 'pending')
|
||||||
|
])
|
||||||
|
message = self.env['jikimo.workorder.exception']._get_message(message_queue_ids)
|
||||||
|
self.assertTrue(message)
|
||||||
|
|
||||||
|
|
||||||
@@ -1273,3 +1273,18 @@ msgstr ""
|
|||||||
#: model:product.template,description_sale:mrp_workorder.product_template_stool_top
|
#: model:product.template,description_sale:mrp_workorder.product_template_stool_top
|
||||||
msgid "wooden stool top"
|
msgid "wooden stool top"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: mrp_workorder
|
||||||
|
#: model:quality.point.test_type,name:mrp_workorder.test_type_register_consumed_materials
|
||||||
|
msgid "Register Consumed Materials"
|
||||||
|
msgstr "登记消耗材料"
|
||||||
|
|
||||||
|
#. module: mrp_workorder
|
||||||
|
#: model:quality.point.test_type,name:mrp_workorder.test_type_register_byproducts
|
||||||
|
msgid "Register By-products"
|
||||||
|
msgstr "按产品注册"
|
||||||
|
|
||||||
|
#. module: mrp_workorder
|
||||||
|
#: model:quality.point.test_type,name:mrp_workorder.test_type_print_label
|
||||||
|
msgid "Print label"
|
||||||
|
msgstr "打印标签"
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||||
|
|
||||||
from odoo import fields, models
|
from odoo import fields, models, api
|
||||||
|
|
||||||
|
|
||||||
class MrpProduction(models.Model):
|
class MrpProduction(models.Model):
|
||||||
@@ -12,7 +12,8 @@ class MrpProduction(models.Model):
|
|||||||
check_ids = fields.One2many('quality.check', 'production_id', string="Checks")
|
check_ids = fields.One2many('quality.check', 'production_id', string="Checks")
|
||||||
|
|
||||||
def _split_productions(self, amounts=False, cancel_remaining_qty=False, set_consumed_qty=False):
|
def _split_productions(self, amounts=False, cancel_remaining_qty=False, set_consumed_qty=False):
|
||||||
productions = super()._split_productions(amounts=amounts, cancel_remaining_qty=cancel_remaining_qty, set_consumed_qty=set_consumed_qty)
|
productions = super()._split_productions(amounts=amounts, cancel_remaining_qty=cancel_remaining_qty,
|
||||||
|
set_consumed_qty=set_consumed_qty)
|
||||||
backorders = productions[1:]
|
backorders = productions[1:]
|
||||||
if not backorders:
|
if not backorders:
|
||||||
return productions
|
return productions
|
||||||
@@ -20,3 +21,4 @@ class MrpProduction(models.Model):
|
|||||||
if wo.current_quality_check_id.component_id:
|
if wo.current_quality_check_id.component_id:
|
||||||
wo.current_quality_check_id._update_component_quantity()
|
wo.current_quality_check_id._update_component_quantity()
|
||||||
return productions
|
return productions
|
||||||
|
|
||||||
|
|||||||
@@ -1,41 +1,44 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<odoo>
|
<odoo>
|
||||||
<!-- Manufacturing Order for Planing view -->
|
<!-- Manufacturing Order for Planing view -->
|
||||||
<record id="mrp_production_tree_view_planning" model="ir.ui.view">
|
<record id="mrp_production_tree_view_planning" model="ir.ui.view">
|
||||||
<field name="name">mrp.production.tree.inherit.planning</field>
|
<field name="name">mrp.production.tree.inherit.planning</field>
|
||||||
<field name="model">mrp.production</field>
|
<field name="model">mrp.production</field>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<tree default_order="date_planned_start asc" decoration-info="state=='confirmed'" decoration-danger="date_planned_start<current_date and state not in ('done','cancel')" decoration-muted="state in ('done','cancel')" string="Manufacturing Orders" name="Production">
|
<tree default_order="date_planned_start asc" decoration-info="state=='confirmed'"
|
||||||
<field name="message_needaction" invisible="1"/>
|
decoration-danger="date_planned_start<current_date and state not in ('done','cancel')"
|
||||||
<field name="name"/>
|
decoration-muted="state in ('done','cancel')" string="Manufacturing Orders" name="Production">
|
||||||
<field name="date_planned_start"/>
|
<field name="message_needaction" invisible="1"/>
|
||||||
<field name="product_id"/>
|
<field name="name"/>
|
||||||
<field name="product_qty" sum="Total Qty" string="Quantity"/>
|
<field name="date_planned_start"/>
|
||||||
<field name="product_uom_id" string="Unit of Measure" options="{'no_open':True,'no_create':True}" groups="uom.group_uom"/>
|
<field name="product_id"/>
|
||||||
<field name="reservation_state" string="Availability"/>
|
<field name="product_qty" sum="Total Qty" string="Quantity"/>
|
||||||
<field name="origin"/>
|
<field name="product_uom_id" string="Unit of Measure" options="{'no_open':True,'no_create':True}"
|
||||||
<field name="state"/>
|
groups="uom.group_uom"/>
|
||||||
</tree>
|
<field name="reservation_state" string="Availability"/>
|
||||||
</field>
|
<field name="origin"/>
|
||||||
</record>
|
<field name="state"/>
|
||||||
|
</tree>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
<!-- <record id="mrp_production_form_inherit_planning" model="ir.ui.view">-->
|
<!-- <record id="mrp_production_form_inherit_planning" model="ir.ui.view">-->
|
||||||
<!-- <field name="name">mrp.production.form_inherit_planning</field>-->
|
<!-- <field name="name">mrp.production.form_inherit_planning</field>-->
|
||||||
<!-- <field name="model">mrp.production</field>-->
|
<!-- <field name="model">mrp.production</field>-->
|
||||||
<!-- <field name="inherit_id" ref="mrp.mrp_production_form_view"/>-->
|
<!-- <field name="inherit_id" ref="mrp.mrp_production_form_view"/>-->
|
||||||
<!-- <field name="arch" type="xml">-->
|
<!-- <field name="arch" type="xml">-->
|
||||||
<!-- <xpath expr="div[hasclass('oe_chatter')]" position="replace">-->
|
<!-- <xpath expr="div[hasclass('oe_chatter')]" position="replace">-->
|
||||||
<!-- <!– 这里放置替换后的内容 –>-->
|
<!-- <!– 这里放置替换后的内容 –>-->
|
||||||
<!-- </xpath>-->
|
<!-- </xpath>-->
|
||||||
<!-- <xpath expr="//notebook" position="after">-->
|
<!-- <xpath expr="//notebook" position="after">-->
|
||||||
<!-- <div class="oe_chatter">-->
|
<!-- <div class="oe_chatter">-->
|
||||||
<!-- <field name="message_follower_ids"/>-->
|
<!-- <field name="message_follower_ids"/>-->
|
||||||
<!-- <field name="activity_ids"/>-->
|
<!-- <field name="activity_ids"/>-->
|
||||||
<!-- <field name="message_ids"/>-->
|
<!-- <field name="message_ids"/>-->
|
||||||
<!-- </div>-->
|
<!-- </div>-->
|
||||||
<!-- </xpath>-->
|
<!-- </xpath>-->
|
||||||
<!-- </field>-->
|
<!-- </field>-->
|
||||||
<!-- </record>-->
|
<!-- </record>-->
|
||||||
|
|
||||||
<record id="mrp_production_view_search_inherit_planning" model="ir.ui.view">
|
<record id="mrp_production_view_search_inherit_planning" model="ir.ui.view">
|
||||||
<field name="name">mrp.production.search.view.inherit.planning</field>
|
<field name="name">mrp.production.search.view.inherit.planning</field>
|
||||||
@@ -43,7 +46,9 @@
|
|||||||
<field name="inherit_id" ref="mrp.view_mrp_production_filter"/>
|
<field name="inherit_id" ref="mrp.view_mrp_production_filter"/>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<filter name="filter_planned" position="attributes">
|
<filter name="filter_planned" position="attributes">
|
||||||
<attribute name="domain">[('is_planned', '=', True), ('date_planned_start', '!=', False), ('date_planned_finished', '!=', False)]</attribute>
|
<attribute name="domain">[('is_planned', '=', True), ('date_planned_start', '!=', False),
|
||||||
|
('date_planned_finished', '!=', False)]
|
||||||
|
</attribute>
|
||||||
</filter>
|
</filter>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
@@ -51,30 +56,33 @@
|
|||||||
<record id="production_order_unplan_server_action" model="ir.actions.server">
|
<record id="production_order_unplan_server_action" model="ir.actions.server">
|
||||||
<field name="name">Unplan orders</field>
|
<field name="name">Unplan orders</field>
|
||||||
<field name="model_id" ref="mrp.model_mrp_production"/>
|
<field name="model_id" ref="mrp.model_mrp_production"/>
|
||||||
<field name="binding_model_id" ref="mrp.model_mrp_production" />
|
<field name="binding_model_id" ref="mrp.model_mrp_production"/>
|
||||||
<field name="binding_view_types">list</field>
|
<field name="binding_view_types">list</field>
|
||||||
<field name="state">code</field>
|
<field name="state">code</field>
|
||||||
<field name="code">records.button_unplan()</field>
|
<field name="code">records.button_unplan()</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record id="mrp.act_product_mrp_production_workcenter" model="ir.actions.act_window">
|
<record id="mrp.act_product_mrp_production_workcenter" model="ir.actions.act_window">
|
||||||
<field name="domain">[('bom_id', '!=', False), ('bom_id.operation_ids.workcenter_id', '=', active_id), ('date_planned_start', '!=', False), ('date_planned_finished', '!=', False)]</field>
|
<field name="domain">[('bom_id', '!=', False), ('bom_id.operation_ids.workcenter_id', '=', active_id),
|
||||||
|
('date_planned_start', '!=', False), ('date_planned_finished', '!=', False)]
|
||||||
|
</field>
|
||||||
<field name="view_id" ref="mrp_production_tree_view_planning"/>
|
<field name="view_id" ref="mrp_production_tree_view_planning"/>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
|
|
||||||
<menuitem id="mrp_workorder_menu_planning"
|
<menuitem id="mrp_workorder_menu_planning"
|
||||||
name="Work Orders"
|
name="Work Orders"
|
||||||
sequence="2"
|
sequence="2"
|
||||||
parent="mrp.mrp_planning_menu_root"
|
parent="mrp.mrp_planning_menu_root"
|
||||||
groups="mrp.group_mrp_routings"/>
|
groups="mrp.group_mrp_routings"/>
|
||||||
<menuitem id="menu_mrp_workorder_production"
|
<menuitem id="menu_mrp_workorder_production"
|
||||||
name="Planning by Production"
|
name="Planning by Production"
|
||||||
sequence="1"
|
sequence="1"
|
||||||
action="mrp.action_mrp_workorder_production"
|
action="mrp.action_mrp_workorder_production"
|
||||||
parent="mrp_workorder_menu_planning"/>
|
parent="mrp_workorder_menu_planning"/>
|
||||||
<menuitem id="menu_mrp_workorder_workcenter"
|
<menuitem id="menu_mrp_workorder_workcenter"
|
||||||
name="Planning by Workcenter"
|
name="Planning by Workcenter"
|
||||||
sequence="2"
|
sequence="2"
|
||||||
action="mrp_workorder.action_mrp_workorder_dependencies_workcenter"
|
action="mrp_workorder.action_mrp_workorder_dependencies_workcenter"
|
||||||
parent="mrp_workorder_menu_planning"/>
|
parent="mrp_workorder_menu_planning"/>
|
||||||
</odoo>
|
</odoo>
|
||||||
|
|||||||
@@ -1050,3 +1050,13 @@ msgstr "工作中心故障"
|
|||||||
#: model:ir.model.fields,field_description:quality.field_quality_point_test_type__active
|
#: model:ir.model.fields,field_description:quality.field_quality_point_test_type__active
|
||||||
msgid "active"
|
msgid "active"
|
||||||
msgstr "有效"
|
msgstr "有效"
|
||||||
|
|
||||||
|
#. module: quality
|
||||||
|
#: model:quality.point.test_type,name:quality.test_type_instructions
|
||||||
|
msgid "Instructions"
|
||||||
|
msgstr "使用说明"
|
||||||
|
|
||||||
|
#. module: quality
|
||||||
|
#: model:quality.point.test_type,name:quality.test_type_picture
|
||||||
|
msgid "Take a Picture"
|
||||||
|
msgstr "照片"
|
||||||
@@ -15,7 +15,7 @@ class TestType(models.Model):
|
|||||||
_description = "Quality Control Test Type"
|
_description = "Quality Control Test Type"
|
||||||
|
|
||||||
# Used instead of selection field in order to hide a choice depending on the view.
|
# Used instead of selection field in order to hide a choice depending on the view.
|
||||||
name = fields.Char('Name', required=True)
|
name = fields.Char('Name', required=True,translate=True)
|
||||||
technical_name = fields.Char('Technical name', required=True)
|
technical_name = fields.Char('Technical name', required=True)
|
||||||
active = fields.Boolean('active', default=True)
|
active = fields.Boolean('active', default=True)
|
||||||
|
|
||||||
|
|||||||
@@ -1185,3 +1185,14 @@ msgstr "请先进行质量检查!"
|
|||||||
#: model_terms:ir.ui.view,arch_db:quality_control.quality_alert_team_view_form
|
#: model_terms:ir.ui.view,arch_db:quality_control.quality_alert_team_view_form
|
||||||
msgid "e.g. The QA Masters"
|
msgid "e.g. The QA Masters"
|
||||||
msgstr "例如:QA大师"
|
msgstr "例如:QA大师"
|
||||||
|
|
||||||
|
|
||||||
|
#. module: quality_control
|
||||||
|
#: model:quality.point.test_type,name:quality_control.test_type_passfail
|
||||||
|
msgid "Pass - Fail"
|
||||||
|
msgstr "通过-失败"
|
||||||
|
|
||||||
|
#. module: quality_control
|
||||||
|
#: model:quality.point.test_type,name:quality_control.test_type_measure
|
||||||
|
msgid "Measure"
|
||||||
|
msgstr "测量"
|
||||||
@@ -6,3 +6,4 @@ from . import stock_move
|
|||||||
from . import stock_move_line
|
from . import stock_move_line
|
||||||
from . import stock_picking
|
from . import stock_picking
|
||||||
from . import stock_lot
|
from . import stock_lot
|
||||||
|
from . import product_category
|
||||||
32
quality_control/models/product_category.py
Normal file
32
quality_control/models/product_category.py
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||||
|
from math import sqrt
|
||||||
|
from dateutil.relativedelta import relativedelta
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
import random
|
||||||
|
|
||||||
|
from odoo import api, models, fields, _
|
||||||
|
from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT, float_round
|
||||||
|
from odoo.osv.expression import OR
|
||||||
|
|
||||||
|
|
||||||
|
class ProductCategory(models.Model):
|
||||||
|
_inherit = 'product.category'
|
||||||
|
@api.model
|
||||||
|
def name_search(self, name='', args=None, operator='ilike', limit=100):
|
||||||
|
if args is None:
|
||||||
|
args = []
|
||||||
|
# 添加过滤条件,确保只返回名称为 'abc' 的记录
|
||||||
|
args += [('name', 'not in', ['Saleable', 'Expenses', 'Deliveries'])]
|
||||||
|
|
||||||
|
# 调用父类的 name_search 方法
|
||||||
|
return super(ProductCategory, self).name_search(name, args=args, operator=operator, limit=limit)
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def search(self, args, limit=100, offset=0, order=None, count=False):
|
||||||
|
# 添加过滤条件,确保只返回名称不在指定列表中的记录
|
||||||
|
args += [('name', 'not in', ['Saleable', 'Expenses', 'Deliveries'])]
|
||||||
|
|
||||||
|
# 调用父类的 search 方法
|
||||||
|
return super(ProductCategory, self).search(args, limit=limit, offset=offset, order=order, count=count)
|
||||||
BIN
quality_control/static/description/质量.png
Normal file
BIN
quality_control/static/description/质量.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 702 B |
@@ -203,7 +203,7 @@
|
|||||||
<record id="quality_alert_action_check" model="ir.actions.act_window">
|
<record id="quality_alert_action_check" model="ir.actions.act_window">
|
||||||
<field name="name">Quality Alerts</field>
|
<field name="name">Quality Alerts</field>
|
||||||
<field name="res_model">quality.alert</field>
|
<field name="res_model">quality.alert</field>
|
||||||
<field name="view_mode">kanban,tree,form,pivot,graph,calendar</field>
|
<field name="view_mode">tree,kanban,form,pivot,graph,calendar</field>
|
||||||
<field name="help" type="html">
|
<field name="help" type="html">
|
||||||
<p class="o_view_nocontent_smiling_face">
|
<p class="o_view_nocontent_smiling_face">
|
||||||
Create a new quality alert
|
Create a new quality alert
|
||||||
@@ -1024,7 +1024,7 @@
|
|||||||
<menuitem
|
<menuitem
|
||||||
id="menu_quality_root"
|
id="menu_quality_root"
|
||||||
name="Quality"
|
name="Quality"
|
||||||
web_icon="quality_control,static/description/icon.svg"
|
web_icon="quality_control,static/description/质量.png"
|
||||||
sequence="150"
|
sequence="150"
|
||||||
groups="quality.group_quality_user"/>
|
groups="quality.group_quality_user"/>
|
||||||
|
|
||||||
@@ -1033,7 +1033,7 @@
|
|||||||
name="Overview"
|
name="Overview"
|
||||||
action="quality_alert_team_action"
|
action="quality_alert_team_action"
|
||||||
parent="menu_quality_root"
|
parent="menu_quality_root"
|
||||||
sequence="5"/>
|
sequence="5" active="False"/>
|
||||||
|
|
||||||
<menuitem
|
<menuitem
|
||||||
id="menu_quality_control"
|
id="menu_quality_control"
|
||||||
|
|||||||
@@ -1,2 +1,3 @@
|
|||||||
pystrich
|
pystrich
|
||||||
cpca
|
cpca
|
||||||
|
pycryptodome==3.20
|
||||||
|
|||||||
@@ -38,3 +38,17 @@ class Manufacturing_Connect(http.Controller):
|
|||||||
res = {'Succeed': False, 'ErrorCode': 202, 'Error': e}
|
res = {'Succeed': False, 'ErrorCode': 202, 'Error': e}
|
||||||
logging.info('get_maintenance_tool_groups_Info error:%s' % e)
|
logging.info('get_maintenance_tool_groups_Info error:%s' % e)
|
||||||
return json.JSONEncoder().encode(res)
|
return json.JSONEncoder().encode(res)
|
||||||
|
|
||||||
|
|
||||||
|
class MultiInheritController():
|
||||||
|
_sub_classes = []
|
||||||
|
|
||||||
|
def __init_subclass__(cls):
|
||||||
|
"""
|
||||||
|
多继承,解决多个字类时方法调用super的问题
|
||||||
|
"""
|
||||||
|
super().__init_subclass__()
|
||||||
|
if len(cls._sub_classes) > 0 and cls not in cls._sub_classes:
|
||||||
|
cls.__bases__ = (cls._sub_classes[-1],)
|
||||||
|
if cls not in cls._sub_classes:
|
||||||
|
cls._sub_classes.append(cls)
|
||||||
|
|||||||
@@ -394,3 +394,30 @@ class MachineToolCategory(models.Model):
|
|||||||
active = fields.Boolean('有效', default=True)
|
active = fields.Boolean('有效', default=True)
|
||||||
category = fields.Selection([('shukong', u'数控'), ('putong', u'普通')], string=u'机床类别',
|
category = fields.Selection([('shukong', u'数控'), ('putong', u'普通')], string=u'机床类别',
|
||||||
default='shukong')
|
default='shukong')
|
||||||
|
|
||||||
|
|
||||||
|
class MachiningAccuracy(models.Model):
|
||||||
|
_name = 'sf.machining.accuracy'
|
||||||
|
_description = '加工精度'
|
||||||
|
name = fields.Char('一般公差', index=True)
|
||||||
|
standard_tolerance = fields.Char(string="标准公差")
|
||||||
|
sync_id = fields.Char('同步ID')
|
||||||
|
|
||||||
|
|
||||||
|
class ReSaleOrder(models.Model):
|
||||||
|
_inherit = 'sale.order'
|
||||||
|
|
||||||
|
person_of_delivery = fields.Char('收货人')
|
||||||
|
telephone_of_delivery = fields.Char('电话号码')
|
||||||
|
address_of_delivery = fields.Char('联系地址')
|
||||||
|
|
||||||
|
|
||||||
|
class EmbryoRedundancy(models.Model):
|
||||||
|
_name = "sf.embryo.redundancy"
|
||||||
|
|
||||||
|
code = fields.Char('编码', required=True)
|
||||||
|
name = fields.Char('名称', required=True)
|
||||||
|
long = fields.Float('长度(mm)', required=True)
|
||||||
|
width = fields.Float('宽度(mm)', required=True)
|
||||||
|
height = fields.Float('高度(mm)', required=True)
|
||||||
|
active = fields.Boolean('有效', default=True)
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ class MrsMaterialModel(models.Model):
|
|||||||
finish_machining = fields.Float("精加工Vc(m/min)")
|
finish_machining = fields.Float("精加工Vc(m/min)")
|
||||||
remark = fields.Text("备注")
|
remark = fields.Text("备注")
|
||||||
gain_way = fields.Selection(
|
gain_way = fields.Selection(
|
||||||
[("自加工", "自加工"), ("外协", "外协"), ("采购", "采购")],
|
[("自加工", "自加工"), ("外协", "委外加工"), ("采购", "采购")],
|
||||||
default="", string="获取方式")
|
default="", string="获取方式")
|
||||||
supplier_ids = fields.One2many('sf.supplier.sort', 'materials_model_id', string='供应商')
|
supplier_ids = fields.One2many('sf.supplier.sort', 'materials_model_id', string='供应商')
|
||||||
active = fields.Boolean('有效', default=True)
|
active = fields.Boolean('有效', default=True)
|
||||||
@@ -100,6 +100,7 @@ class MrsProductionProcess(models.Model):
|
|||||||
travel_day = fields.Float('路途天数/d')
|
travel_day = fields.Float('路途天数/d')
|
||||||
sequence = fields.Integer('排序')
|
sequence = fields.Integer('排序')
|
||||||
|
|
||||||
|
|
||||||
# class MrsProcessingTechnology(models.Model):
|
# class MrsProcessingTechnology(models.Model):
|
||||||
# _name = 'sf.processing.technology'
|
# _name = 'sf.processing.technology'
|
||||||
# _description = '加工工艺'
|
# _description = '加工工艺'
|
||||||
@@ -157,7 +158,9 @@ class MrsProductionProcessParameter(models.Model):
|
|||||||
for parameter in self:
|
for parameter in self:
|
||||||
if parameter.process_id:
|
if parameter.process_id:
|
||||||
name = parameter.process_id.name + '-' + parameter.name
|
name = parameter.process_id.name + '-' + parameter.name
|
||||||
result.append((parameter.id, name))
|
else:
|
||||||
|
name = parameter.name
|
||||||
|
result.append((parameter.id, name))
|
||||||
return result
|
return result
|
||||||
|
|
||||||
# 获取表面工艺的获取方式
|
# 获取表面工艺的获取方式
|
||||||
|
|||||||
@@ -321,7 +321,7 @@ class ToolInventory(models.Model):
|
|||||||
prefix = fields.Char('前缀')
|
prefix = fields.Char('前缀')
|
||||||
postfix = fields.Char('后缀')
|
postfix = fields.Char('后缀')
|
||||||
diameter = fields.Float('直径(mm)')
|
diameter = fields.Float('直径(mm)')
|
||||||
angle = fields.Float('R角(mm)')
|
angle = fields.Float('R角(mm)',default=0)
|
||||||
tool_length = fields.Float('刀具总长(mm)')
|
tool_length = fields.Float('刀具总长(mm)')
|
||||||
blade_length = fields.Float('避空长/刃长(mm)')
|
blade_length = fields.Float('避空长/刃长(mm)')
|
||||||
knife_head_name = fields.Char('刀头名称')
|
knife_head_name = fields.Char('刀头名称')
|
||||||
|
|||||||
@@ -21,8 +21,9 @@ class ToolMaterialsBasicParameters(models.Model):
|
|||||||
neck_length = fields.Float('颈部长度(mm)')
|
neck_length = fields.Float('颈部长度(mm)')
|
||||||
handle_diameter = fields.Float('柄部直径(mm)')
|
handle_diameter = fields.Float('柄部直径(mm)')
|
||||||
handle_length = fields.Float('柄部长度(mm)')
|
handle_length = fields.Float('柄部长度(mm)')
|
||||||
blade_tip_diameter = fields.Integer('刀尖直径(mm)')
|
blade_tip_diameter = fields.Float('刀尖直径(mm)')
|
||||||
blade_tip_working_size = fields.Char('刀尖处理尺寸(R半径mm/倒角度)', size=20)
|
blade_tip_working_size = fields.Char('刀尖倒角度(°)', size=20)
|
||||||
|
tip_r_size = fields.Float('刀尖R角(mm)',default=0)
|
||||||
blade_tip_taper = fields.Integer('刀尖锥度(°)')
|
blade_tip_taper = fields.Integer('刀尖锥度(°)')
|
||||||
blade_diameter = fields.Float('刃部直径(mm)')
|
blade_diameter = fields.Float('刃部直径(mm)')
|
||||||
blade_length = fields.Float('刃部长度(mm)')
|
blade_length = fields.Float('刃部长度(mm)')
|
||||||
@@ -37,7 +38,7 @@ class ToolMaterialsBasicParameters(models.Model):
|
|||||||
width = fields.Float('宽度(mm)')
|
width = fields.Float('宽度(mm)')
|
||||||
cutting_blade_length = fields.Float('切削刃长(mm)')
|
cutting_blade_length = fields.Float('切削刃长(mm)')
|
||||||
relief_angle = fields.Integer('后角(°)')
|
relief_angle = fields.Integer('后角(°)')
|
||||||
blade_tip_circular_arc_radius = fields.Char('刀尖圆弧半径(mm)', size=20)
|
blade_tip_circular_arc_radius = fields.Char('刀尖圆弧半径(mm)', size=20,default='0')
|
||||||
inscribed_circle_diameter = fields.Float('内接圆直径IC/D(mm)')
|
inscribed_circle_diameter = fields.Float('内接圆直径IC/D(mm)')
|
||||||
install_aperture_diameter = fields.Float('安装孔直径(mm)')
|
install_aperture_diameter = fields.Float('安装孔直径(mm)')
|
||||||
chip_breaker_groove = fields.Selection([('无', '无'), ('单面', '单面'), ('双面', '双面')],
|
chip_breaker_groove = fields.Selection([('无', '无'), ('单面', '单面'), ('双面', '双面')],
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<odoo>
|
<odoo>
|
||||||
<data>
|
<data>
|
||||||
<record id="group_quality" model="res.groups">
|
<record id="group_quality" model="res.groups">
|
||||||
<field name="name">质检岗</field>
|
<field name="name">质检岗</field>
|
||||||
<field name="category_id" ref="base.module_category_manufacturing_quality"/>
|
<field name="category_id" ref="base.module_category_manufacturing_quality"/>
|
||||||
</record>
|
</record>
|
||||||
@@ -46,6 +46,11 @@
|
|||||||
<field name="category_id" ref="base.module_category_manufacturing_manufacturing"/>
|
<field name="category_id" ref="base.module_category_manufacturing_manufacturing"/>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
|
<record id="group_production_engineer" model="res.groups">
|
||||||
|
<field name="name">工艺工程师</field>
|
||||||
|
<field name="implied_ids" eval="[(4, ref('group_sf_mrp_user'))]"/>
|
||||||
|
<field name="category_id" ref="base.module_category_manufacturing_manufacturing"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
<record model="ir.module.category" id="module_category_plan">
|
<record model="ir.module.category" id="module_category_plan">
|
||||||
<field name="name">计划</field>
|
<field name="name">计划</field>
|
||||||
@@ -65,7 +70,7 @@
|
|||||||
<record id="group_plan_dispatch" model="res.groups">
|
<record id="group_plan_dispatch" model="res.groups">
|
||||||
<field name="name">计划调度岗</field>
|
<field name="name">计划调度岗</field>
|
||||||
<field name="category_id" ref="module_category_plan"/>
|
<field name="category_id" ref="module_category_plan"/>
|
||||||
<!-- <field name="implied_ids" eval="[(4, ref('base.group_user'))]"/> -->
|
<!-- <field name="implied_ids" eval="[(4, ref('base.group_user'))]"/> -->
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record id="group_plan_director" model="res.groups">
|
<record id="group_plan_director" model="res.groups">
|
||||||
|
|||||||
@@ -242,3 +242,15 @@ access_sf_fixture_materials_basic_parameters_group_sf_stock_manager,sf_fixture_m
|
|||||||
access_sf_multi_mounting_type_group_sf_stock_manager,sf_multi_mounting_type_group_sf_stock_manager,model_sf_multi_mounting_type,sf_base.group_sf_stock_manager,1,0,0,0
|
access_sf_multi_mounting_type_group_sf_stock_manager,sf_multi_mounting_type_group_sf_stock_manager,model_sf_multi_mounting_type,sf_base.group_sf_stock_manager,1,0,0,0
|
||||||
access_sf_machine_brand_group_sf_stock_manager,sf_machine_brand_group_sf_stock_manager,model_sf_machine_brand,sf_base.group_sf_stock_manager,1,0,0,0
|
access_sf_machine_brand_group_sf_stock_manager,sf_machine_brand_group_sf_stock_manager,model_sf_machine_brand,sf_base.group_sf_stock_manager,1,0,0,0
|
||||||
access_sf_cutting_tool_type_group_sf_stock_manager,sf_cutting_tool_type_group_sf_stock_manager,model_sf_cutting_tool_type,sf_base.group_sf_stock_manager,1,0,0,0
|
access_sf_cutting_tool_type_group_sf_stock_manager,sf_cutting_tool_type_group_sf_stock_manager,model_sf_cutting_tool_type,sf_base.group_sf_stock_manager,1,0,0,0
|
||||||
|
|
||||||
|
|
||||||
|
access_sf_cutting_tool_material_group_plan_dispatch,sf_cutting_tool_material_group_plan_dispatch,model_sf_cutting_tool_material,sf_base.group_plan_dispatch,1,0,0,0
|
||||||
|
access_sf_functional_cutting_tool_model_group_plan_dispatch,sf_functional_cutting_tool_model_group_plan_dispatch,model_sf_functional_cutting_tool_model,sf_base.group_plan_dispatch,1,0,0,0
|
||||||
|
access_sf_cutting_tool_type_group_plan_dispatch,sf_cutting_tool_type_group_plan_dispatch,model_sf_cutting_tool_type,sf_base.group_plan_dispatch,1,0,0,0
|
||||||
|
|
||||||
|
|
||||||
|
access_sf_machining_accuracy,sf_machining_accuracy,model_sf_machining_accuracy,base.group_user,1,0,0,0
|
||||||
|
access_sf_machining_accuracy_admin,sf_machining_accuracy_admin,model_sf_machining_accuracy,base.group_system,1,0,0,0
|
||||||
|
|
||||||
|
access_sf_embryo_redundancy,sf_embryo_redundancy,model_sf_embryo_redundancy,base.group_user,1,0,0,0
|
||||||
|
access_sf_embryo_redundancy_admin,sf_embryo_redundancy_admin,model_sf_embryo_redundancy,base.group_system,1,0,0,0
|
||||||
|
|||||||
|
125
sf_base/static/js/customTable.js
Normal file
125
sf_base/static/js/customTable.js
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
// 获取表格数据
|
||||||
|
function getDomData() {
|
||||||
|
const dom = $('div[name=cutting_speed_ids]')
|
||||||
|
if (!dom.length) return
|
||||||
|
const table = dom.find('.o_list_table')
|
||||||
|
const thead = table.children('thead')
|
||||||
|
const tbody = table.children('tbody')
|
||||||
|
const tbody_child = tbody.children()
|
||||||
|
const hideTheadDom = thead.find('[data-name=process_capability]')
|
||||||
|
hideTheadDom.hide().next().hide()
|
||||||
|
hideTheadDom.before('<th customTh>精加工</th><th customTh>粗加工</th>')
|
||||||
|
tbody_child.each(function () {
|
||||||
|
const dom = $(this).children('[name=process_capability]')
|
||||||
|
if(!dom.length) return
|
||||||
|
dom.css('cssText', 'display: none!important').next().css('cssText', 'display: none!important')
|
||||||
|
const isCu = dom.text() == '粗加工' // 是否粗加工
|
||||||
|
const v = dom.next().text() // 切削速度
|
||||||
|
dom.after(`<td customSpeed="1" name="process_capability" is="精加工" val="${ v }">${!isCu ? v : ''}</td><td customSpeed="1" name="process_capability" is="粗加工" val="${ v }">${isCu ? v : ''}</td>`)
|
||||||
|
setListenClick()
|
||||||
|
})
|
||||||
|
return;
|
||||||
|
handleTbody(tbody, newTableData, ΦList, table)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 监听点击
|
||||||
|
function setListenClick() {
|
||||||
|
$(document).click(function (e) {
|
||||||
|
if ($(e.target).attr('customSpeed')) {
|
||||||
|
const orginV = $('[customInput=1]').children('input').val()
|
||||||
|
$('[customInput=1]').parent().html(orginV)
|
||||||
|
const v = $(e.target).attr('val')
|
||||||
|
const is = $(e.target).attr('is')
|
||||||
|
$(e.target).html('')
|
||||||
|
const input = $('<div customInput="1" is="' + is + '" class="o_field_widget o_field_char"><input class="o_input" type="text" autocomplete="off" maxlength="20"></div>')
|
||||||
|
input.children('input').val(v)
|
||||||
|
$(e.target).append(input)
|
||||||
|
input.children('input').focus()
|
||||||
|
input.children('input').select()
|
||||||
|
} else if ($(e.target).attr('customInput')) {
|
||||||
|
|
||||||
|
} else {
|
||||||
|
const orginV = $('[customInput=1]').children('input').val()
|
||||||
|
$('[customInput=1]').parent().html(orginV)
|
||||||
|
const v = $(e.target).attr('val')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
$(document).off('change') // 防止重复绑定
|
||||||
|
$(document).on('change', '[customInput] input', async function () {
|
||||||
|
$(this).parents('td').attr('val', $(this).val())
|
||||||
|
$(this).parents('td').siblings('[customspeed]').attr('val', $(this).val())
|
||||||
|
var eve1 = new Event('change')
|
||||||
|
var eve2 = new Event('input')
|
||||||
|
var eve3 = new Event('click')
|
||||||
|
let patchSpeedDom = $(this).parents('td').siblings('[name=cutting_speed]')
|
||||||
|
let patchProcessDom = $(this).parents('td').siblings('[name=process_capability]')
|
||||||
|
$(this).parents('td').siblings('[customspeed]').text('') // 清空其他加工类型的数据
|
||||||
|
await timeOut(500)
|
||||||
|
patchProcessDom[0].dispatchEvent(eve3)
|
||||||
|
await timeOut(200)
|
||||||
|
const processVal = $(this).parent().attr('is')
|
||||||
|
patchProcessDom.find('select').val(`"${processVal}"`) // 设置源select的val为“加工类型 is”、
|
||||||
|
patchProcessDom.attr("data-tooltip", `${processVal}`)
|
||||||
|
patchProcessDom.find('select')[0].dispatchEvent(eve1)
|
||||||
|
|
||||||
|
patchSpeedDom[0].dispatchEvent(eve3)
|
||||||
|
await timeOut(200)
|
||||||
|
patchSpeedDom.find('input').val($(this).val())
|
||||||
|
await timeOut(50)
|
||||||
|
patchSpeedDom.find('input')[0].dispatchEvent(eve2)
|
||||||
|
patchSpeedDom.find('input')[0].dispatchEvent(eve1)
|
||||||
|
})
|
||||||
|
$(document).off('blur') // 防止重复绑定
|
||||||
|
$(document).on('blur', '[customInput] input', async function () {
|
||||||
|
if(!$(this).length) return
|
||||||
|
|
||||||
|
$(this).parents('td').siblings('[customspeed]').text('') // 清空其他加工类型的数据
|
||||||
|
let patchProcessDom = $(this).parents('td').siblings('[name=process_capability]')
|
||||||
|
try {
|
||||||
|
patchProcessDom[0].dispatchEvent(new Event('click'))
|
||||||
|
const processVal = $(this).parent().attr('is')
|
||||||
|
patchProcessDom.find('select').val(`"${processVal}"`) // 设置源select的val为“加工类型 is”、
|
||||||
|
patchProcessDom.attr("data-tooltip", `${processVal}`)
|
||||||
|
patchProcessDom.find('select')[0].dispatchEvent(new Event('change'))
|
||||||
|
} catch {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
function timeOut(time) {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
setTimeout(() => {
|
||||||
|
resolve()
|
||||||
|
}, time)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function listenAdd() {
|
||||||
|
$('td.o_field_x2many_list_row_add a').click(async function () {
|
||||||
|
await timeOut(500)
|
||||||
|
const tr = $('.o_list_table').children('tbody').children('tr').eq(-2)
|
||||||
|
if(tr.children('td').eq(2).text() == '') {
|
||||||
|
const dom = tr.children('[name=process_capability]')
|
||||||
|
if(!dom.length) return
|
||||||
|
dom.css('cssText', 'display: none!important').next().css('cssText', 'display: none!important')
|
||||||
|
const isCu = dom.text() == '粗加工' // 是否粗加工
|
||||||
|
const v = dom.next().text() // 切削速度
|
||||||
|
dom.after(`<td customSpeed="1" name="process_capability" is="精加工" val="${ v }">${!isCu ? v : ''}</td><td customSpeed="1" name="process_capability" is="粗加工" val="${ v }">${isCu ? v : ''}</td>`)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function listenSave() {
|
||||||
|
$('.o_form_button_save').click( async function () {
|
||||||
|
await timeOut(1000)
|
||||||
|
if($(this).parent().next().length) return
|
||||||
|
$('th[customTh],td[cusomSpeed]').remove()
|
||||||
|
getDomData()
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
listenAdd()
|
||||||
|
listenSave()
|
||||||
|
getDomData()
|
||||||
47
sf_base/static/js/setTableWidth.js
Normal file
47
sf_base/static/js/setTableWidth.js
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
// 因为表格可以拖动设置宽度,所以需要用js设置初始宽度
|
||||||
|
function setBasicParamTableWidth() {
|
||||||
|
// const _100px = 'th[data-name="cutting_blade_length"],th[data-name="cutting_blade_length"],th[data-name="name"],th[data-name="tip_handling_size"],th[data-name="cutting_depth_max"],th[data-name="diameter_inner_circle"],th[data-name="diameter_mounting_hole" ],th[data-name="radius_tip_re" ],th[data-name="is_chip_breaker"],th[data-name="chip_breaker_type_code"],th[data-name="blade_profile"]'
|
||||||
|
// const _65px = 'th[data-name="edge_angle"],th[data-name="relief_angle"],[data-name="total_length"],th[data-name="length"],th[data-name="thickness"],th[data-name="blade_number"]'
|
||||||
|
// const _80px = 'th[data-name="arbor_diameter"],th[data-name="head_height"],th[data-name="head_width"],th[data-name="head_length"],th[data-name="blade_diameter"],th[data-name="blade_length"] ,th[data-name="neck_length"] ,th[data-name="neck_diameter"] ,th[data-name="shank_diameter"],th[data-name="shank_length"],th[data-name="tip_diameter"],th[data-name="knife_tip_taper"],th[data-name="blade_helix_angle"] ,th[data-name="blade_width"],th[data-name="blade_depth"]'
|
||||||
|
// const _50px = 'th[data-name="pitch"],th[data-name="width"],th[data-name="height"]'
|
||||||
|
|
||||||
|
const basicParamDom = $('.fixTableCss')
|
||||||
|
// const basicParamDom_100px = basicParamDom.find(_100px) // 四字以上
|
||||||
|
// const basicParamDom_65px = basicParamDom.find(_65px) // 大概三个字加单位
|
||||||
|
// const basicParamDom_80px = basicParamDom.find(_80px) // 大概四个字加单位
|
||||||
|
// const basicParamDom_50px= basicParamDom.find(_50px) // 大概两个字加单位
|
||||||
|
//
|
||||||
|
// basicParamDom_100px.css({'width': '100px', 'max-width': 'auto', ',min-width': 'auto'})
|
||||||
|
// basicParamDom_65px.css({'width': '65px', 'max-width': 'auto', ',min-width': 'auto'})
|
||||||
|
// basicParamDom_80px.css({'width': '80px', 'max-width': 'auto', ',min-width': 'auto'})
|
||||||
|
// basicParamDom_50px.css({'width': '50px', 'max-width': 'auto', ',min-width': 'auto'})
|
||||||
|
let dom = []
|
||||||
|
try {
|
||||||
|
dom = basicParamDom.find('table').find('thead').children().children()
|
||||||
|
|
||||||
|
} catch {
|
||||||
|
dom = []
|
||||||
|
}
|
||||||
|
if (!dom) return
|
||||||
|
dom.each(function () {
|
||||||
|
if ($(this).hasClass('row_no') >= 0) { // 序号列
|
||||||
|
// 不设置 通过css设置
|
||||||
|
}
|
||||||
|
const text = $(this).text().split('(')
|
||||||
|
if ($(this).attr('data-name') == 'name' || text[0].length > 4) {
|
||||||
|
$(this).width('100px')
|
||||||
|
} else if(text[0].length == 4){
|
||||||
|
$(this).width('80px')
|
||||||
|
} else if(text[0].length == 3){
|
||||||
|
$(this).width('65px')
|
||||||
|
} else if(text[0].length == 2){
|
||||||
|
$(this).width('50px')
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
setBasicParamTableWidth()
|
||||||
|
$('.o_field_many2one_selection').on('click', $('#cutting_tool_material_id + ul'), function () {
|
||||||
|
setTimeout(setBasicParamTableWidth, 500)
|
||||||
|
})
|
||||||
159
sf_base/static/js/updateTable.js
Normal file
159
sf_base/static/js/updateTable.js
Normal file
@@ -0,0 +1,159 @@
|
|||||||
|
// 获取表格数据
|
||||||
|
function getDomData() {
|
||||||
|
const dom = $('#updateTable').prev()
|
||||||
|
if (!dom.length) return
|
||||||
|
const table = $('#updateTable').prev().find('.o_list_table')
|
||||||
|
const customTable = table.clone()
|
||||||
|
customTable.addClass('customTable')
|
||||||
|
table.parent().append(customTable)
|
||||||
|
table.hide()
|
||||||
|
const thead = customTable.children('thead')
|
||||||
|
const tbody = customTable.children('tbody')
|
||||||
|
const tableData = []
|
||||||
|
const tbody_child = tbody.children()
|
||||||
|
|
||||||
|
const tbody_child_len = tbody_child.length
|
||||||
|
|
||||||
|
for (let v = 0; v < tbody_child_len; v++) { // 将数据取出来到tableData里面
|
||||||
|
const data = tbody_child[v].innerText.split('\t')
|
||||||
|
// console.log('dom data',data)
|
||||||
|
const [index, deep, name, Φ, value] = data
|
||||||
|
tableData.push({index, deep, name, Φ, value})
|
||||||
|
}
|
||||||
|
const ΦList = [...new Set(tableData.map(_ => _.name))] // ΦList去重
|
||||||
|
const newTableData = {}
|
||||||
|
tableData.forEach(_ => {
|
||||||
|
const key = _.deep + '|' + _.Φ
|
||||||
|
!newTableData[key] ? newTableData[key] = {i: _.index} : '';
|
||||||
|
if (_.Φ) { // 去除没有Φ的脏数据
|
||||||
|
newTableData[key]['Φ' + _.Φ] = _.value
|
||||||
|
newTableData[key]['Φ' + _.Φ + 'i'] = _.index
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// console.log('qwdh',tableData, ΦList, newTableData);
|
||||||
|
|
||||||
|
if (ΦList.filter(_ => _).length == 0) return;
|
||||||
|
handleThead(thead, ΦList)
|
||||||
|
|
||||||
|
handleTbody(tbody, newTableData, ΦList, table)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重新设置表头、
|
||||||
|
function handleThead(thead, ΦList) {
|
||||||
|
const dom = thead.children().eq(0).children()
|
||||||
|
const len = dom.length
|
||||||
|
dom.eq(0).attr('rowspan', 2)
|
||||||
|
dom.eq(1).attr('rowspan', 2)
|
||||||
|
len == 5 ? dom.eq(2).attr('rowspan', 2) : ''
|
||||||
|
dom.eq(-2).attr('colspan', ΦList.length)
|
||||||
|
dom.eq(-1).remove()
|
||||||
|
|
||||||
|
const tr = document.createElement('tr')
|
||||||
|
for (let v = 0; v < ΦList.length; v++) {
|
||||||
|
const th = document.createElement('th')
|
||||||
|
th.innerText = 'Φ' + ΦList[v]
|
||||||
|
tr.append(th)
|
||||||
|
}
|
||||||
|
thead.append(tr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重新设置表格
|
||||||
|
function handleTbody(tbody, newTableData, ΦList, table) {
|
||||||
|
console.log(newTableData)
|
||||||
|
tbody.html('')
|
||||||
|
let i = 0
|
||||||
|
const data = Object.keys(newTableData)
|
||||||
|
// data.sort((a, b) => {
|
||||||
|
// a = a.split('=')[1].split('%')[0]
|
||||||
|
// b = b.split('=')[1].split('%')[0]
|
||||||
|
// return a - b
|
||||||
|
// })
|
||||||
|
// console.log('wqoqw ',ΦList)
|
||||||
|
data.forEach(_ => {
|
||||||
|
i++
|
||||||
|
const tr = $('<tr class="o_data_row"></tr>')
|
||||||
|
const td0 = $('<td></td>')
|
||||||
|
td0.text(i)
|
||||||
|
tr.append(td0)
|
||||||
|
const lit = _.split('|')
|
||||||
|
//
|
||||||
|
const td1 = $('<td></td>')
|
||||||
|
const td2 = $('<td></td>')
|
||||||
|
td1.text(lit[0])
|
||||||
|
td2.text(lit[1] || '')
|
||||||
|
tr.append(td1)
|
||||||
|
tr.append(td2)
|
||||||
|
ΦList.forEach(Φ => {
|
||||||
|
const td = $('<td class="o_data_cell cursor-pointer o_field_cell o_list_char"></td>')
|
||||||
|
td.text(newTableData[_]['Φ' + Φ])
|
||||||
|
td.attr('col', newTableData[_]['Φ' + Φ + 'i'])
|
||||||
|
td.attr('val', newTableData[_]['Φ' + Φ])
|
||||||
|
td.attr('coustomTd', 1)
|
||||||
|
tr.append(td)
|
||||||
|
})
|
||||||
|
// // for (let j = 0; j < ΦList.length; j++) {
|
||||||
|
// // const td = document.createElement('td')
|
||||||
|
// // td.innerText = newTableData[data[v]][_]
|
||||||
|
// // th.append(td)
|
||||||
|
// // }
|
||||||
|
tbody.append(tr)
|
||||||
|
})
|
||||||
|
// $(document).click(function (e) {
|
||||||
|
// if ($(e.target).attr('coustomTd')) {
|
||||||
|
// const orginV = $('[coustomInput=1]').children('input').val()
|
||||||
|
// $('[coustomInput=1]').parent().html(orginV)
|
||||||
|
// const v = $(e.target).attr('val')
|
||||||
|
// console.log($(e.target));
|
||||||
|
// $(e.target).html('')
|
||||||
|
// const input = $('<div coustomInput="1" name="feed_per_tooth" class="o_field_widget o_field_char"><input class="o_input" type="text" autocomplete="off" maxlength="20"></div>')
|
||||||
|
// input.children('input').val(v)
|
||||||
|
// $(e.target).append(input)
|
||||||
|
// input.children('input').focus()
|
||||||
|
// input.children('input').select()
|
||||||
|
// } else if ($(e.target).attr('coustomInput')) {
|
||||||
|
//
|
||||||
|
// } else {
|
||||||
|
// const orginV = $('[coustomInput=1]').children('input').val()
|
||||||
|
// $('[coustomInput=1]').parent().html(orginV)
|
||||||
|
// const v = $(e.target).attr('val')
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
// $(document).off('change') // 防止重复绑定
|
||||||
|
// $(document).on('change', '[coustomInput] input', function () {
|
||||||
|
// $(this).parents('td').attr('val', $(this).val());
|
||||||
|
// var eve1 = new Event('change');
|
||||||
|
// var eve2 = new Event('input');
|
||||||
|
// var eve3 = new Event('click');
|
||||||
|
// const i = $(this).parents('td').attr('col');
|
||||||
|
// let patchDom = table.find('tbody').children('tr').eq(i - 1);
|
||||||
|
//
|
||||||
|
// if (patchDom.length === 0) {
|
||||||
|
// console.error('No such row found');
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// patchDom = patchDom.children().eq(-1);
|
||||||
|
//
|
||||||
|
// setTimeout(() => {
|
||||||
|
// if (patchDom.length === 0) {
|
||||||
|
// console.error('No such cell found');
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// patchDom[0].dispatchEvent(eve3); // Simulate click event
|
||||||
|
//
|
||||||
|
// setTimeout(() => {
|
||||||
|
// patchDom = patchDom.find('input');
|
||||||
|
// if (patchDom.length === 0) {
|
||||||
|
// console.error('No input found in the target cell');
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// patchDom.val($(this).val());
|
||||||
|
// patchDom[0].dispatchEvent(eve2);
|
||||||
|
// patchDom[0].dispatchEvent(eve1);
|
||||||
|
// }, 200);
|
||||||
|
// }, 500);
|
||||||
|
// });
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
getDomData()
|
||||||
62
sf_base/static/src/js/custom_barcode_handlers.js
Normal file
62
sf_base/static/src/js/custom_barcode_handlers.js
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
/** @odoo-module **/
|
||||||
|
|
||||||
|
import { registry } from "@web/core/registry";
|
||||||
|
import { barcodeGenericHandlers } from '@barcodes/barcode_handlers';
|
||||||
|
import { patch } from "@web/core/utils/patch";
|
||||||
|
|
||||||
|
// 定义新的 clickOnButton 函数
|
||||||
|
function customClickOnButton(selector) {
|
||||||
|
console.log("This is the custom clickOnButton function!");
|
||||||
|
|
||||||
|
const buttons = document.body.querySelectorAll(selector);
|
||||||
|
|
||||||
|
let length = buttons.length;
|
||||||
|
if (length > 0) {
|
||||||
|
buttons[length - 1].click();
|
||||||
|
} else {
|
||||||
|
console.warn(`Button with selector ${selector} not found`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
patch(barcodeGenericHandlers, "start", {
|
||||||
|
start(env, { ui, barcode, notification }) {
|
||||||
|
// 使用新定义的 clickOnButton 函数
|
||||||
|
const COMMANDS = {
|
||||||
|
"O-CMD.EDIT": () => customClickOnButton(".o_form_button_edit"),
|
||||||
|
"O-CMD.DISCARD": () => customClickOnButton(".o_form_button_cancel"),
|
||||||
|
"O-CMD.SAVE": () => customClickOnButton(".o_form_button_save"),
|
||||||
|
"O-CMD.PREV": () => customClickOnButton(".o_pager_previous"),
|
||||||
|
"O-CMD.NEXT": () => customClickOnButton(".o_pager_next"),
|
||||||
|
"O-CMD.PAGER-FIRST": () => updatePager("first"),
|
||||||
|
"O-CMD.PAGER-LAST": () => updatePager("last"),
|
||||||
|
"O-CMD.CONFIRM": () => customClickOnButton(".jikimo_button_confirm"),
|
||||||
|
"O-CMD.FLUSHED": () => customClickOnButton(".jikimo_button_flushed"),
|
||||||
|
};
|
||||||
|
|
||||||
|
barcode.bus.addEventListener("barcode_scanned", (ev) => {
|
||||||
|
const barcode = ev.detail.barcode;
|
||||||
|
if (barcode.startsWith("O-BTN.")) {
|
||||||
|
let targets = [];
|
||||||
|
try {
|
||||||
|
targets = getVisibleElements(ui.activeElement, `[barcode_trigger=${barcode.slice(6)}]`);
|
||||||
|
} catch (_e) {
|
||||||
|
console.warn(`Barcode '${barcode}' is not valid`);
|
||||||
|
}
|
||||||
|
for (let elem of targets) {
|
||||||
|
elem.click();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (barcode.startsWith("O-CMD.")) {
|
||||||
|
const fn = COMMANDS[barcode];
|
||||||
|
if (fn) {
|
||||||
|
fn();
|
||||||
|
} else {
|
||||||
|
notification.add(env._t("Barcode: ") + `'${barcode}'`, {
|
||||||
|
title: env._t("Unknown barcode command"),
|
||||||
|
type: "danger"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
@@ -5,9 +5,12 @@ import { registry } from '@web/core/registry';
|
|||||||
import { formView } from '@web/views/form/form_view';
|
import { formView } from '@web/views/form/form_view';
|
||||||
import { FormController } from '@web/views/form/form_controller';
|
import { FormController } from '@web/views/form/form_controller';
|
||||||
|
|
||||||
|
import { listView } from '@web/views/list/list_view';
|
||||||
|
import { ListController } from '@web/views/list/list_controller'
|
||||||
|
|
||||||
import { onRendered, onMounted } from "@odoo/owl";
|
import { onRendered, onMounted } from "@odoo/owl";
|
||||||
|
|
||||||
export class RemoveFocusController extends FormController {
|
export class RemoveFocusFormController extends FormController {
|
||||||
setup() {
|
setup() {
|
||||||
super.setup();
|
super.setup();
|
||||||
|
|
||||||
@@ -17,7 +20,23 @@ export class RemoveFocusController extends FormController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
registry.category('views').add('remove_focus_view', {
|
registry.category('views').add('remove_focus_form_view', {
|
||||||
...formView,
|
...formView,
|
||||||
Controller: RemoveFocusController,
|
Controller: RemoveFocusFormController,
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
export class RemoveFocusListController extends ListController {
|
||||||
|
setup() {
|
||||||
|
super.setup();
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
this.__owl__.bdom.el.querySelectorAll(':focus').forEach(element => element.blur());
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
registry.category('views').add('remove_focus_list_view', {
|
||||||
|
...listView,
|
||||||
|
Controller: RemoveFocusListController,
|
||||||
});
|
});
|
||||||
52
sf_base/static/src/scss/change.scss
Normal file
52
sf_base/static/src/scss/change.scss
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
.o_list_renderer .o_list_table tbody > tr > td:not(.o_list_record_selector):not(.o_handle_cell):not(.o_list_button):not(.o_list_record_remove) {
|
||||||
|
white-space: nowrap !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-truncate {
|
||||||
|
overflow: unset !important;
|
||||||
|
text-overflow: unset !important;
|
||||||
|
white-space: unset !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 设置表格不超出页面宽度
|
||||||
|
.o_form_view .o_field_widget .o_list_renderer {
|
||||||
|
width: calc(100% - 64px) !important;
|
||||||
|
margin:0 auto;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 表格针对处理
|
||||||
|
.fixTableCss {
|
||||||
|
text-align: center;
|
||||||
|
.o_list_number_th,.o_list_number {
|
||||||
|
text-align: center!important;
|
||||||
|
}
|
||||||
|
.ui-sortable {
|
||||||
|
tr > td:first-child {
|
||||||
|
padding: 0!important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.row_no {
|
||||||
|
padding: 0!important;;
|
||||||
|
width: 35px!important;
|
||||||
|
}
|
||||||
|
|
||||||
|
th[data-name="total_length"],th[data-name="length"],th[data-name="thickness"] {
|
||||||
|
.flex-row-reverse {
|
||||||
|
span {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 其他不能用js处理的表格
|
||||||
|
.otherTableFix {
|
||||||
|
th[data-name="cutting_tool_material_id"] {
|
||||||
|
width: 100px!important;
|
||||||
|
}
|
||||||
|
th[data-name="ramping_angle_max"],th[data-name="ramping_angle_min"] {
|
||||||
|
width: 200px!important;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,6 +14,7 @@
|
|||||||
|
|
||||||
.img-fluid {
|
.img-fluid {
|
||||||
max-width: unset !important;
|
max-width: unset !important;
|
||||||
|
width: 40px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.o_inner_group .img-fluid {
|
.o_inner_group .img-fluid {
|
||||||
|
|||||||
@@ -159,9 +159,6 @@ td.o_required_modifier {
|
|||||||
display:inline;
|
display:inline;
|
||||||
}
|
}
|
||||||
.diameter{
|
.diameter{
|
||||||
display: flex !important;
|
|
||||||
justify-content: flex-start !important;
|
|
||||||
align-items: center !important;
|
|
||||||
}
|
}
|
||||||
.o_address_format {
|
.o_address_format {
|
||||||
display: flex !important;
|
display: flex !important;
|
||||||
|
|||||||
@@ -109,7 +109,7 @@
|
|||||||
<field name="name">form.sf.machine_tool.type</field>
|
<field name="name">form.sf.machine_tool.type</field>
|
||||||
<field name="model">sf.machine_tool.type</field>
|
<field name="model">sf.machine_tool.type</field>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<form string="机床型号" delete="0">
|
<form string="机床型号" delete="0">
|
||||||
<sheet>
|
<sheet>
|
||||||
<div class="oe_title">
|
<div class="oe_title">
|
||||||
<h1>
|
<h1>
|
||||||
@@ -129,31 +129,28 @@
|
|||||||
<field name="machine_tool_picture" widget="image" nolabel="1"/>
|
<field name="machine_tool_picture" widget="image" nolabel="1"/>
|
||||||
</group>
|
</group>
|
||||||
</group>
|
</group>
|
||||||
<group string="加工能力">
|
<group string="加工能力">
|
||||||
<div>
|
<div>
|
||||||
<field name='jg_image_id' widget="custom_many2many_checkboxes">
|
<field name='jg_image_id' widget="custom_many2many_checkboxes">
|
||||||
|
<tree>
|
||||||
|
<field name="name"/>
|
||||||
|
<field name="image" widget="image"/>
|
||||||
|
|
||||||
<tree>
|
</tree>
|
||||||
<field name="name"/>
|
</field>
|
||||||
<field name="image" widget="image"
|
|
||||||
options="{'size': [100, 100], 'click enlarge': True}"/>
|
|
||||||
|
|
||||||
</tree>
|
|
||||||
</field>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</group>
|
</group>
|
||||||
<group string="冷却方式">
|
<group string="冷却方式">
|
||||||
<div>
|
<div>
|
||||||
<field name='lq_image_id' widget="custom_many2many_checkboxes">
|
<field name='lq_image_id' widget="custom_many2many_checkboxes">
|
||||||
|
|
||||||
<tree>
|
<tree>
|
||||||
<field name="name"/>
|
<field name="name"/>
|
||||||
<field name="image" widget="image"
|
<field name="image" widget="image"/>
|
||||||
options="{'size': [100, 100], 'click enlarge': True}"/>
|
|
||||||
|
|
||||||
</tree>
|
</tree>
|
||||||
</field>
|
</field>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</group>
|
</group>
|
||||||
@@ -178,7 +175,7 @@
|
|||||||
<field name="workbench_H" class="o_address_zip" required="1"
|
<field name="workbench_H" class="o_address_zip" required="1"
|
||||||
options="{'format': false}"/>
|
options="{'format': false}"/>
|
||||||
</div>
|
</div>
|
||||||
<field name="workpiece_load"/>
|
<field name="workpiece_load"/>
|
||||||
<label for="machine_tool_L" string="机床尺寸(mm)"/>
|
<label for="machine_tool_L" string="机床尺寸(mm)"/>
|
||||||
<div class="test_model">
|
<div class="test_model">
|
||||||
<label for="machine_tool_L" string="长"/>
|
<label for="machine_tool_L" string="长"/>
|
||||||
@@ -192,7 +189,7 @@
|
|||||||
<field name="machine_tool_H" class="o_address_zip"
|
<field name="machine_tool_H" class="o_address_zip"
|
||||||
options="{'format': false}"/>
|
options="{'format': false}"/>
|
||||||
</div>
|
</div>
|
||||||
<label for="T_trough_num" string="T型槽尺寸:"/>
|
<label for="T_trough_num" string="T型槽尺寸:"/>
|
||||||
<div class="test_model">
|
<div class="test_model">
|
||||||
<label for="T_trough_num" string="槽数"/>
|
<label for="T_trough_num" string="槽数"/>
|
||||||
<field name="T_trough_num" class="o_address_zip"
|
<field name="T_trough_num" class="o_address_zip"
|
||||||
@@ -205,20 +202,20 @@
|
|||||||
<field name="T_trough_distance" class="o_address_zip"
|
<field name="T_trough_distance" class="o_address_zip"
|
||||||
options="{'format': false}"/>
|
options="{'format': false}"/>
|
||||||
</div>
|
</div>
|
||||||
<!-- <field name="feed_speed" required="1"/>-->
|
<!-- <field name="feed_speed" required="1"/>-->
|
||||||
<!-- <label for="precision_min" string="X轴定位精度(mm)"/>-->
|
<!-- <label for="precision_min" string="X轴定位精度(mm)"/>-->
|
||||||
<!-- <div class="test_model">-->
|
<!-- <div class="test_model">-->
|
||||||
<!-- <label for="precision_min" string="最小(min)"/>-->
|
<!-- <label for="precision_min" string="最小(min)"/>-->
|
||||||
<!-- <field name="precision_min" class="o_address_zip" required="1"-->
|
<!-- <field name="precision_min" class="o_address_zip" required="1"-->
|
||||||
<!-- options="{'format': false}"/>-->
|
<!-- options="{'format': false}"/>-->
|
||||||
<!-- <span>&nbsp;</span>-->
|
<!-- <span>&nbsp;</span>-->
|
||||||
<!-- <label for="precision_max" string="最大(max)"/>-->
|
<!-- <label for="precision_max" string="最大(max)"/>-->
|
||||||
<!-- <field name="precision_max" class="o_address_zip" required="1"-->
|
<!-- <field name="precision_max" class="o_address_zip" required="1"-->
|
||||||
<!-- options="{'format': false}"/>-->
|
<!-- options="{'format': false}"/>-->
|
||||||
<!-- </div>-->
|
<!-- </div>-->
|
||||||
|
|
||||||
<!-- <field name="lead_screw" required="1"/>-->
|
<!-- <field name="lead_screw" required="1"/>-->
|
||||||
<!-- <field name="guide_rail" required="1"/>-->
|
<!-- <field name="guide_rail" required="1"/>-->
|
||||||
<field name="number_of_axles" required="1" widget="radio"
|
<field name="number_of_axles" required="1" widget="radio"
|
||||||
options="{'horizontal': true}"/>
|
options="{'horizontal': true}"/>
|
||||||
<label for="x_axis" string="加工行程(mm)"
|
<label for="x_axis" string="加工行程(mm)"
|
||||||
@@ -258,7 +255,7 @@
|
|||||||
</group>
|
</group>
|
||||||
<group string="主轴">
|
<group string="主轴">
|
||||||
<field name="taper_type_id" required="1"/>
|
<field name="taper_type_id" required="1"/>
|
||||||
<label for="distance_min" string="主轴端面-工作台距离(mm)"/>
|
<label for="distance_min" string="主轴端面-工作台距离(mm)"/>
|
||||||
<div class="test_model">
|
<div class="test_model">
|
||||||
<label for="distance_min" string="最小(min)"/>
|
<label for="distance_min" string="最小(min)"/>
|
||||||
<field name="distance_min" class="o_address_zip"
|
<field name="distance_min" class="o_address_zip"
|
||||||
@@ -268,7 +265,7 @@
|
|||||||
<field name="distance_max" class="o_address_zip"
|
<field name="distance_max" class="o_address_zip"
|
||||||
options="{'format': false}"/>
|
options="{'format': false}"/>
|
||||||
</div>
|
</div>
|
||||||
<field name="rotate_speed" string="主轴最高转速(r/min)"
|
<field name="rotate_speed" string="主轴最高转速(r/min)"
|
||||||
options="{'format': false}"/>
|
options="{'format': false}"/>
|
||||||
<field name="spindle_center_distance"/>
|
<field name="spindle_center_distance"/>
|
||||||
<field name="spindle_continuous_power"/>
|
<field name="spindle_continuous_power"/>
|
||||||
@@ -286,50 +283,50 @@
|
|||||||
<page string="进给/精度参数">
|
<page string="进给/精度参数">
|
||||||
<group>
|
<group>
|
||||||
<group string="进给参数">
|
<group string="进给参数">
|
||||||
<field name="X_axis_rapid_traverse_speed"/>
|
<field name="X_axis_rapid_traverse_speed"/>
|
||||||
<field name="Y_axis_rapid_traverse_speed"/>
|
<field name="Y_axis_rapid_traverse_speed"/>
|
||||||
<field name="Z_axis_rapid_traverse_speed"/>
|
<field name="Z_axis_rapid_traverse_speed"/>
|
||||||
<field name="a_axis_rapid_traverse_speed"/>
|
<field name="a_axis_rapid_traverse_speed"/>
|
||||||
<field name="b_axis_rapid_traverse_speed"/>
|
<field name="b_axis_rapid_traverse_speed"/>
|
||||||
<field name="c_axis_rapid_traverse_speed"/>
|
<field name="c_axis_rapid_traverse_speed"/>
|
||||||
<field name="straight_cutting_feed_rate"/>
|
<field name="straight_cutting_feed_rate"/>
|
||||||
<field name="rotary_cutting_feed_rate"/>
|
<field name="rotary_cutting_feed_rate"/>
|
||||||
</group>
|
</group>
|
||||||
<group string="精度参数">
|
<group string="精度参数">
|
||||||
<field name="X_precision"/>
|
<field name="X_precision"/>
|
||||||
<field name="X_precision_repeat"/>
|
<field name="X_precision_repeat"/>
|
||||||
<field name="Y_precision"/>
|
<field name="Y_precision"/>
|
||||||
<field name="Y_precision_repeat"/>
|
<field name="Y_precision_repeat"/>
|
||||||
<field name="Z_precision"/>
|
<field name="Z_precision"/>
|
||||||
<field name="Z_precision_repeat"/>
|
<field name="Z_precision_repeat"/>
|
||||||
<field name="a_precision"/>
|
<field name="a_precision"/>
|
||||||
<field name="a_precision_repeat"/>
|
<field name="a_precision_repeat"/>
|
||||||
<field name="b_precision"/>
|
<field name="b_precision"/>
|
||||||
<field name="b_precision_repeat"/>
|
<field name="b_precision_repeat"/>
|
||||||
<field name="c_precision"/>
|
<field name="c_precision"/>
|
||||||
<field name="c_precision_repeat"/>
|
<field name="c_precision_repeat"/>
|
||||||
</group>
|
</group>
|
||||||
</group>
|
</group>
|
||||||
</page>
|
</page>
|
||||||
<page string="刀库参数">
|
<page string="刀库参数">
|
||||||
<group>
|
<group>
|
||||||
<group string="刀具">
|
<group string="刀具">
|
||||||
<!-- <field name="knife_type" required="1"/>-->
|
<!-- <field name="knife_type" required="1"/>-->
|
||||||
<field name="number_of_knife_library" required="1" options="{'format': false}"/>
|
<field name="number_of_knife_library" required="1" options="{'format': false}"/>
|
||||||
<!-- <field name="tool_speed" required="1"/>-->
|
<!-- <field name="tool_speed" required="1"/>-->
|
||||||
<field name="tool_full_diameter_max"/>
|
<field name="tool_full_diameter_max"/>
|
||||||
<field name="tool_perimeter_diameter_max"/>
|
<field name="tool_perimeter_diameter_max"/>
|
||||||
<field name="tool_long_max"/>
|
<field name="tool_long_max"/>
|
||||||
<!-- <label for="tool_diameter_min" string="刀具刀径(mm)"/>-->
|
<!-- <label for="tool_diameter_min" string="刀具刀径(mm)"/>-->
|
||||||
<!-- <div class="test_model">-->
|
<!-- <div class="test_model">-->
|
||||||
<!-- <label for="tool_diameter_min" string="最小(min)"/>-->
|
<!-- <label for="tool_diameter_min" string="最小(min)"/>-->
|
||||||
<!-- <field name="tool_diameter_min" class="o_address_zip" required="1"-->
|
<!-- <field name="tool_diameter_min" class="o_address_zip" required="1"-->
|
||||||
<!-- options="{'format': false}"/>-->
|
<!-- options="{'format': false}"/>-->
|
||||||
<!-- <span>&nbsp;</span>-->
|
<!-- <span>&nbsp;</span>-->
|
||||||
<!-- <label for="tool_diameter_max" string="最大(max)"/>-->
|
<!-- <label for="tool_diameter_max" string="最大(max)"/>-->
|
||||||
<!-- <field name="tool_diameter_max" class="o_address_zip" required="1"-->
|
<!-- <field name="tool_diameter_max" class="o_address_zip" required="1"-->
|
||||||
<!-- options="{'format': false}"/>-->
|
<!-- options="{'format': false}"/>-->
|
||||||
<!-- </div>-->
|
<!-- </div>-->
|
||||||
<field name="tool_quality_max"/>
|
<field name="tool_quality_max"/>
|
||||||
<field name="T_tool_time"/>
|
<field name="T_tool_time"/>
|
||||||
<field name="C_tool_time"/>
|
<field name="C_tool_time"/>
|
||||||
@@ -617,4 +614,45 @@
|
|||||||
<field name="res_model">sf.machine.control_system</field>
|
<field name="res_model">sf.machine.control_system</field>
|
||||||
<field name="view_mode">tree</field>
|
<field name="view_mode">tree</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
|
#------------------加工精度------------------
|
||||||
|
<record model="ir.ui.view" id="tree_sf_machining_accuracy_view">
|
||||||
|
<field name="name">tree.sf.machining.accuracy</field>
|
||||||
|
<field name="model">sf.machining.accuracy</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<tree string="加工精度" create="0" edit="0" delete="0">
|
||||||
|
<field name="name"/>
|
||||||
|
<field name="standard_tolerance"/>
|
||||||
|
</tree>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="action_sf_machining_accuracy" model="ir.actions.act_window">
|
||||||
|
<field name="name">加工精度</field>
|
||||||
|
<field name="type">ir.actions.act_window</field>
|
||||||
|
<field name="res_model">sf.machining.accuracy</field>
|
||||||
|
<field name="view_mode">tree</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
#------------------坯料冗余量------------------
|
||||||
|
<record model="ir.ui.view" id="tree_sf_embryo_redundancy_view">
|
||||||
|
<field name="name">tree.sf.embryo.redundancy</field>
|
||||||
|
<field name="model">sf.embryo.redundancy</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<tree string="坯料冗余量" create="0" edit="0" delete="0">
|
||||||
|
<field name="name"/>
|
||||||
|
<field name="code"/>
|
||||||
|
<field name="long"/>
|
||||||
|
<field name="width"/>
|
||||||
|
<field name="height"/>
|
||||||
|
</tree>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="action_sf_embryo_redundancy" model="ir.actions.act_window">
|
||||||
|
<field name="name">坯料冗余量</field>
|
||||||
|
<field name="type">ir.actions.act_window</field>
|
||||||
|
<field name="res_model">sf.embryo.redundancy</field>
|
||||||
|
<field name="view_mode">tree</field>
|
||||||
|
</record>
|
||||||
</odoo>
|
</odoo>
|
||||||
@@ -12,7 +12,6 @@
|
|||||||
</tree>
|
</tree>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record model="ir.ui.view" id="mrs_production_process_parameter_form">
|
<record model="ir.ui.view" id="mrs_production_process_parameter_form">
|
||||||
<field name="model">sf.production.process.parameter</field>
|
<field name="model">sf.production.process.parameter</field>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
@@ -26,19 +25,19 @@
|
|||||||
<group>
|
<group>
|
||||||
<group>
|
<group>
|
||||||
<field name="code" readonly="1"/>
|
<field name="code" readonly="1"/>
|
||||||
<field name="process_id" readonly="1"/>
|
<field name="process_id" attrs="{'readonly': [('code', '!=', False)]}"/>
|
||||||
<field name="process_description" readonly="1"/>
|
<field name="process_description" attrs="{'readonly': [('code', '!=', False)]}"/>
|
||||||
<field name="gain_way"/>
|
<field name="gain_way"/>
|
||||||
</group>
|
</group>
|
||||||
<group>
|
<group>
|
||||||
<field name="processing_day" readonly="1"/>
|
<field name="processing_day" attrs="{'readonly': [('code', '!=', False)]}"/>
|
||||||
<field name="travel_day" readonly="1"/>
|
<field name="travel_day" attrs="{'readonly': [('code', '!=', False)]}"/>
|
||||||
<field name="processing_mm" readonly="1"/>
|
<field name="processing_mm" attrs="{'readonly': [('code', '!=', False)]}"/>
|
||||||
</group>
|
</group>
|
||||||
</group>
|
</group>
|
||||||
<notebook>
|
<notebook>
|
||||||
<page string="适用材料">
|
<page string="适用材料">
|
||||||
<field name="materials_model_ids" readonly="1"></field>
|
<field name="materials_model_ids" attrs="{'readonly': [('code', '!=', False)]}"></field>
|
||||||
</page>
|
</page>
|
||||||
</notebook>
|
</notebook>
|
||||||
</sheet>
|
</sheet>
|
||||||
|
|||||||
@@ -141,12 +141,25 @@
|
|||||||
sequence="1"
|
sequence="1"
|
||||||
action="action_sf_machine_brand"/>
|
action="action_sf_machine_brand"/>
|
||||||
|
|
||||||
|
<menuitem
|
||||||
|
id="menu_sf_embryo_redundancy"
|
||||||
|
parent="menu_sf_base"
|
||||||
|
name="坯料冗余"
|
||||||
|
sequence="2"
|
||||||
|
action="action_sf_embryo_redundancy"/>
|
||||||
|
|
||||||
|
<menuitem
|
||||||
|
id="menu_sf_machining_accuracy"
|
||||||
|
parent="menu_sf_base"
|
||||||
|
name="加工精度"
|
||||||
|
sequence="3"
|
||||||
|
action="action_sf_machining_accuracy"/>
|
||||||
|
|
||||||
<menuitem
|
<menuitem
|
||||||
id="menu_sf_machine_control_system"
|
id="menu_sf_machine_control_system"
|
||||||
parent="menu_sf_base"
|
parent="menu_sf_base"
|
||||||
name="数控系统"
|
name="数控系统"
|
||||||
sequence="1"
|
sequence="4"
|
||||||
action="action_sf_machine_control_system"/>
|
action="action_sf_machine_control_system"/>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
<field name="handle_length"/>
|
<field name="handle_length"/>
|
||||||
<field name="blade_tip_diameter"/>
|
<field name="blade_tip_diameter"/>
|
||||||
<field name="blade_tip_working_size"/>
|
<field name="blade_tip_working_size"/>
|
||||||
|
<field name="tip_r_size"/>
|
||||||
<field name="blade_tip_taper"/>
|
<field name="blade_tip_taper"/>
|
||||||
<field name="blade_diameter"/>
|
<field name="blade_diameter"/>
|
||||||
<field name="blade_length"/>
|
<field name="blade_length"/>
|
||||||
@@ -95,6 +96,7 @@
|
|||||||
<field name="handle_length"/>
|
<field name="handle_length"/>
|
||||||
<field name="blade_tip_diameter"/>
|
<field name="blade_tip_diameter"/>
|
||||||
<field name="blade_tip_working_size"/>
|
<field name="blade_tip_working_size"/>
|
||||||
|
<field name="tip_r_size"/>
|
||||||
<field name="blade_tip_taper"/>
|
<field name="blade_tip_taper"/>
|
||||||
<field name="blade_diameter"/>
|
<field name="blade_diameter"/>
|
||||||
<field name="blade_length"/>
|
<field name="blade_length"/>
|
||||||
@@ -139,6 +141,7 @@
|
|||||||
</group>
|
</group>
|
||||||
<group attrs="{'invisible': [('cutting_tool_type', '!=', '整体式刀具')]}">
|
<group attrs="{'invisible': [('cutting_tool_type', '!=', '整体式刀具')]}">
|
||||||
<field name="blade_tip_working_size"/>
|
<field name="blade_tip_working_size"/>
|
||||||
|
<field name="tip_r_size"/>
|
||||||
<field name="blade_tip_diameter" class="diameter"/>
|
<field name="blade_tip_diameter" class="diameter"/>
|
||||||
<field name="blade_tip_taper"/>
|
<field name="blade_tip_taper"/>
|
||||||
<field name="blade_helix_angle"/>
|
<field name="blade_helix_angle"/>
|
||||||
|
|||||||
@@ -80,10 +80,10 @@
|
|||||||
<field name="name">sf.cutter.function.tree</field>
|
<field name="name">sf.cutter.function.tree</field>
|
||||||
<field name="model">sf.functional.cutting.tool.model</field>
|
<field name="model">sf.functional.cutting.tool.model</field>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<tree string="功能刀具类型" create="0" delete="0" edit="1">
|
<tree string="功能刀具类型" create="0" delete="0" edit="1" editable="bottom">
|
||||||
<field name="name" string="名称"/>
|
<field name="name" string="名称" readonly="1"/>
|
||||||
<field name="code"/>
|
<field name="code" readonly="1"/>
|
||||||
<field name="remark"/>
|
<field name="remark" readonly="1"/>
|
||||||
</tree>
|
</tree>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
@@ -222,6 +222,7 @@
|
|||||||
<field name="handle_diameter" class="diameter"/>
|
<field name="handle_diameter" class="diameter"/>
|
||||||
<field name="handle_length"/>
|
<field name="handle_length"/>
|
||||||
<field name="blade_tip_working_size" class="du"/>
|
<field name="blade_tip_working_size" class="du"/>
|
||||||
|
<field name="tip_r_size"/>
|
||||||
<field name="blade_tip_diameter" class="diameter"/>
|
<field name="blade_tip_diameter" class="diameter"/>
|
||||||
<field name="blade_tip_taper" class="du"/>
|
<field name="blade_tip_taper" class="du"/>
|
||||||
<field name="blade_helix_angle" class="du"/>
|
<field name="blade_helix_angle" class="du"/>
|
||||||
@@ -359,6 +360,7 @@
|
|||||||
<field name="cooling_jacket"/>
|
<field name="cooling_jacket"/>
|
||||||
</tree>
|
</tree>
|
||||||
</field>
|
</field>
|
||||||
|
<script src="/sf_base/static/js/setTableWidth.js?time=3"></script>
|
||||||
</page>
|
</page>
|
||||||
<page string="切削速度Vc"
|
<page string="切削速度Vc"
|
||||||
attrs="{'invisible': [('cutting_tool_type', 'not in', ('整体式刀具','刀片'))]}">
|
attrs="{'invisible': [('cutting_tool_type', 'not in', ('整体式刀具','刀片'))]}">
|
||||||
@@ -379,6 +381,9 @@
|
|||||||
<field name="cutting_speed"/>
|
<field name="cutting_speed"/>
|
||||||
</tree>
|
</tree>
|
||||||
</field>
|
</field>
|
||||||
|
<script src="/sf_base/static/js/customTable.js?time=3"></script>
|
||||||
|
<script src="/sf_base/static/js/setTableWidth.js?time=3"></script>
|
||||||
|
|
||||||
</page>
|
</page>
|
||||||
<page string="每齿走刀量fz"
|
<page string="每齿走刀量fz"
|
||||||
attrs="{'invisible': [('cutting_tool_type', 'not in', ('整体式刀具','刀片'))]}">
|
attrs="{'invisible': [('cutting_tool_type', 'not in', ('整体式刀具','刀片'))]}">
|
||||||
@@ -391,6 +396,8 @@
|
|||||||
<field name="feed_per_tooth"/>
|
<field name="feed_per_tooth"/>
|
||||||
</tree>
|
</tree>
|
||||||
</field>
|
</field>
|
||||||
|
<div id="updateTable"></div>
|
||||||
|
<script src="/sf_base/static/js/updateTable.js?time=3"></script>
|
||||||
<field name="feed_per_tooth_ids_3"
|
<field name="feed_per_tooth_ids_3"
|
||||||
attrs="{'invisible': [('cutting_tool_type', 'not in', ('刀片'))]}">
|
attrs="{'invisible': [('cutting_tool_type', 'not in', ('刀片'))]}">
|
||||||
<tree editable="bottom" class="center" create="0" delete="0">
|
<tree editable="bottom" class="center" create="0" delete="0">
|
||||||
@@ -577,7 +584,7 @@
|
|||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record model="ir.ui.view" id="view_cutting_tool_material_search">
|
<record model="ir.ui.view" id="view_cutting_tool_inventory_search">
|
||||||
<field name="name">sf.tool.inventory.search</field>
|
<field name="name">sf.tool.inventory.search</field>
|
||||||
<field name="model">sf.tool.inventory</field>
|
<field name="model">sf.tool.inventory</field>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import traceback
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
import logging
|
import logging
|
||||||
import requests
|
import requests
|
||||||
@@ -35,6 +36,7 @@ class StatusChange(models.Model):
|
|||||||
# 使用super()来调用原始方法(在本例中为'sale.order'模型的'action_confirm'方法)
|
# 使用super()来调用原始方法(在本例中为'sale.order'模型的'action_confirm'方法)
|
||||||
try:
|
try:
|
||||||
res = super(StatusChange, self).action_confirm()
|
res = super(StatusChange, self).action_confirm()
|
||||||
|
logging.info('原生方法返回结果:%s' % res)
|
||||||
# 原有方法执行后,进行额外的操作(如调用外部API)
|
# 原有方法执行后,进行额外的操作(如调用外部API)
|
||||||
process_start_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
process_start_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||||||
config = self.env['res.config.settings'].get_values()
|
config = self.env['res.config.settings'].get_values()
|
||||||
@@ -53,11 +55,14 @@ class StatusChange(models.Model):
|
|||||||
if not ret.get('error'):
|
if not ret.get('error'):
|
||||||
logging.info('接口已经执行=============')
|
logging.info('接口已经执行=============')
|
||||||
else:
|
else:
|
||||||
logging.error('工厂加工同步订单状态失败 {}'.format(ret.text))
|
traceback_error = traceback.format_exc()
|
||||||
raise UserError('工厂加工同步订单状态失败')
|
logging.error("bfm订单状态同步失败:%s" % traceback_error)
|
||||||
|
raise UserError('工厂加工同步订单状态到bfm失败')
|
||||||
except UserError as e:
|
except UserError as e:
|
||||||
logging.error('工厂加工同步订单状态失败 {}'.format(e))
|
traceback_error = traceback.format_exc()
|
||||||
raise UserError('工厂加工同步订单状态失败')
|
logging.error("工厂加工同步订单状态失败:%s " % traceback_error)
|
||||||
|
raise UserError(e)
|
||||||
|
logging.info('最终返回值:%s' % res)
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def action_cancel(self):
|
def action_cancel(self):
|
||||||
|
|||||||
@@ -58,7 +58,7 @@
|
|||||||
<field name="inherit_id" ref="stock.view_picking_form"/>
|
<field name="inherit_id" ref="stock.view_picking_form"/>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<xpath expr="//form//sheet//notebook//page[@name='operations']" position="after">
|
<xpath expr="//form//sheet//notebook//page[@name='operations']" position="after">
|
||||||
<page string="发货信息" name="tracking">
|
<page string="发货信息" name="tracking" attrs="{'invisible': [('picking_type_code', '!=', 'outgoing')]}">
|
||||||
<group>
|
<group>
|
||||||
<group>
|
<group>
|
||||||
<field name="senderNickName" attrs="{'invisible': [('check_out', '!=', 'OUT')]}"/>
|
<field name="senderNickName" attrs="{'invisible': [('check_out', '!=', 'OUT')]}"/>
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
""",
|
""",
|
||||||
'category': 'sf',
|
'category': 'sf',
|
||||||
'website': 'https://www.sf.jikimo.com',
|
'website': 'https://www.sf.jikimo.com',
|
||||||
'depends': ['sf_base', 'web_widget_model_viewer', 'mrp_subcontracting', 'purchase_stock', 'uom', ],
|
'depends': ['sf_base', 'mrp_subcontracting', 'purchase_stock', 'uom'],
|
||||||
'data': [
|
'data': [
|
||||||
'data/product_data.xml',
|
'data/product_data.xml',
|
||||||
'data/uom_data.xml',
|
'data/uom_data.xml',
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" ?>
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
<odoo>
|
<odoo>
|
||||||
<data>
|
<data noupdate="1">
|
||||||
<record id="product_category_embryo_sf" model="product.category">
|
<record id="product_category_embryo_sf" model="product.category">
|
||||||
<field name="name">坯料</field>
|
<field name="name">坯料</field>
|
||||||
<field name="type">坯料</field>
|
<field name="type">坯料</field>
|
||||||
|
|||||||
@@ -1 +1,3 @@
|
|||||||
from . import product_supplierinfo
|
from . import product_supplierinfo
|
||||||
|
from . import stock_rule_inherit
|
||||||
|
|
||||||
|
|||||||
43
sf_dlm/models/stock_rule_inherit.py
Normal file
43
sf_dlm/models/stock_rule_inherit.py
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import logging
|
||||||
|
from odoo import models, fields, api, _
|
||||||
|
|
||||||
|
|
||||||
|
class StockRuleInherit(models.Model):
|
||||||
|
_inherit = 'stock.rule'
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def _run_buy(self, procurements):
|
||||||
|
# 首先调用父类的 _run_buy 方法,以保留原有逻辑
|
||||||
|
super(StockRuleInherit, self)._run_buy(procurements)
|
||||||
|
|
||||||
|
# 然后在这里添加自定义的逻辑
|
||||||
|
for procurement, rule in procurements:
|
||||||
|
product = procurement.product_id
|
||||||
|
# 获取主 BOM
|
||||||
|
bom = self.env['mrp.bom'].search([('product_tmpl_id', '=', product.product_tmpl_id.id)], limit=1)
|
||||||
|
|
||||||
|
if bom:
|
||||||
|
# 遍历 BOM 中的组件(即坯料等)
|
||||||
|
for line in bom.bom_line_ids:
|
||||||
|
raw_material = line.product_id
|
||||||
|
# 检查路线
|
||||||
|
for route in raw_material.route_ids:
|
||||||
|
# print('route.name:', route.name)
|
||||||
|
if route.name == '按订单补给外包商': # 或者用 route.id 检查精确的路线
|
||||||
|
print("按订单补给外包商============是")
|
||||||
|
# 使用 procurement.values['supplier'] 获取供应商
|
||||||
|
supplier = procurement.values.get('supplier')
|
||||||
|
if supplier:
|
||||||
|
domain = rule._make_po_get_domain(procurement.company_id, procurement.values,
|
||||||
|
supplier.partner_id)
|
||||||
|
logging.info("domain=============: %s", domain)
|
||||||
|
po = self.env['purchase.order'].sudo().search([
|
||||||
|
('partner_id', '=', supplier.partner_id.id),
|
||||||
|
('company_id', '=', procurement.company_id.id), # 保证公司一致
|
||||||
|
('origin', '=', procurement.origin), # 根据来源匹配
|
||||||
|
('state', '=', 'draft') # 状态为草稿
|
||||||
|
], limit=1)
|
||||||
|
logging.info("po=: %s", po)
|
||||||
|
if po:
|
||||||
|
po.write({'purchase_type': 'consignment'})
|
||||||
|
break
|
||||||
@@ -9,7 +9,7 @@
|
|||||||
""",
|
""",
|
||||||
'category': 'sf',
|
'category': 'sf',
|
||||||
'website': 'https://www.sf.jikimo.com',
|
'website': 'https://www.sf.jikimo.com',
|
||||||
'depends': ['sf_sale', 'sf_dlm', 'sf_manufacturing'],
|
'depends': ['sf_sale', 'sf_dlm', 'sf_manufacturing','jikimo_attachment_viewer'],
|
||||||
'data': [
|
'data': [
|
||||||
'data/stock_data.xml',
|
'data/stock_data.xml',
|
||||||
'views/product_template_management_view.xml',
|
'views/product_template_management_view.xml',
|
||||||
|
|||||||
@@ -1,6 +1,12 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" ?>
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
<odoo>
|
<odoo>
|
||||||
<data>
|
<data>
|
||||||
|
<record id="mrp.product_template_action" model="ir.actions.act_window">
|
||||||
|
<field name="view_mode">tree,kanban,form,activity</field>
|
||||||
|
<field name="context">
|
||||||
|
{"search_default_categ_id":1,"search_default_consumable": 1, 'default_detailed_type': 'product'}
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
<record model="ir.ui.view" id="view_sale_product_template_form_inherit_sf">
|
<record model="ir.ui.view" id="view_sale_product_template_form_inherit_sf">
|
||||||
<field name="name">product.template.form.inherit.sf</field>
|
<field name="name">product.template.form.inherit.sf</field>
|
||||||
<field name="model">product.template</field>
|
<field name="model">product.template</field>
|
||||||
@@ -10,8 +16,10 @@
|
|||||||
<field name='categ_id' class="custom_required" attrs="{'readonly': [('id', '!=', False)]}"/>
|
<field name='categ_id' class="custom_required" attrs="{'readonly': [('id', '!=', False)]}"/>
|
||||||
<field name='is_bfm' invisible="1"/>
|
<field name='is_bfm' invisible="1"/>
|
||||||
<field name='categ_type' invisible="1"/>
|
<field name='categ_type' invisible="1"/>
|
||||||
|
<field name='part_name' attrs="{'invisible': [('categ_type', '!=', '成品')]}"/>
|
||||||
<field name='part_number' attrs="{'invisible': [('categ_type', '!=', '成品')]}"/>
|
<field name='part_number' attrs="{'invisible': [('categ_type', '!=', '成品')]}"/>
|
||||||
<field name='manual_quotation' attrs="{'invisible':[('upload_model_file', '=', [])]}"/>
|
<field name='manual_quotation' attrs="{'invisible':[('upload_model_file', '=', [])]}"/>
|
||||||
|
<field name="is_customer_provided" attrs="{'invisible': [('categ_type', 'not in', ['成品', '坯料'])], 'readonly': True}" />
|
||||||
<field name="upload_model_file"
|
<field name="upload_model_file"
|
||||||
widget="many2many_binary"
|
widget="many2many_binary"
|
||||||
attrs="{'invisible': ['|', '|',('categ_type', '!=', '成品'),('categ_type', '=', False),('is_bfm','=', True)]}"/>
|
attrs="{'invisible': ['|', '|',('categ_type', '!=', '成品'),('categ_type', '=', False),('is_bfm','=', True)]}"/>
|
||||||
@@ -58,6 +66,9 @@
|
|||||||
attrs="{'invisible': [('categ_type', '!=', '夹具')],'required': [('categ_type', '=', '夹具')]}"
|
attrs="{'invisible': [('categ_type', '!=', '夹具')],'required': [('categ_type', '=', '夹具')]}"
|
||||||
domain="[('fixture_model_id','=',fixture_model_id)]"/>
|
domain="[('fixture_model_id','=',fixture_model_id)]"/>
|
||||||
</field>
|
</field>
|
||||||
|
<xpath expr="//field[@name='uom_id']" position="before">
|
||||||
|
<field name="is_manual_processing" attrs="{'invisible': [('categ_type', 'not in', ['成品', '坯料'])], 'readonly': True}" />
|
||||||
|
</xpath>
|
||||||
<xpath expr="//label[@for='volume']" position="before">
|
<xpath expr="//label[@for='volume']" position="before">
|
||||||
<label for="length" string="尺寸"
|
<label for="length" string="尺寸"
|
||||||
attrs="{'invisible':[('product_variant_count', '>', 1), ('is_product_variant', '=', False)]}"/>
|
attrs="{'invisible':[('product_variant_count', '>', 1), ('is_product_variant', '=', False)]}"/>
|
||||||
@@ -88,7 +99,8 @@
|
|||||||
</div>
|
</div>
|
||||||
<field name="model_volume" string="体积[mm³]"/>
|
<field name="model_volume" string="体积[mm³]"/>
|
||||||
<field name="product_model_type_id" string="模型类型"/>
|
<field name="product_model_type_id" string="模型类型"/>
|
||||||
<field name="model_processing_panel" placeholder="例如R,U" string="加工面板" readonly="1"/>
|
<field name="model_processing_panel" placeholder="例如R,U" string="加工面板"
|
||||||
|
readonly="1"/>
|
||||||
<field name="model_machining_precision"/>
|
<field name="model_machining_precision"/>
|
||||||
<field name="model_process_parameters_ids" string="表面工艺参数"
|
<field name="model_process_parameters_ids" string="表面工艺参数"
|
||||||
widget="many2many_tags"
|
widget="many2many_tags"
|
||||||
@@ -103,11 +115,22 @@
|
|||||||
'刀具')], 'required': True}
|
'刀具')], 'required': True}
|
||||||
</attribute>
|
</attribute>
|
||||||
</xpath>
|
</xpath>
|
||||||
<!-- <xpath expr="//field[@name='default_code']" position="attributes">-->
|
<xpath expr="//sheet//notebook" position="inside">
|
||||||
<!-- <attribute name="attrs">{'readonly': [('categ_type', '=', '刀具')], 'invisible':-->
|
<page string="2D加工图纸" attrs="{'invisible': [('categ_type', 'not in', ['成品', '坯料'])]}">
|
||||||
<!-- [('product_variant_count', '>' , 1)]}-->
|
<field name='machining_drawings' widget="adaptive_viewer"/>
|
||||||
<!-- </attribute>-->
|
</page>
|
||||||
<!-- </xpath>-->
|
</xpath>
|
||||||
|
<xpath expr="//sheet//notebook" position="inside">
|
||||||
|
<page string="质检标准" attrs="{'invisible': [('categ_type', 'not in', ['成品', '坯料'])]}">
|
||||||
|
<field name='quality_standard' widget="adaptive_viewer"/>
|
||||||
|
|
||||||
|
</page>
|
||||||
|
</xpath>
|
||||||
|
<!-- <xpath expr="//field[@name='default_code']" position="attributes">-->
|
||||||
|
<!-- <attribute name="attrs">{'readonly': [('categ_type', '=', '刀具')], 'invisible':-->
|
||||||
|
<!-- [('product_variant_count', '>' , 1)]}-->
|
||||||
|
<!-- </attribute>-->
|
||||||
|
<!-- </xpath>-->
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
@@ -185,6 +208,8 @@
|
|||||||
attrs="{'invisible': [('cutting_tool_type', '!=', '整体式刀具')],'readonly': [('id', '!=', False)]}"/>
|
attrs="{'invisible': [('cutting_tool_type', '!=', '整体式刀具')],'readonly': [('id', '!=', False)]}"/>
|
||||||
<field name="cutting_tool_blade_tip_working_size"
|
<field name="cutting_tool_blade_tip_working_size"
|
||||||
attrs="{'invisible': [('cutting_tool_type', '!=', '整体式刀具')],'readonly': [('id', '!=', False)]}"/>
|
attrs="{'invisible': [('cutting_tool_type', '!=', '整体式刀具')],'readonly': [('id', '!=', False)]}"/>
|
||||||
|
<field name="cutting_tool_blade_tip_r_size"
|
||||||
|
attrs="{'invisible': [('cutting_tool_type', '!=', '整体式刀具')],'readonly': [('id', '!=', False)]}"/>
|
||||||
<field name="cutting_tool_blade_tip_diameter" string="刀尖直径(mm)" class="diameter"
|
<field name="cutting_tool_blade_tip_diameter" string="刀尖直径(mm)" class="diameter"
|
||||||
attrs="{'invisible': [('cutting_tool_type', '!=', '整体式刀具')],'readonly': [('id', '!=', False)]}"/>
|
attrs="{'invisible': [('cutting_tool_type', '!=', '整体式刀具')],'readonly': [('id', '!=', False)]}"/>
|
||||||
<field name="cutting_tool_blade_tip_taper" string="刀尖锥度(°)"
|
<field name="cutting_tool_blade_tip_taper" string="刀尖锥度(°)"
|
||||||
|
|||||||
@@ -7,9 +7,11 @@
|
|||||||
'sequence': 1,
|
'sequence': 1,
|
||||||
'category': 'sf',
|
'category': 'sf',
|
||||||
'website': 'https://www.sf.jikimo.com',
|
'website': 'https://www.sf.jikimo.com',
|
||||||
'depends': ['hr'],
|
'depends': ['base', 'hr'],
|
||||||
'data': [
|
'data': [
|
||||||
'views/hr_employee.xml',
|
'views/hr_employee.xml',
|
||||||
|
'views/res_config_settings_views.xml',
|
||||||
|
'data/cron_data.xml',
|
||||||
],
|
],
|
||||||
'demo': [
|
'demo': [
|
||||||
],
|
],
|
||||||
|
|||||||
15
sf_hr/data/cron_data.xml
Normal file
15
sf_hr/data/cron_data.xml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<odoo>
|
||||||
|
<data noupdate="1">
|
||||||
|
<record model="ir.cron" id="ir_cron_employee_info_sync">
|
||||||
|
<field name="name">员工企微id同步</field>
|
||||||
|
<field name="model_id" ref="hr.model_hr_employee"/>
|
||||||
|
<field name="state">code</field>
|
||||||
|
<field name="code">model._employee_info_sync()</field>
|
||||||
|
<field name="interval_number">1</field>
|
||||||
|
<field name="interval_type">days</field>
|
||||||
|
<field name="numbercall">-1</field>
|
||||||
|
<field name="doall" eval="False"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
@@ -1,2 +1,5 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from . import hr_employee
|
||||||
|
from . import res_config_setting
|
||||||
|
from . import res_users
|
||||||
|
|||||||
64
sf_hr/models/hr_employee.py
Normal file
64
sf_hr/models/hr_employee.py
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
import logging
|
||||||
|
import requests
|
||||||
|
from odoo import models, fields, api, _
|
||||||
|
|
||||||
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class JkmPracticeEmployee(models.Model):
|
||||||
|
_inherit = 'hr.employee'
|
||||||
|
_description = '员工信息'
|
||||||
|
|
||||||
|
we_id = fields.Char(string='企微ID', index=True)
|
||||||
|
|
||||||
|
@api.model_create_multi
|
||||||
|
def create(self, vals_list):
|
||||||
|
for val in vals_list:
|
||||||
|
if 'work_email' in val:
|
||||||
|
val["we_id"] = self._get_we_id(val.get('work_email'))
|
||||||
|
return super(JkmPracticeEmployee, self).create(vals_list)
|
||||||
|
|
||||||
|
def write(self, vals):
|
||||||
|
if 'work_email' in vals:
|
||||||
|
vals["we_id"] = self._get_we_id(vals.get('work_email'))
|
||||||
|
return super(JkmPracticeEmployee, self).write(vals)
|
||||||
|
|
||||||
|
@api.depends('work_contact_id', 'work_contact_id.mobile', 'work_contact_id.email')
|
||||||
|
def _compute_work_contact_details(self):
|
||||||
|
for employee in self:
|
||||||
|
if employee.work_contact_id:
|
||||||
|
employee.mobile_phone = employee.work_contact_id.mobile
|
||||||
|
employee.work_email = employee.work_contact_id.email
|
||||||
|
if employee.work_contact_id.email:
|
||||||
|
employee.we_id = self._get_we_id(employee.work_contact_id.email)
|
||||||
|
|
||||||
|
def _get_we_id(self, work_email):
|
||||||
|
json1 = {
|
||||||
|
'params': {
|
||||||
|
'work_email': work_email
|
||||||
|
}
|
||||||
|
}
|
||||||
|
url = '/api/get/we_id/info'
|
||||||
|
config = self.env['res.config.settings'].get_values()
|
||||||
|
ret = requests.post((config['ims_url'] + url), json=json1, data={})
|
||||||
|
result = ret.json()['result']
|
||||||
|
if result['code'] == 200:
|
||||||
|
if result['we_id']:
|
||||||
|
return result['we_id']
|
||||||
|
return None
|
||||||
|
|
||||||
|
def _employee_info_sync(self):
|
||||||
|
url = '/api/get/organization'
|
||||||
|
config = self.env['res.config.settings'].get_values()
|
||||||
|
ret = requests.post((config['ims_url'] + url), json={}, data={})
|
||||||
|
result = ret.json()['result']
|
||||||
|
if result['code'] == 200:
|
||||||
|
if result['employee_list']:
|
||||||
|
for employee_info in result['employee_list']:
|
||||||
|
if employee_info['work_email']:
|
||||||
|
hr_employee = self.sudo().search([('work_email', '=', employee_info['work_email'])])
|
||||||
|
hr_employee.write({'we_id': employee_info['we_id']})
|
||||||
|
if hr_employee.user_id:
|
||||||
|
hr_employee.user_id.write({'we_employee_id': employee_info['we_id']})
|
||||||
|
else:
|
||||||
|
logging.info('_employee_info_sync error:%s' % result['message'])
|
||||||
30
sf_hr/models/res_config_setting.py
Normal file
30
sf_hr/models/res_config_setting.py
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import logging
|
||||||
|
from odoo import api, fields, models
|
||||||
|
|
||||||
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class ResIMSConfigSettings(models.TransientModel):
|
||||||
|
_inherit = 'res.config.settings'
|
||||||
|
|
||||||
|
ims_url = fields.Char('综合管理系统访问地址')
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def get_values(self):
|
||||||
|
"""
|
||||||
|
重载获取参数的方法,参数都存在系统参数中
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
values = super(ResIMSConfigSettings, self).get_values()
|
||||||
|
config = self.env['ir.config_parameter'].sudo()
|
||||||
|
ims_url = config.get_param('ims_url', default='')
|
||||||
|
values.update(
|
||||||
|
ims_url=ims_url,
|
||||||
|
)
|
||||||
|
return values
|
||||||
|
|
||||||
|
def set_values(self):
|
||||||
|
super(ResIMSConfigSettings, self).set_values()
|
||||||
|
ir_config = self.env['ir.config_parameter'].sudo()
|
||||||
|
ir_config.set_param("ims_url", self.ims_url or "")
|
||||||
15
sf_hr/models/res_users.py
Normal file
15
sf_hr/models/res_users.py
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import random
|
||||||
|
from odoo import models, fields, api
|
||||||
|
from odoo.http import request
|
||||||
|
from odoo.exceptions import AccessDenied
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class ResUsers(models.Model):
|
||||||
|
_inherit = 'res.users'
|
||||||
|
|
||||||
|
we_employee_id = fields.Char(string=u'企业微信账号', related='employee_id.we_id', default="")
|
||||||
@@ -7,7 +7,16 @@
|
|||||||
<field name="inherit_id" ref="hr.view_employee_form"/>
|
<field name="inherit_id" ref="hr.view_employee_form"/>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<xpath expr="//group//field[@name='work_email']" position="attributes">
|
<xpath expr="//group//field[@name='work_email']" position="attributes">
|
||||||
<attribute name="required">1</attribute>
|
<attribute name="required">1</attribute>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//page[@name='public']" position='after'>
|
||||||
|
<page string="企业微信">
|
||||||
|
<group col="2">
|
||||||
|
<group>
|
||||||
|
<field name="we_id"/>
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
</page>
|
||||||
</xpath>
|
</xpath>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|||||||
28
sf_hr/views/res_config_settings_views.xml
Normal file
28
sf_hr/views/res_config_settings_views.xml
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<data>
|
||||||
|
<record id="res_config_settings_finance_view_form_extend" model="ir.ui.view">
|
||||||
|
<field name="name">res.config.settings.finance.view.form.extend</field>
|
||||||
|
<field name="model">res.config.settings</field>
|
||||||
|
<field name="inherit_id" ref="base.res_config_settings_view_form"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//div[hasclass('app_settings_block')]/div" position="before">
|
||||||
|
<div>
|
||||||
|
<h2>综合管理系统接口配置</h2>
|
||||||
|
<div class="row mt16 o_settings_container" id="jd_api">
|
||||||
|
<div class="col-12 col-lg-6 o_setting_box">
|
||||||
|
<div class="o_setting_left_pane"/>
|
||||||
|
<div class="o_setting_right_pane">
|
||||||
|
<div class="text-muted">
|
||||||
|
<label for="ims_url"/>
|
||||||
|
<field name="ims_url"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
'category': 'sf',
|
'category': 'sf',
|
||||||
'author': 'jikimo',
|
'author': 'jikimo',
|
||||||
'website': 'https://sf.cs.jikimo.com',
|
'website': 'https://sf.cs.jikimo.com',
|
||||||
'depends': ['web', 'mail', 'sf_base', 'sf_manufacturing', 'barcodes', ],
|
'depends': ['web', 'sf_manufacturing', 'barcodes'],
|
||||||
'data': [
|
'data': [
|
||||||
# 定义权限组放在最上面
|
# 定义权限组放在最上面
|
||||||
# 权限组
|
# 权限组
|
||||||
@@ -30,7 +30,6 @@
|
|||||||
'views/machine_info_present.xml',
|
'views/machine_info_present.xml',
|
||||||
'views/delivery_record.xml',
|
'views/delivery_record.xml',
|
||||||
'views/res_config_settings_views.xml',
|
'views/res_config_settings_views.xml',
|
||||||
'views/maintenance_views.xml',
|
|
||||||
|
|
||||||
],
|
],
|
||||||
'assets': {
|
'assets': {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import base64
|
|||||||
import logging
|
import logging
|
||||||
import psycopg2
|
import psycopg2
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from odoo import http
|
from odoo import http, fields
|
||||||
from odoo.http import request
|
from odoo.http import request
|
||||||
|
|
||||||
# 数据库连接配置
|
# 数据库连接配置
|
||||||
@@ -15,14 +15,20 @@ db_config = {
|
|||||||
"user": "postgres",
|
"user": "postgres",
|
||||||
"password": "postgres",
|
"password": "postgres",
|
||||||
"port": "5432",
|
"port": "5432",
|
||||||
"host": "172.16.10.98"
|
"host": "172.16.10.131"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def convert_to_seconds(time_str):
|
def convert_to_seconds(time_str):
|
||||||
# 修改正则表达式,使 H、M、S 部分可选
|
# 修改正则表达式,使 H、M、S 部分可选
|
||||||
|
|
||||||
|
if time_str is None:
|
||||||
|
return 0
|
||||||
|
if time_str == 0:
|
||||||
|
return 0
|
||||||
|
|
||||||
pattern = r"(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?"
|
pattern = r"(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?"
|
||||||
|
|
||||||
match = re.match(pattern, time_str)
|
match = re.match(pattern, time_str)
|
||||||
|
|
||||||
if match:
|
if match:
|
||||||
@@ -66,7 +72,7 @@ class Sf_Dashboard_Connect(http.Controller):
|
|||||||
|
|
||||||
# 获取当前时间的时间戳
|
# 获取当前时间的时间戳
|
||||||
current_timestamp = datetime.now().timestamp()
|
current_timestamp = datetime.now().timestamp()
|
||||||
print(current_timestamp)
|
# print(current_timestamp)
|
||||||
|
|
||||||
# tem_list = [
|
# tem_list = [
|
||||||
# "XT-GNJC-WZZX-X800-Y550-Z550-T24-A5-1", "XT-GNJC-LSZX-X800-Y550-Z550-T24-A3-3",
|
# "XT-GNJC-WZZX-X800-Y550-Z550-T24-A5-1", "XT-GNJC-LSZX-X800-Y550-Z550-T24-A3-3",
|
||||||
@@ -158,11 +164,12 @@ class Sf_Dashboard_Connect(http.Controller):
|
|||||||
# 关机时长:初次上线时间 - 通电时间
|
# 关机时长:初次上线时间 - 通电时间
|
||||||
'power_off_time': power_off_time,
|
'power_off_time': power_off_time,
|
||||||
# 关机率:关机时长/初次上线时间
|
# 关机率:关机时长/初次上线时间
|
||||||
'power_off_rate': power_off_rate,
|
# 'power_off_rate': power_off_rate,
|
||||||
'first_online_duration': first_online_duration,
|
'first_online_duration': first_online_duration,
|
||||||
# 停机时间:关机时间 - 运行时间
|
# 停机时间:关机时间 - 运行时间
|
||||||
# 停机时长:关机时间 - 初次上线时间
|
# 停机时长:关机时间 - 初次上线时间
|
||||||
'img': f'data:image/png;base64,{machine_data.machine_tool_picture.decode("utf-8")}',
|
# 'img': f'data:image/png;base64,{machine_data.machine_tool_picture.decode("utf-8")}',
|
||||||
|
'img': f'https://xt.sf.jikimo.com/equipment/get_image/{machine_data.id}',
|
||||||
'equipment_type': machine_data.category_id.name,
|
'equipment_type': machine_data.category_id.name,
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -182,11 +189,11 @@ class Sf_Dashboard_Connect(http.Controller):
|
|||||||
"""
|
"""
|
||||||
res = {'status': 1, 'message': '成功', 'data': {}}
|
res = {'status': 1, 'message': '成功', 'data': {}}
|
||||||
logging.info('前端请求日志数据的参数为:%s' % kw)
|
logging.info('前端请求日志数据的参数为:%s' % kw)
|
||||||
|
# 连接数据库
|
||||||
|
conn = psycopg2.connect(**db_config)
|
||||||
|
cur = conn.cursor()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# 连接数据库
|
|
||||||
conn = psycopg2.connect(**db_config)
|
|
||||||
cur = conn.cursor()
|
|
||||||
machine_list = ast.literal_eval(kw['machine_list'])
|
machine_list = ast.literal_eval(kw['machine_list'])
|
||||||
begin_time_str = kw['begin_time'].strip('"')
|
begin_time_str = kw['begin_time'].strip('"')
|
||||||
end_time_str = kw['end_time'].strip('"')
|
end_time_str = kw['end_time'].strip('"')
|
||||||
@@ -194,7 +201,7 @@ class Sf_Dashboard_Connect(http.Controller):
|
|||||||
begin_time = datetime.strptime(begin_time_str, '%Y-%m-%d %H:%M:%S')
|
begin_time = datetime.strptime(begin_time_str, '%Y-%m-%d %H:%M:%S')
|
||||||
end_time = datetime.strptime(end_time_str, '%Y-%m-%d %H:%M:%S')
|
end_time = datetime.strptime(end_time_str, '%Y-%m-%d %H:%M:%S')
|
||||||
|
|
||||||
print('begin_time: %s' % begin_time)
|
# print('begin_time: %s' % begin_time)
|
||||||
|
|
||||||
for item in machine_list:
|
for item in machine_list:
|
||||||
sql = '''
|
sql = '''
|
||||||
@@ -226,6 +233,100 @@ class Sf_Dashboard_Connect(http.Controller):
|
|||||||
res['message'] = '前端请求日志数据失败,原因:%s' % e
|
res['message'] = '前端请求日志数据失败,原因:%s' % e
|
||||||
return json.dumps(res)
|
return json.dumps(res)
|
||||||
|
|
||||||
|
finally:
|
||||||
|
if cur:
|
||||||
|
cur.close()
|
||||||
|
if conn:
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
@http.route('/api/logs/page_data', type='http', auth='public', methods=['GET', 'POST'],
|
||||||
|
csrf=False, cors="*")
|
||||||
|
def logs_page_data(self, **kw):
|
||||||
|
"""
|
||||||
|
拿到日志数据返回给大屏展示(支持时间戳分页)
|
||||||
|
:param kw:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
res = {'status': 1, 'message': '成功', 'data': {}}
|
||||||
|
logging.info('前端请求日志数据的参数为:%s' % kw)
|
||||||
|
|
||||||
|
# 连接数据库
|
||||||
|
conn = psycopg2.connect(**db_config)
|
||||||
|
cur = conn.cursor()
|
||||||
|
try:
|
||||||
|
# 获取并解析传递的参数
|
||||||
|
machine_list = ast.literal_eval(kw.get('machine_list', '[]'))
|
||||||
|
begin_time_str = kw.get('begin_time', '').strip('"')
|
||||||
|
end_time_str = kw.get('end_time', '').strip('"')
|
||||||
|
page = int(kw.get('page', 1)) # 默认页码为1
|
||||||
|
page_size = int(kw.get('page_size', 80)) # 默认每页条数为10
|
||||||
|
|
||||||
|
begin_time = datetime.strptime(begin_time_str, '%Y-%m-%d %H:%M:%S')
|
||||||
|
end_time = datetime.strptime(end_time_str, '%Y-%m-%d %H:%M:%S')
|
||||||
|
|
||||||
|
# 计算分页的 offset
|
||||||
|
offset = (page - 1) * page_size
|
||||||
|
|
||||||
|
# 先查询符合条件的总记录数
|
||||||
|
total_records = 0
|
||||||
|
for item in machine_list:
|
||||||
|
count_sql = '''
|
||||||
|
SELECT COUNT(*)
|
||||||
|
FROM device_data
|
||||||
|
WHERE device_name = %s AND time >= %s AND time <= %s;
|
||||||
|
'''
|
||||||
|
# 执行总记录数查询
|
||||||
|
cur.execute(count_sql, (item, begin_time, end_time))
|
||||||
|
record_count = cur.fetchone()[0] # 获取总记录数
|
||||||
|
total_records += record_count
|
||||||
|
|
||||||
|
# 计算总页数
|
||||||
|
if total_records > 0:
|
||||||
|
total_pages = (total_records + page_size - 1) // page_size # 向上取整
|
||||||
|
else:
|
||||||
|
total_pages = 0
|
||||||
|
|
||||||
|
# 将总页数和总记录数返回到响应中
|
||||||
|
res['total_records'] = total_records
|
||||||
|
res['total_pages'] = total_pages
|
||||||
|
|
||||||
|
for item in machine_list:
|
||||||
|
sql = '''
|
||||||
|
SELECT time, device_state, program_name
|
||||||
|
FROM device_data
|
||||||
|
WHERE device_name = %s AND time >= %s AND time <= %s
|
||||||
|
ORDER BY time DESC
|
||||||
|
LIMIT %s OFFSET %s;
|
||||||
|
'''
|
||||||
|
# 执行SQL命令,使用参数绑定
|
||||||
|
cur.execute(sql, (item, begin_time, end_time, page_size, offset))
|
||||||
|
results = cur.fetchall()
|
||||||
|
|
||||||
|
# 将数据按照 equipment_code 进行分组
|
||||||
|
if item not in res['data']:
|
||||||
|
res['data'][item] = []
|
||||||
|
|
||||||
|
for result in results:
|
||||||
|
res['data'][item].append({
|
||||||
|
'time': result[0].strftime('%Y-%m-%d %H:%M:%S'),
|
||||||
|
'state': result[1],
|
||||||
|
'production_name': result[2],
|
||||||
|
})
|
||||||
|
|
||||||
|
return json.dumps(res) # 返回分页数据
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logging.info('前端请求日志数据失败,原因:%s' % e)
|
||||||
|
res['status'] = -1
|
||||||
|
res['message'] = '前端请求日志数据失败,原因:%s' % e
|
||||||
|
return json.dumps(res)
|
||||||
|
|
||||||
|
finally:
|
||||||
|
if cur:
|
||||||
|
cur.close()
|
||||||
|
if conn:
|
||||||
|
conn.close()
|
||||||
|
|
||||||
# 返回CNC机床列表
|
# 返回CNC机床列表
|
||||||
@http.route('/api/CNCList', type='http', auth='public', methods=['GET', 'POST'], csrf=False,
|
@http.route('/api/CNCList', type='http', auth='public', methods=['GET', 'POST'], csrf=False,
|
||||||
cors="*")
|
cors="*")
|
||||||
@@ -275,7 +376,7 @@ class Sf_Dashboard_Connect(http.Controller):
|
|||||||
res = {'Succeed': True}
|
res = {'Succeed': True}
|
||||||
line_list_obj = request.env['sf.production.line'].sudo().search([('name', 'ilike', 'CNC')])
|
line_list_obj = request.env['sf.production.line'].sudo().search([('name', 'ilike', 'CNC')])
|
||||||
line_list = list(map(lambda x: x.name, line_list_obj))
|
line_list = list(map(lambda x: x.name, line_list_obj))
|
||||||
print('line_list: %s' % line_list)
|
# print('line_list: %s' % line_list)
|
||||||
res['LineList'] = line_list
|
res['LineList'] = line_list
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -300,21 +401,65 @@ class Sf_Dashboard_Connect(http.Controller):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
plan_obj = request.env['sf.production.plan'].sudo()
|
plan_obj = request.env['sf.production.plan'].sudo()
|
||||||
|
production_obj = request.env['mrp.production'].sudo()
|
||||||
|
work_order_obj = request.env['mrp.workorder'].sudo()
|
||||||
line_list = ast.literal_eval(kw['line_list'])
|
line_list = ast.literal_eval(kw['line_list'])
|
||||||
print('line_list: %s' % line_list)
|
# print('line_list: %s' % line_list)
|
||||||
for line in line_list:
|
for line in line_list:
|
||||||
plan_data = plan_obj.search([('production_line_id.name', '=', line)])
|
|
||||||
# 工单总量
|
# # 工单计划量
|
||||||
plan_data_total_counts = plan_obj.search_count([('production_line_id.name', '=', line)])
|
# plan_data_total_counts = production_obj.search_count(
|
||||||
# 工单完成量
|
# [('production_line_id.name', '=', line), ('state', 'not in', ['cancel']),
|
||||||
plan_data_finish_counts = plan_obj.search_count(
|
# ('active', '=', True)])
|
||||||
[('production_line_id.name', '=', line), ('state', 'in', ['finished'])])
|
|
||||||
# 工单计划量
|
# 工单计划量切换为CNC工单
|
||||||
plan_data_plan_counts = plan_obj.search_count(
|
plan_data_total_counts = work_order_obj.search_count(
|
||||||
[('production_line_id.name', '=', line), ('state', 'not in', ['finished'])])
|
[('production_id.production_line_id.name', '=', line),
|
||||||
# 工单不良累计
|
('state', 'in', ['ready', 'progress', 'done']), ('routing_type', '=', 'CNC加工')])
|
||||||
plan_data_fault_counts = plan_obj.search_count(
|
|
||||||
[('production_line_id.name', '=', line), ('production_id.state', 'in', ['scrap', 'cancel'])])
|
# # 工单完成量
|
||||||
|
# plan_data_finish_counts = plan_obj.search_count(
|
||||||
|
# [('production_line_id.name', '=', line), ('state', 'in', ['finished'])])
|
||||||
|
|
||||||
|
# 工单完成量切换为CNC工单
|
||||||
|
plan_data_finish_counts = work_order_obj.search_count(
|
||||||
|
[('production_id.production_line_id.name', '=', line),
|
||||||
|
('state', 'in', ['done']), ('routing_type', '=', 'CNC加工')])
|
||||||
|
|
||||||
|
# 超期完成量
|
||||||
|
# 搜索所有已经完成的工单
|
||||||
|
plan_data_overtime = work_order_obj.search([
|
||||||
|
('production_id.production_line_id.name', '=', line),
|
||||||
|
('state', 'in', ['done']),
|
||||||
|
('routing_type', '=', 'CNC加工')
|
||||||
|
])
|
||||||
|
|
||||||
|
# 使用 filtered 进行字段比较
|
||||||
|
plan_data_overtime_counts = plan_data_overtime.filtered(
|
||||||
|
lambda order: order.date_finished > order.date_planned_finished
|
||||||
|
)
|
||||||
|
|
||||||
|
# 获取数量
|
||||||
|
plan_data_overtime_counts = len(plan_data_overtime_counts)
|
||||||
|
|
||||||
|
# 查找符合条件的生产计划记录
|
||||||
|
plan_data = plan_obj.search([
|
||||||
|
('production_line_id.name', '=', line),
|
||||||
|
])
|
||||||
|
|
||||||
|
# 过滤出那些检测结果状态为 '返工' 或 '报废' 的记录
|
||||||
|
faulty_plans = plan_data.filtered(lambda p: any(
|
||||||
|
result.test_results in ['返工', '报废'] for result in p.production_id.detection_result_ids
|
||||||
|
))
|
||||||
|
|
||||||
|
# 查找制造订单取消与归档的数量
|
||||||
|
cancel_order_count = production_obj.search_count(
|
||||||
|
[('production_line_id.name', '=', line), ('state', 'in', ['cancel']),
|
||||||
|
('active', '=', False)])
|
||||||
|
|
||||||
|
# 计算符合条件的记录数量
|
||||||
|
# plan_data_fault_counts = len(faulty_plans) + cancel_order_count
|
||||||
|
plan_data_fault_counts = len(faulty_plans)
|
||||||
|
|
||||||
# 工单返工数量
|
# 工单返工数量
|
||||||
|
|
||||||
@@ -326,17 +471,81 @@ class Sf_Dashboard_Connect(http.Controller):
|
|||||||
(plan_data_finish_counts / plan_data_total_counts if plan_data_total_counts > 0 else 0), 3)
|
(plan_data_finish_counts / plan_data_total_counts if plan_data_total_counts > 0 else 0), 3)
|
||||||
|
|
||||||
# 工单进度偏差
|
# 工单进度偏差
|
||||||
plan_data_progress_deviation = plan_data_finish_counts - plan_data_plan_counts
|
plan_data_progress_deviation = plan_data_total_counts - plan_data_finish_counts - plan_data_fault_counts
|
||||||
|
|
||||||
|
# 完成记录
|
||||||
|
plan_data_finish_orders = plan_obj.search(
|
||||||
|
[('production_line_id.name', '=', line), ('state', 'in', ['finished'])])
|
||||||
|
|
||||||
|
# # 检测量
|
||||||
|
# detection_nums = 0
|
||||||
|
# for i in plan_data_finish_orders:
|
||||||
|
# print('i: %s' % i)
|
||||||
|
# if i.production_id.detection_result_ids:
|
||||||
|
# detection_nums += 1
|
||||||
|
#
|
||||||
|
# print('detection_nums: %s' % detection_nums)
|
||||||
|
|
||||||
|
# 检测量
|
||||||
|
detection_data = len(
|
||||||
|
plan_data_finish_orders.mapped('production_id.detection_result_ids').filtered(lambda r: r))
|
||||||
|
|
||||||
|
# 检测合格量
|
||||||
|
pass_nums = plan_data_finish_orders.filtered(lambda p: any(
|
||||||
|
result.test_results in ['合格'] for result in p.production_id.detection_result_ids
|
||||||
|
))
|
||||||
|
|
||||||
|
# 质量合格率
|
||||||
|
pass_rate = 1
|
||||||
|
if pass_nums:
|
||||||
|
pass_rate = round(
|
||||||
|
# (len(pass_nums) / detection_data if len(plan_data_finish_orders) > 0 else 0), 3)
|
||||||
|
(len(pass_nums) / len(plan_data_finish_orders) if len(plan_data_finish_orders) > 0 else 0), 3)
|
||||||
|
|
||||||
|
# 返工率
|
||||||
|
rework_rate = round(
|
||||||
|
(plan_data_rework_counts / plan_data_finish_counts if plan_data_finish_counts > 0 else 0), 3)
|
||||||
|
|
||||||
|
# 交付准时率
|
||||||
|
delay_num = 0
|
||||||
|
for plan in plan_data_finish_orders:
|
||||||
|
sale_obj = request.env['sale.order'].sudo().search([('name', '=', plan.origin)])
|
||||||
|
if sale_obj:
|
||||||
|
if sale_obj.deadline_of_delivery and plan.actual_end_time:
|
||||||
|
# 将 deadline_of_delivery 转换为字符串
|
||||||
|
date_as_string = sale_obj.deadline_of_delivery.strftime('%Y-%m-%d')
|
||||||
|
# 然后使用 strptime 将字符串转换为 datetime 对象
|
||||||
|
date_as_datetime = datetime.strptime(date_as_string, '%Y-%m-%d')
|
||||||
|
|
||||||
|
# 将 actual_end_time 转换为 datetime 对象
|
||||||
|
datetime_value = fields.Datetime.from_string(plan.actual_end_time)
|
||||||
|
|
||||||
|
# 给 datetime_value 加1天
|
||||||
|
new_datetime_value = datetime_value + timedelta(days=1)
|
||||||
|
|
||||||
|
# 比较 new_datetime_value 和 date_as_datetime 的大小
|
||||||
|
if new_datetime_value.date() > date_as_datetime.date():
|
||||||
|
delay_num += 1
|
||||||
|
delay_rate = round((delay_num / plan_data_finish_counts if plan_data_finish_counts > 0 else 0), 3)
|
||||||
|
on_time_rate = 1 - delay_rate
|
||||||
|
|
||||||
if plan_data:
|
if plan_data:
|
||||||
data = {
|
data = {
|
||||||
'plan_data_total_counts': plan_data_total_counts,
|
'plan_data_total_counts': plan_data_total_counts,
|
||||||
'plan_data_finish_counts': plan_data_finish_counts,
|
'plan_data_finish_counts': plan_data_finish_counts,
|
||||||
'plan_data_plan_counts': plan_data_plan_counts,
|
'plan_data_plan_counts': plan_data_total_counts,
|
||||||
'plan_data_fault_counts': plan_data_fault_counts,
|
'plan_data_fault_counts': plan_data_fault_counts,
|
||||||
|
'nopass_orders_counts': detection_data - len(pass_nums),
|
||||||
'finishe_rate': finishe_rate,
|
'finishe_rate': finishe_rate,
|
||||||
'plan_data_progress_deviation': plan_data_progress_deviation,
|
'plan_data_progress_deviation': plan_data_progress_deviation,
|
||||||
'plan_data_rework_counts': plan_data_rework_counts
|
'plan_data_rework_counts': plan_data_rework_counts,
|
||||||
|
'on_time_rate': on_time_rate,
|
||||||
|
# 'detection_data': detection_data,
|
||||||
|
'detection_data': plan_data_finish_counts,
|
||||||
|
'pass_rate': (plan_data_finish_counts - plan_data_fault_counts) / plan_data_finish_counts,
|
||||||
|
'plan_data_overtime_counts': plan_data_overtime_counts,
|
||||||
|
'overtime_rate': plan_data_overtime_counts / plan_data_finish_counts
|
||||||
|
if plan_data_finish_counts > 0 else 0,
|
||||||
}
|
}
|
||||||
res['data'][line] = data
|
res['data'][line] = data
|
||||||
|
|
||||||
@@ -363,7 +572,28 @@ class Sf_Dashboard_Connect(http.Controller):
|
|||||||
end_time_str = kw['end_time'].strip('"')
|
end_time_str = kw['end_time'].strip('"')
|
||||||
begin_time = datetime.strptime(begin_time_str, '%Y-%m-%d %H:%M:%S')
|
begin_time = datetime.strptime(begin_time_str, '%Y-%m-%d %H:%M:%S')
|
||||||
end_time = datetime.strptime(end_time_str, '%Y-%m-%d %H:%M:%S')
|
end_time = datetime.strptime(end_time_str, '%Y-%m-%d %H:%M:%S')
|
||||||
print('line_list: %s' % line_list)
|
# print('line_list: %s' % line_list)
|
||||||
|
print('kw', kw)
|
||||||
|
time_unit = kw.get('time_unit', 'day').strip('"') # 默认单位为天
|
||||||
|
print('time_unit: %s' % time_unit)
|
||||||
|
|
||||||
|
# 日期或小时循环生成器,根据time_unit决定是按天还是按小时
|
||||||
|
def get_time_intervals(start_time, end_time, time_unit):
|
||||||
|
intervals = []
|
||||||
|
current_time = start_time
|
||||||
|
if time_unit == 'hour':
|
||||||
|
delta = timedelta(hours=1)
|
||||||
|
else:
|
||||||
|
delta = timedelta(days=1)
|
||||||
|
|
||||||
|
while current_time < end_time:
|
||||||
|
next_time = current_time + delta
|
||||||
|
# 确保最后一个时间段不会超出end_time
|
||||||
|
if next_time > end_time:
|
||||||
|
next_time = end_time
|
||||||
|
intervals.append((current_time, next_time))
|
||||||
|
current_time = next_time
|
||||||
|
return intervals
|
||||||
|
|
||||||
def get_date_list(start_date, end_date):
|
def get_date_list(start_date, end_date):
|
||||||
date_list = []
|
date_list = []
|
||||||
@@ -374,10 +604,36 @@ class Sf_Dashboard_Connect(http.Controller):
|
|||||||
return date_list
|
return date_list
|
||||||
|
|
||||||
for line in line_list:
|
for line in line_list:
|
||||||
date_list = get_date_list(begin_time, end_time)
|
date_field_name = 'actual_end_time' # 替换为你模型中的实际字段名
|
||||||
order_counts = []
|
order_counts = []
|
||||||
|
|
||||||
date_field_name = 'actual_end_time' # 替换为你模型中的实际字段名
|
if time_unit == 'hour':
|
||||||
|
time_intervals = get_time_intervals(begin_time, end_time, time_unit)
|
||||||
|
print('============================= %s' % time_intervals)
|
||||||
|
|
||||||
|
time_count_dict = {}
|
||||||
|
|
||||||
|
for time_interval in time_intervals:
|
||||||
|
start_time, end_time = time_interval
|
||||||
|
|
||||||
|
orders = plan_obj.search([
|
||||||
|
('production_line_id.name', '=', line),
|
||||||
|
('state', 'in', ['finished']),
|
||||||
|
(date_field_name, '>=', start_time.strftime('%Y-%m-%d %H:%M:%S')),
|
||||||
|
(date_field_name, '<=', end_time.strftime('%Y-%m-%d %H:%M:%S')) # 包括结束时间
|
||||||
|
])
|
||||||
|
|
||||||
|
# 使用小时和分钟作为键,确保每个小时的数据有独立的键
|
||||||
|
key = start_time.strftime('%H:%M:%S') # 只取小时:分钟:秒作为键
|
||||||
|
time_count_dict[key] = len(orders)
|
||||||
|
# order_counts.append()
|
||||||
|
res['data'][line] = {
|
||||||
|
'finish_order_nums': time_count_dict,
|
||||||
|
'plan_order_nums': 28
|
||||||
|
}
|
||||||
|
return json.dumps(res)
|
||||||
|
|
||||||
|
date_list = get_date_list(begin_time, end_time)
|
||||||
|
|
||||||
for date in date_list:
|
for date in date_list:
|
||||||
next_day = date + timedelta(days=1)
|
next_day = date + timedelta(days=1)
|
||||||
@@ -487,7 +743,7 @@ class Sf_Dashboard_Connect(http.Controller):
|
|||||||
end_time_str = kw['end_time'].strip('"')
|
end_time_str = kw['end_time'].strip('"')
|
||||||
begin_time = datetime.strptime(begin_time_str, '%Y-%m-%d %H:%M:%S')
|
begin_time = datetime.strptime(begin_time_str, '%Y-%m-%d %H:%M:%S')
|
||||||
end_time = datetime.strptime(end_time_str, '%Y-%m-%d %H:%M:%S')
|
end_time = datetime.strptime(end_time_str, '%Y-%m-%d %H:%M:%S')
|
||||||
print('line_list: %s' % line_list)
|
# print('line_list: %s' % line_list)
|
||||||
not_done_data = []
|
not_done_data = []
|
||||||
done_data = []
|
done_data = []
|
||||||
final_data = {}
|
final_data = {}
|
||||||
@@ -495,11 +751,22 @@ class Sf_Dashboard_Connect(http.Controller):
|
|||||||
for line in line_list:
|
for line in line_list:
|
||||||
# 未完成订单
|
# 未完成订单
|
||||||
not_done_orders = plan_obj.search(
|
not_done_orders = plan_obj.search(
|
||||||
[('production_line_id.name', '=', line), ('state', 'not in', ['finished'])])
|
[('production_line_id.name', '=', line), ('state', 'not in', ['finished']),
|
||||||
print(not_done_orders)
|
('production_id.state', 'not in', ['cancel', 'done']), ('active', '=', True)
|
||||||
|
])
|
||||||
|
# print(not_done_orders)
|
||||||
|
|
||||||
# 完成订单
|
# 完成订单
|
||||||
finish_orders = plan_obj.search([('production_line_id.name', '=', line), ('state', 'in', ['finished'])])
|
# 获取当前时间,并计算24小时前的时间
|
||||||
print(finish_orders)
|
current_time = datetime.now()
|
||||||
|
time_24_hours_ago = current_time - timedelta(hours=24)
|
||||||
|
|
||||||
|
finish_orders = plan_obj.search([
|
||||||
|
('production_line_id.name', '=', line), ('state', 'in', ['finished']),
|
||||||
|
('production_id.state', 'not in', ['cancel']), ('active', '=', True),
|
||||||
|
('actual_end_time', '>=', time_24_hours_ago)
|
||||||
|
])
|
||||||
|
# print(finish_orders)
|
||||||
|
|
||||||
# 获取所有未完成订单的ID列表
|
# 获取所有未完成订单的ID列表
|
||||||
order_ids = [order.id for order in not_done_orders]
|
order_ids = [order.id for order in not_done_orders]
|
||||||
@@ -555,6 +822,8 @@ class Sf_Dashboard_Connect(http.Controller):
|
|||||||
not_done_data.append(line_dict)
|
not_done_data.append(line_dict)
|
||||||
|
|
||||||
for finish_order in finish_orders:
|
for finish_order in finish_orders:
|
||||||
|
if not finish_order.actual_end_time:
|
||||||
|
continue
|
||||||
blank_name = ''
|
blank_name = ''
|
||||||
try:
|
try:
|
||||||
blank_name = finish_order.production_id.move_raw_ids[0].product_id.name
|
blank_name = finish_order.production_id.move_raw_ids[0].product_id.name
|
||||||
@@ -575,7 +844,8 @@ class Sf_Dashboard_Connect(http.Controller):
|
|||||||
'material': material,
|
'material': material,
|
||||||
'dimensions': dimensions,
|
'dimensions': dimensions,
|
||||||
'order_qty': finish_order.product_qty,
|
'order_qty': finish_order.product_qty,
|
||||||
'finish_time': finish_order.actual_end_time.strftime('%Y-%m-%d %H:%M:%S'),
|
'finish_time': finish_order.actual_end_time.strftime(
|
||||||
|
'%Y-%m-%d %H:%M:%S') if finish_order.actual_end_time else ' '
|
||||||
|
|
||||||
}
|
}
|
||||||
done_data.append(line_dict)
|
done_data.append(line_dict)
|
||||||
@@ -613,27 +883,30 @@ class Sf_Dashboard_Connect(http.Controller):
|
|||||||
'''
|
'''
|
||||||
|
|
||||||
sql2 = '''
|
sql2 = '''
|
||||||
SELECT DISTINCT ON (alarm_time) alarm_time, alarm_repair_time
|
SELECT DISTINCT ON (alarm_start_time) alarm_time, alarm_start_time
|
||||||
FROM device_data
|
FROM device_data
|
||||||
WHERE device_name = %s AND alarm_time IS NOT NULL
|
WHERE device_name = %s AND alarm_start_time IS NOT NULL
|
||||||
ORDER BY alarm_time, time;
|
ORDER BY alarm_start_time, time;
|
||||||
|
|
||||||
'''
|
'''
|
||||||
# 执行SQL命令
|
# 执行SQL命令
|
||||||
cur.execute(sql, (item,))
|
cur.execute(sql, (item,))
|
||||||
result = cur.fetchall()
|
result = cur.fetchall()
|
||||||
print('result========', result)
|
# print('result========', result)
|
||||||
|
|
||||||
cur.execute(sql2, (item,))
|
cur.execute(sql2, (item,))
|
||||||
result2 = cur.fetchall()
|
result2 = cur.fetchall()
|
||||||
print('result2========', result2)
|
# print('result2========', result2)
|
||||||
#
|
#
|
||||||
for row in result:
|
for row in result:
|
||||||
res['data'][item] = {'idle_count': row[0]}
|
res['data'][item] = {'idle_count': row[0]}
|
||||||
alarm_count = []
|
alarm_count = []
|
||||||
for row in result2:
|
for row in result2:
|
||||||
alarm_count.append(row[0])
|
alarm_count.append(row[1])
|
||||||
total_alarm_time += abs(float(row[0]))
|
if row[0]:
|
||||||
|
total_alarm_time += abs(float(row[0]))
|
||||||
|
else:
|
||||||
|
total_alarm_time += 0.0
|
||||||
if len(list(set(alarm_count))) == 1:
|
if len(list(set(alarm_count))) == 1:
|
||||||
if list(set(alarm_count))[0] is None:
|
if list(set(alarm_count))[0] is None:
|
||||||
alarm_count_num = 0
|
alarm_count_num = 0
|
||||||
@@ -659,54 +932,27 @@ class Sf_Dashboard_Connect(http.Controller):
|
|||||||
"""
|
"""
|
||||||
查询设备的异常情况
|
查询设备的异常情况
|
||||||
"""
|
"""
|
||||||
res = {'status': 1, 'message': '成功', 'data': {}}
|
res = {'status': 1, 'message': '成功', 'data': []}
|
||||||
logging.info('前端请求机床数据的参数为:%s' % kw)
|
logging.info('前端请求机床数据的参数为:%s' % kw)
|
||||||
|
|
||||||
# 连接数据库
|
|
||||||
conn = psycopg2.connect(**db_config)
|
|
||||||
cur = conn.cursor()
|
|
||||||
try:
|
try:
|
||||||
# 获取请求的机床数据
|
maintenance_logs_obj = request.env['sf.maintenance.logs'].sudo()
|
||||||
|
# # 获取请求的机床数据
|
||||||
# machine_list = ast.literal_eval(kw['machine_list'])
|
# machine_list = ast.literal_eval(kw['machine_list'])
|
||||||
# idle_times = []
|
|
||||||
# idle_dict = {}
|
|
||||||
|
|
||||||
# for item in machine_list:
|
# for item in machine_list:
|
||||||
sql = '''
|
# machine_data = equipment_obj.search([('code', '=', item)])
|
||||||
SELECT DISTINCT ON (alarm_time) alarm_time, alarm_message, system_date, system_time, alarm_repair_time
|
for log in maintenance_logs_obj.search([]):
|
||||||
FROM device_data
|
res['data'].append({
|
||||||
WHERE alarm_time IS NOT NULL
|
'name': log.name,
|
||||||
ORDER BY alarm_time, time;
|
'alarm_time': log.alarm_time.strftime('%Y-%m-%d %H:%M:%S'),
|
||||||
|
'fault_alarm_info': log.fault_alarm_info if log.fault_alarm_info else ' ',
|
||||||
|
'fault_process': log.fault_process if log.fault_process else ' ',
|
||||||
|
})
|
||||||
|
|
||||||
'''
|
|
||||||
# 执行SQL命令
|
|
||||||
cur.execute(sql)
|
|
||||||
result = cur.fetchall()
|
|
||||||
print('result', result)
|
|
||||||
|
|
||||||
# 将查询结果转换为字典列表
|
|
||||||
data = []
|
|
||||||
for row in result:
|
|
||||||
record = {
|
|
||||||
'alarm_time': row[0],
|
|
||||||
'alarm_message': row[1],
|
|
||||||
'system_date': row[2],
|
|
||||||
'system_time': row[3],
|
|
||||||
'alarm_repair_time': row[4]
|
|
||||||
}
|
|
||||||
data.append(record)
|
|
||||||
|
|
||||||
# 将数据填充到返回结果中
|
|
||||||
res['data'] = data
|
|
||||||
|
|
||||||
# 返回统计结果
|
|
||||||
return json.dumps(res, ensure_ascii=False)
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"An error occurred: {e}")
|
logging.error(f"An error occurred: {e}")
|
||||||
return json.dumps(res)
|
|
||||||
finally:
|
return json.dumps(res)
|
||||||
cur.close()
|
|
||||||
conn.close()
|
|
||||||
|
|
||||||
# 设备oee
|
# 设备oee
|
||||||
@http.route('/api/OEE', type='http', auth='public', methods=['GET', 'POST'], csrf=False, cors="*")
|
@http.route('/api/OEE', type='http', auth='public', methods=['GET', 'POST'], csrf=False, cors="*")
|
||||||
@@ -830,3 +1076,309 @@ class Sf_Dashboard_Connect(http.Controller):
|
|||||||
# 返回数据
|
# 返回数据
|
||||||
res['data'] = oee_data
|
res['data'] = oee_data
|
||||||
return json.dumps(res)
|
return json.dumps(res)
|
||||||
|
|
||||||
|
@http.route(['/equipment/get_image/<int:record_id>'], type='http', auth="public", website=True)
|
||||||
|
def get_image(self, record_id, **kwargs):
|
||||||
|
# 获取模型中的记录
|
||||||
|
record = request.env['maintenance.equipment'].sudo().browse(record_id)
|
||||||
|
|
||||||
|
# 获取图片字段的数据
|
||||||
|
image_data_base64 = record.machine_tool_picture
|
||||||
|
|
||||||
|
if image_data_base64:
|
||||||
|
# 将Base64解码为二进制数据
|
||||||
|
image_data_binary = base64.b64decode(image_data_base64)
|
||||||
|
|
||||||
|
# 返回图片数据,并设置正确的Content-Type
|
||||||
|
return request.make_response(image_data_binary, headers=[('Content-Type', 'image/png')])
|
||||||
|
else:
|
||||||
|
# 如果没有图片数据,返回404
|
||||||
|
return request.not_found()
|
||||||
|
|
||||||
|
# 设备运行率
|
||||||
|
@http.route('/api/RunningTime', type='http', auth='public', methods=['GET', 'POST'], csrf=False, cors="*")
|
||||||
|
def RunningTime(self, **kw):
|
||||||
|
"""
|
||||||
|
获取设备运行时长
|
||||||
|
"""
|
||||||
|
res = {'status': 1, 'message': '成功', 'data': {}}
|
||||||
|
# 连接数据库
|
||||||
|
conn = psycopg2.connect(**db_config)
|
||||||
|
# 获取请求的机床数据
|
||||||
|
machine_list = ast.literal_eval(kw['machine_list'])
|
||||||
|
|
||||||
|
def fetch_result_as_dict(cursor):
|
||||||
|
"""辅助函数:将查询结果转为字典"""
|
||||||
|
columns = [desc[0] for desc in cursor.description]
|
||||||
|
return dict(zip(columns, cursor.fetchone())) if cursor.rowcount != 0 else None
|
||||||
|
|
||||||
|
# 初始化当天、当月和有记录以来的总时长
|
||||||
|
day_total_running_time = 0
|
||||||
|
day_total_process_time = 0
|
||||||
|
day_work_rate = 0
|
||||||
|
month_total_running_time = 0
|
||||||
|
month_total_process_time = 0
|
||||||
|
month_work_rate = 0
|
||||||
|
all_time_total_running_time = 0
|
||||||
|
all_time_total_process_time = 0
|
||||||
|
all_time_work_rate = 0
|
||||||
|
|
||||||
|
for item in machine_list:
|
||||||
|
# 获取当天第一条记录(排除device_state等于‘离线’的记录)
|
||||||
|
with conn.cursor() as cur:
|
||||||
|
cur.execute("""
|
||||||
|
SELECT * FROM device_data
|
||||||
|
WHERE device_name = %s
|
||||||
|
AND time::date = CURRENT_DATE
|
||||||
|
AND device_state != '离线'
|
||||||
|
ORDER BY time ASC
|
||||||
|
LIMIT 1;
|
||||||
|
""", (item,))
|
||||||
|
first_today = fetch_result_as_dict(cur)
|
||||||
|
# print("当天第一条记录(非离线):", first_today)
|
||||||
|
|
||||||
|
# 获取当天最新一条记录(排除device_state等于‘离线’的记录)
|
||||||
|
with conn.cursor() as cur:
|
||||||
|
cur.execute("""
|
||||||
|
SELECT * FROM device_data
|
||||||
|
WHERE device_name = %s
|
||||||
|
AND time::date = CURRENT_DATE
|
||||||
|
AND device_state != '离线'
|
||||||
|
ORDER BY time DESC
|
||||||
|
LIMIT 1;
|
||||||
|
""", (item,))
|
||||||
|
last_today = fetch_result_as_dict(cur)
|
||||||
|
# print("当天最新一条记录(非离线):", last_today)
|
||||||
|
|
||||||
|
# 计算当天运行时长
|
||||||
|
if first_today and last_today:
|
||||||
|
running_time = convert_to_seconds(last_today['run_time']) - convert_to_seconds(first_today['run_time'])
|
||||||
|
process_time = convert_to_seconds(last_today['process_time']) - convert_to_seconds(
|
||||||
|
first_today['process_time'])
|
||||||
|
day_total_running_time += abs(running_time)
|
||||||
|
day_total_process_time += abs(process_time)
|
||||||
|
|
||||||
|
# 获取当月第一条记录(排除device_state等于‘离线’的记录)
|
||||||
|
with conn.cursor() as cur:
|
||||||
|
cur.execute("""
|
||||||
|
SELECT * FROM device_data
|
||||||
|
WHERE device_name = %s
|
||||||
|
AND EXTRACT(YEAR FROM time) = EXTRACT(YEAR FROM CURRENT_DATE)
|
||||||
|
AND EXTRACT(MONTH FROM time) = EXTRACT(MONTH FROM CURRENT_DATE)
|
||||||
|
AND device_state != '离线'
|
||||||
|
ORDER BY time ASC
|
||||||
|
LIMIT 1;
|
||||||
|
""", (item,))
|
||||||
|
first_month = fetch_result_as_dict(cur)
|
||||||
|
# print("当月第一条记录(非离线):", first_month)
|
||||||
|
|
||||||
|
# 获取当月最新一条记录(排除device_state等于‘离线’的记录)
|
||||||
|
with conn.cursor() as cur:
|
||||||
|
cur.execute("""
|
||||||
|
SELECT * FROM device_data
|
||||||
|
WHERE device_name = %s
|
||||||
|
AND EXTRACT(YEAR FROM time) = EXTRACT(YEAR FROM CURRENT_DATE)
|
||||||
|
AND EXTRACT(MONTH FROM time) = EXTRACT(MONTH FROM CURRENT_DATE)
|
||||||
|
AND device_state != '离线'
|
||||||
|
ORDER BY time DESC
|
||||||
|
LIMIT 1;
|
||||||
|
""", (item,))
|
||||||
|
last_month = fetch_result_as_dict(cur)
|
||||||
|
# print("当月最新一条记录(非离线):", last_month)
|
||||||
|
|
||||||
|
# 计算当月运行时长
|
||||||
|
if first_month and last_month:
|
||||||
|
month_running_time = convert_to_seconds(last_month['run_time']) - convert_to_seconds(
|
||||||
|
first_month['run_time'])
|
||||||
|
month_process_time = convert_to_seconds(last_month['process_time']) - convert_to_seconds(
|
||||||
|
first_month['process_time'])
|
||||||
|
month_total_running_time += abs(month_running_time)
|
||||||
|
month_total_process_time += abs(month_process_time)
|
||||||
|
|
||||||
|
# 获取有记录以来的第一条记录(排除device_state等于‘离线’的记录)
|
||||||
|
with conn.cursor() as cur:
|
||||||
|
cur.execute("""
|
||||||
|
SELECT * FROM device_data
|
||||||
|
WHERE device_name = %s
|
||||||
|
AND device_state != '离线'
|
||||||
|
ORDER BY time ASC
|
||||||
|
LIMIT 1;
|
||||||
|
""", (item,))
|
||||||
|
first_all_time = fetch_result_as_dict(cur)
|
||||||
|
# print("有记录以来的第一条记录(非离线):", first_all_time)
|
||||||
|
|
||||||
|
# 获取有记录以来的最新一条记录(排除device_state等于‘离线’的记录)
|
||||||
|
with conn.cursor() as cur:
|
||||||
|
cur.execute("""
|
||||||
|
SELECT * FROM device_data
|
||||||
|
WHERE device_name = %s
|
||||||
|
AND device_state != '离线'
|
||||||
|
ORDER BY time DESC
|
||||||
|
LIMIT 1;
|
||||||
|
""", (item,))
|
||||||
|
last_all_time = fetch_result_as_dict(cur)
|
||||||
|
# print("有记录以来的最新一条记录(非离线):", last_all_time)
|
||||||
|
|
||||||
|
# 计算有记录以来的运行时长
|
||||||
|
if first_all_time and last_all_time:
|
||||||
|
all_time_running_time = convert_to_seconds(last_all_time['run_time']) - convert_to_seconds(
|
||||||
|
first_all_time['run_time'])
|
||||||
|
all_time_process_time = convert_to_seconds(last_all_time['process_time']) - convert_to_seconds(
|
||||||
|
first_all_time['process_time'])
|
||||||
|
all_time_total_running_time += abs(all_time_running_time)
|
||||||
|
all_time_total_process_time += abs(all_time_process_time)
|
||||||
|
|
||||||
|
# 计算当天工作效率
|
||||||
|
if day_total_running_time > day_total_process_time:
|
||||||
|
day_work_rate = day_total_process_time / day_total_running_time if day_total_running_time != 0 else 0
|
||||||
|
else:
|
||||||
|
day_work_rate = day_total_running_time / day_total_process_time if day_total_process_time != 0 else 0
|
||||||
|
print("当天工作效率: %s" % day_work_rate)
|
||||||
|
|
||||||
|
# 计算当月工作效率
|
||||||
|
if month_total_running_time > month_total_process_time:
|
||||||
|
month_work_rate = month_total_process_time / month_total_running_time if month_total_running_time != 0 else 0
|
||||||
|
else:
|
||||||
|
month_work_rate = month_total_running_time / month_total_process_time if month_total_process_time != 0 else 0
|
||||||
|
print("当月工作效率: %s" % month_work_rate)
|
||||||
|
|
||||||
|
# 计算有记录以来的工作效率
|
||||||
|
if all_time_total_running_time > all_time_total_process_time:
|
||||||
|
all_time_work_rate = all_time_total_process_time / all_time_total_running_time if all_time_total_running_time != 0 else 0
|
||||||
|
else:
|
||||||
|
all_time_work_rate = all_time_total_running_time / all_time_total_process_time if all_time_total_process_time != 0 else 0
|
||||||
|
print("有记录以来的工作效率: %s" % all_time_work_rate)
|
||||||
|
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
# 返回数据
|
||||||
|
res['data']['day_work_rate'] = day_work_rate
|
||||||
|
res['data']['month_work_rate'] = month_work_rate
|
||||||
|
res['data']['all_time_work_rate'] = all_time_work_rate
|
||||||
|
|
||||||
|
return json.dumps(res)
|
||||||
|
|
||||||
|
# 设备运行时长
|
||||||
|
@http.route('/api/RunningTimeDetail', type='http', auth='public', methods=['GET', 'POST'], csrf=False, cors="*")
|
||||||
|
def RunningTimeDetail(self, **kw):
|
||||||
|
"""
|
||||||
|
获取
|
||||||
|
"""
|
||||||
|
logging.info("kw=============:%s" % kw)
|
||||||
|
res = {'status': 1, 'message': '成功', 'data': {}}
|
||||||
|
# 连接数据库
|
||||||
|
conn = psycopg2.connect(**db_config)
|
||||||
|
# 获取请求的机床数据
|
||||||
|
machine_list = ast.literal_eval(kw['machine_list'])
|
||||||
|
time_threshold = datetime.now() - timedelta(days=1)
|
||||||
|
|
||||||
|
alarm_last_24_time = 0.0
|
||||||
|
alarm_all_time = 0.0
|
||||||
|
|
||||||
|
def fetch_result_as_dict(cursor):
|
||||||
|
"""辅助函数:将查询结果转为字典"""
|
||||||
|
columns = [desc[0] for desc in cursor.description]
|
||||||
|
return dict(zip(columns, cursor.fetchone())) if cursor.rowcount != 0 else None
|
||||||
|
|
||||||
|
# 获取当前时间的时间戳
|
||||||
|
current_timestamp = datetime.now().timestamp()
|
||||||
|
for item in machine_list:
|
||||||
|
euipment_obj = request.env['maintenance.equipment'].sudo().search([('code', '=', item)])
|
||||||
|
# 机床上线时间段
|
||||||
|
first_online_duration = current_timestamp - euipment_obj.first_online_time.timestamp()
|
||||||
|
with conn.cursor() as cur:
|
||||||
|
cur.execute("""
|
||||||
|
SELECT * FROM device_data
|
||||||
|
WHERE device_name = %s
|
||||||
|
AND device_state != '离线' AND process_time IS NOT NULL
|
||||||
|
ORDER BY time DESC
|
||||||
|
LIMIT 1;
|
||||||
|
""", (item,))
|
||||||
|
last_all_time = fetch_result_as_dict(cur)
|
||||||
|
with conn.cursor() as cur:
|
||||||
|
cur.execute("""
|
||||||
|
SELECT * FROM device_data
|
||||||
|
WHERE device_name = %s
|
||||||
|
AND device_state != '离线' AND time >= %s AND process_time IS NOT NULL
|
||||||
|
ORDER BY time ASC
|
||||||
|
LIMIT 1;
|
||||||
|
""", (item, time_threshold))
|
||||||
|
last_24_time = fetch_result_as_dict(cur)
|
||||||
|
|
||||||
|
with conn.cursor() as cur:
|
||||||
|
cur.execute("""
|
||||||
|
SELECT COUNT(*)
|
||||||
|
FROM (
|
||||||
|
SELECT DISTINCT ON (idle_start_time) idle_start_time
|
||||||
|
FROM device_data
|
||||||
|
WHERE device_name = %s AND idle_start_time IS NOT NULL AND time >= %s
|
||||||
|
ORDER BY idle_start_time, time
|
||||||
|
) subquery;
|
||||||
|
""", (item, time_threshold))
|
||||||
|
idle_count = cur.fetchone()[0]
|
||||||
|
|
||||||
|
alarm_last_24_nums = []
|
||||||
|
with conn.cursor() as cur:
|
||||||
|
cur.execute("""
|
||||||
|
SELECT DISTINCT ON (alarm_start_time) alarm_time, alarm_start_time
|
||||||
|
FROM device_data
|
||||||
|
WHERE device_name = %s
|
||||||
|
AND alarm_start_time IS NOT NULL AND time >= %s;
|
||||||
|
""", (item, time_threshold))
|
||||||
|
results = cur.fetchall()
|
||||||
|
for result in results:
|
||||||
|
alarm_last_24_nums.append(result[1])
|
||||||
|
if result[0]:
|
||||||
|
if float(result[0]) >= 1000:
|
||||||
|
continue
|
||||||
|
alarm_last_24_time += float(result[0])
|
||||||
|
else:
|
||||||
|
alarm_last_24_time += 0.0
|
||||||
|
|
||||||
|
alarm_all_nums = []
|
||||||
|
with conn.cursor() as cur:
|
||||||
|
cur.execute("""
|
||||||
|
SELECT DISTINCT ON (alarm_start_time) alarm_time, alarm_start_time
|
||||||
|
FROM device_data
|
||||||
|
WHERE device_name = %s
|
||||||
|
AND alarm_start_time IS NOT NULL;
|
||||||
|
""", (item,))
|
||||||
|
results = cur.fetchall()
|
||||||
|
for result in results:
|
||||||
|
alarm_all_nums.append(result[1])
|
||||||
|
if result[0]:
|
||||||
|
if float(result[0]) >= 1000:
|
||||||
|
continue
|
||||||
|
alarm_all_time += float(result[0])
|
||||||
|
else:
|
||||||
|
alarm_all_time += 0.0
|
||||||
|
|
||||||
|
# with conn.cursor() as cur:
|
||||||
|
# cur.execute("""
|
||||||
|
# SELECT * FROM device_data
|
||||||
|
# WHERE device_name = %s
|
||||||
|
# AND total_count IS NOT NULL
|
||||||
|
# ORDER BY time ASC
|
||||||
|
# LIMIT 1;
|
||||||
|
# """, (item, ))
|
||||||
|
# total_count = fetch_result_as_dict(cur)
|
||||||
|
|
||||||
|
# 返回数据
|
||||||
|
res['data'][item] = {
|
||||||
|
'wait_time': last_all_time['run_time'] if last_all_time['run_time'] is not None else 0,
|
||||||
|
'cut_time': last_all_time['process_time'] if last_all_time['process_time'] is not None else 0,
|
||||||
|
'cut_24_time': last_24_time['process_time'] if last_24_time else 0,
|
||||||
|
'power_on_time': last_all_time['power_on_time'] if last_all_time['power_on_time'] is not None else 0,
|
||||||
|
'power_on_24_time': last_24_time['power_on_time'] if last_24_time else 0,
|
||||||
|
'alarm_last_24_time': alarm_last_24_time,
|
||||||
|
'alarm_last_24_nums': len(list(set(alarm_last_24_nums))),
|
||||||
|
'idle_count': idle_count,
|
||||||
|
'first_online_time': first_online_duration,
|
||||||
|
'alarm_all_time': alarm_all_time,
|
||||||
|
'alarm_all_nums': len(list(set(alarm_all_nums)))
|
||||||
|
# 'total_count': total_count['total_count'] if total_count else 0
|
||||||
|
}
|
||||||
|
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
return json.dumps(res)
|
||||||
|
|||||||
@@ -217,7 +217,7 @@ class Machine_ftp(models.Model):
|
|||||||
status = fields.Boolean('机床在线状态', readonly=True)
|
status = fields.Boolean('机床在线状态', readonly=True)
|
||||||
# run_status = fields.Selection([('0', '空闲中'), ('1', '加工中'), ('2', '加工中'), ('3', '加工中')], string='机床运行状态',
|
# run_status = fields.Selection([('0', '空闲中'), ('1', '加工中'), ('2', '加工中'), ('3', '加工中')], string='机床运行状态',
|
||||||
# readonly=True, default='0')
|
# readonly=True, default='0')
|
||||||
run_status = fields.Char('机床运行状态', readonly=True)
|
# run_status = fields.Char('机床运行状态', readonly=True)
|
||||||
run_time = fields.Char('机床累计运行时长', readonly=True)
|
run_time = fields.Char('机床累计运行时长', readonly=True)
|
||||||
# 机床系统日期
|
# 机床系统日期
|
||||||
system_date = fields.Char('机床系统日期', readonly=True)
|
system_date = fields.Char('机床系统日期', readonly=True)
|
||||||
@@ -285,26 +285,6 @@ class Machine_ftp(models.Model):
|
|||||||
# # 开动率
|
# # 开动率
|
||||||
run_rate = fields.Char('开动率', readonly=True)
|
run_rate = fields.Char('开动率', readonly=True)
|
||||||
|
|
||||||
# 同步CNC设备到oee
|
|
||||||
def sync_oee(self):
|
|
||||||
"""
|
|
||||||
同步CNC设备到oee
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
for record in self:
|
|
||||||
record.ensure_one()
|
|
||||||
cnc_oee_dict = {
|
|
||||||
'equipment_id': record.id,
|
|
||||||
'type_id': record.type_id.id,
|
|
||||||
'machine_tool_picture': record.machine_tool_picture,
|
|
||||||
'equipment_code': record.code,
|
|
||||||
'function_type': record.function_type,
|
|
||||||
}
|
|
||||||
if self.env['maintenance.equipment.oee.logs'].search([('equipment_id', '=', record.id)]):
|
|
||||||
self.env['maintenance.equipment.oee.logs'].write(cnc_oee_dict)
|
|
||||||
else:
|
|
||||||
self.env['maintenance.equipment.oee.logs'].create(cnc_oee_dict)
|
|
||||||
|
|
||||||
|
|
||||||
class WorkCenterBarcode(models.Model):
|
class WorkCenterBarcode(models.Model):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ class ResBFMConfigSettings(models.TransientModel):
|
|||||||
# ("https://bfm.jikimo.com", "正式环境(https://bfm.jikimo.com)")], string='bfm环境', store=True)
|
# ("https://bfm.jikimo.com", "正式环境(https://bfm.jikimo.com)")], string='bfm环境', store=True)
|
||||||
|
|
||||||
bfm_url_new = fields.Char('业务平台环境路径', placeholder='请输入当前对应的业务平台环境路径')
|
bfm_url_new = fields.Char('业务平台环境路径', placeholder='请输入当前对应的业务平台环境路径')
|
||||||
|
get_check_file_path = fields.Char('获取检查文件路径', default='')
|
||||||
|
|
||||||
@api.model
|
@api.model
|
||||||
def get_values(self):
|
def get_values(self):
|
||||||
@@ -26,9 +27,11 @@ class ResBFMConfigSettings(models.TransientModel):
|
|||||||
values = super(ResBFMConfigSettings, self).get_values()
|
values = super(ResBFMConfigSettings, self).get_values()
|
||||||
config = self.env['ir.config_parameter'].sudo()
|
config = self.env['ir.config_parameter'].sudo()
|
||||||
bfm_url_new = config.get_param('bfm_url_new', default='')
|
bfm_url_new = config.get_param('bfm_url_new', default='')
|
||||||
|
get_check_file_path = config.get_param('get_check_file_path', default='')
|
||||||
|
|
||||||
values.update(
|
values.update(
|
||||||
bfm_url_new=bfm_url_new,
|
bfm_url_new=bfm_url_new,
|
||||||
|
get_check_file_path=get_check_file_path
|
||||||
)
|
)
|
||||||
return values
|
return values
|
||||||
|
|
||||||
@@ -36,3 +39,4 @@ class ResBFMConfigSettings(models.TransientModel):
|
|||||||
super(ResBFMConfigSettings, self).set_values()
|
super(ResBFMConfigSettings, self).set_values()
|
||||||
ir_config = self.env['ir.config_parameter'].sudo()
|
ir_config = self.env['ir.config_parameter'].sudo()
|
||||||
ir_config.set_param("bfm_url_new", self.bfm_url_new or "")
|
ir_config.set_param("bfm_url_new", self.bfm_url_new or "")
|
||||||
|
ir_config.set_param("get_check_file_path", self.get_check_file_path or "")
|
||||||
|
|||||||
@@ -10,6 +10,8 @@
|
|||||||
<searchpanel>
|
<searchpanel>
|
||||||
<field name="routing_type" select="multi" string="工序类型" icon="fa-building" enable_counters="1"/>
|
<field name="routing_type" select="multi" string="工序类型" icon="fa-building" enable_counters="1"/>
|
||||||
<field name="state" select="multi" string="状态" icon="fa-building" enable_counters="1"/>
|
<field name="state" select="multi" string="状态" icon="fa-building" enable_counters="1"/>
|
||||||
|
|
||||||
|
<field name="construction_period_status" select="multi" icon="fa-building" enable_counters="1"/>
|
||||||
<!-- <field name="manual_quotation" select="multi" string="" icon="fa-building" enable_counters="1"/>-->
|
<!-- <field name="manual_quotation" select="multi" string="" icon="fa-building" enable_counters="1"/>-->
|
||||||
</searchpanel>
|
</searchpanel>
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
<group string='状态监控'>
|
<group string='状态监控'>
|
||||||
<group>
|
<group>
|
||||||
<!-- <field name="timestamp"/> -->
|
<!-- <field name="timestamp"/> -->
|
||||||
<field name="status"/>
|
<!-- <field name="status"/> -->
|
||||||
<field name="run_status"/>
|
<field name="run_status"/>
|
||||||
<field name="run_time"/>
|
<field name="run_time"/>
|
||||||
<field name="system_date"/>
|
<field name="system_date"/>
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
<?xml version="1.0"?>
|
|
||||||
<odoo>
|
|
||||||
<!-- 修改设备列表视图-->
|
|
||||||
<record id="sf_machine_hr_equipment_view_tree_inherit" model="ir.ui.view">
|
|
||||||
<field name="name">sf.machine.hr.equipment.view.tree.inherit</field>
|
|
||||||
<field name="model">maintenance.equipment</field>
|
|
||||||
<field name="inherit_id" ref="maintenance.hr_equipment_view_tree"/>
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<xpath expr="//tree" position="inside">
|
|
||||||
<header>
|
|
||||||
<button name="sync_oee" type="object" string="同步设备至OEE"/>
|
|
||||||
</header>
|
|
||||||
</xpath>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
</odoo>
|
|
||||||
@@ -22,6 +22,33 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
<h2>获取检测报告服务配置</h2>
|
||||||
|
<div class="row mt16 o_settings_container" id="check_report_config">
|
||||||
|
<div class="col-12 col-lg-6 o_setting_box">
|
||||||
|
<div class="o_setting_left_pane"/>
|
||||||
|
<div class="o_setting_right_pane">
|
||||||
|
<div class="text-muted">
|
||||||
|
<label for="get_check_file_path" />
|
||||||
|
<field name="get_check_file_path" string="检测报告服务地址"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- </div> -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//div[@id='check_report_config']/div" position="after">
|
||||||
|
<div class="col-12 col-lg-6 o_setting_box">
|
||||||
|
<div class="o_setting_left_pane">
|
||||||
|
<field name="is_get_detection_file"/>
|
||||||
|
</div>
|
||||||
|
<div class="o_setting_right_pane">
|
||||||
|
<div class="text-muted">
|
||||||
|
<label for="is_get_detection_file"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</xpath>
|
</xpath>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user