Merge lp:~npg-team/openobject-addons/assembly_bom_npg into lp:openobject-addons

Proposed by Novapoint Group
Status: Rejected
Rejected by: Fabien (Open ERP)
Proposed branch: lp:~npg-team/openobject-addons/assembly_bom_npg
Merge into: lp:openobject-addons
Diff against target: 3197 lines (+3075/-0)
21 files modified
assembly_bom/__init__.py (+30/-0)
assembly_bom/__openerp__.py (+53/-0)
assembly_bom/change_log.txt (+85/-0)
assembly_bom/mrp.py (+98/-0)
assembly_bom/procurement.py (+60/-0)
assembly_bom/product.py (+242/-0)
assembly_bom/product_view.xml (+18/-0)
assembly_bom/report/__init__.py (+24/-0)
assembly_bom/report/consumption_report.py (+87/-0)
assembly_bom/report/consumption_report.rml (+119/-0)
assembly_bom/report/delivery_report.py (+70/-0)
assembly_bom/report/delivery_report.rml (+384/-0)
assembly_bom/report/normalized_oo2rml.xsl (+696/-0)
assembly_bom/report/report_view.xml (+23/-0)
assembly_bom/report/tiny_sxw2rml.py (+377/-0)
assembly_bom/sale.py (+71/-0)
assembly_bom/stock.py (+378/-0)
assembly_bom/stock_view.xml (+100/-0)
assembly_bom/wizard/__init__.py (+24/-0)
assembly_bom/wizard/consumption_report_wizard.py (+93/-0)
assembly_bom/wizard/consumption_report_wizard.xml (+43/-0)
To merge this branch: bzr merge lp:~npg-team/openobject-addons/assembly_bom_npg
Reviewer Review Type Date Requested Status
Olivier Dony (Odoo) policy + quick technical scan Disapprove
Review via email: mp+78433@code.launchpad.net

Description of the change

NovaPoint Group has developed this module to provide true assembly sales bom support to OpenERP. Adds a new bom type, allows for assembling the BOM at time of picking. Accurately records the inventory.

To post a comment you must log in.
Revision history for this message
Fabien (Open ERP) (fp-tinyerp) wrote :

s/ecoding/encoding/g

Revision history for this message
Fabien (Open ERP) (fp-tinyerp) wrote :

We think it's better to implement this in a community module.

Revision history for this message
Fabien (Open ERP) (fp-tinyerp) wrote :

it's not normal to add a reporting engine in this module.

Revision history for this message
Fabien (Open ERP) (fp-tinyerp) wrote :

Hello,

Thank you for this good contribution. I checked your branches and I would suggest to keep them in your own branch for now on, instead of putting them in the official release. Please register them in apps.openerp.com if it's not already done; if people use them in v6.1, we will think about merging them to the official branch.

As these modules have thousands lines of code, we would not have the time to efficiently review the code, check the usability experience and test for v6.1. I propose to review this later on, if we see people using it from apps.

Thanks,

Revision history for this message
Olivier Dony (Odoo) (odo-openerp) wrote :

Hello,

I would like to explain a little bit more why this merge proposal was rejected, in 2 parts: our policy for merge proposals, then some specific hints.

1. Merge Proposal Acceptance Policy
===================================
There may have been contradicting messages about how and when it is useful to make a merge proposal.
We would like to state this policy very clearly, especially now that extra-addons have been deprecated due to the introduction of OpenERP Apps. So we have now added an official Merge Proposal Acceptance Policy to our contributor documentation, please have a look: http://bit.ly/openerp-contrib-mp

2. Remarks specific to this merge proposal
==========================================
In approximate order of importance:

- based on your minimal description (it would help to have more), it looks like this module should be:
  + either a general purpose extra feature that would be a great addition for OpenERP Apps, as explained in the policy, but does not need to be included in official addons now
  + or simply a modification of the original mrp module, to make the implementation simpler and less brittle (if validated functionally)
- the size of the merge proposal diff is very large (+3000 lines) compared to the scope:
  + lot of copy/pasted code from original addons, are they all really necessary? (if yes, please make merge proposals to split the original functions into smaller, more extensible functions)
  + you included a "tiny_sxw2rml.py" script and dependent files that look totally useless and out of place (1100+ lines!)
  + some parts look quite optional or not directly related to the module's purpose, reports, etc.
- there's a cr.commit() in your code, this is unacceptable! (see coding guidelines)
- usability: no tooltips explaining what an assembly bom is and how it differs from phantom bom (on the Bom type selection)
- you should be careful when comparing computed float values, due to float rounding
- no proper description of the features in the module manifest, nor how to use them
- many useless python imports (netsvc, pooler, ...)
- multiple leftover "print" debugging statements
- some remaining commented out code lines, which should be removed

I hope this helps,

Thanks!

review: Disapprove (policy + quick technical scan)

Unmerged revisions

5304. By Novapoint Group

[Add]: assembly_bom module to provide true assembly sales support to OpenERP

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added directory 'assembly_bom'
2=== added file 'assembly_bom/__init__.py'
3--- assembly_bom/__init__.py 1970-01-01 00:00:00 +0000
4+++ assembly_bom/__init__.py 2011-10-06 15:31:48 +0000
5@@ -0,0 +1,30 @@
6+# -*- coding: utf-8 -*-
7+##############################################################################
8+#
9+# OpenERP, Open Source Management Solution
10+# Copyright (C) 2011 NovaPoint Group LLC (<http://www.novapointgroup.com>)
11+# Copyright (C) 2004-2010 OpenERP SA (<http://www.openerp.com>)
12+#
13+# This program is free software: you can redistribute it and/or modify
14+# it under the terms of the GNU General Public License as published by
15+# the Free Software Foundation, either version 3 of the License, or
16+# (at your option) any later version.
17+#
18+# This program is distributed in the hope that it will be useful,
19+# but WITHOUT ANY WARRANTY; without even the implied warranty of
20+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21+# GNU General Public License for more details.
22+#
23+# You should have received a copy of the GNU General Public License
24+# along with this program. If not, see <http://www.gnu.org/licenses/>
25+#
26+##############################################################################
27+
28+import mrp
29+import report
30+import wizard
31+import product
32+import procurement
33+import stock
34+import sale
35+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
36\ No newline at end of file
37
38=== added file 'assembly_bom/__openerp__.py'
39--- assembly_bom/__openerp__.py 1970-01-01 00:00:00 +0000
40+++ assembly_bom/__openerp__.py 2011-10-06 15:31:48 +0000
41@@ -0,0 +1,53 @@
42+# -*- coding: utf-8 -*-
43+##############################################################################
44+#
45+# OpenERP, Open Source Management Solution
46+# Copyright (C) 2011 NovaPoint Group LLC (<http://www.novapointgroup.com>)
47+# Copyright (C) 2004-2010 OpenERP SA (<http://www.openerp.com>)
48+#
49+# This program is free software: you can redistribute it and/or modify
50+# it under the terms of the GNU General Public License as published by
51+# the Free Software Foundation, either version 3 of the License, or
52+# (at your option) any later version.
53+#
54+# This program is distributed in the hope that it will be useful,
55+# but WITHOUT ANY WARRANTY; without even the implied warranty of
56+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
57+# GNU General Public License for more details.
58+#
59+# You should have received a copy of the GNU General Public License
60+# along with this program. If not, see <http://www.gnu.org/licenses/>
61+#
62+##############################################################################
63+
64+{
65+ "name" : "Assembly BoM",
66+ "version" : "0.22",
67+ "author" : 'NovaPoint Group LLC',
68+ "description": """
69+ Adds a new catagory for the BOM as 'assembly bom'
70+ """,
71+ "category" : "Customization",
72+ "website" : "http://www.novapointgroup.com/",
73+ "depends" : [
74+ 'base',
75+ 'mrp',
76+ 'sale',
77+ 'stock',
78+ ],
79+ "init_xml" : [],
80+
81+ "demo_xml" : [],
82+
83+ "update_xml" : [
84+ 'report/report_view.xml',
85+ 'wizard/consumption_report_wizard.xml',
86+ 'stock_view.xml',
87+ 'product_view.xml',
88+ ],
89+ "test" : [],
90+ "active": False,
91+ "installable": True,
92+ }
93+
94+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
95\ No newline at end of file
96
97=== added file 'assembly_bom/change_log.txt'
98--- assembly_bom/change_log.txt 1970-01-01 00:00:00 +0000
99+++ assembly_bom/change_log.txt 2011-10-06 15:31:48 +0000
100@@ -0,0 +1,85 @@
101+Version 0.22 by Janeesh (22 Sept 2011)
102+ * Updation of Weights and price of products of all Bom types
103+
104+Version 0.21 by Vinod (16 Sept 2011)
105+ * Weights and price of products which are of type assembly bom is automatically updated with the sum of the values of its components when they change for any component.
106+Version 0.20 by Vinod (15 Sept 2011)
107+ * Customized the product view to include the BOM Stock.
108+
109+Version 0.19 by Jabir (18 July 2011)
110+ * Removed location filtering according to available products
111+
112+Version 0.18 by Arif (5 July 2011)
113+ * Update method on bom
114+
115+Version 0.16 > 0.17
116+ * Display warning on no stock available for product with normal bom and make to order
117+
118+Version 0.15 > 0.16
119+ * Display warning on no stock available according to the location selected
120+
121+Version 0.14 > 0.15
122+ * Fixed the issue with product onchange function on sale order line
123+ * Add filtering of stock location according to stock availability on delivery order line
124+Version 0.13 > 0.14
125+ * Done stock checking for assembly bom product
126+ * Fixed the default date issue for stock move and stock picking
127+Version 0.12 > 0.13
128+ * Filter Source Location in Stock Moves
129+ * Show pop-up alertif there is no stock available in stock move
130+
131+Version 0.11 > 0.12
132+ * Call action_assign for all moves
133+
134+Version 0.10 > 0.11
135+ * commenting
136+Version 0.09 > 0.10
137+ * Added the reference in the report
138+ * cleared the issue with the extra move
139+ * Added the sale id for the move with sale order of a product with normal BOM
140+ * Added the from and to dates in the report
141+ * cleared issue with the updation of the shipped (Delivered) field in Sale order Form
142+
143+Version 0.08 > 0.09
144+ * Added the _product_qty function in the product object to correct bom stock value
145+
146+Version 0.07 > 0.08
147+ * Added Sale Order Ref in stock move
148+
149+Version 0.06 > 0.07
150+ * Added date fields in the wizard for consumption report
151+ * Made modifications in the find consumption details method
152+ * Added one more column in the consumption details report
153+ * Modified the find consumption details method in product accordingly
154+
155+Version 0.05 > 0.06
156+ * modified the method 'action_produce_assign_product' in procurement.order object
157+ to remove the duplication of production order while the procurement is tried to run from the sale order
158+ with a product with assembly bom
159+
160+ * Added new py files procurement and product and stock to neatly handle the objects
161+
162+Version 0.04 > 0.05
163+ * Removed unwanted lines from files and modified the consumption report to be working with multiple documents
164+ * and showing a no consumption details message in the report if the product is no longer consumed
165+
166+Version 0.03 > 0.04 by Sinoj
167+ * Added the defaults value for ware house in the product consumption wizard
168+
169+Version 0.02 > 0.03 by Vijayakanth
170+ * Added stock moves to suffice the proper updation of stock ie a move from production to stock then to output and then to
171+ customer location and modified the assembly order destination location id so as to move the finished products to stock
172+
173+ * Added field parent_bom_id in the move object to support multiple lines in sale order (detailed check pending)
174+
175+Version 0.01 > 0.02 by Vijayakanth
176+ * Added a wizard and report to the assembly bom module
177+ * Added one more field production ids in stock move table to make it easy
178+ for getting the relation ship with production
179+ * Added the kit_id field in the move object to store assembly process id (production object)
180+ * This is solely for the assembly bom
181+
182+ * phantom bom subproducts are sold separately and stock move occures to customer location
183+ * assembly bom subroducts are moved to production and tracked using kit_id and finshed goods to customers
184+ * Normal bom subproduct move occures when procurement operation completes and its also tracked using production_ids field
185+
186
187=== added file 'assembly_bom/mrp.py'
188--- assembly_bom/mrp.py 1970-01-01 00:00:00 +0000
189+++ assembly_bom/mrp.py 2011-10-06 15:31:48 +0000
190@@ -0,0 +1,98 @@
191+# -*- ecoding: utf-8 -*-
192+##############################################################################
193+#
194+# OpenERP, Open Source Management Solution
195+# Copyright (C) 2011 NovaPoint Group LLC (<http://www.novapointgroup.com>)
196+# Copyright (C) 2004-2010 OpenERP SA (<http://www.openerp.com>)
197+#
198+# This program is free software: you can redistribute it and/or modify
199+# it under the terms of the GNU General Public License as published by
200+# the Free Software Foundation, either version 3 of the License, or
201+# (at your option) any later version.
202+#
203+# This program is distributed in the hope that it will be useful,
204+# but WITHOUT ANY WARRANTY; without even the implied warranty of
205+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
206+# GNU General Public License for more details.
207+#
208+# You should have received a copy of the GNU General Public License
209+# along with this program. If not, see <http://www.gnu.org/licenses/>
210+#
211+##############################################################################
212+
213+from osv import fields, osv
214+import netsvc
215+import datetime
216+import time
217+
218+class bom(osv.osv):
219+
220+ def _update_product_weight(self, cr, uid, bom_obj, context=None):
221+ weight = 0.00
222+ weight_net = 0.00
223+ standard_price = 0.00
224+ if not context:
225+ context = {}
226+ child_ids = self.search(cr, uid, [('bom_id', 'child_of', [bom_obj.id])], context=context)
227+ if child_ids and bom_obj.id in child_ids:
228+ child_ids.remove(bom_obj.id)
229+ if len(child_ids) == 0:
230+ weight = bom_obj.product_id.weight * bom_obj.product_qty
231+ weight_net = bom_obj.product_id.weight_net * bom_obj.product_qty
232+ standard_price = bom_obj.product_id.standard_price * bom_obj.product_qty
233+ else:
234+ for bom_child in self.browse(cr, uid, child_ids, context=context):
235+ res = self._update_product_weight(cr, uid, bom_child, context=context)
236+ weight += res[0]
237+ weight_net += res[1]
238+ standard_price += res[2]
239+ cr.execute('update product_template set weight=%s, weight_net=%s, standard_price=%s where id=%s'%(weight/bom_obj.product_qty, weight_net/bom_obj.product_qty, standard_price/bom_obj.product_qty, bom_obj.product_id.product_tmpl_id.id))
240+# self.write(cr, uid, bom_obj.product_id.id, {'weight' : weight, 'weight_net' : weight_net, 'standard_price' : standard_price}, context=context)
241+ return (weight, weight_net, standard_price)
242+
243+ def write(self, cr, uid, ids, vals, context=None):
244+
245+ result = super(bom, self).write(cr, uid, ids, vals, context=context)
246+ if result:
247+ for bom_obj in self.browse(cr,uid,ids,context={}):
248+ res = self._update_product_weight(cr,uid,bom_obj,context)
249+# cr.execute('update product_template set weight=%s, weight_net=%s, standard_price=%s where id=%s'%(res[0], res[1], res[2], bom_obj.product_id.product_tmpl_id.id))
250+ return result
251+
252+ def create(self, cr, uid, vals, context=None):
253+ result = super(bom, self).create(cr, uid, vals, context)
254+ bom_obj = self.browse(cr,uid,result,context={})
255+ res = self._update_product_weight(cr,uid,bom_obj,context)
256+ return result
257+ def _compute_type(self, cr, uid, ids, field_name, arg, context=None):
258+ print """ Sets particular method for the selected bom type.
259+ @param field_name: Name of the field
260+ @param arg: User defined argument
261+ @return: Dictionary of values
262+ """
263+ res = dict(map(lambda x: (x,''), ids))
264+ for line in self.browse(cr, uid, ids, context=context):
265+ if line.type in ('phantom','assembly') and not line.bom_id:
266+ res[line.id] = 'set'
267+ continue
268+ if line.bom_lines or line.type == 'phantom':
269+ continue
270+ if line.product_id.supply_method == 'produce':
271+ if line.product_id.procure_method == 'make_to_stock':
272+ res[line.id] = 'stock'
273+ else:
274+ res[line.id] = 'order'
275+ return res
276+
277+
278+
279+ _inherit='mrp.bom'
280+ _columns={
281+ 'method': fields.function(_compute_type, string='Method', method=True, type='selection', selection=[('',''),('stock','On Stock'),('order','On Order'),('set','Set / Pack')]),
282+ 'type': fields.selection([('normal','Normal BoM'),('phantom','Sets / Phantom'),('assembly','Assembly BoM')], 'BoM Type', required=True,
283+ help= "If a sub-product is used in several products, it can be useful to create its own BoM. "\
284+ "Though if you don't want separated production orders for this sub-product, select Set/Phantom as BoM type. "\
285+ "If a Phantom BoM is used for a root product, it will be sold and shipped as a set of components, instead of being produced."),
286+ }
287+bom()
288+
289
290=== added file 'assembly_bom/procurement.py'
291--- assembly_bom/procurement.py 1970-01-01 00:00:00 +0000
292+++ assembly_bom/procurement.py 2011-10-06 15:31:48 +0000
293@@ -0,0 +1,60 @@
294+# -*- ecoding: utf-8 -*-
295+##############################################################################
296+#
297+# OpenERP, Open Source Management Solution
298+# Copyright (C) 2011 NovaPoint Group LLC (<http://www.novapointgroup.com>)
299+# Copyright (C) 2004-2010 OpenERP SA (<http://www.openerp.com>)
300+#
301+# This program is free software: you can redistribute it and/or modify
302+# it under the terms of the GNU General Public License as published by
303+# the Free Software Foundation, either version 3 of the License, or
304+# (at your option) any later version.
305+#
306+# This program is distributed in the hope that it will be useful,
307+# but WITHOUT ANY WARRANTY; without even the implied warranty of
308+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
309+# GNU General Public License for more details.
310+#
311+# You should have received a copy of the GNU General Public License
312+# along with this program. If not, see <http://www.gnu.org/licenses/>
313+#
314+##############################################################################
315+
316+from osv import fields, osv
317+import netsvc
318+import datetime
319+import time
320+
321+class procurement(osv.osv):
322+ _inherit="procurement.order"
323+
324+ def action_produce_assign_product(self, cr, uid, ids, context=None):
325+ """ Blocks the procurement being run for products with assembly bom
326+ @param self: The object pointer.
327+ @param ids: ids of the procurement order document
328+ @param context: standard dictionary
329+ @comment: Blocks the procurement to be run for products with assembly bom
330+ @return: returns either the Manufacturing order id or 0
331+ """
332+ procurement_obj = self.pool.get('procurement.order')
333+ bom_obj=self.pool.get('mrp.bom')
334+ sale_line_obj=self.pool.get('sale.order.line')
335+ res=[]
336+ for procure_record in procurement_obj.browse(cr,uid,ids,context=context):
337+ sale_line_ids=sale_line_obj.search(cr,uid,[('procurement_id','=',procure_record.id)],context=context)
338+ sale_line_id=sale_line_ids and sale_line_ids[0] or False
339+ if sale_line_id:
340+ line=sale_line_obj.browse(cr,uid,sale_line_id,context=context)
341+ if line.product_id:
342+ bom_ids=bom_obj.search(cr,uid,[('product_id','=',line.product_id.id),('type','=','assembly')],context={})
343+ bom_id=bom_ids and bom_ids[0] or False
344+ if bom_id:
345+ #1. print 'PROCUREMENT:"PROCUREMENT FOR THE PRODUCT:%s, Name:%s" HAS BEEN BLOCKED'%(line.product_id.id,line.product_id.name)
346+ return 0
347+ return super(procurement, self).action_produce_assign_product( cr, uid, [procure_record.id], context=context)
348+
349+procurement()
350+
351+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
352+
353+
354
355=== added file 'assembly_bom/product.py'
356--- assembly_bom/product.py 1970-01-01 00:00:00 +0000
357+++ assembly_bom/product.py 2011-10-06 15:31:48 +0000
358@@ -0,0 +1,242 @@
359+# -*- coding: utf-8 -*-
360+##############################################################################
361+#
362+# OpenERP, Open Source Management Solution
363+# Copyright (C) 2011 NovaPoint Group LLC (<http://www.novapointgroup.com>)
364+# Copyright (C) 2004-2010 OpenERP SA (<http://www.openerp.com>)
365+#
366+# This program is free software: you can redistribute it and/or modify
367+# it under the terms of the GNU General Public License as published by
368+# the Free Software Foundation, either version 3 of the License, or
369+# (at your option) any later version.
370+#
371+# This program is distributed in the hope that it will be useful,
372+# but WITHOUT ANY WARRANTY; without even the implied warranty of
373+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
374+# GNU General Public License for more details.
375+#
376+# You should have received a copy of the GNU General Public License
377+# along with this program. If not, see <http://www.gnu.org/licenses/>
378+#
379+
380+from osv import fields, osv
381+import netsvc
382+import datetime
383+import time
384+
385+class product_inherit(osv.osv):
386+
387+ _inherit = 'product.product'
388+
389+ def _product_available(self, cr, uid, ids, field_names=None, arg=False, context=None):
390+ """
391+ For showing the bom stock value as 0 for the products with no bom
392+ @param self: The object pointer.
393+ @param ids: ids of the procurement order document
394+ @param context: standard dictionary
395+ @comment: If 'bom_stock' is there in the field_names list then if the product is having not bom the bom stock value was
396+ shown as real stock value. This override modifies it to zero
397+ @return: returns 0 for the above conditions in the standard format dictionary for multi function fields
398+ """
399+ res = super(product_inherit, self)._product_available(cr, uid, ids, field_names, arg, context=context)
400+ bom_obj=self.pool.get('mrp.bom')
401+ if 'bom_stock' in field_names:
402+ for product_id, stock_qty in res.iteritems():
403+ product = self.browse(cr, uid, product_id)
404+ bom_ids = bom_obj._bom_find(cr, uid, product.id, product.uom_id.id, properties=[])
405+ if not bom_ids:
406+ res[product_id]['bom_stock'] = 0
407+ #1. If possible do this by adding this line at 102 c2c_bom_stock.py this is the reason why its not showing the
408+ #stock value properly for the products with no boms
409+
410+ return res
411+
412+
413+ def find_consumption_details(self,cr,uid,ids,context={}):
414+ """
415+ Finds the consumption details for the products with ids in 'ids'
416+ @param self: The object pointer.
417+ @param ids: ids of the procurement order document
418+ @param context: standard dictionary
419+ @return: returns a list of dictionaries that carries the information regarding the product consumption
420+ """
421+ move_obj=self.pool.get('stock.move')
422+ product_ids=ids
423+ source_loc_id=context.get("source_loc_id",False)
424+ start_date=context.get("start_date",False)
425+ end_date=context.get("end_date",False)
426+ if source_loc_id :
427+ for product_id in product_ids:
428+ detail_list=[]
429+ args=[('product_id',"=",product_id),
430+ ('location_id','=',source_loc_id),
431+ ('state','=','done')]
432+ start_date and args.append(('date',">=",start_date+" 00:00:00"))
433+ end_date and args.append(('date',"<=",end_date+" 00:00:00"))
434+ move_ids=move_obj.search(cr,uid,args)
435+ detail_list=[]
436+ for move in move_obj.browse(cr,uid,move_ids):
437+ details={}
438+
439+ #1. If move is having sale_line_id its assumed to be related with sale
440+ if move.sale_line_id:
441+ line=move.sale_line_id
442+ product_qty=move.product_qty
443+ parent_product=move.product_id
444+ details={
445+ 'product_name':parent_product.name or "",
446+ 'reference':parent_product.default_code or "",
447+ 'type':'sale',
448+ "product_qty":product_qty,
449+ 'consumption':parent_product.name
450+ }
451+
452+ #2. If move is having production_ids its assumed to be related with production using normal bom
453+ if move.production_ids:
454+ production=move.production_ids[0]
455+ bom=production.bom_id and production.bom_id or False
456+ if bom:
457+ bom_type=bom.type
458+ if bom_type=='normal':
459+ parent_product=production.product_id
460+ product_name=parent_product.name
461+ details={
462+ 'bom_type':bom_type,
463+ "product_name":product_name,
464+ "product_qty":move.product_qty,
465+ "parent_product":parent_product,
466+ 'type':'production',
467+ 'consumption':product_name+'- '+bom_type,
468+ 'bom_type':bom_type,
469+ 'reference':parent_product.default_code or ""
470+
471+ }
472+ #3. If move is having kit_id its assumed to be related with production using assembly bom
473+ if move.kit_id:
474+ production=move.kit_id
475+ if production.state=='done':
476+ parent_product=production.product_id
477+ bom=production.bom_id and production.bom_id or False
478+ if bom:
479+ bom_type=bom.type
480+ if bom_type=='assembly':
481+ product_name=parent_product.name
482+ product_qty=move.product_qty
483+ details={
484+ 'bom_type':bom_type,
485+ "product_name":product_name,
486+ "product_qty":move.product_qty,
487+ "parent_product":parent_product,
488+ 'type':'production',
489+ 'consumption':product_name+'- '+bom_type,
490+ 'bom_type':bom_type,
491+ 'reference':parent_product.default_code or ""
492+ }
493+
494+ details and detail_list.append(details)
495+ details={}
496+ consumpt_lines=[]
497+ res={}
498+ #4. This block analyses the details to aggregate them with reference to type of consumption
499+
500+ for detail in detail_list:
501+ product_name=detail.get('product_name')
502+ consumption=detail.get('consumption')
503+
504+ if detail.get('type',False) and detail.get('type',False)=='sale':
505+ if res.has_key(consumption):
506+ new_qty=res.get(consumption,{}).get('product_qty',0)+detail.get('product_qty',0)
507+ res[consumption].update({
508+ 'product_qty':new_qty,
509+ 'product_name':product_name
510+ }
511+ )
512+ else:
513+ res[consumption]={
514+ 'product_qty':detail.get('product_qty',0),
515+ 'consumption':"%(product_name)s- sold separately"%detail,
516+ 'product_name':product_name,
517+ 'bom_type':detail.get('bom_type',' '),
518+ 'type':'Sold separately',
519+ 'reference':detail.get("reference","")
520+ }
521+ if detail.get('type',False) and detail.get('type',False)=='production':
522+
523+ if res.has_key(consumption):
524+ new_qty=res.get(consumption,{}).get('product_qty',0)+detail.get('product_qty',0)
525+ res[consumption].update({
526+ 'product_qty':new_qty,
527+ }
528+ )
529+ else:
530+ res[consumption]={
531+ 'product_qty':detail.get('product_qty',0),
532+ 'consumption':consumption,
533+ 'product_name':product_name,
534+ 'bom_type':detail.get('bom_type',' '),
535+ 'type':'Production using: '+detail.get('bom_type',' ')+' BOM',
536+ 'reference':detail.get("reference","")
537+ }
538+
539+
540+ consumpt_lines=res.values()
541+ total_qty=0
542+ #5. Calculating percentage consumption
543+ for line in consumpt_lines:
544+ total_qty+=line.get('product_qty',0)
545+ total_qty=float(total_qty)
546+ new_consumpt_lines=[]
547+ for line in consumpt_lines:
548+ line.update({
549+ 'percentage':round(line.get('product_qty',0)*100/total_qty, 2)
550+ })
551+ new_consumpt_lines.append(line)
552+ consumpt_lines=new_consumpt_lines
553+
554+ if not consumpt_lines:
555+ consumpt_lines=False
556+
557+ ret={"source_loc_id":source_loc_id,'detail_list':consumpt_lines}
558+
559+ return ret
560+ else:
561+ return {}
562+ _columns = {
563+ 'qty_available': fields.function(_product_available, method=True, type='float', string='Real Stock', help="Current quantities of products in selected locations or all internal if none have been selected.", multi='qty_available'),
564+ 'virtual_available': fields.function(_product_available, method=True, type='float', string='Virtual Stock', help="Futur stock for this product according to the selected location or all internal if none have been selected. Computed as: Real Stock - Outgoing + Incoming.", multi='qty_available'),
565+ 'immediately_usable_qty': fields.function(_product_available, method=True, type='float', string='Immediately Usable Stock', help="Quantities of products really available for sale. Computed as: Real Stock - Outgoing.", multi='qty_available'),
566+ 'bom_stock': fields.function(_product_available, method=True, type='float', string='BoM Stock Value', help="Quantities of products, useful to know how much of this product you could have. Computed as: (Reference stock of This product + How much could I produce of this product)", multi='qty_available'),
567+ }
568+
569+ def _get_root_bom(self, cr, uid, bom_obj, context=None):
570+ if not context:
571+ context = {}
572+ if not bom_obj.bom_id:
573+# if bom_obj.type == 'assembly':
574+ return bom_obj
575+ else:
576+ return self._get_root_bom(cr, uid, bom_obj.bom_id, context=context)
577+
578+ return False
579+
580+
581+ def write(self, cr, uid, ids, vals, context=None):
582+ if not context:
583+ context = {}
584+ res = super(product_inherit, self).write(cr, uid, ids, vals, context=context)
585+
586+ if res and (vals.get('weight', False) or vals.get('weight_net', False) or vals.get('standard_price', False)):
587+ updated_boms = []
588+ mrp_bom_pool = self.pool.get('mrp.bom')
589+ for prod in self.browse(cr, uid, ids, context=context):
590+ bom_ids = mrp_bom_pool.search(cr, uid, [('product_id', '=', prod.id)], context=context)
591+ for bom_obj in mrp_bom_pool.browse(cr, uid, bom_ids, context=context):
592+ root_bom = self._get_root_bom(cr, uid, bom_obj, context=context)
593+ if root_bom and root_bom not in updated_boms:
594+ updated_boms.append(root_bom)
595+ res = mrp_bom_pool._update_product_weight(cr, uid, root_bom, context=context)
596+ cr.execute('update product_template set weight=%s, weight_net=%s, standard_price=%s where id=%s'%(res[0]/root_bom.product_qty, res[1]/root_bom.product_qty, res[2]/root_bom.product_qty, root_bom.product_id.product_tmpl_id.id))
597+# self.write(cr, uid, root_bom.product_id.id, {'weight' : res[0], 'weight_net' : res[1], 'standard_price' : res[2]}, context=context)
598+ return res
599+
600+product_inherit()
601\ No newline at end of file
602
603=== added file 'assembly_bom/product_view.xml'
604--- assembly_bom/product_view.xml 1970-01-01 00:00:00 +0000
605+++ assembly_bom/product_view.xml 2011-10-06 15:31:48 +0000
606@@ -0,0 +1,18 @@
607+<?xml version="1.0" encoding="utf-8"?>
608+<openerp>
609+ <data>
610+ <record id="product_product_tree_view_inherit" model="ir.ui.view">
611+ <field name="name">product.product.tree.inherit</field>
612+ <field name="model">product.product</field>
613+ <field name="type">tree</field>
614+ <field name="inherit_id" ref="product.product_product_tree_view"/>
615+ <field eval="22" name="priority"/>
616+ <field name="arch" type="xml">
617+ <xpath expr="//field[@name='virtual_available']" position="after">
618+ <field name="bom_stock"/>
619+ </xpath>
620+ </field>
621+ </record>
622+
623+ </data>
624+</openerp>
625
626=== added directory 'assembly_bom/report'
627=== added file 'assembly_bom/report/__init__.py'
628--- assembly_bom/report/__init__.py 1970-01-01 00:00:00 +0000
629+++ assembly_bom/report/__init__.py 2011-10-06 15:31:48 +0000
630@@ -0,0 +1,24 @@
631+# -*- coding: utf-8 -*-
632+##############################################################################
633+#
634+# OpenERP, Open Source Management Solution
635+# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
636+#
637+# This program is free software: you can redistribute it and/or modify
638+# it under the terms of the GNU Affero General Public License as
639+# published by the Free Software Foundation, either version 3 of the
640+# License, or (at your option) any later version.
641+#
642+# This program is distributed in the hope that it will be useful,
643+# but WITHOUT ANY WARRANTY; without even the implied warranty of
644+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
645+# GNU Affero General Public License for more details.
646+#
647+# You should have received a copy of the GNU Affero General Public License
648+# along with this program. If not, see <http://www.gnu.org/licenses/>.
649+#
650+##############################################################################
651+
652+import consumption_report
653+
654+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
655\ No newline at end of file
656
657=== added file 'assembly_bom/report/consumption_report.py'
658--- assembly_bom/report/consumption_report.py 1970-01-01 00:00:00 +0000
659+++ assembly_bom/report/consumption_report.py 2011-10-06 15:31:48 +0000
660@@ -0,0 +1,87 @@
661+# -*- coding: utf-8 -*-
662+##############################################################################
663+#
664+# OpenERP, Open Source Management Solution
665+# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
666+#
667+# This program is free software: you can redistribute it and/or modify
668+# it under the terms of the GNU Affero General Public License as
669+# published by the Free Software Foundation, either version 3 of the
670+# License, or (at your option) any later version.
671+#
672+# This program is distributed in the hope that it will be useful,
673+# but WITHOUT ANY WARRANTY; without even the implied warranty of
674+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
675+# GNU Affero General Public License for more details.
676+#
677+# You should have received a copy of the GNU Affero General Public License
678+# along with this program. If not, see <http://www.gnu.org/licenses/>.
679+#
680+##############################################################################
681+
682+import time
683+
684+from report import report_sxw
685+
686+class consumption_report(report_sxw.rml_parse):
687+ def _get_consumption_lines(self,product_id):
688+
689+ """ Finds the consumption details for the product
690+ @param self: The object pointer.
691+ @param product_id: ID of the product
692+ @comment: fetches the consumption details of the product using 'find_consumption_details'
693+ which analyses stock moves to find consumption details
694+ @return: List of dictionaries showing the consumption details
695+ """
696+ res=[]
697+ detail_list=self.localcontext.get('detail_list',[])
698+ cr=self.cr;uid=self.uid
699+ details=self.pool.get("product.product").find_consumption_details(cr,uid,[product_id],context=self.localcontext)
700+ detail_list=details.get('detail_list',[])
701+ return detail_list
702+
703+ def _get_us_date(self,date_string):
704+ """ Converts the input date string to us format
705+ @param self: The object pointer.
706+ @param date_string: Expects date in ISO format YYYY-MM-DD
707+ @comment: uses format_lang method to convert the date to the us format
708+ @return: Returns either the us format date or an empty string
709+ """
710+ us_string=''
711+ if date_string:
712+ us_string=self.formatLang(date_string,date=True)
713+ return us_string
714+
715+ def _format_product_name(self,product_name,reference):
716+ """ Adds the product name and reference
717+ @param self: The object pointer.
718+ @param product_name: name of the product
719+ @param reference: Reference of the product
720+ @comment: adds the the reference and the product name if there is a reference string
721+ @return: returns '[reference] product_name' or 'product_name'
722+ """
723+ if reference:
724+ reference="[%s] "%reference
725+ else:
726+ reference=""
727+ return reference+product_name
728+
729+ def __init__(self, cr, uid, name, context=None):
730+ """ Parser __init__ override
731+ """
732+ super(consumption_report, self).__init__(cr, uid, name, context=context)
733+ self.localcontext.update({
734+ 'time': time,
735+ '_get_consumption_lines':self._get_consumption_lines,
736+ '_get_':self._get_consumption_lines,
737+ '_get_us_date':self._get_us_date,
738+ '_format_product_name':self._format_product_name,
739+ 'source_loc_id':context.get('source_loc_id', False),#1. location id selected for getting consumption details in the wizard
740+ 'start_date':context.get('start_date',False),#2. getting start date selected in the wizard
741+ 'end_date':context.get('end_date',False),#3. getting end date selected in the wizard
742+ })
743+
744+report_sxw.report_sxw('report.product.consumption', 'product.product', 'addons/assembly_bom/report/consumption_report.rml', parser=consumption_report, header=False)
745+
746+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
747+
748
749=== added file 'assembly_bom/report/consumption_report.rml'
750--- assembly_bom/report/consumption_report.rml 1970-01-01 00:00:00 +0000
751+++ assembly_bom/report/consumption_report.rml 2011-10-06 15:31:48 +0000
752@@ -0,0 +1,119 @@
753+<?xml version="1.0"?>
754+<document filename="test.pdf">
755+ <template pageSize="(595.0,842.0)" title="Test" author="Martin Simon" allowSplitting="20">
756+ <pageTemplate id="first">
757+ <frame id="first" x1="57.0" y1="57.0" width="481" height="728"/>
758+ </pageTemplate>
759+ </template>
760+ <stylesheet>
761+ <blockTableStyle id="Standard_Outline">
762+ <blockAlignment value="LEFT"/>
763+ <blockValign value="TOP"/>
764+ </blockTableStyle>
765+ <blockTableStyle id="Table1">
766+ <blockAlignment value="LEFT"/>
767+ <blockValign value="TOP"/>
768+ </blockTableStyle>
769+ <blockTableStyle id="Table2">
770+ <blockAlignment value="LEFT"/>
771+ <blockValign value="TOP"/>
772+ </blockTableStyle>
773+ <blockTableStyle id="Table3">
774+ <blockAlignment value="LEFT"/>
775+ <blockValign value="TOP"/>
776+ </blockTableStyle>
777+ <blockTableStyle id="Table4">
778+ <blockAlignment value="LEFT"/>
779+ <blockValign value="TOP"/>
780+ </blockTableStyle>
781+ <initialize>
782+ <paraStyle name="all" alignment="justify"/>
783+ </initialize>
784+ <paraStyle name="P1" fontName="Helvetica-Bold" fontSize="12.0" leading="15" alignment="LEFT" spaceBefore="12.0" spaceAfter="6.0"/>
785+ <paraStyle name="P2" fontName="Helvetica-Bold" fontSize="12.0" leading="15" alignment="CENTER" spaceBefore="12.0" spaceAfter="6.0"/>
786+ <paraStyle name="P3" fontName="Helvetica" fontSize="10.0" leading="13" alignment="LEFT" spaceBefore="12.0" spaceAfter="6.0"/>
787+ <paraStyle name="P4" fontName="Times-Roman" fontSize="12.0" leading="15" alignment="CENTER" spaceBefore="0.0" spaceAfter="6.0"/>
788+ <paraStyle name="P5" fontName="Helvetica" fontSize="8.0" leading="10" alignment="LEFT" spaceBefore="0.0" spaceAfter="0.0"/>
789+ <paraStyle name="P6" fontName="Helvetica" fontSize="8.0" leading="10" alignment="RIGHT" spaceBefore="0.0" spaceAfter="0.0"/>
790+ <paraStyle name="P7" fontName="Helvetica" fontSize="10.0" leading="13" alignment="CENTER"/>
791+ <paraStyle name="P8" fontName="Times-Roman" fontSize="12.0" leading="15" alignment="CENTER"/>
792+ <paraStyle name="P9" fontName="Helvetica" fontSize="10.0" leading="13" alignment="LEFT" spaceBefore="6.0" spaceAfter="6.0"/>
793+ <paraStyle name="P10" fontName="Helvetica" fontSize="8.0" leading="10" alignment="LEFT" spaceBefore="6.0" spaceAfter="6.0"/>
794+ <paraStyle name="Standard" fontName="Times-Roman"/>
795+ <paraStyle name="Heading" fontName="Helvetica" fontSize="14.0" leading="17" spaceBefore="12.0" spaceAfter="6.0"/>
796+ <paraStyle name="Text body" fontName="Times-Roman" spaceBefore="0.0" spaceAfter="6.0"/>
797+ <paraStyle name="List" fontName="Times-Roman" spaceBefore="0.0" spaceAfter="6.0"/>
798+ <paraStyle name="Caption" fontName="Times-Roman" fontSize="12.0" leading="15" spaceBefore="6.0" spaceAfter="6.0"/>
799+ <paraStyle name="Index" fontName="Times-Roman"/>
800+ <paraStyle name="Table Contents" fontName="Times-Roman"/>
801+ <paraStyle name="terp_tblheader_General" fontName="Helvetica" fontSize="8.0" leading="10" spaceBefore="6.0" spaceAfter="6.0"/>
802+ <paraStyle name="terp_tblheader_General_Centre" fontName="Helvetica" fontSize="8.0" leading="10" alignment="CENTER" spaceBefore="6.0" spaceAfter="6.0"/>
803+ <paraStyle name="Table Heading" fontName="Times-Roman" alignment="CENTER"/>
804+ <paraStyle name="terp_header" fontName="Helvetica-Bold" fontSize="15.0" leading="19" alignment="LEFT" spaceBefore="12.0" spaceAfter="6.0"/>
805+ <paraStyle name="terp_default_8" fontName="Helvetica" fontSize="8.0" leading="10" alignment="LEFT" spaceBefore="0.0" spaceAfter="0.0"/>
806+ <images/>
807+ </stylesheet>
808+ <story>
809+ <para style="P2">Product Consumption Report</para>
810+ <para style="P4">From: [[(_get_us_date(start_date) and _get_us_date(end_date)) and _get_us_date(start_date) or removeParentNode('para')]], To: [[_get_us_date(end_date)]]</para>
811+ <para style="P4">From: [[(_get_us_date(start_date) and not _get_us_date(end_date)) and _get_us_date(start_date) or removeParentNode('para')]]</para>
812+ <para style="P4">To: [[(_get_us_date(end_date) and not _get_us_date(start_date)) and _get_us_date(end_date) or removeParentNode('para')]]</para>
813+ <para style="P8">
814+ <font color="white"> </font>
815+ </para>
816+ <blockTable colWidths="478.0" style="Table1">
817+ <tr>
818+ <td>
819+ <para style="P1">[[repeatIn(objects,'o')]]</para>
820+ <blockTable colWidths="473.0" style="Table2">
821+ <tr>
822+ <td>
823+ <para style="P7">[[(_get_consumption_lines(o.id) and ' ') or removeParentNode('para')]]Product Name: <font face="Times-Roman">[[o.name]]</font></para>
824+ </td>
825+ </tr>
826+ </blockTable>
827+ <blockTable colWidths="142.0,142.0,95.0,95.0" repeatRows="1" style="Table3">
828+ <tr>
829+ <td>
830+ <para style="P9">Product[[(_get_consumption_lines(o.id) and ' ') or removeParentNode('blockTable')]]</para>
831+ </td>
832+ <td>
833+ <para style="P10">Consumption Type</para>
834+ </td>
835+ <td>
836+ <para style="terp_tblheader_General_Centre">Quantity</para>
837+ </td>
838+ <td>
839+ <para style="terp_tblheader_General_Centre">Percentage Consumption</para>
840+ </td>
841+ </tr>
842+ </blockTable>
843+ <blockTable colWidths="142.0,141.0,95.0,95.0" repeatRows="1" style="Table4">
844+ <tr>
845+ <td>
846+ <para style="P5">[[repeatIn(_get_consumption_lines(o.id),'line')]] [[_format_product_name(line.get('product_name'),line.get('reference'))]]</para>
847+ <para style="P5">[[(_get_consumption_lines(o.id) and ' ') or removeParentNode('blockTable')]]</para>
848+ </td>
849+ <td>
850+ <para style="P5">[[line.get('type')]]</para>
851+ </td>
852+ <td>
853+ <para style="P6">[[line.get('product_qty')]]</para>
854+ </td>
855+ <td>
856+ <para style="P6">[[line.get('percentage')]]</para>
857+ </td>
858+ </tr>
859+ </blockTable>
860+ <para style="P3">
861+ <font color="white"> </font>
862+ </para>
863+ </td>
864+ </tr>
865+ </blockTable>
866+ <para style="P4">
867+ <font color="white"> </font>
868+ </para>
869+ </story>
870+</document>
871+
872
873=== added file 'assembly_bom/report/consumption_report.sxw'
874Binary files assembly_bom/report/consumption_report.sxw 1970-01-01 00:00:00 +0000 and assembly_bom/report/consumption_report.sxw 2011-10-06 15:31:48 +0000 differ
875=== added file 'assembly_bom/report/consumption_report_reference+name.sxw'
876Binary files assembly_bom/report/consumption_report_reference+name.sxw 1970-01-01 00:00:00 +0000 and assembly_bom/report/consumption_report_reference+name.sxw 2011-10-06 15:31:48 +0000 differ
877=== added file 'assembly_bom/report/consumption_report_reference_column.sxw'
878Binary files assembly_bom/report/consumption_report_reference_column.sxw 1970-01-01 00:00:00 +0000 and assembly_bom/report/consumption_report_reference_column.sxw 2011-10-06 15:31:48 +0000 differ
879=== added file 'assembly_bom/report/delivery_report.py'
880--- assembly_bom/report/delivery_report.py 1970-01-01 00:00:00 +0000
881+++ assembly_bom/report/delivery_report.py 2011-10-06 15:31:48 +0000
882@@ -0,0 +1,70 @@
883+# -*- coding: utf-8 -*-
884+##############################################################################
885+#
886+# OpenERP, Open Source Management Solution
887+# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
888+#
889+# This program is free software: you can redistribute it and/or modify
890+# it under the terms of the GNU Affero General Public License as
891+# published by the Free Software Foundation, either version 3 of the
892+# License, or (at your option) any later version.
893+#
894+# This program is distributed in the hope that it will be useful,
895+# but WITHOUT ANY WARRANTY; without even the implied warranty of
896+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
897+# GNU Affero General Public License for more details.
898+#
899+# You should have received a copy of the GNU Affero General Public License
900+# along with this program. If not, see <http://www.gnu.org/licenses/>.
901+#
902+##############################################################################
903+
904+import time
905+
906+from report import report_sxw
907+
908+class delivery_report(report_sxw.rml_parse):
909+ def __init__(self, cr, uid, name, context=None):
910+ super(delivery_report, self).__init__(cr, uid, name, context=context)
911+ self.localcontext.update({
912+ 'time': time,
913+# 'detail_list':context.get('detail_list', [])
914+ '_get_parent':self._get_parent,
915+ '_get_childs':self._get_childs
916+ })
917+
918+ def _get_parent(self,pick_id):
919+ cr=self.cr
920+ uid=self.uid
921+ context=self.localcontext
922+ move_obj=self.pool.get('stock.move')
923+ move_ids=move_obj.search(cr,uid,[('picking_id','=',pick_id),("parent_bom_id","!=",False)])
924+ moves=move_obj.browse(cr,uid,move_ids,context=context)
925+ for move in moves:
926+ production=move.kit_id
927+ if production:
928+ product_qty=production.product_qty
929+ sale_id=move.sale_id.id
930+
931+ parent_move_ids=move_obj.search(cr,uid,[("sale_id","=",sale_id),
932+ ("product_qty","=",product_qty),
933+ ("product_id","=",production.product_id.id)])
934+ parent_moves=move_obj.browse(cr,uid,parent_move_ids)
935+ print "parent_move_ids",parent_move_ids
936+# return {'child_moves':moves,"parent_moves":parent_moves}
937+ return parent_moves
938+ break
939+
940+ def _get_childs(self,pick_id):
941+ cr=self.cr
942+ uid=self.uid
943+ context=self.localcontext
944+ move_obj=self.pool.get('stock.move')
945+ move_ids=move_obj.search(cr,uid,[('picking_id','=',pick_id),("parent_bom_id","!=",False)])
946+ print move_ids,'====================move_ids================================'
947+ return move_obj.browse(cr,uid,move_ids,context=context)
948+
949+report_sxw.report_sxw('report.delivery.bom', 'stock.picking', 'addons/assembly_bom/report/delivery_report.rml', parser=delivery_report, header=False)
950+
951+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
952+
953
954=== added file 'assembly_bom/report/delivery_report.rml'
955--- assembly_bom/report/delivery_report.rml 1970-01-01 00:00:00 +0000
956+++ assembly_bom/report/delivery_report.rml 2011-10-06 15:31:48 +0000
957@@ -0,0 +1,384 @@
958+<?xml version="1.0"?>
959+<document filename="test.pdf">
960+ <template pageSize="(595.0,842.0)" title="Test" author="Martin Simon" allowSplitting="20">
961+ <pageTemplate id="first">
962+ <frame id="first" x1="0.0" y1="57.0" width="538" height="728"/>
963+ </pageTemplate>
964+ </template>
965+ <stylesheet>
966+ <blockTableStyle id="Standard_Outline">
967+ <blockAlignment value="LEFT"/>
968+ <blockValign value="TOP"/>
969+ </blockTableStyle>
970+ <blockTableStyle id="Table_Address_detail">
971+ <blockAlignment value="LEFT"/>
972+ <blockValign value="TOP"/>
973+ </blockTableStyle>
974+ <blockTableStyle id="Table_Title_String">
975+ <blockAlignment value="LEFT"/>
976+ <blockValign value="TOP"/>
977+ </blockTableStyle>
978+ <blockTableStyle id="Header_Order_Reference_Tbl">
979+ <blockAlignment value="LEFT"/>
980+ <blockValign value="TOP"/>
981+ <lineStyle kind="LINEBEFORE" colorName="#e6e6e6" start="0,0" stop="0,-1"/>
982+ <lineStyle kind="LINEABOVE" colorName="#e6e6e6" start="0,0" stop="0,0"/>
983+ <lineStyle kind="LINEBELOW" colorName="#e6e6e6" start="0,-1" stop="0,-1"/>
984+ <lineStyle kind="LINEBEFORE" colorName="#e6e6e6" start="1,0" stop="1,-1"/>
985+ <lineStyle kind="LINEABOVE" colorName="#e6e6e6" start="1,0" stop="1,0"/>
986+ <lineStyle kind="LINEBELOW" colorName="#e6e6e6" start="1,-1" stop="1,-1"/>
987+ <lineStyle kind="LINEBEFORE" colorName="#e6e6e6" start="2,0" stop="2,-1"/>
988+ <lineStyle kind="LINEABOVE" colorName="#e6e6e6" start="2,0" stop="2,0"/>
989+ <lineStyle kind="LINEBELOW" colorName="#e6e6e6" start="2,-1" stop="2,-1"/>
990+ <lineStyle kind="LINEBEFORE" colorName="#e6e6e6" start="3,0" stop="3,-1"/>
991+ <lineStyle kind="LINEABOVE" colorName="#e6e6e6" start="3,0" stop="3,0"/>
992+ <lineStyle kind="LINEBELOW" colorName="#e6e6e6" start="3,-1" stop="3,-1"/>
993+ <lineStyle kind="LINEBEFORE" colorName="#e6e6e6" start="4,0" stop="4,-1"/>
994+ <lineStyle kind="LINEAFTER" colorName="#e6e6e6" start="4,0" stop="4,-1"/>
995+ <lineStyle kind="LINEABOVE" colorName="#e6e6e6" start="4,0" stop="4,0"/>
996+ <lineStyle kind="LINEBELOW" colorName="#e6e6e6" start="4,-1" stop="4,-1"/>
997+ </blockTableStyle>
998+ <blockTableStyle id="Content_Order_Reference_Table">
999+ <blockAlignment value="LEFT"/>
1000+ <blockValign value="TOP"/>
1001+ <lineStyle kind="LINEBEFORE" colorName="#e6e6e6" start="0,0" stop="0,-1"/>
1002+ <lineStyle kind="LINEABOVE" colorName="#e6e6e6" start="0,0" stop="0,0"/>
1003+ <lineStyle kind="LINEBELOW" colorName="#e6e6e6" start="0,-1" stop="0,-1"/>
1004+ <lineStyle kind="LINEBEFORE" colorName="#e6e6e6" start="1,0" stop="1,-1"/>
1005+ <lineStyle kind="LINEABOVE" colorName="#e6e6e6" start="1,0" stop="1,0"/>
1006+ <lineStyle kind="LINEBELOW" colorName="#e6e6e6" start="1,-1" stop="1,-1"/>
1007+ <lineStyle kind="LINEBEFORE" colorName="#e6e6e6" start="2,0" stop="2,-1"/>
1008+ <lineStyle kind="LINEABOVE" colorName="#e6e6e6" start="2,0" stop="2,0"/>
1009+ <lineStyle kind="LINEBELOW" colorName="#e6e6e6" start="2,-1" stop="2,-1"/>
1010+ <lineStyle kind="LINEBEFORE" colorName="#e6e6e6" start="3,0" stop="3,-1"/>
1011+ <lineStyle kind="LINEABOVE" colorName="#e6e6e6" start="3,0" stop="3,0"/>
1012+ <lineStyle kind="LINEBELOW" colorName="#e6e6e6" start="3,-1" stop="3,-1"/>
1013+ <lineStyle kind="LINEBEFORE" colorName="#e6e6e6" start="4,0" stop="4,-1"/>
1014+ <lineStyle kind="LINEAFTER" colorName="#e6e6e6" start="4,0" stop="4,-1"/>
1015+ <lineStyle kind="LINEABOVE" colorName="#e6e6e6" start="4,0" stop="4,0"/>
1016+ <lineStyle kind="LINEBELOW" colorName="#e6e6e6" start="4,-1" stop="4,-1"/>
1017+ </blockTableStyle>
1018+ <blockTableStyle id="Move_Line_Header">
1019+ <blockAlignment value="LEFT"/>
1020+ <blockValign value="TOP"/>
1021+ <lineStyle kind="LINEBELOW" colorName="#000000" start="0,-1" stop="0,-1"/>
1022+ <lineStyle kind="LINEBELOW" colorName="#000000" start="1,-1" stop="1,-1"/>
1023+ <lineStyle kind="LINEBELOW" colorName="#000000" start="2,-1" stop="2,-1"/>
1024+ <lineStyle kind="LINEBELOW" colorName="#000000" start="3,-1" stop="3,-1"/>
1025+ <lineStyle kind="LINEBELOW" colorName="#000000" start="4,-1" stop="4,-1"/>
1026+ </blockTableStyle>
1027+ <blockTableStyle id="Move_Line_Contect_Assign_State">
1028+ <blockAlignment value="LEFT"/>
1029+ <blockValign value="TOP"/>
1030+ <lineStyle kind="LINEBELOW" colorName="#e6e6e6" start="0,-1" stop="0,-1"/>
1031+ <lineStyle kind="LINEBEFORE" colorName="#ffffff" start="1,2" stop="1,-1"/>
1032+ <lineStyle kind="LINEABOVE" colorName="#ffffff" start="1,2" stop="1,2"/>
1033+ <lineStyle kind="LINEBELOW" colorName="#ffffff" start="1,-1" stop="1,-1"/>
1034+ <lineStyle kind="LINEBEFORE" colorName="#ffffff" start="2,2" stop="2,-1"/>
1035+ <lineStyle kind="LINEABOVE" colorName="#ffffff" start="2,2" stop="2,2"/>
1036+ <lineStyle kind="LINEBELOW" colorName="#ffffff" start="2,-1" stop="2,-1"/>
1037+ <lineStyle kind="LINEBEFORE" colorName="#ffffff" start="3,2" stop="3,-1"/>
1038+ <lineStyle kind="LINEABOVE" colorName="#ffffff" start="3,2" stop="3,2"/>
1039+ <lineStyle kind="LINEBELOW" colorName="#ffffff" start="3,-1" stop="3,-1"/>
1040+ <lineStyle kind="LINEBEFORE" colorName="#ffffff" start="4,2" stop="4,-1"/>
1041+ <lineStyle kind="LINEAFTER" colorName="#ffffff" start="4,2" stop="4,-1"/>
1042+ <lineStyle kind="LINEABOVE" colorName="#ffffff" start="4,2" stop="4,2"/>
1043+ <lineStyle kind="LINEBELOW" colorName="#ffffff" start="4,-1" stop="4,-1"/>
1044+ <lineStyle kind="LINEBEFORE" colorName="#ffffff" start="0,3" stop="0,-1"/>
1045+ <lineStyle kind="LINEABOVE" colorName="#ffffff" start="0,3" stop="0,3"/>
1046+ <lineStyle kind="LINEBELOW" colorName="#ffffff" start="0,-1" stop="0,-1"/>
1047+ <lineStyle kind="LINEBEFORE" colorName="#ffffff" start="1,3" stop="1,-1"/>
1048+ <lineStyle kind="LINEABOVE" colorName="#ffffff" start="1,3" stop="1,3"/>
1049+ <lineStyle kind="LINEBELOW" colorName="#ffffff" start="1,-1" stop="1,-1"/>
1050+ <lineStyle kind="LINEBEFORE" colorName="#ffffff" start="2,3" stop="2,-1"/>
1051+ <lineStyle kind="LINEABOVE" colorName="#ffffff" start="2,3" stop="2,3"/>
1052+ <lineStyle kind="LINEBELOW" colorName="#ffffff" start="2,-1" stop="2,-1"/>
1053+ <lineStyle kind="LINEBEFORE" colorName="#ffffff" start="3,3" stop="3,-1"/>
1054+ <lineStyle kind="LINEAFTER" colorName="#ffffff" start="3,3" stop="3,-1"/>
1055+ <lineStyle kind="LINEABOVE" colorName="#ffffff" start="3,3" stop="3,3"/>
1056+ <lineStyle kind="LINEBELOW" colorName="#ffffff" start="3,-1" stop="3,-1"/>
1057+ </blockTableStyle>
1058+ <blockTableStyle id="Table1">
1059+ <blockAlignment value="LEFT"/>
1060+ <blockValign value="TOP"/>
1061+ <lineStyle kind="LINEBEFORE" colorName="#ffffff" start="0,0" stop="0,-1"/>
1062+ <lineStyle kind="LINEABOVE" colorName="#ffffff" start="0,0" stop="0,0"/>
1063+ <lineStyle kind="LINEBELOW" colorName="#ffffff" start="0,-1" stop="0,-1"/>
1064+ <lineStyle kind="LINEBEFORE" colorName="#ffffff" start="1,0" stop="1,-1"/>
1065+ <lineStyle kind="LINEABOVE" colorName="#ffffff" start="1,0" stop="1,0"/>
1066+ <lineStyle kind="LINEBELOW" colorName="#ffffff" start="1,-1" stop="1,-1"/>
1067+ <lineStyle kind="LINEBEFORE" colorName="#ffffff" start="2,0" stop="2,-1"/>
1068+ <lineStyle kind="LINEABOVE" colorName="#ffffff" start="2,0" stop="2,0"/>
1069+ <lineStyle kind="LINEBELOW" colorName="#ffffff" start="2,-1" stop="2,-1"/>
1070+ <lineStyle kind="LINEBEFORE" colorName="#ffffff" start="3,0" stop="3,-1"/>
1071+ <lineStyle kind="LINEAFTER" colorName="#ffffff" start="3,0" stop="3,-1"/>
1072+ <lineStyle kind="LINEABOVE" colorName="#ffffff" start="3,0" stop="3,0"/>
1073+ <lineStyle kind="LINEBELOW" colorName="#ffffff" start="3,-1" stop="3,-1"/>
1074+ </blockTableStyle>
1075+ <blockTableStyle id="Table4">
1076+ <blockAlignment value="LEFT"/>
1077+ <blockValign value="TOP"/>
1078+ <lineStyle kind="LINEBELOW" colorName="#e6e6e6" start="0,-1" stop="0,-1"/>
1079+ <lineStyle kind="LINEBELOW" colorName="#e6e6e6" start="1,-1" stop="1,-1"/>
1080+ <lineStyle kind="LINEBELOW" colorName="#e6e6e6" start="2,-1" stop="2,-1"/>
1081+ <lineStyle kind="LINEBELOW" colorName="#e6e6e6" start="3,-1" stop="3,-1"/>
1082+ <lineStyle kind="LINEBELOW" colorName="#e6e6e6" start="4,-1" stop="4,-1"/>
1083+ </blockTableStyle>
1084+ <blockTableStyle id="Table5">
1085+ <blockAlignment value="LEFT"/>
1086+ <blockValign value="TOP"/>
1087+ <lineStyle kind="LINEABOVE" colorName="#000000" start="1,0" stop="1,0"/>
1088+ <lineStyle kind="LINEABOVE" colorName="#000000" start="2,0" stop="2,0"/>
1089+ </blockTableStyle>
1090+ <initialize>
1091+ <paraStyle name="all" alignment="justify"/>
1092+ </initialize>
1093+ <paraStyle name="P1" fontName="Times-Roman" alignment="LEFT"/>
1094+ <paraStyle name="P2" fontName="Helvetica-Bold" fontSize="8.0" leading="10" alignment="CENTER" spaceBefore="0.0" spaceAfter="0.0"/>
1095+ <paraStyle name="P3" fontName="Helvetica" fontSize="14.0" leading="17" alignment="LEFT"/>
1096+ <paraStyle name="P4" fontName="Helvetica" fontSize="8.0" leading="10" alignment="LEFT"/>
1097+ <paraStyle name="P5" fontName="Helvetica-Bold" fontSize="14.0" leading="17" alignment="RIGHT" spaceBefore="0.0" spaceAfter="0.0"/>
1098+ <paraStyle name="P6" fontName="Helvetica-Bold" fontSize="14.0" leading="17" alignment="LEFT" spaceBefore="0.0" spaceAfter="0.0"/>
1099+ <paraStyle name="P7" fontName="Helvetica-Bold" fontSize="14.0" leading="17" alignment="LEFT" spaceBefore="0.0" spaceAfter="0.0"/>
1100+ <paraStyle name="P8" fontName="Helvetica" fontSize="14.0" leading="17" alignment="LEFT" spaceBefore="0.0" spaceAfter="0.0"/>
1101+ <paraStyle name="P9" fontName="Helvetica-Bold" fontSize="8.0" leading="10" alignment="LEFT" spaceBefore="0.0" spaceAfter="0.0"/>
1102+ <paraStyle name="P10" fontName="Helvetica-Bold" fontSize="8.0" leading="10" alignment="LEFT" spaceBefore="0.0" spaceAfter="0.0"/>
1103+ <paraStyle name="P11" fontName="Helvetica-Bold" fontSize="14.0" leading="17" alignment="LEFT" spaceBefore="0.0" spaceAfter="0.0"/>
1104+ <paraStyle name="P12" fontName="Helvetica" fontSize="9.0" leading="11" alignment="LEFT" spaceBefore="0.0" spaceAfter="0.0"/>
1105+ <paraStyle name="Standard" fontName="Times-Roman"/>
1106+ <paraStyle name="Heading" fontName="Helvetica" fontSize="14.0" leading="17" spaceBefore="12.0" spaceAfter="6.0"/>
1107+ <paraStyle name="Text body" fontName="Times-Roman" spaceBefore="0.0" spaceAfter="6.0"/>
1108+ <paraStyle name="List" fontName="Times-Roman" spaceBefore="0.0" spaceAfter="6.0"/>
1109+ <paraStyle name="Caption" fontName="Times-Italic" fontSize="12.0" leading="15" spaceBefore="6.0" spaceAfter="6.0"/>
1110+ <paraStyle name="Index" fontName="Times-Roman"/>
1111+ <paraStyle name="terp_header" fontName="Helvetica-Bold" fontSize="12.0" leading="15" alignment="LEFT" spaceBefore="0.0" spaceAfter="0.0"/>
1112+ <paraStyle name="terp_header_Centre" fontName="Helvetica-Bold" fontSize="12.0" leading="15" alignment="CENTER" spaceBefore="0.0" spaceAfter="0.0"/>
1113+ <paraStyle name="terp_default_8" fontName="Helvetica" fontSize="8.0" leading="10" alignment="LEFT" spaceBefore="0.0" spaceAfter="0.0"/>
1114+ <paraStyle name="terp_default_Bold_8" fontName="Helvetica-Bold" fontSize="8.0" leading="10" alignment="LEFT" spaceBefore="0.0" spaceAfter="0.0"/>
1115+ <paraStyle name="terp_tblheader_Details" fontName="Helvetica-Bold" fontSize="9.0" leading="11" alignment="LEFT" spaceBefore="0.0" spaceAfter="0.0"/>
1116+ <paraStyle name="terp_tblheader_Details_Centre" fontName="Helvetica-Bold" fontSize="9.0" leading="11" alignment="CENTER" spaceBefore="0.0" spaceAfter="0.0"/>
1117+ <paraStyle name="terp_default_Centre_8" fontName="Helvetica" fontSize="8.0" leading="10" alignment="CENTER" spaceBefore="0.0" spaceAfter="0.0"/>
1118+ <paraStyle name="terp_default_Centre_9" fontName="Helvetica" fontSize="9.0" leading="11" alignment="CENTER" spaceBefore="0.0" spaceAfter="0.0"/>
1119+ <paraStyle name="terp_tblheader_General" fontName="Helvetica-Bold" fontSize="8.0" leading="10" alignment="LEFT" spaceBefore="6.0" spaceAfter="6.0"/>
1120+ <paraStyle name="terp_tblheader_General_Centre" fontName="Helvetica-Bold" fontSize="8.0" leading="10" alignment="CENTER" spaceBefore="0.0" spaceAfter="0.0"/>
1121+ <paraStyle name="Table Contents" fontName="Times-Roman"/>
1122+ <paraStyle name="Footer" fontName="Times-Roman"/>
1123+ <paraStyle name="Table Heading" fontName="Times-Roman" alignment="CENTER"/>
1124+ <paraStyle name="Horizontal Line" fontName="Times-Roman" fontSize="6.0" leading="8" spaceBefore="0.0" spaceAfter="14.0"/>
1125+ <paraStyle name="Heading 9" fontName="Helvetica-Bold" fontSize="75%" leading="NaN" spaceBefore="12.0" spaceAfter="6.0"/>
1126+ <paraStyle name="terp_tblheader_General_Right" fontName="Helvetica-Bold" fontSize="8.0" leading="10" alignment="RIGHT" spaceBefore="0.0" spaceAfter="0.0"/>
1127+ <paraStyle name="terp_tblheader_Details_Right" fontName="Helvetica-Bold" fontSize="9.0" leading="11" alignment="RIGHT" spaceBefore="0.0" spaceAfter="0.0"/>
1128+ <paraStyle name="terp_default_Right_8" fontName="Helvetica" fontSize="8.0" leading="10" alignment="RIGHT" spaceBefore="0.0" spaceAfter="0.0"/>
1129+ <paraStyle name="terp_header_Right" fontName="Helvetica-Bold" fontSize="15.0" leading="19" alignment="LEFT" spaceBefore="0.0" spaceAfter="0.0"/>
1130+ <paraStyle name="terp_default_address" fontName="Helvetica" fontSize="10.0" leading="13" alignment="LEFT" spaceBefore="0.0" spaceAfter="0.0"/>
1131+ <paraStyle name="terp_default_9" fontName="Helvetica" fontSize="9.0" leading="11" alignment="LEFT" spaceBefore="0.0" spaceAfter="0.0"/>
1132+ <paraStyle name="terp_default_Bold_9" fontName="Helvetica-Bold" fontSize="9.0" leading="11" alignment="LEFT" spaceBefore="0.0" spaceAfter="0.0"/>
1133+ <paraStyle name="terp_default_Right_9" fontName="Helvetica" fontSize="9.0" leading="11" alignment="RIGHT" spaceBefore="0.0" spaceAfter="0.0"/>
1134+ <paraStyle name="terp_default_2" fontName="Helvetica" fontSize="2.0" leading="3" alignment="LEFT" spaceBefore="0.0" spaceAfter="0.0"/>
1135+ <paraStyle name="terp_default_5cm_Above_Space" fontName="Helvetica" fontSize="8.0" leading="10" alignment="LEFT" spaceBefore="6.0" spaceAfter="0.0"/>
1136+ <paraStyle name="terp_default_1cm_above_space" fontName="Helvetica" fontSize="8.0" leading="10" alignment="LEFT" spaceBefore="3.0" spaceAfter="0.0"/>
1137+ <images/>
1138+ </stylesheet>
1139+ <story>
1140+ <para style="P1">[[repeatIn(objects,'picking')]] </para>
1141+ <para style="terp_default_9">
1142+ <font color="white"> </font>
1143+ </para>
1144+ <blockTable colWidths="269.0,269.0" style="Table_Address_detail">
1145+ <tr>
1146+ <td>
1147+ <para style="terp_default_Bold_9">Shipping Address :</para>
1148+ <para style="terp_default_9">[[ (picking.address_id and picking.address_id.partner_id and picking.address_id.partner_id.title.name) or '' ]] [[ picking.address_id and picking.address_id.partner_id and picking.address_id.partner_id.name ]]</para>
1149+ <para style="terp_default_9">[[ picking.address_id and picking.address_id.street or '' ]]</para>
1150+ <para style="terp_default_9">[[ (picking.address_id and picking.address_id.street2) or removeParentNode('para') ]]</para>
1151+ <para style="terp_default_9">[[ picking.address_id and picking.address_id.zip or '' ]] [[ picking.address_id and picking.address_id.city or '' ]]</para>
1152+ <para style="terp_default_9">[[ (picking.address_id and picking.address_id.state_id and picking.address_id.state_id.name) or removeParentNode('para') ]]</para>
1153+ <para style="terp_default_9">[[ (picking.address_id and picking.address_id.country_id and picking.address_id.country_id.name) or '' ]]</para>
1154+ </td>
1155+ <td>
1156+ <para style="terp_default_Bold_9">Contact Address :</para>
1157+ <para style="terp_default_9">[[ picking.address_id and picking.address_id.title.name or '' ]] [[ picking.address_id and picking.address_id.name or '' ]]</para>
1158+ <para style="terp_default_9">[[ picking.address_id and picking.address_id.street or '' ]]</para>
1159+ <para style="terp_default_9">[[ (picking.address_id and picking.address_id.street2) or removeParentNode('para') ]]</para>
1160+ <para style="terp_default_9">[[ picking.address_id and picking.address_id.zip or '' ]] [[ picking.address_id and picking.address_id.city or '' ]]</para>
1161+ <para style="terp_default_9">[[ (picking.address_id and picking.address_id.state_id and picking.address_id.state_id.name) or removeParentNode('para') ]]</para>
1162+ <para style="terp_default_9">[[ (picking.address_id and picking.address_id.country_id and picking.address_id.country_id.name) or '' ]]</para>
1163+ </td>
1164+ </tr>
1165+ </blockTable>
1166+ <para style="terp_default_5cm_Above_Space">
1167+ <font color="white"> </font>
1168+ </para>
1169+ <para style="terp_default_5cm_Above_Space">
1170+ <font color="white"> </font>
1171+ </para>
1172+ <blockTable colWidths="538.0" style="Table_Title_String">
1173+ <tr>
1174+ <td>
1175+ <para style="terp_header">Packing List: [[ picking.name ]]</para>
1176+ </td>
1177+ </tr>
1178+ </blockTable>
1179+ <para style="terp_default_5cm_Above_Space">
1180+ <font color="white"> </font>
1181+ </para>
1182+ <blockTable colWidths="100.0,100.0,108.0,107.0,109.0" style="Header_Order_Reference_Tbl">
1183+ <tr>
1184+ <td>
1185+ <para style="terp_tblheader_General_Centre">Journal</para>
1186+ </td>
1187+ <td>
1188+ <para style="terp_tblheader_General_Centre">Order(Origin)</para>
1189+ </td>
1190+ <td>
1191+ <para style="terp_tblheader_General_Centre">Recipient</para>
1192+ </td>
1193+ <td>
1194+ <para style="terp_tblheader_General_Centre">Expected Shipping Date </para>
1195+ </td>
1196+ <td>
1197+ <para style="terp_tblheader_General_Centre">weight</para>
1198+ </td>
1199+ </tr>
1200+ </blockTable>
1201+ <blockTable colWidths="100.0,100.0,108.0,107.0,109.0" style="Content_Order_Reference_Table">
1202+ <tr>
1203+ <td>
1204+ <para style="terp_default_Centre_8">[[ picking.stock_journal_id.name]]</para>
1205+ </td>
1206+ <td>
1207+ <para style="terp_default_Centre_8">[[ picking.origin or '']]</para>
1208+ </td>
1209+ <td>
1210+ <para style="terp_default_Centre_8">[[ (picking.address_id and picking.address_id.title.name) or '' ]] [[ (picking.address_id and picking.address_id.name) or '' ]] </para>
1211+ </td>
1212+ <td>
1213+ <para style="terp_default_Centre_8">[[ formatLang(picking.min_date,date_time = True) ]]</para>
1214+ </td>
1215+ <td>
1216+ <para style="terp_default_Centre_8">[[ 'weight' in picking._columns.keys() and picking.weight or '']]</para>
1217+ </td>
1218+ </tr>
1219+ </blockTable>
1220+ <para style="terp_default_5cm_Above_Space">
1221+ <font color="white"> </font>
1222+ </para>
1223+ <blockTable colWidths="256.0,73.0,59.0,59.0,71.0" repeatRows="1" style="Move_Line_Header">
1224+ <tr>
1225+ <td>
1226+ <para style="terp_tblheader_Details">Description</para>
1227+ </td>
1228+ <td>
1229+ <para style="terp_tblheader_Details_Centre">Lot</para>
1230+ </td>
1231+ <td>
1232+ <para style="terp_tblheader_Details_Centre">State</para>
1233+ </td>
1234+ <td>
1235+ <para style="terp_tblheader_Details_Right">Location</para>
1236+ </td>
1237+ <td>
1238+ <para style="terp_tblheader_Details_Right">
1239+ <font color="white"> </font>
1240+ </para>
1241+ <para style="terp_tblheader_Details_Right">
1242+ <font color="white"> </font>
1243+ </para>
1244+ </td>
1245+ </tr>
1246+ </blockTable>
1247+ <section>
1248+ <para style="terp_default_2">
1249+ <font color="white"> </font>
1250+ </para>
1251+ <blockTable colWidths="522.0" style="Move_Line_Contect_Assign_State">
1252+ <tr>
1253+ <td>
1254+ <para style="P9">[[_get_parent(picking.id)[0].product_id.name]]</para>
1255+ <para style="P9">
1256+ <font color="white"> </font>
1257+ </para>
1258+ </td>
1259+ </tr>
1260+ <tr>
1261+ <td>
1262+ <para style="P12">[[ repeatIn([line for line in picking.move_lines if (line.state == 'confirmed' or line.state=='done' or line.state=='assigned')],'move_lines') ]]</para>
1263+ <para style="P10">
1264+ <font face="Times-Roman" size="9.0">[[ (picking.move_lines!=[] and removeParentNode('para')) or removeParentNode('section')]]</font>
1265+ </para>
1266+ </td>
1267+ </tr>
1268+ <tr>
1269+ <td>
1270+ <blockTable colWidths="129.0,129.0,129.0,129.0" style="Table1">
1271+ <tr>
1272+ <td>
1273+ <para style="terp_default_9">[[move_lines.product_id.name and '*'*4+(move_lines.product_id.name)or removeParentNode('tr')]] </para>
1274+ </td>
1275+ <td>
1276+ <para style="terp_default_Centre_9">[[ (move_lines.prodlot_id and move_lines.prodlot_id.name) or '' ]]</para>
1277+ </td>
1278+ <td>
1279+ <para style="terp_default_9">[[ move_lines.state ]]</para>
1280+ </td>
1281+ <td>
1282+ <para style="terp_default_Right_9">[[ (move_lines.location_id and move_lines.location_id.name) or '' ]] </para>
1283+ </td>
1284+ </tr>
1285+ </blockTable>
1286+ <para style="P9">
1287+ <font color="white"> </font>
1288+ </para>
1289+ </td>
1290+ </tr>
1291+ </blockTable>
1292+ </section>
1293+ <para style="terp_default_Bold_9">[[ ([line for line in picking.move_lines if (line.state == 'draft' or line.state=='waiting' )]) and 'Non Assigned Products:' or removeParentNode('para') ]]</para>
1294+ <para style="terp_default_Bold_9">
1295+ <font color="white"> </font>
1296+ </para>
1297+ <para style="terp_default_2"/>
1298+ <section>
1299+ <para style="terp_default_2">[[ repeatIn([line for line in picking.move_lines if (line.state == 'draft' or line.state=='waiting')],'move_lines') ]]</para>
1300+ <para style="terp_default_2">[[ (picking.move_lines!=[] and removeParentNode('para')) or removeParentNode('section')]]</para>
1301+ <blockTable colWidths="257.0,74.0,57.0,61.0,72.0" style="Table4">
1302+ <tr>
1303+ <td>
1304+ <para style="terp_default_9"><font face="Times-Roman" size="9.0">[[ (move_lines.product_id.default_code) or removeParentNode('font') ]] </font>[[“..”*5 +move_lines.product_id.name ]] [[ move_lines.product_id.variants or '']]</para>
1305+ </td>
1306+ <td>
1307+ <para style="terp_default_Centre_9">[[ (move_lines.prodlot_id and move_lines.prodlot_id.name) or '' ]]</para>
1308+ </td>
1309+ <td>
1310+ <para style="terp_default_9">[[ move_lines.state ]]</para>
1311+ </td>
1312+ <td>
1313+ <para style="terp_default_Right_9">[[ (move_lines.location_id and move_lines.location_id.name) or '' ]] </para>
1314+ </td>
1315+ <td>
1316+ <para style="terp_default_Right_9">[[ formatLang(move_lines.product_qty) ]] [[ move_lines.product_uom.name ]]</para>
1317+ </td>
1318+ </tr>
1319+ </blockTable>
1320+ </section>
1321+ <blockTable colWidths="388.0,56.0,77.0" style="Table5">
1322+ <tr>
1323+ <td>
1324+ <para style="terp_default_9">
1325+ <font color="white"> </font>
1326+ </para>
1327+ </td>
1328+ <td>
1329+ <para style="terp_tblheader_Details_Centre">Total</para>
1330+ </td>
1331+ <td>
1332+ <para style="terp_tblheader_Details_Right">[[ formatLang(get_qtytotal(picking.move_lines)['quantity']) ]] [[ get_qtytotal(picking.move_lines)['uom'] ]]</para>
1333+ </td>
1334+ </tr>
1335+ </blockTable>
1336+ <para style="P11">
1337+ <font color="white"> </font>
1338+ </para>
1339+ </story>
1340+</document>
1341+
1342
1343=== added file 'assembly_bom/report/normalized_oo2rml.xsl'
1344--- assembly_bom/report/normalized_oo2rml.xsl 1970-01-01 00:00:00 +0000
1345+++ assembly_bom/report/normalized_oo2rml.xsl 2011-10-06 15:31:48 +0000
1346@@ -0,0 +1,696 @@
1347+<?xml version="1.0" encoding="utf-8"?>
1348+<xsl:stylesheet
1349+ version="1.0"
1350+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
1351+ xmlns:fo="http://www.w3.org/1999/XSL/Format"
1352+ xmlns:office="http://openoffice.org/2000/office"
1353+ xmlns:style="http://openoffice.org/2000/style"
1354+ xmlns:text="http://openoffice.org/2000/text"
1355+ xmlns:table="http://openoffice.org/2000/table"
1356+ xmlns:draw="http://openoffice.org/2000/drawing"
1357+ xmlns:xlink="http://www.w3.org/1999/xlink"
1358+ xmlns:number="http://openoffice.org/2000/datastyle"
1359+ xmlns:svg="http://www.w3.org/2000/svg"
1360+ xmlns:chart="http://openoffice.org/2000/chart"
1361+ xmlns:dr3d="http://openoffice.org/2000/dr3d"
1362+ xmlns:math="http://www.w3.org/1998/Math/MathML"
1363+ xmlns:form="http://openoffice.org/2000/form"
1364+ xmlns:script="http://openoffice.org/2000/script"
1365+ office:class="text" office:version="1.0"
1366+ exclude-result-prefixes = "xsl fo office style text table draw xlink number svg chart dr3d math form script">
1367+
1368+ <!--TODO's: indent, picture cache (trml2pdf) -->
1369+
1370+<xsl:output method="xml" indent="yes" />
1371+<xsl:strip-space elements="*"/>
1372+
1373+<xsl:key name="text_style" match="style:style[@style:family='text']" use="@style:name" />
1374+<xsl:key name="page_break_before" match="style:style[@style:family='paragraph' and ./style:properties/@fo:break-before='page']" use="@style:name" />
1375+<xsl:key name="page_break_after" match="style:style[@style:family='paragraph' and ./style:properties/@fo:break-after='page']" use="@style:name" />
1376+<xsl:key name="table_column_style" match="style:style[@style:family='table-column']" use="@style:name" />
1377+<xsl:key name="table_cell_style" match="style:style[@style:family='table-cell']" use="@style:name" />
1378+<xsl:key name="paragraph_style" match="style:style[@style:family='paragraph']" use="@style:name" />
1379+
1380+<xsl:template match="office:document-content">
1381+ <document filename="test.pdf">
1382+ <xsl:apply-templates select="office:automatic-styles" />
1383+ <xsl:apply-templates select="office:body" />
1384+ </document>
1385+</xsl:template>
1386+
1387+<xsl:template name="page_size">
1388+ <xsl:attribute name="pageSize">
1389+ <xsl:text>(</xsl:text>
1390+ <xsl:value-of select="//transferredfromstylesxml/style:page-master[1]/style:properties/@fo:page-width" />
1391+ <xsl:text>,</xsl:text>
1392+ <xsl:value-of select="//transferredfromstylesxml/style:page-master[1]/style:properties/@fo:page-height" />
1393+ <xsl:text>)</xsl:text>
1394+ </xsl:attribute>
1395+</xsl:template>
1396+
1397+<xsl:template name="fixed_frame">
1398+ <xsl:for-each select="//draw:text-box">
1399+ <frame>
1400+ <xsl:attribute name="id"><xsl:value-of select="./@draw:name" /></xsl:attribute>
1401+ <xsl:attribute name="x1"><xsl:value-of select="./@svg:x" /></xsl:attribute>
1402+ <xsl:attribute name="y1">
1403+ <xsl:value-of
1404+ select="//transferredfromstylesxml/style:page-master[1]/style:properties/@fo:page-height - ./@svg:y - ./@fo:min-height" />
1405+ </xsl:attribute>
1406+ <xsl:attribute name="width">
1407+ <xsl:value-of select="./@svg:width" />
1408+ </xsl:attribute>
1409+ <xsl:attribute name="height">
1410+ <xsl:value-of select="./@fo:min-height" />
1411+ </xsl:attribute>
1412+ </frame>
1413+ </xsl:for-each>
1414+</xsl:template>
1415+
1416+<xsl:template name="margin_sizes">
1417+ <xsl:variable name="margin_left" select="//transferredfromstylesxml/style:page-master[1]/style:properties/@fo:margin-left" />
1418+ <xsl:variable name="margin_right" select="//transferredfromstylesxml/style:page-master[1]/style:properties/@fo:margin-right" />
1419+ <xsl:variable name="margin_top" select="//transferredfromstylesxml/style:page-master[1]/style:properties/@fo:margin-top" />
1420+ <xsl:variable name="margin_bottom" select="//transferredfromstylesxml/style:page-master[1]/style:properties/@fo:margin-bottom" />
1421+ <xsl:variable name="page_width" select="//transferredfromstylesxml/style:page-master[1]/style:properties/@fo:page-width" />
1422+ <xsl:variable name="page_height" select="//transferredfromstylesxml/style:page-master[1]/style:properties/@fo:page-height" />
1423+ <xsl:attribute name="x1"><xsl:value-of select="$margin_left" /></xsl:attribute>
1424+ <xsl:attribute name="y1"><xsl:value-of select="$margin_bottom" /></xsl:attribute>
1425+ <xsl:attribute name="width"><xsl:value-of select="$page_width - $margin_left - $margin_right"/></xsl:attribute>
1426+ <xsl:attribute name="height"><xsl:value-of select="$page_height - $margin_bottom - $margin_top"/></xsl:attribute>
1427+</xsl:template>
1428+
1429+<xsl:template name="text_width">
1430+ <!-- You need this for the workaround to make primitive outlines-->
1431+ <xsl:variable name="margin_left" select="//transferredfromstylesxml/style:page-master[1]/style:properties/@fo:margin-left" />
1432+ <xsl:variable name="margin_right" select="//transferredfromstylesxml/style:page-master[1]/style:properties/@fo:margin-right" />
1433+ <xsl:variable name="page_width" select="//transferredfromstylesxml/style:page-master[1]/style:properties/@fo:page-width" />
1434+ <xsl:value-of select="$page_width - $margin_left - $margin_right - 18"/>
1435+</xsl:template>
1436+
1437+
1438+
1439+<xsl:template match="office:automatic-styles">
1440+ <!--<template pageSize="(21cm, 29.7cm)" leftMargin="1.0cm" rightMargin="2.0cm" topMargin="1.0cm" bottomMargin="1.0cm" title="Test" author="Martin Simon" allowSplitting="20">-->
1441+ <template pageSize="(21cm, 29.7cm)" title="Test" author="Martin Simon" allowSplitting="20">
1442+ <xsl:call-template name="page_size" />
1443+ <pageTemplate id="first">
1444+ <xsl:call-template name="fixed_frame" />
1445+ <frame id="first" x1="2cm" y1="2cm" width="17cm" height="26cm">
1446+ <xsl:call-template name="margin_sizes" />
1447+ </frame>
1448+ </pageTemplate>
1449+ </template>
1450+ <stylesheet>
1451+ <!--A table style to simulate primitive outlines -till the <addOutline> tag is implemented in trml2pdf -->
1452+ <blockTableStyle id="Standard_Outline">
1453+ <blockAlignment value="LEFT"/>
1454+ <blockValign value="TOP"/>
1455+ </blockTableStyle>
1456+ <!--use two standard table grid styles like PyOpenOffice "Old Way": with and without a grid-->
1457+ <!--TODO insert table cell colors here, not within the <td> tag - otherwise
1458+ it will not work with flowables as cell content-->
1459+ <xsl:call-template name="make_blocktablestyle" />
1460+ <initialize>
1461+ <paraStyle name="all" alignment="justify" />
1462+ </initialize>
1463+ <xsl:apply-templates select="style:style" />
1464+ </stylesheet>
1465+</xsl:template>
1466+
1467+<xsl:template name="make_blocktablestyle">
1468+ <xsl:for-each select="//table:table">
1469+ <xsl:variable name="test">
1470+ <xsl:value-of select="./@table:name" />
1471+ </xsl:variable>
1472+ <xsl:if test="not(boolean(count(preceding-sibling::table:table[@table:name=$test])))">
1473+ <!--Test if this is the first table with this style, nested tables not counted-->
1474+ <blockTableStyle id="{@table:name}">
1475+ <xsl:if test=".//draw:image">
1476+ <blockTopPadding value="0"/>
1477+ <blockBottomPadding value="0"/>
1478+ </xsl:if>
1479+ <blockAlignment value="LEFT" />
1480+ <blockValign value="TOP" />
1481+ <xsl:call-template name="make_linestyle" />
1482+ <xsl:call-template name="make_tablebackground" />
1483+ </blockTableStyle>
1484+ </xsl:if>
1485+ </xsl:for-each>
1486+</xsl:template>
1487+
1488+<xsl:template name="make_linestyle">
1489+ <xsl:for-each select=".//table:table-row">
1490+ <xsl:variable name="row" select="position() - 1"/>
1491+ <xsl:for-each select=".//table:table-cell">
1492+ <xsl:variable name="col" select="position() - 1"/>
1493+ <xsl:variable name="linebefore">
1494+ <xsl:value-of select="key('table_cell_style',@table:style-name)/style:properties/@fo:border-left"/>
1495+ </xsl:variable>
1496+ <xsl:if test="not($linebefore='') and not($linebefore='none')">
1497+ <xsl:variable name="colorname">
1498+ <xsl:value-of select="substring-after($linebefore,'#')"/>
1499+ </xsl:variable>
1500+ <lineStyle kind="LINEBEFORE" colorName="#{$colorname}" start="{$col},{$row}" stop="{$col},-1"/>
1501+ </xsl:if>
1502+ <xsl:variable name="lineafter">
1503+ <xsl:value-of select="key('table_cell_style',@table:style-name)/style:properties/@fo:border-right"/>
1504+ </xsl:variable>
1505+ <xsl:if test="not($lineafter='') and not($lineafter='none')">
1506+ <xsl:variable name="colorname">
1507+ <xsl:value-of select="substring-after($lineafter,'#')"/>
1508+ </xsl:variable>
1509+ <lineStyle kind="LINEAFTER" colorName="#{$colorname}" start="{$col},{$row}" stop="{$col},-1"/>
1510+ </xsl:if>
1511+ <xsl:variable name="lineabove">
1512+ <xsl:value-of select="key('table_cell_style',@table:style-name)/style:properties/@fo:border-top"/>
1513+ </xsl:variable>
1514+ <xsl:if test="not($lineabove='') and not($lineabove='none')">
1515+ <xsl:variable name="colorname">
1516+ <xsl:value-of select="substring-after($lineabove,'#')"/>
1517+ </xsl:variable>
1518+ <lineStyle kind="LINEABOVE" colorName="#{$colorname}" start="{$col},{$row}" stop="{$col},{$row}"/>
1519+ </xsl:if>
1520+ <xsl:variable name="linebelow">
1521+ <xsl:value-of select="key('table_cell_style',@table:style-name)/style:properties/@fo:border-bottom"/>
1522+ </xsl:variable>
1523+ <xsl:if test="not($linebelow='') and not($linebelow='none')">
1524+ <xsl:variable name="colorname">
1525+ <xsl:value-of select="substring-after($linebelow,'#')"/>
1526+ </xsl:variable>
1527+ <lineStyle kind="LINEBELOW" colorName="#{$colorname}" start="{$col},{-1}" stop="{$col},{-1}"/>
1528+ </xsl:if>
1529+ <xsl:variable name="grid">
1530+ <xsl:value-of select="key('table_cell_style',@table:style-name)/style:properties/@fo:border"/>
1531+ </xsl:variable>
1532+ <xsl:if test="not($grid='') and not($grid='none')">
1533+ <xsl:variable name="colorname">
1534+ <xsl:value-of select="substring-after($grid,'#')"/>
1535+ </xsl:variable>
1536+ <!-- Don't use grid because we don't need a line between each rows -->
1537+ <lineStyle kind="LINEBEFORE" colorName="#{$colorname}" start="{$col},{$row}" stop="{$col},-1"/>
1538+ <lineStyle kind="LINEAFTER" colorName="#{$colorname}" start="{$col},{$row}" stop="{$col},-1"/>
1539+ <lineStyle kind="LINEABOVE" colorName="#{$colorname}" start="{$col},{$row}" stop="{$col},{$row}"/>
1540+ <lineStyle kind="LINEBELOW" colorName="#{$colorname}" start="{$col},{-1}" stop="{$col},{-1}"/>
1541+ </xsl:if>
1542+ </xsl:for-each>
1543+ </xsl:for-each>
1544+</xsl:template>
1545+
1546+<!-- Was needed to simulate bulleted lists:
1547+<xsl:template match="text:ordered-list|text:unordered-list">
1548+ <xsl:variable name = "text_width">
1549+ <xsl:call-template name="text_width" />
1550+ </xsl:variable>
1551+ <blockTable style="Standard_Outline" colWidths="18,{$text_width}">
1552+ <xsl:apply-templates match="text:list-item" />
1553+</blockTable>
1554+</xsl:template>
1555+
1556+<xsl:template match="text:list-item">
1557+ <tr>
1558+ <td><para><font face="Helvetica-Bold" size="10">*</font></para></td>
1559+ <td>
1560+ <xsl:apply-templates />
1561+ </td>
1562+ </tr>
1563+</xsl:template>
1564+
1565+-->
1566+
1567+
1568+<xsl:template match="office:body">
1569+ <story>
1570+ <xsl:apply-templates />
1571+ <xsl:for-each select="//draw:text-box">
1572+ <currentFrame>
1573+ <xsl:attribute name="name">
1574+ <xsl:value-of select="./@draw:name" />
1575+ </xsl:attribute>
1576+ </currentFrame>
1577+ <xsl:apply-templates>
1578+ <xsl:with-param name="skip_draw" select="0" />
1579+ </xsl:apply-templates>
1580+ <frameEnd />
1581+ </xsl:for-each>
1582+ <xsl:for-each select="//text:ordered-list">
1583+ <para><seqReset id="{./@text:style-name}"/></para>
1584+ </xsl:for-each>
1585+ </story>
1586+</xsl:template>
1587+
1588+<xsl:template match="table:table">
1589+ <blockTable>
1590+ <xsl:attribute name="colWidths">
1591+ <xsl:call-template name="make_columns" />
1592+ </xsl:attribute>
1593+ <xsl:call-template name="make_tableheaders" />
1594+ <xsl:attribute name="style">
1595+ <xsl:value-of select="@table:name" />
1596+ </xsl:attribute>
1597+ <xsl:apply-templates />
1598+ </blockTable>
1599+</xsl:template>
1600+
1601+<xsl:template name="make_tableheaders">
1602+ <xsl:if test="boolean(count(table:table-header-rows))">
1603+ <xsl:attribute name="repeatRows">1</xsl:attribute>
1604+ </xsl:if>
1605+</xsl:template>
1606+
1607+<xsl:template name="make_tablebackground">
1608+ <xsl:for-each select=".//table:table-row">
1609+ <!--Be careful when there are table:table-header-rows as
1610+ parent node of table:table-row -->
1611+ <xsl:variable name="row" select="position() - 1" />
1612+ <xsl:for-each select="./table:table-cell">
1613+ <xsl:variable name="col" select="position() - 1" />
1614+ <xsl:variable name="background">
1615+ <xsl:value-of select="key('table_cell_style',@table:style-name)/style:properties/@fo:background-color" />
1616+ </xsl:variable>
1617+ <xsl:if test="not($background='') and boolean(key('table_cell_style',@table:style-name)/style:properties/@fo:background-color) and starts-with($background,'#')">
1618+ <!--only RGB hexcolors are accepted -->
1619+ <blockBackground colorName="{$background}" start="{$col},{$row}" stop="{$col},-1" />
1620+ </xsl:if>
1621+ </xsl:for-each>
1622+ </xsl:for-each>
1623+</xsl:template>
1624+
1625+<xsl:template name="make_columns">
1626+ <xsl:variable name="columns" >
1627+ <xsl:for-each select="table:table-column">
1628+ <xsl:value-of select="key('table_column_style',@table:style-name)/style:properties/@style:column-width" />
1629+ <xsl:text>,</xsl:text>
1630+ </xsl:for-each>
1631+ </xsl:variable>
1632+ <xsl:value-of select="substring($columns,1,string-length($columns) - 1)" />
1633+ <!--strip the last comma-->
1634+</xsl:template>
1635+
1636+<xsl:template match="table:table-row">
1637+ <tr>
1638+ <xsl:apply-templates />
1639+ </tr>
1640+</xsl:template>
1641+
1642+<xsl:template match="table:table-cell">
1643+ <td>
1644+ <xsl:apply-templates />
1645+ </td>
1646+</xsl:template>
1647+
1648+<xsl:template match="text:section">
1649+ <section>
1650+ <xsl:apply-templates />
1651+ </section>
1652+</xsl:template>
1653+
1654+
1655+<xsl:template match="text:span">
1656+ <font>
1657+ <xsl:call-template name="make_fontnames_span" />
1658+ <xsl:call-template name="make_fontsize_span" />
1659+ <xsl:apply-templates />
1660+ </font>
1661+</xsl:template>
1662+
1663+<xsl:template name="make_fontsize_span">
1664+ <xsl:variable name ="fontsize">
1665+ <xsl:value-of select="key('text_style',@text:style-name)/style:properties/@fo:font-size" />
1666+ </xsl:variable>
1667+ <xsl:if test="not($fontsize='') and boolean(key('text_style',@text:style-name)/style:properties/@fo:font-size)" >
1668+ <xsl:attribute name="size">
1669+ <xsl:value-of select="$fontsize" />
1670+ </xsl:attribute>
1671+ </xsl:if>
1672+</xsl:template>
1673+
1674+<xsl:template name="make_fontnames_span">
1675+ <xsl:attribute name="face">
1676+ <xsl:call-template name="make_fontnames">
1677+ <xsl:with-param name="fontName" select="key('text_style',@text:style-name)/style:properties/@style:font-name" />
1678+ <xsl:with-param name="fontWeight" select="key('text_style',@text:style-name)/style:properties/@fo:font-weight" />
1679+ <xsl:with-param name="fontStyle" select="key('text_style',@text:style-name)/style:properties/@fo:font-style" />
1680+ </xsl:call-template>
1681+ </xsl:attribute>
1682+</xsl:template>
1683+
1684+<xsl:template name="make_image">
1685+ <illustration height="{.//draw:image/@svg:height}" width="{.//draw:image/@svg:width}">
1686+ <image x="0" y="0" file="{substring-after(.//draw:image/@xlink:href,'#Pictures/')}" height="{.//draw:image/@svg:height}" width="{.//draw:image/@svg:width}" />
1687+ </illustration>
1688+</xsl:template>
1689+
1690+<xsl:template name="empty_paragraph">
1691+ <xsl:if test="not(boolean(count(descendant::node())))">
1692+ <xsl:call-template name="distance_point">
1693+ <xsl:with-param name="background" select="key('paragraph_style',@text:style-name)/style:properties/@fo:background-color" />
1694+ </xsl:call-template>
1695+ </xsl:if>
1696+</xsl:template>
1697+
1698+<xsl:template name="distance_point">
1699+ <xsl:param name="background" />
1700+ <xsl:param name="tab_stop"></xsl:param>
1701+ <xsl:variable name="local_back">
1702+ <xsl:choose>
1703+ <xsl:when test="not(boolean($background)) or not(contains($background,'#'))">
1704+ <!-- Do not accept OO colors like "transparent", only hex-colors -->
1705+ <xsl:text>white</xsl:text>
1706+ </xsl:when>
1707+ <xsl:otherwise>
1708+ <xsl:value-of select="$background" />
1709+ </xsl:otherwise>
1710+ </xsl:choose>
1711+ </xsl:variable>
1712+ <font color="{$local_back}">
1713+ <xsl:text> </xsl:text>
1714+ <xsl:if test="boolean($tab_stop)">
1715+ <!-- simulate a tabstop with white/background-color points -->
1716+ <xsl:text>.........</xsl:text>
1717+ </xsl:if>
1718+ </font>
1719+</xsl:template>
1720+
1721+<xsl:template match="text:ordered-list">
1722+ <xsl:apply-templates />
1723+
1724+ <!-- Reset the counter. seqreset is not a trml2pdf tag, but a Platypus Intra Paragraph Markup,
1725+ so it needs a dummy paragraph to enclose it -->
1726+</xsl:template>
1727+
1728+<xsl:template name="make_listitem">
1729+ <xsl:if test="(name(..)='text:list-item')">
1730+ <xsl:attribute name="leftIndent">15</xsl:attribute>
1731+ <xsl:attribute name="bulletIndent">0</xsl:attribute>
1732+ <xsl:choose>
1733+ <xsl:when test="(name(../..)='text:unordered-list')">
1734+ <xsl:variable name="fontsize">
1735+ <xsl:value-of select="number(key('paragraph_style',@text:style-name)/style:properties/@fo:font-size)" />
1736+ </xsl:variable>
1737+ <xsl:choose>
1738+ <xsl:when test="$fontsize='NaN'">
1739+ <!-- you should exclude non-numerical values for bulletFontSize. <== Sometimes the preprocessing went wrong.-->
1740+ <!--use a default bullet font size-->
1741+ <xsl:attribute name="bulletFontSize">6</xsl:attribute>
1742+ </xsl:when>
1743+ <xsl:otherwise>
1744+ <xsl:attribute name="bulletFontSize"><xsl:value-of select="floor(($fontsize div 2) + 1)" /></xsl:attribute>
1745+ </xsl:otherwise>
1746+ </xsl:choose>
1747+ <xsl:attribute name="bulletFontName">ZapfDingbats</xsl:attribute>
1748+ <xsl:attribute name="bulletText">l</xsl:attribute>
1749+ </xsl:when>
1750+ <xsl:otherwise>
1751+ <!-- Generate the numbers for an ordered list -->
1752+ <xsl:variable name="size">
1753+ <xsl:value-of select="key('paragraph_style',@text:style-name)/style:properties/@fo:font-size" />
1754+ </xsl:variable>
1755+ <!-- For ordered lists we use the bullet tag from Platypus Intra Paragraph Markup -->
1756+ <bullet>
1757+ <xsl:if test="not($size='') and boolean(key('paragraph_style',@text:style-name)/style:properties/@fo:font-size)">
1758+ <xsl:attribute name="size">
1759+ <!-- adapt the fontsize to the fontsize of the current paragraph -->
1760+ <xsl:value-of select="$size" />
1761+ </xsl:attribute>
1762+ </xsl:if>
1763+ <seq id="{../../@text:style-name}"/>.</bullet>
1764+
1765+ </xsl:otherwise>
1766+ </xsl:choose>
1767+ </xsl:if>
1768+</xsl:template>
1769+
1770+<xsl:template match="text:drop-down">
1771+ <xsl:value-of select="text:label[2]/@text:value" />
1772+</xsl:template>
1773+
1774+
1775+<xsl:template match="text:p|text:h">
1776+ <xsl:param name="skip_draw" select="1" />
1777+ <xsl:if test="boolean(key('page_break_before',@text:style-name))" >
1778+ <pageBreak />
1779+ </xsl:if>
1780+ <xsl:choose>
1781+ <xsl:when test="boolean(.//draw:image)">
1782+ <xsl:call-template name="make_image" />
1783+ </xsl:when>
1784+ <xsl:when test="boolean(name(..) = 'draw:text-box') and boolean($skip_draw)">
1785+ </xsl:when>
1786+ <xsl:otherwise>
1787+ <para>
1788+ <xsl:attribute name="style">
1789+ <xsl:value-of select="@text:style-name" />
1790+ </xsl:attribute>
1791+ <xsl:call-template name="make_listitem" />
1792+ <xsl:apply-templates />
1793+ <xsl:call-template name="empty_paragraph" />
1794+ </para>
1795+ </xsl:otherwise>
1796+ </xsl:choose>
1797+ <xsl:if test="boolean(key('page_break_after',@text:style-name))" >
1798+ <pageBreak />
1799+ </xsl:if>
1800+</xsl:template>
1801+
1802+<xsl:template match="text:p/text:tab-stop">
1803+ <!-- simulate a tabstop -->
1804+ <xsl:call-template name="distance_point">
1805+ <xsl:with-param name="background" select="key('paragraph_style',@text:style-name)/style:properties/@fo:background-color" />
1806+ <xsl:with-param name="tab_stop">yes</xsl:with-param>
1807+ </xsl:call-template>
1808+</xsl:template>
1809+
1810+<!-- experimental - switched off
1811+<xsl:template match="text:h">
1812+ <para>
1813+ <xsl:attribute name="style">
1814+ <xsl:value-of select="@text:style-name" />
1815+ </xsl:attribute>
1816+ <xsl:call-template name="make_number" />
1817+ <xsl:apply-templates />
1818+ <xsl:call-template name="empty_paragraph" />
1819+ </para>
1820+</xsl:template>
1821+
1822+<xsl:template name="make_number">
1823+ <xsl:choose>
1824+ <xsl:when test="@text:level='1'">
1825+ <xsl:number format="1. " />
1826+ </xsl:when>
1827+ <xsl:when test="@text:level='2'">
1828+ <xsl:number count="text:h[@text:level='1']|text:h[text:level='2']" level="any" format="1.1." />
1829+ </xsl:when>
1830+ </xsl:choose>
1831+</xsl:template>
1832+
1833+-->
1834+
1835+<xsl:template match="style:style[@style:family='paragraph']">
1836+ <paraStyle>
1837+ <xsl:attribute name="name">
1838+ <xsl:value-of select="@style:name" />
1839+ </xsl:attribute>
1840+ <xsl:call-template name="make_indent_paragraph" />
1841+ <xsl:call-template name="make_fontnames_paragraph" />
1842+ <xsl:call-template name="make_fontsize" />
1843+ <!--<xsl:call-template name="make_parent" /> not necessary -
1844+ parent styles processed by PyOpenOffice -->
1845+ <xsl:call-template name="make_alignment" />
1846+ <xsl:call-template name="make_background" />
1847+ <xsl:call-template name="make_space_beforeafter" />
1848+ <xsl:call-template name="make_fontcolor" />
1849+ </paraStyle>
1850+</xsl:template>
1851+
1852+<xsl:template name="make_indent_paragraph">
1853+ <xsl:variable name="right_indent"><xsl:value-of select="style:properties/@fo:margin-right" /></xsl:variable>
1854+ <xsl:variable name="left_indent"><xsl:value-of select="style:properties/@fo:margin-left" /></xsl:variable>
1855+ <xsl:if test="not($right_indent='') and boolean(style:properties/@fo:margin-right)">
1856+ <xsl:attribute name="rightIndent">
1857+ <xsl:value-of select="$right_indent" />
1858+ </xsl:attribute>
1859+ </xsl:if>
1860+ <xsl:if test="not($left_indent='') and boolean(style:properties/@fo:margin-left)">
1861+ <xsl:attribute name="leftIndent">
1862+ <xsl:value-of select="$left_indent" />
1863+ </xsl:attribute>
1864+ </xsl:if>
1865+</xsl:template>
1866+
1867+<xsl:template name="make_background">
1868+ <xsl:variable name="background">
1869+ <xsl:value-of select="style:properties/@fo:background-color" />
1870+ </xsl:variable>
1871+ <xsl:if test="not($background='') and boolean(style:properties/@fo:background-color) and starts-with($background,'#')" >
1872+ <xsl:attribute name="backColor">
1873+ <xsl:value-of select="$background" />
1874+ </xsl:attribute>
1875+ </xsl:if>
1876+</xsl:template>
1877+
1878+<xsl:template name="make_space_beforeafter">
1879+ <xsl:variable name="before">
1880+ <xsl:value-of select="style:properties/@fo:margin-top" />
1881+ </xsl:variable>
1882+ <xsl:variable name="after">
1883+ <xsl:value-of select="style:properties/@fo:margin-bottom" />
1884+ </xsl:variable>
1885+ <xsl:if test="not($before='') and boolean(style:properties/@fo:margin-top)" >
1886+ <xsl:attribute name="spaceBefore">
1887+ <xsl:value-of select="$before" />
1888+ </xsl:attribute>
1889+ </xsl:if>
1890+ <xsl:if test="not($after='') and boolean(style:properties/@fo:margin-bottom)" >
1891+ <xsl:attribute name="spaceAfter">
1892+ <xsl:value-of select="$after" />
1893+ </xsl:attribute>
1894+ </xsl:if>
1895+</xsl:template>
1896+
1897+<xsl:template name="make_fontsize">
1898+ <xsl:variable name="fontSize">
1899+ <xsl:value-of select="style:properties/@fo:font-size" />
1900+ </xsl:variable>
1901+ <xsl:if test="not($fontSize='') and boolean(style:properties/@fo:font-size)">
1902+ <xsl:attribute name="fontSize">
1903+ <xsl:value-of select="$fontSize" />
1904+ </xsl:attribute>
1905+ <xsl:attribute name="leading">
1906+ <xsl:value-of select="$fontSize + floor($fontSize div 5) + 1" />
1907+ <!--use a standard leading related to the font size -->
1908+ </xsl:attribute>
1909+ </xsl:if>
1910+</xsl:template>
1911+
1912+<!--this template is not needed anymore for "normalized" sxw files -->
1913+<xsl:template name="make_parent">
1914+ <xsl:variable name="parent">
1915+ <xsl:value-of select="@style:parent-style-name" />
1916+ </xsl:variable>
1917+ <xsl:if test="not($parent='') and boolean(@style:parent-style-name)">
1918+ <xsl:attribute name="parent">
1919+ <xsl:value-of select="$parent" />
1920+ </xsl:attribute>
1921+ </xsl:if>
1922+</xsl:template>
1923+
1924+<xsl:template name="make_alignment">
1925+ <xsl:variable name="alignment">
1926+ <xsl:value-of select="style:properties/@fo:text-align" />
1927+ </xsl:variable>
1928+ <xsl:if test="not($alignment='') and boolean(style:properties/@fo:text-align)">
1929+ <xsl:choose>
1930+ <xsl:when test="$alignment='start'">
1931+ <xsl:attribute name="alignment">LEFT</xsl:attribute>
1932+ </xsl:when>
1933+ <xsl:when test="$alignment='center'">
1934+ <xsl:attribute name="alignment">CENTER</xsl:attribute>
1935+ </xsl:when>
1936+ <xsl:when test="$alignment='end'">
1937+ <xsl:attribute name="alignment">RIGHT</xsl:attribute>
1938+ </xsl:when>
1939+ <xsl:when test="$alignment='justify'">
1940+ <xsl:attribute name="alignment">JUSTIFY</xsl:attribute>
1941+ </xsl:when>
1942+ </xsl:choose>
1943+ </xsl:if>
1944+</xsl:template>
1945+
1946+<xsl:template name="make_fontnames_paragraph">
1947+ <xsl:attribute name="fontName">
1948+ <xsl:call-template name="make_fontnames">
1949+ <xsl:with-param name="fontName" select="style:properties/@style:font-name" />
1950+ <xsl:with-param name="fontWeight" select="style:properties/@fo:font-weight" />
1951+ <xsl:with-param name="fontStyle" select="style:properties/@fo:font-style" />
1952+ </xsl:call-template>
1953+ </xsl:attribute>
1954+</xsl:template>
1955+
1956+<xsl:template name="make_fontnames">
1957+ <!--much too verbose, needs improvement-->
1958+<xsl:param name="fontName" />
1959+<xsl:param name="fontWeight" />
1960+<xsl:param name="fontStyle" />
1961+<xsl:choose>
1962+<xsl:when test="not($fontName='') and boolean($fontName)">
1963+ <xsl:choose>
1964+ <xsl:when test="contains($fontName,'Courier')">
1965+ <xsl:choose>
1966+ <xsl:when test="($fontWeight='bold') and ($fontStyle='italic')">
1967+ <xsl:text>Courier-BoldOblique</xsl:text>
1968+ </xsl:when>
1969+ <xsl:when test="($fontWeight='bold') and not ($fontStyle='italic')">
1970+ <xsl:text>Courier-Bold</xsl:text>
1971+ </xsl:when>
1972+ <xsl:when test="not($fontWeight='bold') and ($fontStyle='italic')">
1973+ <xsl:text>Courier-Oblique</xsl:text>
1974+ </xsl:when>
1975+ <xsl:otherwise>
1976+ <xsl:text>Courier</xsl:text>
1977+ </xsl:otherwise>
1978+ </xsl:choose>
1979+ </xsl:when>
1980+ <xsl:when test="contains($fontName,'Helvetica') or contains($fontName,'Arial') or contains($fontName,'Sans')">
1981+ <xsl:choose>
1982+ <xsl:when test="($fontWeight='bold') and ($fontStyle='italic')">
1983+ <xsl:text>Helvetica-BoldOblique</xsl:text>
1984+ </xsl:when>
1985+ <xsl:when test="($fontWeight='bold') and not ($fontStyle='italic')">
1986+ <xsl:text>Helvetica-Bold</xsl:text>
1987+ </xsl:when>
1988+ <xsl:when test="not($fontWeight='bold') and ($fontStyle='italic')">
1989+ <xsl:text>Helvetica-Oblique</xsl:text>
1990+ </xsl:when>
1991+ <xsl:otherwise>
1992+ <xsl:text>Helvetica</xsl:text>
1993+ </xsl:otherwise>
1994+ </xsl:choose>
1995+ </xsl:when>
1996+ <xsl:otherwise>
1997+ <xsl:choose>
1998+ <xsl:when test="($fontWeight='bold') and ($fontStyle='italic')">
1999+ <xsl:text>Times-BoldItalic</xsl:text>
2000+ </xsl:when>
2001+ <xsl:when test="($fontWeight='bold') and not ($fontStyle='italic')">
2002+ <xsl:text>Times-Bold</xsl:text>
2003+ </xsl:when>
2004+ <xsl:when test="not($fontWeight='bold') and ($fontStyle='italic')">
2005+ <xsl:text>Times-Italic</xsl:text>
2006+ </xsl:when>
2007+ <xsl:otherwise>
2008+ <xsl:text>Times-Roman</xsl:text>
2009+ </xsl:otherwise>
2010+ </xsl:choose>
2011+ </xsl:otherwise>
2012+ </xsl:choose>
2013+</xsl:when>
2014+<xsl:otherwise>
2015+ <!--Use this as default -->
2016+ <xsl:text>Times-Roman</xsl:text>
2017+</xsl:otherwise>
2018+</xsl:choose>
2019+</xsl:template>
2020+<xsl:template name="make_fontcolor">
2021+ <xsl:variable name="textColor">
2022+ <xsl:value-of select="style:properties/@fo:color"/>
2023+ </xsl:variable>
2024+ <xsl:if test="not($textColor='') and boolean(style:properties/@fo:color)">
2025+ <xsl:attribute name="textColor">
2026+ <xsl:value-of select="$textColor" />
2027+ </xsl:attribute>
2028+ </xsl:if>
2029+</xsl:template>
2030+
2031+<!--
2032+This stylesheet is part of:
2033+PyOpenOffice Version 0.4
2034+Copyright (C) 2005: Martin Simon
2035+Homepage: www.bezirksreiter.de
2036+
2037+GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999
2038+-->
2039+
2040+</xsl:stylesheet>
2041+
2042+
2043
2044=== added file 'assembly_bom/report/picking_bom.sxw'
2045Binary files assembly_bom/report/picking_bom.sxw 1970-01-01 00:00:00 +0000 and assembly_bom/report/picking_bom.sxw 2011-10-06 15:31:48 +0000 differ
2046=== added file 'assembly_bom/report/report_view.xml'
2047--- assembly_bom/report/report_view.xml 1970-01-01 00:00:00 +0000
2048+++ assembly_bom/report/report_view.xml 2011-10-06 15:31:48 +0000
2049@@ -0,0 +1,23 @@
2050+<?xml version="1.0" encoding="utf-8"?>
2051+<openerp>
2052+<data>
2053+ <report
2054+ auto="False"
2055+ id="report_product_consumption_id"
2056+ model="product.product"
2057+ name="product.consumption"
2058+ rml="addons/assembly_bom/report/consumption_report.rml"
2059+ string="Product Consumption"
2060+ menu="False"
2061+ />
2062+
2063+ <!--report
2064+ auto="False"
2065+ id="report_delivery_bom_id"
2066+ model="stock.picking"
2067+ name="delivery.bom"
2068+ rml="addons/assembly_bom/report/delivery_report.rml"
2069+ string="Assembly Delivery Order"
2070+ /-->
2071+</data>
2072+</openerp>
2073
2074=== added file 'assembly_bom/report/tiny_sxw2rml.py'
2075--- assembly_bom/report/tiny_sxw2rml.py 1970-01-01 00:00:00 +0000
2076+++ assembly_bom/report/tiny_sxw2rml.py 2011-10-06 15:31:48 +0000
2077@@ -0,0 +1,377 @@
2078+#!/usr/bin/python
2079+# -*- encoding: utf-8 -*-
2080+##############################################################################
2081+#
2082+# Copyright (c):
2083+#
2084+# 2005 pyopenoffice.py Martin Simon (http://www.bezirksreiter.de)
2085+# 2005 Fabien Pinckaers, TINY SPRL. (http://tiny.be)
2086+#
2087+# WARNING: This program as such is intended to be used by professional
2088+# programmers who take the whole responsability of assessing all potential
2089+# consequences resulting from its eventual inadequacies and bugs
2090+# End users who are looking for a ready-to-use solution with commercial
2091+# garantees and support are strongly adviced to contact a Free Software
2092+# Service Company
2093+#
2094+# This program is Free Software; you can redistribute it and/or
2095+# modify it under the terms of the GNU General Public License
2096+# as published by the Free Software Foundation; either version 2
2097+# of the License, or (at your option) any later version.
2098+#
2099+# This program is distributed in the hope that it will be useful,
2100+# but WITHOUT ANY WARRANTY; without even the implied warranty of
2101+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2102+# GNU General Public License for more details.
2103+#
2104+# You should have received a copy of the GNU General Public License
2105+# along with this program; if not, write to the Free Software
2106+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
2107+#
2108+##############################################################################
2109+
2110+"""
2111+Tiny SXW2RML - The Open ERP's report engine
2112+
2113+Tiny SXW2RMLis part of the Tiny report project.
2114+Tiny Report is a module that allows you to render high quality PDF document
2115+from an OpenOffice template (.sxw) and any relationnal database.
2116+
2117+The whole source code is distributed under the terms of the
2118+GNU Public Licence.
2119+
2120+(c) 2005 pyopenoffice.py Martin Simon (http://www.bezirksreiter.de)
2121+(c) 2005-TODAY, Fabien Pinckaers - Tiny sprl
2122+"""
2123+__version__ = '0.9'
2124+
2125+
2126+import re
2127+import string
2128+import os
2129+import zipfile
2130+import xml.dom.minidom
2131+from reportlab.lib.units import toLength
2132+import base64
2133+
2134+class DomApiGeneral:
2135+ """General DOM API utilities."""
2136+ def __init__(self,content_string="",file=""):
2137+ self.content_string = content_string
2138+ self.re_digits = re.compile(r"(.*?\d)(pt|cm|mm|inch|in)")
2139+
2140+ def _unitTuple(self,string):
2141+ """Split values and units to a tuple."""
2142+ temp = self.re_digits.findall(string)
2143+ if not temp:
2144+ return (string,"")
2145+ else:
2146+ return (temp[0])
2147+
2148+ def stringPercentToFloat(self,string):
2149+ temp = string.replace("""%""","")
2150+ return float(temp)/100
2151+
2152+ def findChildrenByName(self,parent,name,attr_dict={}):
2153+ """Helper functions. Does not work recursively.
2154+ Optional: also test for certain attribute/value pairs."""
2155+ children = []
2156+ for c in parent.childNodes:
2157+ if c.nodeType == c.ELEMENT_NODE and c.nodeName == name:
2158+ children.append(c)
2159+ if attr_dict == {}:
2160+ return children
2161+ else:
2162+ return self._selectForAttributes(nodelist=children,attr_dict=attr_dict)
2163+
2164+ def _selectForAttributes(self,nodelist,attr_dict):
2165+ "Helper function."""
2166+ selected_nodes = []
2167+ for n in nodelist:
2168+ check = 1
2169+ for a in attr_dict.keys():
2170+ if n.getAttribute(a) != attr_dict[a]:
2171+ # at least one incorrect attribute value?
2172+ check = 0
2173+ if check:
2174+ selected_nodes.append(n)
2175+ return selected_nodes
2176+
2177+ def _stringToTuple(self,s):
2178+ """Helper function."""
2179+ try:
2180+ temp = string.split(s,",")
2181+ return int(temp[0]),int(temp[1])
2182+ except:
2183+ return None
2184+
2185+ def _tupleToString(self,t):
2186+ try:
2187+ return self.openOfficeStringUtf8("%s,%s" % (t[0],t[1]))
2188+ except:
2189+ return None
2190+
2191+ def _lengthToFloat(self,value):
2192+ v = value
2193+ if not self.re_digits.search(v):
2194+ return v
2195+ try:
2196+ if v[-4:] == "inch":
2197+ # OO files use "inch" instead of "in" in Reportlab units
2198+ v = v[:-2]
2199+ except:
2200+ pass
2201+ try:
2202+ c = round(toLength(v))
2203+ return c
2204+ except:
2205+ return v
2206+
2207+ def openOfficeStringUtf8(self,string):
2208+ if type(string) == unicode:
2209+ return string.encode("utf-8")
2210+ tempstring = unicode(string,"cp1252").encode("utf-8")
2211+ return tempstring
2212+
2213+class DomApi(DomApiGeneral):
2214+ """This class provides a DOM-API for XML-Files from an SXW-Archive."""
2215+ def __init__(self,xml_content,xml_styles):
2216+ DomApiGeneral.__init__(self)
2217+ self.content_dom = xml.dom.minidom.parseString(xml_content)
2218+ self.styles_dom = xml.dom.minidom.parseString(xml_styles)
2219+ body = self.content_dom.getElementsByTagName("office:body")
2220+ self.body = body and body[0]
2221+
2222+ # TODO:
2223+ self.style_dict = {}
2224+ self.style_properties_dict = {}
2225+
2226+ # ******** always use the following order:
2227+ self.buildStyleDict()
2228+ self.buildStylePropertiesDict()
2229+ if self.styles_dom.getElementsByTagName("style:page-master").__len__()<>0:
2230+ self.page_master = self.styles_dom.getElementsByTagName("style:page-master")[0]
2231+ if self.styles_dom.getElementsByTagName("style:page-layout").__len__()<>0 :
2232+ self.page_master = self.styles_dom.getElementsByTagName("style:page-layout")[0]
2233+ self.document = self.content_dom.getElementsByTagName("office:document-content")[0]
2234+
2235+ def buildStylePropertiesDict(self):
2236+ for s in self.style_dict.keys():
2237+ self.style_properties_dict[s] = self.getStylePropertiesDict(s)
2238+
2239+ def updateWithPercents(self,dict,updatedict):
2240+ """Sometimes you find values like "115%" in the style hierarchy."""
2241+ if not updatedict:
2242+ # no style hierarchies for this style? =>
2243+ return
2244+ new_updatedict = copy.copy(updatedict)
2245+ for u in new_updatedict.keys():
2246+ try:
2247+ if new_updatedict[u].find("""%""") != -1 and dict.has_key(u):
2248+ number = float(self.re_digits.search(dict[u]).group(1))
2249+ unit = self.re_digits.search(dict[u]).group(2)
2250+ new_number = self.stringPercentToFloat(new_updatedict[u]) * number
2251+ if unit == "pt":
2252+ new_number = int(new_number)
2253+ # no floats allowed for "pt"
2254+ # OOo just takes the int, does not round (try it out!)
2255+ new_updatedict[u] = "%s%s" % (new_number,unit)
2256+ else:
2257+ dict[u] = new_updatedict[u]
2258+ except:
2259+ dict[u] = new_updatedict[u]
2260+ dict.update(new_updatedict)
2261+
2262+ def normalizeStyleProperties(self):
2263+ """Transfer all style:style-properties attributes from the
2264+ self.style_properties_hierarchical dict to the automatic-styles
2265+ from content.xml. Use this function to preprocess content.xml for
2266+ XSLT transformations etc.Do not try to implement this function
2267+ with XSlT - believe me, it's a terrible task..."""
2268+ styles_styles = self.styles_dom.getElementsByTagName("style:style")
2269+ automatic_styles = self.content_dom.getElementsByTagName("office:automatic-styles")[0]
2270+ for s in styles_styles:
2271+ automatic_styles.appendChild(s.cloneNode(deep=1))
2272+ content_styles = self.content_dom.getElementsByTagName("style:style")
2273+ # these are the content_styles with styles_styles added!!!
2274+ for s in content_styles:
2275+ c = self.findChildrenByName(s,"style:properties")
2276+ if c == []:
2277+ # some derived automatic styles do not have "style:properties":
2278+ temp = self.content_dom.createElement("style:properties")
2279+ s.appendChild(temp)
2280+ c = self.findChildrenByName(s,"style:properties")
2281+ c = c[0]
2282+ dict = self.style_properties_dict[(s.getAttribute("style:name")).encode("utf-8")] or {}
2283+ for attribute in dict.keys():
2284+ c.setAttribute(self.openOfficeStringUtf8(attribute),self.openOfficeStringUtf8(dict[attribute]))
2285+
2286+ def transferStylesXml(self):
2287+ """Transfer certain sub-trees from styles.xml to the normalized content.xml
2288+ (see above). It is not necessary to do this - for example - with paragraph styles.
2289+ the "normalized" style properties contain all information needed for
2290+ further processing."""
2291+ # TODO: What about table styles etc.?
2292+ outline_styles = self.styles_dom.getElementsByTagName("text:outline-style")
2293+ t = self.content_dom.createElement("transferredfromstylesxml")
2294+ self.document.insertBefore(t,self.body)
2295+ t_new = self.body.previousSibling
2296+ try:
2297+ page_master = self.page_master
2298+ t_new.appendChild(page_master.cloneNode(deep=1))
2299+ t_new.appendChild(outline_styles[0].cloneNode(deep=1))
2300+ except:
2301+ pass
2302+
2303+ def normalizeLength(self):
2304+ """Normalize all lengthes to floats (i.e: 1 inch = 72).
2305+ Always use this after "normalizeContent" and "transferStyles"!"""
2306+ # TODO: The complex attributes of table cell styles are not transferred yet.
2307+ #all_styles = self.content_dom.getElementsByTagName("style:properties")
2308+ #all_styles += self.content_dom.getElementsByTagName("draw:image")
2309+ all_styles = self.content_dom.getElementsByTagName("*")
2310+ for s in all_styles:
2311+ for x in s._attrs.keys():
2312+ v = s.getAttribute(x)
2313+ s.setAttribute(x,"%s" % self._lengthToFloat(v))
2314+ # convert float to string first!
2315+
2316+ def normalizeTableColumns(self):
2317+ """Handle this strange table:number-columns-repeated attribute."""
2318+ columns = self.content_dom.getElementsByTagName("table:table-column")
2319+ for c in columns:
2320+ if c.hasAttribute("table:number-columns-repeated"):
2321+ number = int(c.getAttribute("table:number-columns-repeated"))
2322+ c.removeAttribute("table:number-columns-repeated")
2323+ for i in range(number-1):
2324+ (c.parentNode).insertBefore(c.cloneNode(deep=1),c)
2325+
2326+ def buildStyleDict(self):
2327+ """Store all style:style-nodes from content.xml and styles.xml in self.style_dict.
2328+ Caution: in this dict the nodes from two dom apis are merged!"""
2329+ for st in (self.styles_dom,self.content_dom):
2330+ for s in st.getElementsByTagName("style:style"):
2331+ name = s.getAttribute("style:name").encode("utf-8")
2332+ self.style_dict[name] = s
2333+ return True
2334+
2335+ def toxml(self):
2336+ return self.content_dom.toxml(encoding="utf-8")
2337+
2338+ def getStylePropertiesDict(self,style_name):
2339+ res = {}
2340+
2341+ if self.style_dict[style_name].hasAttribute("style:parent-style-name"):
2342+ parent = self.style_dict[style_name].getAttribute("style:parent-style-name").encode("utf-8")
2343+ res = self.getStylePropertiesDict(parent)
2344+
2345+ childs = self.style_dict[style_name].childNodes
2346+ for c in childs:
2347+ if c.nodeType == c.ELEMENT_NODE and c.nodeName.find("properties")>0 :
2348+ for attr in c._attrs.keys():
2349+ res[attr] = c.getAttribute(attr).encode("utf-8")
2350+ return res
2351+
2352+class PyOpenOffice(object):
2353+ """This is the main class which provides all functionality."""
2354+ def __init__(self, path='.', save_pict=False):
2355+ self.path = path
2356+ self.save_pict = save_pict
2357+ self.images = {}
2358+
2359+ def oo_read(self,fname):
2360+ z = zipfile.ZipFile(fname,"r")
2361+ content = z.read('content.xml')
2362+ style = z.read('styles.xml')
2363+ all = z.namelist()
2364+ for a in all:
2365+ if a[:9]=='Pictures/' and len(a)>10:
2366+ pic_content = z.read(a)
2367+ self.images[a[9:]] = pic_content
2368+ if self.save_pict:
2369+ f=open(os.path.join(self.path, os.path.basename(a)),"wb")
2370+ f.write(pic_content)
2371+ f.close()
2372+ z.close()
2373+ return content,style
2374+
2375+ def oo_replace(self,content):
2376+ regex = [
2377+ (r"<para[^>]*/>", ""),
2378+ #(r"<text:ordered-list.*?>(.*?)</text:ordered-list>", "$1"),
2379+ #(r"<text:unordered-list.*?>(.*?)</text:unordered-list>", "$1"),
2380+ (r"<para(.*)>(.*?)<text:line-break[^>]*/>", "<para$1>$2</para><para$1>"),
2381+ ]
2382+ for key,val in regex:
2383+ content = re.sub(key, val, content)
2384+ return content
2385+
2386+ def unpackNormalize(self,sourcefile):
2387+ c,s = self.oo_read(sourcefile)
2388+ c = self.oo_replace(c)
2389+ dom = DomApi(c,s)
2390+ dom.normalizeStyleProperties()
2391+ dom.transferStylesXml()
2392+ dom.normalizeLength()
2393+ dom.normalizeTableColumns()
2394+ new_c = dom.toxml()
2395+ return new_c
2396+
2397+def sxw2rml(sxw_file, xsl, output='.', save_pict=False):
2398+ from lxml import etree
2399+ from StringIO import StringIO
2400+
2401+ tool = PyOpenOffice(output, save_pict = save_pict)
2402+ res = tool.unpackNormalize(sxw_file)
2403+
2404+ f = StringIO(xsl)
2405+ styledoc = etree.parse(f)
2406+ style = etree.XSLT(styledoc)
2407+
2408+ f = StringIO(res)
2409+ doc = etree.parse(f)
2410+ result = style(doc)
2411+ root = etree.XPathEvaluator(result)("/document/stylesheet")
2412+
2413+ if root:
2414+ root=root[0]
2415+ images = etree.Element("images")
2416+ for img in tool.images:
2417+ node = etree.Element('image', name=img)
2418+ node.text = base64.encodestring(tool.images[img])
2419+ images.append(node)
2420+ root.append(images)
2421+
2422+ try:
2423+ xml = str(result)
2424+ return xml
2425+ except:
2426+ return result
2427+
2428+if __name__ == "__main__":
2429+ import optparse
2430+ parser = optparse.OptionParser(
2431+ version="Tiny Report v%s" % __version__,
2432+ usage = 'tiny_sxw2rml.py [options] file.sxw')
2433+ parser.add_option("-v", "--verbose", default=False, dest="verbose", help="enable basic debugging")
2434+ parser.add_option("-o", "--output", dest="output", default='.', help="directory of image output")
2435+ (opt, args) = parser.parse_args()
2436+ if len(args) != 1:
2437+ parser.error("incorrect number of arguments")
2438+
2439+ import sys
2440+ import StringIO
2441+
2442+ fname = sys.argv[1]
2443+ f = fname
2444+ xsl_file = 'normalized_oo2rml.xsl'
2445+ z = zipfile.ZipFile(fname,"r")
2446+ mimetype = z.read('mimetype')
2447+ if mimetype.split('/')[-1] == 'vnd.oasis.opendocument.text' :
2448+ xsl_file = 'normalized_odt2rml.xsl'
2449+ xsl = file(os.path.join(os.getcwd(), os.path.dirname(sys.argv[0]), xsl_file)).read()
2450+ result = sxw2rml(f, xsl, output=opt.output, save_pict=False)
2451+
2452+ print result
2453+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
2454+
2455
2456=== added file 'assembly_bom/sale.py'
2457--- assembly_bom/sale.py 1970-01-01 00:00:00 +0000
2458+++ assembly_bom/sale.py 2011-10-06 15:31:48 +0000
2459@@ -0,0 +1,71 @@
2460+# -*- ecoding: utf-8 -*-
2461+##############################################################################
2462+#
2463+# OpenERP, Open Source Management Solution
2464+# Copyright (C) 2011 NovaPoint Group LLC (<http://www.novapointgroup.com>)
2465+# Copyright (C) 2004-2010 OpenERP SA (<http://www.openerp.com>)
2466+#
2467+# This program is free software: you can redistribute it and/or modify
2468+# it under the terms of the GNU General Public License as published by
2469+# the Free Software Foundation, either version 3 of the License, or
2470+# (at your option) any later version.
2471+#
2472+# This program is distributed in the hope that it will be useful,
2473+# but WITHOUT ANY WARRANTY; without even the implied warranty of
2474+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2475+# GNU General Public License for more details.
2476+#
2477+# You should have received a copy of the GNU General Public License
2478+# along with this program. If not, see <http://www.gnu.org/licenses/>
2479+#
2480+##############################################################################
2481+
2482+from osv import fields, osv
2483+import netsvc
2484+import datetime
2485+import time
2486+from tools.translate import _
2487+
2488+class sale_order_line(osv.osv):
2489+ _inherit = 'sale.order.line'
2490+ def product_id_change(self, cr, uid, ids, pricelist, product, qty=0,
2491+ uom=False, qty_uos=0, uos=False, name='', partner_id=False,
2492+ lang=False, update_tax=True, date_order=False, packaging=False, fiscal_position=False, flag=False,partner_shipping_id=False, shop_id=False, context={}):
2493+
2494+ ret=super(sale_order_line, self).product_id_change(cr, uid, ids, pricelist, product, qty=qty,uom=uom, qty_uos=qty_uos, uos=uos, name=name, partner_id=partner_id,lang=lang, update_tax=update_tax, date_order=date_order, packaging=packaging, fiscal_position=fiscal_position, flag=flag)
2495+ if not ret.get('warning') and product:
2496+ product_obj = self.pool.get('product.product')
2497+ product_uom_obj = self.pool.get('product.uom')
2498+ product_obj = product_obj.browse(cr, uid, product, context=context)
2499+ uom2 = False
2500+ if uom:
2501+ uom2 = product_uom_obj.browse(cr, uid, uom)
2502+ if product_obj.uom_id.category_id.id != uom2.category_id.id:
2503+ uom = False
2504+
2505+ if (product_obj.type=='product') and product_obj.procure_method=='make_to_order':
2506+ bom_id = self.pool.get('mrp.bom').search(cr, uid, [
2507+ ('product_id', '=', product_obj.id),
2508+ ('bom_id', '=', False),
2509+ ('type', '=', 'assembly')])
2510+ if bom_id:
2511+ bom_id = bom_id[0]
2512+ bom_obj = self.pool.get('mrp.bom').browse(cr, uid, bom_id)
2513+ if bom_id and 'bom_stock_value' in bom_obj._columns.keys() and (bom_obj.bom_stock_value < qty * product_obj.uom_id.factor):
2514+ warning = {
2515+ 'title': _('Not enough stock !'),
2516+ 'message': _('You plan to move %.2f %s but you only have %.2f %s available !\nThe real stock is %.2f %s. (without reservations)') %
2517+ (qty, uom2 and uom2.name or product_obj.uom_id.name,
2518+ max(0,bom_obj.bom_stock_value), product_obj.uom_id.name,
2519+ max(0,product_obj.qty_available), product_obj.uom_id.name)
2520+ }
2521+ ret['warning'] = warning
2522+ return ret
2523+sale_order_line()
2524+
2525+
2526+
2527+
2528+
2529+
2530+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
2531\ No newline at end of file
2532
2533=== added file 'assembly_bom/stock.py'
2534--- assembly_bom/stock.py 1970-01-01 00:00:00 +0000
2535+++ assembly_bom/stock.py 2011-10-06 15:31:48 +0000
2536@@ -0,0 +1,378 @@
2537+# -*- ecoding: utf-8 -*-
2538+##############################################################################
2539+#
2540+# OpenERP, Open Source Management Solution
2541+# Copyright (C) 2011 NovaPoint Group LLC (<http://www.novapointgroup.com>)
2542+# Copyright (C) 2004-2010 OpenERP SA (<http://www.openerp.com>)
2543+#
2544+# This program is free software: you can redistribute it and/or modify
2545+# it under the terms of the GNU General Public License as published by
2546+# the Free Software Foundation, either version 3 of the License, or
2547+# (at your option) any later version.
2548+#
2549+# This program is distributed in the hope that it will be useful,
2550+# but WITHOUT ANY WARRANTY; without even the implied warranty of
2551+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2552+# GNU General Public License for more details.
2553+#
2554+# You should have received a copy of the GNU General Public License
2555+# along with this program. If not, see <http://www.gnu.org/licenses/>
2556+#
2557+##############################################################################
2558+
2559+from osv import fields, osv
2560+import netsvc
2561+import datetime
2562+import time
2563+from tools.translate import _
2564+
2565+class StockMove(osv.osv):
2566+ _inherit = 'stock.move'
2567+
2568+ def _action_explode(self, cr, uid, move, context=None):
2569+ """ Explodes pickings to produce many moves as per the bom lines for products assembly bom.
2570+ @param move: Stock moves
2571+ @constraints: explode takes place for pickings related to sale order
2572+ @return: True
2573+ """
2574+ result=super(StockMove, self)._action_explode(cr, uid, move, context=context)
2575+ bom_obj = self.pool.get('mrp.bom')
2576+ move_obj = self.pool.get('stock.move')
2577+ procurement_obj = self.pool.get('procurement.order')
2578+ product_obj = self.pool.get('product.product')
2579+ wf_service = netsvc.LocalService("workflow")
2580+ production_obj=self.pool.get('mrp.production')
2581+ if move.product_id.supply_method == 'produce' and move.product_id.procure_method == 'make_to_order':
2582+ bis = bom_obj.search(cr, uid, [
2583+ ('product_id','=',move.product_id.id),
2584+ ('bom_id','=',False),
2585+ ('type','=','assembly')])
2586+ if bis:
2587+ factor = move.product_qty
2588+ bom_point = bom_obj.browse(cr, uid, bis[0], context=context)
2589+ res = bom_obj._bom_explode(cr, uid, bom_point, factor, [])
2590+ dest = move.product_id.product_tmpl_id.property_stock_production.id
2591+
2592+ if move.sale_line_id:
2593+
2594+ #1. partial delivery is not allowed for kit type bom product
2595+ move.picking_id.write({'move_type':'one'})
2596+
2597+ #2. manually changing the state to confirmed for each exploded move to allow checking bom stock value
2598+ #related to mrp_jit
2599+ state = 'confirmed'
2600+ if move.state == 'assigned':
2601+ state = 'assigned'
2602+ if res[0] and res[0][0]:
2603+ line=res[0][0]
2604+ valdef = {
2605+ 'picking_id': move.picking_id.id,
2606+ 'product_id': line['product_id'],
2607+ 'product_uom': line['product_uom'],
2608+ 'product_qty': line['product_qty'],
2609+ 'product_uos': line['product_uos'],
2610+ 'product_uos_qty': line['product_uos_qty'],
2611+ 'move_dest_id': move.id,
2612+ 'state': state,
2613+ 'name': line['name'],
2614+ 'location_dest_id': dest,
2615+ #'move_history_ids': [(6,0,[move.id])],
2616+ #'move_history_ids2': [(6,0,[])],
2617+ 'procurements': [],
2618+ 'sale_line_id':False,
2619+ "kit_id":False,
2620+ "parent_bom_id":bis[0],
2621+ 'sale_id':move.sale_line_id.order_id.id
2622+
2623+ }
2624+ move.write(valdef)
2625+
2626+ for line in res[0][1:]:
2627+ valdef = {
2628+ 'picking_id': move.picking_id.id,
2629+ 'product_id': line['product_id'],
2630+ 'product_uom': line['product_uom'],
2631+ 'product_qty': line['product_qty'],
2632+ 'product_uos': line['product_uos'],
2633+ 'product_uos_qty': line['product_uos_qty'],
2634+ 'move_dest_id': move.id,
2635+ 'state': state,
2636+ 'name': line['name'],
2637+ 'location_dest_id': dest,
2638+ #'move_history_ids': [(6,0,[move.id])],
2639+ #'move_history_ids2': [(6,0,[])],
2640+ 'procurements': [],
2641+ 'sale_line_id':False,
2642+ "kit_id":False,
2643+ "parent_bom_id":bis[0],
2644+ 'sale_id':move.sale_line_id.order_id.id
2645+ }
2646+ mid = move_obj.copy(cr, uid, move.id, default=valdef)
2647+ move_obj.action_assign(cr, uid, [mid])
2648+ prodobj = product_obj.browse(cr, uid, line['product_id'], context=context)
2649+
2650+ cr.commit()
2651+ return result
2652+
2653+
2654+ _columns={
2655+ 'sale_id': fields.many2one('sale.order',"Sale Reference"),
2656+ 'kit_id':fields.many2one('mrp.production',"Assembly"),
2657+ 'production_ids': fields.many2many('mrp.production', 'mrp_production_move_ids', 'move_id', 'production_id', 'Related Production'),
2658+ 'parent_bom_id':fields.many2one("mrp.bom",'Related Bom')
2659+ }
2660+ def onchange_quantity(self, cr, uid, ids, product_id, product_qty,
2661+ product_uom, product_uos,location_id=False):
2662+ """ On change of product quantity finds UoM and UoS quantities
2663+ @param product_id: Product id
2664+ @param product_qty: Changed Quantity of product
2665+ @param product_uom: Unit of measure of product
2666+ @param product_uos: Unit of sale of product
2667+ @return: Dictionary of values
2668+ """
2669+ context={}
2670+ if location_id:
2671+ context['location'] =[location_id]
2672+ result=super(StockMove,self).onchange_quantity(cr, uid, ids, product_id, product_qty, product_uom, product_uos)
2673+ if (not product_id) or (product_qty <=0.0):
2674+ return result
2675+ product_obj = self.pool.get('product.product').browse(cr, uid, [product_id],context=context)[0]
2676+ uom2 = product_obj.uom_id
2677+ if (product_obj.type=='product') and (product_obj.virtual_available * uom2.factor < product_qty * product_obj.uom_id.factor) \
2678+ and (product_obj.procure_method=='make_to_stock'):
2679+ warning = {
2680+ 'title': _('Not enough stock !'),
2681+ 'message': _('You plan to move %.2f %s but you only have %.2f %s available !\nThe real stock is %.2f %s. (without reservations)') %
2682+ (product_qty, uom2 and uom2.name or product_obj.uom_id.name,
2683+ max(0,product_obj.virtual_available), product_obj.uom_id.name,
2684+ max(0,product_obj.qty_available), product_obj.uom_id.name)
2685+ }
2686+ result['warning'] = warning
2687+
2688+ if (product_obj.type=='product') and product_obj.procure_method=='make_to_order':
2689+ bom_id = self.pool.get('mrp.bom').search(cr, uid, [
2690+ ('product_id', '=', product_obj.id),
2691+ ('bom_id', '=', False)])
2692+ bom_obj = False
2693+ if bom_id:
2694+ bom_obj = self.pool.get('mrp.bom').browse(cr, uid, bom_id[0])
2695+
2696+ if bom_obj and (bom_obj.bom_stock_value < product_qty * product_obj.uom_id.factor) \
2697+ and (product_obj.procure_method=='make_to_order'):
2698+ warning = {
2699+ 'title': _('Not enough stock !'),
2700+ 'message': _('You plan to move %.2f %s but you only have %.2f %s available !\nThe real stock is %.2f %s. (without reservations)') %
2701+ (product_qty, uom2 and uom2.name or product_obj.uom_id.name,
2702+ max(0,bom_obj.bom_stock_value), product_obj.uom_id.name,
2703+ max(0,product_obj.qty_available), product_obj.uom_id.name)
2704+ }
2705+ result['warning'] = warning
2706+
2707+ return result
2708+ def onchange_product_id(self, cr, uid, ids, prod_id=False, loc_id=False,
2709+ loc_dest_id=False, address_id=False,product_qty=0):
2710+ """ On change of product id, .
2711+ @param prod_id: Changed Product id
2712+ @param loc_id: Source location id
2713+ @param loc_id: Destination location id
2714+ @param address_id: Address id of partner
2715+ @param product_qty: Product Quantity
2716+ @return: Dictionary of values
2717+ """
2718+ warning = {}
2719+ context = {}
2720+ res=super(StockMove,self).onchange_product_id(cr, uid, ids, prod_id=prod_id, loc_id=loc_id,
2721+ loc_dest_id=loc_dest_id, address_id=address_id)
2722+ if not prod_id:
2723+ return res
2724+ if loc_id:
2725+ context['location'] = [loc_id]
2726+ product_obj = self.pool.get('product.product').browse(cr, uid, [prod_id],context=context)[0]
2727+ uom2 = product_obj.uom_id
2728+ if (product_obj.type=='product') and (product_obj.virtual_available * uom2.factor < product_qty * product_obj.uom_id.factor) \
2729+ and (product_obj.procure_method=='make_to_stock'):
2730+ warning = {
2731+ 'title': _('Not enough stock !'),
2732+ 'message': _('You plan to move %.2f %s but you only have %.2f %s available !\nThe real stock is %.2f %s. (without reservations)') %
2733+ (product_qty, uom2 and uom2.name or product_obj.uom_id.name,
2734+ max(0,product_obj.virtual_available), product_obj.uom_id.name,
2735+ max(0,product_obj.qty_available), product_obj.uom_id.name)
2736+ }
2737+ res['warning'] = warning
2738+ if (product_obj.type=='product') and product_obj.procure_method=='make_to_order':
2739+ bom_id = self.pool.get('mrp.bom').search(cr, uid, [
2740+ ('product_id', '=', product_obj.id),
2741+ ('bom_id', '=', False)])
2742+ if bom_id:
2743+ bom_obj = self.pool.get('mrp.bom').browse(cr, uid, bom_id[0])
2744+ if bom_id and (bom_obj.bom_stock_value < product_qty * product_obj.uom_id.factor):
2745+ warning = {
2746+ 'title': _('Not enough stock !'),
2747+ 'message': _('You plan to move %.2f %s but you only have %.2f %s available !\nThe real stock is %.2f %s. (without reservations)') %
2748+ (product_qty, uom2 and uom2.name or product_obj.uom_id.name,
2749+ max(0,bom_obj.bom_stock_value), product_obj.uom_id.name,
2750+ max(0,product_obj.qty_available), product_obj.uom_id.name)
2751+ }
2752+ res['warning'] = warning
2753+ return res
2754+ _defaults = {
2755+ 'date': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'),
2756+ 'date_expected': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'),
2757+ }
2758+
2759+StockMove()
2760+
2761+
2762+class stock_picking(osv.osv):
2763+ _inherit="stock.picking"
2764+
2765+ def do_partial(self, cr, uid, ids, partial_datas, context=None):
2766+ """Creates and processes the assembly (MO) operation for delivery of product with assembly bom during sale
2767+ @param self: The object pointer.
2768+ @param ids: ids of the procurement order document
2769+ @param context: standard dictionary
2770+ @comment: Creates Production order for assembly of the product with assembly bom and
2771+ deliveres the product to customers making moves to ensure stock tracking
2772+ @return: returns dictionary carrying delivered picking details
2773+ """
2774+ bom_obj = self.pool.get('mrp.bom')
2775+ picking_obj=self.pool.get('stock.picking')
2776+ produce_wiz_obj=self.pool.get("mrp.product.produce")
2777+ production_obj=self.pool.get('mrp.production')
2778+ move_obj=self.pool.get("stock.move")
2779+ sale_obj=self.pool.get("sale.order")
2780+ wf_service=netsvc.LocalService('workflow')
2781+ res=super(stock_picking,self).do_partial(cr, uid, ids, partial_datas)
2782+ picking_details=res.values()
2783+ picking_id=picking_details and picking_details[0].get('delivered_picking',False) or False
2784+ if picking_id:
2785+ #expecting the picking state will not be 'done'
2786+ picking=picking_obj.browse(cr,uid,picking_id,context=context)
2787+ sale_order=picking.sale_id
2788+ if not sale_order:
2789+ return res
2790+
2791+ sale_obj.action_ship_end(cr, uid, [picking.sale_id.id], context=context)
2792+ for line in sale_order.order_line:
2793+ bom_ids=bom_obj.search(cr,uid,[('product_id','=',line.product_id.id)],context={})
2794+# procure_id=production_obj.create(cr,uid,{ 'name':'ASSEMBLY'+sale_order.name,
2795+ bom_id=bom_ids and bom_ids[0] or False
2796+ if bom_id:
2797+ bom=bom_obj.browse(cr,uid,bom_id)
2798+ if bom and bom.type=='normal':
2799+ move_ids=[move.id for move in picking.move_lines] or []
2800+ normal_move_ids=move_obj.search(cr,uid,[('id','in',move_ids),('product_id','=',line.product_id.id)])
2801+ ###Expecting
2802+ #1. Sale order is having only one line with the product having normal bom
2803+ if normal_move_ids:
2804+ normal_move_id=normal_move_ids[0]#Refer:#1
2805+ normal_move=move_obj.browse(cr,uid,normal_move_id)
2806+ normal_move.write({
2807+ 'sale_id':line.order_id.id
2808+ })
2809+
2810+ if bom and bom.type=='assembly':
2811+ #1. Warehouse stock and output location selected as stock and output
2812+ stock_id = sale_order.shop_id.warehouse_id.lot_stock_id.id#stock location
2813+ output_id = sale_order.shop_id.warehouse_id.lot_output_id.id#output warehouse location
2814+ #2. sale partner customer location selected as customer
2815+ customer_loc_id=sale_order.partner_id.property_stock_customer.id or False#customer location
2816+ mo_sequence=self.pool.get('ir.sequence').get(cr, uid, 'mrp.production')
2817+ src_loc_id=line.product_id.property_stock_production.id#stock location_id
2818+ production_id=production_obj.create(cr,uid,{
2819+ 'name': mo_sequence or sale_order.name,
2820+ 'origin':sale_order.name,
2821+ 'product_id':line.product_id.id,
2822+ 'product_qty':line.product_uom_qty,
2823+ 'product_uom':line.product_uom.id,
2824+ 'product_uos_qty':(line.product_uos and line.product_uos_qty)\
2825+ or line.product_uom_qty,
2826+ 'product_uos':(line.product_uos and line.product_uos.id)\
2827+ or line.product_uom.id,
2828+ 'location_src_id':src_loc_id,
2829+ # 'location_dest_id':sale_order.shop_id.warehouse_id.lot_stock_id.id,
2830+ 'location_dest_id':stock_id,
2831+ 'date_start':time.strftime('%Y-%m-%d'),
2832+ 'bom_id':bom_ids and bom_ids[0] or False,
2833+ 'picking_id':picking_id,
2834+ # 'procure_id':line.procurement_id.id,
2835+ },context=context)
2836+
2837+ # 3.To suit sale order with multiple lines its achieved by comparing the product in production order
2838+ # and the one sold and the one associated with the parent_bom stored in the move object
2839+
2840+ for move in picking.move_lines:
2841+ sold_product_id=line.product_id.id
2842+ parent_product_id=move.parent_bom_id and move.parent_bom_id.product_id.id or False
2843+ if parent_product_id and parent_product_id==sold_product_id:
2844+ move.write({'kit_id':production_id})
2845+
2846+
2847+ production=production_obj.browse(cr,uid,production_id)
2848+ wf_service.trg_validate(uid, 'mrp.production', production_id, 'button_confirm', cr)
2849+ move_ids=[move.id for move in production.move_lines]
2850+ move_obj.write(cr,uid,move_ids,{'sale_id':line.order_id.id},context=context)
2851+ production_obj.force_production(cr, uid, [production_id])
2852+ move_ids=[move.id for move in production.move_lines]
2853+ production_obj.action_produce(cr, uid, production_id,production.product_qty,'consume_produce')
2854+ wf_service.trg_validate(uid, 'mrp.production',production_id,'button_produce_done', cr)
2855+ date_planned=time.strftime('%Y-%m-%d')
2856+ cr.commit()
2857+ production=production_obj.browse(cr,uid,production_id)
2858+ production_finished=(production.state=='done')
2859+ if production_finished:
2860+ finished_ids=[move.id for move in production.move_created_ids2]
2861+
2862+ move_obj.write(cr,uid,finished_ids,{'sale_id':line.order_id.id},context=context)
2863+
2864+ finished_move_stock_id = self.pool.get('stock.move').create(cr, uid, {
2865+ 'name': line.name[:64],
2866+ 'picking_id': False,
2867+ 'product_id': line.product_id.id,
2868+ 'date': time.strftime('%Y-%m-%d'),
2869+ 'date_expected': time.strftime('%Y-%m-%d'),
2870+ 'product_qty': line.product_uom_qty,
2871+ 'product_uom': line.product_uom.id,
2872+ 'product_uos_qty': line.product_uos_qty,
2873+ 'product_uos': (line.product_uos and line.product_uos.id)\
2874+ or line.product_uom.id,
2875+ 'product_packaging': line.product_packaging.id,
2876+ 'address_id': line.address_allotment_id.id or sale_order.partner_shipping_id.id,
2877+ 'location_id': stock_id,
2878+ 'location_dest_id':output_id,#move destination to output location of the warehouse
2879+ 'sale_line_id': line.id,
2880+ 'tracking_id': False,
2881+ 'state': 'draft',
2882+ #'state': 'waiting',
2883+ 'note': line.notes,
2884+ 'kit_id':False,
2885+ 'company_id': sale_order.company_id.id,
2886+ 'sale_id': sale_order.id,
2887+ })
2888+
2889+ move2_id=move_obj.copy(cr,uid,finished_move_stock_id,{'location_id':output_id, 'sale_line_id':False,'location_dest_id': customer_loc_id})#move to customer location
2890+
2891+ status=move_obj.action_confirm(cr,uid,[finished_move_stock_id,move2_id])
2892+
2893+ move_obj.action_done(cr,uid,[finished_move_stock_id,move2_id])
2894+ return res
2895+
2896+ def action_confirm(self, cr, uid, ids, context=None):
2897+ res=super(stock_picking,self).action_confirm(cr, uid, ids, context)
2898+ cr.commit()
2899+ try:
2900+ self.action_assign(cr, uid, ids)
2901+ except Exception, e:
2902+ print e
2903+ return True
2904+ _defaults = {
2905+ 'date': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'),
2906+ }
2907+
2908+
2909+stock_picking()
2910+
2911+
2912+
2913+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
2914+
2915
2916=== added file 'assembly_bom/stock_view.xml'
2917--- assembly_bom/stock_view.xml 1970-01-01 00:00:00 +0000
2918+++ assembly_bom/stock_view.xml 2011-10-06 15:31:48 +0000
2919@@ -0,0 +1,100 @@
2920+<?xml version="1.0" encoding="utf-8"?>
2921+<openerp>
2922+ <data>
2923+
2924+
2925+ <record id="view_picking_out_form_inherit" model="ir.ui.view">
2926+ <field name="name">stock.picking.out.form.inherit</field>
2927+ <field name="model">stock.picking</field>
2928+ <field name="type">form</field>
2929+ <field name="inherit_id" ref="stock.view_picking_out_form" />
2930+ <field name="arch" type="xml">
2931+ <xpath expr="//field/form/group/field[@name='product_id']" position="replace">
2932+ <field name="product_id" on_change="onchange_product_id(product_id,location_id,location_dest_id, parent.address_id, product_qty)" colspan="4"/>
2933+ </xpath>
2934+ <xpath expr="//form/group/field[@name='product_qty']" position="replace">
2935+ <field name="product_qty" on_change="onchange_quantity(product_id, product_qty, product_uom, product_uos,location_id)" colspan="3" />
2936+ </xpath>
2937+ <xpath expr="//tree/field[@name='product_qty']" position="replace">
2938+ <field name="product_qty" on_change="onchange_quantity(product_id, product_qty, product_uom, product_uos,location_id)" />
2939+ </xpath>
2940+ </field>
2941+ </record>
2942+
2943+
2944+ <record id="view_picking_form_inherit" model="ir.ui.view">
2945+ <field name="name">stock.picking.forminherit</field>
2946+ <field name="model">stock.picking</field>
2947+ <field name="type">form</field>
2948+ <field name="inherit_id" ref="stock.view_picking_form" />
2949+ <field name="arch" type="xml">
2950+ <xpath expr="//field/form/group/field[@name='product_id']" position="replace">
2951+ <field name="product_id" on_change="onchange_product_id(product_id,location_id,location_dest_id, parent.address_id, product_qty)" colspan="4"/>
2952+ </xpath>
2953+ <xpath expr="//form/group/field[@name='product_qty']" position="replace">
2954+ <field name="product_qty" on_change="onchange_quantity(product_id, product_qty, product_uom, product_uos,location_id)" colspan="3" />
2955+ </xpath>
2956+ <xpath expr="//tree/field[@name='product_qty']" position="replace">
2957+ <field name="product_qty" on_change="onchange_quantity(product_id, product_qty, product_uom, product_uos,location_id)" />
2958+ </xpath>
2959+ </field>
2960+ </record>
2961+
2962+ <record id="view_move_form_inherit22" model="ir.ui.view">
2963+ <field name="name">stock.move.form.inherit</field>
2964+ <field name="model">stock.move</field>
2965+ <field name="type">form</field>
2966+ <field eval="4" name="priority"/>
2967+ <field name="inherit_id" ref="stock.view_move_form" />
2968+ <field name="arch" type="xml">
2969+ <data>
2970+ <xpath expr="//field[@name='prodlot_id']" position="after">
2971+ <field name="sale_id" readonly="1" select="1" />
2972+ </xpath>
2973+ <xpath expr="//field[@name='product_id']" position="replace">
2974+ <field name="product_id" on_change="onchange_product_id(product_id,location_id,location_dest_id, False, product_qty)"/>
2975+ </xpath>
2976+ <xpath expr="//field[@name='product_qty']" position="replace">
2977+ <field name="product_qty" on_change="onchange_quantity(product_id, product_qty, product_uom, product_uos,location_id)"/>
2978+ </xpath>
2979+
2980+ </data>
2981+ </field>
2982+ </record>
2983+
2984+
2985+ <record id="view_move_tree_inherit22" model="ir.ui.view">
2986+ <field name="name">stock.move.tree_inherit</field>
2987+ <field name="model">stock.move</field>
2988+ <field name="type">tree</field>
2989+ <field eval="6" name="priority"/>
2990+ <field name="inherit_id" ref="stock.view_move_tree" />
2991+ <field name="arch" type="xml">
2992+ <data>
2993+ <xpath expr="//field[@name='origin']" position="after">
2994+ <field name="sale_id" readonly="1"/>
2995+ </xpath>
2996+ </data>
2997+ </field>
2998+ </record>
2999+
3000+ <record id="view_move_search_inherit22" model="ir.ui.view">
3001+ <field name="name">stock.move.search_inherit</field>
3002+ <field name="model">stock.move</field>
3003+ <field name="type">search</field>
3004+ <field eval="3" name="priority"/>
3005+ <field name="inherit_id" ref="stock.view_move_search" />
3006+ <field name="arch" type="xml">
3007+ <data>
3008+ <xpath expr="//field[@name='origin']" position="after">
3009+ <field name="sale_id" />
3010+ </xpath>
3011+ </data>
3012+ </field>
3013+ </record>
3014+
3015+
3016+
3017+
3018+ </data>
3019+</openerp>
3020
3021=== added directory 'assembly_bom/wizard'
3022=== added file 'assembly_bom/wizard/__init__.py'
3023--- assembly_bom/wizard/__init__.py 1970-01-01 00:00:00 +0000
3024+++ assembly_bom/wizard/__init__.py 2011-10-06 15:31:48 +0000
3025@@ -0,0 +1,24 @@
3026+# -*- coding: utf-8 -*-
3027+##############################################################################
3028+#
3029+# OpenERP, Open Source Management Solution
3030+# Copyright (C) 2011 NovaPoint Group LLC (<http://www.novapointgroup.com>)
3031+# Copyright (C) 2004-2010 OpenERP SA (<http://www.openerp.com>)
3032+#
3033+# This program is free software: you can redistribute it and/or modify
3034+# it under the terms of the GNU General Public License as published by
3035+# the Free Software Foundation, either version 3 of the License, or
3036+# (at your option) any later version.
3037+#
3038+# This program is distributed in the hope that it will be useful,
3039+# but WITHOUT ANY WARRANTY; without even the implied warranty of
3040+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3041+# GNU General Public License for more details.
3042+#
3043+# You should have received a copy of the GNU General Public License
3044+# along with this program. If not, see <http://www.gnu.org/licenses/>
3045+#
3046+##############################################################################
3047+
3048+import consumption_report_wizard
3049+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
3050\ No newline at end of file
3051
3052=== added file 'assembly_bom/wizard/consumption_report_wizard.py'
3053--- assembly_bom/wizard/consumption_report_wizard.py 1970-01-01 00:00:00 +0000
3054+++ assembly_bom/wizard/consumption_report_wizard.py 2011-10-06 15:31:48 +0000
3055@@ -0,0 +1,93 @@
3056+# -*- encoding: utf-8 -*-
3057+##############################################################################
3058+#
3059+# OpenERP, Open Source Management Solution
3060+# Copyright (C) 2011 NovaPoint Group LLC (<http://www.novapointgroup.com>)
3061+# Copyright (C) 2004-2010 OpenERP SA (<http://www.openerp.com>)
3062+#
3063+# This program is free software: you can redistribute it and/or modify
3064+# it under the terms of the GNU General Public License as published by
3065+# the Free Software Foundation, either version 3 of the License, or
3066+# (at your option) any later version.
3067+#
3068+# This program is distributed in the hope that it will be useful,
3069+# but WITHOUT ANY WARRANTY; without even the implied warranty of
3070+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3071+# GNU General Public License for more details.
3072+#
3073+# You should have received a copy of the GNU General Public License
3074+# along with this program. If not, see <http://www.gnu.org/licenses/>
3075+#
3076+##############################################################################
3077+import wizard
3078+import pooler
3079+from osv import osv,fields
3080+import tools
3081+from tools.translate import _
3082+
3083+class consumption_report_wizard(osv.osv_memory):
3084+
3085+ def print_consumption_report(self,cr,uid,ids,context={}):
3086+ """ Updates the context with the details given in the wizard form
3087+ @param self: The object pointer.
3088+ @param ids: ids of the procurement order document
3089+ @param context: standard dictionary
3090+ @comment: The data for stock move analysis is passed through context
3091+ @return: returns standard dictionary to make report
3092+ """
3093+ data = self.read(cr, uid, ids[0])
3094+ warehouse_id=data and data.get('warehouse_id',False) or False
3095+ start_date=data and data.get('start_date',False) or False
3096+ end_date=data and data.get('end_date',False) or False
3097+ warehouse=self.pool.get('stock.warehouse').browse(cr,uid,warehouse_id)
3098+ product_ids=context.get('active_ids',[])
3099+ source_loc_id=warehouse.lot_stock_id and warehouse.lot_stock_id.id or False
3100+ return {
3101+ 'type': 'ir.actions.report.xml',
3102+ 'report_name':"product.consumption",
3103+ 'datas': {
3104+ 'model':'product.product',
3105+ 'id': product_ids and product_ids[0] or False,
3106+ 'ids': product_ids and product_ids or [],
3107+ 'report_type': 'pdf',
3108+ },
3109+ 'nodestroy': False,
3110+ 'context':{
3111+ "source_loc_id":source_loc_id,
3112+ "start_date":start_date,
3113+ "end_date":end_date
3114+ },
3115+
3116+ }
3117+
3118+ _name="product.consumption"
3119+ _columns={
3120+ 'warehouse_id':fields.many2one('stock.warehouse','Select Your Warehouse'),
3121+ 'start_date':fields.date("Start Date"),
3122+ 'end_date':fields.date("End Date")
3123+
3124+ }
3125+
3126+
3127+ def _get_warehouse(self, cr, uid, context=None):
3128+ """ Fetches the default warehouse
3129+ @param self: The object pointer.
3130+ @param ids: ids of the procurement order document
3131+ @param context: standard dictionary
3132+ @comment: Pre-fills the warehouse field by the ware house related to user company
3133+ @return: returns standard dictionary to make report
3134+ """
3135+ cmpny_id = self.pool.get('res.users')._get_company(cr, uid, context=context)
3136+ shop = self.pool.get('sale.shop').search(cr, uid, [('company_id', '=', cmpny_id)])
3137+ if shop:
3138+ shop_obj = self.pool.get('sale.shop').browse(cr, uid, shop[0], context=context)
3139+ return shop_obj.warehouse_id and shop_obj.warehouse_id.id
3140+ return False
3141+
3142+ _defaults = {
3143+ 'warehouse_id': _get_warehouse,
3144+ }
3145+
3146+consumption_report_wizard()
3147+
3148+
3149
3150=== added file 'assembly_bom/wizard/consumption_report_wizard.xml'
3151--- assembly_bom/wizard/consumption_report_wizard.xml 1970-01-01 00:00:00 +0000
3152+++ assembly_bom/wizard/consumption_report_wizard.xml 2011-10-06 15:31:48 +0000
3153@@ -0,0 +1,43 @@
3154+<?xml version="1.0" encoding="utf-8"?>
3155+<openerp>
3156+ <data>
3157+
3158+ <!-- Make Procurement -->
3159+
3160+ <record id="view_product_consumption_id" model="ir.ui.view">
3161+ <field name="name">Product Consumption</field>
3162+ <field name="model">product.consumption</field>
3163+ <field name="type">form</field>
3164+ <field name="arch" type="xml">
3165+ <form string="Product Consumption">
3166+ <field name="warehouse_id" colspan="4" required="1"/>
3167+ <newline/>
3168+ <field name="start_date" colspan="2"/>
3169+ <field name="end_date" colspan="2" />
3170+ <button type="object" name="print_consumption_report" icon="gtk-print" string="Print"/>
3171+ <button special="cancel" icon="gtk-cancel" string="Cancel"/>
3172+ </form>
3173+ </field>
3174+ </record>
3175+
3176+
3177+
3178+ <act_window name="Product Consumption"
3179+ res_model="product.consumption"
3180+ src_model="product.product"
3181+ view_mode="form"
3182+ target="new"
3183+ key2="client_print_multi"
3184+ id="act_product_consumption_id"
3185+ />
3186+
3187+
3188+ <!--wizard
3189+ string="Consumption Report xxx"
3190+ model="product.product"
3191+ name="product.consumption.wiz"
3192+ keyword="client_print_multi"
3193+ id="product_consumption_wizard_id"/-->
3194+
3195+ </data>
3196+</openerp>
3197\ No newline at end of file

Subscribers

People subscribed via source and target branches

to all changes: