odoo-addon-project-task-stock 18.0.1.0.0.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- odoo/addons/project_task_stock/README.rst +146 -0
- odoo/addons/project_task_stock/__init__.py +1 -0
- odoo/addons/project_task_stock/__manifest__.py +24 -0
- odoo/addons/project_task_stock/demo/project_data.xml +28 -0
- odoo/addons/project_task_stock/demo/stock_picking_type_data.xml +11 -0
- odoo/addons/project_task_stock/i18n/es.po +337 -0
- odoo/addons/project_task_stock/i18n/fr.po +337 -0
- odoo/addons/project_task_stock/i18n/hr.po +345 -0
- odoo/addons/project_task_stock/i18n/it.po +344 -0
- odoo/addons/project_task_stock/i18n/project_task_stock.pot +329 -0
- odoo/addons/project_task_stock/i18n/pt_BR.po +345 -0
- odoo/addons/project_task_stock/models/__init__.py +7 -0
- odoo/addons/project_task_stock/models/account_analytic_line.py +21 -0
- odoo/addons/project_task_stock/models/project_project.py +45 -0
- odoo/addons/project_task_stock/models/project_task.py +279 -0
- odoo/addons/project_task_stock/models/stock_move.py +135 -0
- odoo/addons/project_task_stock/models/stock_scrap.py +25 -0
- odoo/addons/project_task_stock/readme/CONFIGURE.md +25 -0
- odoo/addons/project_task_stock/readme/CONTRIBUTORS.md +3 -0
- odoo/addons/project_task_stock/readme/DESCRIPTION.md +1 -0
- odoo/addons/project_task_stock/readme/USAGE.md +21 -0
- odoo/addons/project_task_stock/static/description/icon.png +0 -0
- odoo/addons/project_task_stock/static/description/index.html +485 -0
- odoo/addons/project_task_stock/tests/__init__.py +4 -0
- odoo/addons/project_task_stock/tests/common.py +100 -0
- odoo/addons/project_task_stock/tests/test_project_task_stock.py +350 -0
- odoo/addons/project_task_stock/views/project_project_view.xml +24 -0
- odoo/addons/project_task_stock/views/project_task_type_view.xml +14 -0
- odoo/addons/project_task_stock/views/project_task_view.xml +142 -0
- odoo/addons/project_task_stock/views/stock_move_view.xml +65 -0
- odoo/addons/project_task_stock/views/stock_scrap_views.xml +13 -0
- odoo_addon_project_task_stock-18.0.1.0.0.2.dist-info/METADATA +161 -0
- odoo_addon_project_task_stock-18.0.1.0.0.2.dist-info/RECORD +35 -0
- odoo_addon_project_task_stock-18.0.1.0.0.2.dist-info/WHEEL +5 -0
- odoo_addon_project_task_stock-18.0.1.0.0.2.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
# Copyright 2022-2025 Tecnativa - Víctor Martínez
|
|
2
|
+
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
|
3
|
+
from odoo import fields
|
|
4
|
+
from odoo.tests import Form
|
|
5
|
+
from odoo.tests.common import users
|
|
6
|
+
from odoo.tools import mute_logger
|
|
7
|
+
|
|
8
|
+
from .common import TestProjectStockBase
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class TestProjectTaskStock(TestProjectStockBase):
|
|
12
|
+
@classmethod
|
|
13
|
+
def setUpClass(cls):
|
|
14
|
+
super().setUpClass()
|
|
15
|
+
cls.quant_a = cls._create_stock_quant(cls.product_a, cls.location, 2)
|
|
16
|
+
cls.quant_b = cls._create_stock_quant(cls.product_b, cls.location, 1)
|
|
17
|
+
cls._create_stock_quant(cls.product_c, cls.location, 1)
|
|
18
|
+
cls.task = cls._create_task([(cls.product_a, 2), (cls.product_b, 1)])
|
|
19
|
+
cls.move_product_a = cls.task.move_ids.filtered(
|
|
20
|
+
lambda x: x.product_id == cls.product_a
|
|
21
|
+
)
|
|
22
|
+
cls.move_product_b = cls.task.move_ids.filtered(
|
|
23
|
+
lambda x: x.product_id == cls.product_b
|
|
24
|
+
)
|
|
25
|
+
cls.env.ref("base.user_admin").write(
|
|
26
|
+
{
|
|
27
|
+
"groups_id": [
|
|
28
|
+
(4, cls.env.ref("analytic.group_analytic_accounting").id),
|
|
29
|
+
],
|
|
30
|
+
}
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
@classmethod
|
|
34
|
+
def _create_stock_quant(cls, product, location, qty):
|
|
35
|
+
return cls.env["stock.quant"].create(
|
|
36
|
+
{"product_id": product.id, "location_id": location.id, "quantity": qty}
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
def test_project_task_misc(self):
|
|
40
|
+
self.assertTrue(self.task.group_id)
|
|
41
|
+
self.assertEqual(self.task.picking_type_id, self.picking_type)
|
|
42
|
+
self.assertEqual(self.task.location_id, self.location)
|
|
43
|
+
self.assertEqual(self.task.location_dest_id, self.location_dest)
|
|
44
|
+
self.assertEqual(self.move_product_a.name, self.task.name)
|
|
45
|
+
self.assertEqual(self.move_product_a.group_id, self.task.group_id)
|
|
46
|
+
self.assertEqual(self.move_product_a.reference, self.task.name)
|
|
47
|
+
self.assertEqual(self.move_product_a.location_id, self.location)
|
|
48
|
+
self.assertEqual(self.move_product_a.location_dest_id, self.location_dest)
|
|
49
|
+
self.assertEqual(self.move_product_a.picking_type_id, self.picking_type)
|
|
50
|
+
self.assertEqual(self.move_product_a.raw_material_task_id, self.task)
|
|
51
|
+
self.assertEqual(self.move_product_b.group_id, self.task.group_id)
|
|
52
|
+
self.assertEqual(self.move_product_b.location_id, self.location)
|
|
53
|
+
self.assertEqual(self.move_product_b.location_dest_id, self.location_dest)
|
|
54
|
+
self.assertEqual(self.move_product_b.picking_type_id, self.picking_type)
|
|
55
|
+
self.assertEqual(self.move_product_b.raw_material_task_id, self.task)
|
|
56
|
+
|
|
57
|
+
def _test_task_analytic_lines_from_task(self, amount):
|
|
58
|
+
self.task = self.env["project.task"].browse(self.task.id)
|
|
59
|
+
# Prevent error when hr_timesheet addon is installed.
|
|
60
|
+
stock_analytic_lines = self.task.sudo().stock_analytic_line_ids
|
|
61
|
+
self.assertEqual(len(stock_analytic_lines), 2)
|
|
62
|
+
self.assertEqual(sum(stock_analytic_lines.mapped("unit_amount")), 3)
|
|
63
|
+
self.assertEqual(sum(stock_analytic_lines.mapped("amount")), amount)
|
|
64
|
+
self.assertIn(
|
|
65
|
+
self.analytic_account,
|
|
66
|
+
stock_analytic_lines.mapped("account_id"),
|
|
67
|
+
)
|
|
68
|
+
# Prevent incoherence when hr_timesheet addon is installed.
|
|
69
|
+
if "project_id" in self.task.stock_analytic_line_ids._fields:
|
|
70
|
+
self.assertFalse(self.task.stock_analytic_line_ids.project_id)
|
|
71
|
+
|
|
72
|
+
def test_project_task_without_analytic_account(self):
|
|
73
|
+
self.task = self.env["project.task"].browse(self.task.id)
|
|
74
|
+
# Prevent error when hr_timesheet addon is installed.
|
|
75
|
+
if "allow_timesheets" in self.task.project_id._fields:
|
|
76
|
+
self.task.project_id.allow_timesheets = False
|
|
77
|
+
self.task.project_id.account_id = False
|
|
78
|
+
self.task.write({"stage_id": self.stage_done.id})
|
|
79
|
+
self.task.action_done()
|
|
80
|
+
self.assertFalse(self.task.sudo().stock_analytic_line_ids)
|
|
81
|
+
|
|
82
|
+
@users("manager-user")
|
|
83
|
+
def test_project_task_without_analytic_account_manager_user(self):
|
|
84
|
+
self.test_project_task_without_analytic_account()
|
|
85
|
+
|
|
86
|
+
def test_project_task_user_access_without_stock_group(self):
|
|
87
|
+
project_user_group = self.env.ref("project.group_project_user")
|
|
88
|
+
self.basic_user.write(
|
|
89
|
+
{
|
|
90
|
+
"groups_id": [(6, 0, [project_user_group.id])],
|
|
91
|
+
}
|
|
92
|
+
)
|
|
93
|
+
task_form = Form(self.task.with_user(self.basic_user))
|
|
94
|
+
self.assertEqual(task_form.project_id, self.project)
|
|
95
|
+
|
|
96
|
+
def test_project_task_analytic_lines_without_tags(self):
|
|
97
|
+
self.task = self.env["project.task"].browse(self.task.id)
|
|
98
|
+
self.task.write({"stage_id": self.stage_done.id})
|
|
99
|
+
self.task.action_done()
|
|
100
|
+
self._test_task_analytic_lines_from_task(-40)
|
|
101
|
+
self.assertEqual(
|
|
102
|
+
fields.first(self.task.stock_analytic_line_ids).date,
|
|
103
|
+
fields.Date.from_string("1990-01-01"),
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
@users("manager-user")
|
|
107
|
+
def test_project_task_analytic_lines_without_tags_manager_user(self):
|
|
108
|
+
self.test_project_task_analytic_lines_without_tags()
|
|
109
|
+
|
|
110
|
+
def test_project_task_analytic_lines_with_tag_1(self):
|
|
111
|
+
self.task = self.env["project.task"].browse(self.task.id)
|
|
112
|
+
self.task.write({"stock_analytic_date": "1991-01-01"})
|
|
113
|
+
self.task.write({"stage_id": self.stage_done.id})
|
|
114
|
+
self.task.action_done()
|
|
115
|
+
self._test_task_analytic_lines_from_task(-40)
|
|
116
|
+
self.assertEqual(
|
|
117
|
+
fields.first(self.task.stock_analytic_line_ids).date,
|
|
118
|
+
fields.Date.from_string("1991-01-01"),
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
@users("manager-user")
|
|
122
|
+
def test_project_task_analytic_lines_with_tag_1_manager_user(self):
|
|
123
|
+
self.task.stock_analytic_distribution = {self.analytic_account.id: 100}
|
|
124
|
+
self.test_project_task_analytic_lines_with_tag_1()
|
|
125
|
+
|
|
126
|
+
def test_project_task_analytic_lines_with_tag_2(self):
|
|
127
|
+
self.task = self.env["project.task"].browse(self.task.id)
|
|
128
|
+
self.task.project_id.stock_analytic_date = False
|
|
129
|
+
self.task.write({"stage_id": self.stage_done.id})
|
|
130
|
+
self.task.action_done()
|
|
131
|
+
self._test_task_analytic_lines_from_task(-40)
|
|
132
|
+
self.assertEqual(
|
|
133
|
+
fields.first(self.task.stock_analytic_line_ids).date, fields.date.today()
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
@users("manager-user")
|
|
137
|
+
def test_project_task_analytic_lines_with_tag_2_manager_user(self):
|
|
138
|
+
self.task.stock_analytic_distribution = {
|
|
139
|
+
self.analytic_account.id: 50,
|
|
140
|
+
self.analytic_account_2.id: 50,
|
|
141
|
+
}
|
|
142
|
+
self.test_project_task_analytic_lines_with_tag_2()
|
|
143
|
+
|
|
144
|
+
def test_project_task_process_done(self):
|
|
145
|
+
self.task = self.env["project.task"].browse(self.task.id)
|
|
146
|
+
self.assertEqual(self.move_product_a.state, "draft")
|
|
147
|
+
self.assertEqual(self.move_product_b.state, "draft")
|
|
148
|
+
# Change task stage (auto-confirm + auto-assign)
|
|
149
|
+
self.task.write({"stage_id": self.stage_done.id})
|
|
150
|
+
self.assertEqual(self.move_product_a.state, "assigned")
|
|
151
|
+
self.assertEqual(self.move_product_b.state, "assigned")
|
|
152
|
+
self.assertEqual(self.move_product_a.quantity, 2)
|
|
153
|
+
self.assertEqual(self.move_product_b.quantity, 1)
|
|
154
|
+
self.assertTrue(self.task.stock_moves_is_locked)
|
|
155
|
+
self.task.action_toggle_stock_moves_is_locked()
|
|
156
|
+
self.assertFalse(self.task.stock_moves_is_locked)
|
|
157
|
+
# Add new stock_move
|
|
158
|
+
self.task.write({"stage_id": self.stage_in_progress.id})
|
|
159
|
+
task_form = Form(self.task)
|
|
160
|
+
with task_form.move_ids.new() as move_form:
|
|
161
|
+
move_form.product_id = self.product_c
|
|
162
|
+
move_form.product_uom_qty = 1
|
|
163
|
+
task_form.save()
|
|
164
|
+
move_product_c = self.task.move_ids.filtered(
|
|
165
|
+
lambda x: x.product_id == self.product_c
|
|
166
|
+
)
|
|
167
|
+
self.assertEqual(move_product_c.group_id, self.task.group_id)
|
|
168
|
+
self.assertEqual(move_product_c.state, "draft")
|
|
169
|
+
self.task.action_assign()
|
|
170
|
+
self.assertEqual(move_product_c.state, "assigned")
|
|
171
|
+
self.task.write({"stage_id": self.stage_done.id})
|
|
172
|
+
# action_done
|
|
173
|
+
self.task.action_done()
|
|
174
|
+
self.assertEqual(self.move_product_a.state, "done")
|
|
175
|
+
self.assertEqual(self.move_product_b.state, "done")
|
|
176
|
+
self.assertEqual(self.move_product_a.quantity, 2)
|
|
177
|
+
self.assertEqual(self.move_product_b.quantity, 1)
|
|
178
|
+
self.assertEqual(move_product_c.quantity, 1)
|
|
179
|
+
|
|
180
|
+
@users("basic-user")
|
|
181
|
+
def test_project_task_process_done_basic_user(self):
|
|
182
|
+
self.test_project_task_process_done()
|
|
183
|
+
|
|
184
|
+
@mute_logger("odoo.models.unlink")
|
|
185
|
+
def test_project_task_process_cancel(self):
|
|
186
|
+
self.task = self.env["project.task"].browse(self.task.id)
|
|
187
|
+
self.assertEqual(self.move_product_a.state, "draft")
|
|
188
|
+
self.assertEqual(self.move_product_b.state, "draft")
|
|
189
|
+
# Change task stage
|
|
190
|
+
self.task.write({"stage_id": self.stage_done.id})
|
|
191
|
+
self.assertEqual(self.move_product_a.state, "assigned")
|
|
192
|
+
self.assertEqual(self.move_product_b.state, "assigned")
|
|
193
|
+
# action_done
|
|
194
|
+
self.task.action_done()
|
|
195
|
+
self.assertEqual(self.move_product_a.state, "done")
|
|
196
|
+
self.assertEqual(self.move_product_b.state, "done")
|
|
197
|
+
self.assertEqual(self.move_product_a.quantity, 2)
|
|
198
|
+
self.assertEqual(self.move_product_b.quantity, 1)
|
|
199
|
+
self.assertTrue(self.task.sudo().stock_analytic_line_ids)
|
|
200
|
+
# action_cancel
|
|
201
|
+
self.task.action_cancel()
|
|
202
|
+
self.assertEqual(self.move_product_a.state, "done")
|
|
203
|
+
self.assertEqual(self.move_product_b.state, "done")
|
|
204
|
+
self.assertEqual(self.move_product_a.quantity, 0)
|
|
205
|
+
self.assertEqual(self.move_product_b.quantity, 0)
|
|
206
|
+
self.assertFalse(self.task.sudo().stock_analytic_line_ids)
|
|
207
|
+
quant_a = self.product_a.stock_quant_ids.filtered(
|
|
208
|
+
lambda x: x.location_id == self.location
|
|
209
|
+
)
|
|
210
|
+
quant_b = self.product_b.stock_quant_ids.filtered(
|
|
211
|
+
lambda x: x.location_id == self.location
|
|
212
|
+
)
|
|
213
|
+
quant_c = self.product_c.stock_quant_ids.filtered(
|
|
214
|
+
lambda x: x.location_id == self.location
|
|
215
|
+
)
|
|
216
|
+
self.assertEqual(quant_a.quantity, 2)
|
|
217
|
+
self.assertEqual(quant_b.quantity, 1)
|
|
218
|
+
self.assertEqual(quant_c.quantity, 1)
|
|
219
|
+
|
|
220
|
+
@users("manager-user")
|
|
221
|
+
def test_project_task_process_cancel_manager_user(self):
|
|
222
|
+
self.test_project_task_process_cancel()
|
|
223
|
+
|
|
224
|
+
@mute_logger("odoo.models.unlink")
|
|
225
|
+
def test_project_task_process_unreserve(self):
|
|
226
|
+
self.task = self.env["project.task"].browse(self.task.id)
|
|
227
|
+
self.assertEqual(self.move_product_a.state, "draft")
|
|
228
|
+
self.assertEqual(self.move_product_b.state, "draft")
|
|
229
|
+
# Change task stage (auto-confirm + auto-assign)
|
|
230
|
+
self.task.write({"stage_id": self.stage_done.id})
|
|
231
|
+
self.assertTrue(self.move_product_a.move_line_ids)
|
|
232
|
+
self.assertEqual(self.move_product_a.move_line_ids.task_id, self.task)
|
|
233
|
+
self.assertEqual(self.move_product_a.state, "assigned")
|
|
234
|
+
self.assertEqual(self.move_product_b.state, "assigned")
|
|
235
|
+
self.assertEqual(self.move_product_a.quantity, 2)
|
|
236
|
+
self.assertEqual(self.move_product_b.quantity, 1)
|
|
237
|
+
self.assertTrue(self.task.unreserve_visible)
|
|
238
|
+
# button_unreserve
|
|
239
|
+
self.task.button_unreserve()
|
|
240
|
+
self.assertEqual(self.move_product_a.state, "confirmed")
|
|
241
|
+
self.assertEqual(self.move_product_b.state, "confirmed")
|
|
242
|
+
self.assertEqual(self.move_product_a.quantity, 0)
|
|
243
|
+
self.assertEqual(self.move_product_b.quantity, 0)
|
|
244
|
+
self.assertFalse(self.task.unreserve_visible)
|
|
245
|
+
|
|
246
|
+
@mute_logger("odoo.models.unlink")
|
|
247
|
+
def test_project_task_process_01(self):
|
|
248
|
+
"""Product A move cancel + Product B move OK."""
|
|
249
|
+
self.task = self.env["project.task"].browse(self.task.id)
|
|
250
|
+
self.move_product_b.unlink()
|
|
251
|
+
self.assertEqual(self.move_product_a.state, "draft")
|
|
252
|
+
# Confirm + Edit to qty=0
|
|
253
|
+
self.task.action_confirm()
|
|
254
|
+
self.assertEqual(self.move_product_a.state, "assigned")
|
|
255
|
+
self.move_product_a.product_uom_qty = 0
|
|
256
|
+
self.task.action_done()
|
|
257
|
+
self.assertEqual(self.move_product_a.state, "cancel")
|
|
258
|
+
self.assertEqual(self.move_product_a.quantity, 0)
|
|
259
|
+
# Add extra line
|
|
260
|
+
task_form = Form(self.task)
|
|
261
|
+
with task_form.move_ids.new() as move_form:
|
|
262
|
+
move_form.product_id = self.product_b
|
|
263
|
+
move_form.product_uom_qty = 1
|
|
264
|
+
task_form.save()
|
|
265
|
+
self.move_product_b = self.task.move_ids.filtered(
|
|
266
|
+
lambda x: x.product_id == self.product_b
|
|
267
|
+
)
|
|
268
|
+
self.task.action_confirm()
|
|
269
|
+
self.assertEqual(self.move_product_b.state, "assigned")
|
|
270
|
+
self.task.action_done()
|
|
271
|
+
self.assertEqual(self.move_product_b.state, "done")
|
|
272
|
+
self.assertEqual(self.move_product_b.quantity, 1)
|
|
273
|
+
|
|
274
|
+
def test_project_task_process_02(self):
|
|
275
|
+
self.task.action_confirm()
|
|
276
|
+
self.assertEqual(self.move_product_a.state, "assigned")
|
|
277
|
+
self.assertEqual(self.move_product_b.state, "assigned")
|
|
278
|
+
self.task.action_done()
|
|
279
|
+
self.assertEqual(self.move_product_a.state, "done")
|
|
280
|
+
self.assertEqual(self.move_product_b.state, "done")
|
|
281
|
+
self.assertEqual(len(self.task.stock_analytic_line_ids), 2)
|
|
282
|
+
self.task.action_done()
|
|
283
|
+
self.assertEqual(len(self.task.stock_analytic_line_ids), 2)
|
|
284
|
+
|
|
285
|
+
@users("basic-user")
|
|
286
|
+
def test_project_task_process_unreserve_basic_user(self):
|
|
287
|
+
self.test_project_task_process_unreserve()
|
|
288
|
+
|
|
289
|
+
def test_project_task_action_cancel(self):
|
|
290
|
+
self.assertTrue(self.env["project.task"].browse(self.task.id).action_cancel())
|
|
291
|
+
|
|
292
|
+
@users("basic-user")
|
|
293
|
+
def test_project_task_action_cancel_basic_user(self):
|
|
294
|
+
self.test_project_task_action_cancel()
|
|
295
|
+
|
|
296
|
+
def test_project_task_action_done(self):
|
|
297
|
+
self.task = self.env["project.task"].browse(self.task.id)
|
|
298
|
+
self.task.write({"stage_id": self.stage_done.id})
|
|
299
|
+
self.task.action_done()
|
|
300
|
+
self.assertTrue(self.task.sudo().stock_analytic_line_ids)
|
|
301
|
+
|
|
302
|
+
@users("basic-user")
|
|
303
|
+
def test_project_task_action_done_basic_user(self):
|
|
304
|
+
self.test_project_task_action_done()
|
|
305
|
+
|
|
306
|
+
@mute_logger("odoo.models.unlink")
|
|
307
|
+
def test_project_task_unlink(self):
|
|
308
|
+
self.assertTrue(self.env["project.task"].browse(self.task.id).unlink())
|
|
309
|
+
|
|
310
|
+
@users("basic-user")
|
|
311
|
+
def test_project_task_unlink_basic_user(self):
|
|
312
|
+
self.test_project_task_unlink()
|
|
313
|
+
|
|
314
|
+
@mute_logger("odoo.models.unlink")
|
|
315
|
+
def test_project_project_onchange(self):
|
|
316
|
+
# We do the unreserve first + unlink the quants, otherwise, changing the
|
|
317
|
+
# picking_type_id with the _onchange_picking_type_id() method will change
|
|
318
|
+
# the status of the stock move to "assigned" showing the error of the
|
|
319
|
+
# _check_tasks_with_pending_moves() method.
|
|
320
|
+
self.task.do_unreserve()
|
|
321
|
+
self.quant_a.unlink()
|
|
322
|
+
self.quant_b.unlink()
|
|
323
|
+
new_type = self.env.ref("stock.picking_type_out")
|
|
324
|
+
self.project.write({"picking_type_id": new_type.id})
|
|
325
|
+
self.project._onchange_picking_type_id()
|
|
326
|
+
self.assertEqual(self.project.location_id, new_type.default_location_src_id)
|
|
327
|
+
self.assertEqual(
|
|
328
|
+
self.project.location_dest_id, new_type.default_location_dest_id
|
|
329
|
+
)
|
|
330
|
+
self.task.write({"picking_type_id": new_type.id})
|
|
331
|
+
self.task._onchange_picking_type_id()
|
|
332
|
+
self.assertEqual(self.task.location_id, new_type.default_location_src_id)
|
|
333
|
+
self.assertEqual(self.task.location_dest_id, new_type.default_location_dest_id)
|
|
334
|
+
move = fields.first(self.task.move_ids)
|
|
335
|
+
self.assertEqual(move.location_id, new_type.default_location_src_id)
|
|
336
|
+
|
|
337
|
+
def test_project_task_scrap(self):
|
|
338
|
+
move = fields.first(self.task.move_ids)
|
|
339
|
+
scrap = self.env["stock.scrap"].create(
|
|
340
|
+
{
|
|
341
|
+
"product_id": move.product_id.id,
|
|
342
|
+
"product_uom_id": move.product_id.uom_id.id,
|
|
343
|
+
"scrap_qty": 1,
|
|
344
|
+
"task_id": self.task.id,
|
|
345
|
+
}
|
|
346
|
+
)
|
|
347
|
+
scrap.action_validate()
|
|
348
|
+
self.assertEqual(scrap.state, "done")
|
|
349
|
+
self.assertEqual(len(scrap.move_ids), 1)
|
|
350
|
+
self.assertEqual(scrap.move_ids.raw_material_task_id, self.task)
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8" ?>
|
|
2
|
+
<odoo>
|
|
3
|
+
<record id="edit_project" model="ir.ui.view">
|
|
4
|
+
<field name="name">project.project.form</field>
|
|
5
|
+
<field name="model">project.project</field>
|
|
6
|
+
<field name="inherit_id" ref="project.edit_project" />
|
|
7
|
+
<field name="arch" type="xml">
|
|
8
|
+
<xpath expr="///group[@name='extra_settings']" position="before">
|
|
9
|
+
<group string="Stock" name="stock_misc" groups="stock.group_stock_user">
|
|
10
|
+
<field name="picking_type_id" />
|
|
11
|
+
<field
|
|
12
|
+
name="location_id"
|
|
13
|
+
groups="stock.group_stock_multi_locations"
|
|
14
|
+
/>
|
|
15
|
+
<field
|
|
16
|
+
name="location_dest_id"
|
|
17
|
+
groups="stock.group_stock_multi_locations"
|
|
18
|
+
/>
|
|
19
|
+
<field name="stock_analytic_date" />
|
|
20
|
+
</group>
|
|
21
|
+
</xpath>
|
|
22
|
+
</field>
|
|
23
|
+
</record>
|
|
24
|
+
</odoo>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8" ?>
|
|
2
|
+
<odoo>
|
|
3
|
+
<record id="task_type_edit" model="ir.ui.view">
|
|
4
|
+
<field name="name">project.task.type.form</field>
|
|
5
|
+
<field name="model">project.task.type</field>
|
|
6
|
+
<field name="inherit_id" ref="project.task_type_edit" />
|
|
7
|
+
<field name="arch" type="xml">
|
|
8
|
+
<field name="fold" position="after">
|
|
9
|
+
<field name="use_stock_moves" />
|
|
10
|
+
<field name="done_stock_moves" invisible="not use_stock_moves" />
|
|
11
|
+
</field>
|
|
12
|
+
</field>
|
|
13
|
+
</record>
|
|
14
|
+
</odoo>
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8" ?>
|
|
2
|
+
<odoo>
|
|
3
|
+
<record id="view_task_form2" model="ir.ui.view">
|
|
4
|
+
<field name="name">project.task.form</field>
|
|
5
|
+
<field name="model">project.task</field>
|
|
6
|
+
<field name="inherit_id" ref="project.view_task_form2" />
|
|
7
|
+
<field name="arch" type="xml">
|
|
8
|
+
<xpath expr="///button[@name='action_open_parent_task']" position="after">
|
|
9
|
+
<button
|
|
10
|
+
name="%(action_project_task_moves)d"
|
|
11
|
+
type="action"
|
|
12
|
+
string="Product Moves"
|
|
13
|
+
class="oe_stat_button"
|
|
14
|
+
icon="fa-exchange"
|
|
15
|
+
invisible="not done_stock_moves"
|
|
16
|
+
groups="stock.group_stock_user"
|
|
17
|
+
/>
|
|
18
|
+
<button
|
|
19
|
+
class="oe_stat_button"
|
|
20
|
+
name="action_see_move_scrap"
|
|
21
|
+
type="object"
|
|
22
|
+
icon="fa-arrows-v"
|
|
23
|
+
invisible="scrap_count==0"
|
|
24
|
+
groups="stock.group_stock_user"
|
|
25
|
+
>
|
|
26
|
+
<field name="scrap_count" widget="statinfo" string="Scraps" />
|
|
27
|
+
</button>
|
|
28
|
+
</xpath>
|
|
29
|
+
<field name="date_last_stage_update" position="after">
|
|
30
|
+
<field
|
|
31
|
+
name="stock_analytic_account_id"
|
|
32
|
+
groups="analytic.group_analytic_accounting"
|
|
33
|
+
required="not project_id"
|
|
34
|
+
invisible="not use_stock_moves"
|
|
35
|
+
/>
|
|
36
|
+
<field
|
|
37
|
+
name="stock_analytic_distribution"
|
|
38
|
+
widget="analytic_distribution"
|
|
39
|
+
groups="analytic.group_analytic_accounting"
|
|
40
|
+
invisible="not use_stock_moves"
|
|
41
|
+
/>
|
|
42
|
+
<field name="stock_analytic_date" />
|
|
43
|
+
</field>
|
|
44
|
+
<field name="stage_id" position="before">
|
|
45
|
+
<button
|
|
46
|
+
name="action_confirm"
|
|
47
|
+
string="Confirm materials"
|
|
48
|
+
type="object"
|
|
49
|
+
class="oe_highlight"
|
|
50
|
+
invisible="not allow_moves_action_confirm"
|
|
51
|
+
groups="stock.group_stock_user"
|
|
52
|
+
/>
|
|
53
|
+
<button
|
|
54
|
+
name="action_assign"
|
|
55
|
+
string="Check availability materials"
|
|
56
|
+
type="object"
|
|
57
|
+
class="oe_highlight"
|
|
58
|
+
invisible="not allow_moves_action_assign"
|
|
59
|
+
groups="stock.group_stock_user"
|
|
60
|
+
/>
|
|
61
|
+
<button
|
|
62
|
+
name="button_scrap"
|
|
63
|
+
type="object"
|
|
64
|
+
string="Scrap"
|
|
65
|
+
invisible="stock_state=='done' or not done_stock_moves or not stock_moves_is_locked"
|
|
66
|
+
groups="stock.group_stock_user"
|
|
67
|
+
/>
|
|
68
|
+
<button
|
|
69
|
+
name="button_unreserve"
|
|
70
|
+
type="object"
|
|
71
|
+
string="Unreserve"
|
|
72
|
+
invisible="stock_state=='cancel' or not unreserve_visible or not done_stock_moves or not stock_moves_is_locked"
|
|
73
|
+
groups="stock.group_stock_user"
|
|
74
|
+
/>
|
|
75
|
+
<button
|
|
76
|
+
name="action_cancel"
|
|
77
|
+
type="object"
|
|
78
|
+
string="Cancel Materials"
|
|
79
|
+
invisible="stock_state in ('draft', 'cancel') or not done_stock_moves or not stock_moves_is_locked"
|
|
80
|
+
groups="stock.group_stock_user"
|
|
81
|
+
/>
|
|
82
|
+
<button
|
|
83
|
+
name="action_done"
|
|
84
|
+
type="object"
|
|
85
|
+
string="Transfer Materials"
|
|
86
|
+
invisible="stock_state!='assigned' or not done_stock_moves or not stock_moves_is_locked"
|
|
87
|
+
class="oe_highlight"
|
|
88
|
+
groups="stock.group_stock_user"
|
|
89
|
+
/>
|
|
90
|
+
<button
|
|
91
|
+
name="action_toggle_stock_moves_is_locked"
|
|
92
|
+
invisible="stock_state in ('draft','cancel') or not stock_moves_is_locked or not done_stock_moves"
|
|
93
|
+
string="Unlock"
|
|
94
|
+
groups="stock.group_stock_manager"
|
|
95
|
+
type="object"
|
|
96
|
+
/>
|
|
97
|
+
<button
|
|
98
|
+
name="action_toggle_stock_moves_is_locked"
|
|
99
|
+
invisible="stock_moves_is_locked or not done_stock_moves"
|
|
100
|
+
string="Lock"
|
|
101
|
+
class="oe_highlight"
|
|
102
|
+
groups="stock.group_stock_manager"
|
|
103
|
+
type="object"
|
|
104
|
+
/>
|
|
105
|
+
</field>
|
|
106
|
+
<xpath expr="///page[@name='extra_info']" position="before">
|
|
107
|
+
<!-- Field without groups used as domain in stock_analytic_* fields !-->
|
|
108
|
+
<field name="use_stock_moves" invisible="1" />
|
|
109
|
+
<page
|
|
110
|
+
name="stock_info"
|
|
111
|
+
string="Stock Info"
|
|
112
|
+
invisible="not use_stock_moves"
|
|
113
|
+
groups="stock.group_stock_user"
|
|
114
|
+
>
|
|
115
|
+
<field name="done_stock_moves" invisible="1" />
|
|
116
|
+
<field name="stock_moves_is_locked" invisible="1" />
|
|
117
|
+
<field name="stock_state" invisible="1" />
|
|
118
|
+
<field name="allow_moves_action_confirm" invisible="1" />
|
|
119
|
+
<field name="allow_moves_action_assign" invisible="1" />
|
|
120
|
+
<field name="unreserve_visible" invisible="1" />
|
|
121
|
+
<field
|
|
122
|
+
name="move_ids"
|
|
123
|
+
context="{'list_view_ref': 'project_task_stock.view_stock_move_raw_list', 'form_view_ref':'stock.view_move_form', 'default_raw_material_task_id': id}"
|
|
124
|
+
readonly="done_stock_moves and stock_moves_is_locked"
|
|
125
|
+
/>
|
|
126
|
+
</page>
|
|
127
|
+
</xpath>
|
|
128
|
+
<xpath expr="///page[@name='extra_info']/group[1]" position="inside">
|
|
129
|
+
<group
|
|
130
|
+
string="Stock Info"
|
|
131
|
+
name="stock_info_misc"
|
|
132
|
+
groups="stock.group_stock_user"
|
|
133
|
+
invisible="not use_stock_moves"
|
|
134
|
+
>
|
|
135
|
+
<field name="picking_type_id" />
|
|
136
|
+
<field name="location_id" />
|
|
137
|
+
<field name="location_dest_id" />
|
|
138
|
+
</group>
|
|
139
|
+
</xpath>
|
|
140
|
+
</field>
|
|
141
|
+
</record>
|
|
142
|
+
</odoo>
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8" ?>
|
|
2
|
+
<odoo>
|
|
3
|
+
<record model="ir.actions.act_window" id="action_project_task_moves">
|
|
4
|
+
<field name="name">Inventory Moves</field>
|
|
5
|
+
<field name="type">ir.actions.act_window</field>
|
|
6
|
+
<field name="res_model">stock.move.line</field>
|
|
7
|
+
<field name="view_mode">list,form</field>
|
|
8
|
+
<field
|
|
9
|
+
name="domain"
|
|
10
|
+
>['|', ('move_id.raw_material_task_id', '=', active_id), ('move_id.task_id', '=', active_id)]</field>
|
|
11
|
+
</record>
|
|
12
|
+
<record id="view_stock_move_raw_list" model="ir.ui.view">
|
|
13
|
+
<field name="name">stock.move.raw.list</field>
|
|
14
|
+
<field name="model">stock.move</field>
|
|
15
|
+
<field name="priority">1000</field>
|
|
16
|
+
<field name="arch" type="xml">
|
|
17
|
+
<list
|
|
18
|
+
editable="bottom"
|
|
19
|
+
default_order="sequence"
|
|
20
|
+
decoration-muted="state in ('done', 'cancel')"
|
|
21
|
+
decoration-warning="quantity - product_uom_qty > 0.0001"
|
|
22
|
+
decoration-success="state not in ('done', 'cancel') and quantity - product_uom_qty < 0.0001"
|
|
23
|
+
>
|
|
24
|
+
<field name="company_id" column_invisible="1" />
|
|
25
|
+
<field name="product_id" required="1" readonly="state!='draft'" />
|
|
26
|
+
<field name="sequence" column_invisible="1" />
|
|
27
|
+
<field name="location_id" optional="hide" readonly="1" force_save="1" />
|
|
28
|
+
<field
|
|
29
|
+
name="location_dest_id"
|
|
30
|
+
optional="hide"
|
|
31
|
+
readonly="1"
|
|
32
|
+
force_save="1"
|
|
33
|
+
/>
|
|
34
|
+
<field
|
|
35
|
+
name="picking_type_id"
|
|
36
|
+
optional="hide"
|
|
37
|
+
readonly="1"
|
|
38
|
+
force_save="1"
|
|
39
|
+
/>
|
|
40
|
+
<field name="name" column_invisible="1" />
|
|
41
|
+
<field name="state" column_invisible="1" />
|
|
42
|
+
<field
|
|
43
|
+
name="product_uom"
|
|
44
|
+
groups="uom.group_uom"
|
|
45
|
+
readonly="state!='draft'"
|
|
46
|
+
/>
|
|
47
|
+
<field
|
|
48
|
+
name="product_uom_qty"
|
|
49
|
+
string="To Consume"
|
|
50
|
+
readonly="state!='draft'"
|
|
51
|
+
/>
|
|
52
|
+
<button
|
|
53
|
+
name="action_task_product_forecast_report"
|
|
54
|
+
type="object"
|
|
55
|
+
icon="fa-area-chart"
|
|
56
|
+
title="Forecasted Report"
|
|
57
|
+
invisible="not product_id or state in ['done', 'cancel']"
|
|
58
|
+
/>
|
|
59
|
+
<field name="product_uom_category_id" column_invisible="1" />
|
|
60
|
+
<field name="quantity" string="Consumed" readonly="1" />
|
|
61
|
+
<field name="group_id" column_invisible="1" />
|
|
62
|
+
</list>
|
|
63
|
+
</field>
|
|
64
|
+
</record>
|
|
65
|
+
</odoo>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8" ?>
|
|
2
|
+
<odoo>
|
|
3
|
+
<record id="stock_scrap_form_view" model="ir.ui.view">
|
|
4
|
+
<field name="name">stock.scrap.form</field>
|
|
5
|
+
<field name="model">stock.scrap</field>
|
|
6
|
+
<field name="inherit_id" ref="stock.stock_scrap_form_view" />
|
|
7
|
+
<field name="arch" type="xml">
|
|
8
|
+
<field name="origin" position="after">
|
|
9
|
+
<field name="task_id" invisible="not task_id" readonly="True" />
|
|
10
|
+
</field>
|
|
11
|
+
</field>
|
|
12
|
+
</record>
|
|
13
|
+
</odoo>
|