From 058a83650d3bc146a6294944f3d3f925825d3071 Mon Sep 17 00:00:00 2001 From: mohamedakhalil18 Date: Mon, 19 Jan 2026 15:40:26 +0100 Subject: [PATCH 1/8] Estate module declaration and models --- estate/__init__.py | 1 + estate/__manifest__.py | 13 +++++++++++++ estate/models/__init__.py | 1 + estate/models/estate_property.py | 27 +++++++++++++++++++++++++++ 4 files changed, 42 insertions(+) create mode 100644 estate/__init__.py create mode 100644 estate/__manifest__.py create mode 100644 estate/models/__init__.py create mode 100644 estate/models/estate_property.py diff --git a/estate/__init__.py b/estate/__init__.py new file mode 100644 index 00000000000..9a7e03eded3 --- /dev/null +++ b/estate/__init__.py @@ -0,0 +1 @@ +from . import models \ No newline at end of file diff --git a/estate/__manifest__.py b/estate/__manifest__.py new file mode 100644 index 00000000000..4788121f645 --- /dev/null +++ b/estate/__manifest__.py @@ -0,0 +1,13 @@ +{ + 'name': 'Estate Advirtisements', + 'version':'1.0', + 'summary':'Track advertisements added for real estate and allow selling them', + 'website':'https://www.odoo.com/app/estate', + 'depends':[ + 'base' + ], + 'application':True, + 'installable':True, + 'author':'moali', + 'license':'LGPL-3' +} \ No newline at end of file diff --git a/estate/models/__init__.py b/estate/models/__init__.py new file mode 100644 index 00000000000..f23bb1d5ea9 --- /dev/null +++ b/estate/models/__init__.py @@ -0,0 +1 @@ +from . import estate_property \ No newline at end of file diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py new file mode 100644 index 00000000000..67ea0faab0c --- /dev/null +++ b/estate/models/estate_property.py @@ -0,0 +1,27 @@ +from odoo import fields, models + +class EstateProperty(models.Model): + _name = 'estate_property' + _description = 'Estate Property' + + name = fields.Char(string = "Property Name", required = True) + description = fields.Char(string = 'Property Description') + postcode = fields.Char(string = 'Postal Code') + date_availability = fields.Date(string = 'Available From') + expected_price = fields.Float(string = 'Expected Selling Price', required = True) + selling_price = fields.Float(string = 'Actual Selling Price') + bedrooms = fields.Integer(string = 'Number of Bedrooms') + living_area = fields.Float(string = 'Living Area (sqm)') + facades = fields.Integer(string = 'Number of Facades') + garage = fields.Boolean(string = 'Garage') + garden = fields.Boolean(string = 'Garden') + garden_area = fields.Float(string = 'Garden Area (sqm)') + garden_orientation = fields.Selection( + string = 'Garden Orientation', + selection = [ + ('north', 'North'), + ('south', 'South'), + ('east', 'East'), + ('west', 'West') + ] + ) From 6de9b375fb31efe55aa44ce3cfc84073c1b53695 Mon Sep 17 00:00:00 2001 From: mohamedakhalil18 Date: Tue, 20 Jan 2026 11:19:26 +0100 Subject: [PATCH 2/8] added linting and formatting --- awesome_clicker/__manifest__.py | 35 ++++---- awesome_dashboard/__manifest__.py | 36 ++++---- awesome_dashboard/controllers/__init__.py | 2 +- awesome_dashboard/controllers/controllers.py | 22 ++--- awesome_gallery/__manifest__.py | 32 ++++--- awesome_gallery/models/ir_action.py | 8 +- awesome_gallery/models/ir_ui_view.py | 4 +- awesome_kanban/__manifest__.py | 32 ++++--- awesome_owl/__init__.py | 2 +- awesome_owl/__manifest__.py | 53 ++++++------ awesome_owl/controllers/__init__.py | 2 +- awesome_owl/controllers/controllers.py | 5 +- estate/__init__.py | 2 +- estate/__manifest__.py | 22 +++-- estate/models/__init__.py | 2 +- estate/models/estate_property.py | 43 +++++----- ruff.toml | 85 +++++++++++++++++++ setup.cfg | 2 + website_airproof/__manifest__.py | 88 +++++++++++--------- 19 files changed, 278 insertions(+), 199 deletions(-) create mode 100644 ruff.toml create mode 100644 setup.cfg diff --git a/awesome_clicker/__manifest__.py b/awesome_clicker/__manifest__.py index 56dc2f779b9..aa6d3cd7a71 100644 --- a/awesome_clicker/__manifest__.py +++ b/awesome_clicker/__manifest__.py @@ -1,29 +1,24 @@ # -*- coding: utf-8 -*- { - 'name': "Awesome Clicker", - - 'summary': """ + "name": "Awesome Clicker", + "summary": """ Starting module for "Master the Odoo web framework, chapter 1: Build a Clicker game" """, - - 'description': """ + "description": """ Starting module for "Master the Odoo web framework, chapter 1: Build a Clicker game" """, - - 'author': "Odoo", - 'website': "https://www.odoo.com/", - 'category': 'Tutorials', - 'version': '0.1', - 'application': True, - 'installable': True, - 'depends': ['base', 'web'], - - 'data': [], - 'assets': { - 'web.assets_backend': [ - 'awesome_clicker/static/src/**/*', + "author": "Odoo", + "website": "https://www.odoo.com/", + "category": "Tutorials", + "version": "0.1", + "application": True, + "installable": True, + "depends": ["base", "web"], + "data": [], + "assets": { + "web.assets_backend": [ + "awesome_clicker/static/src/**/*", ], - }, - 'license': 'AGPL-3' + "license": "AGPL-3", } diff --git a/awesome_dashboard/__manifest__.py b/awesome_dashboard/__manifest__.py index a1cd72893d7..a86d9bb991e 100644 --- a/awesome_dashboard/__manifest__.py +++ b/awesome_dashboard/__manifest__.py @@ -1,30 +1,26 @@ # -*- coding: utf-8 -*- { - 'name': "Awesome Dashboard", - - 'summary': """ + "name": "Awesome Dashboard", + "summary": """ Starting module for "Discover the JS framework, chapter 2: Build a dashboard" """, - - 'description': """ + "description": """ Starting module for "Discover the JS framework, chapter 2: Build a dashboard" """, - - 'author': "Odoo", - 'website': "https://www.odoo.com/", - 'category': 'Tutorials', - 'version': '0.1', - 'application': True, - 'installable': True, - 'depends': ['base', 'web', 'mail', 'crm'], - - 'data': [ - 'views/views.xml', + "author": "Odoo", + "website": "https://www.odoo.com/", + "category": "Tutorials", + "version": "0.1", + "application": True, + "installable": True, + "depends": ["base", "web", "mail", "crm"], + "data": [ + "views/views.xml", ], - 'assets': { - 'web.assets_backend': [ - 'awesome_dashboard/static/src/**/*', + "assets": { + "web.assets_backend": [ + "awesome_dashboard/static/src/**/*", ], }, - 'license': 'AGPL-3' + "license": "AGPL-3", } diff --git a/awesome_dashboard/controllers/__init__.py b/awesome_dashboard/controllers/__init__.py index 457bae27e11..b0f26a9a602 100644 --- a/awesome_dashboard/controllers/__init__.py +++ b/awesome_dashboard/controllers/__init__.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- -from . import controllers \ No newline at end of file +from . import controllers diff --git a/awesome_dashboard/controllers/controllers.py b/awesome_dashboard/controllers/controllers.py index 05977d3bd7f..2e408affe36 100644 --- a/awesome_dashboard/controllers/controllers.py +++ b/awesome_dashboard/controllers/controllers.py @@ -8,8 +8,9 @@ logger = logging.getLogger(__name__) + class AwesomeDashboard(http.Controller): - @http.route('/awesome_dashboard/statistics', type='jsonrpc', auth='user') + @http.route("/awesome_dashboard/statistics", type="jsonrpc", auth="user") def get_statistics(self): """ Returns a dict of statistics about the orders: @@ -22,15 +23,14 @@ def get_statistics(self): """ return { - 'average_quantity': random.randint(4, 12), - 'average_time': random.randint(4, 123), - 'nb_cancelled_orders': random.randint(0, 50), - 'nb_new_orders': random.randint(10, 200), - 'orders_by_size': { - 'm': random.randint(0, 150), - 's': random.randint(0, 150), - 'xl': random.randint(0, 150), + "average_quantity": random.randint(4, 12), + "average_time": random.randint(4, 123), + "nb_cancelled_orders": random.randint(0, 50), + "nb_new_orders": random.randint(10, 200), + "orders_by_size": { + "m": random.randint(0, 150), + "s": random.randint(0, 150), + "xl": random.randint(0, 150), }, - 'total_amount': random.randint(100, 1000) + "total_amount": random.randint(100, 1000), } - diff --git a/awesome_gallery/__manifest__.py b/awesome_gallery/__manifest__.py index f0fe026a9c6..6eaeaa6f83f 100644 --- a/awesome_gallery/__manifest__.py +++ b/awesome_gallery/__manifest__.py @@ -1,27 +1,25 @@ # -*- coding: utf-8 -*- { - 'name': "Gallery View", - 'summary': """ + "name": "Gallery View", + "summary": """ Starting module for "Master the Odoo web framework, chapter 3: Create a Gallery View" """, - - 'description': """ + "description": """ Starting module for "Master the Odoo web framework, chapter 3: Create a Gallery View" """, - - 'version': '0.1', - 'application': True, - 'category': 'Tutorials', - 'installable': True, - 'depends': ['web', 'contacts'], - 'data': [ - 'views/views.xml', + "version": "0.1", + "application": True, + "category": "Tutorials", + "installable": True, + "depends": ["web", "contacts"], + "data": [ + "views/views.xml", ], - 'assets': { - 'web.assets_backend': [ - 'awesome_gallery/static/src/**/*', + "assets": { + "web.assets_backend": [ + "awesome_gallery/static/src/**/*", ], }, - 'author': 'Odoo S.A.', - 'license': 'AGPL-3' + "author": "Odoo S.A.", + "license": "AGPL-3", } diff --git a/awesome_gallery/models/ir_action.py b/awesome_gallery/models/ir_action.py index eae20acbf5c..45dc4a2bb71 100644 --- a/awesome_gallery/models/ir_action.py +++ b/awesome_gallery/models/ir_action.py @@ -3,8 +3,8 @@ class ActWindowView(models.Model): - _inherit = 'ir.actions.act_window.view' + _inherit = "ir.actions.act_window.view" - view_mode = fields.Selection(selection_add=[ - ('gallery', "Awesome Gallery") - ], ondelete={'gallery': 'cascade'}) + view_mode = fields.Selection( + selection_add=[("gallery", "Awesome Gallery")], ondelete={"gallery": "cascade"} + ) diff --git a/awesome_gallery/models/ir_ui_view.py b/awesome_gallery/models/ir_ui_view.py index 0c11b8298ac..555008c371f 100644 --- a/awesome_gallery/models/ir_ui_view.py +++ b/awesome_gallery/models/ir_ui_view.py @@ -3,6 +3,6 @@ class View(models.Model): - _inherit = 'ir.ui.view' + _inherit = "ir.ui.view" - type = fields.Selection(selection_add=[('gallery', "Awesome Gallery")]) + type = fields.Selection(selection_add=[("gallery", "Awesome Gallery")]) diff --git a/awesome_kanban/__manifest__.py b/awesome_kanban/__manifest__.py index 6f31bc8de0d..1dc8791b97b 100644 --- a/awesome_kanban/__manifest__.py +++ b/awesome_kanban/__manifest__.py @@ -1,27 +1,25 @@ # -*- coding: utf-8 -*- { - 'name': "Awesome Kanban", - 'summary': """ + "name": "Awesome Kanban", + "summary": """ Starting module for "Master the Odoo web framework, chapter 4: Customize a kanban view" """, - - 'description': """ + "description": """ Starting module for "Master the Odoo web framework, chapter 4: Customize a kanban view. """, - - 'version': '0.1', - 'application': True, - 'category': 'Tutorials', - 'installable': True, - 'depends': ['web', 'crm'], - 'data': [ - 'views/views.xml', + "version": "0.1", + "application": True, + "category": "Tutorials", + "installable": True, + "depends": ["web", "crm"], + "data": [ + "views/views.xml", ], - 'assets': { - 'web.assets_backend': [ - 'awesome_kanban/static/src/**/*', + "assets": { + "web.assets_backend": [ + "awesome_kanban/static/src/**/*", ], }, - 'author': 'Odoo S.A.', - 'license': 'AGPL-3' + "author": "Odoo S.A.", + "license": "AGPL-3", } diff --git a/awesome_owl/__init__.py b/awesome_owl/__init__.py index 457bae27e11..b0f26a9a602 100644 --- a/awesome_owl/__init__.py +++ b/awesome_owl/__init__.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- -from . import controllers \ No newline at end of file +from . import controllers diff --git a/awesome_owl/__manifest__.py b/awesome_owl/__manifest__.py index 55002ab81de..197652f6027 100644 --- a/awesome_owl/__manifest__.py +++ b/awesome_owl/__manifest__.py @@ -1,43 +1,38 @@ # -*- coding: utf-8 -*- { - 'name': "Awesome Owl", - - 'summary': """ + "name": "Awesome Owl", + "summary": """ Starting module for "Discover the JS framework, chapter 1: Owl components" """, - - 'description': """ + "description": """ Starting module for "Discover the JS framework, chapter 1: Owl components" """, - - 'author': "Odoo", - 'website': "https://www.odoo.com", - + "author": "Odoo", + "website": "https://www.odoo.com", # Categories can be used to filter modules in modules listing # Check https://github.com/odoo/odoo/blob/15.0/odoo/addons/base/data/ir_module_category_data.xml # for the full list - 'category': 'Tutorials', - 'version': '0.1', - + "category": "Tutorials", + "version": "0.1", # any module necessary for this one to work correctly - 'depends': ['base', 'web'], - 'application': True, - 'installable': True, - 'data': [ - 'views/templates.xml', + "depends": ["base", "web"], + "application": True, + "installable": True, + "data": [ + "views/templates.xml", ], - 'assets': { - 'awesome_owl.assets_playground': [ - ('include', 'web._assets_helpers'), - ('include', 'web._assets_backend_helpers'), - 'web/static/src/scss/pre_variables.scss', - 'web/static/lib/bootstrap/scss/_variables.scss', - 'web/static/lib/bootstrap/scss/_maps.scss', - ('include', 'web._assets_bootstrap'), - ('include', 'web._assets_core'), - 'web/static/src/libs/fontawesome/css/font-awesome.css', - 'awesome_owl/static/src/**/*', + "assets": { + "awesome_owl.assets_playground": [ + ("include", "web._assets_helpers"), + ("include", "web._assets_backend_helpers"), + "web/static/src/scss/pre_variables.scss", + "web/static/lib/bootstrap/scss/_variables.scss", + "web/static/lib/bootstrap/scss/_maps.scss", + ("include", "web._assets_bootstrap"), + ("include", "web._assets_core"), + "web/static/src/libs/fontawesome/css/font-awesome.css", + "awesome_owl/static/src/**/*", ], }, - 'license': 'AGPL-3' + "license": "AGPL-3", } diff --git a/awesome_owl/controllers/__init__.py b/awesome_owl/controllers/__init__.py index 457bae27e11..b0f26a9a602 100644 --- a/awesome_owl/controllers/__init__.py +++ b/awesome_owl/controllers/__init__.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- -from . import controllers \ No newline at end of file +from . import controllers diff --git a/awesome_owl/controllers/controllers.py b/awesome_owl/controllers/controllers.py index bccfd6fe283..4da2f03a9f3 100644 --- a/awesome_owl/controllers/controllers.py +++ b/awesome_owl/controllers/controllers.py @@ -1,10 +1,11 @@ from odoo import http from odoo.http import request, route + class OwlPlayground(http.Controller): - @http.route(['/awesome_owl'], type='http', auth='public') + @http.route(["/awesome_owl"], type="http", auth="public") def show_playground(self): """ Renders the owl playground page """ - return request.render('awesome_owl.playground') + return request.render("awesome_owl.playground") diff --git a/estate/__init__.py b/estate/__init__.py index 9a7e03eded3..0650744f6bc 100644 --- a/estate/__init__.py +++ b/estate/__init__.py @@ -1 +1 @@ -from . import models \ No newline at end of file +from . import models diff --git a/estate/__manifest__.py b/estate/__manifest__.py index 4788121f645..958acb3875d 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -1,13 +1,11 @@ { - 'name': 'Estate Advirtisements', - 'version':'1.0', - 'summary':'Track advertisements added for real estate and allow selling them', - 'website':'https://www.odoo.com/app/estate', - 'depends':[ - 'base' - ], - 'application':True, - 'installable':True, - 'author':'moali', - 'license':'LGPL-3' -} \ No newline at end of file + "name": "Estate Advirtisements", + "version": "1.0", + "summary": "Track advertisements added for real estate and allow selling them", + "website": "https://www.odoo.com/app/estate", + "depends": ["base"], + "application": True, + "installable": True, + "author": "moali", + "license": "LGPL-3", +} diff --git a/estate/models/__init__.py b/estate/models/__init__.py index f23bb1d5ea9..5e1963c9d2f 100644 --- a/estate/models/__init__.py +++ b/estate/models/__init__.py @@ -1 +1 @@ -from . import estate_property \ No newline at end of file +from . import estate_property diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 67ea0faab0c..152fe47ab5f 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -1,27 +1,28 @@ from odoo import fields, models + class EstateProperty(models.Model): - _name = 'estate_property' - _description = 'Estate Property' + _name = "estate_property" + _description = "Estate Property" - name = fields.Char(string = "Property Name", required = True) - description = fields.Char(string = 'Property Description') - postcode = fields.Char(string = 'Postal Code') - date_availability = fields.Date(string = 'Available From') - expected_price = fields.Float(string = 'Expected Selling Price', required = True) - selling_price = fields.Float(string = 'Actual Selling Price') - bedrooms = fields.Integer(string = 'Number of Bedrooms') - living_area = fields.Float(string = 'Living Area (sqm)') - facades = fields.Integer(string = 'Number of Facades') - garage = fields.Boolean(string = 'Garage') - garden = fields.Boolean(string = 'Garden') - garden_area = fields.Float(string = 'Garden Area (sqm)') + name = fields.Char(string="Property Name", required=True) + description = fields.Char(string="Property Description") + postcode = fields.Char(string="Postal Code") + date_availability = fields.Date(string="Available From") + expected_price = fields.Float(string="Expected Selling Price", required=True) + selling_price = fields.Float(string="Actual Selling Price") + bedrooms = fields.Integer(string="Number of Bedrooms") + living_area = fields.Float(string="Living Area (sqm)") + facades = fields.Integer(string="Number of Facades") + garage = fields.Boolean(string="Garage") + garden = fields.Boolean(string="Garden") + garden_area = fields.Float(string="Garden Area (sqm)") garden_orientation = fields.Selection( - string = 'Garden Orientation', - selection = [ - ('north', 'North'), - ('south', 'South'), - ('east', 'East'), - ('west', 'West') - ] + string="Garden Orientation", + selection=[ + ("north", "North"), + ("south", "South"), + ("east", "East"), + ("west", "West"), + ], ) diff --git a/ruff.toml b/ruff.toml new file mode 100644 index 00000000000..6a7ce0e3a7e --- /dev/null +++ b/ruff.toml @@ -0,0 +1,85 @@ +# automatically generated file by the runbot nightly ruff checks, do not modify +# for ruff version 0.11.4 (or higher) +# note: 'E241', 'E272', 'E201', 'E221' are ignored on runbot in test files when formating a table like structure (more than two space) +# some rules present here are not enabled on runbot (yet) but are still advised to follow when possible : ["B904", "COM812", "E741", "EM101", "I001", "RET", "RUF021", "TRY002", "UP006", "UP007"] + + +target-version = "py310" + +[lint] +preview = true +select = [ + "BLE", # flake8-blind-except + "C", # flake8-comprehensions + "COM", # flake8-commas + "E", # pycodestyle Error + "EM", # flake8-errmsg + "EXE", # flake8-executable + "F", # Pyflakes + "FA", # flake8-future-annotations + "FLY", # flynt + "G", # flake8-logging-format + "I", # isort + "ICN", # flake8-import-conventions + "INT", # flake8-gettext + "ISC", # flake8-implicit-str-concat + "LOG", # flake8-logging + "PGH", # pygrep-hooks + "PIE", # flake8-pie + "PLC", # Pylint Convention + "PLE", # Pylint Error + "PLW", # Pylint Warning + "PYI", # flake8-pyi + "RET", # flake8-return + "RUF", # Ruff-specific rules + "SIM", # flake8-simplify + "SLOT", # flake8-slots + "T", # flake8-print + "TC", # flake8-type-checking + "TID", # flake8-tidy-imports + "TRY", # tryceratops + "UP", # pyupgrade + "W", # pycodestyle Warning + "YTT", # flake8-2020 +] +ignore = [ + "C408", # unnecessary-collection-call + "C420", # unnecessary-dict-comprehension-for-iterable + "C901", # complex-structure + "E266", # multiple-leading-hashes-for-block-comment + "E501", # line-too-long + "E302", + "E301", + "E713", # not-in-test + "EM102", # f-string-in-exception + "FA100", # future-rewritable-type-annotation + "PGH003", # blanket-type-ignore + "PIE790", # unnecessary-placeholder + "PIE808", # unnecessary-range-start + "PLC2701", # import-private-name + "PLW2901", # redefined-loop-name + "RUF001", # ambiguous-unicode-character-string + "RUF005", # collection-literal-concatenation + "RUF012", # mutable-class-default + "RUF100", # unused-noqa + "SIM102", # collapsible-if + "SIM108", # if-else-block-instead-of-if-exp + "SIM117", # multiple-with-statements + "TID252", # relative-imports + "TRY003", # raise-vanilla-args + "TRY300", # try-consider-else + "TRY400", # error-instead-of-exception + "UP031", # printf-string-formatting + "UP038", # non-pep604-isinstance +] + +[lint.per-file-ignores] +"**/__init__.py" = [ + "F401", # unused-import +] + +[lint.isort] +# https://www.odoo.com/documentation/master/contributing/development/coding_guidelines.html#imports +section-order = ["future", "standard-library", "third-party", "first-party", "local-folder"] +known-first-party = ["odoo"] +known-local-folder = ["odoo.addons"] diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 00000000000..98ae51447b6 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,2 @@ +[pycodestyle] +ignore = ["E501","E302","E301"] diff --git a/website_airproof/__manifest__.py b/website_airproof/__manifest__.py index 2c2c62d6a18..86294451616 100644 --- a/website_airproof/__manifest__.py +++ b/website_airproof/__manifest__.py @@ -1,59 +1,69 @@ { - 'name': 'Airproof Theme', - 'description': 'Airproof Theme - Drones, modelling, camera', - 'category': 'Website/Theme', + "name": "Airproof Theme", + "description": "Airproof Theme - Drones, modelling, camera", + "category": "Website/Theme", # 'version': '18.0.1.0', - 'author': 'PSBE Designers', - 'license': 'LGPL-3', - 'depends': ['website_sale', 'website_sale_wishlist', 'website_blog', 'website_mass_mailing'], - 'data': [ + "author": "PSBE Designers", + "license": "LGPL-3", + "depends": [ + "website_sale", + "website_sale_wishlist", + "website_blog", + "website_mass_mailing", + ], + "data": [ # Snippets - 'views/snippets/options.xml', - 'views/snippets/s_airproof_carousel.xml', + "views/snippets/options.xml", + "views/snippets/s_airproof_carousel.xml", # Options - 'data/presets.xml', - 'data/website.xml', + "data/presets.xml", + "data/website.xml", # Menu - 'data/menu.xml', + "data/menu.xml", # Gradients - 'data/gradients.xml', + "data/gradients.xml", # Shapes - 'data/shapes.xml', + "data/shapes.xml", # Pages - 'data/pages/home.xml', - 'data/pages/contact.xml', + "data/pages/home.xml", + "data/pages/contact.xml", # Frontend - 'views/new_page_template_templates.xml', - 'views/website_templates.xml', - 'views/website_sale_templates.xml', - 'views/website_sale_wishlist_templates.xml', + "views/new_page_template_templates.xml", + "views/website_templates.xml", + "views/website_sale_templates.xml", + "views/website_sale_wishlist_templates.xml", # Images - 'data/images.xml', + "data/images.xml", ], - 'assets': { - 'web._assets_primary_variables': [ - 'website_airproof/static/src/scss/primary_variables.scss', + "assets": { + "web._assets_primary_variables": [ + "website_airproof/static/src/scss/primary_variables.scss", ], - 'web._assets_frontend_helpers': [ - ('prepend', 'website_airproof/static/src/scss/bootstrap_overridden.scss'), + "web._assets_frontend_helpers": [ + ("prepend", "website_airproof/static/src/scss/bootstrap_overridden.scss"), ], - 'web.assets_frontend': [ + "web.assets_frontend": [ # SCSS - 'website_airproof/static/src/scss/font.scss', - 'website_airproof/static/src/scss/components/mouse_follower.scss', - 'website_airproof/static/src/scss/layout/header.scss', - 'website_airproof/static/src/scss/pages/product_page.scss', - 'website_airproof/static/src/scss/pages/shop.scss', - 'website_airproof/static/src/scss/snippets/caroussel.scss', - 'website_airproof/static/src/scss/snippets/newsletter.scss', - 'website_airproof/static/src/snippets/s_airproof_carousel/000.scss', + "website_airproof/static/src/scss/font.scss", + "website_airproof/static/src/scss/components/mouse_follower.scss", + "website_airproof/static/src/scss/layout/header.scss", + "website_airproof/static/src/scss/pages/product_page.scss", + "website_airproof/static/src/scss/pages/shop.scss", + "website_airproof/static/src/scss/snippets/caroussel.scss", + "website_airproof/static/src/scss/snippets/newsletter.scss", + "website_airproof/static/src/snippets/s_airproof_carousel/000.scss", # JS - 'website_airproof/static/src/js/mouse_follower.js', + "website_airproof/static/src/js/mouse_follower.js", ], }, - 'new_page_templates': { - 'airproof': { - 'services': ['s_parallax', 's_airproof_key_benefits_h2', 's_call_to_action', 's_airproof_carousel'] + "new_page_templates": { + "airproof": { + "services": [ + "s_parallax", + "s_airproof_key_benefits_h2", + "s_call_to_action", + "s_airproof_carousel", + ] } }, } From e1cc94888270df9dfdc4409fdee18724990ec2c9 Mon Sep 17 00:00:00 2001 From: mohamedakhalil18 Date: Tue, 20 Jan 2026 13:10:51 +0100 Subject: [PATCH 3/8] Added access control to my module --- estate/__init__.py | 1 + estate/__manifest__.py | 3 +++ estate/security/ir.model.access.csv | 2 ++ 3 files changed, 6 insertions(+) create mode 100644 estate/security/ir.model.access.csv diff --git a/estate/__init__.py b/estate/__init__.py index 0650744f6bc..f50a0ddf823 100644 --- a/estate/__init__.py +++ b/estate/__init__.py @@ -1 +1,2 @@ from . import models +from . import security diff --git a/estate/__manifest__.py b/estate/__manifest__.py index 958acb3875d..c04b755165f 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -8,4 +8,7 @@ "installable": True, "author": "moali", "license": "LGPL-3", + "data": [ + "security/ir.model.access.csv", + ], } diff --git a/estate/security/ir.model.access.csv b/estate/security/ir.model.access.csv new file mode 100644 index 00000000000..c787bf33445 --- /dev/null +++ b/estate/security/ir.model.access.csv @@ -0,0 +1,2 @@ +id,name,model_id/id,group_id/id,perm_read,perm_write,perm_create,perm_unlink +1,access_estate_property,model_estate_property,base.group_user,1,1,1,1 From 3a2bc4de6ee3084f783eef6e04b435072ab30193 Mon Sep 17 00:00:00 2001 From: mohamedakhalil18 Date: Tue, 20 Jan 2026 15:31:07 +0100 Subject: [PATCH 4/8] Added action and menus to enable user to add properties --- estate/__init__.py | 1 + estate/__manifest__.py | 2 ++ estate/models/estate_property.py | 27 ++++++++++++++++++++++---- estate/views/estate_property_views.xml | 10 ++++++++++ estate/views/menus.xml | 8 ++++++++ 5 files changed, 44 insertions(+), 4 deletions(-) create mode 100644 estate/views/estate_property_views.xml create mode 100644 estate/views/menus.xml diff --git a/estate/__init__.py b/estate/__init__.py index f50a0ddf823..7ca767700cc 100644 --- a/estate/__init__.py +++ b/estate/__init__.py @@ -1,2 +1,3 @@ from . import models from . import security +from . import views diff --git a/estate/__manifest__.py b/estate/__manifest__.py index c04b755165f..67268bf6515 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -10,5 +10,7 @@ "license": "LGPL-3", "data": [ "security/ir.model.access.csv", + "views/estate_property_views.xml", + "views/menus.xml", ], } diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 152fe47ab5f..5a9ac0d298d 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -2,16 +2,22 @@ class EstateProperty(models.Model): - _name = "estate_property" + _name = "estate.property" _description = "Estate Property" name = fields.Char(string="Property Name", required=True) description = fields.Char(string="Property Description") postcode = fields.Char(string="Postal Code") - date_availability = fields.Date(string="Available From") + date_availability = fields.Date( + string="Available From", + default=fields.Date.add(value=fields.Date.today(), months=3), + copy=False, + ) expected_price = fields.Float(string="Expected Selling Price", required=True) - selling_price = fields.Float(string="Actual Selling Price") - bedrooms = fields.Integer(string="Number of Bedrooms") + selling_price = fields.Float( + string="Actual Selling Price", readonly=True, copy=False + ) + bedrooms = fields.Integer(string="Number of Bedrooms", default=2) living_area = fields.Float(string="Living Area (sqm)") facades = fields.Integer(string="Number of Facades") garage = fields.Boolean(string="Garage") @@ -26,3 +32,16 @@ class EstateProperty(models.Model): ("west", "West"), ], ) + active = fields.Boolean(string="Active", default=True) + status = fields.Selection( + string="Status", + selection=[ + ("new", "New"), + ("offer received", "Offer Received"), + ("offer accepted", "Offer Accepted"), + ("sold", "Sold"), + ("cancelled", "Cancelled"), + ], + default="new", + copy=False, + ) diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml new file mode 100644 index 00000000000..c1d10467906 --- /dev/null +++ b/estate/views/estate_property_views.xml @@ -0,0 +1,10 @@ + + + + + Properties + estate.property + list,form + + + diff --git a/estate/views/menus.xml b/estate/views/menus.xml new file mode 100644 index 00000000000..4e43805fcd6 --- /dev/null +++ b/estate/views/menus.xml @@ -0,0 +1,8 @@ + + + + + + + + From e5fd8595476cd1097dd89e8a2dfe351be8b3db70 Mon Sep 17 00:00:00 2001 From: mohamedakhalil18 Date: Tue, 20 Jan 2026 17:11:08 +0100 Subject: [PATCH 5/8] Added search, filtering, grouping functionalities and reformatted form and list views --- estate/__manifest__.py | 3 +++ estate/views/form_views.xml | 48 +++++++++++++++++++++++++++++++++++ estate/views/list_views.xml | 20 +++++++++++++++ estate/views/search_views.xml | 24 ++++++++++++++++++ 4 files changed, 95 insertions(+) create mode 100644 estate/views/form_views.xml create mode 100644 estate/views/list_views.xml create mode 100644 estate/views/search_views.xml diff --git a/estate/__manifest__.py b/estate/__manifest__.py index 67268bf6515..7deaa55cb75 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -12,5 +12,8 @@ "security/ir.model.access.csv", "views/estate_property_views.xml", "views/menus.xml", + "views/list_views.xml", + "views/form_views.xml", + "views/search_views.xml", ], } diff --git a/estate/views/form_views.xml b/estate/views/form_views.xml new file mode 100644 index 00000000000..c4aef44789e --- /dev/null +++ b/estate/views/form_views.xml @@ -0,0 +1,48 @@ + + + + + Property Form + estate.property + +
+ +

