From f2237b71bcf42f43e27fa77669a0e703c6960c84 Mon Sep 17 00:00:00 2001 From: Carlos Dauden Date: Mon, 29 May 2017 18:28:21 +0200 Subject: [PATCH] [9.0][IMP] partner_financial_risk: Improve performance (#385) * [9.0][IMP] partner_financial_risk: Improve performance * [9.0][IMP] partner_financial_risk: Improve tests and partner hierarchy * [9.0][IMP] partner_stock_risk: Test --- .../models/account_invoice.py | 2 +- partner_financial_risk/models/res_partner.py | 69 ++++++++++------ .../tests/test_partner_financial_risk.py | 80 +++++++++++++------ 3 files changed, 102 insertions(+), 49 deletions(-) diff --git a/partner_financial_risk/models/account_invoice.py b/partner_financial_risk/models/account_invoice.py index a67fff0c2..30714f102 100644 --- a/partner_financial_risk/models/account_invoice.py +++ b/partner_financial_risk/models/account_invoice.py @@ -13,7 +13,7 @@ class AccountInvoice(models.Model): if self.env.context.get('bypass_risk', False): return self.signal_workflow('invoice_open') for invoice in self: - partner = invoice.partner_id + partner = invoice.partner_id.commercial_partner_id exception_msg = "" if partner.risk_exception: exception_msg = _("Financial risk exceeded.\n") diff --git a/partner_financial_risk/models/res_partner.py b/partner_financial_risk/models/res_partner.py index 6fd92e5a2..6f1dd6f34 100644 --- a/partner_financial_risk/models/res_partner.py +++ b/partner_financial_risk/models/res_partner.py @@ -62,32 +62,54 @@ class ResPartner(models.Model): is_editable = self.env.user.has_group( 'base.group_sale_manager') or self.env.user.has_group( 'account.group_account_manager') - for partner in self: + for partner in self.filtered('customer'): partner.risk_allow_edit = is_editable @api.multi @api.depends('invoice_ids', 'invoice_ids.state', 'invoice_ids.amount_total', 'invoice_ids.residual', - 'invoice_ids.company_id.invoice_unpaid_margin') + 'invoice_ids.company_id.invoice_unpaid_margin', + 'child_ids.invoice_ids', 'child_ids.invoice_ids.state', + 'child_ids.invoice_ids.amount_total', + 'child_ids.invoice_ids.residual', + 'child_ids.invoice_ids.company_id.invoice_unpaid_margin') def _compute_risk_invoice(self): + def sum_group(group, field): + return sum([x[field] for x in group if + x['partner_id'][0] in partner_ids]) + customers = self.filtered('customer') + if not customers: + return # pragma: no cover max_date = self._max_risk_date_due() - for partner in self: - invoices_out = partner.invoice_ids.filtered( - lambda x: x.type in ['out_invoice', 'out_refund']) - invoices = invoices_out.filtered( - lambda x: x.state in ['draft', 'proforma', 'proforma2']) - partner.risk_invoice_draft = sum(invoices.mapped('amount_total')) - invoices = invoices_out.filtered( - lambda x: x.state == 'open' and x.date_due >= max_date) - partner.risk_invoice_open = sum(invoices.mapped('residual')) - invoices = invoices_out.filtered( - lambda x: x.state == 'open' and x.date_due < max_date) - partner.risk_invoice_unpaid = sum(invoices.mapped('residual')) + AccountInvoice = self.env['account.invoice'] + partners = customers | customers.mapped('child_ids') + domain = [('type', 'in', ['out_invoice', 'out_refund']), + ('partner_id', 'in', partners.ids)] + draft_group = AccountInvoice.read_group( + domain + [('state', 'in', ['draft', 'proforma', 'proforma2'])], + ['partner_id', 'amount_total'], + ['partner_id']) + open_group = AccountInvoice.read_group( + domain + [('state', '=', 'open'), ('date_due', '>=', max_date)], + ['partner_id', 'residual'], + ['partner_id']) + unpaid_group = AccountInvoice.read_group( + domain + [('state', '=', 'open'), '|', + ('date_due', '=', False), ('date_due', '<', max_date)], + ['partner_id', 'residual'], + ['partner_id']) + for partner in customers: + partner_ids = (partner | partner.child_ids).ids + partner.risk_invoice_draft = sum_group(draft_group, 'amount_total') + partner.risk_invoice_open = sum_group(open_group, 'residual') + partner.risk_invoice_unpaid = sum_group(unpaid_group, 'residual') @api.multi - @api.depends('credit', 'risk_invoice_open', 'risk_invoice_unpaid') + @api.depends('credit', 'risk_invoice_open', 'risk_invoice_unpaid', + 'child_ids.credit', 'child_ids.risk_invoice_open', + 'child_ids.risk_invoice_unpaid') def _compute_risk_account_amount(self): - for partner in self: + for partner in self.filtered('customer'): partner.risk_account_amount = ( partner.credit - partner.risk_invoice_open - partner.risk_invoice_unpaid) @@ -96,7 +118,7 @@ class ResPartner(models.Model): @api.depends(lambda x: x._get_depends_compute_risk_exception()) def _compute_risk_exception(self): risk_field_list = self._risk_field_list() - for partner in self: + for partner in self.filtered('customer'): amount = 0.0 for risk_field in risk_field_list: field_value = getattr(partner, risk_field[0], 0.0) @@ -106,7 +128,7 @@ class ResPartner(models.Model): if getattr(partner, risk_field[2], False): amount += field_value partner.risk_total = amount - if amount > partner.credit_limit: + if partner.credit_limit and amount > partner.credit_limit: partner.risk_exception = True @api.model @@ -129,12 +151,11 @@ class ResPartner(models.Model): @api.model def _get_depends_compute_risk_exception(self): - # TODO: Improve code without performance loss - tuple_list = self._risk_field_list() - res = [x[0] for x in tuple_list] - res.extend([x[1] for x in tuple_list]) - res.extend([x[2] for x in tuple_list]) - res.append('credit_limit') + res = [] + for x in self._risk_field_list(): + res.extend((x[0], x[1], x[2], 'child_ids.%s' % x[0], + 'child_ids.%s' % x[1], 'child_ids.%s' % x[2])) + res.extend(('credit_limit', 'child_ids.credit_limit')) return res @api.model diff --git a/partner_financial_risk/tests/test_partner_financial_risk.py b/partner_financial_risk/tests/test_partner_financial_risk.py index f1bfe7130..d03a93641 100644 --- a/partner_financial_risk/tests/test_partner_financial_risk.py +++ b/partner_financial_risk/tests/test_partner_financial_risk.py @@ -2,54 +2,86 @@ # © 2016 Carlos Dauden # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from openerp.tests.common import TransactionCase +from openerp.tests.common import SavepointCase from openerp import fields -class TestPartnerFinancialRisk(TransactionCase): - def setUp(self): - super(TestPartnerFinancialRisk, self).setUp() - self.env.user.groups_id |= self.env.ref('base.group_sale_manager') - self.partner = self.env['res.partner'].create({ +class TestPartnerFinancialRisk(SavepointCase): + @classmethod + def setUpClass(cls): + super(TestPartnerFinancialRisk, cls).setUpClass() + cls.env.user.groups_id |= cls.env.ref('base.group_sale_manager') + cls.partner = cls.env['res.partner'].create({ 'name': 'Partner test', 'customer': True, }) - self.journal = self.env['account.journal'].create({ + cls.invoice_address = cls.env['res.partner'].create({ + 'name': 'Partner test invoice', + 'parent_id': cls.partner.id, + 'type': 'invoice', + }) + type_revenue = cls.env.ref('account.data_account_type_revenue') + type_payable = cls.env.ref('account.data_account_type_payable') + tax_group_taxes = cls.env.ref('account.tax_group_taxes') + cls.account_sale = cls.env['account.account'].create({ + 'name': 'Sale', + 'code': 'XX_700', + 'user_type_id': type_revenue.id, + 'reconcile': True, + }) + cls.account_customer = cls.env['account.account'].create({ + 'name': 'Customer', + 'code': 'XX_430', + 'user_type_id': type_payable.id, + 'reconcile': True, + }) + cls.journal_sale = cls.env['account.journal'].create({ + 'name': 'Test journal for sale', 'type': 'sale', - 'name': 'Test Sales', 'code': 'TSALE', + 'default_debit_account_id': cls.account_sale.id, + 'default_credit_account_id': cls.account_sale.id, + }) + cls.tax = cls.env['account.tax'].create({ + 'name': 'Tax for sale 10%', + 'type_tax_use': 'sale', + 'tax_group_id': tax_group_taxes.id, + 'amount_type': 'percent', + 'amount': 10.0, }) - self.prod_account = self.env.ref('account.demo_coffee_machine_account') - self.inv_account = self.env.ref('account.demo_sale_of_land_account') - self.invoice = self.env['account.invoice'].create({ - 'journal_id': self.journal.id, - 'company_id': self.env.user.company_id.id, - 'currency_id': self.env.user.company_id.currency_id.id, - 'partner_id': self.partner.id, + cls.invoice = cls.env['account.invoice'].create({ + 'partner_id': cls.partner.id, + 'account_id': cls.account_customer.id, + 'type': 'out_invoice', + 'journal_id': cls.journal_sale.id, + 'payment_term_id': False, 'invoice_line_ids': [(0, 0, { - 'account_id': self.prod_account.id, - 'name': 'Test line', + 'name': 'Test product', + 'account_id': cls.account_sale.id, 'price_unit': 50, 'quantity': 10, - })] + 'invoice_line_tax_ids': [(6, 0, [cls.tax.id])], + })], }) def test_invoices(self): self.partner.risk_invoice_draft_include = True - self.assertAlmostEqual(self.partner.risk_invoice_draft, 500.0) - self.assertAlmostEqual(self.partner.risk_total, 500.0) + self.assertAlmostEqual(self.partner.risk_invoice_draft, 550.0) + self.assertAlmostEqual(self.partner.risk_total, 550.0) self.invoice.signal_workflow('invoice_open') self.assertAlmostEqual(self.partner.risk_invoice_draft, 0.0) self.assertFalse(self.invoice.date_due) self.partner.risk_invoice_unpaid_include = True - self.assertAlmostEqual(self.partner.risk_total, 500.0) + self.assertAlmostEqual(self.partner.risk_total, 550.0) self.partner.credit_limit = 100.0 self.assertTrue(self.partner.risk_exception) - self.partner.credit_limit = 1000.0 + self.partner.credit_limit = 1100.0 self.assertFalse(self.partner.risk_exception) self.partner.risk_invoice_unpaid_limit = 499.0 self.assertTrue(self.partner.risk_exception) - invoice2 = self.invoice.copy() + invoice2 = self.invoice.copy({'partner_id': self.invoice_address.id}) + self.assertAlmostEqual(self.partner.risk_invoice_draft, 550.0) + self.assertAlmostEqual(self.partner.risk_invoice_unpaid, 550.0) wiz_dic = invoice2.invoice_open() wiz = self.env[wiz_dic['res_model']].browse(wiz_dic['res_id']) self.assertEqual(wiz.exception_msg, "Financial risk exceeded.\n") @@ -71,7 +103,7 @@ class TestPartnerFinancialRisk(TransactionCase): "This invoice exceeds the financial risk.\n") self.assertAlmostEqual(self.partner.risk_invoice_open, 0.0) wiz.button_continue() - self.assertAlmostEqual(self.partner.risk_invoice_open, 500.0) + self.assertAlmostEqual(self.partner.risk_invoice_open, 550.0) self.assertTrue(self.partner.risk_allow_edit) self.partner.process_unpaid_invoices() self.assertEqual(self.env['ir.config_parameter'].get_param(