From 3494f5d21108d1fb56f6c9f541f1ba0ba2c56c3b Mon Sep 17 00:00:00 2001 From: shadi-odoo Date: Fri, 3 Jul 2026 16:10:25 +0530 Subject: [PATCH 01/10] [ADD] estate: add new module Provide the required manifest metadata so the module can be recognized and installed correctly by Odoo. The metadata also ensures the module is properly categorized and ready for future development. --- estate/__init__.py | 0 estate/__manifest__.py | 6 ++++++ 2 files changed, 6 insertions(+) create mode 100644 estate/__init__.py create mode 100644 estate/__manifest__.py diff --git a/estate/__init__.py b/estate/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/estate/__manifest__.py b/estate/__manifest__.py new file mode 100644 index 00000000000..8a02e81f661 --- /dev/null +++ b/estate/__manifest__.py @@ -0,0 +1,6 @@ +{ + "name":"Real Estate", + "author":"Disha Shah(SHADI)", + "description":"""Training module for real estate""", + "category":"tutorials" +} \ No newline at end of file From f22996195e04033f790876ec2f4392eb1a578b03 Mon Sep 17 00:00:00 2001 From: shadi-odoo Date: Fri, 3 Jul 2026 16:32:08 +0530 Subject: [PATCH 02/10] [ADD] estate: add manifest configuration Add the remaining manifest metadata required for the module. Include the module version, base dependency, and application settings so the module can be installed and recognized as an Odoo application. --- estate/__manifest__.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/estate/__manifest__.py b/estate/__manifest__.py index 8a02e81f661..3caededdec8 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -1,6 +1,11 @@ { - "name":"Real Estate", - "author":"Disha Shah(SHADI)", - "description":"""Training module for real estate""", - "category":"tutorials" -} \ No newline at end of file + 'name':'Real Estate', + 'version':'1.0', + 'category':'tutorials', + 'depends':['base'], + 'installable':True, + 'application':True, + 'author':'Disha Shah(SHADI)', + 'description':"""Training module for real estate""", + 'license':'LPGL-3', +} From cce83584d36e912b883abe37ded505f67dc84b50 Mon Sep 17 00:00:00 2001 From: shadi-odoo Date: Mon, 6 Jul 2026 18:04:42 +0530 Subject: [PATCH 03/10] [IMP] estate: add estate property model Introduce the property model to allow the Estate module to store and manage information about real estate properties. The model defines the essential details of a property, such as its name, description, expected and selling prices, availability, living space, and additional features like a garage or garden. This provides the core data structure required for property management within the module. task-completed chapter 3 --- estate/__init__.py | 1 + estate/__manifest__.py | 18 +++++++++--------- estate/models/__init__.py | 1 + estate/models/estate_property.py | 19 +++++++++++++++++++ 4 files changed, 30 insertions(+), 9 deletions(-) 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 index e69de29bb2d..0650744f6bc 100644 --- a/estate/__init__.py +++ b/estate/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/estate/__manifest__.py b/estate/__manifest__.py index 3caededdec8..afdb82e0ccd 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -1,11 +1,11 @@ { - 'name':'Real Estate', - 'version':'1.0', - 'category':'tutorials', - 'depends':['base'], - 'installable':True, - 'application':True, - 'author':'Disha Shah(SHADI)', - 'description':"""Training module for real estate""", - 'license':'LPGL-3', + 'name': 'Real Estate', + 'version': '1.0', + 'category': 'tutorials', + 'depends': ['base'], + 'installable': True, + 'application': True, + 'author': 'Disha Shah(SHADI)', + 'description': """Training module for real estate""", + 'license': 'LGPL-3', } diff --git a/estate/models/__init__.py b/estate/models/__init__.py new file mode 100644 index 00000000000..5e1963c9d2f --- /dev/null +++ b/estate/models/__init__.py @@ -0,0 +1 @@ +from . import estate_property diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py new file mode 100644 index 00000000000..d74273974ff --- /dev/null +++ b/estate/models/estate_property.py @@ -0,0 +1,19 @@ +from odoo import fields, models + + +class EstateProperties(models.Model): + _name = "estate.property" + _description = "Real Estate Properties" + name = fields.Char(required=True) + description = fields.Text() + postcode = fields.Char() + date_availability = fields.Date() + expected_price = fields.Float(required=True) + selling_price = fields.Float() + bedrooms = fields.Integer() + living_area = fields.Integer() + facades = fields.Integer() + garage = fields.Boolean() + garden = fields.Boolean() + garden_area = fields.Integer() + garden_orientation = fields.Selection(selection=[('north', 'North'), ('south', 'South'), ('east', 'East'), ('west', 'West')]) From 1a3164ef940b628f9ee34d9e29a9f54455484007 Mon Sep 17 00:00:00 2001 From: shadi-odoo Date: Tue, 7 Jul 2026 17:06:01 +0530 Subject: [PATCH 04/10] [IMP] estate: grant access to property records Allow internal users to access and manage estate property records. Add an access control rule that grants users in the Internal User group permission to read, create, update, and delete property records. This enables users to work with the Estate module while ensuring access is managed through Odoo's security framework. Task : "Chapter 4 completed" --- estate/__manifest__.py | 3 +++ estate/models/estate_property.py | 9 ++++++++- estate/security/ir.model.access.csv | 2 ++ 3 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 estate/security/ir.model.access.csv diff --git a/estate/__manifest__.py b/estate/__manifest__.py index afdb82e0ccd..a8a563efb22 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -3,6 +3,9 @@ 'version': '1.0', 'category': 'tutorials', 'depends': ['base'], + 'data': [ + 'security/ir.model.access.csv', + ], 'installable': True, 'application': True, 'author': 'Disha Shah(SHADI)', diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index d74273974ff..95113164a0e 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -16,4 +16,11 @@ class EstateProperties(models.Model): garage = fields.Boolean() garden = fields.Boolean() garden_area = fields.Integer() - garden_orientation = fields.Selection(selection=[('north', 'North'), ('south', 'South'), ('east', 'East'), ('west', 'West')]) + garden_orientation = fields.Selection( + selection=[ + ("north", "North"), + ("south", "South"), + ("east", "East"), + ("west", "West"), + ] + ) diff --git a/estate/security/ir.model.access.csv b/estate/security/ir.model.access.csv new file mode 100644 index 00000000000..78310a2c61a --- /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 +estate.access_estate_property,access_estate_property,estate.model_estate_property,base.group_user,1,1,1, From 828848ec12dbb2c044eb3570114b95bcc300aa13 Mon Sep 17 00:00:00 2001 From: shadi-odoo Date: Thu, 9 Jul 2026 17:36:04 +0530 Subject: [PATCH 05/10] [IMP] estate: configure property model Complete the basic configuration of the estate property model to make it ready for use in the Estate module. Add menus and a window action so users can access property records from the interface. Configure field defaults and attributes to simplify data entry and prevent unwanted changes. Introduce active and state fields to support property management throughout its lifecycle. Task: Completed Chapter 5 --- estate/__manifest__.py | 2 ++ estate/models/estate_property.py | 21 ++++++++++++++++++--- estate/views/estate_menus.xml | 6 ++++++ estate/views/estate_property_views.xml | 8 ++++++++ 4 files changed, 34 insertions(+), 3 deletions(-) create mode 100644 estate/views/estate_menus.xml create mode 100644 estate/views/estate_property_views.xml diff --git a/estate/__manifest__.py b/estate/__manifest__.py index a8a563efb22..a3ff8224d02 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -5,6 +5,8 @@ 'depends': ['base'], 'data': [ 'security/ir.model.access.csv', + 'views/estate_property_views.xml', + 'views/estate_menus.xml', ], 'installable': True, 'application': True, diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 95113164a0e..5f8f0011081 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -7,10 +7,12 @@ class EstateProperties(models.Model): name = fields.Char(required=True) description = fields.Text() postcode = fields.Char() - date_availability = fields.Date() + date_availability = fields.Date( + copy=False, default=lambda self: fields.Date.add(fields.Date.today(), months=3) + ) expected_price = fields.Float(required=True) - selling_price = fields.Float() - bedrooms = fields.Integer() + selling_price = fields.Float(readonly=True, copy=False) + bedrooms = fields.Integer(default=2) living_area = fields.Integer() facades = fields.Integer() garage = fields.Boolean() @@ -24,3 +26,16 @@ class EstateProperties(models.Model): ("west", "West"), ] ) + active = fields.Boolean(default=True) + state = fields.Selection( + selection=[ + ("new", "New"), + ("offer received", "Offer Received"), + ("offer accepted", "Offer Accepted"), + ("sold", "Sold"), + ("canceled", "Canceled"), + ], + required=True, + copy=False, + default="new", + ) diff --git a/estate/views/estate_menus.xml b/estate/views/estate_menus.xml new file mode 100644 index 00000000000..e87c51cf7fa --- /dev/null +++ b/estate/views/estate_menus.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml new file mode 100644 index 00000000000..63cde3adf07 --- /dev/null +++ b/estate/views/estate_property_views.xml @@ -0,0 +1,8 @@ + + + + Property + estate.property + list,form + + From 970474a257c6d2ba90b519ab2666e0f87296649e Mon Sep 17 00:00:00 2001 From: shadi-odoo Date: Mon, 27 Jul 2026 15:39:42 +0530 Subject: [PATCH 06/10] [IMP] estate: add property views and search filters Create custom list, form, and search views for the estate property model. Add filters for available properties and support grouping by postcode to make property records easier to find and manage. Task: Completed Chapter 6 --- estate/models/estate_property.py | 1 + estate/security/ir.model.access.csv | 1 + estate/views/estate_menus.xml | 2 + estate/views/estate_property_views.xml | 84 +++++++++++++++++++++++++- 4 files changed, 87 insertions(+), 1 deletion(-) diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 5f8f0011081..9d4bccbb318 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -5,6 +5,7 @@ class EstateProperties(models.Model): _name = "estate.property" _description = "Real Estate Properties" name = fields.Char(required=True) + property_type_id = fields.Many2one("estate.property.type", string="Property Type") description = fields.Text() postcode = fields.Char() date_availability = fields.Date( diff --git a/estate/security/ir.model.access.csv b/estate/security/ir.model.access.csv index 78310a2c61a..d6184ad863a 100644 --- a/estate/security/ir.model.access.csv +++ b/estate/security/ir.model.access.csv @@ -1,2 +1,3 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink estate.access_estate_property,access_estate_property,estate.model_estate_property,base.group_user,1,1,1, +access_estate_property_type,access_estate_property_type,model_estate_property_type,base.group_user,1,1,1,1 \ No newline at end of file diff --git a/estate/views/estate_menus.xml b/estate/views/estate_menus.xml index e87c51cf7fa..5e612662d47 100644 --- a/estate/views/estate_menus.xml +++ b/estate/views/estate_menus.xml @@ -2,5 +2,7 @@ + + diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index 63cde3adf07..e7d43bedb4c 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -1,8 +1,90 @@ - Property + Properties estate.property list,form + + + estate.property.list + estate.property + + + + + + + + + + + + + + estate.property.form + estate.property + +
+ +
+

+ +

+
+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ + + + estate.property.search + estate.property + + + + + + + + + + + + + + + +
From 71b0a79721bedac6f87c299a99e6d3effe23779a Mon Sep 17 00:00:00 2001 From: shadi-odoo Date: Tue, 28 Jul 2026 16:54:21 +0530 Subject: [PATCH 07/10] [IMP] estate: add property details and offers Expand the Estate module to store more information about properties and the people involved in them. Properties can now be organized by type and tags, assigned to a salesperson, linked to a buyer, and receive offers from interested customers. These additions make it easier to categorize properties, track ownership and responsibility, and manage the buying process from a single place. Task: Completed Chapter 7 --- estate/__manifest__.py | 3 ++ estate/models/__init__.py | 2 +- estate/models/estate_property.py | 8 ++++- estate/models/estate_property_offer.py | 13 ++++++++ estate/models/estate_property_tag.py | 8 +++++ estate/models/estate_property_type.py | 7 ++++ estate/security/ir.model.access.csv | 4 ++- estate/views/estate_menus.xml | 3 +- estate/views/estate_property_offer_views.xml | 34 ++++++++++++++++++++ estate/views/estate_property_tag_views.xml | 8 +++++ estate/views/estate_property_type_views.xml | 8 +++++ estate/views/estate_property_views.xml | 24 ++++++++++++++ 12 files changed, 118 insertions(+), 4 deletions(-) create mode 100644 estate/models/estate_property_offer.py create mode 100644 estate/models/estate_property_tag.py create mode 100644 estate/models/estate_property_type.py create mode 100644 estate/views/estate_property_offer_views.xml create mode 100644 estate/views/estate_property_tag_views.xml create mode 100644 estate/views/estate_property_type_views.xml diff --git a/estate/__manifest__.py b/estate/__manifest__.py index a3ff8224d02..46aabdf4c7b 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -6,6 +6,9 @@ 'data': [ 'security/ir.model.access.csv', 'views/estate_property_views.xml', + 'views/estate_property_type_views.xml', + 'views/estate_property_tag_views.xml', + 'views/estate_property_offer_views.xml', 'views/estate_menus.xml', ], 'installable': True, diff --git a/estate/models/__init__.py b/estate/models/__init__.py index 5e1963c9d2f..93a6bd86abd 100644 --- a/estate/models/__init__.py +++ b/estate/models/__init__.py @@ -1 +1 @@ -from . import estate_property +from . import estate_property, estate_property_type, estate_property_tag, estate_property_offer diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 9d4bccbb318..717ac3b5719 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -5,7 +5,6 @@ class EstateProperties(models.Model): _name = "estate.property" _description = "Real Estate Properties" name = fields.Char(required=True) - property_type_id = fields.Many2one("estate.property.type", string="Property Type") description = fields.Text() postcode = fields.Char() date_availability = fields.Date( @@ -27,6 +26,13 @@ class EstateProperties(models.Model): ("west", "West"), ] ) + property_type_id = fields.Many2one("estate.property.type", string="Property Type") + sales_person = fields.Many2one( + "res.users", string="Sales Person", default=lambda self: self.env.user + ) + buyer = fields.Many2one("res.users", string="Buyer", copy=False) + tag_ids = fields.Many2many("estate.property.tag", string="Tags") + offer_ids = fields.One2many("estate.property.offer", "property_id", string="Offers") active = fields.Boolean(default=True) state = fields.Selection( selection=[ diff --git a/estate/models/estate_property_offer.py b/estate/models/estate_property_offer.py new file mode 100644 index 00000000000..94f903b6689 --- /dev/null +++ b/estate/models/estate_property_offer.py @@ -0,0 +1,13 @@ +from odoo import models, fields + + +class EstatePropertyOffer(models.Model): + _name = "estate.property.offer" + _description = "Estate Property Offer" + + price = fields.Float(string="Price") + status = fields.Selection( + selection=[("accepted", "Accepted"), ("refused", "Refused")], copy=False + ) + partner_id = fields.Many2one("res.partner", string="Partner", required=True) + property_id = fields.Many2one("estate.property", string="Property", required=True) diff --git a/estate/models/estate_property_tag.py b/estate/models/estate_property_tag.py new file mode 100644 index 00000000000..78ce298bce9 --- /dev/null +++ b/estate/models/estate_property_tag.py @@ -0,0 +1,8 @@ +from odoo import models, fields + + +class EstatePropertyTag(models.Model): + _name = "estate.property.tag" + _description = "Estate Property Tag" + + name = fields.Char(required=True) diff --git a/estate/models/estate_property_type.py b/estate/models/estate_property_type.py new file mode 100644 index 00000000000..91ece8a3d8c --- /dev/null +++ b/estate/models/estate_property_type.py @@ -0,0 +1,7 @@ +from odoo import fields, models + + +class EstatePropertyType(models.Model): + _name = "estate.property.type" + _description = "Real Estate Property Types" + name = fields.Char(required=True) diff --git a/estate/security/ir.model.access.csv b/estate/security/ir.model.access.csv index d6184ad863a..7e540acb6ab 100644 --- a/estate/security/ir.model.access.csv +++ b/estate/security/ir.model.access.csv @@ -1,3 +1,5 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink estate.access_estate_property,access_estate_property,estate.model_estate_property,base.group_user,1,1,1, -access_estate_property_type,access_estate_property_type,model_estate_property_type,base.group_user,1,1,1,1 \ No newline at end of file +access_estate_property_type,access_estate_property_type,model_estate_property_type,base.group_user,1,1,1,1 +estate.access_estate_property_tag,access_estate_property_tag,estate.model_estate_property_tag,base.group_user,1,1,1,1 +estate.access_estate_property_offer,access_estate_property_offer,estate.model_estate_property_offer,base.group_user,1,1,1,1 diff --git a/estate/views/estate_menus.xml b/estate/views/estate_menus.xml index 5e612662d47..112a95c8fe2 100644 --- a/estate/views/estate_menus.xml +++ b/estate/views/estate_menus.xml @@ -2,7 +2,8 @@ - + + diff --git a/estate/views/estate_property_offer_views.xml b/estate/views/estate_property_offer_views.xml new file mode 100644 index 00000000000..b5f615a58c0 --- /dev/null +++ b/estate/views/estate_property_offer_views.xml @@ -0,0 +1,34 @@ + + + + + + estate.property.offer.list + estate.property.offer + + + + + + + + + + + + estate.property.offer.form + estate.property.offer + +
+ + + + + + + +
+
+
+ +
diff --git a/estate/views/estate_property_tag_views.xml b/estate/views/estate_property_tag_views.xml new file mode 100644 index 00000000000..e8100e3d563 --- /dev/null +++ b/estate/views/estate_property_tag_views.xml @@ -0,0 +1,8 @@ + + + + Property Tags + estate.property.tag + list,form + + \ No newline at end of file diff --git a/estate/views/estate_property_type_views.xml b/estate/views/estate_property_type_views.xml new file mode 100644 index 00000000000..8d8012a78aa --- /dev/null +++ b/estate/views/estate_property_type_views.xml @@ -0,0 +1,8 @@ + + + + Property Types + estate.property.type + list,form + + \ No newline at end of file diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index e7d43bedb4c..39131e6d7ae 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -6,12 +6,14 @@ list,form + estate.property.list estate.property + @@ -21,6 +23,8 @@ + + estate.property.form estate.property @@ -34,6 +38,10 @@ + + + + @@ -55,6 +63,21 @@ + + + + + + + + + + + + + + + @@ -68,6 +91,7 @@ + From b5fad8ad9c10d9cfc0a0594a3648bfbd8e6c39cf Mon Sep 17 00:00:00 2001 From: shadi-odoo Date: Thu, 30 Jul 2026 17:48:15 +0530 Subject: [PATCH 08/10] [IMP] estate: automate property and offer information Improve the Estate module by automatically calculating property and offer information. Properties now display their total area and highest offer without requiring manual updates. Offers automatically calculate their deadline based on the validity period while still allowing users to change either value. Default garden details are also filled in automatically to reduce manual data entry and keep records consistent. Task: Completed Chapter 8 --- estate/models/estate_property.py | 31 ++++++++++++++++++-- estate/models/estate_property_offer.py | 23 ++++++++++++++- estate/views/estate_property_offer_views.xml | 4 ++- estate/views/estate_property_views.xml | 11 +++++-- 4 files changed, 62 insertions(+), 7 deletions(-) diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 717ac3b5719..63f07c0bf25 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 fields, models, api class EstateProperties(models.Model): @@ -30,7 +30,7 @@ class EstateProperties(models.Model): sales_person = fields.Many2one( "res.users", string="Sales Person", default=lambda self: self.env.user ) - buyer = fields.Many2one("res.users", string="Buyer", copy=False) + buyer = fields.Many2one("res.partner", string="Buyer", copy=False) tag_ids = fields.Many2many("estate.property.tag", string="Tags") offer_ids = fields.One2many("estate.property.offer", "property_id", string="Offers") active = fields.Boolean(default=True) @@ -46,3 +46,30 @@ class EstateProperties(models.Model): copy=False, default="new", ) + total_area = fields.Float( + compute="_compute_total_area", string="Total Area", store=True + ) + best_price = fields.Float(compute="_compute_best_price", string="Best Offer") + + @api.depends("living_area", "garden_area") + def _compute_total_area(self): + for record in self: + record.total_area = record.living_area + record.garden_area + + @api.depends("offer_ids.price") + def _compute_best_price(self): + for record in self: + if record.offer_ids: + record.best_price = max(record.offer_ids.mapped("price")) + else: + record.best_price = 0 + + @api.onchange("garden") + def _onchange_garden(self): + for record in self: + if record.garden: + record.garden_area = 10 + record.garden_orientation = "north" + else: + record.garden_area = 0 + record.garden_orientation = False diff --git a/estate/models/estate_property_offer.py b/estate/models/estate_property_offer.py index 94f903b6689..bcc36d86414 100644 --- a/estate/models/estate_property_offer.py +++ b/estate/models/estate_property_offer.py @@ -1,4 +1,4 @@ -from odoo import models, fields +from odoo import models, fields, api class EstatePropertyOffer(models.Model): @@ -11,3 +11,24 @@ class EstatePropertyOffer(models.Model): ) partner_id = fields.Many2one("res.partner", string="Partner", required=True) property_id = fields.Many2one("estate.property", string="Property", required=True) + validity = fields.Integer(string="Validity (days)", default=7) + date_deadline = fields.Date( + string="Deadline", + compute="_compute_date_deadline", + inverse="_inverse_date_deadline", + ) + + @api.depends("create_date", "validity") + def _compute_date_deadline(self): + for record in self: + create_date = record.create_date or fields.Date.today() + record.date_deadline = fields.Date.add( + fields.Date.to_date(create_date), days=record.validity + ) + + def _inverse_date_deadline(self): + for record in self: + create_date = record.create_date or fields.Date.today() + record.validity = ( + record.date_deadline - fields.Date.to_date(create_date) + ).days diff --git a/estate/views/estate_property_offer_views.xml b/estate/views/estate_property_offer_views.xml index b5f615a58c0..699dc3ce750 100644 --- a/estate/views/estate_property_offer_views.xml +++ b/estate/views/estate_property_offer_views.xml @@ -23,8 +23,10 @@ - + + + diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index 39131e6d7ae..568f51506e6 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -20,6 +20,7 @@ + @@ -35,11 +36,11 @@

