Merge lp:~jfb-tempo-consulting/unifield-server/US-7646 into lp:unifield-server
- US-7646
- Merge into trunk
Proposed by
jftempo
Status: | Merged |
---|---|
Merged at revision: | 5751 |
Proposed branch: | lp:~jfb-tempo-consulting/unifield-server/US-7646 |
Merge into: | lp:unifield-server |
Diff against target: |
2182 lines (+879/-399) (has conflicts) 21 files modified
bin/addons/consumption_calculation/weekly_forecast_report.py (+46/-46) bin/addons/mission_stock/mission_stock.py (+20/-7) bin/addons/msf_doc_import/purchase_order.py (+6/-5) bin/addons/msf_doc_import/wizard/wizard_po_simulation_screen.py (+39/-18) bin/addons/msf_order_date/wizard/update_lines.py (+7/-1) bin/addons/msf_profile/data/patches.xml (+7/-0) bin/addons/msf_profile/msf_profile.py (+92/-0) bin/addons/procurement_cycle/replenishment.py (+389/-235) bin/addons/procurement_cycle/replenishment_view.xml (+38/-33) bin/addons/procurement_cycle/replenishment_wizard.xml (+2/-2) bin/addons/procurement_cycle/report/replenishment_inventory_review.mako (+9/-9) bin/addons/procurement_cycle/report/replenishment_order_calc.mako (+30/-0) bin/addons/procurement_cycle/report/replenishment_segment.mako (+23/-19) bin/addons/purchase/purchase_order.py (+6/-14) bin/addons/purchase/purchase_order_line.py (+49/-0) bin/addons/purchase/purchase_view.xml (+55/-4) bin/addons/purchase/purchase_workflow.py (+9/-5) bin/addons/stock/product.py (+38/-0) bin/addons/stock/stock_move.py (+9/-0) bin/addons/stock/stock_view.xml (+1/-0) bin/osv/orm.py (+4/-1) Text conflict in bin/addons/msf_profile/data/patches.xml Text conflict in bin/addons/msf_profile/msf_profile.py |
To merge this branch: | bzr merge lp:~jfb-tempo-consulting/unifield-server/US-7646 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
UniField Reviewer Team | Pending | ||
Review via email: mp+386961@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/consumption_calculation/weekly_forecast_report.py' |
2 | --- bin/addons/consumption_calculation/weekly_forecast_report.py 2019-08-14 09:17:27 +0000 |
3 | +++ bin/addons/consumption_calculation/weekly_forecast_report.py 2020-07-27 12:47:56 +0000 |
4 | @@ -414,6 +414,10 @@ |
5 | intervals.append((interval_name, interval_from, interval_to)) |
6 | dict_int_from.setdefault(interval_from.strftime('%Y-%m-%d'), interval_name) |
7 | |
8 | + max_date = fixed_now |
9 | + if intervals: |
10 | + max_date = intervals[-1][2] |
11 | + |
12 | percent_completed = 0.00 |
13 | progress_comment = "" |
14 | product_ids = [] |
15 | @@ -446,7 +450,12 @@ |
16 | |
17 | if len(product_ids) > 0: |
18 | ##### UFTP-220: Filter this list of products for those only appeared in the selected location of the report, not all product |
19 | - new_cr.execute("select distinct product_id from report_stock_inventory where location_id in %s and product_id in %s and state !='cancel'", (tuple(loc_asked_by_user),tuple(product_ids),) ) |
20 | + new_cr.execute(""" |
21 | + select product_id from ( |
22 | + select product_id from report_stock_inventory where location_id in %(loc)s and product_id in %(p_id)s and state !='cancel' group by product_id |
23 | + UNION |
24 | + select product_id from purchase_order_line where location_dest_id in %(loc)s and product_id in %(p_id)s and state in ('validated', 'validated_n', 'sourced_sy', 'sourced_v', 'sourced_n') group by product_id |
25 | + ) x group by product_id """, {'loc': tuple(loc_asked_by_user), 'p_id': tuple(product_ids)} ) |
26 | product_ids = [] |
27 | for row in new_cr.dictfetchall(): |
28 | product_ids.append(row['product_id']) |
29 | @@ -459,7 +468,7 @@ |
30 | while t < nb_products: |
31 | # Get consumption, in-pipe and expired quantities for each product |
32 | product_cons.update(self._get_product_consumption(new_cr, uid, product_ids, location_ids, report, context=context)) |
33 | - in_pipe_vals.update(self._get_in_pipe_vals(new_cr, uid, product_ids, location_ids, report, context=context)) |
34 | + in_pipe_vals.update(self._get_in_pipe_vals(new_cr, uid, product_ids, location_ids, report, max_date, context=context)) |
35 | exp_vals.update(self._get_expiry_batch(new_cr, uid, product_cons, location_ids, report, fixed_now=fixed_now, context=context)) |
36 | |
37 | percent_completed = (t/nb_products) * 100 |
38 | @@ -766,7 +775,7 @@ |
39 | |
40 | return res |
41 | |
42 | - def _get_in_pipe_vals(self, cr, uid, product_ids, location_ids, report, context=None): |
43 | + def _get_in_pipe_vals(self, cr, uid, product_ids, location_ids, report, max_date, context=None): |
44 | """ |
45 | Returns a dictionary with for each product in product_ids, the quantity in-pipe. |
46 | |
47 | @@ -785,14 +794,29 @@ |
48 | context = {} |
49 | |
50 | res = {} |
51 | - |
52 | cr.execute(""" |
53 | - SELECT product_id, sum(qty) AS qty, date |
54 | + SELECT product_id, sum(qty) AS qty, date FROM ( |
55 | + SELECT |
56 | + pol.product_id AS product_id, |
57 | + sum(pol.product_qty/u1.factor/u2.factor) AS qty, |
58 | + coalesce(pol.confirmed_delivery_date, pol.date_planned) AS date |
59 | FROM |
60 | - ((SELECT |
61 | + purchase_order_line pol |
62 | + LEFT JOIN product_product p ON p.id = pol.product_id |
63 | + LEFT JOIN product_template pt ON p.product_tmpl_id = pt.id |
64 | + LEFT JOIN product_uom u1 ON pol.product_uom = u1.id |
65 | + LEFT JOIN product_uom u2 ON pt.uom_id = u2.id |
66 | + WHERE |
67 | + pol.location_dest_id IN %(location_ids)s AND |
68 | + pol.product_id IN %(product_ids)s AND |
69 | + pol.state IN ('validated', 'validated_n', 'sourced_sy', 'sourced_v', 'sourced_n') AND |
70 | + coalesce(pol.confirmed_delivery_date, pol.date_planned) <= %(max_date)s |
71 | + GROUP BY pol.product_id, coalesce(pol.confirmed_delivery_date, pol.date_planned) |
72 | + UNION |
73 | + SELECT |
74 | p.id AS product_id, |
75 | sum(-s.product_qty/u1.factor/u2.factor) AS qty, |
76 | - s.date AS date |
77 | + date(s.date) AS date |
78 | FROM |
79 | stock_move s |
80 | LEFT JOIN product_product p ON p.id = s.product_id |
81 | @@ -800,29 +824,16 @@ |
82 | LEFT JOIN product_uom u1 ON s.product_uom = u1.id |
83 | LEFT JOIN product_uom u2 ON pt.uom_id = u2.id |
84 | WHERE |
85 | - s.location_id IN %(location_ids)s |
86 | - AND |
87 | - s.product_id IN %(product_ids)s |
88 | - AND |
89 | - s.state IN ('assigned', 'confirmed') |
90 | - AND |
91 | - s.id NOT IN |
92 | - (SELECT |
93 | - l.move_dest_id |
94 | - FROM |
95 | - purchase_order_line l |
96 | - LEFT JOIN purchase_order o ON o.id = l.order_id |
97 | - WHERE |
98 | - l.move_dest_id IS NOT NULL |
99 | - AND |
100 | - o.state NOT IN ('approved', 'except_picking', 'except_invoice', 'done') |
101 | - ) |
102 | - GROUP BY p.id, s.date) |
103 | + s.location_id IN %(location_ids)s AND |
104 | + s.product_id IN %(product_ids)s AND |
105 | + s.state IN ('assigned', 'confirmed') AND |
106 | + s.date <= %(max_date)s |
107 | + GROUP BY p.id, date(s.date) |
108 | UNION |
109 | - (SELECT |
110 | + SELECT |
111 | p.id AS product_id, |
112 | sum(s.product_qty/u1.factor/u2.factor) AS qty, |
113 | - s.date AS date |
114 | + date(s.date) AS date |
115 | FROM |
116 | stock_move s |
117 | LEFT JOIN product_product p ON p.id = s.product_id |
118 | @@ -830,29 +841,18 @@ |
119 | LEFT JOIN product_uom u1 ON s.product_uom = u1.id |
120 | LEFT JOIN product_uom u2 ON pt.uom_id = u2.id |
121 | WHERE |
122 | - s.location_dest_id IN %(location_ids)s |
123 | - AND |
124 | - s.product_id IN %(product_ids)s |
125 | - AND |
126 | - s.state IN ('assigned', 'confirmed') |
127 | - AND |
128 | - s.id NOT IN |
129 | - (SELECT |
130 | - l.move_dest_id |
131 | - FROM |
132 | - purchase_order_line l |
133 | - LEFT JOIN purchase_order o ON o.id = l.order_id |
134 | - WHERE |
135 | - l.move_dest_id IS NOT NULL |
136 | - AND |
137 | - o.state NOT IN ('approved', 'except_picking', 'except_invoice', 'done') |
138 | - ) |
139 | - GROUP BY p.id, s.date)) |
140 | + s.location_dest_id IN %(location_ids)s AND |
141 | + s.product_id IN %(product_ids)s AND |
142 | + s.state IN ('assigned', 'confirmed') AND |
143 | + s.date <= %(max_date)s |
144 | + GROUP BY p.id, date(s.date) |
145 | + ) |
146 | AS subrequest |
147 | GROUP BY product_id, date; |
148 | """, { |
149 | 'location_ids': tuple(location_ids), |
150 | - 'product_ids': tuple(product_ids) |
151 | + 'product_ids': tuple(product_ids), |
152 | + 'max_date': max_date.strftime('%Y-%m-%d'), |
153 | }) |
154 | |
155 | for r in cr.dictfetchall(): |
156 | |
157 | === modified file 'bin/addons/mission_stock/mission_stock.py' |
158 | --- bin/addons/mission_stock/mission_stock.py 2020-03-02 07:55:11 +0000 |
159 | +++ bin/addons/mission_stock/mission_stock.py 2020-07-27 12:47:56 +0000 |
160 | @@ -934,13 +934,26 @@ |
161 | |
162 | for report_id in report_ids: |
163 | # In-Pipe moves |
164 | - cr.execute('''SELECT m.product_id, sum(m.product_qty), m.product_uom, p.name |
165 | - FROM stock_move m |
166 | - LEFT JOIN stock_picking s ON m.picking_id = s.id |
167 | - LEFT JOIN res_partner p ON s.partner_id2 = p.id |
168 | - WHERE s.type = 'in' AND m.state in ('confirmed', 'waiting', 'assigned') |
169 | - GROUP BY m.product_id, m.product_uom, p.name |
170 | - ORDER BY m.product_id''') |
171 | + cr.execute(''' |
172 | + SELECT product_id, sum(product_qty), uom_id, p_name |
173 | + FROM ( |
174 | + SELECT pol.product_id as product_id, sum(pol.product_qty) as product_qty, pol.product_uom as uom_id, p.name as p_name |
175 | + FROM purchase_order_line pol, purchase_order po, res_partner p |
176 | + WHERE |
177 | + pol.state in ('validated', 'validated_n', 'sourced_sy', 'sourced_v', 'sourced_n') and |
178 | + po.id = pol.order_id and |
179 | + po.partner_id = p.id |
180 | + GROUP BY pol.product_id, pol.product_uom, p.name |
181 | + UNION |
182 | + SELECT m.product_id as product_id, sum(m.product_qty) as product_qty, m.product_uom as uom_id, p.name as p_name |
183 | + FROM stock_move m |
184 | + LEFT JOIN stock_picking s ON m.picking_id = s.id |
185 | + LEFT JOIN res_partner p ON s.partner_id2 = p.id |
186 | + WHERE |
187 | + s.type = 'in' AND m.state in ('confirmed', 'waiting', 'assigned') |
188 | + GROUP BY m.product_id, m.product_uom, p.name |
189 | + ) x GROUP BY product_id, product_qty, uom_id, p_name |
190 | + ORDER BY product_id''') |
191 | |
192 | in_pipe_moves = cr.fetchall() |
193 | current_product = None |
194 | |
195 | === modified file 'bin/addons/msf_doc_import/purchase_order.py' |
196 | --- bin/addons/msf_doc_import/purchase_order.py 2020-04-21 16:07:23 +0000 |
197 | +++ bin/addons/msf_doc_import/purchase_order.py 2020-07-27 12:47:56 +0000 |
198 | @@ -210,9 +210,9 @@ |
199 | if not po_name: |
200 | raise osv.except_osv(_('Error'), _('No PO name found in the given import file')) |
201 | |
202 | - po_id = self.search(cr, uid, [('name', '=', po_name), ('state', 'in', ['validated', 'validated_p'])], context=context) |
203 | + po_id = self.search(cr, uid, [('name', '=', po_name), ('state', 'in', ['validated', 'validated_p', 'confirmed', 'confirmed_p'])], context=context) |
204 | if not po_id: |
205 | - raise osv.except_osv(_('Error'), _('No validated PO found with the name %s') % po_name) |
206 | + raise osv.except_osv(_('Error'), _('No available PO found with the name %s') % po_name) |
207 | |
208 | return po_id[0] |
209 | |
210 | @@ -348,9 +348,10 @@ |
211 | if pol.line_number in context.get('line_number_to_confirm', []) or \ |
212 | (pol.external_ref and pol.external_ref in context.get('ext_ref_to_confirm', [])): |
213 | try: |
214 | - self.pool.get('purchase.order.line').button_confirmed(cr, uid, [pol.id], context=context) |
215 | - cr.commit() |
216 | - nb_pol_confirmed += 1 |
217 | + if pol.state not in ['confirmed', 'done', 'cancel', 'cancel_r']: |
218 | + self.pool.get('purchase.order.line').button_confirmed(cr, uid, [pol.id], context=context) |
219 | + cr.commit() |
220 | + nb_pol_confirmed += 1 |
221 | except: |
222 | context['rejected_confirmation'] += 1 |
223 | cr.rollback() |
224 | |
225 | === modified file 'bin/addons/msf_doc_import/wizard/wizard_po_simulation_screen.py' |
226 | --- bin/addons/msf_doc_import/wizard/wizard_po_simulation_screen.py 2020-02-17 11:35:43 +0000 |
227 | +++ bin/addons/msf_doc_import/wizard/wizard_po_simulation_screen.py 2020-07-27 12:47:56 +0000 |
228 | @@ -1537,7 +1537,7 @@ |
229 | 'in_ext_ref': fields.char(size=256, string='External Ref.', readonly=True), |
230 | 'type_change': fields.selection([('', ''), ('error', 'Error'), ('new', 'New'), |
231 | ('split', 'Split'), ('del', 'Del'), |
232 | - ('ignore', 'Ignore'), ('warning', 'Warning')], |
233 | + ('ignore', 'Ignore'), ('warning', 'Warning'), ('cdd', 'CDD')], |
234 | string='Change type', readonly=True), |
235 | 'imp_product_id': fields.many2one('product.product', string='Product', |
236 | readonly=True), |
237 | @@ -1615,10 +1615,39 @@ |
238 | self.write(cr, uid, [line.id], {'type_change': 'ignore'}, context=context) |
239 | continue |
240 | |
241 | - if line.po_line_id.state in ('confirmed', 'done'): |
242 | + if line.po_line_id.state == 'done': |
243 | write_vals['type_change'] = 'warning' |
244 | warnings.append(_('PO line has been confirmed and consequently is not editable')) |
245 | |
246 | + # Delivery Confirmed Date |
247 | + dcd_value = values[11] |
248 | + if dcd_value and type(dcd_value) == type(DateTime.now()): |
249 | + write_vals['imp_dcd'] = dcd_value.strftime('%Y-%m-%d') |
250 | + elif dcd_value and isinstance(dcd_value, str): |
251 | + try: |
252 | + time.strptime(dcd_value, '%Y-%m-%d') |
253 | + write_vals['imp_dcd'] = dcd_value |
254 | + except ValueError: |
255 | + err_msg = _('Incorrect date value for field \'Delivery Confirmed Date\'') |
256 | + errors.append(err_msg) |
257 | + write_vals['type_change'] = 'error' |
258 | + elif dcd_value: |
259 | + err_msg = _('Incorrect date value for field \'Delivery Confirmed Date\'') |
260 | + errors.append(err_msg) |
261 | + write_vals['type_change'] = 'error' |
262 | + |
263 | + if line.po_line_id.state == 'confirmed': |
264 | + if write_vals.get('imp_dcd') and write_vals.get('imp_dcd') != line.in_dcd: |
265 | + if self.pool.get('stock.move').search_exists(cr ,uid, [('purchase_line_id', '=', line.po_line_id.id), ('type', '=', 'in'), ('state', '=', 'done')], context=context): |
266 | + write_vals['type_change'] = 'warning' |
267 | + warnings.append(_("IN for line %s has been parially processed, CDD can't be changed") % (line.in_line_number,)) |
268 | + else: |
269 | + write_vals['type_change'] = 'cdd' |
270 | + if not write_vals.get('type_change'): |
271 | + write_vals['type_change'] = 'ignore' |
272 | + self.write(cr, uid, [line.id], write_vals, context=context) |
273 | + continue |
274 | + |
275 | # External Ref. |
276 | write_vals['imp_external_ref'] = values[1] |
277 | pol_ids = None |
278 | @@ -1840,22 +1869,6 @@ |
279 | errors.append(err_msg) |
280 | write_vals['type_change'] = 'error' |
281 | |
282 | - # Delivery Confirmed Date |
283 | - dcd_value = values[11] |
284 | - if dcd_value and type(dcd_value) == type(DateTime.now()): |
285 | - write_vals['imp_dcd'] = dcd_value.strftime('%Y-%m-%d') |
286 | - elif dcd_value and isinstance(dcd_value, str): |
287 | - try: |
288 | - time.strptime(dcd_value, '%Y-%m-%d') |
289 | - write_vals['imp_dcd'] = dcd_value |
290 | - except ValueError: |
291 | - err_msg = _('Incorrect date value for field \'Delivery Confirmed Date\'') |
292 | - errors.append(err_msg) |
293 | - write_vals['type_change'] = 'error' |
294 | - elif dcd_value: |
295 | - err_msg = _('Incorrect date value for field \'Delivery Confirmed Date\'') |
296 | - errors.append(err_msg) |
297 | - write_vals['type_change'] = 'error' |
298 | |
299 | # ESC Confirmed |
300 | if write_vals.get('imp_dcd') and line.simu_id.order_id.partner_type == 'esc': |
301 | @@ -1939,6 +1952,14 @@ |
302 | cr.commit() |
303 | continue |
304 | |
305 | + if line.type_change == 'cdd': |
306 | + line_obj.write(cr, uid, [line.po_line_id.id], {'confirmed_delivery_date': line.imp_dcd}, context=context) |
307 | + in_ids = self.pool.get('stock.move').search(cr ,uid, [('purchase_line_id', '=', line.po_line_id.id), ('type', '=', 'in'), ('state', 'in', ['confirmed', 'assigned'])], context=context) |
308 | + if in_ids: |
309 | + self.pool.get('stock.move').write(cr, uid, in_ids, {'date_expected': line.imp_dcd}, context=context) |
310 | + |
311 | + cr.commit() |
312 | + continue |
313 | line_vals = { |
314 | 'product_id': line.imp_product_id.id, |
315 | 'product_uom': line.imp_uom.id, |
316 | |
317 | === modified file 'bin/addons/msf_order_date/wizard/update_lines.py' |
318 | --- bin/addons/msf_order_date/wizard/update_lines.py 2019-07-22 12:08:20 +0000 |
319 | +++ bin/addons/msf_order_date/wizard/update_lines.py 2020-07-27 12:47:56 +0000 |
320 | @@ -53,6 +53,8 @@ |
321 | |
322 | for obj in obj_obj.browse(cr, uid, obj_ids, context=context): |
323 | delivery_requested_date = obj.delivery_requested_date |
324 | + if type == 'purchase.order' and obj.state != 'draft': |
325 | + delivery_requested_date = obj.delivery_requested_date_modified |
326 | delivery_confirmed_date = obj.delivery_confirmed_date |
327 | stock_take_date = obj.stock_take_date |
328 | |
329 | @@ -153,13 +155,17 @@ |
330 | # working objects |
331 | obj_ids = context.get('active_ids', []) |
332 | |
333 | + ftf = ['delivery_requested_date'] |
334 | if obj_type == 'purchase.order': |
335 | line_obj = self.pool.get('purchase.order.line') |
336 | + ftf += ['state', 'delivery_requested_date_modified'] |
337 | else: |
338 | line_obj = self.pool.get('sale.order.line') |
339 | |
340 | - for obj in obj_obj.browse(cr, uid, obj_ids, fields_to_fetch=['delivery_requested_date'], context=context): |
341 | + for obj in obj_obj.browse(cr, uid, obj_ids, fields_to_fetch=ftf, context=context): |
342 | requested_date = obj.delivery_requested_date |
343 | + if obj_type == 'purchase.order' and obj.state != 'draft': |
344 | + requested_date = obj.delivery_requested_date_modified |
345 | dom = [('order_id', '=', obj.id), ('state', 'in',['draft', 'validated', 'validated_n'])] |
346 | if selected and context.get('button_selected_ids'): |
347 | dom += [('id', 'in', context['button_selected_ids'])] |
348 | |
349 | === modified file 'bin/addons/msf_profile/data/patches.xml' |
350 | --- bin/addons/msf_profile/data/patches.xml 2020-07-24 12:56:20 +0000 |
351 | +++ bin/addons/msf_profile/data/patches.xml 2020-07-27 12:47:56 +0000 |
352 | @@ -564,7 +564,14 @@ |
353 | <field name="method">sync_msg_from_itself</field> |
354 | </record> |
355 | |
356 | +<<<<<<< TREE |
357 | <!-- UF18.0 --> |
358 | +======= |
359 | + <record id="us_7646_dest_loc_on_pol" model="patch.scripts"> |
360 | + <field name="method">us_7646_dest_loc_on_pol</field> |
361 | + </record> |
362 | + |
363 | +>>>>>>> MERGE-SOURCE |
364 | <record id="us_6544_no_sync_on_forced_out" model="patch.scripts"> |
365 | <field name="method">us_6544_no_sync_on_forced_out</field> |
366 | </record> |
367 | |
368 | === modified file 'bin/addons/msf_profile/msf_profile.py' |
369 | --- bin/addons/msf_profile/msf_profile.py 2020-07-24 12:56:20 +0000 |
370 | +++ bin/addons/msf_profile/msf_profile.py 2020-07-27 12:47:56 +0000 |
371 | @@ -53,6 +53,7 @@ |
372 | } |
373 | |
374 | # UF18.0 |
375 | +<<<<<<< TREE |
376 | def us_7412_set_fy_closure_settings(self, cr, uid, *a, **b): |
377 | """ |
378 | Sets the Fiscal Year Closure options depending on the OC. |
379 | @@ -73,6 +74,97 @@ |
380 | SET has_move_regular_bs_to_0 = %s, has_book_pl_results = %s; |
381 | """ |
382 | cr.execute(update_company, (has_move_regular_bs_to_0, has_book_pl_results)) |
383 | +======= |
384 | + def us_7646_dest_loc_on_pol(self, cr, uid, *a, **b): |
385 | + data_obj = self.pool.get('ir.model.data') |
386 | + try: |
387 | + service_id = self.pool.get('stock.location').get_service_location(cr, uid) |
388 | + conso_id = data_obj.get_object_reference(cr, uid, 'stock_override', 'stock_location_non_stockable')[1] |
389 | + log_id = data_obj.get_object_reference(cr, uid, 'stock_override', 'stock_location_logistic')[1] |
390 | + med_id = data_obj.get_object_reference(cr, uid, 'msf_config_locations', 'stock_location_medical')[1] |
391 | + cross_dock_id = data_obj.get_object_reference(cr, uid, 'msf_cross_docking', 'stock_location_cross_docking')[1] |
392 | + except: |
393 | + return True |
394 | + |
395 | + cr.execute('''update purchase_order_line set location_dest_id = %s where id in ( |
396 | + select pol.id |
397 | + from purchase_order_line pol, purchase_order po, product_product prod, product_template tmpl |
398 | + where |
399 | + pol.order_id = po.id and |
400 | + pol.product_id = prod.id and |
401 | + tmpl.id = prod.product_tmpl_id and |
402 | + tmpl.type = 'service_recep' and |
403 | + coalesce(po.cross_docking_ok, 'f') = 'f' and |
404 | + pol.state in ('validated', 'validated_n', 'sourced_sy', 'sourced_v', 'sourced_n') |
405 | + ) ''', (service_id,)) |
406 | + self._logger.warn('POL loc_dest service on %s lines' % (cr.rowcount,)) |
407 | + |
408 | + cr.execute('''update purchase_order_line set location_dest_id = %s where id in ( |
409 | + select pol.id |
410 | + from purchase_order_line pol, purchase_order po, product_product prod, product_template tmpl |
411 | + where |
412 | + pol.order_id = po.id and |
413 | + pol.product_id = prod.id and |
414 | + tmpl.id = prod.product_tmpl_id and |
415 | + tmpl.type = 'consu' and |
416 | + pol.state in ('validated', 'validated_n', 'sourced_sy', 'sourced_v', 'sourced_n') |
417 | + ) ''', (conso_id,)) |
418 | + self._logger.warn('POL loc_dest conso on %s lines' % (cr.rowcount,)) |
419 | + |
420 | + cr.execute('''update purchase_order_line set location_dest_id = so.location_requestor_id |
421 | + from sale_order so, sale_order_line sol, stock_location loc, product_product prod, product_template tmpl |
422 | + where |
423 | + so.id = sol.order_id and |
424 | + loc.id = so.location_requestor_id and |
425 | + loc.usage != 'customer' and |
426 | + so.procurement_request = 't' and |
427 | + purchase_order_line.linked_sol_id = sol.id and |
428 | + purchase_order_line.product_id = prod.id and |
429 | + tmpl.id = prod.product_tmpl_id and |
430 | + tmpl.type != 'consu' and |
431 | + purchase_order_line.state in ('validated', 'validated_n', 'sourced_sy', 'sourced_v', 'sourced_n') ''', (conso_id,)) |
432 | + self._logger.warn('POL loc_dest IR on %s lines' % (cr.rowcount,)) |
433 | + |
434 | + cr.execute('''update purchase_order_line set location_dest_id = %s from purchase_order po |
435 | + where |
436 | + po.id = purchase_order_line.order_id and |
437 | + purchase_order_line.state in ('validated', 'validated_n', 'sourced_sy', 'sourced_v', 'sourced_n') and |
438 | + coalesce(po.cross_docking_ok, 'f') = 't' and |
439 | + location_dest_id is NULL ''', (cross_dock_id,)) |
440 | + self._logger.warn('POL loc_dest Cross Dock on %s lines' % (cr.rowcount,)) |
441 | + |
442 | + cr.execute('''update purchase_order_line set location_dest_id = %s |
443 | + from product_product prod, product_template tmpl, product_nomenclature nom |
444 | + where |
445 | + purchase_order_line.product_id = prod.id and |
446 | + tmpl.nomen_manda_0 = nom.id and |
447 | + nom.name = 'MED' and |
448 | + tmpl.id = prod.product_tmpl_id and |
449 | + location_dest_id is NULL and |
450 | + purchase_order_line.state in ('validated', 'validated_n', 'sourced_sy', 'sourced_v', 'sourced_n') ''', (med_id,)) |
451 | + self._logger.warn('POL loc_dest MED on %s lines' % (cr.rowcount,)) |
452 | + |
453 | + cr.execute('''update purchase_order_line set location_dest_id = %s |
454 | + from product_product prod, product_template tmpl, product_nomenclature nom |
455 | + where |
456 | + purchase_order_line.product_id = prod.id and |
457 | + tmpl.nomen_manda_0 = nom.id and |
458 | + nom.name = 'LOG' and |
459 | + tmpl.id = prod.product_tmpl_id and |
460 | + location_dest_id is NULL and |
461 | + purchase_order_line.state in ('validated', 'validated_n', 'sourced_sy', 'sourced_v', 'sourced_n') ''', (log_id,)) |
462 | + self._logger.warn('POL loc_dest LOG on %s lines' % (cr.rowcount,)) |
463 | + return True |
464 | + |
465 | + |
466 | + def sync_msg_from_itself(self, cr, uid, *a, **b): |
467 | + instance = self.pool.get('res.users').browse(cr, uid, uid, fields_to_fetch=['company_id']).company_id.instance_id |
468 | + if not instance: |
469 | + return True |
470 | + cr.execute(''' update sync_client_message_received set run='t', manually_ran='t', log='Set manually to run without execution', manually_set_run_date=now() where run='f' and source=%s ''', (instance.instance, )) |
471 | + self._logger.warn('Set %s self sync messages as Run' % (cr.rowcount,)) |
472 | + return True |
473 | +>>>>>>> MERGE-SOURCE |
474 | |
475 | |
476 | def us_6544_no_sync_on_forced_out(self, cr, uid, *a, **b): |
477 | |
478 | === modified file 'bin/addons/procurement_cycle/replenishment.py' |
479 | --- bin/addons/procurement_cycle/replenishment.py 2020-06-09 09:28:55 +0000 |
480 | +++ bin/addons/procurement_cycle/replenishment.py 2020-07-27 12:47:56 +0000 |
481 | @@ -15,6 +15,7 @@ |
482 | import decimal_precision as dp |
483 | import math |
484 | |
485 | +life_cycle_status = [('active', _('Active')), ('new', _('New')), ('replaced', _('Replaced')), ('replacing', _('Replacing')), ('phasingout', _('Phasing Out')), ('activereplacing', _('Active-Replacing'))] |
486 | class replenishment_location_config(osv.osv): |
487 | _name = 'replenishment.location.config' |
488 | _description = 'Location Configuration' |
489 | @@ -387,7 +388,7 @@ |
490 | for prod in cr.fetchall(): |
491 | self.pool.get('replenishment.segment.line').create(cr, uid, {'state': 'active', 'product_id': prod[0], 'segment_id': hidden_seg}, context=context) |
492 | |
493 | - # in pipe at coo / only project |
494 | + # move in pipe at coo / only project |
495 | cr.execute(''' |
496 | select move.product_id from |
497 | stock_move move, stock_picking p |
498 | @@ -410,6 +411,27 @@ |
499 | for prod in cr.fetchall(): |
500 | self.pool.get('replenishment.segment.line').create(cr, uid, {'state': 'active', 'product_id': prod[0], 'segment_id': hidden_seg}, context=context) |
501 | |
502 | + # PO lines |
503 | + cr.execute(''' |
504 | + select pol.product_id from |
505 | + purchase_order_line pol |
506 | + where |
507 | + pol.location_dest_id in %s and |
508 | + pol.state in ('validated', 'validated_n', 'sourced_sy', 'sourced_v', 'sourced_n') and |
509 | + pol.product_id not in ( |
510 | + select |
511 | + seg_line.product_id |
512 | + from replenishment_segment_line seg_line, replenishment_segment seg |
513 | + where |
514 | + seg.state in ('draft', 'complete') and seg_line.segment_id = seg.id and seg.location_config_id = %s |
515 | + |
516 | + ) |
517 | + group by pol.product_id |
518 | + ''', (tuple(amc_location_ids), loc_config.id)) |
519 | + |
520 | + for prod in cr.fetchall(): |
521 | + self.pool.get('replenishment.segment.line').create(cr, uid, {'state': 'active', 'product_id': prod[0], 'segment_id': hidden_seg}, context=context) |
522 | + |
523 | return True |
524 | |
525 | replenishment_location_config() |
526 | @@ -693,15 +715,28 @@ |
527 | |
528 | loc_ids = [x.id for x in seg.local_location_ids] |
529 | cr.execute(''' |
530 | - select l.product_id, min(m.date) from stock_move m, stock_picking p, replenishment_segment_line l |
531 | + select prod_id, min(date) from ( |
532 | + select pol.product_id as prod_id, min(coalesce(pol.confirmed_delivery_date, pol.date_planned)) as date |
533 | + from |
534 | + purchase_order_line pol, replenishment_segment_line l |
535 | + where |
536 | + l.product_id = pol.product_id and |
537 | + l.segment_id = %(seg_id)s and |
538 | + pol.state in ('validated', 'validated_n', 'sourced_sy', 'sourced_v', 'sourced_n') and |
539 | + location_dest_id in %(location_id)s |
540 | + group by pol.product_id |
541 | + UNION |
542 | + select l.product_id as prod_id, min(m.date) as date from stock_move m, stock_picking p, replenishment_segment_line l |
543 | where |
544 | m.picking_id = p.id and |
545 | m.state in ('assigned', 'confirmed') and |
546 | - m.location_dest_id in %s and |
547 | + m.location_dest_id in %(location_id)s and |
548 | l.product_id = m.product_id and |
549 | - l.segment_id = %s |
550 | + l.segment_id = %(seg_id)s |
551 | group by l.product_id |
552 | - ''', (tuple(loc_ids), seg.id) |
553 | + ) x |
554 | + group by prod_id |
555 | + ''', {'location_id': tuple(loc_ids), 'seg_id': seg.id} |
556 | ) |
557 | prod_eta = {} |
558 | for x in cr.fetchall(): |
559 | @@ -810,6 +845,7 @@ |
560 | total_month_oc = 0 |
561 | |
562 | valid_rr_fmc = True |
563 | + valid_line = True |
564 | before_today = False |
565 | before_oc = False |
566 | before_rdd = False |
567 | @@ -823,15 +859,29 @@ |
568 | if seg.rule == 'cycle': |
569 | |
570 | cr.execute(''' |
571 | - select m.date, sum(product_qty) from stock_move m, stock_picking p |
572 | + select date, sum(qty) from ( |
573 | + select coalesce(pol.confirmed_delivery_date, pol.date_planned) as date, sum(pol.product_qty) as qty |
574 | + from |
575 | + purchase_order_line pol |
576 | + where |
577 | + pol.product_id=%(product_id)s and |
578 | + pol.state in ('validated', 'validated_n', 'sourced_sy', 'sourced_v', 'sourced_n') and |
579 | + location_dest_id in %(location_id)s and |
580 | + coalesce(pol.confirmed_delivery_date, pol.date_planned) <= %(date)s |
581 | + group by coalesce(pol.confirmed_delivery_date, pol.date_planned) |
582 | + UNION |
583 | + |
584 | + select date(m.date) as date, sum(product_qty) as product_qty from stock_move m, stock_picking p |
585 | where |
586 | m.picking_id = p.id and |
587 | m.state in ('assigned', 'confirmed') and |
588 | - m.location_dest_id in %s and |
589 | - m.product_id = %s and |
590 | - m.date <= %s |
591 | - group by m.date |
592 | - ''', (tuple(loc_ids), line.product_id.id, oc) |
593 | + m.location_dest_id in %(location_id)s and |
594 | + m.product_id = %(product_id)s and |
595 | + m.date <= %(date)s |
596 | + group by date(m.date) |
597 | + ) x |
598 | + group by date |
599 | + ''', {'location_id': tuple(loc_ids), 'product_id': line.product_id.id, 'date': oc} |
600 | ) |
601 | pipe_data = {} |
602 | for x in cr.fetchall(): |
603 | @@ -967,6 +1017,8 @@ |
604 | else: |
605 | valid_rr_fmc = before_today and before_rdd |
606 | |
607 | + valid_line = valid_rr_fmc |
608 | + |
609 | if review_id and loc_ids and seg.rule == 'cycle': |
610 | total_expired_qty = sum_line[line.id].get('expired_rdd_oc', 0) + sum_line[line.id].get('expired_before_rdd', 0) |
611 | for nb_month in range(1, line.segment_id.projected_view+1): |
612 | @@ -996,19 +1048,31 @@ |
613 | # sum fmc from today to ETC - qty in stock |
614 | #qty_lacking = max(0, total_fmc - sum_line.get(line.id, {}).get('pas_no_pipe_no_fmc', 0)) |
615 | if total_month_oc+total_month: |
616 | - ss_stock = seg.safety_stock * ((total_fmc_oc+total_fmc)/(total_month_oc+total_month)) |
617 | - if total_month and pas and pas <= line.buffer_qty + seg.safety_stock * (total_fmc / total_month): |
618 | - wmsg = _('Projected use of safety stock/buffer') |
619 | - warnings.append(wmsg) |
620 | - warnings_html.append('<span title="%s">%s</span>' % (misc.escape_html(wmsg), misc.escape_html(_('SS used')))) |
621 | - if qty_lacking: |
622 | - wmsg = _('Stock-out before next RDD') |
623 | - warnings.append(wmsg) |
624 | - warnings_html.append('<span title="%s">%s</span>' % (misc.escape_html(wmsg), misc.escape_html(_('Stock out')))) |
625 | + if line.status == 'replacing': |
626 | + ss_stock = seg.safety_stock * ((total_fmc_oc+total_fmc)/(line.segment_id.order_coverage+int(line.segment_id.total_lt)/30.44)) |
627 | + else: |
628 | + ss_stock = seg.safety_stock * ((total_fmc_oc+total_fmc)/(total_month_oc+total_month)) |
629 | + |
630 | + if line.status != 'phasingout': |
631 | + if total_month and pas and pas <= line.buffer_qty + seg.safety_stock * (total_fmc / total_month): |
632 | + wmsg = _('Projected use of safety stock/buffer') |
633 | + warnings.append(wmsg) |
634 | + warnings_html.append('<span title="%s">%s</span>' % (misc.escape_html(wmsg), misc.escape_html(_('SS used')))) |
635 | + if qty_lacking: |
636 | + wmsg = _('Stock-out before next RDD') |
637 | + warnings.append(wmsg) |
638 | + warnings_html.append('<span title="%s">%s</span>' % (misc.escape_html(wmsg), misc.escape_html(_('Stock out')))) |
639 | + |
640 | + if line.status == 'activereplacing': |
641 | + replaced_lack = lacking_by_prod.get(line.replaced_product_id.id) |
642 | + if replaced_lack: |
643 | + wmsg = _('SODate of linked products is %s') % (self.pool.get('date.tools').get_date_formatted(cr, uid, datetime=replaced_lack.strftime('%Y-%m-%d'), context=context)) |
644 | + warnings.append(wmsg) |
645 | + warnings_html.append('<span title="%s">%s</span>' % (misc.escape_html(wmsg), misc.escape_html(_('Replaced SO')))) |
646 | |
647 | if lacking: |
648 | qty_lacking_needed_by = today + relativedelta(days=month_of_supply*30.44) |
649 | - if review_id and round(sum_line.get(line.id, {}).get('expired_before_rdd',0)): |
650 | + if line.status != 'phasingout' and review_id and round(sum_line.get(line.id, {}).get('expired_before_rdd',0)): |
651 | wmsg = _('Forecasted expiries') |
652 | warnings.append(wmsg) |
653 | warnings_html.append('<span title="%s">%s</span>' % (misc.escape_html(wmsg), misc.escape_html(_('Expiries')))) |
654 | @@ -1016,39 +1080,57 @@ |
655 | if line.status == 'replaced': |
656 | proposed_order_qty = 0 |
657 | qty_lacking = 0 |
658 | + elif line.status == 'phasingout': |
659 | + proposed_order_qty = 0 |
660 | + qty_lacking = False |
661 | else: |
662 | proposed_order_qty = max(0, total_fmc_oc + ss_stock + line.buffer_qty + sum_line.get(line.id, {}).get('expired_rdd_oc',0) - pas - line.pipeline_between_rdd_oc) |
663 | |
664 | elif seg.rule == 'minmax': |
665 | - proposed_order_qty = max(0, line.max_qty - sum_line.get(line.id, {}).get('real_stock') + sum_line.get(line.id, {}).get('reserved_stock_qty') + sum_line.get(line.id, {}).get('expired_qty_before_eta', 0) - line.pipeline_before_rdd) |
666 | + valid_line = bool(line.min_qty) and bool(line.max_qty) |
667 | + if line.status in ('phasingout', 'replaced'): |
668 | + proposed_order_qty = 0 |
669 | + qty_lacking = False |
670 | + else: |
671 | + proposed_order_qty = max(0, line.max_qty - sum_line.get(line.id, {}).get('real_stock') + sum_line.get(line.id, {}).get('reserved_stock_qty') + sum_line.get(line.id, {}).get('expired_qty_before_eta', 0) - line.pipeline_before_rdd) |
672 | |
673 | - if line.status != 'new' and sum_line.get(line.id, {}).get('real_stock') - sum_line.get(line.id, {}).get('expired_qty_before_eta') <= line.min_qty: |
674 | - if sum_line.get(line.id, {}).get('expired_qty_before_eta'): |
675 | - wmsg = _('Alert: "inventory – batches expiring before ETA <= Min"') |
676 | - warnings.append(wmsg) |
677 | - warnings_html.append('<span title="%s">%s</span>' % (misc.escape_html(wmsg), misc.escape_html(_('Expiries')))) |
678 | - else: |
679 | - wmsg = _('Alert: "inventory <= Min"') |
680 | - warnings.append(wmsg) |
681 | - warnings_html.append('<span title="%s">%s</span>' % (misc.escape_html(wmsg), misc.escape_html(_('Insufficient')))) |
682 | + qty_lacking = line.min_qty - sum_line.get(line.id, {}).get('real_stock') + sum_line.get(line.id, {}).get('reserved_stock_qty') - sum_line.get(line.id, {}).get('expired_qty_before_eta') |
683 | + if line.status != 'new' and sum_line.get(line.id, {}).get('real_stock') - sum_line.get(line.id, {}).get('expired_qty_before_eta') <= line.min_qty: |
684 | + if sum_line.get(line.id, {}).get('expired_qty_before_eta'): |
685 | + wmsg = _('Alert: "inventory – batches expiring before ETA <= Min"') |
686 | + warnings.append(wmsg) |
687 | + warnings_html.append('<span title="%s">%s</span>' % (misc.escape_html(wmsg), misc.escape_html(_('Expiries')))) |
688 | + else: |
689 | + wmsg = _('Alert: "inventory <= Min"') |
690 | + warnings.append(wmsg) |
691 | + warnings_html.append('<span title="%s">%s</span>' % (misc.escape_html(wmsg), misc.escape_html(_('Insufficient')))) |
692 | else: |
693 | - proposed_order_qty = line.auto_qty |
694 | + valid_line = bool(line.auto_qty) |
695 | + if line.status in ('phasingout', 'replaced'): |
696 | + proposed_order_qty = 0 |
697 | + else: |
698 | + proposed_order_qty = line.auto_qty |
699 | |
700 | if not valid_rr_fmc: |
701 | wmsg = _('Invalid FMC') |
702 | warnings.append(wmsg) |
703 | warnings_html.append('<span title="%s">%s</span>' % (misc.escape_html(wmsg), misc.escape_html(_('FMC')))) |
704 | |
705 | - if review_id and month_of_supply and month_of_supply*30.44 > (seg_rdd-today).days + line.segment_id.safety_stock*30.44: |
706 | - wmsg = _('Excess Stock') |
707 | - warnings.append(wmsg) |
708 | - warnings_html.append('<span title="%s">%s</span>' % (misc.escape_html(wmsg), misc.escape_html(_('Excess')))) |
709 | - |
710 | - if review_id and seg.hidden: |
711 | - wmsg = _('Product is not in any related segment, only in stock / pipeline of location') |
712 | - warnings.append(wmsg) |
713 | - warnings_html.append('<span title="%s">%s</span>' % (misc.escape_html(wmsg), misc.escape_html(_('No Segment')))) |
714 | - |
715 | + if line.status != 'phasingout': |
716 | + if review_id and month_of_supply and month_of_supply*30.44 > (seg_rdd-today).days + line.segment_id.safety_stock*30.44: |
717 | + wmsg = _('Excess Stock') |
718 | + warnings.append(wmsg) |
719 | + warnings_html.append('<span title="%s">%s</span>' % (misc.escape_html(wmsg), misc.escape_html(_('Excess')))) |
720 | + |
721 | + if review_id and seg.hidden: |
722 | + wmsg = _('Product is not in any related segment, only in stock / pipeline of location') |
723 | + warnings.append(wmsg) |
724 | + warnings_html.append('<span title="%s">%s</span>' % (misc.escape_html(wmsg), misc.escape_html(_('No Segment')))) |
725 | + |
726 | + if prod_eta.get(line.product_id.id) and prod_eta.get(line.product_id.id) < time.strftime('%Y-%m-%d'): |
727 | + wmsg = _('Pipeline in the past') |
728 | + warnings.append(wmsg) |
729 | + warnings_html.append('<span title="%s">%s</span>' % (misc.escape_html(wmsg), misc.escape_html(_('Delay')))) |
730 | |
731 | #lacking_by_prod[line.product_id.id] = qty_lacking_needed_by |
732 | line_data = { |
733 | @@ -1058,17 +1140,22 @@ |
734 | 'pipeline_qty': round(line.pipeline_before_rdd or 0), |
735 | 'eta_for_next_pipeline': prod_eta.get(line.product_id.id, False), |
736 | 'reserved_stock_qty': sum_line.get(line.id, {}).get('reserved_stock_qty'), |
737 | - 'qty_lacking': False if seg.rule !='cycle' else round(qty_lacking), |
738 | + 'qty_lacking': False if seg.rule not in ('cycle', 'minmax') else round(qty_lacking), |
739 | 'qty_lacking_needed_by': qty_lacking_needed_by and qty_lacking_needed_by.strftime('%Y-%m-%d') or False, |
740 | 'expired_qty_before_cons': False if seg.rule !='cycle' else round(sum_line.get(line.id, {}).get('expired_before_rdd',0)), |
741 | 'expired_qty_before_eta': round(sum_line.get(line.id, {}).get('expired_qty_before_eta',0)), |
742 | 'warning': False, |
743 | 'warning_html': False, |
744 | - 'valid_rr_fmc': valid_rr_fmc, |
745 | + 'valid_rr_fmc': valid_line, |
746 | 'status': line.status, |
747 | 'open_loan': sum_line.get(line.id, {}).get('open_loan', False), |
748 | 'open_donation': sum_line.get(line.id, {}).get('open_donation', False), |
749 | + 'auto_qty': line.auto_qty if seg.rule =='auto' else False, |
750 | + 'buffer_qty': line.buffer_qty if seg.rule =='cycle' else False, |
751 | + 'min_max': '', |
752 | } |
753 | + if seg.rule == 'minmax': |
754 | + line_data['min_max'] = '%d / %d' % (line.min_qty, line.max_qty) |
755 | |
756 | # order_cacl |
757 | if not review_id: |
758 | @@ -1083,11 +1170,13 @@ |
759 | 'projected_stock_qty': round(pas), |
760 | 'cost_price': line.product_id.standard_price, |
761 | }) |
762 | + |
763 | order_calc_line.create(cr, uid, line_data, context=context) |
764 | |
765 | else: # review |
766 | if seg.hidden: |
767 | line_data['valid_rr_fmc'] = False |
768 | + line_data['paired_product_id'] = line.replacing_product_id and line.replacing_product_id.id or line.replaced_product_id and line.replaced_product_id.id |
769 | std_dev_hmc = False |
770 | amc = False |
771 | if total_fmc_hmc.get(line.product_id.id): |
772 | @@ -1138,8 +1227,6 @@ |
773 | 'rule': seg.rule, |
774 | 'min_qty': line.min_qty, |
775 | 'max_qty': line.max_qty, |
776 | - 'auto_qty': line.auto_qty, |
777 | - 'buffer_qty': line.buffer_qty, |
778 | 'safety_stock': seg.safety_stock * coeff, |
779 | 'pas_ids': detailed_pas, |
780 | 'segment_line_id': line.id, |
781 | @@ -1251,164 +1338,178 @@ |
782 | return True |
783 | |
784 | def import_lines(self, cr, uid, ids, context=None): |
785 | + ''' import replenishment.segment ''' |
786 | + |
787 | product_obj = self.pool.get('product.product') |
788 | seg_line_obj = self.pool.get('replenishment.segment.line') |
789 | + wizard_obj = self.pool.get('physical.inventory.import.wizard') |
790 | |
791 | seg = self.browse(cr, uid, ids[0], context=context) |
792 | if not seg.file_to_import: |
793 | raise osv.except_osv(_('Error'), _('Nothing to import.')) |
794 | - file_data = SpreadsheetXML(xmlstring=base64.decodestring(seg.file_to_import)) |
795 | - |
796 | - existing_line = {} |
797 | - for line in seg.line_ids: |
798 | - existing_line[line.product_id.default_code] =line.id |
799 | - |
800 | - idx = -1 |
801 | - |
802 | - status = { |
803 | - _('Active'): 'active', |
804 | - _('New'): 'new', |
805 | - _('Replaced'): 'replaced', |
806 | - _('Replacing'): 'replacing', |
807 | - } |
808 | - error = [] |
809 | - code_created = {} |
810 | - created = 0 |
811 | - updated = 0 |
812 | - ignored = 0 |
813 | - for row in file_data.getRows(): |
814 | - idx += 1 |
815 | - if idx < 8: |
816 | - # header |
817 | - continue |
818 | - line_error = [] |
819 | - prod_code = row.cells[0].data |
820 | - if not prod_code: |
821 | - continue |
822 | - prod_code = prod_code.strip() |
823 | - |
824 | - cells_nb = len(row.cells) |
825 | - |
826 | - data_towrite = { |
827 | - 'status': cells_nb > 3 and status.get(row.cells[3].data and row.cells[3].data.strip()), |
828 | - 'replacing_product_id': False, |
829 | - 'replaced_product_id': False, |
830 | - 'buffer_qty': False, |
831 | - 'min_qty': 0, |
832 | - 'max_qty': 0, |
833 | - 'auto_qty': 0 |
834 | - } |
835 | - for fmc in range(1, 13): |
836 | - data_towrite.update({ |
837 | - 'rr_fmc_%d' % fmc: False, |
838 | - 'rr_fmc_from_%d' % fmc: False, |
839 | - 'rr_fmc_to_%d' % fmc: False, |
840 | - }) |
841 | - |
842 | - |
843 | - col_replacing = 4 |
844 | - col_replaced = 5 |
845 | - col_buffer_min_qty = 8 |
846 | - col_first_fmc = 9 |
847 | - |
848 | - if cells_nb > col_replacing and row.cells[col_replacing].data and row.cells[col_replacing].data.strip(): |
849 | - if data_towrite['status'] != 'replaced': |
850 | - line_error.append(_('Line %d: you can not set a Replacing product on this line, please change the satus or remove the replacing product') % (idx+1, )) |
851 | - else: |
852 | - replacing_id = product_obj.search(cr, uid, [('default_code', '=ilike', row.cells[col_replacing].data.strip())], context=context) |
853 | - if not replacing_id: |
854 | - line_error.append(_('Line %d: replacing product code %s not found') % (idx+1, row.cells[col_replacing].data)) |
855 | - else: |
856 | - data_towrite['replacing_product_id'] = replacing_id[0] |
857 | - elif data_towrite['status'] == 'replaced' and not data_towrite['replacing_product_id']: |
858 | - line_error.append(_('Line %d: replacing product must be set !') % (idx+1, )) |
859 | - |
860 | - if cells_nb > col_replaced and row.cells[col_replaced].data and row.cells[col_replaced].data.strip(): |
861 | - if data_towrite['status'] != 'replacing': |
862 | - line_error.append(_('Line %d: you can not set a Replaced product on this line, please change the satus or remove the replaced product') % (idx+1, )) |
863 | - else: |
864 | - replaced_id = product_obj.search(cr, uid, [('default_code', '=ilike', row.cells[col_replaced].data.strip())], context=context) |
865 | - if not replaced_id: |
866 | - line_error.append(_('Line %d: replaced product code %s not found') % (idx+1, row.cells[col_replaced].data)) |
867 | - else: |
868 | - data_towrite['replaced_product_id'] = replaced_id[0] |
869 | - elif data_towrite['status'] == 'replacing' and not data_towrite['replaced_product_id']: |
870 | - line_error.append(_('Line %d: replaced product must be set !') % (idx+1, )) |
871 | - |
872 | - |
873 | - if cells_nb > col_buffer_min_qty and seg.rule == 'cycle': |
874 | - if row.cells[col_buffer_min_qty].data and not isinstance(row.cells[col_buffer_min_qty].data, (int, long, float)): |
875 | - line_error.append(_('Line %d: Buffer Qty must be a number, found %s') % (idx+1, row.cells[col_buffer_min_qty].data)) |
876 | - else: |
877 | - data_towrite['buffer_qty'] = row.cells[col_buffer_min_qty].data |
878 | + |
879 | + try: |
880 | + file_data = SpreadsheetXML(xmlstring=base64.decodestring(seg.file_to_import)) |
881 | + |
882 | + existing_line = {} |
883 | + for line in seg.line_ids: |
884 | + existing_line[line.product_id.default_code] = line.id |
885 | + |
886 | + idx = -1 |
887 | + |
888 | + status = dict([(x[1], x[0]) for x in life_cycle_status]) |
889 | + error = [] |
890 | + code_created = {} |
891 | + created = 0 |
892 | + updated = 0 |
893 | + ignored = 0 |
894 | + for row in file_data.getRows(): |
895 | + idx += 1 |
896 | + if idx < 8: |
897 | + # header |
898 | + continue |
899 | + |
900 | + if not len(row.cells): |
901 | + # empty line |
902 | + continue |
903 | + |
904 | + line_error = [] |
905 | + prod_code = row.cells[0].data |
906 | + if not prod_code: |
907 | + continue |
908 | + prod_code = prod_code.strip() |
909 | + |
910 | + cells_nb = len(row.cells) |
911 | + |
912 | + data_towrite = { |
913 | + 'status': cells_nb > 3 and status.get(row.cells[3].data and row.cells[3].data.strip()), |
914 | + 'replacing_product_id': False, |
915 | + 'replaced_product_id': False, |
916 | + 'buffer_qty': False, |
917 | + 'min_qty': 0, |
918 | + 'max_qty': 0, |
919 | + 'auto_qty': 0 |
920 | + } |
921 | for fmc in range(1, 13): |
922 | - if cells_nb - 1 >= col_first_fmc and row.cells[col_first_fmc].data: |
923 | - if cells_nb - 1 < col_first_fmc+1: |
924 | - line_error.append(_('Line %d: FMC FROM %d, date expected') % (idx+1, fmc)) |
925 | - continue |
926 | - if not row.cells[col_first_fmc+1].type == 'datetime': |
927 | - line_error.append(_('Line %d: FMC FROM %d, date is not valid, found %s') % (idx+1, fmc, row.cells[col_first_fmc+1].data)) |
928 | - continue |
929 | - if cells_nb - 1 < col_first_fmc+2: |
930 | - line_error.append(_('Line %d: FMC TO %d, date expected') % (idx+1, fmc)) |
931 | - continue |
932 | - if not row.cells[col_first_fmc+2].data or row.cells[col_first_fmc+2].type != 'datetime': |
933 | - line_error.append(_('Line %d: FMC TO %d, date is not valid, found %s') % (idx+1, fmc, row.cells[col_first_fmc+2].data)) |
934 | - continue |
935 | - if not isinstance(row.cells[col_first_fmc].data, (int, long, float)): |
936 | - line_error.append(_('Line %d: FMC %d, number expected, found %s') % (idx+1, fmc, row.cells[col_first_fmc].data)) |
937 | - continue |
938 | + data_towrite.update({ |
939 | + 'rr_fmc_%d' % fmc: False, |
940 | + 'rr_fmc_from_%d' % fmc: False, |
941 | + 'rr_fmc_to_%d' % fmc: False, |
942 | + }) |
943 | + |
944 | + |
945 | + col_replacing = 4 |
946 | + col_replaced = 5 |
947 | + col_buffer_min_qty = 8 |
948 | + col_first_fmc = 9 |
949 | + |
950 | + if cells_nb > col_replacing and row.cells[col_replacing].data and row.cells[col_replacing].data.strip(): |
951 | + if data_towrite['status'] not in ('replaced', 'phasingout'): |
952 | + line_error.append(_('Line %d: you can not set a Replacing product on this line, please change the satus or remove the replacing product') % (idx+1, )) |
953 | + else: |
954 | + replacing_id = product_obj.search(cr, uid, [('default_code', '=ilike', row.cells[col_replacing].data.strip())], context=context) |
955 | + if not replacing_id: |
956 | + line_error.append(_('Line %d: replacing product code %s not found') % (idx+1, row.cells[col_replacing].data)) |
957 | + else: |
958 | + data_towrite['replacing_product_id'] = replacing_id[0] |
959 | + elif data_towrite['status'] == 'replaced' and not data_towrite['replacing_product_id']: |
960 | + line_error.append(_('Line %d: replacing product must be set !') % (idx+1, )) |
961 | + |
962 | + if cells_nb > col_replaced and row.cells[col_replaced].data and row.cells[col_replaced].data.strip(): |
963 | + if data_towrite['status'] not in ('replacing', 'activereplacing'): |
964 | + line_error.append(_('Line %d: you can not set a Replaced product on this line, please change the satus or remove the replaced product') % (idx+1, )) |
965 | + else: |
966 | + replaced_id = product_obj.search(cr, uid, [('default_code', '=ilike', row.cells[col_replaced].data.strip())], context=context) |
967 | + if not replaced_id: |
968 | + line_error.append(_('Line %d: replaced product code %s not found') % (idx+1, row.cells[col_replaced].data)) |
969 | + else: |
970 | + data_towrite['replaced_product_id'] = replaced_id[0] |
971 | + elif data_towrite['status'] in ('replacing', 'activereplacing') and not data_towrite['replaced_product_id']: |
972 | + line_error.append(_('Line %d: replaced product must be set !') % (idx+1, )) |
973 | + |
974 | + |
975 | + if cells_nb > col_buffer_min_qty and seg.rule == 'cycle': |
976 | + if row.cells[col_buffer_min_qty].data and not isinstance(row.cells[col_buffer_min_qty].data, (int, long, float)): |
977 | + line_error.append(_('Line %d: Buffer Qty must be a number, found %s') % (idx+1, row.cells[col_buffer_min_qty].data)) |
978 | + else: |
979 | + data_towrite['buffer_qty'] = row.cells[col_buffer_min_qty].data |
980 | + for fmc in range(1, 13): |
981 | + if cells_nb - 1 >= col_first_fmc and row.cells[col_first_fmc].data: |
982 | + from_data = False |
983 | + fmc_data = row.cells[col_first_fmc].data |
984 | + if fmc == 1: |
985 | + if cells_nb - 1 < col_first_fmc+1: |
986 | + line_error.append(_('Line %d: FMC FROM %d, date expected') % (idx+1, fmc)) |
987 | + continue |
988 | + if not row.cells[col_first_fmc+1].type == 'datetime': |
989 | + line_error.append(_('Line %d: FMC FROM %d, date is not valid, found %s') % (idx+1, fmc, row.cells[col_first_fmc+1].data)) |
990 | + continue |
991 | + from_data = row.cells[col_first_fmc+1].data.strftime('%Y-%m-%d') |
992 | + col_first_fmc += 1 |
993 | + |
994 | + if cells_nb - 1 < col_first_fmc+1: |
995 | + line_error.append(_('Line %d: FMC TO %d, date expected') % (idx+1, fmc)) |
996 | + continue |
997 | + if not row.cells[col_first_fmc+1].data or row.cells[col_first_fmc+1].type != 'datetime': |
998 | + line_error.append(_('Line %d: FMC TO %d, date is not valid, found %s') % (idx+1, fmc, row.cells[col_first_fmc+1].data)) |
999 | + continue |
1000 | + if not isinstance(fmc_data, (int, long, float)): |
1001 | + line_error.append(_('Line %d: FMC %d, number expected, found %s') % (idx+1, fmc, fmc_data)) |
1002 | + continue |
1003 | + data_towrite.update({ |
1004 | + 'rr_fmc_%d' % fmc: fmc_data, |
1005 | + 'rr_fmc_from_%d' % fmc:from_data, |
1006 | + 'rr_fmc_to_%d' % fmc: row.cells[col_first_fmc+1].data.strftime('%Y-%m-%d'), |
1007 | + }) |
1008 | + col_first_fmc += 2 |
1009 | + elif cells_nb > col_buffer_min_qty and seg.rule == 'minmax': |
1010 | + if not row.cells[col_buffer_min_qty] or not isinstance(row.cells[col_buffer_min_qty].data, (int, long, float)): |
1011 | + line_error.append(_('Line %d: Min Qty, number expected, found %s') % (idx+1, row.cells[col_buffer_min_qty].data)) |
1012 | + elif not row.cells[col_buffer_min_qty+1] or not isinstance(row.cells[col_buffer_min_qty+1].data, (int, long, float)): |
1013 | + line_error.append(_('Line %d: Max Qty, number expected, found %s') % (idx+1, row.cells[col_buffer_min_qty+1].data)) |
1014 | + elif row.cells[col_buffer_min_qty+1].data < row.cells[col_buffer_min_qty].data: |
1015 | + line_error.append(_('Line %d: Max Qty (%s) must be larger than Min Qty (%s)') % (idx+1, row.cells[col_buffer_min_qty+1].data, row.cells[col_buffer_min_qty].data)) |
1016 | + else: |
1017 | data_towrite.update({ |
1018 | - 'rr_fmc_%d' % fmc: row.cells[col_first_fmc].data, |
1019 | - 'rr_fmc_from_%d' % fmc: row.cells[col_first_fmc+1].data.strftime('%Y-%m-%d'), |
1020 | - 'rr_fmc_to_%d' % fmc: row.cells[col_first_fmc+2].data.strftime('%Y-%m-%d'), |
1021 | + 'min_qty': row.cells[col_buffer_min_qty].data, |
1022 | + 'max_qty': row.cells[col_buffer_min_qty+1].data, |
1023 | }) |
1024 | - col_first_fmc += 3 |
1025 | - elif cells_nb > col_buffer_min_qty and seg.rule == 'minmax': |
1026 | - if not row.cells[col_buffer_min_qty] or not isinstance(row.cells[col_buffer_min_qty].data, (int, long, float)): |
1027 | - line_error.append(_('Line %d: Min Qty, number expected, found %s') % (idx+1, row.cells[col_buffer_min_qty].data)) |
1028 | - elif not row.cells[col_buffer_min_qty+1] or not isinstance(row.cells[col_buffer_min_qty+1].data, (int, long, float)): |
1029 | - line_error.append(_('Line %d: Max Qty, number expected, found %s') % (idx+1, row.cells[col_buffer_min_qty+1].data)) |
1030 | - elif row.cells[col_buffer_min_qty+1].data < row.cells[col_buffer_min_qty].data: |
1031 | - line_error.append(_('Line %d: Max Qty (%s) must be larger than Min Qty (%s)') % (idx+1, row.cells[col_buffer_min_qty+1].data, row.cells[col_buffer_min_qty].data)) |
1032 | - else: |
1033 | - data_towrite.update({ |
1034 | - 'min_qty': row.cells[col_buffer_min_qty].data, |
1035 | - 'max_qty': row.cells[col_buffer_min_qty+1].data, |
1036 | - }) |
1037 | - elif cells_nb > col_buffer_min_qty: |
1038 | - if not row.cells[col_buffer_min_qty] or not isinstance(row.cells[col_buffer_min_qty].data, (int, long, float)): |
1039 | - line_error.append(_('Line %d: Auto Supply Qty, number expected, found %s') % (idx+1, row.cells[col_buffer_min_qty].data)) |
1040 | - else: |
1041 | - data_towrite['auto_qty'] = row.cells[col_buffer_min_qty].data |
1042 | - |
1043 | - if prod_code not in existing_line: |
1044 | - prod_id = product_obj.search(cr, uid, [('default_code', '=ilike', prod_code)], context=context) |
1045 | - if not prod_id: |
1046 | - line_error.append(_('Line %d: product code %s not found') % (idx+1, prod_code)) |
1047 | - else: |
1048 | - if prod_id[0] in code_created: |
1049 | - line_error.append(_('Line %d: product code %s already defined in the file') % (idx+1, prod_code)) |
1050 | - |
1051 | - code_created[prod_id[0]] = True |
1052 | - data_towrite['product_id'] = prod_id[0] |
1053 | - data_towrite['segment_id'] = seg.id |
1054 | - else: |
1055 | - line_id = existing_line[prod_code] |
1056 | - |
1057 | - if line_error: |
1058 | - error += line_error |
1059 | - ignored += 1 |
1060 | - continue |
1061 | - if 'product_id' in data_towrite: |
1062 | - seg_line_obj.create(cr, uid, data_towrite, context=context) |
1063 | - created += 1 |
1064 | - else: |
1065 | - seg_line_obj.write(cr, uid, line_id, data_towrite, context=context) |
1066 | - updated += 1 |
1067 | + elif cells_nb > col_buffer_min_qty: |
1068 | + if not row.cells[col_buffer_min_qty] or not isinstance(row.cells[col_buffer_min_qty].data, (int, long, float)): |
1069 | + line_error.append(_('Line %d: Auto Supply Qty, number expected, found %s') % (idx+1, row.cells[col_buffer_min_qty].data)) |
1070 | + else: |
1071 | + data_towrite['auto_qty'] = row.cells[col_buffer_min_qty].data |
1072 | + |
1073 | + if prod_code not in existing_line: |
1074 | + prod_id = product_obj.search(cr, uid, [('default_code', '=ilike', prod_code)], context=context) |
1075 | + if not prod_id: |
1076 | + line_error.append(_('Line %d: product code %s not found') % (idx+1, prod_code)) |
1077 | + else: |
1078 | + if prod_id[0] in code_created: |
1079 | + line_error.append(_('Line %d: product code %s already defined in the file') % (idx+1, prod_code)) |
1080 | + |
1081 | + code_created[prod_id[0]] = True |
1082 | + data_towrite['product_id'] = prod_id[0] |
1083 | + data_towrite['segment_id'] = seg.id |
1084 | + else: |
1085 | + line_id = existing_line[prod_code] |
1086 | + |
1087 | + if line_error: |
1088 | + error += line_error |
1089 | + ignored += 1 |
1090 | + continue |
1091 | + if 'product_id' in data_towrite: |
1092 | + seg_line_obj.create(cr, uid, data_towrite, context=context) |
1093 | + created += 1 |
1094 | + else: |
1095 | + seg_line_obj.write(cr, uid, line_id, data_towrite, context=context) |
1096 | + updated += 1 |
1097 | + |
1098 | + except Exception, e: |
1099 | + cr.rollback() |
1100 | + return wizard_obj.message_box_noclose(cr, uid, title=_('Importation errors'), message=_("Unexpected error during import:\n%s") % (misc.get_traceback(e), )) |
1101 | |
1102 | self.write(cr, uid, seg.id, {'file_to_import': False}, context=context) |
1103 | - wizard_obj = self.pool.get('physical.inventory.import.wizard') |
1104 | if error: |
1105 | error.insert(0, _('%d line(s) created, %d line(s) updated, %d line(s) in error') % (created, updated, ignored)) |
1106 | return wizard_obj.message_box_noclose(cr, uid, title=_('Importation errors'), message='\n'.join(error)) |
1107 | @@ -1426,7 +1527,7 @@ |
1108 | if self.pool.get('replenishment.segment.line').search_exist(cr, uid, [('segment_id', 'in', ids), ('status', '=', 'replaced'), ('replacing_product_id', '=', False)], context=context): |
1109 | raise osv.except_osv(_('Warning'), _('Please complete Replacing products with a paired product, see red lines')) |
1110 | |
1111 | - if self.pool.get('replenishment.segment.line').search_exist(cr, uid, [('segment_id', 'in', ids), ('status', '=', 'replacing'), ('replaced_product_id', '=', False)], context=context): |
1112 | + if self.pool.get('replenishment.segment.line').search_exist(cr, uid, [('segment_id', 'in', ids), ('status', 'in', ['replacing', 'activereplacing']), ('replaced_product_id', '=', False)], context=context): |
1113 | raise osv.except_osv(_('Warning'), _('Please complete Replaced products with a paired product, see red lines')) |
1114 | |
1115 | cr.execute(''' |
1116 | @@ -1710,13 +1811,19 @@ |
1117 | |
1118 | prod_obj = self.pool.get('product.product') |
1119 | for seg_id in segment: |
1120 | - # TODO JFB RR: compute_child ? |
1121 | - for prod_id in prod_obj.browse(cr, uid, segment[seg_id]['prod_seg_line'].keys(), fields_to_fetch=['incoming_qty'], context={'to_date': segment[seg_id]['to_date_rdd'], 'location': segment[seg_id]['location_ids']}): |
1122 | - ret[segment[seg_id]['prod_seg_line'][prod_id.id]]['pipeline_before_rdd'] = prod_id.incoming_qty |
1123 | - |
1124 | - if not inv_review: |
1125 | + # compute_child ? |
1126 | + if 'pipeline_before_rdd' in field_name: |
1127 | + for prod_id in prod_obj.browse(cr, uid, segment[seg_id]['prod_seg_line'].keys(), fields_to_fetch=['incoming_qty'], context={'to_date': segment[seg_id]['to_date_rdd'], 'location': segment[seg_id]['location_ids']}): |
1128 | + ret[segment[seg_id]['prod_seg_line'][prod_id.id]]['pipeline_before_rdd'] = prod_id.incoming_qty |
1129 | + |
1130 | + for prod_id, qty in prod_obj.get_pipeline_from_po(cr, uid, segment[seg_id]['prod_seg_line'].keys(), to_date=segment[seg_id]['to_date_rdd'], location_ids=segment[seg_id]['location_ids']).iteritems(): |
1131 | + ret[segment[seg_id]['prod_seg_line'][prod_id]]['pipeline_before_rdd'] += qty |
1132 | + |
1133 | + if not inv_review and 'pipeline_between_rdd_oc' in field_name: |
1134 | for prod_id in prod_obj.browse(cr, uid, segment[seg_id]['prod_seg_line'].keys(), fields_to_fetch=['incoming_qty'], context={'from_strict_date': segment[seg_id]['to_date_rdd'], 'to_date': segment[seg_id]['to_date_oc'], 'location': segment[seg_id]['location_ids']}): |
1135 | ret[segment[seg_id]['prod_seg_line'][prod_id.id]]['pipeline_between_rdd_oc'] = prod_id.incoming_qty |
1136 | + for prod_id, qty in prod_obj.get_pipeline_from_po(cr, uid, segment[seg_id]['prod_seg_line'].keys(), from_date=segment[seg_id]['to_date_rdd'], to_date=segment[seg_id]['to_date_oc'], location_ids=segment[seg_id]['location_ids']).iteritems(): |
1137 | + ret[segment[seg_id]['prod_seg_line'][prod_id]]['pipeline_between_rdd_oc'] += qty |
1138 | |
1139 | return ret |
1140 | |
1141 | @@ -1750,7 +1857,7 @@ |
1142 | ret = {} |
1143 | for _id in ids: |
1144 | ret[_id] = False |
1145 | - for _id in self.search(cr, uid, [('id', 'in', ids), ('status', 'in', ['replaced', 'replacing'])], context=context): |
1146 | + for _id in self.search(cr, uid, [('id', 'in', ids), ('status', 'in', ['replaced', 'replacing', 'phasingout', 'activereplacing'])], context=context): |
1147 | ret[_id] = True |
1148 | return ret |
1149 | |
1150 | @@ -1760,6 +1867,7 @@ |
1151 | ret[_id] = {'warning': False, 'warning_html': ''} |
1152 | |
1153 | |
1154 | + # has stock for new prod ? |
1155 | new_ids = self.search(cr, uid, [('id', 'in', ids), ('status', '=', 'new')], context=context) |
1156 | if new_ids: |
1157 | for line in self.browse(cr, uid, new_ids, fields_to_fetch=['real_stock'], context=context): |
1158 | @@ -1770,23 +1878,41 @@ |
1159 | 'warning_html': '<img src="/openerp/static/images/stock/gtk-dialog-warning.png" title="%s" class="warning"/> <div>%s</div> ' % (misc.escape_html(warn), _('New?')) |
1160 | } |
1161 | |
1162 | + |
1163 | + # has pipe for replaced, phasingout statuses ? |
1164 | cr.execute(''' |
1165 | - select line.id from |
1166 | + select l_id, l_status from ( |
1167 | + select line.id as l_id, line.status as l_status from |
1168 | + purchase_order_line pol, replenishment_segment_line line |
1169 | + where |
1170 | + pol.product_id = line.product_id and |
1171 | + line.id in %(ids)s and |
1172 | + pol.state in ('validated', 'validated_n', 'sourced_sy', 'sourced_v', 'sourced_n') and |
1173 | + line.status in ('replaced', 'phasingout') |
1174 | + UNION |
1175 | + select line.id as l_id, line.status as l_status from |
1176 | stock_move m, stock_picking p, replenishment_segment_line line |
1177 | where |
1178 | m.picking_id = p.id and |
1179 | m.product_id = line.product_id and |
1180 | - line.id in %s and |
1181 | + line.id in %(ids)s and |
1182 | (p.type = 'in' or p.type = 'internal' and p.subtype = 'sysint') and |
1183 | m.state in ('confirmed','waiting','assigned') and |
1184 | - line.status = 'replaced' |
1185 | + line.status in ('replaced', 'phasingout') |
1186 | group by line.id |
1187 | - ''', (tuple(ids), )) |
1188 | + ) x group by l_id, l_status |
1189 | + ''', {'ids': tuple(ids)}) |
1190 | + |
1191 | for line in cr.fetchall(): |
1192 | warn = _('Product has pipeline - check status!') |
1193 | + if line[1] == 'replaced': |
1194 | + error = _('Replaced?') |
1195 | + else: |
1196 | + error = _('Phased out?') |
1197 | + |
1198 | ret[line[0]] = { |
1199 | 'warning': warn, |
1200 | - 'warning_html': '<img src="/openerp/static/images/stock/gtk-dialog-warning.png" title="%s" class="warning"/> <div>%s</div> ' % (misc.escape_html(warn), _('Replaced?')) |
1201 | + 'warning_html': '<img src="/openerp/static/images/stock/gtk-dialog-warning.png" title="%s" class="warning"/> <div>%s</div> ' % (misc.escape_html(warn), error) |
1202 | } |
1203 | return ret |
1204 | |
1205 | @@ -1798,7 +1924,7 @@ |
1206 | 'in_main_list': fields.function(_get_main_list, type='boolean', method=True, string='Prim. prod. list'), |
1207 | 'status_tooltip': fields.function(_get_status_tooltip, type='char', method=True, string='Paired product'), |
1208 | 'display_paired_icon': fields.function(_get_display_paired_icon, type='boolean', method=True, string='Display paired icon'), |
1209 | - 'status': fields.selection([('active', 'Active'), ('new', 'New'), ('replaced', 'Replaced'), ('replacing', 'Replacing')], string='RR Lifecycle'), |
1210 | + 'status': fields.selection(life_cycle_status, string='RR Lifecycle'), |
1211 | 'min_qty': fields.float('Min Qty', related_uom='uom_id'), |
1212 | 'max_qty': fields.float('Max Qty', related_uom='uom_id'), |
1213 | 'auto_qty': fields.float('Auto. Supply Qty', related_uom='uom_id'), |
1214 | @@ -1862,21 +1988,16 @@ |
1215 | return True |
1216 | for line in self.browse(cr, uid, line_ids, context=context): |
1217 | prev_to = False |
1218 | - empty = 0 |
1219 | for x in range(1, 13): |
1220 | rr_fmc = getattr(line, 'rr_fmc_%d'%x) |
1221 | rr_from = getattr(line, 'rr_fmc_from_%d'%x) |
1222 | rr_to = getattr(line, 'rr_fmc_to_%d'%x) |
1223 | if rr_from: |
1224 | - if empty: |
1225 | - error.append(_('%s, FMC FROM %d is not set, you can\'t have gap in FMC (%s is set)') % (line.product_id.default_code, empty, x)) |
1226 | - continue |
1227 | rr_from = datetime.strptime(rr_from, '%Y-%m-%d') |
1228 | if rr_from.day != 1: |
1229 | error.append(_('%s, FMC FROM %d must start the 1st day of the month') % (line.product_id.default_code, x)) |
1230 | if not rr_to: |
1231 | if not rr_fmc: |
1232 | - empty = x |
1233 | continue |
1234 | error.append(_("%s, FMC TO %d can't be empty if FMC from is set") % (line.product_id.default_code, x)) |
1235 | else: |
1236 | @@ -1892,8 +2013,6 @@ |
1237 | if prev_to > rr_from: |
1238 | error.append(_("%s, FMC FROM %d must be later than FMC TO %d") % (line.product_id.default_code, x, x-1)) |
1239 | prev_to = rr_to |
1240 | - elif not empty: |
1241 | - empty = x |
1242 | if error: |
1243 | raise osv.except_osv(_('Error'), _('Please correct the following FMC values:\n%s') % ("\n".join(error))) |
1244 | |
1245 | @@ -1940,19 +2059,25 @@ |
1246 | 'status': 'active', |
1247 | } |
1248 | |
1249 | - def _remove_paired_product(self, cr, uid, vals, context=None): |
1250 | + def _clean_data(self, cr, uid, vals, context=None): |
1251 | if vals and 'status' in vals: |
1252 | - if vals['status'] != 'replacing': |
1253 | + if vals['status'] not in ('replacing', 'activereplacing'): |
1254 | vals['replaced_product_id'] = False |
1255 | - if vals['status'] != 'replaced': |
1256 | + if vals['status'] not in ('replaced', 'phasingout'): |
1257 | vals['replacing_product_id'] = False |
1258 | + for x in range(1, 12): |
1259 | + if vals.get('rr_fmc_to_%d'%x): |
1260 | + try: |
1261 | + vals['rr_fmc_from_%d'%(x+1)] = (datetime.strptime(vals['rr_fmc_to_%d'%x], '%Y-%m-%d') + relativedelta(days=1)).strftime('%Y-%m-%d') |
1262 | + except: |
1263 | + pass |
1264 | |
1265 | def create(self, cr, uid, vals, context=None): |
1266 | - self._remove_paired_product(cr, uid, vals, context=context) |
1267 | + self._clean_data(cr, uid, vals, context=context) |
1268 | return super(replenishment_segment_line, self).create(cr, uid, vals, context=context) |
1269 | |
1270 | def write(self, cr, uid, ids, vals, context=None): |
1271 | - self._remove_paired_product(cr, uid, vals, context=context) |
1272 | + self._clean_data(cr, uid, vals, context=context) |
1273 | return super(replenishment_segment_line, self).write(cr, uid, ids, vals, context=context) |
1274 | |
1275 | def create_multiple_lines(self, cr, uid, parent_id, product_ids, context=None): |
1276 | @@ -1979,13 +2104,14 @@ |
1277 | return {'msg': "Warning, duplicate products already in Segment have been ignored.\nProducts in duplicate:\n - %s" % '\n - '.join(exist_code)} |
1278 | return True |
1279 | |
1280 | - def change_fmc(selc, cr, uid, ids, ch_type, nb, value, context=None): |
1281 | - if not value: |
1282 | + def change_fmc(selc, cr, uid, ids, ch_type, nb, date_str, update_next, context=None): |
1283 | + if not date_str: |
1284 | return {} |
1285 | |
1286 | msg = False |
1287 | + value = {} |
1288 | try: |
1289 | - fmc_date = datetime.strptime(value, '%Y-%m-%d') |
1290 | + fmc_date = datetime.strptime(date_str, '%Y-%m-%d') |
1291 | except: |
1292 | return {} |
1293 | if ch_type == 'from' and fmc_date.day != 1: |
1294 | @@ -1993,11 +2119,13 @@ |
1295 | elif ch_type == 'to': |
1296 | if fmc_date + relativedelta(months=1, day=1, days=-1) != fmc_date: |
1297 | msg = _('FMC TO %s must be the last day of the month') % (nb, ) |
1298 | + if update_next: |
1299 | + value = {'rr_fmc_from_%s'%(int(nb)+1): fmc_date and (fmc_date + relativedelta(days=1)).strftime('%Y-%m-%d') or False} |
1300 | |
1301 | if msg: |
1302 | - return {'warning': {'message': msg}} |
1303 | + return {'warning': {'message': msg}, 'value': value} |
1304 | |
1305 | - return {} |
1306 | + return {'value': value} |
1307 | |
1308 | def set_paired_product(self, cr, uid, ids, context=None): |
1309 | |
1310 | @@ -2419,6 +2547,8 @@ |
1311 | } |
1312 | |
1313 | def import_lines(self, cr, uid, ids, context=None): |
1314 | + ''' import replenishment.order_calc ''' |
1315 | + |
1316 | calc_line_obj = self.pool.get('replenishment.order_calc.line') |
1317 | |
1318 | calc = self.browse(cr, uid, ids[0], context=context) |
1319 | @@ -2431,11 +2561,11 @@ |
1320 | existing_line[line.product_id.default_code] = line.id |
1321 | |
1322 | if calc.rule == 'cycle': |
1323 | + qty_col = 16 |
1324 | + comment_col = 19 |
1325 | + elif calc.rule in ('auto', 'minmax'): |
1326 | qty_col = 14 |
1327 | comment_col = 17 |
1328 | - elif calc.rule in ('auto', 'minmax'): |
1329 | - qty_col = 12 |
1330 | - comment_col = 15 |
1331 | idx = -1 |
1332 | |
1333 | error = [] |
1334 | @@ -2446,6 +2576,9 @@ |
1335 | # header |
1336 | continue |
1337 | |
1338 | + if not len(row.cells): |
1339 | + continue |
1340 | + |
1341 | prod_code = row.cells[0].data |
1342 | if not prod_code: |
1343 | continue |
1344 | @@ -2556,16 +2689,16 @@ |
1345 | 'order_calc_id': fields.many2one('replenishment.order_calc', 'Order Calc', required=1, select=1), |
1346 | 'product_id': fields.many2one('product.product', 'Product Code', select=1, required=1, readonly=1), |
1347 | 'product_description': fields.related('product_id', 'name', string='Description', type='char', size=64, readonly=True, select=True, write_relate=False), |
1348 | - 'status': fields.selection([('active', 'Active'), ('new', 'New'), ('replaced', 'Replaced'), ('replacing', 'Replacing')], string='Life cycle status', readony=1), |
1349 | + 'status': fields.selection(life_cycle_status, string='Life cycle status', readony=1), |
1350 | 'uom_id': fields.related('product_id', 'uom_id', string='UoM', type='many2one', relation='product.uom', readonly=True, select=True, write_relate=False), |
1351 | 'in_main_list': fields.boolean('Prim. prod. list', readonly=1), |
1352 | - 'valid_rr_fmc': fields.boolean('Valid FMC', readonly=1), |
1353 | + 'valid_rr_fmc': fields.boolean('Valid', readonly=1), |
1354 | 'real_stock': fields.float('Real Stock', readonly=1, related_uom='uom_id'), |
1355 | 'pipeline_qty': fields.float('Pipeline Qty', readonly=1, related_uom='uom_id'), |
1356 | 'eta_for_next_pipeline': fields.date('ETA for Next Pipeline', readonly=1), |
1357 | 'reserved_stock_qty': fields.float('Reserved Stock Qty', readonly=1, related_uom='uom_id'), |
1358 | 'projected_stock_qty': fields.float('Projected Stock Level', readonly=1, related_uom='uom_id'), |
1359 | - 'qty_lacking': fields.float('Qty lacking before next RDD', readonly=1, related_uom='uom_id'), |
1360 | + 'qty_lacking': fields.float_null('Qty lacking before next RDD', readonly=1, related_uom='uom_id', null_value='N/A'), |
1361 | 'qty_lacking_needed_by': fields.date('Qty lacking needed by', readonly=1), |
1362 | 'open_loan': fields.boolean('Open Loan', readonly=1), |
1363 | 'open_donation': fields.boolean('Donations pending', readonly=1), |
1364 | @@ -2578,6 +2711,9 @@ |
1365 | 'order_qty_comment': fields.char('Order Qty Comment', size=512), |
1366 | 'warning': fields.text('Warning', readonly='1'), |
1367 | 'warning_html': fields.text('Warning', readonly='1'), |
1368 | + 'buffer_qty': fields.float_null('Buffer Qty', related_uom='uom_id', readonly=1), |
1369 | + 'auto_qty': fields.float('Auto. Supply Qty', related_uom='uom_id', readonly=1), |
1370 | + 'min_max': fields.char('Min/Max', size=128, readonly=1), |
1371 | } |
1372 | |
1373 | replenishment_order_calc_line() |
1374 | @@ -2625,15 +2761,31 @@ |
1375 | 'inv_review': inv_review, |
1376 | } |
1377 | |
1378 | + def pipeline_po(self, cr, uid, ids, context=None): |
1379 | + data = self._selected_data(cr, uid, ids, context=context) |
1380 | + |
1381 | + product_ids = [x.id for x in data['products']] |
1382 | + product_code = [x.default_code for x in data['products']] |
1383 | + |
1384 | + |
1385 | + res = self.pool.get('ir.actions.act_window').open_view_from_xmlid(cr, uid, 'purchase.purchase_line_pipeline_action', ['tree'], new_tab=True, context=context) |
1386 | + res['domain'] = ['&', '&', ('location_dest_id', 'in', data['location_ids']), ('state', 'in', ['validated', 'validated_n', 'sourced_sy', 'sourced_v', 'sourced_n']), ('product_id', 'in', product_ids)] |
1387 | + res['name'] = _('Pipeline %s: %s') % (data['inv_review'].location_config_id.name, ', '.join(product_code)) |
1388 | + res['nodestroy'] = True |
1389 | + res['target'] = 'new' |
1390 | + return res |
1391 | + |
1392 | def pipeline(self, cr, uid, ids, context=None): |
1393 | data = self._selected_data(cr, uid, ids, context=context) |
1394 | |
1395 | product_ids = [x.id for x in data['products']] |
1396 | product_code = [x.default_code for x in data['products']] |
1397 | |
1398 | - res = self.pool.get('ir.actions.act_window').open_view_from_xmlid(cr, uid, 'stock.action_move_form3', ['tree', 'form'], new_tab=True, context=context) |
1399 | + res = self.pool.get('ir.actions.act_window').open_view_from_xmlid(cr, uid, 'stock.action_move_form3', ['tree', 'form'], context=context) |
1400 | res['domain'] = ['&', '&', ('location_dest_id', 'in', data['location_ids']), ('state', 'in', ['confirmed', 'assigned']), ('product_id', 'in', product_ids)] |
1401 | res['name'] = _('Pipeline %s: %s') % (data['inv_review'].location_config_id.name, ', '.join(product_code)) |
1402 | + res['nodestroy'] = True |
1403 | + res['target'] = 'new' |
1404 | return res |
1405 | |
1406 | def stock_by_location(self, cr, uid, ids, context=None): |
1407 | @@ -2657,18 +2809,20 @@ |
1408 | 'product_id': fields.many2one('product.product', 'Product Code', select=1, required=1), # OC |
1409 | 'product_description': fields.related('product_id', 'name', string='Description', type='char', size=64, readonly=True, select=True, write_relate=False), # OC |
1410 | 'uom_id': fields.related('product_id', 'uom_id', string='UoM', type='many2one', relation='product.uom', readonly=True, select=True, write_relate=False), # OC |
1411 | - 'status': fields.selection([('active', 'Active'), ('new', 'New'), ('replaced', 'Replaced'), ('replacing', 'Replacing')], string='Life cycle status'), # OC |
1412 | + 'status': fields.selection(life_cycle_status, string='Life cycle status'), # OC |
1413 | + 'paired_product_id': fields.many2one('product.product', 'Replacing/Replaced product'), |
1414 | 'primay_product_list': fields.char('Primary Product List', size=512), # OC |
1415 | 'rule': fields.selection([('cycle', 'Order Cycle'), ('minmax', 'Min/Max'), ('auto', 'Automatic Supply')], string='Replenishment Rule (Order quantity)', required=1), #Seg |
1416 | - 'min_qty': fields.float('Min Qty', related_uom='uom_id'), # Seg line |
1417 | - 'max_qty': fields.float('Max Qty', related_uom='uom_id'), # Seg line |
1418 | - 'auto_qty': fields.float('Auto. Supply Qty', related_uom='uom_id'), # Seg line |
1419 | + 'min_qty': fields.float_null('Min Qty', related_uom='uom_id'), # Seg line |
1420 | + 'max_qty': fields.float_null('Max Qty', related_uom='uom_id'), # Seg line |
1421 | + 'auto_qty': fields.float_null('Auto. Supply Qty', related_uom='uom_id'), # Seg line |
1422 | 'buffer_qty': fields.float_null('Buffer Qty', related_uom='uom_id'), # Seg line |
1423 | + 'min_max': fields.char('Min / Max', size=128), |
1424 | 'safety_stock': fields.integer('Safety Stock'), # Seg |
1425 | 'segment_ref_name': fields.char('Segment Ref/Name', size=512), # Seg |
1426 | 'rr_fmc_avg': fields.float_null('RR-FMC (average for period)', null_value='N/A'), |
1427 | 'rr_amc': fields.float('RR-AMC'), |
1428 | - 'valid_rr_fmc': fields.boolean('Valid FMC', readonly=1), # OC |
1429 | + 'valid_rr_fmc': fields.boolean('Valid', readonly=1), # OC |
1430 | 'real_stock': fields.float('Real Stock', readonly=1, related_uom='uom_id'), # OC |
1431 | 'pipeline_qty': fields.float('Pipeline Qty', readonly=1, related_uom='uom_id'), # OC |
1432 | 'reserved_stock_qty': fields.float('Reserved Stock Qty', readonly=1, related_uom='uom_id'),# OC |
1433 | |
1434 | === modified file 'bin/addons/procurement_cycle/replenishment_view.xml' |
1435 | --- bin/addons/procurement_cycle/replenishment_view.xml 2020-06-02 14:41:41 +0000 |
1436 | +++ bin/addons/procurement_cycle/replenishment_view.xml 2020-07-27 12:47:56 +0000 |
1437 | @@ -286,7 +286,7 @@ |
1438 | <button name="add_multiple_lines" type="object" icon="gtk-add" string="Add multiple products" colspan="8" /> |
1439 | </group> |
1440 | <field name="line_ids" colspan="4" nolabel="1" default_get="rule=rule" scroll="1" attrs="{'readonly': [('state', '!=', 'draft')]}" o2m_selectable="2"> |
1441 | - <tree editable="top" editable_style="new" colors="red: not status or (display_paired_icon and not status_tooltip)"> |
1442 | + <tree editable="top" editable_style="new" colors="red: not status or (display_paired_icon and not status_tooltip and status!='phasingout')"> |
1443 | <field name="product_id" filter_selector="1" /> |
1444 | <field name="product_description" displayon="noteditable" filter_selector="1" /> |
1445 | <field name="uom_id" invisible="1" /> |
1446 | @@ -302,14 +302,12 @@ |
1447 | <field name="auto_qty" invisible="context.get('rule')!='auto'"/> |
1448 | <field name="buffer_qty" invisible="context.get('rule')!='cycle'"/> |
1449 | <field name="rr_fmc_1" invisible="context.get('rule')!='cycle'" nolabel="1"/> |
1450 | - <field name="rr_fmc_from_1" invisible="context.get('rule')!='cycle'" attrs="{'required': [('rr_fmc_1', '!=', False)]}" on_change="change_fmc('from', '1', rr_fmc_from_1)" nolabel="1"/> |
1451 | - <field name="rr_fmc_to_1" invisible="context.get('rule')!='cycle'" attrs="{'required': [('rr_fmc_1', '!=', False)]}" on_change="change_fmc('to', '1', rr_fmc_to_1)" nolabel="1"/> |
1452 | + <field name="rr_fmc_from_1" invisible="context.get('rule')!='cycle'" attrs="{'required': [('rr_fmc_1', '!=', False)]}" on_change="change_fmc('from', '1', rr_fmc_from_1, False)" nolabel="1"/> |
1453 | + <field name="rr_fmc_to_1" invisible="context.get('rule')!='cycle'" attrs="{'required': [('rr_fmc_1', '!=', False)]}" on_change="change_fmc('to', '1', rr_fmc_to_1, False)" nolabel="1"/> |
1454 | <field name="rr_fmc_2" invisible="context.get('rule')!='cycle'" nolabel="1"/> |
1455 | - <field name="rr_fmc_from_2" invisible="context.get('rule')!='cycle'" attrs="{'required': [('rr_fmc_2', '!=', False)]}" on_change="change_fmc('from', '2', rr_fmc_from_2)" nolabel="1"/> |
1456 | - <field name="rr_fmc_to_2" invisible="context.get('rule')!='cycle'" attrs="{'required': [('rr_fmc_2', '!=', False)]}" on_change="change_fmc('to', '2', rr_fmc_to_2)" nolabel="1"/> |
1457 | + <field name="rr_fmc_to_2" invisible="context.get('rule')!='cycle'" attrs="{'required': [('rr_fmc_2', '!=', False)]}" on_change="change_fmc('to', '2', rr_fmc_to_2, False)" nolabel="1"/> |
1458 | <field name="rr_fmc_3" invisible="context.get('rule')!='cycle'" nolabel="1"/> |
1459 | - <field name="rr_fmc_from_3" invisible="context.get('rule')!='cycle'" attrs="{'required': [('rr_fmc_3', '!=', False)]}" on_change="change_fmc('from', '3', rr_fmc_from_3)" nolabel="1"/> |
1460 | - <field name="rr_fmc_to_3" invisible="context.get('rule')!='cycle'" attrs="{'required': [('rr_fmc_3', '!=', False)]}" on_change="change_fmc('to', '3', rr_fmc_to_3)" nolabel="1"/> |
1461 | + <field name="rr_fmc_to_3" invisible="context.get('rule')!='cycle'" attrs="{'required': [('rr_fmc_3', '!=', False)]}" on_change="change_fmc('to', '3', rr_fmc_to_3, False)" nolabel="1"/> |
1462 | <field name= "list_fmc" invisible="context.get('rule')!='cycle'" displayon="notedition"/> |
1463 | <button name="openform" special="openform" string="Edit 12 FMC" invisible="context.get('rule')!='cycle'" attrs="{'invisible': [('state', '!=', 'draft')]}" icon="terp-go-week" /> |
1464 | <field name="warning_html" widget="html_text" displayon="noteditable"/> |
1465 | @@ -333,41 +331,41 @@ |
1466 | <separator string="To" colspan="1" /> |
1467 | |
1468 | <field name="rr_fmc_1" invisible="context.get('rule')!='cycle'" /> |
1469 | - <field name="rr_fmc_from_1" invisible="context.get('rule')!='cycle'" attrs="{'required': [('rr_fmc_1', '!=', False)]}" on_change="change_fmc('from', '1', rr_fmc_from_1)" nolabel="1"/> |
1470 | - <field name="rr_fmc_to_1" invisible="context.get('rule')!='cycle'" attrs="{'required': [('rr_fmc_1', '!=', False)]}" on_change="change_fmc('to', '1', rr_fmc_to_1)" nolabel="1"/> |
1471 | + <field name="rr_fmc_from_1" invisible="context.get('rule')!='cycle'" attrs="{'required': [('rr_fmc_1', '!=', False)]}" on_change="change_fmc('from', '1', rr_fmc_from_1, False)" nolabel="1"/> |
1472 | + <field name="rr_fmc_to_1" invisible="context.get('rule')!='cycle'" attrs="{'required': [('rr_fmc_1', '!=', False)]}" on_change="change_fmc('to', '1', rr_fmc_to_1, True)" nolabel="1"/> |
1473 | <field name="rr_fmc_2" invisible="context.get('rule')!='cycle'" /> |
1474 | - <field name="rr_fmc_from_2" invisible="context.get('rule')!='cycle'" attrs="{'required': [('rr_fmc_2', '!=', False)]}" on_change="change_fmc('from', '2', rr_fmc_from_2)" nolabel="1"/> |
1475 | - <field name="rr_fmc_to_2" invisible="context.get('rule')!='cycle'" attrs="{'required': [('rr_fmc_2', '!=', False)]}" on_change="change_fmc('to', '2', rr_fmc_to_2)" nolabel="1"/> |
1476 | + <field name="rr_fmc_from_2" invisible="context.get('rule')!='cycle'" readonly="1" nolabel="1"/> |
1477 | + <field name="rr_fmc_to_2" invisible="context.get('rule')!='cycle'" attrs="{'required': [('rr_fmc_2', '!=', False)]}" on_change="change_fmc('to', '2', rr_fmc_to_2, True)" nolabel="1"/> |
1478 | <field name="rr_fmc_3" invisible="context.get('rule')!='cycle'" /> |
1479 | - <field name="rr_fmc_from_3" invisible="context.get('rule')!='cycle'" attrs="{'required': [('rr_fmc_3', '!=', False)]}" on_change="change_fmc('from', '3', rr_fmc_from_3)" nolabel="1"/> |
1480 | - <field name="rr_fmc_to_3" invisible="context.get('rule')!='cycle'" attrs="{'required': [('rr_fmc_3', '!=', False)]}" on_change="change_fmc('to', '3', rr_fmc_to_3)" nolabel="1"/> |
1481 | + <field name="rr_fmc_from_3" invisible="context.get('rule')!='cycle'" readonly="1" nolabel="1"/> |
1482 | + <field name="rr_fmc_to_3" invisible="context.get('rule')!='cycle'" attrs="{'required': [('rr_fmc_3', '!=', False)]}" on_change="change_fmc('to', '3', rr_fmc_to_3, True)" nolabel="1"/> |
1483 | <field name="rr_fmc_4" invisible="context.get('rule')!='cycle'" /> |
1484 | - <field name="rr_fmc_from_4" invisible="context.get('rule')!='cycle'" attrs="{'required': [('rr_fmc_4', '!=', False)]}" on_change="change_fmc('from', '4', rr_fmc_from_4)" nolabel="1"/> |
1485 | - <field name="rr_fmc_to_4" invisible="context.get('rule')!='cycle'" attrs="{'required': [('rr_fmc_4', '!=', False)]}" on_change="change_fmc('to', '4', rr_fmc_to_4)" nolabel="1"/> |
1486 | + <field name="rr_fmc_from_4" invisible="context.get('rule')!='cycle'" readonly="1" nolabel="1"/> |
1487 | + <field name="rr_fmc_to_4" invisible="context.get('rule')!='cycle'" attrs="{'required': [('rr_fmc_4', '!=', False)]}" on_change="change_fmc('to', '4', rr_fmc_to_4, True)" nolabel="1"/> |
1488 | <field name="rr_fmc_5" invisible="context.get('rule')!='cycle'" /> |
1489 | - <field name="rr_fmc_from_5" invisible="context.get('rule')!='cycle'" attrs="{'required': [('rr_fmc_5', '!=', False)]}" on_change="change_fmc('from', '5', rr_fmc_from_5)" nolabel="1"/> |
1490 | - <field name="rr_fmc_to_5" invisible="context.get('rule')!='cycle'" attrs="{'required': [('rr_fmc_5', '!=', False)]}" on_change="change_fmc('to', '5', rr_fmc_to_5)" nolabel="1"/> |
1491 | + <field name="rr_fmc_from_5" invisible="context.get('rule')!='cycle'" readonly="1" nolabel="1"/> |
1492 | + <field name="rr_fmc_to_5" invisible="context.get('rule')!='cycle'" attrs="{'required': [('rr_fmc_5', '!=', False)]}" on_change="change_fmc('to', '5', rr_fmc_to_5, True)" nolabel="1"/> |
1493 | <field name="rr_fmc_6" invisible="context.get('rule')!='cycle'" /> |
1494 | - <field name="rr_fmc_from_6" invisible="context.get('rule')!='cycle'" attrs="{'required': [('rr_fmc_6', '!=', False)]}" on_change="change_fmc('from', '6', rr_fmc_from_6)" nolabel="1"/> |
1495 | - <field name="rr_fmc_to_6" invisible="context.get('rule')!='cycle'" attrs="{'required': [('rr_fmc_6', '!=', False)]}" on_change="change_fmc('to', '6', rr_fmc_to_6)" nolabel="1"/> |
1496 | + <field name="rr_fmc_from_6" invisible="context.get('rule')!='cycle'" readonly="1" nolabel="1"/> |
1497 | + <field name="rr_fmc_to_6" invisible="context.get('rule')!='cycle'" attrs="{'required': [('rr_fmc_6', '!=', False)]}" on_change="change_fmc('to', '6', rr_fmc_to_6, True)" nolabel="1"/> |
1498 | <field name="rr_fmc_7" invisible="context.get('rule')!='cycle'" /> |
1499 | - <field name="rr_fmc_from_7" invisible="context.get('rule')!='cycle'" attrs="{'required': [('rr_fmc_7', '!=', False)]}" on_change="change_fmc('from', '7', rr_fmc_from_7)" nolabel="1"/> |
1500 | - <field name="rr_fmc_to_7" invisible="context.get('rule')!='cycle'" attrs="{'required': [('rr_fmc_7', '!=', False)]}" on_change="change_fmc('to', '7', rr_fmc_to_7)" nolabel="1"/> |
1501 | + <field name="rr_fmc_from_7" invisible="context.get('rule')!='cycle'" readonly="1" nolabel="1"/> |
1502 | + <field name="rr_fmc_to_7" invisible="context.get('rule')!='cycle'" attrs="{'required': [('rr_fmc_7', '!=', False)]}" on_change="change_fmc('to', '7', rr_fmc_to_7, True)" nolabel="1"/> |
1503 | <field name="rr_fmc_8" invisible="context.get('rule')!='cycle'" /> |
1504 | - <field name="rr_fmc_from_8" invisible="context.get('rule')!='cycle'" attrs="{'required': [('rr_fmc_8', '!=', False)]}" on_change="change_fmc('from', '8', rr_fmc_from_8)" nolabel="1"/> |
1505 | - <field name="rr_fmc_to_8" invisible="context.get('rule')!='cycle'" attrs="{'required': [('rr_fmc_8', '!=', False)]}" on_change="change_fmc('to', '8', rr_fmc_to_8)" nolabel="1"/> |
1506 | + <field name="rr_fmc_from_8" invisible="context.get('rule')!='cycle'" readonly="1" nolabel="1"/> |
1507 | + <field name="rr_fmc_to_8" invisible="context.get('rule')!='cycle'" attrs="{'required': [('rr_fmc_8', '!=', False)]}" on_change="change_fmc('to', '8', rr_fmc_to_8, True)" nolabel="1"/> |
1508 | <field name="rr_fmc_9" invisible="context.get('rule')!='cycle'" /> |
1509 | - <field name="rr_fmc_from_9" invisible="context.get('rule')!='cycle'" attrs="{'required': [('rr_fmc_9', '!=', False)]}" on_change="change_fmc('from', '9', rr_fmc_from_9)" nolabel="1"/> |
1510 | - <field name="rr_fmc_to_9" invisible="context.get('rule')!='cycle'" attrs="{'required': [('rr_fmc_9', '!=', False)]}" on_change="change_fmc('to', '9', rr_fmc_to_9)" nolabel="1"/> |
1511 | + <field name="rr_fmc_from_9" invisible="context.get('rule')!='cycle'" readonly="1" nolabel="1"/> |
1512 | + <field name="rr_fmc_to_9" invisible="context.get('rule')!='cycle'" attrs="{'required': [('rr_fmc_9', '!=', False)]}" on_change="change_fmc('to', '9', rr_fmc_to_9, True)" nolabel="1"/> |
1513 | <field name="rr_fmc_10" invisible="context.get('rule')!='cycle'" /> |
1514 | - <field name="rr_fmc_from_10" invisible="context.get('rule')!='cycle'" attrs="{'required': [('rr_fmc_10', '!=', False)]}" on_change="change_fmc('from', '10', rr_fmc_from_10)" nolabel="1"/> |
1515 | - <field name="rr_fmc_to_10" invisible="context.get('rule')!='cycle'" attrs="{'required': [('rr_fmc_10', '!=', False)]}" on_change="change_fmc('to', '10', rr_fmc_to_10)" nolabel="1"/> |
1516 | + <field name="rr_fmc_from_10" invisible="context.get('rule')!='cycle'" readonly="1" nolabel="1"/> |
1517 | + <field name="rr_fmc_to_10" invisible="context.get('rule')!='cycle'" attrs="{'required': [('rr_fmc_10', '!=', False)]}" on_change="change_fmc('to', '10', rr_fmc_to_10, True)" nolabel="1"/> |
1518 | <field name="rr_fmc_11" invisible="context.get('rule')!='cycle'" /> |
1519 | - <field name="rr_fmc_from_11" invisible="context.get('rule')!='cycle'" attrs="{'required': [('rr_fmc_11', '!=', False)]}" on_change="change_fmc('from', '11', rr_fmc_from_11)" nolabel="1"/> |
1520 | - <field name="rr_fmc_to_11" invisible="context.get('rule')!='cycle'" attrs="{'required': [('rr_fmc_11', '!=', False)]}" on_change="change_fmc('to', '11', rr_fmc_to_11)" nolabel="1"/> |
1521 | + <field name="rr_fmc_from_11" invisible="context.get('rule')!='cycle'" readonly="1" nolabel="1"/> |
1522 | + <field name="rr_fmc_to_11" invisible="context.get('rule')!='cycle'" attrs="{'required': [('rr_fmc_11', '!=', False)]}" on_change="change_fmc('to', '11', rr_fmc_to_11, True)" nolabel="1"/> |
1523 | <field name="rr_fmc_12" invisible="context.get('rule')!='cycle'" /> |
1524 | - <field name="rr_fmc_from_12" invisible="context.get('rule')!='cycle'" attrs="{'required': [('rr_fmc_12', '!=', False)]}" on_change="change_fmc('from', '12', rr_fmc_from_12)" nolabel="1"/> |
1525 | - <field name="rr_fmc_to_12" invisible="context.get('rule')!='cycle'" attrs="{'required': [('rr_fmc_12', '!=', False)]}" on_change="change_fmc('to', '12', rr_fmc_to_12)" nolabel="1"/> |
1526 | + <field name="rr_fmc_from_12" invisible="context.get('rule')!='cycle'" readonly="1" nolabel="1"/> |
1527 | + <field name="rr_fmc_to_12" invisible="context.get('rule')!='cycle'" attrs="{'required': [('rr_fmc_12', '!=', False)]}" on_change="change_fmc('to', '12', rr_fmc_to_12, False)" nolabel="1"/> |
1528 | </group> |
1529 | </form> |
1530 | </field> |
1531 | @@ -555,7 +553,10 @@ |
1532 | <field name="uom_id" invisible="1" /> |
1533 | <field name="status" invisible="1" /> |
1534 | <field name="in_main_list" filter_selector="1" /> |
1535 | - <field name="valid_rr_fmc" invisible="context.get('rule')!='cycle'"/> |
1536 | + <field name="auto_qty" invisible="context.get('rule')!='auto'"/> |
1537 | + <field name="buffer_qty" invisible="context.get('rule')!='cycle'"/> |
1538 | + <field name="min_max" invisible="context.get('rule')!='minmax'"/> |
1539 | + <field name="valid_rr_fmc" /> |
1540 | <field name="real_stock" /> |
1541 | <field name="pipeline_qty" /> |
1542 | <field name="eta_for_next_pipeline" /> |
1543 | @@ -680,7 +681,8 @@ |
1544 | <field name="time_unit" /> |
1545 | <newline /> |
1546 | <group colspan="4"> |
1547 | - <button colspan="1" name="pipeline" string="Pipeline" type="object" icon="icons/pipe.png"/> |
1548 | + <button colspan="1" name="pipeline" string="Pipeline (Stock Moves)" type="object" icon="icons/pipe.png"/> |
1549 | + <button colspan="1" name="pipeline_po" string="Pipeline (PO)" type="object" icon="icons/pipe.png"/> |
1550 | <button colspan="1" name="stock_by_location" string="Stock By Location" type="object" icon="gtk-indent"/> |
1551 | </group> |
1552 | <field name="line_ids" colspan="4" nolabel="1" o2m_selectable="2" scroll="1"> |
1553 | @@ -691,9 +693,12 @@ |
1554 | <field name="status" invisible="1" /> |
1555 | <field name="primay_product_list" filter_selector="1" /> |
1556 | <field name="segment_ref_name" /> |
1557 | + <field name="min_max" /> |
1558 | + <field name="auto_qty" /> |
1559 | <field name="projected_stock_qty_amc" /> |
1560 | <field name="rr_fmc_avg" /> |
1561 | <field name="rr_amc" /> |
1562 | + <field name="buffer_qty" /> |
1563 | <field name="valid_rr_fmc" /> |
1564 | <field name="real_stock" /> |
1565 | <field name="pipeline_qty" /> |
1566 | |
1567 | === modified file 'bin/addons/procurement_cycle/replenishment_wizard.xml' |
1568 | --- bin/addons/procurement_cycle/replenishment_wizard.xml 2020-05-04 15:28:58 +0000 |
1569 | +++ bin/addons/procurement_cycle/replenishment_wizard.xml 2020-07-27 12:47:56 +0000 |
1570 | @@ -41,8 +41,8 @@ |
1571 | <form string="Set Paired Product"> |
1572 | <field name="product_id" readonly="1" colspan="4"/> |
1573 | <field name="status" invisible="1" /> |
1574 | - <field name="replacing_product_id" attrs="{'invisible': [('status', '!=', 'replaced')], 'required': [('status', '=', 'replaced')]}" colspan="4" domain="[('id', '!=', product_id)]" /> |
1575 | - <field name="replaced_product_id" attrs="{'invisible': [('status', '!=', 'replacing')], 'required': [('status', '=', 'replacing')]}" colspan="4" domain="[('id', '!=', product_id)]"/> |
1576 | + <field name="replacing_product_id" attrs="{'invisible': [('status', 'not in', ['replaced', 'phasingout'])], 'required': [('status', '=', 'replaced')]}" colspan="4" domain="[('id', '!=', product_id)]" /> |
1577 | + <field name="replaced_product_id" attrs="{'invisible': [('status', 'not in', ['replacing', 'activereplacing'])], 'required': [('status', 'in', ['replacing', 'activereplacing'])]}" colspan="4" domain="[('id', '!=', product_id)]"/> |
1578 | <group colspan="4"> |
1579 | <button name="save_paired" icon="gtk-ok" string="Save" type="object"/> |
1580 | <button special="cancel" string="Close" icon="gtk-cancel" /> |
1581 | |
1582 | === modified file 'bin/addons/procurement_cycle/report/replenishment_inventory_review.mako' |
1583 | --- bin/addons/procurement_cycle/report/replenishment_inventory_review.mako 2020-06-02 14:41:41 +0000 |
1584 | +++ bin/addons/procurement_cycle/report/replenishment_inventory_review.mako 2020-07-27 12:47:56 +0000 |
1585 | @@ -911,6 +911,8 @@ |
1586 | <Row ss:Height="75"> |
1587 | <Cell ss:StyleID="s128"><Data ss:Type="String">${_('Product Code')|x}</Data></Cell> |
1588 | <Cell ss:StyleID="s128"><Data ss:Type="String">${_('Product Description')|x}</Data></Cell> |
1589 | + <Cell ss:StyleID="s128"><Data ss:Type="String">${_('RR Lifecycle')|x}</Data></Cell> |
1590 | + <Cell ss:StyleID="s128"><Data ss:Type="String">${_('Replaced/Replacing')|x}</Data></Cell> |
1591 | <Cell ss:StyleID="s128"><Data ss:Type="String">${_('Primary Product list')|x}</Data></Cell> |
1592 | <Cell ss:StyleID="s129red"><Data ss:Type="String">${_('Warnings Recap')|x}</Data></Cell> |
1593 | <Cell ss:StyleID="s129"><Data ss:Type="String">${_('Segment Ref/name')|x}</Data></Cell> |
1594 | @@ -925,7 +927,7 @@ |
1595 | <Cell ss:StyleID="s130"><Data ss:Type="String">${_('Order coverage')|x} ${getSel(objects[0], 'time_unit')|x}</Data></Cell> |
1596 | <Cell ss:StyleID="s130"><Data ss:Type="String">${_('SS')|x} ${getSel(objects[0], 'time_unit')|x}</Data></Cell> |
1597 | <Cell ss:StyleID="s130"><Data ss:Type="String">${_('Buffer (Qty)')|x}</Data></Cell> |
1598 | - <Cell ss:StyleID="s129"><Data ss:Type="String">${_('Valid RR-FMC(s)')|x}</Data></Cell> |
1599 | + <Cell ss:StyleID="s129"><Data ss:Type="String">${_('Valid')|x}</Data></Cell> |
1600 | <Cell ss:StyleID="s129"><Data ss:Type="String">${_('RR-FMC (average for period)')|x}</Data></Cell> |
1601 | <Cell ss:StyleID="s130"><Data ss:Type="String">${_('RR-AMC (average for AMC period)')|x}</Data></Cell> |
1602 | |
1603 | @@ -965,6 +967,8 @@ |
1604 | <Row ss:AutoFitHeight="0" ss:Height="24"> |
1605 | <Cell ss:StyleID="s141"><Data ss:Type="String">${line.product_id.default_code|x}</Data></Cell> |
1606 | <Cell ss:StyleID="s141"><Data ss:Type="String">${line.product_id.name|x}</Data></Cell> |
1607 | + <Cell ss:StyleID="s141"><Data ss:Type="String">${line.segment_ref_name and getSel(line, 'status') or _('N/A')|x}</Data></Cell> |
1608 | + <Cell ss:StyleID="s141"><Data ss:Type="String">${(line.paired_product_id and line.paired_product_id.default_code or '')|x}</Data></Cell> |
1609 | <Cell ss:StyleID="s141"><Data ss:Type="String">${(line.primay_product_list or '')|x}</Data></Cell> |
1610 | <Cell ss:StyleID="s141"><Data ss:Type="String">${line.warning or ''|xn}</Data></Cell> |
1611 | <Cell ss:StyleID="s141"><Data ss:Type="String">${(line.segment_ref_name or '')|x}</Data></Cell> |
1612 | @@ -972,7 +976,7 @@ |
1613 | <Cell ss:StyleID="s141"><Data ss:Type="String">${(line.segment_ref_name and getSel(line, 'rule') or '')|x}</Data></Cell> |
1614 | % if line.rule == 'minmax': |
1615 | <Cell ss:StyleID="s140"> |
1616 | - % if line.min_qty: |
1617 | + % if line.min_qty is not False: |
1618 | <Data ss:Type="Number">${line.min_qty}</Data> |
1619 | % endif |
1620 | </Cell> |
1621 | @@ -982,7 +986,7 @@ |
1622 | |
1623 | % if line.rule == 'minmax': |
1624 | <Cell ss:StyleID="s140"> |
1625 | - % if line.max_qty: |
1626 | + % if line.max_qty is not False: |
1627 | <Data ss:Type="Number">${line.max_qty}</Data> |
1628 | % endif |
1629 | </Cell> |
1630 | @@ -992,7 +996,7 @@ |
1631 | |
1632 | % if line.rule == 'auto': |
1633 | <Cell ss:StyleID="s140"> |
1634 | - % if line.auto_qty: |
1635 | + % if line.auto_qty is not False: |
1636 | <Data ss:Type="Number">${line.auto_qty}</Data> |
1637 | % endif |
1638 | </Cell> |
1639 | @@ -1036,11 +1040,7 @@ |
1640 | <Cell ss:StyleID="irrel" /> |
1641 | % endif |
1642 | |
1643 | - % if line.rule == 'cycle': |
1644 | - <Cell ss:StyleID="s141"><Data ss:Type="String">${line.valid_rr_fmc and _('Yes') or _('No') }</Data></Cell> |
1645 | - % else: |
1646 | - <Cell ss:StyleID="irrel" /> |
1647 | - % endif |
1648 | + <Cell ss:StyleID="s141"><Data ss:Type="String">${line.valid_rr_fmc and _('Yes') or _('No') }</Data></Cell> |
1649 | |
1650 | <Cell ss:StyleID="s141"> |
1651 | % if line.rr_fmc_avg: |
1652 | |
1653 | === modified file 'bin/addons/procurement_cycle/report/replenishment_order_calc.mako' |
1654 | --- bin/addons/procurement_cycle/report/replenishment_order_calc.mako 2020-06-02 14:41:41 +0000 |
1655 | +++ bin/addons/procurement_cycle/report/replenishment_order_calc.mako 2020-07-27 12:47:56 +0000 |
1656 | @@ -376,6 +376,7 @@ |
1657 | <Column ss:AutoFitWidth="0" ss:Width="87.75"/> <!--prod code --> |
1658 | <Column ss:AutoFitWidth="0" ss:Width="210.75"/> <!--prod desc --> |
1659 | <Column ss:AutoFitWidth="0" ss:Width="63"/> <!-- in list --> |
1660 | + <Column ss:AutoFitWidth="0" ss:Width="66"/> <!-- buffer / minmax / auto qty --> |
1661 | <Column ss:AutoFitWidth="0" ss:Width="63"/> <!-- real stock --> |
1662 | <Column ss:AutoFitWidth="0" ss:Width="66"/> <!--pipeline --> |
1663 | <Column ss:AutoFitWidth="0" ss:Width="82.5" /> <!-- eta --> |
1664 | @@ -492,6 +493,16 @@ |
1665 | <Cell ss:StyleID="s89"><Data ss:Type="String">${_('Product code')|x}</Data><NamedCell ss:Name="Print_Titles"/><NamedCell ss:Name="_FilterDatabase"/><NamedCell ss:Name="Print_Area"/></Cell> |
1666 | <Cell ss:StyleID="s89"><Data ss:Type="String">${_('Description')|x}</Data><NamedCell ss:Name="Print_Titles"/><NamedCell ss:Name="_FilterDatabase"/><NamedCell ss:Name="Print_Area"/></Cell> |
1667 | <Cell ss:StyleID="s90"><Data ss:Type="String">${_('In prod. list')|x}</Data><NamedCell ss:Name="Print_Titles"/><NamedCell ss:Name="_FilterDatabase"/><NamedCell ss:Name="Print_Area"/></Cell> |
1668 | + <Cell ss:StyleID="s90"> |
1669 | + % if objects[0].rule == 'cycle': |
1670 | + <Data ss:Type="String">${_('Buffer')|x}</Data> |
1671 | + % elif objects[0].rule == 'minmax': |
1672 | + <Data ss:Type="String">${_('Min / Max')|x}</Data> |
1673 | + % else: |
1674 | + <Data ss:Type="String">${_('Auto. Supply Qty')|x}</Data> |
1675 | + %endif |
1676 | + <NamedCell ss:Name="Print_Titles"/><NamedCell ss:Name="_FilterDatabase"/><NamedCell ss:Name="Print_Area"/></Cell> |
1677 | + <Cell ss:StyleID="s90"><Data ss:Type="String">${_('Valid')}</Data><NamedCell ss:Name="_FilterDatabase"/><NamedCell ss:Name="Print_Area"/></Cell> |
1678 | <Cell ss:StyleID="s93"><Data ss:Type="String">${_('Real Stock')|x}</Data><NamedCell ss:Name="Print_Titles"/></Cell> |
1679 | <Cell ss:StyleID="s93"><Data ss:Type="String">${_('Pipeline Qty')|x}</Data><NamedCell ss:Name="Print_Titles"/></Cell> |
1680 | <Cell ss:StyleID="s93"><Data ss:Type="String">${_('Eta For next Pipeline')|x}</Data><NamedCell ss:Name="Print_Titles"/></Cell> |
1681 | @@ -526,6 +537,25 @@ |
1682 | <Cell ss:StyleID="s96"><Data ss:Type="String">${prod.product_id.default_code|x}</Data><NamedCell ss:Name="_FilterDatabase"/><NamedCell ss:Name="Print_Area"/></Cell> |
1683 | <Cell ss:StyleID="s96"><Data ss:Type="String">${prod.product_id.name|x}</Data><NamedCell ss:Name="_FilterDatabase"/><NamedCell ss:Name="Print_Area"/></Cell> |
1684 | <Cell ss:StyleID="s95"><Data ss:Type="String">${prod.in_main_list and _('Yes') or _('No')}</Data><NamedCell ss:Name="_FilterDatabase"/><NamedCell ss:Name="Print_Area"/></Cell> |
1685 | + <Cell ss:StyleID="s95"> |
1686 | + % if objects[0].rule == 'cycle': |
1687 | + % if prod.buffer_qty is not False: |
1688 | + ><Data ss:Type="Number">${prod.buffer_qty}</Data> |
1689 | + % else: |
1690 | + <Data ss:Type="String"/> |
1691 | + % endif |
1692 | + % elif objects[0].rule == 'minmax': |
1693 | + <Data ss:Type="String">${prod.min_max}</Data> |
1694 | + % else: |
1695 | + % if prod.auto_qty is not False: |
1696 | + <Data ss:Type="Number">${prod.auto_qty}</Data> |
1697 | + % else: |
1698 | + <Data ss:Type="String"/> |
1699 | + % endif |
1700 | + %endif |
1701 | + <NamedCell ss:Name="_FilterDatabase"/><NamedCell ss:Name="Print_Area"/> |
1702 | + </Cell> |
1703 | + <Cell ss:StyleID="s95"><Data ss:Type="String">${prod.valid_rr_fmc and _('Yes') or _('No') }</Data></Cell> |
1704 | <Cell ss:StyleID="s97"><Data ss:Type="Number">${prod.real_stock}</Data></Cell> |
1705 | <Cell ss:StyleID="s97"><Data ss:Type="Number">${prod.pipeline_qty}</Data></Cell> |
1706 | <Cell ss:StyleID="s97d"> |
1707 | |
1708 | === modified file 'bin/addons/procurement_cycle/report/replenishment_segment.mako' |
1709 | --- bin/addons/procurement_cycle/report/replenishment_segment.mako 2020-05-14 09:26:30 +0000 |
1710 | +++ bin/addons/procurement_cycle/report/replenishment_segment.mako 2020-07-27 12:47:56 +0000 |
1711 | @@ -413,7 +413,9 @@ |
1712 | <% styles = ['s143', 's149'] %> |
1713 | % for fmc in range(1, 13): |
1714 | <Cell ss:StyleID="${styles[i]}"><Data ss:Type="String">${_('RR FMC %d')%fmc|x}</Data><NamedCell ss:Name="Print_Titles"/></Cell> |
1715 | - <Cell ss:StyleID="${styles[i]}"><Data ss:Type="String">${_('From %d')%fmc|x}</Data><NamedCell ss:Name="Print_Titles"/></Cell> |
1716 | + % if fmc == 1: |
1717 | + <Cell ss:StyleID="${styles[i]}"><Data ss:Type="String">${_('From %d')%fmc|x}</Data><NamedCell ss:Name="Print_Titles"/></Cell> |
1718 | + % endif |
1719 | <Cell ss:StyleID="${styles[i]}"><Data ss:Type="String">${_('To %d')%fmc|x}</Data><NamedCell ss:Name="Print_Titles"/></Cell> |
1720 | <% i = 1 - i %> |
1721 | % endfor |
1722 | @@ -455,24 +457,26 @@ |
1723 | <Data ss:Type="Number">${getattr(prod, 'rr_fmc_%d'%fmc) or ''}</Data> |
1724 | % endif |
1725 | </Cell> |
1726 | - <% from_date = getattr(prod, 'rr_fmc_from_%d'%fmc) %> |
1727 | - <Cell ss:StyleID="${styles[i][1]}"> |
1728 | - % if isDate(from_date): |
1729 | - <Data ss:Type="DateTime">${from_date|n}T00:00:00.000</Data> |
1730 | - % else: |
1731 | - <Data ss:Type="String"></Data> |
1732 | - % endif |
1733 | - </Cell> |
1734 | + % if fmc == 1: |
1735 | + <% from_date = getattr(prod, 'rr_fmc_from_%d'%fmc) %> |
1736 | + <Cell ss:StyleID="${styles[i][1]}"> |
1737 | + % if isDate(from_date): |
1738 | + <Data ss:Type="DateTime">${from_date|n}T00:00:00.000</Data> |
1739 | + % else: |
1740 | + <Data ss:Type="String"></Data> |
1741 | + % endif |
1742 | + </Cell> |
1743 | + % endif |
1744 | |
1745 | - <% to_date = getattr(prod, 'rr_fmc_to_%d'%fmc) %> |
1746 | - <Cell ss:StyleID="${styles[i][1]}"> |
1747 | - % if isDate(to_date): |
1748 | - <Data ss:Type="DateTime">${to_date|n}T00:00:00.000</Data> |
1749 | - % else: |
1750 | - <Data ss:Type="String"></Data> |
1751 | - % endif |
1752 | - </Cell> |
1753 | - <% i = 1 - i %> |
1754 | + <% to_date = getattr(prod, 'rr_fmc_to_%d'%fmc) %> |
1755 | + <Cell ss:StyleID="${styles[i][1]}"> |
1756 | + % if isDate(to_date): |
1757 | + <Data ss:Type="DateTime">${to_date|n}T00:00:00.000</Data> |
1758 | + % else: |
1759 | + <Data ss:Type="String"></Data> |
1760 | + % endif |
1761 | + </Cell> |
1762 | + <% i = 1 - i %> |
1763 | % endfor |
1764 | % endif |
1765 | </Row> |
1766 | @@ -534,7 +538,7 @@ |
1767 | <Range>R9C4:R${len(objects[0].line_ids)+9}C4</Range> |
1768 | <Type>List</Type> |
1769 | <CellRangeList/> |
1770 | - <Value>"${_('Active')|x},${_('New')|x},${_('Replaced')|x},${_('Replacing')|x}"</Value> |
1771 | + <Value>"${_('Active')|x},${_('New')|x},${_('Replaced')|x},${_('Replacing')|x},${_('Phasing Out')|x},${_('Active-Replacing')|x}"</Value> |
1772 | </DataValidation> |
1773 | |
1774 | </Worksheet> |
1775 | |
1776 | === modified file 'bin/addons/purchase/purchase_order.py' |
1777 | --- bin/addons/purchase/purchase_order.py 2020-04-22 15:55:54 +0000 |
1778 | +++ bin/addons/purchase/purchase_order.py 2020-07-27 12:47:56 +0000 |
1779 | @@ -828,6 +828,7 @@ |
1780 | }, |
1781 | ), |
1782 | 'delivery_requested_date': fields.date(string='Delivery Requested Date', required=True), |
1783 | + 'delivery_requested_date_modified': fields.date(string='Delivery Requested Date (modified)'), |
1784 | 'delivery_confirmed_date': fields.date(string='Delivery Confirmed Date'), |
1785 | 'ready_to_ship_date': fields.date(string='Ready To Ship Date'), |
1786 | 'shipment_date': fields.date(string='Shipment Date', help='Date on which picking is created at supplier'), |
1787 | @@ -1261,7 +1262,7 @@ |
1788 | default = {} |
1789 | if context is None: |
1790 | context = {} |
1791 | - fields_to_reset = ['delivery_requested_date', 'ready_to_ship_date', 'date_order', 'delivery_confirmed_date', 'arrival_date', 'shipment_date', 'arrival_date', 'date_approve', 'analytic_distribution_id', 'empty_po_cancelled', 'stock_take_date'] |
1792 | + fields_to_reset = ['delivery_requested_date', 'delivery_requested_date_modified', 'ready_to_ship_date', 'date_order', 'delivery_confirmed_date', 'arrival_date', 'shipment_date', 'arrival_date', 'date_approve', 'analytic_distribution_id', 'empty_po_cancelled', 'stock_take_date'] |
1793 | to_del = [] |
1794 | for ftr in fields_to_reset: |
1795 | if ftr not in default: |
1796 | @@ -1359,6 +1360,8 @@ |
1797 | context = {} |
1798 | # field name |
1799 | field_name = context.get('field_name', False) |
1800 | + if field_name == 'requested' and self.search_exists(cr, uid, [('id', 'in', ids), ('delivery_requested_date_modified', '=', False), ('state', '!=', 'draft')], context=context): |
1801 | + raise osv.except_osv(_('Warning'), _('Please fill the "Delivery Requested Date (modified)" field.')) |
1802 | assert field_name, 'The button is not correctly set.' |
1803 | # data |
1804 | data = getattr(self, field_name + '_data')(cr, uid, ids, context=context) |
1805 | @@ -2259,7 +2262,6 @@ |
1806 | if context is None: |
1807 | context = {} |
1808 | move_obj = self.pool.get('stock.move') |
1809 | - data_obj = self.pool.get('ir.model.data') |
1810 | |
1811 | pol = self.ensure_object(cr, uid, 'purchase.order.line', pol) |
1812 | internal = self.ensure_object(cr, uid, 'stock.picking', internal_id) |
1813 | @@ -2269,18 +2271,8 @@ |
1814 | # compute source location: |
1815 | src_location = pol.order_id.location_id |
1816 | |
1817 | - # compute destination location: |
1818 | - dest = pol.order_id.location_id.id |
1819 | - if pol.product_id.type == 'service_recep' and not pol.order_id.cross_docking_ok: |
1820 | - # service with reception are directed to Service Location |
1821 | - dest = self.pool.get('stock.location').get_service_location(cr, uid) |
1822 | - elif pol.product_id.type == 'consu': |
1823 | - dest = data_obj.get_object_reference(cr, uid, 'stock_override', 'stock_location_non_stockable')[1] |
1824 | - elif pol.linked_sol_id and pol.linked_sol_id.order_id.procurement_request and pol.linked_sol_id.order_id.location_requestor_id.usage != 'customer': |
1825 | - dest = pol.linked_sol_id.order_id.location_requestor_id.id |
1826 | - elif self.pool.get('stock.location').chained_location_get(cr, uid, src_location, product=pol.product_id, context=context): |
1827 | - # if input location has a chained location then use it |
1828 | - dest = self.pool.get('stock.location').chained_location_get(cr, uid, src_location, product=pol.product_id, context=context)[0].id |
1829 | + # compute destination location |
1830 | + dest = self.pool.get('purchase.order.line').final_location_dest(cr, uid, pol, context=context) |
1831 | |
1832 | values = { |
1833 | 'name': ''.join((pol.order_id.name, ': ', (pol.name or ''))), |
1834 | |
1835 | === modified file 'bin/addons/purchase/purchase_order_line.py' |
1836 | --- bin/addons/purchase/purchase_order_line.py 2020-04-09 14:44:46 +0000 |
1837 | +++ bin/addons/purchase/purchase_order_line.py 2020-07-27 12:47:56 +0000 |
1838 | @@ -494,6 +494,7 @@ |
1839 | 'move_ids': fields.one2many('stock.move', 'purchase_line_id', 'Reservation', readonly=True, |
1840 | ondelete='set null'), |
1841 | 'move_dest_id': fields.many2one('stock.move', 'Reservation Destination', ondelete='set null', select=True), |
1842 | + 'location_dest_id': fields.many2one('stock.location', 'Final Destination of move', ondelete='set null', select=True), |
1843 | 'price_unit': fields.float('Unit Price', required=True, |
1844 | digits_compute=dp.get_precision('Purchase Price Computation')), |
1845 | 'vat_ok': fields.function(_get_vat_ok, method=True, type='boolean', string='VAT OK', store=False, |
1846 | @@ -1277,6 +1278,8 @@ |
1847 | |
1848 | if not context.get('split_line'): |
1849 | default.update({'stock_take_date': False}) |
1850 | + if 'location_dest_id' not in default: |
1851 | + default['location_dest_id'] = False |
1852 | |
1853 | # from RfQ line to PO line: grab the linked sol if has: |
1854 | if pol.order_id.rfq_ok and context.get('generate_po_from_rfq', False): |
1855 | @@ -1333,6 +1336,7 @@ |
1856 | if not 'soq_updated' in vals: |
1857 | vals['soq_updated'] = False |
1858 | |
1859 | + check_location_dest_ids = [] |
1860 | for line in self.browse(cr, uid, ids, context=context): |
1861 | new_vals = vals.copy() |
1862 | # check qty |
1863 | @@ -1343,6 +1347,9 @@ |
1864 | _('You can not have an order line with a negative or zero quantity') |
1865 | ) |
1866 | |
1867 | + if 'product_id' in vals and line.state in ('validated', 'validated_n', 'sourced_sy', 'sourced_v', 'sourced_n') and line.product_id.id != vals.get('product'): |
1868 | + check_location_dest_ids.append(line.id) |
1869 | + |
1870 | # try to fill "link_so_id": |
1871 | if not line.link_so_id and not vals.get('link_so_id'): |
1872 | linked_so = False |
1873 | @@ -1372,6 +1379,10 @@ |
1874 | 'po_line_id': line.id, |
1875 | }, context=context) |
1876 | |
1877 | + for line in self.browse(cr, uid, check_location_dest_ids, context=context): |
1878 | + super(purchase_order_line, self).write(cr, uid, [line.id], {'location_dest_id': self.final_location_dest(cr, uid, line, context=context)}, context=context) |
1879 | + |
1880 | + |
1881 | if vals.get('stock_take_date'): |
1882 | self._check_stock_take_date(cr, uid, ids, context=context) |
1883 | |
1884 | @@ -2074,6 +2085,44 @@ |
1885 | picking_id.write({}, context=context) |
1886 | return True |
1887 | |
1888 | + def final_location_dest(self, cr, uid, pol_obj, fo_obj=False, context=None): |
1889 | + data_obj = self.pool.get('ir.model.data') |
1890 | + |
1891 | + dest = pol_obj.order_id.location_id.id |
1892 | + |
1893 | + if not pol_obj.product_id: |
1894 | + return dest |
1895 | + |
1896 | + if pol_obj.product_id.type == 'service_recep' and not pol_obj.order_id.cross_docking_ok: |
1897 | + # service with reception are directed to Service Location |
1898 | + return self.pool.get('stock.location').get_service_location(cr, uid) |
1899 | + |
1900 | + if pol_obj.product_id.type == 'consu': |
1901 | + return data_obj.get_object_reference(cr, uid, 'stock_override', 'stock_location_non_stockable')[1] |
1902 | + |
1903 | + fo = fo_obj or pol_obj.linked_sol_id and pol_obj.linked_sol_id.order_id or False |
1904 | + if fo and fo.procurement_request and fo.location_requestor_id.usage != 'customer': |
1905 | + return fo.location_requestor_id.id |
1906 | + |
1907 | + chained = self.pool.get('stock.location').chained_location_get(cr, uid, pol_obj.order_id.location_id, product=pol_obj.product_id, context=context) |
1908 | + if chained: |
1909 | + if chained[0].chained_location_type == 'nomenclature': |
1910 | + # 1st round : Input > Stock, 2nd round Stock -> MED/LOG |
1911 | + chained2 = self.pool.get('stock.location').chained_location_get(cr, uid, chained[0], product=pol_obj.product_id, context=context) |
1912 | + if chained2: |
1913 | + return chained2[0].id |
1914 | + return chained[0].id |
1915 | + |
1916 | + return dest |
1917 | + |
1918 | + def open_po_form(self, cr, uid, ids, context=None): |
1919 | + pol = self.browse(cr, uid, ids[0], fields_to_fetch=['order_id'], context=context) |
1920 | + |
1921 | + res = self.pool.get('ir.actions.act_window').open_view_from_xmlid(cr, uid, 'purchase.purchase_form_action', ['form', 'tree'], new_tab=True, context=context) |
1922 | + res['keep_open'] = True |
1923 | + res['res_id'] = pol.order_id.id |
1924 | + return res |
1925 | + |
1926 | purchase_order_line() |
1927 | |
1928 | |
1929 | |
1930 | === modified file 'bin/addons/purchase/purchase_view.xml' |
1931 | --- bin/addons/purchase/purchase_view.xml 2020-04-14 07:48:08 +0000 |
1932 | +++ bin/addons/purchase/purchase_view.xml 2020-07-27 12:47:56 +0000 |
1933 | @@ -221,8 +221,10 @@ |
1934 | <button name="check_lines_to_fix" string="Check Lines" icon="gtk-dialog-warning" colspan="1" type="object" context="{'rfq_ok': False}"/> |
1935 | <button name="add_multiple_lines" string="Add multiple lines" icon="gtk-add" colspan="4" type="object"/> |
1936 | </group> |
1937 | - <button name="wizard_import_file" string="Import PO confirmation" icon="gtk-execute" colspan="1" type="object" attrs="{'invisible':[('state', 'not in', ['validated', 'validated_p'])]}"/> |
1938 | - <button name="export_po_integration" string="Export PO Validated" icon="gtk-execute" colspan="1" type="object" attrs="{'invisible':[('state', 'not in', ['validated', 'validated_p'])]}"/> |
1939 | + <group colspan="4" col="4" attrs="{'invisible':['&', ('state', 'not in', ['validated', 'validated_p']), '|', ('partner_type', 'not in', ['esc', 'external']), ('state', 'not in', ['confirmed', 'confirmed_p'])]}"> |
1940 | + <button name="wizard_import_file" string="Import PO confirmation" icon="gtk-execute" colspan="1" type="object" /> |
1941 | + <button name="export_po_integration" string="Export PO Validated" icon="gtk-execute" colspan="1" type="object" /> |
1942 | + </group> |
1943 | <field name="order_line" colspan="4" nolabel="1" mode="tree,form" |
1944 | context="{'purchase_id': active_id, 'partner_type': partner_type, 'categ': categ, 'pricelist_id': pricelist_id, 'rfq_ok': False, 'from_fo': po_from_fo or po_from_ir or False}" |
1945 | on_change="order_line_change(order_line)" |
1946 | @@ -314,8 +316,11 @@ |
1947 | <group colspan="4" col="4"> |
1948 | <separator colspan="4" string="Dates"/> |
1949 | <group colspan="2" col="3"> |
1950 | - <field name="delivery_requested_date" attrs="{'readonly': [('state', 'not in', ('draft', 'draft_p', 'validated'))]}" on_change="onchange_requested_date(partner_id,date_order,delivery_requested_date,est_transport_lead_time, order_type, context)"/> |
1951 | - <button colspan="1" name="update_date" string="Apply to lines" type="object" context="{'field_name': 'requested', 'type': 'purchase.order'}" icon="gtk-indent" attrs="{'invisible': [('state', 'not in', ('draft', 'draft_p', 'validated'))]}"/> |
1952 | + <group colspan="2" col="4"> |
1953 | + <field name="delivery_requested_date" attrs="{'readonly': [('state', '!=', 'draft')]}" on_change="onchange_requested_date(partner_id,date_order,delivery_requested_date,est_transport_lead_time, order_type, context)"/> |
1954 | + <field name="delivery_requested_date_modified" attrs="{'readonly': [('state', 'not in', ['draft_p', 'validated', 'validated_p'])]}" /> |
1955 | + </group> |
1956 | + <button colspan="1" name="update_date" string="Apply to lines" type="object" context="{'field_name': 'requested', 'type': 'purchase.order'}" icon="gtk-indent" attrs="{'invisible': [('state', 'not in', ('draft', 'draft_p', 'validated', 'validated_p'))]}"/> |
1957 | </group> |
1958 | <group colspan="2" col="3"> |
1959 | <field name="delivery_confirmed_date" attrs="{'readonly': [('state', 'not in', ['draft', 'draft_p', 'validated', 'validated_p', 'sourced_p'])]}"/> |
1960 | @@ -1007,6 +1012,52 @@ |
1961 | |
1962 | <menuitem action="purchase_line_form_action2" id="menu_purchase_line_order_draft" groups="base.group_extended" parent="menu_procurement_management_invoice" sequence="72"/> |
1963 | |
1964 | + <record id="purchase_order_line_pipeline_tree" model="ir.ui.view"> |
1965 | + <field name="name">purchase.order.line.tree</field> |
1966 | + <field name="model">purchase.order.line</field> |
1967 | + <field name="type">tree</field> |
1968 | + <field name="priority" eval="250" /> |
1969 | + <field name="arch" type="xml"> |
1970 | + <tree string="Purchase Order Lines" hide_delete_button="1" hide_new_button="1"> |
1971 | + <field name="order_id"/> |
1972 | + <field name="partner_id" string="Supplier"/> |
1973 | + <field name="line_number"/> |
1974 | + <field name="product_id"/> |
1975 | + <field name="price_unit"/> |
1976 | + <field name="product_qty"/> |
1977 | + <field name="product_uom"/> |
1978 | + <field name="date_planned" widget="date" /> |
1979 | + <field name="confirmed_delivery_date" widget="date" /> |
1980 | + <field name="state" /> |
1981 | + <button type="object" name="open_po_form" icon="terp-gtk-go-back-rtl" string="View PO" /> |
1982 | + </tree> |
1983 | + </field> |
1984 | + </record> |
1985 | + <record id="purchase_order_line_pipeline_search" model="ir.ui.view"> |
1986 | + <field name="name">purchase.order.line.search</field> |
1987 | + <field name="model">purchase.order.line</field> |
1988 | + <field name="type">search</field> |
1989 | + <field name="priority" eval="250" /> |
1990 | + <field name="arch" type="xml"> |
1991 | + <search> |
1992 | + <field name="order_id"/> |
1993 | + <field name="partner_id" string="Supplier"/> |
1994 | + <field name="product_id"/> |
1995 | + <field name="date_planned" widget="date" /> |
1996 | + <field name="confirmed_delivery_date" widget="date" /> |
1997 | + </search> |
1998 | + </field> |
1999 | + </record> |
2000 | + <record id="purchase_line_pipeline_action" model="ir.actions.act_window"> |
2001 | + <field name="name">PO Line</field> |
2002 | + <field name="type">ir.actions.act_window</field> |
2003 | + <field name="res_model">purchase.order.line</field> |
2004 | + <field name="domain"></field> |
2005 | + <field name="view_type">form</field> |
2006 | + <field name="view_mode">tree</field> |
2007 | + <field name="view_id" ref="purchase_order_line_pipeline_tree" /> |
2008 | + <field name="search_view_id" ref="purchase_order_line_pipeline_search"/> |
2009 | + </record> |
2010 | |
2011 | </data> |
2012 | </openerp> |
2013 | |
2014 | === modified file 'bin/addons/purchase/purchase_workflow.py' |
2015 | --- bin/addons/purchase/purchase_workflow.py 2020-01-23 16:34:09 +0000 |
2016 | +++ bin/addons/purchase/purchase_workflow.py 2020-07-27 12:47:56 +0000 |
2017 | @@ -18,7 +18,7 @@ |
2018 | if pol.is_line_split: |
2019 | split_po_ids = self.search(cr, uid, [('is_line_split', '=', False), ('line_number', '=', pol.line_number), ('order_id', '=', pol.order_id.id)], context=context) |
2020 | if split_po_ids: |
2021 | - split_po = self.browse(cr, uid, split_po_ids[0], context=context) |
2022 | + split_po = self.browse(cr, uid, split_po_ids[0], fields_to_fetch=['linked_sol_id'], context=context) |
2023 | if split_po.linked_sol_id: |
2024 | sol_values['line_number'] = split_po.linked_sol_id.line_number |
2025 | return sol_values |
2026 | @@ -148,6 +148,7 @@ |
2027 | if pol.stock_take_date: |
2028 | line_stock_take = pol.stock_take_date |
2029 | |
2030 | + |
2031 | sol_values = { |
2032 | 'product_id': pol.product_id and pol.product_id.id or False, |
2033 | 'name': pol.name, |
2034 | @@ -171,14 +172,16 @@ |
2035 | 'nomen_sub_3': pol.nomen_sub_3 and pol.nomen_sub_3.id or False, |
2036 | 'nomen_sub_4': pol.nomen_sub_4 and pol.nomen_sub_4.id or False, |
2037 | 'nomen_sub_5': pol.nomen_sub_5 and pol.nomen_sub_5.id or False, |
2038 | - 'confirmed_delivery_date': line_confirmed, |
2039 | 'stock_take_date': line_stock_take, |
2040 | + 'date_planned': pol.date_planned, |
2041 | 'sync_sourced_origin': pol.instance_sync_order_ref and pol.instance_sync_order_ref.name or False, |
2042 | 'type': 'make_to_order', |
2043 | 'is_line_split': pol.is_line_split, |
2044 | 'original_line_id': pol.original_line_id.linked_sol_id.id if pol.original_line_id else False, |
2045 | 'procurement_request': sale_order.procurement_request, |
2046 | } |
2047 | + if pol.state not in ['confirmed', 'done', 'cancel', 'cancel_r']: |
2048 | + sol_values['confirmed_delivery_date'] = line_confirmed |
2049 | |
2050 | # update modification comment if it is set |
2051 | if pol.modification_comment: |
2052 | @@ -341,7 +344,7 @@ |
2053 | 'nomen_sub_5': pol.nomen_sub_5 and pol.nomen_sub_5.id or False, |
2054 | 'confirmed_delivery_date': line_confirmed, |
2055 | 'stock_take_date': line_stock_take, |
2056 | - 'date_planned': (datetime.now() + relativedelta(days=+2)).strftime('%Y-%m-%d'), |
2057 | + 'date_planned': pol.date_planned or (datetime.now() + relativedelta(days=+2)).strftime('%Y-%m-%d'), |
2058 | 'sync_sourced_origin': pol.instance_sync_order_ref and pol.instance_sync_order_ref.name or False, |
2059 | 'set_as_sourced_n': True, |
2060 | } |
2061 | @@ -363,7 +366,7 @@ |
2062 | new_sol_id = self.pool.get('sale.order.line').create(cr, uid, sol_values, context=context) |
2063 | |
2064 | # update current PO line: |
2065 | - self.write(cr, uid, pol.id, {'link_so_id': fo_id, 'linked_sol_id': new_sol_id}, context=context) |
2066 | + self.write(cr, uid, pol.id, {'link_so_id': fo_id, 'linked_sol_id': new_sol_id, 'location_dest_id': self.final_location_dest(cr, uid, pol, fo_obj=sale_order, context=context)}, context=context) |
2067 | |
2068 | context['from_back_sync'] = False |
2069 | return new_sol_id |
2070 | @@ -582,7 +585,8 @@ |
2071 | # doesn't update original qty and uom if already set (from IR) |
2072 | line_update = { |
2073 | 'original_price': pol.price_unit, |
2074 | - 'original_currency_id': pol.currency_id.id |
2075 | + 'original_currency_id': pol.currency_id.id, |
2076 | + 'location_dest_id': self.final_location_dest(cr, uid, pol, context=context), |
2077 | } |
2078 | |
2079 | if not pol.original_product: |
2080 | |
2081 | === modified file 'bin/addons/stock/product.py' |
2082 | --- bin/addons/stock/product.py 2020-01-09 14:41:10 +0000 |
2083 | +++ bin/addons/stock/product.py 2020-07-27 12:47:56 +0000 |
2084 | @@ -426,6 +426,44 @@ |
2085 | res['fields']['qty_available']['string'] = _('Produced Qty') |
2086 | return res |
2087 | |
2088 | + def get_pipeline_from_po(self, cr, uid, ids, from_date=False, to_date=False, location_ids=False, context=None): |
2089 | + ''' |
2090 | + ids: product_ids |
2091 | + |
2092 | + return the pipeline from validated(-p) purchase order line |
2093 | + ''' |
2094 | + |
2095 | + params = [] |
2096 | + query = '' |
2097 | + if location_ids: |
2098 | + query += ' and location_dest_id in %s ' |
2099 | + if isinstance(location_ids, (int, long)): |
2100 | + params.append((location_ids, )) |
2101 | + else: |
2102 | + params.append(tuple(location_ids)) |
2103 | + |
2104 | + if from_date: |
2105 | + query += ' and coalesce(confirmed_delivery_date, date_planned) > %s ' |
2106 | + params.append(from_date) |
2107 | + |
2108 | + if to_date: |
2109 | + query += ' and coalesce(confirmed_delivery_date, date_planned) <= %s ' |
2110 | + params.append(to_date) |
2111 | + |
2112 | + |
2113 | + cr.execute(''' |
2114 | + select |
2115 | + pol.product_id, sum(pol.product_qty) |
2116 | + from |
2117 | + purchase_order_line pol |
2118 | + where |
2119 | + pol.product_id in %s and |
2120 | + pol.state in ('validated', 'validated_n', 'sourced_sy', 'sourced_v', 'sourced_n') |
2121 | + ''' + query + ''' |
2122 | + group by pol.product_id''', |
2123 | + [tuple(ids)]+params) # not_a_user_entry |
2124 | + return dict(cr.fetchall()) |
2125 | + |
2126 | product_product() |
2127 | |
2128 | class product_template(osv.osv): |
2129 | |
2130 | === modified file 'bin/addons/stock/stock_move.py' |
2131 | --- bin/addons/stock/stock_move.py 2020-01-29 16:41:23 +0000 |
2132 | +++ bin/addons/stock/stock_move.py 2020-07-27 12:47:56 +0000 |
2133 | @@ -2415,5 +2415,14 @@ |
2134 | return { |
2135 | 'value': {'integrity_error': 'empty'} |
2136 | } |
2137 | + |
2138 | + def open_in_form(self, cr, uid, ids, context=None): |
2139 | + move = self.browse(cr, uid, ids[0], fields_to_fetch=['picking_id', 'linked_incoming_move'], context=context) |
2140 | + |
2141 | + res = self.pool.get('ir.actions.act_window').open_view_from_xmlid(cr, uid, 'stock.action_picking_tree4', ['form', 'tree'], new_tab=True, context=context) |
2142 | + res['keep_open'] = True |
2143 | + res['res_id'] = move.linked_incoming_move and move.linked_incoming_move.picking_id.id or move.picking_id.id |
2144 | + return res |
2145 | + |
2146 | stock_move() |
2147 | |
2148 | |
2149 | === modified file 'bin/addons/stock/stock_view.xml' |
2150 | --- bin/addons/stock/stock_view.xml 2020-02-25 10:55:04 +0000 |
2151 | +++ bin/addons/stock/stock_view.xml 2020-07-27 12:47:56 +0000 |
2152 | @@ -1251,6 +1251,7 @@ |
2153 | <field name="ssl_check" invisible="True" widget="null_boolean" /> |
2154 | <field name="dg_check" widget="null_boolean" /> |
2155 | <field name="np_check" widget="null_boolean" /> |
2156 | + <button type="object" name="open_in_form" icon="terp-gtk-go-back-rtl" string="View IN" /> |
2157 | </tree> |
2158 | </field> |
2159 | </record> |
2160 | |
2161 | === modified file 'bin/osv/orm.py' |
2162 | --- bin/osv/orm.py 2020-05-25 09:59:55 +0000 |
2163 | +++ bin/osv/orm.py 2020-07-27 12:47:56 +0000 |
2164 | @@ -2117,7 +2117,7 @@ |
2165 | ''' |
2166 | return self.search(cr, user, args, offset, limit, order, context, count) |
2167 | |
2168 | - def search_exist(self, cr, user, args, context=None): |
2169 | + def search_exists(self, cr, user, args, context=None): |
2170 | """ |
2171 | return True if there is at least one element matching the criterions, |
2172 | False otherwise. |
2173 | @@ -2125,6 +2125,9 @@ |
2174 | return bool(self.search(cr, user, args, context=context, |
2175 | limit=1, order='NO_ORDER')) |
2176 | |
2177 | + def search_exist(self, cr, user, args, context=None): |
2178 | + return self.search_exists(cr, user, args, context=context) |
2179 | + |
2180 | def search(self, cr, user, args, offset=0, limit=None, order=None, context=None, count=False): |
2181 | """ |
2182 | Search for records based on a search domain. |