Merge lp:~dorian-kemps/unifield-server/US-3372 into lp:unifield-server

Proposed by jftempo on 2017-09-08
Status: Merged
Merged at revision: 4501
Proposed branch: lp:~dorian-kemps/unifield-server/US-3372
Merge into: lp:unifield-server
Diff against target: 536 lines (+228/-200)
2 files modified
bin/addons/sale/report/sale_loan_stock_moves_report.py (+127/-125)
bin/addons/sale/report/sale_loan_stock_moves_report_xls.mako (+101/-75)
To merge this branch: bzr merge lp:~dorian-kemps/unifield-server/US-3372
Reviewer Review Type Date Requested Status
UniField Reviewer Team 2017-09-08 Pending
Review via email: mp+330423@code.launchpad.net
To post a comment you must log in.
4494. By Dorian on 2017-09-11

US-3372 [FIX] Fixed and error where a PO was fetched instead of a SO

4495. By Dorian on 2017-09-12

US-3372 [IN PROGRESS]

4496. By Dorian on 2017-09-13

US-3372 [IN PROGRESS]

4497. By Dorian on 2017-09-13

US-3372 [IMP] Merged with lp:~jfb-tempo-consulting/unifield-server/us-3372. The unit price is computed from the move's currency to the instance's currency, fixed an id problem and changed mako columns' width

4498. By Dorian on 2017-09-13

US-3372 [FIX] Fixed an substing index

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'bin/addons/sale/report/sale_loan_stock_moves_report.py'
2--- bin/addons/sale/report/sale_loan_stock_moves_report.py 2017-08-25 16:02:26 +0000
3+++ bin/addons/sale/report/sale_loan_stock_moves_report.py 2017-09-13 12:13:20 +0000
4@@ -31,24 +31,26 @@
5 super(sale_loan_stock_moves_report_parser, self).__init__(cr, uid, name, context=context)
6 self.cr = cr
7 self.uid = uid
8+ self.user_company = self._get_user_company()
9 self.localcontext.update({
10 'time': time,
11 'getMoves': self._get_moves,
12 'isQtyOut': self._is_qty_out,
13 'getQty': self._get_qty,
14- 'getInstance': self._get_instance,
15+ 'getUserCompany': self._get_user_company,
16+ 'getFirstSplitOnUnderscore': self._get_first_split_on_underscore,
17+ 'computeCurrency': self._compute_currency,
18 })
19
20 def _is_qty_out(self, move):
21 '''
22 Check if the move is an in or an out
23 '''
24- out = False
25
26 if (move.location_id.usage == 'internal') and (move.location_dest_id and move.location_dest_id.usage in ('customer', 'supplier')):
27- out = True
28+ return True
29
30- return out
31+ return False
32
33 def _get_qty(self, move):
34 '''
35@@ -62,141 +64,141 @@
36
37 return qty
38
39- def _get_move_obj(self, cr, uid, obj_obj, move):
40- '''
41- Return the move's order type object
42- '''
43- obj_id = False
44- obj = False
45- acronym_type = False
46-
47- for split_origin in move.origin.split(":"):
48- if obj_obj == self.pool.get('sale.order'):
49- acronym_type = 'FO'
50- elif obj_obj == self.pool.get('purchase.order'):
51- acronym_type = 'PO'
52-
53- if acronym_type and acronym_type in split_origin:
54- obj_name = str(split_origin.split("/")[-1].split("-")[0])
55- obj_id = obj_obj.search(cr, uid, [('name', 'like', obj_name + '-')])
56- if not obj_id:
57- obj_id = obj_obj.search(cr, uid, [('name', 'like', obj_name)])
58-
59- if obj_id:
60- obj = obj_obj.browse(cr, uid, obj_id[0])
61- break
62-
63- return obj
64-
65 def _get_moves(self, report):
66 '''
67 Return the moves for the report and set their qty balance
68 '''
69 so_obj = self.pool.get('sale.order')
70 po_obj = self.pool.get('purchase.order')
71- result = []
72 sm_list = []
73+ # TODO: we must search on counterpart if filter is used on wizard
74 for move in report.sm_ids:
75 sm_list.append(self.pool.get('stock.move').browse(self.cr, self.uid, move))
76
77- sm_list = sorted(sm_list, key=lambda sm: (sm['product_id']['default_code'], sm['origin'].split(":")[-1], sm['create_date']))
78- balance = 0
79- # dict used to check if the loan flow is done
80- dict_check_done = {}
81- for index, move in enumerate(sm_list, start=0):
82- move.balance = balance
83+ sm_list = sorted(sm_list, key=lambda sm: sm['create_date'])
84+ move_by_fo_po_prod = {}
85+ keys_order = []
86+
87+ get_so_from_po_id = {}
88+ get_po_from_so_id = {}
89+
90+ for move in sm_list:
91 if self._is_qty_out(move):
92- balance -= self._get_qty(move)
93- else:
94- balance += self._get_qty(move)
95-
96- # check if the flow exists in the dict
97- move.status = 'Open'
98- move_ref = str(move.origin.split(":")[-1].split("/")[-1].split("-")[0])
99- if not dict_check_done.get(move_ref, []):
100- if 'FO' in move.origin and 'PO' in move.origin:
101- so_state = self._get_move_obj(self.cr, self.uid, so_obj, move).state
102- po_state = self._get_move_obj(self.cr, self.uid, po_obj, move).state
103- if so_state == 'done' and po_state == 'done':
104- dict_check_done[move_ref] = 'Closed'
105- else:
106- dict_check_done[move_ref] = 'Open'
107- elif 'FO' in move.origin and not 'PO' in move.origin:
108- so_found = self._get_move_obj(self.cr, self.uid, so_obj, move)
109- so_state = so_found.state
110- po_ids = po_obj.search(self.cr, self.uid, [('origin', '=', so_found.name)])
111- if len(po_ids) > 0:
112- po_state = po_obj.browse(self.cr, self.uid, po_ids[0]).state
113- if so_state == 'done' and po_state == 'done':
114- dict_check_done[move_ref] = 'Closed'
115- else:
116- dict_check_done[move_ref] = 'Open'
117- else:
118- dict_check_done[move_ref] = 'Open'
119- elif not 'FO' in move.origin and 'PO' in move.origin:
120- po_found = self._get_move_obj(self.cr, self.uid, po_obj, move)
121- po_state = po_found.state
122- so_ids = so_obj.search(self.cr, self.uid, [('name', '=', po_found.origin)])
123- if len(so_ids) > 0:
124- so_state = po_obj.browse(self.cr, self.uid, so_ids[0]).state
125- if so_state == 'done' and po_state == 'done':
126- dict_check_done[move_ref] = 'Closed'
127- else:
128- dict_check_done[move_ref] = 'Open'
129- else:
130- dict_check_done[move_ref] = 'Open'
131- # set the state according to the flow status
132- setattr(move, 'status', dict_check_done[move_ref])
133-
134- # if the move is the last in the list
135- if move is sm_list[-1]:
136- setattr(move, 'balance', balance)
137- # remove closed flows
138- if report.remove_completed:
139- if move.status == 'Open':
140- result.append(move)
141- else:
142- result.append(move)
143- balance = 0
144- else:
145- # if the move's origin is different than the next one
146- if move.origin.split(":")[-1] not in sm_list[index+1].origin.split(":")[-1] and \
147- sm_list[index + 1].origin.split(":")[-1] not in move.origin.split(":")[-1]:
148- setattr(move, 'balance', balance)
149- # remove closed flows
150- if report.remove_completed:
151- if move.status == 'Open':
152- result.append(move)
153- else:
154- result.append(move)
155- balance = 0
156- else:
157- # if the move's product is different than the next one
158- if move.product_id.id != sm_list[index+1].product_id.id:
159- setattr(move, 'balance', balance)
160- # remove closed flows
161- if report.remove_completed:
162- if move.status == 'Open':
163- result.append(move)
164- else:
165- result.append(move)
166- balance = 0
167- else:
168- setattr(move, 'balance', 0)
169- # remove closed flows
170- if report.remove_completed:
171- if move.status == 'Open':
172- result.append(move)
173- else:
174- result.append(move)
175+ qty = -1 * self._get_qty(move)
176+ else:
177+ qty = self._get_qty(move)
178+
179+ dom = []
180+ status = 'Open'
181+ if move.purchase_line_id:
182+ po_found = move.purchase_line_id.order_id
183+ if po_found.id not in get_so_from_po_id:
184+ if po_found.loan_id:
185+ ids = [po_found.loan_id.id]
186+ elif po_found.origin[-2:] in ['-1', '-2', '-3']:
187+ ids = so_obj.search(self.cr, self.uid, [('name', '=', po_found.origin)])
188+ if not ids:
189+ ids = so_obj.search(self.cr, self.uid, [('name', '=', po_found.origin[0:-2])])
190+ else:
191+ ids = so_obj.search(self.cr, self.uid, [('name', '=', po_found.origin)])
192+ if not ids:
193+ dom = ['%s-%s' % (po_found.origin, i) for i in [1, 2, 3]]
194+ ids = so_obj.search(self.cr, self.uid, [('name', 'in', dom)])
195+ if ids:
196+ so = so_obj.browse(self.cr, self.uid, ids[0])
197+ if so.split_type_sale_order:
198+ ids = so_obj.search(self.cr, self.uid, [('name', '=', '%s-2' % so.name)])
199+ if ids:
200+ so = so_obj.browse(self.cr, self.uid, ids[0])
201+ get_so_from_po_id[po_found.id] = so
202+
203+ so_found = get_so_from_po_id.get(po_found.id)
204+ if so_found and so_found.state == po_found.state == 'done':
205+ status = 'Closed'
206+ elif move.sale_line_id:
207+ so_found = move.sale_line_id.order_id
208+ if so_found.id not in get_po_from_so_id:
209+ if so_found.loan_id:
210+ ids = [so_found.loan_id.id]
211+ elif so_found.name[-2:] in ['-1', '-2', '-3']:
212+ ids = po_obj.search(self.cr, self.uid, [('origin', '=', so_found.name)])
213+ if not ids:
214+ ids = po_obj.search(self.cr, self.uid, [('origin', '=', so_found.name[0:-2])])
215+ else:
216+ ids = po_obj.search(self.cr, self.uid, [('origin', '=', so_found.name)])
217+ if ids:
218+ po_found = po_obj.browse(self.cr, self.uid, ids[0])
219+ if po_found.state == 'split':
220+ ids = po_obj.search(self.cr, self.uid, [('name', '=', '%s-2' % po_found.name)])
221+ po_found = po_obj.browse(self.cr, self.uid, ids[0])
222+ get_po_from_so_id[so_found.id] = po_found
223+
224+ po_found = get_po_from_so_id.get(so_found.id)
225+ if po_found and so_found.state == po_found.state == 'done':
226+ status = 'Closed'
227+
228+ if status != 'Closed' or not report.remove_completed:
229+ setattr(move, 'status', status)
230+ setattr(move, 'balance', 0)
231+
232+ key = (
233+ so_found and so_found.id or 'NF%s' % po_found.id,
234+ po_found and po_found.id or 'NF%s' % so_found.id,
235+ move.product_id.id
236+ )
237+ if key not in move_by_fo_po_prod:
238+ keys_order.append(key)
239+ move_by_fo_po_prod[key] = {'balance': 0, 'moves': []}
240+ move_by_fo_po_prod[key]['balance'] += qty
241+ move_by_fo_po_prod[key]['moves'].append(move)
242+
243+ result = []
244+ for key in keys_order:
245+ move_by_fo_po_prod[key]['moves'][-1].balance = move_by_fo_po_prod[key]['balance']
246+ result.append(move_by_fo_po_prod[key]['moves'])
247
248 return result
249
250- def _get_instance(self):
251- '''
252- Return user's current instance
253- '''
254- return self.pool.get('res.users').browse(self.cr, self.uid, self.uid).company_id.instance_id.name
255+ def _get_user_company(self):
256+ '''
257+ Return user's current company
258+ '''
259+ return self.pool.get('res.users').browse(self.cr, self.uid, self.uid).company_id
260+
261+ def _get_first_split_on_underscore(self, name):
262+ '''
263+ Return the first data from a table with the string split to '_'
264+ '''
265+ res = name
266+ if res:
267+ res = name.rsplit('_', 1)[0]
268+
269+ return res
270+
271+ def _compute_currency(self, move):
272+ '''
273+ Compute an amount of a given currency to the instance's currency
274+ '''
275+ currency_obj = self.pool.get('res.currency')
276+
277+ if not move.price_currency_id:
278+ if move.price_unit is None:
279+ return round(move.product_id.standard_price, 2)
280+ if move.type == 'in':
281+ from_currency_id = move.partner_id.property_product_pricelist_purchase.currency_id.id
282+ else:
283+ from_currency_id = move.partner_id.property_product_pricelist.currency_id.id
284+ else:
285+ from_currency_id = move.price_currency_id.id
286+
287+ context = {'date': move.date}
288+ to_currency_id = self.user_company['currency_id'].id
289+
290+ if from_currency_id == to_currency_id:
291+ return round(move.price_unit, 2)
292+
293+ return round(currency_obj.compute(self.cr, self.uid, from_currency_id, to_currency_id, move.price_unit, round=False, context=context), 2)
294+
295
296 class sale_loan_stock_moves_report_xls(SpreadsheetReport):
297 def __init__(self, name, table, rml=False, parser=report_sxw.rml_parse,
298
299=== modified file 'bin/addons/sale/report/sale_loan_stock_moves_report_xls.mako'
300--- bin/addons/sale/report/sale_loan_stock_moves_report_xls.mako 2017-08-16 08:32:04 +0000
301+++ bin/addons/sale/report/sale_loan_stock_moves_report_xls.mako 2017-09-13 12:13:20 +0000
302@@ -39,25 +39,48 @@
303 </Style>
304
305 <!-- Lines -->
306- <Style ss:ID="line_left">
307- <Alignment ss:Horizontal="Left" ss:Vertical="Bottom"/>
308- <Borders>
309- <Border ss:Position="Left" ss:LineStyle="Continuous" ss:Weight="1"/>
310- <Border ss:Position="Right" ss:LineStyle="Continuous" ss:Weight="1"/>
311- <Border ss:Position="Top" ss:LineStyle="Continuous" ss:Weight="1"/>
312- <Border ss:Position="Bottom" ss:LineStyle="Continuous" ss:Weight="1"/>
313- </Borders>
314- <Font ss:Size="10"/>
315- </Style>
316- <Style ss:ID="line_right">
317- <Alignment ss:Horizontal="Right" ss:Vertical="Bottom"/>
318- <Borders>
319- <Border ss:Position="Left" ss:LineStyle="Continuous" ss:Weight="1"/>
320- <Border ss:Position="Right" ss:LineStyle="Continuous" ss:Weight="1"/>
321- <Border ss:Position="Top" ss:LineStyle="Continuous" ss:Weight="1"/>
322- <Border ss:Position="Bottom" ss:LineStyle="Continuous" ss:Weight="1"/>
323- </Borders>
324- <Font ss:Size="10"/>
325+ <Style ss:ID="line_left0">
326+ <Alignment ss:Horizontal="Left" ss:Vertical="Bottom"/>
327+ <Borders>
328+ <Border ss:Position="Left" ss:LineStyle="Continuous" ss:Weight="1"/>
329+ <Border ss:Position="Right" ss:LineStyle="Continuous" ss:Weight="1"/>
330+ <Border ss:Position="Top" ss:LineStyle="Continuous" ss:Weight="1"/>
331+ <Border ss:Position="Bottom" ss:LineStyle="Continuous" ss:Weight="1"/>
332+ </Borders>
333+ <Font ss:Size="10"/>
334+ </Style>
335+ <Style ss:ID="line_right0">
336+ <Alignment ss:Horizontal="Right" ss:Vertical="Bottom"/>
337+ <Borders>
338+ <Border ss:Position="Left" ss:LineStyle="Continuous" ss:Weight="1"/>
339+ <Border ss:Position="Right" ss:LineStyle="Continuous" ss:Weight="1"/>
340+ <Border ss:Position="Top" ss:LineStyle="Continuous" ss:Weight="1"/>
341+ <Border ss:Position="Bottom" ss:LineStyle="Continuous" ss:Weight="1"/>
342+ </Borders>
343+ <Font ss:Size="10"/>
344+ <NumberFormat ss:Format="#,##0.00"/>
345+ </Style>
346+ <Style ss:ID="line_left1">
347+ <Alignment ss:Horizontal="Left" ss:Vertical="Bottom"/>
348+ <Borders>
349+ <Border ss:Position="Left" ss:LineStyle="Continuous" ss:Weight="1"/>
350+ <Border ss:Position="Right" ss:LineStyle="Continuous" ss:Weight="1"/>
351+ <Border ss:Position="Top" ss:LineStyle="Continuous" ss:Weight="1"/>
352+ <Border ss:Position="Bottom" ss:LineStyle="Continuous" ss:Weight="1"/>
353+ </Borders>
354+ <Font ss:Size="10"/>
355+ <Interior ss:Color="#dddddd" ss:Pattern="Solid"/>
356+ </Style>
357+ <Style ss:ID="line_right1">
358+ <Alignment ss:Horizontal="Right" ss:Vertical="Bottom"/>
359+ <Borders>
360+ <Border ss:Position="Left" ss:LineStyle="Continuous" ss:Weight="1"/>
361+ <Border ss:Position="Right" ss:LineStyle="Continuous" ss:Weight="1"/>
362+ <Border ss:Position="Top" ss:LineStyle="Continuous" ss:Weight="1"/>
363+ <Border ss:Position="Bottom" ss:LineStyle="Continuous" ss:Weight="1"/>
364+ </Borders>
365+ <Font ss:Size="10"/>
366+ <Interior ss:Color="#dddddd" ss:Pattern="Solid"/>
367 <NumberFormat ss:Format="#,##0.00"/>
368 </Style>
369 <Style ss:ID="line_center">
370@@ -71,15 +94,26 @@
371 <Font ss:Size="10"/>
372 <NumberFormat ss:Format="#,##0.00"/>
373 </Style>
374- <Style ss:ID="sShortDate">
375- <NumberFormat ss:Format="Short Date"/>
376- <Alignment ss:Vertical="Center" ss:WrapText="1"/>
377- <Borders>
378- <Border ss:Position="Bottom" ss:LineStyle="Continuous" ss:Weight="1" />
379- <Border ss:Position="Left" ss:LineStyle="Continuous" ss:Weight="1" />
380- <Border ss:Position="Right" ss:LineStyle="Continuous" ss:Weight="1" />
381- <Border ss:Position="Top" ss:LineStyle="Continuous" ss:Weight="1" />
382- </Borders>
383+ <Style ss:ID="sShortDate0">
384+ <NumberFormat ss:Format="Short Date"/>
385+ <Alignment ss:Vertical="Center" ss:WrapText="1"/>
386+ <Borders>
387+ <Border ss:Position="Bottom" ss:LineStyle="Continuous" ss:Weight="1" />
388+ <Border ss:Position="Left" ss:LineStyle="Continuous" ss:Weight="1" />
389+ <Border ss:Position="Right" ss:LineStyle="Continuous" ss:Weight="1" />
390+ <Border ss:Position="Top" ss:LineStyle="Continuous" ss:Weight="1" />
391+ </Borders>
392+ </Style>
393+ <Style ss:ID="sShortDate1">
394+ <NumberFormat ss:Format="Short Date"/>
395+ <Alignment ss:Vertical="Center" ss:WrapText="1"/>
396+ <Borders>
397+ <Border ss:Position="Bottom" ss:LineStyle="Continuous" ss:Weight="1" />
398+ <Border ss:Position="Left" ss:LineStyle="Continuous" ss:Weight="1" />
399+ <Border ss:Position="Right" ss:LineStyle="Continuous" ss:Weight="1" />
400+ <Border ss:Position="Top" ss:LineStyle="Continuous" ss:Weight="1" />
401+ </Borders>
402+ <Interior ss:Color="#dddddd" ss:Pattern="Solid"/>
403 </Style>
404 </Styles>
405
406@@ -87,39 +121,39 @@
407 <ss:Worksheet ss:Name="Loan Report">
408 <Table x:FullColumns="1" x:FullRows="1">
409 ## Product Code
410- <Column ss:AutoFitWidth="1" ss:Width="92.5" />
411+ <Column ss:AutoFitWidth="1" ss:Width="102.5" />
412 ## Product Description
413- <Column ss:AutoFitWidth="1" ss:Width="271.5" />
414+ <Column ss:AutoFitWidth="1" ss:Width="301.5" />
415 ## Order Type
416 <Column ss:AutoFitWidth="1" ss:Width="54.0" />
417 ## Movement Date
418 <Column ss:AutoFitWidth="1" ss:Width="72.25" />
419 ## Move Ref.
420- <Column ss:AutoFitWidth="1" ss:Width="135.75" />
421+ <Column ss:AutoFitWidth="1" ss:Width="105.75" />
422 ## Partner
423 <Column ss:AutoFitWidth="1" ss:Width="135.75" />
424 ## Partner Type
425- <Column ss:AutoFitWidth="1" ss:Width="72.0" />
426+ <Column ss:AutoFitWidth="1" ss:Width="65.5" />
427 ## Instance
428- <Column ss:AutoFitWidth="1" ss:Width="112.25" />
429+ <Column ss:AutoFitWidth="1" ss:Width="172.25" />
430 ## Qty In
431- <Column ss:AutoFitWidth="1" ss:Width="54.25" />
432+ <Column ss:AutoFitWidth="1" ss:Width="74.25" />
433 ## Qty Out
434- <Column ss:AutoFitWidth="1" ss:Width="54.25" />
435+ <Column ss:AutoFitWidth="1" ss:Width="74.25" />
436 ## Qty Balance
437- <Column ss:AutoFitWidth="1" ss:Width="54.25" />
438+ <Column ss:AutoFitWidth="1" ss:Width="69.25" />
439 ## PO/FO Ref.
440- <Column ss:AutoFitWidth="1" ss:Width="302.0" />
441+ <Column ss:AutoFitWidth="1" ss:Width="282.0" />
442 ## Origin Ref.
443- <Column ss:AutoFitWidth="1" ss:Width="202.0" />
444+ <Column ss:AutoFitWidth="1" ss:Width="152.0" />
445 ## Unit Price
446 <Column ss:AutoFitWidth="1" ss:Width="57.25" />
447 ## Currency
448- <Column ss:AutoFitWidth="1" ss:Width="65.75" />
449+ <Column ss:AutoFitWidth="1" ss:Width="45.75" />
450 ## Total Value
451- <Column ss:AutoFitWidth="1" ss:Width="67.25" />
452+ <Column ss:AutoFitWidth="1" ss:Width="62.25" />
453 ## Status
454- <Column ss:AutoFitWidth="1" ss:Width="67.25" />
455+ <Column ss:AutoFitWidth="1" ss:Width="42.25" />
456
457 <%
458 headers_list = [
459@@ -149,51 +183,43 @@
460 % endfor
461 </Row>
462
463- % for o in getMoves(r):
464+ <% i = 0 %>
465+ % for flow in getMoves(r):
466+ <% i = 1 - i %>
467+ % for o in flow:
468 <Row ss:Height="14.25">
469- <Cell ss:StyleID="line_left"><Data ss:Type="String">${o.product_id.default_code|x}</Data></Cell>
470- <Cell ss:StyleID="line_left"><Data ss:Type="String">${o.product_id.name|x}</Data></Cell>
471- <Cell ss:StyleID="line_left"><Data ss:Type="String">${o.reason_type_id.name|x}</Data></Cell>
472+ <Cell ss:StyleID="line_left${i}"><Data ss:Type="String">${o.product_id.default_code|x}</Data></Cell>
473+ <Cell ss:StyleID="line_left${i}"><Data ss:Type="String">${o.product_id.name|x}</Data></Cell>
474+ <Cell ss:StyleID="line_left${i}"><Data ss:Type="String">${o.reason_type_id.name|x}</Data></Cell>
475 %if o.date and isDateTime(o.date):
476- <Cell ss:StyleID="sShortDate"><Data ss:Type="DateTime">${o.date[:10]|n}T${o.date[-8:]|n}.000</Data></Cell>
477+ <Cell ss:StyleID="sShortDate${i}"><Data ss:Type="DateTime">${o.date[:10]|n}T${o.date[-8:]|n}.000</Data></Cell>
478 % else:
479- <Cell ss:StyleID="line_left"><Data ss:Type="String"></Data></Cell>
480+ <Cell ss:StyleID="line_left${i}"><Data ss:Type="String"></Data></Cell>
481 % endif
482- <Cell ss:StyleID="line_left"><Data ss:Type="String">${o.picking_id.name or ''|x}</Data></Cell>
483- <Cell ss:StyleID="line_left"><Data ss:Type="String">${o.partner_id.name or ''|x}</Data></Cell>
484- <Cell ss:StyleID="line_left"><Data ss:Type="String">${o.partner_id.partner_type or ''|x}</Data></Cell>
485- <Cell ss:StyleID="line_right"><Data ss:Type="String">${getInstance()|x}</Data></Cell>
486+ <Cell ss:StyleID="line_left${i}"><Data ss:Type="String">${o.picking_id.name or ''|x}</Data></Cell>
487+ <Cell ss:StyleID="line_left${i}"><Data ss:Type="String">${o.partner_id.name or ''|x}</Data></Cell>
488+ <Cell ss:StyleID="line_left${i}"><Data ss:Type="String">${o.partner_id.partner_type or ''|x}</Data></Cell>
489+ <Cell ss:StyleID="line_left${i}"><Data ss:Type="String">${getUserCompany()['instance_id'].name|x}</Data></Cell>
490 % if isQtyOut(o):
491- <Cell ss:StyleID="line_right"><Data ss:Type="String"></Data></Cell>
492- <Cell ss:StyleID="line_right"><Data ss:Type="Number">${getQty(o)|x}</Data></Cell>
493+ <Cell ss:StyleID="line_right${i}"><Data ss:Type="String"></Data></Cell>
494+ <Cell ss:StyleID="line_right${i}"><Data ss:Type="Number">${getQty(o)|x}</Data></Cell>
495 % else:
496- <Cell ss:StyleID="line_right"><Data ss:Type="Number">${getQty(o)|x}</Data></Cell>
497- <Cell ss:StyleID="line_right"><Data ss:Type="String"></Data></Cell>
498+ <Cell ss:StyleID="line_right${i}"><Data ss:Type="Number">${getQty(o)|x}</Data></Cell>
499+ <Cell ss:StyleID="line_right${i}"><Data ss:Type="String"></Data></Cell>
500 % endif
501 % if o.balance:
502- <Cell ss:StyleID="line_right"><Data ss:Type="String">${o.balance|x}</Data></Cell>
503+ <Cell ss:StyleID="line_right${i}"><Data ss:Type="Number">${o.balance|x}</Data></Cell>
504 % else:
505- <Cell ss:StyleID="line_right"><Data ss:Type="String"></Data></Cell>
506+ <Cell ss:StyleID="line_right${i}"><Data ss:Type="String"></Data></Cell>
507 %endif
508- <Cell ss:StyleID="line_left"><Data ss:Type="String">${o.origin|x}</Data></Cell>
509- <Cell ss:StyleID="line_left"><Data ss:Type="String">${o.purchase_line_id.sync_order_line_db_id or o.sale_line_id.sync_order_line_db_id or ''|x}</Data></Cell>
510- % if o.price_unit:
511- <Cell ss:StyleID="line_right"><Data ss:Type="Number">${round(o.price_unit, 3)|x}</Data></Cell>
512- % else:
513- <Cell ss:StyleID="line_right"><Data ss:Type="Number">${round(o.product_id.standard_price, 3)|x}</Data></Cell>
514- % endif
515- % if o.price_currency_id.name:
516- <Cell ss:StyleID="line_left"><Data ss:Type="String">${o.price_currency_id.name|x}</Data></Cell>
517- % else:
518- <Cell ss:StyleID="line_left"><Data ss:Type="String">${o.product_id.currency_id.name or ''|x}</Data></Cell>
519- % endif
520- % if o.price_unit:
521- <Cell ss:StyleID="line_right"><Data ss:Type="Number">${round(o.price_unit, 3) * getQty(o)|x}</Data></Cell>
522- % else:
523- <Cell ss:StyleID="line_right"><Data ss:Type="Number">${round(o.product_id.standard_price, 3) * getQty(o)|x}</Data></Cell>
524- % endif
525- <Cell ss:StyleID="line_left"><Data ss:Type="String">${o.status|x}</Data></Cell>
526+ <Cell ss:StyleID="line_left${i}"><Data ss:Type="String">${o.origin|x}</Data></Cell>
527+ <Cell ss:StyleID="line_left${i}"><Data ss:Type="String">${getFirstSplitOnUnderscore(o.purchase_line_id.sync_order_line_db_id) or getFirstSplitOnUnderscore(o.sale_line_id.sync_order_line_db_id) or ''|x}</Data></Cell>
528+ <Cell ss:StyleID="line_right${i}"><Data ss:Type="Number">${computeCurrency(o)|x}</Data></Cell>
529+ <Cell ss:StyleID="line_left${i}"><Data ss:Type="String">${getUserCompany()['currency_id'].name|x}</Data></Cell>
530+ <Cell ss:StyleID="line_right${i}"><Data ss:Type="Number">${computeCurrency(o) * getQty(o)|x}</Data></Cell>
531+ <Cell ss:StyleID="line_left${i}"><Data ss:Type="String">${o.status|x}</Data></Cell>
532 </Row>
533+ % endfor
534 % endfor
535
536 </Table>

Subscribers

People subscribed via source and target branches