Merge lp:~openerp-community-testers/openobject-addons/trunk-bug1067541 into lp:openobject-addons

Proposed by Stéphane Bidoul (Acsone)
Status: Needs review
Proposed branch: lp:~openerp-community-testers/openobject-addons/trunk-bug1067541
Merge into: lp:openobject-addons
Diff against target: 337 lines (+304/-1) (has conflicts)
4 files modified
account/account_invoice_view.xml (+1/-1)
hr_timesheet_invoice/tests/__init__.py (+26/-0)
hr_timesheet_invoice/tests/test_multi_company.py (+115/-0)
hr_timesheet_invoice/wizard/hr_timesheet_invoice_create.py (+162/-0)
Text conflict in hr_timesheet_invoice/wizard/hr_timesheet_invoice_create.py
To merge this branch: bzr merge lp:~openerp-community-testers/openobject-addons/trunk-bug1067541
Reviewer Review Type Date Requested Status
Stéphane Bidoul (Acsone) (community) Disapprove
Olivier Dony (Odoo) Pending
Review via email: mp+136772@code.launchpad.net

Description of the change

Apparently this branch from the test sprint was not submitted for merging.

To post a comment you must log in.
Revision history for this message
Stéphane Bidoul (Acsone) (sbi) wrote :

Hello there,

Since this MP has sadly been left unattended since the test sprint, it slowly became conflicting.

We had to recreate a new one [1], so I'm closing this one.

Would you be so kind as to review it before it conflicts again? It's a bit of a corner case but a very real one in multi-company context and it is accompanied with a test.

[1] https://code.launchpad.net/~lmi/openobject-addons/7.0-bug1067541/+merge/178026.

Revision history for this message
Stéphane Bidoul (Acsone) (sbi) wrote :

Marking disapproved to close the MP.

review: Disapprove

Unmerged revisions

7902. By Stéphane Bidoul (Acsone)

[IMP] hr_timesheet_invoice/tests now use the ref and browse_ref from the common test classes

7901. By Stéphane Bidoul (Acsone)

[FIX] bill task work selects wrong receivable account in multi-company context

7900. By Stéphane Bidoul (Acsone)

hr_timesheet_invoice: test for lp:1067541

7899. By Stéphane Bidoul (Acsone)

hr_timesheet_invoice: first python tests in preparation for multicompany regression tests

7898. By Launchpad Translations on behalf of openerp

Launchpad automatic translations update.

7897. By Olivier Dony (Odoo)

[FIX] account: currency_id must be passed to the chart installation wizard

