Compare commits
502 Commits
feature/ne
...
feature/pa
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
284234adf2 | ||
|
|
3ba79d88b5 | ||
|
|
c5755c318c | ||
|
|
dcaca89c6f | ||
|
|
5774f241a0 | ||
|
|
ad02cbf8cb | ||
|
|
196b427867 | ||
|
|
b6f3382e5d | ||
|
|
f2ee382205 | ||
|
|
081880b4bb | ||
|
|
bc475441a2 | ||
|
|
a7c3a604a6 | ||
|
|
0bd65b5da8 | ||
|
|
79d561ba68 | ||
|
|
63d9b4bf39 | ||
|
|
2619953be1 | ||
|
|
9f1514bd14 | ||
|
|
6e46d1d8dc | ||
|
|
403e64a957 | ||
|
|
aaded331cd | ||
|
|
5d2ddfb510 | ||
|
|
f68453a0c6 | ||
|
|
5cff411aaf | ||
|
|
ef5a3decd4 | ||
|
|
ce1860b283 | ||
|
|
b3f56754cd | ||
|
|
202db10cea | ||
|
|
6b61df9d50 | ||
|
|
1cac579a1f | ||
|
|
53ca78a6a3 | ||
|
|
0d28df0415 | ||
|
|
d182641d81 | ||
|
|
7789c901b2 | ||
|
|
aa4b73e406 | ||
|
|
1ca8ebe3cc | ||
|
|
c34cdd595d | ||
|
|
1936b512c2 | ||
|
|
3c470093d3 | ||
|
|
b483097907 | ||
|
|
c6aeff2006 | ||
|
|
170309b3c0 | ||
|
|
58acdc4913 | ||
|
|
d4537fe73d | ||
|
|
eaaa13fb9a | ||
|
|
f92bd8263c | ||
|
|
5f3957864a | ||
|
|
f884963abc | ||
|
|
1e17ac07a9 | ||
|
|
2f3e12e3af | ||
|
|
0ce51a4a66 | ||
|
|
a1bf997d51 | ||
|
|
e6c125cae6 | ||
|
|
ac968b0d6e | ||
|
|
81bebf836d | ||
|
|
d47d3f1638 | ||
|
|
614447d56e | ||
|
|
bf13edc1be | ||
|
|
e99b8692ea | ||
|
|
701decb38f | ||
|
|
3c61726fd3 | ||
|
|
247bebbd75 | ||
|
|
c3ceda2ba9 | ||
|
|
281f3c67c3 | ||
|
|
641a669f46 | ||
|
|
94b1c7d258 | ||
|
|
b06b690ae9 | ||
|
|
37476bcc88 | ||
|
|
af7eeb55f1 | ||
|
|
39ed32f3e9 | ||
|
|
79ec60bd6b | ||
|
|
4b7308fdcd | ||
|
|
198f1145d7 | ||
|
|
64eb66c334 | ||
|
|
cff21a4bcc | ||
|
|
2373f33315 | ||
|
|
d6e2e554a8 | ||
|
|
6c45a0bf4a | ||
|
|
4902fc5d9a | ||
|
|
9e5dbc0104 | ||
|
|
c57918736d | ||
|
|
78a456849c | ||
|
|
1816f1a497 | ||
|
|
da815c8ea1 | ||
|
|
32e3c2f79f | ||
|
|
c478b72e04 | ||
|
|
78b0529809 | ||
|
|
17ffac63cb | ||
|
|
acee32cc39 | ||
|
|
b36cf745c2 | ||
|
|
a24279f6e6 | ||
|
|
68b89999a9 | ||
|
|
7c38c7f643 | ||
|
|
4937c3a812 | ||
|
|
05469c71b8 | ||
|
|
210eea3b0b | ||
|
|
f58d1058f7 | ||
|
|
0622d7525f | ||
|
|
7556c09ac2 | ||
|
|
5344e97b7e | ||
|
|
1b10b7bb53 | ||
|
|
9ab164f0fe | ||
|
|
3b07b26303 | ||
|
|
29a32be8d2 | ||
|
|
3620412a13 | ||
|
|
5e72e3d1dd | ||
|
|
4137b304f3 | ||
|
|
caac651ab4 | ||
|
|
30e496442f | ||
|
|
c28c00a47b | ||
|
|
4f4fdeecb5 | ||
|
|
8742f9b98b | ||
|
|
d7ce69f474 | ||
|
|
cf46cff7e5 | ||
|
|
c124e2962c | ||
|
|
f4312c4d4e | ||
|
|
5928456ffe | ||
|
|
305ea9dff7 | ||
|
|
bdfc393099 | ||
|
|
1e1bd63acf | ||
|
|
fa20bb6eb5 | ||
|
|
c82db7159d | ||
|
|
89d6752012 | ||
|
|
3060b35ab2 | ||
|
|
17ad976a32 | ||
|
|
9f3b351544 | ||
|
|
5dc7dc8e0a | ||
|
|
f1868168af | ||
|
|
f084b5d765 | ||
|
|
854c3ceef2 | ||
|
|
4269ce58db | ||
|
|
3555be86c6 | ||
|
|
764472111f | ||
|
|
ce4fc8b8ab | ||
|
|
67f4917491 | ||
|
|
ad885f1e39 | ||
|
|
993d720c40 | ||
|
|
da75a8cd35 | ||
|
|
b35f842915 | ||
|
|
4e7993d035 | ||
|
|
946f02003e | ||
|
|
435315a166 | ||
|
|
676f8eb7eb | ||
|
|
6e9f66506a | ||
|
|
f3e08f5ccc | ||
|
|
c9d7d70d3c | ||
|
|
ee6e457bbd | ||
|
|
ab5b4c477f | ||
|
|
9330be3753 | ||
|
|
a3a2c96d00 | ||
|
|
31187755b9 | ||
|
|
178f538997 | ||
|
|
304f6ad5ba | ||
|
|
8b40714dad | ||
|
|
cb8e415e60 | ||
|
|
889049b107 | ||
|
|
610028c5f4 | ||
|
|
abfac28094 | ||
|
|
24d8d37e0a | ||
|
|
6780d4e758 | ||
|
|
a1d4e74056 | ||
|
|
3c8fbcf9f9 | ||
|
|
c0bb07efb2 | ||
|
|
675fb54d37 | ||
|
|
ead54cfc87 | ||
|
|
e990f24615 | ||
|
|
dc871e0a1d | ||
|
|
f71246e9fb | ||
|
|
133c1a87f2 | ||
|
|
6f14a39347 | ||
|
|
6e2ee60616 | ||
|
|
3efb30cece | ||
|
|
e56df3d7b9 | ||
|
|
253a06ceaf | ||
|
|
28c6c841be | ||
|
|
f948bf445a | ||
|
|
1b278a1432 | ||
|
|
3fcb3b250f | ||
|
|
57c689b03b | ||
|
|
158ca95dbb | ||
|
|
e059d9266d | ||
|
|
8de2a59b88 | ||
|
|
9ac60ae283 | ||
|
|
07356b5bb2 | ||
|
|
96c79a78ab | ||
|
|
b03eafcf94 | ||
|
|
0b7d7959c5 | ||
|
|
40865b7c6c | ||
|
|
8abb6447d2 | ||
|
|
394c3f9870 | ||
|
|
b254624d68 | ||
|
|
87b2c0b0f8 | ||
|
|
b0e993dd53 | ||
|
|
79183761ae | ||
|
|
c448088906 | ||
|
|
a7a1779e61 | ||
|
|
06842c6df9 | ||
|
|
6532af500f | ||
|
|
1fb88960db | ||
|
|
4c8071fea2 | ||
|
|
4aac3d42b6 | ||
|
|
0f4d25abb4 | ||
|
|
14cc285ee2 | ||
|
|
54b896004b | ||
|
|
7a1237ddf8 | ||
|
|
f80aaf978b | ||
|
|
64f35b5633 | ||
|
|
9e887076f2 | ||
|
|
78fff46452 | ||
|
|
a6d4f99710 | ||
|
|
05f1f1a983 | ||
|
|
f719135abb | ||
|
|
82983871fc | ||
|
|
7f17ff8b78 | ||
|
|
9efece315f | ||
|
|
d82cb1e64c | ||
|
|
7ab2894ae8 | ||
|
|
0991888494 | ||
|
|
42937bcf76 | ||
|
|
32fb6eadbc | ||
|
|
5a1ebabc87 | ||
|
|
57f51d7cfa | ||
|
|
d2ec0cacc3 | ||
|
|
aaf3b20e65 | ||
|
|
5912a170f1 | ||
|
|
ed3feaf2a0 | ||
|
|
d953f4e4a0 | ||
|
|
c2d346282c | ||
|
|
9d85f56af3 | ||
|
|
78eea6aad0 | ||
|
|
5306e45f83 | ||
|
|
4bd40aeeb0 | ||
|
|
f6d69ea193 | ||
|
|
fe5990ff9a | ||
|
|
6db5e72752 | ||
|
|
62fcc93a21 | ||
|
|
d6c8120b7c | ||
|
|
20994e6656 | ||
|
|
7a64851761 | ||
|
|
88f3155132 | ||
|
|
8bd2250d45 | ||
|
|
0e7e7af85a | ||
|
|
098e8c2518 | ||
|
|
5a06811732 | ||
|
|
c4b02f7717 | ||
|
|
a2f29766e6 | ||
|
|
091b9940ef | ||
|
|
c0db279414 | ||
|
|
41096c9c65 | ||
|
|
bf227e3e92 | ||
|
|
fa9ed5885b | ||
|
|
c8df5ed179 | ||
|
|
9d883d112c | ||
|
|
142b94139c | ||
|
|
2a20d54530 | ||
|
|
f2e8d0175b | ||
|
|
690e8ca1b2 | ||
|
|
18073cb726 | ||
|
|
bdac1e7460 | ||
|
|
8cfccfb2e0 | ||
|
|
283950db39 | ||
|
|
9cb1cff671 | ||
|
|
03dbeaf988 | ||
|
|
bef8ba46cd | ||
|
|
23afbc14c9 | ||
|
|
72bf8ad011 | ||
|
|
93b87ec8ff | ||
|
|
d4fb6ef049 | ||
|
|
97facdaf9c | ||
|
|
5eda4427df | ||
|
|
6b2b792df8 | ||
|
|
aad68b23ac | ||
|
|
ff91aa1ad6 | ||
|
|
15eb36a5bc | ||
|
|
79d188fb5c | ||
|
|
42444e6c83 | ||
|
|
d7a25580d1 | ||
|
|
1da3989fa3 | ||
|
|
202fafac84 | ||
|
|
2b9a44761f | ||
|
|
d24b0e8af8 | ||
|
|
b551777d49 | ||
|
|
3253b26818 | ||
|
|
9f67dbe9aa | ||
|
|
01fa7bbe87 | ||
|
|
e8644962c5 | ||
|
|
c793089131 | ||
|
|
8fe7a47367 | ||
|
|
b7e35ce5e5 | ||
|
|
7c17241882 | ||
|
|
ab32f881c2 | ||
|
|
b06becead4 | ||
|
|
f32ca13dff | ||
|
|
83028b3b3d | ||
|
|
ad74fd989b | ||
|
|
179ce86bef | ||
|
|
1b321b424e | ||
|
|
d1ba2cc3d4 | ||
|
|
4738b03a45 | ||
|
|
83e57b63ec | ||
|
|
e14e750bd9 | ||
|
|
1c4a6ca85e | ||
|
|
c922ae1571 | ||
|
|
ed6189a963 | ||
|
|
a6979b213b | ||
|
|
f873ebe079 | ||
|
|
ab692dfb25 | ||
|
|
e50b601ccf | ||
|
|
37c47b0a54 | ||
|
|
6f9cea2759 | ||
|
|
0601e123fb | ||
|
|
08e4541a96 | ||
|
|
66acc19e89 | ||
|
|
5b94a0624c | ||
|
|
1b5b2fd6f9 | ||
|
|
54c502dd64 | ||
|
|
e3db40ae25 | ||
|
|
52ab962ace | ||
|
|
05247ee8d9 | ||
|
|
dbf75684ee | ||
|
|
cb2319492b | ||
|
|
731e6abc90 | ||
|
|
58b33b6c8d | ||
|
|
43c8bbda71 | ||
|
|
e011e4cfd6 | ||
|
|
c8fd2d69bd | ||
|
|
ef1f7b1d08 | ||
|
|
5f12976d8f | ||
|
|
ef60f36c90 | ||
|
|
c8ed45b1a7 | ||
|
|
57fe68bae8 | ||
|
|
aa32fdcd07 | ||
|
|
d0d59135e3 | ||
|
|
27ec5b1b74 | ||
|
|
2c1caabce6 | ||
|
|
8f3cd9ff36 | ||
|
|
6366904c91 | ||
|
|
7f3e6de127 | ||
|
|
5b43cceb94 | ||
|
|
d933416b2d | ||
|
|
e260ef01f0 | ||
|
|
0fef3d7a63 | ||
|
|
dc5c0b1d1e | ||
|
|
9a1cde6abc | ||
|
|
29864b8bb2 | ||
|
|
aa5294f110 | ||
|
|
4387d343b5 | ||
|
|
ff979aa34b | ||
|
|
21af368068 | ||
|
|
d6afd556d3 | ||
|
|
778d2c4ec5 | ||
|
|
fff29b36ee | ||
|
|
fedb79a142 | ||
|
|
d06816052f | ||
|
|
5739e167f4 | ||
|
|
a249548a20 | ||
|
|
7b6538d01e | ||
|
|
7025bde56e | ||
|
|
3585af0cea | ||
|
|
5406224c6f | ||
|
|
18ff0bba54 | ||
|
|
7c13e57ea2 | ||
|
|
b8605b9a97 | ||
|
|
4baa7a9b44 | ||
|
|
3af2f3e03b | ||
|
|
dc68ecb584 | ||
|
|
421f107728 | ||
|
|
1f1bdd08ed | ||
|
|
1a7059aad1 | ||
|
|
8d461e61b3 | ||
|
|
fa0ca83709 | ||
|
|
a783f9c271 | ||
|
|
b4901e89d6 | ||
|
|
6690d3f2cb | ||
|
|
21d6989182 | ||
|
|
97ad3567e3 | ||
|
|
22a69df31b | ||
|
|
c0ad9d8977 | ||
|
|
a927d0e9cb | ||
|
|
5759ca1ca5 | ||
|
|
9955157dd3 | ||
|
|
d42de42709 | ||
|
|
a7dc3a1227 | ||
|
|
9c5f525e90 | ||
|
|
db8bd659c3 | ||
|
|
0dab394d1b | ||
|
|
9a9e47b4ff | ||
|
|
deb2f6ca64 | ||
|
|
5bce9bb816 | ||
|
|
ccda6b627d | ||
|
|
20cfbf7230 | ||
|
|
805eabd07e | ||
|
|
c42c053d66 | ||
|
|
d8e51f44ac | ||
|
|
0b5645496b | ||
|
|
2c37dbf5c4 | ||
|
|
cd6dc7a47e | ||
|
|
7362f0cd30 | ||
|
|
de2924883c | ||
|
|
942aa469e3 | ||
|
|
d57cc59115 | ||
|
|
f8ff3a18a0 | ||
|
|
96171013d6 | ||
|
|
d741d6ccf5 | ||
|
|
8ff73d5647 | ||
|
|
5d3d04b5d3 | ||
|
|
75ed1a6889 | ||
|
|
9bfbea11db | ||
|
|
5eea260618 | ||
|
|
f1e70d2c66 | ||
|
|
da22cb301e | ||
|
|
2b93f2a253 | ||
|
|
8ab42ff7da | ||
|
|
9bf57005f0 | ||
|
|
a80d498eab | ||
|
|
4847596072 | ||
|
|
d4ba58b55a | ||
|
|
73ab43f16a | ||
|
|
306d5074f2 | ||
|
|
745abaab2e | ||
|
|
c22b91f9d9 | ||
|
|
0bea93ea46 | ||
|
|
ecf683e663 | ||
|
|
0af7ef905d | ||
|
|
c169c0a80b | ||
|
|
878d62f483 | ||
|
|
8a33cea8bd | ||
|
|
d6c194eaa8 | ||
|
|
13d32c26b6 | ||
|
|
df146a4b58 | ||
|
|
4a02034ee3 | ||
|
|
acb24d69e3 | ||
|
|
56cfeac214 | ||
|
|
b86ad6e5bb | ||
|
|
f57c95496a | ||
|
|
826b2c3e94 | ||
|
|
29b43c6ab8 | ||
|
|
a434227807 | ||
|
|
3a7fb81276 | ||
|
|
12224fc892 | ||
|
|
64fdedf726 | ||
|
|
d3b61f320f | ||
|
|
6be7c05415 | ||
|
|
92b924754d | ||
|
|
40ddfdd48f | ||
|
|
afa5ad36f2 | ||
|
|
916a5c1054 | ||
|
|
ea914be4c7 | ||
|
|
e6eb41ad24 | ||
|
|
43ee2d1836 | ||
|
|
3bd4e6723e | ||
|
|
0e763c2ca6 | ||
|
|
34353e3614 | ||
|
|
d91c7b22d7 | ||
|
|
ac4861c76e | ||
|
|
6edd124556 | ||
|
|
f426183d22 | ||
|
|
33db91f799 | ||
|
|
8803380a30 | ||
|
|
b5beaad7bf | ||
|
|
304259c653 | ||
|
|
5ccc836b20 | ||
|
|
087bb0e8fe | ||
|
|
09a476ffa5 | ||
|
|
429ad2b77c | ||
|
|
c215e3dc27 | ||
|
|
0a1da79487 | ||
|
|
42e4e15cdb | ||
|
|
a3e7a23979 | ||
|
|
430a628e18 | ||
|
|
5f70690af5 | ||
|
|
b852933a9e | ||
|
|
074c45aa3d | ||
|
|
a52545d19a | ||
|
|
3e55bb4717 | ||
|
|
be408b98de | ||
|
|
12cc5fd0d2 | ||
|
|
589df289be | ||
|
|
7abc88f2c3 | ||
|
|
f318dc758b | ||
|
|
6628e22ac0 | ||
|
|
c4cf4e2392 | ||
|
|
56dfb98df5 | ||
|
|
4251e6dec7 | ||
|
|
bcc9f0301c | ||
|
|
17f2571be3 | ||
|
|
a9b24f7961 | ||
|
|
321125adf6 | ||
|
|
5c0db45223 | ||
|
|
76cc8845d2 | ||
|
|
38031a2537 | ||
|
|
1fe64e2b5d | ||
|
|
d572529a9d | ||
|
|
cb29baa698 | ||
|
|
696d0e2a40 | ||
|
|
75bbcd23a4 | ||
|
|
25002406b8 | ||
|
|
ed3ea32f45 | ||
|
|
63b1f732db | ||
|
|
84ebce0786 | ||
|
|
ed747ae4d9 | ||
|
|
8a103a7555 | ||
|
|
e8c166be31 |
@@ -139,6 +139,7 @@ patch(ListRenderer.prototype, 'jikimo_frontend.ListRenderer', {
|
|||||||
owl.onMounted(() => {
|
owl.onMounted(() => {
|
||||||
this.activeElement = this.uiService.activeElement;
|
this.activeElement = this.uiService.activeElement;
|
||||||
this.setRequired()
|
this.setRequired()
|
||||||
|
this.listherHeaderBodyNum()
|
||||||
})
|
})
|
||||||
return this._super(...arguments);
|
return this._super(...arguments);
|
||||||
},
|
},
|
||||||
@@ -165,6 +166,26 @@ patch(ListRenderer.prototype, 'jikimo_frontend.ListRenderer', {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(e)
|
console.log(e)
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
listherHeaderBodyNum() {
|
||||||
|
const dom = this.tableRef.el
|
||||||
|
try {
|
||||||
|
const thead = $(dom).children('thead')
|
||||||
|
const tbody = $(dom).children('tbody')
|
||||||
|
const thead_tr = thead.children().eq(0)
|
||||||
|
const tbody_tr = tbody.children().eq(0)
|
||||||
|
const thead_th_num = thead_tr.children().length
|
||||||
|
const tbody_tr_num = tbody_tr.children().length
|
||||||
|
const num = thead_th_num - tbody_tr_num
|
||||||
|
if(num == -1) {
|
||||||
|
tbody.children('tr').each(function () {
|
||||||
|
$(this).children('td').eq(0).remove()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -536,3 +536,7 @@ div:has(.o_required_modifier) > label::before {
|
|||||||
position: unset;
|
position: unset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 修复搜索面板checkbox样式
|
||||||
|
.o_search_panel .form-check .form-check-label span {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
@@ -190,7 +190,7 @@ def _create(self, data_list):
|
|||||||
# 如果该用户组被限制创建或更新操作
|
# 如果该用户组被限制创建或更新操作
|
||||||
if rec['is_create_or_update']:
|
if rec['is_create_or_update']:
|
||||||
raise UserError(
|
raise UserError(
|
||||||
_("You are restricted from performing this operation. Please contact the administrator."))
|
_("您没有执行此操作的权限。请联系管理员"))
|
||||||
else:
|
else:
|
||||||
# 如果 'access.right' 模型不存在,可以在这里定义备选逻辑
|
# 如果 'access.right' 模型不存在,可以在这里定义备选逻辑
|
||||||
# 例如,记录日志、发送通知或者简单地跳过这部分逻辑
|
# 例如,记录日志、发送通知或者简单地跳过这部分逻辑
|
||||||
|
|||||||
5
jikimo_purchase_tier_validation/__init__.py
Normal file
5
jikimo_purchase_tier_validation/__init__.py
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from . import controllers
|
||||||
|
from . import models
|
||||||
|
from . import wizards
|
||||||
42
jikimo_purchase_tier_validation/__manifest__.py
Normal file
42
jikimo_purchase_tier_validation/__manifest__.py
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
{
|
||||||
|
'name': "机企猫 采购审批流程",
|
||||||
|
|
||||||
|
'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': ['purchase', 'purchase_tier_validation', 'documents', 'purchase_request', 'account', 'purchase_order_approved'],
|
||||||
|
|
||||||
|
# always loaded
|
||||||
|
'data': [
|
||||||
|
'security/ir.model.access.csv',
|
||||||
|
'data/documents_data.xml',
|
||||||
|
'wizards/upload_file_wizard_view.xml',
|
||||||
|
'views/views.xml',
|
||||||
|
],
|
||||||
|
# only loaded in demonstration mode
|
||||||
|
'demo': [
|
||||||
|
'demo/demo.xml',
|
||||||
|
],
|
||||||
|
|
||||||
|
'assets': {
|
||||||
|
'web.assets_backend': [
|
||||||
|
'jikimo_purchase_tier_validation/static/src/js/ir_model_extend.js',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
3
jikimo_purchase_tier_validation/controllers/__init__.py
Normal file
3
jikimo_purchase_tier_validation/controllers/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from . import controllers
|
||||||
21
jikimo_purchase_tier_validation/controllers/controllers.py
Normal file
21
jikimo_purchase_tier_validation/controllers/controllers.py
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# from odoo import http
|
||||||
|
|
||||||
|
|
||||||
|
# class JikimoPurchaseTierValidation(http.Controller):
|
||||||
|
# @http.route('/jikimo_purchase_tier_validation/jikimo_purchase_tier_validation', auth='public')
|
||||||
|
# def index(self, **kw):
|
||||||
|
# return "Hello, world"
|
||||||
|
|
||||||
|
# @http.route('/jikimo_purchase_tier_validation/jikimo_purchase_tier_validation/objects', auth='public')
|
||||||
|
# def list(self, **kw):
|
||||||
|
# return http.request.render('jikimo_purchase_tier_validation.listing', {
|
||||||
|
# 'root': '/jikimo_purchase_tier_validation/jikimo_purchase_tier_validation',
|
||||||
|
# 'objects': http.request.env['jikimo_purchase_tier_validation.jikimo_purchase_tier_validation'].search([]),
|
||||||
|
# })
|
||||||
|
|
||||||
|
# @http.route('/jikimo_purchase_tier_validation/jikimo_purchase_tier_validation/objects/<model("jikimo_purchase_tier_validation.jikimo_purchase_tier_validation"):obj>', auth='public')
|
||||||
|
# def object(self, obj, **kw):
|
||||||
|
# return http.request.render('jikimo_purchase_tier_validation.object', {
|
||||||
|
# 'object': obj
|
||||||
|
# })
|
||||||
11
jikimo_purchase_tier_validation/data/documents_data.xml
Normal file
11
jikimo_purchase_tier_validation/data/documents_data.xml
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<data noupdate="1">
|
||||||
|
<!-- 创建采购合同文件夹 -->
|
||||||
|
<record id="documents_purchase_contracts_folder" model="documents.folder">
|
||||||
|
<field name="name">采购合同</field>
|
||||||
|
<field name="description">存放采购合同相关文件</field>
|
||||||
|
<field name="sequence">10</field>
|
||||||
|
</record>
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
30
jikimo_purchase_tier_validation/demo/demo.xml
Normal file
30
jikimo_purchase_tier_validation/demo/demo.xml
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
<odoo>
|
||||||
|
<data>
|
||||||
|
<!--
|
||||||
|
<record id="object0" model="jikimo_purchase_tier_validation.jikimo_purchase_tier_validation">
|
||||||
|
<field name="name">Object 0</field>
|
||||||
|
<field name="value">0</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="object1" model="jikimo_purchase_tier_validation.jikimo_purchase_tier_validation">
|
||||||
|
<field name="name">Object 1</field>
|
||||||
|
<field name="value">10</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="object2" model="jikimo_purchase_tier_validation.jikimo_purchase_tier_validation">
|
||||||
|
<field name="name">Object 2</field>
|
||||||
|
<field name="value">20</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="object3" model="jikimo_purchase_tier_validation.jikimo_purchase_tier_validation">
|
||||||
|
<field name="name">Object 3</field>
|
||||||
|
<field name="value">30</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="object4" model="jikimo_purchase_tier_validation.jikimo_purchase_tier_validation">
|
||||||
|
<field name="name">Object 4</field>
|
||||||
|
<field name="value">40</field>
|
||||||
|
</record>
|
||||||
|
-->
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
3
jikimo_purchase_tier_validation/models/__init__.py
Normal file
3
jikimo_purchase_tier_validation/models/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from . import models
|
||||||
215
jikimo_purchase_tier_validation/models/models.py
Normal file
215
jikimo_purchase_tier_validation/models/models.py
Normal file
@@ -0,0 +1,215 @@
|
|||||||
|
from odoo import models, fields, api, _
|
||||||
|
from odoo.exceptions import ValidationError
|
||||||
|
import logging
|
||||||
|
|
||||||
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class jikimo_purchase_tier_validation(models.Model):
|
||||||
|
_name = 'purchase.order'
|
||||||
|
_inherit = ['purchase.order', 'tier.validation']
|
||||||
|
_description = "采购订单"
|
||||||
|
|
||||||
|
_tier_validation_buttons_xpath = "/form/header/button[@id='draft_confirm'][1]"
|
||||||
|
|
||||||
|
contract_document_id = fields.Many2one('documents.document', string='合同文件')
|
||||||
|
contract_file = fields.Binary(related='contract_document_id.datas', string='合同文件内容')
|
||||||
|
contract_file_name = fields.Char(related='contract_document_id.attachment_id.name', string='文件名')
|
||||||
|
|
||||||
|
# 是否已上传合同文件
|
||||||
|
is_upload_contract_file = fields.Boolean(string='是否已上传合同文件', default=False)
|
||||||
|
|
||||||
|
def button_confirm(self):
|
||||||
|
for record in self:
|
||||||
|
if record.need_validation and record.validation_status != 'validated':
|
||||||
|
raise ValidationError(_('此操作需要至少对一条记录进行审批。\n请发起审批申请。'))
|
||||||
|
if record.state in ['to approve']:
|
||||||
|
raise ValidationError(_('请先完成审批。'))
|
||||||
|
if record.state == 'approved':
|
||||||
|
record.state = 'purchase'
|
||||||
|
return super().button_confirm()
|
||||||
|
|
||||||
|
# def button_confirm(self):
|
||||||
|
# self = self.with_context(skip_validation=True)
|
||||||
|
# return super().button_confirm()
|
||||||
|
#
|
||||||
|
# def _check_state_conditions(self, vals):
|
||||||
|
# self.ensure_one()
|
||||||
|
# if self._context.get('skip_validation'):
|
||||||
|
# return False
|
||||||
|
# return (
|
||||||
|
# self._check_state_from_condition()
|
||||||
|
# and vals.get(self._state_field) in self._state_to
|
||||||
|
# )
|
||||||
|
|
||||||
|
def request_validation(self):
|
||||||
|
for record in self:
|
||||||
|
error_messages = []
|
||||||
|
|
||||||
|
# 检查必填字段
|
||||||
|
required_fields = {
|
||||||
|
'partner_ref': '合同名称',
|
||||||
|
'contract_number': '合同编号'
|
||||||
|
}
|
||||||
|
|
||||||
|
missing_fields = [
|
||||||
|
name for field, name in required_fields.items()
|
||||||
|
if not record[field]
|
||||||
|
]
|
||||||
|
|
||||||
|
if missing_fields:
|
||||||
|
error_messages.append('* 如下字段要求必须填写:%s' % '、'.join(missing_fields))
|
||||||
|
|
||||||
|
# 检查合同文件
|
||||||
|
if not record.contract_document_id:
|
||||||
|
error_messages.append('* 必须点击上传合同文件')
|
||||||
|
|
||||||
|
# 如果有任何错误,一次性显示所有错误信息
|
||||||
|
if error_messages:
|
||||||
|
raise ValidationError('\n'.join(error_messages))
|
||||||
|
|
||||||
|
# 添加通知消息
|
||||||
|
if hasattr(record, 'message_post'):
|
||||||
|
current_user = self.env.user.name
|
||||||
|
record.message_post(
|
||||||
|
body=f"<strong>{current_user}</strong> 提交审批",
|
||||||
|
message_type='notification',
|
||||||
|
subtype_xmlid='mail.mt_note'
|
||||||
|
)
|
||||||
|
res = super(jikimo_purchase_tier_validation, self).request_validation()
|
||||||
|
self.state = 'to approve'
|
||||||
|
return res
|
||||||
|
|
||||||
|
def restart_validation(self):
|
||||||
|
res = super(jikimo_purchase_tier_validation, self).restart_validation()
|
||||||
|
self.state = 'draft'
|
||||||
|
return res
|
||||||
|
|
||||||
|
def _validate_tier(self, tiers=False):
|
||||||
|
res = super(jikimo_purchase_tier_validation, self)._validate_tier(tiers)
|
||||||
|
tier_reviews = tiers or self.review_ids
|
||||||
|
|
||||||
|
# 检查是否所有审批都已通过
|
||||||
|
all_approved = all(
|
||||||
|
tier_review.status == 'approved'
|
||||||
|
for tier_review in tier_reviews
|
||||||
|
)
|
||||||
|
|
||||||
|
if all_approved and tier_reviews: # 确保有审批记录
|
||||||
|
self.state = 'approved'
|
||||||
|
|
||||||
|
return res
|
||||||
|
|
||||||
|
def _rejected_tier(self, tiers=False):
|
||||||
|
res = super(jikimo_purchase_tier_validation, self)._rejected_tier(tiers)
|
||||||
|
self.state = 'draft'
|
||||||
|
return res
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def _get_under_validation_exceptions(self):
|
||||||
|
res = super(jikimo_purchase_tier_validation, self)._get_under_validation_exceptions()
|
||||||
|
res.append("state")
|
||||||
|
return res
|
||||||
|
|
||||||
|
# 上传合同文件
|
||||||
|
def upload_contract_file(self):
|
||||||
|
print('upload_contract_file===========================')
|
||||||
|
# self.ensure_one()
|
||||||
|
# return {
|
||||||
|
# 'name': _('上传合同文件'),
|
||||||
|
# 'type': 'ir.actions.act_window',
|
||||||
|
# 'res_model': 'ir.attachment',
|
||||||
|
# 'view_mode': 'form',
|
||||||
|
# 'view_type': 'form',
|
||||||
|
# 'target': 'new',
|
||||||
|
# 'context': {
|
||||||
|
# 'default_res_model': self._name,
|
||||||
|
# 'default_res_id': self.id,
|
||||||
|
# 'default_type': 'binary',
|
||||||
|
# 'default_mimetype': 'application/pdf,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document,image/jpeg,image/png',
|
||||||
|
# }
|
||||||
|
# }
|
||||||
|
|
||||||
|
self.ensure_one()
|
||||||
|
action = {
|
||||||
|
'type': 'ir.actions.act_window',
|
||||||
|
'name': _('上传合同文件'),
|
||||||
|
'res_model': 'ir.attachment.wizard', # 我们需要创建一个新的向导模型
|
||||||
|
'view_mode': 'form',
|
||||||
|
'target': 'new',
|
||||||
|
'context': {
|
||||||
|
'default_res_model': self._name,
|
||||||
|
'default_res_id': self.id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return action
|
||||||
|
|
||||||
|
# 删除合同文件
|
||||||
|
def delete_contract_file(self):
|
||||||
|
self.ensure_one()
|
||||||
|
if self.contract_document_id:
|
||||||
|
try:
|
||||||
|
document = self.contract_document_id
|
||||||
|
|
||||||
|
# 清空关联
|
||||||
|
self.write({
|
||||||
|
'contract_document_id': False,
|
||||||
|
'contract_file': False,
|
||||||
|
'contract_file_name': False
|
||||||
|
})
|
||||||
|
|
||||||
|
# 删除文档
|
||||||
|
if document:
|
||||||
|
document.with_context(no_attachment=True).sudo().unlink()
|
||||||
|
|
||||||
|
self.is_upload_contract_file = False
|
||||||
|
|
||||||
|
# 返回视图动作来刷新当前表单
|
||||||
|
return {
|
||||||
|
'type': 'ir.actions.act_window',
|
||||||
|
'res_model': 'purchase.order',
|
||||||
|
'res_id': self.id,
|
||||||
|
'view_mode': 'form',
|
||||||
|
'view_type': 'form',
|
||||||
|
'target': 'current',
|
||||||
|
'flags': {'mode': 'readonly'},
|
||||||
|
}
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
_logger.error('删除合同文件时出错: %s', str(e))
|
||||||
|
return {
|
||||||
|
'type': 'ir.actions.client',
|
||||||
|
'tag': 'display_notification',
|
||||||
|
'params': {
|
||||||
|
'title': _('错误'),
|
||||||
|
'message': _('删除文件时出现错误'),
|
||||||
|
'type': 'danger',
|
||||||
|
'sticky': True,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
'type': 'ir.actions.client',
|
||||||
|
'tag': 'display_notification',
|
||||||
|
'params': {
|
||||||
|
'title': _('提示'),
|
||||||
|
'message': _('没有需要删除的合同文件'),
|
||||||
|
'type': 'warning',
|
||||||
|
'sticky': False,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class jikimo_purchase_request(models.Model):
|
||||||
|
_inherit = 'purchase.request'
|
||||||
|
_description = "采购申请"
|
||||||
|
|
||||||
|
|
||||||
|
class jikimo_account_payment(models.Model):
|
||||||
|
_inherit = 'account.payment'
|
||||||
|
_description = "付款单"
|
||||||
|
|
||||||
|
|
||||||
|
class jikimo_account_move(models.Model):
|
||||||
|
_inherit = 'account.move'
|
||||||
|
_description = "发票账单"
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||||
|
access_ir_attachment_wizard,ir.attachment.wizard,model_ir_attachment_wizard,base.group_user,1,1,1,1
|
||||||
|
@@ -0,0 +1,14 @@
|
|||||||
|
/** @odoo-module **/
|
||||||
|
|
||||||
|
import {registerPatch} from "@mail/model/model_core";
|
||||||
|
|
||||||
|
registerPatch({
|
||||||
|
name: "ir.model.review",
|
||||||
|
fields: {
|
||||||
|
availableWebViews: {
|
||||||
|
compute() {
|
||||||
|
return ["list", "form", "activity"];
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
24
jikimo_purchase_tier_validation/views/templates.xml
Normal file
24
jikimo_purchase_tier_validation/views/templates.xml
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
<odoo>
|
||||||
|
<data>
|
||||||
|
<!--
|
||||||
|
<template id="listing">
|
||||||
|
<ul>
|
||||||
|
<li t-foreach="objects" t-as="object">
|
||||||
|
<a t-attf-href="#{ root }/objects/#{ object.id }">
|
||||||
|
<t t-esc="object.display_name"/>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</template>
|
||||||
|
<template id="object">
|
||||||
|
<h1><t t-esc="object.display_name"/></h1>
|
||||||
|
<dl>
|
||||||
|
<t t-foreach="object._fields" t-as="field">
|
||||||
|
<dt><t t-esc="field"/></dt>
|
||||||
|
<dd><t t-esc="object[field]"/></dd>
|
||||||
|
</t>
|
||||||
|
</dl>
|
||||||
|
</template>
|
||||||
|
-->
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
98
jikimo_purchase_tier_validation/views/views.xml
Normal file
98
jikimo_purchase_tier_validation/views/views.xml
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<odoo>
|
||||||
|
<data>
|
||||||
|
<record model="ir.ui.view" id="tier_validation_view_approved_purchase_order_form_inherit">
|
||||||
|
<field name="name">tier_validation_view_approved_purchase_order_form_inherit</field>
|
||||||
|
<field name="model">purchase.order</field>
|
||||||
|
<field name="inherit_id" ref="purchase_order_approved.purchase_order_form"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//button[@name='button_release']" position="replace">
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="ir.ui.view" id="tier_validation_view_purchase_order_form_inherit">
|
||||||
|
<field name="name">tier_validation_view_purchase_order_form_inherit</field>
|
||||||
|
<field name="model">purchase.order</field>
|
||||||
|
<field name="inherit_id" ref="purchase.purchase_order_form"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//header/button[@name='button_approve']" position="replace">
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//header/button[@name='button_cancel']" position="replace">
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//header/field[@name='state']" position="replace">
|
||||||
|
<field name="state" widget="statusbar" statusbar_visible="draft,sent,to approve, approved, purchase" readonly="1"/>
|
||||||
|
</xpath>
|
||||||
|
|
||||||
|
|
||||||
|
<xpath expr="//header/button[last()]" position="after">
|
||||||
|
<button name="button_cancel" states="draft,to approve,sent,purchase" string="取消" type="object" data-hotkey="x" />
|
||||||
|
</xpath>
|
||||||
|
|
||||||
|
<xpath expr="//header/button[@name='action_rfq_send'][1]" position="before">
|
||||||
|
<field name="validation_status" invisible="1"/>
|
||||||
|
<field name="is_upload_contract_file" invisible="1"/>
|
||||||
|
<button name="upload_contract_file" string="上传合同" type="object" class="oe_highlight" attrs="{'invisible': ['|', '|', ('validation_status', '!=', 'no'), ('is_upload_contract_file', '=', True), ('state', 'not in', ['draft', 'sent'])]}"/>]}"/>
|
||||||
|
<button name="delete_contract_file" string="删除合同" type="object" class="oe_highlight" attrs="{'invisible': ['|', ('validation_status', '!=', 'no'), ('is_upload_contract_file', '=', False)]}"/>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//notebook/page[1]" position="before">
|
||||||
|
<page string="合同" name="contract_documents"
|
||||||
|
attrs="{'invisible': [('contract_document_id', '=', False)]}"
|
||||||
|
autofocus="autofocus">
|
||||||
|
<group>
|
||||||
|
<group>
|
||||||
|
<field name="contract_document_id" invisible="1"/>
|
||||||
|
<field name="contract_file_name" invisible="1"/>
|
||||||
|
<field name="contract_file"
|
||||||
|
widget="adaptive_viewer"
|
||||||
|
filename="contract_file_name"/>
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
</page>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<!-- actions opening views on models -->
|
||||||
|
<!--
|
||||||
|
<record model="ir.actions.act_window" id="jikimo_purchase_tier_validation.action_window">
|
||||||
|
<field name="name">jikimo_purchase_tier_validation window</field>
|
||||||
|
<field name="res_model">jikimo_purchase_tier_validation.jikimo_purchase_tier_validation</field>
|
||||||
|
<field name="view_mode">tree,form</field>
|
||||||
|
</record>
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!-- server action to the one above -->
|
||||||
|
<!--
|
||||||
|
<record model="ir.actions.server" id="jikimo_purchase_tier_validation.action_server">
|
||||||
|
<field name="name">jikimo_purchase_tier_validation server</field>
|
||||||
|
<field name="model_id" ref="model_jikimo_purchase_tier_validation_jikimo_purchase_tier_validation"/>
|
||||||
|
<field name="state">code</field>
|
||||||
|
<field name="code">
|
||||||
|
action = {
|
||||||
|
"type": "ir.actions.act_window",
|
||||||
|
"view_mode": "tree,form",
|
||||||
|
"res_model": model._name,
|
||||||
|
}
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!-- Top menu item -->
|
||||||
|
<!--
|
||||||
|
<menuitem name="jikimo_purchase_tier_validation" id="jikimo_purchase_tier_validation.menu_root"/>
|
||||||
|
-->
|
||||||
|
<!-- menu categories -->
|
||||||
|
<!--
|
||||||
|
<menuitem name="Menu 1" id="jikimo_purchase_tier_validation.menu_1" parent="jikimo_purchase_tier_validation.menu_root"/>
|
||||||
|
<menuitem name="Menu 2" id="jikimo_purchase_tier_validation.menu_2" parent="jikimo_purchase_tier_validation.menu_root"/>
|
||||||
|
-->
|
||||||
|
<!-- actions -->
|
||||||
|
<!--
|
||||||
|
<menuitem name="List" id="jikimo_purchase_tier_validation.menu_1_list" parent="jikimo_purchase_tier_validation.menu_1"
|
||||||
|
action="jikimo_purchase_tier_validation.action_window"/>
|
||||||
|
<menuitem name="Server to list" id="jikimo_purchase_tier_validation" parent="jikimo_purchase_tier_validation.menu_2"
|
||||||
|
action="jikimo_purchase_tier_validation.action_server"/>
|
||||||
|
-->
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
2
jikimo_purchase_tier_validation/wizards/__init__.py
Normal file
2
jikimo_purchase_tier_validation/wizards/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
from . import upload_file_wizard
|
||||||
|
from . import comment_wizard
|
||||||
15
jikimo_purchase_tier_validation/wizards/comment_wizard.py
Normal file
15
jikimo_purchase_tier_validation/wizards/comment_wizard.py
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
from odoo import models, fields
|
||||||
|
|
||||||
|
|
||||||
|
class CommentWizard(models.TransientModel):
|
||||||
|
_inherit = "comment.wizard"
|
||||||
|
|
||||||
|
def add_comment(self):
|
||||||
|
|
||||||
|
rec = self.env[self.res_model].browse(self.res_id)
|
||||||
|
|
||||||
|
self.review_ids = rec.review_ids
|
||||||
|
|
||||||
|
result = super(CommentWizard, self).add_comment()
|
||||||
|
|
||||||
|
return result
|
||||||
114
jikimo_purchase_tier_validation/wizards/upload_file_wizard.py
Normal file
114
jikimo_purchase_tier_validation/wizards/upload_file_wizard.py
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
from odoo import models, fields, api, _
|
||||||
|
|
||||||
|
|
||||||
|
class IrAttachmentWizard(models.TransientModel):
|
||||||
|
_name = 'ir.attachment.wizard'
|
||||||
|
_description = '文件上传向导'
|
||||||
|
|
||||||
|
attachment = fields.Binary(string='选择文件', required=True)
|
||||||
|
filename = fields.Char(string='文件名')
|
||||||
|
res_model = fields.Char()
|
||||||
|
res_id = fields.Integer()
|
||||||
|
|
||||||
|
# def action_upload_file(self):
|
||||||
|
# self.ensure_one()
|
||||||
|
# # 首先创建 ir.attachment
|
||||||
|
# attachment = self.env['ir.attachment'].create({
|
||||||
|
# 'name': self.filename,
|
||||||
|
# 'type': 'binary',
|
||||||
|
# 'datas': self.attachment,
|
||||||
|
# 'res_model': self.res_model,
|
||||||
|
# 'res_id': self.res_id,
|
||||||
|
# })
|
||||||
|
#
|
||||||
|
# # 获取默认的文档文件夹
|
||||||
|
# workspace = self.env['documents.folder'].search([('name', '=', '采购合同')], limit=1)
|
||||||
|
#
|
||||||
|
# # 创建 documents.document 记录
|
||||||
|
# document = self.env['documents.document'].create({
|
||||||
|
# 'name': self.filename,
|
||||||
|
# 'attachment_id': attachment.id,
|
||||||
|
# 'folder_id': workspace.id,
|
||||||
|
# 'res_model': self.res_model,
|
||||||
|
# 'res_id': self.res_id,
|
||||||
|
# })
|
||||||
|
#
|
||||||
|
# return {
|
||||||
|
# 'type': 'ir.actions.client',
|
||||||
|
# 'tag': 'display_notification',
|
||||||
|
# 'params': {
|
||||||
|
# 'title': _('成功'),
|
||||||
|
# 'message': _('文件上传成功'),
|
||||||
|
# 'type': 'success',
|
||||||
|
# }
|
||||||
|
# }
|
||||||
|
|
||||||
|
def action_upload_file(self):
|
||||||
|
self.ensure_one()
|
||||||
|
# 获取当前用户的 partner_id
|
||||||
|
current_partner = self.env.user.partner_id
|
||||||
|
# 首先创建 ir.attachment
|
||||||
|
attachment = self.env['ir.attachment'].create({
|
||||||
|
'name': self.filename,
|
||||||
|
'type': 'binary',
|
||||||
|
'datas': self.attachment,
|
||||||
|
'res_model': self.res_model,
|
||||||
|
'res_id': self.res_id,
|
||||||
|
})
|
||||||
|
|
||||||
|
# 获取默认的文档文件夹
|
||||||
|
workspace = self.env['documents.folder'].search([('name', '=', '采购合同')], limit=1)
|
||||||
|
|
||||||
|
# 创建 documents.document 记录
|
||||||
|
document = self.env['documents.document'].create({
|
||||||
|
'name': self.filename,
|
||||||
|
'attachment_id': attachment.id,
|
||||||
|
'folder_id': workspace.id,
|
||||||
|
'res_model': self.res_model,
|
||||||
|
'res_id': self.res_id,
|
||||||
|
'partner_id': current_partner.id,
|
||||||
|
})
|
||||||
|
|
||||||
|
# 更新采购订单的合同文档字段
|
||||||
|
purchase_order = self.env['purchase.order'].browse(self.res_id)
|
||||||
|
purchase_order.write({
|
||||||
|
'contract_document_id': document.id,
|
||||||
|
'is_upload_contract_file': True
|
||||||
|
})
|
||||||
|
|
||||||
|
# 显示成功消息并关闭向导
|
||||||
|
message = {
|
||||||
|
'type': 'ir.actions.client',
|
||||||
|
'tag': 'display_notification',
|
||||||
|
'params': {
|
||||||
|
'title': _('成功'),
|
||||||
|
'message': _('文件上传成功'),
|
||||||
|
'type': 'success',
|
||||||
|
'sticky': False, # 自动消失
|
||||||
|
'next': {
|
||||||
|
'type': 'ir.actions.act_window_close'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return message
|
||||||
|
|
||||||
|
|
||||||
|
# def action_upload_file(self):
|
||||||
|
# self.ensure_one()
|
||||||
|
# attachment = self.env['ir.attachment'].create({
|
||||||
|
# 'name': self.filename,
|
||||||
|
# 'type': 'binary',
|
||||||
|
# 'datas': self.attachment,
|
||||||
|
# 'res_model': self.res_model,
|
||||||
|
# 'res_id': self.res_id,
|
||||||
|
# })
|
||||||
|
# return {
|
||||||
|
# 'type': 'ir.actions.client',
|
||||||
|
# 'tag': 'display_notification',
|
||||||
|
# 'params': {
|
||||||
|
# 'title': _('成功'),
|
||||||
|
# 'message': _('文件上传成功'),
|
||||||
|
# 'type': 'success',
|
||||||
|
# }
|
||||||
|
# }
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<record id="view_upload_file_wizard_form" model="ir.ui.view">
|
||||||
|
<field name="name">ir.attachment.wizard.form</field>
|
||||||
|
<field name="model">ir.attachment.wizard</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<form string="上传文件">
|
||||||
|
<group>
|
||||||
|
<field name="attachment" widget="binary" filename="filename" options="{'accepted_file_extensions': '.pdf,.doc,.docx,.jpg,.jpeg,.png'}"/>
|
||||||
|
<field name="filename" invisible="1"/>
|
||||||
|
<field name="res_model" invisible="1"/>
|
||||||
|
<field name="res_id" invisible="1"/>
|
||||||
|
</group>
|
||||||
|
<footer>
|
||||||
|
<button name="action_upload_file" string="确认上传" type="object" class="btn-primary"/>
|
||||||
|
<button string="取消" class="btn-secondary" special="cancel"/>
|
||||||
|
</footer>
|
||||||
|
</form>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
</odoo>
|
||||||
@@ -12,10 +12,6 @@ def _data_install(cr, registry):
|
|||||||
env.ref('jikimo_sale_multiple_supply_methods.product_template_default').product_variant_id.write({'active': False, 'is_bfm': True})
|
env.ref('jikimo_sale_multiple_supply_methods.product_template_default').product_variant_id.write({'active': False, 'is_bfm': True})
|
||||||
env.ref('jikimo_sale_multiple_supply_methods.product_template_embryo_customer_provided').product_variant_id.write({'active': False})
|
env.ref('jikimo_sale_multiple_supply_methods.product_template_embryo_customer_provided').product_variant_id.write({'active': False})
|
||||||
env.ref('jikimo_sale_multiple_supply_methods.product_template_outsourcing').product_variant_id.write({'active': False, 'is_bfm': True})
|
env.ref('jikimo_sale_multiple_supply_methods.product_template_outsourcing').product_variant_id.write({'active': False, 'is_bfm': True})
|
||||||
env.ref('sf_dlm.product_embryo_sf_self_machining').product_tmpl_id.write({'categ_type': '坯料'})
|
|
||||||
env.ref('sf_dlm.product_template_sf').product_tmpl_id.write({'categ_type': '成品'})
|
|
||||||
env.ref('sf_dlm.product_embryo_sf_outsource').product_tmpl_id.write({'categ_type': '坯料'})
|
|
||||||
env.ref('sf_dlm.product_embryo_sf_purchase').product_tmpl_id.write({'categ_type': '坯料'})
|
|
||||||
|
|
||||||
# 为三步制造增加规则
|
# 为三步制造增加规则
|
||||||
warehouse = env['stock.warehouse'].search([('company_id', '=', env.company.id)], limit=1)
|
warehouse = env['stock.warehouse'].search([('company_id', '=', env.company.id)], limit=1)
|
||||||
|
|||||||
@@ -6,12 +6,11 @@
|
|||||||
'author': 'fox',
|
'author': 'fox',
|
||||||
'website': '',
|
'website': '',
|
||||||
'category': '',
|
'category': '',
|
||||||
'depends': ['sf_dlm', 'sale_stock', 'sf_sale'],
|
'depends': ['sf_dlm', 'sale_stock', 'sf_sale', 'sale'],
|
||||||
"data": [
|
"data": [
|
||||||
'security/ir.model.access.csv',
|
'security/ir.model.access.csv',
|
||||||
'data/stock_routes.xml',
|
'data/stock_routes.xml',
|
||||||
'data/product_data.xml',
|
'data/product_data.xml',
|
||||||
'views/sale_order_views.xml',
|
|
||||||
# 'views/product_product_views.xml',
|
# 'views/product_product_views.xml',
|
||||||
],'assets': {
|
],'assets': {
|
||||||
# 'web.assets_backend': [
|
# 'web.assets_backend': [
|
||||||
|
|||||||
@@ -1,2 +1 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from . import main
|
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from . import product_template
|
from . import product_template
|
||||||
from . import sale_order
|
|
||||||
from . import mrp_bom
|
from . import mrp_bom
|
||||||
|
|||||||
@@ -23,4 +23,4 @@ class ProductTemplate(models.Model):
|
|||||||
self.is_bfm = product_template_id.is_bfm
|
self.is_bfm = product_template_id.is_bfm
|
||||||
self.is_manual_processing = product_template_id.is_manual_processing
|
self.is_manual_processing = product_template_id.is_manual_processing
|
||||||
# 复制 seller_ids
|
# 复制 seller_ids
|
||||||
self.seller_ids = [(0, 0, {'partner_id': seller.partner_id.id, 'delay': 1.0}) for seller in product_template_id.seller_ids]
|
self.seller_ids = [(0, 0, {'partner_id': seller.partner_id.id, 'delay': 1.0, 'price': seller.price}) for seller in product_template_id.seller_ids]
|
||||||
|
|||||||
@@ -51,11 +51,11 @@ class JikimoWorkorderException(models.Model):
|
|||||||
return res
|
return res
|
||||||
|
|
||||||
def _get_message(self, message_queue_ids):
|
def _get_message(self, message_queue_ids):
|
||||||
contents = super(JikimoWorkorderException, self)._get_message(message_queue_ids)
|
contents, _ = super(JikimoWorkorderException, self)._get_message(message_queue_ids)
|
||||||
url = self.env['ir.config_parameter'].get_param('web.base.url')
|
url = self.env['ir.config_parameter'].get_param('web.base.url')
|
||||||
action_id = self.env.ref('mrp.mrp_production_action').id
|
action_id = self.env.ref('mrp.mrp_production_action').id
|
||||||
for index, content in enumerate(contents):
|
for index, content in enumerate(contents):
|
||||||
exception_id = self.env['jikimo.workorder.exception'].browse(message_queue_ids[index].res_id)
|
exception_id = self.env['jikimo.workorder.exception'].browse(message_queue_ids[index].res_id)
|
||||||
url = url + '/web#id=%s&view_type=form&action=%s' % (exception_id.workorder_id.production_id.id, action_id)
|
url = url + '/web#id=%s&view_type=form&action=%s' % (exception_id.workorder_id.production_id.id, action_id)
|
||||||
contents[index] = content.replace('{{url}}', url)
|
contents[index] = content.replace('{{url}}', url)
|
||||||
return contents
|
return contents, message_queue_ids
|
||||||
|
|||||||
@@ -138,7 +138,7 @@ if env.user.has_group('mrp.group_mrp_workorder_dependencies'):
|
|||||||
<button name="openMenuPopup" t-att-disabled="isBlocked" class="btn btn-secondary o_workorder_icon_btn fa fa-bars" type="workorder_event" title="menu"/>
|
<button name="openMenuPopup" t-att-disabled="isBlocked" class="btn btn-secondary o_workorder_icon_btn fa fa-bars" type="workorder_event" title="menu"/>
|
||||||
<span groups="mrp_workorder.group_mrp_wo_tablet_timer">
|
<span groups="mrp_workorder.group_mrp_wo_tablet_timer">
|
||||||
<button name="button_pending" type="object" class="btn btn-secondary" attrs="{'invisible': ['|', ('is_user_working', '=', False), ('working_state', '=', 'blocked')]}" barcode_trigger="pause" string="PAUSE"/>
|
<button name="button_pending" type="object" class="btn btn-secondary" attrs="{'invisible': ['|', ('is_user_working', '=', False), ('working_state', '=', 'blocked')]}" barcode_trigger="pause" string="PAUSE"/>
|
||||||
<button name="button_start" type="object" class="btn btn-warning" attrs="{'invisible': ['|', '|', ('is_user_working', '=', True), ('working_state', '=', 'blocked'), ('state', '=', ('done', 'cancel'))]}" barcode_trigger="pause" string="CONTINUE"/>
|
<button name="button_start" type="object" class="btn btn-warning" attrs="{'invisible': ['|', '|', ('is_user_working', '=', True), ('working_state', '=', 'blocked'), ('state', '=', ('done','rework', 'cancel'))]}" barcode_trigger="pause" string="CONTINUE"/>
|
||||||
<button name="button_unblock" type="object" class="btn btn-secondary btn-danger o_unblock" attrs="{'invisible': [('working_state', '!=', 'blocked')]}">Unblock</button>
|
<button name="button_unblock" type="object" class="btn btn-secondary btn-danger o_unblock" attrs="{'invisible': [('working_state', '!=', 'blocked')]}">Unblock</button>
|
||||||
<field name="duration" widget="mrp_timer" class="ms-1" readonly="1"/>
|
<field name="duration" widget="mrp_timer" class="ms-1" readonly="1"/>
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
'sequence': 120,
|
'sequence': 120,
|
||||||
'summary': 'Control the quality of your products',
|
'summary': 'Control the quality of your products',
|
||||||
'website': 'https://www.odoo.com/app/quality',
|
'website': 'https://www.odoo.com/app/quality',
|
||||||
'depends': ['quality'],
|
'depends': ['quality', 'sf_manufacturing'],
|
||||||
'description': """
|
'description': """
|
||||||
Quality Control
|
Quality Control
|
||||||
===============
|
===============
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ from datetime import datetime
|
|||||||
import random
|
import random
|
||||||
|
|
||||||
from odoo import api, models, fields, _
|
from odoo import api, models, fields, _
|
||||||
|
from odoo.api import depends
|
||||||
from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT, float_round
|
from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT, float_round
|
||||||
from odoo.osv.expression import OR
|
from odoo.osv.expression import OR
|
||||||
|
|
||||||
@@ -122,7 +123,13 @@ class QualityPoint(models.Model):
|
|||||||
|
|
||||||
class QualityCheck(models.Model):
|
class QualityCheck(models.Model):
|
||||||
_inherit = "quality.check"
|
_inherit = "quality.check"
|
||||||
|
part_name = fields.Char('零件名称', compute='_compute_part_name_number', readonly=True)
|
||||||
|
part_number = fields.Char('零件图号', compute='_compute_part_name_number', readonly=True)
|
||||||
|
@depends('product_id')
|
||||||
|
def _compute_part_name_number(self):
|
||||||
|
for record in self:
|
||||||
|
record.part_number = record.product_id.part_number
|
||||||
|
record.part_name = record.product_id.part_name
|
||||||
failure_message = fields.Html(related='point_id.failure_message', readonly=True)
|
failure_message = fields.Html(related='point_id.failure_message', readonly=True)
|
||||||
measure = fields.Float('Measure', default=0.0, digits='Quality Tests', tracking=True)
|
measure = fields.Float('Measure', default=0.0, digits='Quality Tests', tracking=True)
|
||||||
measure_success = fields.Selection([
|
measure_success = fields.Selection([
|
||||||
@@ -152,6 +159,34 @@ class QualityCheck(models.Model):
|
|||||||
is_lot_tested_fractionally = fields.Boolean(related='point_id.is_lot_tested_fractionally')
|
is_lot_tested_fractionally = fields.Boolean(related='point_id.is_lot_tested_fractionally')
|
||||||
testing_percentage_within_lot = fields.Float(related="point_id.testing_percentage_within_lot")
|
testing_percentage_within_lot = fields.Float(related="point_id.testing_percentage_within_lot")
|
||||||
product_tracking = fields.Selection(related='product_id.tracking')
|
product_tracking = fields.Selection(related='product_id.tracking')
|
||||||
|
quality_check_type = fields.Selection([
|
||||||
|
('采购入库检', '采购入库检'),
|
||||||
|
('客供料入库检', '客供料入库检'),
|
||||||
|
('退货入库检', '退货入库检'),
|
||||||
|
('生产入库检', '生产入库检'),
|
||||||
|
('外协入库检', '外协入库检'),
|
||||||
|
('成品发货检', '成品发货检'),
|
||||||
|
('工序外协发货检', '工序外协发货检'),
|
||||||
|
('委外坯料发货检', '委外坯料发货检')], string='类型', compute='_compute_quality_check_type', store=True)
|
||||||
|
|
||||||
|
@api.depends('picking_id')
|
||||||
|
def _compute_quality_check_type(self):
|
||||||
|
for check in self:
|
||||||
|
if check.picking_id:
|
||||||
|
picking_type = check.picking_id.picking_type_id.sequence_code
|
||||||
|
type_mapping = {
|
||||||
|
'IN': '采购入库检',
|
||||||
|
'DL': '客供料入库检',
|
||||||
|
'RET': '退货入库检',
|
||||||
|
'SFP': '生产入库检',
|
||||||
|
'OCIN': '外协入库检',
|
||||||
|
'OUT': '成品发货检',
|
||||||
|
'OCOUT': '工序外协发货检',
|
||||||
|
'RES': '委外坯料发货检',
|
||||||
|
}
|
||||||
|
check.quality_check_type = type_mapping.get(picking_type, False)
|
||||||
|
else:
|
||||||
|
check.quality_check_type = False
|
||||||
|
|
||||||
@api.depends('measure_success')
|
@api.depends('measure_success')
|
||||||
def _compute_warning_message(self):
|
def _compute_warning_message(self):
|
||||||
@@ -294,6 +329,19 @@ class QualityAlert(models.Model):
|
|||||||
_inherit = "quality.alert"
|
_inherit = "quality.alert"
|
||||||
|
|
||||||
title = fields.Char('Title')
|
title = fields.Char('Title')
|
||||||
|
part_number = fields.Char(string='零件图号', compute='_compute_part_info', store=True)
|
||||||
|
part_name = fields.Char(string='零件名称', compute='_compute_part_info', store=True)
|
||||||
|
|
||||||
|
@api.depends('product_id', 'picking_id')
|
||||||
|
def _compute_part_info(self):
|
||||||
|
for alert in self:
|
||||||
|
if alert.product_tmpl_id.categ_id.name == '成品':
|
||||||
|
alert.part_number = alert.product_id.part_number
|
||||||
|
alert.part_name = alert.product_id.part_name
|
||||||
|
elif alert.product_id.categ_id.name == '坯料':
|
||||||
|
if alert.picking_id.move_ids_without_package:
|
||||||
|
alert.part_number = alert.picking_id.move_ids_without_package[0].part_number
|
||||||
|
alert.part_name = alert.picking_id.move_ids_without_package[0].part_name
|
||||||
|
|
||||||
def action_see_check(self):
|
def action_see_check(self):
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -90,6 +90,8 @@
|
|||||||
<field name="lot_id" context="{'default_product_id': product_id}"
|
<field name="lot_id" context="{'default_product_id': product_id}"
|
||||||
groups="stock.group_production_lot"/>
|
groups="stock.group_production_lot"/>
|
||||||
<field name="picking_id"/>
|
<field name="picking_id"/>
|
||||||
|
<field name="part_name"/>
|
||||||
|
<field name="part_number"/>
|
||||||
</group>
|
</group>
|
||||||
<group>
|
<group>
|
||||||
<field name="team_id"/>
|
<field name="team_id"/>
|
||||||
@@ -150,6 +152,10 @@
|
|||||||
<field name="date_assign" position="after">
|
<field name="date_assign" position="after">
|
||||||
<field name="company_id" groups="base.main_company"/>
|
<field name="company_id" groups="base.main_company"/>
|
||||||
</field>
|
</field>
|
||||||
|
<field name="product_tmpl_id" position="after">
|
||||||
|
<field name="part_name" optional="show"/>
|
||||||
|
<field name="part_number" optional="show"/>
|
||||||
|
</field>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
@@ -260,6 +266,8 @@
|
|||||||
<field name="company_id" invisible="1"/>
|
<field name="company_id" invisible="1"/>
|
||||||
<field name="product_id" attrs="{'invisible' : [('measure_on', '=', 'operation')]}"/>
|
<field name="product_id" attrs="{'invisible' : [('measure_on', '=', 'operation')]}"/>
|
||||||
<field name="measure_on" attrs="{'readonly': [('point_id', '!=', False)]}"/>
|
<field name="measure_on" attrs="{'readonly': [('point_id', '!=', False)]}"/>
|
||||||
|
<field name="part_name"/>
|
||||||
|
<field name="part_number"/>
|
||||||
<field name="show_lot_text" invisible="1"/>
|
<field name="show_lot_text" invisible="1"/>
|
||||||
<field name="move_line_id" invisible="1"/>
|
<field name="move_line_id" invisible="1"/>
|
||||||
<field name="product_tracking" invisible="1"/>
|
<field name="product_tracking" invisible="1"/>
|
||||||
@@ -389,6 +397,8 @@
|
|||||||
<field name="name" decoration-bf="1"/>
|
<field name="name" decoration-bf="1"/>
|
||||||
<field name="measure_on" optional="show"/>
|
<field name="measure_on" optional="show"/>
|
||||||
<field name='product_id' optional="show"/>
|
<field name='product_id' optional="show"/>
|
||||||
|
<field name="part_name" optional="hide"/>
|
||||||
|
<field name='part_number' optional="show"/>
|
||||||
<field name="lot_id" invisible="context.get('show_lots_text')"/>
|
<field name="lot_id" invisible="context.get('show_lots_text')"/>
|
||||||
<field name="lot_name" invisible="not context.get('show_lots_text')"/>
|
<field name="lot_name" invisible="not context.get('show_lots_text')"/>
|
||||||
<field name="picking_id" optional="hide" string="Transfer"/>
|
<field name="picking_id" optional="hide" string="Transfer"/>
|
||||||
@@ -446,6 +456,10 @@
|
|||||||
<filter string="Control Point" name="groupby_point_id" context="{'group_by': 'point_id'}"/>
|
<filter string="Control Point" name="groupby_point_id" context="{'group_by': 'point_id'}"/>
|
||||||
<filter string="Team" name="groupby_team_id" context="{'group_by': 'team_id'}"/>
|
<filter string="Team" name="groupby_team_id" context="{'group_by': 'team_id'}"/>
|
||||||
</group>
|
</group>
|
||||||
|
<searchpanel>
|
||||||
|
<field name="quality_check_type" icon="fa-filter" enable_counters="1"/>
|
||||||
|
<field name="quality_state" icon="fa-filter" enable_counters="1"/>
|
||||||
|
</searchpanel>
|
||||||
</search>
|
</search>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
@@ -505,6 +519,7 @@
|
|||||||
<field name="name">Quality Checks</field>
|
<field name="name">Quality Checks</field>
|
||||||
<field name="res_model">quality.check</field>
|
<field name="res_model">quality.check</field>
|
||||||
<field name="view_mode">tree,kanban,form,pivot,graph</field>
|
<field name="view_mode">tree,kanban,form,pivot,graph</field>
|
||||||
|
<field name="context">{'is_web_request': True}</field>
|
||||||
<field name="help" type="html">
|
<field name="help" type="html">
|
||||||
<p class="o_view_nocontent_smiling_face">
|
<p class="o_view_nocontent_smiling_face">
|
||||||
No quality check found
|
No quality check found
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
|
import traceback
|
||||||
|
|
||||||
from odoo import http
|
from odoo import http
|
||||||
from odoo.http import request
|
from odoo.http import request
|
||||||
|
|
||||||
@@ -123,7 +125,8 @@ class Sf_Bf_Connect(http.Controller):
|
|||||||
res['factory_order_no'] = order_id.name
|
res['factory_order_no'] = order_id.name
|
||||||
return json.JSONEncoder().encode(res)
|
return json.JSONEncoder().encode(res)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.info('get_bfm_process_order_list error:%s' % e)
|
traceback_error = traceback.format_exc()
|
||||||
|
logging.error('get_bfm_process_order_list error: %s' % traceback_error)
|
||||||
res['status'] = -1
|
res['status'] = -1
|
||||||
res['message'] = '工厂创建销售订单和产品失败,请联系管理员'
|
res['message'] = '工厂创建销售订单和产品失败,请联系管理员'
|
||||||
request.cr.rollback()
|
request.cr.rollback()
|
||||||
|
|||||||
@@ -151,6 +151,12 @@ class JdEclp(models.Model):
|
|||||||
_logger.info('准备调接口1')
|
_logger.info('准备调接口1')
|
||||||
url1 = config['bfm_url_new'] + '/api/create/jd/order'
|
url1 = config['bfm_url_new'] + '/api/create/jd/order'
|
||||||
requests.post(url1, json=json1, data=None)
|
requests.post(url1, json=json1, data=None)
|
||||||
|
# ===============修改销售订单状态为【物流中】===================
|
||||||
|
item = self.env['sale.order'].sudo().search([('name', '=', self.origin)])
|
||||||
|
if not item:
|
||||||
|
raise ValidationError('没有查询到订单号为【%s】的销售订单!' % self.origin)
|
||||||
|
else:
|
||||||
|
item.write({'state': 'physical_distribution'})
|
||||||
_logger.info('调用成功1')
|
_logger.info('调用成功1')
|
||||||
_logger.info('准备调接口2')
|
_logger.info('准备调接口2')
|
||||||
json2 = {
|
json2 = {
|
||||||
|
|||||||
@@ -36,6 +36,8 @@ class StatusChange(models.Model):
|
|||||||
# 使用super()来调用原始方法(在本例中为'sale.order'模型的'action_confirm'方法)
|
# 使用super()来调用原始方法(在本例中为'sale.order'模型的'action_confirm'方法)
|
||||||
try:
|
try:
|
||||||
res = super(StatusChange, self).action_confirm()
|
res = super(StatusChange, self).action_confirm()
|
||||||
|
# 修改销售订单状态为【加工中】
|
||||||
|
self.write({'state': 'processing'})
|
||||||
logging.info('原生方法返回结果:%s' % res)
|
logging.info('原生方法返回结果:%s' % res)
|
||||||
# 原有方法执行后,进行额外的操作(如调用外部API)
|
# 原有方法执行后,进行额外的操作(如调用外部API)
|
||||||
process_start_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
process_start_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import logging
|
import logging
|
||||||
|
from itertools import groupby
|
||||||
from odoo import models, fields, api, _
|
from odoo import models, fields, api, _
|
||||||
|
|
||||||
|
|
||||||
@@ -7,11 +8,10 @@ class StockRuleInherit(models.Model):
|
|||||||
|
|
||||||
@api.model
|
@api.model
|
||||||
def _run_buy(self, procurements):
|
def _run_buy(self, procurements):
|
||||||
# 首先调用父类的 _run_buy 方法,以保留原有逻辑
|
# 判断补货组的采购类型
|
||||||
super(StockRuleInherit, self)._run_buy(procurements)
|
procurements_group = {'standard': [], 'outsourcing': []}
|
||||||
|
|
||||||
# 然后在这里添加自定义的逻辑
|
|
||||||
for procurement, rule in procurements:
|
for procurement, rule in procurements:
|
||||||
|
is_outsourcing = False
|
||||||
product = procurement.product_id
|
product = procurement.product_id
|
||||||
# 获取主 BOM
|
# 获取主 BOM
|
||||||
bom = self.env['mrp.bom'].search([('product_tmpl_id', '=', product.product_tmpl_id.id)], limit=1)
|
bom = self.env['mrp.bom'].search([('product_tmpl_id', '=', product.product_tmpl_id.id)], limit=1)
|
||||||
@@ -23,21 +23,65 @@ class StockRuleInherit(models.Model):
|
|||||||
# 检查路线
|
# 检查路线
|
||||||
for route in raw_material.route_ids:
|
for route in raw_material.route_ids:
|
||||||
# print('route.name:', route.name)
|
# print('route.name:', route.name)
|
||||||
if route.name == '按订单补给外包商': # 或者用 route.id 检查精确的路线
|
if route.name == '按订单补给外包商':
|
||||||
print("按订单补给外包商============是")
|
is_outsourcing = True
|
||||||
# 使用 procurement.values['supplier'] 获取供应商
|
|
||||||
supplier = procurement.values.get('supplier')
|
if is_outsourcing:
|
||||||
if supplier:
|
procurements_group['outsourcing'].append((procurement, rule))
|
||||||
domain = rule._make_po_get_domain(procurement.company_id, procurement.values,
|
else:
|
||||||
supplier.partner_id)
|
procurements_group['standard'].append((procurement, rule))
|
||||||
logging.info("domain=============: %s", domain)
|
|
||||||
po = self.env['purchase.order'].sudo().search([
|
for key, value in procurements_group.items():
|
||||||
('partner_id', '=', supplier.partner_id.id),
|
super(StockRuleInherit, self)._run_buy(value)
|
||||||
('company_id', '=', procurement.company_id.id), # 保证公司一致
|
|
||||||
('origin', '=', procurement.origin), # 根据来源匹配
|
if key == 'outsourcing':
|
||||||
('state', '=', 'draft') # 状态为草稿
|
for procurement, rule in value:
|
||||||
], limit=1)
|
supplier = procurement.values.get('supplier')
|
||||||
logging.info("po=: %s", po)
|
if supplier:
|
||||||
if po:
|
domain = rule._make_po_get_domain(procurement.company_id, procurement.values,
|
||||||
po.write({'purchase_type': 'consignment'})
|
supplier.partner_id)
|
||||||
break
|
logging.info("domain=============: %s", domain)
|
||||||
|
po = self.env['purchase.order'].sudo().search([
|
||||||
|
('partner_id', '=', supplier.partner_id.id),
|
||||||
|
('company_id', '=', procurement.company_id.id), # 保证公司一致
|
||||||
|
('origin', 'like', procurement.origin), # 根据来源匹配
|
||||||
|
('state', '=', 'draft') # 状态为草稿
|
||||||
|
], limit=1)
|
||||||
|
logging.info("po=: %s", po)
|
||||||
|
if po:
|
||||||
|
po.write({'purchase_type': 'outsourcing'})
|
||||||
|
|
||||||
|
# # 首先调用父类的 _run_buy 方法,以保留原有逻辑
|
||||||
|
# super(StockRuleInherit, self)._run_buy(procurements)
|
||||||
|
|
||||||
|
# 然后在这里添加自定义的逻辑
|
||||||
|
# for procurement, rule in procurements:
|
||||||
|
# product = procurement.product_id
|
||||||
|
# # 获取主 BOM
|
||||||
|
# bom = self.env['mrp.bom'].search([('product_tmpl_id', '=', product.product_tmpl_id.id)], limit=1)
|
||||||
|
|
||||||
|
# if bom:
|
||||||
|
# # 遍历 BOM 中的组件(即坯料等)
|
||||||
|
# for line in bom.bom_line_ids:
|
||||||
|
# raw_material = line.product_id
|
||||||
|
# # 检查路线
|
||||||
|
# for route in raw_material.route_ids:
|
||||||
|
# # print('route.name:', route.name)
|
||||||
|
# if route.name == '按订单补给外包商': # 或者用 route.id 检查精确的路线
|
||||||
|
# print("按订单补给外包商============是")
|
||||||
|
# # 使用 procurement.values['supplier'] 获取供应商
|
||||||
|
# supplier = procurement.values.get('supplier')
|
||||||
|
# if supplier:
|
||||||
|
# domain = rule._make_po_get_domain(procurement.company_id, procurement.values,
|
||||||
|
# supplier.partner_id)
|
||||||
|
# logging.info("domain=============: %s", domain)
|
||||||
|
# po = self.env['purchase.order'].sudo().search([
|
||||||
|
# ('partner_id', '=', supplier.partner_id.id),
|
||||||
|
# ('company_id', '=', procurement.company_id.id), # 保证公司一致
|
||||||
|
# ('origin', '=', procurement.origin), # 根据来源匹配
|
||||||
|
# ('state', '=', 'draft') # 状态为草稿
|
||||||
|
# ], limit=1)
|
||||||
|
# logging.info("po=: %s", po)
|
||||||
|
# if po:
|
||||||
|
# po.write({'purchase_type': 'outsourcing'})
|
||||||
|
# break
|
||||||
|
|||||||
@@ -23,6 +23,16 @@ class JkmPracticeEmployee(models.Model):
|
|||||||
vals["we_id"] = self._get_we_id(vals.get('work_email'))
|
vals["we_id"] = self._get_we_id(vals.get('work_email'))
|
||||||
return super(JkmPracticeEmployee, self).write(vals)
|
return super(JkmPracticeEmployee, self).write(vals)
|
||||||
|
|
||||||
|
def unlink(self):
|
||||||
|
for record in self:
|
||||||
|
res_partner_obj = record.env['res.partner'].sudo().search([('email', '=', record.work_email)])
|
||||||
|
if res_partner_obj:
|
||||||
|
res_partner_obj.unlink()
|
||||||
|
res = super(JkmPracticeEmployee, self).unlink()
|
||||||
|
|
||||||
|
return res
|
||||||
|
|
||||||
|
|
||||||
@api.depends('work_contact_id', 'work_contact_id.mobile', 'work_contact_id.email')
|
@api.depends('work_contact_id', 'work_contact_id.mobile', 'work_contact_id.email')
|
||||||
def _compute_work_contact_details(self):
|
def _compute_work_contact_details(self):
|
||||||
for employee in self:
|
for employee in self:
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
# 只有它被屏蔽了
|
# 只有它被屏蔽了
|
||||||
# 'views/SfWorkOrderBarcodes.xml',
|
# 'views/SfWorkOrderBarcodes.xml',
|
||||||
'views/WorkCenterBarcodes.xml',
|
'views/WorkCenterBarcodes.xml',
|
||||||
'views/Stock_picking_Barcodes.xml',
|
# 'views/Stock_picking_Barcodes.xml',
|
||||||
'views/machine_monitor.xml',
|
'views/machine_monitor.xml',
|
||||||
'views/machine_info_present.xml',
|
'views/machine_info_present.xml',
|
||||||
'views/delivery_record.xml',
|
'views/delivery_record.xml',
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
<field name="state" select="multi" string="状态" icon="fa-building" enable_counters="1"/>
|
<field name="state" select="multi" string="状态" icon="fa-building" enable_counters="1"/>
|
||||||
|
|
||||||
<field name="construction_period_status" select="multi" icon="fa-building" enable_counters="1"/>
|
<field name="construction_period_status" select="multi" icon="fa-building" enable_counters="1"/>
|
||||||
|
<field name="tag_type" select="multi" icon="fa-building" enable_counters="1"/>
|
||||||
<!-- <field name="manual_quotation" select="multi" string="" icon="fa-building" enable_counters="1"/>-->
|
<!-- <field name="manual_quotation" select="multi" string="" icon="fa-building" enable_counters="1"/>-->
|
||||||
</searchpanel>
|
</searchpanel>
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||||
{
|
{
|
||||||
'name': '机企猫智能工厂 制造管理',
|
'name': '机企猫智能工厂 制造管理',
|
||||||
'version': '1.0',
|
'version': '1.1',
|
||||||
'summary': '智能工厂制造模块',
|
'summary': '智能工厂制造模块',
|
||||||
'sequence': 1,
|
'sequence': 1,
|
||||||
'description': """
|
'description': """
|
||||||
@@ -24,6 +24,7 @@
|
|||||||
'wizard/production_wizard_views.xml',
|
'wizard/production_wizard_views.xml',
|
||||||
'wizard/production_technology_wizard_views.xml',
|
'wizard/production_technology_wizard_views.xml',
|
||||||
'wizard/production_technology_re_adjust_wizard_views.xml',
|
'wizard/production_technology_re_adjust_wizard_views.xml',
|
||||||
|
'wizard/mrp_workorder_batch_replan_wizard_views.xml',
|
||||||
'views/mrp_views_menus.xml',
|
'views/mrp_views_menus.xml',
|
||||||
'views/agv_scheduling_views.xml',
|
'views/agv_scheduling_views.xml',
|
||||||
'views/stock_lot_views.xml',
|
'views/stock_lot_views.xml',
|
||||||
@@ -37,6 +38,9 @@
|
|||||||
'views/agv_setting_views.xml',
|
'views/agv_setting_views.xml',
|
||||||
'views/sf_maintenance_equipment.xml',
|
'views/sf_maintenance_equipment.xml',
|
||||||
'views/res_config_settings_views.xml',
|
'views/res_config_settings_views.xml',
|
||||||
|
'views/sale_order_views.xml',
|
||||||
|
'views/mrp_workorder_batch_replan.xml',
|
||||||
|
'views/purchase_order_view.xml',
|
||||||
],
|
],
|
||||||
'assets': {
|
'assets': {
|
||||||
|
|
||||||
@@ -49,6 +53,8 @@
|
|||||||
'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/xml/button_show_on_tree.xml',
|
||||||
'sf_manufacturing/static/src/js/workpiece_delivery_wizard_confirm.js',
|
'sf_manufacturing/static/src/js/workpiece_delivery_wizard_confirm.js',
|
||||||
|
'sf_manufacturing/static/src/js/agv_scheduling_resend_confirm.js',
|
||||||
|
'sf_manufacturing/static/src/js/agv_scheduling_cancel_confirm.js',
|
||||||
'sf_manufacturing/static/src/js/qr.js',
|
'sf_manufacturing/static/src/js/qr.js',
|
||||||
'sf_manufacturing/static/src/xml/qr.xml',
|
'sf_manufacturing/static/src/xml/qr.xml',
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,2 +1,3 @@
|
|||||||
from . import controllers
|
from . import controllers
|
||||||
from . import workpiece
|
from . import workpiece
|
||||||
|
from . import main
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import logging
|
import logging
|
||||||
import json
|
import json
|
||||||
|
import traceback
|
||||||
|
|
||||||
from odoo import http
|
from odoo import http
|
||||||
from odoo.http import request
|
from odoo.http import request
|
||||||
from odoo.addons.sf_bf_connect.controllers.controllers import Sf_Bf_Connect
|
from odoo.addons.sf_bf_connect.controllers.controllers import Sf_Bf_Connect
|
||||||
@@ -25,7 +27,7 @@ class JikimoSaleRoutePicking(Sf_Bf_Connect):
|
|||||||
bfm_process_order_list = json.loads(kw['bfm_process_order_list'])
|
bfm_process_order_list = json.loads(kw['bfm_process_order_list'])
|
||||||
order_id = request.env['sale.order'].with_user(request.env.ref("base.user_admin")).sale_order_create(
|
order_id = request.env['sale.order'].with_user(request.env.ref("base.user_admin")).sale_order_create(
|
||||||
company_id, kw['delivery_name'], kw['delivery_telephone'], kw['delivery_address'],
|
company_id, kw['delivery_name'], kw['delivery_telephone'], kw['delivery_address'],
|
||||||
kw['delivery_end_date'], kw['payments_way'], kw['pay_way'], state='draft')
|
kw['delivery_end_date'], kw['payments_way'], kw['pay_way'], kw['order_number'], state='draft')
|
||||||
i = 1
|
i = 1
|
||||||
# 给sale_order的default_code字段赋值
|
# 给sale_order的default_code字段赋值
|
||||||
# aa = request.env['sale.order'].sudo().search([('name', '=', order_id.name)])
|
# aa = request.env['sale.order'].sudo().search([('name', '=', order_id.name)])
|
||||||
@@ -44,7 +46,8 @@ class JikimoSaleRoutePicking(Sf_Bf_Connect):
|
|||||||
i += 1
|
i += 1
|
||||||
res['factory_order_no'] = order_id.name
|
res['factory_order_no'] = order_id.name
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
_logger.info('get_bfm_process_order_list error:%s' % e)
|
traceback_error = traceback.format_exc()
|
||||||
|
logging.error('get_bfm_process_order_list error: %s' % traceback_error)
|
||||||
res['status'] = -1
|
res['status'] = -1
|
||||||
res['message'] = '工厂创建销售订单和产品失败,请联系管理员'
|
res['message'] = '工厂创建销售订单和产品失败,请联系管理员'
|
||||||
request.cr.rollback()
|
request.cr.rollback()
|
||||||
21
sf_manufacturing/i18n/zh_CN.po
Normal file
21
sf_manufacturing/i18n/zh_CN.po
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
#. module: sf_manufacturing
|
||||||
|
#. odoo-python
|
||||||
|
#: code:addons/sf_manufacturing/models/mrp_production.py:0
|
||||||
|
#, python-format
|
||||||
|
msgid "You must enter a serial number for %s"
|
||||||
|
msgstr "您必须为%s输入一个序列号。"
|
||||||
|
|
||||||
|
|
||||||
|
#. module: sf_manufacturing
|
||||||
|
#. odoo-python
|
||||||
|
#: code:addons/sf_manufacturing/models/mrp_production.py:0
|
||||||
|
#, python-format
|
||||||
|
msgid "You must enter a serial number for each line of %s"
|
||||||
|
msgstr "您必须为以下各%s行输入序列号"
|
||||||
|
|
||||||
|
#. module: sf_manufacturing
|
||||||
|
#. odoo-python
|
||||||
|
#: code:addons/sf_manufacturing/models/mrp_production.py:0
|
||||||
|
#, python-format
|
||||||
|
msgid "You have %s incomplete supplies: %s"
|
||||||
|
msgstr "您有%s补给未完成: %s"
|
||||||
12
sf_manufacturing/migrations/1.1/post-migrate.py
Normal file
12
sf_manufacturing/migrations/1.1/post-migrate.py
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
# migrations/1.1.0/post-migrate.py
|
||||||
|
from odoo import api, SUPERUSER_ID
|
||||||
|
|
||||||
|
def migrate(cr, version):
|
||||||
|
# 获取环境
|
||||||
|
env = api.Environment(cr, SUPERUSER_ID, {})
|
||||||
|
|
||||||
|
# 示例:添加新字段
|
||||||
|
env.ref('sf_dlm.product_embryo_sf_self_machining').product_tmpl_id.write({'categ_type': '坯料'})
|
||||||
|
env.ref('sf_dlm.product_template_sf').product_tmpl_id.write({'categ_type': '成品'})
|
||||||
|
env.ref('sf_dlm.product_embryo_sf_outsource').product_tmpl_id.write({'categ_type': '坯料'})
|
||||||
|
env.ref('sf_dlm.product_embryo_sf_purchase').product_tmpl_id.write({'categ_type': '坯料'})
|
||||||
@@ -13,3 +13,6 @@ from . import agv_scheduling
|
|||||||
from . import res_config_setting
|
from . import res_config_setting
|
||||||
from . import sf_technology_design
|
from . import sf_technology_design
|
||||||
from . import sf_production_common
|
from . import sf_production_common
|
||||||
|
from . import sale_order
|
||||||
|
from . import quick_easy_order
|
||||||
|
from . import purchase_order
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
import requests
|
import requests
|
||||||
|
from datetime import timedelta
|
||||||
|
|
||||||
from odoo import models, fields, api, _
|
from odoo import models, fields, api, _
|
||||||
from odoo.exceptions import UserError
|
from odoo.exceptions import UserError
|
||||||
@@ -210,9 +211,18 @@ class AgvScheduling(models.Model):
|
|||||||
def button_cancel(self):
|
def button_cancel(self):
|
||||||
# 弹出二次确认窗口后执行
|
# 弹出二次确认窗口后执行
|
||||||
for rec in self:
|
for rec in self:
|
||||||
if rec.state != '待下发':
|
|
||||||
raise UserError('只有待下发状态的AGV调度任务才能取消!')
|
|
||||||
rec.state = '已取消'
|
rec.state = '已取消'
|
||||||
|
return {
|
||||||
|
'type': 'ir.actions.client',
|
||||||
|
'tag': 'display_notification',
|
||||||
|
'target': 'new',
|
||||||
|
'params': {
|
||||||
|
'message': '任务取消成功!',
|
||||||
|
'type': 'success',
|
||||||
|
'sticky': False,
|
||||||
|
'next': {'type': 'ir.actions.act_window_close'},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
def finish_scheduling(self):
|
def finish_scheduling(self):
|
||||||
"""
|
"""
|
||||||
@@ -232,7 +242,7 @@ class AgvScheduling(models.Model):
|
|||||||
agv_route sf.agv.task.route对象
|
agv_route sf.agv.task.route对象
|
||||||
"""
|
"""
|
||||||
for rec in self:
|
for rec in self:
|
||||||
if rec.state != '待下发':
|
if rec.state not in ['待下发', '配送中']:
|
||||||
return False
|
return False
|
||||||
_logger.info('AGV任务调度:下发调度任务,路线为%s' % agv_task_route)
|
_logger.info('AGV任务调度:下发调度任务,路线为%s' % agv_task_route)
|
||||||
rec.state = '配送中'
|
rec.state = '配送中'
|
||||||
@@ -264,7 +274,45 @@ class AgvScheduling(models.Model):
|
|||||||
'task_delivery_time': fields.Datetime.now()
|
'task_delivery_time': fields.Datetime.now()
|
||||||
})
|
})
|
||||||
return super().write(vals)
|
return super().write(vals)
|
||||||
|
|
||||||
|
def button_cancel_confirm(self):
|
||||||
|
if self.task_delivery_time > fields.Datetime.now() - timedelta(minutes=10):
|
||||||
|
return {
|
||||||
|
'type': 'ir.actions.client',
|
||||||
|
'tag': 'agv_scheduling_cancel_confirm',
|
||||||
|
'params': {
|
||||||
|
'agv_scheduling_id': self.id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
return self.button_cancel()
|
||||||
|
|
||||||
|
def button_resend_confirm(self):
|
||||||
|
if self.task_delivery_time > fields.Datetime.now() - timedelta(minutes=10):
|
||||||
|
return {
|
||||||
|
'type': 'ir.actions.client',
|
||||||
|
'tag': 'agv_scheduling_resend_confirm',
|
||||||
|
'params': {
|
||||||
|
'agv_scheduling_id': self.id,
|
||||||
|
'context': self.env.context,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
return self.button_resend()
|
||||||
|
|
||||||
|
def button_resend(self):
|
||||||
|
self.dispatch_scheduling(self.agv_route_id)
|
||||||
|
return {
|
||||||
|
'type': 'ir.actions.client',
|
||||||
|
'tag': 'display_notification',
|
||||||
|
'target': 'new',
|
||||||
|
'params': {
|
||||||
|
'message': '任务重新下发成功!',
|
||||||
|
'type': 'success',
|
||||||
|
'sticky': False,
|
||||||
|
'next': {'type': 'ir.actions.act_window_close'},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class ResMrpWorkOrder(models.Model):
|
class ResMrpWorkOrder(models.Model):
|
||||||
_inherit = 'mrp.workorder'
|
_inherit = 'mrp.workorder'
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ class ModelType(models.Model):
|
|||||||
|
|
||||||
name = fields.Char('名称')
|
name = fields.Char('名称')
|
||||||
# embryo_tolerance = fields.Char('坯料容余')
|
# embryo_tolerance = fields.Char('坯料容余')
|
||||||
embryo_tolerance_id = fields.Many2one('sf.embryo.redundancy', string='坯料容余')
|
embryo_tolerance_id = fields.Many2one('sf.embryo.redundancy', string='坯料冗余')
|
||||||
product_routing_tmpl_ids = fields.One2many('sf.product.model.type.routing.sort', 'product_model_type_id',
|
product_routing_tmpl_ids = fields.One2many('sf.product.model.type.routing.sort', 'product_model_type_id',
|
||||||
'成品工序模板(自动化产线加工')
|
'成品工序模板(自动化产线加工')
|
||||||
embryo_routing_tmpl_ids = fields.One2many('sf.embryo.model.type.routing.sort', 'embryo_model_type_id',
|
embryo_routing_tmpl_ids = fields.One2many('sf.embryo.model.type.routing.sort', 'embryo_model_type_id',
|
||||||
@@ -75,7 +75,7 @@ class ManualProductModelTypeRoutingSort(models.Model):
|
|||||||
_description = '成品工序排序(人工线下加工)'
|
_description = '成品工序排序(人工线下加工)'
|
||||||
|
|
||||||
sequence = fields.Integer('Sequence')
|
sequence = fields.Integer('Sequence')
|
||||||
route_workcenter_id = fields.Many2one('mrp.routing.workcenter')
|
route_workcenter_id = fields.Many2one('mrp.routing.workcenter', domain=[('routing_type', 'in', ['人工线下加工'])])
|
||||||
is_repeat = fields.Boolean('重复', related='route_workcenter_id.is_repeat')
|
is_repeat = fields.Boolean('重复', related='route_workcenter_id.is_repeat')
|
||||||
routing_type = fields.Selection(string="工序类型", related='route_workcenter_id.routing_type')
|
routing_type = fields.Selection(string="工序类型", related='route_workcenter_id.routing_type')
|
||||||
workcenter_ids = fields.Many2many('mrp.workcenter', required=False, related='route_workcenter_id.workcenter_ids')
|
workcenter_ids = fields.Many2many('mrp.workcenter', required=False, related='route_workcenter_id.workcenter_ids')
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import re
|
|||||||
import requests
|
import requests
|
||||||
from itertools import groupby
|
from itertools import groupby
|
||||||
from collections import defaultdict, namedtuple
|
from collections import defaultdict, namedtuple
|
||||||
|
|
||||||
from odoo import api, fields, models, SUPERUSER_ID, _
|
from odoo import api, fields, models, SUPERUSER_ID, _
|
||||||
from odoo.exceptions import UserError, ValidationError
|
from odoo.exceptions import UserError, ValidationError
|
||||||
from odoo.addons.sf_base.commons.common import Common
|
from odoo.addons.sf_base.commons.common import Common
|
||||||
@@ -18,6 +19,7 @@ class MrpProduction(models.Model):
|
|||||||
_inherit = 'mrp.production'
|
_inherit = 'mrp.production'
|
||||||
_description = "制造订单"
|
_description = "制造订单"
|
||||||
_order = 'create_date desc'
|
_order = 'create_date desc'
|
||||||
|
sale_order_id = fields.Many2one('sale.order', string='销售订单', compute='_compute_sale_order_id', store=True)
|
||||||
deadline_of_delivery = fields.Date('订单交期', tracking=True, compute='_compute_deadline_of_delivery')
|
deadline_of_delivery = fields.Date('订单交期', tracking=True, compute='_compute_deadline_of_delivery')
|
||||||
# tray_ids = fields.One2many('sf.tray', 'production_id', string="托盘")
|
# tray_ids = fields.One2many('sf.tray', 'production_id', string="托盘")
|
||||||
maintenance_count = fields.Integer(compute='_compute_maintenance_count', string="Number of maintenance requests")
|
maintenance_count = fields.Integer(compute='_compute_maintenance_count', string="Number of maintenance requests")
|
||||||
@@ -34,6 +36,29 @@ class MrpProduction(models.Model):
|
|||||||
tool_state_remark = fields.Text(string='功能刀具状态备注(缺刀)', compute='_compute_tool_state_remark', store=True)
|
tool_state_remark = fields.Text(string='功能刀具状态备注(缺刀)', compute='_compute_tool_state_remark', store=True)
|
||||||
tool_state_remark2 = fields.Text(string='功能刀具状态备注(无效刀)', readonly=True)
|
tool_state_remark2 = fields.Text(string='功能刀具状态备注(无效刀)', readonly=True)
|
||||||
|
|
||||||
|
@api.depends('procurement_group_id.mrp_production_ids.move_dest_ids.group_id.sale_id')
|
||||||
|
def _compute_sale_order_id(self):
|
||||||
|
for production in self:
|
||||||
|
# 初始化 sale_order_id 为 False
|
||||||
|
sale_order_id = False
|
||||||
|
# 使用正则表达式查找产品名称中的 'S' 开头的字母数字字符串
|
||||||
|
match = re.search(r'S\d+', production.product_id.with_context(lang='zh_CN').name) # 从字符串开始匹配
|
||||||
|
|
||||||
|
if match:
|
||||||
|
result = match.group(0)
|
||||||
|
try:
|
||||||
|
# 查找与匹配的字符串相符的销售订单
|
||||||
|
sale_order = self.env['sale.order'].search(
|
||||||
|
[('name', '=', result)], limit=1, order='id asc'
|
||||||
|
)
|
||||||
|
if sale_order:
|
||||||
|
production.sale_order_id = sale_order.id
|
||||||
|
else:
|
||||||
|
logging.warning("No sale order found for production {} with product {} (name match: {})".format(
|
||||||
|
production.id, production.product_id.name, result))
|
||||||
|
except Exception as e:
|
||||||
|
logging.error("Error while fetching sale order for production {}: {}".format(production.id, str(e)))
|
||||||
|
|
||||||
@api.depends('procurement_group_id.mrp_production_ids.move_dest_ids.group_id.sale_id')
|
@api.depends('procurement_group_id.mrp_production_ids.move_dest_ids.group_id.sale_id')
|
||||||
def _compute_deadline_of_delivery(self):
|
def _compute_deadline_of_delivery(self):
|
||||||
for production in self:
|
for production in self:
|
||||||
@@ -278,7 +303,7 @@ class MrpProduction(models.Model):
|
|||||||
|
|
||||||
@api.depends(
|
@api.depends(
|
||||||
'move_raw_ids.state', 'move_raw_ids.quantity_done', 'move_finished_ids.state', 'tool_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', 'programming_state')
|
'workorder_ids.state', 'product_qty', 'qty_producing', 'schedule_state', 'programming_state', 'is_adjust')
|
||||||
def _compute_state(self):
|
def _compute_state(self):
|
||||||
for production in self:
|
for production in self:
|
||||||
if not production.state or not production.product_uom_id:
|
if not production.state or not production.product_uom_id:
|
||||||
@@ -309,43 +334,47 @@ class MrpProduction(models.Model):
|
|||||||
for move in production.move_raw_ids if move.product_id):
|
for move in production.move_raw_ids if move.product_id):
|
||||||
production.state = 'progress'
|
production.state = 'progress'
|
||||||
# 新添加的状态逻辑
|
# 新添加的状态逻辑
|
||||||
if (
|
if production.state in ['to_close', 'progress',
|
||||||
production.state == 'to_close' or production.state == 'progress') and production.schedule_state == '未排':
|
'technology_to_confirmed'] and production.schedule_state == '未排':
|
||||||
if not production.workorder_ids or production.is_adjust is True:
|
if not production.workorder_ids or production.is_adjust is True:
|
||||||
production.state = 'technology_to_confirmed'
|
production.state = 'technology_to_confirmed'
|
||||||
else:
|
else:
|
||||||
production.state = 'confirmed'
|
if production.is_adjust is True:
|
||||||
|
production.state = 'technology_to_confirmed'
|
||||||
|
else:
|
||||||
|
production.state = 'confirmed'
|
||||||
elif production.state == 'pending_cam' and production.schedule_state == '未排':
|
elif production.state == 'pending_cam' and production.schedule_state == '未排':
|
||||||
production.state = 'confirmed'
|
production.state = 'confirmed'
|
||||||
elif production.state == 'to_close' and production.schedule_state == '已排':
|
elif production.state == 'to_close' and production.schedule_state == '已排':
|
||||||
production.state = 'pending_cam'
|
production.state = 'pending_cam'
|
||||||
|
elif production.state == 'confirmed' and production.is_adjust is True:
|
||||||
|
production.state = 'technology_to_confirmed'
|
||||||
|
if production.state == 'confirmed' and production.schedule_state == '已排':
|
||||||
|
production.state = 'pending_cam'
|
||||||
if production.state == 'progress':
|
if production.state == 'progress':
|
||||||
if all(wo_state not in ('progress', 'done', 'rework', 'scrap') for wo_state in
|
if all(wo_state not in ('progress', 'done', 'rework', 'scrap') for wo_state in
|
||||||
production.workorder_ids.mapped('state')):
|
production.workorder_ids.mapped('state')):
|
||||||
production.state = 'pending_cam'
|
production.state = 'pending_cam'
|
||||||
if production.is_rework is True:
|
if production.is_rework is True:
|
||||||
production.state = 'rework'
|
production.state = 'rework'
|
||||||
|
if (production.state == 'rework' and production.tool_state == '0'
|
||||||
|
and production.schedule_state == '已排' and production.is_rework is False):
|
||||||
|
production.state = 'pending_cam'
|
||||||
# if production.state == 'pending_cam':
|
# if production.state == 'pending_cam':
|
||||||
# if all(wo_state in 'done' for wo_state in production.workorder_ids.mapped('state')):
|
# if all(wo_state in 'done' for wo_state in production.workorder_ids.mapped('state')):
|
||||||
# production.state = 'done'
|
# production.state = 'done'
|
||||||
if any(
|
if any((wo.test_results == '返工' and wo.state == 'done' and production.programming_state in ['已编程'])
|
||||||
(
|
or (wo.is_rework is True and wo.state == 'done' and production.programming_state in ['编程中', '已编程'])
|
||||||
wo.test_results == '返工' and wo.state == 'done' and production.programming_state in [
|
for wo in production.workorder_ids):
|
||||||
'已编程']) 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'
|
production.state = 'rework'
|
||||||
if any(wo.test_results == '报废' and wo.state == 'done' for wo in production.workorder_ids):
|
if any(wo.test_results == '报废' and wo.state == 'done' for wo in production.workorder_ids):
|
||||||
production.state = 'scrap'
|
production.state = 'scrap'
|
||||||
if any(dr.test_results == '报废' and dr.handle_result == '已处理' for dr in
|
if any(dr.test_results == '报废' and dr.handle_result == '已处理' for dr in
|
||||||
production.detection_result_ids):
|
production.detection_result_ids):
|
||||||
production.state = 'cancel'
|
production.state = 'cancel'
|
||||||
# 如果制造订单的功能刀具为【无效刀】则制造订单状态改为返工
|
if production.workorder_ids and all(wo_state in ('done', 'rework', 'cancel') for wo_state in production.workorder_ids.mapped('state')):
|
||||||
if production.tool_state == '2':
|
if production.state not in ['scrap', 'rework', 'cancel']:
|
||||||
production.state = 'rework'
|
production.state = 'done'
|
||||||
|
|
||||||
# 退回调整
|
# 退回调整
|
||||||
def technology_back_adjust(self):
|
def technology_back_adjust(self):
|
||||||
@@ -368,37 +397,71 @@ class MrpProduction(models.Model):
|
|||||||
if process_parameters:
|
if process_parameters:
|
||||||
raise UserError(_("【工艺设计】-【参数】为%s的在【产品】中不存在,请先创建", ", ".join(process_parameters)))
|
raise UserError(_("【工艺设计】-【参数】为%s的在【产品】中不存在,请先创建", ", ".join(process_parameters)))
|
||||||
if production_confirmed:
|
if production_confirmed:
|
||||||
return {
|
production_count = self.env['mrp.production'].search_count([
|
||||||
'name': _('退回调整'),
|
('origin', '=', self.origin),
|
||||||
'type': 'ir.actions.act_window',
|
('product_id', '=', self.product_id.id),
|
||||||
'view_mode': 'form',
|
('state', '=', 'confirmed')
|
||||||
'res_model': 'sf.production.technology.re_adjust.wizard',
|
])
|
||||||
'target': 'new',
|
if production_count > 1:
|
||||||
'context': {
|
return {
|
||||||
'default_production_id': self.id,
|
'name': _('退回调整'),
|
||||||
'default_origin': self.origin,
|
'type': 'ir.actions.act_window',
|
||||||
}}
|
'views': [(self.env.ref(
|
||||||
|
'sf_manufacturing.sf_production_technology_re_adjust_wizard_form_view').id,
|
||||||
|
'form')],
|
||||||
|
'res_model': 'sf.production.technology.re_adjust.wizard',
|
||||||
|
'target': 'new',
|
||||||
|
'context': {
|
||||||
|
'default_production_id': self.id,
|
||||||
|
'default_origin': self.origin,
|
||||||
|
}}
|
||||||
|
else:
|
||||||
|
return {
|
||||||
|
'name': _('退回调整'),
|
||||||
|
'type': 'ir.actions.act_window',
|
||||||
|
'views': [(self.env.ref(
|
||||||
|
'sf_manufacturing.sf_production_technology_re_adjust_wizard_confirm_form_view').id,
|
||||||
|
'form')],
|
||||||
|
'res_model': 'sf.production.technology.re_adjust.wizard',
|
||||||
|
'target': 'new',
|
||||||
|
'context': {
|
||||||
|
'default_production_id': self.id,
|
||||||
|
'default_origin': self.origin,
|
||||||
|
}}
|
||||||
|
|
||||||
# 工艺确认
|
# 工艺确认
|
||||||
def technology_confirm(self):
|
def technology_confirm(self):
|
||||||
process_parameters = []
|
process_parameters = []
|
||||||
account_moves = []
|
account_moves = []
|
||||||
|
parameters_not = []
|
||||||
|
# 获取原有的工单对应的工序
|
||||||
|
origin_designs = self.workorder_ids.technology_design_id
|
||||||
|
# 获取已删除的工序
|
||||||
|
deleted_designs = origin_designs - self.technology_design_ids
|
||||||
|
if deleted_designs:
|
||||||
|
for deleted_design in deleted_designs:
|
||||||
|
workorder = self.env['mrp.workorder'].search([('technology_design_id', '=', deleted_design.id)])
|
||||||
|
purchase = workorder._get_surface_technics_purchase_ids()
|
||||||
|
account = self.env['account.move'].search([('id', 'in', purchase.invoice_ids.ids)])
|
||||||
|
if account.state not in ['cancel', False]:
|
||||||
|
if purchase.name not in account_moves:
|
||||||
|
account_moves.append(purchase.name)
|
||||||
special_design = self.technology_design_ids.filtered(
|
special_design = self.technology_design_ids.filtered(
|
||||||
lambda a: a.routing_tag == 'special' and a.is_auto is False)
|
lambda a: a.routing_tag == 'special' and a.is_auto is False)
|
||||||
for special in special_design:
|
for special in special_design:
|
||||||
|
if special.route_id.routing_type == '表面工艺' and not special.process_parameters_id:
|
||||||
|
parameters_not.append(special.route_id.name)
|
||||||
if special.process_parameters_id:
|
if special.process_parameters_id:
|
||||||
product_production_process = self.env['product.template'].search(
|
product_production_process = self.env['product.template'].search(
|
||||||
[('server_product_process_parameters_id', '=', special.process_parameters_id.id)])
|
[('server_product_process_parameters_id', '=', special.process_parameters_id.id)])
|
||||||
if not product_production_process:
|
if not product_production_process:
|
||||||
if special.process_parameters_id not in process_parameters:
|
if special.process_parameters_id not in process_parameters:
|
||||||
process_parameters.append(special.process_parameters_id.display_name)
|
process_parameters.append(special.process_parameters_id.display_name)
|
||||||
purchase = self.env['purchase.order'].search([('origin', '=', special.production_id.name)])
|
|
||||||
account = self.env['account.move'].search([('id', 'in', purchase.invoice_ids)])
|
|
||||||
if account.state not in ['cancel', False]:
|
|
||||||
if purchase.name not in account_moves:
|
|
||||||
account_moves.append(purchase.name)
|
|
||||||
if account_moves:
|
if account_moves:
|
||||||
raise UserError(_("请联系工厂生产经理对采购订单为%s生成的账单进行取消", ", ".join(account_moves)))
|
raise UserError(_("请联系工厂生产经理对会计凭证为%s生成的账单进行取消", ", ".join([move.name for move in account_moves])))
|
||||||
|
if parameters_not:
|
||||||
|
raise UserError(_("【工艺设计】-【工序】为%s未选择参数,请选择", ", ".join(parameters_not)))
|
||||||
if process_parameters:
|
if process_parameters:
|
||||||
raise UserError(_("【工艺设计】-【参数】为%s的在【产品】中不存在,请先创建", ", ".join(process_parameters)))
|
raise UserError(_("【工艺设计】-【参数】为%s的在【产品】中不存在,请先创建", ", ".join(process_parameters)))
|
||||||
# 判断同一个加工面的标准工序的顺序是否依次排序
|
# 判断同一个加工面的标准工序的顺序是否依次排序
|
||||||
@@ -426,16 +489,37 @@ class MrpProduction(models.Model):
|
|||||||
error_panel.append(design.panel)
|
error_panel.append(design.panel)
|
||||||
else:
|
else:
|
||||||
if not error_panel and not process_parameters:
|
if not error_panel and not process_parameters:
|
||||||
return {
|
production_count = self.env['mrp.production'].search_count([
|
||||||
'name': _('工艺确认'),
|
('origin', '=', self.origin),
|
||||||
'type': 'ir.actions.act_window',
|
('product_id', '=', self.product_id.id),
|
||||||
'view_mode': 'form',
|
('state', '=', 'technology_to_confirmed')
|
||||||
'res_model': 'sf.production.technology.wizard',
|
])
|
||||||
'target': 'new',
|
if production_count > 1:
|
||||||
'context': {
|
return {
|
||||||
'default_production_id': self.id,
|
'name': _('工艺确认'),
|
||||||
'default_origin': self.origin,
|
'type': 'ir.actions.act_window',
|
||||||
}}
|
'views': [(self.env.ref(
|
||||||
|
'sf_manufacturing.sf_production_technology_wizard_form_view').id,
|
||||||
|
'form')],
|
||||||
|
'res_model': 'sf.production.technology.wizard',
|
||||||
|
'target': 'new',
|
||||||
|
'context': {
|
||||||
|
'default_production_id': self.id,
|
||||||
|
'default_origin': self.origin,
|
||||||
|
}}
|
||||||
|
else:
|
||||||
|
return {
|
||||||
|
'name': _('工艺确认'),
|
||||||
|
'type': 'ir.actions.act_window',
|
||||||
|
'views': [(self.env.ref(
|
||||||
|
'sf_manufacturing.sf_production_technology_wizard_confirm_form_view').id,
|
||||||
|
'form')],
|
||||||
|
'res_model': 'sf.production.technology.wizard',
|
||||||
|
'target': 'new',
|
||||||
|
'context': {
|
||||||
|
'default_production_id': self.id,
|
||||||
|
'default_origin': self.origin,
|
||||||
|
}}
|
||||||
if error_panel:
|
if error_panel:
|
||||||
raise UserError(_("【加工面】为%s的标准工序顺序有误,请调整后重试", ", ".join(error_panel)))
|
raise UserError(_("【加工面】为%s的标准工序顺序有误,请调整后重试", ", ".join(error_panel)))
|
||||||
return True
|
return True
|
||||||
@@ -640,8 +724,8 @@ class MrpProduction(models.Model):
|
|||||||
if self.move_finished_ids.filtered(lambda m: m.product_id == self.product_id).move_line_ids:
|
if self.move_finished_ids.filtered(lambda m: m.product_id == self.product_id).move_line_ids:
|
||||||
self.move_finished_ids.filtered(
|
self.move_finished_ids.filtered(
|
||||||
lambda m: m.product_id == self.product_id).move_line_ids.lot_id = self.lot_producing_id
|
lambda m: m.product_id == self.product_id).move_line_ids.lot_id = self.lot_producing_id
|
||||||
if self.product_id.tracking == 'serial':
|
# if self.product_id.tracking == 'serial':
|
||||||
self._set_qty_producing()
|
# self._set_qty_producing()
|
||||||
|
|
||||||
# 重载根据工序生成工单的程序:如果产品BOM中没有工序时,
|
# 重载根据工序生成工单的程序:如果产品BOM中没有工序时,
|
||||||
# 根据产品对应的模板类型中工序,去生成工单;
|
# 根据产品对应的模板类型中工序,去生成工单;
|
||||||
@@ -679,9 +763,10 @@ class MrpProduction(models.Model):
|
|||||||
}]
|
}]
|
||||||
if production.product_id.categ_id.type in ['成品', '坯料']:
|
if production.product_id.categ_id.type in ['成品', '坯料']:
|
||||||
# # 根据工序设计生成工单
|
# # 根据工序设计生成工单
|
||||||
for route in production.technology_design_ids:
|
technology_design_ids = sorted(production.technology_design_ids, key=lambda x: x.sequence)
|
||||||
|
for route in technology_design_ids:
|
||||||
workorder_has = self.env['mrp.workorder'].search(
|
workorder_has = self.env['mrp.workorder'].search(
|
||||||
[('name', '=', route.route_id.name), ('production_id', '=', production.id)])
|
[('technology_design_id', '=', route.id), ('production_id', '=', production.id)])
|
||||||
if not workorder_has:
|
if not workorder_has:
|
||||||
if route.route_id.routing_type not in ['表面工艺']:
|
if route.route_id.routing_type not in ['表面工艺']:
|
||||||
workorders_values.append(
|
workorders_values.append(
|
||||||
@@ -705,48 +790,24 @@ class MrpProduction(models.Model):
|
|||||||
groupby(production_all, key=lambda x: x.product_id.id)}
|
groupby(production_all, key=lambda x: x.product_id.id)}
|
||||||
for product_id, pd in grouped_product_ids.items():
|
for product_id, pd in grouped_product_ids.items():
|
||||||
product_id_to_production_names[product_id] = [p.name for p in pd]
|
product_id_to_production_names[product_id] = [p.name for p in pd]
|
||||||
|
sorted_workorders = None
|
||||||
for production in production_all:
|
for production in production_all:
|
||||||
|
proc_workorders = []
|
||||||
process_parameter_workorder = self.env['mrp.workorder'].search(
|
process_parameter_workorder = self.env['mrp.workorder'].search(
|
||||||
[('surface_technics_parameters_id', '!=', False), ('production_id', '=', production.id),
|
[('surface_technics_parameters_id', '!=', False), ('production_id', '=', production.id),
|
||||||
('is_subcontract', '=', True)], order='sequence asc')
|
('is_subcontract', '=', True), ('state', '!=', 'cancel')], order='sequence asc')
|
||||||
if process_parameter_workorder:
|
if process_parameter_workorder:
|
||||||
consecutive_workorders = []
|
# 将这些特殊表面工艺工单的采购单与调拨单置为失效
|
||||||
|
for workorder in process_parameter_workorder:
|
||||||
|
workorder._get_surface_technics_purchase_ids().write({'state': 'cancel'})
|
||||||
|
workorder.move_subcontract_workorder_ids.write({'state': 'cancel'})
|
||||||
|
workorder.move_subcontract_workorder_ids.picking_id.write({'state': 'cancel'})
|
||||||
sorted_workorders = sorted(process_parameter_workorder, key=lambda w: w.sequence)
|
sorted_workorders = sorted(process_parameter_workorder, key=lambda w: w.sequence)
|
||||||
for i, workorder in enumerate(sorted_workorders):
|
if not sorted_workorders:
|
||||||
# 检查当前工作订单和下一个工作订单是否连续,并且供应商相同
|
return
|
||||||
if i == 0:
|
for workorders in reversed(sorted_workorders):
|
||||||
consecutive_workorders.append(workorder)
|
self.env['stock.picking'].create_outcontract_picking(workorders, production, sorted_workorders)
|
||||||
elif workorder.sequence == sorted_workorders[
|
self.env['purchase.order'].get_purchase_order(workorders, production, product_id_to_production_names)
|
||||||
i - 1].sequence + 1 and workorder.supplier_id.id == sorted_workorders[i - 1].supplier_id.id:
|
|
||||||
consecutive_workorders.append(workorder)
|
|
||||||
else:
|
|
||||||
# 处理连续组,如果它不为空
|
|
||||||
if consecutive_workorders:
|
|
||||||
# 创建外协出入库单和采购订单
|
|
||||||
self.env['stock.picking'].create_outcontract_picking(consecutive_workorders, production)
|
|
||||||
self.env['purchase.order'].get_purchase_order(consecutive_workorders, production,
|
|
||||||
product_id_to_production_names)
|
|
||||||
if i < len(sorted_workorders) - 1:
|
|
||||||
# 重置连续组,并添加当前工作订单
|
|
||||||
consecutive_workorders = [workorder]
|
|
||||||
else:
|
|
||||||
# 判断最后一笔:
|
|
||||||
if workorder.sequence == sorted_workorders[
|
|
||||||
i - 1].sequence and workorder.supplier_id.id == sorted_workorders[
|
|
||||||
i - 1].supplier_id.id:
|
|
||||||
consecutive_workorders = [workorder]
|
|
||||||
else:
|
|
||||||
# 立即创建外协出入库单和采购订单
|
|
||||||
self.env['stock.picking'].create_outcontract_picking(workorder, production)
|
|
||||||
self.env['purchase.order'].get_purchase_order(workorder, production,
|
|
||||||
product_id_to_production_names)
|
|
||||||
consecutive_workorders = []
|
|
||||||
|
|
||||||
# 处理最后一个组,即使它可能只有一个工作订单
|
|
||||||
if consecutive_workorders:
|
|
||||||
self.env['stock.picking'].create_outcontract_picking(consecutive_workorders, production)
|
|
||||||
self.env['purchase.order'].get_purchase_order(consecutive_workorders, production,
|
|
||||||
product_id_to_production_names)
|
|
||||||
|
|
||||||
# 工单排序
|
# 工单排序
|
||||||
def _reset_work_order_sequence1(self, k):
|
def _reset_work_order_sequence1(self, k):
|
||||||
@@ -832,17 +893,22 @@ class MrpProduction(models.Model):
|
|||||||
# workorder.picking_ids.move_ids = False
|
# workorder.picking_ids.move_ids = False
|
||||||
workorder.picking_ids = False
|
workorder.picking_ids = False
|
||||||
purchase_order = self.env['purchase.order'].search(
|
purchase_order = self.env['purchase.order'].search(
|
||||||
[('state', '=', 'draft'), ('origin', '=', ','.join(production_process)),
|
[('state', '=', 'draft'), ('origin', '=', item.name),
|
||||||
('purchase_type', '=', 'consignment')])
|
('purchase_type', '=', 'consignment')])
|
||||||
for line in purchase_order.order_line:
|
server_template = self.env['product.template'].search(
|
||||||
server_template = self.env['product.template'].search(
|
[('server_product_process_parameters_id', '=',
|
||||||
[('server_product_process_parameters_id', '=', workorder.surface_technics_parameters_id.id),
|
workorder.surface_technics_parameters_id.id),
|
||||||
('detailed_type', '=', 'service')])
|
('detailed_type', '=', 'service')])
|
||||||
purchase_order_line = self.env['purchase.order.line'].search(
|
for po in purchase_order:
|
||||||
[('product_id', '=', server_template.product_variant_id.id), ('id', '=', line.id),
|
for line in po.order_line:
|
||||||
('product_qty', '=', len(production_process))], limit=1, order='id desc')
|
if line.product_id == server_template.product_variant_id:
|
||||||
if purchase_order_line:
|
continue
|
||||||
line.unlink()
|
if server_template.server_product_process_parameters_id != line.product_id.server_product_process_parameters_id:
|
||||||
|
purchase_order_line = self.env['purchase.order.line'].search(
|
||||||
|
[('product_id', '=', server_template.product_variant_id.id), ('id', '=', line.id),
|
||||||
|
('product_qty', '=', 1)], limit=1, order='id desc')
|
||||||
|
if purchase_order_line:
|
||||||
|
line.unlink()
|
||||||
|
|
||||||
def _reset_work_order_sequence(self):
|
def _reset_work_order_sequence(self):
|
||||||
"""
|
"""
|
||||||
@@ -856,10 +922,11 @@ class MrpProduction(models.Model):
|
|||||||
work_ids = workorder_ids.filtered(lambda item: item.sequence == 0)
|
work_ids = workorder_ids.filtered(lambda item: item.sequence == 0)
|
||||||
# 对工单进行逐个插入
|
# 对工单进行逐个插入
|
||||||
for work_id in work_ids:
|
for work_id in work_ids:
|
||||||
for order_id in rec.workorder_ids.filtered(lambda item: item.sequence > 0):
|
order_rework_ids = rec.workorder_ids.filtered(
|
||||||
if work_id.name == order_id.name:
|
lambda item: (item.sequence > 0 and work_id.name == item.name
|
||||||
work_id.sequence = order_id.sequence + 1
|
and work_id.processing_panel == item.processing_panel))
|
||||||
break
|
order_rework_ids = sorted(order_rework_ids, key=lambda item: item.sequence, reverse=True)
|
||||||
|
work_id.sequence = order_rework_ids[0].sequence + 1
|
||||||
# 对该工单之后的工单工序进行加一
|
# 对该工单之后的工单工序进行加一
|
||||||
work_order_ids = rec.workorder_ids.filtered(
|
work_order_ids = rec.workorder_ids.filtered(
|
||||||
lambda item: item.sequence >= work_id.sequence and item.id != work_id.id)
|
lambda item: item.sequence >= work_id.sequence and item.id != work_id.id)
|
||||||
@@ -874,8 +941,16 @@ class MrpProduction(models.Model):
|
|||||||
and item.process_parameters_id == work.surface_technics_parameters_id) or
|
and item.process_parameters_id == work.surface_technics_parameters_id) or
|
||||||
(item.route_id.name == work.name and item.panel
|
(item.route_id.name == work.name and item.panel
|
||||||
and item.panel == work.processing_panel))
|
and item.panel == work.processing_panel))
|
||||||
|
if work.name == '人工线下加工':
|
||||||
|
td_ids = technology_design_ids.filtered(lambda item: (item.route_id.name in work.name))
|
||||||
if td_ids:
|
if td_ids:
|
||||||
work.sequence = td_ids[0].sequence
|
work.sequence = td_ids[0].sequence
|
||||||
|
cancel_work_ids = workorder_ids.filtered(lambda item: item.state in ('已取消', 'cancel'))
|
||||||
|
if cancel_work_ids:
|
||||||
|
sequence = max(workorder_ids.filtered(lambda item: item.state not in ('已取消', 'cancel')),
|
||||||
|
key=lambda w: w.sequence).sequence
|
||||||
|
for cw in cancel_work_ids:
|
||||||
|
cw.sequence = sequence + 1
|
||||||
|
|
||||||
def _reset_work_order_sequence_1(self):
|
def _reset_work_order_sequence_1(self):
|
||||||
"""
|
"""
|
||||||
@@ -1127,13 +1202,15 @@ class MrpProduction(models.Model):
|
|||||||
if self.programming_state in ['已编程']:
|
if self.programming_state in ['已编程']:
|
||||||
cloud_programming = self._cron_get_programming_state()
|
cloud_programming = self._cron_get_programming_state()
|
||||||
result_ids = self.detection_result_ids.filtered(lambda dr: dr.handle_result == '待处理')
|
result_ids = self.detection_result_ids.filtered(lambda dr: dr.handle_result == '待处理')
|
||||||
work_ids = []
|
work_id_list = []
|
||||||
if result_ids:
|
if result_ids:
|
||||||
for result_id in result_ids:
|
work_id_list = [self.workorder_ids.filtered(
|
||||||
work_ids.append(self.workorder_ids.filtered(
|
lambda wk: (wk.name == result_id.routing_type and wk.processing_panel == result_id.processing_panel
|
||||||
lambda wk: (wk.name == result_id.routing_type
|
and wk.state == 'done')).id
|
||||||
and wk.processing_panel == result_id.processing_panel
|
for result_id in result_ids]
|
||||||
and wk.state == 'done')).id)
|
workorder_ids = self.workorder_ids.filtered(
|
||||||
|
lambda wk: wk.technology_design_id.routing_tag == 'standard' and wk.state not in ['rework', 'cancel'])
|
||||||
|
logging.info('标准工艺工单【%s】' % workorder_ids)
|
||||||
return {
|
return {
|
||||||
'name': _('返工'),
|
'name': _('返工'),
|
||||||
'type': 'ir.actions.act_window',
|
'type': 'ir.actions.act_window',
|
||||||
@@ -1142,7 +1219,8 @@ class MrpProduction(models.Model):
|
|||||||
'target': 'new',
|
'target': 'new',
|
||||||
'context': {
|
'context': {
|
||||||
'default_production_id': self.id,
|
'default_production_id': self.id,
|
||||||
'default_workorder_ids': work_ids,
|
'default_workorder_ids': workorder_ids.ids if workorder_ids.ids != [] else self.workorder_ids.ids,
|
||||||
|
'default_hidden_workorder_ids': ','.join(map(str, work_id_list)) if work_id_list != [] else '',
|
||||||
'default_reprogramming_num': cloud_programming['reprogramming_num'],
|
'default_reprogramming_num': cloud_programming['reprogramming_num'],
|
||||||
'default_programming_state': cloud_programming['programming_state'],
|
'default_programming_state': cloud_programming['programming_state'],
|
||||||
'default_is_reprogramming': True if cloud_programming['programming_state'] in ['已下发'] else False
|
'default_is_reprogramming': True if cloud_programming['programming_state'] in ['已下发'] else False
|
||||||
@@ -1296,14 +1374,14 @@ class MrpProduction(models.Model):
|
|||||||
raise_user_error=not self.env.context.get('from_orderpoint'))
|
raise_user_error=not self.env.context.get('from_orderpoint'))
|
||||||
productions = self.env['mrp.production'].sudo().search(
|
productions = self.env['mrp.production'].sudo().search(
|
||||||
[('origin', '=', self.origin)], order='id desc', limit=1)
|
[('origin', '=', self.origin)], order='id desc', limit=1)
|
||||||
|
productions.write({'programming_no': self.programming_no, 'is_remanufacture': True})
|
||||||
move = self.env['stock.move'].search([('origin', '=', productions.name)], order='id desc')
|
move = self.env['stock.move'].search([('origin', '=', productions.name)], order='id desc')
|
||||||
for mo in move:
|
for mo in move:
|
||||||
domain = []
|
domain = []
|
||||||
if mo.procure_method == 'make_to_order' and mo.name != productions.name:
|
if mo.location_id.barcode == 'WH-POSTPRODUCTION' and mo.rule_id.picking_type_id.barcode == 'PC':
|
||||||
if mo.name == '/':
|
domain = [('barcode', '=', 'WH-PC'), ('sequence_code', '=', 'PC')]
|
||||||
domain = [('barcode', '=', 'WH-PC'), ('sequence_code', '=', 'PC')]
|
elif mo.location_id.barcode == 'PL' and mo.rule_id.picking_type_id.barcode == 'INT':
|
||||||
elif mo.name == '拉':
|
domain = [('barcode', '=', 'WH-INTERNAL'), ('sequence_code', '=', 'INT')]
|
||||||
domain = [('barcode', '=', 'WH-INTERNAL'), ('sequence_code', '=', 'INT')]
|
|
||||||
if domain:
|
if domain:
|
||||||
picking_type = self.env['stock.picking.type'].search(domain)
|
picking_type = self.env['stock.picking.type'].search(domain)
|
||||||
mo.write({'picking_type_id': picking_type.id})
|
mo.write({'picking_type_id': picking_type.id})
|
||||||
@@ -1322,7 +1400,6 @@ class MrpProduction(models.Model):
|
|||||||
mo_move.write({'reference': sfp_move.reference, 'partner_id': sfp_move.partner_id.id,
|
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,
|
'picking_id': sfp_move.picking_id.id, 'picking_type_id': sfp_move.picking_type_id.id,
|
||||||
'production_id': False})
|
'production_id': False})
|
||||||
productions.write({'programming_no': self.programming_no, 'is_remanufacture': True})
|
|
||||||
# productions.procurement_group_id.mrp_production_ids.move_dest_ids.write(
|
# productions.procurement_group_id.mrp_production_ids.move_dest_ids.write(
|
||||||
# {'group_id': self.env['procurement.group'].search([('name', '=', sale_order.name)])})
|
# {'group_id': self.env['procurement.group'].search([('name', '=', sale_order.name)])})
|
||||||
stock_picking_remanufacture = self.env['stock.picking'].search([('origin', '=', productions.name)])
|
stock_picking_remanufacture = self.env['stock.picking'].search([('origin', '=', productions.name)])
|
||||||
@@ -1397,7 +1474,27 @@ class MrpProduction(models.Model):
|
|||||||
def _compute_production_type(self):
|
def _compute_production_type(self):
|
||||||
for production in self:
|
for production in self:
|
||||||
production.production_type = '自动化产线加工' if not production.product_id.is_manual_processing else '人工线下加工'
|
production.production_type = '自动化产线加工' if not production.product_id.is_manual_processing else '人工线下加工'
|
||||||
|
|
||||||
|
@api.depends('procurement_group_id.mrp_production_ids.move_dest_ids.group_id.sale_id')
|
||||||
|
def _compute_sale_order_count(self):
|
||||||
|
for production in self:
|
||||||
|
if production.sale_order_id:
|
||||||
|
production.sale_order_count = 1
|
||||||
|
else:
|
||||||
|
production.sale_order_count = 0
|
||||||
|
|
||||||
|
def action_view_sale_orders(self):
|
||||||
|
if self.sale_order_id:
|
||||||
|
action = {
|
||||||
|
'res_model': 'sale.order',
|
||||||
|
'type': 'ir.actions.act_window',
|
||||||
|
}
|
||||||
|
action.update({
|
||||||
|
'view_mode': 'form',
|
||||||
|
'res_id': self.sale_order_id.id,
|
||||||
|
})
|
||||||
|
return action
|
||||||
|
|
||||||
@api.model_create_multi
|
@api.model_create_multi
|
||||||
def create(self, vals_list):
|
def create(self, vals_list):
|
||||||
"""
|
"""
|
||||||
@@ -1413,7 +1510,7 @@ class MrpProduction(models.Model):
|
|||||||
vals['name'] = self.env['stock.picking.type'].browse(picking_type_id).sequence_id.next_by_id()
|
vals['name'] = self.env['stock.picking.type'].browse(picking_type_id).sequence_id.next_by_id()
|
||||||
if not vals.get('procurement_group_id'):
|
if not vals.get('procurement_group_id'):
|
||||||
product_id = self.env['product.product'].browse(vals['product_id'])
|
product_id = self.env['product.product'].browse(vals['product_id'])
|
||||||
if product_id.product_tmpl_id.single_manufacturing:
|
if product_id.product_tmpl_id.single_manufacturing:
|
||||||
if product_id.id not in product_group_id.keys():
|
if product_id.id not in product_group_id.keys():
|
||||||
procurement_group_vals = self._prepare_procurement_group_vals(vals)
|
procurement_group_vals = self._prepare_procurement_group_vals(vals)
|
||||||
group_id = self.env["procurement.group"].create(procurement_group_vals).id
|
group_id = self.env["procurement.group"].create(procurement_group_vals).id
|
||||||
@@ -1423,23 +1520,29 @@ class MrpProduction(models.Model):
|
|||||||
vals['procurement_group_id'] = product_group_id[product_id.id]
|
vals['procurement_group_id'] = product_group_id[product_id.id]
|
||||||
return super(MrpProduction, self).create(vals_list)
|
return super(MrpProduction, self).create(vals_list)
|
||||||
|
|
||||||
@api.depends('procurement_group_id.stock_move_ids.created_purchase_line_id.order_id', 'procurement_group_id.stock_move_ids.move_orig_ids.purchase_line_id.order_id')
|
@api.depends('procurement_group_id.stock_move_ids.created_purchase_line_id.order_id',
|
||||||
|
'procurement_group_id.stock_move_ids.move_orig_ids.purchase_line_id.order_id')
|
||||||
def _compute_purchase_order_count(self):
|
def _compute_purchase_order_count(self):
|
||||||
for production in self:
|
for production in self:
|
||||||
# 找到来源的第一张制造订单的采购组
|
# 找到来源的第一张制造订单的采购组
|
||||||
if production.product_id.product_tmpl_id.single_manufacturing == True:
|
if production.product_id.product_tmpl_id.single_manufacturing == True:
|
||||||
first_production = self.env['mrp.production'].search([('origin', '=', production.origin), ('product_id', '=', production.product_id.id)], limit=1, order='id asc')
|
first_production = self.env['mrp.production'].search(
|
||||||
production.purchase_order_count = len(first_production.procurement_group_id.stock_move_ids.created_purchase_line_id.order_id |
|
[('origin', '=', production.origin), ('product_id', '=', production.product_id.id)], limit=1,
|
||||||
first_production.procurement_group_id.stock_move_ids.move_orig_ids.purchase_line_id.order_id)
|
order='id asc')
|
||||||
|
production.purchase_order_count = len(
|
||||||
|
first_production.procurement_group_id.stock_move_ids.created_purchase_line_id.order_id |
|
||||||
|
first_production.procurement_group_id.stock_move_ids.move_orig_ids.purchase_line_id.order_id)
|
||||||
else:
|
else:
|
||||||
production.purchase_order_count = len(production.procurement_group_id.stock_move_ids.created_purchase_line_id.order_id |
|
production.purchase_order_count = len(
|
||||||
production.procurement_group_id.stock_move_ids.move_orig_ids.purchase_line_id.order_id)
|
production.procurement_group_id.stock_move_ids.created_purchase_line_id.order_id |
|
||||||
|
production.procurement_group_id.stock_move_ids.move_orig_ids.purchase_line_id.order_id)
|
||||||
|
|
||||||
@api.depends('procurement_group_id', 'procurement_group_id.stock_move_ids.group_id')
|
@api.depends('procurement_group_id', 'procurement_group_id.stock_move_ids.group_id')
|
||||||
def _compute_picking_ids(self):
|
def _compute_picking_ids(self):
|
||||||
for order in self:
|
for order in self:
|
||||||
if order.product_id.product_tmpl_id.single_manufacturing == True:
|
if order.product_id.product_tmpl_id.single_manufacturing == True:
|
||||||
first_order = self.env['mrp.production'].search([('origin', '=', order.origin), ('product_id', '=', order.product_id.id)], limit=1, order='id asc')
|
first_order = self.env['mrp.production'].search(
|
||||||
|
[('origin', '=', order.origin), ('product_id', '=', order.product_id.id)], limit=1, order='id asc')
|
||||||
order.picking_ids = self.env['stock.picking'].search([
|
order.picking_ids = self.env['stock.picking'].search([
|
||||||
('group_id', '=', first_order.procurement_group_id.id), ('group_id', '!=', False),
|
('group_id', '=', first_order.procurement_group_id.id), ('group_id', '!=', False),
|
||||||
])
|
])
|
||||||
@@ -1450,6 +1553,55 @@ class MrpProduction(models.Model):
|
|||||||
])
|
])
|
||||||
order.delivery_count = len(order.picking_ids)
|
order.delivery_count = len(order.picking_ids)
|
||||||
|
|
||||||
|
def action_view_purchase_orders(self):
|
||||||
|
self.ensure_one()
|
||||||
|
if self.is_remanufacture:
|
||||||
|
production = self
|
||||||
|
elif self.product_id.product_tmpl_id.single_manufacturing == True:
|
||||||
|
production = self.env['mrp.production'].search(
|
||||||
|
[('origin', '=', self.origin), ('product_id', '=', self.product_id.id)], limit=1, order='id asc')
|
||||||
|
else:
|
||||||
|
production = self
|
||||||
|
purchase_order_ids = (
|
||||||
|
production.procurement_group_id.stock_move_ids.created_purchase_line_id.order_id | production.procurement_group_id.stock_move_ids.move_orig_ids.purchase_line_id.order_id).ids
|
||||||
|
action = {
|
||||||
|
'res_model': 'purchase.order',
|
||||||
|
'type': 'ir.actions.act_window',
|
||||||
|
}
|
||||||
|
if len(purchase_order_ids) == 1:
|
||||||
|
action.update({
|
||||||
|
'view_mode': 'form',
|
||||||
|
'res_id': purchase_order_ids[0],
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
action.update({
|
||||||
|
'name': _("Purchase Order generated from %s", self.name),
|
||||||
|
'domain': [('id', 'in', purchase_order_ids)],
|
||||||
|
'view_mode': 'tree,form',
|
||||||
|
})
|
||||||
|
return action
|
||||||
|
|
||||||
|
def _subcontract_sanity_check(self):
|
||||||
|
for production in self:
|
||||||
|
if production.product_tracking != 'none' and not self.lot_producing_id:
|
||||||
|
raise UserError(_('You must enter a serial number for %s') % production.product_id.name)
|
||||||
|
for sml in production.move_raw_ids.move_line_ids:
|
||||||
|
if sml.tracking != 'none' and not sml.lot_id:
|
||||||
|
picking_ids = production.picking_ids.filtered(
|
||||||
|
lambda p: p.state not in ['done', 'cancel'])
|
||||||
|
picking_num = len(picking_ids)
|
||||||
|
picking_info = ', '.join(
|
||||||
|
['%s:%s' % (picking.picking_type_id.name, picking.name) for picking in picking_ids]
|
||||||
|
)
|
||||||
|
if picking_info:
|
||||||
|
raise UserError(_('You have %s incomplete supplies: %s') % (
|
||||||
|
picking_num, picking_info))
|
||||||
|
else:
|
||||||
|
raise UserError(
|
||||||
|
_('You must enter a serial number for each line of %s') % sml.product_id.display_name)
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
class sf_detection_result(models.Model):
|
class sf_detection_result(models.Model):
|
||||||
_name = 'sf.detection.result'
|
_name = 'sf.detection.result'
|
||||||
_description = "检测结果"
|
_description = "检测结果"
|
||||||
|
|||||||
@@ -86,9 +86,18 @@ class ResMrpRoutingWorkcenter(models.Model):
|
|||||||
@api.model
|
@api.model
|
||||||
def _name_search(self, name, args=None, operator='ilike', limit=100, name_get_uid=None):
|
def _name_search(self, name, args=None, operator='ilike', limit=100, name_get_uid=None):
|
||||||
if self._context.get('production_id'):
|
if self._context.get('production_id'):
|
||||||
|
route_ids = []
|
||||||
technology_design = self.env['sf.technology.design'].search(
|
technology_design = self.env['sf.technology.design'].search(
|
||||||
[('production_id', '=', self._context.get('production_id'))])
|
[('production_id', '=', self._context.get('production_id'))])
|
||||||
route_ids = [t.route_id.id for t in technology_design]
|
for t in technology_design.filtered(lambda a: a.routing_tag == 'special'):
|
||||||
domain = [('id', 'not in', route_ids)]
|
if not t.process_parameters_id:
|
||||||
|
route_ids.append(t.route_id.surface_technics_id.id)
|
||||||
|
domain = [('id', 'not in', route_ids), ('routing_tag', '=', 'special')]
|
||||||
|
return self._search(domain, limit=limit, access_rights_uid=name_get_uid)
|
||||||
|
if self._context.get('is_duplicate') and self._context.get('model_name'):
|
||||||
|
# 查询出已经选择的工序
|
||||||
|
model_type = self.env[self._context.get('model_name')].search_read([],['route_workcenter_id'])
|
||||||
|
route_workcenter_ids = [item['route_workcenter_id'][0] if item['route_workcenter_id'] else False for item in model_type]
|
||||||
|
domain = args + [('id', 'not in', route_workcenter_ids)]
|
||||||
return self._search(domain, limit=limit, access_rights_uid=name_get_uid)
|
return self._search(domain, limit=limit, access_rights_uid=name_get_uid)
|
||||||
return super()._name_search(name, args, operator, limit, name_get_uid)
|
return super()._name_search(name, args, operator, limit, name_get_uid)
|
||||||
|
|||||||
@@ -17,23 +17,27 @@ from odoo.exceptions import UserError, ValidationError
|
|||||||
from odoo.addons.sf_mrs_connect.models.ftp_operate import FtpController
|
from odoo.addons.sf_mrs_connect.models.ftp_operate import FtpController
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class ResMrpWorkOrder(models.Model):
|
class ResMrpWorkOrder(models.Model):
|
||||||
_inherit = 'mrp.workorder'
|
_inherit = 'mrp.workorder'
|
||||||
_order = 'sequence asc'
|
_order = 'sequence asc'
|
||||||
|
|
||||||
product_tmpl_name = fields.Char('坯料产品名称', related='production_bom_id.bom_line_ids.product_id.name')
|
product_tmpl_name = fields.Char('坯料产品名称', related='production_bom_id.bom_line_ids.product_id.name')
|
||||||
|
|
||||||
product_tmpl_id_length = fields.Float(related='production_id.product_tmpl_id.length', readonly=True, store=True,
|
product_tmpl_id_length = fields.Float(string='坯料长度(mm)', related='material_length', readonly=True, store=False)
|
||||||
string="坯料长度(mm)")
|
product_tmpl_id_width = fields.Float(string='坯料宽度(mm)', related='material_width', readonly=True, store=False)
|
||||||
product_tmpl_id_width = fields.Float(related='production_id.product_tmpl_id.width', readonly=True, store=True,
|
product_tmpl_id_height = fields.Float(string='坯料高度(mm)', related='material_height', readonly=True, store=False)
|
||||||
string="坯料宽度(mm)")
|
# product_tmpl_id_length = fields.Float(related='production_id.product_tmpl_id.length', readonly=True, store=True,
|
||||||
product_tmpl_id_height = fields.Float(related='production_id.product_tmpl_id.height', readonly=True, store=True,
|
# string="坯料长度(mm)")
|
||||||
string="坯料高度(mm)")
|
# product_tmpl_id_width = fields.Float(related='production_id.product_tmpl_id.width', readonly=True, store=True,
|
||||||
|
# string="坯料宽度(mm)")
|
||||||
|
# product_tmpl_id_height = fields.Float(related='production_id.product_tmpl_id.height', readonly=True, store=True,
|
||||||
|
# string="坯料高度(mm)")
|
||||||
product_tmpl_id_materials_id = fields.Many2one(related='production_id.product_tmpl_id.materials_id', readonly=True,
|
product_tmpl_id_materials_id = fields.Many2one(related='production_id.product_tmpl_id.materials_id', readonly=True,
|
||||||
store=True, check_company=True, string="材料")
|
store=True, check_company=True, string="材料")
|
||||||
product_tmpl_id_materials_type_id = fields.Many2one(related='production_id.product_tmpl_id.materials_type_id',
|
product_tmpl_id_materials_type_id = fields.Many2one(related='production_id.product_tmpl_id.materials_type_id',
|
||||||
readonly=True, store=True, check_company=True, string="型号")
|
readonly=True, store=True, check_company=True, string="型号")
|
||||||
workcenter_id = fields.Many2one('mrp.workcenter', string='工作中心', required=False)
|
# workcenter_id = fields.Many2one('mrp.workcenter', string='工作中心', required=False)
|
||||||
users_ids = fields.Many2many("res.users", 'users_workorder', related="workcenter_id.users_ids")
|
users_ids = fields.Many2many("res.users", 'users_workorder', related="workcenter_id.users_ids")
|
||||||
processing_panel = fields.Char('加工面')
|
processing_panel = fields.Char('加工面')
|
||||||
sequence = fields.Integer(string='工序')
|
sequence = fields.Integer(string='工序')
|
||||||
@@ -126,7 +130,7 @@ class ResMrpWorkOrder(models.Model):
|
|||||||
Y10_axis = fields.Float(default=0)
|
Y10_axis = fields.Float(default=0)
|
||||||
Z10_axis = fields.Float(default=0)
|
Z10_axis = fields.Float(default=0)
|
||||||
X_deviation_angle = fields.Integer(string="X轴偏差度", default=0)
|
X_deviation_angle = fields.Integer(string="X轴偏差度", default=0)
|
||||||
test_results = fields.Selection([("合格", "合格"), ("返工", "返工"), ("报废", "报废")], default='合格',
|
test_results = fields.Selection([("合格", "合格"), ("返工", "返工")], default='合格',
|
||||||
string="检测结果", tracking=True)
|
string="检测结果", tracking=True)
|
||||||
cnc_ids = fields.One2many("sf.cnc.processing", 'workorder_id', string="CNC加工程序")
|
cnc_ids = fields.One2many("sf.cnc.processing", 'workorder_id', string="CNC加工程序")
|
||||||
cmm_ids = fields.One2many("sf.cmm.program", 'workorder_id', string="CMM程序")
|
cmm_ids = fields.One2many("sf.cmm.program", 'workorder_id', string="CMM程序")
|
||||||
@@ -134,8 +138,11 @@ class ResMrpWorkOrder(models.Model):
|
|||||||
glb_file = fields.Binary("glb模型文件", related='production_id.model_file')
|
glb_file = fields.Binary("glb模型文件", related='production_id.model_file')
|
||||||
is_subcontract = fields.Boolean(string='是否外协')
|
is_subcontract = fields.Boolean(string='是否外协')
|
||||||
surface_technics_parameters_id = fields.Many2one('sf.production.process.parameter', string="表面工艺可选参数")
|
surface_technics_parameters_id = fields.Many2one('sf.production.process.parameter', string="表面工艺可选参数")
|
||||||
picking_ids = fields.Many2many('stock.picking', string='外协出入库单')
|
|
||||||
# purchase_id = fields.Many2one('purchase.order', string='外协采购单')
|
picking_ids = fields.Many2many('stock.picking', string='外协出入库单',
|
||||||
|
compute='_compute_surface_technics_picking_ids')
|
||||||
|
|
||||||
|
purchase_id = fields.Many2many('purchase.order', string='外协采购单')
|
||||||
surface_technics_picking_count = fields.Integer("外协出入库", compute='_compute_surface_technics_picking_ids')
|
surface_technics_picking_count = fields.Integer("外协出入库", compute='_compute_surface_technics_picking_ids')
|
||||||
surface_technics_purchase_count = fields.Integer("外协采购", compute='_compute_surface_technics_purchase_ids')
|
surface_technics_purchase_count = fields.Integer("外协采购", compute='_compute_surface_technics_purchase_ids')
|
||||||
|
|
||||||
@@ -235,15 +242,15 @@ class ResMrpWorkOrder(models.Model):
|
|||||||
for workorder in self:
|
for workorder in self:
|
||||||
if workorder.routing_type == '表面工艺':
|
if workorder.routing_type == '表面工艺':
|
||||||
domain = [('origin', '=', workorder.production_id.name), ('state', 'not in', ['cancel']),
|
domain = [('origin', '=', workorder.production_id.name), ('state', 'not in', ['cancel']),
|
||||||
('partner_id', '!=', False)]
|
('partner_id', '=', workorder.supplier_id.id)]
|
||||||
previous_workorder = self.env['mrp.workorder'].search(
|
previous_workorder = self.env['mrp.workorder'].search(
|
||||||
[('sequence', '=', workorder.sequence - 1), ('routing_type', '=', '表面工艺'),
|
[('sequence', '=', workorder.sequence - 1), ('routing_type', '=', '表面工艺'),
|
||||||
('production_id', '=', workorder.production_id.id)])
|
('production_id', '=', workorder.production_id.id)])
|
||||||
if previous_workorder:
|
# if previous_workorder:
|
||||||
if previous_workorder.supplier_id != workorder.supplier_id:
|
# if previous_workorder.supplier_id != workorder.supplier_id:
|
||||||
process_product = self.env['product.template']._get_process_parameters_product(
|
# domain += [('surface_technics_parameters_id', '=', workorder.surface_technics_parameters_id.id)]
|
||||||
previous_workorder.surface_technics_parameters_id)
|
# else:
|
||||||
domain += [('partner_id', '=', process_product.partner_id.id)]
|
domain += [('surface_technics_parameters_id', '=', workorder.surface_technics_parameters_id.id)]
|
||||||
picking_ids = self.env['stock.picking'].search(domain, order='id asc')
|
picking_ids = self.env['stock.picking'].search(domain, order='id asc')
|
||||||
workorder.surface_technics_picking_count = len(picking_ids)
|
workorder.surface_technics_picking_count = len(picking_ids)
|
||||||
workorder.picking_ids = picking_ids.ids
|
workorder.picking_ids = picking_ids.ids
|
||||||
@@ -267,58 +274,60 @@ class ResMrpWorkOrder(models.Model):
|
|||||||
@api.depends('state', 'production_id.name')
|
@api.depends('state', 'production_id.name')
|
||||||
def _compute_surface_technics_purchase_ids(self):
|
def _compute_surface_technics_purchase_ids(self):
|
||||||
for order in self:
|
for order in self:
|
||||||
if order.routing_type == '表面工艺':
|
if order.routing_type == '表面工艺' and order.state not in ['cancel']:
|
||||||
if order.production_id.production_type == '自动化产线加工':
|
domain = [('purchase_type', '=', 'consignment'),
|
||||||
domain = [('programming_no', '=', order.production_id.programming_no)]
|
('origin', 'like', '%' + self.production_id.name + '%'),
|
||||||
else:
|
('state', '!=', 'cancel')]
|
||||||
domain = [('origin', '=', order.production_id.origin)]
|
|
||||||
production_programming = self.env['mrp.production'].search(domain, order='name asc')
|
|
||||||
production_list = [production.name for production in production_programming]
|
|
||||||
production_no_remanufacture = production_programming.filtered(lambda a: a.is_remanufacture is False)
|
|
||||||
# technology_design = self.env['sf.technology.design'].search(
|
|
||||||
# [('process_parameters_id', '=', order.surface_technics_parameters_id.id),
|
|
||||||
# ('production_id', '=', order.production_id.id)])
|
|
||||||
# if technology_design.is_auto is False:
|
|
||||||
# domain = [('origin', '=', order.production_id.name)]
|
|
||||||
# else:
|
|
||||||
domain = [('purchase_type', '=', 'consignment'),('origin', '=', ','.join(production_list))]
|
|
||||||
purchase = self.env['purchase.order'].search(domain)
|
purchase = self.env['purchase.order'].search(domain)
|
||||||
|
order.surface_technics_purchase_count = 0
|
||||||
if not purchase:
|
if not purchase:
|
||||||
order.surface_technics_purchase_count = 0
|
order.surface_technics_purchase_count = 0
|
||||||
for line in purchase.order_line:
|
for po in purchase:
|
||||||
if line.product_id.server_product_process_parameters_id == order.surface_technics_parameters_id:
|
if any(
|
||||||
if line.product_qty == len(production_no_remanufacture):
|
line.product_id and line.product_id.server_product_process_parameters_id == order.surface_technics_parameters_id
|
||||||
order.surface_technics_purchase_count = len(purchase)
|
for line in po.order_line):
|
||||||
|
order.surface_technics_purchase_count = 1
|
||||||
else:
|
else:
|
||||||
order.surface_technics_purchase_count = 0
|
order.surface_technics_purchase_count = 0
|
||||||
|
|
||||||
def action_view_surface_technics_purchase(self):
|
def action_view_surface_technics_purchase(self):
|
||||||
self.ensure_one()
|
self.ensure_one()
|
||||||
if self.routing_type == '表面工艺':
|
# if self.routing_type == '表面工艺':
|
||||||
if self.production_id.production_type == '自动化产线加工':
|
# if self.production_id.production_type == '自动化产线加工':
|
||||||
domain = [('programming_no', '=', self.production_id.programming_no)]
|
# domain = [('programming_no', '=', self.production_id.programming_no)]
|
||||||
else:
|
# else:
|
||||||
domain = [('origin', '=', self.production_id.origin)]
|
# domain = [('origin', '=', self.production_id.origin)]
|
||||||
production_programming = self.env['mrp.production'].search(domain, order='name asc')
|
# production_programming = self.env['mrp.production'].search(domain, order='name asc')
|
||||||
production_list = [production.name for production in production_programming]
|
# production_list = [production.name for production in production_programming]
|
||||||
|
# production_no_remanufacture = production_programming.filtered(lambda a: a.is_remanufacture is False)
|
||||||
# technology_design = self.env['sf.technology.design'].search(
|
# technology_design = self.env['sf.technology.design'].search(
|
||||||
# [('process_parameters_id', '=', self.surface_technics_parameters_id.id),
|
# [('process_parameters_id', '=', self.surface_technics_parameters_id.id),
|
||||||
# ('production_id', '=', self.production_id.id)])
|
# ('production_id', '=', self.production_id.id)])
|
||||||
# if technology_design.is_auto is False:
|
# if technology_design.is_auto is False:
|
||||||
# domain = [('origin', '=', self.production_id.name)]
|
# domain = [('origin', '=', self.production_id.name)]
|
||||||
# else:
|
# else:
|
||||||
domain = [('origin', '=', ','.join(production_list)), ('purchase_type', '=', 'consignment')]
|
purchase_orders_id = self._get_surface_technics_purchase_ids()
|
||||||
purchase_orders = self.env['purchase.order'].search(domain)
|
|
||||||
result = {
|
result = {
|
||||||
"type": "ir.actions.act_window",
|
"type": "ir.actions.act_window",
|
||||||
"res_model": "purchase.order",
|
"res_model": "purchase.order",
|
||||||
"res_id": purchase_orders.id,
|
"res_id": purchase_orders_id.id,
|
||||||
# "domain": [['id', 'in', self.purchase_id]],
|
# "domain": [['id', 'in', self.purchase_id]],
|
||||||
"name": _("Purchase Orders"),
|
"name": _("Purchase Orders"),
|
||||||
'view_mode': 'form',
|
'view_mode': 'form',
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
def _get_surface_technics_purchase_ids(self):
|
||||||
|
domain = [('origin', 'like', '%' + self.production_id.name + '%'), ('purchase_type', '=', 'consignment')]
|
||||||
|
purchase_orders = self.env['purchase.order'].search(domain)
|
||||||
|
purchase_orders_id = self.env['purchase.order']
|
||||||
|
for po in purchase_orders:
|
||||||
|
for line in po.order_line:
|
||||||
|
if line.product_id.server_product_process_parameters_id == self.surface_technics_parameters_id:
|
||||||
|
if line.product_qty == 1:
|
||||||
|
purchase_orders_id = line.order_id
|
||||||
|
return purchase_orders_id
|
||||||
|
|
||||||
supplier_id = fields.Many2one('res.partner', string='外协供应商')
|
supplier_id = fields.Many2one('res.partner', string='外协供应商')
|
||||||
equipment_id = fields.Many2one('maintenance.equipment', string='加工设备', tracking=True)
|
equipment_id = fields.Many2one('maintenance.equipment', string='加工设备', tracking=True)
|
||||||
# 保存名称
|
# 保存名称
|
||||||
@@ -1014,55 +1023,12 @@ class ResMrpWorkOrder(models.Model):
|
|||||||
'production_id.tool_state', 'production_id.schedule_state', 'sequence',
|
'production_id.tool_state', 'production_id.schedule_state', 'sequence',
|
||||||
'production_id.programming_state')
|
'production_id.programming_state')
|
||||||
def _compute_state(self):
|
def _compute_state(self):
|
||||||
# super()._compute_state()
|
|
||||||
for workorder in self:
|
|
||||||
if workorder.sequence != 1:
|
|
||||||
previous_workorder = self.env['mrp.workorder'].search(
|
|
||||||
[('production_id', '=', workorder.production_id.id),
|
|
||||||
('sequence', '=', workorder.sequence - 1)])
|
|
||||||
if workorder.state == 'pending':
|
|
||||||
if all([wo.state in ('done', 'cancel') for wo in workorder.blocked_by_workorder_ids]):
|
|
||||||
if workorder.production_id.reservation_state == 'assigned' and workorder.production_id.schedule_state == '已排':
|
|
||||||
if ((workorder.sequence == 1 and not workorder.blocked_by_workorder_ids)
|
|
||||||
or (workorder.blocked_by_workorder_ids.state in ('done', 'cancel')
|
|
||||||
and workorder.blocked_by_workorder_ids.test_results not in ['报废', '返工'])
|
|
||||||
or (previous_workorder.state in ('done', 'cancel')
|
|
||||||
and not workorder.blocked_by_workorder_ids
|
|
||||||
and previous_workorder.test_results not in ['报废', '返工'])
|
|
||||||
):
|
|
||||||
workorder.state = 'ready'
|
|
||||||
continue
|
|
||||||
if workorder.production_id.schedule_state == '未排' and workorder.state in ('waiting', 'ready'):
|
|
||||||
if workorder.sequence != 1:
|
|
||||||
workorder.state = 'pending'
|
|
||||||
continue
|
|
||||||
if workorder.state not in ('waiting', 'ready'):
|
|
||||||
continue
|
|
||||||
if workorder.state in (
|
|
||||||
'waiting') and workorder.sequence == 1 and workorder.production_id.schedule_state == '已排':
|
|
||||||
workorder.state = 'ready'
|
|
||||||
continue
|
|
||||||
if not all([wo.state in ('done', 'cancel') for wo in workorder.blocked_by_workorder_ids]):
|
|
||||||
workorder.state = 'pending'
|
|
||||||
if workorder.state in ['waiting']:
|
|
||||||
if previous_workorder.state == 'waiting':
|
|
||||||
workorder.state = 'pending'
|
|
||||||
if workorder.sequence == 1 and workorder.state == 'pending':
|
|
||||||
workorder.state = 'waiting'
|
|
||||||
continue
|
|
||||||
if workorder.production_id.reservation_state not in ('waiting', 'confirmed', 'assigned'):
|
|
||||||
continue
|
|
||||||
if workorder.production_id.reservation_state == 'assigned' and workorder.state == 'waiting' and workorder.production_id.schedule_state == '已排':
|
|
||||||
workorder.state = 'ready'
|
|
||||||
elif workorder.production_id.reservation_state != 'assigned' and workorder.state == 'ready':
|
|
||||||
workorder.state = 'waiting'
|
|
||||||
|
|
||||||
for workorder in self:
|
for workorder in self:
|
||||||
# 如果工单的工序没有进行排序则跳出循环
|
# 如果工单的工序没有进行排序则跳出循环
|
||||||
if workorder.production_id.workorder_ids.filtered(lambda wk: wk.sequence == 0):
|
if workorder.production_id.workorder_ids.filtered(lambda wk: wk.sequence == 0):
|
||||||
continue
|
continue
|
||||||
# ===== 对所有按序号排序的非[进行中、完成、返工、取消]状态的工单,除了第一条之外的工单状态都设置为[等待其他工单] =====
|
# ===== 对所有按序号排序的非[进行中、完成、返工、取消]状态的工单,除了第一条之外的工单状态都设置为[等待其他工单] =====
|
||||||
logging.info(workorder.state)
|
# logging.info(workorder.state)
|
||||||
work_ids = workorder.production_id.workorder_ids.filtered(
|
work_ids = workorder.production_id.workorder_ids.filtered(
|
||||||
lambda wk: wk.state not in ['done', 'rework', 'cancel'])
|
lambda wk: wk.state not in ['done', 'rework', 'cancel'])
|
||||||
if not work_ids:
|
if not work_ids:
|
||||||
@@ -1075,13 +1041,33 @@ class ResMrpWorkOrder(models.Model):
|
|||||||
if workorder.state != 'pending':
|
if workorder.state != 'pending':
|
||||||
workorder.state = 'pending'
|
workorder.state = 'pending'
|
||||||
continue
|
continue
|
||||||
|
# ================= 如果制造订单制造类型为【人工线下加工】==========================
|
||||||
|
if (workorder.production_id.production_type == '人工线下加工'
|
||||||
|
and workorder.production_id.schedule_state == '已排'
|
||||||
|
and len(workorder.production_id.picking_ids.filtered(
|
||||||
|
lambda w: w.state not in ['done', 'cancel'])) == 0):
|
||||||
|
if workorder.is_subcontract is True:
|
||||||
|
purchase_orders_id = self._get_surface_technics_purchase_ids()
|
||||||
|
if purchase_orders_id.state == 'purchase':
|
||||||
|
workorder.state = 'ready'
|
||||||
|
move_out = workorder.move_subcontract_workorder_ids[1]
|
||||||
|
for mo in move_out:
|
||||||
|
if mo.state != 'done':
|
||||||
|
mo.write({'state': 'assigned', 'production_id': False})
|
||||||
|
if not mo.move_line_ids:
|
||||||
|
self.env['stock.move.line'].create(
|
||||||
|
mo.get_move_line(workorder.production_id, workorder))
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
workorder.state = 'waiting'
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
workorder.state = 'ready'
|
||||||
|
continue
|
||||||
# ================= 如果制造订单刀具状态为[无效刀、缺刀] 或者 制造订单状态为[返工]==========================
|
# ================= 如果制造订单刀具状态为[无效刀、缺刀] 或者 制造订单状态为[返工]==========================
|
||||||
if (workorder.production_id.tool_state in ['1', '2'] or workorder.production_id.state == 'rework'
|
if (workorder.production_id.tool_state in ['1', '2'] or workorder.production_id.state == 'rework'
|
||||||
or workorder.production_id.schedule_state != '已排'
|
or workorder.production_id.schedule_state != '已排'
|
||||||
or len(
|
or workorder.production_id.reservation_state not in ['assigned']):
|
||||||
workorder.production_id.picking_ids.filtered(lambda w: w.state not in ['done', 'cancel'])) != 0
|
|
||||||
or workorder.production_id.workorder_ids.filtered(
|
|
||||||
lambda wk: wk.sequence == workorder.sequence - 1).test_results in ['报废', '返工']):
|
|
||||||
if workorder.state != 'waiting':
|
if workorder.state != 'waiting':
|
||||||
workorder.state = 'waiting'
|
workorder.state = 'waiting'
|
||||||
continue
|
continue
|
||||||
@@ -1100,83 +1086,28 @@ class ResMrpWorkOrder(models.Model):
|
|||||||
if workorder.is_subcontract is False:
|
if workorder.is_subcontract is False:
|
||||||
workorder.state = 'ready'
|
workorder.state = 'ready'
|
||||||
else:
|
else:
|
||||||
production_programming = self.env['mrp.production'].search(
|
if len(workorder.production_id.picking_ids.filtered(
|
||||||
[('origin', '=', self.production_id.origin)], order='name asc')
|
lambda w: w.state not in ['done',
|
||||||
production_no_remanufacture = production_programming.filtered(
|
'cancel'])) == 0 and workorder.production_id.programming_state == '已编程':
|
||||||
lambda a: a.is_remanufacture is False)
|
purchase_orders_id = self._get_surface_technics_purchase_ids()
|
||||||
production_list = [production.name for production in production_programming]
|
if purchase_orders_id:
|
||||||
purchase_orders = self.env['purchase.order'].search(
|
if purchase_orders_id.state == 'purchase':
|
||||||
[('origin', 'ilike', ','.join(production_list))])
|
|
||||||
for line in purchase_orders.order_line:
|
|
||||||
if (
|
|
||||||
line.product_id.server_product_process_parameters_id == workorder.surface_technics_parameters_id
|
|
||||||
and line.product_qty == len(production_no_remanufacture)):
|
|
||||||
if purchase_orders.state == 'purchase':
|
|
||||||
workorder.state = 'ready'
|
workorder.state = 'ready'
|
||||||
|
move_out = workorder.move_subcontract_workorder_ids[1]
|
||||||
|
for mo in move_out:
|
||||||
|
if mo.state != 'done':
|
||||||
|
mo.write({'state': 'assigned', 'production_id': False})
|
||||||
|
if not mo.move_line_ids:
|
||||||
|
self.env['stock.move.line'].create(
|
||||||
|
mo.get_move_line(workorder.production_id, workorder))
|
||||||
else:
|
else:
|
||||||
workorder.state = 'waiting'
|
workorder.state = 'waiting'
|
||||||
|
|
||||||
# re_work = self.env['mrp.workorder'].search([('production_id', '=', workorder.production_id.id),
|
|
||||||
# ('processing_panel', '=', workorder.processing_panel),
|
|
||||||
# ('is_rework', '=', True), ('state', 'in', ['done', 'rework'])])
|
|
||||||
# cnc_workorder = self.env['mrp.workorder'].search(
|
|
||||||
# [('production_id', '=', workorder.production_id.id),
|
|
||||||
# ('processing_panel', '=', workorder.processing_panel),
|
|
||||||
# ('routing_type', '=', 'CNC加工'), ('state', 'in', ['done', 'rework']),
|
|
||||||
# ('test_results', '=', '返工')])
|
|
||||||
# cnc_workorder_pending = self.env['mrp.workorder'].search(
|
|
||||||
# [('production_id', '=', workorder.production_id.id),
|
|
||||||
# ('processing_panel', '=', workorder.processing_panel),
|
|
||||||
# ('routing_type', '=', 'CNC加工'), ('state', 'in', ['pending'])])
|
|
||||||
# unclamp_workorder = self.env['mrp.workorder'].search(
|
|
||||||
# [('production_id', '=', workorder.production_id.id),
|
|
||||||
# ('sequence', '=', workorder.sequence - 1),
|
|
||||||
# ('state', 'in', ['done'])])
|
|
||||||
# if workorder.state not in ['cancel', 'progress', 'rework']:
|
|
||||||
# if workorder.production_id.state == 'rework':
|
|
||||||
# if workorder.routing_type == '装夹预调':
|
|
||||||
# # # 有返工工单
|
|
||||||
# # if re_work:
|
|
||||||
# # 新工单
|
|
||||||
# if workorder.is_rework is False:
|
|
||||||
# if (workorder.production_id.programming_state == '已编程'
|
|
||||||
# and workorder.production_id.is_rework is False):
|
|
||||||
# if re_work or cnc_workorder:
|
|
||||||
# workorder.state = 'ready'
|
|
||||||
# else:
|
|
||||||
# if workorder.production_id.is_rework is True:
|
|
||||||
# if re_work or cnc_workorder:
|
|
||||||
# workorder.state = 'waiting'
|
|
||||||
#
|
|
||||||
# elif workorder.routing_type == 'CNC加工':
|
|
||||||
# pre_workorder = self.env['mrp.workorder'].search(
|
|
||||||
# [('production_id', '=', workorder.production_id.id),
|
|
||||||
# ('processing_panel', '=', workorder.processing_panel),
|
|
||||||
# ('routing_type', '=', '装夹预调'), ('state', '=', 'done')])
|
|
||||||
# if pre_workorder:
|
|
||||||
# if re_work:
|
|
||||||
# workorder.state = 'waiting'
|
|
||||||
# elif workorder.routing_type == '解除装夹':
|
|
||||||
# if cnc_workorder:
|
|
||||||
# if not cnc_workorder_pending or unclamp_workorder.test_results == '报废':
|
|
||||||
# workorder.state = 'waiting'
|
|
||||||
# # else:
|
|
||||||
# # if workorder.production_id.is_rework is True:
|
|
||||||
# # workorder.state = 'waiting'
|
|
||||||
# elif workorder.production_id.state == 'progress':
|
|
||||||
# if (workorder.routing_type == '装夹预调' and workorder.production_id.programming_state == '已编程'
|
|
||||||
# and workorder.is_rework is False and workorder.state not in ['done', 'rework', 'cancel']):
|
|
||||||
# if workorder.production_id.is_rework is False:
|
|
||||||
# if re_work or cnc_workorder or unclamp_workorder:
|
|
||||||
# workorder.state = 'ready'
|
|
||||||
# # if (re_work or cnc_workorder) and workorder.production_id.is_rework is False:
|
|
||||||
# # workorder.state = 'ready'
|
|
||||||
# elif workorder.production_id.state == 'scrap':
|
|
||||||
# if workorder.routing_type == '解除装夹' and unclamp_workorder.test_results == '报废':
|
|
||||||
# workorder.state = 'waiting'
|
|
||||||
|
|
||||||
# 重写工单开始按钮方法
|
# 重写工单开始按钮方法
|
||||||
def button_start(self):
|
def button_start(self):
|
||||||
|
# 判断工单状态是否为等待组件
|
||||||
|
if self.state in ['waiting', 'pending']:
|
||||||
|
raise UserError('制造订单【%s】缺少组件信息!' % self.production_id.name)
|
||||||
|
|
||||||
if self.routing_type == 'CNC加工':
|
if self.routing_type == 'CNC加工':
|
||||||
self.env['sf.production.plan'].sudo().search([('name', '=', self.production_id.name)]).write({
|
self.env['sf.production.plan'].sudo().search([('name', '=', self.production_id.name)]).write({
|
||||||
'state': 'processing',
|
'state': 'processing',
|
||||||
@@ -1187,19 +1118,23 @@ class ResMrpWorkOrder(models.Model):
|
|||||||
# 判断是否有坯料的序列号信息
|
# 判断是否有坯料的序列号信息
|
||||||
boolean = False
|
boolean = False
|
||||||
if self.production_id.move_raw_ids:
|
if self.production_id.move_raw_ids:
|
||||||
if self.production_id.move_raw_ids[0].move_line_ids:
|
if self.production_id.move_raw_ids[0].product_id.categ_type == '坯料':
|
||||||
if self.production_id.move_raw_ids[0].move_line_ids:
|
if self.production_id.move_raw_ids[0].move_line_ids:
|
||||||
if self.production_id.move_raw_ids[0].move_line_ids[0].lot_id.name:
|
if self.production_id.move_raw_ids[0].move_line_ids:
|
||||||
boolean = True
|
if self.production_id.move_raw_ids[0].move_line_ids[0].lot_id.name:
|
||||||
|
boolean = True
|
||||||
|
else:
|
||||||
|
boolean = True
|
||||||
if not boolean:
|
if not boolean:
|
||||||
raise UserError('制造订单【%s】缺少组件的序列号信息!' % self.production_id.name)
|
raise UserError('制造订单【%s】缺少组件的序列号信息!' % self.production_id.name)
|
||||||
self.pro_code = self.production_id.move_raw_ids[0].move_line_ids[0].lot_id.name
|
self.pro_code = self.production_id.move_raw_ids[0].move_line_ids[0].lot_id.name
|
||||||
# cnc校验
|
# cnc校验
|
||||||
cnc_workorder = self.search(
|
if self.production_id.production_type == '自动化产线加工':
|
||||||
[('production_id', '=', self.production_id.id), ('routing_type', '=', 'CNC加工')],
|
cnc_workorder = self.search(
|
||||||
limit=1, order='id asc')
|
[('production_id', '=', self.production_id.id), ('routing_type', '=', 'CNC加工')],
|
||||||
if not cnc_workorder.cnc_ids:
|
limit=1, order='id asc')
|
||||||
raise UserError(_('该制造订单还未下发CNC程序,请稍后再试'))
|
# if not cnc_workorder.cnc_ids:
|
||||||
|
# raise UserError(_('该制造订单还未下发CNC程序,请稍后再试'))
|
||||||
else:
|
else:
|
||||||
if self.production_id.tool_state in ['1', '2']:
|
if self.production_id.tool_state in ['1', '2']:
|
||||||
if self.production_id.tool_state == '1':
|
if self.production_id.tool_state == '1':
|
||||||
@@ -1216,15 +1151,33 @@ class ResMrpWorkOrder(models.Model):
|
|||||||
# 表面工艺外协出库单
|
# 表面工艺外协出库单
|
||||||
if self.routing_type == '表面工艺':
|
if self.routing_type == '表面工艺':
|
||||||
if self.is_subcontract is True:
|
if self.is_subcontract is True:
|
||||||
move_out = self.env['stock.move'].search(
|
move_out = self.move_subcontract_workorder_ids[1]
|
||||||
[('location_id', '=', self.env['stock.location'].search(
|
# move_out = self.env['stock.move'].search(
|
||||||
[('barcode', 'ilike', 'WH-PREPRODUCTION')]).id),
|
# [('location_id', '=', self.env['stock.location'].search(
|
||||||
('location_dest_id', '=', self.env['stock.location'].search(
|
# [('barcode', 'ilike', 'WH-PREPRODUCTION')]).id),
|
||||||
[('barcode', 'ilike', 'VL-SPOC')]).id),
|
# ('location_dest_id', '=', self.env['stock.location'].search(
|
||||||
('origin', '=', self.production_id.name)])
|
# [('barcode', 'ilike', 'VL-SPOC')]).id),
|
||||||
if move_out.state != 'done':
|
# ('origin', '=', self.production_id.name), ('state', 'not in', ['cancel', 'done'])])
|
||||||
move_out.write({'state': 'assigned', 'production_id': False})
|
for mo in move_out:
|
||||||
self.env['stock.move.line'].create(move_out.get_move_line(self.production_id, self))
|
if mo.state != 'done':
|
||||||
|
mo.write({'state': 'assigned', 'production_id': False})
|
||||||
|
if not mo.move_line_ids:
|
||||||
|
self.env['stock.move.line'].create(mo.get_move_line(self.production_id, self))
|
||||||
|
# product_qty = mo.product_uom._compute_quantity(
|
||||||
|
# mo.product_uom_qty, mo.product_id.uom_id, rounding_method='HALF-UP')
|
||||||
|
# available_quantity = self.env['stock.quant']._get_available_quantity(
|
||||||
|
# mo.product_id,
|
||||||
|
# mo.location_id,
|
||||||
|
# lot_id=mo.move_line_ids.lot_id,
|
||||||
|
# strict=False,
|
||||||
|
# )
|
||||||
|
# mo._update_reserved_quantity(
|
||||||
|
# product_qty,
|
||||||
|
# available_quantity,
|
||||||
|
# mo.location_id,
|
||||||
|
# lot_id=mo.move_line_ids.lot_id,
|
||||||
|
# strict=False,
|
||||||
|
# )
|
||||||
|
|
||||||
# move_out._action_assign()
|
# move_out._action_assign()
|
||||||
if self.state == 'waiting' or self.state == 'ready' or self.state == 'progress':
|
if self.state == 'waiting' or self.state == 'ready' or self.state == 'progress':
|
||||||
@@ -1329,6 +1282,13 @@ class ResMrpWorkOrder(models.Model):
|
|||||||
picks = record.picking_ids.filtered(lambda p: p.state not in ('done'))
|
picks = record.picking_ids.filtered(lambda p: p.state not in ('done'))
|
||||||
if picks:
|
if picks:
|
||||||
raise UserError('请先完成该工单的工艺外协再进行操作')
|
raise UserError('请先完成该工单的工艺外协再进行操作')
|
||||||
|
# 表面工艺外协,最后一张工单
|
||||||
|
workorders = self.production_id.workorder_ids
|
||||||
|
subcontract_workorders = workorders.filtered(lambda wo: wo.is_subcontract == True and wo.state != 'cancel').sorted('sequence')
|
||||||
|
if self == subcontract_workorders[-1]:
|
||||||
|
# 给下一个库存移动就绪
|
||||||
|
self.move_subcontract_workorder_ids[0].move_dest_ids._action_done()
|
||||||
|
# self.production_id.button_mark_done()
|
||||||
tem_date_planned_finished = record.date_planned_finished
|
tem_date_planned_finished = record.date_planned_finished
|
||||||
tem_date_finished = record.date_finished
|
tem_date_finished = record.date_finished
|
||||||
logging.info('routing_type:%s' % record.routing_type)
|
logging.info('routing_type:%s' % record.routing_type)
|
||||||
@@ -1347,41 +1307,48 @@ class ResMrpWorkOrder(models.Model):
|
|||||||
# record.recreateManufacturingOrWorkerOrder()
|
# record.recreateManufacturingOrWorkerOrder()
|
||||||
is_production_id = False
|
is_production_id = False
|
||||||
rework_workorder = record.production_id.workorder_ids.filtered(lambda p: p.state == 'rework')
|
rework_workorder = record.production_id.workorder_ids.filtered(lambda p: p.state == 'rework')
|
||||||
done_workorder = record.production_id.workorder_ids.filtered(lambda p1: p1.state == 'done')
|
done_workorder = record.production_id.workorder_ids.filtered(lambda p1: p1.state in ['done'])
|
||||||
if (len(rework_workorder) + len(done_workorder) == len(record.production_id.workorder_ids)) or (
|
if (len(rework_workorder) + len(done_workorder) == len(
|
||||||
len(done_workorder) == len(record.production_id.workorder_ids)):
|
record.production_id.workorder_ids.filtered(lambda wo: wo.state != 'cancel'))) or (
|
||||||
|
len(done_workorder) == len(
|
||||||
|
record.production_id.workorder_ids.filtered(lambda wo: wo.state != 'cancel'))):
|
||||||
is_production_id = True
|
is_production_id = True
|
||||||
if record.routing_type in ['解除装夹'] or (
|
if record.routing_type in ['解除装夹'] or (
|
||||||
record.is_rework is True and record.routing_type in ['装夹预调']) or (
|
record.is_rework is True and record.routing_type in ['装夹预调']):
|
||||||
record.test_results in ['返工', '报废'] and record.routing_type in ['CNC加工']):
|
|
||||||
for workorder in record.production_id.workorder_ids:
|
for workorder in record.production_id.workorder_ids:
|
||||||
if workorder.processing_panel == record.processing_panel:
|
if workorder.processing_panel == record.processing_panel:
|
||||||
rfid_code = workorder.rfid_code
|
rfid_code = workorder.rfid_code
|
||||||
workorder.write({'rfid_code_old': rfid_code,
|
workorder.write({'rfid_code_old': rfid_code,
|
||||||
'rfid_code': False})
|
'rfid_code': False})
|
||||||
|
self.env['stock.lot'].sudo().search([('rfid', '=', rfid_code)]).write(
|
||||||
|
{'tool_material_status': '可用'})
|
||||||
if workorder.rfid_code:
|
if workorder.rfid_code:
|
||||||
raise ValidationError(f'【{workorder.name}】工单解绑失败,请重新点击完成按钮!!!')
|
raise ValidationError(f'【{workorder.name}】工单解绑失败,请重新点击完成按钮!!!')
|
||||||
# workorder.rfid_code_old = rfid_code
|
# workorder.rfid_code_old = rfid_code
|
||||||
# workorder.rfid_code = False
|
# workorder.rfid_code = False
|
||||||
logging.info('workorder.rfid_code:%s' % workorder.rfid_code)
|
logging.info('workorder.rfid_code:%s' % workorder.rfid_code)
|
||||||
if is_production_id is True and record.routing_type in ['解除装夹', '表面工艺']:
|
# if is_production_id is True and record.routing_type in ['解除装夹', '表面工艺', '切割']:
|
||||||
|
if is_production_id is True:
|
||||||
logging.info('product_qty:%s' % record.production_id.product_qty)
|
logging.info('product_qty:%s' % record.production_id.product_qty)
|
||||||
for move_raw_id in record.production_id.move_raw_ids:
|
for move_raw_id in record.production_id.move_raw_ids:
|
||||||
move_raw_id.quantity_done = move_raw_id.product_uom_qty
|
move_raw_id.quantity_done = move_raw_id.product_uom_qty
|
||||||
record.process_state = '已完工'
|
record.process_state = '已完工'
|
||||||
record.production_id.process_state = '已完工'
|
record.production_id.process_state = '已完工'
|
||||||
if record.routing_type in ['表面工艺']:
|
# if record.routing_type in ['表面工艺']:
|
||||||
raw_move = self.env['stock.move'].sudo().search(
|
# raw_move = self.env['stock.move'].sudo().search(
|
||||||
[('origin', '=', record.production_id.name),
|
# [('origin', '=', record.production_id.name),
|
||||||
('procure_method', 'in', ['make_to_order', 'make_to_stock']),
|
# ('procure_method', 'in', ['make_to_order', 'make_to_stock']),
|
||||||
('state', '!=', 'done')])
|
# ('state', '!=', 'done')])
|
||||||
if raw_move:
|
# if raw_move:
|
||||||
raw_move.write({'state': 'done'})
|
# raw_move.write({'state': 'done'})
|
||||||
record.production_id.button_mark_done1()
|
record.production_id.button_mark_done1()
|
||||||
# record.production_id.state = 'done'
|
# record.production_id.state = 'done'
|
||||||
|
|
||||||
# 解绑托盘
|
# 解绑托盘
|
||||||
def unbind_tray(self):
|
def unbind_tray(self):
|
||||||
|
for item in self:
|
||||||
|
self.env['stock.lot'].sudo().search([('rfid', '=', item.rfid_code)]).write(
|
||||||
|
{'tool_material_status': '可用'})
|
||||||
self.production_id.workorder_ids.write({
|
self.production_id.workorder_ids.write({
|
||||||
'rfid_code': False,
|
'rfid_code': False,
|
||||||
'tray_serial_number': False,
|
'tray_serial_number': False,
|
||||||
@@ -1497,6 +1464,8 @@ class ResMrpWorkOrder(models.Model):
|
|||||||
# 'default_feeder_station_start_id': feeder_station_start_id,
|
# 'default_feeder_station_start_id': feeder_station_start_id,
|
||||||
}}
|
}}
|
||||||
|
|
||||||
|
move_subcontract_workorder_ids = fields.One2many('stock.move', 'subcontract_workorder_id', string='组件')
|
||||||
|
|
||||||
|
|
||||||
class CNCprocessing(models.Model):
|
class CNCprocessing(models.Model):
|
||||||
_name = 'sf.cnc.processing'
|
_name = 'sf.cnc.processing'
|
||||||
@@ -1705,6 +1674,7 @@ class SfWorkOrderBarcodes(models.Model):
|
|||||||
if workorder_rfid:
|
if workorder_rfid:
|
||||||
for item in workorder_rfid:
|
for item in workorder_rfid:
|
||||||
item.write({'rfid_code': barcode})
|
item.write({'rfid_code': barcode})
|
||||||
|
lot.sudo().write({'tool_material_status': '在用'})
|
||||||
logging.info("Rfid[%s]绑定成功!!!" % barcode)
|
logging.info("Rfid[%s]绑定成功!!!" % barcode)
|
||||||
else:
|
else:
|
||||||
raise UserError('该Rfid【%s】绑定的是【%s】, 不是托盘!!!' % (barcode, lot.product_id.name))
|
raise UserError('该Rfid【%s】绑定的是【%s】, 不是托盘!!!' % (barcode, lot.product_id.name))
|
||||||
|
|||||||
@@ -774,11 +774,10 @@ class ResProductMo(models.Model):
|
|||||||
|
|
||||||
# bfm下单
|
# bfm下单
|
||||||
manual_quotation = fields.Boolean('人工编程', default=False, readonly=True)
|
manual_quotation = fields.Boolean('人工编程', default=False, readonly=True)
|
||||||
part_number = fields.Char(string='零件图号', readonly=True)
|
|
||||||
machining_drawings = fields.Binary('2D加工图纸', readonly=True)
|
machining_drawings = fields.Binary('2D加工图纸', readonly=True)
|
||||||
quality_standard = fields.Binary('质检标准', readonly=True)
|
quality_standard = fields.Binary('质检标准', readonly=True)
|
||||||
part_name = fields.Char(string='零件名称', readonly=True)
|
part_name = fields.Char(string='零件名称', readonly=True)
|
||||||
|
part_number = fields.Char(string='零件图号', readonly=True)
|
||||||
@api.constrains('tool_length')
|
@api.constrains('tool_length')
|
||||||
def _check_tool_length_size(self):
|
def _check_tool_length_size(self):
|
||||||
if self.tool_length > 1000000:
|
if self.tool_length > 1000000:
|
||||||
@@ -859,12 +858,12 @@ class ResProductMo(models.Model):
|
|||||||
raise UserError('请先配置模型类型内的坯料冗余')
|
raise UserError('请先配置模型类型内的坯料冗余')
|
||||||
vals = {
|
vals = {
|
||||||
'name': '%s-%s-%s' % ('P', order_id.name, i),
|
'name': '%s-%s-%s' % ('P', order_id.name, i),
|
||||||
'model_long': item['model_long'] + embryo_redundancy_id.long,
|
'model_long': self.format_float(item['model_long'] + embryo_redundancy_id.long),
|
||||||
'model_width': item['model_width'] + embryo_redundancy_id.width,
|
'model_width': self.format_float(item['model_width'] + embryo_redundancy_id.width),
|
||||||
'model_height': item['model_height'] + embryo_redundancy_id.height,
|
'model_height': self.format_float(item['model_height'] + embryo_redundancy_id.height),
|
||||||
'model_volume': (item['model_long'] + embryo_redundancy_id.long) * (
|
'model_volume': self.format_float((item['model_long'] + embryo_redundancy_id.long) * (
|
||||||
item['model_width'] + embryo_redundancy_id.width) * (
|
item['model_width'] + embryo_redundancy_id.width) * (
|
||||||
item['model_height'] + embryo_redundancy_id.height),
|
item['model_height'] + embryo_redundancy_id.height)),
|
||||||
'product_model_type_id': model_type.id,
|
'product_model_type_id': model_type.id,
|
||||||
'model_processing_panel': item['processing_panel_detail'],
|
'model_processing_panel': item['processing_panel_detail'],
|
||||||
'model_machining_precision': item['model_machining_precision'],
|
'model_machining_precision': item['model_machining_precision'],
|
||||||
@@ -874,8 +873,8 @@ class ResProductMo(models.Model):
|
|||||||
'height': item['model_height'],
|
'height': item['model_height'],
|
||||||
'volume': item['model_long'] * item['model_width'] * item['model_height'],
|
'volume': item['model_long'] * item['model_width'] * item['model_height'],
|
||||||
'model_file': '' if not item['model_file'] else base64.b64decode(item['model_file']),
|
'model_file': '' if not item['model_file'] else base64.b64decode(item['model_file']),
|
||||||
'model_name': attachment.name,
|
'model_name': attachment.name if attachment else None,
|
||||||
'upload_model_file': [(6, 0, [attachment.id])],
|
'upload_model_file': [(6, 0, [attachment.id])] if attachment else None,
|
||||||
'list_price': item['price'],
|
'list_price': item['price'],
|
||||||
'materials_id': self.env['sf.production.materials'].search(
|
'materials_id': self.env['sf.production.materials'].search(
|
||||||
[('materials_no', '=', item['texture_code'])]).id,
|
[('materials_no', '=', item['texture_code'])]).id,
|
||||||
@@ -892,7 +891,7 @@ class ResProductMo(models.Model):
|
|||||||
'machining_drawings': '' if not item['machining_drawings'] else base64.b64decode(
|
'machining_drawings': '' if not item['machining_drawings'] else base64.b64decode(
|
||||||
item['machining_drawings']),
|
item['machining_drawings']),
|
||||||
'quality_standard': '' if not item['quality_standard'] else base64.b64decode(item['quality_standard']),
|
'quality_standard': '' if not item['quality_standard'] else base64.b64decode(item['quality_standard']),
|
||||||
'part_name': item['part_name'],
|
'part_name': item.get('part_name') or '',
|
||||||
}
|
}
|
||||||
tax_id = self.env['account.tax'].sudo().search(
|
tax_id = self.env['account.tax'].sudo().search(
|
||||||
[('type_tax_use', '=', 'sale'), ('amount', '=', item.get('tax')), ('price_include', '=', 'True')])
|
[('type_tax_use', '=', 'sale'), ('amount', '=', item.get('tax')), ('price_include', '=', 'True')])
|
||||||
@@ -907,6 +906,20 @@ class ResProductMo(models.Model):
|
|||||||
self.attachment_update(item['quality_standard_name'], copy_product_id.product_tmpl_id.id,
|
self.attachment_update(item['quality_standard_name'], copy_product_id.product_tmpl_id.id,
|
||||||
'quality_standard', item['quality_standard_mimetype'])
|
'quality_standard', item['quality_standard_mimetype'])
|
||||||
return copy_product_id
|
return copy_product_id
|
||||||
|
|
||||||
|
def format_float(self, value):
|
||||||
|
# 将浮点数转换为字符串
|
||||||
|
value_str = str(value)
|
||||||
|
# 检查小数点的位置
|
||||||
|
if '.' in value_str:
|
||||||
|
# 获取小数部分
|
||||||
|
decimal_part = value_str.split('.')[1]
|
||||||
|
# 判断小数位数是否超过2位
|
||||||
|
if len(decimal_part) > 2:
|
||||||
|
# 超过2位则保留2位小数
|
||||||
|
return "{:.2f}".format(value)
|
||||||
|
# 否则保持原来的位数
|
||||||
|
return float(value_str)
|
||||||
|
|
||||||
def _get_ids(self, param):
|
def _get_ids(self, param):
|
||||||
type_ids = []
|
type_ids = []
|
||||||
@@ -924,6 +937,8 @@ class ResProductMo(models.Model):
|
|||||||
return [(6, 0, process_parameters_ids)]
|
return [(6, 0, process_parameters_ids)]
|
||||||
|
|
||||||
def attachment_create(self, name, data):
|
def attachment_create(self, name, data):
|
||||||
|
if not data:
|
||||||
|
return None
|
||||||
attachment = self.env['ir.attachment'].create({
|
attachment = self.env['ir.attachment'].create({
|
||||||
'datas': base64.b64decode(data),
|
'datas': base64.b64decode(data),
|
||||||
'type': 'binary',
|
'type': 'binary',
|
||||||
|
|||||||
107
sf_manufacturing/models/purchase_order.py
Normal file
107
sf_manufacturing/models/purchase_order.py
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||||
|
|
||||||
|
from collections import defaultdict
|
||||||
|
|
||||||
|
from odoo import api, fields, models, _
|
||||||
|
from odoo.tools import OrderedSet
|
||||||
|
|
||||||
|
|
||||||
|
# _get_surface_technics_purchase_ids
|
||||||
|
class PurchaseOrder(models.Model):
|
||||||
|
_inherit = 'purchase.order'
|
||||||
|
|
||||||
|
production_count = fields.Integer(
|
||||||
|
"关联制造订单",
|
||||||
|
compute='_compute_workorder_count',
|
||||||
|
)
|
||||||
|
|
||||||
|
def action_view_production(self):
|
||||||
|
origins = [order.name for order in self.picking_ids]
|
||||||
|
production_id = self.env['mrp.production'].search([('origin', 'in', origins)])
|
||||||
|
if not production_id:
|
||||||
|
return
|
||||||
|
action = {
|
||||||
|
'res_model': 'mrp.production',
|
||||||
|
'type': 'ir.actions.act_window',
|
||||||
|
}
|
||||||
|
if len(production_id) == 1:
|
||||||
|
action.update({
|
||||||
|
'view_mode': 'form',
|
||||||
|
'res_id': production_id.id,
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
action.update({
|
||||||
|
'name': _("制造订单列表"),
|
||||||
|
'domain': [('id', 'in', production_id.ids)],
|
||||||
|
'view_mode': 'tree,form',
|
||||||
|
})
|
||||||
|
return action
|
||||||
|
|
||||||
|
def _compute_workorder_count(self):
|
||||||
|
for purchase in self:
|
||||||
|
origins = [order.name for order in purchase.picking_ids]
|
||||||
|
production_id = self.env['mrp.production'].search([('origin', 'in', origins)])
|
||||||
|
purchase.production_count = len(production_id)
|
||||||
|
|
||||||
|
# def button_confirm(self):
|
||||||
|
# super().button_confirm()
|
||||||
|
# workorders = self.env['mrp.workorder'].search([('purchase_id', '=', self.id), ('state', '!=', 'cancel')])
|
||||||
|
# for workorder in workorders:
|
||||||
|
# if workorder.routing_type == '表面工艺' and workorder.is_subcontract is True:
|
||||||
|
# move_out = workorder.move_subcontract_workorder_ids[1]
|
||||||
|
# for mo in move_out:
|
||||||
|
# if mo.state != 'done':
|
||||||
|
# mo.write({'state': 'assigned', 'production_id': False})
|
||||||
|
# if not mo.move_line_ids:
|
||||||
|
# self.env['stock.move.line'].create(mo.get_move_line(workorder.production_id, workorder))
|
||||||
|
# return True
|
||||||
|
|
||||||
|
origin_sale_id = fields.Many2one('sale.order', string='销售订单号', store=True, compute='_compute_origin_sale_id')
|
||||||
|
origin_sale_ids = fields.Many2many('sale.order', string='销售订单号(多个)', store=True,
|
||||||
|
compute='_compute_origin_sale_id')
|
||||||
|
|
||||||
|
@api.depends('origin')
|
||||||
|
def _compute_origin_sale_id(self):
|
||||||
|
for purchase in self:
|
||||||
|
if not purchase.origin:
|
||||||
|
continue
|
||||||
|
elif 'MO' in purchase.origin:
|
||||||
|
mp_name_list = [name.strip() for name in purchase['origin'].split(',')]
|
||||||
|
os_ids = list({mp_id.sale_order_id.id for mp_id in self.env['mrp.production'].sudo().search([
|
||||||
|
('name', 'in', mp_name_list)])})
|
||||||
|
if len(os_ids) == 1:
|
||||||
|
purchase.origin_sale_id = os_ids[0]
|
||||||
|
elif len(os_ids) >= 2:
|
||||||
|
purchase.origin_sale_ids = os_ids
|
||||||
|
elif 'S' in purchase.origin:
|
||||||
|
os_name_list = [name.strip() for name in purchase['origin'].split(',')]
|
||||||
|
os_ids = self.env['sale.order'].sudo().search([('name', 'in', os_name_list)])
|
||||||
|
if len(os_ids) == 1:
|
||||||
|
purchase.origin_sale_id = os_ids.id
|
||||||
|
elif len(os_ids) >= 2:
|
||||||
|
purchase.origin_sale_ids = os_ids.ids
|
||||||
|
elif 'IN' in purchase.origin:
|
||||||
|
sp_name_list = [name.strip() for name in purchase['origin'].split(',')]
|
||||||
|
os_ids = list({sp_id.sale_order_id.id for sp_id in self.env['stock.picking'].sudo().search([
|
||||||
|
('name', 'in', sp_name_list)])})
|
||||||
|
if len(os_ids) == 1:
|
||||||
|
purchase.origin_sale_id = os_ids[0]
|
||||||
|
elif len(os_ids) >= 2:
|
||||||
|
purchase.origin_sale_ids = os_ids
|
||||||
|
|
||||||
|
|
||||||
|
class PurchaseOrderLine(models.Model):
|
||||||
|
_inherit = 'purchase.order.line'
|
||||||
|
part_number = fields.Char('零件图号', related='product_id.part_number', readonly=True)
|
||||||
|
related_product = fields.Many2one('product.product', compute='_compute_related_product', string='关联产品',
|
||||||
|
help='经此产品工艺加工成的成品')
|
||||||
|
|
||||||
|
@api.depends('order_id.origin')
|
||||||
|
def _compute_related_product(self):
|
||||||
|
for record in self:
|
||||||
|
if record.product_id.detailed_type:
|
||||||
|
production_id = self.env['mrp.production'].search([('name', '=', record.order_id.origin)])
|
||||||
|
record.related_product = production_id.product_id if production_id else False
|
||||||
|
else:
|
||||||
|
record.related_product = False
|
||||||
@@ -13,6 +13,9 @@ class SaleOrder(models.Model):
|
|||||||
('sent', "报价已发送"),
|
('sent', "报价已发送"),
|
||||||
('supply method', "供货方式待确认"),
|
('supply method', "供货方式待确认"),
|
||||||
('sale', "销售订单"),
|
('sale', "销售订单"),
|
||||||
|
('processing', "加工中"),
|
||||||
|
('physical_distribution', "物流中"),
|
||||||
|
('delivered', "已交付"),
|
||||||
('done', "已锁定"),
|
('done', "已锁定"),
|
||||||
('cancel', "已取消"),
|
('cancel', "已取消"),
|
||||||
])
|
])
|
||||||
@@ -43,6 +46,8 @@ class SaleOrder(models.Model):
|
|||||||
|
|
||||||
# 复制成品模板上的属性
|
# 复制成品模板上的属性
|
||||||
line.product_id.product_tmpl_id.copy_template(product_template_id)
|
line.product_id.product_tmpl_id.copy_template(product_template_id)
|
||||||
|
# 将模板上的single_manufacturing属性复制到成品上
|
||||||
|
line.product_id.single_manufacturing = product_template_id.single_manufacturing
|
||||||
|
|
||||||
order_id = self
|
order_id = self
|
||||||
product = line.product_id
|
product = line.product_id
|
||||||
@@ -147,7 +152,7 @@ class SaleOrder(models.Model):
|
|||||||
|
|
||||||
class SaleOrderLine(models.Model):
|
class SaleOrderLine(models.Model):
|
||||||
_inherit = 'sale.order.line'
|
_inherit = 'sale.order.line'
|
||||||
|
part_number = fields.Char('零件图号', related='product_id.part_number', readonly=True)
|
||||||
# 供货方式
|
# 供货方式
|
||||||
supply_method = fields.Selection([
|
supply_method = fields.Selection([
|
||||||
('automation', "自动化产线加工"),
|
('automation', "自动化产线加工"),
|
||||||
@@ -155,3 +160,10 @@ class SaleOrderLine(models.Model):
|
|||||||
('purchase', "外购"),
|
('purchase', "外购"),
|
||||||
('outsourcing', "委外加工"),
|
('outsourcing', "委外加工"),
|
||||||
], string='供货方式')
|
], string='供货方式')
|
||||||
|
|
||||||
|
def write(self, vals):
|
||||||
|
if 'supply_method' in vals:
|
||||||
|
for line in self:
|
||||||
|
if vals['supply_method'] == 'automation' and line.manual_quotation:
|
||||||
|
raise UserError('当前(%s)产品为人工编程产品,不能选择自动化产线加工' % ','.join(line.mapped('product_id.name')))
|
||||||
|
return super(SaleOrderLine, self).write(vals)
|
||||||
@@ -10,8 +10,14 @@ class SfProductionProcessParameter(models.Model):
|
|||||||
@api.model
|
@api.model
|
||||||
def _name_search(self, name, args=None, operator='ilike', limit=100, name_get_uid=None):
|
def _name_search(self, name, args=None, operator='ilike', limit=100, name_get_uid=None):
|
||||||
if self._context.get('route_id'):
|
if self._context.get('route_id'):
|
||||||
|
parameter = []
|
||||||
routing = self.env['mrp.routing.workcenter'].search([('id', '=', self._context.get('route_id'))])
|
routing = self.env['mrp.routing.workcenter'].search([('id', '=', self._context.get('route_id'))])
|
||||||
domain = [('process_id', '=', routing.surface_technics_id.id)]
|
technology_design = self.env['sf.technology.design'].search(
|
||||||
|
[('production_id', '=', self._context.get('production_id')), ('routing_tag', '=', 'special'),
|
||||||
|
('route_id', '=', self._context.get('route_id'))])
|
||||||
|
for t in technology_design:
|
||||||
|
if t.process_parameters_id:
|
||||||
|
parameter.append(t.process_parameters_id.id)
|
||||||
|
domain = [('process_id', '=', routing.surface_technics_id.id), ('id', 'not in', parameter)]
|
||||||
return self._search(domain, limit=limit, access_rights_uid=name_get_uid)
|
return self._search(domain, limit=limit, access_rights_uid=name_get_uid)
|
||||||
return super()._name_search(name, args, operator, limit, name_get_uid)
|
return super()._name_search(name, args, operator, limit, name_get_uid)
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from odoo import fields, models
|
from collections import Counter
|
||||||
|
|
||||||
|
from odoo import fields, models, api, _
|
||||||
|
from odoo.exceptions import ValidationError
|
||||||
|
|
||||||
|
|
||||||
class sf_technology_design(models.Model):
|
class sf_technology_design(models.Model):
|
||||||
_name = 'sf.technology.design'
|
_name = 'sf.technology.design'
|
||||||
_description = "工艺设计"
|
_description = "工艺设计"
|
||||||
|
group_uniq_id = fields.Integer('同一制造订单唯一id',default=0)
|
||||||
sequence = fields.Integer('序号')
|
sequence = fields.Integer('序号')
|
||||||
route_id = fields.Many2one('mrp.routing.workcenter', '工序')
|
route_id = fields.Many2one('mrp.routing.workcenter', '工序')
|
||||||
process_parameters_id = fields.Many2one('sf.production.process.parameter', string='表面工艺参数')
|
process_parameters_id = fields.Many2one('sf.production.process.parameter', string='表面工艺参数')
|
||||||
@@ -16,6 +19,11 @@ class sf_technology_design(models.Model):
|
|||||||
is_auto = fields.Boolean('是否自动生成', default=False)
|
is_auto = fields.Boolean('是否自动生成', default=False)
|
||||||
active = fields.Boolean('有效', default=True)
|
active = fields.Boolean('有效', default=True)
|
||||||
|
|
||||||
|
# @api.depends('production_id')
|
||||||
|
# def _compute_group_uniq_id(self):
|
||||||
|
# for record in self:
|
||||||
|
|
||||||
|
|
||||||
def json_technology_design_str(self, k, route, i, process_parameter):
|
def json_technology_design_str(self, k, route, i, process_parameter):
|
||||||
workorders_values_str = [0, '', {
|
workorders_values_str = [0, '', {
|
||||||
'route_id': route.id if route.routing_type in ['表面工艺'] else route.route_workcenter_id.id,
|
'route_id': route.id if route.routing_type in ['表面工艺'] else route.route_workcenter_id.id,
|
||||||
@@ -27,5 +35,75 @@ class sf_technology_design(models.Model):
|
|||||||
'is_auto': True}]
|
'is_auto': True}]
|
||||||
return workorders_values_str
|
return workorders_values_str
|
||||||
|
|
||||||
|
def write(self, vals):
|
||||||
|
return super(sf_technology_design, self).write(vals)
|
||||||
def unlink_technology_design(self):
|
def unlink_technology_design(self):
|
||||||
self.active = False
|
self.active = False
|
||||||
|
|
||||||
|
|
||||||
|
@api.model_create_multi
|
||||||
|
def create(self, vals_list):
|
||||||
|
for vals in vals_list:
|
||||||
|
if not vals.get('route_id'):
|
||||||
|
raise ValidationError(_("工序不能为空"))
|
||||||
|
result = super(sf_technology_design, self).create(vals_list)
|
||||||
|
for res in result:
|
||||||
|
record = self.search([('production_id', '=', res.production_id.id), ('active', 'in', [True, False])], order='group_uniq_id desc', limit=1)
|
||||||
|
res.group_uniq_id=record.group_uniq_id + 1
|
||||||
|
return result
|
||||||
|
def get_duplicates_with_inactive(self,technology_designs):
|
||||||
|
# 统计每个 'sequence' 出现的次数
|
||||||
|
sequence_count = Counter(technology_design.sequence for technology_design in technology_designs)
|
||||||
|
|
||||||
|
# 筛选出 'sequence' 重复且 'active' 为 False 的元素
|
||||||
|
result = [
|
||||||
|
technology_design for technology_design in technology_designs
|
||||||
|
if sequence_count[technology_design.sequence] > 1 and technology_design.active is False
|
||||||
|
]
|
||||||
|
|
||||||
|
return result
|
||||||
|
# def rearrange_numbering(self,self_technology_designs):
|
||||||
|
# inactive_designs = self.get_duplicates_with_inactive(self_technology_designs)
|
||||||
|
# if inactive_designs:
|
||||||
|
# max_design = max(self_technology_designs, key=lambda x: x.sequence)
|
||||||
|
# max_sequence = max_design.sequence if max_design else 0
|
||||||
|
# for designs in inactive_designs:
|
||||||
|
# max_sequence += 1
|
||||||
|
# designs.sequence = max_sequence
|
||||||
|
# self_technology_designs.sorted(key=lambda techology_design:techology_design.sequence)
|
||||||
|
# return self_technology_designs
|
||||||
|
|
||||||
|
def get_technology_design(self):
|
||||||
|
return {
|
||||||
|
'sequence':self.sequence,
|
||||||
|
'route_id': self.route_id.id,
|
||||||
|
'process_parameters_id': self.process_parameters_id.id,
|
||||||
|
'panel': self.panel,
|
||||||
|
'routing_tag': self.routing_tag,
|
||||||
|
'time_cycle_manual': self.time_cycle_manual,
|
||||||
|
'is_auto': self.is_auto,
|
||||||
|
'active': self.active,
|
||||||
|
'group_uniq_id':self.group_uniq_id,
|
||||||
|
}
|
||||||
|
def sync_technology_designs(self,production_technology_designs, self_technology_designs):
|
||||||
|
production_id = production_technology_designs[0].production_id.id
|
||||||
|
self_technology_design_dict = {item.group_uniq_id:item for item in self_technology_designs}
|
||||||
|
production_technology_designs_dict = {item.group_uniq_id:item for item in production_technology_designs}
|
||||||
|
for technology_design in production_technology_designs:
|
||||||
|
if not self_technology_design_dict.get(technology_design.group_uniq_id):
|
||||||
|
technology_design.write({'production_id': False})
|
||||||
|
else:
|
||||||
|
technology_design.write(self_technology_design_dict.get(technology_design.group_uniq_id).get_technology_design())
|
||||||
|
for technology_design in self_technology_designs:
|
||||||
|
if not production_technology_designs_dict.get(technology_design.group_uniq_id):
|
||||||
|
technology_design = technology_design.get_technology_design()
|
||||||
|
technology_design.update({'production_id': production_id})
|
||||||
|
technology_design.pop('group_uniq_id')
|
||||||
|
self.env['sf.technology.design'].create(technology_design)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def unified_procedure_multiple_work_orders(self,self_technology_designs,production_item):
|
||||||
|
technology_designs = self.env['sf.technology.design'].sudo().search(
|
||||||
|
[('production_id', '=', production_item.id), ('active', 'in', [True, False])])
|
||||||
|
self.sync_technology_designs(self_technology_designs=self_technology_designs,production_technology_designs=technology_designs)
|
||||||
@@ -281,19 +281,26 @@ class StockRule(models.Model):
|
|||||||
workorder_duration += workorder.duration_expected
|
workorder_duration += workorder.duration_expected
|
||||||
|
|
||||||
sale_order = self.env['sale.order'].sudo().search([('name', '=', production.origin)])
|
sale_order = self.env['sale.order'].sudo().search([('name', '=', production.origin)])
|
||||||
if sale_order:
|
# 如果订单为空,则获取来源制造订单的销售单
|
||||||
# sale_order.write({'schedule_status': 'to schedule'})
|
if not sale_order:
|
||||||
self.env['sf.production.plan'].sudo().with_company(company_id).create({
|
mrp_production = self.env['mrp.production'].sudo().search([('name', '=', production.origin)],
|
||||||
'name': production.name,
|
limit=1)
|
||||||
'order_deadline': sale_order.deadline_of_delivery,
|
if mrp_production:
|
||||||
'production_id': production.id,
|
sale_order = self.env['sale.order'].sudo().search([('name', '=', mrp_production.origin)])
|
||||||
'date_planned_start': production.date_planned_start,
|
else:
|
||||||
'origin': production.origin,
|
mrp_production = production
|
||||||
'product_qty': production.product_qty,
|
# if sale_order:
|
||||||
'product_id': production.product_id.id,
|
# sale_order.write({'schedule_status': 'to schedule'})
|
||||||
'state': 'draft',
|
self.env['sf.production.plan'].sudo().with_company(company_id).create({
|
||||||
})
|
'name': production.name,
|
||||||
technology_design_values = []
|
'order_deadline': sale_order.deadline_of_delivery,
|
||||||
|
'production_id': production.id,
|
||||||
|
'date_planned_start': production.date_planned_start,
|
||||||
|
'origin': mrp_production.origin,
|
||||||
|
'product_qty': production.product_qty,
|
||||||
|
'product_id': production.product_id.id,
|
||||||
|
'state': 'draft',
|
||||||
|
})
|
||||||
all_production = productions
|
all_production = productions
|
||||||
grouped_product_ids = {k: list(g) for k, g in groupby(all_production, key=lambda x: x.product_id.id)}
|
grouped_product_ids = {k: list(g) for k, g in groupby(all_production, key=lambda x: x.product_id.id)}
|
||||||
# 初始化一个字典来存储每个product_id对应的生产订单名称列表
|
# 初始化一个字典来存储每个product_id对应的生产订单名称列表
|
||||||
@@ -303,6 +310,7 @@ class StockRule(models.Model):
|
|||||||
# 为同一个product_id创建一个生产订单名称列表
|
# 为同一个product_id创建一个生产订单名称列表
|
||||||
product_id_to_production_names[product_id] = [production.name for production in all_production]
|
product_id_to_production_names[product_id] = [production.name for production in all_production]
|
||||||
for production_item in productions:
|
for production_item in productions:
|
||||||
|
technology_design_values = []
|
||||||
production_programming = self.env['mrp.production'].search(
|
production_programming = self.env['mrp.production'].search(
|
||||||
[('product_id.id', '=', production_item.product_id.id),
|
[('product_id.id', '=', production_item.product_id.id),
|
||||||
('origin', '=', production_item.origin)],
|
('origin', '=', production_item.origin)],
|
||||||
@@ -310,77 +318,77 @@ class StockRule(models.Model):
|
|||||||
if production_item.product_id.id in product_id_to_production_names:
|
if production_item.product_id.id in product_id_to_production_names:
|
||||||
# 同一个产品多个制造订单对应一个编程单和模型库
|
# 同一个产品多个制造订单对应一个编程单和模型库
|
||||||
# 只调用一次fetchCNC,并将所有生产订单的名称作为字符串传递
|
# 只调用一次fetchCNC,并将所有生产订单的名称作为字符串传递
|
||||||
if not production_item.programming_no and production.production_type == '自动化产线加工':
|
if not production_item.programming_no and production_item.production_type == '自动化产线加工':
|
||||||
if not production_programming.programming_no:
|
if not production_programming.programming_no:
|
||||||
production_item.fetchCNC(
|
production_item.fetchCNC(
|
||||||
', '.join(product_id_to_production_names[production_item.product_id.id]))
|
', '.join(product_id_to_production_names[production_item.product_id.id]))
|
||||||
else:
|
else:
|
||||||
production_item.write({'programming_no': production_programming.programming_no,
|
production_item.write({'programming_no': production_programming.programming_no,
|
||||||
'programming_state': '编程中'})
|
'programming_state': '编程中'})
|
||||||
if not technology_design_values:
|
i = 0
|
||||||
i = 0
|
if production_item.product_id.categ_id.type == '成品':
|
||||||
if production_item.product_id.categ_id.type == '成品':
|
# 根据加工面板的面数及成品工序模板生成工序设计
|
||||||
# 根据加工面板的面数及成品工序模板生成工序设计
|
if production_item.production_type == '自动化产线加工':
|
||||||
if production_item.production_type == '自动化产线加工':
|
model = 'sf.product.model.type.routing.sort'
|
||||||
model = 'sf.product.model.type.routing.sort'
|
domain = [
|
||||||
domain = [
|
('product_model_type_id', '=', production_item.product_id.product_model_type_id.id)]
|
||||||
('product_model_type_id', '=', production_item.product_id.product_model_type_id.id)]
|
else:
|
||||||
else:
|
model = 'sf.manual.product.model.type.routing.sort'
|
||||||
model = 'sf.manual.product.model.type.routing.sort'
|
domain = [('manual_product_model_type_id', '=',
|
||||||
domain = [('manual_product_model_type_id', '=',
|
production_item.product_id.product_model_type_id.id)]
|
||||||
production_item.product_id.product_model_type_id.id)]
|
product_routing_workcenter = self.env[model].search(domain, order='sequence asc')
|
||||||
product_routing_workcenter = self.env[model].search(domain, order='sequence asc')
|
if production_item.production_type == '自动化产线加工':
|
||||||
if production_item.production_type == '自动化产线加工':
|
for k in (production_item.product_id.model_processing_panel.split(',')):
|
||||||
for k in (production_item.product_id.model_processing_panel.split(',')):
|
|
||||||
i += 1
|
|
||||||
for route in product_routing_workcenter:
|
|
||||||
technology_design_values.append(
|
|
||||||
self.env['sf.technology.design'].json_technology_design_str(k, route, i, False))
|
|
||||||
else:
|
|
||||||
for route in product_routing_workcenter:
|
for route in product_routing_workcenter:
|
||||||
i += 1
|
i += 1
|
||||||
technology_design_values.append(
|
technology_design_values.append(
|
||||||
self.env['sf.technology.design'].json_technology_design_str(False, route, i, False))
|
self.env['sf.technology.design'].json_technology_design_str(k, route, i, False))
|
||||||
elif production_item.product_id.categ_id.type == '坯料':
|
else:
|
||||||
embryo_routing_workcenter = self.env['sf.embryo.model.type.routing.sort'].search(
|
for route in product_routing_workcenter:
|
||||||
[('embryo_model_type_id', '=', production_item.product_id.embryo_model_type_id.id)],
|
|
||||||
order='sequence asc'
|
|
||||||
)
|
|
||||||
for route_embryo in embryo_routing_workcenter:
|
|
||||||
i += 1
|
i += 1
|
||||||
technology_design_values.append(
|
technology_design_values.append(
|
||||||
self.env['sf.technology.design'].json_technology_design_str(False, route_embryo, i,
|
self.env['sf.technology.design'].json_technology_design_str(False, route, i, False))
|
||||||
False))
|
elif production_item.product_id.categ_id.type == '坯料':
|
||||||
surface_technics_arr = []
|
embryo_routing_workcenter = self.env['sf.embryo.model.type.routing.sort'].search(
|
||||||
route_workcenter_arr = []
|
[('embryo_model_type_id', '=', production_item.product_id.embryo_model_type_id.id)],
|
||||||
for item in production_item.product_id.product_model_type_id.surface_technics_routing_tmpl_ids:
|
order='sequence asc'
|
||||||
if item.route_workcenter_id.surface_technics_id.id:
|
)
|
||||||
for process_param in production_item.product_id.model_process_parameters_ids:
|
for route_embryo in embryo_routing_workcenter:
|
||||||
if item.route_workcenter_id.surface_technics_id == process_param.process_id:
|
i += 1
|
||||||
surface_technics_arr.append(
|
technology_design_values.append(
|
||||||
item.route_workcenter_id.surface_technics_id.id)
|
self.env['sf.technology.design'].json_technology_design_str(False, route_embryo, i,
|
||||||
route_workcenter_arr.append(item.route_workcenter_id.id)
|
False))
|
||||||
if surface_technics_arr:
|
surface_technics_arr = []
|
||||||
production_process = self.env['sf.production.process'].search(
|
route_workcenter_arr = []
|
||||||
[('id', 'in', surface_technics_arr)],
|
for item in production_item.product_id.product_model_type_id.surface_technics_routing_tmpl_ids:
|
||||||
order='sequence asc'
|
if item.route_workcenter_id.surface_technics_id.id:
|
||||||
)
|
for process_param in production_item.product_id.model_process_parameters_ids:
|
||||||
for p in production_process:
|
if item.route_workcenter_id.surface_technics_id == process_param.process_id:
|
||||||
logging.info('production_process:%s' % p.name)
|
surface_technics_arr.append(
|
||||||
process_parameter = production_item.product_id.model_process_parameters_ids.filtered(
|
item.route_workcenter_id.surface_technics_id.id)
|
||||||
lambda pm: pm.process_id.id == p.id)
|
route_workcenter_arr.append(item.route_workcenter_id.id)
|
||||||
if process_parameter:
|
if surface_technics_arr:
|
||||||
i += 1
|
production_process = self.env['sf.production.process'].search(
|
||||||
route_production_process = self.env[
|
[('id', 'in', surface_technics_arr)],
|
||||||
'mrp.routing.workcenter'].search(
|
order='sequence asc'
|
||||||
[('surface_technics_id', '=', p.id),
|
)
|
||||||
('id', 'in', route_workcenter_arr)])
|
for p in production_process:
|
||||||
technology_design_values.append(
|
logging.info('production_process:%s' % p.name)
|
||||||
self.env['sf.technology.design'].json_technology_design_str(False,
|
process_parameter = production_item.product_id.model_process_parameters_ids.filtered(
|
||||||
route_production_process,
|
lambda pm: pm.process_id.id == p.id)
|
||||||
i,
|
if process_parameter:
|
||||||
process_parameter))
|
i += 1
|
||||||
productions.technology_design_ids = technology_design_values
|
route_production_process = self.env[
|
||||||
|
'mrp.routing.workcenter'].search(
|
||||||
|
[('surface_technics_id', '=', p.id),
|
||||||
|
('id', 'in', route_workcenter_arr)])
|
||||||
|
technology_design_values.append(
|
||||||
|
self.env['sf.technology.design'].json_technology_design_str(False,
|
||||||
|
route_production_process,
|
||||||
|
i,
|
||||||
|
process_parameter))
|
||||||
|
production_item.technology_design_ids = technology_design_values
|
||||||
|
productions.write({'state': 'technology_to_confirmed'})
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
@@ -415,6 +423,8 @@ class ProductionLot(models.Model):
|
|||||||
"""Generate `lot_names` from a string."""
|
"""Generate `lot_names` from a string."""
|
||||||
if first_lot.__contains__(display_name):
|
if first_lot.__contains__(display_name):
|
||||||
first_lot = first_lot[(len(display_name) + 1):]
|
first_lot = first_lot[(len(display_name) + 1):]
|
||||||
|
else:
|
||||||
|
first_lot = first_lot[-3:]
|
||||||
|
|
||||||
# We look if the first lot contains at least one digit.
|
# We look if the first lot contains at least one digit.
|
||||||
caught_initial_number = regex_findall(r"\d+", first_lot)
|
caught_initial_number = regex_findall(r"\d+", first_lot)
|
||||||
@@ -578,40 +588,34 @@ class StockPicking(models.Model):
|
|||||||
address_of_delivery = fields.Char('联系地址', compute='_compute_move_ids', store=True)
|
address_of_delivery = fields.Char('联系地址', compute='_compute_move_ids', store=True)
|
||||||
|
|
||||||
retrospect_ref = fields.Char('追溯参考', compute='_compute_move_ids', store=True)
|
retrospect_ref = fields.Char('追溯参考', compute='_compute_move_ids', store=True)
|
||||||
|
sale_order_id = fields.Many2one('sale.order', '销售单号', compute='_compute_move_ids', store=True)
|
||||||
picking_type_sequence_code = fields.Char(related='picking_type_id.sequence_code')
|
picking_type_sequence_code = fields.Char(related='picking_type_id.sequence_code')
|
||||||
|
|
||||||
@api.depends('move_ids', 'move_ids.product_id')
|
@api.depends('move_ids', 'move_ids.product_id')
|
||||||
def _compute_move_ids(self):
|
def _compute_move_ids(self):
|
||||||
for item in self:
|
for item in self:
|
||||||
if item.move_ids:
|
if item.move_ids:
|
||||||
if item.picking_type_id.sequence_code == 'DL':
|
product_id = item.move_ids[0].product_id
|
||||||
sale_name = item.move_ids[0].product_id.name.split('-')[1]
|
if product_id:
|
||||||
if 'S' in sale_name:
|
sale_info = None
|
||||||
sale_id = self.env['sale.order'].sudo().search([('name', '=', sale_name)])
|
if product_id.categ_id.type == '坯料' and product_id.name.startswith('R-S'):
|
||||||
item.person_of_delivery = sale_id.person_of_delivery
|
parts = product_id.name.split('-')
|
||||||
item.telephone_of_delivery = sale_id.telephone_of_delivery
|
if len(parts) >= 3:
|
||||||
item.address_of_delivery = sale_id.address_of_delivery
|
sale_name = parts[1]
|
||||||
|
sale_info = self.env['sale.order'].sudo().search(
|
||||||
|
[('name', '=', sale_name)])
|
||||||
else:
|
else:
|
||||||
raise ValidationError('坯料名称格式错误,正确格式为[R-S???-?]!!!')
|
sale_order_line = self.env['sale.order.line'].sudo().search(
|
||||||
move_ids = []
|
[('product_id', '=', product_id.id)])
|
||||||
for move_id in item.move_ids:
|
if sale_order_line:
|
||||||
move_ids.append(move_id.product_id.id)
|
sale_info = sale_order_line[0].order_id
|
||||||
boms = self.env['mrp.bom'].sudo().search([('bom_line_ids.product_id', 'in', move_ids)])
|
if sale_info:
|
||||||
if boms:
|
item.sale_order_id = sale_info.id
|
||||||
codes_list = []
|
item.retrospect_ref = sale_info.order_code
|
||||||
for bom in boms:
|
if item.picking_type_id.sequence_code == 'DL':
|
||||||
if bom.product_tmpl_id.default_code:
|
item.person_of_delivery = sale_info.person_of_delivery
|
||||||
code_list = bom.product_tmpl_id.default_code.split('-')
|
item.telephone_of_delivery = sale_info.telephone_of_delivery
|
||||||
if len(code_list) >= 4:
|
item.address_of_delivery = sale_info.address_of_delivery
|
||||||
code = '-'.join(code_list[:4])
|
|
||||||
if code not in codes_list:
|
|
||||||
codes_list.append(code)
|
|
||||||
else:
|
|
||||||
raise ValidationError('坯料成品的内部参考值格式错误')
|
|
||||||
item.retrospect_ref = ','.join(codes_list)
|
|
||||||
elif item.picking_type_id.sequence_code in ['INT', 'PC']:
|
|
||||||
pass
|
|
||||||
|
|
||||||
# 设置外协出入单的名称
|
# 设置外协出入单的名称
|
||||||
def _get_name_Res(self, rescode):
|
def _get_name_Res(self, rescode):
|
||||||
@@ -625,80 +629,103 @@ class StockPicking(models.Model):
|
|||||||
return '%s%s' % (rescode, num)
|
return '%s%s' % (rescode, num)
|
||||||
|
|
||||||
def button_validate(self):
|
def button_validate(self):
|
||||||
if self.picking_type_id.barcode == 'OCOUT':
|
|
||||||
move_out = self.env['stock.move'].search(
|
|
||||||
[('location_id', '=', self.env['stock.location'].search(
|
|
||||||
[('barcode', 'ilike', 'WH-PREPRODUCTION')]).id),
|
|
||||||
('location_dest_id', '=', self.env['stock.location'].search(
|
|
||||||
[('barcode', 'ilike', 'VL-SPOC')]).id),
|
|
||||||
('origin', '=', self.origin)])
|
|
||||||
move_in = self.env['stock.move'].search(
|
|
||||||
[('location_dest_id', '=', self.env['stock.location'].search(
|
|
||||||
[('barcode', 'ilike', 'WH-PREPRODUCTION')]).id),
|
|
||||||
('location_id', '=', self.env['stock.location'].search(
|
|
||||||
[('barcode', 'ilike', 'VL-SPOC')]).id),
|
|
||||||
('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:
|
|
||||||
move_in.write({'production_id': False})
|
|
||||||
if move_out.picking_id.state != 'done':
|
|
||||||
raise UserError(
|
|
||||||
_('该入库单对应的单号为%s的出库单还未完成,不能进行验证操作!' % move_out.picking_id.name))
|
|
||||||
res = super().button_validate()
|
res = super().button_validate()
|
||||||
if res is True and self.picking_type_id.barcode == 'OCIN':
|
picking_type_in = self.env.ref('sf_manufacturing.outcontract_picking_in').id
|
||||||
if self.id == move_out.picking_id.id:
|
if res is True and self.picking_type_id.id == picking_type_in:
|
||||||
# if move_out.move_line_ids.workorder_id.state == 'progress':
|
# 如果是最后一张外协入库单,则设置库存位置的预留数量
|
||||||
move_in = self.env['stock.move'].search(
|
move_in = self.move_ids
|
||||||
[('location_dest_id', '=', self.env['stock.location'].search(
|
if move_in:
|
||||||
[('barcode', 'ilike', 'WH-PREPRODUCTION')]).id),
|
workorder = move_in.subcontract_workorder_id
|
||||||
('location_id', '=', self.env['stock.location'].search(
|
workorders = workorder.production_id.workorder_ids
|
||||||
[('barcode', 'ilike', 'VL-SPOC')]).id),
|
subcontract_workorders = workorders.filtered(lambda wo: wo.is_subcontract == True and wo.state!='cancel').sorted('sequence')
|
||||||
('origin', '=', self.origin)])
|
if workorder == subcontract_workorders[-1]:
|
||||||
production = self.env['mrp.production'].search([('name', '=', self.origin)])
|
self.env['stock.quant']._update_reserved_quantity(
|
||||||
if move_in.state != 'done':
|
move_in.product_id, move_in.location_dest_id, move_in.product_uom_qty,
|
||||||
move_in.write({'state': 'assigned'})
|
lot_id=move_in.move_line_ids.lot_id,
|
||||||
self.env['stock.move.line'].create(move_in.get_move_line(production, None))
|
package_id=False, owner_id=False, strict=False
|
||||||
|
)
|
||||||
|
workorder.button_finish()
|
||||||
|
picking_type_out = self.env.ref('sf_manufacturing.outcontract_picking_out').id
|
||||||
|
if res and self.picking_type_id.id == picking_type_out:
|
||||||
|
move_out = self.move_ids
|
||||||
|
if move_out:
|
||||||
|
workorder = move_out.subcontract_workorder_id
|
||||||
|
workorder.button_start()
|
||||||
|
if self.location_id.name == '成品存货区' and self.location_dest_id.name == '客户':
|
||||||
|
sale_id = self.env['sale.order'].sudo().search(
|
||||||
|
[('name', '=', self.origin)])
|
||||||
|
stock_picking_list = self.env['stock.picking'].sudo().search(
|
||||||
|
[('id', 'in', sale_id.picking_ids.ids)])
|
||||||
|
stock_picking = stock_picking_list.filtered(lambda p: p.state not in ("done", "cancel"))
|
||||||
|
if sale_id and not stock_picking:
|
||||||
|
sale_id.write({'state': 'delivered'})
|
||||||
return res
|
return res
|
||||||
|
|
||||||
# 创建 外协出库入单
|
# 创建 外协出库入单
|
||||||
def create_outcontract_picking(self, sorted_workorders_arr, item):
|
def create_outcontract_picking(self, workorders, item, sorted_workorders):
|
||||||
if len(sorted_workorders_arr) > 1:
|
for workorder in workorders:
|
||||||
sorted_workorders_arr = sorted_workorders_arr[0]
|
if workorder.move_subcontract_workorder_ids:
|
||||||
stock_picking = self.env['stock.picking'].search([('origin', '=', item.name), ('name', 'ilike', 'OCOUT')])
|
workorder.move_subcontract_workorder_ids.write({'state': 'cancel'})
|
||||||
if not stock_picking or sorted_workorders_arr:
|
workorder.move_subcontract_workorder_ids.picking_id.write({'state': 'cancel'})
|
||||||
for sorted_workorders in sorted_workorders_arr:
|
# 创建一个新的补货组
|
||||||
# pick_ids = []
|
procurement_group_id = self.env['procurement.group'].create({
|
||||||
if not sorted_workorders.picking_ids:
|
'name': workorder.name,
|
||||||
# outcontract_stock_move = self.env['stock.move'].search([('production_id', '=', item.id)])
|
'partner_id': self.partner_id.id,
|
||||||
# if not outcontract_stock_move:
|
})
|
||||||
new_picking = True
|
move_dest_id = False
|
||||||
location_id = self.env['stock.location'].search(
|
# 如果当前工单是是制造订单的最后一个工艺外协工单
|
||||||
[('barcode', 'ilike', 'VL-SPOC')]).id,
|
if workorder == next((workorder for workorder in reversed(sorted_workorders) if workorder.is_subcontract),
|
||||||
location_dest_id = self.env['stock.location'].search(
|
None):
|
||||||
[('barcode', 'ilike', 'WH-PREPRODUCTION')]).id,
|
move_dest_id = item.move_raw_ids[0].id
|
||||||
outcontract_picking_type_in = self.env.ref(
|
else:
|
||||||
'sf_manufacturing.outcontract_picking_in').id,
|
# 从sorted_workorders中找到上一工单的move
|
||||||
outcontract_picking_type_out = self.env.ref(
|
if len(sorted_workorders) > 1:
|
||||||
'sf_manufacturing.outcontract_picking_out').id,
|
move_dest_id = \
|
||||||
moves_out = self.env['stock.move'].sudo().create(
|
sorted_workorders[sorted_workorders.index(workorder) + 1].move_subcontract_workorder_ids[1].id
|
||||||
self.env['stock.move']._get_stock_move_values_Res(item, location_dest_id, location_id,
|
new_picking = True
|
||||||
outcontract_picking_type_out))
|
outcontract_picking_type_in = self.env.ref(
|
||||||
picking_out = self.create(
|
'sf_manufacturing.outcontract_picking_in').id,
|
||||||
moves_out._get_new_picking_values_Res(item, sorted_workorders, 'WH/OCOUT/'))
|
outcontract_picking_type_out = self.env.ref(
|
||||||
# pick_ids.append(picking_out.id)
|
'sf_manufacturing.outcontract_picking_out').id,
|
||||||
moves_out.write(
|
context = dict(self.env.context)
|
||||||
{'picking_id': picking_out.id, 'state': 'waiting'})
|
context.update({
|
||||||
moves_out._assign_picking_post_process(new=new_picking)
|
'default_production_id': item.id, # 添加额外信息到 context 中
|
||||||
moves_in = self.env['stock.move'].sudo().create(
|
})
|
||||||
self.env['stock.move']._get_stock_move_values_Res(item, location_id, location_dest_id,
|
moves_in = self.env['stock.move'].sudo().with_context(context).create(
|
||||||
outcontract_picking_type_in))
|
self.env['stock.move']._get_stock_move_values_Res(item, outcontract_picking_type_in,
|
||||||
picking_in = self.create(
|
procurement_group_id.id, move_dest_id))
|
||||||
moves_in._get_new_picking_values_Res(item, sorted_workorders, 'WH/OCIN/'))
|
picking_in = self.create(
|
||||||
# pick_ids.append(picking_in.id)
|
moves_in._get_new_picking_values_Res(item, workorder, 'WH/OCIN/'))
|
||||||
moves_in.write(
|
# pick_ids.append(picking_in.id)
|
||||||
{'picking_id': picking_in.id, 'state': 'waiting'})
|
moves_in.write(
|
||||||
moves_in._assign_picking_post_process(new=new_picking)
|
{'picking_id': picking_in.id, 'state': 'waiting'})
|
||||||
|
moves_in._assign_picking_post_process(new=new_picking)
|
||||||
|
# self.env.context.get('default_production_id')
|
||||||
|
moves_out = self.env['stock.move'].sudo().with_context(context).create(
|
||||||
|
self.env['stock.move']._get_stock_move_values_Res(item, outcontract_picking_type_out,
|
||||||
|
procurement_group_id.id, moves_in.id))
|
||||||
|
workorder.write({'move_subcontract_workorder_ids': [(6, 0, [moves_in.id, moves_out.id])]})
|
||||||
|
picking_out = self.create(
|
||||||
|
moves_out._get_new_picking_values_Res(item, workorder, 'WH/OCOUT/'))
|
||||||
|
# pick_ids.append(picking_out.id)
|
||||||
|
moves_out.write(
|
||||||
|
{'picking_id': picking_out.id, 'state': 'waiting'})
|
||||||
|
moves_out._assign_picking_post_process(new=new_picking)
|
||||||
|
|
||||||
|
@api.depends('move_type', 'immediate_transfer', 'move_ids.state', 'move_ids.picking_id')
|
||||||
|
def _compute_state(self):
|
||||||
|
super(StockPicking, self)._compute_state()
|
||||||
|
for picking in self:
|
||||||
|
# 外协出库单根据工单状态,采购单状态来确定
|
||||||
|
picking_type_id = self.env.ref('sf_manufacturing.outcontract_picking_out').id
|
||||||
|
if picking.picking_type_id.id == picking_type_id:
|
||||||
|
if picking.move_ids:
|
||||||
|
workorder = picking.move_ids[0].subcontract_workorder_id
|
||||||
|
if picking.state == 'assigned':
|
||||||
|
if workorder.state in ['pending',
|
||||||
|
'waiting'] or workorder._get_surface_technics_purchase_ids().state in [
|
||||||
|
'draft', 'sent']:
|
||||||
|
picking.state = 'waiting'
|
||||||
|
|
||||||
|
|
||||||
class ReStockMove(models.Model):
|
class ReStockMove(models.Model):
|
||||||
@@ -707,18 +734,51 @@ class ReStockMove(models.Model):
|
|||||||
materiel_length = fields.Float(string='物料长度', digits=(16, 4))
|
materiel_length = fields.Float(string='物料长度', digits=(16, 4))
|
||||||
materiel_width = fields.Float(string='物料宽度', digits=(16, 4))
|
materiel_width = fields.Float(string='物料宽度', digits=(16, 4))
|
||||||
materiel_height = fields.Float(string='物料高度', digits=(16, 4))
|
materiel_height = fields.Float(string='物料高度', digits=(16, 4))
|
||||||
|
part_number = fields.Char(string='零件图号', compute='_compute_part_info', store=True)
|
||||||
|
part_name = fields.Char(string='零件名称', compute='_compute_part_info', store=True)
|
||||||
|
|
||||||
def _get_stock_move_values_Res(self, item, location_src_id, location_dest_id, picking_type_id):
|
@api.depends('product_id')
|
||||||
route = self.env['stock.route'].sudo().search([('name', '=', '表面工艺外协')])
|
def _compute_part_info(self):
|
||||||
|
for move in self:
|
||||||
|
if move.product_id.categ_id.type == '成品':
|
||||||
|
move.part_number = move.product_id.part_number
|
||||||
|
move.part_name = move.product_id.part_name
|
||||||
|
elif move.product_id.categ_id.type == '坯料':
|
||||||
|
if move.origin:
|
||||||
|
origin = move.origin.split(',')[0] if ',' in move.origin else move.origin
|
||||||
|
mrp_productio_info = self.env['mrp.production'].sudo().search(
|
||||||
|
[('name', '=', origin)])
|
||||||
|
if mrp_productio_info:
|
||||||
|
move.part_number = mrp_productio_info.part_number
|
||||||
|
move.part_name = mrp_productio_info.part_name
|
||||||
|
else:
|
||||||
|
purchase_order_info = self.env['purchase.order'].sudo().search(
|
||||||
|
[('name', '=', origin)])
|
||||||
|
if purchase_order_info:
|
||||||
|
mrp_production_ids = purchase_order_info._get_mrp_productions().ids
|
||||||
|
if mrp_production_ids:
|
||||||
|
mrp_productio_info = self.env['mrp.production'].sudo().search(
|
||||||
|
[('id', '=', mrp_production_ids[0])])
|
||||||
|
if mrp_productio_info:
|
||||||
|
move.part_number = mrp_productio_info.part_number
|
||||||
|
move.part_name = mrp_productio_info.part_name
|
||||||
|
|
||||||
|
def _get_stock_move_values_Res(self, item, picking_type_id, group_id, move_dest_ids=False):
|
||||||
|
route_id = self.env.ref('sf_manufacturing.route_surface_technology_outsourcing').id
|
||||||
|
stock_rule = self.env['stock.rule'].sudo().search(
|
||||||
|
[('route_id', '=', route_id), ('picking_type_id', '=', picking_type_id)])
|
||||||
move_values = {
|
move_values = {
|
||||||
'name': '推',
|
'name': '推',
|
||||||
'company_id': item.company_id.id,
|
'company_id': item.company_id.id,
|
||||||
'product_id': item.bom_id.bom_line_ids.product_id.id,
|
'product_id': item.bom_id.bom_line_ids.product_id.id,
|
||||||
'product_uom': item.bom_id.bom_line_ids.product_uom_id.id,
|
'product_uom': item.bom_id.bom_line_ids.product_uom_id.id,
|
||||||
'product_uom_qty': 1.0,
|
'product_uom_qty': 1.0,
|
||||||
'location_id': location_src_id,
|
'location_id': stock_rule.location_src_id.id,
|
||||||
'location_dest_id': location_dest_id,
|
'location_dest_id': stock_rule.location_dest_id.id,
|
||||||
'origin': item.name,
|
'origin': item.name,
|
||||||
|
'group_id': group_id,
|
||||||
|
'move_dest_ids': [(6, 0, [move_dest_ids])] if move_dest_ids else False,
|
||||||
|
# 'production_id': item.id,
|
||||||
# 'route_ids': False if not route else [(4, route.id)],
|
# 'route_ids': False if not route else [(4, route.id)],
|
||||||
'date_deadline': datetime.now(),
|
'date_deadline': datetime.now(),
|
||||||
'picking_type_id': picking_type_id,
|
'picking_type_id': picking_type_id,
|
||||||
@@ -742,7 +802,7 @@ class ReStockMove(models.Model):
|
|||||||
'picking_type_id': picking_type_id,
|
'picking_type_id': picking_type_id,
|
||||||
'location_id': self.mapped('location_id').id,
|
'location_id': self.mapped('location_id').id,
|
||||||
'location_dest_id': self.mapped('location_dest_id').id,
|
'location_dest_id': self.mapped('location_dest_id').id,
|
||||||
'state': 'confirmed',
|
'state': 'waiting',
|
||||||
}
|
}
|
||||||
|
|
||||||
def get_move_line(self, production_id, sorted_workorders):
|
def get_move_line(self, production_id, sorted_workorders):
|
||||||
@@ -958,7 +1018,7 @@ class ReStockMove(models.Model):
|
|||||||
合并制造订单的完成move单据
|
合并制造订单的完成move单据
|
||||||
"""
|
"""
|
||||||
res = super(ReStockMove, self)._merge_moves_fields()
|
res = super(ReStockMove, self)._merge_moves_fields()
|
||||||
if self[0].origin and self.picking_type_id.name in ['生产发料', '内部调拨']:
|
if self[0].origin and self.picking_type_id.name in ['生产发料', '内部调拨', '生产入库', '客供料入库']:
|
||||||
production = self.env['mrp.production'].search([('name', '=', self[0].origin)], limit=1, order='id asc')
|
production = self.env['mrp.production'].search([('name', '=', self[0].origin)], limit=1, order='id asc')
|
||||||
productions = self.env['mrp.production'].search(
|
productions = self.env['mrp.production'].search(
|
||||||
[('origin', '=', production.origin), ('product_id', '=', production.product_id.id)])
|
[('origin', '=', production.origin), ('product_id', '=', production.product_id.id)])
|
||||||
@@ -970,18 +1030,24 @@ class ReStockMove(models.Model):
|
|||||||
创建调拨单时,在此新增或修改调拨单的数据
|
创建调拨单时,在此新增或修改调拨单的数据
|
||||||
"""
|
"""
|
||||||
res = super(ReStockMove, self)._get_new_picking_values()
|
res = super(ReStockMove, self)._get_new_picking_values()
|
||||||
if self[0].origin and self.picking_type_id.name in ['生产发料', '内部调拨']:
|
## 制造订单报废生成的新制造订单不走合并
|
||||||
production = self.env['mrp.production'].search([('name', '=', self[0].origin)], limit=1, order='id asc')
|
production_remanufacture = None
|
||||||
productions = self.env['mrp.production'].search(
|
if 'origin' in res:
|
||||||
[('origin', '=', production.origin), ('product_id', '=', production.product_id.id)])
|
if self.picking_type_id.name in ['生产发料', '内部调拨']:
|
||||||
res['origin'] = ','.join(productions.mapped('name'))
|
production_remanufacture = self.env['mrp.production'].search(
|
||||||
res['retrospect_ref'] = production.product_id.name
|
[('name', '=', res['origin']), ('is_remanufacture', '=', True)])
|
||||||
|
if not production_remanufacture:
|
||||||
|
if self[0].origin and self.picking_type_id.name in ['生产发料', '内部调拨']:
|
||||||
|
production = self.env['mrp.production'].search([('name', '=', self[0].origin)], limit=1, order='id asc')
|
||||||
|
productions = self.env['mrp.production'].search(
|
||||||
|
[('origin', '=', production.origin), ('product_id', '=', production.product_id.id)])
|
||||||
|
if productions.mapped('name'):
|
||||||
|
res['origin'] = ','.join(productions.mapped('name'))
|
||||||
|
res['retrospect_ref'] = production.product_id.name
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def _single_manufactuing_mo_generate_origin(self, res):
|
subcontract_workorder_id = fields.Many2one('mrp.workorder', '外协工单组件', check_company=True,
|
||||||
"""
|
index='btree_not_null')
|
||||||
单个制造订单的完成move单据修改来源为该制造订单关联的销售订单下所有成品相同的制造订单
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
class ReStockQuant(models.Model):
|
class ReStockQuant(models.Model):
|
||||||
|
|||||||
@@ -11,11 +11,11 @@ access_sf_model_type_group_sale_director,sf_model_type_group_sale_director,model
|
|||||||
access_sf_model_type_group_purchase_director,sf_model_type_group_purchase_director,model_sf_model_type,sf_base.group_purchase_director,1,0,0,0
|
access_sf_model_type_group_purchase_director,sf_model_type_group_purchase_director,model_sf_model_type,sf_base.group_purchase_director,1,0,0,0
|
||||||
access_sf_model_type_group_plan_director,sf_model_type_group_plan_director,model_sf_model_type,sf_base.group_plan_director,1,0,0,0
|
access_sf_model_type_group_plan_director,sf_model_type_group_plan_director,model_sf_model_type,sf_base.group_plan_director,1,0,0,0
|
||||||
access_sf_product_model_type_routing_sort_group_sf_mrp_user,sf_product_model_type_routing_sort,model_sf_product_model_type_routing_sort,sf_base.group_sf_mrp_user,1,0,0,0
|
access_sf_product_model_type_routing_sort_group_sf_mrp_user,sf_product_model_type_routing_sort,model_sf_product_model_type_routing_sort,sf_base.group_sf_mrp_user,1,0,0,0
|
||||||
access_sf_product_model_type_routing_sort_manager,sf_product_model_type_routing_sort,model_sf_product_model_type_routing_sort,sf_base.group_sf_mrp_manager,1,1,1,0
|
access_sf_product_model_type_routing_sort_manager,sf_product_model_type_routing_sort,model_sf_product_model_type_routing_sort,sf_base.group_sf_mrp_manager,1,1,1,1
|
||||||
access_sf_embryo_model_type_routing_sort_group_sf_mrp_user,sf_embryo_model_type_routing_sort,model_sf_embryo_model_type_routing_sort,sf_base.group_sf_mrp_user,1,0,0,0
|
access_sf_embryo_model_type_routing_sort_group_sf_mrp_user,sf_embryo_model_type_routing_sort,model_sf_embryo_model_type_routing_sort,sf_base.group_sf_mrp_user,1,0,0,0
|
||||||
access_sf_embryo_model_type_routing_sort_manager,sf_embryo_model_type_routing_sort,model_sf_embryo_model_type_routing_sort,sf_base.group_sf_mrp_manager,1,1,1,0
|
access_sf_embryo_model_type_routing_sort_manager,sf_embryo_model_type_routing_sort,model_sf_embryo_model_type_routing_sort,sf_base.group_sf_mrp_manager,1,1,1,1
|
||||||
access_sf_surface_technics_model_type_routing_sort,sf_surface_technics_model_type_routing_sort,model_sf_surface_technics_model_type_routing_sort,sf_base.group_sf_mrp_user,1,0,0,0
|
access_sf_surface_technics_model_type_routing_sort,sf_surface_technics_model_type_routing_sort,model_sf_surface_technics_model_type_routing_sort,sf_base.group_sf_mrp_user,1,0,0,0
|
||||||
access_sf_surface_technics_model_type_routing_sort_manager,sf_surface_technics_model_type_routing_sort,model_sf_surface_technics_model_type_routing_sort,sf_base.group_sf_mrp_manager,1,1,1,0
|
access_sf_surface_technics_model_type_routing_sort_manager,sf_surface_technics_model_type_routing_sort,model_sf_surface_technics_model_type_routing_sort,sf_base.group_sf_mrp_manager,1,1,1,1
|
||||||
access_sf_production_line_group_sf_mrp_user,sf.production.line,model_sf_production_line,sf_base.group_sf_mrp_user,1,1,1,0
|
access_sf_production_line_group_sf_mrp_user,sf.production.line,model_sf_production_line,sf_base.group_sf_mrp_user,1,1,1,0
|
||||||
access_sf_production_line_manager,sf.production.line,model_sf_production_line,sf_base.group_sf_mrp_manager,1,1,1,0
|
access_sf_production_line_manager,sf.production.line,model_sf_production_line,sf_base.group_sf_mrp_manager,1,1,1,0
|
||||||
access_maintenance_equipment_tool_group_sf_mrp_user,maintenance_equipment_tool,model_maintenance_equipment_tool,sf_base.group_sf_mrp_user,1,0,0,0
|
access_maintenance_equipment_tool_group_sf_mrp_user,maintenance_equipment_tool,model_maintenance_equipment_tool,sf_base.group_sf_mrp_user,1,0,0,0
|
||||||
@@ -52,6 +52,7 @@ access_mrp_routing_workcenter_manager_group_sf_mrp_user,mrp.routing.workcenter.m
|
|||||||
access_mrp_bom_manager_group_sf_mrp_user,mrp.bom.manager,mrp.model_mrp_bom,sf_base.group_sf_mrp_user,1,1,1,0
|
access_mrp_bom_manager_group_sf_mrp_user,mrp.bom.manager,mrp.model_mrp_bom,sf_base.group_sf_mrp_user,1,1,1,0
|
||||||
access_mrp_bom_line_manager_group_sf_mrp_user,mrp.bom.line.manager,mrp.model_mrp_bom_line,sf_base.group_sf_mrp_user,1,1,1,0
|
access_mrp_bom_line_manager_group_sf_mrp_user,mrp.bom.line.manager,mrp.model_mrp_bom_line,sf_base.group_sf_mrp_user,1,1,1,0
|
||||||
access_mrp_bom_line_group_plan_director,mrp_bom_line_group_plan_director,mrp.model_mrp_bom_line,sf_base.group_plan_director,1,1,1,0
|
access_mrp_bom_line_group_plan_director,mrp_bom_line_group_plan_director,mrp.model_mrp_bom_line,sf_base.group_plan_director,1,1,1,0
|
||||||
|
access_mrp_bom_line_group_sf_stock_user,mrp_bom_line_group_sf_stock_user,mrp.model_mrp_bom_line,sf_base.group_sf_stock_user,1,1,1,0
|
||||||
access_mrp_bom_line_group_sale_director,mrp_bom_line_group_sale_director,mrp.model_mrp_bom_line,sf_base.group_sale_director,1,1,1,0
|
access_mrp_bom_line_group_sale_director,mrp_bom_line_group_sale_director,mrp.model_mrp_bom_line,sf_base.group_sale_director,1,1,1,0
|
||||||
access_mrp_bom_line_group_sale_salemanager,mrp_bom_line_group_sale_salemanager,mrp.model_mrp_bom_line,sf_base.group_sale_salemanager,1,0,1,0
|
access_mrp_bom_line_group_sale_salemanager,mrp_bom_line_group_sale_salemanager,mrp.model_mrp_bom_line,sf_base.group_sale_salemanager,1,0,1,0
|
||||||
access_mrp_bom_line_group_purchase_director,mrp_bom_line_group_purchase_director,mrp.model_mrp_bom_line,sf_base.group_purchase_director,1,1,1,0
|
access_mrp_bom_line_group_purchase_director,mrp_bom_line_group_purchase_director,mrp.model_mrp_bom_line,sf_base.group_purchase_director,1,1,1,0
|
||||||
@@ -116,7 +117,7 @@ access_mrp_production_group_quality,mrp_production,model_mrp_production,sf_base.
|
|||||||
access_mrp_production_group_quality_director,mrp_production,model_mrp_production,sf_base.group_quality_director,1,1,0,0
|
access_mrp_production_group_quality_director,mrp_production,model_mrp_production,sf_base.group_quality_director,1,1,0,0
|
||||||
access_mrp_workorder_group_quality,mrp_workorder,model_mrp_workorder,sf_base.group_quality,1,1,0,0
|
access_mrp_workorder_group_quality,mrp_workorder,model_mrp_workorder,sf_base.group_quality,1,1,0,0
|
||||||
access_mrp_workorder_group_quality_director,mrp_workorder,model_mrp_workorder,sf_base.group_quality_director,1,1,0,0
|
access_mrp_workorder_group_quality_director,mrp_workorder,model_mrp_workorder,sf_base.group_quality_director,1,1,0,0
|
||||||
access_mrp_workorder,mrp_workorder,model_mrp_workorder,sf_base.group_plan_dispatch,1,1,0,0
|
access_mrp_workorder,mrp_workorder,model_mrp_workorder,sf_base.group_plan_dispatch,1,1,1,0
|
||||||
access_sf_production_line_group_plan_dispatch,sf.production.line,model_sf_production_line,sf_base.group_plan_dispatch,1,0,0,0
|
access_sf_production_line_group_plan_dispatch,sf.production.line,model_sf_production_line,sf_base.group_plan_dispatch,1,0,0,0
|
||||||
access_sf_production_line_group_plan_director,sf.production.line,model_sf_production_line,sf_base.group_plan_director,1,1,1,0
|
access_sf_production_line_group_plan_director,sf.production.line,model_sf_production_line,sf_base.group_plan_director,1,1,1,0
|
||||||
access_sf_production_line,sf.production.line,model_sf_production_line,sf_maintenance.sf_group_equipment_user,1,1,1,0
|
access_sf_production_line,sf.production.line,model_sf_production_line,sf_maintenance.sf_group_equipment_user,1,1,1,0
|
||||||
@@ -166,16 +167,23 @@ access_sf_agv_scheduling_group_sf_mrp_manager,sf_agv_scheduling_group_sf_mrp_man
|
|||||||
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
|
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
|
||||||
|
|
||||||
access_sf_technology_design_group_plan_dispatch,sf_technology_design_group_plan_dispatch,model_sf_technology_design,sf_base.group_plan_dispatch,1,1,1,0
|
access_sf_technology_design_group_plan_dispatch,sf_technology_design_group_plan_dispatch,model_sf_technology_design,sf_base.group_plan_dispatch,1,1,1,0
|
||||||
access_sf_technology_design_group_sf_mrp_manager,sf_technology_design_group_sf_mrp_manager,model_sf_technology_design,sf_base.group_sf_mrp_manager,1,1,1,0
|
access_sf_technology_design_group_sf_mrp_manager,sf_technology_design_group_sf_mrp_manager,model_sf_technology_design,sf_base.group_sf_mrp_manager,1,0,0,0
|
||||||
access_sf_technology_design_group_production_engineer,sf_technology_design_group_production_engineer,model_sf_technology_design,sf_base.group_production_engineer,1,1,1,0
|
access_sf_technology_design_group_production_engineer,sf_technology_design_group_production_engineer,model_sf_technology_design,sf_base.group_production_engineer,1,1,1,0
|
||||||
|
access_sf_technology_design_group_sf_equipment_user,sf_technology_design_group_sf_equipment_user,model_sf_technology_design,sf_base.group_sf_equipment_user,1,0,0,0
|
||||||
|
access_sf_technology_design_group_sf_order_user,sf_technology_design_group_sf_order_user,model_sf_technology_design,sf_base.group_sf_order_user,1,0,0,0
|
||||||
access_sf_production_technology_wizard_group_plan_dispatch,sf_production_technology_wizard_group_plan_dispatch,model_sf_production_technology_wizard,sf_base.group_plan_dispatch,1,1,1,0
|
access_sf_production_technology_wizard_group_plan_dispatch,sf_production_technology_wizard_group_plan_dispatch,model_sf_production_technology_wizard,sf_base.group_plan_dispatch,1,1,1,0
|
||||||
access_sf_production_technology_wizard_group_sf_mrp_manager,sf_production_technology_wizard_group_sf_mrp_manager,model_sf_production_technology_wizard,sf_base.group_sf_mrp_manager,1,1,1,0
|
access_sf_production_technology_wizard_group_sf_mrp_manager,sf_production_technology_wizard_group_sf_mrp_manager,model_sf_production_technology_wizard,sf_base.group_sf_mrp_manager,1,0,0,0
|
||||||
access_sf_production_technology_wizard_group_production_engineer,sf_production_technology_wizard_group_production_engineer,model_sf_production_technology_wizard,sf_base.group_production_engineer,1,1,1,0
|
access_sf_production_technology_wizard_group_production_engineer,sf_production_technology_wizard_group_production_engineer,model_sf_production_technology_wizard,sf_base.group_production_engineer,1,1,1,0
|
||||||
access_sf_production_technology_re_adjust_wizard_group_plan_dispatch,sf_production_technology_re_adjust_wizard_group_plan_dispatch,model_sf_production_technology_re_adjust_wizard,sf_base.group_plan_dispatch,1,1,1,0
|
access_sf_production_technology_re_adjust_wizard_group_plan_dispatch,sf_production_technology_re_adjust_wizard_group_plan_dispatch,model_sf_production_technology_re_adjust_wizard,sf_base.group_plan_dispatch,1,1,1,0
|
||||||
access_sf_production_technology_re_adjust_wizard_group_sf_mrp_manager,sf_production_technology_re_adjust_wizard_group_sf_mrp_manager,model_sf_production_technology_re_adjust_wizard,sf_base.group_sf_mrp_manager,1,1,1,0
|
access_sf_production_technology_re_adjust_wizard_group_sf_mrp_manager,sf_production_technology_re_adjust_wizard_group_sf_mrp_manager,model_sf_production_technology_re_adjust_wizard,sf_base.group_sf_mrp_manager,1,1,1,0
|
||||||
access_sf_production_technology_re_adjust_wizard_group_production_engineer,sf_production_technology_re_adjust_wizard_group_production_engineer,model_sf_production_technology_re_adjust_wizard,sf_base.group_production_engineer,1,1,1,0
|
access_sf_production_technology_re_adjust_wizard_group_production_engineer,sf_production_technology_re_adjust_wizard_group_production_engineer,model_sf_production_technology_re_adjust_wizard,sf_base.group_production_engineer,1,1,1,0
|
||||||
|
|
||||||
access_sf_manual_product_model_type_routing_sort_group_sf_mrp_user,sf_manual_product_model_type_routing_sort,model_sf_manual_product_model_type_routing_sort,sf_base.group_sf_mrp_user,1,0,0,0
|
access_sf_manual_product_model_type_routing_sort_group_sf_mrp_user,sf_manual_product_model_type_routing_sort,model_sf_manual_product_model_type_routing_sort,sf_base.group_sf_mrp_user,1,0,0,0
|
||||||
access_sf_manual_product_model_type_routing_sort_manager,sf_manual_product_model_type_routing_sort,model_sf_manual_product_model_type_routing_sort,sf_base.group_sf_mrp_manager,1,1,1,0
|
access_sf_manual_product_model_type_routing_sort_manager,sf_manual_product_model_type_routing_sort,model_sf_manual_product_model_type_routing_sort,sf_base.group_sf_mrp_manager,1,1,1,1
|
||||||
access_sf_manual_product_model_type_routing_sort_group_plan_dispatch,sf_manual_product_model_type_routing_sort_group_plan_dispatch,model_sf_manual_product_model_type_routing_sort,sf_base.group_plan_dispatch,1,0,0,0
|
access_sf_manual_product_model_type_routing_sort_group_plan_dispatch,sf_manual_product_model_type_routing_sort_group_plan_dispatch,model_sf_manual_product_model_type_routing_sort,sf_base.group_plan_dispatch,1,0,0,0
|
||||||
access_sf_detection_result_manager,sf_detection_result_manager,model_sf_detection_result,,1,1,1,1
|
access_sf_detection_result_manager,sf_detection_result_manager,model_sf_detection_result,,1,1,1,1
|
||||||
|
|
||||||
|
access_mrp_workorder_batch_replan_wizard_group_plan_dispatch,mrp_workorder_batch_replan_wizard_group_plan_dispatch,model_mrp_workorder_batch_replan_wizard,sf_base.group_plan_dispatch,1,1,1,0
|
||||||
|
|
||||||
|
access_mrp_workorder_group_purchase_director,mrp_workorder,model_mrp_workorder,sf_base.group_purchase_director,1,1,0,0
|
||||||
|
access_mrp_workorder_group_purchase,mrp_workorder,model_mrp_workorder,sf_base.group_purchase,1,1,0,0
|
||||||
|
@@ -0,0 +1,50 @@
|
|||||||
|
odoo.define('sf_manufacturing.agv_scheduling_button_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 agv_scheduling_cancel_confirm(parent, {params}) {
|
||||||
|
const dialog = new Dialog(parent, {
|
||||||
|
title: "确认",
|
||||||
|
$content: $('<div>').append("工件正在配送中,确定取消"),
|
||||||
|
buttons: [
|
||||||
|
{ text: "确认", classes: 'btn-primary', close: true, click: () => dispatchConfirmed(parent, params) },
|
||||||
|
{ text: "取消", close: true },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
dialog.open();
|
||||||
|
|
||||||
|
|
||||||
|
async function dispatchConfirmed(parent, params) {
|
||||||
|
rpc.query({
|
||||||
|
model: 'sf.agv.scheduling',
|
||||||
|
method: 'button_cancel',
|
||||||
|
args: [params.agv_scheduling_id],
|
||||||
|
kwargs: {
|
||||||
|
context: params.context,
|
||||||
|
}
|
||||||
|
}).then(res => {
|
||||||
|
parent.services.action.doAction({
|
||||||
|
'type': 'ir.actions.client',
|
||||||
|
'tag': 'display_notification',
|
||||||
|
'target': 'new',
|
||||||
|
'params': {
|
||||||
|
'message': '取消成功!',
|
||||||
|
'type': 'success',
|
||||||
|
'sticky': false,
|
||||||
|
'next': {'type': 'ir.actions.act_window_close'},
|
||||||
|
}
|
||||||
|
});
|
||||||
|
parent.services.action.doAction({
|
||||||
|
'type': 'ir.actions.client',
|
||||||
|
'tag': 'reload',
|
||||||
|
});
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
core.action_registry.add('agv_scheduling_cancel_confirm', agv_scheduling_cancel_confirm);
|
||||||
|
return agv_scheduling_cancel_confirm;
|
||||||
|
});
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
odoo.define('sf_manufacturing.agv_scheduling_resend_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 agv_scheduling_resend_confirm(parent, {params}) {
|
||||||
|
const dialog = new Dialog(parent, {
|
||||||
|
title: "确认",
|
||||||
|
$content: $('<div>').append("工件正在配送中,确定重新下发"),
|
||||||
|
buttons: [
|
||||||
|
{ text: "确认", classes: 'btn-primary', close: true, click: () => dispatchConfirmed(parent, params) },
|
||||||
|
{ text: "取消", close: true },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
dialog.open();
|
||||||
|
|
||||||
|
|
||||||
|
async function dispatchConfirmed(parent, params) {
|
||||||
|
rpc.query({
|
||||||
|
model: 'sf.agv.scheduling',
|
||||||
|
method: 'button_resend',
|
||||||
|
args: [params.agv_scheduling_id],
|
||||||
|
kwargs: {
|
||||||
|
context: params.context,
|
||||||
|
}
|
||||||
|
}).then(res => {
|
||||||
|
parent.services.action.doAction({
|
||||||
|
'type': 'ir.actions.client',
|
||||||
|
'tag': 'display_notification',
|
||||||
|
'target': 'new',
|
||||||
|
'params': {
|
||||||
|
'message': '重新下发成功!',
|
||||||
|
'type': 'success',
|
||||||
|
'sticky': false,
|
||||||
|
'next': {'type': 'ir.actions.act_window_close'},
|
||||||
|
}
|
||||||
|
});
|
||||||
|
parent.services.action.doAction({
|
||||||
|
'type': 'ir.actions.client',
|
||||||
|
'tag': 'reload',
|
||||||
|
});
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
core.action_registry.add('agv_scheduling_resend_confirm', agv_scheduling_resend_confirm );
|
||||||
|
return agv_scheduling_resend_confirm;
|
||||||
|
});
|
||||||
@@ -22,8 +22,7 @@ odoo.define('sf_manufacturing.action_dispatch_confirm', function (require) {
|
|||||||
rpc.query({
|
rpc.query({
|
||||||
model: 'sf.workpiece.delivery.wizard',
|
model: 'sf.workpiece.delivery.wizard',
|
||||||
method: 'confirm',
|
method: 'confirm',
|
||||||
args: [params.active_id]
|
args: [params.active_id],
|
||||||
,
|
|
||||||
kwargs: {
|
kwargs: {
|
||||||
context: params.context,
|
context: params.context,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,20 @@
|
|||||||
class="btn-danger"
|
class="btn-danger"
|
||||||
confirm="你确定要取消这条记录吗?"
|
confirm="你确定要取消这条记录吗?"
|
||||||
/>
|
/>
|
||||||
|
<button
|
||||||
|
name="button_cancel_confirm"
|
||||||
|
string="取消" type="object"
|
||||||
|
attrs="{'invisible': [('state', '!=', '配送中')]}"
|
||||||
|
icon="fa-times"
|
||||||
|
class="btn-danger"
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
name="button_resend_confirm"
|
||||||
|
string="重新下发" type="object"
|
||||||
|
attrs="{'invisible': [('state', '!=', '配送中')]}"
|
||||||
|
icon="fa-circle-o-notch"
|
||||||
|
class="btn-success "
|
||||||
|
/>
|
||||||
</tree>
|
</tree>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<data>
|
<data>
|
||||||
#------------------模型类型------------------
|
#------------------模型类型------------------
|
||||||
|
|
||||||
<record model="ir.ui.view" id="search_sf_model_type_view">
|
<record model="ir.ui.view" id="search_sf_model_name_view">
|
||||||
<field name="name">search.sf.model.type</field>
|
<field name="name">search.sf.model.type</field>
|
||||||
<field name="model">sf.model.type</field>
|
<field name="model">sf.model.type</field>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record model="ir.ui.view" id="tree_sf_model_type_view">
|
<record model="ir.ui.view" id="tree_sf_model_name_view">
|
||||||
<field name="name">tree.sf.model.type</field>
|
<field name="name">tree.sf.model.type</field>
|
||||||
<field name="model">sf.model.type</field>
|
<field name="model">sf.model.type</field>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
@@ -24,20 +24,20 @@
|
|||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record model="ir.ui.view" id="form_sf_model_type">
|
<record model="ir.ui.view" id="form_sf_model_name">
|
||||||
<field name="name">form.sf.model.type</field>
|
<field name="name">form.sf.model.type</field>
|
||||||
<field name="model">sf.model.type</field>
|
<field name="model">sf.model.type</field>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<form string="模型类型">
|
<form string="模型类型">
|
||||||
<group>
|
<group>
|
||||||
<field name="name" required="1"/>
|
<field name="name" required="1"/>
|
||||||
<field name="embryo_tolerance_id" required="1" string="坯料容余(mm)"/>
|
<field name="embryo_tolerance_id" required="1"/>
|
||||||
</group>
|
</group>
|
||||||
<group>
|
<group>
|
||||||
<field name='product_routing_tmpl_ids'>
|
<field name='product_routing_tmpl_ids'>
|
||||||
<tree editable='bottom'>
|
<tree editable='bottom'>
|
||||||
<field name="sequence" widget="handle" string="序号"/>
|
<field name="sequence" widget="handle" string="序号"/>
|
||||||
<field name="route_workcenter_id" string="工序" options="{'no_create': True}"/>
|
<field name="route_workcenter_id" string="工序" options="{'no_create': True}" context="{'is_duplicate': True, 'model_name': 'sf.product.model.type.routing.sort'}"/>
|
||||||
<field name="routing_type" string="类型"/>
|
<field name="routing_type" string="类型"/>
|
||||||
<field name="is_repeat" string="重复"/>
|
<field name="is_repeat" string="重复"/>
|
||||||
<field name="workcenter_ids" string="工作中心" widget="many2many_tags"/>
|
<field name="workcenter_ids" string="工作中心" widget="many2many_tags"/>
|
||||||
@@ -48,7 +48,7 @@
|
|||||||
<field name='manual_product_routing_tmpl_ids'>
|
<field name='manual_product_routing_tmpl_ids'>
|
||||||
<tree editable='bottom'>
|
<tree editable='bottom'>
|
||||||
<field name="sequence" widget="handle" string="序号"/>
|
<field name="sequence" widget="handle" string="序号"/>
|
||||||
<field name="route_workcenter_id" string="工序" options="{'no_create': True}"/>
|
<field name="route_workcenter_id" string="工序" options="{'no_create': True}" context="{'is_duplicate': True, 'model_name': 'sf.manual.product.model.type.routing.sort'}"/>
|
||||||
<field name="routing_type" string="类型"/>
|
<field name="routing_type" string="类型"/>
|
||||||
<field name="is_repeat" string="重复"/>
|
<field name="is_repeat" string="重复"/>
|
||||||
<field name="workcenter_ids" string="工作中心" widget="many2many_tags"/>
|
<field name="workcenter_ids" string="工作中心" widget="many2many_tags"/>
|
||||||
@@ -59,7 +59,7 @@
|
|||||||
<field name='embryo_routing_tmpl_ids'>
|
<field name='embryo_routing_tmpl_ids'>
|
||||||
<tree editable='bottom'>
|
<tree editable='bottom'>
|
||||||
<field name="sequence" widget="handle" string="序号"/>
|
<field name="sequence" widget="handle" string="序号"/>
|
||||||
<field name="route_workcenter_id" string="工序" options="{'no_create': True}"/>
|
<field name="route_workcenter_id" string="工序" options="{'no_create': True}" context="{'is_duplicate': True, 'model_name': 'sf.embryo.model.type.routing.sort'}"/>
|
||||||
<field name="routing_type" string="类型"/>
|
<field name="routing_type" string="类型"/>
|
||||||
<field name="is_repeat" string="重复"/>
|
<field name="is_repeat" string="重复"/>
|
||||||
<field name="workcenter_ids" string="工作中心" widget="many2many_tags"/>
|
<field name="workcenter_ids" string="工作中心" widget="many2many_tags"/>
|
||||||
@@ -70,7 +70,7 @@
|
|||||||
<field name='surface_technics_routing_tmpl_ids' style="white-space: pre-wrap;">
|
<field name='surface_technics_routing_tmpl_ids' style="white-space: pre-wrap;">
|
||||||
<tree editable='bottom'>
|
<tree editable='bottom'>
|
||||||
<field name="sequence" widget="handle" string="序号"/>
|
<field name="sequence" widget="handle" string="序号"/>
|
||||||
<field name="route_workcenter_id" string="工序" options="{'no_create': True}"/>
|
<field name="route_workcenter_id" string="工序" options="{'no_create': True}" context="{'is_duplicate': True, 'model_name': 'sf.surface_technics.model.type.routing.sort'}"/>
|
||||||
<field name="routing_type" string="类型"/>
|
<field name="routing_type" string="类型"/>
|
||||||
<field name="is_repeat" string="重复"/>
|
<field name="is_repeat" string="重复"/>
|
||||||
<field name="workcenter_ids" string="工作中心" widget="many2many_tags"/>
|
<field name="workcenter_ids" string="工作中心" widget="many2many_tags"/>
|
||||||
@@ -81,7 +81,7 @@
|
|||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record id="action_sf_model_type" model="ir.actions.act_window">
|
<record id="action_sf_model_name" model="ir.actions.act_window">
|
||||||
<field name="name">模型类型</field>
|
<field name="name">模型类型</field>
|
||||||
<field name="type">ir.actions.act_window</field>
|
<field name="type">ir.actions.act_window</field>
|
||||||
<field name="res_model">sf.model.type</field>
|
<field name="res_model">sf.model.type</field>
|
||||||
@@ -96,11 +96,11 @@
|
|||||||
</record>
|
</record>
|
||||||
|
|
||||||
<menuitem
|
<menuitem
|
||||||
id="menu_sf_model_type"
|
id="menu_sf_model_name"
|
||||||
name="模型类型"
|
name="模型类型"
|
||||||
parent="mrp.menu_mrp_configuration"
|
parent="mrp.menu_mrp_configuration"
|
||||||
sequence="9"
|
sequence="9"
|
||||||
action="action_sf_model_type"
|
action="action_sf_model_name"
|
||||||
/>
|
/>
|
||||||
</data>
|
</data>
|
||||||
</odoo>
|
</odoo>
|
||||||
@@ -18,9 +18,12 @@
|
|||||||
<xpath expr="//field[@name='date_deadline']" position="replace"/>
|
<xpath expr="//field[@name='date_deadline']" position="replace"/>
|
||||||
<xpath expr="//field[@name='name']" position="after">
|
<xpath expr="//field[@name='name']" position="after">
|
||||||
<field name="product_id" readonly="1" optional="show"/>
|
<field name="product_id" readonly="1" optional="show"/>
|
||||||
|
<field name="part_name" readonly="1" optional="hide"/>
|
||||||
|
<field name="part_number" readonly="1" optional="show"/>
|
||||||
</xpath>
|
</xpath>
|
||||||
<xpath expr="//field[@name='product_id']" position="after">
|
<xpath expr="//field[@name='product_id']" position="after">
|
||||||
<field name="product_qty" sum="Total Qty" string="数量" readonly="1" optional="show"/>
|
<field name="product_qty" sum="Total Qty" string="数量" readonly="1" optional="show"/>
|
||||||
|
<field name="deadline_of_delivery" optional="show"/>
|
||||||
</xpath>
|
</xpath>
|
||||||
<xpath expr="//field[@name='product_qty']" position="after">
|
<xpath expr="//field[@name='product_qty']" position="after">
|
||||||
<field name="product_uom_id" string="计量单位" options="{'no_open':True,'no_create':True}"
|
<field name="product_uom_id" string="计量单位" options="{'no_open':True,'no_create':True}"
|
||||||
@@ -43,6 +46,7 @@
|
|||||||
<field name="activity_ids" string="下一个活动" widget="list_activity" optional="hide"/>
|
<field name="activity_ids" string="下一个活动" widget="list_activity" optional="hide"/>
|
||||||
</xpath>
|
</xpath>
|
||||||
<xpath expr="//field[@name='origin']" position="replace">
|
<xpath expr="//field[@name='origin']" position="replace">
|
||||||
|
<field name="sale_order_id" optional="show"/>
|
||||||
<field name="origin" optional="hide"/>
|
<field name="origin" optional="hide"/>
|
||||||
</xpath>
|
</xpath>
|
||||||
<xpath expr="//field[@name='components_availability']" position="replace">
|
<xpath expr="//field[@name='components_availability']" position="replace">
|
||||||
@@ -120,16 +124,13 @@
|
|||||||
<!-- <field name="production_line_state" readonly="1"/>-->
|
<!-- <field name="production_line_state" readonly="1"/>-->
|
||||||
<field name="part_name"/>
|
<field name="part_name"/>
|
||||||
<field name="part_number" string="零件图号"/>
|
<field name="part_number" string="零件图号"/>
|
||||||
<field name="tool_state" attrs="{'invisible': [('production_type', 'not in', ['自动化产线加工'])]}"/>
|
<field name="tool_state"
|
||||||
|
attrs="{'invisible': [('production_type', 'not in', ['自动化产线加工'])]}"/>
|
||||||
<field name="tool_state_remark" string="备注" attrs="{'invisible': [('tool_state', '!=', '1')]}"/>
|
<field name="tool_state_remark" string="备注" attrs="{'invisible': [('tool_state', '!=', '1')]}"/>
|
||||||
|
<field name="sale_order_id" readonly="1"/>
|
||||||
<field name="deadline_of_delivery" readonly="1"/>
|
<field name="deadline_of_delivery" readonly="1"/>
|
||||||
<field name="tool_state_remark2" invisible="1"/>
|
<field name="tool_state_remark2" invisible="1"/>
|
||||||
</xpath>
|
</xpath>
|
||||||
<xpath expr="//header//button[@name='action_cancel']" position="replace">
|
|
||||||
<button name="action_cancel" type="object" string="取消" data-hotkey="z"
|
|
||||||
attrs="{'invisible': ['|', '|', ('id', '=', False), ('state', 'in', ('done', 'cancel')), ('confirm_cancel', '=', True)]}"
|
|
||||||
groups="sf_base.group_sf_mrp_user"/>
|
|
||||||
</xpath>
|
|
||||||
<xpath expr="(//header//button[@name='button_mark_done'])[1]" position="replace">
|
<xpath expr="(//header//button[@name='button_mark_done'])[1]" position="replace">
|
||||||
<button name="button_mark_done"
|
<button name="button_mark_done"
|
||||||
attrs="{'invisible': ['|', '|', ('state', 'in', ('draft', 'cancel', 'done', 'to_close')), ('qty_producing', '=', 0), ('move_raw_ids', '!=', [])]}"
|
attrs="{'invisible': ['|', '|', ('state', 'in', ('draft', 'cancel', 'done', 'to_close')), ('qty_producing', '=', 0), ('move_raw_ids', '!=', [])]}"
|
||||||
@@ -254,13 +255,6 @@
|
|||||||
type="object" groups="sf_base.group_sf_mrp_user"/>
|
type="object" groups="sf_base.group_sf_mrp_user"/>
|
||||||
</xpath>
|
</xpath>
|
||||||
|
|
||||||
<xpath expr="//header//button[@name='action_cancel']" position="replace">
|
|
||||||
<button name="action_cancel" type="object" string="Cancel" data-hotkey="z"
|
|
||||||
attrs="{'invisible': ['|', '|', ('id', '=', False), ('state', 'in', ('done', 'cancel')), ('confirm_cancel', '=', False)]}"
|
|
||||||
confirm="Some product moves have already been confirmed, this manufacturing order can't be completely cancelled. Are you still sure you want to process ?"
|
|
||||||
groups="sf_base.group_sf_mrp_user"/>
|
|
||||||
</xpath>
|
|
||||||
|
|
||||||
<xpath expr="//header//button[@name='button_unbuild']" position="replace">
|
<xpath expr="//header//button[@name='button_unbuild']" position="replace">
|
||||||
<button name="button_unbuild" type="object" string="拆单"
|
<button name="button_unbuild" type="object" string="拆单"
|
||||||
attrs="{'invisible': [('state', '!=', 'done')]}" data-hotkey="shift+v"
|
attrs="{'invisible': [('state', '!=', 'done')]}" data-hotkey="shift+v"
|
||||||
@@ -304,12 +298,14 @@
|
|||||||
type="object" groups="sf_base.group_sf_mrp_user"/>
|
type="object" groups="sf_base.group_sf_mrp_user"/>
|
||||||
</xpath>
|
</xpath>
|
||||||
|
|
||||||
<xpath expr="//header//button[@name='action_cancel']" position="replace">
|
<xpath expr="//header//button[@name='action_cancel'][2]" position="replace">
|
||||||
<button name="action_cancel" type="object" string="取消" data-hotkey="z"
|
<button name="action_cancel" type="object" string="取消" data-hotkey="z"
|
||||||
attrs="{'invisible': ['|', '|', ('id', '=', False), ('state', 'in', ('done', 'cancel')), ('confirm_cancel', '=', False)]}"
|
attrs="{'invisible': ['|', '|', ('id', '=', False), ('state', 'in', ('done', 'rework', 'cancel')), ('confirm_cancel', '=', False)]}"
|
||||||
confirm="Some product moves have already been confirmed, this manufacturing order can't be completely cancelled. Are you still sure you want to process ?"
|
confirm="Some product moves have already been confirmed, this manufacturing order can't be completely cancelled. Are you still sure you want to process ?"
|
||||||
groups="sf_base.group_sf_mrp_user"/>
|
groups="sf_base.group_sf_mrp_user"/>
|
||||||
</xpath>
|
</xpath>
|
||||||
|
<xpath expr="//header//button[@name='action_cancel'][1]" position="replace"></xpath>
|
||||||
|
|
||||||
<xpath expr="//header//button[@name='button_unbuild']" position="replace">
|
<xpath expr="//header//button[@name='button_unbuild']" position="replace">
|
||||||
<button name="button_unbuild" type="object" string="拆单"
|
<button name="button_unbuild" type="object" string="拆单"
|
||||||
attrs="{'invisible': [('state', '!=', 'done')]}" data-hotkey="shift+v"
|
attrs="{'invisible': [('state', '!=', 'done')]}" data-hotkey="shift+v"
|
||||||
@@ -365,8 +361,10 @@
|
|||||||
<field name="sequence" widget="handle"/>
|
<field name="sequence" widget="handle"/>
|
||||||
<field name="route_id" context="{'production_id': production_id}"
|
<field name="route_id" context="{'production_id': production_id}"
|
||||||
attrs="{'readonly': [('id', '!=', False)]}" options="{'no_create': True}"/>
|
attrs="{'readonly': [('id', '!=', False)]}" options="{'no_create': True}"/>
|
||||||
<field name="process_parameters_id" attrs="{'readonly': [('id', '!=', False)]}"
|
<field name="process_parameters_id"
|
||||||
string="参数" context="{'route_id':route_id}" options="{'no_create': True}"/>
|
attrs="{'readonly': [('id', '!=', False),('routing_tag', '=', 'standard')]}"
|
||||||
|
string="参数" context="{'route_id':route_id,'production_id': production_id}"
|
||||||
|
options="{'no_create': True}"/>
|
||||||
<field name="panel" readonly="1"/>
|
<field name="panel" readonly="1"/>
|
||||||
<field name="routing_tag" readonly="1" widget="badge"
|
<field name="routing_tag" readonly="1" widget="badge"
|
||||||
decoration-success="routing_tag == 'standard'"
|
decoration-success="routing_tag == 'standard'"
|
||||||
@@ -387,13 +385,16 @@
|
|||||||
</page>
|
</page>
|
||||||
|
|
||||||
</xpath>
|
</xpath>
|
||||||
<xpath expr="//sheet/group/group/div[@class='d-flex flex-row align-items-start']/span[last()]" position="attributes">
|
<xpath expr="//sheet/group/group/div[@class='d-flex flex-row align-items-start']/span[last()]"
|
||||||
|
position="attributes">
|
||||||
<attribute name="invisible">True</attribute>
|
<attribute name="invisible">True</attribute>
|
||||||
</xpath>
|
</xpath>
|
||||||
<xpath expr="//sheet/group/group/div[@class='d-flex flex-row align-items-start']/button[@name='action_product_forecast_report']" position="attributes">
|
<xpath expr="//sheet/group/group/div[@class='d-flex flex-row align-items-start']/button[@name='action_product_forecast_report']"
|
||||||
|
position="attributes">
|
||||||
<attribute name="invisible">True</attribute>
|
<attribute name="invisible">True</attribute>
|
||||||
</xpath>
|
</xpath>
|
||||||
<xpath expr="//sheet/div[@class='oe_button_box']/button[@name='action_view_mrp_production_childs']/div/span[last()]" position="replace">
|
<xpath expr="//sheet/div[@class='oe_button_box']/button[@name='action_view_mrp_production_childs']/div/span[last()]"
|
||||||
|
position="replace">
|
||||||
<span class="o_stat_text">子MO</span>
|
<span class="o_stat_text">子MO</span>
|
||||||
</xpath>
|
</xpath>
|
||||||
</field>
|
</field>
|
||||||
@@ -433,9 +434,7 @@
|
|||||||
<xpath expr="//tree//button[@name='button_start']" position="replace">
|
<xpath expr="//tree//button[@name='button_start']" position="replace">
|
||||||
<field name="routing_type" invisible="True"/>
|
<field name="routing_type" invisible="True"/>
|
||||||
<button name="button_start" type="object" string="开始" class="btn-success" confirm="是否确认开始?"
|
<button name="button_start" type="object" string="开始" class="btn-success" confirm="是否确认开始?"
|
||||||
attrs="{'invisible': ['|', '|', '|','|', ('production_state','in', ('draft', 'done', 'cancel')),
|
attrs="{'invisible': [('state', '!=', 'ready')]}"
|
||||||
('working_state', '=', 'blocked'), ('state', 'in', ('done', 'cancel')), ('is_user_working', '!=',
|
|
||||||
False), ('routing_type', '=', 'CNC加工')]}"
|
|
||||||
groups="sf_base.group_sf_mrp_user"/>
|
groups="sf_base.group_sf_mrp_user"/>
|
||||||
</xpath>
|
</xpath>
|
||||||
<xpath expr="//tree//button[@name='button_pending']" position="replace">
|
<xpath expr="//tree//button[@name='button_pending']" position="replace">
|
||||||
@@ -578,6 +577,7 @@
|
|||||||
<searchpanel>
|
<searchpanel>
|
||||||
<field name="state" icon="fa-filter" enable_counters="1"/>
|
<field name="state" icon="fa-filter" enable_counters="1"/>
|
||||||
<field name="delivery_status" icon="fa-filter" enable_counters="1"/>
|
<field name="delivery_status" icon="fa-filter" enable_counters="1"/>
|
||||||
|
<field name="production_type" icon="fa-filter" enable_counters="1"/>
|
||||||
</searchpanel>
|
</searchpanel>
|
||||||
</xpath>
|
</xpath>
|
||||||
<filter name='todo' position="replace"/>
|
<filter name='todo' position="replace"/>
|
||||||
@@ -732,5 +732,19 @@
|
|||||||
<!-- parent="mrp.menu_mrp_manufacturing"-->
|
<!-- parent="mrp.menu_mrp_manufacturing"-->
|
||||||
<!-- sequence="1"/>-->
|
<!-- sequence="1"/>-->
|
||||||
|
|
||||||
|
<menuitem id="mrp.menu_mrp_production_action"
|
||||||
|
name="制造订单"
|
||||||
|
parent="mrp.menu_mrp_manufacturing"
|
||||||
|
action="mrp.mrp_production_action"
|
||||||
|
groups="sf_base.group_plan_dispatch,sf_base.group_sf_mrp_manager"
|
||||||
|
sequence="1"/>
|
||||||
|
|
||||||
|
<menuitem id="stock_picking_type_menu"
|
||||||
|
name="驾驶舱"
|
||||||
|
parent="stock.menu_stock_root"
|
||||||
|
action="stock.stock_picking_type_action"
|
||||||
|
groups="sf_base.group_sf_stock_user"
|
||||||
|
sequence="0"/>
|
||||||
|
|
||||||
</data>
|
</data>
|
||||||
</odoo>
|
</odoo>
|
||||||
18
sf_manufacturing/views/mrp_workorder_batch_replan.xml
Normal file
18
sf_manufacturing/views/mrp_workorder_batch_replan.xml
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<odoo>
|
||||||
|
<data>
|
||||||
|
<record id="mrp_production_workorder_tree_editable_view_inherit" model="ir.ui.view">
|
||||||
|
<field name="name">>mrp.workorder.tree.editable.inherit</field>
|
||||||
|
<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/field[1]" position="before">
|
||||||
|
<header>
|
||||||
|
<button string="重新安排" name="%(sf_manufacturing.mrp_workorder_batch_replan_wizard)d" type="action"
|
||||||
|
class="treeHeaderBtn"
|
||||||
|
invisible="context.get('workorder_type') not in ('工件装夹中心','1#自动生产线','工件拆卸中心')"/>
|
||||||
|
</header>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
@@ -29,9 +29,12 @@
|
|||||||
</field>
|
</field>
|
||||||
<field name="product_id" position="after">
|
<field name="product_id" position="after">
|
||||||
<field name="equipment_id" optional="hide"/>
|
<field name="equipment_id" optional="hide"/>
|
||||||
|
<field name="part_name" optional="hide"/>
|
||||||
|
<field name="part_number" optional="show"/>
|
||||||
</field>
|
</field>
|
||||||
<xpath expr="//field[@name='qty_remaining']" position="after">
|
<xpath expr="//field[@name='qty_remaining']" position="after">
|
||||||
<field name="manual_quotation" optional="show"/>
|
<field name="manual_quotation" optional="show"/>
|
||||||
|
<field name="tag_type" optional="show"/>
|
||||||
<field name="construction_period_status" optional="show" widget="badge"
|
<field name="construction_period_status" optional="show" widget="badge"
|
||||||
decoration-success="construction_period_status == '正常'"
|
decoration-success="construction_period_status == '正常'"
|
||||||
decoration-warning="construction_period_status == '预警'"
|
decoration-warning="construction_period_status == '预警'"
|
||||||
@@ -52,10 +55,7 @@
|
|||||||
<!-- 'cancel')), ('working_state', '=', 'blocked'), ('state', 'in', ('done', 'cancel')),-->
|
<!-- '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","=","CNC加工")]}-->
|
||||||
<!-- </attribute>-->
|
<!-- </attribute>-->
|
||||||
<attribute name="attrs">{'invisible': ['|', '|', '|','|','|', ('production_state','in', ('draft',
|
<attribute name="attrs">{'invisible': [('state', '!=', 'ready')]}
|
||||||
'done',
|
|
||||||
'cancel')), ('working_state', '=', 'blocked'), ('state', 'in', ('done', 'cancel')),
|
|
||||||
('is_user_working', '!=', False),("user_permissions","=",False),("name","in",("CNC加工","解除装夹"))]}
|
|
||||||
</attribute>
|
</attribute>
|
||||||
</xpath>
|
</xpath>
|
||||||
<xpath expr="//button[@name='%(mrp.act_mrp_block_workcenter_wo)d']" position="attributes">
|
<xpath expr="//button[@name='%(mrp.act_mrp_block_workcenter_wo)d']" position="attributes">
|
||||||
@@ -103,8 +103,7 @@
|
|||||||
<!-- <field name="target">fullscreen</field>-->
|
<!-- <field name="target">fullscreen</field>-->
|
||||||
<field name="target">current</field>
|
<field name="target">current</field>
|
||||||
<field name="domain">[('state', '!=', 'cancel'),('schedule_state', '=', '已排')]</field>
|
<field name="domain">[('state', '!=', 'cancel'),('schedule_state', '=', '已排')]</field>
|
||||||
<field name="context">{'search_default_product': 1, 'search_default_workcenter_id':
|
<field name="context">{'search_default_product': 1, 'search_default_workcenter_id': active_id}
|
||||||
active_id,'search_default_filter_order_warning':1,'search_default_filter_order_overdue':1,'search_default_filter_order_normal':1}
|
|
||||||
</field>
|
</field>
|
||||||
<field name="help" type="html">
|
<field name="help" type="html">
|
||||||
<p class="o_view_nocontent_workorder">
|
<p class="o_view_nocontent_workorder">
|
||||||
@@ -138,7 +137,7 @@
|
|||||||
<button type="object" name="action_view_surface_technics_purchase" class="oe_stat_button"
|
<button type="object" name="action_view_surface_technics_purchase" class="oe_stat_button"
|
||||||
icon="fa-credit-card"
|
icon="fa-credit-card"
|
||||||
groups="base.group_user,sf_base.group_sf_order_user"
|
groups="base.group_user,sf_base.group_sf_order_user"
|
||||||
attrs="{'invisible': [('surface_technics_purchase_count', '=', 0),('routing_type', '!=', '表面工艺')]}">
|
attrs="{'invisible': [('surface_technics_purchase_count', '=', 0)]}">
|
||||||
<div class="o_field_widget o_stat_info">
|
<div class="o_field_widget o_stat_info">
|
||||||
<span class="o_stat_value">
|
<span class="o_stat_value">
|
||||||
<field name="surface_technics_purchase_count"/>
|
<field name="surface_technics_purchase_count"/>
|
||||||
@@ -174,9 +173,7 @@
|
|||||||
<!-- <button name="button_start" type="object" string="开始" class="btn-success" 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)]}"/>-->
|
<!-- 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="是否确认开始"
|
<button name="button_start" type="object" string="开始" class="btn-success" confirm="是否确认开始"
|
||||||
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)]}"/>
|
attrs="{'invisible': [('state', '!=', 'ready')]}"/>
|
||||||
<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"
|
<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)]}"/>
|
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="是否确认完工"
|
<button name="button_finish" type="object" string="完成" class="btn-success" confirm="是否确认完工"
|
||||||
@@ -251,7 +248,7 @@
|
|||||||
decoration-danger="tag_type == '重新加工'"/>
|
decoration-danger="tag_type == '重新加工'"/>
|
||||||
<field name="is_test_env" invisible="1"/>
|
<field name="is_test_env" invisible="1"/>
|
||||||
<field name="rfid_code" force_save="1" readonly="1" cache="True"
|
<field name="rfid_code" force_save="1" readonly="1" cache="True"
|
||||||
attrs="{'invisible': [('rfid_code_old', '!=', False)]}" />
|
attrs="{'invisible': [('rfid_code_old', '!=', False)]}" widget="qrcode_widget"/>
|
||||||
<field name="rfid_code" string="RFID码(手动输入框)" force_save="1" readonly="0" cache="True"
|
<field name="rfid_code" string="RFID码(手动输入框)" force_save="1" readonly="0" cache="True"
|
||||||
attrs="{'invisible': ['|',('rfid_code_old', '!=', False), ('is_test_env', '=', False)]}"/>
|
attrs="{'invisible': ['|',('rfid_code_old', '!=', False), ('is_test_env', '=', False)]}"/>
|
||||||
<field name="rfid_code_old" readonly="1" attrs="{'invisible': [('rfid_code_old', '=', False)]}"/>
|
<field name="rfid_code_old" readonly="1" attrs="{'invisible': [('rfid_code_old', '=', False)]}"/>
|
||||||
|
|||||||
35
sf_manufacturing/views/purchase_order_view.xml
Normal file
35
sf_manufacturing/views/purchase_order_view.xml
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<odoo>
|
||||||
|
<data>
|
||||||
|
<record model="ir.ui.view" id="view_purchase_order_line_form_inherit_sf1">
|
||||||
|
<field name="name">purchase.order.form.inherit.sf</field>
|
||||||
|
<field name="model">purchase.order</field>
|
||||||
|
<field name="inherit_id" ref="purchase.purchase_order_form"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//field[@name='order_line']/tree/field[@name='name']" position="after">
|
||||||
|
<field name="related_product" optional="show"/>
|
||||||
|
<field name="part_number" optional="show"/>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//sheet//div[@class='oe_button_box']" position="inside">
|
||||||
|
<button class="oe_stat_button" name="action_view_production" type="object" icon="fa-wrench"
|
||||||
|
attrs="{'invisible': [('production_count', '=', 0)]}"
|
||||||
|
>
|
||||||
|
<div class="o_field_widget o_stat_info">
|
||||||
|
<span class="o_stat_value">
|
||||||
|
<field name="production_count"/>
|
||||||
|
</span>
|
||||||
|
<span class="o_stat_text">制造订单</span>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
</xpath>
|
||||||
|
<!-- 添加销售订单号字段-->
|
||||||
|
<xpath expr="//sheet/group/group[2]/div[@name='date_approve']" position="after">
|
||||||
|
<field name="origin_sale_id" readonly="1" string="参考销售订单"
|
||||||
|
attrs="{'invisible': [('origin_sale_id' , '=', False)]}"/>
|
||||||
|
<field name="origin_sale_ids" readonly="1" string="参考销售订单" widget="many2many_tags"
|
||||||
|
attrs="{'invisible': [('origin_sale_ids' , '=', [])]}"/>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
@@ -1,17 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" ?>
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
<odoo>
|
<odoo>
|
||||||
<record id="view_order_form_inherit_sf" model="ir.ui.view">
|
|
||||||
<field name="name">view.sale.order.form.inherit.sf</field>
|
|
||||||
<field name="inherit_id" ref="sale_stock.view_order_form_inherit_sale_stock_qty"/>
|
|
||||||
<field name="model">sale.order</field>
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<xpath expr="//page/field[@name='order_line']/form/group/group/div[@name='ordered_qty']/widget[@name='qty_at_date_widget']" position="replace">
|
|
||||||
</xpath>
|
|
||||||
<xpath expr="//page/field[@name='order_line']/tree/widget[@name='qty_at_date_widget']" position="replace">
|
|
||||||
</xpath>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record id="view_order_form_inherit_supply_method" model="ir.ui.view">
|
<record id="view_order_form_inherit_supply_method" model="ir.ui.view">
|
||||||
<field name="name">view.sale.order.form.inherit.supply.method</field>
|
<field name="name">view.sale.order.form.inherit.supply.method</field>
|
||||||
<field name="inherit_id" ref="sf_sale.view_sale_order_form_inherit_sf"/>
|
<field name="inherit_id" ref="sf_sale.view_sale_order_form_inherit_sf"/>
|
||||||
@@ -25,11 +13,15 @@
|
|||||||
<button name="action_confirm" string="供货方式确认" type="object" attrs="{'invisible': [('state', '!=', 'supply method')]}" confirm="确认供货方式"/>
|
<button name="action_confirm" string="供货方式确认" type="object" attrs="{'invisible': [('state', '!=', 'supply method')]}" confirm="确认供货方式"/>
|
||||||
</xpath>
|
</xpath>
|
||||||
<xpath expr="//header/field[@name='state']" position="attributes">
|
<xpath expr="//header/field[@name='state']" position="attributes">
|
||||||
<attribute name="statusbar_visible">draft,sent,supply method,sale</attribute>
|
<attribute name="statusbar_visible">supply method,sale,processing,physical_distribution,delivered</attribute>
|
||||||
</xpath>
|
</xpath>
|
||||||
<xpath expr="//page/field[@name='order_line']/tree/field[@name='remark']" position="before">
|
<xpath expr="//page/field[@name='order_line']/tree/field[@name='remark']" position="before">
|
||||||
<field name="supply_method" attrs="{'invisible': [('state', '=', 'draft')], 'required': [('state', '=', 'supply method')]}" />
|
<field name="supply_method" attrs="{'invisible': [('state', '=', 'draft')], 'required': [('state', '=', 'supply method')]}" />
|
||||||
</xpath>
|
</xpath>
|
||||||
|
<xpath expr="//field[@name='order_line']/tree/field[@name='model_glb_file']" position="before">
|
||||||
|
<field name="part_number" optional="show"/>
|
||||||
|
</xpath>
|
||||||
|
|
||||||
<xpath expr="//header/button[@name='action_cancel']" position="attributes">
|
<xpath expr="//header/button[@name='action_cancel']" position="attributes">
|
||||||
<attribute name="attrs">{'invisible': [('state', '!=', 'draft')]}</attribute>
|
<attribute name="attrs">{'invisible': [('state', '!=', 'draft')]}</attribute>
|
||||||
</xpath>
|
</xpath>
|
||||||
@@ -102,7 +94,5 @@
|
|||||||
<record id="sale.res_partner_menu" model="ir.ui.menu">
|
<record id="sale.res_partner_menu" model="ir.ui.menu">
|
||||||
<field name="groups_id" eval="[(4, ref('sf_base.group_production_engineer'))]"/>
|
<field name="groups_id" eval="[(4, ref('sf_base.group_production_engineer'))]"/>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</odoo>
|
</odoo>
|
||||||
@@ -17,6 +17,12 @@
|
|||||||
<field name="model">stock.picking</field>
|
<field name="model">stock.picking</field>
|
||||||
<field name="inherit_id" ref="stock.view_picking_form"/>
|
<field name="inherit_id" ref="stock.view_picking_form"/>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//field[@name='backorder_id']" position="after">
|
||||||
|
<field name="retrospect_ref"/>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//field[@name='origin']" position="after">
|
||||||
|
<field name="sale_order_id"/>
|
||||||
|
</xpath>
|
||||||
<xpath expr="//field[@name='user_id']" position="after">
|
<xpath expr="//field[@name='user_id']" position="after">
|
||||||
<field name="picking_type_sequence_code" invisible="1"/>
|
<field name="picking_type_sequence_code" invisible="1"/>
|
||||||
<field name="retrospect_ref"
|
<field name="retrospect_ref"
|
||||||
@@ -26,6 +32,10 @@
|
|||||||
attrs="{'invisible':[('picking_type_sequence_code','!=','DL')]}"/>
|
attrs="{'invisible':[('picking_type_sequence_code','!=','DL')]}"/>
|
||||||
<field name="address_of_delivery" attrs="{'invisible':[('picking_type_sequence_code','!=','DL')]}"/>
|
<field name="address_of_delivery" attrs="{'invisible':[('picking_type_sequence_code','!=','DL')]}"/>
|
||||||
</xpath>
|
</xpath>
|
||||||
|
<xpath expr="//field[@name='product_id']" position="after">
|
||||||
|
<field name="part_number" optional="show"/>
|
||||||
|
<field name="part_name" optional="show"/>
|
||||||
|
</xpath>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
@@ -34,6 +44,9 @@
|
|||||||
<field name="model">stock.picking</field>
|
<field name="model">stock.picking</field>
|
||||||
<field name="inherit_id" ref="stock.vpicktree"/>
|
<field name="inherit_id" ref="stock.vpicktree"/>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//field[@name='origin']" position="before">
|
||||||
|
<field name="sale_order_id" string="销售单号"/>
|
||||||
|
</xpath>
|
||||||
<xpath expr="//field[@name='origin']" position="after">
|
<xpath expr="//field[@name='origin']" position="after">
|
||||||
<field name="retrospect_ref"/>
|
<field name="retrospect_ref"/>
|
||||||
</xpath>
|
</xpath>
|
||||||
@@ -45,11 +58,21 @@
|
|||||||
<field name="model">stock.picking</field>
|
<field name="model">stock.picking</field>
|
||||||
<field name="inherit_id" ref="stock.view_picking_internal_search"/>
|
<field name="inherit_id" ref="stock.view_picking_internal_search"/>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//field[@name='name']" position="replace">
|
||||||
|
<field name="name"/>
|
||||||
|
<field name="retrospect_ref"/>
|
||||||
|
<field name="sale_order_id" string="销售单号"/>
|
||||||
|
</xpath>
|
||||||
<xpath expr="//filter[@name='picking_type']" position="after">
|
<xpath expr="//filter[@name='picking_type']" position="after">
|
||||||
<filter string="追溯参考" name="retrospect_ref" domain="[]"
|
<filter string="追溯参考" name="retrospect" domain="[]"
|
||||||
context="{'group_by': 'retrospect_ref'}"/>
|
context="{'group_by': 'retrospect_ref'}"/>
|
||||||
</xpath>
|
</xpath>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
|
<record id="stock.action_picking_tree_all" model="ir.actions.act_window">
|
||||||
|
<field name="view_ids" eval="[(5, 0, 0),
|
||||||
|
(0, 0, {'view_mode': 'tree', 'view_id': ref('stock.vpicktree')})]"/>
|
||||||
|
</record>
|
||||||
</data>
|
</data>
|
||||||
</odoo>
|
</odoo>
|
||||||
@@ -3,3 +3,4 @@ from . import rework_wizard
|
|||||||
from . import production_wizard
|
from . import production_wizard
|
||||||
from . import production_technology_wizard
|
from . import production_technology_wizard
|
||||||
from . import production_technology_re_adjust_wizard
|
from . import production_technology_re_adjust_wizard
|
||||||
|
from . import mrp_workorder_batch_replan_wizard
|
||||||
|
|||||||
70
sf_manufacturing/wizard/mrp_workorder_batch_replan_wizard.py
Normal file
70
sf_manufacturing/wizard/mrp_workorder_batch_replan_wizard.py
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import logging
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
from odoo import models, api, fields, _
|
||||||
|
|
||||||
|
|
||||||
|
class MrpWorkorderBatchReplanWizard(models.TransientModel):
|
||||||
|
_name = 'mrp.workorder.batch.replan.wizard'
|
||||||
|
_description = '制造订单批量重新安排向导'
|
||||||
|
|
||||||
|
def _get_date_planned_start(self):
|
||||||
|
planned_start_date = datetime.now() + timedelta(hours=2)
|
||||||
|
logging.info('计划开始加工时间: %s', planned_start_date)
|
||||||
|
return planned_start_date
|
||||||
|
|
||||||
|
def _get_default_workorder_count(self):
|
||||||
|
active_ids = self.env.context.get('active_ids', [])
|
||||||
|
return len(active_ids)
|
||||||
|
|
||||||
|
def _get_default_workorder_type(self):
|
||||||
|
active_ids = self.env.context.get('active_ids', [])
|
||||||
|
if active_ids:
|
||||||
|
workorders = self.env['mrp.workorder'].browse(active_ids)
|
||||||
|
if workorders:
|
||||||
|
routing_type = set(workorders.mapped('routing_type'))
|
||||||
|
return '/'.join(sorted(routing_type)) if routing_type else None
|
||||||
|
return None
|
||||||
|
|
||||||
|
workorder_type = fields.Char(string='工单类型', default=_get_default_workorder_type, readonly=True)
|
||||||
|
|
||||||
|
workorder_count = fields.Integer(string='工单数量',
|
||||||
|
default=_get_default_workorder_count,
|
||||||
|
readonly=True)
|
||||||
|
|
||||||
|
workorder_id = fields.Many2many('mrp.workorder', string=u'工单')
|
||||||
|
|
||||||
|
def confirm(self):
|
||||||
|
routing_type = set(self.workorder_id.mapped('routing_type'))
|
||||||
|
if len(routing_type) > 1:
|
||||||
|
raise models.ValidationError("批量重新安排工单类型必须一致。")
|
||||||
|
show_json_popover = self.workorder_id.mapped('show_json_popover')
|
||||||
|
if any(not value for value in show_json_popover):
|
||||||
|
raise models.ValidationError("所选工单必须都为逾期状态")
|
||||||
|
failed_workorders = {}
|
||||||
|
for workorder_info in self.workorder_id:
|
||||||
|
try:
|
||||||
|
workorder_info.action_replan()
|
||||||
|
except Exception as e:
|
||||||
|
reason = str(e)
|
||||||
|
if reason in failed_workorders:
|
||||||
|
failed_workorders[reason].append(
|
||||||
|
workorder_info.production_id.name)
|
||||||
|
else:
|
||||||
|
failed_workorders[reason] = [workorder_info.production_id.name]
|
||||||
|
if failed_workorders:
|
||||||
|
error_messages = "\n".join(
|
||||||
|
[f"制造订单: {', '.join(workorder_names)}, 原因: {reason}" for reason, workorder_names in
|
||||||
|
failed_workorders.items()])
|
||||||
|
return {
|
||||||
|
'type': 'ir.actions.client',
|
||||||
|
'tag': 'display_notification',
|
||||||
|
'params': {
|
||||||
|
'message': f"以下工单重新安排失败:\n{error_messages}",
|
||||||
|
'sticky': False,
|
||||||
|
'color': 'red',
|
||||||
|
'next': {
|
||||||
|
'type': 'ir.actions.act_window_close'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<odoo>
|
||||||
|
<record id="mrp_workorder_batch_replan_wizard_form" model="ir.ui.view">
|
||||||
|
<field name="name">mrp.workorder.batch.replan.wizard.form.view</field>
|
||||||
|
<field name="model">mrp.workorder.batch.replan.wizard</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<form>
|
||||||
|
<group>
|
||||||
|
<field name="workorder_type"/>
|
||||||
|
<field name="workorder_count"/>
|
||||||
|
</group>
|
||||||
|
<footer>
|
||||||
|
<button string="确认" name="confirm" type="object" class="oe_highlight"/>
|
||||||
|
<button string="取消" class="btn-primary" special="cancel"/>
|
||||||
|
</footer>
|
||||||
|
</form>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
|
||||||
|
<record id="mrp_workorder_batch_replan_wizard" model="ir.actions.act_window">
|
||||||
|
<field name="name">重新安排工单</field>
|
||||||
|
<field name="type">ir.actions.act_window</field>
|
||||||
|
<field name="res_model">mrp.workorder.batch.replan.wizard</field>
|
||||||
|
<field name="view_mode">form</field>
|
||||||
|
<field name="view_id" ref="mrp_workorder_batch_replan_wizard_form"/>
|
||||||
|
<field name="target">new</field>
|
||||||
|
<field name="context">{'default_workorder_id': active_ids}</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
</odoo>
|
||||||
@@ -10,11 +10,11 @@ class ProductionTechnologyReAdjustWizard(models.TransientModel):
|
|||||||
|
|
||||||
production_id = fields.Many2one('mrp.production', string='制造订单号')
|
production_id = fields.Many2one('mrp.production', string='制造订单号')
|
||||||
origin = fields.Char(string='源单据')
|
origin = fields.Char(string='源单据')
|
||||||
is_technology_re_adjust = fields.Boolean(default=False)
|
is_technology_re_adjust = fields.Boolean(default=True)
|
||||||
|
|
||||||
def confirm(self):
|
def confirm(self):
|
||||||
if self.is_technology_re_adjust is True:
|
if self.is_technology_re_adjust is True:
|
||||||
domain = [('origin', '=', self.origin), ('state', '=', 'technology_to_confirmed'),
|
domain = [('origin', '=', self.origin), ('state', '=', 'confirmed'),
|
||||||
('product_id', '=', self.production_id.product_id.id)]
|
('product_id', '=', self.production_id.product_id.id)]
|
||||||
else:
|
else:
|
||||||
domain = [('id', '=', self.production_id.id)]
|
domain = [('id', '=', self.production_id.id)]
|
||||||
@@ -24,67 +24,24 @@ class ProductionTechnologyReAdjustWizard(models.TransientModel):
|
|||||||
for production_item in productions:
|
for production_item in productions:
|
||||||
# 该制造订单的其他同一销售订单的制造订单的工艺设计处理
|
# 该制造订单的其他同一销售订单的制造订单的工艺设计处理
|
||||||
if production_item != self.production_id:
|
if production_item != self.production_id:
|
||||||
for td_other in production_item.technology_design_ids:
|
self.env['sf.technology.design'].sudo().unified_procedure_multiple_work_orders(technology_designs, production_item)
|
||||||
if td_other.is_auto is False:
|
|
||||||
td_del = technology_designs.filtered(lambda tdo: tdo.route_id.id == td_other.route_id.id)
|
|
||||||
if not td_del or td_del.active is False:
|
|
||||||
td_other.write({'active': False})
|
|
||||||
for td_main in technology_designs:
|
|
||||||
route_other = production_item.technology_design_ids.filtered(
|
|
||||||
lambda td: td.route_id.id == td_main.route_id.id)
|
|
||||||
if not route_other and td_main.active is True:
|
|
||||||
production_item.write({'technology_design_ids': [(0, 0, {
|
|
||||||
'route_id': td_main.route_id.id,
|
|
||||||
'process_parameters_id': False if td_main.process_parameters_id is False else
|
|
||||||
self.env[
|
|
||||||
'sf.production.process.parameter'].search(
|
|
||||||
[('id', '=', td_main.process_parameters_id.id)]).id,
|
|
||||||
'sequence': td_main.sequence,
|
|
||||||
'is_auto': td_main.is_auto})]})
|
|
||||||
else:
|
|
||||||
for ro in route_other:
|
|
||||||
domain = [('production_id', '=', self.production_id.id),
|
|
||||||
('active', 'in', [True, False]),
|
|
||||||
('route_id', '=', ro.route_id.id)]
|
|
||||||
if ro.route_id.routing_type == '表面工艺':
|
|
||||||
domain += [('process_parameters_id', '=', ro.process_parameters_id.id)]
|
|
||||||
elif ro.route_id.routing_tag == 'special' and ro.is_auto is False:
|
|
||||||
display_name = ro.route_id.display_name
|
|
||||||
domain += [('name', 'ilike', display_name)]
|
|
||||||
elif ro.panel is not False:
|
|
||||||
domain += [('panel', '=', ro.panel)]
|
|
||||||
td_upd = self.env['sf.technology.design'].sudo().search(domain)
|
|
||||||
if td_upd:
|
|
||||||
ro.write({'sequence': td_upd.sequence, 'active': td_upd.active})
|
|
||||||
special_design = self.env['sf.technology.design'].sudo().search(
|
special_design = self.env['sf.technology.design'].sudo().search(
|
||||||
[('routing_tag', '=', 'special'), ('production_id', '=', production_item.id),
|
[('routing_tag', '=', 'special'), ('production_id', '=', production_item.id),
|
||||||
('is_auto', '=', False), ('active', 'in', [True, False])])
|
('is_auto', '=', False), ('active', 'in', [True, False])])
|
||||||
for special in special_design:
|
for special in special_design:
|
||||||
workorders_values = []
|
workorders_values = []
|
||||||
if special.active is False:
|
if special.active is False:
|
||||||
|
is_cancel = False
|
||||||
# 工单采购单外协出入库单皆需取消
|
# 工单采购单外协出入库单皆需取消
|
||||||
domain = [('production_id', '=', special.production_id.id)]
|
domain = [('production_id', '=', special.production_id.id)]
|
||||||
if special.process_parameters_id:
|
if special.process_parameters_id:
|
||||||
domain += [('surface_technics_parameters_id', '=', special.process_parameters_id.id)]
|
domain += [('surface_technics_parameters_id', '=', special.process_parameters_id.id), ('state', '!=', 'cancel')]
|
||||||
else:
|
else:
|
||||||
domain += [('name', '=', special.route_id.name)]
|
domain += [('technology_design_id', '=', special.id), ('state', '!=', 'cancel')]
|
||||||
workorder = self.env['mrp.workorder'].search(domain)
|
workorder = self.env['mrp.workorder'].search(domain)
|
||||||
if workorder.state != 'cancel':
|
|
||||||
workorder.write({'state': 'cancel'})
|
|
||||||
workorder.picking_ids.write({'state': 'cancel'})
|
|
||||||
workorder.picking_ids.move_ids.write({'state': 'cancel'})
|
|
||||||
purchase_order = self.env['purchase.order'].search(
|
|
||||||
[('origin', '=', workorder.production_id.origin)])
|
|
||||||
for line in purchase_order.order_line:
|
|
||||||
if line.product_id.server_product_process_parameters_id == workorder.surface_technics_parameters_id:
|
|
||||||
purchase_order.write({'state': 'cancel'})
|
|
||||||
else:
|
else:
|
||||||
if special.route_id.routing_type == '表面工艺':
|
|
||||||
display_name = special.process_parameters_id.display_name
|
|
||||||
else:
|
|
||||||
display_name = special.route_id.display_name
|
|
||||||
workorder = self.env['mrp.workorder'].search(
|
workorder = self.env['mrp.workorder'].search(
|
||||||
[('name', '=', display_name), ('production_id', '=', special.production_id.id)])
|
[('technology_design_id', '=', special.id), ('production_id', '=', special.production_id.id), ('state', '!=', 'cancel')])
|
||||||
if not workorder:
|
if not workorder:
|
||||||
if special.route_id.routing_type == '表面工艺':
|
if special.route_id.routing_type == '表面工艺':
|
||||||
product_production_process = self.env['product.template'].search(
|
product_production_process = self.env['product.template'].search(
|
||||||
@@ -99,19 +56,24 @@ class ProductionTechnologyReAdjustWizard(models.TransientModel):
|
|||||||
self.env['mrp.workorder'].json_workorder_str(special.production_id, special))
|
self.env['mrp.workorder'].json_workorder_str(special.production_id, special))
|
||||||
special.production_id.write({'workorder_ids': workorders_values})
|
special.production_id.write({'workorder_ids': workorders_values})
|
||||||
else:
|
else:
|
||||||
|
logging.info(workorder.blocked_by_workorder_ids)
|
||||||
if len(workorder.blocked_by_workorder_ids) > 1:
|
if len(workorder.blocked_by_workorder_ids) > 1:
|
||||||
if workorder.sequence == 1:
|
if workorder.sequence == 1:
|
||||||
workorder.blocked_by_workorder_ids = None
|
workorder.blocked_by_workorder_ids = None
|
||||||
else:
|
else:
|
||||||
workorder.blocked_by_workorder_ids = blocked_by_workorder_ids[0]
|
if workorder.blocked_by_workorder_ids:
|
||||||
|
workorder.blocked_by_workorder_ids = workorder.blocked_by_workorder_ids[0]
|
||||||
productions._reset_work_order_sequence()
|
productions._reset_work_order_sequence()
|
||||||
if self.production_id.product_id.categ_id.type == '成品':
|
# 退回时不对外协出入库单和采购单做处理
|
||||||
productions._reset_subcontract_pick_purchase()
|
# if self.production_id.product_id.categ_id.type == '成品':
|
||||||
productions.get_subcontract_pick_purchase()
|
# productions._reset_subcontract_pick_purchase()
|
||||||
|
# productions.get_subcontract_pick_purchase()
|
||||||
productions.is_adjust = True
|
productions.is_adjust = True
|
||||||
for item in productions:
|
for item in productions:
|
||||||
workorders = item.workorder_ids.filtered(lambda wo: wo.state not in ('cancel')).sorted(
|
workorders = item.workorder_ids.filtered(lambda wo: wo.state not in ('cancel')).sorted(
|
||||||
key=lambda a: a.sequence)
|
key=lambda a: a.sequence)
|
||||||
if workorders[0].state in ['pending']:
|
if workorders[0].state in ['pending']:
|
||||||
if workorder[0].production_id.product_id.categ_id.type == '成品' and item.programming_state != '已编程':
|
if workorders[
|
||||||
|
0].production_id.product_id.categ_id.type == '成品' and item.programming_state != '已编程':
|
||||||
workorders[0].state = 'waiting'
|
workorders[0].state = 'waiting'
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,26 @@
|
|||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
|
<record model="ir.ui.view" id="sf_production_technology_re_adjust_wizard_confirm_form_view">
|
||||||
|
<field name="name">sf.production.technology.re_adjust.wizard.form.view</field>
|
||||||
|
<field name="model">sf.production.technology.re_adjust.wizard</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<form>
|
||||||
|
<sheet>
|
||||||
|
<field name="production_id" invisible="1"/>
|
||||||
|
<field name="origin" invisible="1"/>
|
||||||
|
<div>
|
||||||
|
是否确认退回调整
|
||||||
|
</div>
|
||||||
|
<footer>
|
||||||
|
<button string="确认" name="confirm" type="object" class="oe_highlight"/>
|
||||||
|
<button string="取消" class="btn btn-secondary" special="cancel"/>
|
||||||
|
</footer>
|
||||||
|
</sheet>
|
||||||
|
</form>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
<record id="action_sf_production_technology_re_adjust_wizard" model="ir.actions.act_window">
|
<record id="action_sf_production_technology_re_adjust_wizard" model="ir.actions.act_window">
|
||||||
<field name="name">工艺退回调整</field>
|
<field name="name">工艺退回调整</field>
|
||||||
<field name="res_model">sf.production.technology.re_adjust.wizard</field>
|
<field name="res_model">sf.production.technology.re_adjust.wizard</field>
|
||||||
|
|||||||
@@ -11,51 +11,76 @@ class ProductionTechnologyWizard(models.TransientModel):
|
|||||||
|
|
||||||
production_id = fields.Many2one('mrp.production', string='制造订单号')
|
production_id = fields.Many2one('mrp.production', string='制造订单号')
|
||||||
origin = fields.Char(string='源单据')
|
origin = fields.Char(string='源单据')
|
||||||
is_technology_confirm = fields.Boolean(default=False)
|
is_technology_confirm = fields.Boolean(default=True)
|
||||||
|
|
||||||
def confirm(self):
|
def confirm(self):
|
||||||
if self.is_technology_confirm is True and self.production_id.product_id.categ_id.type == '成品':
|
if self.is_technology_confirm is True and self.production_id.product_id.categ_id.type in ['成品', '坯料']:
|
||||||
domain = [('origin', '=', self.origin), ('state', '=', 'technology_to_confirmed'),
|
domain = [('origin', '=', self.origin), ('state', '=', 'technology_to_confirmed'),
|
||||||
('product_id', '=', self.production_id.product_id.id)]
|
('product_id', '=', self.production_id.product_id.id)]
|
||||||
else:
|
else:
|
||||||
domain = [('id', '=', self.production_id.id)]
|
domain = [('id', '=', self.production_id.id)]
|
||||||
technology_designs = self.production_id.technology_design_ids
|
technology_designs = self.env['sf.technology.design'].sudo().search(
|
||||||
|
[('production_id', '=', self.production_id.id), ('active', 'in', [True, False])])
|
||||||
|
# technology_designs = self.production_id.technology_design_ids
|
||||||
productions = self.env['mrp.production'].search(domain)
|
productions = self.env['mrp.production'].search(domain)
|
||||||
for production in productions:
|
for production in productions:
|
||||||
if production != self.production_id:
|
if production != self.production_id:
|
||||||
for td_other in production.technology_design_ids:
|
self.env['sf.technology.design'].sudo().unified_procedure_multiple_work_orders(technology_designs,
|
||||||
if td_other.is_auto is False:
|
production)
|
||||||
td_del = technology_designs.filtered(lambda tdo: tdo.route_id.id == td_other.route_id.id)
|
# 特殊表面工艺
|
||||||
if not td_del or td_del.active is False:
|
special_design = self.env['sf.technology.design'].sudo().search(
|
||||||
td_other.write({'active': False})
|
[('routing_tag', '=', 'special'), ('production_id', '=', production.id),
|
||||||
for td_main in technology_designs:
|
('is_auto', '=', False), ('active', 'in', [True, False])])
|
||||||
route_other = production.technology_design_ids.filtered(
|
for special in special_design:
|
||||||
lambda td: td.route_id.id == td_main.route_id.id)
|
workorders_values = []
|
||||||
if not route_other and td_main.active is True:
|
if special.active is False:
|
||||||
production.write({'technology_design_ids': [(0, 0, {
|
# is_cancel = False
|
||||||
'route_id': td_main.route_id.id,
|
# 工单采购单外协出入库单皆需取消
|
||||||
'process_parameters_id': False if td_main.process_parameters_id is False else self.env[
|
domain = [('production_id', '=', special.production_id.id)]
|
||||||
'sf.production.process.parameter'].search(
|
if special.process_parameters_id:
|
||||||
[('id', '=', td_main.process_parameters_id.id)]).id,
|
domain += [('surface_technics_parameters_id', '=', special.process_parameters_id.id), ('state', '!=', 'cancel')]
|
||||||
'sequence': td_main.sequence})]})
|
else:
|
||||||
|
domain += [('technology_design_id', '=', special.id), ('state', '!=', 'cancel')]
|
||||||
|
workorder = self.env['mrp.workorder'].search(domain)
|
||||||
|
# previous_workorder = self.env['mrp.workorder'].search(
|
||||||
|
# [('sequence', '=', workorder.sequence - 1), ('routing_type', '=', '表面工艺'),
|
||||||
|
# ('production_id', '=', workorder.production_id.id)])
|
||||||
|
# if previous_workorder:
|
||||||
|
# if previous_workorder.supplier_id != workorder.supplier_id:
|
||||||
|
# is_cancel = True
|
||||||
|
# if workorder.state != 'cancel' and is_cancel is True:
|
||||||
|
workorder.write({'state': 'cancel'})
|
||||||
|
workorder.picking_ids.write({'state': 'cancel'})
|
||||||
|
workorder.picking_ids.move_ids.write({'state': 'cancel'})
|
||||||
|
purchase_order = self.env['purchase.order'].search(
|
||||||
|
[('origin', '=', workorder.production_id.name), ('purchase_type', '=', 'consignment')])
|
||||||
|
for line in purchase_order.order_line:
|
||||||
|
if line.product_id.server_product_process_parameters_id == workorder.surface_technics_parameters_id:
|
||||||
|
purchase_order.write({'state': 'cancel'})
|
||||||
|
else:
|
||||||
|
if special.production_id.workorder_ids:
|
||||||
|
workorder = self.env['mrp.workorder'].search(
|
||||||
|
[('technology_design_id', '=', special.id), ('production_id', '=', special.production_id.id), ('state', '!=', 'cancel')])
|
||||||
|
if not workorder:
|
||||||
|
if special.route_id.routing_type == '表面工艺':
|
||||||
|
product_production_process = self.env['product.template'].search(
|
||||||
|
[('server_product_process_parameters_id', '=', special.process_parameters_id.id)])
|
||||||
|
workorders_values.append(
|
||||||
|
self.env[
|
||||||
|
'mrp.workorder']._json_workorder_surface_process_str(special.production_id, special,
|
||||||
|
product_production_process.seller_ids[
|
||||||
|
0].partner_id.id))
|
||||||
|
else:
|
||||||
|
workorders_values.append(
|
||||||
|
self.env['mrp.workorder'].json_workorder_str(special.production_id, special))
|
||||||
|
special.production_id.write({'workorder_ids': workorders_values})
|
||||||
else:
|
else:
|
||||||
for ro in route_other:
|
if len(workorder.blocked_by_workorder_ids) > 1:
|
||||||
domain = [('production_id', '=', self.production_id.id),
|
if workorder.sequence == 1:
|
||||||
('active', 'in', [True, False]),
|
workorder.blocked_by_workorder_ids = None
|
||||||
('route_id', '=', ro.route_id.id)]
|
else:
|
||||||
if ro.route_id.routing_type == '表面工艺':
|
if workorder.blocked_by_workorder_ids:
|
||||||
domain += [('process_parameters_id', '=', ro.process_parameters_id.id)]
|
workorder.blocked_by_workorder_ids = blocked_by_workorder_ids[0]
|
||||||
elif ro.route_id.routing_tag == 'special' and ro.is_auto is False:
|
|
||||||
display_name = ro.route_id.display_name
|
|
||||||
domain += [('name', 'ilike', display_name)]
|
|
||||||
elif ro.panel is not False:
|
|
||||||
domain += [('panel', '=', ro.panel)]
|
|
||||||
td_upd = self.env['sf.technology.design'].sudo().search(domain)
|
|
||||||
if td_upd:
|
|
||||||
ro.write({'sequence': td_upd.sequence, 'active': td_upd.active})
|
|
||||||
# special = production.technology_design_ids.filtered(
|
|
||||||
# lambda td: td.is_auto is False and td.process_parameters_id is not False)
|
|
||||||
# # if special:
|
|
||||||
productions._create_workorder(False)
|
productions._create_workorder(False)
|
||||||
if self.production_id.product_id.categ_id.type == '成品':
|
if self.production_id.product_id.categ_id.type == '成品':
|
||||||
productions.get_subcontract_pick_purchase()
|
productions.get_subcontract_pick_purchase()
|
||||||
@@ -65,4 +90,5 @@ class ProductionTechnologyWizard(models.TransientModel):
|
|||||||
key=lambda a: a.sequence)
|
key=lambda a: a.sequence)
|
||||||
if workorder[0].state in ['pending']:
|
if workorder[0].state in ['pending']:
|
||||||
if workorder[0].production_id.product_id.categ_id.type == '成品' and item.programming_state != '已编程':
|
if workorder[0].production_id.product_id.categ_id.type == '成品' and item.programming_state != '已编程':
|
||||||
workorders[0].state = 'waiting'
|
workorder[0].state = 'waiting'
|
||||||
|
return productions
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<odoo>
|
<odoo>
|
||||||
<record model="ir.ui.view" id="sf_production_technology_wizard_form_view">
|
<record model="ir.ui.view" id="sf_production_technology_wizard_form_view">
|
||||||
<field name="name">sf.production.technology.wizard.form.view</field>
|
<field name="name">sf.production.technology.wizard.form.view</field>
|
||||||
<field name="model">sf.production.technology.wizard</field>
|
<field name="model">sf.production.technology.wizard</field>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
@@ -13,7 +13,28 @@
|
|||||||
对当前制造订单,同一销售订单相同产品所生成的制造订单统一进行工艺调整与确认
|
对当前制造订单,同一销售订单相同产品所生成的制造订单统一进行工艺调整与确认
|
||||||
</div>
|
</div>
|
||||||
<footer>
|
<footer>
|
||||||
<button string="确认" name="confirm" type="object" class="oe_highlight" confirm="是否确认工艺调整"/>
|
<button string="确认" name="confirm" type="object" class="oe_highlight"
|
||||||
|
confirm="是否确认工艺调整"/>
|
||||||
|
<button string="取消" class="btn btn-secondary" special="cancel"/>
|
||||||
|
</footer>
|
||||||
|
</sheet>
|
||||||
|
</form>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="ir.ui.view" id="sf_production_technology_wizard_confirm_form_view">
|
||||||
|
<field name="name">sf.production.technology.wizard.form.view</field>
|
||||||
|
<field name="model">sf.production.technology.wizard</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<form>
|
||||||
|
<sheet>
|
||||||
|
<field name="production_id" invisible="1"/>
|
||||||
|
<field name="origin" invisible="1"/>
|
||||||
|
<div>
|
||||||
|
是否确认工艺调整
|
||||||
|
</div>
|
||||||
|
<footer>
|
||||||
|
<button string="确认" name="confirm" type="object" class="oe_highlight"/>
|
||||||
<button string="取消" class="btn btn-secondary" special="cancel"/>
|
<button string="取消" class="btn btn-secondary" special="cancel"/>
|
||||||
</footer>
|
</footer>
|
||||||
</sheet>
|
</sheet>
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import logging
|
|||||||
from odoo.exceptions import UserError, ValidationError
|
from odoo.exceptions import UserError, ValidationError
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from odoo import models, api, fields, _
|
from odoo import models, api, fields, _
|
||||||
|
from odoo.tools import groupby
|
||||||
|
|
||||||
|
|
||||||
class ReworkWizard(models.TransientModel):
|
class ReworkWizard(models.TransientModel):
|
||||||
@@ -15,6 +16,7 @@ class ReworkWizard(models.TransientModel):
|
|||||||
production_id = fields.Many2one('mrp.production', string='制造订单号')
|
production_id = fields.Many2one('mrp.production', string='制造订单号')
|
||||||
workorder_ids = fields.Many2many('mrp.workorder', 'rework_wizard_to_work_order', string='所有工单',
|
workorder_ids = fields.Many2many('mrp.workorder', 'rework_wizard_to_work_order', string='所有工单',
|
||||||
domain="[('production_id', '=', production_id),('state','=','done')]")
|
domain="[('production_id', '=', production_id),('state','=','done')]")
|
||||||
|
hidden_workorder_ids = fields.Char('')
|
||||||
rework_reason = fields.Selection(
|
rework_reason = fields.Selection(
|
||||||
[("programming", "编程"), ("cutter", "刀具"), ("clamping", "装夹"),
|
[("programming", "编程"), ("cutter", "刀具"), ("clamping", "装夹"),
|
||||||
("operate computer", "操机"),
|
("operate computer", "操机"),
|
||||||
@@ -36,6 +38,61 @@ class ReworkWizard(models.TransientModel):
|
|||||||
|
|
||||||
tool_state = fields.Selection(string='功能刀具状态', related='production_id.tool_state')
|
tool_state = fields.Selection(string='功能刀具状态', related='production_id.tool_state')
|
||||||
|
|
||||||
|
@api.onchange('hidden_workorder_ids')
|
||||||
|
def _onchange_hidden_workorder_ids(self):
|
||||||
|
for item in self:
|
||||||
|
if item.hidden_workorder_ids not in ['', None, False]:
|
||||||
|
hidden_workorder_list = item.hidden_workorder_ids.split(',')
|
||||||
|
# 获取加工面对应需要返工的工单
|
||||||
|
rw_ids = item.workorder_ids.filtered(
|
||||||
|
lambda w: str(w.ids[0]) in hidden_workorder_list and w.processing_panel not in ['', None, False])
|
||||||
|
grouped_rw_ids = {key: list(group) for key, group in groupby(rw_ids, key=lambda w: w.processing_panel)}
|
||||||
|
for panel, panel_rw_ids in grouped_rw_ids.items():
|
||||||
|
work_ids = item.workorder_ids.filtered(lambda w: w.state == 'done' and w.processing_panel == panel)
|
||||||
|
if len(work_ids) == 3 and len(panel_rw_ids) != 3:
|
||||||
|
for work_id in work_ids:
|
||||||
|
if work_id not in panel_rw_ids:
|
||||||
|
hidden_workorder_list.append(str(work_id.ids[0]))
|
||||||
|
elif len(work_ids) == 2 and len(panel_rw_ids) < 2 and panel_rw_ids[0].name == 'CNC加工':
|
||||||
|
if rw_ids.filtered(lambda w: (w.sequence < panel_rw_ids[0].sequence
|
||||||
|
and w.processing_panel != panel_rw_ids[0].processing_panel)):
|
||||||
|
hidden_workorder_list.append(str(work_ids.filtered(
|
||||||
|
lambda w: (w.processing_panel == panel_rw_ids[0].processing_panel
|
||||||
|
and w.name == '装夹预调')).ids[0]))
|
||||||
|
hidden_workorder_list.sort()
|
||||||
|
item.hidden_workorder_ids = ','.join(hidden_workorder_list)
|
||||||
|
|
||||||
|
def efficacy_rework_wo(self, wk_ids):
|
||||||
|
"""限制判断 """
|
||||||
|
# 判断检测结果待处理所对应的工单是否勾选
|
||||||
|
result_ids = self.production_id.detection_result_ids.filtered(lambda dr: dr.handle_result == '待处理')
|
||||||
|
work_id_list = []
|
||||||
|
if result_ids:
|
||||||
|
work_id_list = [self.workorder_ids.filtered(
|
||||||
|
lambda wk: (wk.name == result_id.routing_type and wk.processing_panel == result_id.processing_panel
|
||||||
|
and wk.state == 'done')).id
|
||||||
|
for result_id in result_ids]
|
||||||
|
if len(wk_ids.filtered(lambda wk: wk.id in work_id_list)) != len(work_id_list):
|
||||||
|
raise ValidationError('存在【检测结果】为【待处理】所对应的工单未进行勾选!!!')
|
||||||
|
# 获取已完成的标准工单
|
||||||
|
grouped_rw_ids = {key: list(group) for key, group in groupby(wk_ids, key=lambda w: w.processing_panel)}
|
||||||
|
for panel, panel_rw_ids in grouped_rw_ids.items():
|
||||||
|
# 1、当制造订单内ZM面的工单都已完成时,返工勾选工序时只能勾选上ZM面的所有工序进行返工
|
||||||
|
work_ids = self.workorder_ids.filtered(lambda w: w.state == 'done' and w.processing_panel == panel)
|
||||||
|
if len(work_ids) == 3 and len(panel_rw_ids) != 3:
|
||||||
|
raise ValidationError(
|
||||||
|
'因为[%s]面的工单已全部完成,如果要对[%s]面的工单进行返工,请勾选这个面的所有工单。' % (panel, panel))
|
||||||
|
# 2、当FM工单在CNC工单进行选择返工,并将已全部完成的ZM面工序全部勾选上时,FM工单上所有的已完成的工单(装夹预调工单)也必须进行勾选
|
||||||
|
if not wk_ids.filtered(lambda wk: wk.name == '装夹预调' and wk.processing_panel == panel):
|
||||||
|
if wk_ids.filtered(lambda wk: wk.name == 'CNC加工' and wk.processing_panel == panel):
|
||||||
|
sequence_max = wk_ids.filtered(
|
||||||
|
lambda wk: wk.name == 'CNC加工' and wk.processing_panel == panel).sequence
|
||||||
|
for wk_id in wk_ids.filtered(lambda wk: wk.sequence < sequence_max):
|
||||||
|
if len(wk_ids.filtered(lambda wk: wk.processing_panel == wk_id.processing_panel)) == 3:
|
||||||
|
raise ValidationError(
|
||||||
|
'由于在[%s]面之前存在整个面进行了勾选的情况,所以在勾选了[%s]面的【CNC加工】工单的时,请勾选[%s]面的装夹预调工单!' % (
|
||||||
|
panel, panel, panel))
|
||||||
|
|
||||||
def confirm(self):
|
def confirm(self):
|
||||||
if self.routing_type in ['装夹预调', 'CNC加工']:
|
if self.routing_type in ['装夹预调', 'CNC加工']:
|
||||||
self.is_clamp_measure = False
|
self.is_clamp_measure = False
|
||||||
@@ -50,22 +107,25 @@ class ReworkWizard(models.TransientModel):
|
|||||||
'test_report': self.workorder_id.detection_report})]})
|
'test_report': self.workorder_id.detection_report})]})
|
||||||
self.workorder_id.button_finish()
|
self.workorder_id.button_finish()
|
||||||
else:
|
else:
|
||||||
if self.workorder_ids:
|
if self.hidden_workorder_ids:
|
||||||
rework_workorder_ids = self.production_id.workorder_ids.filtered(
|
hidden_workorder_list = self.hidden_workorder_ids.split(',')
|
||||||
lambda ap: ap.id in self.workorder_ids.ids)
|
rework_workorder_ids = self.workorder_ids.filtered(lambda w: str(w.id) in hidden_workorder_list)
|
||||||
clamp_workorder_ids = None
|
# 调用效验方法
|
||||||
if rework_workorder_ids:
|
self.efficacy_rework_wo(rework_workorder_ids)
|
||||||
# 限制
|
else:
|
||||||
# 1、单独返工CNC工单则不解绑托盘RFID,如单独返工装夹预调工单,则自动解绑托盘RFID;
|
raise ValidationError('请选择返工工单!!!')
|
||||||
# 2、返工CNC工单和装夹预调工单则自动解绑RFID
|
if rework_workorder_ids:
|
||||||
clamp_workorder_ids = rework_workorder_ids.filtered(lambda rp: rp.routing_type == '装夹预调')
|
# 限制
|
||||||
if clamp_workorder_ids:
|
# 1、单独返工CNC工单则不解绑托盘RFID,如单独返工装夹预调工单,则自动解绑托盘RFID;
|
||||||
for clamp_workorder_id in clamp_workorder_ids:
|
# 2、返工CNC工单和装夹预调工单则自动解绑RFID
|
||||||
self.production_id.workorder_ids.filtered(
|
clamp_workorder_ids = rework_workorder_ids.filtered(lambda rp: rp.routing_type == '装夹预调')
|
||||||
lambda wk: wk.processing_panel == clamp_workorder_id.processing_panel).write(
|
if clamp_workorder_ids:
|
||||||
{'rfid_code': False})
|
for clamp_workorder_id in clamp_workorder_ids:
|
||||||
# 返工工单状态设置为【返工】
|
self.production_id.workorder_ids.filtered(
|
||||||
rework_workorder_ids.write({'state': 'rework'})
|
lambda wk: wk.processing_panel == clamp_workorder_id.processing_panel).write(
|
||||||
|
{'rfid_code': None})
|
||||||
|
# 返工工单状态设置为【返工】
|
||||||
|
rework_workorder_ids.write({'state': 'rework'})
|
||||||
# 查询返工工单对应的工艺设计记录,并调用方法拼接数据,用于创建新的工单
|
# 查询返工工单对应的工艺设计记录,并调用方法拼接数据,用于创建新的工单
|
||||||
workorders_values = []
|
workorders_values = []
|
||||||
for work in rework_workorder_ids:
|
for work in rework_workorder_ids:
|
||||||
@@ -76,7 +136,7 @@ class ReworkWizard(models.TransientModel):
|
|||||||
and item.panel == work.processing_panel))
|
and item.panel == work.processing_panel))
|
||||||
if route:
|
if route:
|
||||||
work_list = self.env['mrp.workorder'].json_workorder_str(self.production_id, route[0])
|
work_list = self.env['mrp.workorder'].json_workorder_str(self.production_id, route[0])
|
||||||
work_list[2].update({'tag_type': '重新加工'})
|
work_list[2].update({'tag_type': '重新加工', 'sequence': 0})
|
||||||
workorders_values.append(work_list)
|
workorders_values.append(work_list)
|
||||||
# 创建新工单,并进行返工配置的相关操作
|
# 创建新工单,并进行返工配置的相关操作
|
||||||
if workorders_values:
|
if workorders_values:
|
||||||
@@ -86,6 +146,11 @@ class ReworkWizard(models.TransientModel):
|
|||||||
new_pre_workorder_ids = self.production_id.workorder_ids.filtered(
|
new_pre_workorder_ids = self.production_id.workorder_ids.filtered(
|
||||||
lambda kw: kw.routing_type == '装夹预调' and kw.sequence == 0)
|
lambda kw: kw.routing_type == '装夹预调' and kw.sequence == 0)
|
||||||
self.production_id._reset_work_order_sequence()
|
self.production_id._reset_work_order_sequence()
|
||||||
|
# ====新工单绑定rfid===
|
||||||
|
for new_work_id in new_work_ids:
|
||||||
|
if new_work_id.routing_type in ['CNC加工', '解除装夹']:
|
||||||
|
new_work_id.write({'rfid_code': self.production_id.workorder_ids.filtered(
|
||||||
|
lambda wk: wk.sequence == new_work_id.sequence - 1).rfid_code})
|
||||||
self.production_id.detection_result_ids.filtered(
|
self.production_id.detection_result_ids.filtered(
|
||||||
lambda ap1: ap1.handle_result == '待处理').write({'handle_result': '已处理'})
|
lambda ap1: ap1.handle_result == '待处理').write({'handle_result': '已处理'})
|
||||||
panels = [] # 返工的加工面
|
panels = [] # 返工的加工面
|
||||||
@@ -206,11 +271,7 @@ class ReworkWizard(models.TransientModel):
|
|||||||
elif self.programming_state in ['待编程', '编程中']:
|
elif self.programming_state in ['待编程', '编程中']:
|
||||||
self.production_id.write(
|
self.production_id.write(
|
||||||
{'programming_state': '编程中', 'work_state': '编程中', 'is_rework': True})
|
{'programming_state': '编程中', 'work_state': '编程中', 'is_rework': True})
|
||||||
# ==================申请重新编程=======================
|
# =================================================
|
||||||
if self.is_reprogramming is True:
|
|
||||||
self.production_id.update_programming_state()
|
|
||||||
self.production_id.write(
|
|
||||||
{'programming_state': '编程中', 'work_state': '编程中'})
|
|
||||||
if self.production_id.state == 'progress':
|
if self.production_id.state == 'progress':
|
||||||
self.production_id.write({'programming_state': '已编程', 'work_state': '已编程'})
|
self.production_id.write({'programming_state': '已编程', 'work_state': '已编程'})
|
||||||
if self.reprogramming_num >= 1 and self.programming_state == '已编程':
|
if self.reprogramming_num >= 1 and self.programming_state == '已编程':
|
||||||
@@ -221,6 +282,13 @@ class ReworkWizard(models.TransientModel):
|
|||||||
productions_not_delivered.write(
|
productions_not_delivered.write(
|
||||||
{'state': 'progress', 'programming_state': '已编程', 'work_state': '已编程',
|
{'state': 'progress', 'programming_state': '已编程', 'work_state': '已编程',
|
||||||
'is_rework': False})
|
'is_rework': False})
|
||||||
|
# ==================申请重新编程=======================
|
||||||
|
if self.is_reprogramming is True:
|
||||||
|
self.production_id.update_programming_state()
|
||||||
|
self.production_id.write(
|
||||||
|
{'programming_state': '编程中', 'work_state': '编程中', 'state': 'progress'})
|
||||||
|
# ================= 返工完成,制造订单状态置为加工中 ==============
|
||||||
|
self.production_id.write({'state': 'progress', 'is_rework': False})
|
||||||
|
|
||||||
@api.onchange('production_id')
|
@api.onchange('production_id')
|
||||||
def onchange_processing_panel_id(self):
|
def onchange_processing_panel_id(self):
|
||||||
|
|||||||
@@ -12,9 +12,15 @@
|
|||||||
<field name="tool_state" invisible="True"/>
|
<field name="tool_state" invisible="True"/>
|
||||||
<field name="routing_type" invisible="True"/>
|
<field name="routing_type" invisible="True"/>
|
||||||
<field name="processing_panel_id" invisible="1"/>
|
<field name="processing_panel_id" invisible="1"/>
|
||||||
|
<field name="hidden_workorder_ids" class="css_not_available_msg"/>
|
||||||
<group>
|
<group>
|
||||||
<field name="workorder_ids" string="工序" attrs='{"invisible": [("routing_type","=","装夹预调")]}'>
|
<field name="hidden_workorder_ids" invisible="1"/>
|
||||||
|
<field options="{'no_create': True,'no_open': True}" readonly="1" name="workorder_ids"
|
||||||
|
widget="jikimo_subtree_selector_field"
|
||||||
|
jikimo_selector="True" replace_context="hidden_workorder_ids" string="工序"
|
||||||
|
attrs='{"invisible": [("routing_type","=","装夹预调")]}'>
|
||||||
<tree create="0" editable='bottom' delete="0">
|
<tree create="0" editable='bottom' delete="0">
|
||||||
|
<field name="sequence" readonly="1" string="工序"/>
|
||||||
<field name="processing_panel" readonly="1"/>
|
<field name="processing_panel" readonly="1"/>
|
||||||
<field name="name" readonly="1"/>
|
<field name="name" readonly="1"/>
|
||||||
</tree>
|
</tree>
|
||||||
|
|||||||
@@ -207,8 +207,9 @@ class WorkpieceDeliveryWizard(models.TransientModel):
|
|||||||
workorder.production_line_id.id != self.production_ids[0].production_line_id.id):
|
workorder.production_line_id.id != self.production_ids[0].production_line_id.id):
|
||||||
raise UserError(f'该rfid对应的制造订单号为{workorder.production_id.name}的目的生产线不一致')
|
raise UserError(f'该rfid对应的制造订单号为{workorder.production_id.name}的目的生产线不一致')
|
||||||
|
|
||||||
# 调用打印成品条码方法
|
if workorder.routing_type == '解除装夹':
|
||||||
workorder.print_method()
|
# 调用打印成品条码方法
|
||||||
|
workorder.print_method()
|
||||||
|
|
||||||
# 将对象添加到对应的同模型且是多对多类型里
|
# 将对象添加到对应的同模型且是多对多类型里
|
||||||
self.production_ids |= workorder.production_id
|
self.production_ids |= workorder.production_id
|
||||||
|
|||||||
@@ -11,13 +11,16 @@
|
|||||||
""",
|
""",
|
||||||
'category': 'sf',
|
'category': 'sf',
|
||||||
'website': 'https://www.sf.jikimo.com',
|
'website': 'https://www.sf.jikimo.com',
|
||||||
'depends': ['sale', 'purchase', 'sf_plan', 'jikimo_message_notify', 'stock', 'sf_quality', 'mrp'],
|
'depends': ['sale', 'purchase', 'sf_plan', 'jikimo_message_notify', 'stock', 'sf_quality', 'mrp',
|
||||||
|
'sf_manufacturing', 'product', 'quality'],
|
||||||
'data': [
|
'data': [
|
||||||
'data/bussiness_node.xml',
|
'data/bussiness_node.xml',
|
||||||
'data/cron_data.xml',
|
'data/cron_data.xml',
|
||||||
'data/template_data.xml',
|
'data/template_data.xml',
|
||||||
'security/ir.model.access.csv',
|
'security/ir.model.access.csv',
|
||||||
'views/mrp_workorder_views.xml',
|
'views/mrp_workorder_views.xml',
|
||||||
|
'views/purchase_order_view.xml',
|
||||||
|
'views/stock_picking_view.xml',
|
||||||
],
|
],
|
||||||
'test': [
|
'test': [
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -4,10 +4,12 @@ import logging
|
|||||||
from odoo import http
|
from odoo import http
|
||||||
from odoo.http import request
|
from odoo.http import request
|
||||||
from odoo.addons.sf_mrs_connect.controllers.controllers import Sf_Mrs_Connect
|
from odoo.addons.sf_mrs_connect.controllers.controllers import Sf_Mrs_Connect
|
||||||
|
from odoo.addons.sf_bf_connect.controllers.controllers import Sf_Bf_Connect
|
||||||
from odoo.addons.sf_base.commons.common import Common
|
from odoo.addons.sf_base.commons.common import Common
|
||||||
|
|
||||||
_logger = logging.getLogger(__name__)
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class MessageSfMrsConnect(Sf_Mrs_Connect):
|
class MessageSfMrsConnect(Sf_Mrs_Connect):
|
||||||
|
|
||||||
@http.route('/api/cnc_processing/create', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False,
|
@http.route('/api/cnc_processing/create', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False,
|
||||||
@@ -39,7 +41,8 @@ class MessageSfMrsConnect(Sf_Mrs_Connect):
|
|||||||
_logger.info('无效用刀异常消息推送接口:%s' % e)
|
_logger.info('无效用刀异常消息推送接口:%s' % e)
|
||||||
return json.JSONEncoder().encode(res)
|
return json.JSONEncoder().encode(res)
|
||||||
|
|
||||||
@http.route('/api/maintenance_logs/notify', type='json', auth='public', methods=['GET', 'POST'], csrf=False, cors="*")
|
@http.route('/api/maintenance_logs/notify', type='json', auth='public', methods=['GET', 'POST'], csrf=False,
|
||||||
|
cors="*")
|
||||||
def maintenance_logs_notify(self, **kw):
|
def maintenance_logs_notify(self, **kw):
|
||||||
res = {'code': 200, 'message': '设备故障日志信息推送成功'}
|
res = {'code': 200, 'message': '设备故障日志信息推送成功'}
|
||||||
datas = request.httprequest.data
|
datas = request.httprequest.data
|
||||||
@@ -51,10 +54,26 @@ class MessageSfMrsConnect(Sf_Mrs_Connect):
|
|||||||
try:
|
try:
|
||||||
if not isinstance(log_id, list):
|
if not isinstance(log_id, list):
|
||||||
log_id = [log_id]
|
log_id = [log_id]
|
||||||
maintenance_logs = request.env['sf.maintenance.logs'].sudo().search([('id', 'in', [int(id) for id in log_id])])
|
maintenance_logs = request.env['sf.maintenance.logs'].sudo().search(
|
||||||
|
[('id', 'in', [int(id) for id in log_id])])
|
||||||
if maintenance_logs:
|
if maintenance_logs:
|
||||||
maintenance_logs.add_queue('设备故障')
|
maintenance_logs.add_queue('设备故障')
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
res = {'code': 400, 'message': '设备故障信息推送失败', 'error': str(e)}
|
res = {'code': 400, 'message': '设备故障信息推送失败', 'error': str(e)}
|
||||||
return json.JSONEncoder().encode(res)
|
return json.JSONEncoder().encode(res)
|
||||||
|
|
||||||
|
|
||||||
|
class MessageSfBfConnect(Sf_Bf_Connect):
|
||||||
|
@http.route('/api/bfm_process_order/list', type='http', auth='sf_token', methods=['GET', 'POST'], csrf=False,
|
||||||
|
cors="*")
|
||||||
|
def get_bfm_process_order_list(self, **kw):
|
||||||
|
res = super(MessageSfBfConnect, self).get_bfm_process_order_list(**kw)
|
||||||
|
response_data = json.loads(res.data.decode('utf-8'))
|
||||||
|
if response_data['status'] == 1:
|
||||||
|
try:
|
||||||
|
_logger.info('已进入待接单消息推送:%s' % response_data)
|
||||||
|
sale_order = request.env['sale.order'].sudo().search([('name', '=', response_data['factory_order_no'])])
|
||||||
|
sale_order.add_queue('待接单')
|
||||||
|
except Exception as e:
|
||||||
|
logging.info('add_queue error:%s' % e)
|
||||||
|
return res
|
||||||
|
|||||||
@@ -22,6 +22,15 @@
|
|||||||
<field name="model">sale.order</field>
|
<field name="model">sale.order</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
|
<record id="bussiness_supply_method" model="jikimo.message.bussiness.node">
|
||||||
|
<field name="name">待确认供货方式</field>
|
||||||
|
<field name="model">sale.order</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="bussiness_technology_to_confirmed" model="jikimo.message.bussiness.node">
|
||||||
|
<field name="name">待确认加工路线</field>
|
||||||
|
<field name="model">mrp.production</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
<record id="transfer_inventory" model="jikimo.message.bussiness.node">
|
<record id="transfer_inventory" model="jikimo.message.bussiness.node">
|
||||||
<field name="name">调拨入库</field>
|
<field name="name">调拨入库</field>
|
||||||
@@ -47,7 +56,7 @@
|
|||||||
<!--工单-->
|
<!--工单-->
|
||||||
<record id="bussiness_mrp_workorder_remind" model="jikimo.message.bussiness.node">
|
<record id="bussiness_mrp_workorder_remind" model="jikimo.message.bussiness.node">
|
||||||
<field name="name">工单已下发通知</field>
|
<field name="name">工单已下发通知</field>
|
||||||
<field name="model">mrp.workorder</field>
|
<field name="model">product.product</field>
|
||||||
</record>
|
</record>
|
||||||
<!--发货调拨-->
|
<!--发货调拨-->
|
||||||
<record id="production_completed_warehouse_reminder" model="jikimo.message.bussiness.node">
|
<record id="production_completed_warehouse_reminder" model="jikimo.message.bussiness.node">
|
||||||
@@ -106,5 +115,45 @@
|
|||||||
<field name="name">设备故障</field>
|
<field name="name">设备故障</field>
|
||||||
<field name="model">sf.maintenance.logs</field>
|
<field name="model">sf.maintenance.logs</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
|
<record id="bussiness_state_draft" model="jikimo.message.bussiness.node">
|
||||||
|
<field name="name">待排程</field>
|
||||||
|
<field name="model">mrp.production</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="bussiness_outsourcing" model="jikimo.message.bussiness.node">
|
||||||
|
<field name="name">委外加工采购单提醒</field>
|
||||||
|
<field name="model">purchase.order</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="bussiness_purchase" model="jikimo.message.bussiness.node">
|
||||||
|
<field name="name">外购订单采购单提醒</field>
|
||||||
|
<field name="model">purchase.order</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="bussiness_process_outsourcing" model="jikimo.message.bussiness.node">
|
||||||
|
<field name="name">工序外协采购单通知</field>
|
||||||
|
<field name="model">purchase.order</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="bussiness_outsourced_outbound" model="jikimo.message.bussiness.node">
|
||||||
|
<field name="name">工序外协发料通知</field>
|
||||||
|
<field name="model">mrp.production</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="bussiness_purchase_order_warning" model="jikimo.message.bussiness.node">
|
||||||
|
<field name="name">采购订单预警提醒</field>
|
||||||
|
<field name="model">purchase.order</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="bussiness_purchase_order_overdue" model="jikimo.message.bussiness.node">
|
||||||
|
<field name="name">采购单已逾期提醒</field>
|
||||||
|
<field name="model">purchase.order</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="bussiness_quality_check" model="jikimo.message.bussiness.node">
|
||||||
|
<field name="name">待质检提醒</field>
|
||||||
|
<field name="model">product.product</field>
|
||||||
|
</record>
|
||||||
</data>
|
</data>
|
||||||
</odoo>
|
</odoo>
|
||||||
@@ -10,7 +10,7 @@
|
|||||||
<field name="msgtype">markdown</field>
|
<field name="msgtype">markdown</field>
|
||||||
<field name="urgency">normal</field>
|
<field name="urgency">normal</field>
|
||||||
<field name="content">### 待接单提醒:
|
<field name="content">### 待接单提醒:
|
||||||
单号:销售订单[{{name}}]({{url}})
|
单号:询价单[{{name}}]({{url}})
|
||||||
事项:请确认是否接单。
|
事项:请确认是否接单。
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
@@ -72,14 +72,14 @@
|
|||||||
<field name="msgtype">markdown</field>
|
<field name="msgtype">markdown</field>
|
||||||
<field name="urgency">normal</field>
|
<field name="urgency">normal</field>
|
||||||
<field name="content">### 坯料发料提醒:
|
<field name="content">### 坯料发料提醒:
|
||||||
单号:产品[{{product_id}}]({{request_url}})
|
单号:发料单[{{name}}]({{request_url}})
|
||||||
事项:共{{number}}个生产发料单待确认处理</field>
|
事项:请确认坯料发料单并处理</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record id="template_mrp_workorder_remind" model="jikimo.message.template">
|
<record id="template_mrp_workorder_remind" model="jikimo.message.template">
|
||||||
<field name="name">工单已下发通知</field>
|
<field name="name">工单已下发通知</field>
|
||||||
<field name="model_id" ref="mrp_workorder.model_mrp_workorder"/>
|
<field name="model_id" ref="product.model_product_product"/>
|
||||||
<field name="model">mrp.workorder</field>
|
<field name="model">product.product</field>
|
||||||
<field name="bussiness_node_id" ref="bussiness_mrp_workorder_remind"/>
|
<field name="bussiness_node_id" ref="bussiness_mrp_workorder_remind"/>
|
||||||
<field name="msgtype">markdown</field>
|
<field name="msgtype">markdown</field>
|
||||||
<field name="urgency">normal</field>
|
<field name="urgency">normal</field>
|
||||||
@@ -275,5 +275,131 @@
|
|||||||
机台号:[{{maintenance_equipment_id.name}}]({{url}})
|
机台号:[{{maintenance_equipment_id.name}}]({{url}})
|
||||||
事项:{{create_date}}故障报警</field>
|
事项:{{create_date}}故障报警</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
|
<record id="template_supply_method" model="jikimo.message.template">
|
||||||
|
<field name="name">待确认供货方式</field>
|
||||||
|
<field name="model_id" ref="sale.model_sale_order"/>
|
||||||
|
<field name="model">sale.order</field>
|
||||||
|
<field name="bussiness_node_id" ref="bussiness_supply_method"/>
|
||||||
|
<field name="msgtype">markdown</field>
|
||||||
|
<field name="urgency">normal</field>
|
||||||
|
<field name="content">### 待确认供货方式提醒:
|
||||||
|
单号:销售订单[{{name}}]({{url}})
|
||||||
|
事项:请确认供货方式。
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="template_technology_to_confirmed" model="jikimo.message.template">
|
||||||
|
<field name="name">待确认加工路线</field>
|
||||||
|
<field name="model_id" ref="mrp.model_mrp_production"/>
|
||||||
|
<field name="model">mrp.production</field>
|
||||||
|
<field name="bussiness_node_id" ref="bussiness_technology_to_confirmed"/>
|
||||||
|
<field name="msgtype">markdown</field>
|
||||||
|
<field name="urgency">normal</field>
|
||||||
|
<field name="content">### 待确认加工路线提醒:
|
||||||
|
单号:产品[{{name}}]({{url}})
|
||||||
|
事项:有{{production_num}}个制造订单需要确认加工路线。
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="template_state_draft" model="jikimo.message.template">
|
||||||
|
<field name="name">待排程</field>
|
||||||
|
<field name="model_id" ref="mrp.model_mrp_production"/>
|
||||||
|
<field name="model">mrp.production</field>
|
||||||
|
<field name="bussiness_node_id" ref="bussiness_state_draft"/>
|
||||||
|
<field name="msgtype">markdown</field>
|
||||||
|
<field name="urgency">normal</field>
|
||||||
|
<field name="content">### 待排程提醒:
|
||||||
|
单号:产品[{{name}}]({{url}})
|
||||||
|
事项:有{{production_num}}个制造订单待排程。
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="template_outsourcing_remind" model="jikimo.message.template">
|
||||||
|
<field name="name">委外加工采购单提醒</field>
|
||||||
|
<field name="model_id" ref="purchase.model_purchase_order"/>
|
||||||
|
<field name="model">purchase.order</field>
|
||||||
|
<field name="bussiness_node_id" ref="bussiness_outsourcing"/>
|
||||||
|
<field name="msgtype">markdown</field>
|
||||||
|
<field name="urgency">normal</field>
|
||||||
|
<field name="content">### 委外加工采购通知:
|
||||||
|
单号:委外加工采购单[{{name}}]({{request_url}})
|
||||||
|
事项:请确认委外采购单并处理。</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="template_purchase_remind" model="jikimo.message.template">
|
||||||
|
<field name="name">外购订单采购单提醒</field>
|
||||||
|
<field name="model_id" ref="purchase.model_purchase_order"/>
|
||||||
|
<field name="model">purchase.order</field>
|
||||||
|
<field name="bussiness_node_id" ref="bussiness_purchase"/>
|
||||||
|
<field name="msgtype">markdown</field>
|
||||||
|
<field name="urgency">normal</field>
|
||||||
|
<field name="content">### 外购订单采购通知:
|
||||||
|
单号:外购采购单[{{name}}]({{request_url}})
|
||||||
|
事项:请确认外购采购单并处理。</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="template_process_outsourcing_remind" model="jikimo.message.template">
|
||||||
|
<field name="name">工序外协采购单通知</field>
|
||||||
|
<field name="model_id" ref="purchase.model_purchase_order"/>
|
||||||
|
<field name="model">purchase.order</field>
|
||||||
|
<field name="bussiness_node_id" ref="bussiness_process_outsourcing"/>
|
||||||
|
<field name="msgtype">markdown</field>
|
||||||
|
<field name="urgency">normal</field>
|
||||||
|
<field name="content">### 工序外协采购单通知:
|
||||||
|
单号:工序外协采购,产品[{{name}}]({{url}})
|
||||||
|
事项:请确认{{num}}个工序外协采购单并处理。</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="template_outsourced_outbound_remind" model="jikimo.message.template">
|
||||||
|
<field name="name">工序外协发料通知</field>
|
||||||
|
<field name="model_id" ref="mrp.model_mrp_production"/>
|
||||||
|
<field name="model">mrp.production</field>
|
||||||
|
<field name="bussiness_node_id" ref="bussiness_outsourced_outbound"/>
|
||||||
|
<field name="msgtype">markdown</field>
|
||||||
|
<field name="urgency">normal</field>
|
||||||
|
<field name="content">### 工序外协发料提醒:
|
||||||
|
单号:产品[{{name}}]({{url}})发料单
|
||||||
|
事项:请确认{{num}}个工序外协发料单并发料处理。</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
|
||||||
|
<record id="template_purchase_order_warning" model="jikimo.message.template">
|
||||||
|
<field name="name">采购订单预警提醒</field>
|
||||||
|
<field name="model_id" ref="purchase.model_purchase_order"/>
|
||||||
|
<field name="model">purchase.order</field>
|
||||||
|
<field name="bussiness_node_id" ref="bussiness_purchase_order_warning"/>
|
||||||
|
<field name="msgtype">markdown</field>
|
||||||
|
<field name="send_type">timing</field>
|
||||||
|
<field name="urgency">normal</field>
|
||||||
|
<field name="content">### 采购订单预警提醒
|
||||||
|
事项:[共有{{num}}个采购订单有逾期风险]({{url}})
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="template_purchase_order_overdue" model="jikimo.message.template">
|
||||||
|
<field name="name">采购单已逾期提醒</field>
|
||||||
|
<field name="model_id" ref="purchase.model_purchase_order"/>
|
||||||
|
<field name="model">purchase.order</field>
|
||||||
|
<field name="bussiness_node_id" ref="bussiness_purchase_order_overdue"/>
|
||||||
|
<field name="msgtype">markdown</field>
|
||||||
|
<field name="send_type">timing</field>
|
||||||
|
<field name="urgency">normal</field>
|
||||||
|
<field name="content">### 采购单已逾期提醒
|
||||||
|
事项:[共有{{num}}个采购订单已逾期]({{url}})
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="template_quality_check" model="jikimo.message.template">
|
||||||
|
<field name="name">待质检提醒</field>
|
||||||
|
<field name="model_id" ref="product.model_product_product"/>
|
||||||
|
<field name="model">product.product</field>
|
||||||
|
<field name="bussiness_node_id" ref="bussiness_quality_check"/>
|
||||||
|
<field name="msgtype">markdown</field>
|
||||||
|
<field name="urgency">normal</field>
|
||||||
|
<field name="content">### 待质检提醒:
|
||||||
|
单号:产品[{{name}}]({{url}})
|
||||||
|
事项:有{{num}}个质检单需要处理。</field>
|
||||||
|
</record>
|
||||||
</data>
|
</data>
|
||||||
</odoo>
|
</odoo>
|
||||||
@@ -10,3 +10,7 @@ from . import sf_message_functional_tool_dismantle
|
|||||||
from . import sf_message_mrp_production
|
from . import sf_message_mrp_production
|
||||||
from . import sf_message_quality_cnc_test
|
from . import sf_message_quality_cnc_test
|
||||||
from . import sf_message_maintenance_logs
|
from . import sf_message_maintenance_logs
|
||||||
|
from . import sf_message_mrp_production_wizard
|
||||||
|
from . import sf_message_mrp_production_adjust_wizard
|
||||||
|
from . import sf_message_product
|
||||||
|
from . import sf_message_quality_check
|
||||||
|
|||||||
@@ -13,11 +13,11 @@ class SFMessageMaintenanceLogs(models.Model):
|
|||||||
return res
|
return res
|
||||||
|
|
||||||
def _get_message(self, message_queue_ids):
|
def _get_message(self, message_queue_ids):
|
||||||
contents = super(SFMessageMaintenanceLogs, self)._get_message(message_queue_ids)
|
contents, _ = super(SFMessageMaintenanceLogs, self)._get_message(message_queue_ids)
|
||||||
url = self.env['ir.config_parameter'].get_param('web.base.url')
|
url = self.env['ir.config_parameter'].get_param('web.base.url')
|
||||||
action_id = self.env.ref('sf_maintenance.action_maintenance_logs').id
|
action_id = self.env.ref('sf_maintenance.action_maintenance_logs').id
|
||||||
for index, content in enumerate(contents):
|
for index, content in enumerate(contents):
|
||||||
maintenance_logs_id = self.env['sf.maintenance.logs'].browse(message_queue_ids[index].res_id)
|
maintenance_logs_id = self.env['sf.maintenance.logs'].browse(message_queue_ids[index].res_id)
|
||||||
url = url + '/web#id=%s&view_type=form&action=%s' % (maintenance_logs_id.id, action_id)
|
url = url + '/web#id=%s&view_type=form&action=%s' % (maintenance_logs_id.id, action_id)
|
||||||
contents[index] = content.replace('{{url}}', url)
|
contents[index] = content.replace('{{url}}', url)
|
||||||
return contents
|
return contents, message_queue_ids
|
||||||
@@ -14,19 +14,24 @@ class SFMessageMrpProduction(models.Model):
|
|||||||
'workorder_ids.state', 'product_qty', 'qty_producing')
|
'workorder_ids.state', 'product_qty', 'qty_producing')
|
||||||
def _compute_state(self):
|
def _compute_state(self):
|
||||||
super(SFMessageMrpProduction, self)._compute_state()
|
super(SFMessageMrpProduction, self)._compute_state()
|
||||||
for record in self:
|
try:
|
||||||
if record.state in ['scrap', 'done']:
|
for record in self:
|
||||||
# 查询制造订单下的所有未完成的生产订单
|
if record.state in ['scrap', 'done']:
|
||||||
mrp_production = record.env['mrp.production'].search(
|
# 查询制造订单下的所有未完成的生产订单
|
||||||
[('origin', '=', record.origin), ('state', 'not in', ['scrap', 'done'])])
|
mrp_production = record.env['mrp.production'].search(
|
||||||
if not mrp_production:
|
[('origin', '=', record.origin), ('state', 'not in', ['scrap', 'done'])])
|
||||||
mrp_production_queue = self.env["jikimo.message.queue"].search([('res_id', '=', record.id)])
|
if not mrp_production:
|
||||||
if not mrp_production_queue:
|
mrp_production_queue = self.env["jikimo.message.queue"].search([('res_id', '=', record.id)])
|
||||||
record.add_queue('生产完工入库提醒')
|
if not mrp_production_queue:
|
||||||
|
record.add_queue('生产完工入库提醒')
|
||||||
|
except Exception as e:
|
||||||
|
logging.info('add_queue生产完工入库提醒 error:%s' % e)
|
||||||
|
|
||||||
# 获取发送消息内容
|
# 获取发送消息内容
|
||||||
def _get_message(self, message_queue_ids):
|
def _get_message(self, message_queue_ids):
|
||||||
contents = []
|
contents = []
|
||||||
|
unique_products = set()
|
||||||
|
technology_to_confirmed = set()
|
||||||
for message_queue_id in message_queue_ids:
|
for message_queue_id in message_queue_ids:
|
||||||
if message_queue_id.message_template_id.name == '生产完工入库提醒':
|
if message_queue_id.message_template_id.name == '生产完工入库提醒':
|
||||||
content = message_queue_id.message_template_id.content
|
content = message_queue_id.message_template_id.content
|
||||||
@@ -40,7 +45,58 @@ class SFMessageMrpProduction(models.Model):
|
|||||||
content = content.replace('{{name}}', stock_picking_sfp.name).replace(
|
content = content.replace('{{name}}', stock_picking_sfp.name).replace(
|
||||||
'{{sale_order_name}}', mrp_production.origin).replace('{{request_url}}', url)
|
'{{sale_order_name}}', mrp_production.origin).replace('{{request_url}}', url)
|
||||||
contents.append(content)
|
contents.append(content)
|
||||||
|
if message_queue_id.message_template_id.name == '待确认加工路线':
|
||||||
|
content = message_queue_id.message_template_id.content
|
||||||
|
mrp_production = self.env['mrp.production'].sudo().search([('id', '=', int(message_queue_id.res_id))])
|
||||||
|
technology_to_confirmed.add(mrp_production.product_id.id)
|
||||||
|
if message_queue_id.message_template_id.name == '待排程':
|
||||||
|
content = message_queue_id.message_template_id.content
|
||||||
|
mrp_production = self.env['mrp.production'].sudo().search([('id', '=', int(message_queue_id.res_id))])
|
||||||
|
unique_products.add(mrp_production.product_id.id)
|
||||||
|
if message_queue_id.message_template_id.name == '工序外协发料通知':
|
||||||
|
content = message_queue_id.message_template_id.content
|
||||||
|
mrp_production = self.env['mrp.production'].sudo().search([('id', '=', int(message_queue_id.res_id))])
|
||||||
|
mrp_production_list = self.env['mrp.production'].sudo().search(
|
||||||
|
[('product_id', '=', mrp_production.product_id.id)])
|
||||||
|
mrp_production_names = mrp_production_list.mapped('name')
|
||||||
|
stock_picking_num = self.env['stock.picking'].sudo().search_count(
|
||||||
|
[('origin', 'in', mrp_production_names), ('state', '=', 'assigned')])
|
||||||
|
if stock_picking_num >= 1:
|
||||||
|
url = self.env['ir.config_parameter'].sudo().get_param('web.base.url')
|
||||||
|
action_id = self.env.ref('sf_message.action_picking_outsourced_tree_ready').id
|
||||||
|
menu_id = self.env.ref('stock.menu_stock_root').id
|
||||||
|
url_with_id = f"{url}/web#view_type=list&action={action_id}&menu_id={menu_id}"
|
||||||
|
content = content.replace('{{name}}', mrp_production.product_id.name).replace('{{url}}',
|
||||||
|
url_with_id).replace(
|
||||||
|
'{{num}}', str(stock_picking_num))
|
||||||
|
contents.append(content)
|
||||||
|
if unique_products:
|
||||||
|
action_id = self.env.ref('sf_plan.sf_production_plan_action1').id
|
||||||
|
unique_products_contents = self.get_production_info(content, unique_products, 'confirmed',
|
||||||
|
action_id)
|
||||||
|
contents.extend(unique_products_contents)
|
||||||
|
if technology_to_confirmed:
|
||||||
|
action_id = self.env.ref('mrp.mrp_production_action').id
|
||||||
|
technology_to_confirmed_contents = self.get_production_info(content, technology_to_confirmed,
|
||||||
|
'technology_to_confirmed',
|
||||||
|
action_id)
|
||||||
|
contents.extend(technology_to_confirmed_contents)
|
||||||
logging.info('生产完工入库提醒: %s' % contents)
|
logging.info('生产完工入库提醒: %s' % contents)
|
||||||
|
return contents, message_queue_ids
|
||||||
|
|
||||||
|
def get_production_info(self, content, product_ids, state, action_id):
|
||||||
|
contents = []
|
||||||
|
for products_id in product_ids:
|
||||||
|
product_name = self.env['product.product'].sudo().search([('id', '=', products_id)]).name
|
||||||
|
production_num = self.env['mrp.production'].sudo().search_count(
|
||||||
|
[('product_id', '=', products_id), ('state', '=', state)])
|
||||||
|
if production_num >= 1:
|
||||||
|
url = self.env['ir.config_parameter'].sudo().get_param('web.base.url')
|
||||||
|
url_with_id = f"{url}/web#view_type=list&action={action_id}"
|
||||||
|
new_content = (content.replace('{{name}}', product_name)
|
||||||
|
.replace('{{url}}', url_with_id)
|
||||||
|
.replace('{{production_num}}', str(production_num)))
|
||||||
|
contents.append(new_content)
|
||||||
return contents
|
return contents
|
||||||
|
|
||||||
def request_url(self, id):
|
def request_url(self, id):
|
||||||
@@ -48,7 +104,7 @@ class SFMessageMrpProduction(models.Model):
|
|||||||
action_id = self.env.ref('stock.action_picking_tree_all').id
|
action_id = self.env.ref('stock.action_picking_tree_all').id
|
||||||
menu_id = self.env['ir.model.data'].search([('name', '=', 'module_theme_treehouse')]).id
|
menu_id = self.env['ir.model.data'].search([('name', '=', 'module_theme_treehouse')]).id
|
||||||
# 查询参数
|
# 查询参数
|
||||||
params = {'id': id, 'menu_id': menu_id, 'action': action_id, 'model': 'mrp.production',
|
params = {'id': id, 'menu_id': menu_id, 'action': action_id, 'model': 'mrp.production',
|
||||||
'view_type': 'form'}
|
'view_type': 'form'}
|
||||||
# 拼接查询参数
|
# 拼接查询参数
|
||||||
query_string = urlencode(params)
|
query_string = urlencode(params)
|
||||||
|
|||||||
17
sf_message/models/sf_message_mrp_production_adjust_wizard.py
Normal file
17
sf_message/models/sf_message_mrp_production_adjust_wizard.py
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import logging
|
||||||
|
from odoo import models, fields, api, _
|
||||||
|
|
||||||
|
|
||||||
|
class SFMessageMrpProductionAdjustWizard(models.TransientModel):
|
||||||
|
_name = 'sf.production.technology.re_adjust.wizard'
|
||||||
|
_description = "制造订单工艺调整"
|
||||||
|
_inherit = ['sf.production.technology.re_adjust.wizard', 'jikimo.message.dispatch']
|
||||||
|
|
||||||
|
def confirm(self):
|
||||||
|
super(SFMessageMrpProductionAdjustWizard, self).confirm()
|
||||||
|
try:
|
||||||
|
for production_info in self.production_id:
|
||||||
|
if production_info.state == 'technology_to_confirmed':
|
||||||
|
production_info.add_queue('待确认加工路线')
|
||||||
|
except Exception as e:
|
||||||
|
logging.info('add_queue待确认加工路线 error:%s' % e)
|
||||||
23
sf_message/models/sf_message_mrp_production_wizard.py
Normal file
23
sf_message/models/sf_message_mrp_production_wizard.py
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import logging
|
||||||
|
from odoo import models, fields, api, _
|
||||||
|
|
||||||
|
|
||||||
|
class SFMessageMrpProductionWizard(models.TransientModel):
|
||||||
|
_name = 'sf.production.technology.wizard'
|
||||||
|
_description = "制造订单工艺确认向导"
|
||||||
|
_inherit = ['sf.production.technology.wizard', 'jikimo.message.dispatch']
|
||||||
|
|
||||||
|
def confirm(self):
|
||||||
|
productions = super(SFMessageMrpProductionWizard, self).confirm()
|
||||||
|
try:
|
||||||
|
for production_info in self.production_id:
|
||||||
|
if production_info.state == 'confirmed' and production_info.product_id.categ_id.type == '成品':
|
||||||
|
production_info.add_queue('待排程')
|
||||||
|
for production_id in productions:
|
||||||
|
workorder_ids = production_id.workorder_ids.filtered(
|
||||||
|
lambda p: p.routing_type == '表面工艺' and p.state != 'cancel')
|
||||||
|
for workorder_id in workorder_ids:
|
||||||
|
purchase_orders_id = workorder_id._get_surface_technics_purchase_ids()
|
||||||
|
purchase_orders_id.add_queue('工序外协采购单通知')
|
||||||
|
except Exception as e:
|
||||||
|
logging.info('add_queue待排程 error:%s' % e)
|
||||||
57
sf_message/models/sf_message_product.py
Normal file
57
sf_message/models/sf_message_product.py
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from odoo import models, fields, api, _
|
||||||
|
from urllib.parse import urlencode
|
||||||
|
|
||||||
|
|
||||||
|
class SFMessageProduct(models.Model):
|
||||||
|
_name = 'product.product'
|
||||||
|
_inherit = ['product.product', 'jikimo.message.dispatch']
|
||||||
|
|
||||||
|
def _get_message(self, message_queue_ids):
|
||||||
|
contents = []
|
||||||
|
for message_queue_id in message_queue_ids:
|
||||||
|
if message_queue_id.message_template_id.name == '工单已下发通知':
|
||||||
|
content = message_queue_id.message_template_id.content
|
||||||
|
product_product = self.env['product.product'].sudo().search([('id', '=', int(message_queue_id.res_id))])
|
||||||
|
mrp_production_list = self.env['mrp.production'].sudo().search(
|
||||||
|
[('product_id', '=', product_product.id)])
|
||||||
|
production_num = 0
|
||||||
|
for mrp_production_info in mrp_production_list:
|
||||||
|
routing_type = '人工线下加工' if mrp_production_info.production_type == '人工线下加工' else '装夹预调'
|
||||||
|
mrp_production_ready = mrp_production_info.workorder_ids.filtered(
|
||||||
|
lambda w: w.routing_type == routing_type and w.state == 'ready')
|
||||||
|
if mrp_production_ready:
|
||||||
|
production_num += 1
|
||||||
|
if production_num >= 1:
|
||||||
|
url = self.get_request_url()
|
||||||
|
content = content.replace('{{product_id}}', product_product.name).replace(
|
||||||
|
'{{number}}', str(production_num)).replace(
|
||||||
|
'{{request_url}}', url)
|
||||||
|
contents.append(content)
|
||||||
|
if message_queue_id.message_template_id.name == '待质检提醒':
|
||||||
|
content = message_queue_id.message_template_id.content
|
||||||
|
product_product = self.env['product.product'].sudo().search([('id', '=', int(message_queue_id.res_id))])
|
||||||
|
quality_check_num = self.env['quality.check'].sudo().search_count(
|
||||||
|
[('product_id', '=', product_product.id), ('quality_state', '=', 'none')])
|
||||||
|
if quality_check_num >= 1:
|
||||||
|
url = self.env['ir.config_parameter'].sudo().get_param('web.base.url')
|
||||||
|
action_id = self.env.ref('quality_control.quality_check_action_report').id
|
||||||
|
url_with_id = f"{url}/web#view_type=list&action={action_id}"
|
||||||
|
content = content.replace('{{name}}', product_product.name).replace('{{url}}', url_with_id).replace(
|
||||||
|
'{{num}}', str(quality_check_num))
|
||||||
|
contents.append(content)
|
||||||
|
return contents, message_queue_ids
|
||||||
|
|
||||||
|
def get_request_url(self):
|
||||||
|
url = self.env['ir.config_parameter'].sudo().get_param('web.base.url')
|
||||||
|
action_id = self.env.ref('sf_message.mrp_workorder_issued_action').id
|
||||||
|
menu_id = self.env.ref('mrp.menu_mrp_root').id
|
||||||
|
active_id = self.env['mrp.workcenter'].sudo().search([('name', '=', '工件装夹中心')]).id
|
||||||
|
# 查询参数
|
||||||
|
params = {'menu_id': menu_id, 'action': action_id, 'model': 'mrp.workorder',
|
||||||
|
'view_type': 'list', 'active_id': active_id}
|
||||||
|
# 拼接查询参数
|
||||||
|
query_string = urlencode(params)
|
||||||
|
# 拼接URL
|
||||||
|
full_url = url + "/web#" + query_string
|
||||||
|
return full_url
|
||||||
@@ -8,20 +8,62 @@ class SFMessagePurchase(models.Model):
|
|||||||
|
|
||||||
def _get_message(self, message_queue_ids):
|
def _get_message(self, message_queue_ids):
|
||||||
contents = []
|
contents = []
|
||||||
|
process_outsourcing = set()
|
||||||
for message_queue_id in message_queue_ids:
|
for message_queue_id in message_queue_ids:
|
||||||
if message_queue_id.message_template_id.name == '坯料采购提醒':
|
if message_queue_id.message_template_id.name in (
|
||||||
|
'坯料采购提醒', '委外加工采购单提醒', '外购订单采购单提醒'):
|
||||||
content = message_queue_id.message_template_id.content
|
content = message_queue_id.message_template_id.content
|
||||||
url = self.request_url(int(message_queue_id.res_id))
|
url = self.request_url(int(message_queue_id.res_id))
|
||||||
purchase_order_line = self.env['purchase.order'].search([('id', '=', int(message_queue_id.res_id))])
|
purchase_order_line = self.env['purchase.order'].search([('id', '=', int(message_queue_id.res_id))])
|
||||||
content = content.replace('{{name}}', purchase_order_line.name).replace(
|
content = content.replace('{{name}}', purchase_order_line.name).replace(
|
||||||
'{{request_url}}', url)
|
'{{request_url}}', url)
|
||||||
contents.append(content)
|
contents.append(content)
|
||||||
return contents
|
if message_queue_id.message_template_id.name == '工序外协采购单通知':
|
||||||
|
content = message_queue_id.message_template_id.content
|
||||||
|
purchase_order_line = self.env['purchase.order'].sudo().search(
|
||||||
|
[('id', '=', int(message_queue_id.res_id))])
|
||||||
|
mrp_production = self.env['mrp.production'].sudo().search([('name', '=', purchase_order_line.origin)])
|
||||||
|
process_outsourcing.add(mrp_production.product_id.id)
|
||||||
|
if message_queue_id.message_template_id.name in ('采购订单预警提醒', '采购单已逾期提醒'):
|
||||||
|
content = message_queue_id.message_template_id.content
|
||||||
|
if message_queue_id.message_template_id.name == '采购订单预警提醒':
|
||||||
|
domain = [('delivery_warning', '=', 'warning')]
|
||||||
|
action_id = self.env.ref("sf_message.purchase_form_warning_action").id
|
||||||
|
else:
|
||||||
|
domain = [('delivery_warning', '=', 'overdue')]
|
||||||
|
action_id = self.env.ref("sf_message.purchase_form_overdue_action").id
|
||||||
|
purchase_order_num = self.env['purchase.order'].sudo().search_count(domain)
|
||||||
|
if purchase_order_num >= 1:
|
||||||
|
url = self.env['ir.config_parameter'].sudo().get_param('web.base.url')
|
||||||
|
menu_id = self.env.ref('purchase.menu_purchase_form_action').id
|
||||||
|
url_with_id = f"{url}/web#view_type=list&action={action_id}&menu_id={menu_id}"
|
||||||
|
content = content.replace('{{url}}', url_with_id).replace('{{num}}', str(purchase_order_num))
|
||||||
|
contents.append(content)
|
||||||
|
if process_outsourcing:
|
||||||
|
content_info = content
|
||||||
|
for products_id in process_outsourcing:
|
||||||
|
production_num = 0
|
||||||
|
product_name = self.env['product.product'].sudo().search([('id', '=', products_id)]).name
|
||||||
|
production_list = self.env['mrp.production'].sudo().search(
|
||||||
|
[('product_id', '=', products_id), ('state', '=', 'confirmed')])
|
||||||
|
for production_info in production_list:
|
||||||
|
workorder_ids = len(production_info.workorder_ids.filtered(
|
||||||
|
lambda p: p.routing_type == '表面工艺' and p.state != 'cancel'))
|
||||||
|
production_num += workorder_ids
|
||||||
|
if production_num >= 1:
|
||||||
|
url = self.env['ir.config_parameter'].sudo().get_param('web.base.url')
|
||||||
|
action_id = self.env.ref('purchase.purchase_form_action').id
|
||||||
|
url_with_id = f"{url}/web#view_type=list&action={action_id}"
|
||||||
|
new_content = (content_info.replace('{{name}}', product_name)
|
||||||
|
.replace('{{url}}', url_with_id)
|
||||||
|
.replace('{{num}}', str(production_num)))
|
||||||
|
contents.append(new_content)
|
||||||
|
return contents, message_queue_ids
|
||||||
|
|
||||||
def request_url(self, id):
|
def request_url(self, id):
|
||||||
url = self.env['ir.config_parameter'].get_param('web.base.url')
|
url = self.env['ir.config_parameter'].get_param('web.base.url')
|
||||||
action_id = self.env.ref('purchase.purchase_form_action').id
|
action_id = self.env.ref('purchase.purchase_form_action').id
|
||||||
menu_id = self.env['ir.model.data'].search([('name', '=', 'module_website_payment')]).id
|
menu_id = self.env.ref('purchase.menu_purchase_form_action').id
|
||||||
# 查询参数
|
# 查询参数
|
||||||
params = {'id': id, 'menu_id': menu_id, 'action': action_id,
|
params = {'id': id, 'menu_id': menu_id, 'action': action_id,
|
||||||
'model': 'purchase.order',
|
'model': 'purchase.order',
|
||||||
@@ -31,3 +73,27 @@ class SFMessagePurchase(models.Model):
|
|||||||
# 拼接URL
|
# 拼接URL
|
||||||
full_url = url + "/web#" + query_string
|
full_url = url + "/web#" + query_string
|
||||||
return full_url
|
return full_url
|
||||||
|
|
||||||
|
def _overdue_or_warning_func(self):
|
||||||
|
last_overdue_order, last_warning_order = super(SFMessagePurchase, self)._overdue_or_warning_func()
|
||||||
|
if last_overdue_order:
|
||||||
|
business_node_id = self.env.ref('sf_message.template_purchase_order_overdue').id
|
||||||
|
purchase_order_has = self._get_message_queue(business_node_id)
|
||||||
|
if not purchase_order_has:
|
||||||
|
last_overdue_order.add_queue("采购单已逾期提醒")
|
||||||
|
if last_warning_order:
|
||||||
|
business_node_id = self.env.ref('sf_message.template_purchase_order_warning').id
|
||||||
|
purchase_order_has = self._get_message_queue(business_node_id)
|
||||||
|
if not purchase_order_has:
|
||||||
|
last_warning_order.add_queue("采购订单预警提醒")
|
||||||
|
|
||||||
|
def _get_message_queue(self, business_node_id):
|
||||||
|
message_template = self.env["jikimo.message.template"].sudo().search([
|
||||||
|
("model", "=", self._name),
|
||||||
|
("bussiness_node_id", "=", business_node_id)
|
||||||
|
], limit=1)
|
||||||
|
purchase_order_has = self.env['jikimo.message.queue'].sudo().search([
|
||||||
|
('message_status', '=', 'pending'),
|
||||||
|
('message_template_id', '=', message_template.id)
|
||||||
|
])
|
||||||
|
return purchase_order_has
|
||||||
|
|||||||
34
sf_message/models/sf_message_quality_check.py
Normal file
34
sf_message/models/sf_message_quality_check.py
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import logging
|
||||||
|
from odoo import models, fields, api, _
|
||||||
|
|
||||||
|
|
||||||
|
class SFMessageQualityCheck(models.Model):
|
||||||
|
_name = 'quality.check'
|
||||||
|
_inherit = ['quality.check', 'jikimo.message.dispatch']
|
||||||
|
|
||||||
|
@api.model_create_multi
|
||||||
|
def create(self, vals_list):
|
||||||
|
result = super().create(vals_list)
|
||||||
|
try:
|
||||||
|
# 判断是否为web页面创建请求
|
||||||
|
is_web_request = self.env.context.get('is_web_request', False)
|
||||||
|
if not is_web_request:
|
||||||
|
for obj in result:
|
||||||
|
jikimo_message_queue = self.get_message_queue(obj.product_id.id)
|
||||||
|
if not jikimo_message_queue:
|
||||||
|
obj.product_id.add_queue('待质检提醒')
|
||||||
|
except Exception as e:
|
||||||
|
logging.info('add_queue待质检提醒 error:%s' % e)
|
||||||
|
return result
|
||||||
|
|
||||||
|
def get_message_queue(self, res_id):
|
||||||
|
business_node_id = self.env.ref('sf_message.bussiness_quality_check').id
|
||||||
|
message_template = self.env["jikimo.message.template"].sudo().search([
|
||||||
|
("name", "=", '待质检提醒'),
|
||||||
|
("bussiness_node_id", "=", business_node_id)
|
||||||
|
], limit=1)
|
||||||
|
jikimo_message_queue = self.env['jikimo.message.queue'].sudo().search(
|
||||||
|
[('res_id', '=', res_id), ("message_status", "in", ("pending", "sent")),
|
||||||
|
('message_template_id', '=', message_template.id)])
|
||||||
|
return jikimo_message_queue
|
||||||
@@ -33,4 +33,4 @@ class SFMessageQualityCncTest(models.Model):
|
|||||||
content_template = content.replace('{{judge_num}}', str(i))
|
content_template = content.replace('{{judge_num}}', str(i))
|
||||||
content_template = content_template.replace('{{url}}', url_with_id)
|
content_template = content_template.replace('{{url}}', url_with_id)
|
||||||
contents.append(content_template)
|
contents.append(content_template)
|
||||||
return contents
|
return contents, message_queue_ids
|
||||||
|
|||||||
@@ -8,17 +8,6 @@ class SFMessageSale(models.Model):
|
|||||||
_name = 'sale.order'
|
_name = 'sale.order'
|
||||||
_inherit = ['sale.order', 'jikimo.message.dispatch']
|
_inherit = ['sale.order', 'jikimo.message.dispatch']
|
||||||
|
|
||||||
@api.model_create_multi
|
|
||||||
def create(self, vals_list):
|
|
||||||
res = super(SFMessageSale, self).create(vals_list)
|
|
||||||
if res:
|
|
||||||
try:
|
|
||||||
logging.info('add_queue res:%s' % res)
|
|
||||||
res.add_queue('待接单')
|
|
||||||
except Exception as e:
|
|
||||||
logging.info('add_queue error:%s' % e)
|
|
||||||
return res
|
|
||||||
|
|
||||||
# 确认接单
|
# 确认接单
|
||||||
def action_confirm(self):
|
def action_confirm(self):
|
||||||
res = super(SFMessageSale, self).action_confirm()
|
res = super(SFMessageSale, self).action_confirm()
|
||||||
@@ -29,19 +18,34 @@ class SFMessageSale(models.Model):
|
|||||||
purchase_order_id = []
|
purchase_order_id = []
|
||||||
if picking_ids:
|
if picking_ids:
|
||||||
for picking_id in picking_ids:
|
for picking_id in picking_ids:
|
||||||
|
picking_id.add_queue('待确认加工路线')
|
||||||
purchase_order_ids = (
|
purchase_order_ids = (
|
||||||
picking_id.procurement_group_id.stock_move_ids.created_purchase_line_id.order_id |
|
picking_id.procurement_group_id.stock_move_ids.created_purchase_line_id.order_id |
|
||||||
picking_id.procurement_group_id.stock_move_ids.move_orig_ids.purchase_line_id.order_id).ids
|
picking_id.procurement_group_id.stock_move_ids.move_orig_ids.purchase_line_id.order_id).ids
|
||||||
purchase_order_id.extend(purchase_order_ids)
|
purchase_order_id.extend(purchase_order_ids)
|
||||||
if purchase_order_id:
|
if purchase_order_id:
|
||||||
purchase_order_list = self.env['purchase.order'].sudo().search([('id', 'in', purchase_order_id)])
|
purchase_order_list = self.env['purchase.order'].sudo().search(
|
||||||
|
[('id', 'in', purchase_order_id)])
|
||||||
for purchase_order_info in purchase_order_list:
|
for purchase_order_info in purchase_order_list:
|
||||||
purchase_order_info.add_queue('坯料采购提醒')
|
purchase_order_info.add_queue('坯料采购提醒')
|
||||||
|
purchase_order_ids = self.order_line.purchase_line_ids.order_id | self.procurement_group_id.stock_move_ids.created_purchase_line_id.order_id | self.procurement_group_id.stock_move_ids.move_orig_ids.purchase_line_id.order_id
|
||||||
|
for purchase_order_id in purchase_order_ids:
|
||||||
|
if purchase_order_id.purchase_type == 'outsourcing':
|
||||||
|
purchase_order_id.add_queue('委外加工采购单提醒')
|
||||||
|
if purchase_order_id.purchase_type == 'outside':
|
||||||
|
purchase_order_id.add_queue('外购订单采购单提醒')
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.info('add_queue error:%s' % e)
|
logging.info('add_queue error:%s' % e)
|
||||||
logging.info('action_confirm res:%s' % res)
|
logging.info('action_confirm res:%s' % res)
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
def confirm_to_supply_method(self):
|
||||||
|
super(SFMessageSale, self).confirm_to_supply_method()
|
||||||
|
try:
|
||||||
|
self.add_queue('待确认供货方式')
|
||||||
|
except Exception as e:
|
||||||
|
logging.info('add_queue待确认供货方式 error:%s' % e)
|
||||||
|
|
||||||
# 继承并重写jikimo.message.dispatch的_get_message()
|
# 继承并重写jikimo.message.dispatch的_get_message()
|
||||||
def _get_message(self, message_queue_ids):
|
def _get_message(self, message_queue_ids):
|
||||||
contents = []
|
contents = []
|
||||||
@@ -53,14 +57,16 @@ class SFMessageSale(models.Model):
|
|||||||
time_range = timedelta(minutes=2)
|
time_range = timedelta(minutes=2)
|
||||||
i = 0
|
i = 0
|
||||||
for item in message_queue_ids:
|
for item in message_queue_ids:
|
||||||
if item.message_template_id.bussiness_node_id.name == '待接单':
|
if item.message_template_id.bussiness_node_id.name in ('待接单', '待确认供货方式'):
|
||||||
content = super(SFMessageSale, self)._get_message(item)
|
content, _ = super(SFMessageSale, self)._get_message(item)
|
||||||
action_id = self.env.ref('sale.action_quotations_with_onboarding').id
|
action_id = self.env.ref('sale.action_quotations_with_onboarding').id \
|
||||||
|
if item.message_template_id.bussiness_node_id.name == '待接单' \
|
||||||
|
else self.env.ref('sale.action_orders').id
|
||||||
url_with_id = f"{url}/web#id={item.res_id}&view_type=form&action={action_id}"
|
url_with_id = f"{url}/web#id={item.res_id}&view_type=form&action={action_id}"
|
||||||
content = content[0].replace('{{url}}', url_with_id)
|
content = content[0].replace('{{url}}', url_with_id)
|
||||||
contents.append(content)
|
contents.append(content)
|
||||||
elif item.message_template_id.bussiness_node_id.name == '确认接单':
|
elif item.message_template_id.bussiness_node_id.name == '确认接单':
|
||||||
content = super(SFMessageSale, self)._get_message(item)
|
content, _ = super(SFMessageSale, self)._get_message(item)
|
||||||
sale_order_line = self.env['sale.order.line'].sudo().search([('order_id', '=', int(item.res_id))])
|
sale_order_line = self.env['sale.order.line'].sudo().search([('order_id', '=', int(item.res_id))])
|
||||||
product = sale_order_line[0].product_id.name if len(sale_order_line) == 1 else '%s...' % \
|
product = sale_order_line[0].product_id.name if len(sale_order_line) == 1 else '%s...' % \
|
||||||
sale_order_line[
|
sale_order_line[
|
||||||
@@ -97,7 +103,7 @@ class SFMessageSale(models.Model):
|
|||||||
elif bussiness_node == '销售订单已逾期':
|
elif bussiness_node == '销售订单已逾期':
|
||||||
content = content_template.replace('{{overdue_num}}', str(i))
|
content = content_template.replace('{{overdue_num}}', str(i))
|
||||||
contents.append(content)
|
contents.append(content)
|
||||||
return contents
|
return contents, message_queue_ids
|
||||||
|
|
||||||
# # 销售订单逾期预警和已逾期
|
# # 销售订单逾期预警和已逾期
|
||||||
def _overdue_or_warning_func(self):
|
def _overdue_or_warning_func(self):
|
||||||
|
|||||||
@@ -12,26 +12,44 @@ class SFMessageStockPicking(models.Model):
|
|||||||
@api.model_create_multi
|
@api.model_create_multi
|
||||||
def create(self, vals):
|
def create(self, vals):
|
||||||
result = super(SFMessageStockPicking, self).create(vals)
|
result = super(SFMessageStockPicking, self).create(vals)
|
||||||
for obj in result:
|
try:
|
||||||
if obj.location_id.name == '进货' and obj.location_dest_id.name == '刀具房':
|
for obj in result:
|
||||||
obj.add_queue('调拨入库')
|
if obj.location_id.name == '进货' and obj.location_dest_id.name == '刀具房':
|
||||||
|
obj.add_queue('调拨入库')
|
||||||
|
except Exception as e:
|
||||||
|
logging.info('add_queue调拨入库 error:%s' % e)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
@api.depends('move_type', 'immediate_transfer', 'move_ids.state', 'move_ids.picking_id')
|
@api.depends('move_type', 'immediate_transfer', 'move_ids.state', 'move_ids.picking_id')
|
||||||
def _compute_state(self):
|
def _compute_state(self):
|
||||||
super(SFMessageStockPicking, self)._compute_state()
|
super(SFMessageStockPicking, self)._compute_state()
|
||||||
for record in self:
|
try:
|
||||||
if record.state == 'assigned' and record.check_in == 'PC':
|
for record in self:
|
||||||
record.add_queue('坯料发料提醒')
|
if (record.state == 'assigned' and record.picking_type_id.sequence_code == 'PC'
|
||||||
|
and record.product_id.categ_id.type == '坯料'):
|
||||||
|
record.add_queue('坯料发料提醒')
|
||||||
|
|
||||||
if record.picking_type_id.sequence_code == 'SFP' and record.state == 'done':
|
if record.picking_type_id.sequence_code == 'SFP' and record.state == 'done':
|
||||||
stock_picking_sfp = record.env['stock.picking'].search(
|
stock_picking_sfp = record.env['stock.picking'].search(
|
||||||
[('origin', '=', record.origin), ('state', '!=', 'done'),
|
[('origin', '=', record.origin), ('state', '!=', 'done'),
|
||||||
('picking_type_id.sequence_code', '=', 'SFP')])
|
('picking_type_id.sequence_code', '=', 'SFP')])
|
||||||
if not stock_picking_sfp:
|
if not stock_picking_sfp:
|
||||||
stock_picking_send = self.env["jikimo.message.queue"].sudo().search([('res_id', '=', record.id)])
|
stock_picking_send = self.env["jikimo.message.queue"].sudo().search(
|
||||||
if not stock_picking_send:
|
[('res_id', '=', record.id)])
|
||||||
record.add_queue('订单发货提醒')
|
if not stock_picking_send:
|
||||||
|
record.add_queue('订单发货提醒')
|
||||||
|
if record.picking_type_id.sequence_code == 'OCOUT' and record.state == 'assigned':
|
||||||
|
mrp_production = self.env['mrp.production'].sudo().search([('name', '=', record.origin)])
|
||||||
|
production_list = self.env['mrp.production'].sudo().search(
|
||||||
|
[('product_id', '=', mrp_production.product_id.id)])
|
||||||
|
manufacturing_order_names = production_list.mapped('name')
|
||||||
|
stock_picking_list = self.env['stock.picking'].sudo().search(
|
||||||
|
[('origin', 'in', manufacturing_order_names), ('picking_type_id.sequence_code', '=', 'OCOUT')])
|
||||||
|
all_ready_or_done = all(picking.state in ['assigned', 'done'] for picking in stock_picking_list)
|
||||||
|
if all_ready_or_done:
|
||||||
|
mrp_production.add_queue('工序外协发料通知')
|
||||||
|
except Exception as e:
|
||||||
|
logging.info('add_queue_compute_state error:%s' % e)
|
||||||
|
|
||||||
def deal_stock_picking_sfp(self, message_queue_id): # 处理订单发货提醒
|
def deal_stock_picking_sfp(self, message_queue_id): # 处理订单发货提醒
|
||||||
content = None
|
content = None
|
||||||
@@ -49,32 +67,23 @@ class SFMessageStockPicking(models.Model):
|
|||||||
|
|
||||||
def _get_message(self, message_queue_ids):
|
def _get_message(self, message_queue_ids):
|
||||||
contents = []
|
contents = []
|
||||||
product_id = []
|
|
||||||
for message_queue_id in message_queue_ids:
|
for message_queue_id in message_queue_ids:
|
||||||
i = 0
|
|
||||||
if message_queue_id.message_template_id.name == '坯料发料提醒':
|
if message_queue_id.message_template_id.name == '坯料发料提醒':
|
||||||
content = message_queue_id.message_template_id.content
|
content = message_queue_id.message_template_id.content
|
||||||
stock_picking_line = self.env['stock.picking'].sudo().search([('id', '=', int(message_queue_id.res_id))])
|
stock_picking_line = self.env['stock.picking'].sudo().search(
|
||||||
mrp_production_info = self.env['mrp.production'].sudo().search(
|
[('id', '=', int(message_queue_id.res_id))])
|
||||||
[('name', '=', stock_picking_line.origin)])
|
url = self.env['ir.config_parameter'].sudo().get_param('web.base.url')
|
||||||
mrp_production_list = self.env['mrp.production'].sudo().search(
|
action_id = self.env.ref('stock.action_picking_tree_ready').id
|
||||||
[('product_id', '=', mrp_production_info.product_id.id)])
|
menu_id = self.env.ref('stock.menu_stock_root').id
|
||||||
for mrp_production_line in mrp_production_list:
|
url_with_id = f"{url}/web#view_type=form&action={action_id}&menu_id={menu_id}&id={stock_picking_line.id}"
|
||||||
picking_ids = mrp_production_line.picking_ids
|
content = content.replace('{{name}}', stock_picking_line.name).replace(
|
||||||
for picking_id in picking_ids:
|
'{{request_url}}', url_with_id)
|
||||||
if picking_id.state == 'assigned' and picking_id.check_in == 'PC':
|
contents.append(content)
|
||||||
i += 1
|
|
||||||
if i > 0 and mrp_production_info.product_id.id not in product_id:
|
|
||||||
url = self.request_url()
|
|
||||||
content = content.replace('{{product_id}}', mrp_production_info.product_id.name).replace(
|
|
||||||
'{{number}}', str(i)).replace('{{request_url}}', url)
|
|
||||||
product_id.append(mrp_production_info.product_id.id)
|
|
||||||
contents.append(content)
|
|
||||||
elif message_queue_id.message_template_id.name == '订单发货提醒':
|
elif message_queue_id.message_template_id.name == '订单发货提醒':
|
||||||
content = self.deal_stock_picking_sfp(message_queue_id)
|
content = self.deal_stock_picking_sfp(message_queue_id)
|
||||||
if content:
|
if content:
|
||||||
contents.append(content)
|
contents.append(content)
|
||||||
return contents
|
return contents, message_queue_ids
|
||||||
|
|
||||||
def get_special_url(self, id, tmplate_name, special_name, model_id):
|
def get_special_url(self, id, tmplate_name, special_name, model_id):
|
||||||
menu_id = 0
|
menu_id = 0
|
||||||
@@ -86,19 +95,6 @@ class SFMessageStockPicking(models.Model):
|
|||||||
else:
|
else:
|
||||||
return super(SFMessageStockPicking, self).get_special_url(id, tmplate_name, special_name, model_id)
|
return super(SFMessageStockPicking, self).get_special_url(id, tmplate_name, special_name, model_id)
|
||||||
|
|
||||||
def request_url(self):
|
|
||||||
url = self.env['ir.config_parameter'].sudo().get_param('web.base.url')
|
|
||||||
action_id = self.env.ref('stock.stock_picking_type_action').id
|
|
||||||
menu_id = self.env['ir.model.data'].sudo().search([('name', '=', 'module_theme_treehouse')]).id
|
|
||||||
# 查询参数
|
|
||||||
params = {'menu_id': menu_id, 'action': action_id, 'model': 'stock.picking',
|
|
||||||
'view_type': 'kanban'}
|
|
||||||
# 拼接查询参数
|
|
||||||
query_string = urlencode(params)
|
|
||||||
# 拼接URL
|
|
||||||
full_url = url + "/web#" + query_string
|
|
||||||
return full_url
|
|
||||||
|
|
||||||
def request_url1(self, id):
|
def request_url1(self, id):
|
||||||
url = self.env['ir.config_parameter'].sudo().get_param('web.base.url')
|
url = self.env['ir.config_parameter'].sudo().get_param('web.base.url')
|
||||||
action_id = self.env.ref('stock.action_picking_tree_all').id
|
action_id = self.env.ref('stock.action_picking_tree_all').id
|
||||||
|
|||||||
@@ -16,4 +16,6 @@ class SfMessageTemplate(models.Model):
|
|||||||
res.append('mrp.workorder')
|
res.append('mrp.workorder')
|
||||||
res.append('sf.maintenance.logs')
|
res.append('sf.maintenance.logs')
|
||||||
res.append('quality.cnc.test')
|
res.append('quality.cnc.test')
|
||||||
|
res.append('mrp.production')
|
||||||
|
res.append('product.product')
|
||||||
return res
|
return res
|
||||||
|
|||||||
@@ -12,19 +12,30 @@ class SFMessageWork(models.Model):
|
|||||||
_name = 'mrp.workorder'
|
_name = 'mrp.workorder'
|
||||||
_inherit = ['mrp.workorder', 'jikimo.message.dispatch']
|
_inherit = ['mrp.workorder', 'jikimo.message.dispatch']
|
||||||
|
|
||||||
@api.depends('production_availability', 'blocked_by_workorder_ids.state', 'production_id.tool_state')
|
@api.depends('production_availability', 'blocked_by_workorder_ids', 'blocked_by_workorder_ids.state',
|
||||||
|
'production_id.tool_state', 'production_id.schedule_state', 'sequence',
|
||||||
|
'production_id.programming_state')
|
||||||
def _compute_state(self):
|
def _compute_state(self):
|
||||||
super(SFMessageWork, self)._compute_state()
|
super(SFMessageWork, self)._compute_state()
|
||||||
for workorder in self:
|
for workorder in self:
|
||||||
if workorder.state == 'ready' and workorder.routing_type == '装夹预调':
|
min_sequence_wk = None
|
||||||
|
work_ids = workorder.production_id.workorder_ids.filtered(lambda w: w.routing_type == '装夹预调')
|
||||||
|
if work_ids:
|
||||||
|
min_sequence_wk = work_ids.sorted(key=lambda w: w.sequence)[0]
|
||||||
|
if (
|
||||||
|
workorder.state == 'ready' and workorder.routing_type == '装夹预调' and workorder.id == min_sequence_wk.id) or (
|
||||||
|
workorder.state == 'ready' and workorder.routing_type == '人工线下加工'):
|
||||||
|
message_template = self.env["jikimo.message.template"].sudo().search(
|
||||||
|
[("name", "=", '工单已下发通知')], limit=1)
|
||||||
jikimo_message_queue = self.env['jikimo.message.queue'].sudo().search(
|
jikimo_message_queue = self.env['jikimo.message.queue'].sudo().search(
|
||||||
[('res_id', '=', workorder.id), ("message_status", "=", "pending")])
|
[('res_id', '=', workorder.production_id.product_id.id),
|
||||||
|
("message_status", "in", ("pending", "sent")),
|
||||||
|
('message_template_id', '=', message_template.id)])
|
||||||
if not jikimo_message_queue:
|
if not jikimo_message_queue:
|
||||||
workorder.add_queue('工单已下发通知')
|
workorder.production_id.product_id.add_queue('工单已下发通知')
|
||||||
|
|
||||||
def _get_message(self, message_queue_ids):
|
def _get_message(self, message_queue_ids):
|
||||||
contents = []
|
contents = []
|
||||||
product_id = []
|
|
||||||
bussiness_node = None
|
bussiness_node = None
|
||||||
url = self.env['ir.config_parameter'].sudo().get_param('web.base.url')
|
url = self.env['ir.config_parameter'].sudo().get_param('web.base.url')
|
||||||
current_time_strf = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
current_time_strf = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||||
@@ -37,20 +48,7 @@ class SFMessageWork(models.Model):
|
|||||||
}
|
}
|
||||||
i = 0
|
i = 0
|
||||||
for message_queue_id in message_queue_ids:
|
for message_queue_id in message_queue_ids:
|
||||||
if message_queue_id.message_template_id.name == '工单已下发通知':
|
if message_queue_id.message_template_id.name in template_names['预警'] + template_names['已逾期']:
|
||||||
content = message_queue_id.message_template_id.content
|
|
||||||
mrp_workorder_line = self.env['mrp.workorder'].sudo().search([('id', '=', int(message_queue_id.res_id))])
|
|
||||||
mrp_workorder_list = self.env['mrp.workorder'].sudo().search(
|
|
||||||
[('product_id', '=', mrp_workorder_line.product_id.id), ('state', '=', 'ready'),
|
|
||||||
('routing_type', '=', '装夹预调')])
|
|
||||||
if len(mrp_workorder_list) > 0 and mrp_workorder_line.product_id.id not in product_id:
|
|
||||||
url = self.request_url()
|
|
||||||
content = content.replace('{{product_id}}', mrp_workorder_line.product_id.name).replace(
|
|
||||||
'{{number}}', str(len(mrp_workorder_list))).replace(
|
|
||||||
'{{request_url}}', url)
|
|
||||||
product_id.append(mrp_workorder_line.product_id.id)
|
|
||||||
contents.append(content)
|
|
||||||
elif message_queue_id.message_template_id.name in template_names['预警'] + template_names['已逾期']:
|
|
||||||
item = message_queue_id.message_template_id
|
item = message_queue_id.message_template_id
|
||||||
bussiness_node = item.bussiness_node_id.name
|
bussiness_node = item.bussiness_node_id.name
|
||||||
for reminder_time in item.reminder_time_ids:
|
for reminder_time in item.reminder_time_ids:
|
||||||
@@ -93,21 +91,7 @@ class SFMessageWork(models.Model):
|
|||||||
elif bussiness_node in template_names['已逾期']:
|
elif bussiness_node in template_names['已逾期']:
|
||||||
content = content_template.replace('{{overdue_num}}', str(i))
|
content = content_template.replace('{{overdue_num}}', str(i))
|
||||||
contents.append(content)
|
contents.append(content)
|
||||||
return contents
|
return contents, message_queue_ids
|
||||||
|
|
||||||
def request_url(self):
|
|
||||||
url = self.env['ir.config_parameter'].sudo().get_param('web.base.url')
|
|
||||||
action_id = self.env.ref('sf_message.mrp_workorder_issued_action').id
|
|
||||||
menu_id = self.env['ir.model.data'].sudo().search([('name', '=', 'module_stock_dropshipping')]).id
|
|
||||||
active_id = self.env['mrp.workcenter'].sudo().search([('name', '=', '工件装夹中心')]).id
|
|
||||||
# 查询参数
|
|
||||||
params = {'menu_id': menu_id, 'action': action_id, 'model': 'mrp.workorder',
|
|
||||||
'view_type': 'list', 'active_id': active_id}
|
|
||||||
# 拼接查询参数
|
|
||||||
query_string = urlencode(params)
|
|
||||||
# 拼接URL
|
|
||||||
full_url = url + "/web#" + query_string
|
|
||||||
return full_url
|
|
||||||
|
|
||||||
def _overdue_or_warning_func(self):
|
def _overdue_or_warning_func(self):
|
||||||
workorders = self.env['mrp.workorder'].search(
|
workorders = self.env['mrp.workorder'].search(
|
||||||
|
|||||||
46
sf_message/views/purchase_order_view.xml
Normal file
46
sf_message/views/purchase_order_view.xml
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<odoo>
|
||||||
|
<record id="purchase_form_warning_action" model="ir.actions.act_window">
|
||||||
|
<field name="name">采购订单</field>
|
||||||
|
<field name="type">ir.actions.act_window</field>
|
||||||
|
<field name="res_model">purchase.order</field>
|
||||||
|
<field name="view_mode">tree,kanban,form,pivot,graph,calendar,activity</field>
|
||||||
|
<field name="view_ids" eval="[(5, 0, 0),
|
||||||
|
(0, 0, {'view_mode': 'tree', 'view_id': ref('purchase.purchase_order_view_tree')}),
|
||||||
|
(0, 0, {'view_mode': 'kanban', 'view_id': ref('purchase.purchase_order_view_kanban_without_dashboard')}),
|
||||||
|
]"/>
|
||||||
|
<field name="domain">[('state','in',('purchase', 'done'))]</field>
|
||||||
|
<field name="search_view_id" ref="purchase.purchase_order_view_search"/>
|
||||||
|
<field name="context">{'search_default_filter_order_warning':1}</field>
|
||||||
|
<field name="help" type="html">
|
||||||
|
<p class="o_view_nocontent_smiling_face">
|
||||||
|
还没有采购订单, 我们先创建一个!
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
当您下单到您的供应商,确定您的询价它会变成采购订单.
|
||||||
|
</p>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="purchase_form_overdue_action" model="ir.actions.act_window">
|
||||||
|
<field name="name">采购订单</field>
|
||||||
|
<field name="type">ir.actions.act_window</field>
|
||||||
|
<field name="res_model">purchase.order</field>
|
||||||
|
<field name="view_mode">tree,kanban,form,pivot,graph,calendar,activity</field>
|
||||||
|
<field name="view_ids" eval="[(5, 0, 0),
|
||||||
|
(0, 0, {'view_mode': 'tree', 'view_id': ref('purchase.purchase_order_view_tree')}),
|
||||||
|
(0, 0, {'view_mode': 'kanban', 'view_id': ref('purchase.purchase_order_view_kanban_without_dashboard')}),
|
||||||
|
]"/>
|
||||||
|
<field name="domain">[('state','in',('purchase', 'done'))]</field>
|
||||||
|
<field name="search_view_id" ref="purchase.purchase_order_view_search"/>
|
||||||
|
<field name="context">{'search_default_filter_order_overdue':1}</field>
|
||||||
|
<field name="help" type="html">
|
||||||
|
<p class="o_view_nocontent_smiling_face">
|
||||||
|
还没有采购订单, 我们先创建一个!
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
当您下单到您的供应商,确定您的询价它会变成采购订单.
|
||||||
|
</p>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
</odoo>
|
||||||
37
sf_message/views/stock_picking_view.xml
Normal file
37
sf_message/views/stock_picking_view.xml
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<odoo>
|
||||||
|
|
||||||
|
<record id="view_picking_internal_search_sf" model="ir.ui.view">
|
||||||
|
<field name="name">stock.picking.search</field>
|
||||||
|
<field name="model">stock.picking</field>
|
||||||
|
<field name="inherit_id" ref="stock.view_picking_internal_search"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//filter[@name='activities_exception']" position="before">
|
||||||
|
<filter name="outsourced" string="外协出库" domain="[('picking_type_id.name', '=', '外协出库')]"
|
||||||
|
invisible="1"/>
|
||||||
|
<separator/>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
|
||||||
|
<record id="action_picking_outsourced_tree_ready" model="ir.actions.act_window">
|
||||||
|
<field name="name">待办</field>
|
||||||
|
<field name="res_model">stock.picking</field>
|
||||||
|
<field name="type">ir.actions.act_window</field>
|
||||||
|
<field name="view_mode">tree,kanban,form,calendar</field>
|
||||||
|
<field name="domain"></field>
|
||||||
|
<field name="context">{'search_default_outsourced': 1,'contact_display': 'partner_address',
|
||||||
|
'search_default_available': 1}
|
||||||
|
</field>
|
||||||
|
<field name="search_view_id" ref="stock.view_picking_internal_search"/>
|
||||||
|
<field name="help" type="html">
|
||||||
|
<p class="o_view_nocontent_smiling_face">
|
||||||
|
没有仓库调拨。 让我们创建一个!
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
移动允许您将产品从一个位置移动到另外一个位置。
|
||||||
|
</p>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
</odoo>
|
||||||
@@ -26,12 +26,18 @@ class Sf_Mrs_Connect(http.Controller, MultiInheritController):
|
|||||||
ret = json.loads(ret['result'])
|
ret = json.loads(ret['result'])
|
||||||
logging.info('下发编程单:%s' % ret)
|
logging.info('下发编程单:%s' % ret)
|
||||||
domain = [('programming_no', '=', ret['programming_no'])]
|
domain = [('programming_no', '=', ret['programming_no'])]
|
||||||
if ret['manufacturing_type'] in ('scrap', 'invalid_tool_rework'):
|
if ret['manufacturing_type'] in ('scrap', 'invalid_tool_rework', 'rework'):
|
||||||
domain += [('state', 'not in', ['done', 'scrap', 'cancel'])]
|
domain += [('state', 'not in', ['done', 'scrap', 'cancel'])]
|
||||||
else:
|
else:
|
||||||
domain += [('state', 'in', ['confirmed', 'pending_cam'])]
|
domain += [('state', 'in', ['confirmed', 'pending_cam'])]
|
||||||
productions = request.env['mrp.production'].with_user(
|
productions = request.env['mrp.production'].with_user(
|
||||||
request.env.ref("base.user_admin")).search(domain)
|
request.env.ref("base.user_admin")).search(domain)
|
||||||
|
productions_technology_to_confirmed = request.env['mrp.production'].with_user(
|
||||||
|
request.env.ref("base.user_admin")).search(
|
||||||
|
[('programming_no', '=', ret['programming_no']), ('state', 'in', ['technology_to_confirmed'])])
|
||||||
|
if productions_technology_to_confirmed:
|
||||||
|
res = {'status': -2, 'message': '查询到待工艺确认的制造订单'}
|
||||||
|
return json.JSONEncoder().encode(res)
|
||||||
if productions:
|
if productions:
|
||||||
# 拉取所有加工面的程序文件
|
# 拉取所有加工面的程序文件
|
||||||
for r in ret['processing_panel'].split(','):
|
for r in ret['processing_panel'].split(','):
|
||||||
@@ -50,7 +56,7 @@ class Sf_Mrs_Connect(http.Controller, MultiInheritController):
|
|||||||
res['message'] = '编程单号为%s的CNC程序文件从FTP拉取失败' % (ret['programming_no'])
|
res['message'] = '编程单号为%s的CNC程序文件从FTP拉取失败' % (ret['programming_no'])
|
||||||
return json.JSONEncoder().encode(res)
|
return json.JSONEncoder().encode(res)
|
||||||
for production in productions:
|
for production in productions:
|
||||||
production.write({'programming_state': '已编程', 'work_state': '已编程'})
|
production.write({'programming_state': '已编程', 'work_state': '已编程', 'is_rework': False})
|
||||||
for panel in ret['processing_panel'].split(','):
|
for panel in ret['processing_panel'].split(','):
|
||||||
# 查询状态为进行中且工序类型为CNC加工的工单
|
# 查询状态为进行中且工序类型为CNC加工的工单
|
||||||
cnc_workorder_has = production.workorder_ids.filtered(
|
cnc_workorder_has = production.workorder_ids.filtered(
|
||||||
|
|||||||
@@ -13,7 +13,8 @@ class sf_production_plan(models.Model):
|
|||||||
_description = 'sf_production_plan'
|
_description = 'sf_production_plan'
|
||||||
_inherit = ['mail.thread']
|
_inherit = ['mail.thread']
|
||||||
# _order = 'state desc, write_date desc'
|
# _order = 'state desc, write_date desc'
|
||||||
|
part_name = fields.Char('零件名称', related='product_id.part_name', readonly=True)
|
||||||
|
part_number = fields.Char('零件图号', related='product_id.part_number', readonly=True)
|
||||||
state = fields.Selection([
|
state = fields.Selection([
|
||||||
('draft', '待排程'),
|
('draft', '待排程'),
|
||||||
('done', '已排程'),
|
('done', '已排程'),
|
||||||
@@ -231,26 +232,39 @@ class sf_production_plan(models.Model):
|
|||||||
if not record.production_line_id:
|
if not record.production_line_id:
|
||||||
raise ValidationError("未选择生产线")
|
raise ValidationError("未选择生产线")
|
||||||
else:
|
else:
|
||||||
|
# 自动化产线加工
|
||||||
if record.production_id.workorder_ids:
|
if record.production_id.workorder_ids:
|
||||||
last_cnc_start = record.date_planned_start if record.date_planned_start else datetime.now()
|
# 自动化产线加工
|
||||||
for item in record.production_id.workorder_ids:
|
if record.production_id.production_type == '自动化产线加工':
|
||||||
if item.name == 'CNC加工':
|
# 找到第一张CNC加工工单
|
||||||
# 将同一个面的所有工单筛选出来
|
first_cnc_workorder = record.production_id.workorder_ids.filtered(lambda x: x.name == 'CNC加工')[0]
|
||||||
workorder_list = record.production_id.workorder_ids.filtered(lambda x: x.processing_panel == item.processing_panel)
|
date_start = record.date_planned_start if record.date_planned_start else datetime.now()
|
||||||
routing_workcenter = record.env['mrp.routing.workcenter'].sudo().search(
|
routing_workcenter = first_cnc_workorder.technology_design_id.route_id
|
||||||
[('name', '=', 'CNC加工')], limit=1)
|
# 设置一个小的开始时间
|
||||||
# 设置一个小的开始时间
|
first_cnc_workorder.date_planned_start = datetime.now() - timedelta(days=100)
|
||||||
item.date_planned_start = datetime.now() - timedelta(days=100)
|
first_cnc_workorder.date_planned_finished = date_start + timedelta(
|
||||||
item.date_planned_finished = last_cnc_start + timedelta(
|
minutes=routing_workcenter.time_cycle + routing_workcenter.reserved_duration)
|
||||||
minutes=routing_workcenter.time_cycle)
|
first_cnc_workorder.date_planned_start = date_start
|
||||||
item.date_planned_start = last_cnc_start
|
record.sudo().production_id.plan_start_processing_time = first_cnc_workorder.date_planned_start
|
||||||
record.sudo().production_id.plan_start_processing_time = item.date_planned_start
|
first_cnc_workorder.duration_expected = routing_workcenter.time_cycle + routing_workcenter.reserved_duration
|
||||||
item.duration_expected = routing_workcenter.time_cycle
|
record.calculate_plan_time(first_cnc_workorder, record.production_id.workorder_ids)
|
||||||
pre_duration , next_duration = record.calculate_plan_time(item, workorder_list)
|
# 找到最后一张CNC加工工单
|
||||||
record.date_planned_finished = item.date_planned_finished
|
last_cnc_workorder = record.production_id.workorder_ids.filtered(lambda x: x.name == 'CNC加工')[-1]
|
||||||
# 计算下一个cnc工单的开始时间
|
record.date_planned_finished = last_cnc_workorder.date_planned_finished
|
||||||
last_cnc_start = workorder_list[-1].date_planned_finished + timedelta(minutes=pre_duration)
|
else:
|
||||||
# 没有工单也能排程
|
# 人工线下加工只排第一张工单
|
||||||
|
item = record.production_id.workorder_ids[0]
|
||||||
|
wo_start = record.date_planned_start if record.date_planned_start else datetime.now()
|
||||||
|
routing_workcenter = item.technology_design_id.route_id
|
||||||
|
item.date_planned_start = datetime.now() - timedelta(days=100)
|
||||||
|
item.date_planned_finished = wo_start + timedelta(
|
||||||
|
minutes=routing_workcenter.time_cycle + routing_workcenter.reserved_duration)
|
||||||
|
item.date_planned_start = wo_start
|
||||||
|
record.sudo().production_id.plan_start_processing_time = item.date_planned_start
|
||||||
|
item.duration_expected = routing_workcenter.time_cycle + routing_workcenter.reserved_duration
|
||||||
|
record.calculate_plan_time(item, record.production_id.workorder_ids)
|
||||||
|
last_cnc_workorder = record.production_id.workorder_ids[-1]
|
||||||
|
record.date_planned_finished = last_cnc_workorder.date_planned_finished
|
||||||
record.state = 'done'
|
record.state = 'done'
|
||||||
# record.production_id.schedule_state = '已排'
|
# record.production_id.schedule_state = '已排'
|
||||||
record.sudo().production_id.schedule_state = '已排'
|
record.sudo().production_id.schedule_state = '已排'
|
||||||
@@ -259,10 +273,10 @@ class sf_production_plan(models.Model):
|
|||||||
# sale_obj = self.env['sale.order'].search([('name', '=', record.origin)])
|
# sale_obj = self.env['sale.order'].search([('name', '=', record.origin)])
|
||||||
# if 'S' in sale_obj.name:
|
# if 'S' in sale_obj.name:
|
||||||
# sale_obj.schedule_status = 'to process'
|
# sale_obj.schedule_status = 'to process'
|
||||||
mrp_production_ids = record.production_id._get_children().ids
|
# mrp_production_ids = record.production_id._get_children().ids
|
||||||
print('mrp_production_ids', mrp_production_ids)
|
# print('mrp_production_ids', mrp_production_ids)
|
||||||
for i in mrp_production_ids:
|
# for i in mrp_production_ids:
|
||||||
record.env['mrp.production'].sudo().browse(i).schedule_state = '已排'
|
# record.env['mrp.production'].sudo().browse(i).schedule_state = '已排'
|
||||||
# record.production_id.date_planned_start = record.date_planned_start
|
# record.production_id.date_planned_start = record.date_planned_start
|
||||||
# record.production_id.date_planned_finished = record.date_planned_finished
|
# record.production_id.date_planned_finished = record.date_planned_finished
|
||||||
record.sudo().production_id.production_line_id = record.production_line_id.id
|
record.sudo().production_id.production_line_id = record.production_line_id.id
|
||||||
@@ -306,44 +320,40 @@ class sf_production_plan(models.Model):
|
|||||||
def calculate_plan_time(self, item, workorder_list):
|
def calculate_plan_time(self, item, workorder_list):
|
||||||
"""
|
"""
|
||||||
根据CNC工单的时间去计算之前的其他工单的开始结束时间
|
根据CNC工单的时间去计算之前的其他工单的开始结束时间
|
||||||
|
param:
|
||||||
|
item: 基准工单(根据该工单的开始结束时间去计算其他工单的开始结束时间)
|
||||||
|
workorder_list: 需排程的工单列表
|
||||||
"""
|
"""
|
||||||
item_position = 0
|
item_position = 0
|
||||||
for index, workorder in enumerate(workorder_list):
|
for index, workorder in enumerate(workorder_list):
|
||||||
if workorder.id == item.id:
|
if workorder.id == item.id:
|
||||||
item_position = index
|
item_position = index
|
||||||
break
|
break
|
||||||
routing_workcenters = self.env['mrp.routing.workcenter'].sudo().search([])
|
|
||||||
# 记录所有前序工序时长
|
|
||||||
previous_workorder_duration = 0
|
|
||||||
for i in range(item_position, -1, -1):
|
for i in range(item_position, -1, -1):
|
||||||
if i < 1:
|
if i < 1:
|
||||||
break
|
break
|
||||||
current_workorder = workorder_list[i]
|
current_workorder = workorder_list[i]
|
||||||
next_workorder = workorder_list[i - 1]
|
next_workorder = workorder_list[i - 1]
|
||||||
routing_workcenter = routing_workcenters.filtered(lambda x: x.name == next_workorder.name)[0]
|
routing_workcenter = next_workorder.technology_design_id.route_id
|
||||||
# 设置一个小的开始时间
|
# 设置一个小的开始时间
|
||||||
next_workorder.date_planned_start = datetime.now() - timedelta(days=100)
|
next_workorder.date_planned_start = datetime.now() - timedelta(days=100)
|
||||||
next_workorder.date_planned_finished = current_workorder.date_planned_start
|
next_workorder.date_planned_finished = current_workorder.date_planned_start
|
||||||
next_workorder.date_planned_start = next_workorder.date_planned_finished - timedelta(
|
next_workorder.date_planned_start = next_workorder.date_planned_finished - timedelta(
|
||||||
minutes=routing_workcenter.time_cycle)
|
minutes=routing_workcenter.time_cycle + routing_workcenter.reserved_duration)
|
||||||
next_workorder.duration_expected = routing_workcenter.time_cycle
|
next_workorder.duration_expected = routing_workcenter.time_cycle + routing_workcenter.reserved_duration
|
||||||
previous_workorder_duration += routing_workcenter.time_cycle
|
|
||||||
# 记录所有后续工序时长
|
|
||||||
next_workorder_duration = 0
|
|
||||||
for i in range(item_position, len(workorder_list) - 1):
|
for i in range(item_position, len(workorder_list) - 1):
|
||||||
if i > len(workorder_list) - 1:
|
if i > len(workorder_list) - 1:
|
||||||
break
|
break
|
||||||
current_workorder = workorder_list[i]
|
current_workorder = workorder_list[i]
|
||||||
next_workorder = workorder_list[i + 1]
|
next_workorder = workorder_list[i + 1]
|
||||||
routing_workcenter = routing_workcenters.filtered(lambda x: x.name == next_workorder.name)[0]
|
routing_workcenter = next_workorder.technology_design_id.route_id
|
||||||
# 设置一个小的开始时间
|
# 设置一个小的开始时间
|
||||||
next_workorder.date_planned_start = datetime.now() - timedelta(days=100)
|
next_workorder.date_planned_start = datetime.now() - timedelta(days=100)
|
||||||
next_workorder.date_planned_finished = current_workorder.date_planned_finished + timedelta(
|
next_workorder.date_planned_finished = current_workorder.date_planned_finished + timedelta(
|
||||||
minutes=routing_workcenter.time_cycle)
|
minutes=routing_workcenter.time_cycle + routing_workcenter.reserved_duration)
|
||||||
next_workorder.date_planned_start = current_workorder.date_planned_finished
|
next_workorder.date_planned_start = current_workorder.date_planned_finished
|
||||||
next_workorder.duration_expected = routing_workcenter.time_cycle
|
next_workorder.duration_expected = routing_workcenter.time_cycle + routing_workcenter.reserved_duration
|
||||||
next_workorder_duration += routing_workcenter.time_cycle
|
|
||||||
return previous_workorder_duration, next_workorder_duration
|
|
||||||
|
|
||||||
def calculate_plan_time_after(self, item, workorder_id_list):
|
def calculate_plan_time_after(self, item, workorder_id_list):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -17,6 +17,8 @@
|
|||||||
decoration-danger="state == 'finished'"/>
|
decoration-danger="state == 'finished'"/>
|
||||||
<field name="name"/>
|
<field name="name"/>
|
||||||
<field name="origin"/>
|
<field name="origin"/>
|
||||||
|
<field name="part_number" optional="show"/>
|
||||||
|
<field name="part_name" optional="hide"/>
|
||||||
<field name="order_deadline" widget="date"/>
|
<field name="order_deadline" widget="date"/>
|
||||||
<field name="product_qty"/>
|
<field name="product_qty"/>
|
||||||
<field name="production_line_id"/>
|
<field name="production_line_id"/>
|
||||||
@@ -257,7 +259,7 @@
|
|||||||
|
|
||||||
|
|
||||||
<record id="sf_production_plan_action" model="ir.actions.act_window">
|
<record id="sf_production_plan_action" model="ir.actions.act_window">
|
||||||
<field name="name">制造订单生产计划</field>
|
<field name="name">CNC产线计划排程</field>
|
||||||
<field name="type">ir.actions.act_window</field>
|
<field name="type">ir.actions.act_window</field>
|
||||||
<field name="res_model">sf.production.plan</field>
|
<field name="res_model">sf.production.plan</field>
|
||||||
<field name="view_mode">gantt,tree,form</field>
|
<field name="view_mode">gantt,tree,form</field>
|
||||||
|
|||||||
@@ -2023,6 +2023,11 @@ msgstr "<span class=\"o_stat_text\">损失</span>"
|
|||||||
msgid "<span class=\"o_stat_text\">Maintenance</span>"
|
msgid "<span class=\"o_stat_text\">Maintenance</span>"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: sf_manufacturing
|
||||||
|
#: model:ir.model.fields.selection,name:sf_manufacturing.selection__mrp_production__state__confirmed
|
||||||
|
msgid "待排程"
|
||||||
|
msgstr "待排程"
|
||||||
|
|
||||||
#. module: mrp
|
#. module: mrp
|
||||||
#: model_terms:ir.ui.view,arch_db:mrp.product_product_form_view_bom_button
|
#: model_terms:ir.ui.view,arch_db:mrp.product_product_form_view_bom_button
|
||||||
#: model_terms:ir.ui.view,arch_db:mrp.product_template_form_view_bom_button
|
#: model_terms:ir.ui.view,arch_db:mrp.product_template_form_view_bom_button
|
||||||
@@ -6678,7 +6683,7 @@ msgstr "账单状态"
|
|||||||
#: model_terms:ir.ui.view,arch_db:account.account_journal_dashboard_kanban_view
|
#: model_terms:ir.ui.view,arch_db:account.account_journal_dashboard_kanban_view
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Bills"
|
msgid "Bills"
|
||||||
msgstr "账单"
|
msgstr "发票账单"
|
||||||
|
|
||||||
#. module: account
|
#. module: account
|
||||||
#: model_terms:ir.ui.view,arch_db:account.account_journal_dashboard_kanban_view
|
#: model_terms:ir.ui.view,arch_db:account.account_journal_dashboard_kanban_view
|
||||||
@@ -15907,7 +15912,7 @@ msgstr "内部参考"
|
|||||||
#: model_terms:ir.ui.view,arch_db:account.account_journal_dashboard_kanban_view
|
#: model_terms:ir.ui.view,arch_db:account.account_journal_dashboard_kanban_view
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Internal Transfer"
|
msgid "Internal Transfer"
|
||||||
msgstr "内部转账"
|
msgstr "厂内出入库"
|
||||||
|
|
||||||
#. module: account
|
#. module: account
|
||||||
#: model:ir.model.fields,field_description:account.field_res_config_settings__transfer_account_id
|
#: model:ir.model.fields,field_description:account.field_res_config_settings__transfer_account_id
|
||||||
@@ -16133,7 +16138,7 @@ msgstr "库存概览"
|
|||||||
#. module: stock
|
#. module: stock
|
||||||
#: model:ir.actions.act_window,name:stock.stock_picking_type_action
|
#: model:ir.actions.act_window,name:stock.stock_picking_type_action
|
||||||
msgid "Internal Transfers"
|
msgid "Internal Transfers"
|
||||||
msgstr "厂内出入库"
|
msgstr "库存概览"
|
||||||
|
|
||||||
#. module: stock
|
#. module: stock
|
||||||
#: model:ir.model.fields,field_description:stock.field_stock_quant__inventory_quantity_set
|
#: model:ir.model.fields,field_description:stock.field_stock_quant__inventory_quantity_set
|
||||||
@@ -23023,7 +23028,7 @@ msgstr "支付:支付收据"
|
|||||||
#: model_terms:ir.ui.view,arch_db:account.view_account_payment_search
|
#: model_terms:ir.ui.view,arch_db:account.view_account_payment_search
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Payments"
|
msgid "Payments"
|
||||||
msgstr "支付"
|
msgstr "付款单"
|
||||||
|
|
||||||
#. module: account
|
#. module: account
|
||||||
#: model_terms:ir.actions.act_window,help:account.action_account_payments
|
#: model_terms:ir.actions.act_window,help:account.action_account_payments
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ class SfQualityCncTest(models.Model):
|
|||||||
equipment_id = fields.Many2one(related='workorder_id.equipment_id', string='加工设备')
|
equipment_id = fields.Many2one(related='workorder_id.equipment_id', string='加工设备')
|
||||||
production_line_id = fields.Many2one(related='workorder_id.production_line_id',
|
production_line_id = fields.Many2one(related='workorder_id.production_line_id',
|
||||||
string='生产线')
|
string='生产线')
|
||||||
part_number = fields.Char(related='workorder_id.part_number', string='成品零件图号')
|
part_number = fields.Char(related='workorder_id.part_number', string='零件图号')
|
||||||
detection_report = fields.Binary(related='workorder_id.detection_report', readonly=True, string='检测报告')
|
detection_report = fields.Binary(related='workorder_id.detection_report', readonly=True, string='检测报告')
|
||||||
state = fields.Selection([
|
state = fields.Selection([
|
||||||
('waiting', '待判定'),
|
('waiting', '待判定'),
|
||||||
@@ -25,7 +25,7 @@ class SfQualityCncTest(models.Model):
|
|||||||
('pass', '合格'),
|
('pass', '合格'),
|
||||||
('fail', '不合格')], string='判定结果')
|
('fail', '不合格')], string='判定结果')
|
||||||
number = fields.Integer('数量', default=1)
|
number = fields.Integer('数量', default=1)
|
||||||
test_results = fields.Selection([("合格", "合格"), ("返工", "返工"), ("报废", "报废")], string="检测结果")
|
test_results = fields.Selection([("合格", "合格"), ("返工", "返工")], string="检测结果")
|
||||||
reason = fields.Selection(
|
reason = fields.Selection(
|
||||||
[("programming", "编程"), ("cutter", "刀具"), ("clamping", "装夹"), ("operate computer", "操机"),
|
[("programming", "编程"), ("cutter", "刀具"), ("clamping", "装夹"), ("operate computer", "操机"),
|
||||||
("technology", "工艺"), ("customer redrawing", "客户改图")], string="原因")
|
("technology", "工艺"), ("customer redrawing", "客户改图")], string="原因")
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user