Skip to content
Snippets Groups Projects
account_period.py 5.35 KiB
Newer Older
Houzefa Abbasbhay's avatar
Houzefa Abbasbhay committed
##############################################################################
Houzefa Abbasbhay's avatar
Houzefa Abbasbhay committed
#    Accounting periods, for Odoo
#    Copyright © 2018, 2021 XCG Consulting <https://xcg-consulting.fr>
#
#    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/>.
#
Houzefa Abbasbhay's avatar
Houzefa Abbasbhay committed
##############################################################################
from odoo import _, api, exceptions, fields, models, tools


class AccountPeriod(models.Model):
    """Account period defined an accounting period.

    This module aims to recreate the same behaviour as the account.period model from
    previous versions of Odoo.
    _name = "account.period"
    _description = "Accounting period"
    _order = "date_start"
arthur.mayer's avatar
arthur.mayer committed
    date_range_id = fields.Many2one(
        string="Date Range",
        comodel_name="date.range",
        delegate=True,
        ondelete="cascade",
        index=True,
        required=True,
    )

    date_cutoff = fields.Date(
        string="Cut-off date",
        help=(
            "Optional cut-off date which helps force transaction dates and "
            "this period for a few days after the end of this period."
arthur.mayer's avatar
arthur.mayer committed
    @api.depends("date_cutoff", "date_end")
    def _get_date_effective_cutoff(self):
        for period in self:
arthur.mayer's avatar
arthur.mayer committed
            period.date_effective_cutoff = period.date_cutoff or period.date_end

    date_effective_cutoff = fields.Date(
        compute=_get_date_effective_cutoff,
        string="Effective cut-off date",
        help="The cut-off date when defined; the ending date otherwise.",
        readonly=True,
        store=True,
    )

arthur.mayer's avatar
arthur.mayer committed
    fiscal_year_id = fields.Many2one(
        comodel_name="account.fiscal.year",
        string="Fiscal year",
        help="The fiscal year this period is in.",
        required=True,
Vincent Hatakeyama's avatar
Vincent Hatakeyama committed
        index=True,
    )

    state = fields.Selection(
        selection=[("draft", "Open"), ("done", "Closed")],
        string="State",
        default="draft",
        readonly=True,
        required=True,
    def reopen_period(self):
        """Called by a button to re-open a closed period."""

        self.ensure_one()

        self.state = "draft"

    def unlink(self):
        date_range_record = self.date_range_id
        res = super().unlink()
        date_range_record.unlink()
        return res

    def find(self, date=None):
        if not date:
            date = fields.Date.context_today(self)

        company = self.env.company
        domain = [
            ("company_id", "=", company.id),
            ("date_start", "<=", date),
            ("date_effective_cutoff", ">=", date),
        ]
        period = self.search(
            domain + [("state", "!=", "done")],
            limit=1,
        )

        if not period:
            # Create missing periods on-the-fly when running tests / DB populate.
            if (
                tools.config["test_enable"]
                or tools.config["test_file"]
                or self.env.context.get("install_demo", False)
                or hasattr(self.env.registry, "populated_models")
            ) and (
                not self.env.context.get("period_close_enable")
                or not self.env["account.period"].search(
                    domain + [("state", "=", "done")],
                    limit=1,
                )
            ):
                year_start = date.replace(day=1, month=1)
                year_end = date.replace(day=31, month=12)
                fiscalyear = (
                    self.env["account.fiscal.year"]
                    .sudo()
                    .search(
                        [
                            ("company_id", "=", company.id),
                            ("date_from", "<=", year_start),
                            ("date_to", ">=", year_end),
                        ],
                        limit=1,
                    )
                )
                if not fiscalyear:
                    fiscalyear = (
                        self.env["account.fiscal.year"]
                        .sudo()
                        .create(
                            {
                                "company_id": company.id,
                                "date_from": year_start,
                                "date_to": year_end,
                                "name": str(date.year),
                            }
                        )
                    )
                    fiscalyear.create_periods()
                period = self.env["account.period"].search(
                    domain + [("state", "!=", "done")],
                    limit=1,
                )

            else:
                raise exceptions.UserError(
                    _("No period found around %(date)s in the company %(name)s.")
                    % {"date": date, "name": company.sudo().name}
                )

        return period