From 838d9b8cfd48ef15838ce9c5f1631aae96fd1d0e Mon Sep 17 00:00:00 2001 From: mibav-odoo Date: Wed, 31 Dec 2025 16:35:45 +0530 Subject: [PATCH 1/8] [ADD] estate:Initialize new module -Define manifest file -Define new model and security file Chapter 1: Architecture Overview Chapter 2: A New Application Chapter 3: Models And Basic Fields Chapter 4: Security - A Brief Introduction --- estate/__init__.py | 1 + estate/__manifest__.py | 11 +++++++++++ estate/models/__init__.py | 1 + estate/models/estate_property.py | 25 +++++++++++++++++++++++++ estate/security/ir.model.access.csv | 2 ++ 5 files changed, 40 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 create mode 100644 estate/security/ir.model.access.csv diff --git a/estate/__init__.py b/estate/__init__.py new file mode 100644 index 00000000000..0650744f6bc --- /dev/null +++ b/estate/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/estate/__manifest__.py b/estate/__manifest__.py new file mode 100644 index 00000000000..76456d95461 --- /dev/null +++ b/estate/__manifest__.py @@ -0,0 +1,11 @@ +{ + 'name': 'Estate', + 'version': '0.1', + 'summary': 'This is the Real Estate Advertisement module', + 'description': 'This is the Real Estate Advertisement module.', + 'data': [ + 'security/ir.model.access.csv' + ], + 'author': 'Milan Bavishi', + '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..545ed3706b5 --- /dev/null +++ b/estate/models/estate_property.py @@ -0,0 +1,25 @@ +from odoo import fields, models + + +class EstateProperty(models.Model): + _name = "estate_property" + _description = "This is the table of real estate property data" + + 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( + string='Garden Orientation', + 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..32389642d4f --- /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 ce13e139b9d79fce0921a49a84157ac3ed40b9b2 Mon Sep 17 00:00:00 2001 From: mibav-odoo Date: Thu, 1 Jan 2026 17:57:32 +0530 Subject: [PATCH 2/8] [ADD] estate:add basic menus, form and list views - Created estate property form and list view - Created search bar fields Chapter 5: Finally, Some UI To Play With Chapter 6: Basic Views --- estate/__manifest__.py | 4 +- estate/models/estate_property.py | 22 +++++-- estate/views/estate_menus.xml | 16 +++++ estate/views/estate_property_views.xml | 89 ++++++++++++++++++++++++++ 4 files changed, 126 insertions(+), 5 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 76456d95461..2d002cbed32 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -4,7 +4,9 @@ 'summary': 'This is the Real Estate Advertisement module', 'description': 'This is the Real Estate Advertisement module.', 'data': [ - 'security/ir.model.access.csv' + 'security/ir.model.access.csv', + 'views/estate_property_views.xml', + 'views/estate_menus.xml' ], 'author': 'Milan Bavishi', 'license': 'LGPL-3' diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 545ed3706b5..310c185af21 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -1,17 +1,19 @@ from odoo import fields, models +from dateutil.relativedelta import relativedelta class EstateProperty(models.Model): _name = "estate_property" _description = "This is the table of real estate property data" - name = fields.Char(required=True) + name = fields.Char(required=True, default="Unknown") description = fields.Text() postcode = fields.Char() - date_availability = fields.Date() + date_availability = fields.Date(copy=False, + default=lambda self: fields.Date.today() + relativedelta(month=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() @@ -23,3 +25,15 @@ class EstateProperty(models.Model): ('south', 'South'), ('east', 'East'), ('west', 'West')]) + state = fields.Selection( + string='State', + default='new', + required=True, + copy=False, + selection=[('new', 'New'), + ('offer received', 'Offer Received'), + ('offer accepted', 'Offer accepted'), + ('sold', 'Sold'), + ('cancelled', 'Cancelled')] + ) + active = fields.Boolean() diff --git a/estate/views/estate_menus.xml b/estate/views/estate_menus.xml new file mode 100644 index 00000000000..b94dcac94e9 --- /dev/null +++ b/estate/views/estate_menus.xml @@ -0,0 +1,16 @@ + + + + + + + diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml new file mode 100644 index 00000000000..8c4dfc825fa --- /dev/null +++ b/estate/views/estate_property_views.xml @@ -0,0 +1,89 @@ + + + + Estate Property + estate_property + list,form + + + + Estate_Property_list + estate_property + + + + + + + + + + + + + + + + + + + + + + + Estate_Property_view_form + estate_property + +
+ +
+

+ +

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ + + Estate_Property_search + estate_property + + + + + + + + + + + +
From c2aec788f661a4cfc4717080f01df091f2196eee Mon Sep 17 00:00:00 2001 From: mibav-odoo Date: Fri, 2 Jan 2026 12:57:21 +0530 Subject: [PATCH 3/8] [IMP] estate: Added Custom list, form and search views Created search views for the estate.property model. Added menu items and actions to open the property Type Created new model estate.property.type Chapter 6: Basic Views --- estate/__manifest__.py | 1 + estate/models/__init__.py | 1 + estate/models/estate_property.py | 29 ++++++++++++------- estate/models/estate_property_type.py | 8 ++++++ estate/security/ir.model.access.csv | 1 + estate/views/estate_menus.xml | 9 ++++++ estate/views/estate_property_type_views.xml | 32 +++++++++++++++++++++ estate/views/estate_property_views.xml | 19 +++++++----- 8 files changed, 82 insertions(+), 18 deletions(-) create mode 100644 estate/models/estate_property_type.py create mode 100644 estate/views/estate_property_type_views.xml diff --git a/estate/__manifest__.py b/estate/__manifest__.py index 2d002cbed32..ade34545d3b 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -6,6 +6,7 @@ 'data': [ 'security/ir.model.access.csv', 'views/estate_property_views.xml', + 'views/estate_property_type_views.xml', 'views/estate_menus.xml' ], 'author': 'Milan Bavishi', diff --git a/estate/models/__init__.py b/estate/models/__init__.py index 5e1963c9d2f..40092a2d810 100644 --- a/estate/models/__init__.py +++ b/estate/models/__init__.py @@ -1 +1,2 @@ from . import estate_property +from . import estate_property_type diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 310c185af21..589b0914779 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -1,9 +1,10 @@ -from odoo import fields, models from dateutil.relativedelta import relativedelta +from odoo import fields, models + class EstateProperty(models.Model): - _name = "estate_property" + _name = "estate.property" _description = "This is the table of real estate property data" name = fields.Char(required=True, default="Unknown") @@ -21,19 +22,25 @@ class EstateProperty(models.Model): garden_area = fields.Integer() garden_orientation = fields.Selection( string='Garden Orientation', - selection=[('north', 'North'), - ('south', 'South'), - ('east', 'East'), - ('west', 'West')]) + selection=[ + ('north', 'North'), + ('south', 'South'), + ('east', 'East'), + ('west', 'West') + ] + ) state = fields.Selection( string='State', default='new', required=True, copy=False, - selection=[('new', 'New'), - ('offer received', 'Offer Received'), - ('offer accepted', 'Offer accepted'), - ('sold', 'Sold'), - ('cancelled', 'Cancelled')] + selection=[ + ('new', 'New'), + ('offer received', 'Offer Received'), + ('offer accepted', 'Offer accepted'), + ('sold', 'Sold'), + ('cancelled', 'Cancelled') + ] ) active = fields.Boolean() + property_type_id = fields.Many2one('estate.property.type', string="Property Type") diff --git a/estate/models/estate_property_type.py b/estate/models/estate_property_type.py new file mode 100644 index 00000000000..3a73bc95ee0 --- /dev/null +++ b/estate/models/estate_property_type.py @@ -0,0 +1,8 @@ +from odoo import fields, models + + +class EstatePropertyTypes(models.Model): + _name = "estate.property.type" + _description = "This is table contain the types of property" + + name = fields.Char(required=True) diff --git a/estate/security/ir.model.access.csv b/estate/security/ir.model.access.csv index 32389642d4f..ac680bd78a6 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_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 b94dcac94e9..b64f72f562a 100644 --- a/estate/views/estate_menus.xml +++ b/estate/views/estate_menus.xml @@ -12,5 +12,14 @@ name="Properties" parent="estate_property_advertisements" action="estate_property_action"/> + + diff --git a/estate/views/estate_property_type_views.xml b/estate/views/estate_property_type_views.xml new file mode 100644 index 00000000000..0b5a56c1602 --- /dev/null +++ b/estate/views/estate_property_type_views.xml @@ -0,0 +1,32 @@ + + + + Property Type + estate.property.type + list,form + + + + estate.property.type.view.form + estate.property.type + +
+ +
+

