-
Houzefa Abbasbhay authored
Specifics to play better with accounting changes: * No longer change the base ``date`` field; add ``accounting_date`` instead. * Build periods when loading accounting data for a new company.
Houzefa Abbasbhay authoredSpecifics to play better with accounting changes: * No longer change the base ``date`` field; add ``accounting_date`` instead. * Build periods when loading accounting data for a new company.
account_fiscalyear.py 7.03 KiB
##############################################################################
#
# Accounting periods, for Odoo
# Copyright (C) 2018 XCG Consulting <http://odoo.consulting>
#
# 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 dateutil.relativedelta import relativedelta
from odoo import SUPERUSER_ID, _, api, fields, models
from odoo.exceptions import ValidationError
_logger = logging.getLogger(__name__)
class AccountFiscalyear(models.Model):
"""Account fiscal year defined an accounting year.
"""
_name = "account.fiscalyear"
_description = "Fiscal year"
_order = "date_start, id"
code = fields.Char(
string="Code",
help="Unique code of this fiscal year.",
index=True,
required=True,
)
company_id = fields.Many2one(
comodel_name="res.company",
string="Company",
default=lambda rec: rec.env.company,
help="The company the fiscal year is in.",
required=True,
index=True,
)
name = fields.Char(
string="Name",
help="Displayed name of this fiscal year.",
required=True,
)
period_ids = fields.One2many(
comodel_name="account.period",
inverse_name="fiscalyear_id",
string="Periods",
help="The periods inside this fiscal year.",
)
date_start = fields.Date(
compute="_compute_date_start",
string="Start Date",
store=True,
# required=True,
)
# TODO should not be changed after creation
date_stop = fields.Date(
string="Stop Date", help="When this fiscal year ends", required=True
)
_sql_constraints = [
(
"unique_code_per_company",
"UNIQUE(code, company_id)",
"The code must be unique.",
)
]
@api.depends("date_stop")
def _compute_date_start(self):
for fiscalyear in self:
if not fiscalyear.date_stop:
continue
fiscalyear.date_start = (
fiscalyear.date_stop - fiscalyear._get_duration()
)
@api.constrains("date_stop")
def _check_date_stop(self):
""" Check end date value when fiscal
year last day/month is provided.
"""
config_fy_month = self.env.company.fiscalyear_last_month # str
config_fy_day = self.env.company.fiscalyear_last_day # int
if not config_fy_day and not config_fy_month:
return
ed = self.date_stop
if str(ed.month) != config_fy_month or ed.day != config_fy_day:
raise ValidationError(
_(
"The end date must match the fiscal "
"year general config (%s/%s)"
)
% (config_fy_month, config_fy_day)
)
def create_period(self):
""" Call by the "Create Periods" Button.
This function use "date_stop" field to generate
automatically all periods.
"""
for fy in self:
ed = fy.date_stop
sd = ed - fy._get_duration()
while sd < ed:
period_ed = sd + relativedelta(months=1, days=-1)
if period_ed > fy.date_stop:
period_ed = fy.date_stop
fy.write(
{
"period_ids": [
(
0,
0,
{
"name": sd.strftime("%m/%Y"),
"code": sd.strftime("%m/%Y"),
"date_start": sd,
"date_stop": period_ed,
},
)
]
}
)
sd = sd + relativedelta(months=1)
return True
def _get_duration(self):
"""
:return: relativedelta of duration of a fiscal year
"""
# This is a method so it can be changed in the case of needing to
# handle shorter or longer fiscal year
return relativedelta(months=12, days=-1)
def action_realloc_period_am(self):
""" Call by the "Reallocate Periods" action.
Look up all accounts move where periods is empty and
try to reallocate it.
"""
for record in self:
# Only administrator can reallocate periods.
if not record.env.uid == SUPERUSER_ID:
raise ValidationError(
_(
"It is not possible to run this action,"
"please contact your administrator."
)
)
account_moves = record.env["account.move"].search(
[("period_id", "=", False), ("state", "=", "posted")]
)
for am in account_moves:
# Cache some data.
company = am.company_id
# Periods are ordered by date so selecting the first one is
# fine.
period = record.env["account.period"].search(
[
("company_id", "=", company.id),
("date_start", "<=", am.date),
("date_stop", ">=", am.date),
],
limit=1,
)
if period:
_logger.info(
"reallocate period :%s: onto account move :%s:",
period.code,
am.name,
)
am.write(
{"period_id": period.id, "transaction_date": am.date}
)
def find(self, dt=None):
"""
:param dt: date (odoo format so string), if None, use today
:return: fiscal years that include the indicated date, using either
the company_id in the context or if not set, the user company_id
"""
if not dt:
dt = fields.Date.context_today(self)
company_id = self.env.context.get("company_id") or self.env.company.id
return self.search(
[
("date_start", "<=", dt),
("date_stop", ">=", dt),
("company_id", "=", company_id),
]
)