You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

273 lines
12 KiB

# -*- coding: utf-8 -*-
# Copyright 2016 Carlos Dauden <carlos.dauden@tecnativa.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from collections import defaultdict
from datetime import datetime
from dateutil.relativedelta import relativedelta
from openerp import api, fields, models
class ResPartner(models.Model):
_inherit = 'res.partner'
move_line_ids = fields.One2many(
comodel_name='account.move.line',
inverse_name='partner_id',
string='Account Moves'
)
risk_invoice_draft_include = fields.Boolean(
string='Include Draft Invoices', help='Full risk computation')
risk_invoice_draft_limit = fields.Monetary(
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='Total Draft Invoices',
help='Total amount of invoices in Draft or Pro-forma state')
risk_invoice_open_include = fields.Boolean(
string='Include Open Invoices/Principal Balance',
help='Full risk computation.\n'
'Residual amount of move lines not reconciled with the same '
'account that is set as partner receivable and date maturity '
'not exceeded, considering Due Margin set in account settings.')
risk_invoice_open_limit = fields.Monetary(
string='Limit In Open Invoices/Principal Balance',
help='Set 0 if it is not locked')
risk_invoice_open = fields.Monetary(
compute='_compute_risk_account_amount', store=True,
string='Total Open Invoices/Principal Balance',
help='Residual amount of move lines not reconciled with the same '
'account that is set as partner receivable and date maturity '
'not exceeded, considering Due Margin set in account settings.')
risk_invoice_unpaid_include = fields.Boolean(
string='Include Unpaid Invoices/Principal Balance',
help='Full risk computation.\n'
'Residual amount of move lines not reconciled with the same '
'account that is set as partner receivable and date maturity '
'exceeded, considering Due Margin set in account settings.')
risk_invoice_unpaid_limit = fields.Monetary(
string='Limit In Unpaid Invoices/Principal Balance',
help='Set 0 if it is not locked')
risk_invoice_unpaid = fields.Monetary(
compute='_compute_risk_account_amount', store=True,
string='Total Unpaid Invoices/Principal Balance',
help='Residual amount of move lines not reconciled with the same '
'account that is set as partner receivable and date maturity '
'exceeded, considering Due Margin set in account settings.')
risk_account_amount_include = fields.Boolean(
string='Include Other Account Open Amount',
help='Full risk computation.\n'
'Residual amount of move lines not reconciled with distinct '
'account that is set as partner receivable and date maturity '
'not exceeded, considering Due Margin set in account settings.')
risk_account_amount_limit = fields.Monetary(
string='Limit Other Account Open Amount',
help='Set 0 if it is not locked')
risk_account_amount = fields.Monetary(
compute='_compute_risk_account_amount', store=True,
string='Total Other Account Open Amount',
help='Residual amount of move lines not reconciled with distinct '
'account that is set as partner receivable and date maturity '
'not exceeded, considering Due Margin set in account settings.')
risk_account_amount_unpaid_include = fields.Boolean(
string='Include Other Account Unpaid Amount',
help='Full risk computation.\n'
'Residual amount of move lines not reconciled with distinct '
'account that is set as partner receivable and date maturity '
'exceeded, considering Due Margin set in account settings.')
risk_account_amount_unpaid_limit = fields.Monetary(
string='Limit Other Account Unpaid Amount',
help='Set 0 if it is not locked')
risk_account_amount_unpaid = fields.Monetary(
compute='_compute_risk_account_amount', store=True,
string='Total Other Account Unpaid Amount',
help='Residual amount of move lines not reconciled with distinct '
'account that is set as partner receivable and date maturity '
'exceeded, considering Due Margin set in account settings.')
risk_total = fields.Monetary(
compute='_compute_risk_exception',
string='Total Risk', help='Sum of total risk included')
risk_exception = fields.Boolean(
compute='_compute_risk_exception',
string='Risk Exception',
help='It Indicate if partner risk exceeded')
credit_policy = fields.Char()
risk_allow_edit = fields.Boolean(compute='_compute_risk_allow_edit')
credit_limit = fields.Float(track_visibility='onchange')
@api.multi
def _compute_risk_allow_edit(self):
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.filtered('customer'):
partner.risk_allow_edit = is_editable
@api.multi
@api.depends(
'customer', 'invoice_ids', 'invoice_ids.state',
'invoice_ids.amount_total',
'child_ids.invoice_ids', 'child_ids.invoice_ids.state',
'child_ids.invoice_ids.amount_total')
def _compute_risk_invoice(self):
all_partners_and_children = {}
all_partner_ids = []
for partner in self.filtered('customer'):
if not partner.id:
continue
all_partners_and_children[partner] = self.with_context(
active_test=False).search([('id', 'child_of', partner.id)]).ids
all_partner_ids += all_partners_and_children[partner]
if not all_partner_ids:
return
total_group = self.env['account.invoice'].sudo().read_group(
[('type', 'in', ['out_invoice', 'out_refund']),
('state', 'in', ['draft', 'proforma', 'proforma2']),
('partner_id', 'in', self.ids)],
['partner_id', 'amount_total'],
['partner_id'])
for partner, child_ids in all_partners_and_children.items():
partner.risk_invoice_draft = sum(
x['amount_total']
for x in total_group if x['partner_id'][0] in child_ids)
@api.model
def _risk_account_groups(self):
max_date = self._max_risk_date_due()
return {
'open': {
'domain': [('reconciled', '=', False),
('account_id.internal_type', '=', 'receivable'),
('date_maturity', '>=', max_date)],
'fields': ['partner_id', 'account_id', 'amount_residual'],
'group_by': ['partner_id', 'account_id']
},
'unpaid': {
'domain': [('reconciled', '=', False),
('account_id.internal_type', '=', 'receivable'),
('date_maturity', '<', max_date)],
'fields': ['partner_id', 'account_id', 'amount_residual'],
'group_by': ['partner_id', 'account_id']
}
}
@api.multi
@api.depends('move_line_ids.amount_residual',
'move_line_ids.date_maturity',
'company_id.invoice_unpaid_margin')
def _compute_risk_account_amount(self):
AccountMoveLine = self.env['account.move.line'].sudo()
customers = self.filtered(lambda x: x.customer and not x.parent_id)
if not customers:
return
groups = self._risk_account_groups()
for key, group in groups.iteritems():
group['read_group'] = AccountMoveLine.read_group(
group['domain'] + [('partner_id', 'in', customers.ids)],
group['fields'],
group['group_by'],
lazy=False,
)
for partner in customers:
partner.update(partner._prepare_risk_account_vals(groups))
@api.multi
def _prepare_risk_account_vals(self, groups):
vals = {
'risk_invoice_open': 0.0,
'risk_invoice_unpaid': 0.0,
'risk_account_amount': 0.0,
'risk_account_amount_unpaid': 0.0,
}
for reg in groups['open']['read_group']:
if reg['partner_id'][0] != self.id:
continue
if self.property_account_receivable_id.id == reg['account_id'][0]:
vals['risk_invoice_open'] += reg['amount_residual']
else:
vals['risk_account_amount'] += reg['amount_residual']
for reg in groups['unpaid']['read_group']:
if reg['partner_id'][0] != self.id:
continue # pragma: no cover
if self.property_account_receivable_id.id == reg['account_id'][0]:
vals['risk_invoice_unpaid'] += reg['amount_residual']
else:
vals['risk_account_amount_unpaid'] += reg['amount_residual']
return vals
@api.multi
@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.filtered('customer'):
amount = 0.0
for risk_field in risk_field_list:
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, risk_field[2], False):
amount += field_value
partner.risk_total = amount
if partner.credit_limit and amount > partner.credit_limit:
partner.risk_exception = True
@api.model
def _max_risk_date_due(self):
return fields.Date.to_string(datetime.today().date() - relativedelta(
days=self.env.user.company_id.invoice_unpaid_margin))
@api.model
def _risk_field_list(self):
return [
('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'),
('risk_account_amount_unpaid', 'risk_account_amount_unpaid_limit',
'risk_account_amount_unpaid_include'),
]
@api.model
def _get_depends_compute_risk_exception(self):
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
def process_unpaid_invoices(self):
max_date = self._max_risk_date_due()
ConfigParameter = self.env['ir.config_parameter']
last_check = ConfigParameter.get_param(
'partner_financial_risk.last_check', default='2016-01-01')
groups = self.env['account.move.line'].sudo().read_group(
[('reconciled', '=', False),
('partner_id', '!=', False),
('account_id.internal_type', '=', 'receivable'),
('date_maturity', '>=', last_check),
('date_maturity', '<', max_date)],
['company_id', 'partner_id'],
['company_id', 'partner_id'],
lazy=False,
)
group_dic = defaultdict(list)
for group in groups:
group_dic[group['company_id'][0]].append(group['partner_id'][0])
for company_id, partner_ids in group_dic.iteritems():
partners = self.browse(partner_ids)
partners.with_context(
force_company=company_id,
)._compute_risk_account_amount()
ConfigParameter.set_param(
'partner_financial_risk.last_check', max_date)
return True