Merge lp:~camptocamp/purchase-wkfl/7.0-merge-po-hooks-lep into lp:~purchase-core-editors/purchase-wkfl/7.0
- 7.0-merge-po-hooks-lep
- Merge into 7.0
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Yannick Vaucher @ Camptocamp | ||||
Approved revision: | 41 | ||||
Merged at revision: | 34 | ||||
Proposed branch: | lp:~camptocamp/purchase-wkfl/7.0-merge-po-hooks-lep | ||||
Merge into: | lp:~purchase-core-editors/purchase-wkfl/7.0 | ||||
Diff against target: |
480 lines (+448/-0) 6 files modified
purchase_group_hooks/__init__.py (+22/-0) purchase_group_hooks/__openerp__.py (+47/-0) purchase_group_hooks/purchase_group_hooks.py (+236/-0) purchase_group_hooks/test/merge_order.yml (+41/-0) purchase_group_hooks/tests/__init__.py (+26/-0) purchase_group_hooks/tests/test_merge_po.py (+76/-0) |
||||
To merge this branch: | bzr merge lp:~camptocamp/purchase-wkfl/7.0-merge-po-hooks-lep | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Yannick Vaucher @ Camptocamp | code review, no test | Approve | |
martin (community) | Approve | ||
Pedro Manuel Baeza | Abstain | ||
Alexandre Fayolle - camptocamp | code review, no test | Approve | |
Romain Deheele - Camptocamp (community) | Abstain | ||
Review via email: mp+216745@code.launchpad.net |
Commit message
Description of the change
Pedro Manuel Baeza (pedro.baeza) wrote : | # |
Leonardo Pistone (lepistone) wrote : | # |
Hi Pedro.
For trunk, I am actually going to propose it in the official branches tomorrow. See lp:~camptocamp/openobject-addons/trunk-refactor-po-merge-lep
For 7.0, I thought it would be easier to do an extra module.
Thanks!
Leonardo Pistone (lepistone) wrote : | # |
Pedro, I proposed this to core trunk:
https:/
Romain Deheele - Camptocamp (romaindeheele) wrote : | # |
I asked myself about the way to merge origins.
A case seems not managed:
If a PO has origins "A B" and an other PO has "B C", merge result won't be "A B C".
With standard OE processes, it's probably impossible, it's a bit a far-fetched case, so I abstain me.
Leonardo Pistone (lepistone) wrote : | # |
Your point makes sense, Romain.
I would rather leave it as it is, since:
1. This is just a refactor with a few tests added. It is probably better to keep the existing functionality for now.
2. I'm not sure that can happen often in practice, and the implementation could use string manipulation (brittle) or new field (complex).
Thanks!
martin (riess82) wrote : | # |
I would like to suggest the following changes to purchase_
- Change _key_fields_
- function _update_
Leonardo Pistone (lepistone) wrote : | # |
You are right Martin, thanks.
You can set review: Needs fixing and press "save comment" (no comment is OK because you already commented).
martin (riess82) : | # |
Leonardo Pistone (lepistone) wrote : | # |
I fixed martin's remarks, and added a new unit test. We're back into "needs review".
Alexandre Fayolle - camptocamp (alexandre-fayolle-c2c) wrote : | # |
LGTM
Leonardo Pistone (lepistone) wrote : | # |
Martin, can you please reconsider your review? your points should be fixed now.
Pedro Manuel Baeza (pedro.baeza) wrote : | # |
I abstain for the moment until I can review the MP.
Leonardo Pistone (lepistone) wrote : | # |
highlights from the similar MP on trunk: Olivier Dony said they will merge on the core in trunk but not in 7.0, so this extra addon for 7.0 makes sense. Olivier made some remarks I agree with, they'll be fixed here as well ASAP.
thanks!
martin (riess82) wrote : | # |
I would suggest that both _key_fields_
Leonardo Pistone (lepistone) wrote : | # |
Martin,
I am not convinced we need mutability here. Couldn't we still do "return res + ('c',)"?
martin (riess82) wrote : | # |
Leonardo, you are right. Both methods will work.
Yannick Vaucher @ Camptocamp (yvaucher-c2c) wrote : | # |
martin, can you change your status here ? Or is it intended to let it in Needs Fixing ?
martin (riess82) : | # |
Yannick Vaucher @ Camptocamp (yvaucher-c2c) wrote : | # |
LGTM
Preview Diff
1 | === added directory 'purchase_group_hooks' |
2 | === added file 'purchase_group_hooks/__init__.py' |
3 | --- purchase_group_hooks/__init__.py 1970-01-01 00:00:00 +0000 |
4 | +++ purchase_group_hooks/__init__.py 2014-04-29 08:56:20 +0000 |
5 | @@ -0,0 +1,22 @@ |
6 | +# -*- coding: utf-8 -*- |
7 | +############################################################################## |
8 | +# |
9 | +# Author: Leonardo Pistone |
10 | +# Copyright 2014 Camptocamp SA |
11 | +# |
12 | +# This program is free software: you can redistribute it and/or modify |
13 | +# it under the terms of the GNU Affero General Public License as |
14 | +# published by the Free Software Foundation, either version 3 of the |
15 | +# License, or (at your option) any later version. |
16 | +# |
17 | +# This program is distributed in the hope that it will be useful, |
18 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
19 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
20 | +# GNU Affero General Public License for more details. |
21 | +# |
22 | +# You should have received a copy of the GNU Affero General Public License |
23 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
24 | +# |
25 | +############################################################################## |
26 | + |
27 | +from . import purchase_group_hooks # noqa |
28 | |
29 | === added file 'purchase_group_hooks/__openerp__.py' |
30 | --- purchase_group_hooks/__openerp__.py 1970-01-01 00:00:00 +0000 |
31 | +++ purchase_group_hooks/__openerp__.py 2014-04-29 08:56:20 +0000 |
32 | @@ -0,0 +1,47 @@ |
33 | +# -*- coding: utf-8 -*- |
34 | +############################################################################## |
35 | +# |
36 | +# Author: Leonardo Pistone |
37 | +# Copyright 2014 Camptocamp SA |
38 | +# |
39 | +# This program is free software: you can redistribute it and/or modify |
40 | +# it under the terms of the GNU Affero General Public License as |
41 | +# published by the Free Software Foundation, either version 3 of the |
42 | +# License, or (at your option) any later version. |
43 | +# |
44 | +# This program is distributed in the hope that it will be useful, |
45 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
46 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
47 | +# GNU Affero General Public License for more details. |
48 | +# |
49 | +# You should have received a copy of the GNU Affero General Public License |
50 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
51 | +# |
52 | +############################################################################## |
53 | +{'name': 'Add hooks to the merge PO feature.', |
54 | + 'version': '0.1', |
55 | + 'author': 'Camptocamp', |
56 | + 'maintainer': 'Camptocamp', |
57 | + 'category': 'Purchase Management', |
58 | + 'complexity': "normal", |
59 | + 'depends': ['purchase'], |
60 | + 'description': """ |
61 | + In the core OpenERP purchase module, there is a wizard to merge purchase |
62 | + orders. That feature is convenient, but as soon as a field is added to the |
63 | + purchase order, it does not work anymore and needs to be patched. |
64 | + The original implementation does not provide any hooks for extension, and |
65 | + modules can only reimplement a method completely. This required a lot of copy |
66 | + and paste, and worse, it breaks if two modules attempt to do that. |
67 | + |
68 | + Therefore, this module reimplements the feature, with the same basic result |
69 | + in the standard case. Hooks are provided for extra modules that add fields |
70 | + or change the logic. |
71 | + """, |
72 | + 'website': 'http://www.camptocamp.com/', |
73 | + 'data': [], |
74 | + 'installable': True, |
75 | + 'auto_install': False, |
76 | + 'license': 'AGPL-3', |
77 | + 'application': False, |
78 | + 'test': ['test/merge_order.yml'], |
79 | + } |
80 | |
81 | === added file 'purchase_group_hooks/purchase_group_hooks.py' |
82 | --- purchase_group_hooks/purchase_group_hooks.py 1970-01-01 00:00:00 +0000 |
83 | +++ purchase_group_hooks/purchase_group_hooks.py 2014-04-29 08:56:20 +0000 |
84 | @@ -0,0 +1,236 @@ |
85 | +# -*- coding: utf-8 -*- |
86 | +############################################################################## |
87 | +# |
88 | +# Author: Leonardo Pistone |
89 | +# Copyright 2014 Camptocamp SA |
90 | +# |
91 | +# This program is free software: you can redistribute it and/or modify |
92 | +# it under the terms of the GNU Affero General Public License as |
93 | +# published by the Free Software Foundation, either version 3 of the |
94 | +# License, or (at your option) any later version. |
95 | +# |
96 | +# This program is distributed in the hope that it will be useful, |
97 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
98 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
99 | +# GNU Affero General Public License for more details. |
100 | +# |
101 | +# You should have received a copy of the GNU Affero General Public License |
102 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
103 | +# |
104 | +############################################################################## |
105 | + |
106 | +from openerp.osv.orm import Model |
107 | +from openerp import netsvc |
108 | +from openerp.osv.orm import browse_record, browse_null |
109 | + |
110 | + |
111 | +class PurchaseOrder(Model): |
112 | + _inherit = 'purchase.order' |
113 | + |
114 | + def _key_fields_for_grouping(self): |
115 | + """Return a list of fields used to identify orders that can be merged. |
116 | + |
117 | + Orders that have this fields equal can be merged. |
118 | + |
119 | + This function can be extended by other modules to modify the list. |
120 | + """ |
121 | + return ('partner_id', 'location_id', 'pricelist_id') |
122 | + |
123 | + def _key_fields_for_grouping_lines(self): |
124 | + """Return a list of fields used to identify order lines that can be |
125 | + merged. |
126 | + |
127 | + Lines that have this fields equal can be merged. |
128 | + |
129 | + This function can be extended by other modules to modify the list. |
130 | + """ |
131 | + return ('name', 'date_planned', 'taxes_id', 'price_unit', 'product_id', |
132 | + 'move_dest_id', 'account_analytic_id') |
133 | + |
134 | + def _make_key_for_grouping(self, order, fields): |
135 | + """From an order, return a tuple to be used as a key. |
136 | + |
137 | + If two orders have the same key, they can be merged. |
138 | + """ |
139 | + key_list = [] |
140 | + for field in fields: |
141 | + field_value = getattr(order, field) |
142 | + |
143 | + if isinstance(field_value, browse_record): |
144 | + field_value = field_value.id |
145 | + elif isinstance(field_value, browse_null): |
146 | + field_value = False |
147 | + elif isinstance(field_value, list): |
148 | + field_value = ((6, 0, tuple([v.id for v in field_value])),) |
149 | + key_list.append((field, field_value)) |
150 | + key_list.sort() |
151 | + return tuple(key_list) |
152 | + |
153 | + def _can_merge(self, order): |
154 | + """Can the order be considered for merging with others? |
155 | + |
156 | + This method can be surcharged in other modules. |
157 | + """ |
158 | + return order.state == 'draft' |
159 | + |
160 | + def _initial_merged_order_data(self, order): |
161 | + """Build the initial values of a merged order.""" |
162 | + return { |
163 | + 'origin': order.origin, |
164 | + 'date_order': order.date_order, |
165 | + 'partner_id': order.partner_id.id, |
166 | + 'dest_address_id': order.dest_address_id.id, |
167 | + 'warehouse_id': order.warehouse_id.id, |
168 | + 'location_id': order.location_id.id, |
169 | + 'pricelist_id': order.pricelist_id.id, |
170 | + 'state': 'draft', |
171 | + 'order_line': {}, |
172 | + 'notes': '%s' % (order.notes or '',), |
173 | + 'fiscal_position': ( |
174 | + order.fiscal_position and order.fiscal_position.id or False |
175 | + ), |
176 | + } |
177 | + |
178 | + def _update_merged_order_data(self, merged_data, order): |
179 | + if order.date_order < merged_data['date_order']: |
180 | + merged_data['date_order'] = order.date_order |
181 | + if order.notes: |
182 | + merged_data['notes'] = ( |
183 | + (merged_data['notes'] or '') + ('\n%s' % (order.notes,)) |
184 | + ) |
185 | + if order.origin: |
186 | + if ( |
187 | + order.origin not in merged_data['origin'] |
188 | + and merged_data['origin'] not in order.origin |
189 | + ): |
190 | + merged_data['origin'] = ( |
191 | + (merged_data['origin'] or '') + ' ' + order.origin |
192 | + ) |
193 | + return merged_data |
194 | + |
195 | + def _group_orders(self, input_orders): |
196 | + """Return a dictionary where each element is in the form: |
197 | + |
198 | + tuple_key: (dict_of_new_order_data, list_of_old_order_ids) |
199 | + |
200 | + """ |
201 | + key_fields = self._key_fields_for_grouping() |
202 | + grouped_orders = {} |
203 | + |
204 | + if len(input_orders) < 2: |
205 | + return {} |
206 | + |
207 | + for input_order in input_orders: |
208 | + key = self._make_key_for_grouping(input_order, key_fields) |
209 | + if key in grouped_orders: |
210 | + grouped_orders[key] = ( |
211 | + self._update_merged_order_data( |
212 | + grouped_orders[key][0], |
213 | + input_order |
214 | + ), |
215 | + grouped_orders[key][1] + [input_order.id] |
216 | + ) |
217 | + else: |
218 | + grouped_orders[key] = ( |
219 | + self._initial_merged_order_data(input_order), |
220 | + [input_order.id] |
221 | + ) |
222 | + grouped_order_data = grouped_orders[key][0] |
223 | + |
224 | + for input_line in input_order.order_line: |
225 | + line_key = self._make_key_for_grouping( |
226 | + input_line, |
227 | + self._key_fields_for_grouping_lines() |
228 | + ) |
229 | + o_line = grouped_order_data['order_line'].setdefault( |
230 | + line_key, {} |
231 | + ) |
232 | + if o_line: |
233 | + # merge the line with an existing line |
234 | + o_line['product_qty'] += ( |
235 | + input_line.product_qty |
236 | + * input_line.product_uom.factor |
237 | + / o_line['uom_factor'] |
238 | + ) |
239 | + else: |
240 | + # append a new "standalone" line |
241 | + for field in ('product_qty', 'product_uom'): |
242 | + field_val = getattr(input_line, field) |
243 | + if isinstance(field_val, browse_record): |
244 | + field_val = field_val.id |
245 | + o_line[field] = field_val |
246 | + o_line['uom_factor'] = ( |
247 | + input_line.product_uom |
248 | + and input_line.product_uom.factor |
249 | + or 1.0) |
250 | + |
251 | + return self._cleanup_merged_line_data(grouped_orders) |
252 | + |
253 | + def _cleanup_merged_line_data(self, grouped_orders): |
254 | + """Remove keys from merged lines, and merges of 1 order.""" |
255 | + result = {} |
256 | + for order_key, (order_data, old_ids) in grouped_orders.iteritems(): |
257 | + if len(old_ids) > 1: |
258 | + for key, value in order_data['order_line'].iteritems(): |
259 | + del value['uom_factor'] |
260 | + value.update(dict(key)) |
261 | + order_data['order_line'] = [ |
262 | + (0, 0, value) |
263 | + for value in order_data['order_line'].itervalues() |
264 | + ] |
265 | + result[order_key] = (order_data, old_ids) |
266 | + return result |
267 | + |
268 | + def _create_new_orders(self, cr, uid, grouped_orders, context=None): |
269 | + """Create the new merged orders in the database. |
270 | + |
271 | + Return a dictionary that puts the created order ids in relation to the |
272 | + original ones, in the form |
273 | + |
274 | + new_order_id: [old_order_1_id, old_order_2_id] |
275 | + |
276 | + """ |
277 | + new_old_rel = {} |
278 | + for key in grouped_orders: |
279 | + new_order_data, old_order_ids = grouped_orders[key] |
280 | + new_id = self.create(cr, uid, new_order_data, context=context) |
281 | + new_old_rel[new_id] = old_order_ids |
282 | + return new_old_rel |
283 | + |
284 | + def _fix_workflow(self, cr, uid, new_old_rel): |
285 | + """Fix the workflow of the old and new orders. |
286 | + |
287 | + Specifically, cancel the old ones and assign workflows to the new ones. |
288 | + |
289 | + """ |
290 | + wf_service = netsvc.LocalService("workflow") |
291 | + for new_order_id in new_old_rel: |
292 | + old_order_ids = new_old_rel[new_order_id] |
293 | + for old_id in old_order_ids: |
294 | + wf_service.trg_redirect(uid, 'purchase.order', old_id, |
295 | + new_order_id, cr) |
296 | + wf_service.trg_validate(uid, 'purchase.order', old_id, |
297 | + 'purchase_cancel', cr) |
298 | + |
299 | + def do_merge(self, cr, uid, input_order_ids, context=None): |
300 | + """Merge Purchase Orders. |
301 | + |
302 | + This method replaces the original one in the purchase module because |
303 | + it did not provide any hooks for customization. |
304 | + |
305 | + Receive a list of order ids, and return a dictionary where each |
306 | + element is in the form: |
307 | + |
308 | + new_order_id: [old_order_1_id, old_order_2_id] |
309 | + |
310 | + New orders are created, and old orders are deleted. |
311 | + |
312 | + """ |
313 | + input_orders = self.browse(cr, uid, input_order_ids, context=context) |
314 | + mergeable_orders = filter(self._can_merge, input_orders) |
315 | + grouped_orders = self._group_orders(mergeable_orders) |
316 | + |
317 | + new_old_rel = self._create_new_orders(cr, uid, grouped_orders, |
318 | + context=context) |
319 | + self._fix_workflow(cr, uid, new_old_rel) |
320 | + return new_old_rel |
321 | |
322 | === added directory 'purchase_group_hooks/test' |
323 | === added file 'purchase_group_hooks/test/merge_order.yml' |
324 | --- purchase_group_hooks/test/merge_order.yml 1970-01-01 00:00:00 +0000 |
325 | +++ purchase_group_hooks/test/merge_order.yml 2014-04-29 08:56:20 +0000 |
326 | @@ -0,0 +1,41 @@ |
327 | +- |
328 | + In order to merge RFQ, I merge two RFQ which has same supplier and check new merged order. |
329 | +- |
330 | + !python {model: purchase.order}: | |
331 | + new_id = self.do_merge(cr, uid, [ref('purchase.purchase_order_4'), ref('purchase.purchase_order_7')]) |
332 | + order4 = self.browse(cr, uid, ref('purchase.purchase_order_4')) |
333 | + order7 = self.browse(cr, uid, ref('purchase.purchase_order_7')) |
334 | + total_qty = sum([x.product_qty for x in order4.order_line] + [x.product_qty for x in order7.order_line]) |
335 | + |
336 | + assert order4.state == 'cancel', "Merged order should be canceled" |
337 | + assert order7.state == 'cancel', "Merged order should be canceled" |
338 | + |
339 | + def merged_data(lines): |
340 | + product_id =[] |
341 | + product_uom = [] |
342 | + res = {} |
343 | + for line in lines: |
344 | + product_id.append(line.product_id.id) |
345 | + product_uom.append(line.product_uom.id) |
346 | + res.update({'product_ids': product_id,'product_uom':product_uom}) |
347 | + return res |
348 | + |
349 | + for order in self.browse(cr, uid, new_id.keys()): |
350 | + total_new_qty = [x.product_qty for x in order.order_line] |
351 | + total_new_qty = sum(total_new_qty) |
352 | + |
353 | + assert total_new_qty == total_qty,"product quantities are not correspond: {} != {}".format(total_new_qty, total_qty) |
354 | + assert order.partner_id == order4.partner_id ,"partner is not correspond" |
355 | + assert order.warehouse_id == order4.warehouse_id or order7.warehouse_id,"Warehouse is not correspond" |
356 | + assert order.state == 'draft',"New created order state should be in draft" |
357 | + assert order.pricelist_id == order4.pricelist_id,"Price list is not correspond" |
358 | + assert order.date_order == order4.date_order ,"Date of order is not correspond" |
359 | + assert order.location_id == order4.location_id ,"Location is not correspond" |
360 | + n_product_data = merged_data(order.order_line) |
361 | + o_product_data= merged_data(order4.order_line) |
362 | + o_pro_data = merged_data(order7.order_line) |
363 | + |
364 | + assert n_product_data == o_product_data or o_pro_data,"product data are not correspond" |
365 | + |
366 | + |
367 | + |
368 | |
369 | === added directory 'purchase_group_hooks/tests' |
370 | === added file 'purchase_group_hooks/tests/__init__.py' |
371 | --- purchase_group_hooks/tests/__init__.py 1970-01-01 00:00:00 +0000 |
372 | +++ purchase_group_hooks/tests/__init__.py 2014-04-29 08:56:20 +0000 |
373 | @@ -0,0 +1,26 @@ |
374 | +# -*- coding: utf-8 -*- |
375 | +############################################################################## |
376 | +# |
377 | +# Author: Leonardo Pistone |
378 | +# Copyright 2014 Camptocamp SA |
379 | +# |
380 | +# This program is free software: you can redistribute it and/or modify |
381 | +# it under the terms of the GNU Affero General Public License as |
382 | +# published by the Free Software Foundation, either version 3 of the |
383 | +# License, or (at your option) any later version. |
384 | +# |
385 | +# This program is distributed in the hope that it will be useful, |
386 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
387 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
388 | +# GNU Affero General Public License for more details. |
389 | +# |
390 | +# You should have received a copy of the GNU Affero General Public License |
391 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
392 | +# |
393 | +############################################################################## |
394 | + |
395 | +from . import test_merge_po |
396 | + |
397 | +checks = [ |
398 | + test_merge_po, |
399 | +] |
400 | |
401 | === added file 'purchase_group_hooks/tests/test_merge_po.py' |
402 | --- purchase_group_hooks/tests/test_merge_po.py 1970-01-01 00:00:00 +0000 |
403 | +++ purchase_group_hooks/tests/test_merge_po.py 2014-04-29 08:56:20 +0000 |
404 | @@ -0,0 +1,76 @@ |
405 | +from mock import Mock |
406 | + |
407 | +from openerp.tests.common import BaseCase |
408 | +from openerp.osv.orm import browse_record |
409 | + |
410 | + |
411 | +class TestGroupOrders(BaseCase): |
412 | + |
413 | + def setUp(self): |
414 | + super(TestGroupOrders, self).setUp() |
415 | + self.order1 = Mock() |
416 | + self.order2 = Mock() |
417 | + |
418 | + self.order1.order_line = self.order2.order_line = [] |
419 | + self.order1.origin = self.order2.origin = '' |
420 | + self.order1.notes = self.order2.notes = '' |
421 | + |
422 | + # I have to use the registry to get an instance of a model. I cannot |
423 | + # use the class constructor because that is modified to return nothing. |
424 | + self.po = self.registry('purchase.order') |
425 | + |
426 | + def test_no_orders(self): |
427 | + """Group an empty list of orders as an empty dictionary.""" |
428 | + |
429 | + grouped = self.po._group_orders([]) |
430 | + self.assertEquals(grouped, {}) |
431 | + |
432 | + def test_one_order(self): |
433 | + """A single order will not be grouped.""" |
434 | + grouped = self.po._group_orders([self.order1]) |
435 | + self.assertEquals(grouped, {}) |
436 | + |
437 | + def test_two_similar_orders(self): |
438 | + """Two orders with the right conditions can be merged. |
439 | + |
440 | + We do not care about the order lines here. |
441 | + """ |
442 | + self.order1.partner_id = self.order2.partner_id = Mock( |
443 | + spec=browse_record, id=1) |
444 | + self.order1.location_id = self.order2.location_id = Mock( |
445 | + spec=browse_record, id=2) |
446 | + self.order1.pricelist_id = self.order2.pricelist_id = Mock( |
447 | + spec=browse_record, id=3) |
448 | + |
449 | + self.order1.id = 51 |
450 | + self.order2.id = 52 |
451 | + |
452 | + grouped = self.po._group_orders([self.order1, self.order2]) |
453 | + expected_key = (('location_id', 2), ('partner_id', 1), |
454 | + ('pricelist_id', 3)) |
455 | + self.assertEquals(grouped.keys(), [expected_key]) |
456 | + self.assertEquals(grouped[expected_key][1], [51, 52]) |
457 | + |
458 | + def test_merge_origin_and_notes(self): |
459 | + self.order1.origin = 'ORIGIN1' |
460 | + self.order2.origin = 'ORIGIN2' |
461 | + |
462 | + self.order1.notes = 'Notes1' |
463 | + self.order2.notes = 'Notes2' |
464 | + |
465 | + self.order1.partner_id = self.order2.partner_id = Mock( |
466 | + spec=browse_record, id=1) |
467 | + self.order1.location_id = self.order2.location_id = Mock( |
468 | + spec=browse_record, id=2) |
469 | + self.order1.pricelist_id = self.order2.pricelist_id = Mock( |
470 | + spec=browse_record, id=3) |
471 | + |
472 | + grouped = self.po._group_orders([self.order1, self.order2]) |
473 | + |
474 | + expected_key = (('location_id', 2), ('partner_id', 1), |
475 | + ('pricelist_id', 3)) |
476 | + |
477 | + merged_data = grouped[expected_key][0] |
478 | + |
479 | + self.assertEquals(merged_data['origin'], 'ORIGIN1 ORIGIN2') |
480 | + self.assertEquals(merged_data['notes'], 'Notes1\nNotes2') |
Hi, Leonardo, very good workaround, but I think it deserves to propose this change to 7.0/trunk official branches instead of a module, so that we don't have to update module with each change/fix made in the replaced part. It doesn't imply changes in DB layout, so it can be merged in stable 7.0, but at least if we get this on trunk, it's a good win.
Regards.