Skip to content
Snippets Groups Projects
Commit 4d912af4 authored by Martin Trigaux's avatar Martin Trigaux
Browse files

[ADD] hr_timesheet_invoice: grouping invoice by partner

Add option to group analytic lines by partner to generate invoices.
Refactoring of the method that was kind of unclear, splitting in separated
methods and adding tests.
Add graph view in analytic lines view.
parent ddc80832
Branches
Tags
No related merge requests found
Showing
with 378 additions and 134 deletions
......@@ -104,7 +104,18 @@
</field>
</field>
</record>
<record id="view_account_analytic_line_pivot" model="ir.ui.view">
<field name="name">account.analytic.line.pivot</field>
<field name="model">account.analytic.line</field>
<field name="inherit_id" ref="analytic.view_account_analytic_line_pivot"/>
<field name="arch" type="xml">
<field name="account_id" position="after">
<field name="partner_id" type="row"/>
</field>
</field>
</record>
<menuitem groups="analytic.group_analytic_accounting" id="next_id_40"
name="Analytic" parent="account.menu_finance_generic_reporting"
sequence="4"/>
......
......@@ -278,7 +278,7 @@
<field name="name">Time &amp; Materials to Invoice</field>
<field name="res_model">account.analytic.line</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="view_mode">tree,form,pivot</field>
<field name="domain">[('invoice_id','=',False)]</field>
<field name="context">{'search_default_to_invoice': 1}</field>
<field name="search_view_id" ref="analytic.view_account_analytic_line_filter"/>
......
......@@ -242,6 +242,18 @@
</search>
</field>
</record>
<record id="view_account_analytic_line_pivot" model="ir.ui.view">
<field name="name">account.analytic.line.pivot</field>
<field name="model">account.analytic.line</field>
<field name="arch" type="xml">
<pivot string="Analytic Entries" >
<field name="account_id" type="row"/>
<field name="amount" type="measure"/>
<field name="unit_amount" type="measure"/>
</pivot>
</field>
</record>
<record id="action_account_analytic_line_form" model="ir.actions.act_window">
<field name="name">Analytic Entries</field>
<field name="type">ir.actions.act_window</field>
......@@ -365,7 +377,7 @@
<field name="name">Analytic Journal Items</field>
<field name="res_model">account.analytic.line</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="view_mode">tree,form,pivot</field>
</record>
</data>
......
......@@ -154,14 +154,109 @@ class account_analytic_line(osv.osv):
price = 0.0
return price
def _prepare_cost_invoice(self, cr, uid, partner, company_id, currency_id, analytic_lines, group_by_partner=False, context=None):
""" returns values used to create main invoice from analytic lines"""
account_payment_term_obj = self.pool['account.payment.term']
if group_by_partner:
invoice_name = partner.name
else:
invoice_name = analytic_lines[0].account_id.name
date_due = False
if partner.property_payment_term:
pterm_list= account_payment_term_obj.compute(cr, uid,
partner.property_payment_term.id, value=1,
date_ref=time.strftime('%Y-%m-%d'))
if pterm_list:
pterm_list = [line[0] for line in pterm_list]
pterm_list.sort()
date_due = pterm_list[-1]
return {
'name': "%s - %s" % (time.strftime('%d/%m/%Y'), invoice_name),
'partner_id': partner.id,
'company_id': company_id,
'payment_term': partner.property_payment_term.id or False,
'account_id': partner.property_account_receivable.id,
'currency_id': currency_id,
'date_due': date_due,
'fiscal_position': partner.property_account_position.id
}
def _prepare_cost_invoice_line(self, cr, uid, invoice_id, product_id, uom, user_id,
factor_id, account, analytic_lines, journal_type, data, context=None):
product_obj = self.pool['product.product']
uom_context = dict(context or {}, uom=uom)
total_price = sum(l.amount for l in analytic_lines)
total_qty = sum(l.unit_amount for l in analytic_lines)
if data.get('product'):
# force product, use its public price
if isinstance(data['product'], (tuple, list)):
product_id = data['product'][0]
else:
product_id = data['product']
unit_price = self._get_invoice_price(cr, uid, account, product_id, user_id, total_qty, uom_context)
elif journal_type == 'general' and product_id:
# timesheets, use sale price
unit_price = self._get_invoice_price(cr, uid, account, product_id, user_id, total_qty, uom_context)
else:
# expenses, using price from amount field
unit_price = total_price*-1.0 / total_qty
factor = self.pool['hr_timesheet_invoice.factor'].browse(cr, uid, factor_id, context=uom_context)
factor_name = factor.customer_name
curr_invoice_line = {
'price_unit': unit_price,
'quantity': total_qty,
'product_id': product_id,
'discount': factor.factor,
'invoice_id': invoice_id,
'name': factor_name,
'uos_id': uom,
'account_analytic_id': account.id,
}
if product_id:
product = product_obj.browse(cr, uid, product_id, context=uom_context)
factor_name = product_obj.name_get(cr, uid, [product_id], context=uom_context)[0][1]
if factor.customer_name:
factor_name += ' - ' + factor.customer_name
general_account = product.property_account_income or product.categ_id.property_account_income_categ
if not general_account:
raise UserError(_("Configuration Error!") + '\n' + _("Please define income account for product '%s'.") % product.name)
taxes = product.taxes_id or general_account.tax_ids
tax = self.pool['account.fiscal.position'].map_tax(cr, uid, account.partner_id.property_account_position, taxes)
curr_invoice_line.update({
'invoice_line_tax_id': [(6, 0, tax)],
'name': factor_name,
'invoice_line_tax_id': [(6, 0, tax)],
'account_id': general_account.id,
})
note = []
for line in analytic_lines:
# set invoice_line_note
details = []
if data.get('date', False):
details.append(line['date'])
if data.get('time', False):
if line['product_uom_id']:
details.append("%s %s" % (line.unit_amount, line.product_uom_id.name))
else:
details.append("%s" % (line['unit_amount'], ))
if data.get('name', False):
details.append(line['name'])
if details:
note.append(u' - '.join(map(lambda x: unicode(x) or '',details)))
if note:
curr_invoice_line['name'] += "\n" + ("\n".join(map(lambda x: unicode(x) or '',note)))
return curr_invoice_line
def invoice_cost_create(self, cr, uid, ids, data=None, context=None):
analytic_account_obj = self.pool.get('account.analytic.account')
account_payment_term_obj = self.pool.get('account.payment.term')
invoice_obj = self.pool.get('account.invoice')
product_obj = self.pool.get('product.product')
invoice_factor_obj = self.pool.get('hr_timesheet_invoice.factor')
fiscal_pos_obj = self.pool.get('account.fiscal.position')
product_uom_obj = self.pool.get('product.uom')
invoice_line_obj = self.pool.get('account.invoice.line')
invoices = []
if context is None:
......@@ -169,127 +264,78 @@ class account_analytic_line(osv.osv):
if data is None:
data = {}
journal_types = {}
# use key (partner/account, company, currency)
# creates one invoice per key
invoice_grouping = {}
# grouping on partner instead of analytic account
group_by_partner = data.get('group_by_partner', False)
currency_id = False
# prepare for iteration on journal and accounts
for line in self.pool.get('account.analytic.line').browse(cr, uid, ids, context=context):
if line.journal_id.type not in journal_types:
journal_types[line.journal_id.type] = set()
journal_types[line.journal_id.type].add(line.account_id.id)
for journal_type, account_ids in journal_types.items():
for account in analytic_account_obj.browse(cr, uid, list(account_ids), context=context):
partner = account.partner_id
for line in self.browse(cr, uid, ids, context=context):
# check if currency is the same in different accounts when grouping by partner
if not currency_id :
currency_id = line.account_id.pricelist_id.currency_id.id
if line.account_id.pricelist_id and line.account_id.pricelist_id.currency_id:
if line.account_id.pricelist_id.currency_id.id != currency_id and group_by_partner:
raise UserError(_('Error!'),
_('You cannot group invoices having different currencies on different analytic accounts for the same partner.'))
if group_by_partner:
key = (line.account_id.partner_id.id,
line.account_id.company_id.id,
line.account_id.pricelist_id.currency_id.id)
invoice_grouping.setdefault(key, []).append(line)
else:
key = (line.account_id.id,
line.account_id.company_id.id,
line.account_id.pricelist_id.currency_id.id)
invoice_grouping.setdefault(key, []).append(line)
for (key_id, company_id, currency_id), analytic_lines in invoice_grouping.items():
# key_id is either an account.analytic.account, either a res.partner
# don't really care, what's important is the analytic lines that
# will be used to create the invoice lines
partner = analytic_lines[0].account_id.partner_id # will be the same for every line
curr_invoice = self._prepare_cost_invoice(cr, uid, partner, company_id, currency_id, analytic_lines, group_by_partner, context=context)
invoice_context = dict(context,
lang=partner.lang,
force_company=company_id, # set force_company in context so the correct product properties are selected (eg. income account)
company_id=company_id) # set company_id in context, so the correct default journal will be selected
last_invoice = invoice_obj.create(cr, uid, curr_invoice, context=invoice_context)
invoices.append(last_invoice)
# use key (product, uom, user, invoiceable, analytic account, journal type)
# creates one invoice line per key
invoice_lines_grouping = {}
for analytic_line in analytic_lines:
account = analytic_line.account_id
if (not partner) or not (account.pricelist_id):
raise UserError(_('Contract incomplete. Please fill in the Customer and Pricelist fields.'))
date_due = False
if partner.property_payment_term:
pterm_list= account_payment_term_obj.compute(cr, uid,
partner.property_payment_term.id, value=1,
date_ref=time.strftime('%Y-%m-%d'))
if pterm_list:
pterm_list = [line[0] for line in pterm_list]
pterm_list.sort()
date_due = pterm_list[-1]
curr_invoice = {
'name': time.strftime('%d/%m/%Y') + ' - '+account.name,
'partner_id': account.partner_id.id,
'company_id': account.company_id.id,
'payment_term': partner.property_payment_term.id or False,
'account_id': partner.property_account_receivable.id,
'currency_id': account.pricelist_id.currency_id.id,
'date_due': date_due,
'fiscal_position': account.partner_id.property_account_position.id
}
context2 = context.copy()
context2['lang'] = partner.lang
# set company_id in context, so the correct default journal will be selected
context2['force_company'] = curr_invoice['company_id']
# set force_company in context so the correct product properties are selected (eg. income account)
context2['company_id'] = curr_invoice['company_id']
last_invoice = invoice_obj.create(cr, uid, curr_invoice, context=context2)
invoices.append(last_invoice)
cr.execute("""SELECT product_id, user_id, to_invoice, sum(amount), sum(unit_amount), product_uom_id
FROM account_analytic_line as line LEFT JOIN account_analytic_journal journal ON (line.journal_id = journal.id)
WHERE account_id = %s
AND line.id IN %s AND journal.type = %s AND to_invoice IS NOT NULL
GROUP BY product_id, user_id, to_invoice, product_uom_id""", (account.id, tuple(ids), journal_type))
for product_id, user_id, factor_id, total_price, qty, uom in cr.fetchall():
context2.update({'uom': uom})
if data.get('product'):
# force product, use its public price
product_id = data['product'][0]
unit_price = self._get_invoice_price(cr, uid, account, product_id, user_id, qty, context2)
elif journal_type == 'general' and product_id:
# timesheets, use sale price
unit_price = self._get_invoice_price(cr, uid, account, product_id, user_id, qty, context2)
else:
# expenses, using price from amount field
unit_price = total_price*-1.0 / qty
factor = invoice_factor_obj.browse(cr, uid, factor_id, context=context2)
# factor_name = factor.customer_name and line_name + ' - ' + factor.customer_name or line_name
factor_name = factor.customer_name
curr_line = {
'price_unit': unit_price,
'quantity': qty,
'product_id': product_id or False,
'discount': factor.factor,
'invoice_id': last_invoice,
'name': factor_name,
'uos_id': uom,
'account_analytic_id': account.id,
}
product = product_obj.browse(cr, uid, product_id, context=context2)
if product:
factor_name = product_obj.name_get(cr, uid, [product_id], context=context2)[0][1]
if factor.customer_name:
factor_name += ' - ' + factor.customer_name
general_account = product.property_account_income or product.categ_id.property_account_income_categ
if not general_account:
raise UserError(_("Configuration Error!") + '\n' + _("Please define income account for product '%s'.") % product.name)
taxes = product.taxes_id or general_account.tax_ids
tax = fiscal_pos_obj.map_tax(cr, uid, account.partner_id.property_account_position, taxes)
curr_line.update({
'invoice_line_tax_id': [(6,0,tax )],
'name': factor_name,
'invoice_line_tax_id': [(6,0,tax)],
'account_id': general_account.id,
})
#
# Compute for lines
#
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))
line_ids = cr.dictfetchall()
note = []
for line in line_ids:
# set invoice_line_note
details = []
if data.get('date', False):
details.append(line['date'])
if data.get('time', False):
if line['product_uom_id']:
details.append("%s %s" % (line['unit_amount'], product_uom_obj.browse(cr, uid, [line['product_uom_id']],context2)[0].name))
else:
details.append("%s" % (line['unit_amount'], ))
if data.get('name', False):
details.append(line['name'])
if details:
note.append(u' - '.join(map(lambda x: unicode(x) or '',details)))
if note:
curr_line['name'] += "\n" + ("\n".join(map(lambda x: unicode(x) or '',note)))
invoice_line_obj.create(cr, uid, curr_line, context=context)
cr.execute("update account_analytic_line set invoice_id=%s WHERE account_id = %s and id IN %s", (last_invoice, account.id, tuple(ids)))
self.invalidate_cache(cr, uid, ['invoice_id'], ids, context=context)
invoice_obj.button_reset_taxes(cr, uid, [last_invoice], context)
raise UserError(_('Contract incomplete. Please fill in the Customer and Pricelist fields for %s.') % (account.name))
if not analytic_line.to_invoice:
raise UserError(_('Trying to invoice non invoiceable line for %s.') % (analytic_line.product_id.name))
key = (analytic_line.product_id.id,
analytic_line.product_uom_id.id,
analytic_line.user_id.id,
analytic_line.to_invoice.id,
analytic_line.account_id,
analytic_line.journal_id.type)
invoice_lines_grouping.setdefault(key, []).append(analytic_line)
# finally creates the invoice line
for (product_id, uom, user_id, factor_id, account, journal_type), lines_to_invoice in invoice_lines_grouping.items():
curr_invoice_line = self._prepare_cost_invoice_line(cr, uid, last_invoice,
product_id, uom, user_id, factor_id, account, lines_to_invoice,
journal_type, data, context=context)
invoice_line_obj.create(cr, uid, curr_invoice_line, context=context)
self.write(cr, uid, [l.id for l in analytic_lines], {'invoice_id': last_invoice}, context=context)
invoice_obj.button_reset_taxes(cr, uid, [last_invoice], context)
return invoices
......
......@@ -114,3 +114,66 @@
assert aline.invoice_id == invoice_id, "Invoice doesn't match the one at analytic line"
assert float_compare(invoice_id.amount_untaxed, 187.5, precision_digits=2) == 0, "Invoice amount mismatch: %s" % invoice_id.amount_untaxed
assert float_compare(invoice_id.amount_tax, 50, precision_digits=2) == 0, "Invoice tax mismatch: %s" % invoice_id.amount_tax
-
In order to test group invoicing by customer.
-
I create an analytic account 'Sales Integration' for customer Asustek.
-
!record {model: account.analytic.account, id: analytic_asustek01}:
name: Sales Integration
partner_id: base.res_partner_1
-
I create an analytic account 'Asustek Website' for customer Asustek.
-
!record {model: account.analytic.account, id: analytic_asustek02}:
name: Asustek Website
partner_id: base.res_partner_1
-
I create an account analytic line.
-
!record {model: account.analytic.line, id: account_analytic_line_0 }:
account_id: analytic_asustek01
amount: -1.0
general_account_id: account.a_expense
journal_id: hr_timesheet.analytic_journal
name: sales module extension
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
-
I create an account analytic line.
-
!record {model: account.analytic.line, id: account_analytic_line_3 }:
account_id: analytic_asustek02
amount: -1.0
general_account_id: account.a_expense
journal_id: hr_timesheet.analytic_journal
name: template design
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
-
Now I create invoice on analytic Line using "Invoice analytic Line" wizard and set group by partner true.
-
!record {model: hr.timesheet.invoice.create, id: hr_timesheet_invoice_create_1}:
date: 1
name: 1
product: product.product_product_consultant
time: 1
group_by_partner: 1
-
I click on "Create Invoice" button of "Invoice analytic Line" wizard to create invoice and check that only one invoice is created for customer Asustek.
-
!python {model: hr.timesheet.invoice.create}: |
action_result = self.do_create(cr, uid, [ref("hr_timesheet_invoice_create_1")], {
"active_ids": [ref("hr_timesheet_invoice.account_analytic_line_0"),ref("hr_timesheet_invoice.account_analytic_line_3")]
})
invoice_pool = self.pool.get('account.invoice')
invoice_domain = action_result['domain']
invoice_ids = invoice_pool.search(cr, uid, invoice_domain)
invoice_id = invoice_pool.browse(cr, uid, invoice_ids)[0]
assert len(invoice_ids) == 1,"only one invoice should be created for customer Asustek."
assert invoice_id.amount_untaxed == 375.0, "Invoice amount mismatch: %s" % invoice_id.amount_untaxed
from . import test_invoice
from openerp.tests.common import TransactionCase
class TestInvoice(TransactionCase):
"""Tests to generate invoices from analytic lines
"""
def setUp(self):
super(TestInvoice, self).setUp()
cr, uid = self.cr, self.uid
self.account_invoice = self.registry('account.invoice')
self.account_analytic_account = self.registry('account.analytic.account')
self.account_analytic_line = self.registry('account.analytic.line')
self.product_product = self.registry('product.product')
self.think_big_id = self.registry("ir.model.data").get_object_reference(cr, uid, "base", "res_partner_18")[1]
self.timesheet_journal_id = self.registry("ir.model.data").get_object_reference(cr, uid, "hr_timesheet", "analytic_journal")[1]
self.expense_journal_id = self.registry("ir.model.data").get_object_reference(cr, uid, "account", "exp")[1]
self.expense_account_id = self.registry("ir.model.data").get_object_reference(cr, uid, "account", "a_expense")[1]
self.expense_account_id = self.registry("ir.model.data").get_object_reference(cr, uid, "account", "a_expense")[1]
self.factor_100_id = self.registry("ir.model.data").get_object_reference(cr, uid, "hr_timesheet_invoice", "timesheet_invoice_factor1")[1]
self.potato_account_id = self.account_analytic_account.create(cr, uid, {
'name': 'Potatoes Project',
'partner_id': self.think_big_id,
'type': 'contract',
'state': 'open',
})
self.carrot_account_id = self.account_analytic_account.create(cr, uid, {
'name': 'Carrot & Development',
'partner_id': self.think_big_id,
'type': 'contract',
'state': 'open',
})
self.potato_id = self.product_product.create(cr, uid, {
'name': 'Potato',
'list_price': 2,
})
self.carrot_id = self.product_product.create(cr, uid, {
'name': 'Carrot',
'list_price': 3,
})
def test_signle_invoice(self):
cr, uid = self.cr, self.uid
first_line = {
'name': 'One potato',
'amount': 2,
'unit_amount': 1,
'product_id': self.potato_id,
'account_id': self.potato_account_id,
'general_account_id': self.expense_account_id,
'journal_id': self.expense_journal_id,
'partner_id': self.think_big_id,
'to_invoice': self.factor_100_id,
}
second_line = {
'name': 'Two carrots',
'amount': 6,
'unit_amount': 2,
'product_id': self.carrot_id,
'account_id': self.carrot_account_id,
'general_account_id': self.expense_account_id,
'journal_id': self.expense_journal_id,
'partner_id': self.think_big_id,
'to_invoice': self.factor_100_id,
}
first_line_id = self.account_analytic_line.create(cr, uid, first_line)
second_line_id = self.account_analytic_line.create(cr, uid, second_line)
data = {'group_by_partner': False, 'date': True, 'name': True}
invoice_ids = self.account_analytic_line.invoice_cost_create(cr, uid, [first_line_id, second_line_id], data)
self.assertEquals(len(invoice_ids), 2)
for invoice in self.account_invoice.browse(cr, uid, invoice_ids[0]):
self.assertEquals(len(invoice.invoice_line), 1)
line = invoice.invoice_line[0]
if line.product_id.id == self.potato_id:
self.assertEquals(line.account_analytic_id.id, self.potato_account_id)
self.assertEquals(line.price_unit, -2)
self.assertEquals(line.quantity, 1)
else:
self.assertEquals(line.product_id, self.carrot_id)
self.assertEquals(line.account_analytic_id.id, self.carrot_account_id)
self.assertEquals(line.price_unit, -3)
self.assertEquals(line.quantity, 2)
data = {'group_by_partner': True, 'date': True, 'name': True}
first_line_id = self.account_analytic_line.create(cr, uid, first_line)
second_line_id = self.account_analytic_line.create(cr, uid, second_line)
invoice_ids = self.account_analytic_line.invoice_cost_create(cr, uid, [first_line_id, second_line_id], data)
self.assertEquals(len(invoice_ids), 1)
invoice = self.account_invoice.browse(cr, uid, invoice_ids[0])
self.assertEquals(len(invoice.invoice_line), 2)
for line in invoice.invoice_line:
if line.product_id.id == self.potato_id:
self.assertEquals(line.account_analytic_id.id, self.potato_account_id)
self.assertEquals(line.price_unit, -2)
self.assertEquals(line.quantity, 1)
else:
self.assertEquals(line.product_id.id, self.carrot_id)
self.assertEquals(line.account_analytic_id.id, self.carrot_account_id)
self.assertEquals(line.price_unit, -3)
self.assertEquals(line.quantity, 2)
......@@ -40,6 +40,7 @@ class final_invoice_create(osv.osv_memory):
'name': fields.boolean('Log of Activity', help='Display detail of work in the invoice line.'),
'price': fields.boolean('Cost', help='Display cost of the item you reinvoice'),
'product': fields.many2one('product.product', 'Product', help='The product that will be used to invoice the remaining amount'),
'group_by_partner': fields.boolean('Group by Partner', help="If this box is checked, the system will group invoices by customer."),
}
def do_create(self, cr, uid, ids, context=None):
......
......@@ -34,6 +34,7 @@ class hr_timesheet_invoice_create(osv.osv_memory):
'name': fields.boolean('Description', help='The detail of each work done will be displayed on the invoice'),
'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'),
'product': fields.many2one('product.product', 'Force Product', help='Fill 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.'),
'group_by_partner': fields.boolean('Group by Partner', help='If this box is checked, the system will group invoices by customer.'),
}
_defaults = {
......@@ -51,7 +52,7 @@ class hr_timesheet_invoice_create(osv.osv_memory):
@param context: A standard dictionary for contextual values
"""
analytic_obj = self.pool.get('account.analytic.line')
data = context and context.get('active_ids', [])
data = context and context.get('active_ids', []) or []
for analytic in analytic_obj.browse(cr, uid, data, context=context):
if analytic.invoice_id:
raise UserError(_("Invoice is already linked to some of the analytic line(s)!"))
......
......@@ -26,7 +26,7 @@
<field name="model">hr.timesheet.invoice.create.final</field>
<field name="arch" type="xml">
<form string="Invoice contract">
<p>Do you want to show details of each activity to your customer?</p>
<p>Show details in invoice lines</p>
<group>
<group>
<field name="date"/>
......@@ -35,9 +35,12 @@
<field name="name"/>
<field name="price"/>
</group>
</group>
<group string="Force to use a special product" groups="base.group_no_one">
<field name="product"/>
<group string="Force to use a special product" groups="base.group_no_one">
<field name="product"/>
</group>
<group string="Create one invoice by partner">
<field name="group_by_partner"/>
</group>
</group>
<footer>
<button name="do_create" string="Create Invoice" type="object" class="oe_highlight"/>
......
......@@ -8,7 +8,7 @@
<field name="arch" type="xml">
<form string="Create Invoice">
<group cols="2">
<group string="Do you want to show details of work in invoice?" colspan="1" cols="2">
<group string="Show details in invoice lines" colspan="1" cols="2">
<field name="date"/>
<field name="time"/>
<field name="name"/>
......@@ -18,6 +18,9 @@
<p class="oe_grey" colspan="2">When reinvoicing costs, the amount on the invoice lines is given by the sale price of the corresponding product (if any, and if its sale price is not 0). You can use the following field to enforce the use of a single product for all the chosen lines in the future invoices.</p>
<field name="product"/>
</group>
<group string="Create one invoice by partner">
<field name="group_by_partner"/>
</group>
</group>
<footer>
<button name="do_create" string="Create Invoices" type="object" class="oe_highlight"/>
......
......@@ -76,7 +76,7 @@
<field name="name">Invoice Tasks</field>
<field name="res_model">account.analytic.line</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="view_mode">tree,form,pivot</field>
<field name="domain">[]</field>
<field name="context">{'search_default_to_invoice': 1}</field>
<field name="view_id" ref="view_account_analytic_line_tree_inherit_account_id"/>
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment