From b1adea643f9dcf97b7800c2ac8f427fef6f9eba7 Mon Sep 17 00:00:00 2001 From: vrushibh Date: Fri, 5 Jun 2026 15:43:19 +0530 Subject: [PATCH 1/8] [ADD] estate: initialize estate module Add the basic module structure and manifest file for the estate application. --- estate/__init__.py | 0 estate/__manifest__.py | 7 +++++++ 2 files changed, 7 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..473c75d013c --- /dev/null +++ b/estate/__manifest__.py @@ -0,0 +1,7 @@ +{ + "name": "Real Estate", + "depends": ["base"], + "application": True, + "installable": True, + +} \ No newline at end of file From 8594029912f4524a7c6bdeb1c3db8ae6c7356cd8 Mon Sep 17 00:00:00 2001 From: vrushibh Date: Mon, 8 Jun 2026 15:29:21 +0530 Subject: [PATCH 2/8] [ADD] estate: add estate.property model and basic fields Define the 'estate.property' model. - Add basic fields: name, description, postcode, date_availability, expected_price, selling_price, bedrooms, living_area, facades, garage, garden, garden_area, and garden_orientation. - Set 'name' and 'expected_price' as required fields. - Initialize the models package and set up imports. --- estate/__init__.py | 1 + estate/__manifest__.py | 3 +-- estate/models/__init__.py | 1 + estate/models/estate_property.py | 28 ++++++++++++++++++++++++++++ 4 files changed, 31 insertions(+), 2 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 473c75d013c..bae78a5fc6a 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -3,5 +3,4 @@ "depends": ["base"], "application": True, "installable": True, - -} \ No newline at end of file +} 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..29cc5e845dd --- /dev/null +++ b/estate/models/estate_property.py @@ -0,0 +1,28 @@ +from odoo import fields, models + + +class EstateProperty(models.Model): + _name = "estate.property" + _description = "Real Estate Property" + + 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"), + ], + string="Garden Orientation", + ) From 10c70a56258d77b1d35a386e48534ccd641cc215 Mon Sep 17 00:00:00 2001 From: vrushibh Date: Thu, 11 Jun 2026 18:52:21 +0530 Subject: [PATCH 3/8] [ADD] estate: add security access rules for estate.property - Create ir.model.access.csv in security folder - Grant read, write, create, and unlink permissions to base.group_user - Register security file in __manifest__.py - Fix manifest warnings by adding author and license keys --- estate/__manifest__.py | 5 +++++ estate/security/ir.model.access.csv | 2 ++ 2 files changed, 7 insertions(+) create mode 100644 estate/security/ir.model.access.csv diff --git a/estate/__manifest__.py b/estate/__manifest__.py index bae78a5fc6a..0360c444086 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -1,6 +1,11 @@ { "name": "Real Estate", + "author": "Odoo S.A.", + "license": "LGPL-3", "depends": ["base"], + "data": [ + "security/ir.model.access.csv", + ], "application": True, "installable": True, } diff --git a/estate/security/ir.model.access.csv b/estate/security/ir.model.access.csv new file mode 100644 index 00000000000..d9d6ba57cc5 --- /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 +access_estate_property,access_estate_property,model_estate_property,base.group_user,1,1,1,1 From 8fb2322a321cbda5a639335b270984c01c63ec82 Mon Sep 17 00:00:00 2001 From: vrushibh Date: Thu, 18 Jun 2026 12:26:45 +0530 Subject: [PATCH 4/8] [ADD] estate: add basic views, action and menus - Add tree (list), form, and search views for 'estate.property' in views/estate_property_views.xml. - Configure action 'action_estate_property' to load 'list,form,kanban' views and default filter 'available'. - Define advertisements menu structure in views/estate_menus.xml. - Clean up hardcoded field column widths from the list view in views/estate_property_views.xml to use standard auto-sizing layout. --- estate/__manifest__.py | 3 + estate/models/estate_property.py | 27 +++++-- estate/models/estate_property_type.py | 8 ++ estate/security/estate_security.xml | 29 ++++++++ estate/security/ir.model.access.csv | 3 +- estate/static/description/icon.png | Bin 0 -> 8694 bytes estate/views/estate_menus.xml | 9 +++ estate/views/estate_property_views.xml | 97 +++++++++++++++++++++++++ 8 files changed, 170 insertions(+), 6 deletions(-) create mode 100644 estate/models/estate_property_type.py create mode 100644 estate/security/estate_security.xml create mode 100644 estate/static/description/icon.png 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 0360c444086..e2211fb996c 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -4,7 +4,10 @@ "license": "LGPL-3", "depends": ["base"], "data": [ + "security/estate_security.xml", "security/ir.model.access.csv", + "views/estate_property_views.xml", + "views/estate_menus.xml", ], "application": True, "installable": True, diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 29cc5e845dd..d3bb6285923 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -1,17 +1,20 @@ from odoo import fields, models +from dateutil.relativedelta import relativedelta class EstateProperty(models.Model): _name = "estate.property" _description = "Real Estate Property" - name = fields.Char(required=True) + name = fields.Char(required=True, string="Property Name") description = fields.Text() postcode = fields.Char() - date_availability = fields.Date() - expected_price = fields.Float(required=True) - selling_price = fields.Float() - bedrooms = fields.Integer() + date_availability = fields.Date( + default=lambda self: fields.Date.context_today(self) + relativedelta(months=3) + ) + expected_price = fields.Float(required=True, copy=False, default=0.0) + selling_price = fields.Float(readonly=True, copy=False) + bedrooms = fields.Integer(default=2) living_area = fields.Integer() facades = fields.Integer() garage = fields.Boolean() @@ -26,3 +29,17 @@ class EstateProperty(models.Model): ], string="Garden Orientation", ) + active = fields.Boolean(default=True) + state = fields.Selection( + selection=[ + ("new", "New"), + ("offer_received", "Offer Received"), + ("offer_accepted", "Offer Accepted"), + ("sold", "Sold"), + ("cancelled", "Cancelled"), + ], + required=True, + copy=False, + default="new", + string="State", + ) diff --git a/estate/models/estate_property_type.py b/estate/models/estate_property_type.py new file mode 100644 index 00000000000..26c6ce17aa1 --- /dev/null +++ b/estate/models/estate_property_type.py @@ -0,0 +1,8 @@ +from odoo import models, fields + + +class EstatePropertyType(models.Model): + _name = "estate.property.type" + _description = "Real Estate Property Type" + + name = fields.Char(required=True, string="Property Type") diff --git a/estate/security/estate_security.xml b/estate/security/estate_security.xml new file mode 100644 index 00000000000..8ffe601261a --- /dev/null +++ b/estate/security/estate_security.xml @@ -0,0 +1,29 @@ + + + + + + Real Estate + 10 + + + + Access Level + + + + + Agent + + + + + + Manager + + + + + + + diff --git a/estate/security/ir.model.access.csv b/estate/security/ir.model.access.csv index d9d6ba57cc5..55113ee4775 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 -access_estate_property,access_estate_property,model_estate_property,base.group_user,1,1,1,1 +access_estate_property_agent,access_estate_property_agent,model_estate_property,group_estate_agent,1,1,1,0 +access_estate_property_manager,access_estate_property_manager,model_estate_property,group_estate_manager,1,1,1,1 diff --git a/estate/static/description/icon.png b/estate/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..ac5f35cf3404f2ef4c6932737005f02b337c2a52 GIT binary patch literal 8694 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 literal 0 HcmV?d00001 diff --git a/estate/views/estate_menus.xml b/estate/views/estate_menus.xml new file mode 100644 index 00000000000..8a7a69ec9b1 --- /dev/null +++ b/estate/views/estate_menus.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml new file mode 100644 index 00000000000..bd37accd558 --- /dev/null +++ b/estate/views/estate_property_views.xml @@ -0,0 +1,97 @@ + + + + + Properties + estate.property + list,form,kanban + {'search_default_available': 1} + +

+ Create your first real estate property advertisement! +

+

+ Here you can define all the details of the properties you want to sell. +

+
+
+ + estate.property.list + estate.property + + + + + + + + + + + + + + + + estate.property.form + estate.property + +
+ +
+

+ +

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ + + estate.property.search + estate.property + + + + + + + + + + + + + + + + +
+
From 67d44d97d21290e2f4c5b232d25cef4fdc3e7fc3 Mon Sep 17 00:00:00 2001 From: vrushibh Date: Mon, 22 Jun 2026 17:58:25 +0530 Subject: [PATCH 5/8] [IMP] estate: add tag and offer models with relations Introduce the property tag (estate.property.tag) and property offer (estate.property.offer) models, establishing relationship fields. - Add estate.property.tag model with basic views. - Add Many2many field tag_ids to estate.property. - Add estate.property.offer model with form and list views. - Add One2many field offer_ids to estate.property. - Add security access rules (ACLs) for both new models task-7 --- estate/__manifest__.py | 5 +++ estate/models/__init__.py | 3 ++ estate/models/estate_property.py | 28 +++++++++----- estate/models/estate_property_offer.py | 18 +++++++++ estate/models/estate_property_tag.py | 10 +++++ estate/security/ir.model.access.csv | 10 ++++- estate/views/estate_property_offer_views.xml | 31 ++++++++++++++++ estate/views/estate_property_tag_views.xml | 39 ++++++++++++++++++++ 8 files changed, 132 insertions(+), 12 deletions(-) create mode 100644 estate/models/estate_property_offer.py create mode 100644 estate/models/estate_property_tag.py create mode 100644 estate/views/estate_property_offer_views.xml create mode 100644 estate/views/estate_property_tag_views.xml diff --git a/estate/__manifest__.py b/estate/__manifest__.py index e2211fb996c..b00f9afce22 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- { "name": "Real Estate", "author": "Odoo S.A.", @@ -7,7 +8,11 @@ "security/estate_security.xml", "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", + "data/estate_demo.xml", ], "application": True, "installable": True, diff --git a/estate/models/__init__.py b/estate/models/__init__.py index 5e1963c9d2f..4b57c1674fc 100644 --- a/estate/models/__init__.py +++ b/estate/models/__init__.py @@ -1 +1,4 @@ from . import estate_property +from . import estate_property_tag +from . import estate_property_type +from . import estate_property_offer diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index d3bb6285923..809658aa5c4 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -1,6 +1,7 @@ -from odoo import fields, models from dateutil.relativedelta import relativedelta +from odoo import fields, models + class EstateProperty(models.Model): _name = "estate.property" @@ -22,24 +23,31 @@ class EstateProperty(models.Model): 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"), ], string="Garden Orientation", ) active = fields.Boolean(default=True) state = fields.Selection( selection=[ - ("new", "New"), - ("offer_received", "Offer Received"), - ("offer_accepted", "Offer Accepted"), - ("sold", "Sold"), - ("cancelled", "Cancelled"), + ('new', "New"), + ('offer_received', "Offer Received"), + ('offer_accepted', "Offer Accepted"), + ('sold', "Sold"), + ('cancelled', "Cancelled"), ], required=True, copy=False, default="new", string="State", ) + property_type_id = fields.Many2one("estate.property.type", string="Property Type") + buyer_id = fields.Many2one("res.partner", string="Buyer", copy=False) + salesperson_id = fields.Many2one( + "res.users", string="Salesperson", default=lambda self: self.env.user + ) + tag_ids = fields.Many2many("estate.property.tag", string="Property Tags") + offer_ids = fields.One2many("estate.property.offer", "property_id", string="Offers") diff --git a/estate/models/estate_property_offer.py b/estate/models/estate_property_offer.py new file mode 100644 index 00000000000..c9949ed0382 --- /dev/null +++ b/estate/models/estate_property_offer.py @@ -0,0 +1,18 @@ +from odoo import fields, models + + +class EstatePropertyOffer(models.Model): + _name = "estate.property.offer" + _description = "Real Estate Property Offer" + + price = fields.Float(required=True) + status = fields.Selection( + selection=[ + ('accepted', 'Accepted'), + ('rejected', 'Rejected'), + ], + copy=False, + string="Status", + ) + 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..c90d1143652 --- /dev/null +++ b/estate/models/estate_property_tag.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- + +from odoo import fields, models + + +class EstatePropertyTag(models.Model): + _name = "estate.property.tag" + _description = "Real Estate Property Tag" + + name = fields.Char(required=True) diff --git a/estate/security/ir.model.access.csv b/estate/security/ir.model.access.csv index 55113ee4775..0ae4ad14d54 100644 --- a/estate/security/ir.model.access.csv +++ b/estate/security/ir.model.access.csv @@ -1,3 +1,9 @@ id,name,model_id/id,group_id/id,perm_read,perm_write,perm_create,perm_unlink -access_estate_property_agent,access_estate_property_agent,model_estate_property,group_estate_agent,1,1,1,0 -access_estate_property_manager,access_estate_property_manager,model_estate_property,group_estate_manager,1,1,1,1 +access_estate_property_agent,access_estate_property_agent,model_estate_property,estate_group_agent,1,1,1,0 +access_estate_property_manager,access_estate_property_manager,model_estate_property,estate_group_manager,1,1,1,1 +access_estate_property_type_agent,access_estate_property_type_agent,model_estate_property_type,estate_group_agent,1,1,1,1 +access_estate_property_type_manager,access_estate_property_type_manager,model_estate_property_type,estate_group_manager,1,1,1,1 +access_estate_property_tag_agent,access_estate_property_tag_agent,model_estate_property_tag,estate_group_agent,1,1,1,1 +access_estate_property_tag_manager,access_estate_property_tag_manager,model_estate_property_tag,estate_group_manager,1,1,1,1 +access_estate_property_offer_agent,access_estate_property_offer_agent,model_estate_property_offer,estate_group_agent,1,1,1,1 +access_estate_property_offer_manager,access_estate_property_offer_manager,model_estate_property_offer,estate_group_manager,1,1,1,1 diff --git a/estate/views/estate_property_offer_views.xml b/estate/views/estate_property_offer_views.xml new file mode 100644 index 00000000000..16d222cef52 --- /dev/null +++ b/estate/views/estate_property_offer_views.xml @@ -0,0 +1,31 @@ + + + + + estate.property.offer.view.list + estate.property.offer + + + + + + + + + + + estate.property.offer.view.form + estate.property.offer + +
+ + + + + + + +
+
+
+
\ No newline at end of file diff --git a/estate/views/estate_property_tag_views.xml b/estate/views/estate_property_tag_views.xml new file mode 100644 index 00000000000..ed016ca6260 --- /dev/null +++ b/estate/views/estate_property_tag_views.xml @@ -0,0 +1,39 @@ + + + + + Property Tags + estate.property.tag + list,form + +

+ Create your first property tag! +

+
+
+ + + estate.property.tag.view.list + estate.property.tag + + + + + + + + + estate.property.tag.view.form + estate.property.tag + +
+ + + + + +
+
+
+ +
From e65e22aab05d929117bb49f10df4b0f97b747fa2 Mon Sep 17 00:00:00 2001 From: vrushibh Date: Mon, 22 Jun 2026 18:02:24 +0530 Subject: [PATCH 6/8] [FIX] estate: address TL review feedback and add demo data Address the code styling, domain filters, and manifest configurations requested in the code review. Additionally, set up demo data for the property, type, and tag models. --- estate/data/estate_demo.xml | 69 +++++++ estate/models/estate_property_type.py | 2 +- estate/security/estate_security.xml | 40 ++-- estate/views/estate_menus.xml | 37 +++- estate/views/estate_property_type_views.xml | 39 ++++ estate/views/estate_property_views.xml | 196 +++++++++++--------- 6 files changed, 269 insertions(+), 114 deletions(-) create mode 100644 estate/data/estate_demo.xml create mode 100644 estate/views/estate_property_type_views.xml diff --git a/estate/data/estate_demo.xml b/estate/data/estate_demo.xml new file mode 100644 index 00000000000..b16fe8169a9 --- /dev/null +++ b/estate/data/estate_demo.xml @@ -0,0 +1,69 @@ + + + + + Residential + + + commercial + + + + Cozy + + + Cool + + + Nice + + + + vrushibh Apartment + 75000.00 + 4 + 250 + 35000 + 2026-09-15 + + + + + + Cozy Apartment + 200000.00 + 2 + 85 + 380001 + + + + + + nice Apartment + 206000.00 + 2 + 85 + 380001 + + + + + cool Apartment + 206000.00 + 2 + 85 + 380001 + + + + + jumbo Apartment + 206000.00 + 2 + 85 + 380001 + + + + \ No newline at end of file diff --git a/estate/models/estate_property_type.py b/estate/models/estate_property_type.py index 26c6ce17aa1..216472e442c 100644 --- a/estate/models/estate_property_type.py +++ b/estate/models/estate_property_type.py @@ -1,4 +1,4 @@ -from odoo import models, fields +from odoo import fields, models class EstatePropertyType(models.Model): diff --git a/estate/security/estate_security.xml b/estate/security/estate_security.xml index 8ffe601261a..b6432bce42b 100644 --- a/estate/security/estate_security.xml +++ b/estate/security/estate_security.xml @@ -1,29 +1,27 @@ - - - Real Estate - 10 - + + Real Estate + 10 + - - Access Level - - + + Access Level + + - - Agent - - - + + Agent + + + - - Manager - - - - + + Manager + + + + - diff --git a/estate/views/estate_menus.xml b/estate/views/estate_menus.xml index 8a7a69ec9b1..3c08d2c78fa 100644 --- a/estate/views/estate_menus.xml +++ b/estate/views/estate_menus.xml @@ -1,9 +1,36 @@ - - - - - + + + + + + + + + + diff --git a/estate/views/estate_property_type_views.xml b/estate/views/estate_property_type_views.xml new file mode 100644 index 00000000000..819000b7da1 --- /dev/null +++ b/estate/views/estate_property_type_views.xml @@ -0,0 +1,39 @@ + + + + + Property Types + estate.property.type + list,form + +

+ Create your first property type! +

+
+
+ + + estate.property.type.view.list + estate.property.type + + + + + + + + + estate.property.type.view.form + estate.property.type + +
+ + + + + +
+
+
+ +
diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index bd37accd558..58cf28b0496 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -1,97 +1,119 @@ - + - - - Properties - estate.property - list,form,kanban - {'search_default_available': 1} - -

- Create your first real estate property advertisement! -

-

- Here you can define all the details of the properties you want to sell. -

-
-
- - estate.property.list - estate.property - - - - - - - - - - - - + + Properties + estate.property + list,form,kanban + {'search_default_available': 1} + +

+ Create your first real estate property advertisement! +

+

+ Here you can define all the details of the properties you want to sell. +

+
+
- - estate.property.form - estate.property - -
- -
-

- -

-
+ + estate.property.view.list + estate.property + + + + + + + + + + + + + + + + + estate.property.view.form + estate.property + + + +
+

+ +

+ +
+ + + + + + + + + + + + + - - + + + + + + + + + + + + - - + + - - - - - - - - - - - - - - - - - -
- -
-
+ + + + + +
+ +
+
+ + + estate.property.view.search + estate.property + + + + + + + + + + + + + + + + + + + + + + + + - - estate.property.search - estate.property - - - - - - - - - - - - - - - - -
From ee21cac66000e277b8e14f2f7b5f0595e63c7b40 Mon Sep 17 00:00:00 2001 From: vrushibh Date: Thu, 2 Jul 2026 10:46:53 +0530 Subject: [PATCH 7/8] [IMP] estate: automate fields and views Prior to this commit: -Users had to manually calculate total area and find the best offer. -Offers had no validity or deadline tracking. -Garden details had to be typed manually when enabling a garden. -Views and tags lacked distinct colors and code descriptions. Post this commit: -Total area, square area, and best offer price are computed automatically. -Offers now calculate validity and deadline dates bidirectionally. -Enabling a garden automatically sets default area and orientation. -Property tags show color and property types show their code in dropdowns. -Views, actions, and search filters are updated to support the new fields. Why This Approach: -Using @api.depends decorators ensures computed fields update automatically in real-time. -Inverse methods on offer deadline ensure bidirectional synchronization with validity days. -Onchange handler for garden dynamically guides the user in the frontend before saving. task-8 --- estate/__manifest__.py | 1 - estate/models/estate_property.py | 52 ++++++++++++++++++-- estate/models/estate_property_offer.py | 34 ++++++++++++- estate/models/estate_property_tag.py | 11 ++++- estate/models/estate_property_type.py | 11 ++++- estate/views/estate_menus.xml | 13 +++++ estate/views/estate_property_offer_views.xml | 15 +++++- estate/views/estate_property_tag_views.xml | 4 ++ estate/views/estate_property_type_views.xml | 6 ++- estate/views/estate_property_views.xml | 22 +++++++-- 10 files changed, 156 insertions(+), 13 deletions(-) diff --git a/estate/__manifest__.py b/estate/__manifest__.py index b00f9afce22..96841850318 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- { "name": "Real Estate", "author": "Odoo S.A.", diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 809658aa5c4..3a2a81b6437 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -1,6 +1,6 @@ from dateutil.relativedelta import relativedelta -from odoo import fields, models +from odoo import api, fields, models class EstateProperty(models.Model): @@ -11,7 +11,7 @@ class EstateProperty(models.Model): description = fields.Text() postcode = fields.Char() date_availability = fields.Date( - default=lambda self: fields.Date.context_today(self) + relativedelta(months=3) + default=lambda self: fields.Date.context_today(self) + relativedelta(months=3), ) expected_price = fields.Float(required=True, copy=False, default=0.0) selling_price = fields.Float(readonly=True, copy=False) @@ -47,7 +47,53 @@ class EstateProperty(models.Model): property_type_id = fields.Many2one("estate.property.type", string="Property Type") buyer_id = fields.Many2one("res.partner", string="Buyer", copy=False) salesperson_id = fields.Many2one( - "res.users", string="Salesperson", default=lambda self: self.env.user + "res.users", + string="Salesperson", + default=lambda self: self.env.user, ) tag_ids = fields.Many2many("estate.property.tag", string="Property Tags") offer_ids = fields.One2many("estate.property.offer", "property_id", string="Offers") + + total_area = fields.Integer( + string="Total Area (sqm)", + compute="_compute_total_area", + help="Total area of the property (living area + garden area)", + ) + best_price = fields.Float( + string="Best Offer", + compute="_compute_best_price", + help="The highest offer received for this property", + store="true", + ) + squre_area = fields.Integer( + string="Squre Area", + compute="_compute_total_squre", + ) + + @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("total_area") + def _compute_total_squre(self): + for record in self: + record.squre_area = record.total_area * record.total_area + + @api.depends("offer_ids.price") + def _compute_best_price(self): + for record in self: + prices = record.offer_ids.mapped("price") + if prices: + record.best_price = max(prices) + else: + record.best_price = 0.0 + + @api.onchange("garden") + def _onchange_garden(self): + if self.garden: + self.garden_area = 10 + self.garden_orientation = "north" + else: + self.garden_area = 0 + self.garden_orientation = False diff --git a/estate/models/estate_property_offer.py b/estate/models/estate_property_offer.py index c9949ed0382..04bd472e937 100644 --- a/estate/models/estate_property_offer.py +++ b/estate/models/estate_property_offer.py @@ -1,4 +1,6 @@ -from odoo import fields, models +from datetime import timedelta + +from odoo import api, fields, models class EstatePropertyOffer(models.Model): @@ -14,5 +16,33 @@ class EstatePropertyOffer(models.Model): copy=False, string="Status", ) - partner_id = fields.Many2one("res.partner", string="Partner",required=True) + 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", 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: + if record.create_date: + create_date = record.create_date.date() + else: + create_date = fields.Date.today() + record.date_deadline = create_date + timedelta(days=record.validity) + + def _inverse_date_deadline(self): + for record in self: + if record.create_date: + create_date = record.create_date.date() + else: + create_date = fields.Date.today() + if record.date_deadline: + record.validity = (record.date_deadline - create_date).days + + @api.onchange("date_deadline") + def _onchange_date_deadline(self): + self._inverse_date_deadline() diff --git a/estate/models/estate_property_tag.py b/estate/models/estate_property_tag.py index c90d1143652..ee4c53fb21b 100644 --- a/estate/models/estate_property_tag.py +++ b/estate/models/estate_property_tag.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +from random import randint from odoo import fields, models @@ -6,5 +6,14 @@ class EstatePropertyTag(models.Model): _name = "estate.property.tag" _description = "Real Estate Property Tag" + _rec_name = 'name' + + def _default_color(self): + return randint(1, 11) name = fields.Char(required=True) + description = fields.Char(required=True) + color = fields.Integer( + string='Color Index', + default=lambda self: self._default_color(), + ) diff --git a/estate/models/estate_property_type.py b/estate/models/estate_property_type.py index 216472e442c..4861a5f634a 100644 --- a/estate/models/estate_property_type.py +++ b/estate/models/estate_property_type.py @@ -1,8 +1,17 @@ -from odoo import fields, models +from odoo import api, fields, models class EstatePropertyType(models.Model): _name = "estate.property.type" _description = "Real Estate Property Type" + _rec_names_search = ["name", "code"] name = fields.Char(required=True, string="Property Type") + code = fields.Char(string="Code") + + @api.depends("name", "code") + def _compute_display_name(self): + for record in self: + record.display_name = ( + f"{record.name} [{record.code}]" if record.code else record.name + ) diff --git a/estate/views/estate_menus.xml b/estate/views/estate_menus.xml index 3c08d2c78fa..89728c67f31 100644 --- a/estate/views/estate_menus.xml +++ b/estate/views/estate_menus.xml @@ -33,4 +33,17 @@ action="estate_property_type_action" sequence="10"/> + + + diff --git a/estate/views/estate_property_offer_views.xml b/estate/views/estate_property_offer_views.xml index 16d222cef52..c6605fe56c9 100644 --- a/estate/views/estate_property_offer_views.xml +++ b/estate/views/estate_property_offer_views.xml @@ -1,14 +1,23 @@ + + Offers Type + estate.property.offer + + estate.property.offer.view.list estate.property.offer - + + + + + @@ -20,9 +29,13 @@
+ + + +
diff --git a/estate/views/estate_property_tag_views.xml b/estate/views/estate_property_tag_views.xml index ed016ca6260..900ffe81c8d 100644 --- a/estate/views/estate_property_tag_views.xml +++ b/estate/views/estate_property_tag_views.xml @@ -18,6 +18,8 @@ + + @@ -30,6 +32,8 @@ + + diff --git a/estate/views/estate_property_type_views.xml b/estate/views/estate_property_type_views.xml index 819000b7da1..bf1afeda951 100644 --- a/estate/views/estate_property_type_views.xml +++ b/estate/views/estate_property_type_views.xml @@ -18,6 +18,7 @@ + @@ -29,7 +30,10 @@
- + + + +
diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index 58cf28b0496..8431a299bc2 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -27,9 +27,12 @@ + + + @@ -48,13 +51,14 @@ - + + @@ -64,6 +68,8 @@ + + @@ -79,8 +85,17 @@ - - + + + + + + + + + + + @@ -108,6 +123,7 @@ + From 4890895c141851bf23f7c3f5c356f2074b5aeaf1 Mon Sep 17 00:00:00 2001 From: vrushibh Date: Thu, 2 Jul 2026 11:07:34 +0530 Subject: [PATCH 8/8] [FIX] estate: add description in property tag demo data Steps to Reproduce: 1. Install or update the `estate` module with demo data enabled. 2. The database initialization/registry preload fails when loading ` estate_demo.xml`. Issue: - A `psycopg2.errors.NotNullViolation: null value in column description of relation estate_property_tag violates not-null constraint` error is thrown, preventing the module from being installed. Cause: - The `description` field on the `estate.property.tag` model is defined as `required=True`. However, the demo tag records (`Cozy`, `Cool`, `Nice`) defined in `estate_demo.xml` did not include the `description` field, violating the not-null constraint. Fix: - Add the `description` field with text values to all the property tag records in `estate_demo.xml`. --- estate/data/estate_demo.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/estate/data/estate_demo.xml b/estate/data/estate_demo.xml index b16fe8169a9..fdaf54b7de2 100644 --- a/estate/data/estate_demo.xml +++ b/estate/data/estate_demo.xml @@ -10,12 +10,15 @@ Cozy + Cozy property tag Cool + Cool property tag Nice + Nice property tag