From 02ff1dbbf8d5fa722c842406a383dbff5a25ada9 Mon Sep 17 00:00:00 2001 From: japat-odoo Date: Mon, 16 Feb 2026 12:45:07 +0530 Subject: [PATCH] [ADD] project_recognition_sync: sync project planned date with journal item MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Extend project.project to add is_recognition_required compute field that checks if any posted income/expense journal items have a recognition_date different from the project date_start. - Add a warning banner and a “Recognise the invoices” button on the project form when recognition is required. - Extend account.move.line to add a recognition_date field and override action_automatic_entry to prefill the wizard default_date from the related project. - Extend the automatic entry wizard (`account.automatic.entry.wizard`) to write the selected date into recognition_date for selected journal items. --- project_recognition_sync/__init__.py | 2 + project_recognition_sync/__manifest__.py | 15 +++++ project_recognition_sync/models/__init__.py | 2 + .../models/account_move_line.py | 25 +++++++++ .../models/project_project.py | 56 +++++++++++++++++++ .../views/project_project_view.xml | 32 +++++++++++ project_recognition_sync/wizard/__init__.py | 1 + .../wizard/automatic_entry_wizard.py | 13 +++++ 8 files changed, 146 insertions(+) create mode 100644 project_recognition_sync/__init__.py create mode 100644 project_recognition_sync/__manifest__.py create mode 100644 project_recognition_sync/models/__init__.py create mode 100644 project_recognition_sync/models/account_move_line.py create mode 100644 project_recognition_sync/models/project_project.py create mode 100644 project_recognition_sync/views/project_project_view.xml create mode 100644 project_recognition_sync/wizard/__init__.py create mode 100644 project_recognition_sync/wizard/automatic_entry_wizard.py diff --git a/project_recognition_sync/__init__.py b/project_recognition_sync/__init__.py new file mode 100644 index 00000000000..9b4296142f4 --- /dev/null +++ b/project_recognition_sync/__init__.py @@ -0,0 +1,2 @@ +from . import models +from . import wizard diff --git a/project_recognition_sync/__manifest__.py b/project_recognition_sync/__manifest__.py new file mode 100644 index 00000000000..f48defd5816 --- /dev/null +++ b/project_recognition_sync/__manifest__.py @@ -0,0 +1,15 @@ +{ + "name": "Linking project date", + "version": "1.0", + "author": "japat", + "depends": [ + "project", + "account", + "sale_management", + ], + "data": [ + "views/project_project_view.xml", + ], + "installable": True, + "license": "LGPL-3", +} diff --git a/project_recognition_sync/models/__init__.py b/project_recognition_sync/models/__init__.py new file mode 100644 index 00000000000..6f2071f2070 --- /dev/null +++ b/project_recognition_sync/models/__init__.py @@ -0,0 +1,2 @@ +from . import project_project +from . import account_move_line diff --git a/project_recognition_sync/models/account_move_line.py b/project_recognition_sync/models/account_move_line.py new file mode 100644 index 00000000000..617cd7a9123 --- /dev/null +++ b/project_recognition_sync/models/account_move_line.py @@ -0,0 +1,25 @@ +from odoo import fields, models + + +class AccountMoveLine(models.Model): + _inherit = "account.move.line" + + recognition_date = fields.Date( + string="Recognition Date", + help="Reloaded date from Planned date of corresponding project of sale order", + ) + + def action_automatic_entry(self, default_action=None): + result = super().action_automatic_entry(default_action) + + ctx = dict(result.get("context", {})) + + project = self.move_id.line_ids.mapped( + "sale_line_ids.order_id.project_id" + ).filtered(lambda p: p.date_start) + + if project: + ctx["default_date"] = project[0].date_start + + result["context"] = ctx + return result diff --git a/project_recognition_sync/models/project_project.py b/project_recognition_sync/models/project_project.py new file mode 100644 index 00000000000..4cdfa0dc5e4 --- /dev/null +++ b/project_recognition_sync/models/project_project.py @@ -0,0 +1,56 @@ +from odoo import api, fields, models + + +class ProjectProject(models.Model): + _inherit = "project.project" + + is_recognition_required = fields.Boolean(compute="_compute_is_recognition_required") + + @api.depends("date_start", "sale_order_id.invoice_ids.line_ids.recognition_date") + def _compute_is_recognition_required(self): + for project in self: + project.is_recognition_required = False + + if not project.sale_order_id or not project.date_start: + continue + + journal_items = self.env["account.move.line"].search( + [ + ("move_id", "in", project.sale_order_id.invoice_ids.ids), + ("account_id.internal_group", "in", ["income", "expense"]), + ("parent_state", "=", "posted"), + ("recognition_date", "!=", project.date_start), + ], + limit=1, + ) + + project.is_recognition_required = bool(journal_items) + + def open_cut_off_wizard(self): + self.ensure_one() + + journal_items = self.env["account.move.line"].search( + [ + ("move_id", "in", self.sale_order_id.invoice_ids.ids), + ("account_id.internal_group", "in", ["income", "expense"]), + ("parent_state", "=", "posted"), + ("recognition_date", "!=", self.date_start), + ] + ) + + action = self.env["ir.actions.act_window"]._for_xml_id( + "account.account_automatic_entry_wizard_action" + ) + + ctx = dict(self.env.context or {}) + ctx.update( + { + "active_ids": journal_items.ids, + "active_model": "account.move.line", + "default_action": "change_period", + "default_date": self.date_start, + } + ) + + action["context"] = ctx + return action diff --git a/project_recognition_sync/views/project_project_view.xml b/project_recognition_sync/views/project_project_view.xml new file mode 100644 index 00000000000..b327d21d5f4 --- /dev/null +++ b/project_recognition_sync/views/project_project_view.xml @@ -0,0 +1,32 @@ + + + + + project.project.form.inherit.recognition + project.project + + + + +