Merge lp:~vauxoo/addons-vauxoo/6.1-account_rent_dev_luist into lp:addons-vauxoo/6.1

Proposed by Luis Torres - http://www.vauxoo.com
Status: Merged
Merged at revision: 626
Proposed branch: lp:~vauxoo/addons-vauxoo/6.1-account_rent_dev_luist
Merge into: lp:addons-vauxoo/6.1
Diff against target: 973 lines (+815/-20)
9 files modified
account_analytic_analysis_rent/__init__.py (+1/-0)
account_analytic_analysis_rent/__openerp__.py (+6/-2)
account_analytic_analysis_rent/account_analytic_account_rent.py (+198/-9)
account_analytic_analysis_rent/account_analytic_account_rent.xml (+166/-5)
account_analytic_analysis_rent/product.py (+41/-2)
account_analytic_analysis_rent/product_view.xml (+68/-2)
account_analytic_analysis_rent/wizard/__init__.py (+1/-0)
account_analytic_analysis_rent/wizard/lines_invoice_create.py (+268/-0)
account_analytic_analysis_rent/wizard/lines_invoice_create_view.xml (+66/-0)
To merge this branch: bzr merge lp:~vauxoo/addons-vauxoo/6.1-account_rent_dev_luist
Reviewer Review Type Date Requested Status
Julio Serna-http://www.vauxoo.com Pending
Review via email: mp+205866@code.launchpad.net

Description of the change

Se mergeo la opción de contratos