+ +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
diff --git a/estate/views/list_views.xml b/estate/views/list_views.xml new file mode 100644 index 00000000000..0b87bf4326c --- /dev/null +++ b/estate/views/list_views.xml @@ -0,0 +1,20 @@ + + + + + Property List + estate.property + + + + + + + + + + + + + + diff --git a/estate/views/search_views.xml b/estate/views/search_views.xml new file mode 100644 index 00000000000..4ee1b4673e5 --- /dev/null +++ b/estate/views/search_views.xml @@ -0,0 +1,24 @@ + + + + + Property Search + estate.property + + + + + + + + + + + + + + + + + + From 49403380d88f214720d55b06fddaafbbae3e901a Mon Sep 17 00:00:00 2001 From: mohamedakhalil18 Date: Wed, 21 Jan 2026 13:52:01 +0100 Subject: [PATCH 6/8] [IMP] Estate Included related fields in order to allow the communication between the estate property base model and tags, types, offers, users, and partners model to extend the funcationalities of the current module to cater for the business need of having specific sales info. for each record --- awesome_clicker/__init__.py | 1 - awesome_clicker/__manifest__.py | 1 - awesome_dashboard/__init__.py | 1 - awesome_dashboard/__manifest__.py | 1 - awesome_dashboard/controllers/__init__.py | 1 - awesome_dashboard/controllers/controllers.py | 2 - awesome_gallery/__init__.py | 1 - awesome_gallery/__manifest__.py | 1 - awesome_gallery/models/__init__.py | 4 +- awesome_gallery/models/ir_action.py | 3 +- awesome_gallery/models/ir_ui_view.py | 1 - awesome_kanban/__init__.py | 1 - awesome_kanban/__manifest__.py | 1 - awesome_owl/__init__.py | 1 - awesome_owl/__manifest__.py | 1 - awesome_owl/controllers/__init__.py | 1 - awesome_owl/controllers/controllers.py | 2 +- estate/__init__.py | 4 +- estate/models/__init__.py | 2 +- estate/models/estate_property.py | 7 +- estate/models/property_offer.py | 11 +++ estate/models/property_tag.py | 8 ++ estate/models/property_type.py | 8 ++ estate/security/ir.model.access.csv | 3 + estate/views/estate_property_views.xml | 18 +++++ estate/views/form_views.xml | 78 +++++++++++++++++++- estate/views/list_views.xml | 23 ++++++ estate/views/menus.xml | 9 ++- estate/views/search_views.xml | 8 +- website_airproof/__manifest__.py | 4 +- 30 files changed, 171 insertions(+), 36 deletions(-) create mode 100644 estate/models/property_offer.py create mode 100644 estate/models/property_tag.py create mode 100644 estate/models/property_type.py diff --git a/awesome_clicker/__init__.py b/awesome_clicker/__init__.py index 40a96afc6ff..e69de29bb2d 100644 --- a/awesome_clicker/__init__.py +++ b/awesome_clicker/__init__.py @@ -1 +0,0 @@ -# -*- coding: utf-8 -*- diff --git a/awesome_clicker/__manifest__.py b/awesome_clicker/__manifest__.py index aa6d3cd7a71..0ee7372dadf 100644 --- a/awesome_clicker/__manifest__.py +++ b/awesome_clicker/__manifest__.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- { "name": "Awesome Clicker", "summary": """ diff --git a/awesome_dashboard/__init__.py b/awesome_dashboard/__init__.py index b0f26a9a602..f705942f8f1 100644 --- a/awesome_dashboard/__init__.py +++ b/awesome_dashboard/__init__.py @@ -1,3 +1,2 @@ -# -*- coding: utf-8 -*- from . import controllers diff --git a/awesome_dashboard/__manifest__.py b/awesome_dashboard/__manifest__.py index a86d9bb991e..0be8062fbbb 100644 --- a/awesome_dashboard/__manifest__.py +++ b/awesome_dashboard/__manifest__.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- { "name": "Awesome Dashboard", "summary": """ diff --git a/awesome_dashboard/controllers/__init__.py b/awesome_dashboard/controllers/__init__.py index b0f26a9a602..f705942f8f1 100644 --- a/awesome_dashboard/controllers/__init__.py +++ b/awesome_dashboard/controllers/__init__.py @@ -1,3 +1,2 @@ -# -*- coding: utf-8 -*- from . import controllers diff --git a/awesome_dashboard/controllers/controllers.py b/awesome_dashboard/controllers/controllers.py index 2e408affe36..2d0d9f4cbff 100644 --- a/awesome_dashboard/controllers/controllers.py +++ b/awesome_dashboard/controllers/controllers.py @@ -1,10 +1,8 @@ -# -*- coding: utf-8 -*- import logging import random from odoo import http -from odoo.http import request logger = logging.getLogger(__name__) diff --git a/awesome_gallery/__init__.py b/awesome_gallery/__init__.py index a0fdc10fe11..0650744f6bc 100644 --- a/awesome_gallery/__init__.py +++ b/awesome_gallery/__init__.py @@ -1,2 +1 @@ -# -*- coding: utf-8 -*- from . import models diff --git a/awesome_gallery/__manifest__.py b/awesome_gallery/__manifest__.py index 6eaeaa6f83f..76200eff3a4 100644 --- a/awesome_gallery/__manifest__.py +++ b/awesome_gallery/__manifest__.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- { "name": "Gallery View", "summary": """ diff --git a/awesome_gallery/models/__init__.py b/awesome_gallery/models/__init__.py index 7f0930ee744..c1bbf7a7f7d 100644 --- a/awesome_gallery/models/__init__.py +++ b/awesome_gallery/models/__init__.py @@ -1,4 +1,2 @@ -# -*- coding: utf-8 -*- # import filename_python_file_within_folder_or_subfolder -from . import ir_action -from . import ir_ui_view +from . import ir_action, ir_ui_view diff --git a/awesome_gallery/models/ir_action.py b/awesome_gallery/models/ir_action.py index 45dc4a2bb71..00d0ff823a3 100644 --- a/awesome_gallery/models/ir_action.py +++ b/awesome_gallery/models/ir_action.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- from odoo import fields, models @@ -6,5 +5,5 @@ class ActWindowView(models.Model): _inherit = "ir.actions.act_window.view" view_mode = fields.Selection( - selection_add=[("gallery", "Awesome Gallery")], ondelete={"gallery": "cascade"} + selection_add=[("gallery", "Awesome Gallery")], ondelete={"gallery": "cascade"}, ) diff --git a/awesome_gallery/models/ir_ui_view.py b/awesome_gallery/models/ir_ui_view.py index 555008c371f..72d3816cb90 100644 --- a/awesome_gallery/models/ir_ui_view.py +++ b/awesome_gallery/models/ir_ui_view.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- from odoo import fields, models diff --git a/awesome_kanban/__init__.py b/awesome_kanban/__init__.py index 40a96afc6ff..e69de29bb2d 100644 --- a/awesome_kanban/__init__.py +++ b/awesome_kanban/__init__.py @@ -1 +0,0 @@ -# -*- coding: utf-8 -*- diff --git a/awesome_kanban/__manifest__.py b/awesome_kanban/__manifest__.py index 1dc8791b97b..c388dc533b2 100644 --- a/awesome_kanban/__manifest__.py +++ b/awesome_kanban/__manifest__.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- { "name": "Awesome Kanban", "summary": """ diff --git a/awesome_owl/__init__.py b/awesome_owl/__init__.py index b0f26a9a602..f705942f8f1 100644 --- a/awesome_owl/__init__.py +++ b/awesome_owl/__init__.py @@ -1,3 +1,2 @@ -# -*- coding: utf-8 -*- from . import controllers diff --git a/awesome_owl/__manifest__.py b/awesome_owl/__manifest__.py index 197652f6027..52b5287856c 100644 --- a/awesome_owl/__manifest__.py +++ b/awesome_owl/__manifest__.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- { "name": "Awesome Owl", "summary": """ diff --git a/awesome_owl/controllers/__init__.py b/awesome_owl/controllers/__init__.py index b0f26a9a602..f705942f8f1 100644 --- a/awesome_owl/controllers/__init__.py +++ b/awesome_owl/controllers/__init__.py @@ -1,3 +1,2 @@ -# -*- coding: utf-8 -*- from . import controllers diff --git a/awesome_owl/controllers/controllers.py b/awesome_owl/controllers/controllers.py index 4da2f03a9f3..6e83f679212 100644 --- a/awesome_owl/controllers/controllers.py +++ b/awesome_owl/controllers/controllers.py @@ -1,5 +1,5 @@ from odoo import http -from odoo.http import request, route +from odoo.http import request class OwlPlayground(http.Controller): diff --git a/estate/__init__.py b/estate/__init__.py index 7ca767700cc..a41dbcd72ea 100644 --- a/estate/__init__.py +++ b/estate/__init__.py @@ -1,3 +1 @@ -from . import models -from . import security -from . import views +from . import models, security, views diff --git a/estate/models/__init__.py b/estate/models/__init__.py index 5e1963c9d2f..0187ac91511 100644 --- a/estate/models/__init__.py +++ b/estate/models/__init__.py @@ -1 +1 @@ -from . import estate_property +from . import estate_property, property_offer, property_tag, property_type diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 5a9ac0d298d..a930c1a9707 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -15,7 +15,7 @@ class EstateProperty(models.Model): ) expected_price = fields.Float(string="Expected Selling Price", required=True) selling_price = fields.Float( - string="Actual Selling Price", readonly=True, copy=False + string="Actual Selling Price", readonly=True, copy=False, ) bedrooms = fields.Integer(string="Number of Bedrooms", default=2) living_area = fields.Float(string="Living Area (sqm)") @@ -45,3 +45,8 @@ class EstateProperty(models.Model): default="new", copy=False, ) + property_type_id = fields.Many2one("estate.property.type", string="Property Type") + user_id = fields.Many2one("res.users", string="Salesperson", default=lambda self: self.env.user) + partner_id = fields.Many2one("res.partner", string="Buyer", copy=False) + property_tag_ids = fields.Many2many("estate.property.tag", string="Property Tag") + property_offers_ids = fields.One2many("estate.property.offer", "property_id", string="Offers") diff --git a/estate/models/property_offer.py b/estate/models/property_offer.py new file mode 100644 index 00000000000..ccea3fef4e7 --- /dev/null +++ b/estate/models/property_offer.py @@ -0,0 +1,11 @@ +from odoo import fields, models + + +class PropertyOffer (models.Model): + _name = "estate.property.offer" + _description = "Property Purchase Offers" + + price = fields.Float(string="Price") + property_id = fields.Many2one("estate.property", string="Property Name", required=True) + partner_id = fields.Many2one("res.partner", string="Partner", required=True) + status = fields.Selection(string="Status", selection=[("accepted", "Accepted"), ("refused", "Refused")], copy=False) diff --git a/estate/models/property_tag.py b/estate/models/property_tag.py new file mode 100644 index 00000000000..8478de6e3b4 --- /dev/null +++ b/estate/models/property_tag.py @@ -0,0 +1,8 @@ +from odoo import fields, models + + +class PropertyTag(models.Model): + _name = "estate.property.tag" + _description = "Estate Property Tag" + + name = fields.Char(string="Property Tag", required=True) diff --git a/estate/models/property_type.py b/estate/models/property_type.py new file mode 100644 index 00000000000..8ea4310f187 --- /dev/null +++ b/estate/models/property_type.py @@ -0,0 +1,8 @@ +from odoo import fields, models + + +class PropertyType(models.Model): + _name = "estate.property.type" + _description = "Estate Property Type" + + name = fields.Char(string="Property Type", required=True) diff --git a/estate/security/ir.model.access.csv b/estate/security/ir.model.access.csv index c787bf33445..26a80f21aa9 100644 --- a/estate/security/ir.model.access.csv +++ b/estate/security/ir.model.access.csv @@ -1,2 +1,5 @@ id,name,model_id/id,group_id/id,perm_read,perm_write,perm_create,perm_unlink 1,access_estate_property,model_estate_property,base.group_user,1,1,1,1 +2,access_estate_property_type,model_estate_property_type,base.group_user,1,1,1,1 +3,access_estate_property_tag,model_estate_property_tag,base.group_user,1,1,1,1 +4,access_estate_property_offer,model_estate_property_offer,base.group_user,1,1,1,1 diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index c1d10467906..c0727a85c9a 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -7,4 +7,22 @@ list,form + + Property Types + estate.property.type + list,form + + + + Property Tags + estate.property.tag + list,form + + + + Property Offers + estate.property.offer + list,form + + diff --git a/estate/views/form_views.xml b/estate/views/form_views.xml index c4aef44789e..6907654bb72 100644 --- a/estate/views/form_views.xml +++ b/estate/views/form_views.xml @@ -11,7 +11,9 @@ + + @@ -33,9 +35,22 @@ + + + + + - - + + + + + + + + + + @@ -45,4 +60,63 @@ + + Property Type Form + estate.property.type + +
+ +

