account-closing/account_cutoff_prepaid/models/account_cutoff.py

193 lines
7.5 KiB
Python
Raw Normal View History

2021-03-23 19:28:14 +00:00
# Copyright 2016 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from flectra import models, fields, api, _
from flectra.exceptions import UserError, ValidationError
class AccountCutoff(models.Model):
_inherit = 'account.cutoff'
@api.model
def _get_default_source_journals(self):
res = []
cutoff_type = self.env.context.get('cutoff_type')
mapping = {
'prepaid_expense': 'purchase',
'prepaid_revenue': 'sale',
}
if cutoff_type in mapping:
src_journals = self.env['account.journal'].search(
[('type', '=', mapping[cutoff_type])])
if src_journals:
res = src_journals.ids
return res
source_journal_ids = fields.Many2many(
'account.journal', column1='cutoff_id', column2='journal_id',
string='Source Journals', readonly=True,
default=lambda self: self._get_default_source_journals(),
states={'draft': [('readonly', False)]})
forecast = fields.Boolean(
string='Forecast',
readonly=True, states={'draft': [('readonly', False)]},
help="The Forecast mode allows the user to compute "
"the prepaid revenue/expense between 2 dates in the future.")
start_date = fields.Date(string='Start Date')
end_date = fields.Date(string='End Date')
_sql_constraints = [(
'date_type_forecast_company_uniq',
'unique('
'cutoff_date, company_id, cutoff_type,'
' forecast, start_date, end_date)',
'A cut-off of the same type already exists with the same date(s) !'
)]
@api.multi
@api.constrains('start_date', 'end_date', 'forecast')
def _check_start_end_dates(self):
for prepaid in self:
if prepaid.forecast and prepaid.start_date and prepaid.end_date \
and prepaid.start_date > prepaid.end_date:
raise ValidationError(_(
'The start date is after the end date!'))
@api.onchange('forecast')
def onchange_forecast(self):
return {'warning': {
'title': _('Warning'),
'message': _(
"Don't forget to Re-Generate Lines after entering or "
"leaving forecast mode.")}}
@api.multi
def _prepare_prepaid_lines(self, aml, mapping):
self.ensure_one()
start_date_dt = fields.Date.from_string(aml.start_date)
end_date_dt = fields.Date.from_string(aml.end_date)
# Here, we compute the amount of the cutoff
# That's the important part !
total_days = (end_date_dt - start_date_dt).days + 1
if self.forecast:
out_days = 0
forecast_start_date_dt = fields.Date.from_string(self.start_date)
forecast_end_date_dt = fields.Date.from_string(self.end_date)
if end_date_dt > forecast_end_date_dt:
out_days += (end_date_dt - forecast_end_date_dt).days
if start_date_dt < forecast_start_date_dt:
out_days += (forecast_start_date_dt - start_date_dt).days
prepaid_days = total_days - out_days
else:
cutoff_date_str = self.cutoff_date
cutoff_date_dt = fields.Date.from_string(cutoff_date_str)
if start_date_dt > cutoff_date_dt:
prepaid_days = total_days
else:
prepaid_days = (end_date_dt - cutoff_date_dt).days
assert total_days > 0,\
'Should never happen. Total days should always be > 0'
cutoff_amount = (aml.debit - aml.credit) *\
prepaid_days / float(total_days)
# we use account mapping here
if aml.account_id.id in mapping:
cutoff_account_id = mapping[aml.account_id.id]
else:
cutoff_account_id = aml.account_id.id
res = {
'parent_id': self.id,
'move_line_id': aml.id,
'partner_id': aml.partner_id.id or False,
'name': aml.name,
'start_date': start_date_dt,
'end_date': end_date_dt,
'account_id': aml.account_id.id,
'cutoff_account_id': cutoff_account_id,
'analytic_account_id': aml.analytic_account_id.id or False,
'total_days': total_days,
'prepaid_days': prepaid_days,
'amount': aml.credit - aml.debit,
'currency_id': self.company_currency_id.id,
'cutoff_amount': cutoff_amount,
}
return res
@api.multi
def get_prepaid_lines(self):
self.ensure_one()
aml_obj = self.env['account.move.line']
line_obj = self.env['account.cutoff.line']
mapping_obj = self.env['account.cutoff.mapping']
if not self.source_journal_ids:
raise UserError(
_("You should set at least one Source Journal."))
cutoff_date_str = self.cutoff_date
# Delete existing lines
self.line_ids.unlink()
if self.forecast:
domain = [
('start_date', '<=', self.end_date),
('end_date', '>=', self.start_date),
('journal_id', 'in', self.source_journal_ids.ids)
]
else:
domain = [
('start_date', '!=', False),
('journal_id', 'in', self.source_journal_ids.ids),
('end_date', '>', cutoff_date_str),
('date', '<=', cutoff_date_str)
]
# Search for account move lines in the source journals
amls = aml_obj.search(domain)
# Create mapping dict
mapping = mapping_obj._get_mapping_dict(
self.company_id.id,
self.cutoff_type
)
# Loop on selected account move lines to create the cutoff lines
for aml in amls:
line_obj.create(self._prepare_prepaid_lines(aml, mapping))
return True
@api.model
def _default_cutoff_account_id(self):
account_id = super()._default_cutoff_account_id()
cutoff_type = self.env.context.get('cutoff_type')
company = self.env.user.company_id
if cutoff_type == 'prepaid_revenue':
account_id = company.default_prepaid_revenue_account_id.id or False
elif cutoff_type == 'prepaid_expense':
account_id = company.default_prepaid_expense_account_id.id or False
return account_id
@api.model
def create(self, vals):
res = super(AccountCutoff, self).create(vals)
if not res.source_journal_ids:
journals = self.env["account.journal"].search([])
res.source_journal_ids = journals
return res
class AccountCutoffLine(models.Model):
_inherit = 'account.cutoff.line'
move_line_id = fields.Many2one(
'account.move.line', string='Account Move Line', readonly=True)
move_date = fields.Date(
related='move_line_id.date', string='Account Move Date', readonly=True)
invoice_id = fields.Many2one(
related='move_line_id.invoice_id', string='Invoice', readonly=True)
start_date = fields.Date(string='Start Date', readonly=True)
end_date = fields.Date(string='End Date', readonly=True)
total_days = fields.Integer('Total Number of Days', readonly=True)
prepaid_days = fields.Integer(
string='Prepaid Days', readonly=True,
help="In regular mode, this is the number of days after the "
"cut-off date. In forecast mode, this is the number of days "
"between the start date and the end date.")