diff --git a/partner_financial_risk/README.rst b/partner_financial_risk/README.rst index 6be9d4ee6..e33dc1413 100644 --- a/partner_financial_risk/README.rst +++ b/partner_financial_risk/README.rst @@ -15,8 +15,9 @@ Configuration To configure this module, you need to: -#. Go to *Invoicing > Configuration > Settings > Invoicing & Payments > - Financial Risk* and set *Maturity Margin*. +#. Go to *Invoicing > Configuration > Settings > Invoicing & Payments* +#. Go to *Financial Risk* and set *Due Margin* (This field adds days of margin + before that a invoice is consider as unpaid) Usage ===== diff --git a/partner_financial_risk/__init__.py b/partner_financial_risk/__init__.py index cde864bae..35e7c9600 100644 --- a/partner_financial_risk/__init__.py +++ b/partner_financial_risk/__init__.py @@ -1,3 +1,4 @@ # -*- coding: utf-8 -*- from . import models +from . import wizard diff --git a/partner_financial_risk/__openerp__.py b/partner_financial_risk/__openerp__.py index 4c5dc4dfa..b97b714d1 100644 --- a/partner_financial_risk/__openerp__.py +++ b/partner_financial_risk/__openerp__.py @@ -15,6 +15,7 @@ 'views/res_config_view.xml', 'views/res_partner_view.xml', 'views/sale_view.xml', + 'wizard/sale_order_risk_exceeded_view.xml', ], 'installable': True, } diff --git a/partner_financial_risk/models/res_company.py b/partner_financial_risk/models/res_company.py index 916fd4b7c..c03b33235 100644 --- a/partner_financial_risk/models/res_company.py +++ b/partner_financial_risk/models/res_company.py @@ -8,6 +8,6 @@ from openerp import fields, models class ResCompany(models.Model): _inherit = 'res.company' - invoice_maturity_margin = fields.Integer( + invoice_due_margin = fields.Integer( string="Maturity Margin", help="Days after due date to set an invoice as unpaid") diff --git a/partner_financial_risk/models/res_config.py b/partner_financial_risk/models/res_config.py index 3ca789cb3..c3fd64504 100644 --- a/partner_financial_risk/models/res_config.py +++ b/partner_financial_risk/models/res_config.py @@ -8,5 +8,5 @@ from openerp import fields, models class AccountConfigSettings(models.TransientModel): _inherit = 'account.config.settings' - invoice_maturity_margin = fields.Integer( - related='company_id.invoice_maturity_margin') + invoice_due_margin = fields.Integer( + related='company_id.invoice_due_margin') diff --git a/partner_financial_risk/models/res_partner.py b/partner_financial_risk/models/res_partner.py index ec567494d..5b99fcdf5 100644 --- a/partner_financial_risk/models/res_partner.py +++ b/partner_financial_risk/models/res_partner.py @@ -11,48 +11,51 @@ class ResPartner(models.Model): _inherit = 'res.partner' risk_sale_order_include = fields.Boolean( - string='Include Sale Orders', help='Compute in total risk') + string='Include Sales Orders', help='Full risk computation') risk_sale_order_limit = fields.Monetary( - string='Limit Sale Orders', help='Set 0 if it not lock') + string='Limit Sales Orders', help='Set 0 if it is not locked') risk_sale_order = fields.Monetary( compute='_compute_risk_sale_order', store=True, - string='Sale Order Not Invoiced', - help='Total not invoiced of sale order in *Sale Order* state') + string='Total Sales Orders Not Invoiced', + help='Total not invoiced of sales orders in Sale Order state') + risk_invoice_draft_include = fields.Boolean( - string='Include Draft Invoices', help='Compute in total risk') + string='Include Draft Invoices', help='Full risk computation') risk_invoice_draft_limit = fields.Monetary( - string='Limit In Draft Invoices', help='Set 0 if it not lock') + string='Limit In Draft Invoices', help='Set 0 if it is not locked') risk_invoice_draft = fields.Monetary( compute='_compute_risk_invoice', store=True, - string='Not Validated Invoice', + string='Total Draft Invoices', help='Total amount of invoices in Draft or Pro-forma state') risk_invoice_open_include = fields.Boolean( - string='Include Open Invoices', help='Compute in total risk') + string='Include Open Invoices', help='Full risk computation') risk_invoice_open_limit = fields.Monetary( - string='Limit In Open Invoices', help='Set 0 if it not lock') + string='Limit In Open Invoices', help='Set 0 if it is not locked') risk_invoice_open = fields.Monetary( compute='_compute_risk_invoice', store=True, - string='Open Invoice', + string='Total Open Invoices', help='Residual amount of invoices in Open state and the date due is ' - 'not exceeded, considering Maturity Margin set in account ' + 'not exceeded, considering Due Margin set in account ' 'settings') risk_invoice_unpaid_include = fields.Boolean( - string='Include Unpaid Invoices', help='Compute in total risk') + string='Include Due Invoices', help='Full risk computation') risk_invoice_unpaid_limit = fields.Monetary( - string='Limit In Unpaid Invoices', help='Set 0 if it not lock') + string='Limit In Due Invoices', help='Set 0 if it is not locked') risk_invoice_unpaid = fields.Monetary( compute='_compute_risk_invoice', store=True, - string='Unpaid Invoice', + string='Total Due Invoices', help='Residual amount of invoices in Open state and the date due is ' - 'exceeded, considering Maturity Margin set in account settings') + 'exceeded, considering Due Margin set in account settings') + risk_account_amount_include = fields.Boolean( - string='Include Other Account Amount', help='Compute in total risk') + string='Include Other Account Amount', help='Full risk computation') risk_account_amount_limit = fields.Monetary( - string='Limit Other Account Amount', help='Set 0 if it not lock') + string='Limit Other Account Amount', help='Set 0 if it is not locked') risk_account_amount = fields.Monetary( compute='_compute_risk_account_amount', string='Other Account Amount', help='Difference between accounting credit and rest of totals') + risk_total = fields.Monetary( compute='_compute_risk_exception', string='Total Risk', help='Sum of total risk included') @@ -71,7 +74,7 @@ class ResPartner(models.Model): @api.multi @api.depends('invoice_ids', 'invoice_ids.state', 'invoice_ids.amount_total', 'invoice_ids.residual', - 'invoice_ids.company_id.invoice_maturity_margin') + 'invoice_ids.company_id.invoice_due_margin') def _compute_risk_invoice(self): max_date = self._max_risk_date_due() for partner in self: @@ -94,25 +97,26 @@ class ResPartner(models.Model): partner.risk_invoice_unpaid) @api.multi - @api.depends( - 'risk_sale_order', 'risk_sale_order_include', 'risk_sale_order_limit', - 'risk_invoice_draft', 'risk_invoice_draft_include', - 'risk_invoice_draft_limit', 'risk_invoice_open', - 'risk_invoice_open_include', 'risk_invoice_open_limit', - 'risk_invoice_unpaid', 'risk_invoice_unpaid_include', - 'risk_invoice_unpaid_limit', 'risk_account_amount', - 'risk_account_amount_include', 'risk_account_amount_limit', - 'credit_limit',) + @api.depends('risk_sale_order', 'risk_sale_order_include', + 'risk_sale_order_limit', + 'risk_invoice_draft', 'risk_invoice_draft_include', + 'risk_invoice_draft_limit', 'risk_invoice_open', + 'risk_invoice_open_include', 'risk_invoice_open_limit', + 'risk_invoice_unpaid', 'risk_invoice_unpaid_include', + 'risk_invoice_unpaid_limit', 'risk_account_amount', + 'risk_account_amount_include', 'risk_account_amount_limit', + 'credit_limit') + # @api.depends(lambda x: x._depends_list) def _compute_risk_exception(self): risk_field_list = self._risk_field_list() for partner in self: amount = 0.0 for risk_field in risk_field_list: - field_value = getattr(partner, risk_field, 0.0) - max_value = getattr(partner, '%s_limit' % risk_field, 0.0) + field_value = getattr(partner, risk_field[0], 0.0) + max_value = getattr(partner, risk_field[1], 0.0) if max_value and field_value > max_value: partner.risk_exception = True - if getattr(partner, '%s_include' % risk_field, False): + if getattr(partner, risk_field[2], False): amount += field_value partner.risk_total = amount if amount > partner.credit_limit: @@ -121,9 +125,32 @@ class ResPartner(models.Model): @api.model def _max_risk_date_due(self): return fields.Date.to_string(datetime.today().date() - relativedelta( - days=self.env.user.company_id.invoice_maturity_margin)) + days=self.env.user.company_id.invoice_due_margin)) @api.model def _risk_field_list(self): - return ['risk_sale_order', 'risk_invoice_draft', 'risk_invoice_open', - 'risk_invoice_unpaid', 'risk_account_amount'] + return [ + ('risk_sale_order', 'risk_sale_order_limit', + 'risk_sale_order_include'), + ('risk_invoice_draft', 'risk_invoice_draft_limit', + 'risk_invoice_draft_include'), + ('risk_invoice_open', 'risk_invoice_open_limit', + 'risk_invoice_open_include'), + ('risk_invoice_unpaid', 'risk_invoice_unpaid_limit', + 'risk_invoice_unpaid_include'), + ('risk_account_amount', 'risk_account_amount_limit', + 'risk_account_amount_include'), + ] + + @api.model + def _depends_list(self): + ss = ( + 'risk_sale_order', 'risk_sale_order_include', 'risk_sale_order_limit', + 'risk_invoice_draft', 'risk_invoice_draft_include', + 'risk_invoice_draft_limit', 'risk_invoice_open', + 'risk_invoice_open_include', 'risk_invoice_open_limit', + 'risk_invoice_unpaid', 'risk_invoice_unpaid_include', + 'risk_invoice_unpaid_limit', 'risk_account_amount', + 'risk_account_amount_include', 'risk_account_amount_limit', + 'credit_limit') + return ss \ No newline at end of file diff --git a/partner_financial_risk/models/sale.py b/partner_financial_risk/models/sale.py index 37a785746..6955a7491 100644 --- a/partner_financial_risk/models/sale.py +++ b/partner_financial_risk/models/sale.py @@ -2,7 +2,7 @@ # © 2016 Carlos Dauden # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from openerp import api, exceptions, fields, models, _ +from openerp import api, fields, models, _ class SaleOrder(models.Model): @@ -24,18 +24,36 @@ class SaleOrder(models.Model): @api.multi def action_confirm(self): - partner = self.partner_id - if partner.risk_exception: - raise exceptions.UserError(_( - "Financial risk exceeded.\n" - "You can not confirm this sale order" - )) - elif partner.risk_sale_order_include and ( - (partner.risk_total + self.amount_total) > - partner.credit_limit): - raise exceptions.UserError(_( - "This sale order exceeds the financial risk.\n" - "You can not confirm this sale order" - )) - + if not self.env.context.get('bypass_risk', False): + partner = self.partner_id + risk_exception = False + if partner.risk_exception: + risk_exception = True + exception_msg = _("Financial risk exceeded.\n") + elif partner.risk_sale_order_limit and ( + (partner.risk_sale_order + self.amount_total) > + partner.risk_sale_order_limit): + risk_exception = True + exception_msg = _( + "This sale order exceeds the sales orders risk.\n") + elif partner.risk_sale_order_include and ( + (partner.risk_total + self.amount_total) > + partner.credit_limit): + risk_exception = True + exception_msg = _( + "This sale order exceeds the financial risk.\n") + if risk_exception: + wizard = self.env['sale.order.risk.exceeded'].create({ + 'exception_msg': exception_msg, + 'partner_id': partner.id, + 'amount': self.amount_total}) + return { + 'type': 'ir.actions.act_window', + 'name': _('Partner risk exceeded'), + 'res_model': wizard._name, + 'res_id': wizard.id, + 'view_type': 'form', + 'view_mode': 'form', + 'target': 'new', + } return super(SaleOrder, self).action_confirm() diff --git a/partner_financial_risk/views/res_company_view.xml b/partner_financial_risk/views/res_company_view.xml index d5832aae9..7ec47bb8d 100644 --- a/partner_financial_risk/views/res_company_view.xml +++ b/partner_financial_risk/views/res_company_view.xml @@ -9,7 +9,7 @@ res.company - + diff --git a/partner_financial_risk/views/res_config_view.xml b/partner_financial_risk/views/res_config_view.xml index d583fc27a..1226b2d42 100644 --- a/partner_financial_risk/views/res_config_view.xml +++ b/partner_financial_risk/views/res_config_view.xml @@ -11,8 +11,8 @@ diff --git a/partner_financial_risk/views/res_partner_view.xml b/partner_financial_risk/views/res_partner_view.xml index 5d34aae6f..2f8525006 100644 --- a/partner_financial_risk/views/res_partner_view.xml +++ b/partner_financial_risk/views/res_partner_view.xml @@ -10,39 +10,61 @@ - - - - - - + +
+ + +
+