Otherwise the installation of a chart of account
via the settings page fails miserably.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'account/account_invoice_view.xml'
2--- account/account_invoice_view.xml 2012-11-26 18:28:11 +0000
3+++ account/account_invoice_view.xml 2012-11-28 20:08:30 +0000
4@@ -344,7 +344,7 @@
5 <field name="sent" invisible="1"/>
6 <notebook colspan="4">
7 <page string="Invoice Lines">
8- <field name="invoice_line" nolabel="1" widget="one2many_list" context="{'type': type}">
9+ <field name="invoice_line" nolabel="1" widget="one2many_list" context="{'type': type, 'company_id': company_id}">
10 <tree string="Invoice Lines" editable="bottom">
11 <field name="product_id"
12 on_change="product_id_change(product_id, uos_id, quantity, name, parent.type, parent.partner_id, parent.fiscal_position, price_unit, parent.currency_id, context, parent.company_id)"/>
13
14=== added directory 'hr_timesheet_invoice/tests'
15=== added file 'hr_timesheet_invoice/tests/__init__.py'
16--- hr_timesheet_invoice/tests/__init__.py 1970-01-01 00:00:00 +0000
17+++ hr_timesheet_invoice/tests/__init__.py 2012-11-28 20:08:30 +0000
18@@ -0,0 +1,26 @@
19+# -*- coding: utf-8 -*-
20+##############################################################################
21+#
22+# OpenERP, Open Source Management Solution
23+# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
24+#
25+# This program is free software: you can redistribute it and/or modify
26+# it under the terms of the GNU Affero General Public License as
27+# published by the Free Software Foundation, either version 3 of the
28+# License, or (at your option) any later version.
29+#
30+# This program is distributed in the hope that it will be useful,
31+# but WITHOUT ANY WARRANTY; without even the implied warranty of
32+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
33+# GNU Affero General Public License for more details.
34+#
35+# You should have received a copy of the GNU Affero General Public License
36+# along with this program. If not, see <http://www.gnu.org/licenses/>.
37+#
38+##############################################################################
39+from . import test_multi_company
40+
41+checks = [
42+ test_multi_company,
43+]
44+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
45
46=== added file 'hr_timesheet_invoice/tests/test_multi_company.py'
47--- hr_timesheet_invoice/tests/test_multi_company.py 1970-01-01 00:00:00 +0000
48+++ hr_timesheet_invoice/tests/test_multi_company.py 2012-11-28 20:08:30 +0000
49@@ -0,0 +1,115 @@
50+# -*- coding: utf-8 -*-
51+##############################################################################
52+#
53+# OpenERP, Open Source Management Solution
54+# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
55+#
56+# This program is free software: you can redistribute it and/or modify
57+# it under the terms of the GNU Affero General Public License as
58+# published by the Free Software Foundation, either version 3 of the
59+# License, or (at your option) any later version.
60+#
61+# This program is distributed in the hope that it will be useful,
62+# but WITHOUT ANY WARRANTY; without even the implied warranty of
63+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
64+# GNU Affero General Public License for more details.
65+#
66+# You should have received a copy of the GNU Affero General Public License
67+# along with this program. If not, see <http://www.gnu.org/licenses/>.
68+#
69+##############################################################################
70+
71+from openerp.tests import common
72+
73+class test_multi_company(common.TransactionCase):
74+
75+ QTY = 5.0
76+ PRICE = 75
77+
78+ def prepare(self):
79+ #super(test_multi_company, self).setUp()
80+
81+ self.company_obj = self.registry('res.company')
82+ self.analytic_account_obj = self.registry('account.analytic.account')
83+ self.analytic_line_obj = self.registry('account.analytic.line')
84+ self.invoice_obj = self.registry('account.invoice')
85+ self.product_obj = self.registry('product.product')
86+
87+ # load main company
88+ self.company_a = self.browse_ref('base.main_company')
89+ # create an analytic account
90+ self.aa_id = self.analytic_account_obj.create(self.cr, self.uid, {
91+ 'name': 'Project',
92+ 'company_id': self.company_a.id,
93+ 'partner_id': self.ref('base.res_partner_2'),
94+ 'pricelist_id': self.ref('product.list0'),
95+ })
96+ # set a known price on product
97+ self.product_obj.write(self.cr, self.uid, self.ref('product.product_product_consultant'), {
98+ 'list_price': self.PRICE,
99+ })
100+
101+ def create_invoice(self):
102+ # create an analytic line to invoice
103+ line_id = self.analytic_line_obj.create(self.cr, self.uid, {
104+ 'account_id': self.aa_id,
105+ 'amount': -1.0,
106+ 'general_account_id': self.ref('account.a_expense'),
107+ 'journal_id': self.ref('hr_timesheet.analytic_journal'),
108+ 'name': 'some work',
109+ 'product_id': self.ref('product.product_product_consultant'),
110+ 'product_uom_id': self.ref('product.product_uom_hour'),
111+ 'to_invoice': self.ref('hr_timesheet_invoice.timesheet_invoice_factor2'), # 50%
112+ 'unit_amount': self.QTY,
113+ })
114+ # XXX too strong coupling with UI?
115+ wizard_obj = self.registry('hr.timesheet.invoice.create')
116+ wizard_id = wizard_obj.create(self.cr, self.uid, {
117+ 'date': True,
118+ 'name': True,
119+ 'price': True,
120+ 'time': True,
121+ }, context={'active_ids': [line_id]})
122+ act_win = wizard_obj.do_create(self.cr, self.uid, [wizard_id], context={'active_ids': [line_id]})
123+ invoice_ids = self.invoice_obj.search(self.cr, self.uid, act_win['domain'])
124+ invoices = self.invoice_obj.browse(self.cr, self.uid, invoice_ids)
125+ self.assertEquals(1, len(invoices))
126+ return invoices[0]
127+
128+ def test_00(self):
129+ """ invoice task work basic test """
130+ self.prepare()
131+ invoice = self.create_invoice()
132+ self.assertEquals(round(self.QTY*self.PRICE*0.5, 2), invoice.amount_untaxed)
133+
134+ def test_01(self):
135+ """ invoice task work for analytic account of other company """
136+ self.prepare()
137+ # create a company B with its own account chart
138+ self.company_b_id = self.company_obj.create(self.cr, self.uid, {'name': 'Company B'})
139+ self.company_b = self.company_obj.browse(self.cr, self.uid, self.company_b_id)
140+ mc_wizard = self.registry('wizard.multi.charts.accounts')
141+ mc_wizard_id = mc_wizard.create(self.cr, self.uid, {
142+ 'company_id': self.company_b_id,
143+ 'chart_template_id': self.ref('account.conf_chart0'),
144+ 'code_digits': 2,
145+ #'sale_tax': config.sale_tax.id,
146+ #'purchase_tax': config.purchase_tax.id,
147+ 'sale_tax_rate': 0.10,
148+ 'purchase_tax_rate': 0.10,
149+ #'complete_tax_set': config.complete_tax_set,
150+ 'currency_id': self.company_b.currency_id.id,
151+ })
152+ mc_wizard.execute(self.cr, self.uid, [mc_wizard_id])
153+ # set our analytic account on company B
154+ self.analytic_account_obj.write(self.cr, self.uid, [self.aa_id], {
155+ 'company_id': self.company_b_id,
156+ })
157+ invoice = self.create_invoice()
158+ self.assertEquals(self.company_b_id, invoice.company_id.id, "invoice created for wrong company")
159+ self.assertEquals(self.company_b_id, invoice.journal_id.company_id.id, "invoice created with journal of wrong company")
160+ self.assertEquals(self.company_b_id, invoice.invoice_line[0].account_id.company_id.id, "invoice line created with account of wrong company")
161+ self.assertEquals(self.company_b_id, invoice.account_id.company_id.id, "invoice line created with partner account of wrong company")
162+ #self.assertEquals(self.company_b_id, invoice.fiscal_position.company_id.id, "invoice line created with fiscal position of wrong company")
163+
164+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
165
166=== modified file 'hr_timesheet_invoice/wizard/hr_timesheet_invoice_create.py'
167--- hr_timesheet_invoice/wizard/hr_timesheet_invoice_create.py 2012-11-28 11:40:02 +0000
168+++ hr_timesheet_invoice/wizard/hr_timesheet_invoice_create.py 2012-11-28 20:08:30 +0000
169@@ -23,6 +23,168 @@
170 from osv import osv, fields
171 from tools.translate import _
172
173+<<<<<<< TREE
174+=======
175+## Create an invoice based on selected timesheet lines
176+#
177+
178+class account_analytic_line(osv.osv):
179+ _inherit = "account.analytic.line"
180+ def _get_invoice_price(self, cr, uid, account, product_id, user_id, qty, context = {}):
181+ pro_price_obj = self.pool.get('product.pricelist')
182+ if account.pricelist_id:
183+ pl = account.pricelist_id.id
184+ price = pro_price_obj.price_get(cr,uid,[pl], product_id, qty or 1.0, account.partner_id.id, context=context)[pl]
185+ else:
186+ price = 0.0
187+ return price
188+
189+
190+ #
191+ # data = {
192+ # 'date': boolean
193+ # 'time': boolean
194+ # 'name': boolean
195+ # 'price': boolean
196+ # 'product': many2one id
197+ # }
198+ def invoice_cost_create(self, cr, uid, ids, data=None, context=None):
199+ analytic_account_obj = self.pool.get('account.analytic.account')
200+ account_payment_term_obj = self.pool.get('account.payment.term')
201+ invoice_obj = self.pool.get('account.invoice')
202+ product_obj = self.pool.get('product.product')
203+ invoice_factor_obj = self.pool.get('hr_timesheet_invoice.factor')
204+ fiscal_pos_obj = self.pool.get('account.fiscal.position')
205+ product_uom_obj = self.pool.get('product.uom')
206+ invoice_line_obj = self.pool.get('account.invoice.line')
207+ res_partner_obj = self.pool.get('res.partner')
208+ invoices = []
209+ if context is None:
210+ context = {}
211+ if data is None:
212+ data = {}
213+
214+ account_ids = {}
215+ for line in self.pool.get('account.analytic.line').browse(cr, uid, ids, context=context):
216+ account_ids[line.account_id.id] = True
217+
218+ account_ids = account_ids.keys() #data['accounts']
219+ for account in analytic_account_obj.browse(cr, uid, account_ids, context=context):
220+ context2 = context.copy()
221+ context2['lang'] = account.partner_id.lang
222+ # set company_id in context, so the correct default journal will be selected
223+ # when creating the invoice
224+ context2['company_id'] = account.company_id.id
225+ # set force_company in context so the correct properties are selected
226+ # (eg. income account, receivable account)
227+ context2['force_company'] = account.company_id.id
228+
229+ # browse partner in a context with force_company so we get the correct properties
230+ partner = res_partner_obj.browse(cr, uid, account.partner_id.id, context=context2)
231+
232+ if (not partner) or not (account.pricelist_id):
233+ raise osv.except_osv(_('Analytic Account incomplete !'),
234+ _('Please fill in the Partner or Customer and Sale Pricelist fields in the Analytic Account:\n%s.') % (account.name,))
235+
236+ date_due = False
237+ if partner.property_payment_term:
238+ pterm_list= account_payment_term_obj.compute(cr, uid,
239+ partner.property_payment_term.id, value=1,
240+ date_ref=time.strftime('%Y-%m-%d'))
241+ if pterm_list:
242+ pterm_list = [line[0] for line in pterm_list]
243+ pterm_list.sort()
244+ date_due = pterm_list[-1]
245+
246+ curr_invoice = {
247+ 'name': time.strftime('%d/%m/%Y')+' - '+account.name,
248+ 'partner_id': account.partner_id.id,
249+ 'company_id': account.company_id.id,
250+ 'payment_term': partner.property_payment_term.id or False,
251+ 'account_id': partner.property_account_receivable.id,
252+ 'currency_id': account.pricelist_id.currency_id.id,
253+ 'date_due': date_due,
254+ 'fiscal_position': account.partner_id.property_account_position.id
255+ }
256+
257+ last_invoice = invoice_obj.create(cr, uid, curr_invoice, context=context2)
258+ invoices.append(last_invoice)
259+
260+ cr.execute("SELECT product_id, user_id, to_invoice, sum(unit_amount), product_uom_id, name " \
261+ "FROM account_analytic_line as line " \
262+ "WHERE account_id = %s " \
263+ "AND id IN %s AND to_invoice IS NOT NULL " \
264+ "GROUP BY product_id, user_id, to_invoice, product_uom_id, name", (account.id, tuple(ids),))
265+
266+ for product_id, user_id, factor_id, qty, uom, line_name in cr.fetchall():
267+ if data.get('product'):
268+ product_id = data['product'][0]
269+ product = product_obj.browse(cr, uid, product_id, context=context2)
270+ if not product:
271+ raise osv.except_osv(_('Error!'), _('There is no product defined for the line %s. Please select one or force the product through the wizard.') % (line_name))
272+ factor = invoice_factor_obj.browse(cr, uid, factor_id, context=context2)
273+ factor_name = product_obj.name_get(cr, uid, [product_id], context=context2)[0][1]
274+ if factor.customer_name:
275+ factor_name += ' - ' + factor.customer_name
276+
277+ ctx = context.copy()
278+ ctx.update({'uom':uom})
279+
280+ price = self._get_invoice_price(cr, uid, account, product_id, user_id, qty, ctx)
281+
282+ general_account = product.product_tmpl_id.property_account_income or product.categ_id.property_account_income_categ
283+ if not general_account:
284+ raise osv.except_osv(_("Configuration Error!"), _("Please define income account for product '%s'.") % product.name)
285+ taxes = product.taxes_id or general_account.tax_ids
286+ tax = fiscal_pos_obj.map_tax(cr, uid, account.partner_id.property_account_position, taxes)
287+ curr_line = {
288+ 'price_unit': price,
289+ 'quantity': qty,
290+ 'discount':factor.factor,
291+ 'invoice_line_tax_id': [(6,0,tax )],
292+ 'invoice_id': last_invoice,
293+ 'name': factor_name,
294+ 'product_id': product_id,
295+ 'invoice_line_tax_id': [(6,0,tax)],
296+ 'uos_id': uom,
297+ 'account_id': general_account.id,
298+ 'account_analytic_id': account.id,
299+ }
300+
301+ #
302+ # Compute for lines
303+ #
304+ 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))
305+
306+ line_ids = cr.dictfetchall()
307+ note = []
308+ for line in line_ids:
309+ # set invoice_line_note
310+ details = []
311+ if data.get('date', False):
312+ details.append(line['date'])
313+ if data.get('time', False):
314+ if line['product_uom_id']:
315+ details.append("%s %s" % (line['unit_amount'], product_uom_obj.browse(cr, uid, [line['product_uom_id']],context2)[0].name))
316+ else:
317+ details.append("%s" % (line['unit_amount'], ))
318+ if data.get('name', False):
319+ details.append(line['name'])
320+ note.append(u' - '.join(map(lambda x: unicode(x) or '',details)))
321+
322+ if note:
323+ curr_line['name'] += "\n" + ("\n".join(map(lambda x: unicode(x) or '',note)))
324+ invoice_line_obj.create(cr, uid, curr_line, context=context)
325+ cr.execute("update account_analytic_line set invoice_id=%s WHERE account_id = %s and id IN %s", (last_invoice, account.id, tuple(ids)))
326+
327+ invoice_obj.button_reset_taxes(cr, uid, [last_invoice], context)
328+ return invoices
329+
330+#
331+# TODO: check unit of measure !!!
332+#
333+
334+>>>>>>> MERGE-SOURCE
335 class hr_timesheet_invoice_create(osv.osv_memory):
336
337 _name = 'hr.timesheet.invoice.create'

Subscribers

People subscribed via source and target branches

to all changes: