Tower: upload at_accounting 18.0.1.7 (via marketplace)

This commit is contained in:
2026-04-28 07:33:58 +00:00
parent 8512a1d196
commit 53a05d2240

View File

@@ -0,0 +1,428 @@
from odoo import api, models, fields, _
from odoo.exceptions import UserError
from odoo.osv import expression
from odoo.tools import date_utils
from odoo.addons.account.models.company import SOFT_LOCK_DATE_FIELDS, LOCK_DATE_FIELDS
from datetime import date, timedelta
class AccountChangeLockDate(models.TransientModel):
"""
This wizard is used to change the lock date
"""
_name = 'account.change.lock.date'
_description = 'Change Lock Date'
company_id = fields.Many2one(
comodel_name='res.company',
required=True,
readonly=True,
default=lambda self: self.env.company,
)
fiscalyear_lock_date = fields.Date(
string='Lock Everything',
default=lambda self: self.env.company.fiscalyear_lock_date,
help="Any entry up to and including that date will be postponed to a later time, in accordance with its journal's sequence.",
)
fiscalyear_lock_date_for_me = fields.Date(
string='Lock Everything For Me',
compute='_compute_lock_date_exceptions',
)
fiscalyear_lock_date_for_everyone = fields.Date(
string='Lock Everything For Everyone',
compute='_compute_lock_date_exceptions',
)
min_fiscalyear_lock_date_exception_for_me_id = fields.Many2one(
comodel_name='account.lock_exception',
compute='_compute_lock_date_exceptions',
)
min_fiscalyear_lock_date_exception_for_everyone_id = fields.Many2one(
comodel_name='account.lock_exception',
compute='_compute_lock_date_exceptions',
)
tax_lock_date = fields.Date(
string="Lock Tax Return",
default=lambda self: self.env.company.tax_lock_date,
help="Any entry with taxes up to and including that date will be postponed to a later time, in accordance with its journal's sequence. "
"The tax lock date is automatically set when the tax closing entry is posted.",
)
tax_lock_date_for_me = fields.Date(
string='Lock Tax Return For Me',
compute='_compute_lock_date_exceptions',
)
tax_lock_date_for_everyone = fields.Date(
string='Lock Tax Return For Everyone',
compute='_compute_lock_date_exceptions',
)
min_tax_lock_date_exception_for_me_id = fields.Many2one(
comodel_name='account.lock_exception',
compute='_compute_lock_date_exceptions',
)
min_tax_lock_date_exception_for_everyone_id = fields.Many2one(
comodel_name='account.lock_exception',
compute='_compute_lock_date_exceptions',
)
sale_lock_date = fields.Date(
string='Lock Sales',
default=lambda self: self.env.company.sale_lock_date,
help="Any sales entry prior to and including this date will be postponed to a later date, in accordance with its journal's sequence.",
)
sale_lock_date_for_me = fields.Date(
string='Lock Sales For Me',
compute='_compute_lock_date_exceptions',
)
sale_lock_date_for_everyone = fields.Date(
string='Lock Sales For Everyone',
compute='_compute_lock_date_exceptions',
)
min_sale_lock_date_exception_for_me_id = fields.Many2one(
comodel_name='account.lock_exception',
compute='_compute_lock_date_exceptions',
)
min_sale_lock_date_exception_for_everyone_id = fields.Many2one(
comodel_name='account.lock_exception',
compute='_compute_lock_date_exceptions',
)
purchase_lock_date = fields.Date(
string='Lock Purchases',
default=lambda self: self.env.company.purchase_lock_date,
help="Any purchase entry prior to and including this date will be postponed to a later date, in accordance with its journal's sequence.",
)
purchase_lock_date_for_me = fields.Date(
string='Lock Purchases For Me',
compute='_compute_lock_date_exceptions',
)
purchase_lock_date_for_everyone = fields.Date(
string='Lock Purchases For Everyone',
compute='_compute_lock_date_exceptions',
)
min_purchase_lock_date_exception_for_me_id = fields.Many2one(
comodel_name='account.lock_exception',
compute='_compute_lock_date_exceptions',
)
min_purchase_lock_date_exception_for_everyone_id = fields.Many2one(
comodel_name='account.lock_exception',
compute='_compute_lock_date_exceptions',
)
hard_lock_date = fields.Date(
string='Hard Lock',
default=lambda self: self.env.company.hard_lock_date,
help="Any entry up to and including that date will be postponed to a later time, in accordance with its journal sequence. "
"This lock date is irreversible and does not allow any exception.",
)
current_hard_lock_date = fields.Date(
string='Current Hard Lock',
related='company_id.hard_lock_date',
readonly=True,
)
exception_needed = fields.Boolean( # TODO: remove in master (18.1)
string='Exception needed',
compute='_compute_exception_needed',
)
exception_needed_fields = fields.Char(
# String of comma separated values of the field(s) the exception applies to
compute='_compute_exception_needed_fields',
)
exception_applies_to = fields.Selection(
string='Exception applies',
selection=[
('me', "for me"),
('everyone', "for everyone"),
],
default='me',
required=True,
)
exception_duration = fields.Selection(
string='Exception Duration',
selection=[
('5min', "for 5 minutes"),
('15min', "for 15 minutes"),
('1h', "for 1 hour"),
('24h', "for 24 hours"),
('forever', "forever"),
],
default='5min',
required=True,
)
exception_reason = fields.Char(
string='Exception Reason',
)
show_draft_entries_warning = fields.Boolean(
string="Show Draft Entries Warning",
compute='_compute_show_draft_entries_warning',
)
@api.depends('company_id')
@api.depends_context('user', 'company')
def _compute_lock_date_exceptions(self):
for wizard in self:
exceptions = self.env['account.lock_exception'].search(
self.env['account.lock_exception']._get_active_exceptions_domain(wizard.company_id, SOFT_LOCK_DATE_FIELDS)
)
for field in SOFT_LOCK_DATE_FIELDS:
field_exceptions = exceptions.filtered(lambda e: e.lock_date_field == field)
field_exceptions_for_me = field_exceptions.filtered(lambda e: e.user_id.id == self.env.user.id)
field_exceptions_for_everyone = field_exceptions.filtered(lambda e: not e.user_id.id)
min_exception_for_me = min(field_exceptions_for_me, key=lambda e: e[field] or date.min) if field_exceptions_for_me else False
min_exception_for_everyone = min(field_exceptions_for_everyone, key=lambda e: e[field] or date.min) if field_exceptions_for_everyone else False
wizard[f"min_{field}_exception_for_me_id"] = min_exception_for_me
wizard[f"min_{field}_exception_for_everyone_id"] = min_exception_for_everyone
wizard[f"{field}_for_me"] = min_exception_for_me.lock_date if min_exception_for_me else False
wizard[f"{field}_for_everyone"] = min_exception_for_everyone.lock_date if min_exception_for_everyone else False
def _get_draft_moves_in_locked_period_domain(self):
self.ensure_one()
lock_date_domains = []
if self.hard_lock_date:
lock_date_domains.append([('date', '<=', self.hard_lock_date)])
if self.fiscalyear_lock_date:
lock_date_domains.append([('date', '<=', self.fiscalyear_lock_date)])
if self.sale_lock_date:
lock_date_domains.append([
('date', '<=', self.sale_lock_date),
('journal_id.type', '=', 'sale')])
if self.purchase_lock_date:
lock_date_domains.append([
('date', '<=', self.purchase_lock_date),
('journal_id.type', '=', 'purchase')])
return [
('company_id', 'child_of', self.env.company.id),
('state', '=', 'draft'),
*expression.OR(lock_date_domains),
]
@api.depends('fiscalyear_lock_date', 'tax_lock_date', 'sale_lock_date', 'purchase_lock_date', 'hard_lock_date')
def _compute_show_draft_entries_warning(self):
for wizard in self:
draft_entries = self.env['account.move'].search(self._get_draft_moves_in_locked_period_domain(), limit=1)
wizard.show_draft_entries_warning = bool(draft_entries)
def _get_changes_needing_exception(self):
self.ensure_one()
return {
field: self[field]
for field in SOFT_LOCK_DATE_FIELDS
if self.env.company[field] and (not self[field] or self[field] < self.env.company[field])
}
@api.depends(*SOFT_LOCK_DATE_FIELDS)
def _compute_exception_needed(self):
# TODO: remove in master (18.1)
for wizard in self:
wizard.exception_needed = bool(wizard._get_changes_needing_exception())
@api.depends(*SOFT_LOCK_DATE_FIELDS)
def _compute_exception_needed_fields(self):
for wizard in self:
changes_needing_exception = wizard._get_changes_needing_exception()
wizard.exception_needed_fields = ','.join(changes_needing_exception)
def _prepare_lock_date_values(self, exception_vals_list=None):
"""
Return a dictionary (lock date field -> field value)
It only contains lock dates which are changed and for which no exception is added
"""
self.ensure_one()
if self.env.company.hard_lock_date and (not self.hard_lock_date or self.hard_lock_date < self.env.company.hard_lock_date):
raise UserError(_('It is not possible to decrease or remove the Hard Lock Date.'))
lock_date_values = {
field: self[field]
for field in LOCK_DATE_FIELDS
if self[field] != self.env.company[field]
}
for field, lock_date in lock_date_values.items():
if lock_date and lock_date > fields.Date.context_today(self):
raise UserError(_('You cannot set a Lock Date in the future.'))
# We do not change fields for which we add an exception
if exception_vals_list:
for exception_vals in exception_vals_list:
for field in LOCK_DATE_FIELDS:
if field in exception_vals:
lock_date_values.pop(field, None)
return lock_date_values
def _prepare_exception_values(self):
self.ensure_one()
changes_needing_exception = self._get_changes_needing_exception()
if not changes_needing_exception:
return False
# Exceptions for everyone and forever are just "normal" changes to the lock date.
if self.exception_applies_to == 'everyone' and self.exception_duration == 'forever':
return False
exception_errors = []
if not self.exception_applies_to:
exception_errors.append(_('You need to select who the exception applies to.'))
if not self.exception_duration:
exception_errors.append(_('You need to select a duration for the exception.'))
if exception_errors:
raise UserError('\n'.join(exception_errors))
exception_base_values = {
'company_id': self.env.company.id,
}
exception_base_values['user_id'] = {
'me': self.env.user.id,
'everyone': False,
}[self.exception_applies_to]
exception_timedelta = {
'5min': timedelta(minutes=5),
'15min': timedelta(minutes=15),
'1h': timedelta(hours=1),
'24h': timedelta(hours=24),
'forever': False,
}[self.exception_duration]
if exception_timedelta:
exception_base_values['end_datetime'] = self.env.cr.now() + exception_timedelta
if self.exception_reason:
exception_base_values['reason'] = self.exception_reason
exception_vals_list = [
{
**exception_base_values,
field: value,
}
for field, value in changes_needing_exception.items()
]
return exception_vals_list
def _get_current_period_dates(self, lock_date_field):
""" Gets the date_from - either the previous lock date or the start of the fiscal year.
"""
self.ensure_one()
company_lock_date = self.env.company[lock_date_field]
if company_lock_date:
date_from = company_lock_date + timedelta(days=1)
else:
date_from = date_utils.get_fiscal_year(self[lock_date_field])[0]
return date_from, self[lock_date_field]
def _create_default_report_external_values(self, lock_date_field):
# to be overriden
pass
def _change_lock_date(self, lock_date_values=None):
self.ensure_one()
if lock_date_values is None:
lock_date_values = self._prepare_lock_date_values()
# Possibly create default report external values for tax
tax_lock_date = lock_date_values.get('tax_lock_date', None)
if tax_lock_date and tax_lock_date != self.env.company['tax_lock_date']:
self._create_default_report_external_values('tax_lock_date')
# Possibly create default report external values for fiscal year
fiscalyear_lock_date = lock_date_values.get('fiscalyear_lock_date', None)
hard_lock_date = lock_date_values.get('hard_lock_date', None)
if fiscalyear_lock_date or hard_lock_date:
fiscal_lock_date, field = max([
(fiscalyear_lock_date, 'fiscalyear_lock_date'),
(hard_lock_date, 'hard_lock_date'),
], key=lambda t: t[0] or date.min)
company_fiscal_lock_date = max(
self.env.company.fiscalyear_lock_date or date.min,
self.env.company.hard_lock_date or date.min,
)
if fiscal_lock_date != company_fiscal_lock_date:
self._create_default_report_external_values(field)
self.env.company.sudo().write(lock_date_values)
def change_lock_date(self):
self.ensure_one()
if self.env.user.has_group('account.group_account_manager'):
exception_vals_list = self._prepare_exception_values()
changed_lock_date_values = self._prepare_lock_date_values(exception_vals_list=exception_vals_list)
if exception_vals_list:
self.env['account.lock_exception'].create(exception_vals_list)
self._change_lock_date(changed_lock_date_values)
else:
raise UserError(_('Only Billing Administrators are allowed to change lock dates!'))
return {'type': 'ir.actions.act_window_close'}
def action_show_draft_moves_in_locked_period(self):
self.ensure_one()
return {
'view_mode': 'list',
'name': _('Draft Entries'),
'res_model': 'account.move',
'type': 'ir.actions.act_window',
'domain': self._get_draft_moves_in_locked_period_domain(),
'search_view_id': [self.env.ref('account.view_account_move_filter').id, 'search'],
'views': [[self.env.ref('account.view_move_tree_multi_edit').id, 'list'], [self.env.ref('account.view_move_form').id, 'form']],
}
def action_reopen_wizard(self):
# This action can be used to keep the wizard open after doing something else
return {
'type': 'ir.actions.act_window',
'res_model': self._name,
'res_id': self.id,
'view_mode': 'form',
'target': 'new',
}
def _action_revoke_min_exception(self, exception_field):
self.ensure_one()
exception = self[exception_field]
if exception:
exception.action_revoke()
self._compute_lock_date_exceptions()
return self.action_reopen_wizard()
def action_revoke_min_sale_lock_date_exception_for_me(self):
return self._action_revoke_min_exception('min_sale_lock_date_exception_for_me_id')
def action_revoke_min_purchase_lock_date_exception_for_me(self):
return self._action_revoke_min_exception('min_purchase_lock_date_exception_for_me_id')
def action_revoke_min_tax_lock_date_exception_for_me(self):
return self._action_revoke_min_exception('min_tax_lock_date_exception_for_me_id')
def action_revoke_min_fiscalyear_lock_date_exception_for_me(self):
return self._action_revoke_min_exception('min_fiscalyear_lock_date_exception_for_me_id')
def action_revoke_min_sale_lock_date_exception_for_everyone(self):
return self._action_revoke_min_exception('min_sale_lock_date_exception_for_everyone_id')
def action_revoke_min_purchase_lock_date_exception_for_everyone(self):
return self._action_revoke_min_exception('min_purchase_lock_date_exception_for_everyone_id')
def action_revoke_min_tax_lock_date_exception_for_everyone(self):
return self._action_revoke_min_exception('min_tax_lock_date_exception_for_everyone_id')
def action_revoke_min_fiscalyear_lock_date_exception_for_everyone(self):
return self._action_revoke_min_exception('min_fiscalyear_lock_date_exception_for_everyone_id')
def _create_default_report_external_values(self, lock_date_field):
"""
Calls the _generate_default_external_values in account_report
to create default external values for either all reports except the tax reports,
or only the tax reports, depending on the lock date type:
- max(fiscalyear_lock_date, hard_lock_date) is used to create default values in all reports except the tax reports for that date
- tax_lock_date is used to create default values only in tax reports for that date
"""
# extends account.accountant
date_from, date_to = self._get_current_period_dates(lock_date_field)
self.env['account.report']._generate_default_external_values(date_from, date_to, lock_date_field == 'tax_lock_date')