############################################################################## # # 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 datetime from odoo import _, api, exceptions, fields, models class AccountMove(models.Model): """Add a period & dates onto accounting documents. """ _inherit = "account.move" period_id = fields.Many2one( comodel_name="account.period", string="Period", help="The period this accounting document is in.", ondelete="restrict", states={"posted": [("readonly", True), ("required", True)]}, ) transaction_date = fields.Date( string="Transaction date", help=( "Invoicing date when provided; otherwise this is the accounting " "date." ), states={"posted": [("readonly", True)]}, ) @api.model def create(self, vals): """Override to set a transaction date from invoices. """ if not vals.get("date"): vals["date"] = fields.Date.context_today(self) invoice = self.env.context.get("invoice") if invoice and isinstance(invoice, models.BaseModel): if not isinstance(vals, dict): vals = {} vals["transaction_date"] = invoice.date_invoice return super(AccountMove, self).create(vals) @api.model def fields_get(self, allfields=None, attributes=None): """Override to tweak the default "date" field. """ ret = super(AccountMove, self).fields_get( allfields=allfields, attributes=attributes ) date_field = ret.get("date") if date_field: date_field.update( { "string": _("Accounting date"), "help": _("Validation date of the accounting document."), "readonly": True, "states": {}, } ) return ret @api.multi def post(self): """Override accounting document validation to: - Set accounting dates (default "date" field). - Also set the transaction date ("transaction_date" field) when empty. """ self.fill_accounting_dates() return super(AccountMove, self).post() @api.multi def fill_accounting_dates(self): """- Set accounting dates (default "date" field). - Also set the transaction date ("transaction_date" field) when empty. - Force the period to always be around the current date. """ acc_date = datetime.date.today() for accdoc in self: # Cache some data. company = accdoc.company_id # Set the acc_date only if the force_period_on_date # context has provided. if self.env.context.get("force_period_on_date"): # If accounting document date is empty, get today date. acc_date = fields.Date.from_string(accdoc.date) or acc_date # Periods are ordered by date so selecting the first one is fine. period = self.env["account.period"].search( [ ("company_id", "=", company.id), ("date_start", "<=", acc_date), ("date_effective_cutoff", ">=", acc_date), ], limit=1, ) if not period: raise exceptions.Warning( _('No period found around %s in the "%s" company.') % (acc_date, company.sudo().name) ) # When we are between the period end and cut-off date, force the # last day of the period. in_cutoff = False if accdoc.journal_id.type == "sale": period_end = fields.Date.from_string(period.date_stop) in_cutoff = acc_date > period_end acc_date = period_end if in_cutoff else acc_date # The data to update the accounting document with. accdoc_values = {"date": acc_date, "period_id": period.id} # Set a transaction date when no previous one set. Also, force it # during cut-off. if in_cutoff or not accdoc.transaction_date: accdoc_values["transaction_date"] = acc_date # Ready! Update the accounting document. accdoc.write(accdoc_values) return True