General Limits

+
+
+ + + + + + + + + + + + + + + +
+ +
+

Specific Limits

+
+
+ + + + + +
- - - - - - - - - - - - - - - - - - - - - - - - - - +
+ + + + + + + + + + + + + + + + + +
diff --git a/partner_financial_risk/wizard/__init__.py b/partner_financial_risk/wizard/__init__.py new file mode 100644 index 000000000..58f6a0f1f --- /dev/null +++ b/partner_financial_risk/wizard/__init__.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- + +from . import sale_order_risk_exceeded diff --git a/partner_financial_risk/wizard/sale_order_risk_exceeded.py b/partner_financial_risk/wizard/sale_order_risk_exceeded.py new file mode 100644 index 000000000..90776704e --- /dev/null +++ b/partner_financial_risk/wizard/sale_order_risk_exceeded.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +# © 2016 Carlos Dauden +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from openerp import api, fields, models + + +class SaleOrderRiskExceeded(models.TransientModel): + _name = 'sale.order.risk.exceeded' + + partner_id = fields.Many2one( + comodel_name='res.partner', readonly=True, string='Customer') + exception_msg = fields.Text(readonly=True) + + @api.multi + def button_continue(self): + self.ensure_one() + so = self.env['sale.order'].browse(self.env.context['active_id']) + return so.with_context(bypass_risk=True).action_confirm() diff --git a/partner_financial_risk/wizard/sale_order_risk_exceeded_view.xml b/partner_financial_risk/wizard/sale_order_risk_exceeded_view.xml new file mode 100644 index 000000000..909199379 --- /dev/null +++ b/partner_financial_risk/wizard/sale_order_risk_exceeded_view.xml @@ -0,0 +1,30 @@ + + + + + Partner risk exceeded + sale.order.risk.exceeded + +
+

The partner has exceeded his risk

+ + + + + + +
+
+
\ No newline at end of file