- - + + @@ -48,6 +49,7 @@ + @@ -61,14 +63,17 @@ + - + + + From 3a5ae9167f221a553618e3a87b02c732a46c5ad8 Mon Sep 17 00:00:00 2001 From: shadi-odoo Date: Wed, 5 Aug 2026 16:43:06 +0530 Subject: [PATCH 09/10] [IMP] estate: address review feedback Improve the Estate module by applying the review comments received during code review. Update the code style to follow Odoo's coding conventions, improve formatting and indentation for better readability, remove unnecessary metadata, and make the implementation more consistent with the project's standards. These changes improve code quality and maintainability without changing the module's behavior. --- .gitignore | 1 + estate/__manifest__.py | 2 +- estate/models/estate_property.py | 45 +++++++-------- estate/models/estate_property_offer.py | 14 ++--- estate/models/estate_property_type.py | 1 + estate/views/estate_property_offer_views.xml | 3 +- estate/views/estate_property_tag_views.xml | 2 +- estate/views/estate_property_type_views.xml | 2 +- estate/views/estate_property_views.xml | 59 ++++++++++---------- 9 files changed, 67 insertions(+), 62 deletions(-) diff --git a/.gitignore b/.gitignore index b6e47617de1..5017d2852d6 100644 --- a/.gitignore +++ b/.gitignore @@ -127,3 +127,4 @@ dmypy.json # Pyre type checker .pyre/ +estate/models/ir_ui_view.py diff --git a/estate/__manifest__.py b/estate/__manifest__.py index 46aabdf4c7b..49d73e53a74 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -13,7 +13,7 @@ ], 'installable': True, 'application': True, - 'author': 'Disha Shah(SHADI)', + 'author': 'Odoo S.A.', 'description': """Training module for real estate""", 'license': 'LGPL-3', } diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 63f07c0bf25..0b0c4c30a3a 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -4,52 +4,53 @@ class EstateProperties(models.Model): _name = "estate.property" _description = "Real Estate Properties" - name = fields.Char(required=True) + + active = fields.Boolean(default=True) + bedrooms = fields.Integer(default=2) + best_price = fields.Float(compute="_compute_best_price", string="Best Offer") + buyer = fields.Many2one("res.partner", string="Buyer", copy=False) description = fields.Text() - postcode = fields.Char() date_availability = fields.Date( copy=False, default=lambda self: fields.Date.add(fields.Date.today(), months=3) ) expected_price = fields.Float(required=True) - selling_price = fields.Float(readonly=True, copy=False) - bedrooms = fields.Integer(default=2) - living_area = fields.Integer() facades = fields.Integer() garage = fields.Boolean() garden = fields.Boolean() garden_area = fields.Integer() garden_orientation = fields.Selection( selection=[ - ("north", "North"), - ("south", "South"), - ("east", "East"), - ("west", "West"), + ('north', "North"), + ('south', "South"), + ('east', "East"), + ('west', "West"), ] ) - property_type_id = fields.Many2one("estate.property.type", string="Property Type") - sales_person = fields.Many2one( - "res.users", string="Sales Person", default=lambda self: self.env.user - ) - buyer = fields.Many2one("res.partner", string="Buyer", copy=False) - tag_ids = fields.Many2many("estate.property.tag", string="Tags") + living_area = fields.Integer() + name = fields.Char(required=True) + postcode = fields.Char() offer_ids = fields.One2many("estate.property.offer", "property_id", string="Offers") - active = fields.Boolean(default=True) + property_type_id = fields.Many2one("estate.property.type", string="Property Type") + selling_price = fields.Float(readonly=True, copy=False) state = fields.Selection( selection=[ - ("new", "New"), - ("offer received", "Offer Received"), - ("offer accepted", "Offer Accepted"), - ("sold", "Sold"), - ("canceled", "Canceled"), + ('new', "New"), + ('offer received', "Offer Received"), + ('offer accepted', "Offer Accepted"), + ('sold', "Sold"), + ('canceled', "Canceled"), ], required=True, copy=False, default="new", ) + sales_person = fields.Many2one( + "res.users", string="Sales Person", default=lambda self: self.env.user + ) + tag_ids = fields.Many2many("estate.property.tag", string="Tags") total_area = fields.Float( compute="_compute_total_area", string="Total Area", store=True ) - best_price = fields.Float(compute="_compute_best_price", string="Best Offer") @api.depends("living_area", "garden_area") def _compute_total_area(self): diff --git a/estate/models/estate_property_offer.py b/estate/models/estate_property_offer.py index bcc36d86414..33ddc5490aa 100644 --- a/estate/models/estate_property_offer.py +++ b/estate/models/estate_property_offer.py @@ -5,18 +5,18 @@ class EstatePropertyOffer(models.Model): _name = "estate.property.offer" _description = "Estate Property Offer" - price = fields.Float(string="Price") - status = fields.Selection( - selection=[("accepted", "Accepted"), ("refused", "Refused")], copy=False - ) - partner_id = fields.Many2one("res.partner", string="Partner", required=True) - property_id = fields.Many2one("estate.property", string="Property", required=True) - validity = fields.Integer(string="Validity (days)", default=7) date_deadline = fields.Date( string="Deadline", compute="_compute_date_deadline", inverse="_inverse_date_deadline", ) + price = fields.Float(string="Price") + partner_id = fields.Many2one("res.partner", string="Partner", required=True) + property_id = fields.Many2one("estate.property", string="Property", required=True) + status = fields.Selection( + selection=[('accepted', "Accepted"), ('refused', "Refused")], copy=False + ) + validity = fields.Integer(string="Validity (days)", default=7) @api.depends("create_date", "validity") def _compute_date_deadline(self): diff --git a/estate/models/estate_property_type.py b/estate/models/estate_property_type.py index 91ece8a3d8c..5a19090ec40 100644 --- a/estate/models/estate_property_type.py +++ b/estate/models/estate_property_type.py @@ -4,4 +4,5 @@ class EstatePropertyType(models.Model): _name = "estate.property.type" _description = "Real Estate Property Types" + name = fields.Char(required=True) diff --git a/estate/views/estate_property_offer_views.xml b/estate/views/estate_property_offer_views.xml index 699dc3ce750..46c1a0c91f3 100644 --- a/estate/views/estate_property_offer_views.xml +++ b/estate/views/estate_property_offer_views.xml @@ -2,12 +2,13 @@ - + estate.property.offer.list estate.property.offer + diff --git a/estate/views/estate_property_tag_views.xml b/estate/views/estate_property_tag_views.xml index e8100e3d563..9083e3231d0 100644 --- a/estate/views/estate_property_tag_views.xml +++ b/estate/views/estate_property_tag_views.xml @@ -5,4 +5,4 @@ estate.property.tag list,form - \ No newline at end of file + diff --git a/estate/views/estate_property_type_views.xml b/estate/views/estate_property_type_views.xml index 8d8012a78aa..e50051fee30 100644 --- a/estate/views/estate_property_type_views.xml +++ b/estate/views/estate_property_type_views.xml @@ -5,4 +5,4 @@ estate.property.type list,form - \ No newline at end of file + diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index 568f51506e6..23c04362127 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -17,7 +17,7 @@ - + @@ -63,14 +63,16 @@ + + - - - + + + @@ -89,31 +91,30 @@ - - - estate.property.search - estate.property - - - - - - - - - - - + + + estate.property.search + estate.property + + + + + + + + + + - - - - + + + + From df3ac4e605cc9b66907ee3ebbccefc08a9b7861f Mon Sep 17 00:00:00 2001 From: shadi-odoo Date: Fri, 7 Aug 2026 15:31:50 +0530 Subject: [PATCH 10/10] [IMP] estate: manage property offers and status Add actions to manage the status of properties and their offers. Properties can now be marked as sold or cancelled, with checks to prevent invalid status changes. Offers can be accepted or refused, and accepting an offer automatically updates the property's buyer and selling price. Task: Completed Chapter 9 --- estate/models/estate_property.py | 14 ++++++++++++++ estate/models/estate_property_offer.py | 11 +++++++++++ estate/views/estate_property_views.xml | 8 +++++++- 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 0b0c4c30a3a..ef594ec1f56 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -1,4 +1,6 @@ from odoo import fields, models, api +from odoo.exceptions import UserError + class EstateProperties(models.Model): @@ -74,3 +76,15 @@ def _onchange_garden(self): else: record.garden_area = 0 record.garden_orientation = False + + def action_sold(self): + for record in self: + if record.state == "canceled": + raise UserError("Canceled properties cannot be sold.") + record.state = "sold" + + def action_cancel(self): + for record in self: + if record.state == "sold": + raise UserError("Sold properties cannot be canceled.") + record.state = "canceled" diff --git a/estate/models/estate_property_offer.py b/estate/models/estate_property_offer.py index 33ddc5490aa..afbc51b7c58 100644 --- a/estate/models/estate_property_offer.py +++ b/estate/models/estate_property_offer.py @@ -32,3 +32,14 @@ def _inverse_date_deadline(self): record.validity = ( record.date_deadline - fields.Date.to_date(create_date) ).days + + def action_accept(self): + for record in self: + record.status = "accepted" + record.property_id.selling_price = record.price + record.property_id.buyer = record.partner_id + record.property_id.state = "offer accepted" + + def action_refuse(self): + for record in self: + record.status = "refused" diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index 23c04362127..198102728a4 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -31,6 +31,10 @@ estate.property
+
+

@@ -42,6 +46,7 @@

+ @@ -64,7 +69,6 @@ - @@ -75,6 +79,8 @@ +