diff --git a/addons/account/models/account.py b/addons/account/models/account.py index 50388f30f49..19842d28fe5 100644 --- a/addons/account/models/account.py +++ b/addons/account/models/account.py @@ -177,6 +177,16 @@ class AccountAccount(models.Model): def mark_as_reconciled(self): return self.write({'last_time_entries_checked': time.strftime(DEFAULT_SERVER_DATETIME_FORMAT)}) + @api.multi + def action_open_reconcile(self): + # Open reconciliation view for this account + action_context = {'show_mode_selector': False, 'account_ids': [self.id,]} + return { + 'type': 'ir.actions.client', + 'tag': 'manual_reconciliation_view', + 'context': action_context, + } + class AccountJournal(models.Model): _name = "account.journal" diff --git a/addons/account/models/account_invoice.py b/addons/account/models/account_invoice.py index 763290d4619..a47ebeb8a2d 100644 --- a/addons/account/models/account_invoice.py +++ b/addons/account/models/account_invoice.py @@ -1162,7 +1162,7 @@ class AccountInvoiceLine(models.Model): if not self.product_id: fpos = self.invoice_id.fiscal_position_id self.invoice_line_tax_ids = fpos.map_tax(self.account_id.tax_ids).ids - else: + elif not self.price_unit: self._set_taxes() @api.onchange('uom_id') diff --git a/addons/account/models/account_move.py b/addons/account/models/account_move.py index 3564ba348b7..b59e43a152e 100644 --- a/addons/account/models/account_move.py +++ b/addons/account/models/account_move.py @@ -213,6 +213,10 @@ class AccountMove(models.Model): return [x.id for x in reversed_moves] return True + @api.multi + def open_reconcile_view(self): + return self.line_ids.open_reconcile_view() + class AccountMoveLine(models.Model): _name = "account.move.line" @@ -421,6 +425,7 @@ class AccountMoveLine(models.Model): # Note : this short-circuiting is better for performances, but also required # since postgresql doesn't implement empty list (so 'AND id in ()' is useless) return [] + res_ids = res_ids and tuple(res_ids) assert res_type in ('partner', 'account') assert account_type in ('payable', 'receivable', None) @@ -447,21 +452,22 @@ class AccountMoveLine(models.Model): {3} {4} {5} + AND l.company_id = {6} AND EXISTS ( SELECT NULL FROM account_move_line l WHERE l.account_id = a.id - {6} + {7} AND l.amount_residual > 0 ) AND EXISTS ( SELECT NULL FROM account_move_line l WHERE l.account_id = a.id - {6} + {7} AND l.amount_residual < 0 ) - GROUP BY {7} a.id, a.name, a.code, {res_alias}.last_time_entries_checked + GROUP BY {8} a.id, a.name, a.code, {res_alias}.last_time_entries_checked ORDER BY {res_alias}.last_time_entries_checked ) as s WHERE (last_time_entries_checked IS NULL OR max_date > last_time_entries_checked) @@ -472,6 +478,7 @@ class AccountMoveLine(models.Model): is_partner and ' ' or "AND at.type <> 'payable' AND at.type <> 'receivable'", account_type and "AND at.type = %(account_type)s" or '', res_ids and 'AND ' + res_alias + '.id in %(res_ids)s' or '', + self.env.user.company_id.id, is_partner and 'AND l.partner_id = p.id' or ' ', is_partner and 'l.partner_id, p.id,' or ' ', res_alias=res_alias @@ -1141,6 +1148,18 @@ class AccountMoveLine(models.Model): tables, where_clause, where_clause_params = query.get_sql() return tables, where_clause, where_clause_params + @api.multi + def open_reconcile_view(self): + model, action_id = self.pool['ir.model.data'].get_object_reference(self._cr, self._uid, 'account', "action_account_moves_all_a") + action = self.pool[model].read(self._cr, self._uid, action_id, context=self._context) + ids = [] + for aml in self: + if aml.account_id.reconcile: + ids.extend([r.debit_move_id.id for r in aml.matched_debit_ids] if aml.credit > 0 else [r.credit_move_id.id for r in aml.matched_credit_ids]) + ids.append(aml.id) + action['domain'] = [('id', 'in', ids)] + return action + class AccountPartialReconcile(models.Model): _name = "account.partial.reconcile" diff --git a/addons/account/models/chart_template.py b/addons/account/models/chart_template.py index 1d0ec80306c..c094bef3db4 100644 --- a/addons/account/models/chart_template.py +++ b/addons/account/models/chart_template.py @@ -749,7 +749,7 @@ class WizardMultiChartsAccounts(models.TransientModel): # Create the current year earning account (outside of the CoA) self.env['account.account'].create({ - 'code': '9999', + 'code': '999999', 'name': _('Undistributed Profits/Losses'), 'user_type_id': self.env.ref("account.data_unaffected_earnings").id, 'company_id': company.id,}) diff --git a/addons/account/models/partner.py b/addons/account/models/partner.py index 2bde6466a4c..88850c2153c 100644 --- a/addons/account/models/partner.py +++ b/addons/account/models/partner.py @@ -306,7 +306,8 @@ class ResPartner(models.Model): # generate where clause to include multicompany rules where_query = account_invoice_report._where_calc([ - ('partner_id', 'in', all_partner_ids), ('state', 'not in', ['draft', 'cancel']), ('company_id', '=', self.env.user.company_id.id) + ('partner_id', 'in', all_partner_ids), ('state', 'not in', ['draft', 'cancel']), ('company_id', '=', self.env.user.company_id.id), + ('type', 'in', ('out_invoice', 'out_refund')) ]) account_invoice_report._apply_ir_rules(where_query, 'read') from_clause, where_clause, where_clause_params = where_query.get_sql() diff --git a/addons/account/tests/__init__.py b/addons/account/tests/__init__.py index e4a6224852d..01e519c02cf 100644 --- a/addons/account/tests/__init__.py +++ b/addons/account/tests/__init__.py @@ -12,6 +12,7 @@ from . import test_chart_of_account from . import test_fiscal_position from . import test_manual_reconciliation from . import test_payment +from . import test_product_id_change from . import test_reconciliation from . import test_search from . import test_tax diff --git a/addons/account/tests/test_product_id_change.py b/addons/account/tests/test_product_id_change.py new file mode 100644 index 00000000000..a2008b64bc7 --- /dev/null +++ b/addons/account/tests/test_product_id_change.py @@ -0,0 +1,91 @@ +from openerp.addons.account.tests.account_test_classes import AccountingTestCase +import time + +class TestProductIdChange(AccountingTestCase): + """Test that when an included tax is mapped by a fiscal position, the included tax must be + subtracted to the price of the product. + """ + + def setUp(self): + super(TestProductIdChange, self).setUp() + self.invoice_model = self.env['account.invoice'] + self.fiscal_position_model = self.env['account.fiscal.position'] + self.fiscal_position_tax_model = self.env['account.fiscal.position.tax'] + self.tax_model = self.env['account.tax'] + self.pricelist_model = self.env['product.pricelist'] + self.res_partner_model = self.env['res.partner'] + self.product_tmpl_model = self.env['product.template'] + self.product_model = self.env['product.product'] + self.invoice_line_model = self.env['account.invoice.line'] + self.account_receivable = self.env['account.account'].search([('user_type_id', '=', self.env.ref('account.data_account_type_receivable').id)], limit=1) + self.account_revenue = self.env['account.account'].search([('user_type_id', '=', self.env.ref('account.data_account_type_revenue').id)], limit=1) + + def test_product_id_change(self): + partner = self.res_partner_model.create(dict(name="George")) + tax_include_sale = self.tax_model.create(dict(name="Include tax", + type_tax_use='sale', + amount='21.00', + price_include=True)) + tax_include_purchase = self.tax_model.create(dict(name="Include tax", + type_tax_use='purchase', + amount='21.00', + price_include=True)) + tax_exclude_sale = self.tax_model.create(dict(name="Exclude tax", + type_tax_use='sale', + amount='0.00')) + tax_exclude_purchase = self.tax_model.create(dict(name="Exclude tax", + type_tax_use='purchase', + amount='0.00')) + product_tmpl = self.product_tmpl_model.create(dict(name="Voiture", + list_price='121', + standard_price='121', + taxes_id=[(6, 0, [tax_include_sale.id])], + supplier_taxes_id=[(6, 0, [tax_include_purchase.id])])) + product = self.product_model.create(dict(product_tmpl_id=product_tmpl.id)) + fp = self.fiscal_position_model.create(dict(name="fiscal position", sequence=1)) + fp_tax_sale = self.fiscal_position_tax_model.create(dict(position_id=fp.id, + tax_src_id=tax_include_sale.id, + tax_dest_id=tax_exclude_sale.id)) + fp_tax_purchase = self.fiscal_position_tax_model.create(dict(position_id=fp.id, + tax_src_id=tax_include_purchase.id, + tax_dest_id=tax_exclude_purchase.id)) + + out_invoice = self.invoice_model.create({ + 'partner_id': partner.id, + 'reference_type': 'none', + 'name': 'invoice to client', + 'account_id': self.account_receivable.id, + 'type': 'out_invoice', + 'date_invoice': time.strftime('%Y') + '-06-26', + 'fiscal_position_id': fp.id, + }) + out_line = self.invoice_line_model.create({ + 'product_id': product.id, + 'quantity': 1, + 'price_unit': 121.0, + 'invoice_id': out_invoice.id, + 'name': 'something out', + 'account_id': self.account_revenue.id, + }) + + in_invoice = self.invoice_model.create({ + 'partner_id': partner.id, + 'reference_type': 'none', + 'name': 'invoice to supplier', + 'account_id': self.account_receivable.id, + 'type': 'in_invoice', + 'date_invoice': time.strftime('%Y') + '-06-26', + 'fiscal_position_id': fp.id, + }) + in_line = self.invoice_line_model.create({ + 'product_id': product.id, + 'quantity': 1, + 'price_unit': 121.0, + 'invoice_id': in_invoice.id, + 'name': 'something in', + 'account_id': self.account_revenue.id, + }) + out_line._onchange_product_id() + self.assertEquals(100, out_line.price_unit, "The included tax must be subtracted to the price") + in_line._onchange_product_id() + self.assertEquals(100, in_line.price_unit, "The included tax must be subtracted to the price") diff --git a/addons/account/views/account_view.xml b/addons/account/views/account_view.xml index 1b6249c4cdb..be22f4e63d7 100644 --- a/addons/account/views/account_view.xml +++ b/addons/account/views/account_view.xml @@ -21,7 +21,11 @@ - +