+
+
+
+
+
+ + + estate.property.type.view.list + estate.property.type + + + + + + +
diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index 8c4dfc825fa..3d222b51d07 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -2,13 +2,13 @@ Estate Property - estate_property + estate.property list,form - Estate_Property_list - estate_property + estate.property.list + estate.property @@ -31,8 +31,8 @@ - Estate_Property_view_form - estate_property + estate.property.view.form + estate.property
@@ -64,6 +64,7 @@ + @@ -73,8 +74,8 @@ - Estate_Property_search - estate_property + estate.property.search + estate.property @@ -83,6 +84,10 @@ + + + + From d50ff2d17e5fcfb989491be2aff1fd9c39c063d7 Mon Sep 17 00:00:00 2001 From: mibav-odoo Date: Mon, 5 Jan 2026 16:35:31 +0530 Subject: [PATCH 4/8] [IMP] estate: add relational fields and offer views Created Many2one, Many2many, and One2many relations between models. Added a new list and form view for the estate.property.offer model. Updated the property view. Chapter 7: Relations Between Models --- estate/__manifest__.py | 2 + estate/models/__init__.py | 2 + estate/models/estate_property.py | 6 +++ estate/models/estate_property_offer.py | 18 +++++++ estate/models/estate_property_tag.py | 8 +++ estate/security/ir.model.access.csv | 4 +- estate/views/estate_menus.xml | 13 +++-- estate/views/estate_property_offer_views.xml | 23 +++++++++ estate/views/estate_property_tag_views.xml | 36 ++++++++++++++ estate/views/estate_property_views.xml | 51 +++++++++++++------- 10 files changed, 141 insertions(+), 22 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 ade34545d3b..90ce6d1856f 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -7,6 +7,8 @@ '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' ], 'author': 'Milan Bavishi', diff --git a/estate/models/__init__.py b/estate/models/__init__.py index 40092a2d810..2f1821a39c1 100644 --- a/estate/models/__init__.py +++ b/estate/models/__init__.py @@ -1,2 +1,4 @@ from . import estate_property from . import estate_property_type +from . import estate_property_tag +from . import estate_property_offer diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 589b0914779..0be322c3d5e 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -44,3 +44,9 @@ class EstateProperty(models.Model): ) active = fields.Boolean() property_type_id = fields.Many2one('estate.property.type', string="Property Type") + customer = fields.Many2one("res.partner", string="Customer", copy=False) + salesperson = fields.Many2one( + "res.users", string="Salesperson", default=lambda self: self.env.user + ) + tag_ids = fields.Many2many('estate.property.tag', string="Tag type") + 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..002781e75bb --- /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 = "This is the table of offer that is received for property" + + price = fields.Float('price') + status = fields.Selection( + string="Status", + selection=[ + ('accepted', 'Accepted'), + ('refused', 'Refused') + ], + copy=False + ) + partner_id = fields.Many2one('res.partner', required=True) + property_id = fields.Many2one('estate.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..1e2a660e399 --- /dev/null +++ b/estate/models/estate_property_tag.py @@ -0,0 +1,8 @@ +from odoo import fields, models + + +class EstatePropertyTags(models.Model): + _name = "estate.property.tag" + _description = "This table contain the types of tags" + + name = fields.Char(required=True) diff --git a/estate/security/ir.model.access.csv b/estate/security/ir.model.access.csv index ac680bd78a6..05bd9eefba4 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 access_estate_property,access_estate_property,model_estate_property,base.group_user,1,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 +access_estate_property_tag,access_estate_property_tag,model_estate_property_tag,base.group_user,1,1,1,1 +access_estate_property_offer,access_estate_property_offer,model_estate_property_offer,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 b64f72f562a..f780b18daa2 100644 --- a/estate/views/estate_menus.xml +++ b/estate/views/estate_menus.xml @@ -6,7 +6,8 @@ + parent="estate_property_menu_root" + sequence="1"/> + parent="estate_property_menu_root" + sequence="2"/> + action="estate_property_type_action"/> + diff --git a/estate/views/estate_property_offer_views.xml b/estate/views/estate_property_offer_views.xml new file mode 100644 index 00000000000..0eed09fd2a3 --- /dev/null +++ b/estate/views/estate_property_offer_views.xml @@ -0,0 +1,23 @@ + + + + Property Offer + estate.property.offer + list,form + + + + estate.property.offer.view.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..a4b0388fab4 --- /dev/null +++ b/estate/views/estate_property_tag_views.xml @@ -0,0 +1,36 @@ + + + + Property Tag + estate.property.tag + list,form + + + + estate.property.tag.view.form + estate.property.tag + +
+ +
+
+
+

+
+
+
+
+
+ + + estate.property.tag.view.list + estate.property.tag + + + + + + + +
diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index 3d222b51d07..f9784f7c1b2 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -11,21 +11,18 @@ estate.property - + + + - - - + + - - - -
@@ -40,21 +37,25 @@

+

+ +

- + - - - - - - + + + + + + + + - - + @@ -64,7 +65,21 @@ - + + + + + + + + + + + + + + + From ff6aab4dca28f779b09400957fd7db7d00838574 Mon Sep 17 00:00:00 2001 From: mibav-odoo Date: Tue, 6 Jan 2026 23:17:26 +0530 Subject: [PATCH 5/8] [IMP] estate: add computed fields and onchange logic Define 'total_area' and 'best_price' as computed fields on the property model. Add date_deadline and validity with inverse methods to handle offer durations. Use onchange to update garden area and orientation when garden is toggled. Chapter 8: Computed Fields And Onchanges --- estate/models/estate_property.py | 65 +++++++++++++------- estate/models/estate_property_offer.py | 17 ++++- estate/views/estate_property_offer_views.xml | 26 ++++++-- estate/views/estate_property_tag_views.xml | 10 +-- estate/views/estate_property_views.xml | 5 +- 5 files changed, 89 insertions(+), 34 deletions(-) diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 0be322c3d5e..303d58062e1 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -1,17 +1,18 @@ from dateutil.relativedelta import relativedelta -from odoo import fields, models +from odoo import api, fields, models class EstateProperty(models.Model): _name = "estate.property" _description = "This is the table of real estate property data" - name = fields.Char(required=True, default="Unknown") + name = fields.Char(required=True) description = fields.Text() postcode = fields.Char() - date_availability = fields.Date(copy=False, - default=lambda self: fields.Date.today() + relativedelta(month=3)) + date_availability = fields.Date( + copy=False, default=lambda self: fields.Date.today() + relativedelta(month=3) + ) expected_price = fields.Float(required=True) selling_price = fields.Float(readonly=True, copy=False) bedrooms = fields.Integer(default=2) @@ -21,32 +22,54 @@ class EstateProperty(models.Model): garden = fields.Boolean() garden_area = fields.Integer() garden_orientation = fields.Selection( - string='Garden Orientation', + string="Garden Orientation", selection=[ - ('north', 'North'), - ('south', 'South'), - ('east', 'East'), - ('west', 'West') - ] + ("north", "North"), + ("south", "South"), + ("east", "East"), + ("west", "West"), + ], ) state = fields.Selection( - string='State', - default='new', + string="State", + default="new", required=True, copy=False, 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"), + ], ) active = fields.Boolean() - property_type_id = fields.Many2one('estate.property.type', string="Property Type") + property_type_id = fields.Many2one("estate.property.type", string="Property Type") customer = fields.Many2one("res.partner", string="Customer", copy=False) salesperson = fields.Many2one( "res.users", string="Salesperson", default=lambda self: self.env.user ) - tag_ids = fields.Many2many('estate.property.tag', string="Tag type") - offer_ids = fields.One2many('estate.property.offer', 'property_id', string="Offers") + tag_ids = fields.Many2many("estate.property.tag", string="Tag type") + offer_ids = fields.One2many("estate.property.offer", "property_id", string="Offers") + total_area = fields.Integer(compute="_compute_total") + best_price = fields.Float(compute="_compute_maximum") + + @api.depends("living_area", "garden_area") + def _compute_total(self): + for record in self: + record.total_area = record.living_area + record.garden_area + + @api.depends("offer_ids.price") + def _compute_maximum(self): + for record in self: + prices = record.offer_ids.mapped("price") + record.best_price = max(prices) if prices else 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 = None diff --git a/estate/models/estate_property_offer.py b/estate/models/estate_property_offer.py index 002781e75bb..828832ec5bc 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 dateutil.relativedelta import relativedelta + +from odoo import api, fields, models class EstatePropertyOffer(models.Model): @@ -16,3 +18,16 @@ class EstatePropertyOffer(models.Model): ) partner_id = fields.Many2one('res.partner', required=True) property_id = fields.Many2one('estate.property', required=True) + validity = fields.Integer(default=7) + date_deadline = fields.Date(compute="_compute_date_deadline", inverse="_inverse_date_deadline", store=True) + + @api.depends("validity") + def _compute_date_deadline(self): + for record in self: + create = record.create_date or fields.Date.today() + record.date_deadline = (create + relativedelta(days=record.validity)) + + def _inverse_date_deadline(self): + for record in self: + create = record.create_date or fields.Date.today() + record.validity = (record.date_deadline - fields.Date.today(create)).days diff --git a/estate/views/estate_property_offer_views.xml b/estate/views/estate_property_offer_views.xml index 0eed09fd2a3..3a189544943 100644 --- a/estate/views/estate_property_offer_views.xml +++ b/estate/views/estate_property_offer_views.xml @@ -6,18 +6,36 @@ list,form - + estate.property.offer.view.form estate.property.offer
- - - + + + + + + +
+ + estate_property_offer.view.list + estate.property.offer + + + + + + + + + + +
diff --git a/estate/views/estate_property_tag_views.xml b/estate/views/estate_property_tag_views.xml index a4b0388fab4..b8b76a447a2 100644 --- a/estate/views/estate_property_tag_views.xml +++ b/estate/views/estate_property_tag_views.xml @@ -12,12 +12,9 @@
-
-
-
-

-
+ +

+
@@ -31,6 +28,5 @@ - diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index f9784f7c1b2..19e28186cf2 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -43,12 +43,14 @@ + + @@ -63,7 +65,7 @@ - + @@ -100,6 +102,7 @@ + From cfaae95151dc688b38140c2e8c72fabfac867247 Mon Sep 17 00:00:00 2001 From: mibav-odoo Date: Wed, 7 Jan 2026 23:58:48 +0530 Subject: [PATCH 6/8] [IMP] estate: Improve property and offer management and fix review comments -Add accept/refuse actions for property offers with validation. -Update property state and selling price on offer acceptance -Add action buttons for offers and properties -Add SQL constraint -Rename compute methods to follow naming conventions -Fix XML IDs according to guidelines -Make non-essential fields optional for better form readability -Add missing newline at end of access rights CSV file Chapter 9: Ready For Some Action Chapter 10: Constraints --- estate/models/estate_property.py | 39 ++++++++++++++++---- estate/models/estate_property_offer.py | 27 +++++++++++++- estate/models/estate_property_tag.py | 5 +++ estate/models/estate_property_type.py | 5 +++ estate/security/ir.model.access.csv | 2 +- estate/views/estate_menus.xml | 35 ++++++++++-------- estate/views/estate_property_offer_views.xml | 11 +++++- estate/views/estate_property_tag_views.xml | 4 +- estate/views/estate_property_type_views.xml | 4 +- estate/views/estate_property_views.xml | 28 +++++++------- 10 files changed, 116 insertions(+), 44 deletions(-) diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 303d58062e1..ac52d2b69a5 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -1,6 +1,7 @@ from dateutil.relativedelta import relativedelta -from odoo import api, fields, models +from odoo import _, api, fields, models +from odoo.exceptions import UserError class EstateProperty(models.Model): @@ -45,22 +46,32 @@ class EstateProperty(models.Model): ) active = fields.Boolean() property_type_id = fields.Many2one("estate.property.type", string="Property Type") - customer = fields.Many2one("res.partner", string="Customer", copy=False) + customer = fields.Many2one( + "res.partner", string="Customer", copy=False, readonly=True + ) salesperson = fields.Many2one( - "res.users", string="Salesperson", default=lambda self: self.env.user + "res.users", + string="Salesperson", + readonly=True, + default=lambda self: self.env.user, ) tag_ids = fields.Many2many("estate.property.tag", string="Tag type") offer_ids = fields.One2many("estate.property.offer", "property_id", string="Offers") - total_area = fields.Integer(compute="_compute_total") - best_price = fields.Float(compute="_compute_maximum") + total_area = fields.Integer(compute="_compute_total_area") + best_price = fields.Float(compute="_compute_best_price") + + _check_expected_price = models.Constraint( + "check(expected_price > 0)", + "The Expected Price must be positive", + ) @api.depends("living_area", "garden_area") - def _compute_total(self): + 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_maximum(self): + def _compute_best_price(self): for record in self: prices = record.offer_ids.mapped("price") record.best_price = max(prices) if prices else 0.0 @@ -73,3 +84,17 @@ def _onchange_garden(self): else: self.garden_area = 0 self.garden_orientation = None + + def action_cancel(self): + if self.filtered(lambda record: record.state == "sold"): + raise UserError( + _("You cannot cancel the property offer that already sold.") + ) + self.write({"state": "cancelled"}) + + def action_sold(self): + if self.filtered(lambda record: record.state == "cancelled"): + raise UserError( + _("You cannot Sold the property offer that already Cancelled") + ) + self.write({"state": "sold"}) diff --git a/estate/models/estate_property_offer.py b/estate/models/estate_property_offer.py index 828832ec5bc..483e59d6f53 100644 --- a/estate/models/estate_property_offer.py +++ b/estate/models/estate_property_offer.py @@ -1,6 +1,7 @@ from dateutil.relativedelta import relativedelta -from odoo import api, fields, models +from odoo import _, api, fields, models +from odoo.exceptions import UserError class EstatePropertyOffer(models.Model): @@ -21,6 +22,11 @@ class EstatePropertyOffer(models.Model): validity = fields.Integer(default=7) date_deadline = fields.Date(compute="_compute_date_deadline", inverse="_inverse_date_deadline", store=True) + _check_offer_price = models.Constraint( + "check(price > 0)", + "Offer price must be positive", + ) + @api.depends("validity") def _compute_date_deadline(self): for record in self: @@ -31,3 +37,22 @@ def _inverse_date_deadline(self): for record in self: create = record.create_date or fields.Date.today() record.validity = (record.date_deadline - fields.Date.today(create)).days + + def action_accept_offer(self): + for offer in self: + if offer.property_id.customer: + raise UserError(_("Only one offer can be accepted.")) + offer.property_id.customer = offer.partner_id + offer.property_id.selling_price = offer.price + offer.property_id.state = "sold" + offer.status = "accepted" + other_offer = offer.property_id.offer_ids.filtered( + lambda s: s.id != offer.id + ) + other_offer.status = "refused" + + def action_refuse_offer(self): + for offer in self: + offer.status = "refused" + + return True diff --git a/estate/models/estate_property_tag.py b/estate/models/estate_property_tag.py index 1e2a660e399..49bcd213b01 100644 --- a/estate/models/estate_property_tag.py +++ b/estate/models/estate_property_tag.py @@ -6,3 +6,8 @@ class EstatePropertyTags(models.Model): _description = "This table contain the types of tags" name = fields.Char(required=True) + + _name_uniq = models.Constraint( + "unique(name)", + "Tag name is must be Unique", + ) diff --git a/estate/models/estate_property_type.py b/estate/models/estate_property_type.py index 3a73bc95ee0..2e96360a96d 100644 --- a/estate/models/estate_property_type.py +++ b/estate/models/estate_property_type.py @@ -6,3 +6,8 @@ class EstatePropertyTypes(models.Model): _description = "This is table contain the types of property" name = fields.Char(required=True) + + _name_uniq = models.Constraint( + "unique(name)", + "Property Types must be Unique", + ) diff --git a/estate/security/ir.model.access.csv b/estate/security/ir.model.access.csv index 05bd9eefba4..89f97c50842 100644 --- a/estate/security/ir.model.access.csv +++ b/estate/security/ir.model.access.csv @@ -2,4 +2,4 @@ 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_type,access_estate_property_type,model_estate_property_type,base.group_user,1,1,1,1 access_estate_property_tag,access_estate_property_tag,model_estate_property_tag,base.group_user,1,1,1,1 -access_estate_property_offer,access_estate_property_offer,model_estate_property_offer,base.group_user,1,1,1,1 \ No newline at end of file +access_estate_property_offer,access_estate_property_offer,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 f780b18daa2..9440c20e1fc 100644 --- a/estate/views/estate_menus.xml +++ b/estate/views/estate_menus.xml @@ -1,32 +1,35 @@ + parent="estate_property_menu" + sequence="5"/> + parent="estate_property_menu_advertisements" + action="estate_property_action" + sequence="5"/> + parent="estate_property_menu" + sequence="10"/> + parent="estate_property_menu_settings" + action="estate_property_type_action" + sequence="5"/> + parent="estate_property_menu_settings" + action="estate_property_tag_action" + sequence="10"/> diff --git a/estate/views/estate_property_offer_views.xml b/estate/views/estate_property_offer_views.xml index 3a189544943..908dc6492bd 100644 --- a/estate/views/estate_property_offer_views.xml +++ b/estate/views/estate_property_offer_views.xml @@ -15,7 +15,6 @@ - @@ -32,6 +31,16 @@ + +

diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index edaae90d614..df42738e0b3 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -17,7 +17,8 @@ - + @@ -48,7 +49,7 @@

- +