mirror of
https://github.com/OCA/account-financial-tools.git
synced 2025-02-02 12:47:26 +02:00
231 lines
9.5 KiB
Python
231 lines
9.5 KiB
Python
# -*- coding: utf-8 -*-
|
|
##############################################################################
|
|
#
|
|
# Author: Nicolas Bessi, Guewen Baconnier
|
|
# Copyright 2012-2014 Camptocamp SA
|
|
#
|
|
# This program is free software: you can redistribute it and/or modify
|
|
# it under the terms of the GNU Affero General Public License as
|
|
# published by the Free Software Foundation, either version 3 of the
|
|
# License, or (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU Affero General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU Affero General Public License
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
#
|
|
##############################################################################
|
|
import logging
|
|
|
|
from openerp import models, fields, api, _
|
|
|
|
logger = logging.getLogger('credit.line.control')
|
|
|
|
|
|
class CreditControlLine(models.Model):
|
|
""" A credit control line describes an amount due by a customer for a due date.
|
|
|
|
A line is created once the due date of the payment is exceeded.
|
|
It is created in "draft" and some actions are available (send by email,
|
|
print, ...)
|
|
"""
|
|
|
|
_name = "credit.control.line"
|
|
_description = "A credit control line"
|
|
_rec_name = "id"
|
|
_order = "date DESC"
|
|
|
|
date = fields.Date(string='Controlling date',
|
|
required=True,
|
|
select=True)
|
|
# maturity date of related move line we do not use
|
|
# a related field in order to
|
|
# allow manual changes
|
|
date_due = fields.Date(string='Due date',
|
|
required=True,
|
|
readonly=True,
|
|
states={'draft': [('readonly', False)]})
|
|
|
|
date_entry = fields.Date(string='Entry date',
|
|
related='move_line_id.date',
|
|
store=True,
|
|
readonly=True)
|
|
|
|
date_sent = fields.Date(string='Sent date',
|
|
readonly=True,
|
|
states={'draft': [('readonly', False)]})
|
|
|
|
state = fields.Selection([('draft', 'Draft'),
|
|
('ignored', 'Ignored'),
|
|
('to_be_sent', 'Ready To Send'),
|
|
('sent', 'Done'),
|
|
('error', 'Error'),
|
|
('email_error', 'Emailing Error')],
|
|
'State',
|
|
required=True,
|
|
readonly=True,
|
|
default='draft',
|
|
help="Draft lines need to be triaged.\n"
|
|
"Ignored lines are lines for which we do "
|
|
"not want to send something.\n"
|
|
"Draft and ignored lines will be "
|
|
"generated again on the next run.")
|
|
|
|
channel = fields.Selection([('letter', 'Letter'),
|
|
('email', 'Email')],
|
|
string='Channel',
|
|
required=True,
|
|
readonly=True,
|
|
states={'draft': [('readonly', False)]})
|
|
|
|
invoice_id = fields.Many2one('account.invoice',
|
|
string='Invoice',
|
|
readonly=True)
|
|
|
|
partner_id = fields.Many2one('res.partner',
|
|
string='Partner',
|
|
required=True)
|
|
|
|
amount_due = fields.Float(string='Due Amount Tax incl.',
|
|
required=True, readonly=True)
|
|
|
|
balance_due = fields.Float(string='Due balance', required=True,
|
|
readonly=True)
|
|
|
|
mail_message_id = fields.Many2one('mail.mail', string='Sent Email',
|
|
readonly=True)
|
|
|
|
move_line_id = fields.Many2one('account.move.line',
|
|
string='Move line',
|
|
required=True,
|
|
readonly=True)
|
|
|
|
account_id = fields.Many2one('account.account',
|
|
related='move_line_id.account_id',
|
|
store=True,
|
|
readonly=True)
|
|
|
|
currency_id = fields.Many2one('res.currency',
|
|
related='move_line_id.currency_id',
|
|
store=True,
|
|
readonly=True)
|
|
|
|
company_id = fields.Many2one('res.company',
|
|
related='move_line_id.company_id',
|
|
store=True,
|
|
readonly=True)
|
|
|
|
# we can allow a manual change of policy in draft state
|
|
policy_level_id = fields.Many2one('credit.control.policy.level',
|
|
string='Overdue Level',
|
|
required=True,
|
|
readonly=True,
|
|
states={'draft': [('readonly', False)]})
|
|
|
|
policy_id = fields.Many2one('credit.control.policy',
|
|
related='policy_level_id.policy_id',
|
|
store=True,
|
|
readonly=True)
|
|
|
|
level = fields.Integer('credit.control.policy.level',
|
|
related='policy_level_id.level',
|
|
store=True,
|
|
readonly=True)
|
|
|
|
manually_overridden = fields.Boolean(string='Manually overridden')
|
|
|
|
run_id = fields.Many2one(comodel_name='credit.control.run',
|
|
string='Source')
|
|
|
|
@api.model
|
|
def _prepare_from_move_line(self, move_line, level, controlling_date,
|
|
open_amount):
|
|
""" Create credit control line """
|
|
data = {}
|
|
data['date'] = controlling_date
|
|
data['date_due'] = move_line.date_maturity
|
|
data['state'] = 'draft'
|
|
data['channel'] = level.channel
|
|
data['invoice_id'] = (move_line.invoice.id if
|
|
move_line.invoice else False)
|
|
data['partner_id'] = move_line.partner_id.id
|
|
data['amount_due'] = (move_line.amount_currency or move_line.debit or
|
|
move_line.credit)
|
|
data['balance_due'] = open_amount
|
|
data['policy_level_id'] = level.id
|
|
data['move_line_id'] = move_line.id
|
|
return data
|
|
|
|
@api.model
|
|
def create_or_update_from_mv_lines(self, lines, level, controlling_date,
|
|
check_tolerance=True):
|
|
""" Create or update line based on levels
|
|
|
|
if check_tolerance is true credit line will not be
|
|
created if open amount is too small.
|
|
eg. we do not want to send a letter for 10 cents
|
|
of open amount.
|
|
|
|
:param lines: move.line id recordset
|
|
:param level: credit.control.policy.level record
|
|
:param controlling_date: date string of the credit controlling date.
|
|
Generally it should be the same
|
|
as create date
|
|
:param check_tolerance: boolean if True credit line
|
|
will not be generated if open amount
|
|
is smaller than company defined
|
|
tolerance
|
|
|
|
:returns: recordset of created credit lines
|
|
"""
|
|
currency_obj = self.env['res.currency']
|
|
user = self.env.user
|
|
currencies = currency_obj.search([])
|
|
|
|
tolerance = {}
|
|
tolerance_base = user.company_id.credit_control_tolerance
|
|
user_currency = user.company_id.currency_id
|
|
for currency in currencies:
|
|
tolerance[currency.id] = currency.compute(tolerance_base,
|
|
user_currency)
|
|
|
|
new_lines = self.browse()
|
|
for move_line in lines:
|
|
open_amount = move_line.amount_residual_currency
|
|
cur_tolerance = tolerance.get(move_line.currency_id.id,
|
|
tolerance_base)
|
|
if check_tolerance and open_amount < cur_tolerance:
|
|
continue
|
|
vals = self._prepare_from_move_line(move_line,
|
|
level,
|
|
controlling_date,
|
|
open_amount)
|
|
line = self.create(vals)
|
|
new_lines += line
|
|
|
|
# when we have lines generated earlier in draft,
|
|
# on the same level, it means that we have left
|
|
# them, so they are to be considered as ignored
|
|
previous_drafts = self.search([('move_line_id', '=', move_line.id),
|
|
('policy_level_id', '=', level.id),
|
|
('state', '=', 'draft'),
|
|
('id', '!=', line.id)])
|
|
if previous_drafts:
|
|
previous_drafts.write({'state': 'ignored'})
|
|
|
|
return new_lines
|
|
|
|
@api.multi
|
|
def unlink(self):
|
|
for line in self:
|
|
if line.state != 'draft':
|
|
raise api.Warning(
|
|
_('You are not allowed to delete a credit control '
|
|
'line that is not in draft state.')
|
|
)
|
|
|
|
return super(CreditControlLine, self).unlink()
|