diff --git a/addons/account/models/account.py b/addons/account/models/account.py
index 45273bfbca8..e2f8e6eaeaa 100644
--- a/addons/account/models/account.py
+++ b/addons/account/models/account.py
@@ -745,7 +745,12 @@ class AccountTax(models.Model):
if not round_tax:
prec += 5
- total_excluded = total_included = base = round(price_unit * quantity, prec)
+
+ base_values = self.env.context.get('base_values')
+ if not base_values:
+ total_excluded = total_included = base = round(price_unit * quantity, prec)
+ else:
+ total_excluded, total_included, base = base_values
# Sorting key is mandatory in this case. When no key is provided, sorted() will perform a
# search. However, the search method is overridden in account.tax in order to add a domain
@@ -753,9 +758,10 @@ class AccountTax(models.Model):
# case of group taxes.
for tax in self.sorted(key=lambda r: r.sequence):
if tax.amount_type == 'group':
- ret = tax.children_tax_ids.compute_all(price_unit, currency, quantity, product, partner)
+ children = tax.children_tax_ids.with_context(base_values=(total_excluded, total_included, base))
+ ret = children.compute_all(price_unit, currency, quantity, product, partner)
total_excluded = ret['total_excluded']
- base = ret['base']
+ base = ret['base'] if tax.include_base_amount else base
total_included = ret['total_included']
tax_amount = total_included - total_excluded
taxes += ret['taxes']
diff --git a/addons/account/models/account_bank_statement.py b/addons/account/models/account_bank_statement.py
index 204f0161a85..931ae7f41df 100644
--- a/addons/account/models/account_bank_statement.py
+++ b/addons/account/models/account_bank_statement.py
@@ -472,7 +472,7 @@ class AccountBankStatementLine(models.Model):
'details': {
'name': _("Automatically reconciled items"),
'model': 'account.move',
- 'ids': automatic_reconciliation_entries.ids
+ 'ids': automatic_reconciliation_entries.mapped('journal_entry_ids').ids
}
}]
return {
diff --git a/addons/account/models/account_invoice.py b/addons/account/models/account_invoice.py
index 4e8e5c9c0e4..efbbeb17011 100644
--- a/addons/account/models/account_invoice.py
+++ b/addons/account/models/account_invoice.py
@@ -612,6 +612,14 @@ class AccountInvoice(models.Model):
'account_analytic_id': tax['analytic'] and line.account_analytic_id.id or False,
'account_id': self.type in ('out_invoice', 'in_invoice') and (tax['account_id'] or line.account_id.id) or (tax['refund_account_id'] or line.account_id.id),
}
+
+ # If the taxes generate moves on the same financial account as the invoice line,
+ # propagate the analytic account from the invoice line to the tax line.
+ # This is necessary in situations were (part of) the taxes cannot be reclaimed,
+ # to ensure the tax move is allocated to the proper analytic account.
+ if not vals.get('account_analytic_id') and line.account_analytic_id and vals['account_id'] == line.account_id.id:
+ vals['account_analytic_id'] = line.account_analytic_id.id
+
return vals
@api.multi
@@ -622,14 +630,6 @@ class AccountInvoice(models.Model):
taxes = line.invoice_line_tax_ids.compute_all(price_unit, self.currency_id, line.quantity, line.product_id, self.partner_id)['taxes']
for tax in taxes:
val = self._prepare_tax_line_vals(line, tax)
-
- # If the taxes generate moves on the same financial account as the invoice line,
- # propagate the analytic account from the invoice line to the tax line.
- # This is necessary in situations were (part of) the taxes cannot be reclaimed,
- # to ensure the tax move is allocated to the proper analytic account.
- if not val.get('account_analytic_id') and line.account_analytic_id and val['account_id'] == line.account_id.id:
- val['account_analytic_id'] = line.account_analytic_id.id
-
key = self.env['account.tax'].browse(tax['id']).get_grouping_key(val)
if key not in tax_grouped:
@@ -1350,15 +1350,17 @@ class AccountInvoiceTax(models.Model):
for invoice in self.mapped('invoice_id'):
tax_grouped[invoice.id] = invoice.get_taxes_values()
for tax in self:
- key = self.env['account.tax'].browse(tax.tax_id.id).get_grouping_key({
- 'tax_id': tax.tax_id.id,
- 'account_id': tax.account_id.id,
- 'account_analytic_id': tax.account_analytic_id.id,
- })
- if tax.invoice_id and key in tax_grouped[tax.invoice_id.id]:
- tax.base = tax_grouped[tax.invoice_id.id][key]['base']
- else:
- _logger.warning('Tax Base Amount not computable probably due to a change in an underlying tax (%s).', tax.tax_id.name)
+ tax.base = 0.0
+ if tax.tax_id:
+ key = tax.tax_id.get_grouping_key({
+ 'tax_id': tax.tax_id.id,
+ 'account_id': tax.account_id.id,
+ 'account_analytic_id': tax.account_analytic_id.id,
+ })
+ if tax.invoice_id and key in tax_grouped[tax.invoice_id.id]:
+ tax.base = tax_grouped[tax.invoice_id.id][key]['base']
+ else:
+ _logger.warning('Tax Base Amount not computable probably due to a change in an underlying tax (%s).', tax.tax_id.name)
invoice_id = fields.Many2one('account.invoice', string='Invoice', ondelete='cascade', index=True)
name = fields.Char(string='Tax Description', required=True)
diff --git a/addons/account/models/account_move.py b/addons/account/models/account_move.py
index 64a5c538841..01d3b8199f9 100644
--- a/addons/account/models/account_move.py
+++ b/addons/account/models/account_move.py
@@ -981,6 +981,9 @@ class AccountMoveLine(models.Model):
return True
rec_move_ids = self.env['account.partial.reconcile']
for account_move_line in self:
+ for invoice in account_move_line.payment_id.invoice_ids:
+ if account_move_line in invoice.payment_move_line_ids:
+ account_move_line.payment_id.write({'invoice_ids': [(3, invoice.id, None)]})
rec_move_ids += account_move_line.matched_debit_ids
rec_move_ids += account_move_line.matched_credit_ids
return rec_move_ids.unlink()
diff --git a/addons/account/report/account_invoice_report.py b/addons/account/report/account_invoice_report.py
index 9d0b3d3da27..eb969cadec4 100644
--- a/addons/account/report/account_invoice_report.py
+++ b/addons/account/report/account_invoice_report.py
@@ -45,7 +45,7 @@ class AccountInvoiceReport(models.Model):
user_currency_price_total = fields.Float(string="Total Without Tax", compute='_compute_amounts_in_user_currency', digits=0)
price_average = fields.Float(string='Average Price', readonly=True, group_operator="avg")
user_currency_price_average = fields.Float(string="Average Price", compute='_compute_amounts_in_user_currency', digits=0)
- currency_rate = fields.Float(string='Currency Rate', readonly=True)
+ currency_rate = fields.Float(string='Currency Rate', readonly=True, group_operator="avg")
nbr = fields.Integer(string='# of Lines', readonly=True) # TDE FIXME master: rename into nbr_lines
type = fields.Selection([
('out_invoice', 'Customer Invoice'),
diff --git a/addons/account/tests/test_tax.py b/addons/account/tests/test_tax.py
index d626f4e20e0..84aa784fad3 100644
--- a/addons/account/tests/test_tax.py
+++ b/addons/account/tests/test_tax.py
@@ -43,10 +43,46 @@ class TestTax(AccountTestUsers):
(4, self.percent_tax.id, 0)
]
})
+ self.group_tax_bis = self.tax_model.create({
+ 'name': "Group tax bis",
+ 'amount_type': 'group',
+ 'amount': 0,
+ 'sequence': 6,
+ 'children_tax_ids': [
+ (4, self.fixed_tax.id, 0),
+ (4, self.percent_tax.id, 0)
+ ]
+ })
+ self.group_of_group_tax = self.tax_model.create({
+ 'name': "Group of group tax",
+ 'amount_type': 'group',
+ 'amount': 0,
+ 'sequence': 7,
+ 'children_tax_ids': [
+ (4, self.group_tax.id, 0),
+ (4, self.group_tax_bis.id, 0)
+ ]
+ })
self.bank_journal = self.env['account.journal'].search([('type', '=', 'bank'), ('company_id', '=', self.account_manager.company_id.id)])[0]
self.bank_account = self.bank_journal.default_debit_account_id
self.expense_account = self.env['account.account'].search([('user_type_id.type', '=', 'payable')], limit=1) #Should be done by onchange later
+ def test_tax_group_of_group_tax(self):
+ self.fixed_tax.include_base_amount = True
+ self.group_tax.include_base_amount = True
+ self.group_of_group_tax.include_base_amount = True
+ res = self.group_of_group_tax.compute_all(200.0)
+ self.assertEquals(res['total_excluded'], 200.0)
+ # After calculation of first group
+ # base = 210
+ # total_included = 231
+ # Base of the first grouped is passed
+ # Base after the second group (220) is dropped.
+ # Base of the group of groups is passed out,
+ # so we obtain base as after first group
+ self.assertEquals(res['base'], 210.0)
+ self.assertEquals(res['total_included'], 263.0)
+
def test_tax_group(self):
res = self.group_tax.compute_all(200.0)
self.assertEquals(res['total_excluded'], 200.0)
diff --git a/addons/account/views/account_invoice_view.xml b/addons/account/views/account_invoice_view.xml
index 568bdead85d..e1da859f9d0 100644
--- a/addons/account/views/account_invoice_view.xml
+++ b/addons/account/views/account_invoice_view.xml
@@ -135,8 +135,8 @@
Use in conjunction with contracts to calculate your monthly revenue for multi-month contracts.
- Install Now diff --git a/addons/board/static/src/js/dashboard.js b/addons/board/static/src/js/dashboard.js index 87ff74f826a..3575f36f655 100644 --- a/addons/board/static/src/js/dashboard.js +++ b/addons/board/static/src/js/dashboard.js @@ -139,7 +139,11 @@ var DashBoard = form_common.FormWidget.extend({ }, on_close_action: function(e) { if (confirm(_t("Are you sure you want to remove this item ?"))) { - $(e.currentTarget).parents('.oe_action:first').remove(); + var $container = $(e.currentTarget).parents('.oe_action:first'); + var am = _.findWhere(this.action_managers, { am_id: $container.data('am_id') }); + am.destroy(); + this.action_managers.splice(_.indexOf(this.action_managers, am), 1); + $container.remove(); this.do_save_dashboard(); } }, @@ -223,7 +227,13 @@ var DashBoard = form_common.FormWidget.extend({ var am = new ActionManager(this), // FIXME: ideally the dashboard view shall be refactored like kanban. $action = $('#' + this.view.element_id + '_action_' + index); - $action.parent().data('action_attrs', action_attrs); + var $action_container = $action.closest('.oe_action'); + var am_id = _.uniqueId('action_manager_'); + am.am_id = am_id; + $action_container.data({ + action_attrs: action_attrs, + am_id: am_id, + }); this.action_managers.push(am); am.appendTo($action).then(function () { am.do_action(action).then(function () { diff --git a/addons/hr_expense/models/hr_expense.py b/addons/hr_expense/models/hr_expense.py index fa4c6498366..2756b0ebd42 100644 --- a/addons/hr_expense/models/hr_expense.py +++ b/addons/hr_expense/models/hr_expense.py @@ -171,79 +171,71 @@ class HrExpense(models.Model): ''' main function that is called when trying to create the accounting entries related to an expense ''' - journal_dict = {} for expense in self: - acc_date = expense.sheet_id.accounting_date or fields.Date.context_today(self) - jrn = expense.sheet_id.bank_journal_id if expense.payment_mode == 'company_account' else expense.sheet_id.journal_id - journal_dict.setdefault(jrn, self.env['hr.expense']) - journal_dict[jrn] |= expense - - for journal, expense_list in journal_dict.items(): + journal = expense.sheet_id.bank_journal_id if expense.payment_mode == 'company_account' else expense.sheet_id.journal_id #create the move that will contain the accounting entries - # reference if the same for every expense - ref = len(expense_list.mapped('employee_id.address_home_id.ref')) == 1 and expense_list[0].employee_id.address_home_id.ref or False + acc_date = expense.sheet_id.accounting_date or expense.date move = self.env['account.move'].create({ 'journal_id': journal.id, 'company_id': self.env.user.company_id.id, 'date': acc_date, - 'ref': ref, + 'ref': expense.sheet_id.name, # force the name to the default value, to avoid an eventual 'default_name' in the context # to set it to '' which cause no number to be given to the account.move when posted. 'name': '/', }) - for expense in expense_list: - company_currency = expense.company_id.currency_id - diff_currency_p = expense.currency_id != company_currency - #one account.move.line per expense (+taxes..) - move_lines = expense._move_line_get() + company_currency = expense.company_id.currency_id + diff_currency_p = expense.currency_id != company_currency + #one account.move.line per expense (+taxes..) + move_lines = expense._move_line_get() - #create one more move line, a counterline for the total on payable account - payment_id = False - total, total_currency, move_lines = expense._compute_expense_totals(company_currency, move_lines, acc_date) - if expense.payment_mode == 'company_account': - if not expense.sheet_id.bank_journal_id.default_credit_account_id: - raise UserError(_("No credit account found for the %s journal, please configure one.") % (expense.sheet_id.bank_journal_id.name)) - emp_account = expense.sheet_id.bank_journal_id.default_credit_account_id.id - journal = expense.bank_journal_id - #create payment - payment_methods = (total < 0) and journal.outbound_payment_method_ids or journal.inbound_payment_method_ids - journal_currency = journal.currency_id or journal.company_id.currency_id - payment = self.env['account.payment'].create({ - 'payment_method_id': payment_methods and payment_methods[0].id or False, - 'payment_type': total < 0 and 'outbound' or 'inbound', - 'partner_id': expense.employee_id.address_home_id.commercial_partner_id.id, - 'partner_type': 'supplier', - 'journal_id': journal.id, - 'payment_date': expense.date, - 'state': 'reconciled', - 'currency_id': diff_currency_p and expense.currency_id.id or journal_currency.id, - 'amount': diff_currency_p and abs(total_currency) or abs(total), - 'name': expense.name, + #create one more move line, a counterline for the total on payable account + payment_id = False + total, total_currency, move_lines = expense._compute_expense_totals(company_currency, move_lines, acc_date) + if expense.payment_mode == 'company_account': + if not expense.sheet_id.bank_journal_id.default_credit_account_id: + raise UserError(_("No credit account found for the %s journal, please configure one.") % (expense.sheet_id.bank_journal_id.name)) + emp_account = expense.sheet_id.bank_journal_id.default_credit_account_id.id + journal = expense.sheet_id.bank_journal_id + #create payment + payment_methods = (total < 0) and journal.outbound_payment_method_ids or journal.inbound_payment_method_ids + journal_currency = journal.currency_id or journal.company_id.currency_id + payment = self.env['account.payment'].create({ + 'payment_method_id': payment_methods and payment_methods[0].id or False, + 'payment_type': total < 0 and 'outbound' or 'inbound', + 'partner_id': expense.employee_id.address_home_id.commercial_partner_id.id, + 'partner_type': 'supplier', + 'journal_id': journal.id, + 'payment_date': expense.date, + 'state': 'reconciled', + 'currency_id': diff_currency_p and expense.currency_id.id or journal_currency.id, + 'amount': diff_currency_p and abs(total_currency) or abs(total), + 'name': expense.name, + }) + payment_id = payment.id + else: + if not expense.employee_id.address_home_id: + raise UserError(_("No Home Address found for the employee %s, please configure one.") % (expense.employee_id.name)) + emp_account = expense.employee_id.address_home_id.property_account_payable_id.id + + move_lines.append({ + 'type': 'dest', + 'name': expense.employee_id.name, + 'price': total, + 'account_id': emp_account, + 'date_maturity': acc_date, + 'amount_currency': diff_currency_p and total_currency or False, + 'currency_id': diff_currency_p and expense.currency_id.id or False, + 'payment_id': payment_id, }) - payment_id = payment.id - else: - if not expense.employee_id.address_home_id: - raise UserError(_("No Home Address found for the employee %s, please configure one.") % (expense.employee_id.name)) - emp_account = expense.employee_id.address_home_id.property_account_payable_id.id - move_lines.append({ - 'type': 'dest', - 'name': expense.employee_id.name, - 'price': total, - 'account_id': emp_account, - 'date_maturity': acc_date, - 'amount_currency': diff_currency_p and total_currency or False, - 'currency_id': diff_currency_p and expense.currency_id.id or False, - 'payment_id': payment_id, - }) - - #convert eml into an osv-valid format - lines = map(lambda x: (0, 0, expense._prepare_move_line(x)), move_lines) - move.write({'line_ids': lines}) - expense.sheet_id.write({'account_move_id': move.id}) - if expense.payment_mode == 'company_account': - expense.sheet_id.paid_expense_sheets() + #convert eml into an osv-valid format + lines = map(lambda x: (0, 0, expense._prepare_move_line(x)), move_lines) + move.write({'line_ids': lines}) + expense.sheet_id.write({'account_move_id': move.id}) move.post() + if expense.payment_mode == 'company_account': + expense.sheet_id.paid_expense_sheets() return True @api.multi @@ -526,3 +518,11 @@ class HrExpenseSheet(models.Model): res['domain'] = [('res_model', '=', 'hr.expense'), ('res_id', 'in', self.expense_line_ids.ids)] res['context'] = {'default_res_model': 'hr.expense.sheet', 'default_res_id': self.id} return res + + @api.multi + def action_open_journal_entries(self): + res = self.env['ir.actions.act_window'].for_xml_id('account', 'action_move_journal_line') + refs = [x.name for x in self] + res['domain'] = [('ref', 'in', refs)] + res['context'] = {} + return res diff --git a/addons/hr_expense/views/hr_expense_views.xml b/addons/hr_expense/views/hr_expense_views.xml index 255778731b8..5088ff3b571 100644 --- a/addons/hr_expense/views/hr_expense_views.xml +++ b/addons/hr_expense/views/hr_expense_views.xml @@ -422,6 +422,13 @@ type="object">