To post a comment you must log in.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'account_analytic_analysis_rent/__init__.py'
2--- account_analytic_analysis_rent/__init__.py 2013-06-03 17:04:02 +0000
3+++ account_analytic_analysis_rent/__init__.py 2014-02-11 23:05:08 +0000
4@@ -1,3 +1,4 @@
5 import account_analytic_account_rent
6 import product
7 import report
8+import wizard
9
10=== modified file 'account_analytic_analysis_rent/__openerp__.py'
11--- account_analytic_analysis_rent/__openerp__.py 2013-06-03 17:04:02 +0000
12+++ account_analytic_analysis_rent/__openerp__.py 2014-02-11 23:05:08 +0000
13@@ -24,12 +24,16 @@
14 'name' : "Account Analytic Account Rent",
15 'category' : "account_analytic",
16 'version' : "1.0",
17- 'depends' : ['account_analytic_analysis','product'],
18+ 'depends' : ['account_analytic_analysis','product','account_voucher'],
19 'author' : "Vauxoo",
20 'description' : """
21 This module added product in account_analytic_analysis to product control
22 """,
23- 'data' : ['account_analytic_account_rent.xml','product_view.xml','account_analytic_analysis_report.xml'],
24+ 'data' : ['account_analytic_account_rent.xml',
25+ 'product_view.xml',
26+ 'account_analytic_analysis_report.xml',
27+ 'wizard/lines_invoice_create_view.xml',
28+ ],
29 'installable': True,
30 'auto_install': False,
31 }
32
33=== modified file 'account_analytic_analysis_rent/account_analytic_account_rent.py'
34--- account_analytic_analysis_rent/account_analytic_account_rent.py 2013-06-03 17:04:02 +0000
35+++ account_analytic_analysis_rent/account_analytic_account_rent.py 2014-02-11 23:05:08 +0000
36@@ -22,12 +22,158 @@
37 from openerp.osv import osv, fields
38 from datetime import datetime, timedelta
39 from tools.translate import _
40+from datetime import datetime, timedelta
41+from dateutil.relativedelta import relativedelta
42+
43+class analytic_term(osv.osv):
44+ _name='analytic.term'
45+
46+ _columns={
47+ 'name': fields.char('Term',size=64,required=True),
48+ 'no_term': fields.integer('No Term')
49+ }
50+
51+class account_analytic_product(osv.osv):
52+ _name='account.analytic.product'
53+
54+
55+ def onchange_prodlot(self, cr , uid, ids, prodlot_id, context=None):
56+ if context==None:
57+ context={}
58+ res={}
59+ if prodlot_id:
60+ prodlot_obj=self.pool.get('stock.production.lot')
61+ if prodlot_obj.browse(cr, uid, prodlot_id, context=context).stock_available < 0:
62+ res={'value':{'prodlot_id': False },'warning':{
63+ 'title': _('This product is already rented !'),
64+ 'message': _('This product is already rented,check serial number.')
65+ }}
66+ return res
67+
68+ def onchange_product_id(self, cr, uid, ids, product_id, context=None):
69+ res={}
70+ list=[]
71+ if context==None:
72+ context={}
73+ if product_id:
74+ product_obj=self.pool.get('product.product')
75+ for prod in product_obj.browse(cr, uid, [product_id], context):
76+ type='rent'
77+ if prod.accesory_ok:
78+ type='accesory'
79+ list_data = [{'name':feature.name.id} for feature in prod.feature_ids]
80+ res={'value':{'type': type, }}
81+ return res
82+
83+ _columns={
84+ 'product_id':fields.many2one('product.product','Product', required=True, domain=['|', ('rent_ok','=',True), ('accesory_ok','=',True) ]),
85+ 'type': fields.selection([('rent','Rent'),('accesory','Accesory')],'Type'),
86+ 'prodlot_id': fields.many2one('stock.production.lot', 'Production Lot', help="Production lot is used to put a serial number on the production", select=True),
87+ 'analytic_id':fields.many2one('account.analytic.account','Account Analytic')
88+ }
89+
90+class account_analytic_line(osv.osv):
91+ _inherit='account.analytic.line'
92+ _order='product_id'
93+
94+ def _check_inv(self, cr, uid, ids, vals):
95+ select = ids
96+ if isinstance(select, (int, long)):
97+ select = [ids]
98+ if ( not vals.has_key('invoice_id')) or vals['invoice_id' ] == False:
99+ for line in self.browse(cr, uid, select):
100+ if line.invoice_id and 'account_id' not in vals:
101+ raise osv.except_osv(_('Error !'),
102+ _('You cannot modify an invoiced analytic lines!'))
103+ return True
104+
105+ def onchange_copys(self, cr, uid, id, w_start, w_end, context=None):
106+ res={}
107+ if context==None:
108+ context={}
109+ return {'value':{'unit_amount': w_end - w_start} }
110+
111+ _columns={
112+ 'w_start': fields.integer('Inicial'),
113+ 'w_end': fields.integer('Final'),
114+ 'feature_id': fields.many2one('product.feature.line','Feature'),
115+ 'prodlot_id': fields.many2one('stock.production.lot', 'Production Lot', help="Production lot is used to put a serial number on the production", select=True),
116+ }
117+
118+
119+class account_invoice_line(osv.osv):
120+ _inherit='account.invoice.line'
121+
122+ _columns={
123+ 'w_start': fields.integer('Inicial'),
124+ 'w_end': fields.integer('Final'),
125+ 'prodlot_id': fields.many2one('stock.production.lot', 'Production Lot', help="Production lot is used to put a serial number on the production", select=True),
126+ }
127+account_invoice_line()
128+
129
130 class account_analytic_account(osv.osv):
131 _inherit='account.analytic.account'
132
133+ def onchange_product_lines(self, cr, uid, ids, product_ids, feature_ids, context=None):
134+ res={}
135+ list_feature=[]
136+ if context==None:
137+ context={}
138+ if product_ids:
139+ product_obj=self.pool.get('product.product')
140+ for prod in product_ids:
141+ if prod[2]['product_id']:
142+ for feature in product_obj.browse(cr, uid, prod[2]['product_id'], context=context).feature_ids:
143+ list_feature.append({'name': feature.name and feature.name.id or False, 'product_line_id':prod[2]['product_id'],'counter':feature.counter or False, 'prodlot_feature_id' : prod[2]['prodlot_id']})
144+ return {'value':{'feature_ids': [(0, 6, data) for data in list_feature]}}
145+
146+ def _get_journal(self, cr, uid, context=None):
147+ if context is None:
148+ context = {}
149+ type_inv = context.get('type', 'out_invoice')
150+ user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
151+ company_id = context.get('company_id', user.company_id.id)
152+ type2journal = {'out_invoice': 'sale', 'in_invoice': 'purchase', 'out_refund': 'sale_refund', 'in_refund': 'purchase_refund'}
153+ journal_obj = self.pool.get('account.journal')
154+ res = journal_obj.search(cr, uid, [('type', '=', type2journal.get(type_inv, 'sale')),
155+ ('company_id', '=', company_id)],
156+ limit=1)
157+ return res and res[0] or False
158+
159+ def _compute_lines(self, cr, uid, ids, name, args, context=None):
160+ result = {}
161+ for contract in self.browse(cr, uid, ids, context=context):
162+ src = []
163+ lines = []
164+ for line in contract.line_ids:
165+ if line.invoice_id:
166+ if line.invoice_id.state=='paid':
167+ for l in line.invoice_id.payment_ids:
168+ if l.id not in lines:
169+ lines.append(l.id)
170+ result[contract.id] = lines
171+ return result
172+
173+ def _compute_lines_inv(self, cr, uid, ids, name, args, context=None):
174+ result = {}
175+ for contract in self.browse(cr, uid, ids, context=context):
176+ src = []
177+ lines = []
178+ for line in contract.line_ids:
179+ if line.invoice_id and line.invoice_id.id not in lines:
180+ lines.append(line.invoice_id.id)
181+ result[contract.id]=lines
182+ return result
183+
184 _columns={
185- 'product_id':fields.many2one('product.product','Product', domain=[('rent','=',False), ('rent_ok','=',True)]),
186+ 'product_ids':fields.one2many('account.analytic.product','analytic_id', 'Products'),
187+ 'term_id': fields.many2one('analytic.term','Term', required=True),
188+ 'voucher_ids': fields.function(_compute_lines, relation='account.move.line', type="many2many", string='Payments'),
189+ 'invoice_ids': fields.function(_compute_lines_inv, relation='account.invoice', type="many2many", string='Invoice'),
190+ 'group_product': fields.boolean('Group Product'),
191+ 'journal_id':fields.many2one('account.journal','Journal', required=True),
192+ 'feature_ids': fields.one2many('product.feature.line', 'analytic_id', 'Features')
193 }
194
195 def set_close(self, cr, uid, ids, context=None):
196@@ -37,10 +183,16 @@
197 product_obj=self.pool.get('product.product')
198 ware_id=warehouse_obj.search(cr, uid, [], context=context)[0]
199 warehouse=warehouse_obj.browse(cr ,uid, ware_id, context=context)
200+ location = self.pool.get('stock.location').search(cr, uid, [('usage', '=', 'customer')], context=context)
201+ if location:
202+ location=location[0]
203+ else:
204+ raise osv.except_osv(_('Error!'), _('You not have a configured client location'))
205 for contract in self.browse(cr, uid , ids , context=context):
206- picking_id=picking_obj.create(cr, uid, {'origin':contract.name, 'address_id':contract.contact_id.id,'date':contract.date_start,'type':'in'}, context=context)
207- move_obj.create(cr, uid, {'name':contract.product_id.name,'product_id':contract.product_id.id,'product_qty':1,'picking_id':picking_id,'product_uom':contract.product_id.uom_id.id,'location_id':warehouse.lot_output_id.id,'location_dest_id':warehouse.lot_input_id.id}, context=context)
208- product_obj.write(cr, uid, contract.product_id.id, {'rent':False, 'contract_id': False}, context=context)
209+ picking_id=picking_obj.create(cr, uid, {'origin':contract.name or False, 'address_id': contract.contact_id and contract.contact_id.id or False,'date':contract.date_start or False,'type':'in'}, context=context)
210+ for prod in contract.product_ids:
211+ move_obj.create(cr, uid, {'name':prod.product_id.name,'product_id':prod.product_id.id,'product_qty':1,'picking_id':picking_id,'product_uom':prod.product_id.uom_id.id,'location_id':location,'location_dest_id': warehouse.lot_input_id and warehouse.lot_input_id.id or False, 'prodlot_id': prod.prodlot_id and prod.prodlot_id.id or 1}, context=context)
212+ product_obj.write(cr, uid, prod.product_id.id, {'rent':False, 'contract_id': False}, context=context)
213 return super(account_analytic_account, self).set_close(cr, uid, ids, context=context)
214
215 def set_open(self, cr, uid, ids, context=None):
216@@ -48,17 +200,45 @@
217 move_obj=self.pool.get('stock.move')
218 warehouse_obj=self.pool.get('stock.warehouse')
219 product_obj=self.pool.get('product.product')
220+ line_obj=self.pool.get('account.analytic.line')
221 ware_id=warehouse_obj.search(cr, uid, [], context=context)[0]
222 warehouse=warehouse_obj.browse(cr ,uid, ware_id, context=context)
223
224 for contract in self.browse(cr, uid , ids , context=context):
225- picking_id=picking_obj.create(cr, uid, {'origin':contract.name, 'address_id':contract.contact_id.id,'date':contract.date_start,'type':'out'}, context=context)
226- move_obj.create(cr, uid, {'name':contract.product_id.name,'product_id':contract.product_id.id,'product_qty':1,'picking_id':picking_id,'product_uom':contract.product_id.uom_id.id,'location_id':warehouse.lot_stock_id.id,'location_dest_id':warehouse.lot_output_id.id}, context=context)
227- product_obj.write(cr, uid, contract.product_id.id, {'rent':True,'contract_id':contract.id}, context=context)
228+ date_invoice=contract.date_start
229+ #~ date_invoice=datetime.strptime(contract.date_start, "%Y-%m-%d")
230+ picking_id=picking_obj.create(cr, uid, {'origin':contract.name, 'address_id': contract.contact_id and contract.contact_id.id or False,'date':contract.date_start,'type':'out'}, context=context)
231+ for prod in contract.product_ids:
232+ move_obj.create(cr, uid, {'name':prod.product_id.name,'product_id':prod.product_id.id,'product_qty':1,'picking_id':picking_id,'product_uom':prod.product_id.uom_id.id,'location_id': warehouse.lot_stock_id and warehouse.lot_stock_id.id or False,'location_dest_id': warehouse.lot_output_id and warehouse.lot_output_id.id or False, 'prodlot_id': prod.prodlot_id and prod.prodlot_id.id or 1}, context=context)
233+ for line in range(0,contract.term_id.no_term):
234+ for prod in contract.product_ids:
235+ a = prod.product_id.product_tmpl_id.property_account_income.id
236+ if not a:
237+ a = prod.product_id.categ_id.property_account_income_categ.id
238+ for feature in contract.feature_ids:
239+ if feature.product_line_id.id==prod.product_id.id:
240+ line_obj.create(cr, uid, {'date':date_invoice,'name':feature.name and feature.name.name or False,'product_id':prod.product_id.id,'product_uom_id':prod.product_id.uom_id.id,'general_account_id':a,'to_invoice':1,'account_id': contract and contract.id or False,'journal_id': contract.journal_id and contract.journal_id.analytic_journal_id and contract.journal_id.analytic_journal_id.id or False,'amount': feature and feature.cost or False, 'feature_id':feature and feature.id or False, 'prodlot_id': prod.prodlot_id and prod.prodlot_id.id or 1},context=context)
241+ product_obj.write(cr, uid, prod.product_id.id, {'rent':True,'contract_id': contract and contract.id or False}, context=context)
242+ date_invoice=(datetime.strptime(date_invoice, "%Y-%m-%d") + relativedelta(months=1)).strftime("%Y-%m-%d")
243 return super(account_analytic_account, self).set_open(cr, uid, ids, context=context)
244
245+ def fields_view_get(self, cr, uid, view_id=None, view_type=False, context=None, toolbar=False, submenu=False):
246+ journal_obj = self.pool.get('account.journal')
247+ if context is None:
248+ context = {}
249+ res = super(account_analytic_account,self).fields_view_get(cr, uid, view_id=view_id, view_type=view_type, context=context, toolbar=toolbar, submenu=submenu)
250+ type = 'sale'
251+ for field in res['fields']:
252+ if field == 'journal_id' and type:
253+ journal_select = journal_obj._name_search(cr, uid, '', [('type', '=', type)], context=context, limit=None, name_get_uid=1)
254+ res['fields'][field]['selection'] = journal_select
255+
256+ return res
257+
258 _defaults = {
259 'state': 'draft',
260+ 'group_product': True,
261+ 'journal_id': _get_journal,
262 }
263 account_analytic_account()
264
265@@ -77,5 +257,14 @@
266 res['product']=line.product_id.id
267 return res
268 hr_timesheet_invoice_create()
269-
270-
271+
272+
273+class stock_production_lot(osv.osv):
274+ _inherit='stock.production.lot'
275+
276+ def name_get(self, cr, uid, ids, context=None):
277+ if ids and ids[0] == False:
278+ ids=[]
279+ return super(stock_production_lot, self).name_get(
280+ cr, uid, ids, context=context)
281+stock_production_lot()
282
283=== modified file 'account_analytic_analysis_rent/account_analytic_account_rent.xml'
284--- account_analytic_analysis_rent/account_analytic_account_rent.xml 2013-06-03 17:04:02 +0000
285+++ account_analytic_analysis_rent/account_analytic_account_rent.xml 2014-02-11 23:05:08 +0000
286@@ -10,11 +10,172 @@
287 <field name="arch" type="xml">
288 <notebook position="inside">
289 <page string="Product">
290- <field name="product_id"/>
291- </page>
292- </notebook>
293- </field>
294- </record>
295+ <field name="product_ids" on_change="onchange_product_lines(product_ids, feature_ids)" nolabel="1">
296+ <tree string="products" editable="top">
297+ <field name="product_id" on_change="onchange_product_id(product_id)"/>
298+ <field name="prodlot_id"
299+ context="{'product_id':product_id}"
300+ domain="[('product_id','=?',product_id)]"
301+ on_change="onchange_prodlot(prodlot_id)"/>
302+ <field name="type"/>
303+ </tree>
304+ <form string="products">
305+ <field name="product_id" on_change="onchange_product_id(product_id)"/>
306+ <field name="prodlot_id"
307+ context="{'product_id':product_id}"
308+ domain="[('product_id','=?',product_id)]"
309+ on_change="onchange_prodlot(prodlot_id)"/>
310+ <field name="type"/>
311+ </form>
312+ </field>
313+ <newline/>
314+ <field name="feature_ids" nolabel="1">
315+ <tree string='Features' editable="top">
316+ <field name="product_line_id"/>
317+ <field name="name"/>
318+ <field name="counter"/>
319+ <field name="cost"/>
320+ <field name="prodlot_feature_id" invisible = '1'/>
321+ </tree>
322+ <form string='Features'>
323+ <field name="product_line_id"/>
324+ <field name="name"/>
325+ <field name="counter"/>
326+ <field name="cost"/>
327+ </form>
328+ </field>
329+ </page>
330+ </notebook>
331+ </field>
332+ </record>
333+
334+ <record model="ir.ui.view" id="account_analytic_analysis_invoice_page">
335+ <field name="name">account.analytic.analysis.invoice.page</field>
336+ <field name="model">account.analytic.account</field>
337+ <field name="type">form</field>
338+ <field name="inherit_id" ref="account.view_account_analytic_account_form"/>
339+ <field name="arch" type="xml">
340+ <notebook position="inside">
341+ <page string="Invoice">
342+ <field name="invoice_ids" nolabel="1"/>
343+ </page>
344+ </notebook>
345+ </field>
346+ </record>
347+
348+ <record model="ir.ui.view" id="account_analytic_analysis_payments">
349+ <field name="name">account.analytic.analysis.payments</field>
350+ <field name="model">account.analytic.account</field>
351+ <field name="type">form</field>
352+ <field name="inherit_id" ref="account.view_account_analytic_account_form"/>
353+ <field name="arch" type="xml">
354+ <notebook position="inside">
355+ <page string="Payments">
356+ <field name="voucher_ids" nolabel="1"/>
357+ </page>
358+ </notebook>
359+ </field>
360+ </record>
361+
362+
363+ <record id="view_analytic_term_tree" model="ir.ui.view">
364+ <field name="name">view.analytic.term.tree</field>
365+ <field name="model">analytic.term</field>
366+ <field name="type">tree</field>
367+ <field name="arch" type="xml">
368+ <tree string="Analytic Term">
369+ <field name="name"/>
370+ <field name="no_term"/>
371+ </tree>
372+ </field>
373+ </record>
374+
375+ <record id="view_analytic_term_form" model="ir.ui.view">
376+ <field name="name">view.analytic.term.form</field>
377+ <field name="model">analytic.term</field>
378+ <field name="type">form</field>
379+ <field name="arch" type="xml">
380+ <form string="Analytic Term">
381+ <field name="name"/>
382+ <field name="no_term"/>
383+ </form>
384+ </field>
385+ </record>
386+
387+ <record model="ir.actions.act_window" id="analytic_term_action">
388+ <field name="name">Analytic Term</field>
389+ <field name="res_model">analytic.term</field>
390+ <field name="view_type">form</field>
391+ <field name="view_mode">tree,form</field>
392+ </record>
393+ <menuitem id="analytic_contract" name="Contract" parent="base.menu_sale_config_sales"/>
394+ <menuitem action="analytic_term_action" id="analytic_term_action_menu" parent="analytic_contract"/>
395+
396+
397+ <record model="ir.ui.view" id="account_analytic_analysis_term_contract">
398+ <field name="name">account.analytic.analysis.term.contract</field>
399+ <field name="model">account.analytic.account</field>
400+ <field name="type">form</field>
401+ <field name="inherit_id" ref="account.view_account_analytic_account_form"/>
402+ <field name="arch" type="xml">
403+ <field name="quantity_max" position="after">
404+ <field name="term_id"/>
405+ </field>
406+ </field>
407+ </record>
408+
409+ <record model="ir.ui.view" id="account_analytic_analysis_journal_contract">
410+ <field name="name">account.analytic.analysis.journal.contract</field>
411+ <field name="model">account.analytic.account</field>
412+ <field name="type">form</field>
413+ <field name="inherit_id" ref="account.view_account_analytic_account_form"/>
414+ <field name="arch" type="xml">
415+ <field name="user_id" position="after">
416+ <field name="journal_id" widget="selection"/>
417+ </field>
418+ </field>
419+ </record>
420+
421+ <record model="ir.ui.view" id="account_analytic_analysis_group_prod">
422+ <field name="name">account.analytic.analysis.group.prod</field>
423+ <field name="model">account.analytic.account</field>
424+ <field name="type">form</field>
425+ <field name="inherit_id" ref="hr_timesheet_invoice.account_analytic_account_form_form"/>
426+ <field name="arch" type="xml">
427+ <field name="amount_invoiced" position="after">
428+ <field name="group_product"/>
429+ </field>
430+ </field>
431+ </record>
432+
433+
434+ <record model="ir.ui.view" id="account_analytic_line_product_visible">
435+ <field name="name">account.analytic.line.product.visible</field>
436+ <field name="model">account.analytic.line</field>
437+ <field name="type">form</field>
438+ <field name="inherit_id" ref="account.view_account_analytic_line_tree"/>
439+ <field name="arch" type="xml">
440+ <field name="product_id" position="replace">
441+ <field name="product_id" on_change="on_change_unit_amount(product_id, unit_amount, company_id, product_uom_id, journal_id)" />
442+ </field>
443+ </field>
444+ </record>
445+
446+ <record model="ir.ui.view" id="account_invoice_line_write">
447+ <field name="name">account.invoice.line_write</field>
448+ <field name="model">account.invoice.line</field>
449+ <field name="type">form</field>
450+ <field name="inherit_id" ref="account.view_invoice_line_tree"/>
451+ <field name="arch" type="xml">
452+ <field name="name" position="after">
453+ <field name="product_id" />
454+ <field name="prodlot_id" />
455+ <field name="w_start" />
456+ <field name="w_end" />
457+ </field>
458+ </field>
459+ </record>
460+
461
462 </data>
463 </openerp>
464
465=== modified file 'account_analytic_analysis_rent/product.py'
466--- account_analytic_analysis_rent/product.py 2013-06-03 17:04:02 +0000
467+++ account_analytic_analysis_rent/product.py 2014-02-11 23:05:08 +0000
468@@ -22,15 +22,54 @@
469 from openerp.osv import osv, fields
470 from datetime import datetime, timedelta
471 from tools.translate import _
472+import decimal_precision as dp
473
474 class product_template(osv.osv):
475 _inherit='product.template'
476
477+
478+
479 _columns={
480 'rent_ok':fields.boolean('Rentable'),
481+ 'accesory_ok':fields.boolean('Accesory'),
482 'rent': fields.boolean('Rent', readonly=True),
483- 'contract_id': fields.many2one('account.analytic.account', 'Contract', readonly=True)
484+ 'contract_id': fields.many2one('account.analytic.account', 'Contract', readonly=True),
485 }
486
487 product_template()
488-
489+
490+class product_product(osv.osv):
491+ _inherit='product.product'
492+
493+ _columns={
494+ 'feature_ids': fields.one2many('product.feature.line', 'product_id', 'Features')
495+ }
496+
497+product_product()
498+
499+class product_feature(osv.osv):
500+ _name='product.feature'
501+
502+ _columns={
503+ 'name':fields.char('Name', size=64, required=True),
504+ 'description':fields.char('Description', size=256),
505+ }
506+
507+product_feature()
508+
509+class product_feature_line(osv.osv):
510+ _name='product.feature.line'
511+ _order='product_id'
512+ _columns={
513+ 'name':fields.many2one('product.feature', 'Feature', required=True),
514+ 'product_id':fields.many2one('product.product','Product'),
515+ 'product_line_id':fields.many2one('product.product','Product'),
516+ 'counter':fields.integer('Counter'),
517+ 'analytic_id':fields.many2one('account.analytic.account','Product'),
518+ 'cost':fields.float('cost', digits_compute=dp.get_precision('Account')),
519+ 'prodlot_feature_id': fields.many2one('stock.production.lot', 'Production Lot', help="Production lot is used to put a serial number on the production", select=True),
520+ }
521+
522+product_feature_line()
523+
524+
525
526=== modified file 'account_analytic_analysis_rent/product_view.xml'
527--- account_analytic_analysis_rent/product_view.xml 2013-06-03 17:04:02 +0000
528+++ account_analytic_analysis_rent/product_view.xml 2014-02-11 23:05:08 +0000
529@@ -3,19 +3,42 @@
530 <data>
531
532 <record model="ir.ui.view" id="product_rentable">
533- <field name="name">product_rentable</field>
534+ <field name="name">product.rentable</field>
535 <field name="model">product.product</field>
536 <field name="type">form</field>
537 <field name="inherit_id" ref="product.product_normal_form_view"/>
538 <field name="arch" type="xml">
539 <field name="purchase_ok" position="after">
540 <field name="rent_ok"/>
541+ <field name="accesory_ok"/>
542 </field>
543 </field>
544 </record>
545
546+ <record model="ir.ui.view" id="product_feature_product">
547+ <field name="name">product.feature.product</field>
548+ <field name="model">product.product</field>
549+ <field name="type">form</field>
550+ <field name="inherit_id" ref="product.product_normal_form_view"/>
551+ <field name="arch" type="xml">
552+ <group name="misc" position="after">
553+ <group name="Features" colspan="2">
554+ <separator colspan="4" string='Features'/>
555+ <field name="feature_ids" nolabel="1">
556+ <tree string='Features'>
557+ <field name="name"/>
558+ </tree>
559+ <form string='Features'>
560+ <field name="name"/>
561+ </form>
562+ </field>
563+ </group>
564+ </group>
565+ </field>
566+ </record>
567+
568 <record model="ir.ui.view" id="product_rent">
569- <field name="name">product_rent</field>
570+ <field name="name">product.rent</field>
571 <field name="model">product.product</field>
572 <field name="type">form</field>
573 <field name="inherit_id" ref="product.product_normal_form_view"/>
574@@ -49,5 +72,48 @@
575
576 <menuitem action="product_rent_action" id="product_rent_action_menu" parent="base.menu_sales"/>
577
578+ <record model="ir.actions.act_window" id="product_accesory_action">
579+ <field name="name">Accesory Product</field>
580+ <field name="res_model">product.product</field>
581+ <field name="view_type">form</field>
582+ <field name="view_mode">tree,form</field>
583+ <field name="domain">[('accesory_ok','=',True)]</field>
584+ </record>
585+
586+ <menuitem action="product_accesory_action" id="product_accesory_action_menu" parent="base.menu_sales"/>
587+
588+ <record id="view_feature_product_tree" model="ir.ui.view">
589+ <field name="name">view.feature.product.tree</field>
590+ <field name="model">product.feature</field>
591+ <field name="type">tree</field>
592+ <field name="arch" type="xml">
593+ <tree string="Features">
594+ <field name="name"/>
595+ <field name="description"/>
596+ </tree>
597+ </field>
598+ </record>
599+
600+ <record id="view_feature_product_form" model="ir.ui.view">
601+ <field name="name">view.feature.product.form</field>
602+ <field name="model">product.feature</field>
603+ <field name="type">form</field>
604+ <field name="arch" type="xml">
605+ <form string="Features">
606+ <field name="name"/>
607+ <field name="description"/>
608+ </form>
609+ </field>
610+ </record>
611+
612+ <record model="ir.actions.act_window" id="product_feature_action">
613+ <field name="name">Features</field>
614+ <field name="res_model">product.feature</field>
615+ <field name="view_type">form</field>
616+ <field name="view_mode">tree,form</field>
617+ </record>
618+ <menuitem id="product_feature" name="Features" parent="base.menu_sale_config_sales"/>
619+ <menuitem action="product_feature_action" id="product_feature_action_menu" parent="product_feature"/>
620+
621 </data>
622 </openerp>
623
624=== added directory 'account_analytic_analysis_rent/wizard'
625=== added file 'account_analytic_analysis_rent/wizard/__init__.py'
626--- account_analytic_analysis_rent/wizard/__init__.py 1970-01-01 00:00:00 +0000
627+++ account_analytic_analysis_rent/wizard/__init__.py 2014-02-11 23:05:08 +0000
628@@ -0,0 +1,1 @@
629+import lines_invoice_create
630
631=== added file 'account_analytic_analysis_rent/wizard/lines_invoice_create.py'
632--- account_analytic_analysis_rent/wizard/lines_invoice_create.py 1970-01-01 00:00:00 +0000
633+++ account_analytic_analysis_rent/wizard/lines_invoice_create.py 2014-02-11 23:05:08 +0000
634@@ -0,0 +1,268 @@
635+# -*- coding: utf-8 -*-
636+##############################################################################
637+#
638+# OpenERP, Open Source Management Solution
639+# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
640+#
641+# This program is free software: you can redistribute it and/or modify
642+# it under the terms of the GNU Affero General Public License as
643+# published by the Free Software Foundation, either version 3 of the
644+# License, or (at your option) any later version.
645+#
646+# This program is distributed in the hope that it will be useful,
647+# but WITHOUT ANY WARRANTY; without even the implied warranty of
648+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
649+# GNU Affero General Public License for more details.
650+#
651+# You should have received a copy of the GNU Affero General Public License
652+# along with this program. If not, see <http://www.gnu.org/licenses/>.
653+#
654+##############################################################################
655+
656+import time
657+from datetime import datetime
658+from osv import osv, fields
659+from tools.translate import _
660+
661+## Create an invoice based on selected timesheet lines
662+#
663+
664+class account_analytic_line(osv.osv):
665+ _inherit = "account.analytic.line"
666+
667+ #
668+ # data = {
669+ # 'date': boolean
670+ # 'time': boolean
671+ # 'name': boolean
672+ # 'price': boolean
673+ # 'product': many2one id
674+ # }
675+ def invoice_cost_create(self, cr, uid, ids, data={}, context=None):
676+ analytic_account_obj = self.pool.get('account.analytic.account')
677+ res_partner_obj = self.pool.get('res.partner')
678+ account_payment_term_obj = self.pool.get('account.payment.term')
679+ invoice_obj = self.pool.get('account.invoice')
680+ product_obj = self.pool.get('product.product')
681+ invoice_factor_obj = self.pool.get('hr_timesheet_invoice.factor')
682+ pro_price_obj = self.pool.get('product.pricelist')
683+ fiscal_pos_obj = self.pool.get('account.fiscal.position')
684+ product_uom_obj = self.pool.get('product.uom')
685+ invoice_line_obj = self.pool.get('account.invoice.line')
686+ feature_line_obj = self.pool.get('product.feature.line')
687+ invoices = []
688+ if context is None:
689+ context = {}
690+
691+ account_ids = {}
692+ for line in self.pool.get('account.analytic.line').browse(cr, uid, ids, context=context):
693+ account_ids[line.account_id.id] = True
694+
695+ account_ids = account_ids.keys() #data['accounts']
696+ for account in analytic_account_obj.browse(cr, uid, account_ids, context=context):
697+ partner = account.partner_id
698+ if (not partner) or not (account.pricelist_id):
699+ raise osv.except_osv(_('Analytic Account incomplete'),
700+ _('Please fill in the Partner or Customer and Sale Pricelist fields in the Analytic Account:\n%s') % (account.name,))
701+
702+ if not partner.address:
703+ raise osv.except_osv(_('Partner incomplete'),
704+ _('Please fill in the Address field in the Partner: %s.') % (partner.name,))
705+
706+ date_due = False
707+ if partner.property_payment_term:
708+ pterm_list= account_payment_term_obj.compute(cr, uid,
709+ partner.property_payment_term.id, value=1,
710+ date_ref=time.strftime('%Y-%m-%d'))
711+ if pterm_list:
712+ pterm_list = [line[0] for line in pterm_list]
713+ pterm_list.sort()
714+ date_due = pterm_list[-1]
715+ curr_invoice = {
716+ 'name': time.strftime('%d/%m/%Y')+' - '+account.name,
717+ 'partner_id': account.partner_id.id,
718+ 'address_contact_id': res_partner_obj.address_get(cr, uid,
719+ [account.partner_id.id], adr_pref=['contact'])['contact'],
720+ 'address_invoice_id': res_partner_obj.address_get(cr, uid,
721+ [account.partner_id.id], adr_pref=['invoice'])['invoice'],
722+ 'payment_term': partner.property_payment_term.id or False,
723+ 'account_id': partner.property_account_receivable.id,
724+ 'currency_id': account.pricelist_id.currency_id.id,
725+ 'date_due': date_due,
726+ 'fiscal_position': account.partner_id.property_account_position.id,
727+ 'type':'out_invoice',
728+ 'journal_id':account.journal_id.id
729+ }
730+ last_invoice = invoice_obj.create(cr, uid, curr_invoice, context=context)
731+ invoices.append(last_invoice)
732+ context2 = context.copy()
733+ context2['lang'] = partner.lang
734+ cr.execute("SELECT product_id, to_invoice, unit_amount, product_uom_id, w_start, w_end, name, amount, feature_id, prodlot_id " \
735+ "FROM account_analytic_line as line " \
736+ "WHERE account_id = %s " \
737+ "AND id IN %s AND to_invoice IS NOT NULL " , (account.id, tuple(ids),))
738+
739+ for product_id, factor_id, qty, uom, w_start, w_end, name, amount, feature_id, prodlot_id in cr.fetchall():
740+ product = product_obj.browse(cr, uid, product_id, context2)
741+ if not product:
742+ raise osv.except_osv(_('Error'), _('At least one line has no product !'))
743+ factor_name = ''
744+ factor = invoice_factor_obj.browse(cr, uid, factor_id, context2)
745+
746+ ctx = context.copy()
747+ ctx.update({'uom':uom})
748+ if account.pricelist_id:
749+ pl = account.pricelist_id.id
750+ price = pro_price_obj.price_get(cr,uid,[pl], product_id or data.get('product', False), qty or 1.0, account.partner_id.id, context=ctx)[pl]
751+ else:
752+ price = 0.0
753+
754+ taxes = product.taxes_id
755+ tax = fiscal_pos_obj.map_tax(cr, uid, account.partner_id.property_account_position, taxes)
756+ account_id = product.product_tmpl_id.property_account_income.id or product.categ_id.property_account_income_categ.id
757+ if not account_id:
758+ raise osv.except_osv(_("Configuration Error"), _("No income account defined for product '%s'") % product.name)
759+ curr_line = {
760+ 'price_unit': amount or price,
761+ 'quantity': qty,
762+ 'discount':factor.factor,
763+ 'invoice_line_tax_id': [(6,0,tax )],
764+ 'invoice_id': last_invoice,
765+ 'name': name,
766+ 'product_id': product_id or data.get('product',product_id),
767+ 'invoice_line_tax_id': [(6,0,tax)],
768+ 'uos_id': uom,
769+ 'account_id': account_id,
770+ 'account_analytic_id': account.id,
771+ 'w_start': int(w_start or 0),
772+ 'w_end': int(w_end or 0),
773+ 'prodlot_id': prodlot_id,
774+ }
775+ feature_line_obj.write(cr, uid, feature_id, {'counter': int(w_end or 0)}, context=context)
776+ #
777+ # Compute for lines
778+ #
779+ cr.execute("SELECT * FROM account_analytic_line WHERE account_id = %s and id IN %s AND product_id=%s and to_invoice=%s ORDER BY account_analytic_line.date", (account.id, tuple(ids), product_id, factor_id))
780+
781+ line_ids = cr.dictfetchall()
782+ note = []
783+ for line in line_ids:
784+ # set invoice_line_note
785+ details = []
786+ if data.get('date', False):
787+ details.append(line['date'])
788+ if data.get('time', False):
789+ if line['product_uom_id']:
790+ details.append("%s %s" % (line['unit_amount'], product_uom_obj.browse(cr, uid, [line['product_uom_id']],context2)[0].name))
791+ else:
792+ details.append("%s" % (line['unit_amount'], ))
793+ if data.get('name', False):
794+ details.append(line['name'])
795+ note.append(u' - '.join(map(lambda x: unicode(x) or '',details)))
796+
797+ curr_line['note'] = "\n".join(map(lambda x: unicode(x) or '',note))
798+ invoice_line_obj.create(cr, uid, curr_line, context=context)
799+ cr.execute("update account_analytic_line set invoice_id=%s WHERE account_id = %s and id IN %s", (last_invoice, account.id, tuple(ids)))
800+
801+ invoice_obj.button_reset_taxes(cr, uid, [last_invoice], context)
802+ return invoices
803+
804+class lines_create(osv.osv_memory):
805+
806+ _name = 'lines.create'
807+ _columns = {
808+ 'month': fields.selection([('1','January'), ('2','February'),
809+ ('3','March'), ('4','April'), ('5','May'), ('6','June'),
810+ ('7','July'), ('8','August'), ('9','September'), ('10','October'),
811+ ('11','November'), ('12','December')], 'Month'),
812+ 'line_ids': fields.many2many('account.analytic.line','analytic_wiz_lines_rel','wiz_id','line_id','lines'),
813+ 'contract_id': fields.many2one('account.analytic.account','Contract')
814+ #~ 'time': fields.boolean('Time spent', help='The time of each work done will be displayed on the invoice'),
815+ #~ 'name': fields.boolean('Description', help='The detail of each work done will be displayed on the invoice'),
816+ #~ 'price': fields.boolean('Cost', help='The cost of each work done will be displayed on the invoice. You probably don\'t want to check this'),
817+ #~ 'product': fields.many2one('product.product', 'Product', help='Complete this field only if you want to force to use a specific product. Keep empty to use the real product that comes from the cost.'),
818+ }
819+
820+ def default_get(self, cr, uid, fields, context=None):
821+ if context is None:
822+ context = {}
823+ res = super(lines_create, self).default_get(
824+ cr, uid, fields, context=context)
825+ analytic_obj = self.pool.get('account.analytic.account')
826+ product_obj = self.pool.get('product.product')
827+ line_obj = self.pool.get('account.analytic.line')
828+ lines_ids=[]
829+ if context.get('active_model') == 'account.analytic.account':
830+ res['contract_id']=context.get('active_id')
831+ res['month']=str(datetime.strptime(datetime.now().strftime('%Y-%m-%d'), "%Y-%m-%d").month)
832+ for contract in analytic_obj.browse(cr,uid,context['active_ids'],context):
833+ lines_ids=[]
834+ for line in contract.line_ids:
835+ if not line.invoice_id and line.to_invoice:
836+ date_line=datetime.strptime(line.date, "%Y-%m-%d")
837+ #~ date=datetime.strptime(datetime.now().strftime('%Y-%m-%d'), "%Y-%m-%d")
838+ if date_line.month==res['month']:
839+ lines_ids.append(line.id)
840+ res['line_ids']=lines_ids
841+ return res
842+
843+ def onchange_date(self, cr, uid, ids, month, contract_id, context=None):
844+ if context is None:
845+ context = {}
846+ res={}
847+ analytic_obj = self.pool.get('account.analytic.account')
848+ line_obj = self.pool.get('account.analytic.line')
849+ lines_ids=[]
850+ for contract in analytic_obj.browse(cr,uid,[contract_id],context):
851+ lines_ids=[]
852+ for line in contract.line_ids:
853+ if not line.invoice_id and line.to_invoice:
854+ date_line=datetime.strptime(line.date, "%Y-%m-%d")
855+ #~ date_new=datetime.strptime(month, "%Y-%m-%d")
856+ if str(month)==str(date_line.month):
857+ for feature in contract.feature_ids:
858+ if feature.id==line.feature_id.id:
859+ line_obj.write(cr, uid, line.id, {'w_start':feature.counter}, context=context)
860+ lines_ids.append(line.id)
861+ res['value']={'line_ids':lines_ids}
862+ return res
863+
864+
865+
866+ def do_create(self, cr, uid, ids, context=None):
867+ analytic_obj = self.pool.get('account.analytic.account')
868+ data = self.read(cr, uid, ids, [], context=context)[0]
869+ if context.get('active_model') == 'account.analytic.account':
870+ invs=[]
871+ for contract in analytic_obj.browse(cr,uid,context['active_ids'],context):
872+ new_lines=[]
873+ for prod in contract.product_ids:
874+ lines_ids=[]
875+ for line in contract.line_ids:
876+ if not line.invoice_id and line.to_invoice and line.product_id.id==prod.product_id.id and prod.type=='rent':
877+ date_line=datetime.strptime(line.date, "%Y-%m-%d")
878+ #~ date=datetime.strptime(data['date'], "%Y-%m-%d")
879+ if str(date_line.month)==data['month']:
880+ lines_ids.append(line.id)
881+ if not lines_ids and prod.type=='rent':
882+ raise osv.except_osv(_('Warning !'), _("Invoice is already linked to some of the analytic line(s)!"))
883+ if contract.group_product and prod.type=='rent':
884+ new_lines=new_lines + lines_ids
885+ elif not contract.group_product and prod.type=='rent':
886+ invs.append(self.pool.get('account.analytic.line').invoice_cost_create(cr, uid, lines_ids, {}, context=context))
887+ if contract.group_product:
888+ invs.append(self.pool.get('account.analytic.line').invoice_cost_create(cr, uid, new_lines, {}, context=context))
889+ mod_obj = self.pool.get('ir.model.data')
890+ act_obj = self.pool.get('ir.actions.act_window')
891+ mod_ids = mod_obj.search(cr, uid, [('name', '=', 'action_invoice_tree1')], context=context)[0]
892+ res_id = mod_obj.read(cr, uid, mod_ids, ['res_id'], context=context)['res_id']
893+ act_win = act_obj.read(cr, uid, res_id, [], context=context)
894+ act_win['domain'] = [('id','in',invs),('type','=','out_invoice')]
895+ act_win['name'] = _('Invoices')
896+ return act_win
897+
898+
899+lines_create()
900+
901+
902+
903
904=== added file 'account_analytic_analysis_rent/wizard/lines_invoice_create_view.xml'
905--- account_analytic_analysis_rent/wizard/lines_invoice_create_view.xml 1970-01-01 00:00:00 +0000
906+++ account_analytic_analysis_rent/wizard/lines_invoice_create_view.xml 2014-02-11 23:05:08 +0000
907@@ -0,0 +1,66 @@
908+<?xml version="1.0" encoding="utf-8"?>
909+<openerp>
910+ <data>
911+
912+ <record id="view_lines_create" model="ir.ui.view">
913+ <field name="name">lines.create.form</field>
914+ <field name="model">lines.create</field>
915+ <field name="type">form</field>
916+ <field name="arch" type="xml">
917+ <form string="Create Invoice">
918+ <notebook colspan="4">
919+ <page string="Billing Data">
920+ <group col="4" colspan="4">
921+ <separator string="Do you want to show details of work in invoice ?" colspan="4"/>
922+ <field name="month" on_change="onchange_date(month,contract_id)"/>
923+ <newline/>
924+ <field name="line_ids" nolabel="1" colspan="4">
925+ <tree editable="top" string="Analytic Entries">
926+ <field name="date" />
927+ <field name="ref" />
928+ <field name="name" />
929+ <field name="journal_id" />
930+ <field name="amount" sum="Total" />
931+ <field name="product_id" on_change="on_change_unit_amount(product_id, unit_amount, company_id, product_uom_id, journal_id)" />
932+ <field name="prodlot_id"/>
933+ <field name="w_start" on_change="onchange_copys(w_start,w_end)"/>
934+ <field name="w_end" on_change="onchange_copys(w_start,w_end)" />
935+ <field name="unit_amount" on_change="on_change_unit_amount(product_id, unit_amount, company_id, product_uom_id)" sum="Total Quantity" />
936+ <field name="product_uom_id" on_change="on_change_unit_amount(product_id, unit_amount, company_id, product_uom_id)" />
937+ <field name="general_account_id" />
938+ <field name="user_id" invisible="1"/>
939+
940+ </tree>
941+ </field>
942+ <field name="contract_id" invisible="1"/>
943+ </group>
944+ </page>
945+ </notebook>
946+ <separator colspan="4"/>
947+ <group col="2" colspan="4">
948+ <button special="cancel" string="Cancel" icon='gtk-cancel'/>
949+ <button name="do_create" string="Create Invoices" colspan="1" type="object" icon="terp-gtk-go-back-rtl"/>
950+ </group>
951+ </form>
952+ </field>
953+ </record>
954+
955+ <record id="lines_invoice_create" model="ir.actions.act_window">
956+ <field name="name">Create Invoice</field>
957+ <field name="type">ir.actions.act_window</field>
958+ <field name="res_model">lines.create</field>
959+ <field name="view_type">form</field>
960+ <field name="view_mode">form</field>
961+ <field name="target">new</field>
962+ </record>
963+
964+ <act_window id="lines_invoice_create_wizard"
965+ name="Create Invoice"
966+ src_model="account.analytic.account"
967+ res_model="lines.create"
968+ view_mode="form"
969+ target="new"
970+ key2="client_action_multi"/>
971+
972+ </data>
973+</openerp>