Compare commits
574 Commits
release/re
...
release/re
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a2990b8f2e | ||
|
|
5121e455d2 | ||
|
|
4a14fa6bf6 | ||
|
|
f251878637 | ||
|
|
5b3193d3ff | ||
|
|
52befb6233 | ||
|
|
9ce1963d44 | ||
|
|
1b710b205f | ||
|
|
0d253c54c8 | ||
|
|
d2c5fdb509 | ||
|
|
40209958f2 | ||
|
|
778896a670 | ||
|
|
fcacf609e9 | ||
|
|
5cd134758a | ||
|
|
b8f8e90444 | ||
|
|
b50a852f77 | ||
|
|
ea94d74657 | ||
|
|
b5bea1f811 | ||
|
|
8416630593 | ||
|
|
6572ca25fe | ||
|
|
6a7e6ee5c5 | ||
|
|
aff35f2dd4 | ||
|
|
8ead5655cf | ||
|
|
d6b3a5a3f1 | ||
|
|
19d8f6ae73 | ||
|
|
ae8e304d7f | ||
|
|
e154f5b763 | ||
|
|
804bdb60e8 | ||
|
|
302636635c | ||
|
|
c465822774 | ||
|
|
f9063bf3d9 | ||
|
|
ea6fb5e570 | ||
|
|
0e672aef79 | ||
|
|
11ec04de5b | ||
|
|
efe98b5133 | ||
|
|
2d2abfce25 | ||
|
|
821a7a63be | ||
|
|
de221ba67e | ||
|
|
c5bb9a32d0 | ||
|
|
925fc2f2b3 | ||
|
|
41f379ef5f | ||
|
|
acc791978d | ||
|
|
f24c1ab4a5 | ||
|
|
179899e483 | ||
|
|
b8d21b7fa5 | ||
|
|
295f7ecae3 | ||
|
|
d2a76a03d2 | ||
|
|
4424cb58f4 | ||
|
|
cd0ea08b21 | ||
|
|
10065e95d3 | ||
|
|
2c06c6f6a1 | ||
|
|
1eeaf2f85d | ||
|
|
aa96e63fff | ||
|
|
7ac09653c3 | ||
|
|
9c3ed0166b | ||
|
|
3c8ce870df | ||
|
|
d8e65a20d5 | ||
|
|
10a622b52d | ||
|
|
c1ffd3f870 | ||
|
|
a98b7456c0 | ||
|
|
7e3d40bd38 | ||
|
|
83d45b6d3f | ||
|
|
fe237dc742 | ||
|
|
35c13dc51c | ||
|
|
08477e4d94 | ||
|
|
069d1f50b0 | ||
|
|
dd60dee22d | ||
|
|
e49362e2e3 | ||
|
|
d5b5231873 | ||
|
|
2b080c1639 | ||
|
|
bd3bfc979e | ||
|
|
5a3233d539 | ||
|
|
3e8f045a40 | ||
|
|
d1fab6aab0 | ||
|
|
a3357e01aa | ||
|
|
c46a148856 | ||
|
|
5794f75f0b | ||
|
|
01d9d4a636 | ||
|
|
2931d6a92d | ||
|
|
5e6ae1ff55 | ||
|
|
d7a0dc578b | ||
|
|
44a11c839f | ||
|
|
9808f49b3d | ||
|
|
b40a87df88 | ||
|
|
72e9443048 | ||
|
|
c6f6927d57 | ||
|
|
ade7588a9c | ||
|
|
281f03670e | ||
|
|
7356e0afb7 | ||
|
|
fcd86e230a | ||
|
|
bf1271a742 | ||
|
|
324ed283c4 | ||
|
|
ebb8007549 | ||
|
|
361f187026 | ||
|
|
b35f444e49 | ||
|
|
9b2a2d644d | ||
|
|
93ca2000a1 | ||
|
|
ca43aa836e | ||
|
|
26ad5b5b3f | ||
|
|
937ea42af3 | ||
|
|
9b957848c2 | ||
|
|
36579d22ac | ||
|
|
e662490cb5 | ||
|
|
038ce8e139 | ||
|
|
e1a4784092 | ||
|
|
6d4d393b9b | ||
|
|
c956b06f57 | ||
|
|
da02d68c12 | ||
|
|
eb9b43dc91 | ||
|
|
ab66e24c9d | ||
|
|
fbbce6332d | ||
|
|
554b86e641 | ||
|
|
54317a529e | ||
|
|
78d00e9157 | ||
|
|
89cb61f244 | ||
|
|
deef246a6d | ||
|
|
9de201705f | ||
|
|
8f8d83d4cb | ||
|
|
c703012ec4 | ||
|
|
f1780181fa | ||
|
|
f5e36f601c | ||
|
|
6c0b84ec43 | ||
|
|
3e438a10ee | ||
|
|
9218633a5e | ||
|
|
c646e70a66 | ||
|
|
40521e06a8 | ||
|
|
7fee98cdee | ||
|
|
4b7c277a25 | ||
|
|
064397b32b | ||
|
|
048155bc9c | ||
|
|
d439c9140d | ||
|
|
296659ebb0 | ||
|
|
a82063d078 | ||
|
|
f17ff2bc57 | ||
|
|
5df3de7dcd | ||
|
|
b37738fbc3 | ||
|
|
79f3dce0ec | ||
|
|
4b87ea3f0e | ||
|
|
098ac0e7e9 | ||
|
|
6825364a2b | ||
|
|
0cac8fbb4f | ||
|
|
dce748c6a7 | ||
|
|
536c766272 | ||
|
|
422edbcb02 | ||
|
|
d13961ff5f | ||
|
|
b603934637 | ||
|
|
62d303168e | ||
|
|
39bc206344 | ||
|
|
715d835633 | ||
|
|
9e356682dc | ||
|
|
5dc392a27b | ||
|
|
3d2c62f5db | ||
|
|
1e3f0d5ee5 | ||
|
|
b26919a40d | ||
|
|
9ffe338532 | ||
|
|
08d333e4e2 | ||
|
|
47c501c6c6 | ||
|
|
335c79e618 | ||
|
|
ba986995ed | ||
|
|
2a45cdd53a | ||
|
|
0cdf37bb50 | ||
|
|
bae2592587 | ||
|
|
c86cc27510 | ||
|
|
2dded43e39 | ||
|
|
eb6e638018 | ||
|
|
f64c21dacd | ||
|
|
426aa78ed4 | ||
|
|
42c65496ff | ||
|
|
e893abd83e | ||
|
|
78e2e7184e | ||
|
|
ce575adc88 | ||
|
|
6668e0ee2f | ||
|
|
647d21dea3 | ||
|
|
0e13e2190e | ||
|
|
7f3d50a130 | ||
|
|
7b2908defa | ||
|
|
a8211171b0 | ||
|
|
c57daa2c52 | ||
|
|
4ed7ecf628 | ||
|
|
8a6ebb331a | ||
|
|
b333e27c51 | ||
|
|
6761851407 | ||
|
|
ada7936d1b | ||
|
|
9c40aa9a7e | ||
|
|
0694541653 | ||
|
|
e4e2dca22c | ||
|
|
e259fd0c05 | ||
|
|
a767c6491f | ||
|
|
6c2045d0c3 | ||
|
|
6fe56605ab | ||
|
|
bf2c7fd3c5 | ||
|
|
54fc91baa2 | ||
|
|
376f2b3079 | ||
|
|
3c50f19e2d | ||
|
|
08622b10b9 | ||
|
|
dc5b68cca0 | ||
|
|
1d399527e0 | ||
|
|
b3bfa69656 | ||
|
|
96e226836e | ||
|
|
062ca66328 | ||
|
|
7ab8269088 | ||
|
|
1241737dea | ||
|
|
813e424ec9 | ||
|
|
8c08893aa1 | ||
|
|
5ad35de76b | ||
|
|
3c8dac799d | ||
|
|
29c1c7a54d | ||
|
|
2c27a9b575 | ||
|
|
0e4b9e44f8 | ||
|
|
a07a1ba268 | ||
|
|
f537fbc597 | ||
|
|
a6b7167f3b | ||
|
|
c4de966dea | ||
|
|
03cfc00be5 | ||
|
|
cc05e8423e | ||
|
|
e78516f73c | ||
|
|
a277bb402e | ||
|
|
e5793638f7 | ||
|
|
9673fd165d | ||
|
|
e92905fe32 | ||
|
|
1b521ae460 | ||
|
|
4e0d8f1c88 | ||
|
|
aecf2121a1 | ||
|
|
e2d7576a5e | ||
|
|
cc13e5cd9a | ||
|
|
271f34a03b | ||
|
|
366e816268 | ||
|
|
1532184008 | ||
|
|
9f180e307d | ||
|
|
f0a4cf1d0f | ||
|
|
6c734eead4 | ||
|
|
f7e4ce416a | ||
|
|
5a60eed5b1 | ||
|
|
bf34de58fc | ||
|
|
cccc2f8493 | ||
|
|
894d3b9ea3 | ||
|
|
0b85f29262 | ||
|
|
9dbea66b73 | ||
|
|
9a7ac4dfa6 | ||
|
|
e6ca4c27ac | ||
|
|
a8560a0684 | ||
|
|
4f74996f24 | ||
|
|
5b26abc203 | ||
|
|
49654f1ff6 | ||
|
|
a9b7f99944 | ||
|
|
34e858ffe4 | ||
|
|
41efe81119 | ||
|
|
7cab8cd287 | ||
|
|
5f9c5961a5 | ||
|
|
a79500d0ad | ||
|
|
4399700c3d | ||
|
|
36fd17b6c7 | ||
|
|
9d52466f61 | ||
|
|
112efccb7c | ||
|
|
45b9177da4 | ||
|
|
745658eff2 | ||
|
|
ce8b0127b8 | ||
|
|
f58fb2ea13 | ||
|
|
d333621c7a | ||
|
|
563023fa3c | ||
|
|
3d2c7425c4 | ||
|
|
aaa19b96fd | ||
|
|
5d8f0f83b2 | ||
|
|
1547a6064f | ||
|
|
55b2daa0a3 | ||
|
|
d469c09f15 | ||
|
|
a03cb31b29 | ||
|
|
d2b20761b5 | ||
|
|
0f47b4b8c3 | ||
|
|
0cfb16b9d9 | ||
|
|
8ec5e05739 | ||
|
|
5393ef686a | ||
|
|
e92c9675b6 | ||
|
|
e6bad80031 | ||
|
|
070890e3b6 | ||
|
|
6e50774b23 | ||
|
|
fc18d34495 | ||
|
|
ed0d57c364 | ||
|
|
3f27ae0f35 | ||
|
|
cec426920b | ||
|
|
09a6255d9c | ||
|
|
0a6cf74bc2 | ||
|
|
c0202330f8 | ||
|
|
1dabdd157e | ||
|
|
36dc8070fa | ||
|
|
a5da18bbc7 | ||
|
|
510e8d49fb | ||
|
|
051273017d | ||
|
|
f49557090a | ||
|
|
d1656278ea | ||
|
|
0675937588 | ||
|
|
c0d006d139 | ||
|
|
2fa47eae27 | ||
|
|
cb2e1ecabe | ||
|
|
9f1233d20e | ||
|
|
a783f7ec7b | ||
|
|
db50253100 | ||
|
|
5f7f905540 | ||
|
|
1c86cc1421 | ||
|
|
1ea6f649f0 | ||
|
|
115a3e0712 | ||
|
|
f0421b873c | ||
|
|
913feea2a9 | ||
|
|
5d36513e59 | ||
|
|
94d6085f92 | ||
|
|
f361106c40 | ||
|
|
a46cd0f515 | ||
|
|
2c6e1a40f5 | ||
|
|
ffb775abb7 | ||
|
|
2c15532c73 | ||
|
|
1980ee2a9a | ||
|
|
bba4e55770 | ||
|
|
a9bbe98e8f | ||
|
|
11e3bbddd8 | ||
|
|
c01dfddfb4 | ||
|
|
50dcc9f7ba | ||
|
|
41a7aea01e | ||
|
|
066b256b0c | ||
|
|
754047bf02 | ||
|
|
f5bf95ab6e | ||
|
|
5ea25fae73 | ||
|
|
c367e39d5f | ||
|
|
dfec70081b | ||
|
|
d0c35695d5 | ||
|
|
95eb6bd18b | ||
|
|
599d2280f5 | ||
|
|
e1397f4b93 | ||
|
|
d49e8779f9 | ||
|
|
71c606974c | ||
|
|
a0ed14aed1 | ||
|
|
be3d4979b7 | ||
|
|
373ce89a2c | ||
|
|
56317ab111 | ||
|
|
683d79a4e3 | ||
|
|
362801f527 | ||
|
|
a81ad08740 | ||
|
|
d2d5702125 | ||
|
|
9c47a6bdba | ||
|
|
f0a887887e | ||
|
|
fffe0a230b | ||
|
|
81dbbf980c | ||
|
|
2600d15e2b | ||
|
|
c60b91c315 | ||
|
|
639dd4e78d | ||
|
|
3c4286319f | ||
|
|
55016918eb | ||
|
|
4236600b39 | ||
|
|
e84842d0a3 | ||
|
|
60df55d71e | ||
|
|
6643684e9b | ||
|
|
f77a1f6167 | ||
|
|
a467aef925 | ||
|
|
b1b805959a | ||
|
|
e602095b50 | ||
|
|
39214e5352 | ||
|
|
ba05827126 | ||
|
|
89bd9533d7 | ||
|
|
fd2228ba59 | ||
|
|
2758817048 | ||
|
|
2e916337e9 | ||
|
|
47b8a0051b | ||
|
|
76e6502afe | ||
|
|
1b96f420ec | ||
|
|
00645364b3 | ||
|
|
fdad5100ae | ||
|
|
8cd03a1040 | ||
|
|
41c2523201 | ||
|
|
22a1ae11a6 | ||
|
|
4939c6d6db | ||
|
|
460670843f | ||
|
|
66caeee1cd | ||
|
|
28041cbef9 | ||
|
|
36a2bcca6e | ||
|
|
fddddf1649 | ||
|
|
3e61e31314 | ||
|
|
549a64b012 | ||
|
|
dbaad85670 | ||
|
|
b731ffba33 | ||
|
|
9048b32405 | ||
|
|
98d2aa756a | ||
|
|
3749872780 | ||
|
|
de8bebc1f9 | ||
|
|
b468ac216c | ||
|
|
05f5b10833 | ||
|
|
8686d93651 | ||
|
|
258e24eb05 | ||
|
|
19c4b99bae | ||
|
|
cc4b58b136 | ||
|
|
33fdd0f051 | ||
|
|
d36250aa48 | ||
|
|
ad43b43beb | ||
|
|
f9ace6da7c | ||
|
|
12b4b418ea | ||
|
|
60b77e09d6 | ||
|
|
891fe257e1 | ||
|
|
6859458db2 | ||
|
|
ca7c3867cd | ||
|
|
4bcf8236ef | ||
|
|
74d557a0fc | ||
|
|
3686c0e070 | ||
|
|
536d7e9e79 | ||
|
|
67e79c5fb8 | ||
|
|
0c3217824e | ||
|
|
9eddfe36d6 | ||
|
|
9655281b67 | ||
|
|
18b584438a | ||
|
|
2deaffb4eb | ||
|
|
709f29185d | ||
|
|
886de3195c | ||
|
|
d9e5ffa68d | ||
|
|
8996297521 | ||
|
|
030a900580 | ||
|
|
0d1cb49cb7 | ||
|
|
4370236edf | ||
|
|
e01209f343 | ||
|
|
b4803f03fa | ||
|
|
c9d4f0667a | ||
|
|
8840e9642d | ||
|
|
96c22a5d46 | ||
|
|
30c823189c | ||
|
|
3ed503391e | ||
|
|
121861863f | ||
|
|
c2c8d63848 | ||
|
|
befe0c2b00 | ||
|
|
44a9ad340b | ||
|
|
55bac0020f | ||
|
|
a5d8e88f1d | ||
|
|
d3124bb9fe | ||
|
|
64fd6ef5dc | ||
|
|
001b2ead57 | ||
|
|
d0be097a9b | ||
|
|
0be8d0fbb2 | ||
|
|
71faa8a835 | ||
|
|
12afc1ef83 | ||
|
|
34624e6592 | ||
|
|
a16effd8db | ||
|
|
f59299d4d5 | ||
|
|
c7cef27ad6 | ||
|
|
b88b0fc38a | ||
|
|
4297d41dda | ||
|
|
3f85d058cc | ||
|
|
680bed9944 | ||
|
|
d3e307d195 | ||
|
|
86a011343d | ||
|
|
fe4bd3834e | ||
|
|
d3992c2a2e | ||
|
|
e62ed636e7 | ||
|
|
f0fdeff699 | ||
|
|
dec85dc92f | ||
|
|
f4f9591f35 | ||
|
|
efb7334486 | ||
|
|
b9c1224442 | ||
|
|
ca1a3ff120 | ||
|
|
da8e543819 | ||
|
|
eb236fea11 | ||
|
|
1d271d9167 | ||
|
|
c5cf614645 | ||
|
|
167c280fa4 | ||
|
|
69157a7b1f | ||
|
|
2939e6bf82 | ||
|
|
96bbf3a677 | ||
|
|
e3580d9ac8 | ||
|
|
ded1789813 | ||
|
|
759f947fa7 | ||
|
|
302aed16ec | ||
|
|
7d1db793b1 | ||
|
|
83396f5434 | ||
|
|
fe9baf08f0 | ||
|
|
b47cd0cdfe | ||
|
|
251e289fd1 | ||
|
|
1d4ecb9895 | ||
|
|
f3a7dcc6a4 | ||
|
|
ebd56eb5e0 | ||
|
|
4f99a162b5 | ||
|
|
28843b0733 | ||
|
|
8a0c968b10 | ||
|
|
ef1c7b6b25 | ||
|
|
03fe730c50 | ||
|
|
a0d3b40548 | ||
|
|
dfba055019 | ||
|
|
9d3c4f8163 | ||
|
|
08831812ae | ||
|
|
8b75658787 | ||
|
|
25ac346cb7 | ||
|
|
2d15782228 | ||
|
|
856237c4cb | ||
|
|
a47c751b04 | ||
|
|
9bec39f4e6 | ||
|
|
33bf88968c | ||
|
|
7163e025d2 | ||
|
|
11ec448e81 | ||
|
|
6c2fb043c4 | ||
|
|
1dcecea3d5 | ||
|
|
88f3ac3d4e | ||
|
|
983618d798 | ||
|
|
7a9c0ff163 | ||
|
|
77f7602b23 | ||
|
|
70b8521b90 | ||
|
|
1b89642dae | ||
|
|
1968ee17cb | ||
|
|
5e83fb382f | ||
|
|
974671ee07 | ||
|
|
d8b4797ffe | ||
|
|
b07e7707c7 | ||
|
|
8726f8cac3 | ||
|
|
cb7960387b | ||
|
|
6eaf92b841 | ||
|
|
ec0b5e88b6 | ||
|
|
f1b8ee41bc | ||
|
|
97c0b95e2d | ||
|
|
7ac342bfd4 | ||
|
|
6c820ead44 | ||
|
|
c536dde2cc | ||
|
|
801ff9e076 | ||
|
|
d0e049e881 | ||
|
|
81158e2fb4 | ||
|
|
3404cccdc8 | ||
|
|
45bb78d7e9 | ||
|
|
83334e2915 | ||
|
|
d4c30f645b | ||
|
|
bd642812a9 | ||
|
|
3eca74518a | ||
|
|
37977be862 | ||
|
|
a0bd214118 | ||
|
|
7885794322 | ||
|
|
7152b54017 | ||
|
|
0fef714d61 | ||
|
|
6a2ff72dce | ||
|
|
3094379e86 | ||
|
|
77815c45a0 | ||
|
|
dd7c69ce1b | ||
|
|
e02f792300 | ||
|
|
9aca786522 | ||
|
|
134e23ea2f | ||
|
|
129f8a4d7d | ||
|
|
79e3006cdb | ||
|
|
6b140fe6dd | ||
|
|
7533d23d3e | ||
|
|
e8512b23e4 | ||
|
|
b582bfbafa | ||
|
|
11907d4c5e | ||
|
|
ed4903b6f1 | ||
|
|
b383a6d229 | ||
|
|
27eb959a2a | ||
|
|
1672a3982e | ||
|
|
40f8392bd3 | ||
|
|
1c8644983b | ||
|
|
566d9fce9c | ||
|
|
b071b14bbd | ||
|
|
9d95442720 | ||
|
|
fd225fa0e1 | ||
|
|
52fa229896 | ||
|
|
937efa5f0f | ||
|
|
87153fab3f | ||
|
|
21c33bb662 | ||
|
|
b0da7977f5 | ||
|
|
ae6764495e | ||
|
|
869e3e4b4f | ||
|
|
050e82673a | ||
|
|
1210625cfc | ||
|
|
804b401a87 | ||
|
|
a7a8e73616 | ||
|
|
d60fefb2ec | ||
|
|
4a86871039 | ||
|
|
93c3548b40 | ||
|
|
396084a498 | ||
|
|
73cc244994 | ||
|
|
fa6303bbef | ||
|
|
5ca555c520 | ||
|
|
487f6c2054 | ||
|
|
cbc8a41984 | ||
|
|
025ad213d6 | ||
|
|
0b267cc88a | ||
|
|
5be5e8b3ec |
4
jikimo_account_process/__init__.py
Normal file
4
jikimo_account_process/__init__.py
Normal file
@@ -0,0 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from . import controllers
|
||||
from . import models
|
||||
35
jikimo_account_process/__manifest__.py
Normal file
35
jikimo_account_process/__manifest__.py
Normal file
@@ -0,0 +1,35 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
{
|
||||
'name': "jikimo_account_process",
|
||||
|
||||
'summary': """
|
||||
Short (1 phrase/line) summary of the module's purpose, used as
|
||||
subtitle on modules listing or apps.openerp.com""",
|
||||
|
||||
'description': """
|
||||
Long description of module's purpose
|
||||
""",
|
||||
|
||||
'author': "My Company",
|
||||
'website': "https://www.yourcompany.com",
|
||||
|
||||
# Categories can be used to filter modules in modules listing
|
||||
# Check https://github.com/odoo/odoo/blob/16.0/odoo/addons/base/data/ir_module_category_data.xml
|
||||
# for the full list
|
||||
'category': 'Uncategorized',
|
||||
'version': '0.1',
|
||||
|
||||
# any module necessary for this one to work correctly
|
||||
'depends': ['base', 'account'],
|
||||
|
||||
# always loaded
|
||||
'data': [
|
||||
# 'security/ir.model.access.csv',
|
||||
# 'views/views.xml',
|
||||
# 'views/templates.xml',
|
||||
],
|
||||
# only loaded in demonstration mode
|
||||
'demo': [
|
||||
# 'demo/demo.xml',
|
||||
],
|
||||
}
|
||||
3
jikimo_account_process/controllers/__init__.py
Normal file
3
jikimo_account_process/controllers/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from . import controllers
|
||||
21
jikimo_account_process/controllers/controllers.py
Normal file
21
jikimo_account_process/controllers/controllers.py
Normal file
@@ -0,0 +1,21 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# from odoo import http
|
||||
|
||||
|
||||
# class JikimoAccountProcess(http.Controller):
|
||||
# @http.route('/jikimo_account_process/jikimo_account_process', auth='public')
|
||||
# def index(self, **kw):
|
||||
# return "Hello, world"
|
||||
|
||||
# @http.route('/jikimo_account_process/jikimo_account_process/objects', auth='public')
|
||||
# def list(self, **kw):
|
||||
# return http.request.render('jikimo_account_process.listing', {
|
||||
# 'root': '/jikimo_account_process/jikimo_account_process',
|
||||
# 'objects': http.request.env['jikimo_account_process.jikimo_account_process'].search([]),
|
||||
# })
|
||||
|
||||
# @http.route('/jikimo_account_process/jikimo_account_process/objects/<model("jikimo_account_process.jikimo_account_process"):obj>', auth='public')
|
||||
# def object(self, obj, **kw):
|
||||
# return http.request.render('jikimo_account_process.object', {
|
||||
# 'object': obj
|
||||
# })
|
||||
4
jikimo_account_process/models/__init__.py
Normal file
4
jikimo_account_process/models/__init__.py
Normal file
@@ -0,0 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from . import models
|
||||
from . import account_move
|
||||
15
jikimo_account_process/models/account_move.py
Normal file
15
jikimo_account_process/models/account_move.py
Normal file
@@ -0,0 +1,15 @@
|
||||
from odoo import models, fields, api
|
||||
|
||||
from odoo.exceptions import ValidationError
|
||||
|
||||
|
||||
class CustomAccountMoveLine(models.Model):
|
||||
_inherit = 'account.move'
|
||||
_description = "account move line"
|
||||
|
||||
@api.model_create_multi
|
||||
def create(self, vals):
|
||||
for val in vals:
|
||||
val['name'] = self.env['ir.sequence'].next_by_code('account.move') or '/'
|
||||
# 因为供应商与客户支付创建流程是先创建move line在修改来填充account_payment与move line的关联
|
||||
return super(CustomAccountMoveLine, self).create(vals)
|
||||
18
jikimo_account_process/models/models.py
Normal file
18
jikimo_account_process/models/models.py
Normal file
@@ -0,0 +1,18 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# from odoo import models, fields, api
|
||||
|
||||
|
||||
# class jikimo_account_process(models.Model):
|
||||
# _name = 'jikimo_account_process.jikimo_account_process'
|
||||
# _description = 'jikimo_account_process.jikimo_account_process'
|
||||
|
||||
# name = fields.Char()
|
||||
# value = fields.Integer()
|
||||
# value2 = fields.Float(compute="_value_pc", store=True)
|
||||
# description = fields.Text()
|
||||
#
|
||||
# @api.depends('value')
|
||||
# def _value_pc(self):
|
||||
# for record in self:
|
||||
# record.value2 = float(record.value) / 100
|
||||
@@ -5,7 +5,7 @@ import {patch} from '@web/core/utils/patch';
|
||||
import {_t} from "@web/core/l10n/translation";
|
||||
import {FormStatusIndicator} from "@web/views/form/form_status_indicator/form_status_indicator";
|
||||
import {ListRenderer} from "@web/views/list/list_renderer";
|
||||
import {StatusBarField} from "@web/views/fields/statusbar/statusbar_field";
|
||||
// import {StatusBarField} from "@web/views/fields/statusbar/statusbar_field";
|
||||
|
||||
import {Field} from "@web/views/fields/field";
|
||||
|
||||
@@ -53,6 +53,23 @@ const tableRequiredList = [
|
||||
]
|
||||
|
||||
patch(FormStatusIndicator.prototype, 'jikimo_frontend.FormStatusIndicator', {
|
||||
setup() {
|
||||
owl.onMounted(() => {
|
||||
try {
|
||||
const dom = this.__owl__.bdom.el
|
||||
const buttonsDom = $(dom).find('.o_form_status_indicator_buttons ')
|
||||
if (buttonsDom) {
|
||||
const dom1 = buttonsDom.children('.o_form_button_save')
|
||||
const dom2 = buttonsDom.children('.o_form_button_cancel')
|
||||
dom1.append('保存')
|
||||
dom2.append('取消')
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
}
|
||||
|
||||
});
|
||||
},
|
||||
// 你可以重写或者添加一些方法和属性
|
||||
async _onDiscardChanges() {
|
||||
// var self = this;
|
||||
@@ -153,47 +170,36 @@ patch(ListRenderer.prototype, 'jikimo_frontend.ListRenderer', {
|
||||
|
||||
|
||||
// 根据进度条设置水印
|
||||
const statusbar_params = {
|
||||
'已完工': 'bg-primary',
|
||||
'完成': 'bg-primary',
|
||||
'采购订单': 'bg-primary',
|
||||
'作废': 'bg-danger',
|
||||
'封存(报废)': 'bg-danger',
|
||||
}
|
||||
patch(StatusBarField.prototype, 'jikimo_frontend.StatusBarField', {
|
||||
setup() {
|
||||
owl.onMounted(this.ribbons);
|
||||
return this._super(...arguments);
|
||||
},
|
||||
ribbons() {
|
||||
try {
|
||||
const dom = $('.o_form_sheet.position-relative')
|
||||
const status = statusbar_params[this.currentName]
|
||||
if(status && dom.length) {
|
||||
dom.prepend(`<div class="o_widget o_widget_web_ribbon">
|
||||
<div class="ribbon ribbon-top-right">
|
||||
<span class="bg-opacity-75 ${status}" title="">${this.currentName}</span>
|
||||
</div>
|
||||
</div>`)
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
}
|
||||
}
|
||||
})
|
||||
// const statusbar_params = {
|
||||
// '已完工': 'bg-primary',
|
||||
// '完成': 'bg-primary',
|
||||
// '采购订单': 'bg-primary',
|
||||
// '作废': 'bg-danger',
|
||||
// '封存(报废)': 'bg-danger',
|
||||
// }
|
||||
// patch(StatusBarField.prototype, 'jikimo_frontend.StatusBarField', {
|
||||
// setup() {
|
||||
// owl.onMounted(this.ribbons);
|
||||
// return this._super(...arguments);
|
||||
// },
|
||||
// ribbons() {
|
||||
// try {
|
||||
// const dom = $('.o_form_sheet.position-relative')
|
||||
// const status = statusbar_params[this.currentName]
|
||||
// if(status && dom.length) {
|
||||
// dom.prepend(`<div class="o_widget o_widget_web_ribbon">
|
||||
// <div class="ribbon ribbon-top-right">
|
||||
// <span class="bg-opacity-75 ${status}" title="">${this.currentName}</span>
|
||||
// </div>
|
||||
// </div>`)
|
||||
// }
|
||||
// } catch (e) {
|
||||
// console.log(e)
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
|
||||
$(function () {
|
||||
document.addEventListener('click', function () {
|
||||
const dom = $('.o_form_status_indicator_buttons ')
|
||||
if (dom) {
|
||||
const dom1 = dom.children().eq(0)
|
||||
const dom2 = dom.children().eq(1)
|
||||
if (!dom1.text()) {
|
||||
dom1.append('保存')
|
||||
dom2.append('取消')
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
function customRequired() {
|
||||
let timer = null
|
||||
|
||||
@@ -530,4 +530,5 @@ div:has(.o_required_modifier) > label::before {
|
||||
// 修复表格内容覆盖表头bug
|
||||
.o_list_renderer .o_list_table tbody th {
|
||||
position: unset;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -324,4 +324,4 @@ def unlink(self):
|
||||
|
||||
|
||||
BaseModel._create = _create
|
||||
BaseModel.unlink = unlink
|
||||
# BaseModel.unlink = unlink
|
||||
5
jikimo_system_order/__init__.py
Normal file
5
jikimo_system_order/__init__.py
Normal file
@@ -0,0 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from . import controllers
|
||||
from . import models
|
||||
from . import wizard
|
||||
39
jikimo_system_order/__manifest__.py
Normal file
39
jikimo_system_order/__manifest__.py
Normal file
@@ -0,0 +1,39 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
{
|
||||
'name': "jikimo_system_order",
|
||||
|
||||
'summary': """
|
||||
系统工单""",
|
||||
|
||||
'description': """
|
||||
用于处理针对系统的工作任务;
|
||||
员工可以通过系统工单发起申请,由维护人员处理以后,填写处理结果。
|
||||
""",
|
||||
|
||||
'author': "机企猫",
|
||||
'website': "http://www.jikimo.com",
|
||||
|
||||
# Categories can be used to filter modules in modules listing
|
||||
# Check https://github.com/odoo/odoo/blob/master/odoo/addons/base/module/module_data.xml
|
||||
# for the full list
|
||||
'category': 'Uncategorized',
|
||||
'version': '0.1',
|
||||
|
||||
# any module necessary for this one to work correctly
|
||||
'depends': ['base','mail'],
|
||||
|
||||
# always loaded
|
||||
'data': [
|
||||
'security/account_security.xml',
|
||||
'security/ir.model.access.csv',
|
||||
'wizard/order_wizard.xml',
|
||||
'views/notice_user_config.xml',
|
||||
'views/yizuo_system_order_view.xml',
|
||||
'views/work_order_number.xml',
|
||||
'views/res_config_settings_views.xml',
|
||||
],
|
||||
# only loaded in demonstration mode
|
||||
'demo': [
|
||||
'demo/demo.xml',
|
||||
],
|
||||
}
|
||||
3
jikimo_system_order/controllers/__init__.py
Normal file
3
jikimo_system_order/controllers/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from . import controllers
|
||||
20
jikimo_system_order/controllers/controllers.py
Normal file
20
jikimo_system_order/controllers/controllers.py
Normal file
@@ -0,0 +1,20 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from odoo import http
|
||||
|
||||
# class TopSystemOrder(http.Controller):
|
||||
# @http.route('/jikimo_system_order/jikimo_system_order/', auth='public')
|
||||
# def index(self, **kw):
|
||||
# return "Hello, world"
|
||||
|
||||
# @http.route('/jikimo_system_order/jikimo_system_order/objects/', auth='public')
|
||||
# def list(self, **kw):
|
||||
# return http.request.render('jikimo_system_order.listing', {
|
||||
# 'root': '/jikimo_system_order/jikimo_system_order',
|
||||
# 'objects': http.request.env['jikimo_system_order.jikimo_system_order'].search([]),
|
||||
# })
|
||||
|
||||
# @http.route('/jikimo_system_order/jikimo_system_order/objects/<model("jikimo_system_order.jikimo_system_order"):obj>/', auth='public')
|
||||
# def object(self, obj, **kw):
|
||||
# return http.request.render('jikimo_system_order.object', {
|
||||
# 'object': obj
|
||||
# })
|
||||
30
jikimo_system_order/demo/demo.xml
Normal file
30
jikimo_system_order/demo/demo.xml
Normal file
@@ -0,0 +1,30 @@
|
||||
<odoo>
|
||||
<data>
|
||||
<!-- -->
|
||||
<!-- <record id="object0" model="jikimo_system_order.jikimo_system_order"> -->
|
||||
<!-- <field name="name">Object 0</field> -->
|
||||
<!-- <field name="value">0</field> -->
|
||||
<!-- </record> -->
|
||||
<!-- -->
|
||||
<!-- <record id="object1" model="jikimo_system_order.jikimo_system_order"> -->
|
||||
<!-- <field name="name">Object 1</field> -->
|
||||
<!-- <field name="value">10</field> -->
|
||||
<!-- </record> -->
|
||||
<!-- -->
|
||||
<!-- <record id="object2" model="jikimo_system_order.jikimo_system_order"> -->
|
||||
<!-- <field name="name">Object 2</field> -->
|
||||
<!-- <field name="value">20</field> -->
|
||||
<!-- </record> -->
|
||||
<!-- -->
|
||||
<!-- <record id="object3" model="jikimo_system_order.jikimo_system_order"> -->
|
||||
<!-- <field name="name">Object 3</field> -->
|
||||
<!-- <field name="value">30</field> -->
|
||||
<!-- </record> -->
|
||||
<!-- -->
|
||||
<!-- <record id="object4" model="jikimo_system_order.jikimo_system_order"> -->
|
||||
<!-- <field name="name">Object 4</field> -->
|
||||
<!-- <field name="value">40</field> -->
|
||||
<!-- </record> -->
|
||||
<!-- -->
|
||||
</data>
|
||||
</odoo>
|
||||
7
jikimo_system_order/models/__init__.py
Normal file
7
jikimo_system_order/models/__init__.py
Normal file
@@ -0,0 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from . import constant
|
||||
from . import order_classify
|
||||
from . import system_work_order
|
||||
from . import work_order_template
|
||||
from . import res_config_setting
|
||||
7
jikimo_system_order/models/constant.py
Normal file
7
jikimo_system_order/models/constant.py
Normal file
@@ -0,0 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# 工单状态
|
||||
STATE_SELECTION = [('draft', u'草稿'), ('unconfirmed', u'待确认'), ('pending', u'待处理'),
|
||||
('processed', u'已处理待评分'), ('completed', u'已完成'), ('closed', u'已关闭')]
|
||||
|
||||
GRADE = [('1', '1非常不满意'), ('2', '2不满意'), ('3', '3一般'), ('4', '4满意'), ('5', '5非常满意')]
|
||||
25
jikimo_system_order/models/order_classify.py
Normal file
25
jikimo_system_order/models/order_classify.py
Normal file
@@ -0,0 +1,25 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from odoo import models, fields, api
|
||||
from odoo.exceptions import ValidationError
|
||||
|
||||
|
||||
class OrderClassify(models.Model):
|
||||
_name = 'order.classify'
|
||||
_order = 'sequence, name'
|
||||
|
||||
|
||||
@api.constrains('name')
|
||||
def check_base_name(self):
|
||||
"""类型名称唯一"""
|
||||
name_obj = self.env['order.classify'].search([('name', '=', self.name)])
|
||||
if len(name_obj) >= 2:
|
||||
raise ValidationError(u'该类型已存在')
|
||||
|
||||
# 名称
|
||||
name = fields.Char(string=u'名称', size=20)
|
||||
# 排序
|
||||
sequence = fields.Integer(default=10)
|
||||
# 是否有效
|
||||
state = fields.Boolean(default=True, string='是否有效')
|
||||
|
||||
32
jikimo_system_order/models/res_config_setting.py
Normal file
32
jikimo_system_order/models/res_config_setting.py
Normal file
@@ -0,0 +1,32 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import logging
|
||||
from odoo import api, fields, models, _
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ResModelWeConfigSettings(models.TransientModel):
|
||||
_inherit = 'res.config.settings'
|
||||
|
||||
lost_agent_id = fields.Char('企微通知应用ID')
|
||||
|
||||
@api.model
|
||||
def get_values(self):
|
||||
"""
|
||||
重载获取参数的方法,参数都存在系统参数中
|
||||
:return:
|
||||
"""
|
||||
values = super(ResModelWeConfigSettings, self).get_values()
|
||||
config = self.env['ir.config_parameter'].sudo()
|
||||
lost_agent_id = config.get_param('lost_agent_id', default='')
|
||||
values.update(
|
||||
lost_agent_id=lost_agent_id,
|
||||
)
|
||||
return values
|
||||
|
||||
def set_values(self):
|
||||
super(ResModelWeConfigSettings, self).set_values()
|
||||
ir_config = self.env['ir.config_parameter'].sudo()
|
||||
ir_config.set_param("lost_agent_id", self.lost_agent_id or "")
|
||||
|
||||
183
jikimo_system_order/models/system_work_order.py
Normal file
183
jikimo_system_order/models/system_work_order.py
Normal file
@@ -0,0 +1,183 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from odoo import models, fields, api
|
||||
from odoo.exceptions import ValidationError
|
||||
from odoo import exceptions
|
||||
from .constant import STATE_SELECTION, GRADE
|
||||
import datetime
|
||||
import logging
|
||||
|
||||
|
||||
class SystemWorkOrder(models.Model):
|
||||
_name = 'system.work.order'
|
||||
_inherit = ['mail.thread', 'mail.activity.mixin']
|
||||
_order = 'date desc'
|
||||
_description = u'系统工单'
|
||||
_rec_name = 'order_number'
|
||||
|
||||
def get_is_technicist(self):
|
||||
self._cr.execute(
|
||||
"select u.id from res_users u left join res_groups_users_rel r on r.uid = u.id where r.gid in (select g.id from res_groups g where g.name = '技术员权限') and u.id ='%s'",
|
||||
(self.env.user.id,))
|
||||
hr = self._cr.dictfetchall()
|
||||
if len(hr) > 0:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
# def get_user_department_id(self):
|
||||
# """根据用户id系统员工id"""
|
||||
# employee = self.env['hr.employee'].sudo().search([('user_id', '=', self.env.uid)], limit=1)
|
||||
# if employee:
|
||||
# if len(employee) > 0:
|
||||
# if not employee.department_id:
|
||||
# raise exceptions.Warning(u'您当前使用的用户没有所属部门')
|
||||
# return employee.department_id
|
||||
# else:
|
||||
# return False
|
||||
# else:
|
||||
# raise exceptions.Warning(u'您当前使用的用户没有关联员工')
|
||||
|
||||
@api.onchange('order_template_id')
|
||||
def get_title(self):
|
||||
"""选择模板自动填充"""
|
||||
if self.order_template_id:
|
||||
self.title = self.order_template_id.title_template
|
||||
self.text = self.order_template_id.text_template
|
||||
|
||||
# 工单编号
|
||||
order_number = fields.Char(string=u'工单编号', default='/')
|
||||
# 紧急程度
|
||||
urgency_degree = fields.Selection([('0', u'0星'), ('1', u'一星'), ('2', u'二星'), ('3', u'三星'), ('4', u'四星'),
|
||||
('5', u'五星')], string=u'紧急程度', help='五星为最紧急!', default='5')
|
||||
# 工单分类(可以配置,并调整优先级)
|
||||
order_type = fields.Many2one('order.classify', string=u'工单分类', domain=[('state', '=', True)])
|
||||
# 发起人所属公司(res.company)
|
||||
initiator_company_id = fields.Many2one('res.company', string=u'发起人所属公司', default=lambda self: self.env.user.company_id)
|
||||
# 发起人部门(hr.department)
|
||||
# initiator_department_id = fields.Many2one('hr.department', string=u'发起人部门', default=get_user_department_id)
|
||||
# 发起人(hr.employee)
|
||||
initiator_id = fields.Many2one('res.users', string=u'发起人', default=lambda self: self.env.user)
|
||||
# 发起时间
|
||||
date = fields.Datetime(string=u'发起时间', default=lambda self: fields.datetime.now())
|
||||
# 确认人
|
||||
confirm_id = fields.Many2one('res.users', string=u'确认人')
|
||||
# 确认日期
|
||||
confirmation_date = fields.Datetime(string=u'确认时间')
|
||||
# 模板
|
||||
order_template_id = fields.Many2one('work.order.template', string=u'模板', domain=[('state', '=', True)])
|
||||
# 标题
|
||||
title = fields.Char(string=u'标题')
|
||||
# 正文
|
||||
text = fields.Html(string=u'正文')
|
||||
# 状态[草稿\待确认\待处理\已处理\已关闭]
|
||||
state = fields.Selection(STATE_SELECTION, default='draft', string=u'状态')
|
||||
# 关闭原因
|
||||
close_cause = fields.Text(string=u'关闭问题原因')
|
||||
# 关闭时间
|
||||
close_time = fields.Datetime(string=u'关闭问题时间')
|
||||
# 关闭人
|
||||
close_user_id = fields.Many2one('res.users', string=u'关闭人')
|
||||
# 解决人
|
||||
solve_people_id = fields.Many2one('res.users', string=u'解决人')
|
||||
# 用户实际问题
|
||||
users_problem = fields.Text(string=u'用户实际问题')
|
||||
# 最终解决方案
|
||||
solution = fields.Text(string=u'最终解决方案')
|
||||
# 判断是否为技术人员
|
||||
# is_technicist = fields.Boolean(string=u'是否为技术人员', default=get_is_technicist)
|
||||
# 打分
|
||||
grade = fields.Selection(GRADE, string=u'评分')
|
||||
# 评价按钮的显示
|
||||
is_display = fields.Boolean('控制显示评价按钮', compute='compute_is_display')
|
||||
|
||||
def compute_is_display(self):
|
||||
for item in self:
|
||||
if item.state == 'processed' and self.env.user.id == item.initiator_id.id:
|
||||
item.is_display = True
|
||||
else:
|
||||
item.is_display = False
|
||||
|
||||
@api.onchange('order_type')
|
||||
def _onchange_order_type(self):
|
||||
self.order_template_id = None
|
||||
self.title = None
|
||||
self.text = None
|
||||
|
||||
@api.model
|
||||
def create(self, vals):
|
||||
# 创建编号
|
||||
if vals.get('order_number', '/') == '/':
|
||||
vals['order_number'] = self.env['ir.sequence'].get('system.work.order') or '/'
|
||||
return super(SystemWorkOrder, self).create(vals)
|
||||
|
||||
def do_draft(self, order=None):
|
||||
"""状态草稿"""
|
||||
bill = self
|
||||
if order:
|
||||
bill = order
|
||||
if bill.state == 'unconfirmed':
|
||||
state_remark = u'待确认 --> 草稿'
|
||||
# bill.message_post(u'操作人:%s,操作时间:%s,状态变更过程:%s' % (self.env.user.name,
|
||||
# (datetime.datetime.now() + datetime.timedelta(hours=8)).strftime('%Y-%m-%d %H:%M:%S'), state_remark))
|
||||
bill.state = 'draft'
|
||||
|
||||
def do_unconfirmed(self):
|
||||
"""状态待确认"""
|
||||
if self.state == 'draft':
|
||||
state_remark = u'草稿 --> 待确认'
|
||||
# self.message_post(u'操作人:%s,操作时间:%s,状态变更过程:%s' % (
|
||||
# self.env.user.name,
|
||||
# (datetime.datetime.now() + datetime.timedelta(hours=8)).strftime('%Y-%m-%d %H:%M:%S'), state_remark))
|
||||
self.state = 'unconfirmed'
|
||||
# 获取通知人
|
||||
objs = self.env['system.order.notice'].search([])
|
||||
user_ids = objs.notice_user_ids.filtered(lambda item: item.we_employee_id not in ['', False])
|
||||
we_employee_ids = user_ids.mapped('we_employee_id')
|
||||
lost_agent_id = self.env['ir.config_parameter'].sudo().get_param('lost_agent_id')
|
||||
wechat = self.env['we.config'].sudo().get_wechat(agent_id=lost_agent_id)
|
||||
# agent_id, user_ids, content
|
||||
content = """您有一张工单<font color=\"warning\">待处理</font>:**工单标题:{2}**
|
||||
>创建人:{1}
|
||||
>提交时间:{3}
|
||||
>紧急程度:{0}星
|
||||
请查看工单消息,并及时处理!
|
||||
""".format(self.urgency_degree,
|
||||
self.initiator_id.name, self.title, (self.date + datetime.timedelta(hours=8)).strftime('%Y-%m-%d %H:%M'))
|
||||
for we_employee_id in we_employee_ids:
|
||||
try:
|
||||
wechat.message.send_markdown(agent_id=lost_agent_id, user_ids=we_employee_id, content=content)
|
||||
except Exception as e:
|
||||
logging.error('工单处理发送消息异常%s' % str(e))
|
||||
|
||||
return True
|
||||
|
||||
def do_pending(self):
|
||||
"""状态待处理"""
|
||||
if self.state == 'unconfirmed':
|
||||
state_remark = u'待确认 --> 待处理'
|
||||
# self.message_post(u'操作人:%s,操作时间:%s,状态变更过程:%s' % (
|
||||
# self.env.user.name,
|
||||
# (datetime.datetime.now() + datetime.timedelta(hours=8)).strftime('%Y-%m-%d %H:%M:%S'), state_remark))
|
||||
self.state = 'pending'
|
||||
self.confirm_id = self.env.user
|
||||
self.confirmation_date = fields.datetime.now()
|
||||
return True
|
||||
|
||||
def urned_off(self):
|
||||
"""状态关闭"""
|
||||
if self.close_cause:
|
||||
self.state = 'closed'
|
||||
self.close_time = fields.datetime.now()
|
||||
else:
|
||||
raise ValidationError(u'请注明关闭原因')
|
||||
return True
|
||||
|
||||
def unlink(self):
|
||||
for item in self:
|
||||
if item.state != "draft":
|
||||
raise ValidationError(u'只能删除状态为【草稿】的工单。')
|
||||
elif item.env.uid != item.initiator_id.id:
|
||||
raise ValidationError(u'非本人不能删除')
|
||||
else:
|
||||
super(SystemWorkOrder, item).unlink()
|
||||
38
jikimo_system_order/models/work_order_template.py
Normal file
38
jikimo_system_order/models/work_order_template.py
Normal file
@@ -0,0 +1,38 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from odoo import models, fields, api
|
||||
|
||||
|
||||
class WorkOrderTemplate(models.Model):
|
||||
_name = 'work.order.template'
|
||||
_order = 'num'
|
||||
|
||||
# 编号
|
||||
num = fields.Char(string=u'编号', default='/')
|
||||
# 名称
|
||||
name = fields.Char(string=u'模板名称', required="1")
|
||||
# 分类
|
||||
work_order_type = fields.Many2one('order.classify', string=u'系统工单分类', domain=[('state', '=', True)])
|
||||
# 模板标题
|
||||
title_template = fields.Char(string=u'模板标题')
|
||||
# 模板正文
|
||||
text_template = fields.Html(string=u'模板正文')
|
||||
# 模板说明
|
||||
template_explain = fields.Text(string=u'模板说明')
|
||||
# 是否有效
|
||||
state = fields.Boolean(default=True, string=u'是否有效')
|
||||
|
||||
@api.model
|
||||
def create(self, vals):
|
||||
# 创建编号
|
||||
if vals.get('num', '/') == '/':
|
||||
vals['num'] = self.env['ir.sequence'].get('work.order.template') or '/'
|
||||
return super(WorkOrderTemplate, self).create(vals)
|
||||
|
||||
|
||||
class SystemOrderNotice(models.Model):
|
||||
_name = 'system.order.notice'
|
||||
_description = '工单处理人设置'
|
||||
|
||||
notice_user_ids = fields.Many2many('res.users', string='工单处理人')
|
||||
|
||||
24
jikimo_system_order/security/account_security.xml
Normal file
24
jikimo_system_order/security/account_security.xml
Normal file
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data noupdate="0"> <!-- noupdate表示,当模块升级时是否更新本条数据-->
|
||||
<!--运维权限组-->
|
||||
<record id="group_operations_permissions_rwc" model="res.groups">
|
||||
<field name="name">运维权限</field>
|
||||
</record>
|
||||
|
||||
<record id="system_order_user_rule" model="ir.rule">
|
||||
<field name="name">用户访问工单信息</field>
|
||||
<field name="model_id" ref="model_system_work_order"/>
|
||||
<field name="groups" eval="[(4, ref('base.group_user'))]"/>
|
||||
<field name="domain_force">[('initiator_id', '=', user.id)]</field>
|
||||
</record>
|
||||
|
||||
<record id="system_order_group_operations_rule" model="ir.rule">
|
||||
<field name="name">运维访问工单信息</field>
|
||||
<field name="model_id" ref="model_system_work_order"/>
|
||||
<field name="groups" eval="[(4, ref('jikimo_system_order.group_operations_permissions_rwc'))]"/>
|
||||
<field name="domain_force">[(1, '=', 1)]</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
16
jikimo_system_order/security/ir.model.access.csv
Normal file
16
jikimo_system_order/security/ir.model.access.csv
Normal file
@@ -0,0 +1,16 @@
|
||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
|
||||
inside_system_order_classify_r,jikimo_system_order.order_classify,model_order_classify,,1,1,1,1
|
||||
inside_system_work_order_rc,jikimo_system_order.system_work_order,model_system_work_order,,1,1,1,1
|
||||
inside_work_order_template_r,jikimo_system_order.work_order_template,model_work_order_template,,1,1,1,1
|
||||
|
||||
inside_system_order_classify_rwc,jikimo_system_order.order_classify,model_order_classify,group_operations_permissions_rwc,1,1,1,0
|
||||
inside_system_work_order_rwc,jikimo_system_order.system_work_order,model_system_work_order,group_operations_permissions_rwc,1,1,1,0
|
||||
inside_work_order_template_rwc,jikimo_system_order.work_order_template,model_work_order_template,group_operations_permissions_rwc,1,1,1,0
|
||||
|
||||
order_close_wizard_group_user,jikimo_system_order.order_close_wizard,model_order_close_wizard,base.group_user,1,1,1,1
|
||||
order_other_wizard_group_user,jikimo_system_order.order_other_wizard,model_order_other_wizard,base.group_user,1,1,1,1
|
||||
order_technician_wizard_group_user,jikimo_system_order.order_technician_wizard,model_order_technician_wizard,base.group_user,1,1,1,1
|
||||
system_work_order_wizard_group_user,jikimo_system_order.system_work_order_wizard,model_system_work_order_wizard,base.group_user,1,1,1,1
|
||||
|
||||
system_order_notice_group_user,jikimo_system_order.system_order_notice,model_system_order_notice,base.group_user,1,1,1,1
|
||||
|
BIN
jikimo_system_order/static/description/icon.png
Normal file
BIN
jikimo_system_order/static/description/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.7 KiB |
58
jikimo_system_order/views/notice_user_config.xml
Normal file
58
jikimo_system_order/views/notice_user_config.xml
Normal file
@@ -0,0 +1,58 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<odoo>
|
||||
<data>
|
||||
# ---------- 工单通知处理人设置 ------------
|
||||
|
||||
<record model="ir.ui.view" id="tree_system_order_notice_view">
|
||||
<field name="name">tree.system.order.notice</field>
|
||||
<field name="model">system.order.notice</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="工单处理人设置" editable="top">
|
||||
<field name="notice_user_ids" widget="many2many_tags" required="1" options="{'no_create': True, 'no_edit': True}"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
||||
<record model="ir.ui.view" id="search_system_order_notice_view">
|
||||
<field name="name">search.system.order.notice</field>
|
||||
<field name="model">system.order.notice</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="工单处理人设置">
|
||||
<field name="notice_user_ids" string="模糊搜索"
|
||||
filter_domain="[('notice_user_ids', 'ilike', self)]"/>
|
||||
<separator></separator>
|
||||
|
||||
<field name="notice_user_ids" string="处理人"/>
|
||||
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
||||
<record model="ir.actions.act_window" id="action_system_order_notice_view">
|
||||
<field name="name">工单处理人</field>
|
||||
<field name="res_model">system.order.notice</field>
|
||||
<field name="view_mode">tree</field>
|
||||
<field name="domain">[]</field>
|
||||
<field name="context">{}</field>
|
||||
<field name="help" type="html">
|
||||
<p class="o_view_nocontent_smiling_face">
|
||||
[工单处理人] 还没有哦!点左上角的[创建]按钮,沙发归你了!
|
||||
</p>
|
||||
<p>
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
28
jikimo_system_order/views/res_config_settings_views.xml
Normal file
28
jikimo_system_order/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_we_view_form_inherit" model="ir.ui.view">
|
||||
<field name="name">res.config.settings.we.view.form.inherit.bpm</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[6]" position="after">
|
||||
<div>
|
||||
<h2>企微通知应用ID</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="lost_agent_id"/>
|
||||
<field name="lost_agent_id"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
23
jikimo_system_order/views/work_order_number.xml
Normal file
23
jikimo_system_order/views/work_order_number.xml
Normal file
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<odoo>
|
||||
<data noupdate="True">
|
||||
<!-- 工单流水号 -->
|
||||
<record id="seq_work_order" model="ir.sequence">
|
||||
<field name="name">seq_work_order</field>
|
||||
<field name="company_id"/>
|
||||
<field name="code">system.work.order</field>
|
||||
<field name="prefix">SO%(year)s%(month)s%(day)s</field>
|
||||
<field name="padding">1</field>
|
||||
</record>
|
||||
|
||||
<!-- 模板编号 -->
|
||||
<record id="seq_order_template" model="ir.sequence">
|
||||
<field name="name">seq_order_template</field>
|
||||
<field name="company_id"/>
|
||||
<field name="code">work.order.template</field>
|
||||
<field name="prefix">TL</field>
|
||||
<field name="padding">1</field>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
243
jikimo_system_order/views/yizuo_system_order_view.xml
Normal file
243
jikimo_system_order/views/yizuo_system_order_view.xml
Normal file
@@ -0,0 +1,243 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<odoo>
|
||||
<data>
|
||||
<!--工单信息-->
|
||||
<record model="ir.ui.view" id="work_order_tree">
|
||||
<field name="name">工单信息</field>
|
||||
<field name="model">system.work.order</field><!--对应表单名称-->
|
||||
<field name="arch" type="xml">
|
||||
<tree>
|
||||
<field name="state" widget="badge" decoration-primary="state == 'draft'"
|
||||
decoration-success="state in ('processed', 'completed')"
|
||||
decoration-danger="state == 'pending'" decoration-warning="state in ('unconfirmed')"/>
|
||||
<field name="order_number"/>
|
||||
<field name="title"/>
|
||||
<field name="initiator_id"/>
|
||||
<field name="date"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!--新建系统工单-->
|
||||
<record model="ir.ui.view" id="ork_order_form">
|
||||
<field name="name">新建系统工单</field>
|
||||
<field name="model">system.work.order</field>
|
||||
<field name="arch" type="xml">
|
||||
<form>
|
||||
<header>
|
||||
<field name="is_display" invisible="1"/>
|
||||
<button string='提交' class="oe_highlight" states="draft"
|
||||
type="object" name="do_unconfirmed"
|
||||
attrs="{'invisible': [('state', '!=', 'draft')]}"/>
|
||||
<button string='追回编辑' states="unconfirmed"
|
||||
type="action" name="%(system_work_order_wizard_view_act_window)d"
|
||||
context="{'explain':'确认要执行此操作吗?','object_name':'system.work.order','function_name':'do_draft','object_id':id}"/>
|
||||
|
||||
<button name="do_pending" states="unconfirmed"
|
||||
string="确认可处理" type="object" class="oe_highlight"
|
||||
groups="jikimo_system_order.group_operations_permissions_rwc"/>
|
||||
|
||||
<button string='处理工单' class="oe_highlight" states="pending"
|
||||
type="action" name="%(launch_order_technician_wizard)d"
|
||||
groups="jikimo_system_order.group_operations_permissions_rwc"/>
|
||||
<button string='评价' class="oe_highlight" attrs="{'invisible': [('is_display', '=', False)]}"
|
||||
type="action" name="%(launch_order_other_wizard)d" context="{'active_id':id}"/>
|
||||
<button name="%(launch_order_close_wizard)d" string="关闭该工单"
|
||||
attrs="{'invisible': ['|',('state', '=', 'draft'),'|',('state','=','completed'),('state','=','closed')]}"
|
||||
type="action" context="{'active_id':id}"/>
|
||||
|
||||
<field name="state" widget="statusbar"/>
|
||||
</header>
|
||||
<sheet>
|
||||
<group>
|
||||
<!-- <label for="order_number" class="oe_edit_only"/>-->
|
||||
<group>
|
||||
<field name="order_number" required="True" readonly="1"/>
|
||||
<field name="order_type" required="True" attrs="{'readonly': [('state', '!=', 'draft')]}" options="{'no_create': True}"/>
|
||||
<field name="date" required="True" readonly="True"/>
|
||||
<field name="order_template_id" attrs="{'readonly': [('state', '!=', 'draft')]}"
|
||||
domain="[('work_order_type','=',order_type),('state','=',True)]" options="{'no_create': True}"/>
|
||||
<field name="confirmation_date" readonly="True"/>
|
||||
<field name="urgency_degree" required="True" attrs="{'readonly': [('state','!=','draft')]}" widget="priority"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="initiator_company_id" required="True" readonly="True"/>
|
||||
<!-- <field name="initiator_department_id" required="True" readonly="True"/>-->
|
||||
<field name="initiator_id" required="True" readonly="True"/>
|
||||
<field name="confirm_id" readonly="True"/>
|
||||
<field name="solve_people_id" readonly="True"/>
|
||||
<field name="close_user_id" readonly="True"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="title" attrs="{'readonly': [('state', '!=', 'draft')]}" required="True"/>
|
||||
</group>
|
||||
</group>
|
||||
<notebook>
|
||||
<page string="工单内容">
|
||||
<field name="text" attrs="{'readonly': [('state','!=','draft')]}" required="True"/>
|
||||
</page>
|
||||
<page string="解决方案">
|
||||
<group>
|
||||
<field name="users_problem" readonly="True"/>
|
||||
<field name="solution" readonly="True"/>
|
||||
</group>
|
||||
</page>
|
||||
<page string="其他">
|
||||
<group>
|
||||
<field name="close_cause" readonly="True"/>
|
||||
<field name="close_time" readonly="True"/>
|
||||
<field name="grade" readonly="True"/>
|
||||
</group>
|
||||
</page>
|
||||
</notebook>
|
||||
</sheet>
|
||||
<!-- <div class="oe_chatter">-->
|
||||
<!-- <field name="message_follower_ids" widget="mail_followers"/>-->
|
||||
<!-- <field name="message_ids" widget="mail_thread"/>-->
|
||||
<!-- </div>-->
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- 搜索工单 -->
|
||||
<record model="ir.ui.view" id="restaurant_search">
|
||||
<field name="name">搜索工单</field>
|
||||
<field name="model">system.work.order</field>
|
||||
<field name="arch" type="xml">
|
||||
<search>
|
||||
<field string='发起人' name="initiator_id" widget="char" required="True"/>
|
||||
<field string='标题' name="title" widget="char"/>
|
||||
<field string='正文' name="text" widget="html"/>
|
||||
<field string='实际问题' name="users_problem" widget="text"/>
|
||||
<field string='解决方案' name="solution" widget="text"/>
|
||||
<filter name="today" string="今日工单" domain="[('date','=',time.strftime('%%Y-%%m-%%d'))]"/>
|
||||
<filter name="yesterday" string="昨日工单"
|
||||
domain="[('date', '=', (context_today() - relativedelta(days=1)).strftime('%Y-%m-%d'))]"/>
|
||||
<filter name="month" string="本月工单"
|
||||
domain="[('date','>=', time.strftime('%Y-%m-01')),('date','<', (context_today() + relativedelta(months=1)).strftime('%Y-%m-01'))]"/>
|
||||
<filter name="last_month" string="上月工单"
|
||||
domain="[('date','<', time.strftime('%Y-%m-01')),('date','>=', (context_today() - relativedelta(months=1)).strftime('%Y-%m-01'))]"/>
|
||||
<filter name="unconfirmed" string="待确认" domain="[('state','=','unconfirmed')]"/>
|
||||
<filter name="pending" string="待处理" domain="[('state','=','pending')]"/>
|
||||
<filter name="processed" string="已处理"
|
||||
domain="['|', ('state','=','processed'), ('state','=','closed')]"/>
|
||||
<group>
|
||||
<filter string='发起人' name="initiator_id" context='{"group_by":"initiator_id"}'/>
|
||||
<filter string='工单分类' name="order_type" context='{"group_by":"order_type"}'/>
|
||||
<filter string='模板' name="order_template_id" context='{"group_by":"order_template_id"}'/>
|
||||
<filter string='状态' name="state" context='{"group_by":"state"}'/>
|
||||
<filter string='紧急情况' name="state" context='{"group_by":"urgency_degree"}'/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="graph_tree">
|
||||
<field name="name">工单图表</field>
|
||||
<field name="model">system.work.order</field><!--对应表单名称-->
|
||||
<field name="arch" type="xml">
|
||||
<pivot>
|
||||
<field name="date" type="row" interval="day"/>
|
||||
<field name="order_type" type="col"/>
|
||||
<field name="state" type="row"/>
|
||||
</pivot>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- 工单 -->
|
||||
<record model="ir.actions.act_window" id="system_order">
|
||||
<field name="name">工单</field>
|
||||
<field name="res_model">system.work.order</field>
|
||||
<field name="view_mode">tree,form,search,graph,pivot</field>
|
||||
</record>
|
||||
|
||||
<!--工单模板信息-->
|
||||
<record model="ir.ui.view" id="order_template_tree">
|
||||
<field name="name">工单模板信息</field>
|
||||
<field name="model">work.order.template</field><!--对应表单名称-->
|
||||
<field name="arch" type="xml">
|
||||
<tree>
|
||||
<field name="num"/>
|
||||
<field name="name"/>
|
||||
<field name="work_order_type"/>
|
||||
<field name="title_template"/>
|
||||
<field name="template_explain"/>
|
||||
<field name="state"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!--新建系统工单模板-->
|
||||
<record model="ir.ui.view" id="order_template_form">
|
||||
<field name="name">新建系统工单模板</field>
|
||||
<field name="model">work.order.template</field>
|
||||
<field name="arch" type="xml">
|
||||
<form>
|
||||
<sheet>
|
||||
<group>
|
||||
<field name="num" required="True" readonly="True"/>
|
||||
<field name="name" required="True"/>
|
||||
<field name="work_order_type" required="True"/>
|
||||
<field name="template_explain" required="True" style="height: 50px;"/>
|
||||
<field name="title_template" required="True"/>
|
||||
<field name="state"/>
|
||||
<field name="text_template" required="True"/>
|
||||
</group>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- 工单模板 -->
|
||||
<record model="ir.actions.act_window" id="work_template">
|
||||
<field name="name">工单模板</field>
|
||||
<field name="res_model">work.order.template</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
</record>
|
||||
|
||||
<!--工单分类信息-->
|
||||
<record model="ir.ui.view" id="order_type_tree">
|
||||
<field name="name">工单分类信息</field>
|
||||
<field name="model">order.classify</field><!--对应表单名称-->
|
||||
<field name="arch" type="xml">
|
||||
<tree>
|
||||
<field name="sequence" widget="handle"/>
|
||||
<field name="name"/>
|
||||
<field name="state"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!--新建系统分类信息-->
|
||||
<record model="ir.ui.view" id="order_type_form">
|
||||
<field name="name">新建系统分类信息</field>
|
||||
<field name="model">order.classify</field>
|
||||
<field name="arch" type="xml">
|
||||
<form>
|
||||
<sheet>
|
||||
<group>
|
||||
<field name="name" required="True"/>
|
||||
<field name="sequence" invisible="True"/>
|
||||
<field name="state"/>
|
||||
</group>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- 工单分类 -->
|
||||
<record model="ir.actions.act_window" id="classify">
|
||||
<field name="name">工单分类</field>
|
||||
<field name="res_model">order.classify</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
</record>
|
||||
|
||||
|
||||
<menuitem name="系统工单" id="work_order_1_list" web_icon="jikimo_system_order,static/description/icon.png"/>
|
||||
<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_type" parent="work_order_1_list" action="classify" groups="jikimo_system_order.group_operations_permissions_rwc"/>
|
||||
<menuitem name="工单设置" id="system_order_notice_user_config" parent="work_order_1_list" action="action_system_order_notice_view" groups="jikimo_system_order.group_operations_permissions_rwc"/>
|
||||
</data>
|
||||
</odoo>
|
||||
6
jikimo_system_order/wizard/__init__.py
Normal file
6
jikimo_system_order/wizard/__init__.py
Normal file
@@ -0,0 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from . import order_other_wizard
|
||||
from . import order_technician_wizard
|
||||
from . import order_close_wizard
|
||||
from . import system_work_order_wizard
|
||||
79
jikimo_system_order/wizard/order_close_wizard.py
Normal file
79
jikimo_system_order/wizard/order_close_wizard.py
Normal file
@@ -0,0 +1,79 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from odoo import models, fields, api
|
||||
from odoo.addons.jikimo_system_order.models.constant import STATE_SELECTION
|
||||
from odoo.exceptions import ValidationError
|
||||
import datetime, logging
|
||||
|
||||
|
||||
class OrderCloseWizard(models.TransientModel):
|
||||
_name = 'order.close.wizard'
|
||||
|
||||
|
||||
def get_context(self):
|
||||
if self._context.get('active_id'):
|
||||
obj = self.env['system.work.order'].browse(self._context.get('active_id'))
|
||||
if obj.initiator_id.id != self.env.user.id:
|
||||
raise ValidationError(u'非本人无法操作')
|
||||
return obj
|
||||
|
||||
order_id = fields.Many2one('system.work.order', string=u'工单ID',
|
||||
default=lambda self: self.get_context().id)
|
||||
# 关闭原因
|
||||
close_cause = fields.Text(string=u'关闭问题原因', default=lambda self: self.get_context().close_cause)
|
||||
# 关闭时间
|
||||
close_time = fields.Datetime(string=u'关闭问题时间', default=fields.datetime.now())
|
||||
# 状态
|
||||
state = fields.Selection(STATE_SELECTION, default='closed', string=u'状态')
|
||||
# 关闭人
|
||||
close_user_id = fields.Many2one('res.users', string=u'关闭人', default=lambda self: self.env.user)
|
||||
|
||||
|
||||
def sure(self):
|
||||
self.order_id.close_cause = self.close_cause
|
||||
self.order_id.close_time = self.close_time
|
||||
if self.order_id.state == 'unconfirmed':
|
||||
state_remark = u'待确认 --> 已关闭'
|
||||
if self.order_id.state == 'pending':
|
||||
state_remark = u'待处理 --> 已关闭'
|
||||
if self.order_id.state == 'processed':
|
||||
state_remark = u'已处理待评分 --> 已关闭'
|
||||
# self.order_id.message_post(u'操作人:%s,操作时间:%s,状态变更过程:%s' % (
|
||||
# self.env.user.name,
|
||||
# (datetime.datetime.now() + datetime.timedelta(hours=8)).strftime('%Y-%m-%d %H:%M:%S'), state_remark))
|
||||
self.order_id.state = self.state
|
||||
self.order_id.close_user_id = self.close_user_id
|
||||
we_employee_ids = []
|
||||
if self.order_id.initiator_id.we_employee_id:
|
||||
we_employee_ids.append(self.order_id.initiator_id.we_employee_id)
|
||||
lost_agent_id = self.env['ir.config_parameter'].sudo().get_param('lost_agent_id')
|
||||
wechat = self.env['we.config'].sudo().get_wechat(agent_id=lost_agent_id)
|
||||
# agent_id, user_ids, content
|
||||
content = """您提交的工单-**工单标题:{0}**-<font color=\"#FF0000\">**已关闭**</font>
|
||||
>提交时间:{1}
|
||||
>处理时间:{2}
|
||||
>处理人:{3}
|
||||
如有问题,请联系系统管理员!
|
||||
""".format(self.order_id.title,
|
||||
(self.order_id.date + datetime.timedelta(hours=8)).strftime(
|
||||
'%Y-%m-%d %H:%M'), (datetime.datetime.now() + datetime.timedelta(
|
||||
hours=8)).strftime('%Y-%m-%d %H:%M'), self.env.user.name or '')
|
||||
# wechat.message.send_markdown(agent_id=lost_agent_id, user_ids=we_employee_ids, content=content)
|
||||
for we_employee_id in we_employee_ids:
|
||||
try:
|
||||
wechat.message.send_markdown(agent_id=lost_agent_id, user_ids=we_employee_id, content=content)
|
||||
except Exception as e:
|
||||
logging.error('工单关闭发送消息异常%s' % str(e))
|
||||
return {}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
42
jikimo_system_order/wizard/order_other_wizard.py
Normal file
42
jikimo_system_order/wizard/order_other_wizard.py
Normal file
@@ -0,0 +1,42 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from odoo import models, fields, api
|
||||
from odoo.exceptions import ValidationError
|
||||
from odoo.addons.jikimo_system_order.models.constant import STATE_SELECTION, GRADE
|
||||
import datetime
|
||||
|
||||
|
||||
class OrderOtherWizard(models.TransientModel):
|
||||
_name = 'order.other.wizard'
|
||||
|
||||
|
||||
def get_context(self):
|
||||
if self._context.get('active_id'):
|
||||
obj = self.env['system.work.order'].browse(self._context.get('active_id'))
|
||||
if obj.initiator_id.id != self.env.user.id:
|
||||
raise ValidationError(u'非本人无法操作')
|
||||
return obj
|
||||
|
||||
order_id = fields.Many2one('system.work.order', string=u'工单ID',
|
||||
default=lambda self: self.get_context().id)
|
||||
# 关闭时间
|
||||
close_time = fields.Datetime(string=u'关闭时间', default=fields.datetime.now())
|
||||
# 状态
|
||||
state = fields.Selection(STATE_SELECTION, default='completed', string=u'状态')
|
||||
# 打分
|
||||
grade = fields.Selection(GRADE, string=u'评分')
|
||||
# 关闭人
|
||||
close_user_id = fields.Many2one('res.users', string=u'关闭人', default=lambda self: self.env.user)
|
||||
|
||||
|
||||
def sure(self):
|
||||
self.order_id.close_time = self.close_time
|
||||
self.order_id.grade = self.grade
|
||||
if self.order_id.state == 'processed':
|
||||
state_remark = u'已处理待评分 --> 已完成'
|
||||
# self.order_id.message_post(u'操作人:%s,操作时间:%s,状态变更过程:%s' % (
|
||||
# self.env.user.name,
|
||||
# (datetime.datetime.now() + datetime.timedelta(hours=8)).strftime('%Y-%m-%d %H:%M:%S'), state_remark))
|
||||
self.order_id.state = self.state
|
||||
self.order_id.close_user_id = self.close_user_id
|
||||
return {}
|
||||
59
jikimo_system_order/wizard/order_technician_wizard.py
Normal file
59
jikimo_system_order/wizard/order_technician_wizard.py
Normal file
@@ -0,0 +1,59 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from odoo import models, fields, api
|
||||
from odoo.addons.jikimo_system_order.models.constant import STATE_SELECTION
|
||||
import datetime
|
||||
import logging
|
||||
|
||||
|
||||
class OrderTechnicianWizard(models.TransientModel):
|
||||
_name = 'order.technician.wizard'
|
||||
|
||||
order_id = fields.Many2one('system.work.order', string=u'工单ID',
|
||||
default=lambda self: self.env.context.get('active_id'))
|
||||
# 解决人
|
||||
solve_people_id = fields.Many2one('res.users', string=u'解决人', default=lambda self: self.env.user)
|
||||
# 用户实际问题
|
||||
users_problem = fields.Text(string=u'用户实际问题')
|
||||
# 最终解决方案
|
||||
solution = fields.Text(string=u'最终解决方案')
|
||||
# 状态
|
||||
state = fields.Selection(STATE_SELECTION, default='processed', string=u'状态')
|
||||
|
||||
def sure(self):
|
||||
self.order_id.solve_people_id = self.solve_people_id
|
||||
self.order_id.users_problem = self.users_problem
|
||||
self.order_id.solution = self.solution
|
||||
if self.order_id.state == 'pending':
|
||||
state_remark = u'待处理 --> 已处理待评分'
|
||||
# self.order_id.message_post(u'操作人:%s,操作时间:%s,状态变更过程:%s' % (
|
||||
# self.env.user.name,
|
||||
# (datetime.datetime.now() + datetime.timedelta(hours=8)).strftime('%Y-%m-%d %H:%M:%S'), state_remark))
|
||||
self.order_id.state = self.state
|
||||
# 获取通知人
|
||||
# objs = self.env['system.order.notice'].search([])
|
||||
# user_ids = objs.notice_user_ids.filtered(lambda item: item.we_employee_id not in ['', False])
|
||||
# we_employee_ids = user_ids.mapped('we_employee_id')
|
||||
we_employee_ids = []
|
||||
if self.order_id.initiator_id.we_employee_id:
|
||||
we_employee_ids.append(self.order_id.initiator_id.we_employee_id)
|
||||
print(we_employee_ids)
|
||||
lost_agent_id = self.env['ir.config_parameter'].sudo().get_param('lost_agent_id')
|
||||
wechat = self.env['we.config'].sudo().get_wechat(agent_id=lost_agent_id)
|
||||
# agent_id, user_ids, content
|
||||
content = """您提交的工单-**工单标题:{0}**-<font color=\"info\">**已处理**</font>
|
||||
>提交时间:{1}
|
||||
>处理反馈:{4}
|
||||
>处理时间:{2}
|
||||
>处理人:{3}
|
||||
如有问题,请联系系统管理员!
|
||||
""".format(self.order_id.title,
|
||||
(self.order_id.date + datetime.timedelta(hours=8)).strftime('%Y-%m-%d %H:%M'), (datetime.datetime.now() + datetime.timedelta(hours=8)).strftime('%Y-%m-%d %H:%M'), self.env.user.name or '', self.solution or '')
|
||||
# wechat.message.send_markdown(agent_id=lost_agent_id, user_ids=we_employee_ids, content=content)
|
||||
for we_employee_id in we_employee_ids:
|
||||
try:
|
||||
wechat.message.send_markdown(agent_id=lost_agent_id, user_ids=we_employee_id, content=content)
|
||||
except Exception as e:
|
||||
logging.error('工单处理发送消息异常%s' % str(e))
|
||||
|
||||
return {}
|
||||
122
jikimo_system_order/wizard/order_wizard.xml
Normal file
122
jikimo_system_order/wizard/order_wizard.xml
Normal file
@@ -0,0 +1,122 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
<!-- 技术员向导form-->
|
||||
<record model="ir.ui.view" id="wizard_technician_form_view">
|
||||
<field name="name">技术员向导</field>
|
||||
<field name="model">order.technician.wizard</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="技术员编辑">
|
||||
<group>
|
||||
<field name="order_id" required="1" readonly="1"/>
|
||||
<field name="solve_people_id" required="1"/>
|
||||
<field name="users_problem" required="1" style="height: 50px;"/>
|
||||
<field name="solution" required="1" style="height: 50px;"/>
|
||||
</group>
|
||||
<footer>
|
||||
<button name="sure" string="确定" type="object" class="oe_highlight"/>
|
||||
or
|
||||
<button string="取消" class="oe_link" special="cancel"/>
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.actions.act_window" id="launch_order_technician_wizard">
|
||||
<field name="name">技术员编辑</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">order.technician.wizard</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="view_id" ref="wizard_technician_form_view"/>
|
||||
<field name="context">{'display_default_code':False}</field>
|
||||
<field name="target">new</field>
|
||||
</record>
|
||||
|
||||
|
||||
|
||||
<!-- 其它向导form-->
|
||||
<record model="ir.ui.view" id="wizard_other_form_view">
|
||||
<field name="name">其它向导</field>
|
||||
<field name="model">order.other.wizard</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="其它编辑">
|
||||
<group>
|
||||
<field name="order_id" required="1" readonly="1"/>
|
||||
<field name="close_time" required="1" readonly="1"/>
|
||||
<field name="grade" required="1"/>
|
||||
<field name="close_user_id" required="1" readonly="1"/>
|
||||
</group>
|
||||
<footer>
|
||||
<button name="sure" string="确定" type="object" class="oe_highlight"/>
|
||||
or
|
||||
<button string="取消" class="oe_link" special="cancel"/>
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.actions.act_window" id="launch_order_other_wizard">
|
||||
<field name="name">其它编辑</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">order.other.wizard</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="view_id" ref="wizard_other_form_view"/>
|
||||
<field name="context">{'display_default_code':False}</field>
|
||||
<field name="target">new</field>
|
||||
</record>
|
||||
|
||||
<!--关闭向导form-->
|
||||
<record model="ir.ui.view" id="wizard_close_form_view">
|
||||
<field name="name">关闭向导</field>
|
||||
<field name="model">order.close.wizard</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="关闭工单">
|
||||
<group>
|
||||
<field name="order_id" required="1" readonly="1"/>
|
||||
<field name="close_cause" required="1" style="height: 50px;"/>
|
||||
<field name="close_time" required="1" readonly="1"/>
|
||||
<field name="close_user_id" required="1" readonly="1"/>
|
||||
</group>
|
||||
<footer>
|
||||
<button name="sure" string="确定" type="object" class="oe_highlight"/>
|
||||
or
|
||||
<button string="取消" class="oe_link" special="cancel"/>
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.actions.act_window" id="launch_order_close_wizard">
|
||||
<field name="name">关闭工单</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">order.close.wizard</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="view_id" ref="wizard_close_form_view"/>
|
||||
<field name="context">{'display_default_code':False}</field>
|
||||
<field name="target">new</field>
|
||||
</record>
|
||||
|
||||
<record id="system_work_order_wizard_view" model="ir.ui.view">
|
||||
<field name="name">system_work_order_wizard_view</field>
|
||||
<field name="model">system.work.order.wizard</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="二次确认">
|
||||
<field name="explain" readonly="1"/>
|
||||
<footer>
|
||||
<button name="sure" string="确定" type="object" class="oe_highlight"/>
|
||||
or
|
||||
<button string="取消" class="oe_link" special="cancel"/>
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.actions.act_window" id="system_work_order_wizard_view_act_window">
|
||||
<field name="name">二次确认</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">system.work.order.wizard</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="target">new</field>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
42
jikimo_system_order/wizard/system_work_order_wizard.py
Normal file
42
jikimo_system_order/wizard/system_work_order_wizard.py
Normal file
@@ -0,0 +1,42 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @Time : 2017/12/12 9:46
|
||||
# @Author : GuoXiang
|
||||
# @Site :
|
||||
# @File : system_work_order_wizard.py
|
||||
# @Software: PyCharm
|
||||
# @Desc :
|
||||
# @license : Copyright©2018 www.dasmaster.com All Rights Reserved.
|
||||
# @Contact : xg1230205321@163.com
|
||||
from odoo import models, api, fields
|
||||
from odoo.exceptions import ValidationError
|
||||
|
||||
|
||||
class SystemWorkOrderWizard(models.TransientModel):
|
||||
_name = "system.work.order.wizard"
|
||||
_description = u"追回确认"
|
||||
|
||||
|
||||
def _get_explain(self):
|
||||
if self._context.get('object_id'):
|
||||
obj = self.env['system.work.order'].browse(self._context.get('object_id'))
|
||||
if obj.initiator_id.id != self.env.user.id:
|
||||
raise ValidationError(u'非本人无法操作')
|
||||
if self._context.get('explain'):
|
||||
return self._context["explain"]
|
||||
|
||||
explain = fields.Char(default=_get_explain)
|
||||
|
||||
|
||||
def sure(self):
|
||||
"""
|
||||
确认
|
||||
:return:
|
||||
"""
|
||||
if self._context.get('object_id') and self._context.get('object_name') and self._context.get(
|
||||
'explain') and self._context.get('function_name'):
|
||||
work_sheet_obj = self.env[self._context["object_name"]].search([('id', '=', int(self._context["object_id"]))])
|
||||
class_name = self._context.get('object_name') # 获得对象类名
|
||||
method_name = self._context.get('function_name') # 获得对象的方法
|
||||
obj_function = getattr(self.env[class_name], method_name)
|
||||
obj_function(work_sheet_obj)
|
||||
@@ -35,6 +35,7 @@
|
||||
],
|
||||
'web.assets_backend': [
|
||||
'sf_base/static/src/scss/*.scss',
|
||||
'sf_base/static/src/js/*.js',
|
||||
],
|
||||
|
||||
},
|
||||
|
||||
@@ -5,3 +5,4 @@ from . import fixture
|
||||
from . import functional_fixture
|
||||
from . import tool_other_features
|
||||
from . import basic_parameters_fixture
|
||||
from . import ir_sequence
|
||||
|
||||
@@ -13,7 +13,7 @@ class BasicParametersFixture(models.Model):
|
||||
diameter = fields.Float('直径(mm)', digits=(16, 2))
|
||||
|
||||
# '零点卡盘' 字段
|
||||
weight = fields.Float('重量(mm)', digits=(16, 2))
|
||||
weight = fields.Float('重量(kg)', digits=(16, 2))
|
||||
orientation_dish_diameter = fields.Float('定位盘直径(mm)', digits=(16, 2))
|
||||
clamping_diameter = fields.Float('装夹直径(mm)', digits=(16, 2))
|
||||
clamping_num = fields.Selection([('1', '1'), ('2', '2'), ('4', '4'), ('6', '6'), ('8', '8')], string='装夹单元数')
|
||||
|
||||
@@ -61,12 +61,10 @@ class MrsMaterialModel(models.Model):
|
||||
supplier_ids = fields.One2many('sf.supplier.sort', 'materials_model_id', string='供应商')
|
||||
active = fields.Boolean('有效', default=True)
|
||||
|
||||
@api.onchange('gain_way')
|
||||
def _check_gain_way(self):
|
||||
if not self.gain_way:
|
||||
raise UserError("请选择获取方式")
|
||||
if self.gain_way in ['外协', '采购']:
|
||||
if not self.supplier_ids:
|
||||
@api.constrains("gain_way")
|
||||
def _check_supplier_ids(self):
|
||||
for item in self:
|
||||
if item.gain_way in ('外协', '采购') and not item.supplier_ids:
|
||||
raise UserError("请添加供应商")
|
||||
|
||||
|
||||
@@ -86,17 +84,21 @@ class MrsProductionProcessCategory(models.Model):
|
||||
class MrsProductionProcess(models.Model):
|
||||
_name = 'sf.production.process'
|
||||
_description = '表面工艺'
|
||||
order = 'sequence asc'
|
||||
|
||||
code = fields.Char("编码")
|
||||
name = fields.Char('名称')
|
||||
remark = fields.Text("备注")
|
||||
sequence = fields.Integer('排序')
|
||||
# processing_order_ids = fields.One2many('sf.processing.order', 'production_process_id', string='工序')
|
||||
partner_process_ids = fields.Many2many('res.partner', 'process_ids', '加工工厂')
|
||||
active = fields.Boolean('有效', default=True)
|
||||
parameter_ids = fields.One2many('sf.production.process.parameter', 'process_id', string='可选参数')
|
||||
category_id = fields.Many2one('sf.production.process.category')
|
||||
category_id = fields.Many2one('sf.production.process.category', string='表面工艺类别')
|
||||
# workcenter_ids = fields.Many2many('mrp.workcenter', 'rel_workcenter_process', required=True)
|
||||
|
||||
processing_day = fields.Float('加工天数/d')
|
||||
travel_day = fields.Float('路途天数/d')
|
||||
sequence = fields.Integer('排序')
|
||||
|
||||
# class MrsProcessingTechnology(models.Model):
|
||||
# _name = 'sf.processing.technology'
|
||||
@@ -143,8 +145,12 @@ class MrsProductionProcessParameter(models.Model):
|
||||
is_check = fields.Boolean(default=False)
|
||||
# price = fields.Float('单价')
|
||||
process_id = fields.Many2one('sf.production.process', string='表面工艺')
|
||||
process_description = fields.Char(string='工艺描述')
|
||||
materials_model_ids = fields.Many2many('sf.materials.model', 'applicable_material', string='适用材料')
|
||||
processing_day = fields.Float('加工天数/d')
|
||||
travel_day = fields.Float('路途天数/d')
|
||||
active = fields.Boolean('有效', default=True)
|
||||
processing_mm = fields.Char('加工厚度/mm')
|
||||
|
||||
def name_get(self):
|
||||
result = []
|
||||
|
||||
74
sf_base/models/ir_sequence.py
Normal file
74
sf_base/models/ir_sequence.py
Normal file
@@ -0,0 +1,74 @@
|
||||
import calendar
|
||||
from datetime import timedelta
|
||||
|
||||
from odoo import models, fields
|
||||
|
||||
|
||||
class IrSequence(models.Model):
|
||||
_inherit = 'ir.sequence'
|
||||
|
||||
date_range_period = fields.Selection(
|
||||
[('day', '每日'), ('month', '每月'), ('year', '每年')],
|
||||
string='日期期间',
|
||||
)
|
||||
|
||||
def _next(self, sequence_date=None):
|
||||
""" Returns the next number in the preferred sequence in all the ones given in self."""
|
||||
if not self.use_date_range:
|
||||
return self._next_do()
|
||||
# date mode
|
||||
dt = sequence_date or self._context.get('ir_sequence_date', fields.Date.today())
|
||||
seq_date = self.env['ir.sequence.date_range'].search(
|
||||
[
|
||||
('sequence_id', '=', self.id),
|
||||
('date_from', '<=', dt),
|
||||
('date_to', '>=', dt),
|
||||
('date_range_period', '=', self.date_range_period)
|
||||
], limit=1)
|
||||
if not seq_date:
|
||||
if self.date_range_period:
|
||||
seq_date = self._create_date_range_seq_by_period(dt, self.date_range_period)
|
||||
else:
|
||||
seq_date = self._create_date_range_seq(dt)
|
||||
return seq_date.with_context(ir_sequence_date_range=seq_date.date_from)._next()
|
||||
|
||||
def _create_date_range_seq_by_period(self, date, period):
|
||||
if period == 'year':
|
||||
year = fields.Date.from_string(date).strftime('%Y')
|
||||
date_from = '{}-01-01'.format(year)
|
||||
date_to = '{}-12-31'.format(year)
|
||||
if period == 'month':
|
||||
# 计算当前月份的第一天和最后一天
|
||||
year = fields.Date.from_string(date).strftime('%Y')
|
||||
month = fields.Date.from_string(date).strftime('%m')
|
||||
date_from = fields.Date.from_string(date).strftime('%Y-%m-01')
|
||||
date_to = '{}-{}-{}'.format(year, month, calendar.monthrange(int(year), int(month))[1])
|
||||
if period == 'day':
|
||||
date_from = date
|
||||
date_to = date
|
||||
date_range = self.env['ir.sequence.date_range'].search(
|
||||
[
|
||||
('sequence_id', '=', self.id),
|
||||
('date_to', '>=', date_from),
|
||||
('date_to', '<=', date),
|
||||
('date_range_period', '=', period)
|
||||
],
|
||||
order='date_to desc', limit=1)
|
||||
if date_range:
|
||||
date_from = date_range.date_to + timedelta(days=1)
|
||||
seq_date_range = self.env['ir.sequence.date_range'].sudo().create({
|
||||
'date_from': date_from,
|
||||
'date_to': date_to,
|
||||
'sequence_id': self.id,
|
||||
'date_range_period': period,
|
||||
})
|
||||
return seq_date_range
|
||||
|
||||
|
||||
class IrSequenceDateRange(models.Model):
|
||||
_inherit = 'ir.sequence.date_range'
|
||||
|
||||
date_range_period = fields.Selection(
|
||||
[('day', '每日'), ('month', '每月'), ('year', '每年')],
|
||||
string='日期期间',
|
||||
)
|
||||
@@ -1,10 +0,0 @@
|
||||
diff a/sf_base/models/tool_base_new.py b/sf_base/models/tool_base_new.py (rejected hunks)
|
||||
@@ -108,6 +108,4 @@
|
||||
cutting_speed_ids = fields.One2many('sf.cutting.speed', 'standard_library_id', string='切削速度Vc')
|
||||
- feed_per_tooth_ids = fields.One2many('sf.feed.per.tooth', 'standard_library_id', '每齿走刀量fz',
|
||||
- domain=[('cutting_speed', '!=', False)])
|
||||
- feed_per_tooth_ids_3 = fields.One2many('sf.feed.per.tooth', 'standard_library_id', '每齿走刀量fz',
|
||||
- domain=[('cutting_speed', '!=', False)])
|
||||
+ feed_per_tooth_ids = fields.One2many('sf.feed.per.tooth', 'standard_library_id', '每齿走刀量fz')
|
||||
+ feed_per_tooth_ids_3 = fields.One2many('sf.feed.per.tooth', 'standard_library_id', '每齿走刀量fz')
|
||||
|
||||
23
sf_base/static/src/js/remove_focus.js
Normal file
23
sf_base/static/src/js/remove_focus.js
Normal file
@@ -0,0 +1,23 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { registry } from '@web/core/registry';
|
||||
|
||||
import { formView } from '@web/views/form/form_view';
|
||||
import { FormController } from '@web/views/form/form_controller';
|
||||
|
||||
import { onRendered, onMounted } from "@odoo/owl";
|
||||
|
||||
export class RemoveFocusController extends FormController {
|
||||
setup() {
|
||||
super.setup();
|
||||
|
||||
onMounted(() => {
|
||||
this.__owl__.bdom.el.querySelectorAll(':focus').forEach(element => element.blur());
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
registry.category('views').add('remove_focus_view', {
|
||||
...formView,
|
||||
Controller: RemoveFocusController,
|
||||
});
|
||||
@@ -16,7 +16,7 @@
|
||||
<record model="ir.ui.view" id="mrs_production_process_parameter_form">
|
||||
<field name="model">sf.production.process.parameter</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="表面工艺可选参数" create="0" delete="0">
|
||||
<form string="表面工艺可选参数" create="0" delete="0" >
|
||||
<sheet>
|
||||
<div class="oe_title">
|
||||
<h1>
|
||||
@@ -27,13 +27,18 @@
|
||||
<group>
|
||||
<field name="code" readonly="1"/>
|
||||
<field name="process_id" readonly="1"/>
|
||||
<field name="process_description" readonly="1"/>
|
||||
<field name="gain_way"/>
|
||||
</group>
|
||||
|
||||
<group>
|
||||
<field name="processing_day" readonly="1"/>
|
||||
<field name="travel_day" readonly="1"/>
|
||||
<field name="processing_mm" readonly="1"/>
|
||||
</group>
|
||||
</group>
|
||||
<notebook>
|
||||
<page string="适用材料">
|
||||
<field name="materials_model_ids"></field>
|
||||
<field name="materials_model_ids" readonly="1"></field>
|
||||
</page>
|
||||
</notebook>
|
||||
</sheet>
|
||||
@@ -48,7 +53,7 @@
|
||||
<search>
|
||||
<filter name="filter_active" string="已归档" domain="[('active','=',False)]"/>
|
||||
<field name="name" string="名称" filter_domain="[('name','ilike',self)]"/>
|
||||
<field name="code" string="编码" filter_domain="[('codeNum','ilike',self)]"/>
|
||||
<field name="code" string="编码" filter_domain="[('code','ilike',self)]"/>
|
||||
<searchpanel class="account_root">
|
||||
<field name="process_id" icon="fa-filter"/>
|
||||
</searchpanel>
|
||||
@@ -136,7 +141,7 @@
|
||||
<field name="model">sf.production.process.category</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="表面工艺类别" default_order="sequence, id" create="0" edit="0" delete="1">
|
||||
<field name="sequence" widget="handle" string="序号"/>
|
||||
<field name="sequence" widget="handle" string="序号" readonly="1"/>
|
||||
<field name="code"/>
|
||||
<field name="name" string="名称"/>
|
||||
</tree>
|
||||
@@ -159,7 +164,8 @@
|
||||
<record model="ir.ui.view" id="sf_production_process_tree">
|
||||
<field name="model">sf.production.process</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="表面工艺" create="0" edit="0" delete="1">
|
||||
<tree string="表面工艺" create="0" edit="0" delete="0">
|
||||
<field name="sequence" string="加工顺序" readonly="1"/>
|
||||
<field name="code"/>
|
||||
<field name="name" string="名称"/>
|
||||
<field name="remark"/>
|
||||
@@ -170,7 +176,7 @@
|
||||
<record model="ir.ui.view" id="sf_production_process_form">
|
||||
<field name="model">sf.production.process</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="表面工艺" create="0" edit="1" delete="1">
|
||||
<form string="表面工艺" create="0" delete="0">
|
||||
<sheet>
|
||||
<div class="oe_title">
|
||||
<h1>
|
||||
@@ -179,41 +185,46 @@
|
||||
</div>
|
||||
<group>
|
||||
<group>
|
||||
<field name="code"/>
|
||||
<field name="code" readonly="1"/>
|
||||
<field name="category_id" readonly="1"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="processing_day" readonly="1"/>
|
||||
<field name="travel_day" readonly="1"/>
|
||||
</group>
|
||||
<notebook>
|
||||
<page string="可选参数">
|
||||
<field name="parameter_ids">
|
||||
<tree force_save="1">
|
||||
<field name="code" readonly="1" force_save="1"/>
|
||||
<field name="name"/>
|
||||
<field name="gain_way"/>
|
||||
<field name='process_id' default="default"/>
|
||||
</tree>
|
||||
<form>
|
||||
<sheet>
|
||||
<group>
|
||||
<group>
|
||||
<field name="code"/>
|
||||
<field name="name" string="参数名"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name='process_id'/>
|
||||
<field name="gain_way"/>
|
||||
</group>
|
||||
</group>
|
||||
<notebook>
|
||||
<page string="适用材料">
|
||||
<field name="materials_model_ids"/>
|
||||
</page>
|
||||
</notebook>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</page>
|
||||
|
||||
</notebook>
|
||||
</group>
|
||||
<notebook>
|
||||
<page string="可选参数" >
|
||||
<field name="parameter_ids" >
|
||||
<tree force_save="1" create="0">
|
||||
<field name="code" readonly="1" force_save="1"/>
|
||||
<field name="name" readonly="1"/>
|
||||
<field name="gain_way"/>
|
||||
<field name='process_id' default="default"/>
|
||||
</tree>
|
||||
<form>
|
||||
<sheet>
|
||||
<group>
|
||||
<group>
|
||||
<field name="code"/>
|
||||
<field name="name" string="参数名"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name='process_id'/>
|
||||
<field name="gain_way"/>
|
||||
</group>
|
||||
</group>
|
||||
<notebook>
|
||||
<page string="适用材料">
|
||||
<field name="materials_model_ids"/>
|
||||
</page>
|
||||
</notebook>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</page>
|
||||
|
||||
</notebook>
|
||||
<group>
|
||||
<group>
|
||||
<field name="remark"/>
|
||||
@@ -251,7 +262,7 @@
|
||||
<group>
|
||||
<group>
|
||||
<field name="materials_no" readonly="1" force_save="1"/>
|
||||
<field name="gain_way" required="1"/>
|
||||
<field name="gain_way" required="0"/>
|
||||
<field name="tensile_strength" required="1"/>
|
||||
<field name="hardness" required="1"/>
|
||||
<field name="density" readonly="1"/>
|
||||
@@ -270,9 +281,9 @@
|
||||
<notebook>
|
||||
<page string="供应商">
|
||||
<field name='supplier_ids' class="supplier_ids_set_css">
|
||||
<tree editable='bottom'>
|
||||
<tree editable='bottom' delete="1">
|
||||
<field name="sequence" widget="handle" string="序号"/>
|
||||
<field name="partner_id" string="名称"/>
|
||||
<field name="partner_id" string="名称" options="{'no_create': True}"/>
|
||||
</tree>
|
||||
</field>
|
||||
</page>
|
||||
|
||||
@@ -171,7 +171,7 @@
|
||||
<field name="width"/>
|
||||
<field name="height"/>
|
||||
<field name="diameter"/>
|
||||
<field name="weight"/>
|
||||
<field name="weight" string="重量(kg)"/>
|
||||
<field name="orientation_dish_diameter"/>
|
||||
<field name="clamping_diameter"/>
|
||||
<field name="clamping_num"/>
|
||||
@@ -197,7 +197,7 @@
|
||||
<field name="width"/>
|
||||
<field name="height"/>
|
||||
<field name="diameter"/>
|
||||
<field name="weight"/>
|
||||
<field name="weight" string="重量(kg)"/>
|
||||
<field name="clamping_diameter"/>
|
||||
<field name="connector_diameter"/>
|
||||
<field name="chucking_power_max"/>
|
||||
@@ -220,7 +220,7 @@
|
||||
<field name="length"/>
|
||||
<field name="width"/>
|
||||
<field name="height"/>
|
||||
<field name="weight"/>
|
||||
<field name="weight" string="重量(kg)"/>
|
||||
<field name="gripper_length_min"/>
|
||||
<field name="gripper_width_min"/>
|
||||
<field name="gripper_height_min"/>
|
||||
@@ -248,7 +248,7 @@
|
||||
<field name="length"/>
|
||||
<field name="width"/>
|
||||
<field name="height"/>
|
||||
<field name="weight"/>
|
||||
<field name="weight" string="重量(kg)"/>
|
||||
<field name="gripper_length_min"/>
|
||||
<field name="gripper_width_min"/>
|
||||
<field name="gripper_height_min"/>
|
||||
@@ -278,7 +278,7 @@
|
||||
<field name="width"/>
|
||||
<field name="height"/>
|
||||
<field name="height_tolerance_value"/>
|
||||
<field name="weight"/>
|
||||
<field name="weight" string="重量(kg)"/>
|
||||
<field name="gripper_length_min"/>
|
||||
<field name="gripper_width_min"/>
|
||||
<field name="gripper_height_min"/>
|
||||
@@ -307,7 +307,7 @@
|
||||
<field name="length"/>
|
||||
<field name="width"/>
|
||||
<field name="height"/>
|
||||
<field name="weight"/>
|
||||
<field name="weight" string="重量(kg)"/>
|
||||
<field name="gripper_length_min"/>
|
||||
<field name="gripper_width_min"/>
|
||||
<field name="gripper_height_min"/>
|
||||
@@ -335,7 +335,7 @@
|
||||
<field name="width"/>
|
||||
<field name="height"/>
|
||||
<field name="diameter"/>
|
||||
<field name="weight"/>
|
||||
<field name="weight" string="重量(kg)"/>
|
||||
<field name="gripper_length_min"/>
|
||||
<field name="gripper_width_min"/>
|
||||
<field name="gripper_height_min"/>
|
||||
|
||||
@@ -36,7 +36,7 @@ class Http(models.AbstractModel):
|
||||
post_time = int(datas['HTTP_TIMESTAMP'])
|
||||
datetime_post = datetime.fromtimestamp(post_time)
|
||||
datetime_now = datetime.now().replace(microsecond=0)
|
||||
datetime_del = datetime_now + timedelta(seconds=5)
|
||||
datetime_del = datetime_now + timedelta(seconds=30)
|
||||
if datetime_post > datetime_del:
|
||||
raise AuthenticationError('请求已过期')
|
||||
check_str = '%s%s%s' % (datas['HTTP_TOKEN'], post_time, factory_secret.sf_secret_key)
|
||||
|
||||
@@ -149,7 +149,7 @@ class JdEclp(models.Model):
|
||||
},
|
||||
}
|
||||
_logger.info('准备调接口1')
|
||||
url1 = config['bfm_url'] + '/api/create/jd/order'
|
||||
url1 = config['bfm_url_new'] + '/api/create/jd/order'
|
||||
requests.post(url1, json=json1, data=None)
|
||||
_logger.info('调用成功1')
|
||||
_logger.info('准备调接口2')
|
||||
@@ -158,9 +158,12 @@ class JdEclp(models.Model):
|
||||
'orderNo': self.origin,
|
||||
},
|
||||
}
|
||||
url2 = config['bfm_url'] + '/api/get/jd/no'
|
||||
url2 = config['bfm_url_new'] + '/api/get/jd/no'
|
||||
response = requests.post(url2, json=json2, data=None)
|
||||
# _logger.info('调用成功2', response.json()['result']['wbNo'])
|
||||
tem_ret = response.json().get('result')
|
||||
if not tem_ret:
|
||||
raise ValidationError('京东物流返回异常,请联系管理员')
|
||||
self.carrier_tracking_ref = response.json()['result'].get('wbNo')
|
||||
if not self.carrier_tracking_ref:
|
||||
raise ValidationError('物流下单未成功,请联系管理员')
|
||||
@@ -193,7 +196,7 @@ class JdEclp(models.Model):
|
||||
'no': self.origin,
|
||||
},
|
||||
}
|
||||
url1 = config['bfm_url'] + '/api/create/jd/bill'
|
||||
url1 = config['bfm_url_new'] + '/api/create/jd/bill'
|
||||
response = requests.post(url1, json=json1, data=None)
|
||||
# _logger.info('调用成功2', response.json())
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
from datetime import datetime
|
||||
import logging
|
||||
import requests
|
||||
from odoo import fields, models
|
||||
from odoo.exceptions import UserError
|
||||
from odoo import fields, models, _
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -14,26 +15,49 @@ class StatusChange(models.Model):
|
||||
def action_confirm(self):
|
||||
# 在原有方法执行前记录日志和执行其他操作
|
||||
logging.info('函数已经执行=============')
|
||||
server_product_none = []
|
||||
for order in self.order_line:
|
||||
gain_way_no = order.product_template_id.model_process_parameters_ids.filtered(lambda a: not a.gain_way)
|
||||
if gain_way_no:
|
||||
process_parameters = [item.name for item in gain_way_no]
|
||||
raise UserError(
|
||||
_("请先至【制造】-【配置】中【表面工艺可选参数】为【%s】填写获取方式", ", ".join(process_parameters)))
|
||||
for item in order.product_template_id.model_process_parameters_ids:
|
||||
if item.gain_way == '外协':
|
||||
server_product = self.env['product.template'].search(
|
||||
[('server_product_process_parameters_id', '=', item.id),
|
||||
('detailed_type', '=', 'service')])
|
||||
if not server_product:
|
||||
server_product_none.append(item.name)
|
||||
if server_product_none:
|
||||
raise UserError(_("请先至【产品】中创建【表面工艺参数】为【%s】的服务产品", ", ".join(server_product_none)))
|
||||
|
||||
# 使用super()来调用原始方法(在本例中为'sale.order'模型的'action_confirm'方法)
|
||||
res = super(StatusChange, self).action_confirm()
|
||||
|
||||
# 原有方法执行后,进行额外的操作(如调用外部API)
|
||||
process_start_time = str(datetime.now())
|
||||
config = self.env['res.config.settings'].get_values()
|
||||
json1 = {
|
||||
'params': {
|
||||
'model_name': 'jikimo.process.order',
|
||||
'field_name': 'name',
|
||||
'default_code': self.default_code,
|
||||
'state': '加工中',
|
||||
'process_start_time': process_start_time,
|
||||
},
|
||||
}
|
||||
url1 = config['bfm_url'] + '/api/get/state/get_order'
|
||||
requests.post(url1, json=json1, data=None)
|
||||
logging.info('接口已经执行=============')
|
||||
|
||||
try:
|
||||
res = super(StatusChange, self).action_confirm()
|
||||
# 原有方法执行后,进行额外的操作(如调用外部API)
|
||||
process_start_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||||
config = self.env['res.config.settings'].get_values()
|
||||
json1 = {
|
||||
'params': {
|
||||
'model_name': 'jikimo.process.order',
|
||||
'field_name': 'name',
|
||||
'default_code': self.default_code,
|
||||
'state': '加工中',
|
||||
'process_start_time': process_start_time,
|
||||
},
|
||||
}
|
||||
url1 = config['bfm_url_new'] + '/api/get/state/get_order'
|
||||
ret = requests.post(url1, json=json1, data=None)
|
||||
ret = ret.json()
|
||||
if not ret.get('error'):
|
||||
logging.info('接口已经执行=============')
|
||||
else:
|
||||
logging.error('工厂加工同步订单状态失败 {}'.format(ret.text))
|
||||
raise UserError('工厂加工同步订单状态失败')
|
||||
except UserError as e:
|
||||
logging.error('工厂加工同步订单状态失败 {}'.format(e))
|
||||
raise UserError('工厂加工同步订单状态失败')
|
||||
return res
|
||||
|
||||
def action_cancel(self):
|
||||
@@ -54,7 +78,7 @@ class StatusChange(models.Model):
|
||||
'state': '待派单',
|
||||
},
|
||||
}
|
||||
url1 = config['bfm_url'] + '/api/get/state/cancel_order'
|
||||
url1 = config['bfm_url_new'] + '/api/get/state/cancel_order'
|
||||
requests.post(url1, json=json1, data=None)
|
||||
|
||||
return res
|
||||
@@ -202,12 +226,12 @@ class FinishStatusChange(models.Model):
|
||||
[('id', 'child_of', self.picking_type_id.warehouse_id.view_location_id.id),
|
||||
('usage', '!=', 'supplier')])
|
||||
if self.env['stock.move'].search([
|
||||
('state', 'in', ['confirmed', 'partially_available', 'waiting', 'assigned']),
|
||||
('product_qty', '>', 0),
|
||||
('location_id', 'in', wh_location_ids),
|
||||
('move_orig_ids', '=', False),
|
||||
('picking_id', 'not in', self.ids),
|
||||
('product_id', 'in', lines.product_id.ids)], limit=1):
|
||||
('state', 'in', ['confirmed', 'partially_available', 'waiting', 'assigned']),
|
||||
('product_qty', '>', 0),
|
||||
('location_id', 'in', wh_location_ids),
|
||||
('move_orig_ids', '=', False),
|
||||
('picking_id', 'not in', self.ids),
|
||||
('product_id', 'in', lines.product_id.ids)], limit=1):
|
||||
action = self.action_view_reception_report()
|
||||
action['context'] = {'default_picking_ids': self.ids}
|
||||
return action
|
||||
|
||||
@@ -122,7 +122,7 @@ class ResMrpBomMo(models.Model):
|
||||
# 查bom的原材料
|
||||
def get_raw_bom(self, product):
|
||||
raw_bom = self.env['product.product'].search(
|
||||
[('categ_id.type', '=', '原材料'), ('materials_type_id', '=', product.materials_type_id.id)])
|
||||
[('categ_id.type', '=', '原材料'), ('materials_type_id', '=', product.materials_type_id.id)],limit=1)
|
||||
return raw_bom
|
||||
|
||||
|
||||
|
||||
@@ -22,6 +22,16 @@
|
||||
<field name="company_id" ref="base.main_company"/>
|
||||
</record>
|
||||
|
||||
<record id="stock_location_tool_dismantle" model="stock.location">
|
||||
<field name="name">拆解</field>
|
||||
<field name="location_id" ref="stock.stock_location_locations_virtual"/>
|
||||
<field name="usage">internal</field>
|
||||
<field name="barcode">DJCJ</field>
|
||||
<field name="scrap_location">true</field>
|
||||
<field name="active">true</field>
|
||||
<field name="company_id" ref="base.main_company"/>
|
||||
</record>
|
||||
|
||||
|
||||
<record id="after_assembly_picking_in" model="stock.picking.type">
|
||||
<field name="name">刀具组装入库</field>
|
||||
|
||||
3
sf_hr/__init__.py
Normal file
3
sf_hr/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from . import models
|
||||
22
sf_hr/__manifest__.py
Normal file
22
sf_hr/__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': ['hr'],
|
||||
'data': [
|
||||
'views/hr_employee.xml',
|
||||
],
|
||||
'demo': [
|
||||
],
|
||||
'qweb': [
|
||||
],
|
||||
'license': 'LGPL-3',
|
||||
'installable': True,
|
||||
'application': False,
|
||||
'auto_install': False,
|
||||
}
|
||||
2
sf_hr/models/__init__.py
Normal file
2
sf_hr/models/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
0
sf_hr/security/ir.model.access.csv
Normal file
0
sf_hr/security/ir.model.access.csv
Normal file
|
|
15
sf_hr/views/hr_employee.xml
Normal file
15
sf_hr/views/hr_employee.xml
Normal file
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<odoo>
|
||||
<data>
|
||||
<record id="view_form_employee_extend" model="ir.ui.view">
|
||||
<field name="name">employee_form</field>
|
||||
<field name="model">hr.employee</field>
|
||||
<field name="inherit_id" ref="hr.view_employee_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//group//field[@name='work_email']" position="attributes">
|
||||
<attribute name="required">1</attribute>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
@@ -1,2 +1,3 @@
|
||||
from . import models
|
||||
from . import wizard
|
||||
from . import controllers
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
'views/machine_info_present.xml',
|
||||
'views/delivery_record.xml',
|
||||
'views/res_config_settings_views.xml',
|
||||
'views/maintenance_views.xml',
|
||||
|
||||
],
|
||||
'assets': {
|
||||
|
||||
1
sf_machine_connect/controllers/__init__.py
Normal file
1
sf_machine_connect/controllers/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from . import controllers
|
||||
832
sf_machine_connect/controllers/controllers.py
Normal file
832
sf_machine_connect/controllers/controllers.py
Normal file
@@ -0,0 +1,832 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import ast
|
||||
import json
|
||||
import base64
|
||||
import logging
|
||||
import psycopg2
|
||||
from datetime import datetime, timedelta
|
||||
from odoo import http
|
||||
from odoo.http import request
|
||||
|
||||
# 数据库连接配置
|
||||
db_config = {
|
||||
"database": "timeseries_db",
|
||||
"user": "postgres",
|
||||
"password": "postgres",
|
||||
"port": "5432",
|
||||
"host": "172.16.10.98"
|
||||
}
|
||||
|
||||
|
||||
def convert_to_seconds(time_str):
|
||||
# 修改正则表达式,使 H、M、S 部分可选
|
||||
|
||||
pattern = r"(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?"
|
||||
match = re.match(pattern, time_str)
|
||||
|
||||
if match:
|
||||
# 提取各时间单位,如果某个单位缺失则默认设为0
|
||||
hours = int(match.group(1)) if match.group(1) else 0
|
||||
minutes = int(match.group(2)) if match.group(2) else 0
|
||||
seconds = int(match.group(3)) if match.group(3) else 0
|
||||
|
||||
# 计算总秒数
|
||||
total_seconds = hours * 3600 + minutes * 60 + seconds
|
||||
if total_seconds == 0:
|
||||
# return None
|
||||
pattern = r"(?:(\d+)小时)?(?:(\d+)分钟)?(?:(\d+)秒)?"
|
||||
match = re.match(pattern, time_str)
|
||||
if match:
|
||||
# 提取各时间单位,如果某个单位缺失则默认设为0
|
||||
hours = int(match.group(1)) if match.group(1) else 0
|
||||
minutes = int(match.group(2)) if match.group(2) else 0
|
||||
seconds = int(match.group(3)) if match.group(3) else 0
|
||||
|
||||
# 计算总秒数
|
||||
total_seconds = hours * 3600 + minutes * 60 + seconds
|
||||
return total_seconds
|
||||
else:
|
||||
return None
|
||||
return total_seconds
|
||||
|
||||
|
||||
class Sf_Dashboard_Connect(http.Controller):
|
||||
|
||||
@http.route('/api/get_machine_datas/list', type='http', auth='public', methods=['GET', 'POST'], csrf=False,
|
||||
cors="*")
|
||||
def get_machine_datas_list(self, **kw):
|
||||
"""
|
||||
拿到机床数据返回给大屏展示
|
||||
:param kw:
|
||||
:return:
|
||||
"""
|
||||
res = {'status': 1, 'message': '成功', 'data': []}
|
||||
logging.info('前端请求机床数据的参数为:%s' % kw)
|
||||
|
||||
# 获取当前时间的时间戳
|
||||
current_timestamp = datetime.now().timestamp()
|
||||
print(current_timestamp)
|
||||
|
||||
# tem_list = [
|
||||
# "XT-GNJC-WZZX-X800-Y550-Z550-T24-A5-1", "XT-GNJC-LSZX-X800-Y550-Z550-T24-A3-3",
|
||||
# "XT-GNJC-LSZX-X800-Y550-Z550-T24-A3-4", "XT-GNJC-LSZX-X800-Y550-Z550-T24-A3-5",
|
||||
# "XT-GNJC-LSZX-X800-Y550-Z550-T24-A3-6", "XT-GNJC-LSZX-X800-Y550-Z550-T24-A3-7",
|
||||
# "XT-GNJC-LSZX-X800-Y550-Z550-T24-A3-8", "XT-GNJC-WZZX-X800-Y550-Z550-T24-A5-2",
|
||||
# "XT-GNJC-GSZG-X600-Y400-Z350-T21-A3-9", "XT-GNJC-GSZG-X600-Y400-Z350-T21-A3-10",
|
||||
# "XT-GNJC-GSZG-X600-Y400-Z350-T21-A3-11", "XT-GNJC-GSZG-X600-Y400-Z350-T21-A3-12",
|
||||
# "XT-GNJC-GSZG-X600-Y400-Z350-T21-A3-13", "XT-GNJC-GSZG-X600-Y400-Z350-T21-A3-14"
|
||||
# ]
|
||||
try:
|
||||
equipment_obj = request.env['maintenance.equipment'].sudo()
|
||||
# 获取请求的机床数据
|
||||
machine_list = ast.literal_eval(kw['machine_list'])
|
||||
for item in machine_list:
|
||||
machine_data = equipment_obj.search([('code', '=', item)])
|
||||
|
||||
# 机床上线时间段
|
||||
first_online_duration = current_timestamp - int(machine_data.first_online_time.timestamp())
|
||||
|
||||
power_off_time = None
|
||||
power_off_rate = None
|
||||
if machine_data.machine_power_on_time:
|
||||
power_off_time = first_online_duration - convert_to_seconds(machine_data.machine_power_on_time)
|
||||
power_off_rate = round((power_off_time / first_online_duration), 3)
|
||||
else:
|
||||
power_off_time = False
|
||||
power_off_rate = False
|
||||
if machine_data:
|
||||
res['data'].append({
|
||||
'active': machine_data.status,
|
||||
'id': machine_data.id,
|
||||
'name': machine_data.name,
|
||||
'brand': machine_data.type_id.name,
|
||||
'code': machine_data.code,
|
||||
'status': machine_data.status,
|
||||
'run_status': machine_data.run_status,
|
||||
'run_time': machine_data.run_time,
|
||||
'system_date': machine_data.system_date,
|
||||
'system_time': machine_data.system_time,
|
||||
'cut_time': machine_data.cut_time,
|
||||
'cut_status': machine_data.cut_status,
|
||||
'program': machine_data.program,
|
||||
'program_name': machine_data.program_name,
|
||||
'program_status': machine_data.program_status,
|
||||
'tool_num': machine_data.tool_num,
|
||||
'machine_power_on_time': machine_data.machine_power_on_time,
|
||||
'product_counts': machine_data.product_counts,
|
||||
'mode': machine_data.mode,
|
||||
'start_time': machine_data.start_time,
|
||||
'end_time': machine_data.end_time,
|
||||
'program_start_time': machine_data.program_start_time,
|
||||
'program_end_time': machine_data.program_end_time,
|
||||
'standby_start_time': machine_data.standby_start_time,
|
||||
'standby_end_time': machine_data.standby_end_time,
|
||||
'offline_start_time': machine_data.offline_start_time,
|
||||
'offline_end_time': machine_data.offline_end_time,
|
||||
'emg_status': machine_data.emg_status,
|
||||
'current_program': machine_data.current_program,
|
||||
'current_program_seq': machine_data.current_program_seq,
|
||||
'x_abs_pos': machine_data.x_abs_pos,
|
||||
'y_abs_pos': machine_data.y_abs_pos,
|
||||
'z_abs_pos': machine_data.z_abs_pos,
|
||||
'feed_speed_set': machine_data.feed_speed_set,
|
||||
'act_feed_speed': machine_data.act_feed_speed,
|
||||
'spindle_speed_set': machine_data.spindle_speed_set,
|
||||
'act_spindle_speed': machine_data.act_spindle_speed,
|
||||
'spindle_load': machine_data.spindle_load,
|
||||
'x_axis_load': machine_data.x_axis_load,
|
||||
'y_axis_load': machine_data.y_axis_load,
|
||||
'z_axis_load': machine_data.z_axis_load,
|
||||
'rapid_feed': machine_data.rapid_feed,
|
||||
'feed_rate': machine_data.feed_rate,
|
||||
'x_mach_coord': machine_data.x_mach_coord,
|
||||
'y_mach_coord': machine_data.y_mach_coord,
|
||||
'z_mach_coord': machine_data.z_mach_coord,
|
||||
'x_rel_coord': machine_data.x_rel_coord,
|
||||
'y_rel_coord': machine_data.y_rel_coord,
|
||||
'z_rel_coord': machine_data.z_rel_coord,
|
||||
'x_dis_coord': machine_data.x_dis_coord,
|
||||
'y_dis_coord': machine_data.y_dis_coord,
|
||||
'z_dis_coord': machine_data.z_dis_coord,
|
||||
'alarm_time': machine_data.alarm_time,
|
||||
'alarm_msg': machine_data.alarm_msg,
|
||||
'clear_time': machine_data.clear_time,
|
||||
# 计算出来的数据
|
||||
# 开动率:运行时间/通电时间
|
||||
'run_rate': machine_data.run_rate,
|
||||
# 关机时长:初次上线时间 - 通电时间
|
||||
'power_off_time': power_off_time,
|
||||
# 关机率:关机时长/初次上线时间
|
||||
'power_off_rate': power_off_rate,
|
||||
'first_online_duration': first_online_duration,
|
||||
# 停机时间:关机时间 - 运行时间
|
||||
# 停机时长:关机时间 - 初次上线时间
|
||||
'img': f'data:image/png;base64,{machine_data.machine_tool_picture.decode("utf-8")}',
|
||||
'equipment_type': machine_data.category_id.name,
|
||||
})
|
||||
|
||||
return json.dumps(res)
|
||||
except Exception as e:
|
||||
logging.info('前端请求机床数据失败,原因:%s' % e)
|
||||
res['status'] = -1
|
||||
res['message'] = '前端请求机床数据失败,原因:%s' % e
|
||||
return json.JSONEncoder().encode(res)
|
||||
|
||||
@http.route('/api/logs/list', type='http', auth='public', methods=['GET', 'POST'], csrf=False, cors="*")
|
||||
def logs_list(self, **kw):
|
||||
"""
|
||||
拿到日志数据返回给大屏展示
|
||||
:param kw:
|
||||
:return:
|
||||
"""
|
||||
res = {'status': 1, 'message': '成功', 'data': {}}
|
||||
logging.info('前端请求日志数据的参数为:%s' % kw)
|
||||
|
||||
try:
|
||||
# 连接数据库
|
||||
conn = psycopg2.connect(**db_config)
|
||||
cur = conn.cursor()
|
||||
machine_list = ast.literal_eval(kw['machine_list'])
|
||||
begin_time_str = kw['begin_time'].strip('"')
|
||||
end_time_str = kw['end_time'].strip('"')
|
||||
|
||||
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')
|
||||
|
||||
print('begin_time: %s' % begin_time)
|
||||
|
||||
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;
|
||||
'''
|
||||
# 执行SQL命令,使用参数绑定
|
||||
cur.execute(sql, (item, begin_time, end_time))
|
||||
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) # 注意使用 json.dumps 而不是直接用 json.JSONEncoder().encode()
|
||||
|
||||
except Exception as e:
|
||||
logging.info('前端请求日志数据失败,原因:%s' % e)
|
||||
res['status'] = -1
|
||||
res['message'] = '前端请求日志数据失败,原因:%s' % e
|
||||
return json.dumps(res)
|
||||
|
||||
# 返回CNC机床列表
|
||||
@http.route('/api/CNCList', type='http', auth='public', methods=['GET', 'POST'], csrf=False,
|
||||
cors="*")
|
||||
def CNCList(self, **kw):
|
||||
"""
|
||||
获取CNC机床列表
|
||||
:param kw:
|
||||
:return:
|
||||
"""
|
||||
|
||||
# logging.info('CNCList:%s' % kw)
|
||||
try:
|
||||
res = {'Succeed': True}
|
||||
# cnc_list = request.env['sf.cnc.equipment'].sudo().search([])
|
||||
# cnc_list = ["XT-GNJC-WZZX-X800-Y550-Z550-T24-A5-1", "XT-GNJC-LSZX-X800-Y550-Z550-T24-A3-3",
|
||||
# "XT-GNJC-LSZX-X800-Y550-Z550-T24-A3-4", "XT-GNJC-LSZX-X800-Y550-Z550-T24-A3-5",
|
||||
# "XT-GNJC-LSZX-X800-Y550-Z550-T24-A3-6", "XT-GNJC-LSZX-X800-Y550-Z550-T24-A3-7",
|
||||
# "XT-GNJC-LSZX-X800-Y550-Z550-T24-A3-8", "XT-GNJC-WZZX-X800-Y550-Z550-T24-A5-2",
|
||||
# "XT-GNJC-GSZG-X600-Y400-Z350-T21-A3-9", "XT-GNJC-GSZG-X600-Y400-Z350-T21-A3-10",
|
||||
# "XT-GNJC-GSZG-X600-Y400-Z350-T21-A3-11", "XT-GNJC-GSZG-X600-Y400-Z350-T21-A3-12",
|
||||
# "XT-GNJC-GSZG-X600-Y400-Z350-T21-A3-13", "XT-GNJC-GSZG-X600-Y400-Z350-T21-A3-14"]
|
||||
|
||||
cnc_list_obj = request.env['maintenance.equipment'].sudo().search(
|
||||
[('function_type', '!=', False), ('active', '=', True)])
|
||||
cnc_list = list(map(lambda x: x.code, cnc_list_obj))
|
||||
print('cnc_list: %s' % cnc_list)
|
||||
res['CNCList'] = cnc_list
|
||||
|
||||
except Exception as e:
|
||||
res = {'Succeed': False, 'ErrorCode': 202, 'Error': e}
|
||||
logging.info('CNCList error:%s' % e)
|
||||
return json.JSONEncoder().encode(res)
|
||||
|
||||
# 返回产线列表
|
||||
|
||||
@http.route('/api/LineList', type='http', auth='public', methods=['GET', 'POST'], csrf=False,
|
||||
|
||||
cors="*")
|
||||
def LineList(self, **kw):
|
||||
"""
|
||||
获取产线列表
|
||||
:param kw:
|
||||
:return:
|
||||
"""
|
||||
|
||||
try:
|
||||
res = {'Succeed': True}
|
||||
line_list_obj = request.env['sf.production.line'].sudo().search([('name', 'ilike', 'CNC')])
|
||||
line_list = list(map(lambda x: x.name, line_list_obj))
|
||||
print('line_list: %s' % line_list)
|
||||
res['LineList'] = line_list
|
||||
|
||||
except Exception as e:
|
||||
res = {'Succeed': False, 'ErrorCode': 202, 'Error': e}
|
||||
logging.info('LineList error:%s' % e)
|
||||
|
||||
return json.JSONEncoder().encode(res)
|
||||
|
||||
# 获取产线产量相关
|
||||
|
||||
@http.route('/api/LineProduct', type='http', auth='public', methods=['GET', 'POST'], csrf=False,
|
||||
|
||||
cors="*")
|
||||
def LineProduct(self, **kw):
|
||||
"""
|
||||
获取产线产量相关
|
||||
:param kw:
|
||||
:return:
|
||||
"""
|
||||
res = {'status': 1, 'message': '成功', 'data': {}}
|
||||
logging.info('前端请求产线产量数据的参数为:%s' % kw)
|
||||
|
||||
try:
|
||||
plan_obj = request.env['sf.production.plan'].sudo()
|
||||
line_list = ast.literal_eval(kw['line_list'])
|
||||
print('line_list: %s' % 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_finish_counts = plan_obj.search_count(
|
||||
[('production_line_id.name', '=', line), ('state', 'in', ['finished'])])
|
||||
# 工单计划量
|
||||
plan_data_plan_counts = plan_obj.search_count(
|
||||
[('production_line_id.name', '=', line), ('state', 'not in', ['finished'])])
|
||||
# 工单不良累计
|
||||
plan_data_fault_counts = plan_obj.search_count(
|
||||
[('production_line_id.name', '=', line), ('production_id.state', 'in', ['scrap', 'cancel'])])
|
||||
|
||||
# 工单返工数量
|
||||
|
||||
plan_data_rework_counts = plan_obj.search_count(
|
||||
[('production_line_id.name', '=', line), ('production_id.state', 'in', ['rework'])])
|
||||
|
||||
# 工单完成率
|
||||
finishe_rate = round(
|
||||
(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
|
||||
|
||||
if plan_data:
|
||||
data = {
|
||||
'plan_data_total_counts': plan_data_total_counts,
|
||||
'plan_data_finish_counts': plan_data_finish_counts,
|
||||
'plan_data_plan_counts': plan_data_plan_counts,
|
||||
'plan_data_fault_counts': plan_data_fault_counts,
|
||||
'finishe_rate': finishe_rate,
|
||||
'plan_data_progress_deviation': plan_data_progress_deviation,
|
||||
'plan_data_rework_counts': plan_data_rework_counts
|
||||
}
|
||||
res['data'][line] = data
|
||||
|
||||
return json.dumps(res) # 注意使用 json.dumps 而不是直接用 json.JSONEncoder().encode()
|
||||
|
||||
except Exception as e:
|
||||
logging.info('前端请求产线产量数据失败,原因:%s' % e)
|
||||
res['status'] = -1
|
||||
res['message'] = '前端请求产线产量数据失败,原因:%s' % e
|
||||
return json.dumps(res)
|
||||
|
||||
# 日完成量统计
|
||||
@http.route('/api/DailyFinishCount', type='http', auth='public', methods=['GET', 'POST'], csrf=False, cors="*")
|
||||
def DailyFinishCount(self, **kw):
|
||||
"""
|
||||
获取日完成量统计
|
||||
:param kw:
|
||||
:return:
|
||||
"""
|
||||
res = {'status': 1, 'message': '成功', 'data': {}}
|
||||
plan_obj = request.env['sf.production.plan'].sudo()
|
||||
line_list = ast.literal_eval(kw['line_list'])
|
||||
begin_time_str = kw['begin_time'].strip('"')
|
||||
end_time_str = kw['end_time'].strip('"')
|
||||
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')
|
||||
print('line_list: %s' % line_list)
|
||||
|
||||
def get_date_list(start_date, end_date):
|
||||
date_list = []
|
||||
current_date = start_date
|
||||
while current_date <= end_date:
|
||||
date_list.append(current_date)
|
||||
current_date += timedelta(days=1)
|
||||
return date_list
|
||||
|
||||
for line in line_list:
|
||||
date_list = get_date_list(begin_time, end_time)
|
||||
order_counts = []
|
||||
|
||||
date_field_name = 'actual_end_time' # 替换为你模型中的实际字段名
|
||||
|
||||
for date in date_list:
|
||||
next_day = date + timedelta(days=1)
|
||||
orders = plan_obj.search([('production_line_id.name', '=', line), ('state', 'in', ['finished']),
|
||||
(date_field_name, '>=', date.strftime('%Y-%m-%d 00:00:00')),
|
||||
(date_field_name, '<', next_day.strftime('%Y-%m-%d 00:00:00'))
|
||||
])
|
||||
|
||||
rework_orders = plan_obj.search(
|
||||
[('production_line_id.name', '=', line), ('production_id.state', 'in', ['rework']),
|
||||
(date_field_name, '>=', date.strftime('%Y-%m-%d 00:00:00')),
|
||||
(date_field_name, '<', next_day.strftime('%Y-%m-%d 00:00:00'))
|
||||
])
|
||||
not_passed_orders = plan_obj.search(
|
||||
[('production_line_id.name', '=', line), ('production_id.state', 'in', ['scrap', 'cancel']),
|
||||
(date_field_name, '>=', date.strftime('%Y-%m-%d 00:00:00')),
|
||||
(date_field_name, '<', next_day.strftime('%Y-%m-%d 00:00:00'))
|
||||
])
|
||||
order_counts.append({
|
||||
'date': date.strftime('%Y-%m-%d'),
|
||||
'order_count': len(orders),
|
||||
'rework_orders': len(rework_orders),
|
||||
'not_passed_orders': len(not_passed_orders)
|
||||
})
|
||||
# 外面包一层,没什么是包一层不能解决的,包一层就能区分了,类似于包一层div
|
||||
# 外面包一层的好处是,可以把多个数据结构打包在一起,方便前端处理
|
||||
|
||||
# date_list_dict = {line: order_counts}
|
||||
|
||||
res['data'][line] = order_counts
|
||||
return json.dumps(res)
|
||||
|
||||
# 实时产量
|
||||
@http.route('/api/RealTimeProduct', type='http', auth='public', methods=['GET', 'POST'], csrf=False, cors="*")
|
||||
def RealTimeProduct(self, **kw):
|
||||
"""
|
||||
获取实时产量
|
||||
:param kw:
|
||||
:return:
|
||||
"""
|
||||
res = {'status': 1, 'message': '成功', 'data': {}}
|
||||
plan_obj = request.env['sf.production.plan'].sudo()
|
||||
line_list = ast.literal_eval(kw['line_list'])
|
||||
begin_time_str = kw['begin_time'].strip('"')
|
||||
end_time_str = kw['end_time'].strip('"')
|
||||
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')
|
||||
|
||||
def get_hourly_intervals(start_time, end_time):
|
||||
intervals = []
|
||||
current_time = start_time
|
||||
while current_time < end_time:
|
||||
next_hour = current_time + timedelta(hours=1)
|
||||
intervals.append((current_time, min(next_hour, end_time)))
|
||||
current_time = next_hour
|
||||
return intervals
|
||||
|
||||
# 当班计划量
|
||||
for line in line_list:
|
||||
plan_order_nums = plan_obj.search_count(
|
||||
[('production_line_id.name', '=', line), ('state', 'not in', ['draft']),
|
||||
('date_planned_start', '>=', begin_time),
|
||||
('date_planned_start', '<', end_time)
|
||||
])
|
||||
finish_order_nums = plan_obj.search_count(
|
||||
[('production_line_id.name', '=', line), ('state', 'in', ['finished']),
|
||||
('date_planned_start', '>=', begin_time),
|
||||
('date_planned_start', '<', end_time)
|
||||
])
|
||||
hourly_intervals = get_hourly_intervals(begin_time, end_time)
|
||||
production_counts = []
|
||||
|
||||
for start, end in hourly_intervals:
|
||||
orders = plan_obj.search([
|
||||
('actual_end_time', '>=', start.strftime('%Y-%m-%d %H:%M:%S')),
|
||||
('actual_end_time', '<', end.strftime('%Y-%m-%d %H:%M:%S')),
|
||||
('production_line_id.name', '=', line)
|
||||
])
|
||||
production_counts.append({
|
||||
'start_time': start.strftime('%Y-%m-%d %H:%M:%S'),
|
||||
'end_time': end.strftime('%Y-%m-%d %H:%M:%S'),
|
||||
'production_count': len(orders)
|
||||
})
|
||||
production_counts_dict = {'production_counts': production_counts,
|
||||
'plan_order_nums': plan_order_nums,
|
||||
'finish_order_nums': finish_order_nums,
|
||||
}
|
||||
|
||||
res['data'][line] = production_counts_dict
|
||||
# res['data'].append({line: production_counts})
|
||||
return json.dumps(res)
|
||||
|
||||
# 工单明细
|
||||
@http.route('/api/OrderDetail', type='http', auth='public', methods=['GET', 'POST'], csrf=False, cors="*")
|
||||
def OrderDetail(self, **kw):
|
||||
"""
|
||||
获取工单明细
|
||||
:param kw:
|
||||
:return:
|
||||
"""
|
||||
|
||||
# res = {'status': 1, 'message': '成功', 'not_done_data': [], 'done_data': []}
|
||||
res = {'status': 1, 'message': '成功', 'data': {}}
|
||||
plan_obj = request.env['sf.production.plan'].sudo()
|
||||
line_list = ast.literal_eval(kw['line_list'])
|
||||
begin_time_str = kw['begin_time'].strip('"')
|
||||
end_time_str = kw['end_time'].strip('"')
|
||||
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')
|
||||
print('line_list: %s' % line_list)
|
||||
not_done_data = []
|
||||
done_data = []
|
||||
final_data = {}
|
||||
|
||||
for line in line_list:
|
||||
# 未完成订单
|
||||
not_done_orders = plan_obj.search(
|
||||
[('production_line_id.name', '=', line), ('state', 'not in', ['finished'])])
|
||||
print(not_done_orders)
|
||||
# 完成订单
|
||||
finish_orders = plan_obj.search([('production_line_id.name', '=', line), ('state', 'in', ['finished'])])
|
||||
print(finish_orders)
|
||||
|
||||
# 获取所有未完成订单的ID列表
|
||||
order_ids = [order.id for order in not_done_orders]
|
||||
# 获取所有已完成订单的ID列表
|
||||
finish_order_ids = [order.id for order in finish_orders]
|
||||
|
||||
# 对ID进行排序
|
||||
sorted_order_ids = sorted(order_ids)
|
||||
|
||||
finish_sorted_order_ids = sorted(finish_order_ids)
|
||||
|
||||
# 创建ID与序号的对应关系
|
||||
id_to_sequence = {order_id: index + 1 for index, order_id in enumerate(sorted_order_ids)}
|
||||
|
||||
finish_id_to_sequence = {order_id: index + 1 for index, order_id in enumerate(finish_sorted_order_ids)}
|
||||
|
||||
# # 输出结果或进一步处理
|
||||
# for order_id, sequence in id_to_sequence.items():
|
||||
# print(f"Order ID: {order_id} - Sequence: {sequence}")
|
||||
|
||||
for order in not_done_orders:
|
||||
blank_name = ''
|
||||
try:
|
||||
blank_name = order.production_id.move_raw_ids[0].product_id.name
|
||||
except:
|
||||
continue
|
||||
# blank_name = 'R-S00109-1 [碳素结构钢 Q235-118.0 * 72.0 * 21.0]'
|
||||
# 正则表达式
|
||||
material_pattern = r'\[(.*?)-' # 从 [ 开始,碰到 - 停止
|
||||
dimensions = blank_name.split('-')[-1].split(']')[0]
|
||||
|
||||
# 匹配材料名称
|
||||
material_match = re.search(material_pattern, blank_name)
|
||||
material = material_match.group(1) if material_match else 'No match found'
|
||||
|
||||
state_dict = {
|
||||
'draft': '待排程',
|
||||
'done': '已排程',
|
||||
'processing': '生产中',
|
||||
'finished': '已完成'
|
||||
}
|
||||
|
||||
line_dict = {
|
||||
'sequence': id_to_sequence[order.id],
|
||||
'workorder_name': order.name,
|
||||
'blank_name': blank_name,
|
||||
'material': material,
|
||||
'dimensions': dimensions,
|
||||
'order_qty': order.product_qty,
|
||||
'state': state_dict[order.state],
|
||||
|
||||
}
|
||||
not_done_data.append(line_dict)
|
||||
|
||||
for finish_order in finish_orders:
|
||||
blank_name = ''
|
||||
try:
|
||||
blank_name = finish_order.production_id.move_raw_ids[0].product_id.name
|
||||
except:
|
||||
continue
|
||||
|
||||
material_pattern = r'\[(.*?)-' # 从 [ 开始,碰到 - 停止
|
||||
dimensions = blank_name.split('-')[-1].split(']')[0]
|
||||
|
||||
# 匹配材料名称
|
||||
material_match = re.search(material_pattern, blank_name)
|
||||
material = material_match.group(1) if material_match else 'No match found'
|
||||
|
||||
line_dict = {
|
||||
'sequence': finish_id_to_sequence[finish_order.id],
|
||||
'workorder_name': finish_order.name,
|
||||
'blank_name': blank_name,
|
||||
'material': material,
|
||||
'dimensions': dimensions,
|
||||
'order_qty': finish_order.product_qty,
|
||||
'finish_time': finish_order.actual_end_time.strftime('%Y-%m-%d %H:%M:%S'),
|
||||
|
||||
}
|
||||
done_data.append(line_dict)
|
||||
|
||||
# 开始包一层
|
||||
res['data'][line] = {'not_done_data': not_done_data, 'done_data': done_data}
|
||||
return json.dumps(res)
|
||||
|
||||
# 查询pg库来获得待机次数
|
||||
@http.route('/api/IdleAlarmCount', type='http', auth='public', methods=['GET', 'POST'], csrf=False, cors="*")
|
||||
def idle_alarm_count(self, **kw):
|
||||
"""
|
||||
查询设备的待机次数
|
||||
"""
|
||||
res = {'status': 1, 'message': '成功', 'data': {}}
|
||||
logging.info('前端请求机床数据的参数为:%s' % kw)
|
||||
|
||||
# 连接数据库
|
||||
conn = psycopg2.connect(**db_config)
|
||||
cur = conn.cursor()
|
||||
try:
|
||||
# 获取请求的机床数据
|
||||
machine_list = ast.literal_eval(kw['machine_list'])
|
||||
total_alarm_time = 0
|
||||
alarm_count_num = 0
|
||||
for item in machine_list:
|
||||
sql = '''
|
||||
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
|
||||
ORDER BY idle_start_time, time
|
||||
) subquery;
|
||||
'''
|
||||
|
||||
sql2 = '''
|
||||
SELECT DISTINCT ON (alarm_time) alarm_time, alarm_repair_time
|
||||
FROM device_data
|
||||
WHERE device_name = %s AND alarm_time IS NOT NULL
|
||||
ORDER BY alarm_time, time;
|
||||
|
||||
'''
|
||||
# 执行SQL命令
|
||||
cur.execute(sql, (item,))
|
||||
result = cur.fetchall()
|
||||
print('result========', result)
|
||||
|
||||
cur.execute(sql2, (item,))
|
||||
result2 = cur.fetchall()
|
||||
print('result2========', result2)
|
||||
#
|
||||
for row in result:
|
||||
res['data'][item] = {'idle_count': row[0]}
|
||||
alarm_count = []
|
||||
for row in result2:
|
||||
alarm_count.append(row[0])
|
||||
total_alarm_time += abs(float(row[0]))
|
||||
if len(list(set(alarm_count))) == 1:
|
||||
if list(set(alarm_count))[0] is None:
|
||||
alarm_count_num = 0
|
||||
else:
|
||||
alarm_count_num = 1
|
||||
else:
|
||||
alarm_count_num = len(list(set(alarm_count)))
|
||||
res['data'][item]['total_alarm_time'] = total_alarm_time / 3600
|
||||
res['data'][item]['alarm_count_num'] = alarm_count_num
|
||||
|
||||
# 返回统计结果
|
||||
return json.dumps(res)
|
||||
except Exception as e:
|
||||
print(f"An error occurred: {e}")
|
||||
return json.dumps(res)
|
||||
finally:
|
||||
cur.close()
|
||||
conn.close()
|
||||
|
||||
# 查询pg库来获得异常情况
|
||||
@http.route('/api/alarm/logs', type='http', auth='public', methods=['GET', 'POST'], csrf=False, cors="*")
|
||||
def alarm_logs(self, **kw):
|
||||
"""
|
||||
查询设备的异常情况
|
||||
"""
|
||||
res = {'status': 1, 'message': '成功', 'data': {}}
|
||||
logging.info('前端请求机床数据的参数为:%s' % kw)
|
||||
|
||||
# 连接数据库
|
||||
conn = psycopg2.connect(**db_config)
|
||||
cur = conn.cursor()
|
||||
try:
|
||||
# 获取请求的机床数据
|
||||
# machine_list = ast.literal_eval(kw['machine_list'])
|
||||
# idle_times = []
|
||||
# idle_dict = {}
|
||||
|
||||
# for item in machine_list:
|
||||
sql = '''
|
||||
SELECT DISTINCT ON (alarm_time) alarm_time, alarm_message, system_date, system_time, alarm_repair_time
|
||||
FROM device_data
|
||||
WHERE alarm_time IS NOT NULL
|
||||
ORDER BY alarm_time, time;
|
||||
|
||||
'''
|
||||
# 执行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:
|
||||
print(f"An error occurred: {e}")
|
||||
return json.dumps(res)
|
||||
finally:
|
||||
cur.close()
|
||||
conn.close()
|
||||
|
||||
# 设备oee
|
||||
@http.route('/api/OEE', type='http', auth='public', methods=['GET', 'POST'], csrf=False, cors="*")
|
||||
def OEE(self, **kw):
|
||||
"""
|
||||
获取产线等oee
|
||||
"""
|
||||
res = {'status': 1, 'message': '成功', 'data': {}}
|
||||
logging.info('前端请求oee数据的参数为:%s' % kw)
|
||||
|
||||
try:
|
||||
count_oee = 1
|
||||
workcenter_obj = request.env['mrp.workcenter'].sudo()
|
||||
workcenter_list = ast.literal_eval(kw['workcenter_list'])
|
||||
print('workcenter_list: %s' % workcenter_list)
|
||||
for line in workcenter_list:
|
||||
res['data'][line] = workcenter_obj.search([('name', '=', line)]).oee
|
||||
count_oee *= workcenter_obj.search([('name', '=', line)]).oee
|
||||
res['data']['综合oee'] = count_oee / 1000000
|
||||
except Exception as e:
|
||||
print(f"An error occurred: {e}")
|
||||
|
||||
return json.dumps(res)
|
||||
|
||||
# # 查询某段时间的设备oee
|
||||
# @http.route('/api/OEEByTime', type='http', auth='public', methods=['GET', 'POST'], csrf=False, cors="*")
|
||||
# def OEEByTime(self, **kw):
|
||||
# """
|
||||
# 获取某段时间的oee
|
||||
# """
|
||||
# res = {'status': 1, 'message': '成功', 'data': {}}
|
||||
# logging.info('前端请求获取某段时间的oee的参数为:%s' % kw)
|
||||
# workcenter_list = ast.literal_eval(kw['workcenter_list'])
|
||||
# begin_time_str = kw['begin_time'].strip('"')
|
||||
# end_time_str = kw['end_time'].strip('"')
|
||||
# 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')
|
||||
# print('workcenter_list: %s' % workcenter_list)
|
||||
# # 连接数据库
|
||||
# conn = psycopg2.connect(**db_config)
|
||||
# cur = conn.cursor()
|
||||
# # 查询并计算OEE平均值
|
||||
# oee_data = {}
|
||||
# for workcenter in workcenter_list:
|
||||
# cur.execute("""
|
||||
# SELECT AVG(oee) as avg_oee
|
||||
# FROM oee_data
|
||||
# WHERE workcenter_name = %s
|
||||
# AND time BETWEEN %s AND %s
|
||||
# """, (workcenter, begin_time, end_time))
|
||||
#
|
||||
# result = cur.fetchone()
|
||||
# avg_oee = result[0] if result else 0.0
|
||||
# oee_data[workcenter] = avg_oee
|
||||
#
|
||||
# # 返回数据
|
||||
# res['data'] = oee_data
|
||||
# return json.dumps(res)
|
||||
|
||||
@http.route('/api/OEEByTime', type='http', auth='public', methods=['GET', 'POST'], csrf=False, cors="*")
|
||||
def OEEByTime(self, **kw):
|
||||
"""
|
||||
获取某段时间的OEE,根据用户指定的时间单位(day或hour)返回对应的平均值。
|
||||
如果不传time_unit,则默认按天返回,并补全没有数据的时间段,填充0值。
|
||||
"""
|
||||
res = {'status': 1, 'message': '成功', 'data': {}}
|
||||
logging.info('前端请求获取某段时间的OEE的参数为:%s' % kw)
|
||||
|
||||
# 获取并解析参数
|
||||
workcenter_list = ast.literal_eval(kw['workcenter_list'])
|
||||
begin_time_str = kw['begin_time'].strip('"')
|
||||
end_time_str = kw['end_time'].strip('"')
|
||||
time_unit = kw.get('time_unit', 'day') # 默认单位为天
|
||||
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')
|
||||
|
||||
# 连接数据库
|
||||
conn = psycopg2.connect(**db_config)
|
||||
cur = conn.cursor()
|
||||
|
||||
# 根据时间单位选择不同的时间格式
|
||||
if time_unit == 'hour':
|
||||
time_format = 'YYYY-MM-DD HH24:00:00'
|
||||
time_delta = timedelta(hours=1)
|
||||
else: # 默认为'day'
|
||||
time_format = 'YYYY-MM-DD'
|
||||
time_delta = timedelta(days=1)
|
||||
|
||||
# 查询并计算OEE平均值
|
||||
oee_data = {}
|
||||
for workcenter in workcenter_list:
|
||||
cur.execute(f"""
|
||||
SELECT to_char(time, '{time_format}') as time_unit, AVG(oee) as avg_oee
|
||||
FROM oee_data
|
||||
WHERE workcenter_name = %s
|
||||
AND time BETWEEN %s AND %s
|
||||
GROUP BY time_unit
|
||||
ORDER BY time_unit
|
||||
""", (workcenter, begin_time, end_time))
|
||||
|
||||
results = cur.fetchall()
|
||||
# 初始化当前产线的OEE数据字典
|
||||
workcenter_oee = {row[0]: row[1] for row in results}
|
||||
|
||||
# 补全缺失的时间段
|
||||
current_time = begin_time
|
||||
if time_unit != 'hour':
|
||||
while current_time <= end_time:
|
||||
time_key = current_time.strftime('%Y-%m-%d')
|
||||
if time_key not in workcenter_oee:
|
||||
workcenter_oee[time_key] = 0
|
||||
current_time += time_delta
|
||||
|
||||
# 按时间排序
|
||||
oee_data[workcenter] = dict(sorted(workcenter_oee.items()))
|
||||
|
||||
# 关闭数据库连接
|
||||
cur.close()
|
||||
conn.close()
|
||||
|
||||
# 返回数据
|
||||
res['data'] = oee_data
|
||||
return json.dumps(res)
|
||||
@@ -2,3 +2,4 @@ from . import ftp_client
|
||||
from . import ftp_operate
|
||||
from . import py2opcua
|
||||
from . import res_config_setting
|
||||
from . import mrp_workorder
|
||||
|
||||
@@ -121,130 +121,189 @@ class Machine_ftp(models.Model):
|
||||
"""
|
||||
_inherit = 'maintenance.equipment'
|
||||
|
||||
# 机床首次上线时间(默认取值2024年08月01日零点)
|
||||
|
||||
def _get_default_online_time(self):
|
||||
return datetime(2024, 1, 1, 0, 0, 0)
|
||||
|
||||
first_online_time = fields.Datetime(string='首次上线时间', default=_get_default_online_time)
|
||||
|
||||
# workorder_ids = fields.One2many('mrp.workorder', 'machine_tool_id', string='工单')
|
||||
|
||||
# 机床配置项目
|
||||
# ftp相关
|
||||
ftp_num = fields.Char('ftp账号')
|
||||
ftp_pwd = fields.Char('ftp密码')
|
||||
ftp_host = fields.Char('ftp地址')
|
||||
ftp_port = fields.Integer('ftp端口')
|
||||
ftp_remote_path = fields.Char('机床ftp路径')
|
||||
# 补偿值写入相关
|
||||
x_compensation_node = fields.Char('x补偿值节点')
|
||||
y_compensation_node = fields.Char('y补偿值节点')
|
||||
# 数采配置相关
|
||||
machine_ip = fields.Char('机床IP')
|
||||
machine_signed = fields.Char('机床刷新间隔')
|
||||
machine_status = fields.Char('机床在线状态')
|
||||
machine_cnc_type = fields.Char('机床CNC型号')
|
||||
machine_axis_count = fields.Char('机床轴总数')
|
||||
machine_run_status = fields.Char('机床运行状态')
|
||||
machine_emg_status = fields.Char('机床急停状态')
|
||||
machine_cut_status = fields.Char('机床当前切削状态')
|
||||
machine_mode = fields.Char('机床当前操作模式')
|
||||
machine_spindle_load = fields.Char('机床主轴负载')
|
||||
machine_x_mach = fields.Char('机床X轴机械坐标')
|
||||
machine_x_abs_mach = fields.Char('机床X轴当前位置')
|
||||
machine_x_rel_mach = fields.Char('机床X轴相对工件坐标')
|
||||
machine_x_dis_mach = fields.Char('机床X轴目标距离')
|
||||
machine_x_axis_load = fields.Char('机床X轴伺服轴负载')
|
||||
machine_y_mach = fields.Char('机床Y轴机械坐标')
|
||||
machine_y_abs_mach = fields.Char('机床Y轴当前位置')
|
||||
machine_y_rel_mach = fields.Char('机床Y轴相对工件坐标')
|
||||
machine_y_dis_mach = fields.Char('机床Y轴目标距离')
|
||||
machine_y_axis_load = fields.Char('机床Y轴伺服轴负载')
|
||||
machine_z_mach = fields.Char('机床Z轴机械坐标')
|
||||
machine_z_abs_mach = fields.Char('机床Z轴当前位置')
|
||||
machine_z_rel_mach = fields.Char('机床Z轴相对工件坐标')
|
||||
machine_z_dis_mach = fields.Char('机床Z轴目标距离')
|
||||
machine_z_axis_load = fields.Char('机床Z轴伺服轴负载')
|
||||
machine_tool_num = fields.Char('机床当前刀位号')
|
||||
machine_program = fields.Char('机床主程序名称')
|
||||
machine_current_prg = fields.Char('机床当前执行指令')
|
||||
machine_prg_seq = fields.Char('机床当前执行语句号')
|
||||
machine_spindle_speed_set = fields.Char('机床设定主轴速度')
|
||||
machine_act_spindle_speed = fields.Char('机床实际主轴转速')
|
||||
machine_feed_speed_set = fields.Char('机床设定进给速度')
|
||||
machine_act_feed_speed = fields.Char('机床实际进给速度')
|
||||
machine_spindle_feed = fields.Char('机床主轴倍率')
|
||||
machine_feed_rate = fields.Char('机床进给倍率')
|
||||
machine_rapid_feed = fields.Char('机床快速移动倍率')
|
||||
machine_run_time = fields.Char('机床运行时间')
|
||||
machine_cut_time = fields.Char('机床切削时间')
|
||||
machine_keep_alive_time = fields.Char('机床上电时间')
|
||||
machine_circle_time = fields.Char('机床循环时间')
|
||||
machine_product_counts = fields.Char('机床加工件数')
|
||||
machine_system_date = fields.Char('机床系统日期')
|
||||
machine_system_time = fields.Char('机床系统时间')
|
||||
machine_alarm_msg = fields.Char('机床系统报警')
|
||||
# # 机床配置项目
|
||||
# # ftp相关
|
||||
# ftp_num = fields.Char('ftp账号')
|
||||
# ftp_pwd = fields.Char('ftp密码')
|
||||
# ftp_host = fields.Char('ftp地址')
|
||||
# ftp_port = fields.Integer('ftp端口')
|
||||
# ftp_remote_path = fields.Char('机床ftp路径')
|
||||
# # 补偿值写入相关
|
||||
# x_compensation_node = fields.Char('x补偿值节点')
|
||||
# y_compensation_node = fields.Char('y补偿值节点')
|
||||
# # 数采配置相关
|
||||
# machine_ip = fields.Char('机床IP')
|
||||
# machine_signed = fields.Char('机床刷新间隔')
|
||||
# machine_status = fields.Char('机床在线状态')
|
||||
# machine_cnc_type = fields.Char('机床CNC型号')
|
||||
# machine_axis_count = fields.Char('机床轴总数')
|
||||
# machine_run_status = fields.Char('机床运行状态')
|
||||
# machine_emg_status = fields.Char('机床急停状态')
|
||||
# machine_cut_status = fields.Char('机床当前切削状态')
|
||||
# machine_mode = fields.Char('机床当前操作模式')
|
||||
# machine_spindle_load = fields.Char('机床主轴负载')
|
||||
# machine_x_mach = fields.Char('机床X轴机械坐标')
|
||||
# machine_x_abs_mach = fields.Char('机床X轴当前位置')
|
||||
# machine_x_rel_mach = fields.Char('机床X轴相对工件坐标')
|
||||
# machine_x_dis_mach = fields.Char('机床X轴目标距离')
|
||||
# machine_x_axis_load = fields.Char('机床X轴伺服轴负载')
|
||||
# machine_y_mach = fields.Char('机床Y轴机械坐标')
|
||||
# machine_y_abs_mach = fields.Char('机床Y轴当前位置')
|
||||
# machine_y_rel_mach = fields.Char('机床Y轴相对工件坐标')
|
||||
# machine_y_dis_mach = fields.Char('机床Y轴目标距离')
|
||||
# machine_y_axis_load = fields.Char('机床Y轴伺服轴负载')
|
||||
# machine_z_mach = fields.Char('机床Z轴机械坐标')
|
||||
# machine_z_abs_mach = fields.Char('机床Z轴当前位置')
|
||||
# machine_z_rel_mach = fields.Char('机床Z轴相对工件坐标')
|
||||
# machine_z_dis_mach = fields.Char('机床Z轴目标距离')
|
||||
# machine_z_axis_load = fields.Char('机床Z轴伺服轴负载')
|
||||
# machine_tool_num = fields.Char('机床当前刀位号')
|
||||
# machine_program = fields.Char('机床主程序名称')
|
||||
# machine_current_prg = fields.Char('机床当前执行指令')
|
||||
# machine_prg_seq = fields.Char('机床当前执行语句号')
|
||||
# machine_spindle_speed_set = fields.Char('机床设定主轴速度')
|
||||
# machine_act_spindle_speed = fields.Char('机床实际主轴转速')
|
||||
# machine_feed_speed_set = fields.Char('机床设定进给速度')
|
||||
# machine_act_feed_speed = fields.Char('机床实际进给速度')
|
||||
# machine_spindle_feed = fields.Char('机床主轴倍率')
|
||||
# machine_feed_rate = fields.Char('机床进给倍率')
|
||||
# machine_rapid_feed = fields.Char('机床快速移动倍率')
|
||||
# machine_run_time = fields.Char('机床运行时间')
|
||||
# machine_cut_time = fields.Char('机床切削时间')
|
||||
# machine_keep_alive_time = fields.Char('机床上电时间')
|
||||
# machine_circle_time = fields.Char('机床循环时间')
|
||||
# machine_product_counts = fields.Char('机床加工件数')
|
||||
# machine_system_date = fields.Char('机床系统日期')
|
||||
# machine_system_time = fields.Char('机床系统时间')
|
||||
# machine_alarm_msg = fields.Char('机床系统报警')
|
||||
|
||||
# 刀位配置
|
||||
tool_num1 = fields.Char('刀位1')
|
||||
tool_num2 = fields.Char('刀位2')
|
||||
tool_num3 = fields.Char('刀位3')
|
||||
tool_num4 = fields.Char('刀位4')
|
||||
tool_num5 = fields.Char('刀位5')
|
||||
tool_num6 = fields.Char('刀位6')
|
||||
tool_num7 = fields.Char('刀位7')
|
||||
tool_num8 = fields.Char('刀位8')
|
||||
tool_num9 = fields.Char('刀位9')
|
||||
tool_num10 = fields.Char('刀位10')
|
||||
tool_num11 = fields.Char('刀位11')
|
||||
tool_num12 = fields.Char('刀位12')
|
||||
tool_num13 = fields.Char('刀位13')
|
||||
tool_num14 = fields.Char('刀位14')
|
||||
tool_num15 = fields.Char('刀位15')
|
||||
tool_num16 = fields.Char('刀位16')
|
||||
tool_num17 = fields.Char('刀位17')
|
||||
tool_num18 = fields.Char('刀位18')
|
||||
tool_num19 = fields.Char('刀位19')
|
||||
tool_num20 = fields.Char('刀位20')
|
||||
tool_num21 = fields.Char('刀位21')
|
||||
tool_num22 = fields.Char('刀位22')
|
||||
tool_num23 = fields.Char('刀位23')
|
||||
tool_num24 = fields.Char('刀位24')
|
||||
# # 刀位配置
|
||||
# tool_num1 = fields.Char('刀位1')
|
||||
# tool_num2 = fields.Char('刀位2')
|
||||
# tool_num3 = fields.Char('刀位3')
|
||||
# tool_num4 = fields.Char('刀位4')
|
||||
# tool_num5 = fields.Char('刀位5')
|
||||
# tool_num6 = fields.Char('刀位6')
|
||||
# tool_num7 = fields.Char('刀位7')
|
||||
# tool_num8 = fields.Char('刀位8')
|
||||
# tool_num9 = fields.Char('刀位9')
|
||||
# tool_num10 = fields.Char('刀位10')
|
||||
# tool_num11 = fields.Char('刀位11')
|
||||
# tool_num12 = fields.Char('刀位12')
|
||||
# tool_num13 = fields.Char('刀位13')
|
||||
# tool_num14 = fields.Char('刀位14')
|
||||
# tool_num15 = fields.Char('刀位15')
|
||||
# tool_num16 = fields.Char('刀位16')
|
||||
# tool_num17 = fields.Char('刀位17')
|
||||
# tool_num18 = fields.Char('刀位18')
|
||||
# tool_num19 = fields.Char('刀位19')
|
||||
# tool_num20 = fields.Char('刀位20')
|
||||
# tool_num21 = fields.Char('刀位21')
|
||||
# tool_num22 = fields.Char('刀位22')
|
||||
# tool_num23 = fields.Char('刀位23')
|
||||
# tool_num24 = fields.Char('刀位24')
|
||||
|
||||
# 机床采集项目
|
||||
timestamp = fields.Datetime('时间戳', readonly=True)
|
||||
signed = fields.Integer('刷新间隔', readonly=True)
|
||||
status = fields.Boolean('在线状态', readonly=True)
|
||||
time_on = fields.Char('总在线时长', readonly=True)
|
||||
time_on_now = fields.Char('本次在线时长', readonly=True)
|
||||
tool_num = fields.Integer('当前刀具', readonly=True)
|
||||
program = fields.Char('当前程序', readonly=True)
|
||||
run_status = fields.Selection([('0', '空闲中'), ('1', '加工中'), ('2', '加工中'), ('3', '加工中')], string='运行状态',
|
||||
readonly=True, default='0')
|
||||
run_time = fields.Char('总运行时长', readonly=True)
|
||||
cut_time = fields.Char('总切削时长', readonly=True)
|
||||
cut_status = fields.Selection([('0', '未切削'), ('1', '切削中'), ('2', '切削中'), ('3', '切削中')], string='切削状态',
|
||||
readonly=True, default='0')
|
||||
spindle_speed = fields.Char('主轴转速', readonly=True)
|
||||
status = fields.Boolean('机床在线状态', readonly=True)
|
||||
# run_status = fields.Selection([('0', '空闲中'), ('1', '加工中'), ('2', '加工中'), ('3', '加工中')], string='机床运行状态',
|
||||
# readonly=True, default='0')
|
||||
run_status = fields.Char('机床运行状态', readonly=True)
|
||||
run_time = fields.Char('机床累计运行时长', readonly=True)
|
||||
# 机床系统日期
|
||||
system_date = fields.Char('机床系统日期', readonly=True)
|
||||
# 机床系统时间
|
||||
system_time = fields.Char('机床系统时间', readonly=True)
|
||||
cut_time = fields.Char('机床累计切削时间', readonly=True)
|
||||
# cut_status = fields.Selection([('0', '未切削'), ('1', '切削中'), ('2', '切削中'), ('3', '切削中')], string='机床当前切削状态',
|
||||
# readonly=True, default='0')
|
||||
cut_status = fields.Char('机床当前切削状态', readonly=True)
|
||||
# 当前程序名
|
||||
program = fields.Char('机床当前程序', readonly=True)
|
||||
# 当前刀具号
|
||||
tool_num = fields.Integer('机床当前刀具号', readonly=True)
|
||||
# 机床通电开机时间, 机床加工件数, 机床当前操作模式, 开始加工时间, 结束加工时间, 加工程序开始时间, 加工程序结束时间, 待机开始时间,
|
||||
# 待机结束时间, 机床离线开始时间, 机床离线结束时间, 机床急停状态, 机床主程序名称, 程序运行的状态, 机床当前执行指令, 机床当前执行语句号
|
||||
# 机床X轴当前位置, 机床Y轴当前位置, 机床Z轴当前位置
|
||||
machine_power_on_time = fields.Char('机床通电开机时间', readonly=True)
|
||||
product_counts = fields.Char('机床加工件数', readonly=True)
|
||||
mode = fields.Char('机床当前操作模式', readonly=True)
|
||||
start_time = fields.Char('开始加工时间', readonly=True)
|
||||
end_time = fields.Char('结束加工时间', readonly=True)
|
||||
program_start_time = fields.Char('加工程序开始时间', readonly=True)
|
||||
program_end_time = fields.Char('加工程序结束时间', readonly=True)
|
||||
standby_start_time = fields.Char('待机开始时间', readonly=True)
|
||||
standby_end_time = fields.Char('待机结束时间', readonly=True)
|
||||
offline_start_time = fields.Char('机床离线开始时间', readonly=True)
|
||||
offline_end_time = fields.Char('机床离线结束时间', readonly=True)
|
||||
emg_status = fields.Char('机床急停状态', readonly=True)
|
||||
program_name = fields.Char('机床主程序名称', readonly=True)
|
||||
program_status = fields.Char('程序运行状态', readonly=True)
|
||||
current_program = fields.Char('机床当前执行指令', readonly=True)
|
||||
current_program_seq = fields.Char('机床当前执行语句号', readonly=True)
|
||||
x_abs_pos = fields.Char('机床X轴当前位置(mm)', readonly=True)
|
||||
y_abs_pos = fields.Char('机床Y轴当前位置(mm)', readonly=True)
|
||||
z_abs_pos = fields.Char('机床Z轴当前位置(mm)', readonly=True)
|
||||
|
||||
tool_num_process_time1 = fields.Char('刀位1', readonly=True, default='0')
|
||||
tool_num_process_time2 = fields.Char('刀位2', readonly=True, default='0')
|
||||
tool_num_process_time3 = fields.Char('刀位3', readonly=True, default='0')
|
||||
tool_num_process_time4 = fields.Char('刀位4', readonly=True, default='0')
|
||||
tool_num_process_time5 = fields.Char('刀位5', readonly=True, default='0')
|
||||
tool_num_process_time6 = fields.Char('刀位6', readonly=True, default='0')
|
||||
tool_num_process_time7 = fields.Char('刀位7', readonly=True, default='0')
|
||||
tool_num_process_time8 = fields.Char('刀位8', readonly=True, default='0')
|
||||
tool_num_process_time9 = fields.Char('刀位9', readonly=True, default='0')
|
||||
tool_num_process_time10 = fields.Char('刀位10', readonly=True, default='0')
|
||||
tool_num_process_time11 = fields.Char('刀位11', readonly=True, default='0')
|
||||
tool_num_process_time12 = fields.Char('刀位12', readonly=True, default='0')
|
||||
tool_num_process_time13 = fields.Char('刀位13', readonly=True, default='0')
|
||||
tool_num_process_time14 = fields.Char('刀位14', readonly=True, default='0')
|
||||
tool_num_process_time15 = fields.Char('刀位15', readonly=True, default='0')
|
||||
tool_num_process_time16 = fields.Char('刀位16', readonly=True, default='0')
|
||||
tool_num_process_time17 = fields.Char('刀位17', readonly=True, default='0')
|
||||
tool_num_process_time18 = fields.Char('刀位18', readonly=True, default='0')
|
||||
tool_num_process_time19 = fields.Char('刀位19', readonly=True, default='0')
|
||||
tool_num_process_time20 = fields.Char('刀位20', readonly=True, default='0')
|
||||
tool_num_process_time21 = fields.Char('刀位21', readonly=True, default='0')
|
||||
tool_num_process_time22 = fields.Char('刀位22', readonly=True, default='0')
|
||||
tool_num_process_time23 = fields.Char('刀位23', readonly=True, default='0')
|
||||
tool_num_process_time24 = fields.Char('刀位24', readonly=True, default='0')
|
||||
# 机床设定进给速度, 机床实际进给速度, 机床设定主轴转速, 机床实际主轴转速, 机床主轴负载, 机床X轴伺服轴负载, 机床Y轴伺服轴负载
|
||||
# 机床Z轴伺服轴负载, 机床快速移动倍率, 机床进给倍率, 机床X轴机械坐标, 机床Y轴机械坐标, 机床Z轴机械坐标, 机床X轴相对工件坐标
|
||||
# 机床Y轴相对工件坐标, 机床Z轴相对工件坐标, 机床X轴目标距离, 机床Y轴目标距离, 机床Z轴目标距离
|
||||
feed_speed_set = fields.Char('机床设定进给速度(mm/min)', readonly=True)
|
||||
act_feed_speed = fields.Char('机床实际进给速度(mm/min)', readonly=True)
|
||||
spindle_speed_set = fields.Char('机床设定主轴转速(r/min)', readonly=True)
|
||||
act_spindle_speed = fields.Char('机床实际主轴转速(r/min)', readonly=True)
|
||||
spindle_load = fields.Char('机床主轴负载(%)', readonly=True)
|
||||
x_axis_load = fields.Char('机床X轴伺服轴负载(%)', readonly=True)
|
||||
y_axis_load = fields.Char('机床Y轴伺服轴负载(%)', readonly=True)
|
||||
z_axis_load = fields.Char('机床Z轴伺服轴负载(%)', readonly=True)
|
||||
rapid_feed = fields.Char('机床快速移动倍率(%)', readonly=True)
|
||||
feed_rate = fields.Char('机床进给倍率(%)', readonly=True)
|
||||
x_mach_coord = fields.Char('机床X轴机械坐标(mm)', readonly=True)
|
||||
y_mach_coord = fields.Char('机床Y轴机械坐标(mm)', readonly=True)
|
||||
z_mach_coord = fields.Char('机床Z轴机械坐标(mm)', readonly=True)
|
||||
x_rel_coord = fields.Char('机床X轴相对工件坐标(mm)', readonly=True)
|
||||
y_rel_coord = fields.Char('机床Y轴相对工件坐标(mm)', readonly=True)
|
||||
z_rel_coord = fields.Char('机床Z轴相对工件坐标(mm)', readonly=True)
|
||||
x_dis_coord = fields.Char('机床X轴目标距离(mm)', readonly=True)
|
||||
y_dis_coord = fields.Char('机床Y轴目标距离(mm)', readonly=True)
|
||||
z_dis_coord = fields.Char('机床Z轴目标距离(mm)', readonly=True)
|
||||
|
||||
# 故障报警时间, 故障报警信息, 故障消除时间(复原时间)
|
||||
alarm_time = fields.Char('故障报警时间', readonly=True)
|
||||
alarm_msg = fields.Char('故障报警信息', readonly=True)
|
||||
clear_time = 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):
|
||||
@@ -259,39 +318,59 @@ class WorkCenterBarcode(models.Model):
|
||||
button_compensation_state = fields.Boolean(string='是否已经补偿', readonly=True)
|
||||
button_up_all_state = fields.Boolean(string='是否已经全部下发', readonly=True)
|
||||
machine_tool_id = fields.Many2one('sf.machine_tool.type', string='机床')
|
||||
machine_tool_name = fields.Char(string='机床名称', default='未知机床', compute='_run_info', readonly=True)
|
||||
machine_tool_type_id = fields.Char(string='机床型号', default='未知型号', compute='_run_info', readonly=True)
|
||||
machine_tool_status = fields.Boolean(string='在线状态', compute='_run_info', readonly=True)
|
||||
# machine_tool_name = fields.Char(string='机床名称', default='未知机床', compute='_run_info', readonly=True)
|
||||
# machine_tool_type_id = fields.Char(string='机床型号', default='未知型号', compute='_run_info', readonly=True)
|
||||
# machine_tool_status = fields.Boolean(string='在线状态', compute='_run_info', readonly=True)
|
||||
# machine_tool_run_status = fields.Selection([('0', '关机中'), ('1', '加工中'), ('2', '加工中'), ('3', '加工中')],
|
||||
# string='运行状态', compute='_run_info', readonly=True, default='0')
|
||||
# machine_tool_timestamp = fields.Datetime('时间戳', compute='_run_info', readonly=True)
|
||||
# machine_tool_time_on = fields.Char('总在线时长', compute='_run_info', readonly=True)
|
||||
# machine_tool_time_on_now = fields.Char('本次在线时长', compute='_run_info', readonly=True)
|
||||
# machine_tool_tool_num = fields.Integer('当前刀具', compute='_run_info', readonly=True)
|
||||
# machine_tool_program = fields.Char('当前程序', compute='_run_info', readonly=True)
|
||||
# machine_tool_machine_ip = fields.Char('机床IP', compute='_run_info', readonly=True)
|
||||
# machine_tool_cut_status = fields.Selection([('0', '未切削'), ('1', '切削中'), ('2', '切削中'), ('3', '切削中')],
|
||||
# string='切削状态', compute='_run_info', readonly=True, default='0')
|
||||
# machine_tool_compensation_value_x = fields.Char('x补偿值', compute='_run_info', readonly=True)
|
||||
# machine_tool_compensation_value_y = fields.Char('y补偿值', compute='_run_info', readonly=True)
|
||||
|
||||
machine_tool_name = fields.Char(string='机床名称', default='未知机床', readonly=True)
|
||||
machine_tool_type_id = fields.Char(string='机床型号', default='未知型号', readonly=True)
|
||||
machine_tool_status = fields.Boolean(string='在线状态', readonly=True)
|
||||
machine_tool_run_status = fields.Selection([('0', '关机中'), ('1', '加工中'), ('2', '加工中'), ('3', '加工中')],
|
||||
string='运行状态', compute='_run_info', readonly=True, default='0')
|
||||
machine_tool_timestamp = fields.Datetime('时间戳', compute='_run_info', readonly=True)
|
||||
machine_tool_time_on = fields.Char('总在线时长', compute='_run_info', readonly=True)
|
||||
machine_tool_time_on_now = fields.Char('本次在线时长', compute='_run_info', readonly=True)
|
||||
machine_tool_tool_num = fields.Integer('当前刀具', compute='_run_info', readonly=True)
|
||||
machine_tool_program = fields.Char('当前程序', compute='_run_info', readonly=True)
|
||||
machine_tool_machine_ip = fields.Char('机床IP', compute='_run_info', readonly=True)
|
||||
string='运行状态', readonly=True, default='0')
|
||||
machine_tool_timestamp = fields.Datetime('时间戳', readonly=True)
|
||||
machine_tool_time_on = fields.Char('总在线时长', readonly=True)
|
||||
machine_tool_time_on_now = fields.Char('本次在线时长', readonly=True)
|
||||
machine_tool_tool_num = fields.Integer('当前刀具', readonly=True)
|
||||
machine_tool_program = fields.Char('当前程序', readonly=True)
|
||||
machine_tool_machine_ip = fields.Char('机床IP', readonly=True)
|
||||
machine_tool_cut_status = fields.Selection([('0', '未切削'), ('1', '切削中'), ('2', '切削中'), ('3', '切削中')],
|
||||
string='切削状态', compute='_run_info', readonly=True, default='0')
|
||||
machine_tool_compensation_value_x = fields.Char('x补偿值', compute='_run_info', readonly=True)
|
||||
machine_tool_compensation_value_y = fields.Char('y补偿值', compute='_run_info', readonly=True)
|
||||
string='切削状态', readonly=True, default='0')
|
||||
machine_tool_compensation_value_x = fields.Char('x补偿值', readonly=True)
|
||||
machine_tool_compensation_value_y = fields.Char('y补偿值', readonly=True)
|
||||
|
||||
# 工单状态
|
||||
|
||||
delivery_records = fields.One2many('delivery.record', 'workorder_id', string="下发记录")
|
||||
|
||||
@api.depends('equipment_id.timestamp')
|
||||
def _run_info(self):
|
||||
# self.machine_tool_name = '1号机床'
|
||||
self.machine_tool_name = self.equipment_id.name
|
||||
self.machine_tool_type_id = self.equipment_id.type_id.name
|
||||
self.machine_tool_status = self.equipment_id.status
|
||||
self.machine_tool_run_status = self.equipment_id.run_status
|
||||
self.machine_tool_timestamp = self.equipment_id.timestamp
|
||||
self.machine_tool_time_on = self.equipment_id.time_on
|
||||
self.machine_tool_time_on_now = self.equipment_id.time_on_now
|
||||
self.machine_tool_tool_num = self.equipment_id.tool_num
|
||||
self.machine_tool_program = self.equipment_id.program
|
||||
self.machine_tool_machine_ip = self.equipment_id.machine_ip
|
||||
self.machine_tool_cut_status = self.equipment_id.cut_status
|
||||
self.machine_tool_compensation_value_x = self.compensation_value_x
|
||||
self.machine_tool_compensation_value_y = self.compensation_value_y
|
||||
# self.machine_tool_name = self.equipment_id.name
|
||||
# self.machine_tool_type_id = self.equipment_id.type_id.name
|
||||
# self.machine_tool_status = self.equipment_id.status
|
||||
# self.machine_tool_run_status = self.equipment_id.run_status
|
||||
# self.machine_tool_timestamp = self.equipment_id.timestamp
|
||||
# self.machine_tool_time_on = self.equipment_id.time_on
|
||||
# self.machine_tool_time_on_now = self.equipment_id.time_on_now
|
||||
# self.machine_tool_tool_num = self.equipment_id.tool_num
|
||||
# self.machine_tool_program = self.equipment_id.program
|
||||
# self.machine_tool_machine_ip = self.equipment_id.machine_ip
|
||||
# self.machine_tool_cut_status = self.equipment_id.cut_status
|
||||
# self.machine_tool_compensation_value_x = self.compensation_value_x
|
||||
# self.machine_tool_compensation_value_y = self.compensation_value_y
|
||||
pass
|
||||
|
||||
def compensation(self):
|
||||
|
||||
|
||||
38
sf_machine_connect/models/mrp_workorder.py
Normal file
38
sf_machine_connect/models/mrp_workorder.py
Normal file
@@ -0,0 +1,38 @@
|
||||
import re
|
||||
|
||||
from odoo import fields, models, api
|
||||
|
||||
|
||||
class ResMrpWorkOrder(models.Model):
|
||||
_inherit = 'mrp.workorder'
|
||||
|
||||
mixed_search_field = fields.Char(string='坯料产品名称/RFID')
|
||||
|
||||
@api.model
|
||||
def web_read_group(self, domain, fields, groupby, limit=None, offset=0, orderby=False,
|
||||
lazy=True, expand=False, expand_limit=None, expand_orderby=False):
|
||||
domain = domain or []
|
||||
for index, item in enumerate(domain):
|
||||
if isinstance(item, list):
|
||||
if item[0] == 'mixed_search_field':
|
||||
if self._is_rfid_code(item[2]):
|
||||
domain[index] = ['rfid_code', item[1], item[2]]
|
||||
else:
|
||||
domain[index] = ['product_tmpl_name', item[1], item[2]]
|
||||
|
||||
return super(ResMrpWorkOrder, self).web_read_group(domain, fields, groupby, limit=limit, offset=offset, orderby=orderby,
|
||||
lazy=lazy, expand=expand, expand_limit=expand_limit, expand_orderby=expand_orderby)
|
||||
|
||||
def _is_rfid_code(self, tag):
|
||||
"""
|
||||
判断是否是rfid_code
|
||||
"""
|
||||
# 基于长度判断(假设RFID标签长度为10到16个字符)
|
||||
if not 10 <= len(tag) <= 16:
|
||||
return False
|
||||
|
||||
# 基于字符集判断(仅包含数字和字母)
|
||||
if not re.match("^[0-9]*$", tag):
|
||||
return False
|
||||
|
||||
return True
|
||||
@@ -8,11 +8,14 @@ _logger = logging.getLogger(__name__)
|
||||
class ResBFMConfigSettings(models.TransientModel):
|
||||
_inherit = 'res.config.settings'
|
||||
|
||||
bfm_url = fields.Selection(
|
||||
[("https://bfm.cs.jikimo.com", "开发环境(https://bfm.cs.jikimo.com)"),
|
||||
("https://bfm.t.jikimo.com", "测试环境(https://bfm.t.jikimo.com)"),
|
||||
# ("正式环境", "https://bfm.jikimo.com")], string='bfm环境', store=True)
|
||||
("https://bfm.jikimo.com", "正式环境(https://bfm.jikimo.com)")], string='bfm环境', store=True)
|
||||
# bfm_url = fields.Selection(
|
||||
# [("https://bfm.cs.jikimo.com", "开发环境(https://bfm.cs.jikimo.com)"),
|
||||
# ("https://bfm.t.jikimo.com", "测试环境(https://bfm.t.jikimo.com)"),
|
||||
# ("https://bfm.r.jikimo.com", "预发布环境(https://bfm.r.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='请输入当前对应的业务平台环境路径')
|
||||
|
||||
@api.model
|
||||
def get_values(self):
|
||||
@@ -22,14 +25,14 @@ class ResBFMConfigSettings(models.TransientModel):
|
||||
"""
|
||||
values = super(ResBFMConfigSettings, self).get_values()
|
||||
config = self.env['ir.config_parameter'].sudo()
|
||||
bfm_url = config.get_param('bfm_url', default='')
|
||||
bfm_url_new = config.get_param('bfm_url_new', default='')
|
||||
|
||||
values.update(
|
||||
bfm_url=bfm_url,
|
||||
bfm_url_new=bfm_url_new,
|
||||
)
|
||||
return values
|
||||
|
||||
def set_values(self):
|
||||
super(ResBFMConfigSettings, self).set_values()
|
||||
ir_config = self.env['ir.config_parameter'].sudo()
|
||||
ir_config.set_param("bfm_url", self.bfm_url or "")
|
||||
ir_config.set_param("bfm_url_new", self.bfm_url_new or "")
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
<filter string="自动编程" name="no_manual_quotation" domain="[('manual_quotation', '=', False)]"/>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='production_id']" position="before">
|
||||
<field name="mixed_search_field"/>
|
||||
<field name="product_tmpl_name"/>
|
||||
<field name="rfid_code"/>
|
||||
</xpath>
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
<field name="program_name"/>
|
||||
<field name="functional_tool_type_id"/>
|
||||
<field name="cutting_tool_name"/>
|
||||
<field name="tool_state"/>
|
||||
<field name="cutting_tool_no"/>
|
||||
<field name="processing_type"/>
|
||||
<field name="margin_x_y"/>
|
||||
|
||||
@@ -13,294 +13,139 @@
|
||||
<page string="机床运行状态" attrs="{'invisible': [('equipment_type', '!=', '机床')]}">
|
||||
<group string='状态监控'>
|
||||
<group>
|
||||
<field name="timestamp"/>
|
||||
<field name="signed"/>
|
||||
<!-- <field name="timestamp"/> -->
|
||||
<field name="status"/>
|
||||
<field name="time_on"/>
|
||||
<field name="time_on_now"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="run_status"/>
|
||||
<field name="run_time"/>
|
||||
<field name="system_date"/>
|
||||
<field name="first_online_time"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="cut_status"/>
|
||||
<field name="cut_time"/>
|
||||
<!-- <field name="cut_time"/> -->
|
||||
<field name="program"/>
|
||||
<field name="tool_num"/>
|
||||
<field name="spindle_speed"/>
|
||||
</group>
|
||||
</group>
|
||||
<!-- <group string='刀位统计'> -->
|
||||
<!-- <group> -->
|
||||
<!-- <group> -->
|
||||
<!-- <field name="tool_num_process_time1"/> -->
|
||||
<!-- <field name="tool_num_process_time5"/> -->
|
||||
<!-- <field name="tool_num_process_time9"/> -->
|
||||
<!-- <field name="tool_num_process_time13"/> -->
|
||||
<!-- <field name="tool_num_process_time17"/> -->
|
||||
<!-- <field name="tool_num_process_time21"/> -->
|
||||
<!-- </group> -->
|
||||
<!-- <group> -->
|
||||
<!-- <field name="tool_num_process_time2"/> -->
|
||||
<!-- <field name="tool_num_process_time6"/> -->
|
||||
<!-- <field name="tool_num_process_time10"/> -->
|
||||
<!-- <field name="tool_num_process_time14"/> -->
|
||||
<!-- <field name="tool_num_process_time18"/> -->
|
||||
<!-- <field name="tool_num_process_time22"/> -->
|
||||
<!-- </group> -->
|
||||
<!-- </group> -->
|
||||
<!-- <group> -->
|
||||
<!-- <group> -->
|
||||
<!-- <field name="tool_num_process_time3"/> -->
|
||||
<!-- <field name="tool_num_process_time7"/> -->
|
||||
<!-- <field name="tool_num_process_time11"/> -->
|
||||
<!-- <field name="tool_num_process_time15"/> -->
|
||||
<!-- <field name="tool_num_process_time19"/> -->
|
||||
<!-- <field name="tool_num_process_time23"/> -->
|
||||
<!-- </group> -->
|
||||
<!-- <group> -->
|
||||
<!-- <field name="tool_num_process_time4"/> -->
|
||||
<!-- <field name="tool_num_process_time8"/> -->
|
||||
<!-- <field name="tool_num_process_time12"/> -->
|
||||
<!-- <field name="tool_num_process_time16"/> -->
|
||||
<!-- <field name="tool_num_process_time20"/> -->
|
||||
<!-- <field name="tool_num_process_time24"/> -->
|
||||
<!-- </group> -->
|
||||
<!-- </group> -->
|
||||
<!-- </group> -->
|
||||
|
||||
<!-- <div class="o_address_format"> -->
|
||||
<!-- <label for="tool_num_process_time1" string="刀位1"/> -->
|
||||
<!-- <field name="tool_num_process_time1" class="o_form_label"/> -->
|
||||
<!-- <span>&nbsp;</span> -->
|
||||
<!-- <label for="tool_num_process_time2" string="刀位2"/> -->
|
||||
<!-- <field name="tool_num_process_time2" class="o_form_label"/> -->
|
||||
<!-- <span>&nbsp;</span> -->
|
||||
<!-- <label for="tool_num_process_time3" string="刀位3"/> -->
|
||||
<!-- <field name="tool_num_process_time3" class="o_form_label"/> -->
|
||||
<!-- <span>&nbsp;</span> -->
|
||||
<!-- <label for="tool_num_process_time4" string="刀位4"/> -->
|
||||
<!-- <field name="tool_num_process_time4" class="o_form_label"/> -->
|
||||
<!-- <div></div> -->
|
||||
<!-- <label for="tool_num_process_time5" string="刀位5"/> -->
|
||||
<!-- <field name="tool_num_process_time5" class="o_form_label"/> -->
|
||||
<!-- <span>&nbsp;</span> -->
|
||||
<!-- <label for="tool_num_process_time6" string="刀位6"/> -->
|
||||
<!-- <field name="tool_num_process_time6" class="o_form_label"/> -->
|
||||
<!-- <span>&nbsp;</span> -->
|
||||
<!-- <label for="tool_num_process_time7" string="刀位7"/> -->
|
||||
<!-- <field name="tool_num_process_time7" class="o_form_label"/> -->
|
||||
<!-- <span>&nbsp;</span> -->
|
||||
<!-- <label for="tool_num_process_time8" string="刀位8"/> -->
|
||||
<!-- <field name="tool_num_process_time8" class="o_form_label"/> -->
|
||||
<!-- <div></div> -->
|
||||
<!-- <label for="tool_num_process_time9" string="刀位9"/> -->
|
||||
<!-- <field name="tool_num_process_time9" class="o_form_label"/> -->
|
||||
<!-- <span>&nbsp;</span> -->
|
||||
<!-- <label for="tool_num_process_time10" string="刀位10"/> -->
|
||||
<!-- <field name="tool_num_process_time10" class="o_form_label"/> -->
|
||||
<!-- <span>&nbsp;</span> -->
|
||||
<!-- <label for="tool_num_process_time11" string="刀位11"/> -->
|
||||
<!-- <field name="tool_num_process_time11" class="o_form_label"/> -->
|
||||
<!-- <span>&nbsp;</span> -->
|
||||
<!-- <label for="tool_num_process_time12" string="刀位12"/> -->
|
||||
<!-- <field name="tool_num_process_time12" class="o_form_label"/> -->
|
||||
<!-- <div></div> -->
|
||||
<!-- <label for="tool_num_process_time13" string="刀位13"/> -->
|
||||
<!-- <field name="tool_num_process_time13" class="o_form_label"/> -->
|
||||
<!-- <span>&nbsp;</span> -->
|
||||
<!-- <label for="tool_num_process_time14" string="刀位14"/> -->
|
||||
<!-- <field name="tool_num_process_time14" class="o_form_label"/> -->
|
||||
<!-- <span>&nbsp;</span> -->
|
||||
<!-- <label for="tool_num_process_time15" string="刀位15"/> -->
|
||||
<!-- <field name="tool_num_process_time15" class="o_form_label"/> -->
|
||||
<!-- <span>&nbsp;</span> -->
|
||||
<!-- <label for="tool_num_process_time16" string="刀位16"/> -->
|
||||
<!-- <field name="tool_num_process_time16" class="o_form_label"/> -->
|
||||
<!-- <div></div> -->
|
||||
<!-- <label for="tool_num_process_time17" string="刀位17"/> -->
|
||||
<!-- <field name="tool_num_process_time17" class="o_form_label"/> -->
|
||||
<!-- <span>&nbsp;</span> -->
|
||||
<!-- <label for="tool_num_process_time18" string="刀位18"/> -->
|
||||
<!-- <field name="tool_num_process_time18" class="o_form_label"/> -->
|
||||
<!-- <span>&nbsp;</span> -->
|
||||
<!-- <label for="tool_num_process_time19" string="刀位19"/> -->
|
||||
<!-- <field name="tool_num_process_time19" class="o_form_label"/> -->
|
||||
<!-- <span>&nbsp;</span> -->
|
||||
<!-- <label for="tool_num_process_time20" string="刀位20"/> -->
|
||||
<!-- <field name="tool_num_process_time20" class="o_form_label"/> -->
|
||||
<!-- <div></div> -->
|
||||
<!-- <label for="tool_num_process_time21" string="刀位21"/> -->
|
||||
<!-- <field name="tool_num_process_time21" class="o_form_label"/> -->
|
||||
<!-- <span>&nbsp;</span> -->
|
||||
<!-- <label for="tool_num_process_time22" string="刀位22"/> -->
|
||||
<!-- <field name="tool_num_process_time22" class="o_form_label"/> -->
|
||||
<!-- <span>&nbsp;</span> -->
|
||||
<!-- <label for="tool_num_process_time23" string="刀位23"/> -->
|
||||
<!-- <field name="tool_num_process_time23" class="o_form_label"/> -->
|
||||
<!-- <span>&nbsp;</span> -->
|
||||
<!-- <label for="tool_num_process_time24" string="刀位24"/> -->
|
||||
<!-- <field name="tool_num_process_time24" class="o_form_label"/> -->
|
||||
<!-- </div> -->
|
||||
<!-- </group> -->
|
||||
</page>
|
||||
<page string="机床运行数据" attrs="{'invisible': [('equipment_type', '!=', '机床')]}">
|
||||
<!-- <group string="机床配置">--> -->
|
||||
<group string="ftp相关">
|
||||
<group>
|
||||
<field name="ftp_num"/>
|
||||
<field name="ftp_pwd"/>
|
||||
<field name="ftp_host"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="ftp_port"/>
|
||||
<field name="ftp_remote_path"/>
|
||||
</group>
|
||||
<group string="运行数据">
|
||||
<group>
|
||||
<field name="machine_power_on_time"/>
|
||||
<field name="product_counts"/>
|
||||
<field name="mode"/>
|
||||
<field name="start_time"/>
|
||||
<field name="end_time"/>
|
||||
<field name="program_start_time"/>
|
||||
<field name="program_end_time"/>
|
||||
<field name="standby_start_time"/>
|
||||
<field name="standby_end_time"/>
|
||||
<field name="offline_start_time"/>
|
||||
<field name="offline_end_time"/>
|
||||
<field name="emg_status"/>
|
||||
<field name="program_name"/>
|
||||
<field name="program_status"/>
|
||||
<field name="current_program"/>
|
||||
<field name="current_program_seq"/>
|
||||
<field name="x_abs_pos"/>
|
||||
<field name="y_abs_pos"/>
|
||||
<field name="z_abs_pos"/>
|
||||
</group>
|
||||
<group string="补偿值相关">
|
||||
<group>
|
||||
<field name="x_compensation_node"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="y_compensation_node"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="feed_speed_set"/>
|
||||
<field name="act_feed_speed"/>
|
||||
<field name="spindle_speed_set"/>
|
||||
<field name="act_spindle_speed"/>
|
||||
<field name="spindle_load"/>
|
||||
<field name="x_axis_load"/>
|
||||
<field name="y_axis_load"/>
|
||||
<field name="z_axis_load"/>
|
||||
<field name="rapid_feed"/>
|
||||
<field name="feed_rate"/>
|
||||
<field name="x_mach_coord"/>
|
||||
<field name="y_mach_coord"/>
|
||||
<field name="z_mach_coord"/>
|
||||
<field name="x_rel_coord"/>
|
||||
<field name="y_rel_coord"/>
|
||||
<field name="z_rel_coord"/>
|
||||
<field name="x_dis_coord"/>
|
||||
<field name="y_dis_coord"/>
|
||||
<field name="z_dis_coord"/>
|
||||
</group>
|
||||
<group string="数采相关">
|
||||
<group>
|
||||
<field name="machine_ip"/>
|
||||
<field name="machine_signed"/>
|
||||
<field name="machine_status"/>
|
||||
<field name="machine_cnc_type"/>
|
||||
<field name="machine_axis_count"/>
|
||||
<field name="machine_run_status"/>
|
||||
<field name="machine_emg_status"/>
|
||||
<field name="machine_cut_status"/>
|
||||
<field name="machine_mode"/>
|
||||
<field name="machine_spindle_load"/>
|
||||
<field name="machine_x_mach"/>
|
||||
<field name="machine_x_abs_mach"/>
|
||||
<field name="machine_x_rel_mach"/>
|
||||
<field name="machine_x_dis_mach"/>
|
||||
<field name="machine_x_axis_load"/>
|
||||
<field name="machine_y_mach"/>
|
||||
<field name="machine_y_abs_mach"/>
|
||||
<field name="machine_y_rel_mach"/>
|
||||
<field name="machine_y_dis_mach"/>
|
||||
<field name="machine_y_axis_load"/>
|
||||
<field name="machine_z_mach"/>
|
||||
<field name="machine_z_abs_mach"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="machine_z_rel_mach"/>
|
||||
<field name="machine_z_dis_mach"/>
|
||||
<field name="machine_z_axis_load"/>
|
||||
<field name="machine_tool_num"/>
|
||||
<field name="machine_program"/>
|
||||
<field name="machine_current_prg"/>
|
||||
<field name="machine_prg_seq"/>
|
||||
<field name="machine_spindle_speed_set"/>
|
||||
<field name="machine_act_spindle_speed"/>
|
||||
<field name="machine_feed_speed_set"/>
|
||||
<field name="machine_act_feed_speed"/>
|
||||
<field name="machine_spindle_feed"/>
|
||||
<field name="machine_feed_rate"/>
|
||||
<field name="machine_rapid_feed"/>
|
||||
<field name="machine_run_time"/>
|
||||
<field name="machine_cut_time"/>
|
||||
<field name="machine_keep_alive_time"/>
|
||||
<field name="machine_circle_time"/>
|
||||
<field name="machine_product_counts"/>
|
||||
<field name="machine_system_date"/>
|
||||
<field name="machine_system_time"/>
|
||||
<field name="machine_alarm_msg"/>
|
||||
</group>
|
||||
</group>
|
||||
<!-- </group> -->
|
||||
</page>
|
||||
<!-- <page string="刀位配置"> -->
|
||||
<!-- <group string="刀位配置"> -->
|
||||
</group>
|
||||
<!-- <group string="ftp相关"> -->
|
||||
<!-- <group> -->
|
||||
<!-- <group> -->
|
||||
<!-- <field name="tool_num1"/> -->
|
||||
<!-- <field name="tool_num5"/> -->
|
||||
<!-- <field name="tool_num9"/> -->
|
||||
<!-- <field name="tool_num13"/> -->
|
||||
<!-- <field name="tool_num17"/> -->
|
||||
<!-- <field name="tool_num21"/> -->
|
||||
<!-- </group> -->
|
||||
<!-- <group> -->
|
||||
<!-- <field name="tool_num2"/> -->
|
||||
<!-- <field name="tool_num6"/> -->
|
||||
<!-- <field name="tool_num10"/> -->
|
||||
<!-- <field name="tool_num14"/> -->
|
||||
<!-- <field name="tool_num18"/> -->
|
||||
<!-- <field name="tool_num22"/> -->
|
||||
<!-- </group> -->
|
||||
<!-- <field name="ftp_num"/> -->
|
||||
<!-- <field name="ftp_pwd"/> -->
|
||||
<!-- <field name="ftp_host"/> -->
|
||||
<!-- </group> -->
|
||||
<!-- <group> -->
|
||||
<!-- <group> -->
|
||||
<!-- <field name="tool_num3"/> -->
|
||||
<!-- <field name="tool_num7"/> -->
|
||||
<!-- <field name="tool_num11"/> -->
|
||||
<!-- <field name="tool_num15"/> -->
|
||||
<!-- <field name="tool_num19"/> -->
|
||||
<!-- <field name="tool_num23"/> -->
|
||||
<!-- </group> -->
|
||||
<!-- <group> -->
|
||||
<!-- <field name="tool_num4"/> -->
|
||||
<!-- <field name="tool_num8"/> -->
|
||||
<!-- <field name="tool_num12"/> -->
|
||||
<!-- <field name="tool_num16"/> -->
|
||||
<!-- <field name="tool_num20"/> -->
|
||||
<!-- <field name="tool_num24"/> -->
|
||||
<!-- </group> -->
|
||||
<!-- <field name="ftp_port"/> -->
|
||||
<!-- <field name="ftp_remote_path"/> -->
|
||||
<!-- </group> -->
|
||||
<!-- </group> -->
|
||||
<!-- </page> -->
|
||||
<!-- </notebook> -->
|
||||
<!-- <group string="补偿值相关"> -->
|
||||
<!-- <group> -->
|
||||
<!-- <field name="x_compensation_node"/> -->
|
||||
<!-- </group> -->
|
||||
<!-- <group> -->
|
||||
<!-- <field name="y_compensation_node"/> -->
|
||||
<!-- </group> -->
|
||||
<!-- </group> -->
|
||||
<!-- <group string="数采相关"> -->
|
||||
<!-- <group> -->
|
||||
<!-- <field name="machine_ip"/> -->
|
||||
<!-- <field name="machine_signed"/> -->
|
||||
<!-- <field name="machine_status"/> -->
|
||||
<!-- <field name="machine_cnc_type"/> -->
|
||||
<!-- <field name="machine_axis_count"/> -->
|
||||
<!-- <field name="machine_run_status"/> -->
|
||||
<!-- <field name="machine_emg_status"/> -->
|
||||
<!-- <field name="machine_cut_status"/> -->
|
||||
<!-- <field name="machine_mode"/> -->
|
||||
<!-- <field name="machine_spindle_load"/> -->
|
||||
<!-- <field name="machine_x_mach"/> -->
|
||||
<!-- <field name="machine_x_abs_mach"/> -->
|
||||
<!-- <field name="machine_x_rel_mach"/> -->
|
||||
<!-- <field name="machine_x_dis_mach"/> -->
|
||||
<!-- <field name="machine_x_axis_load"/> -->
|
||||
<!-- <field name="machine_y_mach"/> -->
|
||||
<!-- <field name="machine_y_abs_mach"/> -->
|
||||
<!-- <field name="machine_y_rel_mach"/> -->
|
||||
<!-- <field name="machine_y_dis_mach"/> -->
|
||||
<!-- <field name="machine_y_axis_load"/> -->
|
||||
<!-- <field name="machine_z_mach"/> -->
|
||||
<!-- <field name="machine_z_abs_mach"/> -->
|
||||
<!-- </group> -->
|
||||
<!-- <group> -->
|
||||
<!-- <field name="machine_z_rel_mach"/> -->
|
||||
<!-- <field name="machine_z_dis_mach"/> -->
|
||||
<!-- <field name="machine_z_axis_load"/> -->
|
||||
<!-- <field name="machine_tool_num"/> -->
|
||||
<!-- <field name="machine_program"/> -->
|
||||
<!-- <field name="machine_current_prg"/> -->
|
||||
<!-- <field name="machine_prg_seq"/> -->
|
||||
<!-- <field name="machine_spindle_speed_set"/> -->
|
||||
<!-- <field name="machine_act_spindle_speed"/> -->
|
||||
<!-- <field name="machine_feed_speed_set"/> -->
|
||||
<!-- <field name="machine_act_feed_speed"/> -->
|
||||
<!-- <field name="machine_spindle_feed"/> -->
|
||||
<!-- <field name="machine_feed_rate"/> -->
|
||||
<!-- <field name="machine_rapid_feed"/> -->
|
||||
<!-- <field name="machine_run_time"/> -->
|
||||
<!-- <field name="machine_cut_time"/> -->
|
||||
<!-- <field name="machine_keep_alive_time"/> -->
|
||||
<!-- <field name="machine_circle_time"/> -->
|
||||
<!-- <field name="machine_product_counts"/> -->
|
||||
<!-- <field name="machine_system_date"/> -->
|
||||
<!-- <field name="machine_system_time"/> -->
|
||||
<!-- <field name="machine_alarm_msg"/> -->
|
||||
<!-- </group> -->
|
||||
<!-- </group> -->
|
||||
</page>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- 刀具寿命 -->
|
||||
<!-- <record id="view_cutting_tool_inherited" model="ir.ui.view"> -->
|
||||
<!-- <field name="name">sf_cutting_tool_extension</field> -->
|
||||
<!-- <field name="model">sf.cutting_tool.type</field> -->
|
||||
<!-- <field name="inherit_id" ref="sf_base.form_sf_machine_tool_type"/> -->
|
||||
<!-- <field name="arch" type="xml"> -->
|
||||
<!-- <xpath expr="//form//group" position="after"> -->
|
||||
<!-- <group string='刀具寿命'> -->
|
||||
<!-- <group> -->
|
||||
<!-- <field name="total_cut_time"/> -->
|
||||
<!-- <field name="tool_position"/> -->
|
||||
|
||||
<!-- </group> -->
|
||||
<!-- <group> -->
|
||||
<!-- <field name="predict_life_time"/> -->
|
||||
|
||||
<!-- </group> -->
|
||||
|
||||
<!-- <div> -->
|
||||
|
||||
<!-- <div> -->
|
||||
<!-- <field name='is_connect_tool_position' invisible='1'/> -->
|
||||
<!-- <button string="绑定刀位" name="tool_connect_machine" type="object" confirm="是否确认绑定此刀位" -->
|
||||
<!-- class="btn-primary"/> -->
|
||||
<!-- attrs='{"invisible": [("is_connect_tool_position","!=", -->
|
||||
<!-- "False")]}' -->
|
||||
<!-- <span> </span> -->
|
||||
<!-- <button string="解绑刀位" name="tool_unconnect_machine" type="object" confirm="是否解绑此刀位" -->
|
||||
<!-- class="btn-primary"/> -->
|
||||
<!-- attrs='{"invisible": [("is_connect_tool_position","!=", -->
|
||||
<!-- "False")]}' -->
|
||||
|
||||
<!-- </div> -->
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- </div> -->
|
||||
<!-- </group> -->
|
||||
<!-- </xpath> -->
|
||||
<!-- </field> -->
|
||||
<!-- </record> -->
|
||||
</odoo>
|
||||
|
||||
17
sf_machine_connect/views/maintenance_views.xml
Normal file
17
sf_machine_connect/views/maintenance_views.xml
Normal file
@@ -0,0 +1,17 @@
|
||||
<?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>
|
||||
@@ -14,8 +14,8 @@
|
||||
<div class="o_setting_left_pane"/>
|
||||
<div class="o_setting_right_pane">
|
||||
<div class="text-muted">
|
||||
<label for="bfm_url" />
|
||||
<field name="bfm_url" string="访问地址"/>
|
||||
<label for="bfm_url_new" />
|
||||
<field name="bfm_url_new" string="业务平台访问地址"/>
|
||||
</div>
|
||||
</div>
|
||||
<!-- </div> -->
|
||||
|
||||
@@ -10,21 +10,21 @@ class SfMaintenanceLogs(models.Model):
|
||||
name = fields.Char(string='名称')
|
||||
type = fields.Selection([('type1', '类型1'), ('type2', '类型2')], string='类型')
|
||||
brand = fields.Many2one('sf.machine.brand', related='maintenance_equipment_id.brand_id', string='品牌')
|
||||
maintenance_equipment_id = fields.Many2one('maintenance.equipment', string='设备')
|
||||
maintenance_equipment_id = fields.Many2one('maintenance.equipment', string='机台号')
|
||||
maintenance_equipment_oee_id = fields.Many2one('maintenance.equipment.oee', string='设备oee')
|
||||
code_location = fields.Char(string='编码位置')
|
||||
fault_type = fields.Selection(
|
||||
[('电气类', '电气类'), ('机械类', '机械类'), ('程序类', '程序类'), ('系统类', '系统类')], string='故障类型')
|
||||
fault_code = fields.Char(string='故障代码')
|
||||
fault_alarm_info = fields.Char(string='故障报警信息')
|
||||
fault_alarm_info = fields.Text(string='故障报警信息')
|
||||
alarm_level = fields.Selection([('一级', '一级(严重)'), ('二级', '二级(中等)'), ('三级', '三级(轻微)')],
|
||||
string='报警级别')
|
||||
alarm_time = fields.Datetime(string='报警时间')
|
||||
alarm_time = fields.Datetime(string='故障报警时间')
|
||||
alarm_way = fields.Selection([('文本提示报警', '文本提示报警'), ('声光报警', '声光报警'), ('图文报警', '图文报警')],
|
||||
string='报警方式')
|
||||
fault_process = fields.Text(string='故障处理方法')
|
||||
operator = fields.Many2one('res.users', string='处理人')
|
||||
recovery_time = fields.Datetime(string='复原时间')
|
||||
recovery_time = fields.Datetime(string='故障消除时间')
|
||||
fault_duration = fields.Float(string='故障时长')
|
||||
note = fields.Text(string='备注')
|
||||
active = fields.Boolean('Active', default=True)
|
||||
|
||||
@@ -7,20 +7,23 @@ class SfMaintenanceEquipmentOEE(models.Model):
|
||||
_description = '设备OEE'
|
||||
|
||||
name = fields.Char('设备oee')
|
||||
equipment_id = fields.Many2one('maintenance.equipment', '设备',
|
||||
equipment_id = fields.Many2one('maintenance.equipment', '机台号',
|
||||
domain="[('category_id.equipment_type', '=', '机床'),('state_zc', '=', '已注册')]")
|
||||
type_id = fields.Many2one('sf.machine_tool.type', '型号', related='equipment_id.type_id')
|
||||
machine_tool_picture = fields.Binary('设备图片', related='equipment_id.machine_tool_picture')
|
||||
machine_tool_picture = fields.Binary('设备图片', related='equipment_id.machine_tool_picture')
|
||||
state = fields.Selection(
|
||||
[("正常", "正常"), ("故障停机", "故障停机"), ("计划维保", "计划维保"), ("空闲", "空闲"),
|
||||
("封存(报废)", "封存(报废)")],
|
||||
default='正常', string="机床状态", related='equipment_id.state')
|
||||
run_time = fields.Float('正常运行总时长(h)')
|
||||
equipment_time = fields.Float('总时长(h)')
|
||||
done_nums = fields.Integer('累计加工总件数')
|
||||
utilization_rate = fields.Char('开动率')
|
||||
fault_time = fields.Float('故障停机总时长(h)')
|
||||
run_time = fields.Float('加工时长(h)')
|
||||
equipment_time = fields.Float('开机时长(h)')
|
||||
done_nums = fields.Integer('加工件数')
|
||||
utilization_rate = fields.Char('可用率')
|
||||
fault_time = fields.Float('故障时长')
|
||||
fault_nums = fields.Integer('故障次数')
|
||||
# 故障率
|
||||
fault_rate = fields.Char('故障率')
|
||||
# 设备故障日志
|
||||
sf_maintenance_logs_ids = fields.One2many('sf.maintenance.logs', 'maintenance_equipment_oee_id', '设备故障日志',
|
||||
related='equipment_id.sf_maintenance_logs_ids')
|
||||
oee_logs = fields.One2many('maintenance.equipment.oee.logs', 'equipment_oee_id', string='运行日志')
|
||||
@@ -38,12 +41,58 @@ class SfMaintenanceEquipmentOEELog(models.Model):
|
||||
_name = 'maintenance.equipment.oee.logs'
|
||||
_description = '设备运行日志'
|
||||
|
||||
name = fields.Char('运行日志')
|
||||
run_time = fields.Datetime('时间')
|
||||
state = fields.Selection([("开机", "开机"), ("关机", "关机"), ("等待", "等待"), ("开始加工", "开始加工"),
|
||||
("结束加工", "结束加工"), ("故障", "故障"),
|
||||
("检修", "检修"), ("保养", "保养")], default="", string="事件/状态")
|
||||
workorder_id = fields.Char('加工订单')
|
||||
time = fields.Char('持续时长')
|
||||
color = fields.Char('颜色', default=1)
|
||||
equipment_id = fields.Many2one('maintenance.equipment', '机台号', readonly='True')
|
||||
equipment_code = fields.Char('设备编码', readonly='True')
|
||||
name = fields.Char('设备名称', readonly='True')
|
||||
function_type = fields.Selection(
|
||||
[("ZXJGZX", "钻铣加工中心"), ("CXJGZX", "车削加工中心"), ("FHJGZX", "复合加工中心")],
|
||||
default="", string="功能类型")
|
||||
machine_tool_picture = fields.Binary('设备图片')
|
||||
type_id = fields.Many2one('sf.machine_tool.type', '品牌型号', reaonly='True')
|
||||
state = fields.Selection([("加工", "加工"), ("关机", "关机"), ("待机", "待机"), ("故障", "故障"),
|
||||
("检修", "检修"), ("保养", "保养")], default="", string="实时状态")
|
||||
online_time = fields.Char('开机时长', reaonly='True')
|
||||
|
||||
offline_time = fields.Char('关机时长', reaonly='True')
|
||||
offline_nums = fields.Integer('关机次数', reaonly='True')
|
||||
# 待机时长
|
||||
|
||||
idle_time = fields.Char('待机时长', reaonly='True')
|
||||
|
||||
# 待机率
|
||||
idle_rate = fields.Char('待机率', reaonly='True')
|
||||
|
||||
work_time = fields.Char('加工时长', reaonly='True')
|
||||
work_rate = fields.Char('可用率', reaonly='True')
|
||||
fault_time = fields.Char('故障时长', reaonly='True')
|
||||
fault_rate = fields.Char('故障率', reaonly='True')
|
||||
fault_nums = fields.Integer('故障次数', reaonly='True')
|
||||
|
||||
detail_ids = fields.One2many('maintenance.equipment.oee.log.detail', 'log_id', string='日志详情')
|
||||
|
||||
# maintenance_time = fields.Char('维保时长')
|
||||
# work_nums = fields.Integer('加工件数')
|
||||
equipment_oee_id = fields.Many2one('maintenance.equipment.oee', '设备OEE')
|
||||
|
||||
@api.onchange('equipment_id')
|
||||
def get_name(self):
|
||||
self.name = self.equipment_id.name
|
||||
self.equipment_code = self.equipment_id.code
|
||||
|
||||
|
||||
# 设备运行日志详情
|
||||
class SfMaintenanceEquipmentOEELogDetail(models.Model):
|
||||
_name = 'maintenance.equipment.oee.log.detail'
|
||||
_description = '设备运行日志详情'
|
||||
_order = 'time desc'
|
||||
|
||||
# sequence = fields.Integer('序号', related='id')
|
||||
time = fields.Datetime('时间')
|
||||
state = fields.Selection([("加工", "加工"), ("关机", "关机"), ("待机", "待机"), ("故障", "故障"),
|
||||
("检修", "检修"), ("保养", "保养")], default="", string="事件/状态")
|
||||
production_name = fields.Char('加工工单')
|
||||
|
||||
log_id = fields.Many2one('maintenance.equipment.oee.logs', '日志')
|
||||
# equipment_code = fields.Char('设备编码', related='log_id.equipment_code')
|
||||
equipment_code = fields.Char('设备编码', readonly='True')
|
||||
|
||||
|
||||
@@ -67,3 +67,6 @@ access_sf_cutting_tool_type_admin_sf_group_equipment_user,sf_cutting_tool_type_a
|
||||
access_sf_cutting_tool_type_group_purchase_director_sf_group_equipment_user,sf_cutting_tool_type_group_purchase_director,sf_base.model_sf_cutting_tool_type,sf_maintenance.sf_group_equipment_user,1,0,0,0
|
||||
access_sf_cutting_tool_type_group_sale_director_sf_group_equipment_user,sf_cutting_tool_type_group_sale_director,sf_base.model_sf_cutting_tool_type,sf_maintenance.sf_group_equipment_user,1,0,0,0
|
||||
access_sf_cutting_tool_type_group_plan_director_sf_group_equipment_user,sf_cutting_tool_type_group_plan_director,sf_base.model_sf_cutting_tool_type,sf_maintenance.sf_group_equipment_user,1,0,0,0
|
||||
|
||||
access_maintenance_equipment_oee_logs,maintenance_equipment_oee_logs,model_maintenance_equipment_oee_logs,sf_maintenance.sf_group_equipment_manager,1,1,1,1
|
||||
access_maintenance_equipment_oee_log_detail,maintenance_equipment_oee_log_detail,model_maintenance_equipment_oee_log_detail,sf_maintenance.sf_group_equipment_manager,1,1,1,1
|
||||
|
@@ -14,6 +14,7 @@
|
||||
<field name="utilization_rate"/>
|
||||
<field name="fault_time"/>
|
||||
<field name="fault_nums"/>
|
||||
<field name="fault_rate"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
@@ -49,19 +50,19 @@
|
||||
</group>
|
||||
|
||||
</group>
|
||||
<notebook>
|
||||
<page string="运行日志">
|
||||
<field name="oee_logs">
|
||||
<tree create="1" edit="1" delete="1" editable="bottom">
|
||||
<field name = 'run_time'/>
|
||||
<field name = 'state'/>
|
||||
<field name = 'workorder_id'/>
|
||||
<field name = 'time'/>
|
||||
<field name = 'color' widget="color"/>
|
||||
</tree>
|
||||
</field>
|
||||
</page>
|
||||
</notebook>
|
||||
<!-- <notebook> -->
|
||||
<!-- <page string="运行日志"> -->
|
||||
<!-- <field name="oee_logs"> -->
|
||||
<!-- <tree create="1" edit="1" delete="1" editable="bottom"> -->
|
||||
<!-- <field name = 'run_time'/> -->
|
||||
<!-- <field name = 'state'/> -->
|
||||
<!-- <field name = 'workorder_id'/> -->
|
||||
<!-- <field name = 'time'/> -->
|
||||
<!-- <field name = 'color' widget="color"/> -->
|
||||
<!-- </tree> -->
|
||||
<!-- </field> -->
|
||||
<!-- </page> -->
|
||||
<!-- </notebook> -->
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
|
||||
@@ -7,22 +7,20 @@
|
||||
<field name="model">sf.maintenance.logs</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree>
|
||||
<field name="code"/>
|
||||
<field name="type" optional="hide"/>
|
||||
<field name="brand"/>
|
||||
<field name="maintenance_equipment_id"/>
|
||||
<field name="code_location" optional="hide"/>
|
||||
<field name="fault_type"/>
|
||||
<field name="fault_code" optional="hide"/>
|
||||
<field name="alarm_time"/>
|
||||
<field name="fault_alarm_info"/>
|
||||
<field name="alarm_level" optional="hide"/>
|
||||
<field name="alarm_time"/>
|
||||
<field name="alarm_way" optional="hide"/>
|
||||
<field name="fault_process"/>
|
||||
<field name="operator"/>
|
||||
<field name="fault_process" optional="hide"/>
|
||||
<field name="operator" optional="hide"/>
|
||||
<field name="recovery_time"/>
|
||||
<field name="fault_duration"/>
|
||||
<field name="note"/>
|
||||
<field name="note" optional="hide"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
@@ -45,25 +43,32 @@
|
||||
<group>
|
||||
<group>
|
||||
|
||||
<field name="name"/>
|
||||
<field name="type" required="1" widget="radio" options="{'horizontal': true}"/>
|
||||
<field name="brand"/>
|
||||
<!-- <field name="name"/> -->
|
||||
<!-- <field name="type" required="1" widget="radio" options="{'horizontal': true}"/> -->
|
||||
<field name="maintenance_equipment_id"/>
|
||||
<field name="code_location"/>
|
||||
<field name="fault_type" required="1" widget="radio" options="{'horizontal': true}"/>
|
||||
<field name="fault_code"/>
|
||||
<field name="fault_process"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="fault_alarm_info"/>
|
||||
<field name="brand"/>
|
||||
<field name="alarm_time"/>
|
||||
<field name="alarm_way" required="1" widget="radio" options="{'horizontal': true}"/>
|
||||
<field name="operator"/>
|
||||
<field name="recovery_time"/>
|
||||
<field name="fault_duration"/>
|
||||
<field name="note"/>
|
||||
<field name="fault_alarm_info"/>
|
||||
|
||||
<!-- <field name="code_location"/> -->
|
||||
<!-- <field name="fault_type" required="1" widget="radio" options="{'horizontal': true}"/> -->
|
||||
<!-- <field name="fault_code"/> -->
|
||||
|
||||
</group>
|
||||
<group>
|
||||
<field name="operator"/>
|
||||
|
||||
<field name="fault_process"/>
|
||||
<!-- <field name="alarm_way" required="1" widget="radio" options="{'horizontal': true}"/> -->
|
||||
<field name="recovery_time"/>
|
||||
<field name="fault_duration"/>
|
||||
|
||||
|
||||
</group>
|
||||
|
||||
</group>
|
||||
<group>
|
||||
<field name="note"/>
|
||||
</group>
|
||||
</sheet>
|
||||
</form>
|
||||
@@ -100,6 +105,248 @@
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- 设备运行日志 -->
|
||||
<record id="view_maintenance_logs_run_tree" model="ir.ui.view">
|
||||
<field name="name">maintenance.logs.run.tree</field>
|
||||
<field name="model">maintenance.equipment.oee.logs</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree>
|
||||
<field name="equipment_id"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_maintenance_logs_run_form" model="ir.ui.view">
|
||||
<field name="name">maintenance.logs.run.form</field>
|
||||
<field name="model">maintenance.equipment.oee.logs</field>
|
||||
<field name="arch" type="xml">
|
||||
<!-- <form string="设备运行日志"> -->
|
||||
<!-- <header> -->
|
||||
<!-- <field name="equipment_id" readonly="1"/> -->
|
||||
<!-- </header> -->
|
||||
<!-- <sheet> -->
|
||||
<!-- <div class="oe_title"> -->
|
||||
<!-- <h1> -->
|
||||
<!-- <field name="start_time" readonly="1"/> -->
|
||||
<!-- </h1> -->
|
||||
<!-- </div> -->
|
||||
<!-- <group> -->
|
||||
<!-- <group> -->
|
||||
<!-- <field name="stop_time" readonly="1"/> -->
|
||||
<!-- <field name="duration" readonly="1"/> -->
|
||||
<!-- <field name="oee" readonly="1"/> -->
|
||||
<!-- </group> -->
|
||||
<!-- <group> -->
|
||||
<!-- <field name="note"/> -->
|
||||
<!-- </group> -->
|
||||
|
||||
<!-- </group> -->
|
||||
<!-- </sheet> -->
|
||||
<!-- </form> -->
|
||||
<form string="设备运行日志">
|
||||
<!-- <header> -->
|
||||
<!-- <field name="name" readonly="1"/> -->
|
||||
<!-- </header> -->
|
||||
<sheet>
|
||||
<div class="oe_title">
|
||||
<h1>
|
||||
<field name="name"/>
|
||||
</h1>
|
||||
</div>
|
||||
<group>
|
||||
<group>
|
||||
<group>
|
||||
<field name="equipment_id" domain="[('name','ilike','加工中心')]"/>
|
||||
<field name="type_id"/>
|
||||
<field name="state"/>
|
||||
<field name="equipment_code"/>
|
||||
<field name="function_type"/>
|
||||
</group>
|
||||
</group>
|
||||
<group>
|
||||
<group>
|
||||
<!-- <field name="state" nolabel="1"/> -->
|
||||
<field name="state" string=""/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="machine_tool_picture" widget="image" nolabel="1"/>
|
||||
</group>
|
||||
</group>
|
||||
</group>
|
||||
<group>
|
||||
<group>
|
||||
<group>
|
||||
<field name="online_time" readonly="1"/>
|
||||
<field name="offline_time" readonly="1"/>
|
||||
<field name="fault_rate" readonly="1"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="offline_nums" readonly="1"/>
|
||||
<field name="fault_time" readonly="1"/>
|
||||
<field name="fault_nums" readonly="1"/>
|
||||
</group>
|
||||
</group>
|
||||
<group>
|
||||
<group>
|
||||
<field name="idle_time"/>
|
||||
<field name="idle_rate"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="work_time"/>
|
||||
<field name="work_rate"/>
|
||||
</group>
|
||||
</group>
|
||||
|
||||
</group>
|
||||
<notebook>
|
||||
<page string="24H日志详情">
|
||||
<!-- 筛选出24小时内的日志 -->
|
||||
<!-- <field name="detail_ids" domain="[('time','<',(datetime.datetime.now() - datetime.timedelta(days=1)).strftime('%Y-%m-%d %H:%M:%S'))]"> -->
|
||||
<field name="detail_ids" domain="[('state','ilike','加工')]">
|
||||
<tree>
|
||||
<!-- <field name="sequence"/> -->
|
||||
<field name="time"/>
|
||||
<field name="state"/>
|
||||
<field name="production_name"/>
|
||||
</tree>
|
||||
<!-- <form> -->
|
||||
<!-- <field name="sequence"/> -->
|
||||
<!-- <field name="time"/> -->
|
||||
<!-- <field name="state"/> -->
|
||||
<!-- <field name="production_id"/> -->
|
||||
<!-- </form> -->
|
||||
|
||||
</field>
|
||||
</page>
|
||||
<page string="历史日志详情">
|
||||
<field name="detail_ids">
|
||||
<tree>
|
||||
<!-- <field name="sequence"/> -->
|
||||
<field name="time"/>
|
||||
<field name="state"/>
|
||||
<field name="production_name"/>
|
||||
</tree>
|
||||
<!-- <form> -->
|
||||
<!-- <field name="sequence"/> -->
|
||||
<!-- <field name="time"/> -->
|
||||
<!-- <field name="state"/> -->
|
||||
<!-- <field name="production_id"/> -->
|
||||
<!-- </form> -->
|
||||
|
||||
</field>
|
||||
</page>
|
||||
</notebook>
|
||||
</sheet>
|
||||
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
||||
<!-- <record id="view_maintenance_logs_run_search" model="ir.ui.view"> -->
|
||||
<!-- <field name="name">maintenance.logs.run.search</field> -->
|
||||
<!-- <field name="model">maintenance.equipment.oee.logs</field> -->
|
||||
<!-- <field name="arch" type="xml"> -->
|
||||
<!-- <search> -->
|
||||
<!-- <field name="equipment_id"/> -->
|
||||
<!-- <field name="start_time"/> -->
|
||||
<!-- <field name="stop_time"/> -->
|
||||
<!-- <field name="duration"/> -->
|
||||
<!-- <field name="oee"/> -->
|
||||
<!-- <field name="note"/> -->
|
||||
<!-- </search> -->
|
||||
|
||||
<!-- </field> -->
|
||||
<!-- </record> -->
|
||||
|
||||
<!-- 设备运行日志详情 -->
|
||||
<record id="view_maintenance_logs_run_detail_tree" model="ir.ui.view">
|
||||
<field name="name">maintenance.logs.run.detail.tree</field>
|
||||
<field name="model">maintenance.equipment.oee.log.detail</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree>
|
||||
<!-- <field name="sequence"/> -->
|
||||
<field name="time"/>
|
||||
<field name="state"/>
|
||||
<field name="production_name"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_maintenance_logs_run_detail_form" model="ir.ui.view">
|
||||
<field name="name">maintenance.logs.run.detail.form</field>
|
||||
<field name="model">maintenance.equipment.oee.log.detail</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="设备运行日志详情">
|
||||
<sheet>
|
||||
<group>
|
||||
<group>
|
||||
<field name="state"/>
|
||||
<!-- <field name="production_id"/> -->
|
||||
</group>
|
||||
<group>
|
||||
<!-- <field name="sequence"/> -->
|
||||
<field name="time"/>
|
||||
</group>
|
||||
</group>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- <record id="view_maintenance_logs_run_detail_search" model="ir.ui.view"> -->
|
||||
<!-- <field name="name">maintenance.logs.run.detail.search</field> -->
|
||||
<!-- <field name="model">maintenance.equipment.oee.logs.detail</field> -->
|
||||
<!-- <field name="arch" type="xml"> -->
|
||||
<!-- <search> -->
|
||||
<!-- <field name="equipment_id"/> -->
|
||||
<!-- <field name="start_time"/> -->
|
||||
<!-- <field name="stop_time"/> -->
|
||||
<!-- <field name="duration"/> -->
|
||||
<!-- <field name="oee"/> -->
|
||||
<!-- <field name="note"/> -->
|
||||
<!-- </search> -->
|
||||
<!-- </field> -->
|
||||
<!-- </record> -->
|
||||
|
||||
<!-- 设备运行日志详情action -->
|
||||
<!-- <record id="action_maintenance_logs_run_detail" model="ir.actions.act_window"> -->
|
||||
<!-- <field name="name">设备运行日志详情</field> -->
|
||||
<!-- <field name="type">ir.actions.act_window</field> -->
|
||||
<!-- <field name="res_model">maintenance.equipment.oee.logs.detail</field> -->
|
||||
<!-- <field name="view_mode">tree,form</field> -->
|
||||
<!-- <field name="view_id" ref="view_maintenance_logs_run_detail_tree"/> -->
|
||||
<!-- <field name="help" type="html"> -->
|
||||
<!-- <p class="oe_view_nocontent_create"> -->
|
||||
<!-- 设备运行日志详情 -->
|
||||
<!-- </p> -->
|
||||
<!-- </field> -->
|
||||
<!-- -->
|
||||
<!-- </record> -->
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<record id="action_maintenance_logs_run" model="ir.actions.act_window">
|
||||
<field name="name">设备运行日志</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">maintenance.equipment.oee.logs</field>
|
||||
<!-- <field name="search_view_id" ref="view_maintenance_logs_run_search"/> -->
|
||||
<field name="view_mode">tree,form</field>
|
||||
<!-- <field name="view_mode">form</field> -->
|
||||
<field name="view_id" ref="view_maintenance_logs_run_tree"/>
|
||||
<field name="help" type="html">
|
||||
<p class="oe_view_nocontent_create">
|
||||
设备运行日志
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
||||
<menuitem name="设备运行日志" id="menu_maintenance_logs_run" parent="maintenance.menu_m_request"
|
||||
sequence="10" action="action_maintenance_logs_run"/>
|
||||
|
||||
|
||||
<!-- Action -->
|
||||
|
||||
|
||||
@@ -14,10 +14,15 @@
|
||||
'data': [
|
||||
'data/stock_data.xml',
|
||||
'data/empty_racks_data.xml',
|
||||
'data/panel_data.xml',
|
||||
'data/agv_scheduling_data.xml',
|
||||
'security/group_security.xml',
|
||||
'security/ir.model.access.csv',
|
||||
'wizard/workpiece_delivery_views.xml',
|
||||
'wizard/rework_wizard_views.xml',
|
||||
'wizard/production_wizard_views.xml',
|
||||
'views/mrp_views_menus.xml',
|
||||
'views/agv_scheduling_views.xml',
|
||||
'views/stock_lot_views.xml',
|
||||
'views/mrp_production_addional_change.xml',
|
||||
'views/mrp_routing_workcenter_view.xml',
|
||||
@@ -27,7 +32,7 @@
|
||||
'views/model_type_view.xml',
|
||||
'views/agv_setting_views.xml',
|
||||
'views/sf_maintenance_equipment.xml',
|
||||
|
||||
'views/res_config_settings_views.xml',
|
||||
],
|
||||
'assets': {
|
||||
|
||||
@@ -37,7 +42,9 @@
|
||||
'web.assets_backend': [
|
||||
'sf_manufacturing/static/src/xml/kanban_change.xml',
|
||||
'sf_manufacturing/static/src/js/kanban_change.js',
|
||||
'sf_manufacturing/static/src/scss/kanban_change.scss'
|
||||
'sf_manufacturing/static/src/scss/kanban_change.scss',
|
||||
'sf_manufacturing/static/src/xml/button_show_on_tree.xml',
|
||||
'sf_manufacturing/static/src/js/workpiece_delivery_wizard_confirm.js',
|
||||
]
|
||||
|
||||
},
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
import logging
|
||||
import json
|
||||
from datetime import datetime
|
||||
|
||||
from odoo.addons.sf_manufacturing.models.agv_scheduling import RepeatTaskException
|
||||
from odoo import http
|
||||
from odoo.http import request
|
||||
|
||||
@@ -25,7 +27,8 @@ class Manufacturing_Connect(http.Controller):
|
||||
{'content': ret, 'name': 'AutoDeviceApi/GetWoInfo'})
|
||||
logging.info('RfidCode:%s' % ret['RfidCode'])
|
||||
if 'RfidCode' in ret:
|
||||
workorder = request.env['mrp.workorder'].sudo().search([('rfid_code', '=', ret['RfidCode'])])
|
||||
workorder = request.env['mrp.workorder'].sudo().search(
|
||||
[('rfid_code', '=', ret['RfidCode']), ('state', '!=', 'rework')])
|
||||
if workorder:
|
||||
for item in workorder:
|
||||
res['Datas'].append({
|
||||
@@ -122,7 +125,8 @@ class Manufacturing_Connect(http.Controller):
|
||||
logging.info('RfidCode:%s' % ret['RfidCode'])
|
||||
if 'RfidCode' in ret:
|
||||
workorder = request.env['mrp.workorder'].sudo().search(
|
||||
[('routing_type', '=', '装夹预调'), ('rfid_code', '=', ret['RfidCode'])], limit=1, order='id asc')
|
||||
[('routing_type', '=', '装夹预调'), ('rfid_code', '=', ret['RfidCode']), ('state', '!=', 'rework')],
|
||||
limit=1, order='id asc')
|
||||
if workorder:
|
||||
for item in workorder:
|
||||
if item.material_center_point:
|
||||
@@ -143,7 +147,7 @@ class Manufacturing_Connect(http.Controller):
|
||||
logging.info('get_qcCheck error:%s' % e)
|
||||
return json.JSONEncoder().encode(res)
|
||||
|
||||
@http.route('/AutoDeviceApi/FeedBackStart', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False,
|
||||
@http.route('/AutoDeviceApi/FeedBackStart', type='json', auth='none', methods=['GET', 'POST'], csrf=False,
|
||||
cors="*")
|
||||
def button_Work_START(self, **kw):
|
||||
"""
|
||||
@@ -163,7 +167,7 @@ class Manufacturing_Connect(http.Controller):
|
||||
equipment_id = ret["DeviceId"]
|
||||
workorder = request.env['mrp.workorder'].sudo().search(
|
||||
[('production_id', '=', production_id), ('routing_type', '=', routing_type),
|
||||
('rfid_code', '!=', False)], limit=1)
|
||||
('rfid_code', '!=', False), ('state', '!=', 'rework')], limit=1)
|
||||
if not workorder:
|
||||
res = {'Succeed': False, 'ErrorCode': 202, 'Error': '该工单不存在'}
|
||||
return json.JSONEncoder().encode(res)
|
||||
@@ -191,7 +195,7 @@ class Manufacturing_Connect(http.Controller):
|
||||
logging.info('button_Work_START error:%s' % e)
|
||||
return json.JSONEncoder().encode(res)
|
||||
|
||||
@http.route('/AutoDeviceApi/FeedBackEnd', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False,
|
||||
@http.route('/AutoDeviceApi/FeedBackEnd', type='json', auth='none', methods=['GET', 'POST'], csrf=False,
|
||||
cors="*")
|
||||
def button_Work_End(self, **kw):
|
||||
"""
|
||||
@@ -211,7 +215,7 @@ class Manufacturing_Connect(http.Controller):
|
||||
routing_type = ret['CraftId']
|
||||
workorder = request.env['mrp.workorder'].sudo().search(
|
||||
[('production_id', '=', production_id), ('routing_type', '=', routing_type),
|
||||
('rfid_code', '!=', False)], limit=1)
|
||||
('rfid_code', '!=', False), ('state', '!=', 'rework')], limit=1)
|
||||
if not workorder:
|
||||
res = {'Succeed': False, 'ErrorCode': 202, 'Error': '该工单不存在'}
|
||||
return json.JSONEncoder().encode(res)
|
||||
@@ -220,28 +224,29 @@ class Manufacturing_Connect(http.Controller):
|
||||
return json.JSONEncoder().encode(res)
|
||||
# workorder.write({'date_finished': datetime.now()})
|
||||
if ret['IsComplete'] is True:
|
||||
workorder.button_finish()
|
||||
# workorder.process_state = '待解除装夹'
|
||||
# workorder.sudo().production_id.process_state = '待解除装夹'
|
||||
workorder.write({'date_finished': datetime.now()})
|
||||
|
||||
# 根据工单的实际结束时间修改排程单的结束时间、状态,同时修改销售订单的状态
|
||||
# if workorder.date_finished:
|
||||
# request.env['sf.production.plan'].sudo().search([('production_id', '=', production_id)]).write(
|
||||
# {'actual_end_time': workorder.date_finished,
|
||||
# 'state': 'finished'})
|
||||
# production_obj = request.env['mrp.production'].sudo().search([('name', '=', production_id)])
|
||||
# if production_obj:
|
||||
# production_obj.sudo().work_order_state = '已完成'
|
||||
# production_obj.write({'state': 'done'})
|
||||
# request.env['sale.order'].sudo().search(
|
||||
# [('name', '=', production_obj.origin)]).write({'schedule_status': 'to deliver'})
|
||||
# workorder.process_state = '待解除装夹'
|
||||
# workorder.sudo().production_id.process_state = '待解除装夹'
|
||||
|
||||
# 根据工单的实际结束时间修改排程单的结束时间、状态,同时修改销售订单的状态
|
||||
# if workorder.date_finished:
|
||||
# request.env['sf.production.plan'].sudo().search([('production_id', '=', production_id)]).write(
|
||||
# {'actual_end_time': workorder.date_finished,
|
||||
# 'state': 'finished'})
|
||||
# production_obj = request.env['mrp.production'].sudo().search([('name', '=', production_id)])
|
||||
# if production_obj:
|
||||
# production_obj.sudo().work_order_state = '已完成'
|
||||
# production_obj.write({'state': 'done'})
|
||||
# request.env['sale.order'].sudo().search(
|
||||
# [('name', '=', production_obj.origin)]).write({'schedule_status': 'to deliver'})
|
||||
|
||||
except Exception as e:
|
||||
res = {'Succeed': False, 'ErrorCode': 202, 'Error': e}
|
||||
logging.info('button_Work_End error:%s' % e)
|
||||
return json.JSONEncoder().encode(res)
|
||||
|
||||
@http.route('/AutoDeviceApi/PartQualityInspect', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False,
|
||||
@http.route('/AutoDeviceApi/PartQualityInspect', type='json', auth='none', methods=['GET', 'POST'], csrf=False,
|
||||
cors="*")
|
||||
def PartQualityInspect(self, **kw):
|
||||
"""
|
||||
@@ -257,39 +262,27 @@ class Manufacturing_Connect(http.Controller):
|
||||
request.env['center_control.interface.log'].sudo().create(
|
||||
{'content': ret, 'name': 'AutoDeviceApi/PartQualityInspect'})
|
||||
production_id = ret['BillId']
|
||||
routing_type = ret['CraftId']
|
||||
# routing_type = ret['CraftId']
|
||||
workorder = request.env['mrp.workorder'].sudo().search(
|
||||
[('production_id', '=', production_id), ('routing_type', '=', routing_type)], limit=1)
|
||||
[('production_id', '=', production_id), ('routing_type', '=', 'CNC加工'),
|
||||
('state', 'not in', ['rework', 'done', 'cancel'])], order='sequence asc',
|
||||
limit=1)
|
||||
if workorder:
|
||||
# workorder.test_results = ret['Quality']
|
||||
logging.info('制造订单:%s' % workorder.production_id.name)
|
||||
if 'ReportPaht' in ret:
|
||||
download_state = request.env['mrp.workorder'].with_user(
|
||||
request.env.ref("base.user_admin")).download_reportfile_tmp(workorder,
|
||||
ret['ReportPaht'])
|
||||
if download_state == 1:
|
||||
detection_ret = request.env['mrp.workorder'].with_user(
|
||||
request.env.ref("base.user_admin")).get_detection_file(workorder, ret['ReportPaht'])
|
||||
if detection_ret is True:
|
||||
stock_picking_type = request.env['stock.picking.type'].sudo().search(
|
||||
[('sequence_code', '=', 'SFP')])
|
||||
if stock_picking_type:
|
||||
stock_picking = request.env['stock.picking'].sudo().search(
|
||||
[('product_id', '=', workorder.product_id.id),
|
||||
('origin', '=', workorder.production_id.origin),
|
||||
('picking_type_id', '=', stock_picking_type.id)])
|
||||
if stock_picking:
|
||||
quality_check = request.env['quality.check'].sudo().search(
|
||||
[('product_id', '=', workorder.product_id.id),
|
||||
('picking_id', '=', stock_picking.id)])
|
||||
if quality_check:
|
||||
logging.info('质检单:%s' % quality_check.name)
|
||||
quality_check.write({'report_pdf': workorder.detection_report})
|
||||
elif download_state == 2:
|
||||
res = {'Succeed': False, 'ErrorCode': 205,
|
||||
'Error': 'ReportPaht中的工件号与制造订单%s不匹配,请检查ReportPaht是否正确' % workorder.production_id.name}
|
||||
else:
|
||||
res = {'Succeed': False, 'ErrorCode': 204, 'Error': '检测报告文件从FTP拉取失败'}
|
||||
if ret['ReportPaht'].find('.pdf') != -1:
|
||||
download_state = request.env['mrp.workorder'].with_user(
|
||||
request.env.ref("base.user_admin")).download_reportfile_tmp(workorder,
|
||||
ret['ReportPaht'])
|
||||
if download_state is True:
|
||||
detection_ret = request.env['mrp.workorder'].with_user(
|
||||
request.env.ref("base.user_admin")).get_detection_file(workorder, ret['ReportPaht'])
|
||||
logging.info('detection_ret:%s' % detection_ret)
|
||||
if detection_ret is False:
|
||||
res = {'Succeed': False, 'ErrorCode': 205, 'Error': '检测报告文件读取失败'}
|
||||
else:
|
||||
res = {'Succeed': False, 'ErrorCode': 204, 'Error': '检测报告文件从FTP拉取失败'}
|
||||
else:
|
||||
res = {'Succeed': False, 'ErrorCode': 203, 'Error': '未传ReportPaht字段'}
|
||||
else:
|
||||
@@ -299,7 +292,7 @@ class Manufacturing_Connect(http.Controller):
|
||||
logging.info('PartQualityInspect error:%s' % e)
|
||||
return json.JSONEncoder().encode(res)
|
||||
|
||||
@http.route('/AutoDeviceApi/CMMProgDolod', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False,
|
||||
@http.route('/AutoDeviceApi/CMMProgDolod', type='json', auth='none', methods=['GET', 'POST'], csrf=False,
|
||||
cors="*")
|
||||
def CMMProgDolod(self, **kw):
|
||||
"""
|
||||
@@ -317,7 +310,7 @@ class Manufacturing_Connect(http.Controller):
|
||||
if 'RfidCode' in ret:
|
||||
logging.info('RfidCode:%s' % ret['RfidCode'])
|
||||
workorder = request.env['mrp.workorder'].sudo().search(
|
||||
[('rfid_code', '=', ret['RfidCode']), ('routing_type', '=', 'CNC加工')])
|
||||
[('rfid_code', '=', ret['RfidCode']), ('routing_type', '=', 'CNC加工'), ('state', '!=', 'rework')])
|
||||
if workorder:
|
||||
for item in workorder.cmm_ids:
|
||||
if item.program_create_date is not False:
|
||||
@@ -339,7 +332,7 @@ class Manufacturing_Connect(http.Controller):
|
||||
logging.info('CMMProgDolod error:%s' % e)
|
||||
return json.JSONEncoder().encode(res)
|
||||
|
||||
@http.route('/AutoDeviceApi/NCProgDolod', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False,
|
||||
@http.route('/AutoDeviceApi/NCProgDolod', type='json', auth='none', methods=['GET', 'POST'], csrf=False,
|
||||
cors="*")
|
||||
def NCProgDolod(self, **kw):
|
||||
"""
|
||||
@@ -357,7 +350,7 @@ class Manufacturing_Connect(http.Controller):
|
||||
if 'RfidCode' in ret:
|
||||
logging.info('RfidCode:%s' % ret['RfidCode'])
|
||||
workorder = request.env['mrp.workorder'].sudo().search(
|
||||
[('rfid_code', '=', ret['RfidCode']), ('routing_type', '=', 'CNC加工')])
|
||||
[('rfid_code', '=', ret['RfidCode']), ('routing_type', '=', 'CNC加工'), ('state', '!=', 'rework')])
|
||||
if workorder:
|
||||
for item in workorder.cnc_ids:
|
||||
res['Datas'].append({
|
||||
@@ -395,7 +388,7 @@ class Manufacturing_Connect(http.Controller):
|
||||
ret = json.loads(datas)
|
||||
request.env['center_control.interface.log'].sudo().create(
|
||||
{'content': ret, 'name': 'AutoDeviceApi/LocationChange'})
|
||||
logging.info('LocationChange_ret===========:%s' % ret)
|
||||
logging.info('库位变更LocationChange_ret:%s' % ret)
|
||||
RfidCode = ret['RfidCode']
|
||||
ChangeType = ret['ChangeType']
|
||||
OldDeciveId = ret['OldDeciveId']
|
||||
@@ -405,34 +398,80 @@ class Manufacturing_Connect(http.Controller):
|
||||
OldDeciveStart = ret['OldDeciveStart']
|
||||
OldDeciveEnd = ret['OldDeciveEnd']
|
||||
|
||||
temp_val_sn_id = None
|
||||
old_localtion = None
|
||||
# if ChangeType == 'Part' or ChangeType == 'Tool':
|
||||
stock_lot_obj = request.env['stock.lot'].sudo().search(
|
||||
[('rfid', '=', RfidCode)], limit=1)
|
||||
logging.info('stock_lot_obj===========:%s' % stock_lot_obj)
|
||||
if not stock_lot_obj:
|
||||
res = {'Succeed': False, 'ErrorCode': 202, 'Error': '未根据RfidCode找到该产品'}
|
||||
return json.JSONEncoder().encode(res)
|
||||
if OldPosition:
|
||||
old_localtion = request.env['sf.shelf.location'].sudo().search(
|
||||
[('barcode', '=', OldPosition)], limit=1)
|
||||
logging.info('old_localtion===========:%s' % old_localtion)
|
||||
new_localtion = request.env['sf.shelf.location'].sudo().search(
|
||||
[('barcode', '=', NewPosition)], limit=1)
|
||||
logging.info('new_localtion===========:%s' % new_localtion)
|
||||
if not new_localtion:
|
||||
res = {'Succeed': False, 'ErrorCode': 202, 'Error': '没有该目标位置'}
|
||||
return json.JSONEncoder().encode(res)
|
||||
if old_localtion:
|
||||
temp_val_sn_id = old_localtion.product_sn_id
|
||||
logging.info('temp_val_sn_id===========:%s' % temp_val_sn_id)
|
||||
old_localtion.product_sn_id = None
|
||||
new_localtion.product_sn_id = temp_val_sn_id
|
||||
logging.info('====1======')
|
||||
else:
|
||||
new_localtion.product_sn_id = stock_lot_obj.id
|
||||
logging.info('=====2======')
|
||||
if ChangeType == 'Part':
|
||||
temp_val_sn_id = None
|
||||
old_localtion = None
|
||||
# if ChangeType == 'Part' or ChangeType == 'Tool':
|
||||
stock_lot_obj = request.env['stock.lot'].sudo().search(
|
||||
[('rfid', '=', RfidCode)], limit=1)
|
||||
logging.info('stock_lot_obj===========:%s' % stock_lot_obj)
|
||||
if not stock_lot_obj:
|
||||
res = {'Succeed': False, 'ErrorCode': 202, 'Error': '未根据RfidCode找到该产品'}
|
||||
return json.JSONEncoder().encode(res)
|
||||
if OldPosition:
|
||||
old_localtion = request.env['sf.shelf.location'].sudo().search(
|
||||
[('barcode', '=', OldPosition)], limit=1)
|
||||
logging.info('old_localtion===========:%s' % old_localtion)
|
||||
new_localtion = request.env['sf.shelf.location'].sudo().search(
|
||||
[('barcode', '=', NewPosition)], limit=1)
|
||||
logging.info('new_localtion===========:%s' % new_localtion)
|
||||
if not new_localtion:
|
||||
res = {'Succeed': False, 'ErrorCode': 202, 'Error': '没有该目标位置'}
|
||||
return json.JSONEncoder().encode(res)
|
||||
if old_localtion:
|
||||
temp_val_sn_id = old_localtion.product_sn_id
|
||||
logging.info('temp_val_sn_id===========:%s' % temp_val_sn_id)
|
||||
old_localtion.product_sn_id = None
|
||||
new_localtion.product_sn_id = temp_val_sn_id
|
||||
logging.info('====1======')
|
||||
else:
|
||||
new_localtion.product_sn_id = stock_lot_obj.id
|
||||
logging.info('=====2======')
|
||||
elif ChangeType == 'Tool':
|
||||
# 对功能刀具库位变更信息进行更改
|
||||
def write_tool(DeciveId):
|
||||
if 'Tool' in DeciveId:
|
||||
shelfinfo = list(filter(lambda x: x.get('DeviceId') == DeciveId,
|
||||
request.env['sf.shelf.location'].sudo().get_sf_shelf_location_info(
|
||||
DeciveId)))
|
||||
total_data = request.env['sf.shelf.location.datasync'].sudo().get_total_data()
|
||||
for item in shelfinfo:
|
||||
logging.info('货架已获取信息:%s' % item)
|
||||
shelf_barcode = request.env['sf.shelf.location.datasync'].sudo().find_our_code(
|
||||
total_data, item['Postion'])
|
||||
location_id = request.env['sf.shelf.location'].sudo().search(
|
||||
[('barcode', '=', shelf_barcode)],
|
||||
limit=1)
|
||||
if location_id:
|
||||
# 如果是线边刀库信息,则对功能刀具移动生成记录
|
||||
if 'Tool' in item['Postion']:
|
||||
tool = request.env['sf.functional.cutting.tool.entity'].sudo().search(
|
||||
[('rfid', '=', item['RfidCode']), ('functional_tool_status', '!=', '已拆除')])
|
||||
tool.sudo().tool_in_out_stock_location(location_id)
|
||||
if tool:
|
||||
location_id.product_sn_id = tool.barcode_id.id
|
||||
# 修改功能刀具状态
|
||||
if item.get('State') == '报警':
|
||||
if tool.functional_tool_status != item.get('State'):
|
||||
tool.write({
|
||||
'functional_tool_status': item['State']
|
||||
})
|
||||
else:
|
||||
location_id.product_sn_id = False
|
||||
if item['RfidCode']:
|
||||
logging.info('Rfid为【%s】的功能刀具在系统中不存在!' % item['RfidCode'])
|
||||
else:
|
||||
equipment_id = request.env['maintenance.equipment'].sudo().search([('name', '=', DeciveId)])
|
||||
if equipment_id:
|
||||
equipment_id.sudo().register_equipment_tool()
|
||||
else:
|
||||
res_1 = {'Succeed': False, 'ErrorCode': 202, 'Error': f'设备【{DeciveId}】不存在'}
|
||||
return json.JSONEncoder().encode(res_1)
|
||||
|
||||
if OldDeciveId:
|
||||
write_tool(OldDeciveId)
|
||||
elif NewDeciveId:
|
||||
write_tool(NewDeciveId)
|
||||
except Exception as e:
|
||||
res = {'Succeed': False, 'ErrorCode': 202, 'Error': e}
|
||||
logging.info('LocationChange error:%s' % e)
|
||||
@@ -457,43 +496,56 @@ class Manufacturing_Connect(http.Controller):
|
||||
if 'DeviceId' in ret:
|
||||
logging.info('DeviceId:%s' % ret['DeviceId'])
|
||||
if 'IsComplete' in ret:
|
||||
rfid_codes = []
|
||||
workorder_ids = []
|
||||
if ret['IsComplete'] is True or ret['IsComplete'] is False:
|
||||
for i in range(1, 5):
|
||||
logging.info('F-RfidCode:%s' % i)
|
||||
if f'RfidCode{i}' in ret:
|
||||
rfid_code = ret[f'RfidCode{i}']
|
||||
logging.info('RfidCode:%s' % rfid_code)
|
||||
if rfid_code is not None:
|
||||
if rfid_code is not None and rfid_code != '':
|
||||
rfid_codes.append(rfid_code)
|
||||
domain = [
|
||||
('rfid_code', '=', rfid_code),
|
||||
('routing_type', '=', 'CNC加工')
|
||||
('routing_type', '=', 'CNC加工'), ('state', '!=', 'rework')
|
||||
]
|
||||
workorder = request.env['mrp.workorder'].sudo().search(domain, order='id asc')
|
||||
if workorder:
|
||||
for order in workorder:
|
||||
workorder_ids.append(order.id)
|
||||
if order.production_line_state == '待上产线':
|
||||
logging.info(
|
||||
'工单产线状态:%s' % order.production_line_state)
|
||||
panel_workorder = request.env['mrp.workorder'].sudo().search(
|
||||
[('rfid_code', '=', rfid_code),
|
||||
[('rfid_code', '=', rfid_code), ('state', '!=', 'rework'),
|
||||
('processing_panel', '=', order.processing_panel)])
|
||||
if panel_workorder:
|
||||
panel_workorder.write({'production_line_state': '已上产线'})
|
||||
workpiece_delivery = request.env['sf.workpiece.delivery'].sudo().search(
|
||||
[
|
||||
('rfid_code', '=', rfid_code), ('type', '=', '上产线'),
|
||||
('production_id', '=', order.production_id.id)])
|
||||
if workpiece_delivery.status == '待下发':
|
||||
workpiece_delivery.write({'is_manual_work': True})
|
||||
# workpiece_delivery = request.env['sf.workpiece.delivery'].sudo().search(
|
||||
# [
|
||||
# ('rfid_code', '=', rfid_code), ('type', '=', '上产线'),
|
||||
# ('production_id', '=', order.production_id.id),
|
||||
# ('workorder_id', '=', order.id),
|
||||
# ('workorder_state', '=', 'done')])
|
||||
# if workpiece_delivery.status == '待下发':
|
||||
# workpiece_delivery.write({'is_manual_work': True})
|
||||
# 下发
|
||||
else:
|
||||
res = {'Succeed': False, 'ErrorCode': 204,
|
||||
'Error': 'DeviceId为%s没有对应的已配送工件数据' % ret['DeviceId']}
|
||||
if ret['IsComplete'] is True:
|
||||
# 向AGV任务调度下发运送空料架任务
|
||||
workorders = request.env['mrp.workorder'].browse(workorder_ids)
|
||||
request.env['sf.agv.scheduling'].add_scheduling(ret['DeviceId'], '运送空料架', workorders)
|
||||
else:
|
||||
res = {'Succeed': False, 'ErrorCode': 203, 'Error': '未传IsComplete字段'}
|
||||
else:
|
||||
res = {'Succeed': False, 'ErrorCode': 201, 'Error': '未传DeviceId字段'}
|
||||
except RepeatTaskException as e:
|
||||
logging.info('AGVToProduct error:%s' % e)
|
||||
except Exception as e:
|
||||
res = {'Succeed': False, 'ErrorCode': 202, 'Error': e}
|
||||
res = {'Succeed': False, 'ErrorCode': 202, 'Error': str(e)}
|
||||
logging.info('AGVToProduct error:%s' % e)
|
||||
return json.JSONEncoder().encode(res)
|
||||
|
||||
@@ -516,7 +568,8 @@ class Manufacturing_Connect(http.Controller):
|
||||
logging.info('ret:%s' % ret)
|
||||
if 'DeviceId' in ret:
|
||||
logging.info('DeviceId:%s' % ret['DeviceId'])
|
||||
delivery_Arr = []
|
||||
# delivery_Arr = []
|
||||
workorder_ids = []
|
||||
if 'IsComplete' in ret:
|
||||
if ret['IsComplete'] is True or ret['IsComplete'] is False:
|
||||
for i in range(1, 5):
|
||||
@@ -524,48 +577,59 @@ class Manufacturing_Connect(http.Controller):
|
||||
if f'RfidCode{i}' in ret:
|
||||
rfid_code = ret[f'RfidCode{i}']
|
||||
logging.info('RfidCode:%s' % rfid_code)
|
||||
if rfid_code is not None:
|
||||
if rfid_code is not None and rfid_code != '':
|
||||
domain = [
|
||||
('rfid_code', '=', rfid_code),
|
||||
('routing_type', '=', 'CNC加工')
|
||||
('routing_type', '=', 'CNC加工'), ('state', '!=', 'rework')
|
||||
]
|
||||
workorder = request.env['mrp.workorder'].sudo().search(domain, order='id asc')
|
||||
if workorder:
|
||||
for order in workorder:
|
||||
workorder_ids.append(order.id)
|
||||
if order.production_line_state == '已上产线':
|
||||
logging.info(
|
||||
'工单产线状态:%s' % order.production_line_state)
|
||||
panel_workorder = request.env['mrp.workorder'].sudo().search(
|
||||
[('rfid_code', '=', rfid_code),
|
||||
[('rfid_code', '=', rfid_code), ('state', '!=', 'rework'),
|
||||
('processing_panel', '=', order.processing_panel)])
|
||||
if panel_workorder:
|
||||
panel_workorder.write({'production_line_state': '已下产线'})
|
||||
workpiece_delivery = request.env['sf.workpiece.delivery'].sudo().search(
|
||||
[
|
||||
('rfid_code', '=', rfid_code), ('type', '=', '下产线'),
|
||||
('production_id', '=', order.production_id.id)])
|
||||
delivery_Arr.append(workpiece_delivery.id)
|
||||
workorder.write({'state': 'to be detected'})
|
||||
# workpiece_delivery = request.env['sf.workpiece.delivery'].sudo().search(
|
||||
# [
|
||||
# ('rfid_code', '=', rfid_code), ('type', '=', '下产线'),
|
||||
# ('production_id', '=', order.production_id.id),
|
||||
# ('workorder_id', '=', order.id),
|
||||
# ('workorder_state', '=', 'done')])
|
||||
# if workpiece_delivery:
|
||||
# delivery_Arr.append(workpiece_delivery.id)
|
||||
else:
|
||||
res = {'Succeed': False, 'ErrorCode': 204,
|
||||
'Error': 'DeviceId为%s没有对应的已配送工件数据' % ret['DeviceId']}
|
||||
if delivery_Arr:
|
||||
logging.info('delivery_Arr:%s' % delivery_Arr)
|
||||
delivery_workpiece = request.env['sf.workpiece.delivery'].sudo().search(
|
||||
[('id', 'in', delivery_Arr)])
|
||||
if delivery_workpiece:
|
||||
logging.info('开始向agv下发下产线任务')
|
||||
agv_site = request.env['sf.agv.site'].sudo().search([])
|
||||
if agv_site:
|
||||
has_site = agv_site.update_site_state()
|
||||
if has_site is True:
|
||||
is_free = delivery_workpiece._check_avgsite_state()
|
||||
if is_free is True:
|
||||
delivery_workpiece._delivery_avg()
|
||||
logging.info('agv下发下产线任务下发完成')
|
||||
# if delivery_Arr:
|
||||
# logging.info('delivery_Arr:%s' % delivery_Arr)
|
||||
# delivery_workpiece = request.env['sf.workpiece.delivery'].sudo().search(
|
||||
# [('id', 'in', delivery_Arr)])
|
||||
# if delivery_workpiece:
|
||||
# logging.info('开始向agv下发下产线任务')
|
||||
# agv_site = request.env['sf.agv.site'].sudo().search([])
|
||||
# if agv_site:
|
||||
# has_site = agv_site.update_site_state()
|
||||
# if has_site is True:
|
||||
# is_free = delivery_workpiece._check_avgsite_state()
|
||||
# if is_free is True:
|
||||
# delivery_workpiece._delivery_avg()
|
||||
# logging.info('agv下发下产线任务下发完成')
|
||||
if ret['IsComplete'] is True:
|
||||
# 向AGV任务调度下发下产线任务
|
||||
workorders = request.env['mrp.workorder'].browse(workorder_ids)
|
||||
request.env['sf.agv.scheduling'].add_scheduling(ret['DeviceId'], '下产线', workorders)
|
||||
else:
|
||||
res = {'Succeed': False, 'ErrorCode': 203, 'Error': '未传IsComplete字段'}
|
||||
except RepeatTaskException as e:
|
||||
logging.info('AGVToProduct error:%s' % e)
|
||||
except Exception as e:
|
||||
res = {'Succeed': False, 'ErrorCode': 202, 'Error': e}
|
||||
res = {'Succeed': False, 'ErrorCode': 202, 'Error': str(e)}
|
||||
logging.info('AGVDownProduct error:%s' % e)
|
||||
return json.JSONEncoder().encode(res)
|
||||
|
||||
@@ -603,3 +667,32 @@ class Manufacturing_Connect(http.Controller):
|
||||
res = {'Succeed': False, 'ErrorCode': 202, 'Error': e}
|
||||
logging.info('AGVDownProduct error:%s' % e)
|
||||
return json.JSONEncoder().encode(res)
|
||||
|
||||
@http.route('/AutoDeviceApi/AgvStationState', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False,
|
||||
cors="*")
|
||||
def AGVStationState(self, **kw):
|
||||
"""
|
||||
中控推送接驳站状态
|
||||
:param kw:
|
||||
:return:
|
||||
"""
|
||||
logging.info('AGVStationState:%s' % kw)
|
||||
try:
|
||||
res = {'Succeed': True}
|
||||
datas = request.httprequest.data
|
||||
ret = json.loads(datas)
|
||||
request.env['center_control.interface.log'].sudo().create(
|
||||
{'content': ret, 'name': 'AutoDeviceApi/AGVStationState'})
|
||||
logging.info('ret:%s' % ret)
|
||||
ret = ret['param']
|
||||
params = {}
|
||||
for i in range(len(ret)):
|
||||
if 'DeviceId' in ret[i] and 'AtHome' in ret[i]:
|
||||
logging.info('DeviceId:%s, AtHome:%s' % (ret[i]['DeviceId'], ret[i]['AtHome']))
|
||||
params[ret[i]['DeviceId']] = '占用' if ret[i]['AtHome'] else '空闲'
|
||||
if params:
|
||||
request.env['sf.agv.site'].update_site_state(params)
|
||||
except Exception as e:
|
||||
res = {'Succeed': False, 'ErrorCode': 202, 'Error': str(e)}
|
||||
logging.info('AGVDownProduct error:%s' % e)
|
||||
return json.JSONEncoder().encode(res)
|
||||
@@ -8,7 +8,7 @@ from odoo.http import request
|
||||
|
||||
class Workpiece(http.Controller):
|
||||
|
||||
@http.route('/agvApi/backfeed', type='json', auth='none', methods=['GET', 'POST'], csrf=False,
|
||||
@http.route('/agvApi/backfeed', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False,
|
||||
cors="*")
|
||||
def backfeed(self, **kw):
|
||||
"""
|
||||
@@ -25,15 +25,14 @@ class Workpiece(http.Controller):
|
||||
if 'reqCode' in ret:
|
||||
if 'method' in ret:
|
||||
if ret['method'] == 'end':
|
||||
req_codes = ret['reqCode'].split(',')
|
||||
for req_code in req_codes:
|
||||
workpiece_delivery = request.env['sf.workpiece.delivery'].sudo().search(
|
||||
[('name', '=', req_code.strip()), ('task_completion_time', '=', False)])
|
||||
if workpiece_delivery:
|
||||
workpiece_delivery.write({'status': '已配送', 'task_completion_time': datetime.now()})
|
||||
else:
|
||||
res = {'Succeed': False, 'ErrorCode': 203,
|
||||
'Error': '该reqCode暂未查到对应的工件配送记录'}
|
||||
# 找到对应的AGV调度任务
|
||||
agv_scheduling = request.env['sf.agv.scheduling'].sudo().search(
|
||||
[('name', '=', ret['reqCode']), ('state', '=', '配送中')])
|
||||
if agv_scheduling:
|
||||
agv_scheduling.finish_scheduling()
|
||||
else:
|
||||
res = {'Succeed': False, 'ErrorCode': 203,
|
||||
'Error': '该reqCode暂未查到对应的AGV任务记录'}
|
||||
else:
|
||||
res = {'Succeed': False, 'ErrorCode': 204, 'Error': '未传method字段'}
|
||||
else:
|
||||
|
||||
16
sf_manufacturing/data/agv_scheduling_data.xml
Normal file
16
sf_manufacturing/data/agv_scheduling_data.xml
Normal file
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data noupdate="0">
|
||||
<record id="sequence_agv_scheduling" model="ir.sequence">
|
||||
<field name="name">AGV调度</field>
|
||||
<field name="code">sf.agv.scheduling</field>
|
||||
<field name="prefix">B%(year)s%(month)s%(day)s</field>
|
||||
<field name="padding">4</field>
|
||||
<field name="number_next">1</field>
|
||||
<field name="implementation">standard</field>
|
||||
<field name="use_date_range">True</field>
|
||||
<field name="date_range_period">day</field>
|
||||
<field name="company_id" eval="False"/>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
24
sf_manufacturing/data/panel_data.xml
Normal file
24
sf_manufacturing/data/panel_data.xml
Normal file
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data noupdate="1">
|
||||
<record id="panel_zm" model="sf.processing.panel">
|
||||
<field name="name">ZM</field>
|
||||
</record>
|
||||
|
||||
<record id="panel_fm" model="sf.processing.panel">
|
||||
<field name="name">FM</field>
|
||||
</record>
|
||||
<record id="panel_yc" model="sf.processing.panel">
|
||||
<field name="name">YC</field>
|
||||
</record>
|
||||
<record id="panel_zc" model="sf.processing.panel">
|
||||
<field name="name">ZC</field>
|
||||
</record>
|
||||
<record id="panel_qc" model="sf.processing.panel">
|
||||
<field name="name">QC</field>
|
||||
</record>
|
||||
<record id="panel_hc" model="sf.processing.panel">
|
||||
<field name="name">HC</field>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
@@ -1,6 +1,16 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data noupdate="1">
|
||||
<record model="ir.cron" id="ir_cron_mrp_production">
|
||||
<field name="name">返工且编程中的制造订单定时获取Cloud编程单状态</field>
|
||||
<field name="model_id" ref="model_mrp_production"/>
|
||||
<field name="state">code</field>
|
||||
<field name="code">model._cron_get_programming_state()</field>
|
||||
<field name="interval_number">3</field>
|
||||
<field name="interval_type">minutes</field>
|
||||
<field name="numbercall">-1</field>
|
||||
</record>
|
||||
|
||||
<record id="sequence_routing_workcenter" model="ir.sequence">
|
||||
<field name="name">工序编码规则</field>
|
||||
<field name="code">mrp.routing.workcenter</field>
|
||||
|
||||
@@ -9,3 +9,5 @@ from . import stock
|
||||
from . import res_user
|
||||
from . import production_line_base
|
||||
from . import agv_setting
|
||||
from . import agv_scheduling
|
||||
from . import res_config_setting
|
||||
|
||||
267
sf_manufacturing/models/agv_scheduling.py
Normal file
267
sf_manufacturing/models/agv_scheduling.py
Normal file
@@ -0,0 +1,267 @@
|
||||
import requests
|
||||
|
||||
from odoo import models, fields, api, _
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
import logging
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class RepeatTaskException(UserError):
|
||||
pass
|
||||
|
||||
|
||||
class AgvScheduling(models.Model):
|
||||
_name = 'sf.agv.scheduling'
|
||||
_description = 'agv调度'
|
||||
_order = 'id desc'
|
||||
|
||||
name = fields.Char('任务单号', index=True, copy=False)
|
||||
|
||||
def _get_agv_route_type_selection(self):
|
||||
return self.env['sf.agv.task.route'].fields_get(['route_type'])['route_type']['selection']
|
||||
|
||||
agv_route_type = fields.Selection(selection=_get_agv_route_type_selection, string='任务类型', required=True)
|
||||
agv_route_id = fields.Many2one('sf.agv.task.route', '任务路线')
|
||||
start_site_id = fields.Many2one('sf.agv.site', '起点接驳站', required=True)
|
||||
end_site_id = fields.Many2one('sf.agv.site', '终点接驳站', tracking=True)
|
||||
site_state = fields.Selection([
|
||||
('占用', '占用'),
|
||||
('空闲', '空闲')], string='终点接驳站状态', default='占用')
|
||||
state = fields.Selection([
|
||||
('待下发', '待下发'),
|
||||
('配送中', '配送中'),
|
||||
('已配送', '已配送'),
|
||||
('已取消', '已取消')], string='状态', default='待下发', tracking=True)
|
||||
workorder_ids = fields.Many2many('mrp.workorder', 'sf_agv_scheduling_mrp_workorder_ref', string='关联工单')
|
||||
task_create_time = fields.Datetime('任务创建时间')
|
||||
task_delivery_time = fields.Datetime('任务下发时间')
|
||||
task_completion_time = fields.Datetime('任务完成时间')
|
||||
task_duration = fields.Char('任务时长', compute='_compute_task_duration')
|
||||
|
||||
@api.depends('agv_route_type')
|
||||
def _compute_delivery_workpieces(self):
|
||||
for record in self:
|
||||
if record.agv_route_type == '运送空料架':
|
||||
record.delivery_workpieces = '/'
|
||||
else:
|
||||
record.delivery_workpieces = '、'.join(record.workorder_ids.mapped('production_id.name'))
|
||||
|
||||
delivery_workpieces = fields.Char('配送工件', compute=_compute_delivery_workpieces)
|
||||
|
||||
@api.model
|
||||
def web_search_read(self, domain=None, fields=None, offset=0, limit=None, order=None, count_limit=None):
|
||||
domain = domain or []
|
||||
new_domain = []
|
||||
for index, item in enumerate(domain):
|
||||
if isinstance(item, list):
|
||||
if item[0] == 'delivery_workpieces':
|
||||
new_domain.append('&')
|
||||
new_domain.append(['workorder_ids.production_id.name', item[1], item[2]])
|
||||
new_domain.append(['agv_route_type', '!=', '运送空料架'])
|
||||
continue
|
||||
new_domain.append(item)
|
||||
|
||||
return super(AgvScheduling, self).web_search_read(new_domain, fields, limit=limit, offset=offset)
|
||||
|
||||
@api.depends('task_completion_time', 'task_delivery_time')
|
||||
def _compute_task_duration(self):
|
||||
for rec in self:
|
||||
if rec.task_completion_time and rec.task_delivery_time:
|
||||
rec.task_duration = str(rec.task_completion_time - rec.task_delivery_time)
|
||||
else:
|
||||
rec.task_duration = ''
|
||||
|
||||
@api.model_create_multi
|
||||
def create(self, vals_list):
|
||||
# We generate a standard reference
|
||||
for vals in vals_list:
|
||||
vals['name'] = self.env['ir.sequence'].next_by_code('sf.agv.scheduling') or _('New')
|
||||
return super().create(vals_list)
|
||||
|
||||
def add_scheduling(self, agv_start_site_name, agv_route_type, workorders):
|
||||
""" add_scheduling(agv_start_site_id, agv_route_type, workorders) -> agv_scheduling
|
||||
新增AGV调度
|
||||
params:
|
||||
agv_start_site_name: AGV起点接驳站名称
|
||||
agv_route_type: AGV任务类型
|
||||
workorders: 工单
|
||||
"""
|
||||
_logger.info('创建AGV调度任务\r\n起点为【%s】,任务类型为【%s】,工单为【%s】' % (agv_start_site_name, agv_route_type, workorders))
|
||||
if not workorders:
|
||||
raise UserError(_('工单不能为空'))
|
||||
agv_start_site = self.env['sf.agv.site'].sudo().search([('name', '=', agv_start_site_name)], limit=1)
|
||||
if not agv_start_site:
|
||||
raise UserError(_('不存在名称为【%s】的接驳站,请先创建!' % agv_start_site_name))
|
||||
# 如果存在相同任务类型工单的AGV调度任务,则提示错误
|
||||
agv_scheduling = self.sudo().search([
|
||||
('workorder_ids', 'in', workorders.ids),
|
||||
('agv_route_type', '=', agv_route_type),
|
||||
('state', 'in', ['待下发', '配送中'])
|
||||
], limit=1)
|
||||
if agv_scheduling:
|
||||
# 计算agv_scheduling.workorder_ids与workorders的交集
|
||||
repetitive_workorders = agv_scheduling.workorder_ids & workorders
|
||||
raise RepeatTaskException(
|
||||
'制造订单号【%s】已存在于【%s】AGV调度任务,请勿重复下发!' %
|
||||
(','.join(repetitive_workorders.mapped('production_id.name')), agv_scheduling.name)
|
||||
)
|
||||
|
||||
vals = {
|
||||
'start_site_id': agv_start_site.id,
|
||||
'agv_route_type': agv_route_type,
|
||||
'workorder_ids': workorders.ids,
|
||||
# 'workpiece_delivery_ids': deliveries.mapped('id') if deliveries else [],
|
||||
'task_create_time': fields.Datetime.now()
|
||||
}
|
||||
# 如果只有唯一任务路线,则自动赋予终点接驳站跟任务名称
|
||||
agv_routes = self.env['sf.agv.task.route'].sudo().search([
|
||||
('route_type', '=', agv_route_type),
|
||||
('start_site_id', '=', agv_start_site.id)
|
||||
])
|
||||
if not agv_routes:
|
||||
raise UserError(_('不存在起点为【%s】的【%s】任务路线,请先创建!' % (agv_start_site_name, agv_route_type)))
|
||||
idle_route = None
|
||||
if len(agv_routes) == 1:
|
||||
idle_route = agv_routes[0]
|
||||
vals.update({'end_site_id': idle_route.end_site_id.id, 'agv_route_id': idle_route.id})
|
||||
else:
|
||||
# 判断终点接驳站是否为空闲
|
||||
idle_routes = agv_routes.filtered(lambda r: r.end_site_id.state == '空闲')
|
||||
if idle_routes:
|
||||
# 将空闲的路线按照终点接驳站名称排序
|
||||
idle_routes = sorted(idle_routes, key=lambda r: r.end_site_id.name)
|
||||
idle_route = idle_routes[0]
|
||||
vals.update({'end_site_id': idle_route.end_site_id.id, 'agv_route_id': idle_route.id})
|
||||
try:
|
||||
scheduling = self.env['sf.agv.scheduling'].sudo().create(vals)
|
||||
# 触发空闲接驳站状态更新,触发新任务下发
|
||||
if idle_route and idle_route.end_site_id.state == '空闲':
|
||||
scheduling.dispatch_scheduling(idle_route)
|
||||
|
||||
except Exception as e:
|
||||
_logger.error('添加AGV调度任务失败: %s', e)
|
||||
raise UserError(_('添加AGV调度任务失败: %s', e))
|
||||
|
||||
return scheduling
|
||||
|
||||
def on_site_state_change(self, agv_site_id, agv_site_state):
|
||||
"""
|
||||
响应AGV接驳站站点状态变化
|
||||
params:
|
||||
agv_site_id: 接驳站ID
|
||||
agv_site_state: 站点状态('空闲', '占用')
|
||||
"""
|
||||
if agv_site_state == '空闲':
|
||||
# 查询终点接驳站为agv_site_id的AGV路线
|
||||
task_routes = self.env['sf.agv.task.route'].sudo().search([('end_site_id', '=', agv_site_id)])
|
||||
agv_scheduling = self.env['sf.agv.scheduling'].sudo().search(
|
||||
[('state', '=', '待下发'), ('agv_route_type', 'in', task_routes.mapped('route_type'))],
|
||||
order='id asc',
|
||||
limit=1
|
||||
)
|
||||
task_route = task_routes.filtered(
|
||||
lambda r: r.start_site_id == agv_scheduling.start_site_id and r.start_site_id == agv_scheduling.start_site_id
|
||||
)
|
||||
if task_route:
|
||||
# 下发AGV调度任务并修改接驳站状态为占用
|
||||
agv_scheduling.dispatch_scheduling(task_route)
|
||||
|
||||
def _delivery_avg(self):
|
||||
config = self.env['res.config.settings'].get_values()
|
||||
position_code_arr = [{
|
||||
'positionCode': self.start_site_id.name,
|
||||
'code': '00'
|
||||
}, {
|
||||
'positionCode': self.end_site_id.name,
|
||||
'code': '00'
|
||||
}]
|
||||
res = {'reqCode': self.name, 'reqTime': '', 'clientCode': '', 'tokenCode': '',
|
||||
'taskTyp': 'F01', 'ctnrTyp': '', 'ctnrCode': '', 'wbCode': config['wbcode'],
|
||||
'positionCodePath': position_code_arr,
|
||||
'podCode': '',
|
||||
'podDir': '', 'materialLot': '', 'priority': '', 'taskCode': '', 'agvCode': '', 'materialLot': '',
|
||||
'data': ''}
|
||||
try:
|
||||
logging.info('AGV请求路径:%s' % config['agv_rcs_url'])
|
||||
logging.info('AGV-json:%s' % res)
|
||||
headers = {'Content-Type': 'application/json'}
|
||||
ret = requests.post((config['agv_rcs_url']), json=res, headers=headers)
|
||||
ret = ret.json()
|
||||
logging.info('config-ret:%s' % ret)
|
||||
if ret['code'] == 0:
|
||||
return True
|
||||
else:
|
||||
raise UserError(ret['message'])
|
||||
except Exception as e:
|
||||
logging.info('config-e:%s' % e)
|
||||
raise UserError("工件配送请求agv失败:%s" % e)
|
||||
|
||||
def button_cancel(self):
|
||||
# 弹出二次确认窗口后执行
|
||||
for rec in self:
|
||||
if rec.state != '待下发':
|
||||
raise UserError('只有待下发状态的AGV调度任务才能取消!')
|
||||
rec.state = '已取消'
|
||||
|
||||
def finish_scheduling(self):
|
||||
"""
|
||||
完成调度任务
|
||||
"""
|
||||
for rec in self:
|
||||
if rec.state != '配送中':
|
||||
return False
|
||||
_logger.info('AGV任务调度:完成任务%s' % rec)
|
||||
rec.state = '已配送'
|
||||
rec.task_completion_time = fields.Datetime.now()
|
||||
|
||||
def dispatch_scheduling(self, agv_task_route):
|
||||
"""
|
||||
下发调度任务
|
||||
params:
|
||||
agv_route sf.agv.task.route对象
|
||||
"""
|
||||
for rec in self:
|
||||
if rec.state != '待下发':
|
||||
return False
|
||||
_logger.info('AGV任务调度:下发调度任务,路线为%s' % agv_task_route)
|
||||
rec.state = '配送中'
|
||||
rec.task_delivery_time = fields.Datetime.now()
|
||||
rec.site_state = '空闲'
|
||||
rec.end_site_id = agv_task_route.end_site_id.id
|
||||
rec.agv_route_id = agv_task_route.id
|
||||
is_agv_task_dispatch = self.env['ir.config_parameter'].sudo().get_param('is_agv_task_dispatch')
|
||||
if is_agv_task_dispatch:
|
||||
rec._delivery_avg()
|
||||
# 更新接驳站状态
|
||||
rec.env['sf.agv.site'].update_site_state({rec.end_site_id.name: '占用'}, False)
|
||||
|
||||
def write(self, vals):
|
||||
if vals.get('state', False):
|
||||
if vals['state'] == '已取消':
|
||||
self.env['sf.workpiece.delivery'].search([('agv_scheduling_id', '=', self.id)]).write({'status': '待下发'})
|
||||
elif vals['state'] == '已配送':
|
||||
self.env['sf.workpiece.delivery'].search([('agv_scheduling_id', '=', self.id)]).write({
|
||||
'status': '已配送',
|
||||
'feeder_station_destination_id': self.end_site_id.id,
|
||||
'route_id': self.agv_route_id.id,
|
||||
'task_completion_time': fields.Datetime.now()
|
||||
})
|
||||
elif vals['state'] == '配送中':
|
||||
self.env['sf.workpiece.delivery'].search([('agv_scheduling_id', '=', self.id)]).write({
|
||||
'feeder_station_destination_id': self.end_site_id.id,
|
||||
'route_id': self.agv_route_id.id,
|
||||
'task_delivery_time': fields.Datetime.now()
|
||||
})
|
||||
return super().write(vals)
|
||||
|
||||
|
||||
class ResMrpWorkOrder(models.Model):
|
||||
_inherit = 'mrp.workorder'
|
||||
|
||||
agv_scheduling_ids = fields.Many2many(
|
||||
'sf.agv.scheduling',
|
||||
'sf_agv_scheduling_mrp_workorder_ref',
|
||||
string='AGV调度',
|
||||
domain=[('state', '!=', '已取消')])
|
||||
@@ -5,50 +5,77 @@ import time
|
||||
from odoo import fields, models, api
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class AgvSetting(models.Model):
|
||||
_name = 'sf.agv.site'
|
||||
_description = 'agv站点'
|
||||
|
||||
name = fields.Char('位置编号')
|
||||
owning_region = fields.Char('所属区域')
|
||||
# owning_region = fields.Char('所属区域')
|
||||
state = fields.Selection([
|
||||
('占用', '占用'),
|
||||
('空闲', '空闲')], string='状态')
|
||||
divide_the_work = fields.Char('主要分工')
|
||||
active = fields.Boolean('有效', default=True)
|
||||
workcenter_id = fields.Many2one(string='所属区域', comodel_name='mrp.workcenter', tracking=True,
|
||||
domain=[('is_agv_scheduling', '=', True)])
|
||||
|
||||
def update_site_state(self):
|
||||
# 调取中控的接驳站接口并修改对应站点的状态
|
||||
config = self.env['res.config.settings'].get_values()
|
||||
# token = sf_sync_config['token'Ba F2CF5DCC-1A00-4234-9E95-65603F70CC8A]
|
||||
headers = {'Authorization': config['center_control_Authorization']}
|
||||
center_control_url = config['center_control_url'] + "/AutoDeviceApi/GetAgvStationState?date="
|
||||
timestamp = int(time.time())
|
||||
center_control_url += str(timestamp)
|
||||
logging.info('工件配送-请求中控地址:%s' % center_control_url)
|
||||
try:
|
||||
center_control_r = requests.get(center_control_url, headers=headers, timeout=10) # 设置超时为60秒
|
||||
ret = center_control_r.json()
|
||||
logging.info('工件配送-请求中控站点信息:%s' % ret)
|
||||
self.env['center_control.interface.log'].sudo().create(
|
||||
{'content': ret, 'name': 'AutoDeviceApi/GetAgvStationState?date=%s' % str(timestamp)})
|
||||
if ret['Succeed'] is True:
|
||||
datas = ret['Datas']
|
||||
for item in self:
|
||||
for da in datas:
|
||||
if da['DeviceId'] == item.name:
|
||||
if da['AtHome'] is True:
|
||||
item.state = '占用'
|
||||
else:
|
||||
item.state = '空闲'
|
||||
return True
|
||||
except requests.exceptions.Timeout:
|
||||
logging.error('工件配送-请求中控接口超时')
|
||||
return False
|
||||
except requests.exceptions.RequestException as e:
|
||||
logging.error('工件配送-请求中控接口错误: %s', e)
|
||||
return False
|
||||
# name必须唯一
|
||||
_sql_constraints = [
|
||||
('name_uniq', 'unique (name)', '站点编号必须唯一!'),
|
||||
]
|
||||
|
||||
# def update_site_state(self):
|
||||
# # 调取中控的接驳站接口并修改对应站点的状态
|
||||
# config = self.env['res.config.settings'].get_values()
|
||||
# # token = sf_sync_config['token'Ba F2CF5DCC-1A00-4234-9E95-65603F70CC8A]
|
||||
# headers = {'Authorization': config['center_control_Authorization']}
|
||||
# center_control_url = config['center_control_url'] + "/AutoDeviceApi/GetAgvStationState?date="
|
||||
# timestamp = int(time.time())
|
||||
# center_control_url += str(timestamp)
|
||||
# logging.info('工件配送-请求中控地址:%s' % center_control_url)
|
||||
# try:
|
||||
# center_control_r = requests.get(center_control_url, headers=headers, timeout=10) # 设置超时为60秒
|
||||
# ret = center_control_r.json()
|
||||
# logging.info('工件配送-请求中控站点信息:%s' % ret)
|
||||
# self.env['center_control.interface.log'].sudo().create(
|
||||
# {'content': ret, 'name': 'AutoDeviceApi/GetAgvStationState?date=%s' % str(timestamp)})
|
||||
# if ret['Succeed'] is True:
|
||||
# datas = ret['Datas']
|
||||
# for item in self:
|
||||
# for da in datas:
|
||||
# if da['DeviceId'] == item.name:
|
||||
# if da['AtHome'] is True:
|
||||
# item.state = '占用'
|
||||
# else:
|
||||
# item.state = '空闲'
|
||||
# return True
|
||||
# except requests.exceptions.Timeout:
|
||||
# logging.error('工件配送-请求中控接口超时')
|
||||
# return False
|
||||
# except requests.exceptions.RequestException as e:
|
||||
# logging.error('工件配送-请求中控接口错误: %s', e)
|
||||
# return False
|
||||
|
||||
def update_site_state(self, agv_site_state_arr, notify=True):
|
||||
"""
|
||||
更新接驳站状态
|
||||
params:
|
||||
agv_site_state_arr: {'A01': '空闲', 'B01': '占用'}
|
||||
notify: 是否通知调度(非中控发起的状态改变不触发调度任务)
|
||||
"""
|
||||
if isinstance(agv_site_state_arr, dict):
|
||||
for agv_site_name, is_occupy in agv_site_state_arr.items():
|
||||
agv_site = self.env['sf.agv.site'].sudo().search([('name', '=', agv_site_name)])
|
||||
if agv_site:
|
||||
agv_site.state = is_occupy
|
||||
if notify:
|
||||
self.env['sf.agv.scheduling'].on_site_state_change(agv_site.id, agv_site.state)
|
||||
else:
|
||||
_logger.error("更新失败:接驳站站点错误!%s" % agv_site_name)
|
||||
raise UserError("更新失败:接驳站站点错误!")
|
||||
|
||||
|
||||
class AgvTaskRoute(models.Model):
|
||||
@@ -71,6 +98,17 @@ class AgvTaskRoute(models.Model):
|
||||
if self.end_site_id == self.start_site_id:
|
||||
raise UserError("您选择的终点接驳站与起点接驳站重复,请重新选择")
|
||||
|
||||
workcenter_id = fields.Many2one(string='所属区域', comodel_name='mrp.workcenter', domain=[('is_agv_scheduling', '=', True)],
|
||||
compute="_compute_region")
|
||||
|
||||
@api.depends('end_site_id')
|
||||
def _compute_region(self):
|
||||
for record in self:
|
||||
if record.end_site_id:
|
||||
record.workcenter_id = record.end_site_id.workcenter_id
|
||||
else:
|
||||
record.workcenter_id = None
|
||||
|
||||
|
||||
class Center_controlInterfaceLog(models.Model):
|
||||
_name = 'center_control.interface.log'
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import base64
|
||||
import datetime
|
||||
import logging
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import requests
|
||||
from itertools import groupby
|
||||
from odoo import api, fields, models, _
|
||||
from collections import defaultdict, namedtuple
|
||||
from odoo import api, fields, models, SUPERUSER_ID, _
|
||||
from odoo.exceptions import UserError, ValidationError
|
||||
from odoo.addons.sf_base.commons.common import Common
|
||||
from odoo.tools import float_compare, float_round, float_is_zero, format_datetime
|
||||
@@ -24,6 +28,46 @@ class MrpProduction(models.Model):
|
||||
work_order_state = fields.Selection([('未排', '未排'), ('已排', '已排'), ('已完成', '已完成')],
|
||||
string='工单状态', default='未排')
|
||||
|
||||
detection_result_ids = fields.One2many('sf.detection.result', 'production_id', '检测报告')
|
||||
tool_state = fields.Selection([('0', '正常'), ('1', '缺刀'), ('2', '无效刀')], string='功能刀具状态', default='0',
|
||||
store=True, compute='_compute_tool_state')
|
||||
tool_state_remark = fields.Text(string='功能刀具状态备注(缺刀)', compute='_compute_tool_state_remark', store=True)
|
||||
tool_state_remark2 = fields.Text(string='功能刀具状态备注(无效刀)', readonly=True)
|
||||
|
||||
@api.depends('workorder_ids.tool_state_remark')
|
||||
def _compute_tool_state_remark(self):
|
||||
for item in self:
|
||||
if item.workorder_ids:
|
||||
workorder_ids = item.workorder_ids.filtered(lambda a: a.state not in ['rework', 'done', 'cancel'])
|
||||
if workorder_ids.filtered(lambda a: a.tool_state == '1'):
|
||||
work_ids = workorder_ids.filtered(lambda a: a.tool_state == '1')
|
||||
tool_state_remark = ''
|
||||
for work_id in work_ids:
|
||||
if tool_state_remark == '':
|
||||
tool_state_remark = f'{work_id.tool_state_remark}'
|
||||
else:
|
||||
tool_state_remark = f"{tool_state_remark}\n{work_id.tool_state_remark}"
|
||||
item.tool_state_remark = tool_state_remark
|
||||
else:
|
||||
item.tool_state_remark = False
|
||||
|
||||
@api.depends('workorder_ids.tool_state')
|
||||
def _compute_tool_state(self):
|
||||
for item in self:
|
||||
if item.workorder_ids:
|
||||
tool_state = item.tool_state
|
||||
workorder_ids = item.workorder_ids.filtered(lambda a: a.state not in ['rework', 'done', 'cancel'])
|
||||
if workorder_ids.filtered(lambda a: a.tool_state == '2'):
|
||||
item.tool_state = '2'
|
||||
elif workorder_ids.filtered(lambda a: a.tool_state == '1'):
|
||||
item.tool_state = '1'
|
||||
else:
|
||||
item.tool_state = '0'
|
||||
if tool_state == '2' and item.tool_state != '2':
|
||||
item.detection_result_ids.filtered(
|
||||
lambda a: a.detailed_reason == '无效功能刀具' and a.handle_result == '待处理').write(
|
||||
{'handle_result': '已处理'})
|
||||
|
||||
# state = fields.Selection(selection_add=[
|
||||
# ('pending_scheduling', '待排程'),
|
||||
# ('pending_processing', '待加工'),
|
||||
@@ -34,9 +78,11 @@ class MrpProduction(models.Model):
|
||||
('confirmed', '待排程'),
|
||||
('pending_cam', '待加工'),
|
||||
('progress', '加工中'),
|
||||
('rework', '返工'),
|
||||
('scrap', '报废'),
|
||||
('to_close', 'To Close'),
|
||||
('done', 'Done'),
|
||||
('cancel', 'Cancelled')], string='State',
|
||||
('cancel', '已取消')], string='State',
|
||||
compute='_compute_state', copy=False, index=True, readonly=True,
|
||||
store=True, tracking=True,
|
||||
help=" * Draft: The MO is not confirmed yet.\n"
|
||||
@@ -51,10 +97,13 @@ class MrpProduction(models.Model):
|
||||
programming_no = fields.Char('编程单号')
|
||||
work_state = fields.Char('业务状态')
|
||||
programming_state = fields.Selection(
|
||||
[('编程中', '编程中'), ('已编程', '已编程')], string='编程状态', tracking=True)
|
||||
[('编程中', '编程中'), ('已编程', '已编程'), ('已编程未下发', '已编程未下发'), ('已下发', '已下发')],
|
||||
string='编程状态',
|
||||
tracking=True)
|
||||
glb_file = fields.Binary("glb模型文件")
|
||||
production_line_id = fields.Many2one('sf.production.line', string='生产线', tracking=True)
|
||||
plan_start_processing_time = fields.Datetime('计划开始加工时间')
|
||||
is_rework = fields.Boolean(string='是否返工', default=False)
|
||||
# production_line_state = fields.Selection(
|
||||
# [('待上产线', '待上产线'), ('已上产线', '已上产线'), ('已下产线', '已下产线')],
|
||||
# string='上/下产线', default='待上产线', tracking=True)
|
||||
@@ -74,11 +123,42 @@ class MrpProduction(models.Model):
|
||||
# 上传零件图纸
|
||||
part_drawing = fields.Binary('零件图纸')
|
||||
|
||||
manual_quotation = fields.Boolean('人工编程', default=False, readonly=True)
|
||||
rework_production = fields.Many2one('mrp.production', string='返工的制造订单')
|
||||
@api.depends('product_id.manual_quotation')
|
||||
def _compute_manual_quotation(self):
|
||||
for item in self:
|
||||
item.manual_quotation = item.product_id.manual_quotation
|
||||
|
||||
manual_quotation = fields.Boolean('人工编程', default=False, compute=_compute_manual_quotation, store=True)
|
||||
is_scrap = fields.Boolean('是否报废', default=False)
|
||||
is_remanufacture = fields.Boolean('是否重新制造', default=False)
|
||||
remanufacture_count = fields.Integer("重新制造订单数量", compute='_compute_remanufacture_production_ids')
|
||||
remanufacture_production_id = fields.Many2one('mrp.production', string='')
|
||||
|
||||
@api.depends('remanufacture_production_id')
|
||||
def _compute_remanufacture_production_ids(self):
|
||||
for production in self:
|
||||
if production.remanufacture_production_id:
|
||||
remanufacture_production = self.env['mrp.production'].search(
|
||||
[('id', '=', production.remanufacture_production_id.id)])
|
||||
if remanufacture_production:
|
||||
production.remanufacture_count = len(remanufacture_production)
|
||||
else:
|
||||
production.remanufacture_count = 0
|
||||
|
||||
def action_view_remanufacture_productions(self):
|
||||
self.ensure_one()
|
||||
mrp_production = self.env['mrp.production'].search(
|
||||
[('id', '=', self.remanufacture_production_id.id)])
|
||||
action = {
|
||||
'res_model': 'mrp.production',
|
||||
'type': 'ir.actions.act_window',
|
||||
'view_mode': 'form',
|
||||
'res_id': mrp_production.id,
|
||||
}
|
||||
return action
|
||||
|
||||
@api.depends(
|
||||
'move_raw_ids.state', 'move_raw_ids.quantity_done', 'move_finished_ids.state',
|
||||
'move_raw_ids.state', 'move_raw_ids.quantity_done', 'move_finished_ids.state', 'tool_state',
|
||||
'workorder_ids.state', 'product_qty', 'qty_producing', 'schedule_state')
|
||||
def _compute_state(self):
|
||||
for production in self:
|
||||
@@ -114,12 +194,38 @@ class MrpProduction(models.Model):
|
||||
if (
|
||||
production.state == 'to_close' or production.state == 'progress') and production.schedule_state == '未排':
|
||||
production.state = 'confirmed'
|
||||
elif production.state == 'pending_cam' and production.schedule_state == '未排':
|
||||
production.state = 'confirmed'
|
||||
elif production.state == 'to_close' and production.schedule_state == '已排':
|
||||
production.state = 'pending_cam'
|
||||
|
||||
if production.state == 'progress':
|
||||
if all(wo_state not in ('progress', 'done') for wo_state in production.workorder_ids.mapped('state')):
|
||||
if all(wo_state not in ('progress', 'done', 'rework', 'scrap') for wo_state in
|
||||
production.workorder_ids.mapped('state')):
|
||||
production.state = 'pending_cam'
|
||||
if production.is_rework is True:
|
||||
production.state = 'rework'
|
||||
# if production.state == 'pending_cam':
|
||||
# if all(wo_state in 'done' for wo_state in production.workorder_ids.mapped('state')):
|
||||
# production.state = 'done'
|
||||
if any(
|
||||
(
|
||||
wo.test_results == '返工' and wo.state == 'done' and production.programming_state in [
|
||||
'已编程']) or (
|
||||
wo.state == 'rework' and production.programming_state == '编程中') or (
|
||||
wo.is_rework is True and wo.state == 'done' and production.programming_state in ['编程中',
|
||||
'已编程'])
|
||||
for wo in
|
||||
production.workorder_ids):
|
||||
production.state = 'rework'
|
||||
if any(wo.test_results == '报废' and wo.state == 'done' for wo in production.workorder_ids):
|
||||
production.state = 'scrap'
|
||||
if any(dr.test_results == '报废' and dr.handle_result == '已处理' for dr in
|
||||
production.detection_result_ids):
|
||||
production.state = 'cancel'
|
||||
# 如果制造订单的功能刀具为【无效刀】则制造订单状态改为返工
|
||||
if production.tool_state == '2':
|
||||
production.state = 'rework'
|
||||
|
||||
def action_check(self):
|
||||
"""
|
||||
@@ -150,27 +256,64 @@ class MrpProduction(models.Model):
|
||||
for production in self:
|
||||
production.maintenance_count = len(production.request_ids)
|
||||
|
||||
# 制造订单报废:编程单更新
|
||||
def updateCNC(self):
|
||||
# 获取cloud编程单的状态
|
||||
def _cron_get_programming_state(self):
|
||||
try:
|
||||
res = {'production_no': self.name, 'programming_no': self.programming_no,
|
||||
'order_no': self.origin}
|
||||
if not self:
|
||||
reproduction = self.env['mrp.production'].search(
|
||||
[('state', '=', 'rework'), ('programming_state', '=', '编程中'), ('is_rework', '=', True)])
|
||||
else:
|
||||
reproduction = self
|
||||
if reproduction:
|
||||
programming_no_set = set([str(item.programming_no) for item in reproduction])
|
||||
programming_no = list(programming_no_set)
|
||||
programming_no_str = ','.join(programming_no)
|
||||
res = {'programming_no': programming_no_str}
|
||||
logging.info('res=%s:' % res)
|
||||
configsettings = self.env['res.config.settings'].get_values()
|
||||
config_header = Common.get_headers(self, configsettings['token'], configsettings['sf_secret_key'])
|
||||
url = '/api/intelligent_programming/get_state'
|
||||
config_url = configsettings['sf_url'] + url
|
||||
ret = requests.post(config_url, json=res, data=None, headers=config_header)
|
||||
ret = ret.json()
|
||||
result = json.loads(ret['result'])
|
||||
if result['status'] == 1:
|
||||
for item in result['programming_list']:
|
||||
if not self:
|
||||
for rp in reproduction:
|
||||
if rp.programming_no == item['programming_no']:
|
||||
rp.write({'programming_state': '已编程未下发' if item[
|
||||
'programming_state'] == '已编程' else '编程中'})
|
||||
|
||||
else:
|
||||
return item
|
||||
|
||||
else:
|
||||
raise UserError(ret['message'])
|
||||
except Exception as e:
|
||||
logging.info('cron_get_programming_state error:%s' % e)
|
||||
|
||||
# 编程单更新
|
||||
def update_programming_state(self):
|
||||
try:
|
||||
res = {'programming_no': self.programming_no,
|
||||
'manufacturing_type': 'rework' if self.is_scrap is False else 'scrap'}
|
||||
logging.info('res=%s:' % res)
|
||||
configsettings = self.env['res.config.settings'].get_values()
|
||||
config_header = Common.get_headers(self, configsettings['token'], configsettings['sf_secret_key'])
|
||||
url = '/api/intelligent_programming/update_intelligent_programmings'
|
||||
url = '/api/intelligent_programming/reset_state_again'
|
||||
config_url = configsettings['sf_url'] + url
|
||||
res['token'] = configsettings['token']
|
||||
ret = requests.post(config_url, json={}, data=res, headers=config_header)
|
||||
ret = requests.post(config_url, json=res, data=None, headers=config_header)
|
||||
ret = ret.json()
|
||||
logging.info('updateCNC-ret:%s' % ret)
|
||||
if ret['status'] == 1:
|
||||
self.write({'work_state': '已编程'})
|
||||
result = json.loads(ret['result'])
|
||||
logging.info('update_programming_state-ret:%s' % result)
|
||||
if result['status'] == 1:
|
||||
self.write({'is_rework': True})
|
||||
else:
|
||||
raise UserError(ret['message'])
|
||||
except Exception as e:
|
||||
logging.info('updateCNC error:%s' % e)
|
||||
raise UserError("更新程单失败,请联系管理员")
|
||||
logging.info('update_programming_state error:%s' % e)
|
||||
raise UserError("更新编程单状态失败,请联系管理员")
|
||||
|
||||
# cnc程序获取
|
||||
def fetchCNC(self, production_names):
|
||||
@@ -196,7 +339,7 @@ class MrpProduction(models.Model):
|
||||
'material_type_code': self.env['sf.materials.model'].search(
|
||||
[('id', '=', cnc.product_id.materials_type_id.id)]).materials_no,
|
||||
'machining_processing_panel': cnc.product_id.model_processing_panel,
|
||||
'machining_precision': cnc.product_id.model_machining_precision,
|
||||
'machining_precision': '',
|
||||
'embryo_long': cnc.product_id.bom_ids.bom_line_ids.product_id.length,
|
||||
'embryo_height': cnc.product_id.bom_ids.bom_line_ids.product_id.height,
|
||||
'embryo_width': cnc.product_id.bom_ids.bom_line_ids.product_id.width,
|
||||
@@ -333,44 +476,45 @@ class MrpProduction(models.Model):
|
||||
# self.env['mrp.workorder'].json_workorder_str(k, production, route))
|
||||
# 表面工艺工序
|
||||
# 获取表面工艺id
|
||||
if production.product_id.model_process_parameters_ids:
|
||||
logging.info('model_process_parameters_ids:%s' % production.product_id.model_process_parameters_ids)
|
||||
surface_technics_arr = []
|
||||
# 工序id
|
||||
route_workcenter_arr = []
|
||||
for item in production.product_id.product_model_type_id.surface_technics_routing_tmpl_ids:
|
||||
surface_technics_arr.append(item.route_workcenter_id.surface_technics_id.id)
|
||||
route_workcenter_arr.append(item.route_workcenter_id.id)
|
||||
if surface_technics_arr:
|
||||
production_process_category = self.env['sf.production.process.category'].search(
|
||||
[('production_process_ids.id', 'in', surface_technics_arr)],
|
||||
order='sequence asc'
|
||||
)
|
||||
# 用filter刷选表面工艺id'是否存在工艺类别对象里
|
||||
if production_process_category:
|
||||
for p in production_process_category:
|
||||
logging.info('production_process_category:%s' % p.name)
|
||||
production_process = p.production_process_ids.filtered(
|
||||
lambda pp: pp.id in surface_technics_arr)
|
||||
if production_process:
|
||||
process_parameter = production.product_id.model_process_parameters_ids.filtered(
|
||||
lambda pm: pm.process_id.id == production_process.id)
|
||||
if process_parameter:
|
||||
# 产品为表面工艺服务的供应商
|
||||
product_production_process = self.env['product.template'].search(
|
||||
[('server_product_process_parameters_id', '=', process_parameter.id)])
|
||||
if product_production_process:
|
||||
route_production_process = self.env[
|
||||
'mrp.routing.workcenter'].search(
|
||||
[('surface_technics_id', '=', production_process.id),
|
||||
('id', 'in', route_workcenter_arr)])
|
||||
if route_production_process:
|
||||
workorders_values.append(
|
||||
self.env[
|
||||
'mrp.workorder']._json_workorder_surface_process_str(
|
||||
production, route_production_process,
|
||||
process_parameter,
|
||||
product_production_process.seller_ids[0].partner_id.id))
|
||||
# 工序id
|
||||
surface_technics_arr = []
|
||||
route_workcenter_arr = []
|
||||
for item in production.product_id.product_model_type_id.surface_technics_routing_tmpl_ids:
|
||||
if item.route_workcenter_id.surface_technics_id.id:
|
||||
for process_param in production.product_id.model_process_parameters_ids:
|
||||
logging.info('process_param:%s%s' % (process_param.id, process_param.name))
|
||||
if item.route_workcenter_id.surface_technics_id == process_param.process_id:
|
||||
logging.info(
|
||||
'surface_technics_id:%s%s' % (item.route_workcenter_id.surface_technics_id.id,
|
||||
item.route_workcenter_id.surface_technics_id.name))
|
||||
surface_technics_arr.append(item.route_workcenter_id.surface_technics_id.id)
|
||||
route_workcenter_arr.append(item.route_workcenter_id.id)
|
||||
if surface_technics_arr:
|
||||
production_process = self.env['sf.production.process'].search(
|
||||
[('id', 'in', surface_technics_arr)],
|
||||
order='sequence asc'
|
||||
)
|
||||
for p in production_process:
|
||||
logging.info('production_process:%s' % p.name)
|
||||
# if production_process:
|
||||
process_parameter = production.product_id.model_process_parameters_ids.filtered(
|
||||
lambda pm: pm.process_id.id == p.id)
|
||||
if process_parameter:
|
||||
# 产品为表面工艺服务的供应商
|
||||
product_production_process = self.env['product.template'].search(
|
||||
[('server_product_process_parameters_id', '=', process_parameter.id)])
|
||||
if product_production_process:
|
||||
route_production_process = self.env[
|
||||
'mrp.routing.workcenter'].search(
|
||||
[('surface_technics_id', '=', p.id),
|
||||
('id', 'in', route_workcenter_arr)])
|
||||
if route_production_process:
|
||||
workorders_values.append(
|
||||
self.env[
|
||||
'mrp.workorder']._json_workorder_surface_process_str(
|
||||
production, route_production_process,
|
||||
process_parameter,
|
||||
product_production_process.seller_ids[0].partner_id.id))
|
||||
elif production.product_id.categ_id.type == '坯料':
|
||||
embryo_routing_workcenter = self.env['sf.embryo.model.type.routing.sort'].search(
|
||||
[('embryo_model_type_id', '=', production.product_id.embryo_model_type_id.id)],
|
||||
@@ -429,28 +573,6 @@ class MrpProduction(models.Model):
|
||||
for workorder in production.workorder_ids:
|
||||
workorder.duration_expected = workorder._get_duration_expected()
|
||||
|
||||
# 在之前的销售单上重新生成制造订单
|
||||
def create_production1_values(self, production):
|
||||
production_values_str = {'origin': production.origin,
|
||||
'product_id': production.product_id.id,
|
||||
'product_description_variants': production.product_description_variants,
|
||||
'product_qty': production.product_qty,
|
||||
'product_uom_id': production.product_uom_id.id,
|
||||
'location_src_id': production.location_src_id.id,
|
||||
'location_dest_id': production.location_dest_id.id,
|
||||
'bom_id': production.bom_id.id,
|
||||
'date_deadline': production.date_deadline,
|
||||
'date_planned_start': production.date_planned_start,
|
||||
'date_planned_finished': production.date_planned_finished,
|
||||
'procurement_group_id': False,
|
||||
'propagate_cancel': production.propagate_cancel,
|
||||
'orderpoint_id': production.orderpoint_id.id,
|
||||
'picking_type_id': production.picking_type_id.id,
|
||||
'company_id': production.company_id.id,
|
||||
'move_dest_ids': production.move_dest_ids.ids,
|
||||
'user_id': production.user_id.id}
|
||||
return production_values_str
|
||||
|
||||
# 工单排序
|
||||
def _reset_work_order_sequence1(self, k):
|
||||
for rec in self:
|
||||
@@ -519,70 +641,93 @@ class MrpProduction(models.Model):
|
||||
|
||||
def _reset_work_order_sequence(self):
|
||||
for rec in self:
|
||||
sequence_list = {}
|
||||
workorder_ids = rec.workorder_ids.filtered(lambda item: item.state in ('返工', 'rework'))
|
||||
# 产品模型类型
|
||||
model_type_id = rec.product_id.product_model_type_id
|
||||
# 产品加工面板
|
||||
model_processing_panel = rec.product_id.model_processing_panel
|
||||
if model_type_id:
|
||||
if model_processing_panel:
|
||||
tmpl_num = 1
|
||||
panel_list = model_processing_panel.split(',')
|
||||
for panel in panel_list:
|
||||
panel_sequence_list = {}
|
||||
# 成品工序
|
||||
product_routing_tmpl_ids = model_type_id.product_routing_tmpl_ids
|
||||
if product_routing_tmpl_ids:
|
||||
for tmpl_id in product_routing_tmpl_ids:
|
||||
panel_sequence_list.update({tmpl_id.route_workcenter_id.name: tmpl_num})
|
||||
tmpl_num += 1
|
||||
sequence_list.update({panel: panel_sequence_list})
|
||||
# 表面工艺工序
|
||||
# 模型类型的表面工艺工序模版
|
||||
surface_tmpl_ids = model_type_id.surface_technics_routing_tmpl_ids
|
||||
# 产品选择的表面工艺
|
||||
model_process_parameters_ids = rec.product_id.model_process_parameters_ids
|
||||
process_dict = {}
|
||||
if model_process_parameters_ids:
|
||||
for process_parameters_id in model_process_parameters_ids:
|
||||
process_id = process_parameters_id.process_id
|
||||
for surface_tmpl_id in surface_tmpl_ids:
|
||||
if process_id == surface_tmpl_id.route_workcenter_id.surface_technics_id:
|
||||
surface_tmpl_name = surface_tmpl_id.route_workcenter_id.name
|
||||
process_dict.update({int(process_id.category_id.code): '%s-%s' % (
|
||||
surface_tmpl_name, process_parameters_id.name)})
|
||||
process_list = sorted(process_dict.keys())
|
||||
for process_num in process_list:
|
||||
sequence_list.update({process_dict.get(process_num): tmpl_num})
|
||||
tmpl_num += 1
|
||||
# 坯料工序
|
||||
tmpl_num = 1
|
||||
embryo_routing_tmpl_ids = model_type_id.embryo_routing_tmpl_ids
|
||||
if embryo_routing_tmpl_ids:
|
||||
for tmpl_id in embryo_routing_tmpl_ids:
|
||||
sequence_list.update({tmpl_id.route_workcenter_id.name: tmpl_num})
|
||||
if not workorder_ids:
|
||||
sequence_list = {}
|
||||
if model_type_id:
|
||||
if model_processing_panel:
|
||||
tmpl_num = 1
|
||||
panel_list = model_processing_panel.split(',')
|
||||
for panel in panel_list:
|
||||
panel_sequence_list = {}
|
||||
# 成品工序
|
||||
product_routing_tmpl_ids = model_type_id.product_routing_tmpl_ids
|
||||
if product_routing_tmpl_ids:
|
||||
for tmpl_id in product_routing_tmpl_ids:
|
||||
panel_sequence_list.update({tmpl_id.route_workcenter_id.name: tmpl_num})
|
||||
tmpl_num += 1
|
||||
sequence_list.update({panel: panel_sequence_list})
|
||||
# 表面工艺工序
|
||||
# 模型类型的表面工艺工序模版
|
||||
surface_tmpl_ids = model_type_id.surface_technics_routing_tmpl_ids
|
||||
# 产品选择的表面工艺参数
|
||||
model_process_parameters_ids = rec.product_id.model_process_parameters_ids
|
||||
process_dict = {}
|
||||
if model_process_parameters_ids:
|
||||
for process_parameters_id in model_process_parameters_ids:
|
||||
process_id = process_parameters_id.process_id
|
||||
for surface_tmpl_id in surface_tmpl_ids:
|
||||
if process_id == surface_tmpl_id.route_workcenter_id.surface_technics_id:
|
||||
surface_tmpl_name = surface_tmpl_id.route_workcenter_id.name
|
||||
process_dict.update({int(process_id.sequence): '%s-%s' % (
|
||||
surface_tmpl_name, process_parameters_id.name)})
|
||||
process_list = sorted(process_dict.keys())
|
||||
for process_num in process_list:
|
||||
sequence_list.update({process_dict.get(process_num): tmpl_num})
|
||||
tmpl_num += 1
|
||||
# 坯料工序
|
||||
tmpl_num = 1
|
||||
embryo_routing_tmpl_ids = model_type_id.embryo_routing_tmpl_ids
|
||||
if embryo_routing_tmpl_ids:
|
||||
for tmpl_id in embryo_routing_tmpl_ids:
|
||||
sequence_list.update({tmpl_id.route_workcenter_id.name: tmpl_num})
|
||||
tmpl_num += 1
|
||||
else:
|
||||
raise ValidationError('该产品【加工面板】为空!')
|
||||
else:
|
||||
raise ValidationError('该产品【加工面板】为空!')
|
||||
|
||||
else:
|
||||
raise ValidationError('该产品没有选择【模版类型】!')
|
||||
|
||||
for work in rec.workorder_ids:
|
||||
if sequence_list.get(work.name):
|
||||
work.sequence = sequence_list[work.name]
|
||||
elif sequence_list.get(work.processing_panel):
|
||||
processing_panel = sequence_list.get(work.processing_panel)
|
||||
if processing_panel.get(work.name):
|
||||
work.sequence = processing_panel[work.name]
|
||||
raise ValidationError('该产品没有选择【模版类型】!')
|
||||
logging.info('sequence_list: %s' % sequence_list)
|
||||
for work in rec.workorder_ids:
|
||||
work_name = work.name
|
||||
logging.info(work_name)
|
||||
if sequence_list.get(work_name):
|
||||
work.sequence = sequence_list[work_name]
|
||||
elif sequence_list.get(work.processing_panel):
|
||||
processing_panel = sequence_list.get(work.processing_panel)
|
||||
if processing_panel.get(work_name):
|
||||
work.sequence = processing_panel[work_name]
|
||||
else:
|
||||
raise ValidationError('工序【%s】在产品选择的模版类型中不存在!' % work.name)
|
||||
else:
|
||||
raise ValidationError('工序【%s】在产品选择的模版类型中不存在!' % work.name)
|
||||
else:
|
||||
raise ValidationError('工序【%s】在产品选择的模版类型中不存在!' % work.name)
|
||||
# if work.name == '获取CNC加工程序':
|
||||
# work.button_start()
|
||||
# #work.fetchCNC()
|
||||
# work.button_finish()
|
||||
# 当单个面触发返工时,将新生成的工单插入到返工工单下方,并且后面的所以工单工序重排
|
||||
elif rec.workorder_ids.filtered(lambda item: item.sequence == 0):
|
||||
# 获取新增的返工工单
|
||||
work_ids = rec.workorder_ids.filtered(lambda item: item.sequence == 0)
|
||||
# 获取当前返工面最后一个工单工序
|
||||
sequence_max = sorted(
|
||||
rec.workorder_ids.filtered(lambda item: item.processing_panel == work_ids[0].processing_panel),
|
||||
key=lambda item: item.sequence, reverse=True)[0].sequence
|
||||
# 对当前返工工单之后的工单工序进行重排
|
||||
work_order_ids = rec.workorder_ids.filtered(lambda item: item.sequence > sequence_max)
|
||||
for work_id in work_order_ids:
|
||||
work_id.sequence = work_id.sequence + 3
|
||||
# 生成新增的返工工单的工序
|
||||
# 成品工序
|
||||
panel_sequence_list = {}
|
||||
product_routing_tmpl_ids = model_type_id.product_routing_tmpl_ids
|
||||
if product_routing_tmpl_ids:
|
||||
for tmpl_id in product_routing_tmpl_ids:
|
||||
sequence_max += 1
|
||||
panel_sequence_list.update({tmpl_id.route_workcenter_id.name: sequence_max})
|
||||
for work_id in work_ids:
|
||||
work_name = work_id.name
|
||||
if panel_sequence_list.get(work_name):
|
||||
work_id.sequence = panel_sequence_list[work_name]
|
||||
|
||||
# 创建工单并进行排序
|
||||
def _create_workorder(self, item):
|
||||
@@ -590,6 +735,52 @@ class MrpProduction(models.Model):
|
||||
self._reset_work_order_sequence()
|
||||
return True
|
||||
|
||||
def process_range_time(self):
|
||||
for production in self:
|
||||
works = production.workorder_ids
|
||||
pro_plan = self.env['sf.production.plan'].search([('production_id', '=', production.id)], limit=1)
|
||||
if not pro_plan:
|
||||
continue
|
||||
type_map = {'装夹预调': False, 'CNC加工': False, '解除装夹': False}
|
||||
# 最后一次加工结束时间
|
||||
last_time = pro_plan.date_planned_start
|
||||
# 预置时间
|
||||
for work in works:
|
||||
count = type_map.get(work.routing_type)
|
||||
date_planned_end = None
|
||||
date_planned_start = None
|
||||
duration_expected = datetime.timedelta(minutes=work.duration_expected)
|
||||
reserve_time = datetime.timedelta(minutes=work.reserved_duration)
|
||||
if not count:
|
||||
# 第一轮加工
|
||||
if work.routing_type == '装夹预调':
|
||||
date_planned_end = last_time - reserve_time
|
||||
date_planned_start = date_planned_end - duration_expected
|
||||
elif work.routing_type == 'CNC加工':
|
||||
date_planned_start = last_time
|
||||
date_planned_end = last_time + duration_expected
|
||||
last_time = date_planned_end
|
||||
else:
|
||||
date_planned_start = last_time + reserve_time
|
||||
date_planned_end = date_planned_start + duration_expected
|
||||
last_time = date_planned_end
|
||||
type_map.update({work.routing_type: True})
|
||||
else:
|
||||
date_planned_start = last_time + reserve_time
|
||||
date_planned_end = date_planned_start + duration_expected
|
||||
last_time = date_planned_end
|
||||
work.leave_id.write({
|
||||
'date_from': date_planned_start,
|
||||
'date_to': date_planned_end,
|
||||
})
|
||||
# work.write({'date_planned_start': date_planned_start, 'date_planned_finished': date_planned_end})
|
||||
work.date_planned_start = date_planned_start
|
||||
work.date_planned_finished = date_planned_end
|
||||
routing_workcenter = self.env['mrp.routing.workcenter'].sudo().search(
|
||||
[('name', '=', work.routing_type)])
|
||||
|
||||
work.write({'date_planned_start': date_planned_start, 'date_planned_finished': date_planned_end,'duration_expected':routing_workcenter.time_cycle})
|
||||
|
||||
# 修改标记已完成方法
|
||||
def button_mark_done1(self):
|
||||
if not self.workorder_ids.filtered(lambda w: w.routing_type not in ['表面工艺']):
|
||||
@@ -631,9 +822,11 @@ class MrpProduction(models.Model):
|
||||
|
||||
for production in self:
|
||||
logging.info('qty_produced:%s' % production.qty_produced)
|
||||
if production.qty_produced == 0.0:
|
||||
production.qty_produced = 1.0
|
||||
production.write({
|
||||
'date_finished': fields.Datetime.now(),
|
||||
'product_qty': production.product_qty if production.qty_produced < 1.0 else production.qty_produced,
|
||||
'product_qty': production.qty_produced,
|
||||
'priority': '0',
|
||||
'is_locked': True,
|
||||
'state': 'done',
|
||||
@@ -683,3 +876,315 @@ class MrpProduction(models.Model):
|
||||
'view_mode': 'tree,form',
|
||||
})
|
||||
return action
|
||||
|
||||
# 报废
|
||||
def button_scrap_new(self):
|
||||
cloud_programming = self._cron_get_programming_state()
|
||||
return {
|
||||
'name': _('报废'),
|
||||
'type': 'ir.actions.act_window',
|
||||
'view_mode': 'form',
|
||||
'res_model': 'sf.production.wizard',
|
||||
'target': 'new',
|
||||
'context': {
|
||||
'default_production_id': self.id,
|
||||
'default_reprogramming_num': cloud_programming['reprogramming_num'],
|
||||
'default_programming_states': cloud_programming['programming_state'],
|
||||
'default_is_reprogramming': True if cloud_programming['programming_state'] in ['已下发'] else False
|
||||
}
|
||||
}
|
||||
|
||||
# 返工
|
||||
def button_rework(self):
|
||||
cloud_programming = None
|
||||
if self.programming_state in ['已编程']:
|
||||
cloud_programming = self._cron_get_programming_state()
|
||||
return {
|
||||
'name': _('返工'),
|
||||
'type': 'ir.actions.act_window',
|
||||
'view_mode': 'form',
|
||||
'res_model': 'sf.rework.wizard',
|
||||
'target': 'new',
|
||||
'context': {
|
||||
'default_production_id': self.id,
|
||||
'default_reprogramming_num': cloud_programming['reprogramming_num'],
|
||||
'default_programming_state': cloud_programming['programming_state'],
|
||||
'default_is_reprogramming': True if cloud_programming['programming_state'] in ['已下发'] else False
|
||||
}
|
||||
}
|
||||
|
||||
# 更新程序
|
||||
def do_update_program(self):
|
||||
program_production = self
|
||||
if len(program_production) >= 1:
|
||||
# same_product_id = None
|
||||
# is_not_same_product = 0
|
||||
for item in program_production:
|
||||
# if same_product_id is None:
|
||||
# same_product_id = item.product_id
|
||||
# if item.product_id != same_product_id:
|
||||
# is_not_same_product += 1
|
||||
if item.state != "rework" and item.programming_state != "已编程未下发":
|
||||
raise UserError("请选择状态为返工且已编程未下发的制造订单")
|
||||
# if is_not_same_product >= 1:
|
||||
# raise UserError("您选择的记录中含有其他产品的制造订单,请选择同一产品的制造订单")
|
||||
grouped_program_ids = {k: list(g) for k, g in groupby(program_production, key=lambda x: x.programming_no)}
|
||||
program_to_production_names = {}
|
||||
for programming_no, program_production in grouped_program_ids.items():
|
||||
program_to_production_names[programming_no] = [production.name for production in program_production]
|
||||
for production in self:
|
||||
if production.programming_no in program_to_production_names:
|
||||
productions_not_delivered = self.env['mrp.production'].search(
|
||||
[('programming_no', '=', production.programming_no), ('programming_state', '=', '已编程未下发')])
|
||||
rework_workorder = production.workorder_ids.filtered(lambda m: m.state == 'rework')
|
||||
if rework_workorder:
|
||||
for rework_item in rework_workorder:
|
||||
pending_workorder = production.workorder_ids.filtered(
|
||||
lambda m1: m1.state in [
|
||||
'pending'] and m1.processing_panel == rework_item.processing_panel and m1.routing_type == 'CNC加工')
|
||||
if not pending_workorder.cnc_ids:
|
||||
production.get_new_program(rework_item.processing_panel)
|
||||
# production.write({'state': 'progress', 'programming_state': '已编程', 'is_rework': False})
|
||||
productions_not_delivered.write(
|
||||
{'state': 'progress', 'programming_state': '已编程', 'is_rework': False})
|
||||
|
||||
# 从cloud获取重新编程过的最新程序
|
||||
def get_new_program(self, processing_panel):
|
||||
try:
|
||||
res = {'programming_no': self.programming_no, 'processing_panel': processing_panel}
|
||||
configsettings = self.env['res.config.settings'].get_values()
|
||||
config_header = Common.get_headers(self, configsettings['token'], configsettings['sf_secret_key'])
|
||||
url = '/api/intelligent_programming/get_new_program'
|
||||
config_url = configsettings['sf_url'] + url
|
||||
r = requests.post(config_url, json=res, data=None, headers=config_header)
|
||||
r = r.json()
|
||||
result = json.loads(r['result'])
|
||||
if result['status'] == 1:
|
||||
program_path_tmp_panel = os.path.join('/tmp', result['folder_name'], 'return', processing_panel)
|
||||
if os.path.exists(program_path_tmp_panel):
|
||||
files_r = os.listdir(program_path_tmp_panel)
|
||||
if files_r:
|
||||
for file_name in files_r:
|
||||
file_path = os.path.join(program_path_tmp_panel, file_name)
|
||||
os.remove(file_path)
|
||||
download_state = self.env['sf.cnc.processing'].download_file_tmp(result['folder_name'],
|
||||
processing_panel)
|
||||
if download_state is False:
|
||||
raise UserError('编程单号为%s的CNC程序文件从FTP拉取失败' % (self.programming_no))
|
||||
productions = self.env['mrp.production'].search(
|
||||
[('programming_no', '=', self.programming_no), ('state', 'not in', ('cancel', 'done'))])
|
||||
if productions:
|
||||
for production in productions:
|
||||
panel_workorder = production.workorder_ids.filtered(lambda
|
||||
pw: pw.processing_panel == processing_panel and pw.routing_type == 'CNC加工' and pw.state not in (
|
||||
'rework', 'done'))
|
||||
if panel_workorder:
|
||||
if panel_workorder.cmm_ids:
|
||||
panel_workorder.cmm_ids.sudo().unlink()
|
||||
if panel_workorder.cnc_ids:
|
||||
panel_workorder.cnc_ids.sudo().unlink()
|
||||
self.env['sf.cam.work.order.program.knife.plan'].sudo().unlink_cam_plan(
|
||||
production)
|
||||
# program_path_tmp_panel = os.path.join('C://Users//43484//Desktop//fsdownload//test',
|
||||
# processing_panel)
|
||||
logging.info('program_path_tmp_panel:%s' % program_path_tmp_panel)
|
||||
files_panel = os.listdir(program_path_tmp_panel)
|
||||
if files_panel:
|
||||
for file in files_panel:
|
||||
file_extension = os.path.splitext(file)[1]
|
||||
if file_extension.lower() == '.pdf':
|
||||
panel_file_path = os.path.join(program_path_tmp_panel, file)
|
||||
logging.info('panel_file_path:%s' % panel_file_path)
|
||||
panel_workorder.write(
|
||||
{'cnc_ids': panel_workorder.cnc_ids.sudo()._json_cnc_processing(processing_panel,
|
||||
result),
|
||||
'cmm_ids': panel_workorder.cmm_ids.sudo()._json_cmm_program(processing_panel, result),
|
||||
'cnc_worksheet': base64.b64encode(open(panel_file_path, 'rb').read())})
|
||||
logging.info('len(cnc_worksheet):%s' % len(panel_workorder.cnc_worksheet))
|
||||
pre_workorder = production.workorder_ids.filtered(lambda
|
||||
ap: ap.routing_type == '装夹预调' and ap.processing_panel == processing_panel and ap.state not in (
|
||||
'rework', 'done'))
|
||||
if pre_workorder:
|
||||
pre_workorder.write(
|
||||
{'processing_drawing': base64.b64encode(open(panel_file_path, 'rb').read())})
|
||||
# if production.state == 'rework' and production.programming_state == '已编程未下发':
|
||||
# production.write(
|
||||
# {'state': 'progress', 'programming_state': '已编程', 'is_rework': False})
|
||||
# logging.info('返工含有已编程未下发的程序更新完成:%s' % production.name)
|
||||
|
||||
else:
|
||||
raise UserError(result['message'])
|
||||
except Exception as e:
|
||||
logging.info('get_new_program error:%s' % e)
|
||||
raise UserError("从云平台获取最新程序失败,请联系管理员")
|
||||
|
||||
def recreateManufacturing(self, item):
|
||||
"""
|
||||
重新生成制造订单
|
||||
"""
|
||||
if self.is_scrap is True:
|
||||
procurement_requests = []
|
||||
sale_order = self.env['sale.order'].sudo().search([('name', '=', self.origin)])
|
||||
values = self.env['mrp.production'].create_production1_values(self)
|
||||
# productions = self.env['mrp.production'].with_user(SUPERUSER_ID).sudo().with_company(
|
||||
# self.company_id).create(
|
||||
# values)
|
||||
# 查询出库移动记录
|
||||
out_picking = self.env['stock.picking'].search(
|
||||
[('origin', '=', sale_order.name), ('name', 'ilike', 'WH/OUT/')])
|
||||
move = out_picking.move_ids.filtered(lambda pd: pd.product_id == self.product_id)
|
||||
move_values = {'product_description_variants': '',
|
||||
'date_planned': fields.Datetime.now(),
|
||||
'date_deadline': fields.Datetime.now(),
|
||||
'move_dest_ids': move,
|
||||
'group_id': move.group_id,
|
||||
'route_ids': [],
|
||||
'warehouse_id': self.warehouse_id,
|
||||
'priority': 0,
|
||||
'orderpoint_id': False,
|
||||
'product_packaging_id': False}
|
||||
procurement_requests.append(self.env['procurement.group'].Procurement(
|
||||
move.product_id, 1.0, move.product_uom,
|
||||
move.location_id, move.rule_id and move.rule_id.name or "/",
|
||||
sale_order.name, move.company_id, move_values))
|
||||
self.env['procurement.group'].run(procurement_requests,
|
||||
raise_user_error=not self.env.context.get('from_orderpoint'))
|
||||
productions = self.env['mrp.production'].sudo().search(
|
||||
[('origin', '=', self.origin)], order='id desc', limit=1)
|
||||
move = self.env['stock.move'].search([('origin', '=', productions.name)], order='id desc')
|
||||
for mo in move:
|
||||
if mo.procure_method == 'make_to_order' and mo.name != productions.name:
|
||||
if mo.name == '/':
|
||||
domain = [('barcode', '=', 'WH-PC'), ('sequence_code', '=', 'PC')]
|
||||
elif mo.name == '拉':
|
||||
domain = [('barcode', '=', 'WH-INTERNAL'), ('sequence_code', '=', 'INT')]
|
||||
picking_type = self.env['stock.picking.type'].search(domain)
|
||||
mo.write({'picking_type_id': picking_type.id})
|
||||
mo._assign_picking()
|
||||
else:
|
||||
if mo.reference != productions.name:
|
||||
mo.reference = productions.name
|
||||
if mo.production_id:
|
||||
if mo.production_id != productions:
|
||||
mo.production_id = False
|
||||
mo_move = self.env['stock.move'].search(
|
||||
[('origin', '=', sale_order.name), ('reference', 'ilike', 'WH/MO/')])
|
||||
if mo_move:
|
||||
sfp_move = self.env['stock.move'].search(
|
||||
[('origin', '=', sale_order.name), ('reference', 'ilike', 'WH/SFP/')], limit=1)
|
||||
mo_move.write({'reference': sfp_move.reference, 'partner_id': sfp_move.partner_id.id,
|
||||
'picking_id': sfp_move.picking_id.id, 'picking_type_id': sfp_move.picking_type_id.id,
|
||||
'production_id': False})
|
||||
productions.write({'programming_no': self.programming_no, 'is_remanufacture': True})
|
||||
# productions.procurement_group_id.mrp_production_ids.move_dest_ids.write(
|
||||
# {'group_id': self.env['procurement.group'].search([('name', '=', sale_order.name)])})
|
||||
stock_picking = None
|
||||
pc_picking = self.env['stock.picking'].search(
|
||||
[('origin', '=', productions.name), ('name', 'ilike', 'WH/PC/')])
|
||||
stock_picking = pc_picking
|
||||
int_picking = self.env['stock.picking'].search(
|
||||
[('origin', '=', productions.name), ('name', 'ilike', 'WH/INT/')])
|
||||
stock_picking |= int_picking
|
||||
for pick in stock_picking:
|
||||
if pick.move_ids:
|
||||
product_type_id = pick.move_ids[0].product_id.categ_id
|
||||
if product_type_id.name == '坯料':
|
||||
location_id = self.env['stock.location'].search([('name', '=', '坯料存货区')])
|
||||
if not location_id:
|
||||
logging.info(f'没有搜索到【坯料存货区】: {location_id}')
|
||||
break
|
||||
if pick.picking_type_id.name == '内部调拨':
|
||||
if pick.location_dest_id.product_type != product_type_id:
|
||||
pick.location_dest_id = location_id.id
|
||||
elif pick.picking_type_id.name == '生产发料':
|
||||
if pick.location_id.product_type != product_type_id:
|
||||
pick.location_id = location_id.id
|
||||
scarp_process_parameter_workorder = self.env['mrp.workorder'].search(
|
||||
[('surface_technics_parameters_id', '!=', False), ('production_id', '=', self.id),
|
||||
('is_subcontract', '=', True)])
|
||||
if scarp_process_parameter_workorder:
|
||||
production_programming = self.env['mrp.production'].search(
|
||||
[('programming_no', '=', self.programming_no), ('id', '!=', productions.id)], order='name asc')
|
||||
production_list = [production.name for production in production_programming]
|
||||
purchase_orders = self.env['purchase.order'].search([('origin', 'ilike', ','.join(production_list))])
|
||||
for purchase_item in purchase_orders.order_line:
|
||||
for process_item in scarp_process_parameter_workorder:
|
||||
if purchase_item.product_id.categ_type == '表面工艺':
|
||||
if purchase_item.product_id.server_product_process_parameters_id == process_item.surface_technics_parameters_id:
|
||||
print(purchase_orders.origin.find(productions.name))
|
||||
if purchase_orders.origin.find(productions.name) == -1:
|
||||
purchase_orders.origin += ',' + productions.name
|
||||
if item['is_reprogramming'] is False:
|
||||
productions._create_workorder(item)
|
||||
productions.programming_state = '已编程'
|
||||
else:
|
||||
productions.programming_state = '编程中'
|
||||
return productions
|
||||
|
||||
# 在之前的销售单上重新生成制造订单
|
||||
def create_production1_values(self, production):
|
||||
production_values_str = {'origin': production.origin,
|
||||
'product_id': production.product_id.id,
|
||||
'programming_state': '已编程',
|
||||
'product_description_variants': production.product_description_variants,
|
||||
'product_qty': production.product_qty,
|
||||
'product_uom_id': production.product_uom_id.id,
|
||||
'location_src_id': production.location_src_id.id,
|
||||
'location_dest_id': production.location_dest_id.id,
|
||||
'bom_id': production.bom_id.id,
|
||||
'date_deadline': production.date_deadline,
|
||||
'date_planned_start': production.date_planned_start,
|
||||
'date_planned_finished': production.date_planned_finished,
|
||||
# 'procurement_group_id': self.env["procurement.group"].create(
|
||||
# {'name': production.name}).id,
|
||||
'propagate_cancel': production.propagate_cancel,
|
||||
'orderpoint_id': production.orderpoint_id.id,
|
||||
'picking_type_id': production.picking_type_id.id,
|
||||
'company_id': production.company_id.id,
|
||||
'move_dest_ids': production.move_dest_ids.ids,
|
||||
'user_id': production.user_id.id}
|
||||
return production_values_str
|
||||
|
||||
|
||||
class sf_detection_result(models.Model):
|
||||
_name = 'sf.detection.result'
|
||||
_description = "检测结果"
|
||||
|
||||
production_id = fields.Many2one('mrp.production')
|
||||
processing_panel = fields.Char('加工面')
|
||||
routing_type = fields.Selection([
|
||||
('装夹预调', '装夹预调'),
|
||||
('CNC加工', 'CNC加工')], string="工序类型")
|
||||
|
||||
rework_reason = fields.Selection(
|
||||
[("programming", "编程"), ("cutter", "刀具"), ("clamping", "装夹"),
|
||||
("operate computer", "操机"),
|
||||
("technology", "工艺"), ("customer redrawing", "客户改图")], string="原因", tracking=True)
|
||||
detailed_reason = fields.Text('详细原因')
|
||||
test_results = fields.Selection([("合格", "合格"), ("返工", "返工"), ("报废", "报废")],
|
||||
string="检测结果", tracking=True)
|
||||
test_report = fields.Binary('检测报告', readonly=True)
|
||||
handle_result = fields.Selection([("待处理", "待处理"), ("已处理", "已处理")], default='', string="处理结果",
|
||||
tracking=True)
|
||||
|
||||
# 查看检测报告
|
||||
def button_look_test_report(self):
|
||||
return {
|
||||
'res_model': 'sf.detection.result',
|
||||
'type': 'ir.actions.act_window',
|
||||
'res_id': self.id,
|
||||
'views': [(self.env.ref('sf_manufacturing.sf_test_report_form').id, 'form')],
|
||||
# 'view_mode': 'form',
|
||||
# 'context': {
|
||||
# 'default_id': self.id
|
||||
# },
|
||||
'target': 'new'
|
||||
}
|
||||
|
||||
|
||||
class sf_processing_panel(models.Model):
|
||||
_name = 'sf.processing.panel'
|
||||
_description = "加工面"
|
||||
|
||||
name = fields.Char('加工面')
|
||||
active = fields.Boolean('有效', default=True)
|
||||
|
||||
@@ -21,7 +21,7 @@ class ResMrpRoutingWorkcenter(models.Model):
|
||||
workcenter_ids = fields.Many2many('mrp.workcenter', 'rel_workcenter_route', required=True)
|
||||
bom_id = fields.Many2one('mrp.bom', required=False)
|
||||
surface_technics_id = fields.Many2one('sf.production.process', string="表面工艺")
|
||||
|
||||
reserved_duration = fields.Float('预留时长', default=30, tracking=True)
|
||||
def get_no(self):
|
||||
international_standards = self.search(
|
||||
[('code', '!=', ''), ('active', 'in', [True, False])],
|
||||
|
||||
@@ -5,24 +5,50 @@ from odoo.addons.resource.models.resource import Intervals
|
||||
|
||||
|
||||
class ResWorkcenter(models.Model):
|
||||
_inherit = "mrp.workcenter"
|
||||
_name = "mrp.workcenter"
|
||||
_inherit = ['mrp.workcenter', 'mail.thread']
|
||||
|
||||
# 生产线显示
|
||||
production_line_show = fields.Char(string='生产线名称')
|
||||
equipment_id = fields.Many2one(
|
||||
'maintenance.equipment', string="设备",
|
||||
)
|
||||
equipment_id = fields.Many2one('maintenance.equipment', string="设备", tracking=True)
|
||||
production_line_id = fields.Many2one('sf.production.line', string='生产线',
|
||||
related='equipment_id.production_line_id', store=True)
|
||||
|
||||
is_process_outsourcing = fields.Boolean('工艺外协')
|
||||
users_ids = fields.Many2many("res.users", 'users_workcenter')
|
||||
users_ids = fields.Many2many("res.users", 'users_workcenter', tracking=True)
|
||||
|
||||
def write(self, vals):
|
||||
if 'users_ids' in vals:
|
||||
old_users = self.users_ids
|
||||
res = super(ResWorkcenter, self).write(vals)
|
||||
new_users = self.users_ids
|
||||
added_users = new_users - old_users
|
||||
removed_users = old_users - new_users
|
||||
if added_users or removed_users:
|
||||
message = "增加 → %s ; 移除 → %s (可操作用户)" % (
|
||||
# ','.join(added_users.mapped('name')), ','.join(removed_users.mapped('name')))
|
||||
added_users.mapped('name'), removed_users.mapped('name'))
|
||||
self.message_post(body=message)
|
||||
return res
|
||||
return super(ResWorkcenter, self).write(vals)
|
||||
|
||||
name = fields.Char('Work Center', related='resource_id.name', store=True, readonly=False, tracking=True)
|
||||
time_efficiency = fields.Float('Time Efficiency', related='resource_id.time_efficiency', default=100, store=True,
|
||||
readonly=False, tracking=True)
|
||||
default_capacity = fields.Float(
|
||||
'Capacity', default=1.0,
|
||||
help="Default number of pieces (in product UoM) that can be produced in parallel (at the same time) at this work center. For example: the capacity is 5 and you need to produce 10 units, then the operation time listed on the BOM will be multiplied by two. However, note that both time before and after production will only be counted once.",
|
||||
tracking=True)
|
||||
|
||||
oee_target = fields.Float(
|
||||
string='OEE Target', help="Overall Effective Efficiency Target in percentage", default=90, tracking=True)
|
||||
oee = fields.Float(compute='_compute_oee', help='Overall Equipment Effectiveness, based on the last month', store=True)
|
||||
|
||||
time_start = fields.Float('Setup Time', tracking=True)
|
||||
time_stop = fields.Float('Cleanup Time', tracking=True)
|
||||
costs_hour = fields.Float(string='Cost per hour', help='Hourly processing cost.', default=0.0, tracking=True)
|
||||
|
||||
equipment_status = fields.Selection(
|
||||
[("正常", "正常"), ("故障停机", "故障停机"), ("计划维保", "计划维保"),("空闲", "空闲"),("封存(报废)", "封存(报废)")],
|
||||
[("正常", "正常"), ("故障停机", "故障停机"), ("计划维保", "计划维保"), ("空闲", "空闲"), ("封存(报废)", "封存(报废)")],
|
||||
string="设备状态", related='equipment_id.state')
|
||||
|
||||
# @api.depends('equipment_id')
|
||||
@@ -99,6 +125,8 @@ class ResWorkcenter(models.Model):
|
||||
res[wc_id] = [(datetime.fromtimestamp(s), datetime.fromtimestamp(e)) for s, e, _ in final_intervals_wc]
|
||||
return res
|
||||
|
||||
# AGV是否可配送
|
||||
is_agv_scheduling = fields.Boolean(string="AGV所属区域", tracking=True)
|
||||
|
||||
class ResWorkcenterProductivity(models.Model):
|
||||
_inherit = 'mrp.workcenter.productivity'
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -5,8 +5,10 @@ import base64
|
||||
import hashlib
|
||||
import os
|
||||
from odoo import models, fields, api, _
|
||||
from odoo.exceptions import ValidationError
|
||||
from odoo.exceptions import ValidationError, UserError
|
||||
from odoo.modules import get_resource_path
|
||||
|
||||
|
||||
from OCC.Extend.DataExchange import read_step_file
|
||||
from OCC.Extend.DataExchange import write_stl_file
|
||||
|
||||
@@ -106,6 +108,15 @@ class ResProductMo(models.Model):
|
||||
|
||||
name = fields.Char('产品名称', compute='_compute_tool_name', store=True, required=False)
|
||||
|
||||
@api.constrains('seller_ids')
|
||||
def _check_seller_ids(self):
|
||||
if self.categ_type == '表面工艺':
|
||||
if self.seller_ids:
|
||||
if self.seller_ids[0].price == 0.0:
|
||||
raise UserError("请在该产品【采购】中的【价格】进行输入")
|
||||
else:
|
||||
raise UserError("请在【采购】中输入供应商信息")
|
||||
|
||||
@api.depends('cutting_tool_model_id', 'specification_id')
|
||||
def _compute_tool_name(self):
|
||||
for item in self:
|
||||
@@ -113,6 +124,10 @@ class ResProductMo(models.Model):
|
||||
name = '%s%s' % (item.cutting_tool_model_id.name, item.specification_id.name)
|
||||
item.name = name
|
||||
|
||||
def _get_process_parameters_product(self, production_process):
|
||||
return self.env['product.template'].search(
|
||||
[('server_product_process_parameters_id', '=', production_process.id)]).seller_ids[0]
|
||||
|
||||
@api.onchange('cutting_tool_model_id')
|
||||
def _onchange_cutting_tool_model_id(self):
|
||||
for item in self:
|
||||
@@ -640,6 +655,10 @@ class ResProductMo(models.Model):
|
||||
'part_number': item.get('part_number') or '',
|
||||
'active': True,
|
||||
}
|
||||
tax_id = self.env['account.tax'].sudo().search(
|
||||
[('type_tax_use', '=', 'sale'), ('amount', '=', item.get('tax')), ('price_include', '=', 'True')])
|
||||
if tax_id:
|
||||
vals.update({'taxes_id': [(6, 0, [int(tax_id)])]})
|
||||
copy_product_id.sudo().write(vals)
|
||||
product_id.product_tmpl_id.active = False
|
||||
return copy_product_id
|
||||
@@ -736,7 +755,11 @@ class ResProductMo(models.Model):
|
||||
# 产品名称唯一性校验
|
||||
for item in templates:
|
||||
if len(self.search([('name', '=', item.name)])) > 1:
|
||||
raise ValidationError('产品名称【%s】已存在' % item.name)
|
||||
raise UserError('产品名称【%s】已存在' % item.name)
|
||||
if item.categ_type == '表面工艺':
|
||||
if len(self.search([('server_product_process_parameters_id', '=',
|
||||
item.server_product_process_parameters_id.id)])) > 1:
|
||||
raise UserError('表面工艺参数为【%s】的产品已存在' % item.server_product_process_parameters_id.name)
|
||||
if "create_product_product" not in self._context:
|
||||
templates._create_variant_ids()
|
||||
|
||||
@@ -800,7 +823,7 @@ class ResProductFixture(models.Model):
|
||||
diameter = fields.Float('直径(mm)', digits=(16, 2))
|
||||
|
||||
# '零点卡盘' 字段
|
||||
weight = fields.Float('重量(mm)', digits=(16, 2))
|
||||
weight = fields.Float('重量(kg)', digits=(16, 2))
|
||||
orientation_dish_diameter = fields.Float('定位盘直径(mm)', digits=(16, 2))
|
||||
clamping_diameter = fields.Float('装夹直径(mm)', digits=(16, 2))
|
||||
clamping_num = fields.Selection([('1', '1'), ('2', '2'), ('4', '4'), ('6', '6'), ('8', '8')], string='装夹单元数')
|
||||
@@ -947,6 +970,7 @@ class SfMaintenanceEquipmentAndProductTemplate(models.Model):
|
||||
raise ValidationError("机床基坐标获取失败")
|
||||
|
||||
|
||||
|
||||
class SfMaintenanceEquipmentTool(models.Model):
|
||||
_name = 'maintenance.equipment.tool'
|
||||
_description = '机床刀位'
|
||||
|
||||
22
sf_manufacturing/models/res_config_setting.py
Normal file
22
sf_manufacturing/models/res_config_setting.py
Normal file
@@ -0,0 +1,22 @@
|
||||
from odoo import models, fields, api
|
||||
|
||||
|
||||
class ResConfigSettings(models.TransientModel):
|
||||
_inherit = 'res.config.settings'
|
||||
|
||||
is_agv_task_dispatch = fields.Boolean('是否下发AGV任务', default=False)
|
||||
|
||||
@api.model
|
||||
def get_values(self):
|
||||
values = super(ResConfigSettings, self).get_values()
|
||||
config = self.env['ir.config_parameter'].sudo()
|
||||
is_agv_task_dispatch = config.get_param('is_agv_task_dispatch')
|
||||
values.update(
|
||||
is_agv_task_dispatch=is_agv_task_dispatch,
|
||||
)
|
||||
return values
|
||||
|
||||
def set_values(self):
|
||||
super(ResConfigSettings, self).set_values()
|
||||
config = self.env['ir.config_parameter'].sudo()
|
||||
config.set_param("is_agv_task_dispatch", self.is_agv_task_dispatch or False)
|
||||
@@ -68,6 +68,7 @@ class StockRule(models.Model):
|
||||
|
||||
@api.model
|
||||
def _run_pull(self, procurements):
|
||||
logging.info(procurements)
|
||||
moves_values_by_company = defaultdict(list)
|
||||
mtso_products_by_locations = defaultdict(list)
|
||||
|
||||
@@ -168,7 +169,6 @@ class StockRule(models.Model):
|
||||
else:
|
||||
forecasted_qties_by_loc[rule.location_src_id][procurement.product_id.id] -= qty_needed
|
||||
procure_method = 'make_to_stock'
|
||||
|
||||
move_values = rule._get_stock_move_values(*procurement)
|
||||
move_values['procure_method'] = procure_method
|
||||
moves_values_by_company[procurement.company_id.id].append(move_values)
|
||||
@@ -176,10 +176,10 @@ class StockRule(models.Model):
|
||||
for company_id, moves_values in moves_values_by_company.items():
|
||||
# create the move as SUPERUSER because the current user may not have the rights to do it (mto product
|
||||
# launched by a sale for example)
|
||||
moves = self.env['stock.move'].with_user(SUPERUSER_ID).sudo().with_company(company_id).create(moves_values)
|
||||
moves = self.env['stock.move'].with_user(SUPERUSER_ID).sudo().with_company(company_id).create(
|
||||
moves_values)
|
||||
# Since action_confirm launch following procurement_group we should activate it.
|
||||
moves._action_confirm()
|
||||
|
||||
return True
|
||||
|
||||
@api.model
|
||||
@@ -204,7 +204,8 @@ class StockRule(models.Model):
|
||||
productions = self.env['mrp.production'].with_user(SUPERUSER_ID).sudo().with_company(company_id).create(
|
||||
productions_values)
|
||||
|
||||
self.env['stock.move'].sudo().create(productions._get_moves_raw_values())
|
||||
# self.env['stock.move'].sudo().create(productions._get_moves_raw_values())
|
||||
# self.env['stock.move'].sudo().create(productions._get_moves_finished_values())
|
||||
|
||||
'''
|
||||
创建工单
|
||||
@@ -216,6 +217,23 @@ class StockRule(models.Model):
|
||||
(
|
||||
p.move_dest_ids.procure_method != 'make_to_order' and not
|
||||
p.move_raw_ids and not p.workorder_ids)).action_confirm()
|
||||
# 处理 根据制造订单生成的采购单坯料入库时到原材料库,手动将原材料位置该为坯料存货区
|
||||
for production in productions:
|
||||
if production.picking_ids:
|
||||
product_type_id = production.picking_ids[0].move_ids[0].product_id.categ_id
|
||||
if product_type_id.name == '坯料':
|
||||
location_id = self.env['stock.location'].search([('name', '=', '坯料存货区')])
|
||||
if not location_id:
|
||||
logging.info(f'没有搜索到【坯料存货区】: {location_id}')
|
||||
break
|
||||
for picking_id in production.picking_ids:
|
||||
if picking_id.picking_type_id.name == '内部调拨':
|
||||
if picking_id.location_dest_id.product_type != product_type_id:
|
||||
picking_id.location_dest_id = location_id.id
|
||||
elif picking_id.picking_type_id.name == '生产发料':
|
||||
if picking_id.location_id.product_type != product_type_id:
|
||||
picking_id.location_id = location_id.id
|
||||
|
||||
for production in productions:
|
||||
'''
|
||||
创建制造订单时生成序列号
|
||||
@@ -270,14 +288,70 @@ class StockRule(models.Model):
|
||||
# 为同一个product_id创建一个生产订单名称列表
|
||||
product_id_to_production_names[product_id] = [production.name for production in all_production]
|
||||
for production_item in productions:
|
||||
production_programming = self.env['mrp.production'].search(
|
||||
[('product_id.id', '=', production_item.product_id.id),
|
||||
('origin', '=', production_item.origin)],
|
||||
limit=1, order='id asc')
|
||||
if production_item.product_id.id in product_id_to_production_names:
|
||||
if not production_programming.programming_no:
|
||||
if production_item.product_id.model_process_parameters_ids:
|
||||
is_purchase = False
|
||||
sorted_process_parameters = sorted(production_item.product_id.model_process_parameters_ids,
|
||||
key=lambda w: w.id)
|
||||
|
||||
consecutive_process_parameters = []
|
||||
m = 0
|
||||
for i in range(len(sorted_process_parameters) - 1):
|
||||
if m == 0:
|
||||
is_purchase = False
|
||||
if self.env['product.template']._get_process_parameters_product(
|
||||
sorted_process_parameters[i]).partner_id == self.env[
|
||||
'product.template']._get_process_parameters_product(sorted_process_parameters[
|
||||
i + 1]).partner_id and \
|
||||
sorted_process_parameters[i].gain_way == '外协':
|
||||
if sorted_process_parameters[i] not in consecutive_process_parameters:
|
||||
consecutive_process_parameters.append(sorted_process_parameters[i])
|
||||
consecutive_process_parameters.append(sorted_process_parameters[i + 1])
|
||||
m += 1
|
||||
continue
|
||||
else:
|
||||
if m == len(consecutive_process_parameters) - 1 and m != 0:
|
||||
self.env['purchase.order'].get_purchase_order(consecutive_process_parameters,
|
||||
production_item,
|
||||
product_id_to_production_names)
|
||||
if sorted_process_parameters[i] in consecutive_process_parameters:
|
||||
is_purchase = True
|
||||
consecutive_process_parameters = []
|
||||
m = 0
|
||||
# 当前面的连续外协采购单生成再生成当前外协采购单
|
||||
if is_purchase is False:
|
||||
self.env['purchase.order'].get_purchase_order(consecutive_process_parameters,
|
||||
production_item,
|
||||
product_id_to_production_names)
|
||||
if m == len(consecutive_process_parameters) - 1 and m != 0:
|
||||
self.env['purchase.order'].get_purchase_order(consecutive_process_parameters,
|
||||
production_item,
|
||||
product_id_to_production_names)
|
||||
if sorted_process_parameters[i] in consecutive_process_parameters:
|
||||
is_purchase = True
|
||||
consecutive_process_parameters = []
|
||||
m = 0
|
||||
if m == len(consecutive_process_parameters) - 1 and m != 0:
|
||||
self.env['purchase.order'].get_purchase_order(consecutive_process_parameters,
|
||||
production_item,
|
||||
product_id_to_production_names)
|
||||
if is_purchase is False and m == 0:
|
||||
if len(sorted_process_parameters) == 1:
|
||||
self.env['purchase.order'].get_purchase_order(sorted_process_parameters,
|
||||
production_item,
|
||||
product_id_to_production_names)
|
||||
else:
|
||||
self.env['purchase.order'].get_purchase_order(sorted_process_parameters[i],
|
||||
production_item,
|
||||
product_id_to_production_names)
|
||||
# # 同一个产品多个制造订单对应一个编程单和模型库
|
||||
# # 只调用一次fetchCNC,并将所有生产订单的名称作为字符串传递
|
||||
if not production_item.programming_no:
|
||||
production_programming = self.env['mrp.production'].search(
|
||||
[('product_id.id', '=', production_item.product_id.id),
|
||||
('origin', '=', production_item.origin)],
|
||||
limit=1, order='id asc')
|
||||
if not production_programming.programming_no:
|
||||
production_item.fetchCNC(
|
||||
', '.join(product_id_to_production_names[production_item.product_id.id]))
|
||||
@@ -367,7 +441,7 @@ class ProductionLot(models.Model):
|
||||
if product.tracking == "serial":
|
||||
last_serial = self.env['stock.lot'].search(
|
||||
[('company_id', '=', company.id), ('product_id', '=', product.id)],
|
||||
limit=1, order='id DESC')
|
||||
limit=1, order='name desc')
|
||||
if last_serial:
|
||||
if product.categ_id.name == '刀具':
|
||||
return self.env['stock.lot'].get_tool_generate_lot_names1(company, product)
|
||||
@@ -404,6 +478,7 @@ class ProductionLot(models.Model):
|
||||
|
||||
def print_single_method(self):
|
||||
|
||||
print('self.name========== %s' % self.name)
|
||||
self.ensure_one()
|
||||
qr_code_data = self.qr_code_image
|
||||
if not qr_code_data:
|
||||
@@ -466,12 +541,11 @@ class ProductionLot(models.Model):
|
||||
class StockPicking(models.Model):
|
||||
_inherit = 'stock.picking'
|
||||
|
||||
# workorder_in_id = fields.One2many('mrp.workorder', 'picking_in_id')
|
||||
# workorder_out_id = fields.One2many('mrp.workorder', 'picking_out_id')
|
||||
surface_technics_parameters_id = fields.Many2one('sf.production.process.parameter', string="表面工艺可选参数")
|
||||
|
||||
# 设置外协出入单的名称
|
||||
def _get_name_Res(self, rescode):
|
||||
last_picking = self.sudo().search([('name', 'like', rescode)], order='create_date desc,id desc', limit=1)
|
||||
last_picking = self.sudo().search([('name', 'ilike', rescode)], order='create_date desc,id desc', limit=1)
|
||||
if not last_picking:
|
||||
num = "%04d" % 1
|
||||
else:
|
||||
@@ -497,7 +571,7 @@ class StockPicking(models.Model):
|
||||
[('barcode', 'ilike', 'WH-PREPRODUCTION')]).id),
|
||||
('location_id', '=', self.env['stock.location'].search(
|
||||
[('barcode', 'ilike', 'VL-SPOC')]).id),
|
||||
('origin', '=', self.origin)])
|
||||
('origin', '=', self.origin), ('picking_id', '=', self.id)])
|
||||
if self.location_id == move_in.location_id and self.location_dest_id == move_in.location_dest_id:
|
||||
if move_out.origin == move_in.origin:
|
||||
if move_out.picking_id.state != 'done':
|
||||
@@ -514,7 +588,7 @@ class StockPicking(models.Model):
|
||||
[('barcode', 'ilike', 'VL-SPOC')]).id),
|
||||
('origin', '=', self.origin)])
|
||||
production = self.env['mrp.production'].search([('name', '=', self.origin)])
|
||||
if move_in:
|
||||
if move_in.state != 'done':
|
||||
move_in.write({'state': 'assigned'})
|
||||
self.env['stock.move.line'].create(move_in.get_move_line(production, None))
|
||||
|
||||
@@ -524,7 +598,7 @@ class StockPicking(models.Model):
|
||||
def create_outcontract_picking(self, sorted_workorders_arr, item):
|
||||
m = 0
|
||||
for sorted_workorders in sorted_workorders_arr:
|
||||
pick_ids = []
|
||||
# pick_ids = []
|
||||
if m == 0:
|
||||
outcontract_stock_move = self.env['stock.move'].search(
|
||||
[('workorder_id', '=', sorted_workorders.id), ('production_id', '=', item.id)])
|
||||
@@ -543,7 +617,7 @@ class StockPicking(models.Model):
|
||||
outcontract_picking_type_out))
|
||||
picking_out = self.create(
|
||||
moves_out._get_new_picking_values_Res(item, sorted_workorders, 'WH/OCOUT/'))
|
||||
pick_ids.append(picking_out.id)
|
||||
# pick_ids.append(picking_out.id)
|
||||
moves_out.write(
|
||||
{'picking_id': picking_out.id, 'state': 'waiting', 'workorder_id': sorted_workorders.id})
|
||||
moves_out._assign_picking_post_process(new=new_picking)
|
||||
@@ -552,12 +626,12 @@ class StockPicking(models.Model):
|
||||
outcontract_picking_type_in))
|
||||
picking_in = self.create(
|
||||
moves_in._get_new_picking_values_Res(item, sorted_workorders, 'WH/OCIN/'))
|
||||
pick_ids.append(picking_in.id)
|
||||
# pick_ids.append(picking_in.id)
|
||||
moves_in.write(
|
||||
{'picking_id': picking_in.id, 'state': 'waiting', 'workorder_id': sorted_workorders.id})
|
||||
moves_in._assign_picking_post_process(new=new_picking)
|
||||
m += 1
|
||||
sorted_workorders.write({'picking_ids': [(6, 0, pick_ids)]})
|
||||
# sorted_workorders.write({'picking_ids': [(6, 0, pick_ids)]})
|
||||
|
||||
|
||||
class ReStockMove(models.Model):
|
||||
@@ -588,6 +662,7 @@ class ReStockMove(models.Model):
|
||||
return {
|
||||
'name': self.env['stock.picking']._get_name_Res(rescode),
|
||||
'origin': item.name,
|
||||
'surface_technics_parameters_id': sorted_workorders.surface_technics_parameters_id.id,
|
||||
'company_id': self.mapped('company_id').id,
|
||||
'user_id': False,
|
||||
'move_type': self.mapped('group_id').move_type or 'direct',
|
||||
|
||||
@@ -29,7 +29,7 @@ access_mrp_workorder_group_sf_mrp_user,mrp_workorder,model_mrp_workorder,sf_base
|
||||
access_mrp_workorder_manager,mrp_workorder,model_mrp_workorder,sf_base.group_sf_mrp_manager,1,1,1,0
|
||||
access_mrp_workcenter_group_sf_mrp_user,mrp_workcenter,model_mrp_workcenter,sf_base.group_sf_mrp_user,1,0,0,0
|
||||
access_mrp_workcenter_manager,mrp_workcenter,model_mrp_workcenter,sf_base.group_sf_mrp_manager,1,1,1,0
|
||||
access_mrp_workcenter_productivity_group_sf_mrp_user,mrp_workcenter_productivity,model_mrp_workcenter_productivity,sf_base.group_sf_mrp_user,1,0,0,0
|
||||
access_mrp_workcenter_productivity_group_sf_equipment_user,mrp_workcenter_productivity,model_mrp_workcenter_productivity,sf_base.group_sf_equipment_user,1,1,1,0
|
||||
access_mrp_workcenter_productivity_manager,mrp_workcenter_productivity,model_mrp_workcenter_productivity,sf_base.group_sf_mrp_manager,1,1,1,0
|
||||
access_sf_workpiece_delivery_group_sf_order_user,sf_workpiece_delivery_group_sf_order_user,model_sf_workpiece_delivery,sf_base.group_sf_order_user,1,1,1,0
|
||||
access_sf_workpiece_delivery_group_sf_equipment_user,sf_workpiece_delivery_group_sf_equipment_user,model_sf_workpiece_delivery,sf_base.group_sf_equipment_user,1,1,0,0
|
||||
@@ -141,3 +141,21 @@ access_sf_model_type_group_sf_stock_manager,sf_model_type_group_sf_mrp_manager,m
|
||||
access_mrp_bom_byproduct_group_sf_stock_user,mrp_bom_byproduct_group_sf_stock_user,mrp.model_mrp_bom_byproduct,sf_base.group_sf_stock_user,1,0,0,0
|
||||
access_mrp_bom_byproduct_group_sf_stock_manager,mrp_bom_byproduct_group_sf_mrp_manager,mrp.model_mrp_bom_byproduct,sf_base.group_sf_stock_manager,1,0,0,0
|
||||
|
||||
access_sf_rework_wizard_group_sf_order_user,sf_rework_wizard_group_sf_order_user,model_sf_rework_wizard,sf_base.group_sf_order_user,1,1,1,0
|
||||
access_sf_rework_wizard_group_plan_dispatch,sf_rework_wizard_group_plan_dispatch,model_sf_rework_wizard,sf_base.group_plan_dispatch,1,1,1,0
|
||||
access_sf_detection_result_group_sf_order_user,sf_detection_result_group_sf_order_user,model_sf_detection_result,sf_base.group_sf_order_user,1,1,1,0
|
||||
access_sf_detection_result_group_plan_dispatch,sf_detection_result_group_plan_dispatch,model_sf_detection_result,sf_base.group_plan_dispatch,1,1,1,0
|
||||
access_sf_detection_result_group_sf_equipment_user,sf_detection_result_group_sf_equipment_user,model_sf_detection_result,sf_base.group_sf_equipment_user,1,1,1,0
|
||||
access_sf_processing_panel_group_sf_order_user,sf_processing_panel_group_sf_order_user,model_sf_processing_panel,sf_base.group_sf_order_user,1,1,1,0
|
||||
access_sf_production_wizard_group_sf_order_user,sf_production_wizard_group_sf_order_user,model_sf_production_wizard,sf_base.group_sf_order_user,1,1,1,0
|
||||
access_sf_processing_panel_group_plan_dispatch,sf_processing_panel_group_plan_dispatch,model_sf_processing_panel,sf_base.group_plan_dispatch,1,1,1,0
|
||||
|
||||
access_sf_agv_scheduling_admin,sf_agv_scheduling_admin,model_sf_agv_scheduling,base.group_system,1,1,1,1
|
||||
access_sf_agv_scheduling_group_sf_order_user,sf_agv_scheduling_group_sf_order_user,model_sf_agv_scheduling,sf_base.group_sf_order_user,1,1,1,0
|
||||
access_sf_agv_scheduling_group_sf_mrp_manager,sf_agv_scheduling_group_sf_mrp_manager,model_sf_agv_scheduling,sf_base.group_sf_mrp_manager,1,1,1,0
|
||||
access_sf_agv_scheduling_group_sf_equipment_user,sf_agv_scheduling_group_sf_equipment_user,model_sf_agv_scheduling,sf_base.group_sf_equipment_user,1,1,1,0
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@@ -1,16 +1,36 @@
|
||||
var RFID = ''
|
||||
$(document).off('keydown')
|
||||
console.log(2222)
|
||||
$(document).on('keydown', '.modal.d-block.o_technical_modal,body.o_web_client', function (e) {
|
||||
const dom = $('.customRFID')
|
||||
if(!dom.length) return
|
||||
$(document).on('keydown', 'body.o_web_client', function (e) {
|
||||
setTimeout(() => {
|
||||
RFID = ''
|
||||
|
||||
}, 200)
|
||||
if(e.key == 'Enter' && e.keyCode == 13 || e.key == 'Tab' && e.keyCode == 9){
|
||||
if(!RFID || RFID.length <= 3) return;
|
||||
dom.children('span').text(RFID)
|
||||
RFID = ''
|
||||
|
||||
let fieldValue1 = $('[name="routing_type"]');
|
||||
console.log('字段值:', fieldValue1.text());
|
||||
console.log(RFID)
|
||||
let fieldValue2 = $('[name="rfid_code"]');
|
||||
console.log('字段值2:', fieldValue2.text());
|
||||
// if(!RFID || RFID.length <= 3) return;
|
||||
// $('[name="button_start"]').trigger('click')
|
||||
// setTimeout(() => {
|
||||
// $('.o_dialog .modal-footer .btn-primary').trigger('click')
|
||||
// }, 50)
|
||||
// RFID = ''
|
||||
// return;
|
||||
|
||||
// fieldValue2.val() === '')
|
||||
// 检查字段值是否等于“装夹预调”
|
||||
if (fieldValue1.text() === '装夹预调') {
|
||||
if (!RFID || RFID.length <= 3) return;
|
||||
$('[name="button_start"]').trigger('click');
|
||||
setTimeout(() => {
|
||||
$('.o_dialog .modal-footer .btn-primary').trigger('click');
|
||||
}, 100);
|
||||
}
|
||||
|
||||
RFID = '';
|
||||
return;
|
||||
}
|
||||
RFID += e.key
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
odoo.define('sf_manufacturing.action_dispatch_confirm', function (require) {
|
||||
const core = require('web.core');
|
||||
const ajax = require('web.ajax');
|
||||
const Dialog = require('web.Dialog');
|
||||
var rpc = require('web.rpc');
|
||||
var _t = core._t;
|
||||
|
||||
async function dispatch_confirm(parent, {params}) {
|
||||
const dialog = new Dialog(parent, {
|
||||
title: "确认",
|
||||
$content: $('<div>').append("请确认是否仅配送" + params.workorder_count + "个工件?"),
|
||||
buttons: [
|
||||
{ text: "确认", classes: 'btn-primary', close: true, click: () => dispatchConfirmed(parent, params) },
|
||||
{ text: "取消", close: true },
|
||||
],
|
||||
});
|
||||
dialog.open();
|
||||
|
||||
|
||||
async function dispatchConfirmed(parent, params) {
|
||||
console.log(parent, 'parent')
|
||||
rpc.query({
|
||||
model: 'sf.workpiece.delivery.wizard',
|
||||
method: 'confirm',
|
||||
args: [params.active_id]
|
||||
,
|
||||
kwargs: {
|
||||
context: params.context,
|
||||
}
|
||||
}).then(res => {
|
||||
parent.services.action.doAction({
|
||||
'type': 'ir.actions.client',
|
||||
'tag': 'display_notification',
|
||||
'target': 'new',
|
||||
'params': {
|
||||
'message': '任务下发成功!AGV任务调度编号为【' + res.name + '】',
|
||||
'type': 'success',
|
||||
'sticky': false,
|
||||
'next': {'type': 'ir.actions.act_window_close'},
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
core.action_registry.add('dispatch_confirm', dispatch_confirm);
|
||||
return dispatch_confirm;
|
||||
});
|
||||
57
sf_manufacturing/static/src/xml/button_show_on_tree.xml
Normal file
57
sf_manufacturing/static/src/xml/button_show_on_tree.xml
Normal file
@@ -0,0 +1,57 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<templates xml:space="preserve">
|
||||
<t t-name="sf_manufacturing.button_show" t-inherit="web.ListView.Buttons" t-inherit-mode="extension" owl="1">
|
||||
<xpath expr="//div/t[@t-if='nbSelected']" position="after">
|
||||
<t t-elif="!nbSelected">
|
||||
<t t-foreach="archInfo.headerButtons" t-as="button" t-key="button.id">
|
||||
<t t-if="button.modifiers.force_show">
|
||||
<ListViewHeaderButton
|
||||
list="model.root"
|
||||
clickParams="button.clickParams"
|
||||
defaultRank="button.defaultRank"
|
||||
domain="props.domain"
|
||||
icon="button.icon"
|
||||
string="button.string"
|
||||
title="button.title"
|
||||
className="button.className+' ms-2'"
|
||||
/>
|
||||
</t>
|
||||
</t>
|
||||
</t>
|
||||
</xpath>
|
||||
<xpath expr="//div/t[@t-if='nbSelected']" position="replace">
|
||||
<t t-if="nbSelected">
|
||||
<t t-foreach="archInfo.headerButtons" t-as="button" t-key="button.id">
|
||||
<t t-if="!button.modifiers.force_show">
|
||||
<ListViewHeaderButton
|
||||
list="model.root"
|
||||
clickParams="button.clickParams"
|
||||
defaultRank="button.defaultRank"
|
||||
domain="props.domain"
|
||||
icon="button.icon"
|
||||
string="button.string"
|
||||
title="button.title"
|
||||
/>
|
||||
</t>
|
||||
</t>
|
||||
<t t-if="!env.isSmall">
|
||||
<t t-call="web.ListView.Selection"/>
|
||||
</t>
|
||||
<t t-foreach="archInfo.headerButtons" t-as="button" t-key="button.id">
|
||||
<t t-if="button.modifiers.force_show == 1">
|
||||
<ListViewHeaderButton
|
||||
list="model.root"
|
||||
clickParams="button.clickParams"
|
||||
defaultRank="button.defaultRank"
|
||||
domain="props.domain"
|
||||
icon="button.icon"
|
||||
string="button.string"
|
||||
title="button.title"
|
||||
className="button.className+' ms-2'"
|
||||
/>
|
||||
</t>
|
||||
</t>
|
||||
</t>
|
||||
</xpath>
|
||||
</t>
|
||||
</templates>
|
||||
79
sf_manufacturing/views/agv_scheduling_views.xml
Normal file
79
sf_manufacturing/views/agv_scheduling_views.xml
Normal file
@@ -0,0 +1,79 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
<!-- agv站点 -->
|
||||
<record id="view_agv_scheduling_tree" model="ir.ui.view">
|
||||
<field name="name">agv调度</field>
|
||||
<field name="model">sf.agv.scheduling</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree editable="bottom" delete="0" create="0">
|
||||
<field name="state" widget="badge"
|
||||
decoration-success="state == '已配送'"
|
||||
decoration-warning="state == '待下发'"
|
||||
decoration-danger="state == '配送中'"
|
||||
decoration-info="state == '已取消'"
|
||||
/>
|
||||
<field name="agv_route_type" invisible="1"/>
|
||||
<field name="name"/>
|
||||
<field name="agv_route_id"/>
|
||||
<field name="start_site_id"/>
|
||||
<field name="end_site_id"/>
|
||||
<field name="site_state"/>
|
||||
<field name="delivery_workpieces"/>
|
||||
<field name="task_create_time" readonly="1"/>
|
||||
<field name="task_delivery_time" readonly="1"/>
|
||||
<field name="task_completion_time" readonly="1"/>
|
||||
<field name="task_duration" readonly="1"/>
|
||||
|
||||
<button
|
||||
name="button_cancel"
|
||||
string="取消" type="object"
|
||||
attrs="{'invisible': ['|', ('state', '!=', '待下发'), ('agv_route_type', '=', '运送空料架')]}"
|
||||
icon="fa-times"
|
||||
class="btn-danger"
|
||||
confirm="你确定要取消这条记录吗?"
|
||||
/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_agv_scheduling_search" model="ir.ui.view">
|
||||
<field name="name">sf.agv.scheduling.search</field>
|
||||
<field name="model">sf.agv.scheduling</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="AGV调度">
|
||||
<field name="name"/>
|
||||
<field name="agv_route_id"/>
|
||||
<field name="start_site_id"/>
|
||||
<field name="end_site_id"/>
|
||||
<field name="delivery_workpieces"/>
|
||||
<field name="state" string="状态"/>
|
||||
<filter name="filter_to_be_issued" string="待下发" domain="[('state', 'in', ['待下发'])]"/>
|
||||
<filter name="filter_delivering" string="配送中" domain="[('state', 'in', ['配送中'])]"/>
|
||||
<filter name="filter_delivered" string="已配送" domain="[('state', 'in', ['已配送'])]"/>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_agv_scheduling_tree" model="ir.actions.act_window">
|
||||
<field name="name">AGV调度</field>
|
||||
<field name="res_model">sf.agv.scheduling</field>
|
||||
<field name="view_mode">tree</field>
|
||||
<field name="context">
|
||||
{
|
||||
"search_default_filter_to_be_issued": 1,
|
||||
"search_default_filter_delivering": 1,
|
||||
}
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<menuitem
|
||||
id="menu_action_agv_scheduling"
|
||||
name="AGV调度"
|
||||
sequence="28"
|
||||
action="action_agv_scheduling_tree"
|
||||
parent="mrp.menu_mrp_manufacturing"
|
||||
groups="sf_base.group_sf_order_user,sf_base.group_sf_mrp_manager,sf_base.group_sf_equipment_user"
|
||||
/>
|
||||
</data>
|
||||
</odoo>
|
||||
@@ -8,7 +8,7 @@
|
||||
<field name="arch" type="xml">
|
||||
<tree editable="bottom">
|
||||
<field name="name" required="1" attrs="{'readonly': [('id', '!=', False)]}"/>
|
||||
<field name="owning_region" required="1" attrs="{'readonly': [('id', '!=', False)]}"/>
|
||||
<field name="workcenter_id" required="1" options="{'no_create': True}"/>
|
||||
<field name="state" required="1" attrs="{'readonly': [('id', '!=', False)]}"/>
|
||||
<field name="divide_the_work" required="1"/>
|
||||
</tree>
|
||||
@@ -40,8 +40,9 @@
|
||||
<field name="start_site_id" required="1" options="{'no_create': True}" string="起点接驳站"
|
||||
attrs="{'readonly': [('id', '!=', False)]}"/>
|
||||
<field name="end_site_id" required="1" options="{'no_create': True}" string="终点接驳站"/>
|
||||
<field name="destination_production_line_id" required="1" options="{'no_create': True}"
|
||||
attrs="{'readonly': [('id', '!=', False)]}"/>
|
||||
<!-- <field name="destination_production_line_id" required="1" options="{'no_create': True}"-->
|
||||
<!-- attrs="{'readonly': [('id', '!=', False)]}"/>-->
|
||||
<field name="workcenter_id"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
|
||||
<record id="custom_mrp_production_tree_view" model="ir.ui.view">
|
||||
<field name="name">custom.mrp.production.tree</field>
|
||||
<field name="model">mrp.production</field>
|
||||
<field name="inherit_id" ref="mrp.mrp_production_tree_view"/>
|
||||
<field name="arch" type="xml">
|
||||
<!-- <xpath expr="//button[@name='do_unreserve']" position="after">-->
|
||||
<!-- <button name="do_update_program" type="object" string="更新程序"-->
|
||||
<!-- groups="sf_base.group_sf_mrp_user"/>-->
|
||||
<!-- </xpath>-->
|
||||
<xpath expr="//field[@name='product_id']" position="replace"/>
|
||||
<xpath expr="//field[@name='product_qty']" position="replace"/>
|
||||
<xpath expr="//field[@name='product_uom_id']" position="replace"/>
|
||||
@@ -65,31 +70,38 @@
|
||||
<!-- <attribute name="statusbar_visible">draft,confirmed,progress,pending_processing,completed,done -->
|
||||
<!-- </attribute> -->
|
||||
<attribute name="statusbar_visible">
|
||||
confirmed,pending_cam,progress,done
|
||||
confirmed,pending_cam,progress,rework,scrap,done
|
||||
</attribute>
|
||||
</xpath>
|
||||
<xpath expr="//sheet//group//group[2]//label" position="before">
|
||||
<!-- <field name="process_state"/> -->
|
||||
<field name="state"/>
|
||||
<field name="state" readonly="1"/>
|
||||
<!-- <field name="process_state"/> -->
|
||||
|
||||
</xpath>
|
||||
<xpath expr="//sheet//group//group//div[3]" position="after">
|
||||
<field name="manual_quotation" readonly="1"/>
|
||||
<field name="programming_no" readonly="1"/>
|
||||
<field name="programming_state" readonly="1" decoration-success="programming_state == '已编程'"
|
||||
decoration-warning="programming_state =='编程中'"/>
|
||||
<field name="programming_state" readonly="1"
|
||||
decoration-success="programming_state == '已编程'"
|
||||
decoration-warning="programming_state =='编程中'"
|
||||
decoration-danger="programming_state =='已编程未下发'"/>
|
||||
<field name="work_state" invisible="1"/>
|
||||
<field name="schedule_state" invisible='1'/>
|
||||
<field name="is_scrap" invisible='1'/>
|
||||
<field name="is_rework" invisible='1'/>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='user_id']" position="before">
|
||||
<field name="plan_start_processing_time" readonly="1"/>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='user_id']" position="after">
|
||||
<field name="production_line_id" readonly="1"/>
|
||||
<!-- <field name="production_line_state" readonly="1"/>-->
|
||||
<!-- <field name="production_line_state" readonly="1"/>-->
|
||||
<field name="part_number" string="成品的零件图号"/>
|
||||
<field name="part_drawing"/>
|
||||
<field name="tool_state"/>
|
||||
<field name="tool_state_remark" string="备注" attrs="{'invisible': [('tool_state', '!=', '1')]}"/>
|
||||
<field name="tool_state_remark2" invisible="1"/>
|
||||
</xpath>
|
||||
<xpath expr="//header//button[@name='action_cancel']" position="replace">
|
||||
<button name="action_cancel" type="object" string="取消" data-hotkey="z"
|
||||
@@ -106,9 +118,20 @@
|
||||
<xpath expr="(//header//button[@name='button_mark_done'])[2]" position="replace">
|
||||
<button name="button_mark_done"
|
||||
attrs="{'invisible': ['|', '|', ('state', 'in', ('draft', 'cancel', 'done', 'to_close')), ('qty_producing', '=', 0), ('move_raw_ids', '=', [])]}"
|
||||
string="验证" type="object" class="oe_highlight" data-hotkey="g"
|
||||
string="验证" type="object" data-hotkey="g"
|
||||
groups="sf_base.group_sf_mrp_user"/>
|
||||
</xpath>
|
||||
<xpath expr="(//header//button[@name='button_scrap'])" position="replace">
|
||||
<button name="button_scrap" invisible="1"/>
|
||||
<button name="do_update_program" string="更新程序" type="object" groups="sf_base.group_sf_mrp_user"
|
||||
confirm="是否确认更新程序"
|
||||
attrs="{'invisible': ['|',('state', '!=', 'rework'),('programming_state', '!=', '已编程未下发')]}"/>
|
||||
<button name="button_rework" string="返工" type="object" groups="sf_base.group_sf_mrp_user"
|
||||
attrs="{'invisible': ['|','|',('state', '!=', 'rework') ,('programming_state', '!=', '已编程'),('is_rework', '=', True)]}"/>
|
||||
<button name="button_scrap_new" string="报废" type="object"
|
||||
groups="sf_base.group_sf_mrp_user"
|
||||
attrs="{'invisible': ['|',('is_scrap', '=', False),('state','=','cancel')]}"/>
|
||||
</xpath>
|
||||
<xpath expr="(//header//button[@name='button_mark_done'])[3]" position="replace">
|
||||
<button name="button_mark_done" attrs="{'invisible': [
|
||||
'|',
|
||||
@@ -138,11 +161,11 @@
|
||||
attrs="{'invisible': ['|', ('is_planned', '=', False), ('state', '=', 'cancel')]}"
|
||||
data-hotkey="x" groups="sf_base.group_sf_mrp_user"/>
|
||||
</xpath>
|
||||
<xpath expr="//header//button[@name='button_scrap']" position="replace">
|
||||
<button name="button_scrap" type="object" string="报废"
|
||||
attrs="{'invisible': [('state', 'in', ('cancel', 'draft'))]}" data-hotkey="y"
|
||||
groups="sf_base.group_sf_mrp_user"/>
|
||||
</xpath>
|
||||
<!-- <xpath expr="//header//button[@name='button_scrap']" position="replace">-->
|
||||
<!-- <button name="button_scrap" type="object" string="报废"-->
|
||||
<!-- attrs="{'invisible': [('state', 'in', ('cancel', 'draft'))]}" data-hotkey="y"-->
|
||||
<!-- groups="sf_base.group_sf_mrp_user"/>-->
|
||||
<!-- </xpath>-->
|
||||
|
||||
|
||||
<xpath expr="//header//button[@name='action_confirm']" position="replace">
|
||||
@@ -178,6 +201,19 @@
|
||||
data-hotkey="l"/>
|
||||
</xpath>
|
||||
|
||||
<xpath expr="//button[@name='action_view_mo_delivery']" position="before">
|
||||
<button class="oe_stat_button" name="action_view_remanufacture_productions" type="object"
|
||||
icon="fa-wrench" attrs="{'invisible': [('remanufacture_count', '=', 0)]}"
|
||||
groups="mrp.group_mrp_user">
|
||||
<div class="o_field_widget o_stat_info">
|
||||
<span class="o_stat_value">
|
||||
<field name="remanufacture_count"/>
|
||||
</span>
|
||||
<span class="o_stat_text">新的制造</span>
|
||||
</div>
|
||||
</button>
|
||||
</xpath>
|
||||
|
||||
<xpath expr="//header//button[@name='action_toggle_is_locked']" position="replace">
|
||||
<button name="action_toggle_is_locked"
|
||||
attrs="{'invisible': ['|', ('show_lock', '=', False), ('is_locked', '=', True)]}"
|
||||
@@ -259,6 +295,32 @@
|
||||
[])]}
|
||||
</attribute>
|
||||
</xpath>
|
||||
|
||||
<xpath expr="//sheet//notebook//page[@name='operations']" position="after">
|
||||
<page string="检测结果" attrs="{'invisible': [('detection_result_ids', '=', [])]}">
|
||||
<field name="detection_result_ids" string="" readonly="0">
|
||||
<tree sample="1">
|
||||
<field name="production_id" invisible="1"/>
|
||||
<field name="processing_panel"/>
|
||||
<field name="routing_type"/>
|
||||
<field name="rework_reason"/>
|
||||
<field name="detailed_reason"/>
|
||||
<field name="test_results"/>
|
||||
<field name="handle_result"/>
|
||||
<field name="test_report" invisible="1"/>
|
||||
<button name="button_look_test_report" string="查看测试报告" type="object"
|
||||
attrs="{'invisible': [('test_report', '=', False)]}"
|
||||
class="btn btn-primary me-1"/>
|
||||
</tree>
|
||||
</field>
|
||||
</page>
|
||||
</xpath>
|
||||
<xpath expr="//sheet//notebook//page[@name='components']" position="attributes">
|
||||
<attribute name="string">投料</attribute>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='components_availability']" position="attributes">
|
||||
<attribute name="string">投料状态</attribute>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
@@ -280,6 +342,16 @@
|
||||
<field name="model">mrp.workorder</field>
|
||||
<field name="inherit_id" ref="mrp.mrp_production_workorder_tree_editable_view"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//tree" position="attributes">
|
||||
<attribute name="default_order">sequence</attribute>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='state']" position="replace">
|
||||
<field name="state" widget="badge" decoration-warning="state == 'progress'"
|
||||
decoration-success="state == 'done'" decoration-danger="state in ('cancel','rework')"
|
||||
decoration-muted="state == 'to be detected'"
|
||||
decoration-info="state not in ('progress', 'done', 'cancel','rework','to be detected')"
|
||||
attrs="{'invisible': [('production_state', '=', 'draft')], 'column_invisible': [('parent.state', '=', 'draft')]}"/>
|
||||
</xpath>
|
||||
<xpath expr="//tree//button[@name='button_start']" position="replace">
|
||||
<field name="routing_type" invisible="True"/>
|
||||
<button name="button_start" type="object" string="开始" class="btn-success" confirm="是否确认开始?"
|
||||
@@ -295,7 +367,7 @@
|
||||
</xpath>
|
||||
<xpath expr="//tree//button[@name='button_finish']" position="replace">
|
||||
<button name="button_finish" type="object" string="Done" class="btn-success"
|
||||
attrs="{'invisible': ['|', '|', '|', ('production_state', 'in', ('draft', 'done', 'cancel')), ('working_state', '=', 'blocked'), ('is_user_working', '=', False), ('routing_type', '=', 'CNC加工')]}"
|
||||
attrs="{'invisible': ['|', '|', '|', ('production_state', 'in', ('draft', 'done', 'cancel')), ('working_state', '=', 'blocked'), ('is_user_working', '=', False)]}"
|
||||
groups="sf_base.group_sf_mrp_user"
|
||||
confirm="是否确认完成?"/>
|
||||
</xpath>
|
||||
@@ -365,7 +437,15 @@
|
||||
<xpath expr="//header//button[@name='action_cancel']" position="replace">
|
||||
<button name="action_cancel" type="object" string="取消" groups="sf_base.group_sf_mrp_user"/>
|
||||
</xpath>
|
||||
|
||||
<xpath expr="//field[@name='state']" position="replace">
|
||||
<field name="state" decoration-success="state in ('done', 'to_close')"
|
||||
decoration-warning="state == 'progress'" decoration-info="state == 'confirmed'"
|
||||
decoration-danger="state in ('cancel','rework','scrap')" decoration-muted="state == 'draft'"
|
||||
optional="show" widget="badge" class="text-dark"/>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='state']" position="after">
|
||||
<field name="tool_state" invisible="1"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
@@ -397,6 +477,25 @@
|
||||
<field name="model">mrp.production</field>
|
||||
<field name="inherit_id" ref="mrp.view_mrp_production_filter"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//filter[@name='filter_in_progress']" position="before">
|
||||
<filter string="返工" name="filter_rework" domain="[('state', '=', 'rework')]"/>
|
||||
<filter string="报废" name="filter_scrap" domain="[('state', '=', 'scrap')]"/>
|
||||
</xpath>
|
||||
<xpath expr="//filter[@name='planning_issues']" position="before">
|
||||
<separator/>
|
||||
<filter string="返工且已编程" name="filter_rework_programmed"
|
||||
domain="[('state', '=', 'rework'),('programming_state', '=', '已编程')]"/>
|
||||
<filter string="返工且已编程未下发" name="filter_rework_programmed_not_delivered"
|
||||
domain="[('state', '=', 'rework'),('programming_state', '=', '已编程未下发')]"/>
|
||||
<separator/>
|
||||
<filter name="filter_programming" string="编程中"
|
||||
domain="[('programming_state', '=', '编程中')]"/>
|
||||
<filter name="filter_programmed" string="已编程"
|
||||
domain="[('programming_state', '=', '已编程')]"/>
|
||||
<filter name="filter_programmed_not_delivered" string="已编程未下发"
|
||||
domain="[('programming_state', '=', '已编程未下发')]"/>
|
||||
<separator/>
|
||||
</xpath>
|
||||
<xpath expr="//search" position="inside">
|
||||
<searchpanel class="account_root">
|
||||
<field name="state" icon="fa-filter" enable_counters="1"/>
|
||||
@@ -482,5 +581,72 @@
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
||||
<record model="ir.ui.view" id="sf_test_report_form">
|
||||
<field name="name">sf.detection.result</field>
|
||||
<field name="model">sf.detection.result</field>
|
||||
<field name="arch" type="xml">
|
||||
<form>
|
||||
<group>
|
||||
<field name="handle_result"/>
|
||||
<field name="test_report" readonly="1" widget="pdf_viewer"/>
|
||||
</group>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
||||
<record id="action_test_report_form" model="ir.actions.act_window">
|
||||
<field name="name">检测报告</field>
|
||||
<field name="res_model">sf.detection.result</field>
|
||||
<field name="view_mode">form</field>
|
||||
</record>
|
||||
<!-- <menuitem action="mrp.mrp_production_action"-->
|
||||
<!-- id="mrp_production_action"-->
|
||||
<!-- sequence="1" active="False"/>-->
|
||||
|
||||
<!-- <!– <record id="mrp_production_action_sf" model="ir.actions.act_window">–>-->
|
||||
<!-- <!– <field name="name">Manufacturing Orders</field>–>-->
|
||||
<!-- <!– <field name="type">ir.actions.act_window</field>–>-->
|
||||
<!-- <!– <field name="res_model">mrp.production</field>–>-->
|
||||
<!-- <!– <field name="view_mode">tree,kanban,form,calendar,pivot,graph</field>–>-->
|
||||
<!-- <!– <field name="search_view_id" ref="mrp.view_mrp_production_filter"/>–>-->
|
||||
<!-- <!– <!– <field name="context">{'search_default_todo': True, 'default_company_id': allowed_company_ids[0]}</field>–>–>-->
|
||||
<!-- <!– <field name="domain">[('picking_type_id.active', '=', True)]</field>–>-->
|
||||
<!-- <!– </record>–>-->
|
||||
<!-- <!– <menuitem action="mrp_production_action_sf"–>-->
|
||||
<!-- <!– id="menu_mrp_production_action_sf"–>-->
|
||||
<!-- <!– parent="mrp.menu_mrp_manufacturing"–>-->
|
||||
<!-- <!– sequence="2"–>-->
|
||||
<!-- <!– string="制造订单"/>–>-->
|
||||
<!-- <record id="mrp_production_action_sf" model="ir.actions.act_window">-->
|
||||
<!-- <field name="name">Manufacturing Orders</field>-->
|
||||
<!-- <field name="type">ir.actions.act_window</field>-->
|
||||
<!-- <field name="res_model">mrp.production</field>-->
|
||||
<!-- <field name="view_mode">tree,kanban,form,calendar,pivot,graph</field>-->
|
||||
<!-- <field name="view_id" eval="False"/>-->
|
||||
<!-- <field name="search_view_id" ref="mrp.view_mrp_production_filter"/>-->
|
||||
<!-- <field name="context">{'search_default_todo': True, 'default_company_id':-->
|
||||
<!-- allowed_company_ids[0],'search_default_filter_rework':1,'search_default_filter_programmed':1}-->
|
||||
<!-- </field>-->
|
||||
<!-- <field name="domain">[('picking_type_id.active', '=', True)]</field>-->
|
||||
<!-- <!– <!– <field name="help" type="html">–>,'search_default_filter_rework': 1,–>-->
|
||||
<!-- <!– 'search_default_filter_programming': 1–>-->
|
||||
<!-- <!– <p class="o_view_nocontent_smiling_face">–>-->
|
||||
<!-- <!– No manufacturing order found. Let's create one.–>-->
|
||||
<!-- <!– </p>–>-->
|
||||
<!-- <!– <p>–>-->
|
||||
<!-- <!– Consume <a name="%(product.product_template_action)d" type='action' tabindex="-1">components</a> and–>-->
|
||||
<!-- <!– build finished products using–>-->
|
||||
<!-- <!– <a name="%(mrp_bom_form_action)d" type='action' tabindex="-1">bills of materials</a>–>-->
|
||||
<!-- <!– </p>–>-->
|
||||
<!-- <!– </field>–>-->
|
||||
<!-- </record>-->
|
||||
|
||||
<!-- <menuitem action="sf_manufacturing.mrp_production_action_sf"-->
|
||||
<!-- id="mrp_production_action"-->
|
||||
<!-- parent="mrp.menu_mrp_manufacturing"-->
|
||||
<!-- sequence="1"/>-->
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
@@ -17,6 +17,7 @@
|
||||
<field name="bom_product_template_attribute_value_ids" position="after">
|
||||
<field name="routing_type" required="1"/>
|
||||
<field name="is_repeat"/>
|
||||
<field name="reserved_duration"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
@@ -24,6 +24,20 @@
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="custom_model_form_view_inherit" model="ir.ui.view">
|
||||
<field name="name">custom.model.form.view.inherit</field>
|
||||
<field name="model">mrp.workcenter</field>
|
||||
<field name="inherit_id" ref="mrp.mrp_workcenter_view"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr='//form//sheet' position="after">
|
||||
<div class="oe_chatter">
|
||||
<field name="message_follower_ids"/>
|
||||
<field name="message_ids"/>
|
||||
</div>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="mrp_workcenter_view_kanban_inherit_workorder" model="ir.ui.view">
|
||||
<field name="name">mrp.workcenter.view.kanban.inherit.mrp.workorder</field>
|
||||
<field name="model">mrp.workcenter</field>
|
||||
@@ -168,6 +182,7 @@
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='resource_calendar_id']" position="after">
|
||||
<field name="is_process_outsourcing"/>
|
||||
<field name="is_agv_scheduling"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
@@ -7,11 +7,10 @@
|
||||
<field name="arch" type="xml">
|
||||
<field name="name" position="replace">
|
||||
<field name="is_subcontract" invisible="1"/>
|
||||
|
||||
<field name="name" decoration-success="is_subcontract" decoration-bf="is_subcontract"/>
|
||||
</field>
|
||||
<field name="name" position="before">
|
||||
<field name="sequence" optional="hide"/>
|
||||
<field name="sequence" string="序号"/>
|
||||
<field name='user_permissions' invisible="1"/>
|
||||
</field>
|
||||
<field name="name" position="after">
|
||||
@@ -19,6 +18,7 @@
|
||||
</field>
|
||||
<field name="state" position="after">
|
||||
<field name="work_state" optional="hide"/>
|
||||
<field name="tool_state" invisible="1"/>
|
||||
<field name="product_tmpl_name" invisible="1"/>
|
||||
</field>
|
||||
<field name="duration" position="replace">
|
||||
@@ -36,14 +36,22 @@
|
||||
<xpath expr="//field[@name='date_planned_start']" position="replace">
|
||||
<field name="date_planned_start" string="计划开始日期" optional="show"/>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='date_planned_start']" position="before">
|
||||
<field name="reserved_duration" string="计划预留时间" optional="show"/>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='date_planned_finished']" position="replace">
|
||||
<field name="date_planned_finished" string="计划结束日期" optional="hide"/>
|
||||
</xpath>
|
||||
<xpath expr="//button[@name='button_start']" position="attributes">
|
||||
<!-- <attribute name="attrs">{'invisible': ['|', '|', '|','|','|', ('production_state','in', ('draft',-->
|
||||
<!-- 'done',-->
|
||||
<!-- 'cancel')), ('working_state', '=', 'blocked'), ('state', 'in', ('done', 'cancel')),-->
|
||||
<!-- ('is_user_working', '!=', False),("user_permissions","=",False),("name","=","CNC加工")]}-->
|
||||
<!-- </attribute>-->
|
||||
<attribute name="attrs">{'invisible': ['|', '|', '|','|','|', ('production_state','in', ('draft',
|
||||
'done',
|
||||
'cancel')), ('working_state', '=', 'blocked'), ('state', 'in', ('done', 'cancel')),
|
||||
('is_user_working', '!=', False),("user_permissions","=",False),("name","=","获取CNC加工程序")]}
|
||||
('is_user_working', '!=', False),("user_permissions","=",False),("name","in",("CNC加工","解除装夹"))]}
|
||||
</attribute>
|
||||
</xpath>
|
||||
<xpath expr="//button[@name='%(mrp.act_mrp_block_workcenter_wo)d']" position="attributes">
|
||||
@@ -113,7 +121,25 @@
|
||||
<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="//form" position="inside">
|
||||
<script src="sf_manufacturing/static/src/js/customRFID.js"/>
|
||||
</xpath>
|
||||
<xpath expr="//header/field[@name='state']" position="replace">
|
||||
<field name="state" widget="statusbar"
|
||||
statusbar_visible="pending,waiting,ready,progress,to be detected,done,rework"/>
|
||||
</xpath>
|
||||
<xpath expr="//div[@name='button_box']" position="inside">
|
||||
<button type="object" name="action_view_surface_technics_purchase" class="oe_stat_button"
|
||||
icon="fa-credit-card"
|
||||
groups="base.group_user,sf_base.group_sf_order_user"
|
||||
attrs="{'invisible': [('surface_technics_purchase_count', '=', 0),('routing_type', '!=', '表面工艺')]}">
|
||||
<div class="o_field_widget o_stat_info">
|
||||
<span class="o_stat_value">
|
||||
<field name="surface_technics_purchase_count"/>
|
||||
</span>
|
||||
<span class="o_stat_text">采购</span>
|
||||
</div>
|
||||
</button>
|
||||
<button type="object" name="action_view_surface_technics_picking" class="oe_stat_button" icon="fa-truck"
|
||||
groups="base.group_user,sf_base.group_sf_order_user"
|
||||
attrs="{'invisible': [('surface_technics_picking_count', '=', 0)]}">
|
||||
@@ -121,9 +147,12 @@
|
||||
</button>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='state']" position="before">
|
||||
<field name='tool_state' invisible="1"/>
|
||||
<field name='user_permissions' invisible="1"/>
|
||||
<field name='name' invisible="1"/>
|
||||
<field name='is_rework' invisible="1"/>
|
||||
<field name='is_delivery' invisible="1"/>
|
||||
<field name="is_trayed" invisible="1"/>
|
||||
<!-- <field name='is_send_program_again' invisible="1"/>-->
|
||||
<!-- 工单form页面的开始停工按钮等 -->
|
||||
<!-- <button name="button_start" type="object" string="开始" class="btn-success" -->
|
||||
@@ -136,8 +165,12 @@
|
||||
<!-- attrs="{'invisible': ['|', '|', ('production_state', 'not in', ('pending_processing', 'pending_cam', 'pending_era_cam')), ('state','!=','progress'), ('routing_type', 'not in', ('装夹预调', 'CNC加工', '解除装夹'))]}" -->
|
||||
<!-- groups="sf_base.group_sf_mrp_user" confirm="是否确认完工"/> -->
|
||||
|
||||
<!-- <button name="button_start" type="object" string="开始" class="btn-success" confirm="是否确认开始"-->
|
||||
<!-- attrs="{'invisible': ['|', '|', '|', ('production_state','in', ('draft', 'done', 'cancel')), ('working_state', '=', 'blocked'), ('state', 'in', ('done', 'cancel','to be detected')), ('is_user_working', '!=', False)]}"/>-->
|
||||
<button name="button_start" type="object" string="开始" class="btn-success" confirm="是否确认开始"
|
||||
attrs="{'invisible': ['|', '|', '|', ('production_state','in', ('draft', 'done', 'cancel')), ('working_state', '=', 'blocked'), ('state', 'in', ('done', 'cancel')), ('is_user_working', '!=', False)]}"/>
|
||||
attrs="{'invisible': ['|', '|', '|', '|', '|', ('routing_type', '=', '装夹预调'), ('routing_type', '=', '解除装夹'), ('production_state','in', ('draft', 'done', 'cancel')), ('working_state', '=', 'blocked'), ('state', 'in', ('done', 'cancel','to be detected')), ('is_user_working', '!=', False)]}"/>
|
||||
<button name="button_start" type="object" string="开始" class="btn-success"
|
||||
attrs="{'invisible': ['|', '|', '|', '|', ('routing_type', '!=', '装夹预调'), ('production_state','in', ('draft', 'done', 'cancel')), ('working_state', '=', 'blocked'), ('state', 'in', ('done', 'cancel','to be detected')), ('is_user_working', '!=', False)]}"/>
|
||||
<button name="button_pending" type="object" string="暂停" class="btn-warning"
|
||||
attrs="{'invisible': ['|', '|', ('production_state', 'in', ('draft', 'done', 'cancel')), ('working_state', '=', 'blocked'), ('is_user_working', '=', False)]}"/>
|
||||
<button name="button_finish" type="object" string="完成" class="btn-success" confirm="是否确认完工"
|
||||
@@ -158,13 +191,16 @@
|
||||
<!-- context="{'default_workcenter_id': workcenter_id}" class="btn-danger" -->
|
||||
<!-- groups="sf_base.group_sf_mrp_user" -->
|
||||
<!-- attrs="{'invisible': ['|', '|', ('production_state', 'in', ('draft', 'done', 'cancel')), ('working_state', '!=', 'blocked'),('state','=','done')]}"/> -->
|
||||
<button name="button_workpiece_delivery" type="object" string="工件配送" class="btn-primary"
|
||||
attrs="{'invisible': ['|','|',('routing_type','!=','装夹预调'),('is_delivery','=',True),('state','!=','done')]}"/>
|
||||
<!-- <button name="button_send_program_again" type="object" string="重新下发NC程序" class="btn-primary"-->
|
||||
<!-- confirm="是否确认重新下发NC程序?"-->
|
||||
<!-- groups="sf_base.group_sf_order_user,sf_base.group_sf_equipment_user"-->
|
||||
<!-- attrs="{'invisible': ['|', '|', '|',('routing_type','!=','装夹预调'),('state','in',['done', 'cancel',-->
|
||||
<!-- 'progress']),('cnc_worksheet','=',False),('is_send_program_again','=',True)]}"/>-->
|
||||
<!-- <button name="button_workpiece_delivery" type="object" string="工件配送" class="btn-primary"-->
|
||||
<!-- attrs="{'invisible': ['|','|','|','|',('routing_type','!=','装夹预调'),('is_delivery','=',True),('state','!=','done'),('is_rework','=',True),'&',('rfid_code','in',['',False]),('state','=','done')]}"/>-->
|
||||
<button name="button_rework_pre" type="object" string="返工"
|
||||
class="btn-primary"
|
||||
attrs="{'invisible': ['|','|',('routing_type','!=','装夹预调'),('state','!=','progress'),('is_rework','=',True)]}"/>
|
||||
<button name="unbind_tray" type="object" string="解绑托盘"
|
||||
class="btn-primary"
|
||||
attrs="{'invisible': ['|', '|', '|', ('routing_type','!=','装夹预调'),('state','!=','progress'), ('is_trayed', '=', False), ('state', 'in', ('done'))]}"/>
|
||||
<button name="print_method" type="object" string="打印二维码" class="btn-primary"
|
||||
attrs="{'invisible': ['|',('routing_type','!=','解除装夹'),('state','!=','done')]}"/>
|
||||
</xpath>
|
||||
<xpath expr="//page[1]" position="before">
|
||||
<page string="开料要求" attrs='{"invisible": [("routing_type","!=","切割")]}'>
|
||||
@@ -218,11 +254,11 @@
|
||||
attrs="{'invisible': [('routing_type', '!=', 'CNC加工')]}"/>
|
||||
<field name="processing_panel" readonly="1"
|
||||
attrs='{"invisible": [("routing_type","in",("获取CNC加工程序","切割"))]}'/>
|
||||
<field name="equipment_id"
|
||||
<field name="equipment_id" readonly="1"
|
||||
attrs='{"invisible": [("routing_type","in",("获取CNC加工程序","切割","装夹预调"))]}'/>
|
||||
<field name="production_line_id"
|
||||
attrs='{"invisible": [("routing_type","in",("获取CNC加工程序","切割"))]}'/>
|
||||
<field name="production_line_state"
|
||||
<field name="production_line_state" readonly="1"
|
||||
attrs='{"invisible": [("routing_type","in",("获取CNC加工程序","切割","装夹预调"))]}'/>
|
||||
<!-- <field name="functional_fixture_id" -->
|
||||
<!-- attrs='{"invisible": [("routing_type","!=","装夹预调")]}'/> -->
|
||||
@@ -469,7 +505,8 @@
|
||||
<field name="status" readonly="1" widget="badge"
|
||||
decoration-success="status == '已配送'"
|
||||
decoration-warning="status == '待下发'"
|
||||
decoration-danger="status == '待配送'"/>
|
||||
decoration-danger="status == '待配送'"
|
||||
decoration-info="status== '已取消'"/>
|
||||
</tree>
|
||||
</field>
|
||||
</page>
|
||||
@@ -481,16 +518,18 @@
|
||||
<field name="results" invisible="1"/>
|
||||
<page string="后置三元检测" attrs='{"invisible": [("routing_type","!=","CNC加工")]}'>
|
||||
<group>
|
||||
<field name="test_results" attrs='{"invisible":[("results","!=",False)]}'/>
|
||||
<field name="test_results"
|
||||
attrs='{"readonly":[("state","!=","to be detected")],"invisible":[("results","!=",False)]}'/>
|
||||
<!-- <field name="is_remanufacture" attrs='{"invisible":[("test_results","!=","报废")]}'/>-->
|
||||
<!-- <field name="is_fetchcnc"-->
|
||||
<!-- attrs='{"invisible":["|",("test_results","=","合格"),("is_remanufacture","=",False)]}'/>-->
|
||||
<!-- <field name="reason"-->
|
||||
<!-- attrs='{"required":[("test_results","!=","合格")],"invisible":[("test_results","=","合格")]}'/>-->
|
||||
<!-- <field name="detailed_reason" attrs='{"invisible":[("test_results","=","合格")]}'/>-->
|
||||
<!-- <field name="results" readonly="1" attrs='{"invisible":[("results","!=","合格")]}'/>-->
|
||||
<field name="reason"
|
||||
attrs='{"required":[("test_results","!=","合格")],"invisible":[("test_results","=","合格")]}'/>
|
||||
<field name="detailed_reason"
|
||||
attrs='{"required":[("test_results","!=","合格")],"invisible":[("test_results","=","合格")]}'/>
|
||||
<!-- <field name="results" readonly="1" attrs='{"invisible":[("results","!=","合格")]}'/>-->
|
||||
<field name="detection_report" attrs='{"invisible":[("results","!=",False)]}'
|
||||
widget="pdf_viewer"/>
|
||||
widget="pdf_viewer" readonly="1"/>
|
||||
</group>
|
||||
<!-- <div class="col-12 col-lg-6 o_setting_box">-->
|
||||
<!-- <button type="object" class="oe_highlight" name="recreateManufacturingOrWorkerOrder"-->
|
||||
@@ -502,11 +541,12 @@
|
||||
<xpath expr="//page[1]" position="before">
|
||||
<page string="CNC程序" attrs='{"invisible": [("routing_type","!=","CNC加工")]}'>
|
||||
<field name="cnc_ids" widget="one2many" string="工作程序" default_order="sequence_number,id"
|
||||
readonly="1">
|
||||
readonly="0">
|
||||
<tree>
|
||||
<field name="sequence_number"/>
|
||||
<field name="program_name"/>
|
||||
<field name="cutting_tool_name"/>
|
||||
<field name="tool_state"/>
|
||||
<field name="cutting_tool_no"/>
|
||||
<field name="processing_type"/>
|
||||
<field name="margin_x_y"/>
|
||||
@@ -587,6 +627,9 @@
|
||||
<field name="product_id" position="after">
|
||||
<field name="part_number" string="成品零件图号"/>
|
||||
</field>
|
||||
<xpath expr="//filter[@name='progress']" position="after">
|
||||
<filter string="待检测" name="state" domain="[('state','=','to be detected')]"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
@@ -618,25 +661,27 @@
|
||||
<field name="arch" type="xml">
|
||||
<tree string="工件配送" class="center" create="0" delete="0">
|
||||
<header>
|
||||
<button name="button_delivery" type="object" string="配送" class="oe_highlight"/>
|
||||
<button name="button_delivery" type="object" string="工件配送" class="btn-primary" attrs="{'force_show':1}"/>
|
||||
</header>
|
||||
<field name="status" widget="badge"
|
||||
decoration-success="status == '已配送'"
|
||||
decoration-warning="status == '待下发'"
|
||||
decoration-danger="status == '待配送'"/>
|
||||
decoration-danger="status == '已下发'"
|
||||
decoration-info="status == '已取消'"
|
||||
/>
|
||||
<field name="name"/>
|
||||
<field name="production_id"/>
|
||||
<field name="type" readonly="1"/>
|
||||
<field name="production_line_id" options="{'no_create': True}" readonly="1"/>
|
||||
<field name="route_id" options="{'no_create': True}"
|
||||
domain="[('route_type','in',['上产线','下产线'])]"/>
|
||||
<!-- <field name="route_id" options="{'no_create': True}"-->
|
||||
<!-- domain="[('route_type','in',['上产线','下产线'])]"/>-->
|
||||
<field name="feeder_station_start_id" readonly="1" force_save="1"/>
|
||||
<field name="feeder_station_destination_id" readonly="1" force_save="1"/>
|
||||
<!-- <field name="feeder_station_destination_id" readonly="1" force_save="1"/>-->
|
||||
<field name="is_cnc_program_down" readonly="1"/>
|
||||
<!-- <field name="rfid_code"/>-->
|
||||
<field name="task_delivery_time" readonly="1"/>
|
||||
<field name="task_completion_time" readonly="1"/>
|
||||
<field name="delivery_duration" widget="float_time"/>
|
||||
<!-- <field name="task_delivery_time" readonly="1"/>-->
|
||||
<!-- <field name="task_completion_time" readonly="1"/>-->
|
||||
<!-- <field name="delivery_duration" widget="float_time"/>-->
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
@@ -691,7 +736,7 @@
|
||||
<field name="arch" type="xml">
|
||||
<search string="工件配送">
|
||||
<filter name="filter_to_be_issued" string="待下发" domain="[('status', 'in', ['待下发'])]"/>
|
||||
<filter name="filter_waiting_delivery" string="待配送" domain="[('status', 'in', ['待配送'])]"/>
|
||||
<filter name="filter_issued" string="已下发" domain="[('status', 'in', ['已下发'])]"/>
|
||||
<filter name="filter_delivered" string="已配送" domain="[('status', 'in', ['已配送'])]"/>
|
||||
<field name="rfid_code"/>
|
||||
<field name="production_id"/>
|
||||
@@ -715,11 +760,11 @@
|
||||
<field name="res_model">sf.workpiece.delivery</field>
|
||||
<field name="search_view_id" ref="sf_workpiece_delivery_search"/>
|
||||
<field name="context">{'search_default_filter_to_be_issued': 1,
|
||||
'search_default_filter_waiting_delivery': 1}
|
||||
'search_default_filter_issued': 1}
|
||||
</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="domain">
|
||||
[('type','in',['上产线','下产线']),('workorder_state','=','done'),('is_manual_work','=',false)]
|
||||
[('type','in',['上产线','下产线']),('workorder_state','in',['done','rework']),('is_manual_work','=',false)]
|
||||
</field>
|
||||
</record>
|
||||
|
||||
@@ -802,5 +847,11 @@
|
||||
<field name="view_mode">tree</field>
|
||||
<field name="domain">[('type','in',['运送空料架']),('name','not ilike','WDO')]</field>
|
||||
</record>
|
||||
|
||||
<menuitem id="mrp.menu_mrp_manufacturing"
|
||||
name="Operations"
|
||||
sequence="10"
|
||||
parent="mrp.menu_mrp_root"
|
||||
groups="sf_base.group_sf_order_user,sf_base.group_sf_mrp_manager,sf_base.group_sf_equipment_user"/>
|
||||
</odoo>
|
||||
|
||||
|
||||
@@ -100,7 +100,7 @@
|
||||
<field name="model">sf.maintenance.logs</field>
|
||||
<field name="inherit_id" ref="sf_maintenance.view_maintenance_logs_tree"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='code']" position="after">
|
||||
<xpath expr="//field[@name='type']" position="after">
|
||||
<field name="production_line_id" optional="hide"/>
|
||||
</xpath>
|
||||
</field>
|
||||
|
||||
24
sf_manufacturing/views/res_config_settings_views.xml
Normal file
24
sf_manufacturing/views/res_config_settings_views.xml
Normal file
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
<record id="res_config_settings_view_form_sf_sync" model="ir.ui.view">
|
||||
<field name="name">res.config.settings.view.form.inherit.sf_sync</field>
|
||||
<field name="model">res.config.settings</field>
|
||||
<field name="inherit_id" ref="base_setup.res_config_settings_view_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//div[@id='agv_config']/div" position="after">
|
||||
<div class="col-12 col-lg-6 o_setting_box">
|
||||
<div class="o_setting_left_pane">
|
||||
<field name="is_agv_task_dispatch"/>
|
||||
</div>
|
||||
<div class="o_setting_right_pane">
|
||||
<div class="text-muted">
|
||||
<label for="is_agv_task_dispatch"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
@@ -1 +1,3 @@
|
||||
from . import workpiece_delivery_wizard
|
||||
from . import rework_wizard
|
||||
from . import production_wizard
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user