+ Property Type +

+ + + + + +
+
+
+
+ + + Property Tag Form + estate.property.tag + +
+ +

+ Property Tag +

+ + + + + +
+
+
+
+ + + Property Offer Form + estate.property.offer + +
+ +

+ Property Offer +

+ + + + + + + +
+
+
+
+ diff --git a/estate/views/list_views.xml b/estate/views/list_views.xml index 0b87bf4326c..033c747775c 100644 --- a/estate/views/list_views.xml +++ b/estate/views/list_views.xml @@ -8,6 +8,7 @@ + @@ -17,4 +18,26 @@ + + Property Type List + estate.property.type + + + + + + + + + Property Offer List + estate.property.offer + + + + + + + + + diff --git a/estate/views/menus.xml b/estate/views/menus.xml index 4e43805fcd6..4bff1d24f7a 100644 --- a/estate/views/menus.xml +++ b/estate/views/menus.xml @@ -1,8 +1,13 @@ - - + + + + + + + diff --git a/estate/views/search_views.xml b/estate/views/search_views.xml index 4ee1b4673e5..f365a98ffc9 100644 --- a/estate/views/search_views.xml +++ b/estate/views/search_views.xml @@ -8,17 +8,17 @@ + - - - - + + + diff --git a/website_airproof/__manifest__.py b/website_airproof/__manifest__.py index 86294451616..48267ad98aa 100644 --- a/website_airproof/__manifest__.py +++ b/website_airproof/__manifest__.py @@ -63,7 +63,7 @@ "s_airproof_key_benefits_h2", "s_call_to_action", "s_airproof_carousel", - ] - } + ], + }, }, } From 6d86c689814fe15c2481224995fcb60fc074d899 Mon Sep 17 00:00:00 2001 From: mohamedakhalil18 Date: Wed, 21 Jan 2026 16:05:09 +0100 Subject: [PATCH 7/8] [IMP] Estate Added computation fields and onchange methods to facilitate data entry for each user and incorporate automation that computes best offer - garden orientation and area - validity dates and deadlines --- estate/models/estate_property.py | 28 ++++++++++++++++++++++++++-- estate/models/property_offer.py | 19 ++++++++++++++++++- estate/views/form_views.xml | 4 ++++ estate/views/list_views.xml | 2 ++ 4 files changed, 50 insertions(+), 3 deletions(-) diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index a930c1a9707..ec8ffe7e3b0 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -1,4 +1,4 @@ -from odoo import fields, models +from odoo import api, fields, models class EstateProperty(models.Model): @@ -22,7 +22,7 @@ class EstateProperty(models.Model): facades = fields.Integer(string="Number of Facades") garage = fields.Boolean(string="Garage") garden = fields.Boolean(string="Garden") - garden_area = fields.Float(string="Garden Area (sqm)") + garden_area = fields.Float(string="Garden Area (sqm)", compute="_automate_garden") garden_orientation = fields.Selection( string="Garden Orientation", selection=[ @@ -31,7 +31,9 @@ class EstateProperty(models.Model): ("east", "East"), ("west", "West"), ], + compute="_automate_garden", ) + total_area = fields.Float(string="Total Area (sqm)", compute="_compute_area") active = fields.Boolean(string="Active", default=True) status = fields.Selection( string="Status", @@ -50,3 +52,25 @@ class EstateProperty(models.Model): partner_id = fields.Many2one("res.partner", string="Buyer", copy=False) property_tag_ids = fields.Many2many("estate.property.tag", string="Property Tag") property_offers_ids = fields.One2many("estate.property.offer", "property_id", string="Offers") + best_offer = fields.Float(string="Best Offer", compute="_compute_best_offer") + + @api.depends("living_area", "garden_area") + def _compute_area(self): + for record in self: + record.total_area = record.garden_area + record.living_area + + @api.depends("property_offers_ids.price") + def _compute_best_offer(self): + for record in self: + # for offer in record.property_offers_ids: + # best_offer= max(offer.price, best_offer) + record.best_offer = max(record.mapped("property_offers_ids.price"), default=0) + + @api.onchange("garden") + def _automate_garden(self): + if self.garden: + self.garden_area = 10 + self.garden_orientation = "north" + else: + self.garden_area = None + self.garden_orientation = None diff --git a/estate/models/property_offer.py b/estate/models/property_offer.py index ccea3fef4e7..0eb6e4f06b3 100644 --- a/estate/models/property_offer.py +++ b/estate/models/property_offer.py @@ -1,4 +1,4 @@ -from odoo import fields, models +from odoo import api, fields, models class PropertyOffer (models.Model): @@ -9,3 +9,20 @@ class PropertyOffer (models.Model): property_id = fields.Many2one("estate.property", string="Property Name", required=True) partner_id = fields.Many2one("res.partner", string="Partner", required=True) status = fields.Selection(string="Status", selection=[("accepted", "Accepted"), ("refused", "Refused")], copy=False) + validity = fields.Integer(string="Validity", default="7") + date_deadline = fields.Date(string="Validity Date Deadline", compute="_compute_validity", inverse="_inverse_date") + + @api.depends("validity") + def _compute_validity(self): + for record in self: + if record.create_date: + record.date_deadline = fields.Date.add(record.create_date, days=record.validity) + else: + record.date_deadline = fields.Date.add(fields.Date.today(), days=record.validity) + + def _inverse_date(self): + for record in self: + if record.create_date: + record.validity = (record.date_deadline - fields.Date.to_date(record.create_date)).days + else: + record.validity = (record.date_deadline - fields.Date.today()).days diff --git a/estate/views/form_views.xml b/estate/views/form_views.xml index 6907654bb72..0d896e3019c 100644 --- a/estate/views/form_views.xml +++ b/estate/views/form_views.xml @@ -19,6 +19,7 @@ + @@ -34,6 +35,7 @@ + @@ -111,6 +113,8 @@ + + diff --git a/estate/views/list_views.xml b/estate/views/list_views.xml index 033c747775c..42081364b2a 100644 --- a/estate/views/list_views.xml +++ b/estate/views/list_views.xml @@ -35,6 +35,8 @@ + + From d13aea3f8468ca970510b5b1a149e2dfe13f84f0 Mon Sep 17 00:00:00 2001 From: mohamedakhalil18 Date: Wed, 21 Jan 2026 17:07:39 +0100 Subject: [PATCH 8/8] [IMP] Estate Added action buttons to sell/cancel an apartment and accept or refuse a given offer. The added actions and functionalities are aiming to provide the user of the estate app with more flexible actions in order to complete their tasks of selling or cancelling real estate listings as well as accepting or refusing any given offer against that listing. Moreover, accepting an offer will automatically sell the listing to that bidder --- estate/models/estate_property.py | 16 +++++++++++++++- estate/models/property_offer.py | 22 ++++++++++++++++++++-- estate/views/form_views.xml | 5 +++++ estate/views/list_views.xml | 2 ++ 4 files changed, 42 insertions(+), 3 deletions(-) diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index ec8ffe7e3b0..e5fc71dca4e 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -1,4 +1,4 @@ -from odoo import api, fields, models +from odoo import api, exceptions, fields, models class EstateProperty(models.Model): @@ -74,3 +74,17 @@ def _automate_garden(self): else: self.garden_area = None self.garden_orientation = None + + def sell_apartment(self): + for record in self: + if record.status == "cancelled": + raise exceptions.UserError("Cancelled apartments can not be sold") + record.status = "sold" + return True + + def cancel_apartment(self): + for record in self: + if record.status == "sold": + raise exceptions.UserError("Sold apartments can not be cancelled") + record.status = "cancelled" + return False diff --git a/estate/models/property_offer.py b/estate/models/property_offer.py index 0eb6e4f06b3..af47fb62611 100644 --- a/estate/models/property_offer.py +++ b/estate/models/property_offer.py @@ -1,4 +1,4 @@ -from odoo import api, fields, models +from odoo import api, exceptions, fields, models class PropertyOffer (models.Model): @@ -10,7 +10,7 @@ class PropertyOffer (models.Model): partner_id = fields.Many2one("res.partner", string="Partner", required=True) status = fields.Selection(string="Status", selection=[("accepted", "Accepted"), ("refused", "Refused")], copy=False) validity = fields.Integer(string="Validity", default="7") - date_deadline = fields.Date(string="Validity Date Deadline", compute="_compute_validity", inverse="_inverse_date") + date_deadline = fields.Date(string="Deadline", compute="_compute_validity", inverse="_inverse_date") @api.depends("validity") def _compute_validity(self): @@ -26,3 +26,21 @@ def _inverse_date(self): record.validity = (record.date_deadline - fields.Date.to_date(record.create_date)).days else: record.validity = (record.date_deadline - fields.Date.today()).days + + def action_confirm(self): + for record in self: + if not record.status: + if record.property_id.status in ("sold", "cancelled"): + raise exceptions.UserError("This offer can not be accepted") + record.property_id.sell_apartment() + record.status = "accepted" + record.property_id.selling_price = record.price + record.property_id.partner_id = record.partner_id + + return True + + def action_cancel(self): + for record in self: + if not record.status: + record.status = "refused" + return True diff --git a/estate/views/form_views.xml b/estate/views/form_views.xml index 0d896e3019c..e1a856a4614 100644 --- a/estate/views/form_views.xml +++ b/estate/views/form_views.xml @@ -6,6 +6,10 @@ estate.property
+
+

@@ -13,6 +17,7 @@ + diff --git a/estate/views/list_views.xml b/estate/views/list_views.xml index 42081364b2a..02849136f78 100644 --- a/estate/views/list_views.xml +++ b/estate/views/list_views.xml @@ -37,6 +37,8 @@ +