Merge lp:~dorian-kemps/unifield-server/US-7158 into lp:unifield-server
- US-7158
- Merge into trunk
Proposed by
jftempo
Status: | Merged |
---|---|
Merged at revision: | 5781 |
Proposed branch: | lp:~dorian-kemps/unifield-server/US-7158 |
Merge into: | lp:unifield-server |
Diff against target: |
1701 lines (+747/-271) 23 files modified
bin/addons/delivery_mechanism/delivery_mechanism.py (+4/-3) bin/addons/msf_doc_import/wizard/wizard_in_simulation_screen.py (+26/-4) bin/addons/msf_doc_import/wizard/wizard_po_simulation_screen.py (+30/-6) bin/addons/msf_outgoing/msf_outgoing.py (+26/-1) bin/addons/msf_outgoing/wizard/incoming_shipment_processor.py (+4/-0) bin/addons/msf_profile/data/patches.xml (+8/-0) bin/addons/msf_profile/i18n/fr_MF.po (+35/-13) bin/addons/msf_profile/msf_profile.py (+38/-1) bin/addons/msf_sync_data_server/data/sync_server.sync_rule.csv (+4/-2) bin/addons/msf_tools/report/report_stopped_products.py (+12/-12) bin/addons/order_types/stock.py (+5/-1) bin/addons/product/product.py (+1/-1) bin/addons/product_attributes/product_attributes.py (+298/-67) bin/addons/product_attributes/product_attributes_data.xml (+159/-131) bin/addons/purchase/purchase_order_line.py (+4/-1) bin/addons/sale/sale_order.py (+23/-8) bin/addons/sale/sale_workflow.py (+6/-0) bin/addons/sale/wizard/internal_request_import.py (+12/-5) bin/addons/sourcing/sale_order_line.py (+6/-0) bin/addons/specific_rules/specific_rules.py (+2/-2) bin/addons/specific_rules/stock.py (+5/-4) bin/addons/stock/stock_move.py (+11/-0) bin/addons/sync_client/update.py (+28/-9) |
To merge this branch: | bzr merge lp:~dorian-kemps/unifield-server/US-7158 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
UniField Reviewer Team | Pending | ||
Review via email: mp+383119@code.launchpad.net |
Commit message
Description of the change
To post a comment you must log in.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'bin/addons/delivery_mechanism/delivery_mechanism.py' |
2 | --- bin/addons/delivery_mechanism/delivery_mechanism.py 2020-03-20 14:08:02 +0000 |
3 | +++ bin/addons/delivery_mechanism/delivery_mechanism.py 2020-04-29 08:55:42 +0000 |
4 | @@ -25,6 +25,7 @@ |
5 | from tools.translate import _ |
6 | from order_types.stock import check_rw_warning |
7 | import logging |
8 | +import tools |
9 | |
10 | |
11 | class stock_picking_processing_info(osv.osv_memory): |
12 | @@ -701,9 +702,9 @@ |
13 | logging.getLogger('stock.picking').warn('Exception do_incoming_shipment', exc_info=True) |
14 | for wiz in inc_proc_obj.read(new_cr, uid, wizard_ids, ['picking_id'], context=context): |
15 | self.update_processing_info(new_cr, uid, wiz['picking_id'][0], False, { |
16 | - 'error_msg': '%s\n\nPlease reset the incoming shipment '\ |
17 | - 'processing and fix the source of the error'\ |
18 | - 'before re-try the processing.' % str(e), |
19 | + 'error_msg': _('Error: %s\n\nPlease reset the incoming shipment '\ |
20 | + 'processing and fix the source of the error '\ |
21 | + 'before re-try the processing.') % tools.ustr(e.value), |
22 | }, context=context) |
23 | finally: |
24 | # Close the cursor |
25 | |
26 | === modified file 'bin/addons/msf_doc_import/wizard/wizard_in_simulation_screen.py' |
27 | --- bin/addons/msf_doc_import/wizard/wizard_in_simulation_screen.py 2019-08-28 08:33:22 +0000 |
28 | +++ bin/addons/msf_doc_import/wizard/wizard_in_simulation_screen.py 2020-04-29 08:55:42 +0000 |
29 | @@ -1414,9 +1414,19 @@ |
30 | |
31 | # Product |
32 | prod_id = False |
33 | + loc_id = line.move_id and line.move_id.location_id.id or line.parent_line_id and \ |
34 | + line.parent_line_id.move_id.location_id.id or False |
35 | if values.get('product_code') == line.move_product_id.default_code: |
36 | - prod_id = line.move_product_id and line.move_product_id.id or False |
37 | - write_vals['imp_product_id'] = prod_id |
38 | + if line.move_product_id: |
39 | + p_error, p_msg = prod_obj._test_restriction_error(cr, uid, [line.move_product_id.id], |
40 | + vals={'location_id': loc_id}, |
41 | + context=context) |
42 | + if p_error: # Check constraints on products |
43 | + write_vals['type_change'] = 'error' |
44 | + errors.append(p_msg) |
45 | + else: |
46 | + prod_id = line.move_product_id.id |
47 | + write_vals['imp_product_id'] = prod_id |
48 | else: |
49 | prod_id = False |
50 | if values.get('product_code'): |
51 | @@ -1438,9 +1448,21 @@ |
52 | self.write(cr, uid, [line.id], write_vals, context=context) |
53 | continue |
54 | else: |
55 | - write_vals['imp_product_id'] = prod_ids[0] |
56 | + p_error, p_msg = prod_obj._test_restriction_error(cr, uid, [prod_id], vals={'location_id': loc_id}, |
57 | + context=context) |
58 | + if p_error: # Check constraints on products |
59 | + write_vals['type_change'] = 'error' |
60 | + errors.append(p_msg) |
61 | + else: |
62 | + write_vals['imp_product_id'] = prod_ids[0] |
63 | else: |
64 | - write_vals['imp_product_id'] = prod_id |
65 | + p_error, p_msg = prod_obj._test_restriction_error(cr, uid, [prod_id], vals={'location_id': loc_id}, |
66 | + context=context) |
67 | + if p_error: # Check constraints on products |
68 | + write_vals['type_change'] = 'error' |
69 | + errors.append(p_msg) |
70 | + else: |
71 | + write_vals['imp_product_id'] = prod_id |
72 | |
73 | product = False |
74 | if write_vals.get('imp_product_id'): |
75 | |
76 | === modified file 'bin/addons/msf_doc_import/wizard/wizard_po_simulation_screen.py' |
77 | --- bin/addons/msf_doc_import/wizard/wizard_po_simulation_screen.py 2020-02-17 11:35:43 +0000 |
78 | +++ bin/addons/msf_doc_import/wizard/wizard_po_simulation_screen.py 2020-04-29 08:55:42 +0000 |
79 | @@ -1655,8 +1655,18 @@ |
80 | write_vals['type_change'] = 'error' |
81 | |
82 | # Product |
83 | + partner_id = line.simu_id.order_id.partner_id.id |
84 | if (values[2] and values[2] == line.in_product_id.default_code): |
85 | - write_vals['imp_product_id'] = line.in_product_id and line.in_product_id.id or False |
86 | + if line.in_product_id: |
87 | + p_error, p_msg = prod_obj._test_restriction_error(cr, uid, [line.in_product_id.id], |
88 | + vals={'partner_id': partner_id}, context=context) |
89 | + if p_error: # Check constraints on products |
90 | + write_vals['type_change'] = 'error' |
91 | + errors.append(p_msg) |
92 | + else: |
93 | + write_vals['imp_product_id'] = line.in_product_id.id |
94 | + else: |
95 | + write_vals['imp_product_id'] = False |
96 | else: |
97 | prod_id = False |
98 | if values[2]: |
99 | @@ -1664,14 +1674,28 @@ |
100 | |
101 | if not prod_id and values[2]: |
102 | prod_ids = prod_obj.search(cr, uid, [('default_code', '=', values[2])], context=context) |
103 | - if not prod_ids: |
104 | + if prod_ids: |
105 | + p_error, p_msg = prod_obj._test_restriction_error(cr, uid, [prod_ids[0]], |
106 | + vals={'partner_id': partner_id}, |
107 | + context=context) |
108 | + if p_error: # Check constraints on products |
109 | + write_vals['type_change'] = 'error' |
110 | + errors.append(p_msg) |
111 | + else: |
112 | + write_vals['imp_product_id'] = prod_ids[0] |
113 | + else: |
114 | write_vals['type_change'] = 'error' |
115 | errors.append(_('Product %s not found in database') % values[2]) |
116 | + else: |
117 | + if prod_id: |
118 | + p_error, p_msg = prod_obj._test_restriction_error(cr, uid, [prod_id], vals={'partner_id': partner_id}, |
119 | + context=context) |
120 | + if p_error: # Check constraints on products |
121 | + write_vals['type_change'] = 'error' |
122 | + errors.append(p_msg) |
123 | + else: |
124 | + write_vals['imp_product_id'] = prod_id |
125 | else: |
126 | - write_vals['imp_product_id'] = prod_ids[0] |
127 | - else: |
128 | - write_vals['imp_product_id'] = prod_id |
129 | - if not prod_id: |
130 | write_vals['type_change'] = 'error' |
131 | |
132 | write_vals['ad_info'] = False |
133 | |
134 | === modified file 'bin/addons/msf_outgoing/msf_outgoing.py' |
135 | --- bin/addons/msf_outgoing/msf_outgoing.py 2020-02-10 17:16:32 +0000 |
136 | +++ bin/addons/msf_outgoing/msf_outgoing.py 2020-04-29 08:55:42 +0000 |
137 | @@ -569,7 +569,11 @@ |
138 | ('from_pack', '=', family.from_pack), |
139 | ('to_pack', '=', family.to_pack) |
140 | ], context=context) |
141 | - for move in move_obj.browse(cr, uid, move_ids, fields_to_fetch=['product_uom', 'qty_per_pack'], context=context): |
142 | + ftf = ['product_id', 'product_uom', 'qty_per_pack', 'location_dest_id'] |
143 | + for move in move_obj.browse(cr, uid, move_ids, fields_to_fetch=ftf, context=context): |
144 | + if move.product_id and move.product_id.state.code == 'forbidden': # Check constraints on lines |
145 | + check_vals = {'location_dest_id': move.location_dest_id.id, 'move': move} |
146 | + self.pool.get('product.product')._get_restriction_error(cr, uid, [move.product_id.id], check_vals, context=context) |
147 | if family.selected_number < int(family.num_of_packs) and move.product_uom.rounding == 1 and \ |
148 | move.qty_per_pack % move.product_uom.rounding != 0: |
149 | raise osv.except_osv(_('Error'), _('Warning, this range of packs contains one or more products with a decimal quantity per pack. All packs must be processed together')) |
150 | @@ -1587,6 +1591,10 @@ |
151 | |
152 | # closing FO lines: |
153 | for stock_move in packing.move_lines: |
154 | + if stock_move.product_id and stock_move.product_id.state.code == 'forbidden': # Check constraints on lines |
155 | + check_vals = {'location_dest_id': stock_move.location_dest_id.id, 'move': stock_move} |
156 | + self.pool.get('product.product')._get_restriction_error(cr, uid, [stock_move.product_id.id], |
157 | + check_vals, context=context) |
158 | if stock_move.sale_line_id: |
159 | open_moves = self.pool.get('stock.move').search_exist(cr, uid, [ |
160 | ('sale_line_id', '=', stock_move.sale_line_id.id), |
161 | @@ -3050,6 +3058,11 @@ |
162 | for line in wizard.move_ids: |
163 | move = line.move_id |
164 | |
165 | + if move.product_id and move.product_id.state.code == 'forbidden': # Check constraints on lines |
166 | + check_vals = {'location_dest_id': move.location_dest_id.id, 'move': move} |
167 | + self.pool.get('product.product')._get_restriction_error(cr, uid, [move.product_id.id], check_vals, |
168 | + context=context) |
169 | + |
170 | if move.picking_id.id != picking.id: |
171 | continue |
172 | |
173 | @@ -3327,6 +3340,10 @@ |
174 | # for now, each new line from the wizard corresponds to a new stock.move |
175 | # it could be interesting to regroup according to production lot/asset id |
176 | for line in move_to_process: |
177 | + if line.product_id and line.product_id.state.code == 'forbidden': # Check constraints on lines |
178 | + check_vals = {'location_dest_id': line.location_dest_id.id, 'move': line} |
179 | + self.pool.get('product.product')._get_restriction_error(cr, uid, [line.product_id.id], check_vals, context=context) |
180 | + |
181 | if line.qty_to_process <= 0 or line.state != 'assigned' or line.product_qty == 0: |
182 | continue |
183 | |
184 | @@ -3559,6 +3576,10 @@ |
185 | # For each processed lines, save the processed quantity to update the draft picking ticket |
186 | # and create a new line on PPL |
187 | for line in picking.move_lines: |
188 | + if line.product_id: # Check constraints on lines |
189 | + check_vals = {'location_dest_id': line.location_dest_id.id, 'move': line} |
190 | + self.pool.get('product.product')._get_restriction_error(cr, uid, [line.product_id.id], check_vals, context=context) |
191 | + |
192 | if line.state != 'assigned': |
193 | line.qty_to_process = 0 |
194 | |
195 | @@ -3707,6 +3728,10 @@ |
196 | picking = self.browse(cr, uid, ids[0], context=context) |
197 | rounding_issues = [] |
198 | for move in picking.move_lines: |
199 | + if move.product_id and move.product_id.state.code == 'forbidden': # Check constraints on lines |
200 | + check_vals = {'location_dest_id': move.location_dest_id.id, 'move': move} |
201 | + self.pool.get('product.product')._get_restriction_error(cr, uid, [move.product_id.id], check_vals, context=context) |
202 | + |
203 | if move.state == 'done': |
204 | continue |
205 | if not ppl_processor._check_rounding(cr, uid, move.product_uom, move.num_of_packs, move.product_qty, context=context): |
206 | |
207 | === modified file 'bin/addons/msf_outgoing/wizard/incoming_shipment_processor.py' |
208 | --- bin/addons/msf_outgoing/wizard/incoming_shipment_processor.py 2019-08-28 08:33:22 +0000 |
209 | +++ bin/addons/msf_outgoing/wizard/incoming_shipment_processor.py 2020-04-29 08:55:42 +0000 |
210 | @@ -336,6 +336,10 @@ |
211 | ) |
212 | |
213 | for line in proc.move_ids: |
214 | + if line.product_id: # Check constraints on products |
215 | + self.pool.get('product.product')._get_restriction_error(cr, uid, [line.product_id.id], |
216 | + {'location_id': line.location_id.id}, |
217 | + context=context) |
218 | # If one line as an error, return to wizard |
219 | if line.integrity_status not in ['empty', 'missing_1', 'to_smaller_than_from', 'overlap', 'gap', 'missing_weight']: |
220 | return { |
221 | |
222 | === modified file 'bin/addons/msf_profile/data/patches.xml' |
223 | --- bin/addons/msf_profile/data/patches.xml 2020-03-23 10:37:13 +0000 |
224 | +++ bin/addons/msf_profile/data/patches.xml 2020-04-29 08:55:42 +0000 |
225 | @@ -522,10 +522,18 @@ |
226 | <field name="method">us_7025_7039_fix_nr_empty_ins</field> |
227 | </record> |
228 | |
229 | + <record id="us_7215_prod_set_active_sync" model="patch.scripts"> |
230 | + <field name="method">us_7215_prod_set_active_sync</field> |
231 | + </record> |
232 | + |
233 | <!-- UF17.0 --> |
234 | <record id="us_7221_reset_starting_balance" model="patch.scripts"> |
235 | <field name="method">us_7221_reset_starting_balance</field> |
236 | </record> |
237 | |
238 | + <record id="us_7158_prod_set_uf_status" model="patch.scripts"> |
239 | + <field name="method">us_7158_prod_set_uf_status</field> |
240 | + </record> |
241 | + |
242 | </data> |
243 | </openerp> |
244 | |
245 | === modified file 'bin/addons/msf_profile/i18n/fr_MF.po' |
246 | --- bin/addons/msf_profile/i18n/fr_MF.po 2020-04-20 08:36:07 +0000 |
247 | +++ bin/addons/msf_profile/i18n/fr_MF.po 2020-04-29 08:55:42 +0000 |
248 | @@ -5408,7 +5408,7 @@ |
249 | #: code:addons/product_attributes/product_attributes.py:1072 |
250 | #, python-format |
251 | msgid "be %s externally" |
252 | -msgstr "be %s externally" |
253 | +msgstr "être %s à l'extérieur" |
254 | |
255 | #. modules: procurement_auto, procurement, procurement_cycle |
256 | #: view:stock.warehouse.orderpoint:0 |
257 | @@ -8020,7 +8020,7 @@ |
258 | #: code:addons/product_attributes/product_attributes.py:1080 |
259 | #, python-format |
260 | msgid "be supplied/exchanged internally" |
261 | -msgstr "be supplied/exchanged internally" |
262 | +msgstr "être approvisionné/échangé en interne" |
263 | |
264 | #. module: msf_outgoing |
265 | #: view:stock.move.memory.shipment.returnpacks:0 |
266 | @@ -14719,7 +14719,7 @@ |
267 | #: code:addons/product_attributes/product_attributes.py:1068 |
268 | #, python-format |
269 | msgid "be exchanged" |
270 | -msgstr "be exchanged" |
271 | +msgstr "être échangé" |
272 | |
273 | #. modules: base, base_setup, unifield_setup |
274 | #: model:res.country,name:base.sl |
275 | @@ -41825,7 +41825,7 @@ |
276 | #: code:addons/product_attributes/product_attributes.py:1093 |
277 | #, python-format |
278 | msgid "status" |
279 | -msgstr "status" |
280 | +msgstr "statut" |
281 | |
282 | #. module: msf_doc_import |
283 | #: code:addons/msf_doc_import/check_line.py:187 |
284 | @@ -48544,7 +48544,7 @@ |
285 | #: code:addons/product_attributes/product_attributes.py:1076 |
286 | #, python-format |
287 | msgid "be %s ESC" |
288 | -msgstr "be %s ESC" |
289 | +msgstr "être %s l'ESC" |
290 | |
291 | #. module: kit |
292 | #: code:addons/kit/kit_creation.py:83 |
293 | @@ -51500,7 +51500,7 @@ |
294 | #: code:addons/product_attributes/product_attributes.py:1088 |
295 | #, python-format |
296 | msgid "be stored anymore" |
297 | -msgstr "be stored anymore" |
298 | +msgstr "être stocké" |
299 | |
300 | #. module: account |
301 | #: model:ir.actions.act_window,name:account.action_account_print_journal |
302 | @@ -52998,7 +52998,7 @@ |
303 | #: code:addons/product_attributes/product_attributes.py:1093 |
304 | #, python-format |
305 | msgid "product creator" |
306 | -msgstr "product creator" |
307 | +msgstr "créateur produit" |
308 | |
309 | #. modules: procurement, account_hq_entries |
310 | #: view:hq.entries:0 |
311 | @@ -54577,7 +54577,7 @@ |
312 | #: code:addons/product_attributes/product_attributes.py:1076 |
313 | #, python-format |
314 | msgid "purchased at" |
315 | -msgstr "purchased at" |
316 | +msgstr "acheté à" |
317 | |
318 | #. module: msf_doc_import |
319 | #: model:product.nomenclature,name:msf_doc_import.nomen_tbd0 |
320 | @@ -64441,7 +64441,7 @@ |
321 | #: code:addons/product_attributes/product_attributes.py:1076 |
322 | #, python-format |
323 | msgid "shipped to" |
324 | -msgstr "shipped to" |
325 | +msgstr "envoyé vers" |
326 | |
327 | #. module: account |
328 | #: code:addons/account/account.py:1247 |
329 | @@ -79019,7 +79019,7 @@ |
330 | #: code:addons/product_attributes/product_attributes.py:1084 |
331 | #, python-format |
332 | msgid "be consumed internally" |
333 | -msgstr "be consumed internally" |
334 | +msgstr "être consommé en interne" |
335 | |
336 | #. module: msf_doc_import |
337 | #: code:addons/msf_doc_import/wizard/abstract_wizard_import.py:317 |
338 | @@ -83471,7 +83471,7 @@ |
339 | #: code:addons/product_attributes/product_attributes.py:1072 |
340 | #, python-format |
341 | msgid "shipped" |
342 | -msgstr "Expédié" |
343 | +msgstr "expédié" |
344 | |
345 | #. module: account_period_closing_level |
346 | #: code:addons/account_period_closing_level/account_period.py:200 |
347 | @@ -89806,8 +89806,8 @@ |
348 | #. module: product_attributes |
349 | #: code:addons/product_attributes/product_attributes.py:1096 |
350 | #, python-format |
351 | -msgid "The product [%s] %s gets the %s '%s' and consequently can't %s" |
352 | -msgstr "The product [%s] %s gets the %s '%s' and consequently can't %s" |
353 | +msgid "The product [%s] gets the %s '%s' and consequently can't %s" |
354 | +msgstr "Le produit [%s] a le %s '%s' et ne peut donc plus %s" |
355 | |
356 | #. module: sale |
357 | #: model:ir.actions.act_window,help:sale.action_shop_form |
358 | @@ -109019,6 +109019,18 @@ |
359 | msgid "more FMC" |
360 | msgstr "plus de PCM" |
361 | |
362 | +#. module: product_attributes |
363 | +#: code:addons/product_attributes/product_attributes.py:1196 |
364 | +#, python-format |
365 | +msgid "be sent" |
366 | +msgstr "être envoyé" |
367 | + |
368 | +#. module: product_attributes |
369 | +#: code:addons/product_attributes/product_attributes.py:1206 |
370 | +#, python-format |
371 | +msgid "%s line %s: " |
372 | +msgstr "%s ligne %s: " |
373 | + |
374 | #. module: msf_doc_import |
375 | #: code:addons/msf_doc_import/account.py:287 |
376 | #, python-format |
377 | @@ -109195,3 +109207,13 @@ |
378 | #: code:addons/account_override/invoice.py:1272 |
379 | msgid "Split Stock Transfer Voucher" |
380 | msgstr "Fractionner Bon Client" |
381 | + |
382 | +#. module: delivery_mechanism |
383 | +#: code:addons/delivery_mechanism/delivery_mechanism.py:705 |
384 | +#, python-format |
385 | +msgid "Error: %s\n" |
386 | +"\n" |
387 | +"Please reset the incoming shipment processing and fix the source of the error before re-try the processing." |
388 | +msgstr "Erreur: %s\n" |
389 | +"\n" |
390 | +"Veuillez réinitialiser le processus de la livraison entrante et corriger la source de cette erreur avant de recommencer le processus." |
391 | |
392 | === modified file 'bin/addons/msf_profile/msf_profile.py' |
393 | --- bin/addons/msf_profile/msf_profile.py 2020-04-01 10:25:53 +0000 |
394 | +++ bin/addons/msf_profile/msf_profile.py 2020-04-29 08:55:42 +0000 |
395 | @@ -52,8 +52,25 @@ |
396 | 'model': lambda *a: 'patch.scripts', |
397 | } |
398 | |
399 | - |
400 | # UF17.0 |
401 | + def us_7158_prod_set_uf_status(self, cr, uid, *a, **b): |
402 | + st_obj = self.pool.get('product.status') |
403 | + stopped_ids = st_obj.search(cr, uid, [('code', '=', 'stopped'), ('active', 'in', ['t', 'f'])]) |
404 | + phase_out_ids = st_obj.search(cr, uid, [('code', '=', 'phase_out')]) |
405 | + |
406 | + st12_ids = st_obj.search(cr, uid, [('code', 'in', ['status1', 'status2']), ('active', 'in', ['t', 'f'])]) |
407 | + valid_ids = st_obj.search(cr, uid, [('code', '=', 'valid')]) |
408 | + |
409 | + # state 1, state2, blank to valid |
410 | + cr.execute('''update product_template set state = %s where state is NUll or state in %s''' , (valid_ids[0], tuple(st12_ids))) |
411 | + cr.execute('''update stock_mission_report_line set product_state='valid' where product_state is NULL or product_state in ('', 'status1', 'status2')''') |
412 | + |
413 | + # stopped to pahse_out |
414 | + cr.execute('''update product_template set state = %s where state = %s''' , (phase_out_ids[0], stopped_ids[0])) |
415 | + cr.execute('''update stock_mission_report_line set product_state='phase_out' where product_state = 'stopped' ''') |
416 | + |
417 | + return True |
418 | + |
419 | def us_7221_reset_starting_balance(self, cr, uid, *a, **b): |
420 | """ |
421 | Reset the Starting Balance of the first register created for each journal if it is still in Draft state |
422 | @@ -78,9 +95,29 @@ |
423 | AND journal_id IN (SELECT id FROM account_journal WHERE type in ('bank', 'cash')); |
424 | """) |
425 | self._logger.warn('Starting Balance set to zero in %s registers.' % (cr.rowcount,)) |
426 | + |
427 | return True |
428 | |
429 | # UF16.1 |
430 | + def us_7215_prod_set_active_sync(self, cr, uids, *a, **b): |
431 | + if not self.pool.get('sync.client.message_received'): |
432 | + # new instance |
433 | + return True |
434 | + cr.execute(""" |
435 | + update product_product p set |
436 | + active_change_date=d.last_modification, active_sync_change_date=d.sync_date |
437 | + from |
438 | + ir_model_data d |
439 | + where |
440 | + d.model='product.product' and |
441 | + d.module='sd' and |
442 | + d.res_id = p.id and |
443 | + touched like '%''state''%' |
444 | + """) |
445 | + self._logger.warn('Set active_sync_change_date on %d product' % (cr.rowcount,)) |
446 | + |
447 | + return True |
448 | + |
449 | def remove_ir_actions_linked_to_deleted_modules(self, cr, uid, *a, **b): |
450 | # delete remove actions |
451 | cr.execute("delete from ir_act_window where id in (select res_id from ir_model_data where module in ('procurement_report', 'threshold_value') and model='ir.actions.act_window')") |
452 | |
453 | === modified file 'bin/addons/msf_sync_data_server/data/sync_server.sync_rule.csv' |
454 | --- bin/addons/msf_sync_data_server/data/sync_server.sync_rule.csv 2020-03-04 17:30:01 +0000 |
455 | +++ bin/addons/msf_sync_data_server/data/sync_server.sync_rule.csv 2020-04-29 08:55:42 +0000 |
456 | @@ -123,8 +123,10 @@ |
457 | msf_sync_data_server.price_list_version,FALSE,TRUE,FALSE,FALSE,bidirectional,Bidirectional,[],"['active', 'date_end', 'date_start', 'name', 'pricelist_id/id']",MISSION,product.pricelist.version,,Price List Version,Valid,,561 |
458 | msf_sync_data_server.country_restrictions,TRUE,TRUE,FALSE,TRUE,bidirectional,Down,[],['name'],MISSION,res.country.restriction,,Country restrictions,Valid,,570 |
459 | msf_sync_data_server.country_code_mapping,TRUE,TRUE,TRUE,TRUE,bidirectional,Down,[],"['instance_id/id', 'mapping_value']",COORDINATIONS,country.export.mapping,,Country Code Mapping,Valid,,571 |
460 | -msf_sync_data_server.oc_product_creator_itc_esc_hq,TRUE,TRUE,FALSE,TRUE,bidirectional,Down,"['|','|','|',('international_status','=','UniData'),('international_status','=','ITC'),('international_status','=','ESC'),('international_status','=','HQ'), ('active', 'in', ['t','f'])]","['alert_time', 'batch_management', 'categ_id/id', 'closed_article', 'manufacturer_txt', 'manufacturer_ref', 'code', 'cold_chain/id', 'composed_kit', 'xmlid_code', 'cost_method', 'country_restriction/id', 'dangerous_goods', 'default_code', 'description', 'description2', 'description_purchase', 'description_sale', 'gmdn_code', 'gmdn_description', 'heat_sensitive_item/id', 'international_status/id', 'justification_code_id/id', 'library', 'life_time', 'list_ids/id','med_device_class', 'name', 'name_template', 'controlled_substance', 'nomen_manda_0/id', 'nomen_manda_1/id', 'nomen_manda_2/id', 'nomen_manda_3/id', 'options_ids/id', 'perishable', 'procure_delay', 'procure_method', 'produce_delay', 'product_catalog_page', 'product_catalog_path', 'property_account_expense/id', 'property_account_income/id', 'property_stock_account_input/id', 'property_stock_account_output/id', 'restricted_country', 'short_shelf_life', 'single_use', 'sterilized', 'standard_price', 'sublist', 'subtype', 'asset_type_id', 'supply_method', 'type', 'un_code', 'uom_id/id', 'uom_po_id/id','use_time', 'valuation', 'weight', 'weight_net', 'active', 'state', 'state_ud', 'old_code', 'new_code', 'function_value', 'form_value', 'fit_value', 'standard_ok', 'local_from_hq', 'transport_ok','volume', 'soq_quantity', 'soq_weight', 'soq_volume', 'msfid', 'oc_subscription']",OC,product.product,,"OC Product (Creator = ITC, ESC, UniData or HQ)",Valid,,600 |
461 | -msf_sync_data_server.mission_product_creator_local,TRUE,TRUE,FALSE,TRUE,bidirectional,Down,"[('international_status','=','Local'), ('active', 'in', ['t','f'])]","['alert_time', 'batch_management', 'categ_id/id', 'closed_article', 'manufacturer_txt', 'manufacturer_ref', 'code', 'xmlid_code','cold_chain/id', 'composed_kit', 'cost_method', 'country_restriction/id', 'dangerous_goods', 'default_code', 'description', 'description2', 'description_purchase', 'description_sale', 'gmdn_code', 'gmdn_description', 'heat_sensitive_item/id', 'international_status/id', 'justification_code_id/id', 'library', 'life_time', 'list_ids/id','med_device_class', 'name', 'name_template', 'controlled_substance', 'nomen_manda_0/id', 'nomen_manda_1/id', 'nomen_manda_2/id', 'nomen_manda_3/id', 'options_ids/id', 'perishable', 'procure_delay', 'procure_method', 'produce_delay', 'product_catalog_page', 'product_catalog_path', 'property_account_expense/id', 'property_account_income/id', 'property_stock_account_input/id', 'property_stock_account_output/id', 'restricted_country', 'short_shelf_life', 'single_use', 'sterilized', 'standard_price', 'sublist', 'subtype', 'asset_type_id', 'supply_method', 'type', 'un_code', 'uom_id/id', 'uom_po_id/id','use_time', 'valuation', 'weight', 'weight_net', 'active', 'state', 'old_code', 'new_code', 'function_value', 'form_value', 'fit_value', 'standard_ok','transport_ok','volume', 'soq_quantity', 'soq_weight','soq_volume']",MISSION,product.product,,Mission Product (Creator = local),Valid,,601 |
462 | +msf_sync_data_server.oc_product_creator_itc_esc_hq,TRUE,TRUE,FALSE,TRUE,bidirectional,Down,"['|','|','|',('international_status','=','UniData'),('international_status','=','ITC'),('international_status','=','ESC'),('international_status','=','HQ'), ('active', 'in', ['t','f'])]","['alert_time', 'batch_management', 'categ_id/id', 'closed_article', 'manufacturer_txt', 'manufacturer_ref', 'code', 'cold_chain/id', 'composed_kit', 'xmlid_code', 'cost_method', 'country_restriction/id', 'dangerous_goods', 'default_code', 'description', 'description2', 'description_purchase', 'description_sale', 'gmdn_code', 'gmdn_description', 'heat_sensitive_item/id', 'international_status/id', 'justification_code_id/id', 'library', 'life_time', 'list_ids/id','med_device_class', 'name', 'name_template', 'controlled_substance', 'nomen_manda_0/id', 'nomen_manda_1/id', 'nomen_manda_2/id', 'nomen_manda_3/id', 'options_ids/id', 'perishable', 'procure_delay', 'procure_method', 'produce_delay', 'product_catalog_page', 'product_catalog_path', 'property_account_expense/id', 'property_account_income/id', 'property_stock_account_input/id', 'property_stock_account_output/id', 'restricted_country', 'short_shelf_life', 'single_use', 'sterilized', 'standard_price', 'sublist', 'subtype', 'asset_type_id', 'supply_method', 'type', 'un_code', 'uom_id/id', 'uom_po_id/id','use_time', 'valuation', 'weight', 'weight_net', 'state', 'state_ud', 'old_code', 'new_code', 'function_value', 'form_value', 'fit_value', 'standard_ok', 'local_from_hq', 'transport_ok','volume', 'soq_quantity', 'soq_weight', 'soq_volume', 'msfid', 'oc_subscription']",OC,product.product,,"OC Product (Creator = ITC, ESC, UniData or HQ)",Valid,,600 |
463 | +msf_sync_data_server.mission_product_creator_local,TRUE,TRUE,FALSE,TRUE,bidirectional,Down,"[('international_status','=','Local'), ('active', 'in', ['t','f'])]","['alert_time', 'batch_management', 'categ_id/id', 'closed_article', 'manufacturer_txt', 'manufacturer_ref', 'code', 'xmlid_code','cold_chain/id', 'composed_kit', 'cost_method', 'country_restriction/id', 'dangerous_goods', 'default_code', 'description', 'description2', 'description_purchase', 'description_sale', 'gmdn_code', 'gmdn_description', 'heat_sensitive_item/id', 'international_status/id', 'justification_code_id/id', 'library', 'life_time', 'list_ids/id','med_device_class', 'name', 'name_template', 'controlled_substance', 'nomen_manda_0/id', 'nomen_manda_1/id', 'nomen_manda_2/id', 'nomen_manda_3/id', 'options_ids/id', 'perishable', 'procure_delay', 'procure_method', 'produce_delay', 'product_catalog_page', 'product_catalog_path', 'property_account_expense/id', 'property_account_income/id', 'property_stock_account_input/id', 'property_stock_account_output/id', 'restricted_country', 'short_shelf_life', 'single_use', 'sterilized', 'standard_price', 'sublist', 'subtype', 'asset_type_id', 'supply_method', 'type', 'un_code', 'uom_id/id', 'uom_po_id/id','use_time', 'valuation', 'weight', 'weight_net', 'state', 'old_code', 'new_code', 'function_value', 'form_value', 'fit_value', 'standard_ok','transport_ok','volume', 'soq_quantity', 'soq_weight','soq_volume']",MISSION,product.product,,Mission Product (Creator = local),Valid,,601 |
464 | +msf_sync_data_server.oc_product_creator_itc_esc_hq_active,TRUE,TRUE,FALSE,TRUE,bidirectional,Down,"['|','|','|',('international_status','=','UniData'),('international_status','=','ITC'),('international_status','=','ESC'),('international_status','=','HQ'), ('active', 'in', ['t','f']), ('active_change_date', '!=', False)]","['active', 'local_from_hq']",OC,product.product,,"OC Product Active (Creator = ITC, ESC, UniData or HQ)",Valid,,602 |
465 | +msf_sync_data_server.mission_product_creator_local_active,TRUE,TRUE,FALSE,TRUE,bidirectional,Down,"[('international_status','=','Local'), ('active', 'in', ['t','f']), ('active_change_date', '!=', False)]","['active']",MISSION,product.product,,Mission Product Active (Creator = local),Valid,,603 |
466 | msf_sync_data_server.standard_product_list,TRUE,TRUE,TRUE,TRUE,bidirectional,Down,"[('standard_list_ok','=','True')]","['creation_date', 'creator', 'description', 'last_update_date', 'name', 'order_list_print_ok', 'ref', 'standard_list_ok', 'type']",OC,product.list,,Standard Product List,Valid,,605 |
467 | msf_sync_data_server.standard_product_list_line,TRUE,TRUE,TRUE,TRUE,bidirectional,Down,"[('list_id' , 'in', ('product.list', 'id', [('standard_list_ok','=','True')]))]","['comment','list_id/id','ref','name/id']",OC,product.list.line,,Standard Product List Line,Valid,,606 |
468 | msf_sync_data_server.tax_code,TRUE,TRUE,FALSE,TRUE,bidirectional,Down,[],"['code', 'info', 'name', 'notprintable', 'sign']",OC,account.tax.code,,Tax Code,Valid,,610 |
469 | |
470 | === modified file 'bin/addons/msf_tools/report/report_stopped_products.py' |
471 | --- bin/addons/msf_tools/report/report_stopped_products.py 2019-10-31 08:51:00 +0000 |
472 | +++ bin/addons/msf_tools/report/report_stopped_products.py 2020-04-29 08:55:42 +0000 |
473 | @@ -184,34 +184,34 @@ |
474 | data_obj = self.pool.get('ir.model.data') |
475 | smrl_obj = self.pool.get('stock.mission.report.line') |
476 | |
477 | - stopped_state_id = data_obj.get_object_reference(self.cr, self.uid, 'product_attributes', 'status_3')[1] |
478 | + phase_out_state_id = data_obj.get_object_reference(self.cr, self.uid, 'product_attributes', 'status_2')[1] |
479 | status_local_id = data_obj.get_object_reference(self.cr, self.uid, 'product_attributes', 'int_4')[1] |
480 | temporary_status_id = data_obj.get_object_reference(self.cr, self.uid, 'product_attributes', 'int_5')[1] |
481 | |
482 | - hq_stopped_ids = prod_obj.search(self.cr, self.uid, [ |
483 | - ('state', '=', stopped_state_id), |
484 | + hq_phase_out_ids = prod_obj.search(self.cr, self.uid, [ |
485 | + ('state', '=', phase_out_state_id), |
486 | ('international_status', '!=', status_local_id), |
487 | ('international_status', '!=', temporary_status_id)], |
488 | context=self.localcontext) |
489 | |
490 | smrl_ids = smrl_obj.search(self.cr, self.uid, [ |
491 | ('full_view', '=', False), |
492 | - ('product_state', '=', 'stopped'), |
493 | + ('product_state', '=', 'phase_out'), |
494 | '|', ('internal_qty', '!=', 0), |
495 | ('in_pipe_qty', '!=', 0) |
496 | ], context=self.localcontext) |
497 | |
498 | - sm_stopped_ids = smrl_obj.read(self.cr, self.uid, smrl_ids, ['product_id'], context=self.localcontext) |
499 | - sm_stopped_ids = [x.get('product_id')[0] for x in sm_stopped_ids] |
500 | + sm_phase_out_ids = smrl_obj.read(self.cr, self.uid, smrl_ids, ['product_id'], context=self.localcontext) |
501 | + sm_phase_out_ids = [x.get('product_id')[0] for x in sm_phase_out_ids] |
502 | |
503 | # build a list of stopped products with unique ids and sorted by default_code: |
504 | - stopped_ids = list(set(hq_stopped_ids + sm_stopped_ids)) |
505 | + phase_out_ids = list(set(hq_phase_out_ids + sm_phase_out_ids)) |
506 | ls = [] |
507 | - for prod in prod_obj.browse(self.cr, self.uid, stopped_ids, context=self.localcontext): |
508 | + for prod in prod_obj.browse(self.cr, self.uid, phase_out_ids, context=self.localcontext): |
509 | ls.append( (prod.id, prod.default_code) ) |
510 | - sorted_stopped_ids = [x[0] for x in sorted(ls, key=lambda tup: tup[1])] |
511 | + sorted_phase_out_ids = [x[0] for x in sorted(ls, key=lambda tup: tup[1])] |
512 | |
513 | - return prod_obj.browse(self.cr, self.uid, sorted_stopped_ids, context=self.localcontext) |
514 | + return prod_obj.browse(self.cr, self.uid, sorted_phase_out_ids, context=self.localcontext) |
515 | |
516 | def get_stock_mission_report_lines(self, product): |
517 | ''' |
518 | @@ -221,10 +221,10 @@ |
519 | smrl_obj = self.pool.get('stock.mission.report.line') |
520 | smrl_ids = smrl_obj.search(self.cr, self.uid, [('product_id', '=', product.id)], context=self.localcontext) |
521 | |
522 | - stopped_state_id = data_obj.get_object_reference(self.cr, self.uid, 'product_attributes', 'status_3')[1] |
523 | + phase_out_state_id = data_obj.get_object_reference(self.cr, self.uid, 'product_attributes', 'status_2')[1] |
524 | |
525 | res = [smrl for smrl in smrl_obj.browse(self.cr, self.uid, smrl_ids, context=self.localcontext) if \ |
526 | - not smrl.full_view and (smrl.product_state == 'stopped' or product.state.id == stopped_state_id) and |
527 | + not smrl.full_view and (smrl.product_state == 'phase_out' or product.state.id == phase_out_state_id) and |
528 | (smrl.internal_qty != 0 or smrl.in_pipe_qty != 0) and smrl.mission_report_id.instance_id.state != 'inactive'] |
529 | |
530 | return res |
531 | |
532 | === modified file 'bin/addons/order_types/stock.py' |
533 | --- bin/addons/order_types/stock.py 2019-09-18 14:06:52 +0000 |
534 | +++ bin/addons/order_types/stock.py 2020-04-29 08:55:42 +0000 |
535 | @@ -212,8 +212,12 @@ |
536 | certif = False |
537 | for pick in self.browse(cr, uid, ids, context=context): |
538 | if pick.type in ['in', 'out']: |
539 | - if not context.get('yesorno', False) : |
540 | + if not context.get('yesorno', False): |
541 | for move in pick.move_lines: |
542 | + if pick.type == 'out' and move.product_id and move.product_id.state.code == 'forbidden': # Check constraints on lines |
543 | + check_vals = {'location_dest_id': move.location_dest_id.id, 'move': move} |
544 | + self.pool.get('product.product')._get_restriction_error(cr, uid, [move.product_id.id], |
545 | + check_vals, context=context) |
546 | if move.order_type in ['donation_exp', 'donation_st', 'in_kind']: |
547 | certif = True |
548 | break |
549 | |
550 | === modified file 'bin/addons/product/product.py' |
551 | --- bin/addons/product/product.py 2019-12-11 14:54:37 +0000 |
552 | +++ bin/addons/product/product.py 2020-04-29 08:55:42 +0000 |
553 | @@ -340,7 +340,7 @@ |
554 | 'warranty': fields.float('Warranty (months)'), |
555 | 'sale_ok': fields.boolean('Can be Sold', help="Determines if the product can be visible in the list of product within a selection from a sale order line."), |
556 | 'purchase_ok': fields.boolean('Can be Purchased', help="Determine if the product is visible in the list of products within a selection from a purchase order line."), |
557 | - 'state': fields.integer('Status'), |
558 | + 'state': fields.integer('UniField Status', required=1), |
559 | 'uom_id': fields.many2one('product.uom', 'Default Unit Of Measure', required=True, help="Default Unit of Measure used for all stock operation."), |
560 | 'uom_po_id': fields.many2one('product.uom', 'Purchase Unit of Measure', required=True, help="Default Unit of Measure used for purchase orders. It must be in the same category than the default unit of measure."), |
561 | 'uos_id' : fields.many2one('product.uom', 'Unit of Sale', |
562 | |
563 | === modified file 'bin/addons/product_attributes/product_attributes.py' |
564 | --- bin/addons/product_attributes/product_attributes.py 2020-04-06 08:43:29 +0000 |
565 | +++ bin/addons/product_attributes/product_attributes.py 2020-04-29 08:55:42 +0000 |
566 | @@ -25,6 +25,7 @@ |
567 | from lxml import etree |
568 | import tools |
569 | from datetime import datetime |
570 | +import logging |
571 | |
572 | class product_section_code(osv.osv): |
573 | _name = "product.section.code" |
574 | @@ -47,8 +48,13 @@ |
575 | 'no_internal': fields.boolean(string='Internal partners orders'), |
576 | 'no_consumption': fields.boolean(string='Consumption'), |
577 | 'no_storage': fields.boolean(string='Storage'), |
578 | + 'active': fields.boolean('Active'), |
579 | + 'mapped_to': fields.many2one('product.status', string='Replaced by'), |
580 | } |
581 | |
582 | + _defaults = { |
583 | + 'active': True, |
584 | + } |
585 | def unlink(self, cr, uid, ids, context=None): |
586 | if context is None: |
587 | context = {} |
588 | @@ -268,13 +274,20 @@ |
589 | class product_attributes_template(osv.osv): |
590 | _inherit = "product.template" |
591 | |
592 | + |
593 | _columns = { |
594 | 'type': fields.selection([('product','Stockable Product'),('consu', 'Non-Stockable')], 'Product Type', required=True, help="Will change the way procurements are processed. Consumables are stockable products with infinite stock, or for use when you have no inventory management in the system."), |
595 | + 'state': fields.many2one('product.status', 'UniField Status', help="Tells the user if he can use the product or not.", required=1), |
596 | } |
597 | |
598 | + def _get_valid_stat(self, cr, uid, context=None): |
599 | + st_ids = self.pool.get('product.status').search(cr, uid, [('code', '=', 'valid')], context=context) |
600 | + return st_ids and st_ids[0] |
601 | + |
602 | _defaults = { |
603 | 'type': 'product', |
604 | 'cost_method': lambda *a: 'average', |
605 | + 'state': _get_valid_stat, |
606 | } |
607 | |
608 | product_attributes_template() |
609 | @@ -290,19 +303,18 @@ |
610 | |
611 | product_country_restriction() |
612 | |
613 | -class product_template(osv.osv): |
614 | - _inherit = 'product.template' |
615 | - |
616 | - _columns = { |
617 | - 'state': fields.many2one('product.status', 'Status', help="Tells the user if he can use the product or not."), |
618 | - } |
619 | - |
620 | -product_template() |
621 | - |
622 | |
623 | class product_attributes(osv.osv): |
624 | _inherit = "product.product" |
625 | |
626 | + mapping_ud = { |
627 | + 'valid': 'valid', |
628 | + 'outdated': 'valid', |
629 | + 'discontinued': 'valid', |
630 | + 'forbidden': 'forbidden', |
631 | + 'archived': 'archived', |
632 | + } |
633 | + |
634 | def execute_migration(self, cr, moved_column, new_column): |
635 | super(product_attributes, self).execute_migration(cr, moved_column, new_column) |
636 | |
637 | @@ -662,7 +674,7 @@ |
638 | res[_id] = False |
639 | |
640 | if self.pool.get('res.company')._get_instance_level(cr, uid) == 'section': |
641 | - for _id in self.search(cr, uid, [('id', 'in', ids), ('standard_ok', '=', 'non_standard_local')], context=context): |
642 | + for _id in self.search(cr, uid, [('id', 'in', ids), ('standard_ok', '=', 'non_standard_local'), ('active', 'in', ['t', 'f'])], context=context): |
643 | res[_id] = True |
644 | |
645 | return res |
646 | @@ -958,7 +970,9 @@ |
647 | string='Standardization Level', |
648 | required=True, |
649 | ), |
650 | - 'local_from_hq': fields.function(_get_local_from_hq, method=1, type='boolean', string='Non-Standard Local from HQ'), |
651 | + 'local_from_hq': fields.function(_get_local_from_hq, method=1, type='boolean', string='Non-Standard Local from HQ', help='Set to True when HQ generates a sync update on NSL product', internal=1), |
652 | + 'active_change_date': fields.datetime('Date of last active change', readonly=1), |
653 | + 'active_sync_change_date': fields.datetime('Date of last active sync change', readonly=1), |
654 | 'soq_weight': fields.float(digits=(16,5), string='SoQ Weight'), |
655 | 'soq_volume': fields.float(digits=(16,5), string='SoQ Volume'), |
656 | 'soq_quantity': fields.float(digits=(16,2), string='SoQ Quantity', related_uom='uom_id', help="Standard Ordering Quantity. Quantity according to which the product should be ordered. The SoQ is usually determined by the typical packaging of the product."), |
657 | @@ -967,6 +981,21 @@ |
658 | 'uf_create_date': fields.datetime(_('Creation date')), |
659 | } |
660 | |
661 | + def need_to_push(self, cr, uid, ids, touched_fields=None, field='sync_date', empty_ids=False, context=None): |
662 | + if touched_fields != ['active', 'local_from_hq', 'id']: |
663 | + return super(product_attributes, self).need_to_push(cr, uid, ids, touched_fields=touched_fields, field=field, empty_ids=empty_ids, context=context) |
664 | + |
665 | + if not empty_ids and not ids: |
666 | + return ids |
667 | + |
668 | + cr.execute(""" |
669 | + SELECT id FROM product_product |
670 | + WHERE |
671 | + ( active_sync_change_date IS NULL AND active_change_date IS NOT NULL ) OR active_change_date > active_sync_change_date |
672 | + """) |
673 | + return [row[0] for row in cr.fetchall()] |
674 | + |
675 | + |
676 | def _get_default_sensitive_item(self, cr, uid, context=None): |
677 | """ |
678 | Return the ID of the product.heat_sensitive item with 'No' value. |
679 | @@ -1116,6 +1145,17 @@ |
680 | if location.usage != 'inventory' and not location.destruction_location and (not bef_scrap_id or location.id != bef_scrap_id): |
681 | constraints.append('storage') |
682 | |
683 | + # Compute the constraint if a destination location is passed in vals |
684 | + if vals.get('location_dest_id'): |
685 | + dest_location = self.pool.get('stock.location').browse(cr, uid, vals.get('location_dest_id'), context=context) |
686 | + if not (dest_location.destruction_location or dest_location.quarantine_location): |
687 | + if vals.get('move') and vals['move'].sale_line_id and not vals['move'].sale_line_id.order_id.procurement_request: |
688 | + if (vals['move'].picking_id.shipment_id and vals['move'].picking_id.shipment_id.partner_id.partner_type != 'internal') or \ |
689 | + vals['move'].picking_id.partner_id.partner_type != 'internal': |
690 | + constraints.append('cant_use') |
691 | + else: |
692 | + constraints.append('cant_use') |
693 | + |
694 | # Compute constraints if constraints is passed in vals |
695 | if vals.get('constraints'): |
696 | if isinstance(vals.get('constraints'), list): |
697 | @@ -1151,17 +1191,21 @@ |
698 | error = True |
699 | msg = _('be stored anymore') |
700 | st_cond = product.state.no_storage |
701 | + elif product.state.code == 'forbidden' and 'cant_use' in constraints: |
702 | + error = True |
703 | + msg = _('be sent') |
704 | + st_cond = product.state.no_consumption |
705 | |
706 | if error: |
707 | # Build the error message |
708 | st_type = st_cond and _('status') or _('product creator') |
709 | st_name = st_cond and product.state.name or product.international_status.name |
710 | |
711 | - error_msg = _('The product [%s] %s gets the %s \'%s\' and consequently can\'t %s') % (product.default_code, |
712 | - product.name, |
713 | - st_type, |
714 | - st_name, |
715 | - msg) |
716 | + error_msg = '' |
717 | + if vals.get('move'): |
718 | + error_msg = _('%s line %s: ') % (vals['move'].picking_id.name, vals['move'].line_number) |
719 | + error_msg += _('The product [%s] gets the %s \'%s\' and consequently can\'t %s') \ |
720 | + % (product.default_code, st_type, st_name, msg) |
721 | if context.get('noraise'): |
722 | error = False |
723 | |
724 | @@ -1174,10 +1218,7 @@ |
725 | res, error_msg = self._test_restriction_error(cr, uid, ids, vals=vals, context=context) |
726 | |
727 | if res: |
728 | - if isinstance(error_msg, unicode): |
729 | - error_msg = error_msg.encode('ascii', 'ignore') |
730 | raise osv.except_osv(_('Error'), error_msg) |
731 | - return False |
732 | |
733 | |
734 | def change_soq_quantity(self, cr, uid, ids, soq, uom_id, context=None): |
735 | @@ -1282,6 +1323,7 @@ |
736 | if context.get('sync_update_execution') and vals.get('local_from_hq'): |
737 | vals['active'] = False |
738 | |
739 | + |
740 | def update_existing_translations(model, res_id, xmlid): |
741 | # If we are in the creation of product by sync. engine, attach the already existing translations to this product |
742 | if context.get('sync_update_execution'): |
743 | @@ -1342,8 +1384,22 @@ |
744 | vals['heat_sensitive_item'] = heat2_id |
745 | vals.update(self.onchange_heat(cr, uid, False, vals['heat_sensitive_item'], context=context).get('value', {})) |
746 | |
747 | - if intstat_code and 'oc_subscription' not in vals: |
748 | - vals['oc_subscription'] = intstat_code == 'unidata' |
749 | + if intstat_code: |
750 | + if 'oc_subscription' not in vals: |
751 | + vals['oc_subscription'] = intstat_code == 'unidata' |
752 | + |
753 | + if not context.get('sync_update_execution'): |
754 | + if 'state_ud' in vals: |
755 | + if self.mapping_ud.get(vals['state_ud']): |
756 | + vals['state'] = \ |
757 | + self.pool.get('product.status').search(cr, uid, [('code', '=', self.mapping_ud.get(vals['state_ud']))], |
758 | + context=context)[0] |
759 | + if vals['state_ud'] == 'archived': |
760 | + vals['active'] = False |
761 | + if not vals['oc_subscription']: |
762 | + vals['active'] = False |
763 | + elif vals.get('state_ud') != 'archived': |
764 | + vals['active'] = True |
765 | |
766 | for f in ['sterilized', 'closed_article', 'single_use']: |
767 | if f in vals and not vals.get(f): |
768 | @@ -1352,6 +1408,11 @@ |
769 | vals['uf_create_date'] = vals.get('uf_create_date') or datetime.now() |
770 | |
771 | self.convert_price(cr, uid, vals, context) |
772 | + |
773 | + if not context.get('sync_update_execution') and vals.get('active') is False: |
774 | + # tirgger sync update on state only if created as inactive (as active=Ture is the default) |
775 | + vals['active_change_date'] = datetime.now() |
776 | + |
777 | res = super(product_attributes, self).create(cr, uid, vals, context=context) |
778 | |
779 | if context.get('sync_update_execution'): |
780 | @@ -1410,6 +1471,58 @@ |
781 | elif vals['standard_ok'] == 'False': |
782 | vals['standard_ok'] = 'non_standard' |
783 | |
784 | + if vals and 'state' in vals: |
785 | + # here to manage old sync updates |
786 | + st_obj = self.pool.get('product.status') |
787 | + if vals['state']: |
788 | + st = st_obj.browse(cr, uid, vals['state'], fields_to_fetch=['mapped_to']) |
789 | + if st and st.mapped_to: |
790 | + vals['state'] = st.mapped_to.id |
791 | + else: |
792 | + vals['state'] = st_obj.search(cr, uid, [('code', '=', 'valid')], context=context)[0] |
793 | + |
794 | + def hq_cron_deactivate_ud_products(self, cr, uid, context=None): |
795 | + if self.pool.get('res.company')._get_instance_level(cr, uid) != 'section': |
796 | + return False |
797 | + |
798 | + ids = [] |
799 | + products_used = set() |
800 | + |
801 | + ud_prod_ids = self.search(cr, uid, ['&', ('international_status', '=', 'UniData'), '|', '|', ('oc_subscription', '=', False), ('state_ud', '=', 'archived'), ('state', '=', 'Phase Out')], context=context) |
802 | + if ud_prod_ids: |
803 | + products_used = self.unidata_products_used(cr, uid, ud_prod_ids) |
804 | + ids = list(set(ud_prod_ids) - products_used) |
805 | + if ids: |
806 | + self.write(cr, uid, ids, {'active': False}, context=context) |
807 | + |
808 | + logging.getLogger('UD deactivation').info('%d products deactivated, %d kept as active' % (len(ids), len(products_used))) |
809 | + |
810 | + return True |
811 | + |
812 | + def unidata_products_used(self, cr, uid, ids): |
813 | + if not ids: |
814 | + return set() |
815 | + |
816 | + cr.execute(''' |
817 | + select |
818 | + l.product_id |
819 | + from |
820 | + stock_mission_report r, msf_instance i, stock_mission_report_line l |
821 | + where |
822 | + i.id = r.instance_id and |
823 | + i.state = 'active' and |
824 | + l.mission_report_id = r.id and |
825 | + l.product_id in %s and |
826 | + r.full_view = 'f' and |
827 | + ( l.internal_qty > 0 or l.in_pipe_qty > 0) |
828 | + group by l.product_id |
829 | + ''' , (tuple(ids), )) |
830 | + ud_unable_to_inactive = set([x[0] for x in cr.fetchall()]) |
831 | + |
832 | + cr.execute('select name from product_list_line where name in %s', (tuple(ids), )) |
833 | + ud_unable_to_inactive = ud_unable_to_inactive.union([x[0] for x in cr.fetchall()]) |
834 | + return ud_unable_to_inactive |
835 | + |
836 | def write(self, cr, uid, ids, vals, context=None): |
837 | if not ids: |
838 | return True |
839 | @@ -1466,45 +1579,86 @@ |
840 | _("Product Code %s must include a 'Z' character") % (vals['default_code'],), |
841 | ) |
842 | |
843 | + if context.get('sync_update_execution') and vals.get('local_from_hq') and vals.get('active'): |
844 | + del(vals['active']) |
845 | + |
846 | + |
847 | + check_reactivate = False |
848 | + prod_state = '' |
849 | + if 'state_ud' in vals: |
850 | + # just update SMRL that belongs to our instance: |
851 | + local_smrl_ids = smrl_obj.search(cr, uid, [ |
852 | + ('product_id', 'in', ids), |
853 | + ('full_view', '=', False), |
854 | + ('mission_report_id.local_report', '=', True), |
855 | + ('state_ud', '!=', vals['state_ud'] or ''), |
856 | + ], context=context) |
857 | + if local_smrl_ids: |
858 | + no_sync_context = context.copy() |
859 | + no_sync_context['sync_update_execution'] = False |
860 | + smrl_obj.write(cr, 1, local_smrl_ids, {'state_ud': vals['state_ud'] or ''}, context=no_sync_context) |
861 | + |
862 | + if not context.get('sync_update_execution'): |
863 | + if self.mapping_ud.get(vals['state_ud']): |
864 | + prod_state = self.mapping_ud[vals['state_ud']] |
865 | + vals['state'] = prod_status_obj.search(cr, uid, [('code', '=', prod_state)], context=context)[0] |
866 | + |
867 | + if vals['state_ud'] == 'archived': |
868 | + vals['active'] = False |
869 | + elif 'oc_subscription' not in vals: |
870 | + check_reactivate = True |
871 | + |
872 | + unidata_product = False |
873 | + if 'international_status' in vals: |
874 | + intstat_code = '' |
875 | + if vals['international_status']: |
876 | + intstat_id = vals['international_status'] |
877 | + if isinstance(intstat_id, (int,long)): |
878 | + intstat_id = [intstat_id] |
879 | + intstat_code = int_stat_obj.read(cr, uid, intstat_id, ['code'], context=context)[0]['code'] |
880 | + unidata_product = intstat_code == 'unidata' |
881 | + |
882 | + if intstat_code: |
883 | + # just update SMRL that belongs to our instance: |
884 | + local_smrl_ids = smrl_obj.search(cr, uid, [ |
885 | + ('international_status_code', '!=', intstat_code), |
886 | + ('product_id', 'in', ids), |
887 | + ('full_view', '=', False), |
888 | + ('mission_report_id.local_report', '=', True) |
889 | + ], context=context) |
890 | + if local_smrl_ids: |
891 | + no_sync_context = context.copy() |
892 | + no_sync_context['sync_update_execution'] = False |
893 | + smrl_obj.write(cr, 1, local_smrl_ids, {'international_status_code': intstat_code or ''}, context=no_sync_context) |
894 | + else: |
895 | + unidata_product = self.search_exist(cr, uid, [('id', 'in', ids), ('international_status', '=', 'UniData'), ('active', 'in', ['t', 'f'])], context=context) |
896 | + |
897 | + |
898 | + reactivated_by_oc_subscription = False |
899 | + if unidata_product and not context.get('sync_update_execution') and 'oc_subscription' in vals: |
900 | + if 'international_status' not in vals: |
901 | + if self.search_exist(cr, uid, [('id', 'in', ids), ('international_status', '!=', 'UniData'), ('active', 'in', ['t', 'f'])], context=context): |
902 | + raise osv.except_osv(_('Waning'), _("You can write the oc_subscription field on multiple products only if all products are UniData !")) |
903 | + |
904 | + if not vals['oc_subscription']: |
905 | + vals['active'] = False |
906 | + prod_state = 'archived' |
907 | + elif prod_state != 'archived': |
908 | + if not prod_state and 'state' not in vals: |
909 | + # uf state is archived or phase_out, we must map it with uf state |
910 | + reactivated_by_oc_subscription = True |
911 | + |
912 | + vals['active'] = True |
913 | + |
914 | # update local stock mission report lines : |
915 | - if 'state' in vals: |
916 | - prod_state = '' |
917 | + if not prod_state and 'state' in vals: |
918 | if vals['state']: |
919 | state_id = vals['state'] |
920 | if isinstance(state_id, (int, long)): |
921 | state_id = [state_id] |
922 | prod_state = prod_status_obj.read(cr, uid, state_id, ['code'], context=context)[0]['code'] |
923 | - local_smrl_ids = smrl_obj.search(cr, uid, [('product_state', '!=', prod_state), ('product_id', 'in', ids), ('full_view', '=', False), ('mission_report_id.local_report', '=', True)], context=context) |
924 | - if local_smrl_ids: |
925 | - no_sync_context = context.copy() |
926 | - no_sync_context['sync_update_execution'] = False |
927 | - smrl_obj.write(cr, 1, local_smrl_ids, {'product_state': prod_state}, context=no_sync_context) |
928 | - |
929 | - if intstat_code: |
930 | - # just update SMRL that belongs to our instance: |
931 | - local_smrl_ids = smrl_obj.search(cr, uid, [ |
932 | - ('international_status_code', '!=', intstat_code), |
933 | - ('product_id', 'in', ids), |
934 | - ('full_view', '=', False), |
935 | - ('mission_report_id.local_report', '=', True) |
936 | - ], context=context) |
937 | - if local_smrl_ids: |
938 | - no_sync_context = context.copy() |
939 | - no_sync_context['sync_update_execution'] = False |
940 | - smrl_obj.write(cr, 1, local_smrl_ids, {'international_status_code': intstat_code or ''}, context=no_sync_context) |
941 | - |
942 | - if 'state_ud' in vals: |
943 | - # just update SMRL that belongs to our instance: |
944 | - local_smrl_ids = smrl_obj.search(cr, uid, [ |
945 | - ('product_id', 'in', ids), |
946 | - ('full_view', '=', False), |
947 | - ('mission_report_id.local_report', '=', True), |
948 | - ('state_ud', '!=', vals['state_ud'] or ''), |
949 | - ], context=context) |
950 | - if local_smrl_ids: |
951 | - no_sync_context = context.copy() |
952 | - no_sync_context['sync_update_execution'] = False |
953 | - smrl_obj.write(cr, 1, local_smrl_ids, {'state_ud': vals['state_ud'] or ''}, context=no_sync_context) |
954 | + |
955 | + |
956 | |
957 | product_uom_categ = [] |
958 | if 'uom_id' in vals or 'uom_po_id' in vals: |
959 | @@ -1524,17 +1678,59 @@ |
960 | |
961 | if context.get('sync_update_execution') and not context.get('bypass_sync_update', False): |
962 | if vals.get('active', None) is False: |
963 | - if self.deactivate_product(cr, uid, ids, context=context) is not True: |
964 | - vals.update({ |
965 | - 'active': True, |
966 | - }) |
967 | - elif vals.get('active', None) is True: |
968 | - vals.update({ |
969 | - 'active': True, |
970 | - # 'state': phase_out_status, |
971 | - }) |
972 | - |
973 | - if 'active' in vals: |
974 | + deactivate_result = self.deactivate_product(cr, uid, ids, context=context, try_only=True) |
975 | + if not deactivate_result['ok']: |
976 | + vals['active'] = True |
977 | + if unidata_product: |
978 | + |
979 | + prod_code = self.read(cr, uid, ids[0], ['default_code'], context=context) |
980 | + error_msg = [] |
981 | + |
982 | + wiz_error = self.pool.get('product.deactivation.error').browse(cr, uid, deactivate_result['error'], context=context) |
983 | + if wiz_error.stock_exist: |
984 | + error_msg.append('Stock exists (internal locations)') |
985 | + |
986 | + doc_errors = [] |
987 | + for error in wiz_error.error_lines: |
988 | + doc_errors.append(error.doc_ref) |
989 | + |
990 | + if doc_errors: |
991 | + error_msg.append('Product is contained in opened documents :\n - %s' % ' \n - '.join(doc_errors)) |
992 | + raise osv.except_osv('Warning', 'Product %s cannot be deactivated: \n * %s ' % (prod_code['default_code'], "\n * ".join(error_msg))) |
993 | + |
994 | + elif unidata_product: |
995 | + # unidata prodcut inactive must also be archived: 1st set as phase out by the update one |
996 | + vals['state'] = prod_status_obj.search(cr, uid, [('code', '=', 'archived')], context=context)[0] |
997 | + |
998 | + if prod_state == 'archived' and unidata_product: |
999 | + # received archived: set as phase out, when the "active" update is processed it will set archived state if inactivation is allowed |
1000 | + vals['state'] = prod_status_obj.search(cr, uid, [('code', '=', 'phase_out')], context=context)[0] |
1001 | + |
1002 | + ud_unable_to_inactive = [] |
1003 | + if 'active' in vals and not vals['active'] and not context.get('sync_update_execution') and unidata_product: |
1004 | + ud_unable_to_inactive = self.unidata_products_used(cr, uid, ids) |
1005 | + if not prod_state: |
1006 | + vals['state'] = prod_status_obj.search(cr, uid, [('code', '=', 'archived')], context=context)[0] |
1007 | + if ud_unable_to_inactive: |
1008 | + ids = list(set(ids) - ud_unable_to_inactive) |
1009 | + ud_unable_to_inactive = list(ud_unable_to_inactive) |
1010 | + |
1011 | + if 'state' in vals: |
1012 | + local_smrl_ids = smrl_obj.search(cr, uid, [('product_state', '!=', prod_state), ('product_id', 'in', ids), ('full_view', '=', False), ('mission_report_id.local_report', '=', True)], context=context) |
1013 | + if local_smrl_ids: |
1014 | + no_sync_context = context.copy() |
1015 | + no_sync_context['sync_update_execution'] = False |
1016 | + smrl_obj.write(cr, 1, local_smrl_ids, {'product_state': prod_state}, context=no_sync_context) |
1017 | + |
1018 | + if ids and 'active' in vals: |
1019 | + |
1020 | + # to manage sync update generation on active field |
1021 | + fields_to_update = ['active_change_date=%(now)s'] |
1022 | + if context.get('sync_update_execution'): |
1023 | + fields_to_update += ['active_sync_change_date=%(now)s'] |
1024 | + cr.execute('update product_product set '+', '.join(fields_to_update)+' where id in %(ids)s and active != %(active)s', {'now': fields.datetime.now(), 'ids': tuple(ids), 'active': vals['active']}) # not_a_user_entry |
1025 | + |
1026 | + |
1027 | local_smrl_ids = smrl_obj.search(cr, uid, [ |
1028 | ('product_id', 'in', ids), |
1029 | ('full_view', '=', False), |
1030 | @@ -1566,7 +1762,18 @@ |
1031 | self.set_as_edonly(cr, uid, ids, context=context) |
1032 | else: |
1033 | self.set_as_nobn_noed(cr, uid, ids, context=context) |
1034 | + |
1035 | res = super(product_attributes, self).write(cr, uid, ids, vals, context=context) |
1036 | + if ud_unable_to_inactive: |
1037 | + vals['active'] = True |
1038 | + vals['state'] = prod_status_obj.search(cr, uid, [('code', '=', 'phase_out')], context=context)[0] |
1039 | + super(product_attributes, self).write(cr, uid, ud_unable_to_inactive, vals, context=context) |
1040 | + |
1041 | + local_smrl_ids = smrl_obj.search(cr, uid, [('product_state', '!=', 'phase_out'), ('product_id', 'in', ud_unable_to_inactive), ('full_view', '=', False), ('mission_report_id.local_report', '=', True)], context=context) |
1042 | + if local_smrl_ids: |
1043 | + no_sync_context = context.copy() |
1044 | + no_sync_context['sync_update_execution'] = False |
1045 | + smrl_obj.write(cr, 1, local_smrl_ids, {'product_state': 'phase_out'}, context=no_sync_context) |
1046 | |
1047 | if product_uom_categ: |
1048 | uom_categ = 'uom_id' in vals and vals['uom_id'] and self.pool.get('product.uom').browse(cr, uid, vals['uom_id'], context=context).category_id.id or False |
1049 | @@ -1575,9 +1782,27 @@ |
1050 | if (uom_categ and uom_categ not in product_uom_categ) or (uos_categ and uos_categ not in product_uom_categ): |
1051 | raise osv.except_osv(_('Error'), _('You cannot choose an UoM which is not in the same UoM category of default UoM')) |
1052 | |
1053 | + if ud_unable_to_inactive: |
1054 | + ids = ids + ud_unable_to_inactive |
1055 | + |
1056 | + if reactivated_by_oc_subscription: |
1057 | + self.set_state_from_state_ud(cr, uid, ids, context=context) |
1058 | + |
1059 | + if check_reactivate: |
1060 | + # ud set only state_ud != archived, check if product must be reactivated |
1061 | + set_as_active = self.search(cr, uid, [('active', '=', False), ('oc_subscription', '=', True), ('id', 'in', ids)], context=context) |
1062 | + if set_as_active: |
1063 | + self.write(cr, uid, set_as_active, {'active': True}, context=context) |
1064 | return res |
1065 | |
1066 | |
1067 | + def set_state_from_state_ud(self, cr, uid, ids, context=None): |
1068 | + for grp in self.read_group(cr, uid, [('id', 'in', ids)], fields=['state_ud'], groupby=['state_ud'], context=context): |
1069 | + ids_to_w = self.search(cr, uid, grp['__domain'], context=context) |
1070 | + if ids_to_w: |
1071 | + self.write(cr, uid, ids_to_w, {'state_ud': grp['state_ud']}, context=context) |
1072 | + return True |
1073 | + |
1074 | def reactivate_product(self, cr, uid, ids, context=None): |
1075 | ''' |
1076 | Re-activate product. |
1077 | @@ -1603,7 +1828,7 @@ |
1078 | |
1079 | return True |
1080 | |
1081 | - def deactivate_product(self, cr, uid, ids, context=None): |
1082 | + def deactivate_product(self, cr, uid, ids, context=None, try_only=False): |
1083 | ''' |
1084 | De-activate product. |
1085 | Check if the product is not used in any document in Unifield |
1086 | @@ -1614,7 +1839,6 @@ |
1087 | if isinstance(ids, (int, long)): |
1088 | ids = [ids] |
1089 | |
1090 | - # TODO JFB RR: constraint |
1091 | location_obj = self.pool.get('stock.location') |
1092 | po_line_obj = self.pool.get('purchase.order.line') |
1093 | tender_line_obj = self.pool.get('tender.line') |
1094 | @@ -1859,6 +2083,10 @@ |
1095 | 'doc_ref': invoice.invoice_id.number, |
1096 | 'doc_id': invoice.invoice_id.id}, context=context) |
1097 | |
1098 | + |
1099 | + if try_only: |
1100 | + return {'ok': False, 'error': wizard_id} |
1101 | + |
1102 | if context.get('sync_update_execution', False): |
1103 | context['bypass_sync_update'] = True |
1104 | self.write(cr, uid, product.id, { |
1105 | @@ -1873,6 +2101,9 @@ |
1106 | 'target': 'new', |
1107 | 'context': context} |
1108 | |
1109 | + if try_only: |
1110 | + return {'ok': True, 'error': False} |
1111 | + |
1112 | if context.get('sync_update_execution', False): |
1113 | context['bypass_sync_update'] = True |
1114 | |
1115 | |
1116 | === modified file 'bin/addons/product_attributes/product_attributes_data.xml' |
1117 | --- bin/addons/product_attributes/product_attributes_data.xml 2019-03-25 14:02:29 +0000 |
1118 | +++ bin/addons/product_attributes/product_attributes_data.xml 2020-04-29 08:55:42 +0000 |
1119 | @@ -2,86 +2,101 @@ |
1120 | <openerp> |
1121 | <data noupdate="0"> |
1122 | |
1123 | - <record model="product.status" id="status_1"> |
1124 | - <field name="code">valid</field> |
1125 | - <field name="name">Valid</field> |
1126 | - <field name="no_external" eval="False" /> |
1127 | - <field name="no_esc" eval="False" /> |
1128 | - <field name="no_internal" eval="False" /> |
1129 | - <field name="no_consumption" eval="False" /> |
1130 | - <field name="no_storage" eval="False" /> |
1131 | - </record> |
1132 | - <record model="product.status" id="status_2"> |
1133 | - <field name="code">phase_out</field> |
1134 | - <field name="name">Phase Out</field> |
1135 | - <field name="no_external" eval="True" /> |
1136 | - <field name="no_esc" eval="False" /> |
1137 | - <field name="no_internal" eval="False" /> |
1138 | - <field name="no_consumption" eval="False" /> |
1139 | - <field name="no_storage" eval="False" /> |
1140 | - </record> |
1141 | - <record model="product.status" id="status_3"> |
1142 | - <field name="code">stopped</field> |
1143 | - <field name="name">Stopped</field> |
1144 | - <field name="no_external" eval="True" /> |
1145 | - <field name="no_esc" eval="True" /> |
1146 | - <field name="no_internal" eval="True" /> |
1147 | - <field name="no_consumption" eval="False" /> |
1148 | - <field name="no_storage" eval="False" /> |
1149 | - </record> |
1150 | - <record model="product.status" id="status_4"> |
1151 | - <field name="code">archived</field> |
1152 | - <field name="name">Archived</field> |
1153 | - <field name="no_external" eval="True" /> |
1154 | - <field name="no_esc" eval="True" /> |
1155 | - <field name="no_internal" eval="True" /> |
1156 | - <field name="no_consumption" eval="True" /> |
1157 | - <field name="no_storage" eval="True" /> |
1158 | - </record> |
1159 | - <record model="product.status" id="status_5"> |
1160 | - <field name="code">status1</field> |
1161 | - <field name="name">Status 1</field> |
1162 | - <field name="no_external" eval="False" /> |
1163 | - <field name="no_esc" eval="False" /> |
1164 | - <field name="no_internal" eval="False" /> |
1165 | - <field name="no_consumption" eval="False" /> |
1166 | - <field name="no_storage" eval="False" /> |
1167 | - </record> |
1168 | - <record model="product.status" id="status_6"> |
1169 | - <field name="code">status2</field> |
1170 | - <field name="name">Status 2</field> |
1171 | - <field name="no_external" eval="False" /> |
1172 | - <field name="no_esc" eval="False" /> |
1173 | - <field name="no_internal" eval="False" /> |
1174 | - <field name="no_consumption" eval="False" /> |
1175 | - <field name="no_storage" eval="False" /> |
1176 | - </record> |
1177 | + <record model="product.status" id="status_1"> |
1178 | + <field name="code">valid</field> |
1179 | + <field name="name">Valid</field> |
1180 | + <field name="no_external" eval="False" /> |
1181 | + <field name="no_esc" eval="False" /> |
1182 | + <field name="no_internal" eval="False" /> |
1183 | + <field name="no_consumption" eval="False" /> |
1184 | + <field name="no_storage" eval="False" /> |
1185 | + </record> |
1186 | + <record model="product.status" id="status_forbidden"> |
1187 | + <field name="code">forbidden</field> |
1188 | + <field name="name">Forbidden</field> |
1189 | + <field name="no_external" eval="True" /> |
1190 | + <field name="no_esc" eval="True" /> |
1191 | + <field name="no_internal" eval="True" /> |
1192 | + <field name="no_consumption" eval="True" /> |
1193 | + <field name="no_storage" eval="True" /> |
1194 | + </record> |
1195 | + <record model="product.status" id="status_2"> |
1196 | + <field name="code">phase_out</field> |
1197 | + <field name="name">Phase Out</field> |
1198 | + <field name="no_external" eval="True" /> |
1199 | + <field name="no_esc" eval="False" /> |
1200 | + <field name="no_internal" eval="False" /> |
1201 | + <field name="no_consumption" eval="False" /> |
1202 | + <field name="no_storage" eval="False" /> |
1203 | + </record> |
1204 | + <record model="product.status" id="status_3"> |
1205 | + <field name="code">stopped</field> |
1206 | + <field name="name">Stopped (to del)</field> |
1207 | + <field name="no_external" eval="True" /> |
1208 | + <field name="no_esc" eval="True" /> |
1209 | + <field name="no_internal" eval="True" /> |
1210 | + <field name="no_consumption" eval="False" /> |
1211 | + <field name="no_storage" eval="False" /> |
1212 | + <field name="active" eval="False" /> |
1213 | + <field name="mapped_to" ref="status_2" /> |
1214 | + </record> |
1215 | + <record model="product.status" id="status_4"> |
1216 | + <field name="code">archived</field> |
1217 | + <field name="name">Archived</field> |
1218 | + <field name="no_external" eval="True" /> |
1219 | + <field name="no_esc" eval="True" /> |
1220 | + <field name="no_internal" eval="True" /> |
1221 | + <field name="no_consumption" eval="True" /> |
1222 | + <field name="no_storage" eval="True" /> |
1223 | + </record> |
1224 | + <record model="product.status" id="status_5"> |
1225 | + <field name="code">status1</field> |
1226 | + <field name="name">Status 1 (to del)</field> |
1227 | + <field name="no_external" eval="False" /> |
1228 | + <field name="no_esc" eval="False" /> |
1229 | + <field name="no_internal" eval="False" /> |
1230 | + <field name="no_consumption" eval="False" /> |
1231 | + <field name="no_storage" eval="False" /> |
1232 | + <field name="active" eval="False" /> |
1233 | + <field name="mapped_to" ref="status_1" /> |
1234 | + </record> |
1235 | + <record model="product.status" id="status_6"> |
1236 | + <field name="code">status2</field> |
1237 | + <field name="name">Status 2 (to_del)</field> |
1238 | + <field name="no_external" eval="False" /> |
1239 | + <field name="no_esc" eval="False" /> |
1240 | + <field name="no_internal" eval="False" /> |
1241 | + <field name="no_consumption" eval="False" /> |
1242 | + <field name="no_storage" eval="False" /> |
1243 | + <field name="active" eval="False" /> |
1244 | + <field name="mapped_to" ref="status_1" /> |
1245 | + </record> |
1246 | |
1247 | - <record model="product.international.status" id="int_1"> |
1248 | - <field name="code">itc</field> |
1249 | - <field name="name">ITC</field> |
1250 | - </record> |
1251 | - <record model="product.international.status" id="int_2"> |
1252 | - <field name="code">esc</field> |
1253 | + <record model="product.international.status" id="int_1"> |
1254 | + <field name="code">itc</field> |
1255 | + <field name="name">ITC</field> |
1256 | + </record> |
1257 | + <record model="product.international.status" id="int_2"> |
1258 | + <field name="code">esc</field> |
1259 | <field name="name">ESC</field> |
1260 | - </record> |
1261 | - <record model="product.international.status" id="int_3"> |
1262 | - <field name="code">hq</field> |
1263 | - <field name="name">HQ</field> |
1264 | - </record> |
1265 | - <record model="product.international.status" id="int_4"> |
1266 | - <field name="code">local</field> |
1267 | - <field name="name">Local</field> |
1268 | - </record> |
1269 | - <record model="product.international.status" id="int_5"> |
1270 | - <field name="code">temp</field> |
1271 | - <field name="name">Temporary</field> |
1272 | - </record> |
1273 | - <record model="product.international.status" id="int_6"> |
1274 | - <field name="code">unidata</field> |
1275 | + </record> |
1276 | + <record model="product.international.status" id="int_3"> |
1277 | + <field name="code">hq</field> |
1278 | + <field name="name">HQ</field> |
1279 | + </record> |
1280 | + <record model="product.international.status" id="int_4"> |
1281 | + <field name="code">local</field> |
1282 | + <field name="name">Local</field> |
1283 | + </record> |
1284 | + <record model="product.international.status" id="int_5"> |
1285 | + <field name="code">temp</field> |
1286 | + <field name="name">Temporary</field> |
1287 | + </record> |
1288 | + <record model="product.international.status" id="int_6"> |
1289 | + <field name="code">unidata</field> |
1290 | <field name="name">UniData</field> |
1291 | - </record> |
1292 | - |
1293 | + </record> |
1294 | + |
1295 | <record model="product.heat_sensitive" id="heat_yes"> |
1296 | <field name="code">yes</field> |
1297 | <field name="name">Yes</field> |
1298 | @@ -115,59 +130,72 @@ |
1299 | <field name="active" eval="False" /> |
1300 | </record> |
1301 | |
1302 | - <record model="product.cold_chain" id="cold_1"> |
1303 | - <field name="code">*</field> |
1304 | - <field name="name">* - Keep Cool: used for a kit or article containing cold chain module or item(s)</field> |
1305 | - </record> |
1306 | - <record model="product.cold_chain" id="cold_2"> |
1307 | - <field name="code">*0</field> |
1308 | - <field name="name">*0 - Problem if any window blue</field> |
1309 | - </record> |
1310 | - <record model="product.cold_chain" id="cold_3"> |
1311 | - <field name="code">*0F</field> |
1312 | - <field name="name">*0F - Problem if any window blue or Freeze-tag = ALARM</field> |
1313 | - </record> |
1314 | - <record model="product.cold_chain" id="cold_4"> |
1315 | - <field name="code">*A</field> |
1316 | - <field name="name">*A - Problem if A, B, C and/or D blue = ALARM</field> |
1317 | - </record> |
1318 | - <record model="product.cold_chain" id="cold_5"> |
1319 | - <field name="code">*AF</field> |
1320 | - <field name="name">*AF - Problem if A, B, C and/or D blue or Freeze-tag = ALARM</field> |
1321 | - </record> |
1322 | - <record model="product.cold_chain" id="cold_6"> |
1323 | - <field name="code">*B</field> |
1324 | - <field name="name">*B - Problem if B, C and/or D blue = ALARM</field> |
1325 | - </record> |
1326 | - <record model="product.cold_chain" id="cold_7"> |
1327 | - <field name="code">*BF</field> |
1328 | - <field name="name">*BF - Problem if B, C and/or D blue or Freeze-tag = ALARM</field> |
1329 | - </record> |
1330 | - <record model="product.cold_chain" id="cold_8"> |
1331 | - <field name="code">*C</field> |
1332 | - <field name="name">*C - Problem if C and D blue</field> |
1333 | - </record> |
1334 | - <record model="product.cold_chain" id="cold_9"> |
1335 | - <field name="code">*CF</field> |
1336 | - <field name="name">*CF - Problem if C and/or D blue or Freeze-tag = ALARM</field> |
1337 | - </record> |
1338 | - <record model="product.cold_chain" id="cold_10"> |
1339 | - <field name="code">*D</field> |
1340 | - <field name="name">*D - Store and transport at -25°C (store in deepfreezer, transport with dry-ice)</field> |
1341 | - </record> |
1342 | - <record model="product.cold_chain" id="cold_11"> |
1343 | - <field name="code">*F</field> |
1344 | - <field name="name">*F - Cannot be frozen: check FreezeWatch</field> |
1345 | - </record> |
1346 | - <record model="product.cold_chain" id="cold_12"> |
1347 | - <field name="code">*25</field> |
1348 | - <field name="name">*25 - Must be kept below 25°C (but not necesseraly in cold chain)</field> |
1349 | - </record> |
1350 | - <record model="product.cold_chain" id="cold_13"> |
1351 | - <field name="code">*25F</field> |
1352 | - <field name="name">*25F - Must be kept below 25°C and cannot be frozen: check FreezeWatch</field> |
1353 | - </record> |
1354 | - |
1355 | - </data> |
1356 | + <record model="product.cold_chain" id="cold_1"> |
1357 | + <field name="code">*</field> |
1358 | + <field name="name">* - Keep Cool: used for a kit or article containing cold chain module or item(s)</field> |
1359 | + </record> |
1360 | + <record model="product.cold_chain" id="cold_2"> |
1361 | + <field name="code">*0</field> |
1362 | + <field name="name">*0 - Problem if any window blue</field> |
1363 | + </record> |
1364 | + <record model="product.cold_chain" id="cold_3"> |
1365 | + <field name="code">*0F</field> |
1366 | + <field name="name">*0F - Problem if any window blue or Freeze-tag = ALARM</field> |
1367 | + </record> |
1368 | + <record model="product.cold_chain" id="cold_4"> |
1369 | + <field name="code">*A</field> |
1370 | + <field name="name">*A - Problem if A, B, C and/or D blue = ALARM</field> |
1371 | + </record> |
1372 | + <record model="product.cold_chain" id="cold_5"> |
1373 | + <field name="code">*AF</field> |
1374 | + <field name="name">*AF - Problem if A, B, C and/or D blue or Freeze-tag = ALARM</field> |
1375 | + </record> |
1376 | + <record model="product.cold_chain" id="cold_6"> |
1377 | + <field name="code">*B</field> |
1378 | + <field name="name">*B - Problem if B, C and/or D blue = ALARM</field> |
1379 | + </record> |
1380 | + <record model="product.cold_chain" id="cold_7"> |
1381 | + <field name="code">*BF</field> |
1382 | + <field name="name">*BF - Problem if B, C and/or D blue or Freeze-tag = ALARM</field> |
1383 | + </record> |
1384 | + <record model="product.cold_chain" id="cold_8"> |
1385 | + <field name="code">*C</field> |
1386 | + <field name="name">*C - Problem if C and D blue</field> |
1387 | + </record> |
1388 | + <record model="product.cold_chain" id="cold_9"> |
1389 | + <field name="code">*CF</field> |
1390 | + <field name="name">*CF - Problem if C and/or D blue or Freeze-tag = ALARM</field> |
1391 | + </record> |
1392 | + <record model="product.cold_chain" id="cold_10"> |
1393 | + <field name="code">*D</field> |
1394 | + <field name="name">*D - Store and transport at -25°C (store in deepfreezer, transport with dry-ice)</field> |
1395 | + </record> |
1396 | + <record model="product.cold_chain" id="cold_11"> |
1397 | + <field name="code">*F</field> |
1398 | + <field name="name">*F - Cannot be frozen: check FreezeWatch</field> |
1399 | + </record> |
1400 | + <record model="product.cold_chain" id="cold_12"> |
1401 | + <field name="code">*25</field> |
1402 | + <field name="name">*25 - Must be kept below 25°C (but not necesseraly in cold chain)</field> |
1403 | + </record> |
1404 | + <record model="product.cold_chain" id="cold_13"> |
1405 | + <field name="code">*25F</field> |
1406 | + <field name="name">*25F - Must be kept below 25°C and cannot be frozen: check FreezeWatch</field> |
1407 | + </record> |
1408 | + |
1409 | + <record id="ir_cron_deactivate_ud_products" model="ir.cron"> |
1410 | + <field name="function">hq_cron_deactivate_ud_products</field> |
1411 | + <field name="user_id">1</field> |
1412 | + <field name="name">HQ: deactivate UD products</field> |
1413 | + <field name="interval_type">days</field> |
1414 | + <field eval="-1" name="numbercall"/> |
1415 | + <field eval="5" name="priority"/> |
1416 | + <field eval="0" name="doall"/> |
1417 | + <field eval="1" name="active"/> |
1418 | + <field eval="1" name="interval_number"/> |
1419 | + <field name="model">product.product</field> |
1420 | + </record> |
1421 | + |
1422 | +</data> |
1423 | </openerp> |
1424 | |
1425 | |
1426 | === modified file 'bin/addons/purchase/purchase_order_line.py' |
1427 | --- bin/addons/purchase/purchase_order_line.py 2020-02-10 17:16:32 +0000 |
1428 | +++ bin/addons/purchase/purchase_order_line.py 2020-04-29 08:55:42 +0000 |
1429 | @@ -1263,9 +1263,12 @@ |
1430 | default = {} |
1431 | |
1432 | # do not copy canceled purchase.order.line: |
1433 | - pol = self.browse(cr, uid, p_id, fields_to_fetch=['state', 'order_id', 'linked_sol_id'], context=context) |
1434 | + pol = self.browse(cr, uid, p_id, fields_to_fetch=['state', 'order_id', 'linked_sol_id', 'product_id'], context=context) |
1435 | if pol.state in ['cancel', 'cancel_r'] and not context.get('allow_cancelled_pol_copy', False): |
1436 | return False |
1437 | + if pol.product_id: # Check constraints on lines |
1438 | + self.pool.get('product.product')._get_restriction_error(cr, uid, [pol.product_id.id], |
1439 | + {'partner_id': pol.order_id.partner_id.id}, context=context) |
1440 | |
1441 | default.update({'state': 'draft', 'move_ids': [], 'invoiced': 0, 'invoice_lines': [], 'commitment_line_ids': []}) |
1442 | |
1443 | |
1444 | === modified file 'bin/addons/sale/sale_order.py' |
1445 | --- bin/addons/sale/sale_order.py 2019-12-12 13:30:26 +0000 |
1446 | +++ bin/addons/sale/sale_order.py 2020-04-29 08:55:42 +0000 |
1447 | @@ -2958,6 +2958,7 @@ |
1448 | context = {} |
1449 | if not vals.get('product_id') and context.get('sale_id', []): |
1450 | vals.update({'type': 'make_to_order'}) |
1451 | + so_obj = self.pool.get('sale.order') |
1452 | |
1453 | self.check_empty_line(cr, uid, False, vals, context=context) |
1454 | # UF-1739: as we do not have product_uos_qty in PO (only in FO), we recompute here the product_uos_qty for the SYNCHRO |
1455 | @@ -2969,18 +2970,32 @@ |
1456 | qty = float(qty) |
1457 | vals.update({'product_uos_qty' : qty * product_obj.read(cr, uid, product_id, ['uos_coeff'])['uos_coeff']}) |
1458 | |
1459 | - # Internal request |
1460 | pricelist = False |
1461 | order_id = vals.get('order_id', False) |
1462 | + order_data = False |
1463 | if order_id: |
1464 | - order_data = self.pool.get('sale.order').\ |
1465 | - read(cr, uid, order_id, ['procurement_request', 'pricelist_id', 'fo_created_by_po_sync'], context) |
1466 | - if order_data['procurement_request']: |
1467 | + ftf = ['procurement_request', 'pricelist_id', 'fo_created_by_po_sync', 'partner_id'] |
1468 | + order_data = so_obj.browse(cr, uid, order_id, fields_to_fetch=ftf, context=context) |
1469 | + |
1470 | + if product_id: # Check constraints on lines |
1471 | + partner_id = False |
1472 | + if order_data: |
1473 | + partner_id = order_data.partner_id.id |
1474 | + if order_data and order_data.procurement_request: |
1475 | + self.pool.get('product.product')._get_restriction_error(cr, uid, [product_id], |
1476 | + {'constraints': 'consumption'}, context=context) |
1477 | + else: |
1478 | + self._check_product_constraints(cr, uid, vals.get('type'), vals.get('po_cft'), product_id, partner_id, |
1479 | + check_fnct=False, context=context) |
1480 | + |
1481 | + # Internal request |
1482 | + if order_data: |
1483 | + if order_data.procurement_request: |
1484 | vals.update({'cost_price': vals.get('cost_price', False)}) |
1485 | - if order_data['pricelist_id']: |
1486 | - pricelist = order_data['pricelist_id'][0] |
1487 | + if order_data.pricelist_id: |
1488 | + pricelist = order_data.pricelist_id.id |
1489 | # New line created out of synchro on a FO/IR created by synchro |
1490 | - if order_data['fo_created_by_po_sync'] and not context.get('sync_message_execution'): |
1491 | + if order_data.fo_created_by_po_sync and not context.get('sync_message_execution'): |
1492 | vals.update({'created_by_sync': True}) |
1493 | |
1494 | # force the line creation with the good state, otherwise track changes for order state will |
1495 | @@ -3003,7 +3018,7 @@ |
1496 | so_line_ids = super(sale_order_line, self).create(cr, uid, vals, context=context) |
1497 | if not vals.get('sync_order_line_db_id', False): # 'sync_order_line_db_id' not in vals or vals: |
1498 | if vals.get('order_id', False): |
1499 | - name = self.pool.get('sale.order').browse(cr, uid, vals.get('order_id'), context=context).name |
1500 | + name = so_obj.browse(cr, uid, vals.get('order_id'), context=context).name |
1501 | super(sale_order_line, self).write(cr, uid, so_line_ids, {'sync_order_line_db_id': name + "_" + str(so_line_ids), } , context=context) |
1502 | |
1503 | if vals.get('stock_take_date'): |
1504 | |
1505 | === modified file 'bin/addons/sale/sale_workflow.py' |
1506 | --- bin/addons/sale/sale_workflow.py 2020-01-30 17:08:29 +0000 |
1507 | +++ bin/addons/sale/sale_workflow.py 2020-04-29 08:55:42 +0000 |
1508 | @@ -567,6 +567,12 @@ |
1509 | |
1510 | for sol in self.browse(cr, uid, ids, context=context): |
1511 | to_write = {} |
1512 | + if sol.product_id: # Check constraints on lines |
1513 | + if sol.procurement_request: |
1514 | + check_vals = {'constraints': 'consumption'} |
1515 | + else: |
1516 | + check_vals = {'obj_type': 'sale.order', 'partner_id': sol.order_id.partner_id.id} |
1517 | + self.pool.get('product.product')._get_restriction_error(cr, uid, [sol.product_id.id], vals=check_vals, context=context) |
1518 | if sol.order_id.procurement_request and not sol.order_id.location_requestor_id: |
1519 | raise osv.except_osv(_('Warning !'), |
1520 | _('You can not validate the line without a Location Requestor.')) |
1521 | |
1522 | === modified file 'bin/addons/sale/wizard/internal_request_import.py' |
1523 | --- bin/addons/sale/wizard/internal_request_import.py 2019-11-07 14:47:03 +0000 |
1524 | +++ bin/addons/sale/wizard/internal_request_import.py 2020-04-29 08:55:42 +0000 |
1525 | @@ -617,11 +617,18 @@ |
1526 | if prod_ids: |
1527 | product_id = prod_ids[0] |
1528 | prod_cols = ['standard_price', 'uom_id', 'uom_po_id'] |
1529 | - product = prod_obj.read(cr, uid, product_id, prod_cols, context=context) |
1530 | - line_data.update({ |
1531 | - 'imp_product_id': product_id, |
1532 | - 'imp_comment': comment or '', |
1533 | - }) |
1534 | + p_error, p_msg = prod_obj._test_restriction_error(cr, uid, [product_id], |
1535 | + vals={'constraints': 'consumption'}, |
1536 | + context=context) |
1537 | + if p_error: # Check constraints on products |
1538 | + red = True |
1539 | + line_errors += p_msg + '. ' |
1540 | + else: |
1541 | + product = prod_obj.read(cr, uid, product_id, prod_cols, context=context) |
1542 | + line_data.update({ |
1543 | + 'imp_product_id': product_id, |
1544 | + 'imp_comment': comment or '', |
1545 | + }) |
1546 | else: |
1547 | if ir_imp.no_prod_as_comment: |
1548 | nb_treated_lines_by_nomen += 1 |
1549 | |
1550 | === modified file 'bin/addons/sourcing/sale_order_line.py' |
1551 | --- bin/addons/sourcing/sale_order_line.py 2020-03-19 10:08:38 +0000 |
1552 | +++ bin/addons/sourcing/sale_order_line.py 2020-04-29 08:55:42 +0000 |
1553 | @@ -1613,6 +1613,12 @@ |
1554 | company_currency_id = self.pool.get('res.users').get_company_currency_id(cr, uid) |
1555 | |
1556 | for sourcing_line in self.browse(cr, uid, ids, context=context): |
1557 | + if sourcing_line.procurement_request: # Check constraints on lines |
1558 | + check_vals = {'constraints': 'consumption'} |
1559 | + else: |
1560 | + check_vals = {'obj_type': 'sale.order', 'partner_id': sourcing_line.order_id.partner_id.id} |
1561 | + self.pool.get('product.product')._get_restriction_error(cr, uid, [sourcing_line.product_id.id], vals=check_vals, |
1562 | + context=context) |
1563 | if sourcing_line.supplier and sourcing_line.supplier_type == 'esc' and \ |
1564 | sourcing_line.supplier_split_po == 'yes' and not sourcing_line.related_sourcing_id: |
1565 | raise osv.except_osv(_('Error'), _('For this Supplier you have to select a Sourcing Group')) |
1566 | |
1567 | === modified file 'bin/addons/specific_rules/specific_rules.py' |
1568 | --- bin/addons/specific_rules/specific_rules.py 2020-03-19 10:08:38 +0000 |
1569 | +++ bin/addons/specific_rules/specific_rules.py 2020-04-29 08:55:42 +0000 |
1570 | @@ -532,7 +532,7 @@ |
1571 | if line.hidden_batch_management_mandatory and not line.prod_lot_id: |
1572 | raise osv.except_osv(_('Error'), _('The product %s is batch mandatory but the line with this product has no batch') % product_obj.name_get(cr, uid, [line.product_id.id])[0][1]) |
1573 | |
1574 | - if line.product_id: |
1575 | + if line.product_id and line.product_id.state.code != 'forbidden': |
1576 | # Check constraints on lines |
1577 | product_obj._get_restriction_error(cr, uid, [line.product_id.id], {'location_id': line.location_id.id}, context=context) |
1578 | |
1579 | @@ -769,7 +769,7 @@ |
1580 | result.setdefault('value', {})['hidden_perishable_mandatory'] = False |
1581 | if product: |
1582 | product_obj = self.pool.get('product.product').browse(cr, uid, product) |
1583 | - if location_id: |
1584 | + if location_id and product_obj.state.code != 'forbidden': |
1585 | result, test = self.pool.get('product.product')._on_change_restriction_error(cr, uid, product, field_name='product_id', values=result, vals={'location_id': location_id}) |
1586 | if test: |
1587 | return result |
1588 | |
1589 | === modified file 'bin/addons/specific_rules/stock.py' |
1590 | --- bin/addons/specific_rules/stock.py 2019-09-18 14:06:52 +0000 |
1591 | +++ bin/addons/specific_rules/stock.py 2020-04-29 08:55:42 +0000 |
1592 | @@ -78,7 +78,7 @@ |
1593 | raise osv.except_osv(_('Error'), _('Please enter at least one line in stock inventory before confirm it.')) |
1594 | |
1595 | for inventory_line in inventory.inventory_line_id: |
1596 | - if inventory_line.product_id: |
1597 | + if inventory_line.product_id and inventory_line.product_id.state.code != 'forbidden': |
1598 | # Check product constrainsts |
1599 | product_obj._get_restriction_error(cr, uid, [inventory_line.product_id.id], {'location_id': inventory_line.location_id.id}, context=context) |
1600 | |
1601 | @@ -412,13 +412,14 @@ |
1602 | context = {} |
1603 | if location_id: |
1604 | context = {'location': location_id, 'compute_child': False} |
1605 | + if prodlot_id: |
1606 | + context.update({'prodlot_id': prodlot_id}) |
1607 | + product = product_obj.browse(cr, uid, product_id, context=context) |
1608 | + if location_id and product.state.code != 'forbidden': |
1609 | # Test the compatibility of the product with the location |
1610 | value, test = product_obj._on_change_restriction_error(cr, uid, product_id, field_name=field_change, values=value, vals={'location_id': location_id}) |
1611 | if test: |
1612 | return value |
1613 | - if prodlot_id: |
1614 | - context.update({'prodlot_id': prodlot_id}) |
1615 | - product = product_obj.browse(cr, uid, product_id, context=context) |
1616 | value.update({'product_uom': product.uom_id.id, |
1617 | 'hidden_perishable_mandatory': product.perishable, |
1618 | 'hidden_batch_management_mandatory': product.batch_management}) |
1619 | |
1620 | === modified file 'bin/addons/stock/stock_move.py' |
1621 | --- bin/addons/stock/stock_move.py 2020-01-29 16:41:23 +0000 |
1622 | +++ bin/addons/stock/stock_move.py 2020-04-29 08:55:42 +0000 |
1623 | @@ -930,6 +930,8 @@ |
1624 | def copy(self, cr, uid, id, default=None, context=None): |
1625 | if default is None: |
1626 | default = {} |
1627 | + prod_obj = self.pool.get('product.product') |
1628 | + |
1629 | default = default.copy() |
1630 | if 'qty_processed' not in default: |
1631 | default['qty_processed'] = 0 |
1632 | @@ -941,6 +943,15 @@ |
1633 | if 'integrity_error' not in default: |
1634 | default['integrity_error'] = 'empty' |
1635 | |
1636 | + if 'product_id' in default: # Check constraints on lines |
1637 | + move = self.browse(cr, uid, id, fields_to_fetch=['type'], context=context) |
1638 | + if move.type == 'in': |
1639 | + prod_obj._get_restriction_error(cr, uid, [move.product_id.id], {'partner_id': move.picking_id.partner_id.id}, |
1640 | + context=context) |
1641 | + elif move.type == 'out' and move.product_id.state.code == 'forbidden': |
1642 | + check_vals = {'location_dest_id': move.location_dest_id.id, 'move': move} |
1643 | + prod_obj._get_restriction_error(cr, uid, [move.product_id.id], check_vals, context=context) |
1644 | + |
1645 | return super(stock_move, self).copy(cr, uid, id, default, context=context) |
1646 | |
1647 | def copy_data(self, cr, uid, id, defaults=None, context=None): |
1648 | |
1649 | === modified file 'bin/addons/sync_client/update.py' |
1650 | --- bin/addons/sync_client/update.py 2020-01-03 13:16:16 +0000 |
1651 | +++ bin/addons/sync_client/update.py 2020-04-29 08:55:42 +0000 |
1652 | @@ -314,6 +314,20 @@ |
1653 | max_offset = len(update_ids) |
1654 | while min_offset < max_offset: |
1655 | offset = (min_offset + 200) < max_offset and min_offset + 200 or max_offset |
1656 | + |
1657 | + # specific case to split the active field on product.product |
1658 | + # i.e: at COO an update received on product must not block a possible update on active field to the project |
1659 | + cr.execute(''' |
1660 | + update product_product set active_sync_change_date = upd.create_date |
1661 | + from sync_client_update_to_send upd, ir_model_data d, sync_client_rule rule |
1662 | + where |
1663 | + rule.id = upd.rule_id and |
1664 | + rule.sequence_number in (602, 603) and |
1665 | + d.model = 'product.product' and |
1666 | + d.name = upd.sdref and |
1667 | + product_product.id = d.res_id and |
1668 | + upd.id in %s |
1669 | + ''', (tuple(update_ids[min_offset:offset]),)) |
1670 | for update in self.browse(cr, uid, update_ids[min_offset:offset], context=context): |
1671 | try: |
1672 | self.pool.get('ir.model.data').update_sd_ref(cr, uid, |
1673 | @@ -569,15 +583,20 @@ |
1674 | # Prepare updates |
1675 | # TODO: skip updates not preparable |
1676 | for update in updates: |
1677 | - if self.search_exist(cr, uid, |
1678 | - [('sdref', '=', update.sdref), |
1679 | - ('is_deleted', '=', False), |
1680 | - ('run', '=', False), |
1681 | - ('rule_sequence', '=', update.rule_sequence), |
1682 | - ('sequence_number', '<', update.sequence_number)]): |
1683 | - # previous not run on the same (sdref, rule_sequence): do not execute |
1684 | - self._set_not_run(cr, uid, [update.id], log="Cannot execute due to previous not run on the same record/rule.", context=context) |
1685 | - continue |
1686 | + prev_nr_ids = self.search(cr, uid, |
1687 | + [('sdref', '=', update.sdref), |
1688 | + ('is_deleted', '=', False), |
1689 | + ('run', '=', False), |
1690 | + ('rule_sequence', '=', update.rule_sequence), |
1691 | + ('sequence_number', '<', update.sequence_number)]) |
1692 | + # previous not run on the same (sdref, rule_sequence): do not execute |
1693 | + if prev_nr_ids: |
1694 | + if update.rule_sequence in (602, 603): |
1695 | + # update on product state, we don't care of previous NR |
1696 | + self.write(cr, uid, prev_nr_ids, {'run': 't', 'log': 'Set as Run due to a later update on the same record/rule.', 'editable': False, 'execution_date': datetime.now()}, context=context) |
1697 | + else: |
1698 | + self._set_not_run(cr, uid, [update.id], log="Cannot execute due to previous not run on the same record/rule.", context=context) |
1699 | + continue |
1700 | |
1701 | row = eval(update.values) |
1702 |