[MERGE] hr_timesheet_invoice: fix bug 1048305 + add regression test, courtesy of Stephane Bidoul (Acsone)
The semantics for invoice taxes in OpenERP is to use the product taxes if the product has any (and if there is a specific product), and otherwise use those on the G/L account that is used. This is how taxes are determined when creating invoices. lp bug: https://launchpad.net/bugs/1048305 fixed bzr revid: odo@openerp.com-20121030162443-rn18sl1twp2t1mex
This commit is contained in:
commit
eefc873a4b
|
@ -50,6 +50,7 @@ reports.""",
|
||||||
],
|
],
|
||||||
'demo': ['hr_timesheet_invoice_demo.xml'],
|
'demo': ['hr_timesheet_invoice_demo.xml'],
|
||||||
'test': ['test/test_hr_timesheet_invoice.yml',
|
'test': ['test/test_hr_timesheet_invoice.yml',
|
||||||
|
'test/test_hr_timesheet_invoice_no_prod_tax.yml',
|
||||||
'test/hr_timesheet_invoice_report.yml',
|
'test/hr_timesheet_invoice_report.yml',
|
||||||
],
|
],
|
||||||
'installable': True,
|
'installable': True,
|
||||||
|
|
|
@ -30,6 +30,42 @@
|
||||||
-
|
-
|
||||||
!python {model: hr.analytic.timesheet}: |
|
!python {model: hr.analytic.timesheet}: |
|
||||||
self.on_change_account_id(cr, uid, [ref('account_analytic_line_developyamlforhrmodule0')], ref('account.analytic_agrolait'))
|
self.on_change_account_id(cr, uid, [ref('account_analytic_line_developyamlforhrmodule0')], ref('account.analytic_agrolait'))
|
||||||
|
|
||||||
|
-
|
||||||
|
I create a Tax Codes
|
||||||
|
-
|
||||||
|
!record {model: account.tax.code, id: tax_case}:
|
||||||
|
name: Tax_case
|
||||||
|
company_id: base.main_company
|
||||||
|
sign: 1
|
||||||
|
-
|
||||||
|
I create a Tax
|
||||||
|
-
|
||||||
|
!record {model: account.tax, id: tax10}:
|
||||||
|
name: Tax 10.0
|
||||||
|
amount: 10.0
|
||||||
|
type: fixed
|
||||||
|
sequence: 1
|
||||||
|
company_id: base.main_company
|
||||||
|
type_tax_use: all
|
||||||
|
tax_code_id: tax_case
|
||||||
|
-
|
||||||
|
I create an Account with the tax
|
||||||
|
-
|
||||||
|
!record {model: account.account, id: account_income_i000}:
|
||||||
|
name: "income account"
|
||||||
|
code: "I000"
|
||||||
|
type: 'other'
|
||||||
|
user_type: "account.data_account_type_income"
|
||||||
|
tax_ids: [tax10]
|
||||||
|
-
|
||||||
|
I set the account as property_account_income on the product and I set the tax on the product
|
||||||
|
-
|
||||||
|
!record {model: product.product, id: product.product_product_consultant}:
|
||||||
|
property_account_income: account_income_i000
|
||||||
|
uom_id: product.product_uom_hour
|
||||||
|
taxes_id: [tax10]
|
||||||
|
list_price: 75
|
||||||
-
|
-
|
||||||
I create invoice on analytic Line using "Invoice analytic Line" wizard.
|
I create invoice on analytic Line using "Invoice analytic Line" wizard.
|
||||||
-
|
-
|
||||||
|
@ -39,13 +75,11 @@
|
||||||
price: 1
|
price: 1
|
||||||
product: product.product_product_consultant
|
product: product.product_product_consultant
|
||||||
time: 1
|
time: 1
|
||||||
|
|
||||||
-
|
-
|
||||||
I click on "Create Invoice" button of "Invoice analytic Line" wizard to create invoice.
|
I click on "Create Invoice" button of "Invoice analytic Line" wizard to create invoice.
|
||||||
-
|
-
|
||||||
!python {model: hr.timesheet.invoice.create}: |
|
!python {model: hr.timesheet.invoice.create}: |
|
||||||
self.do_create(cr, uid, [ref("hr_timesheet_invoice_create_0")], {"active_ids": [ref("hr_timesheet_invoice.account_analytic_line_developyamlforhrmodule0")]})
|
self.do_create(cr, uid, [ref("hr_timesheet_invoice_create_0")], {"active_ids": [ref("hr_timesheet_invoice.account_analytic_line_developyamlforhrmodule0")]})
|
||||||
|
|
||||||
-
|
-
|
||||||
I check that Invoice is created for this timesheet.
|
I check that Invoice is created for this timesheet.
|
||||||
-
|
-
|
||||||
|
@ -67,8 +101,8 @@
|
||||||
assert product == product_exp
|
assert product == product_exp
|
||||||
assert aline.invoice_id, "Invoice created, but analytic line wasn't updated."
|
assert aline.invoice_id, "Invoice created, but analytic line wasn't updated."
|
||||||
assert aline.invoice_id == invoice_id, "Invoice doesn't match the one at analytic line"
|
assert aline.invoice_id == invoice_id, "Invoice doesn't match the one at analytic line"
|
||||||
# The expected amount is hard coded here, please keep updated:
|
|
||||||
assert invoice_id.amount_untaxed == 187.5, "Invoice amount mismatch: %s" % invoice_id.amount_untaxed
|
assert invoice_id.amount_untaxed == 187.5, "Invoice amount mismatch: %s" % invoice_id.amount_untaxed
|
||||||
|
assert invoice_id.amount_tax == 50, "Invoice tax mismatch: %s" % invoice_id.amount_tax
|
||||||
-
|
-
|
||||||
I create final invoice for this analytic account.
|
I create final invoice for this analytic account.
|
||||||
-
|
-
|
||||||
|
|
|
@ -0,0 +1,119 @@
|
||||||
|
-
|
||||||
|
In order to test hr_timesheet_invoice in OpenERP, I create account line to manage invoice based on costs.
|
||||||
|
-
|
||||||
|
I create an account analytic line.
|
||||||
|
-
|
||||||
|
!record {model: account.analytic.line, id: account_analytic_line_developyamlforhrmodule1 }:
|
||||||
|
account_id: account.analytic_agrolait
|
||||||
|
amount: -1.0
|
||||||
|
general_account_id: account.a_expense
|
||||||
|
journal_id: hr_timesheet.analytic_journal
|
||||||
|
name: develop yaml for hr module
|
||||||
|
product_id: product.product_product_consultant
|
||||||
|
product_uom_id: product.product_uom_hour
|
||||||
|
to_invoice: hr_timesheet_invoice.timesheet_invoice_factor2
|
||||||
|
unit_amount: 5.00
|
||||||
|
-
|
||||||
|
Assign partner name and price list in analytic account.
|
||||||
|
-
|
||||||
|
!record {model: account.analytic.account, id: account.analytic_agrolait}:
|
||||||
|
partner_id: base.res_partner_2
|
||||||
|
pricelist_id: product.list0
|
||||||
|
-
|
||||||
|
I open this account and make the state as pending.
|
||||||
|
-
|
||||||
|
!python {model: account.analytic.account}: |
|
||||||
|
self.set_open(cr, uid, [ref('account.analytic_agrolait')], None)
|
||||||
|
self.set_pending(cr, uid, [ref('account.analytic_agrolait')], None)
|
||||||
|
-
|
||||||
|
I assign account on analytic account line.
|
||||||
|
-
|
||||||
|
!python {model: hr.analytic.timesheet}: |
|
||||||
|
self.on_change_account_id(cr, uid, [ref('account_analytic_line_developyamlforhrmodule1')], ref('account.analytic_agrolait'))
|
||||||
|
-
|
||||||
|
I create a Tax Code
|
||||||
|
-
|
||||||
|
!record {model: account.tax.code, id: tax_case}:
|
||||||
|
name: Tax_case
|
||||||
|
company_id: base.main_company
|
||||||
|
sign: 1
|
||||||
|
-
|
||||||
|
I create a Tax
|
||||||
|
-
|
||||||
|
!record {model: account.tax, id: tax8}:
|
||||||
|
name: Tax 8.0
|
||||||
|
amount: 8.0
|
||||||
|
type: fixed
|
||||||
|
sequence: 1
|
||||||
|
company_id: base.main_company
|
||||||
|
type_tax_use: all
|
||||||
|
tax_code_id: tax_case
|
||||||
|
-
|
||||||
|
I create an Account with the tax
|
||||||
|
-
|
||||||
|
!record {model: account.account, id: account_income_i000}:
|
||||||
|
name: "income account"
|
||||||
|
code: "I000"
|
||||||
|
type: 'other'
|
||||||
|
user_type: "account.data_account_type_income"
|
||||||
|
tax_ids: [tax8]
|
||||||
|
-
|
||||||
|
I set the account as property_account_income on the product and I set no tax on the product (so default tax from the account will be used)
|
||||||
|
-
|
||||||
|
!record {model: product.product, id: product.product_product_consultant}:
|
||||||
|
property_account_income: account_income_i000
|
||||||
|
uom_id: product.product_uom_hour
|
||||||
|
taxes_id: []
|
||||||
|
list_price: 75
|
||||||
|
-
|
||||||
|
I create invoice on analytic Line using "Invoice analytic Line" wizard.
|
||||||
|
-
|
||||||
|
!record {model: hr.timesheet.invoice.create, id: hr_timesheet_invoice_create_0}:
|
||||||
|
date: 1
|
||||||
|
name: 1
|
||||||
|
price: 1
|
||||||
|
product: product.product_product_consultant
|
||||||
|
time: 1
|
||||||
|
-
|
||||||
|
I click on "Create Invoice" button of "Invoice analytic Line" wizard to create invoice.
|
||||||
|
-
|
||||||
|
!python {model: hr.timesheet.invoice.create}: |
|
||||||
|
self.do_create(cr, uid, [ref("hr_timesheet_invoice_create_0")], {"active_ids": [ref("hr_timesheet_invoice.account_analytic_line_developyamlforhrmodule1")]})
|
||||||
|
-
|
||||||
|
I check that Invoice is created for this timesheet.
|
||||||
|
-
|
||||||
|
!python {model: account.analytic.line}: |
|
||||||
|
aline = self.browse(cr, uid, ref('account_analytic_line_developyamlforhrmodule1'))
|
||||||
|
analytic_account_obj = self.pool.get('account.analytic.account')
|
||||||
|
data = self.pool.get('hr.timesheet.invoice.create').read(cr, uid, [ref("hr_timesheet_invoice_create_0")], [], context)[0]
|
||||||
|
partner = aline.account_id.partner_id.id
|
||||||
|
|
||||||
|
invoice_obj = self.pool.get('account.invoice')
|
||||||
|
invoice_ids = invoice_obj.search(cr, uid, [('partner_id', '=', partner)])
|
||||||
|
invoice_id = invoice_obj.browse(cr, uid, invoice_ids)[0]
|
||||||
|
|
||||||
|
for invoice in invoice_id.invoice_line:
|
||||||
|
product = invoice.product_id.id
|
||||||
|
|
||||||
|
product_exp = data['product'][0]
|
||||||
|
|
||||||
|
assert product == product_exp
|
||||||
|
assert aline.invoice_id, "Invoice created, but analytic line wasn't updated."
|
||||||
|
assert aline.invoice_id == invoice_id, "Invoice doesn't match the one at analytic line"
|
||||||
|
assert invoice_id.amount_untaxed == 187.5, "Invoice amount mismatch: %s" % invoice_id.amount_untaxed
|
||||||
|
assert invoice_id.amount_tax == 40, "Invoice tax mismatch: %s" % invoice_id.amount_tax
|
||||||
|
-
|
||||||
|
I create final invoice for this analytic account.
|
||||||
|
-
|
||||||
|
!record {model: hr.timesheet.invoice.create.final, id: hr_timesheet_invoice_create_final_0}:
|
||||||
|
date: 1
|
||||||
|
name: 1
|
||||||
|
price: 1
|
||||||
|
time: 1
|
||||||
|
-
|
||||||
|
I click on "Create Invoice" button to create Invoice and validate the invoice.
|
||||||
|
-
|
||||||
|
!python {model: hr.timesheet.invoice.create.final}: |
|
||||||
|
import netsvc
|
||||||
|
wkf_service = netsvc.LocalService("workflow")
|
||||||
|
res = self.do_create(cr, uid, [ref("hr_timesheet_invoice_create_final_0")], {"active_ids": [ref("account.analytic_agrolait")]})
|
|
@ -49,7 +49,6 @@ class account_analytic_line(osv.osv):
|
||||||
# }
|
# }
|
||||||
def invoice_cost_create(self, cr, uid, ids, data=None, context=None):
|
def invoice_cost_create(self, cr, uid, ids, data=None, context=None):
|
||||||
analytic_account_obj = self.pool.get('account.analytic.account')
|
analytic_account_obj = self.pool.get('account.analytic.account')
|
||||||
res_partner_obj = self.pool.get('res.partner')
|
|
||||||
account_payment_term_obj = self.pool.get('account.payment.term')
|
account_payment_term_obj = self.pool.get('account.payment.term')
|
||||||
invoice_obj = self.pool.get('account.invoice')
|
invoice_obj = self.pool.get('account.invoice')
|
||||||
product_obj = self.pool.get('product.product')
|
product_obj = self.pool.get('product.product')
|
||||||
|
@ -115,7 +114,7 @@ class account_analytic_line(osv.osv):
|
||||||
|
|
||||||
for product_id, user_id, factor_id, qty, uom, line_name in cr.fetchall():
|
for product_id, user_id, factor_id, qty, uom, line_name in cr.fetchall():
|
||||||
if data.get('product'):
|
if data.get('product'):
|
||||||
product_id = data['product'][0]
|
product_id = data['product'][0]
|
||||||
product = product_obj.browse(cr, uid, product_id, context=context2)
|
product = product_obj.browse(cr, uid, product_id, context=context2)
|
||||||
if not product:
|
if not product:
|
||||||
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))
|
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))
|
||||||
|
@ -132,7 +131,7 @@ class account_analytic_line(osv.osv):
|
||||||
general_account = product.product_tmpl_id.property_account_income or product.categ_id.property_account_income_categ
|
general_account = product.product_tmpl_id.property_account_income or product.categ_id.property_account_income_categ
|
||||||
if not general_account:
|
if not general_account:
|
||||||
raise osv.except_osv(_("Configuration Error!"), _("Please define income account for product '%s'.") % product.name)
|
raise osv.except_osv(_("Configuration Error!"), _("Please define income account for product '%s'.") % product.name)
|
||||||
taxes = product.taxes_id
|
taxes = product.taxes_id or general_account.tax_ids
|
||||||
tax = fiscal_pos_obj.map_tax(cr, uid, account.partner_id.property_account_position, taxes)
|
tax = fiscal_pos_obj.map_tax(cr, uid, account.partner_id.property_account_position, taxes)
|
||||||
curr_line = {
|
curr_line = {
|
||||||
'price_unit': price,
|
'price_unit': price,
|
||||||
|
@ -211,7 +210,7 @@ class hr_timesheet_invoice_create(osv.osv_memory):
|
||||||
data = context and context.get('active_ids', [])
|
data = context and context.get('active_ids', [])
|
||||||
for analytic in analytic_obj.browse(cr, uid, data, context=context):
|
for analytic in analytic_obj.browse(cr, uid, data, context=context):
|
||||||
if analytic.invoice_id:
|
if analytic.invoice_id:
|
||||||
raise osv.except_osv(_('Warning!'), _("Invoice is already linked to some of the analytic line(s)!"))
|
raise osv.except_osv(_('Warning!'), _("Invoice is already linked to some of the analytic line(s)!"))
|
||||||
|
|
||||||
def do_create(self, cr, uid, ids, context=None):
|
def do_create(self, cr, uid, ids, context=None):
|
||||||
data = self.read(cr, uid, ids, [], context=context)[0]
|
data = self.read(cr, uid, ids, [], context=context)[0]
|
||||||
|
|
Loading…
Reference in New Issue