From 4da5a7b7f154631db114800b4b58a0c6743f0ac7 Mon Sep 17 00:00:00 2001
From: hapat-odoo
Date: Fri, 31 Oct 2025 11:32:49 +0530
Subject: [PATCH 01/13] [ADD] estate: Added a New Module estate_property
Added a new module for estate property and completed tasks 1, 2, and 3.
I added a models folder and learned to push data into PostgreSQL using ORM.
---
Estate/__init__.py | 1 +
Estate/__manifest__.py | 8 ++++++++
Estate/models/__init__.py | 1 +
Estate/models/estate_property.py | 35 ++++++++++++++++++++++++++++++++
4 files changed, 45 insertions(+)
create mode 100644 Estate/__init__.py
create mode 100644 Estate/__manifest__.py
create mode 100644 Estate/models/__init__.py
create mode 100644 Estate/models/estate_property.py
diff --git a/Estate/__init__.py b/Estate/__init__.py
new file mode 100644
index 00000000000..9a7e03eded3
--- /dev/null
+++ b/Estate/__init__.py
@@ -0,0 +1 @@
+from . import models
\ No newline at end of file
diff --git a/Estate/__manifest__.py b/Estate/__manifest__.py
new file mode 100644
index 00000000000..63ced7ca137
--- /dev/null
+++ b/Estate/__manifest__.py
@@ -0,0 +1,8 @@
+{
+ 'name' :'estate',
+ 'depends':['base'],
+ 'application': True,
+ 'installable': True,
+ 'category': 'Tutorials',
+ 'license': 'AGPL-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..796e4d2903f
--- /dev/null
+++ b/Estate/models/estate_property.py
@@ -0,0 +1,35 @@
+#always check for odoo import from venv due to which errors in psql might arise
+#structure module name
+
+#Models
+# init
+# module property - this consist of all files u wanna add and substract
+#init
+#manifest
+
+
+
+from odoo import models, fields
+
+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([
+ ('north', 'North'),
+ ('south', 'South'),
+ ('east', 'East'),
+ ('west', 'West'),
+ ])
From e5c07e0db4ad550d628294cb304e93da398cc305 Mon Sep 17 00:00:00 2001
From: hapat-odoo
Date: Fri, 31 Oct 2025 15:15:00 +0530
Subject: [PATCH 02/13] [IMP] estate: Implemented the Security Logic
Enhanced the Security Logic for improved protection. Added the access control
rights for the estate_property.py file
---
Estate/__init__.py | 3 ++-
Estate/__manifest__.py | 7 ++++++-
Estate/models/__init__.py | 1 +
Estate/models/estate_property.py | 11 +----------
Estate/security/ir.model.access.csv | 2 ++
Estate/views/estate_menus.xml | 2 ++
Estate/views/estate_property_views.xml | 0
7 files changed, 14 insertions(+), 12 deletions(-)
create mode 100644 Estate/security/ir.model.access.csv
create mode 100644 Estate/views/estate_menus.xml
create mode 100644 Estate/views/estate_property_views.xml
diff --git a/Estate/__init__.py b/Estate/__init__.py
index 9a7e03eded3..899bcc97f0f 100644
--- a/Estate/__init__.py
+++ b/Estate/__init__.py
@@ -1 +1,2 @@
-from . import models
\ No newline at end of file
+from . import models
+
diff --git a/Estate/__manifest__.py b/Estate/__manifest__.py
index 63ced7ca137..870a555325c 100644
--- a/Estate/__manifest__.py
+++ b/Estate/__manifest__.py
@@ -3,6 +3,11 @@
'depends':['base'],
'application': True,
'installable': True,
+ 'author':'estate',
'category': 'Tutorials',
- 'license': 'AGPL-3'
+ 'license': 'AGPL-3',
+ 'data': [
+ 'security/ir.model.access.csv'
+ 'views/estate_property_views.xml'
+ ]
}
diff --git a/Estate/models/__init__.py b/Estate/models/__init__.py
index 5e1963c9d2f..c5006b18cf8 100644
--- a/Estate/models/__init__.py
+++ b/Estate/models/__init__.py
@@ -1 +1,2 @@
from . import estate_property
+
diff --git a/Estate/models/estate_property.py b/Estate/models/estate_property.py
index 796e4d2903f..f58ac118a9d 100644
--- a/Estate/models/estate_property.py
+++ b/Estate/models/estate_property.py
@@ -1,16 +1,7 @@
-#always check for odoo import from venv due to which errors in psql might arise
-#structure module name
-
-#Models
-# init
-# module property - this consist of all files u wanna add and substract
-#init
-#manifest
-
-
from odoo import models, fields
+
class EstateProperty(models.Model):
_name = "estate.property"
_description = "Real Estate Property"
diff --git a/Estate/security/ir.model.access.csv b/Estate/security/ir.model.access.csv
new file mode 100644
index 00000000000..3b6948e942e
--- /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,model_estate_property,base.group_user,1,1,1,1
diff --git a/Estate/views/estate_menus.xml b/Estate/views/estate_menus.xml
new file mode 100644
index 00000000000..94ec487e3ee
--- /dev/null
+++ b/Estate/views/estate_menus.xml
@@ -0,0 +1,2 @@
+
+
Date: Wed, 5 Nov 2025 15:14:49 +0530
Subject: [PATCH 03/13] [IMP] estate: Added menu structure and property action
Introduced a new menu structure for the estate module, including a root menu and submenus for properties. Also added an action for managing estate properties with a corresponding view mode.
---
Estate/__manifest__.py | 13 +++++++------
Estate/views/estate_menus.xml | 12 +++++++++++-
Estate/views/estate_property_views.xml | 12 ++++++++++++
3 files changed, 30 insertions(+), 7 deletions(-)
diff --git a/Estate/__manifest__.py b/Estate/__manifest__.py
index 870a555325c..a38f73ebb1b 100644
--- a/Estate/__manifest__.py
+++ b/Estate/__manifest__.py
@@ -1,13 +1,14 @@
{
- 'name' :'estate',
- 'depends':['base'],
+ 'name': 'estate',
+ 'depends': ['base'],
'application': True,
'installable': True,
- 'author':'estate',
+ 'author': 'estate',
'category': 'Tutorials',
'license': 'AGPL-3',
'data': [
- 'security/ir.model.access.csv'
- 'views/estate_property_views.xml'
- ]
+ 'security/ir.model.access.csv',
+ 'views/estate_property_views.xml',
+ 'views/estate_menus.xml',
+ ],
}
diff --git a/Estate/views/estate_menus.xml b/Estate/views/estate_menus.xml
index 94ec487e3ee..9df798df43a 100644
--- a/Estate/views/estate_menus.xml
+++ b/Estate/views/estate_menus.xml
@@ -1,2 +1,12 @@
-
+
+
+
+
+
diff --git a/Estate/views/estate_property_views.xml b/Estate/views/estate_property_views.xml
index e69de29bb2d..def2d0ea112 100644
--- a/Estate/views/estate_property_views.xml
+++ b/Estate/views/estate_property_views.xml
@@ -0,0 +1,12 @@
+
+
+ Properties
+ estate.property
+ list,form
+
+
+ Create a new property
+
+
+
+
From d95830456d3b469f986b56cf711f19deb8a50607 Mon Sep 17 00:00:00 2001
From: hapat-odoo
Date: Thu, 6 Nov 2025 11:29:06 +0530
Subject: [PATCH 04/13] [IMP] estate: menu structure and added property views
---
Estate/views/estate_menus.xml | 19 +++++++--
Estate/views/estate_property_views.xml | 58 ++++++++++++++++++++++++++
2 files changed, 73 insertions(+), 4 deletions(-)
diff --git a/Estate/views/estate_menus.xml b/Estate/views/estate_menus.xml
index 9df798df43a..a94b6c98069 100644
--- a/Estate/views/estate_menus.xml
+++ b/Estate/views/estate_menus.xml
@@ -1,12 +1,23 @@
+
-
+
+
+
+
diff --git a/Estate/views/estate_property_views.xml b/Estate/views/estate_property_views.xml
index def2d0ea112..6b38116a90c 100644
--- a/Estate/views/estate_property_views.xml
+++ b/Estate/views/estate_property_views.xml
@@ -1,3 +1,4 @@
+
Properties
@@ -9,4 +10,61 @@
+
+
+ estate.property.list
+ estate.property
+
+
+
+
+
+
+
+
+
+ Properties (List)
+ estate.property
+ list
+
+
+
+
+
+
+ estate.property.form
+ estate.property
+
+
+
+
+
+
+ Property(Form)
+ estate.property
+ form
+
+
+
From eb055cb4a28e326abc31a96a024c9d4b65a7b7e3 Mon Sep 17 00:00:00 2001
From: hapat-odoo
Date: Thu, 6 Nov 2025 12:31:26 +0530
Subject: [PATCH 05/13] [IMP] estate: Added state field and search view
-added search view state field and list and form views
[IMP] estate: Added list & form view
-added filter for new or offer received stage
[IMP] estate: Implemented models and views for property offers
-added models and views for property offers field
---
Estate/models/estate_property.py | 13 +++++++++++++
Estate/views/estate_menus.xml | 2 --
Estate/views/estate_property_views.xml | 20 ++++++++++++++++++++
3 files changed, 33 insertions(+), 2 deletions(-)
diff --git a/Estate/models/estate_property.py b/Estate/models/estate_property.py
index f58ac118a9d..9533c66b6dc 100644
--- a/Estate/models/estate_property.py
+++ b/Estate/models/estate_property.py
@@ -24,3 +24,16 @@ class EstateProperty(models.Model):
('east', 'East'),
('west', 'West'),
])
+ state = fields.Selection(
+ [
+ ('new', 'New'),
+ ('offer_received', 'Offer Received'),
+ ('offer_accepted', 'Offer Accepted'),
+ ('sold', 'Sold'),
+ ('canceled', 'Canceled'),
+ ],
+ string="Status",
+ required=True,
+ copy=False,
+ default='new'
+ )
\ No newline at end of file
diff --git a/Estate/views/estate_menus.xml b/Estate/views/estate_menus.xml
index a94b6c98069..d1826527722 100644
--- a/Estate/views/estate_menus.xml
+++ b/Estate/views/estate_menus.xml
@@ -5,12 +5,10 @@
-
-
+
+
+ estate.property.search
+ estate.property
+
+
+
+
+
+
+
+
+
+
+
+
From c9b39135a8f2583c03658ca728ce730fdaeb306b Mon Sep 17 00:00:00 2001
From: hapat-odoo
Date: Mon, 10 Nov 2025 00:00:39 +0530
Subject: [PATCH 06/13] [IMP] estate: Implemented models and views for property
offers, tags, and types
Implemented models and views for property offers
tags and types
---
Estate/__manifest__.py | 3 ++
Estate/models/__init__.py | 3 ++
Estate/models/estate_property.py | 8 +++++-
Estate/models/estate_property_offer.py | 16 +++++++++++
Estate/models/estate_property_tag.py | 7 +++++
Estate/models/estate_property_type.py | 7 +++++
Estate/security/ir.model.access.csv | 3 ++
Estate/views/estate_menus.xml | 13 +++++++++
Estate/views/estate_property_offer_views.xml | 29 ++++++++++++++++++++
Estate/views/estate_property_tags_views.xml | 7 +++++
Estate/views/estate_property_type_views.xml | 7 +++++
Estate/views/estate_property_views.xml | 22 +++++++++++++--
12 files changed, 122 insertions(+), 3 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_tags_views.xml
create mode 100644 Estate/views/estate_property_type_views.xml
diff --git a/Estate/__manifest__.py b/Estate/__manifest__.py
index a38f73ebb1b..68e2053758a 100644
--- a/Estate/__manifest__.py
+++ b/Estate/__manifest__.py
@@ -9,6 +9,9 @@
'data': [
'security/ir.model.access.csv',
'views/estate_property_views.xml',
+ 'views/estate_property_type_views.xml',
+ 'views/estate_property_offer_views.xml',
+ 'views/estate_property_tags_views.xml',
'views/estate_menus.xml',
],
}
diff --git a/Estate/models/__init__.py b/Estate/models/__init__.py
index c5006b18cf8..c58783065a3 100644
--- a/Estate/models/__init__.py
+++ b/Estate/models/__init__.py
@@ -1,2 +1,5 @@
from . import estate_property
+from . import estate_property_offer
+from . import estate_property_type
+from . import estate_property_tag
diff --git a/Estate/models/estate_property.py b/Estate/models/estate_property.py
index 9533c66b6dc..fa8df5520b1 100644
--- a/Estate/models/estate_property.py
+++ b/Estate/models/estate_property.py
@@ -36,4 +36,10 @@ class EstateProperty(models.Model):
required=True,
copy=False,
default='new'
- )
\ No newline at end of file
+ )
+ active = fields.Boolean(default=True)
+ property_type_id = fields.Many2one("estate.property.type", string="Property Type")
+ buyer_id = fields.Many2one("res.partner", string="Buyer")
+ salesperson_id = fields.Many2one("res.users", string="Salesperson")
+ tag_ids = fields.Many2many("estate.property.tag", string="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..467eca5a3c6
--- /dev/null
+++ b/Estate/models/estate_property_offer.py
@@ -0,0 +1,16 @@
+from odoo import models, fields
+
+class EstatePropertyOffer(models.Model):
+ _name = "estate.property.offer"
+ _description = "Real Estate Property Offer"
+ price = fields.Float(required=True)
+ status = fields.Selection(
+ [
+ ('pending', 'Pending'),
+ ('accepted', 'Accepted'),
+ ('refused', 'Refused'),
+ ],
+ )
+
+ property_id = fields.Many2one("estate.property", string="Property", required=True)
+ partner_id = fields.Many2one("res.partner", string="Buyer", required=True)
diff --git a/Estate/models/estate_property_tag.py b/Estate/models/estate_property_tag.py
new file mode 100644
index 00000000000..8612845e807
--- /dev/null
+++ b/Estate/models/estate_property_tag.py
@@ -0,0 +1,7 @@
+from odoo import models, fields
+
+class EstatePropertyTag(models.Model):
+ _name = "estate.property.tag"
+ _description = "Real Estate Property Tag"
+
+ name = fields.Char(required=True)
\ No newline at end of file
diff --git a/Estate/models/estate_property_type.py b/Estate/models/estate_property_type.py
new file mode 100644
index 00000000000..85a0f7217e3
--- /dev/null
+++ b/Estate/models/estate_property_type.py
@@ -0,0 +1,7 @@
+from odoo import models, fields
+
+class EstatePropertyType(models.Model):
+ _name = "estate.property.type"
+ _description = "Real Estate Property Type"
+
+ name = fields.Char(required=True)
\ No newline at end of file
diff --git a/Estate/security/ir.model.access.csv b/Estate/security/ir.model.access.csv
index 3b6948e942e..f9b2ee19371 100644
--- a/Estate/security/ir.model.access.csv
+++ b/Estate/security/ir.model.access.csv
@@ -1,2 +1,5 @@
id,name,model_id/id,group_id/id,perm_read,perm_write,perm_create,perm_unlink
estate.access_estate_property,access_estate_property,model_estate_property,base.group_user,1,1,1,1
+
+estate.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,model_estate_property_tag,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 d1826527722..dc6ef565650 100644
--- a/Estate/views/estate_menus.xml
+++ b/Estate/views/estate_menus.xml
@@ -18,4 +18,17 @@
name="Form"
parent="estate_property_menu"
action="estate_property_form_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..80115a18077
--- /dev/null
+++ b/Estate/views/estate_property_offer_views.xml
@@ -0,0 +1,29 @@
+
+
+ estate.property.offer.list
+ estate.property.offer
+
+
+
+
+
+
+
+
+
+
+ estate.property.offer.form
+ estate.property.offer
+
+
+
+
+
\ No newline at end of file
diff --git a/Estate/views/estate_property_tags_views.xml b/Estate/views/estate_property_tags_views.xml
new file mode 100644
index 00000000000..9f06bbdbf11
--- /dev/null
+++ b/Estate/views/estate_property_tags_views.xml
@@ -0,0 +1,7 @@
+
+
+ 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..6cbd93a23cf
--- /dev/null
+++ b/Estate/views/estate_property_type_views.xml
@@ -0,0 +1,7 @@
+
+
+ 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 2d8ef30fa32..fa3e0b10d5f 100644
--- a/Estate/views/estate_property_views.xml
+++ b/Estate/views/estate_property_views.xml
@@ -40,7 +40,6 @@
-
@@ -53,8 +52,25 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -87,4 +103,6 @@
+
+
From b4739528f8ec955792ea185ece57905e2ea667e1 Mon Sep 17 00:00:00 2001
From: hapat-odoo
Date: Mon, 10 Nov 2025 15:31:47 +0530
Subject: [PATCH 07/13] [IMP] estate: added automation logic
Enhanced property model and views with new fields and improved form layout
---
Estate/__manifest__.py | 2 +-
Estate/models/estate_property.py | 44 +++++++++++-
Estate/views/estate_property_views.xml | 99 +++++++++++++++-----------
3 files changed, 99 insertions(+), 46 deletions(-)
diff --git a/Estate/__manifest__.py b/Estate/__manifest__.py
index 68e2053758a..8f29426172d 100644
--- a/Estate/__manifest__.py
+++ b/Estate/__manifest__.py
@@ -8,10 +8,10 @@
'license': 'AGPL-3',
'data': [
'security/ir.model.access.csv',
- 'views/estate_property_views.xml',
'views/estate_property_type_views.xml',
'views/estate_property_offer_views.xml',
'views/estate_property_tags_views.xml',
+ 'views/estate_property_views.xml',
'views/estate_menus.xml',
],
}
diff --git a/Estate/models/estate_property.py b/Estate/models/estate_property.py
index fa8df5520b1..a1b64665664 100644
--- a/Estate/models/estate_property.py
+++ b/Estate/models/estate_property.py
@@ -1,5 +1,6 @@
-from odoo import models, fields
+from odoo import models, fields , api
+from datetime import timedelta
class EstateProperty(models.Model):
@@ -9,7 +10,7 @@ class EstateProperty(models.Model):
name = fields.Char(required=True)
description = fields.Text()
postcode = fields.Char()
- date_availability = fields.Date()
+ create_date = fields.Date()
expected_price = fields.Float(required=True)
selling_price = fields.Float()
bedrooms = fields.Integer()
@@ -43,3 +44,42 @@ class EstateProperty(models.Model):
salesperson_id = fields.Many2one("res.users", string="Salesperson")
tag_ids = fields.Many2many("estate.property.tag", string="Tags")
offer_ids = fields.One2many("estate.property.offer", "property_id", string="Offers")
+ total_area = fields.Float(compute="_compute_total_area", string="Total Area", store=True)
+ best_price = fields.Float(compute="_compute_best_price", string="Best Offer", store=True)
+ validity_days = fields.Integer(default=7)
+ deadline_date = fields.Date(compute="_compute_validity_date", string="Validity Date", store=True)
+ @api.depends('living_area', 'garden_area')
+ def _compute_total_area(self):
+ for record in self:
+ record.total_area = record.living_area + (record.garden_area or 0)
+ @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.0
+
+ @api.depends('create_date', 'validity_days')
+ def _compute_validity_date(self):
+ if self.create_date:
+ for record in self:
+ record.deadline_date = record.create_date + timedelta(days=record.validity_days)
+ else:
+ for record in self:
+ record.deadline_date = fields.Date.today() + timedelta(days=record.validity_days)
+
+ @api.onchange('garden_area')
+ def _onchange_garden(self):
+ for record in self:
+ if self.garden_area == 10:
+ self.garden_orientation = 'north'
+ if self.garden_area == 20:
+ self.garden_orientation = 'south'
+ if self.garden_area == 30:
+ self.garden_orientation = 'east'
+ if self.garden_area == 40:
+ self.garden_orientation = 'west'
+ else:
+ self.garden_area = 0
+ self.garden_orientation = False
\ No newline at end of file
diff --git a/Estate/views/estate_property_views.xml b/Estate/views/estate_property_views.xml
index fa3e0b10d5f..deac9838911 100644
--- a/Estate/views/estate_property_views.xml
+++ b/Estate/views/estate_property_views.xml
@@ -30,51 +30,64 @@
+
+ estate.property.form
+ estate.property
+
+
+
+
+
Property(Form)
From 87dad3d1a96223a97544dabb65cf22b8f5777972 Mon Sep 17 00:00:00 2001
From: hapat-odoo
Date: Mon, 10 Nov 2025 18:37:49 +0530
Subject: [PATCH 08/13] [IMP] estate: Implemented offer acceptance and refusal
actions
Implemented offer acceptance and refusal actions;
added state management buttons in property views
[FIX] estate: resolve Runbot style and linting errors (Ruff/Semgrep)
This commit fixes all Runbot Check Style issues for the estate module.
[FIX] estate: Cleaned up code by removing unnecessary blank lines in model files
---
Estate/__init__.py | 1 -
Estate/models/__init__.py | 1 -
Estate/models/estate_property.py | 127 +++++++++++--------
Estate/models/estate_property_offer.py | 11 +-
Estate/models/estate_property_tag.py | 3 +-
Estate/models/estate_property_type.py | 3 +-
Estate/views/estate_property_offer_views.xml | 4 +
Estate/views/estate_property_views.xml | 9 ++
8 files changed, 102 insertions(+), 57 deletions(-)
diff --git a/Estate/__init__.py b/Estate/__init__.py
index 899bcc97f0f..0650744f6bc 100644
--- a/Estate/__init__.py
+++ b/Estate/__init__.py
@@ -1,2 +1 @@
from . import models
-
diff --git a/Estate/models/__init__.py b/Estate/models/__init__.py
index c58783065a3..82c319c6537 100644
--- a/Estate/models/__init__.py
+++ b/Estate/models/__init__.py
@@ -2,4 +2,3 @@
from . import estate_property_offer
from . import estate_property_type
from . import estate_property_tag
-
diff --git a/Estate/models/estate_property.py b/Estate/models/estate_property.py
index a1b64665664..8b6c983c2cc 100644
--- a/Estate/models/estate_property.py
+++ b/Estate/models/estate_property.py
@@ -1,5 +1,4 @@
-
-from odoo import models, fields , api
+from odoo import models, fields, api
from datetime import timedelta
@@ -10,7 +9,7 @@ class EstateProperty(models.Model):
name = fields.Char(required=True)
description = fields.Text()
postcode = fields.Char()
- create_date = fields.Date()
+ create_date = fields.Datetime()
expected_price = fields.Float(required=True)
selling_price = fields.Float()
bedrooms = fields.Integer()
@@ -20,66 +19,90 @@ class EstateProperty(models.Model):
garden = fields.Boolean()
garden_area = fields.Integer()
garden_orientation = fields.Selection([
- ('north', 'North'),
- ('south', 'South'),
- ('east', 'East'),
- ('west', 'West'),
+ ("north", "North"),
+ ("south", "South"),
+ ("east", "East"),
+ ("west", "West"),
])
- state = fields.Selection(
- [
- ('new', 'New'),
- ('offer_received', 'Offer Received'),
- ('offer_accepted', 'Offer Accepted'),
- ('sold', 'Sold'),
- ('canceled', 'Canceled'),
- ],
- string="Status",
- required=True,
- copy=False,
- default='new'
- )
active = fields.Boolean(default=True)
- property_type_id = fields.Many2one("estate.property.type", string="Property Type")
+
+ state = fields.Selection([
+ ("new", "New"),
+ ("offer_received", "Offer Received"),
+ ("offer_accepted", "Offer Accepted"),
+ ("sold", "Sold"),
+ ("canceled", "Canceled"),
+ ], string="Status", required=True, copy=False, default="new")
+
+ property_type_id = fields.Many2one("estate.property.type", string="Property Type")
buyer_id = fields.Many2one("res.partner", string="Buyer")
salesperson_id = fields.Many2one("res.users", string="Salesperson")
tag_ids = fields.Many2many("estate.property.tag", string="Tags")
offer_ids = fields.One2many("estate.property.offer", "property_id", string="Offers")
+
total_area = fields.Float(compute="_compute_total_area", string="Total Area", store=True)
best_price = fields.Float(compute="_compute_best_price", string="Best Offer", store=True)
- validity_days = fields.Integer(default=7)
- deadline_date = fields.Date(compute="_compute_validity_date", string="Validity Date", store=True)
- @api.depends('living_area', 'garden_area')
+
+ validity = fields.Integer(default=7)
+ date_deadline = fields.Date(
+ compute="_compute_date_deadline",
+ inverse="_inverse_date_deadline",
+ store=True,
+ )
+
+ @api.depends("living_area", "garden_area")
def _compute_total_area(self):
for record in self:
- record.total_area = record.living_area + (record.garden_area or 0)
- @api.depends('offer_ids.price')
+ record.total_area = (record.living_area or 0) + (record.garden_area or 0)
+
+ @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.0
-
- @api.depends('create_date', 'validity_days')
- def _compute_validity_date(self):
- if self.create_date:
- for record in self:
- record.deadline_date = record.create_date + timedelta(days=record.validity_days)
- else:
- for record in self:
- record.deadline_date = fields.Date.today() + timedelta(days=record.validity_days)
-
- @api.onchange('garden_area')
- def _onchange_garden(self):
+ record.best_price = max(record.offer_ids.mapped("price")) if record.offer_ids else 0.0
+
+ @api.depends("create_date", "validity")
+ def _compute_date_deadline(self):
for record in self:
- if self.garden_area == 10:
- self.garden_orientation = 'north'
- if self.garden_area == 20:
- self.garden_orientation = 'south'
- if self.garden_area == 30:
- self.garden_orientation = 'east'
- if self.garden_area == 40:
- self.garden_orientation = 'west'
+ create_date = record.create_date or fields.Date.today()
+ if hasattr(create_date, "date"):
+ create_date = create_date.date()
+ record.date_deadline = create_date + timedelta(days=record.validity)
+
+ def _inverse_date_deadline(self):
+ for record in self:
+ create_date = record.create_date or fields.Date.today()
+ if hasattr(create_date, "date"):
+ create_date = create_date.date()
+ delta = (record.date_deadline - create_date).days if record.date_deadline else 0
+ record.validity = delta
+
+ @api.onchange("garden")
+ def _onchange_garden(self):
+ for record in self:
+ if record.garden:
+ record.garden_area = 10
+ record.garden_orientation = "north"
else:
- self.garden_area = 0
- self.garden_orientation = False
\ No newline at end of file
+ record.garden_area = 0
+ record.garden_orientation = False
+
+ def action_set_sold(self):
+ for record in self:
+ record.state = "sold"
+
+ def action_set_canceled(self):
+ for record in self:
+ record.state = "canceled"
+
+ def action_set_next_status(self):
+ for record in self:
+ if record.state == "new":
+ record.state = "offer_received"
+ elif record.state == "offer_received":
+ record.state = "offer_accepted"
+ elif record.state == "offer_accepted":
+ record.state = "sold"
+
+ def action_back_to_new(self):
+ for record in self:
+ record.state = "new"
diff --git a/Estate/models/estate_property_offer.py b/Estate/models/estate_property_offer.py
index 467eca5a3c6..7a2e3dd34aa 100644
--- a/Estate/models/estate_property_offer.py
+++ b/Estate/models/estate_property_offer.py
@@ -1,5 +1,6 @@
from odoo import models, fields
+
class EstatePropertyOffer(models.Model):
_name = "estate.property.offer"
_description = "Real Estate Property Offer"
@@ -11,6 +12,14 @@ class EstatePropertyOffer(models.Model):
('refused', 'Refused'),
],
)
-
+
property_id = fields.Many2one("estate.property", string="Property", required=True)
partner_id = fields.Many2one("res.partner", string="Buyer", required=True)
+
+ def action_accept_offer(self):
+ for record in self:
+ record.status = 'accepted'
+
+ def action_refuse_offer(self):
+ for record in self:
+ record.status = 'refused'
diff --git a/Estate/models/estate_property_tag.py b/Estate/models/estate_property_tag.py
index 8612845e807..3f730299abb 100644
--- a/Estate/models/estate_property_tag.py
+++ b/Estate/models/estate_property_tag.py
@@ -1,7 +1,8 @@
from odoo import models, fields
+
class EstatePropertyTag(models.Model):
_name = "estate.property.tag"
_description = "Real Estate Property Tag"
- name = fields.Char(required=True)
\ No newline at end of file
+ name = fields.Char(required=True)
diff --git a/Estate/models/estate_property_type.py b/Estate/models/estate_property_type.py
index 85a0f7217e3..ad121b33163 100644
--- a/Estate/models/estate_property_type.py
+++ b/Estate/models/estate_property_type.py
@@ -1,7 +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)
\ No newline at end of file
+ name = fields.Char(required=True)
diff --git a/Estate/views/estate_property_offer_views.xml b/Estate/views/estate_property_offer_views.xml
index 80115a18077..8dbc66dec82 100644
--- a/Estate/views/estate_property_offer_views.xml
+++ b/Estate/views/estate_property_offer_views.xml
@@ -6,6 +6,8 @@
+
+
@@ -20,6 +22,8 @@
+
+
diff --git a/Estate/views/estate_property_views.xml b/Estate/views/estate_property_views.xml
index deac9838911..726f3e4f16f 100644
--- a/Estate/views/estate_property_views.xml
+++ b/Estate/views/estate_property_views.xml
@@ -39,6 +39,13 @@
+
@@ -78,6 +85,8 @@
+
+
From 7a6c138d3798ea069d5577b3eca0515a0836ac80 Mon Sep 17 00:00:00 2001
From: hapat-odoo
Date: Tue, 11 Nov 2025 16:07:02 +0530
Subject: [PATCH 09/13] [IMP] estate: Implemented constraints for price
validation
-Refactored validity field to validity_days
-added constraints for price validation
---
Estate/models/estate_property.py | 31 +++++++++++++++++++++-----
Estate/models/estate_property_offer.py | 13 ++++++++++-
Estate/views/estate_property_views.xml | 4 ++--
3 files changed, 39 insertions(+), 9 deletions(-)
diff --git a/Estate/models/estate_property.py b/Estate/models/estate_property.py
index 8b6c983c2cc..e7e4f83f9dd 100644
--- a/Estate/models/estate_property.py
+++ b/Estate/models/estate_property.py
@@ -1,5 +1,6 @@
from odoo import models, fields, api
from datetime import timedelta
+from odoo.exceptions import ValidationError
class EstateProperty(models.Model):
@@ -39,11 +40,9 @@ class EstateProperty(models.Model):
salesperson_id = fields.Many2one("res.users", string="Salesperson")
tag_ids = fields.Many2many("estate.property.tag", string="Tags")
offer_ids = fields.One2many("estate.property.offer", "property_id", string="Offers")
-
total_area = fields.Float(compute="_compute_total_area", string="Total Area", store=True)
best_price = fields.Float(compute="_compute_best_price", string="Best Offer", store=True)
-
- validity = fields.Integer(default=7)
+ validity_days = fields.Integer(default=7)
date_deadline = fields.Date(
compute="_compute_date_deadline",
inverse="_inverse_date_deadline",
@@ -60,13 +59,13 @@ def _compute_best_price(self):
for record in self:
record.best_price = max(record.offer_ids.mapped("price")) if record.offer_ids else 0.0
- @api.depends("create_date", "validity")
+ @api.depends("create_date", "validity_days")
def _compute_date_deadline(self):
for record in self:
create_date = record.create_date or fields.Date.today()
if hasattr(create_date, "date"):
create_date = create_date.date()
- record.date_deadline = create_date + timedelta(days=record.validity)
+ record.date_deadline = create_date + timedelta(days=record.validity_days)
def _inverse_date_deadline(self):
for record in self:
@@ -74,7 +73,7 @@ def _inverse_date_deadline(self):
if hasattr(create_date, "date"):
create_date = create_date.date()
delta = (record.date_deadline - create_date).days if record.date_deadline else 0
- record.validity = delta
+ record.validity_days = delta
@api.onchange("garden")
def _onchange_garden(self):
@@ -106,3 +105,23 @@ def action_set_next_status(self):
def action_back_to_new(self):
for record in self:
record.state = "new"
+
+ @api.constrains("selling_price","expected_price")
+ def _check_selling_price_ratio(self):
+ for record in self:
+ if record.selling_price <= 0:
+ raise ValidationError("Selling price must be greater than or equal to 0")
+ if record.selling_price < 0.9 * record.expected_price:
+ raise ValidationError("Selling price must be at least 90% of the expected price")
+ if record.expected_price < 0:
+ raise ValidationError("Expected price must be greater than 0")
+
+ _check_expected_price = models.Constraint(
+ 'CHECK(expected_price < 0)',
+ 'The expected price of a property must be strictly positive.'
+ )
+
+ _check_selling_price = models.Constraint(
+ 'CHECK(selling_price < 0)',
+ 'The selling price of a property must be positive.'
+ )
diff --git a/Estate/models/estate_property_offer.py b/Estate/models/estate_property_offer.py
index 7a2e3dd34aa..885c7f2d885 100644
--- a/Estate/models/estate_property_offer.py
+++ b/Estate/models/estate_property_offer.py
@@ -1,4 +1,5 @@
-from odoo import models, fields
+from odoo import models, fields, api
+from odoo.exceptions import ValidationError
class EstatePropertyOffer(models.Model):
@@ -23,3 +24,13 @@ def action_accept_offer(self):
def action_refuse_offer(self):
for record in self:
record.status = 'refused'
+ @api.constrains("price")
+ def _check_price_ratio(self):
+ for record in self:
+ if record.price <= 0.0:
+ raise ValidationError("Price must be greater than 0")
+
+ _check_price = models.Constraint(
+ 'CHECK(price < 0)',
+ 'The price must be strictly positive.'
+ )
\ No newline at end of file
diff --git a/Estate/views/estate_property_views.xml b/Estate/views/estate_property_views.xml
index 726f3e4f16f..3682f69750b 100644
--- a/Estate/views/estate_property_views.xml
+++ b/Estate/views/estate_property_views.xml
@@ -71,7 +71,7 @@
-
+
@@ -127,4 +127,4 @@
-
+
\ No newline at end of file
From ab7946f03f2435957e3169c0bb774010c10d7499 Mon Sep 17 00:00:00 2001
From: hapat-odoo
Date: Wed, 12 Nov 2025 12:46:13 +0530
Subject: [PATCH 10/13] [IMP] estate: added Inline Views, widgets, list order
-Implemented inline views and widgets to enhance the user interface within the
Real Estate module.
-Introduced list-ordering functionality to improve the organization and sorting
of listings.
-Added attribute and option configurations to increase customization capabilities
and overall flexibility for users.
---
Estate/models/estate_property.py | 9 +++--
Estate/models/estate_property_offer.py | 7 ++--
Estate/models/estate_property_tag.py | 5 ++-
Estate/models/estate_property_type.py | 30 +++++++++++++++-
Estate/views/estate_menus.xml | 2 +-
Estate/views/estate_property_offer_views.xml | 9 ++---
Estate/views/estate_property_tags_views.xml | 12 ++++++-
Estate/views/estate_property_type_views.xml | 37 ++++++++++++++++++--
Estate/views/estate_property_views.xml | 16 +++------
9 files changed, 97 insertions(+), 30 deletions(-)
diff --git a/Estate/models/estate_property.py b/Estate/models/estate_property.py
index e7e4f83f9dd..5d9edf6a601 100644
--- a/Estate/models/estate_property.py
+++ b/Estate/models/estate_property.py
@@ -6,7 +6,8 @@
class EstateProperty(models.Model):
_name = "estate.property"
_description = "Real Estate Property"
-
+ _order = "id desc"
+
name = fields.Char(required=True)
description = fields.Text()
postcode = fields.Char()
@@ -106,12 +107,12 @@ def action_back_to_new(self):
for record in self:
record.state = "new"
- @api.constrains("selling_price","expected_price")
+ @api.constrains("selling_price", "expected_price")
def _check_selling_price_ratio(self):
for record in self:
if record.selling_price <= 0:
raise ValidationError("Selling price must be greater than or equal to 0")
- if record.selling_price < 0.9 * record.expected_price:
+ if record.selling_price < 0.9 * record.expected_price:
raise ValidationError("Selling price must be at least 90% of the expected price")
if record.expected_price < 0:
raise ValidationError("Expected price must be greater than 0")
@@ -125,3 +126,5 @@ def _check_selling_price_ratio(self):
'CHECK(selling_price < 0)',
'The selling price of a property must be positive.'
)
+
+ property_type_id = fields.Many2one("estate.property.type", string="Property Type")
diff --git a/Estate/models/estate_property_offer.py b/Estate/models/estate_property_offer.py
index 885c7f2d885..9d51bc799a2 100644
--- a/Estate/models/estate_property_offer.py
+++ b/Estate/models/estate_property_offer.py
@@ -5,6 +5,7 @@
class EstatePropertyOffer(models.Model):
_name = "estate.property.offer"
_description = "Real Estate Property Offer"
+ _order = "price desc"
price = fields.Float(required=True)
status = fields.Selection(
[
@@ -24,13 +25,9 @@ def action_accept_offer(self):
def action_refuse_offer(self):
for record in self:
record.status = 'refused'
+
@api.constrains("price")
def _check_price_ratio(self):
for record in self:
if record.price <= 0.0:
raise ValidationError("Price must be greater than 0")
-
- _check_price = models.Constraint(
- 'CHECK(price < 0)',
- 'The price must be strictly positive.'
- )
\ No newline at end of file
diff --git a/Estate/models/estate_property_tag.py b/Estate/models/estate_property_tag.py
index 3f730299abb..c7f01db7a4b 100644
--- a/Estate/models/estate_property_tag.py
+++ b/Estate/models/estate_property_tag.py
@@ -1,8 +1,11 @@
-from odoo import models, fields
+from odoo import models, fields, api
+from odoo.exceptions import ValidationError
class EstatePropertyTag(models.Model):
_name = "estate.property.tag"
_description = "Real Estate Property Tag"
+ _order = "name desc"
+ color = fields.Integer( string='Color Index', default=3)
name = fields.Char(required=True)
diff --git a/Estate/models/estate_property_type.py b/Estate/models/estate_property_type.py
index ad121b33163..62d382a993e 100644
--- a/Estate/models/estate_property_type.py
+++ b/Estate/models/estate_property_type.py
@@ -1,8 +1,36 @@
-from odoo import models, fields
+from odoo import models, fields, api
+from odoo.exceptions import ValidationError
class EstatePropertyType(models.Model):
_name = "estate.property.type"
_description = "Real Estate Property Type"
+ _order="name desc"
name = fields.Char(required=True)
+ property_ids = fields.One2many(
+ "estate.property", "property_type_id", string="Properties")
+ sequence = fields.Integer('Sequence', default=7)
+ offer_count = fields.Integer(
+ string="Number of Offers",
+ compute="_compute_offer_count"
+ )
+
+ @api.depends('property_ids.offer_ids')
+ def _compute_offer_count(self):
+ for property_type in self:
+ offer_count = 0
+ for property in property_type.property_ids:
+ offer_count += len(property.offer_ids)
+ property_type.offer_count = offer_count
+ @api.constrains('name')
+ def _check_type_name_unique(self):
+ for record in self:
+ existing_type = self.search([('name', '=', record.name)])
+ if existing_type:
+ raise ValidationError(f"The property type name '{record.name}' must be unique.")
+
+ _check_type_name_unique_ratio = models.Constraint(
+ 'CHECK(name)',
+ 'The property name must be unique.'
+ )
\ No newline at end of file
diff --git a/Estate/views/estate_menus.xml b/Estate/views/estate_menus.xml
index dc6ef565650..42ebaf48576 100644
--- a/Estate/views/estate_menus.xml
+++ b/Estate/views/estate_menus.xml
@@ -12,7 +12,7 @@
+ action="action_estate_property"/>