From f6b793decd9bcd10a87116499a9de46f36effd78 Mon Sep 17 00:00:00 2001
From: pimai-odoo
Date: Mon, 27 Oct 2025 20:04:32 +0530
Subject: [PATCH 01/11] [ADD] estate: created a new module
Added a new 'real estate' module, created the database, and defined necessary
columns.
---
estate/__init__.py | 1 +
estate/__manifest__.py | 4 ++++
estate/models/__init__.py | 1 +
estate/models/estate_property.py | 23 +++++++++++++++++++++++
4 files changed, 29 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..a9545d463e3
--- /dev/null
+++ b/estate/__manifest__.py
@@ -0,0 +1,4 @@
+{
+ 'name': 'Real Estate',
+ 'depends': ['base'],
+}
diff --git a/estate/models/__init__.py b/estate/models/__init__.py
new file mode 100644
index 00000000000..f4c8fd6db6d
--- /dev/null
+++ b/estate/models/__init__.py
@@ -0,0 +1 @@
+from . import estate_property
\ No newline at end of file
diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py
new file mode 100644
index 00000000000..6d907ee40d7
--- /dev/null
+++ b/estate/models/estate_property.py
@@ -0,0 +1,23 @@
+from odoo import models,fields
+
+class estateproperty(models.Model):
+ _name = "estate.property"
+ _description = "Real Estate Property"
+
+ name = fields.Char(required='True')
+ description = fields.Text(string="Description")
+ postcode = fields.Char(string="Postcode")
+ date_availability = fields.Date(string="Available From")
+ expected_price = fields.Float(string="Expected Price", required=True)
+ selling_price = fields.Float(string="Selling Price", readonly=True)
+ bedrooms = fields.Integer(string="Bedrooms", default=2)
+ living_area = fields.Integer(string="Living Area (sqm)")
+ facades = fields.Integer(string="Facades")
+ garage = fields.Boolean(string="Garage")
+ garden = fields.Boolean(string="Garden")
+ garden_area = fields.Integer(string="Garden Area (sqm)")
+ garden_orientation = fields.Selection(
+ selection=[('north', 'North'),('south', 'South'),('east', 'East'),('west', 'West')],
+ string="Garden Orientation"
+ )
+ active = fields.Boolean(string="Active", default=True)
\ No newline at end of file
From c86ae0a4b9a27e87d137cad6b395d2efb3467e5d Mon Sep 17 00:00:00 2001
From: pimai-odoo
Date: Tue, 28 Oct 2025 19:09:09 +0530
Subject: [PATCH 02/11] [IMP] estate: update fields and menus in real estate
Set default values for fields, added new field attributes, and created menus
for accessing default list and form views in Real Estate module
---
estate/__init__.py | 2 +-
estate/__manifest__.py | 5 ++++
estate/models/__init__.py | 2 +-
estate/models/estate_property.py | 32 +++++++++++++++++++++-----
estate/security/ir.model.access.csv | 2 ++
estate/views/estate_menus.xml | 7 ++++++
estate/views/estate_property_views.xml | 12 ++++++++++
7 files changed, 54 insertions(+), 8 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..0650744f6bc 100644
--- a/estate/__init__.py
+++ b/estate/__init__.py
@@ -1 +1 @@
-from . import models
\ No newline at end of file
+from . import models
diff --git a/estate/__manifest__.py b/estate/__manifest__.py
index a9545d463e3..0c63df47519 100644
--- a/estate/__manifest__.py
+++ b/estate/__manifest__.py
@@ -1,4 +1,9 @@
{
'name': 'Real Estate',
'depends': ['base'],
+ 'data': [
+ 'security/ir.model.access.csv',
+ 'views/estate_property_views.xml',
+ 'views/estate_menus.xml'
+ ]
}
diff --git a/estate/models/__init__.py b/estate/models/__init__.py
index f4c8fd6db6d..5e1963c9d2f 100644
--- a/estate/models/__init__.py
+++ b/estate/models/__init__.py
@@ -1 +1 @@
-from . import estate_property
\ No newline at end of file
+from . import estate_property
diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py
index 6d907ee40d7..16da4fbb6fd 100644
--- a/estate/models/estate_property.py
+++ b/estate/models/estate_property.py
@@ -1,13 +1,14 @@
-from odoo import models,fields
+from odoo import models, fields
+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)
description = fields.Text(string="Description")
postcode = fields.Char(string="Postcode")
- date_availability = fields.Date(string="Available From")
+ date_availability = fields.Date(string="Availability Date", default=fields.Date.today()+relativedelta(months=3))
expected_price = fields.Float(string="Expected Price", required=True)
selling_price = fields.Float(string="Selling Price", readonly=True)
bedrooms = fields.Integer(string="Bedrooms", default=2)
@@ -17,7 +18,26 @@ class estateproperty(models.Model):
garden = fields.Boolean(string="Garden")
garden_area = fields.Integer(string="Garden Area (sqm)")
garden_orientation = fields.Selection(
- selection=[('north', 'North'),('south', 'South'),('east', 'East'),('west', 'West')],
- string="Garden Orientation"
+ selection=[
+ ("north", "North"),
+ ("south", "South"),
+ ("east", "East"),
+ ("west", "West"),
+ ],
+ string="Garden Orientation",
)
- active = fields.Boolean(string="Active", default=True)
\ No newline at end of file
+ state = fields.Selection(
+ [
+ ("new", "New"),
+ ("offer_received", "Offer Received"),
+ ("offer_accepted", "Offer Accepted"),
+ ("sold", "Sold"),
+ ("cancelled", "Cancelled"),
+ ],
+ string="Status",
+ required=True,
+ copy=False,
+ default="new",
+ )
+ active = fields.Boolean(string="Active", default=True)
+
diff --git a/estate/security/ir.model.access.csv b/estate/security/ir.model.access.csv
new file mode 100644
index 00000000000..98f4671fb0d
--- /dev/null
+++ b/estate/security/ir.model.access.csv
@@ -0,0 +1,2 @@
+id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
+estate.access_estate_property,access_estate_property,estate.model_estate_property,base.group_user,1,1,1,1
diff --git a/estate/views/estate_menus.xml b/estate/views/estate_menus.xml
new file mode 100644
index 00000000000..96696b214c3
--- /dev/null
+++ b/estate/views/estate_menus.xml
@@ -0,0 +1,7 @@
+
+
+
\ No newline at end of file
diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml
new file mode 100644
index 00000000000..def2d0ea112
--- /dev/null
+++ b/estate/views/estate_property_views.xml
@@ -0,0 +1,12 @@
+
+
+ Properties
+ estate.property
+ list,form
+
+
+ Create a new property
+
+
+
+
From 3e951c1d9e8f1da3c0035828c282fc019cca4448 Mon Sep 17 00:00:00 2001
From: pimai-odoo
Date: Wed, 29 Oct 2025 15:52:44 +0530
Subject: [PATCH 03/11] [IMP] estate: add list/form/search views with group by
in Real Estate
Created list, form, and search views, and added group by functionality in the Real Estate.
---
estate/models/estate_property.py | 24 ++++-----
estate/views/estate_menus.xml | 2 +-
estate/views/estate_property_views.xml | 68 ++++++++++++++++++++++++++
3 files changed, 82 insertions(+), 12 deletions(-)
diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py
index 16da4fbb6fd..ea35038cf1a 100644
--- a/estate/models/estate_property.py
+++ b/estate/models/estate_property.py
@@ -1,22 +1,23 @@
from odoo import models, fields
from dateutil.relativedelta import relativedelta
+
class estateproperty(models.Model):
_name = "estate.property"
_description = "Real Estate Property"
name = fields.Char(required=True)
- description = fields.Text(string="Description")
- postcode = fields.Char(string="Postcode")
- date_availability = fields.Date(string="Availability Date", default=fields.Date.today()+relativedelta(months=3))
- expected_price = fields.Float(string="Expected Price", required=True)
- selling_price = fields.Float(string="Selling Price", readonly=True)
- bedrooms = fields.Integer(string="Bedrooms", default=2)
- living_area = fields.Integer(string="Living Area (sqm)")
- facades = fields.Integer(string="Facades")
- garage = fields.Boolean(string="Garage")
- garden = fields.Boolean(string="Garden")
- garden_area = fields.Integer(string="Garden Area (sqm)")
+ description = fields.Text("Description")
+ postcode = fields.Char("Postcode")
+ date_availability = fields.Date("Availability Date", default=fields.Date.today()+relativedelta(months=3))
+ expected_price = fields.Float("Expected Price", required=True)
+ selling_price = fields.Float("Selling Price", readonly=True)
+ bedrooms = fields.Integer("Bedrooms", default=2)
+ living_area = fields.Integer("Living Area (sqm)")
+ facades = fields.Integer("Facades")
+ garage = fields.Boolean("Garage")
+ garden = fields.Boolean("Garden")
+ garden_area = fields.Integer("Garden Area (sqm)")
garden_orientation = fields.Selection(
selection=[
("north", "North"),
@@ -40,4 +41,5 @@ class estateproperty(models.Model):
default="new",
)
active = fields.Boolean(string="Active", default=True)
+
diff --git a/estate/views/estate_menus.xml b/estate/views/estate_menus.xml
index 96696b214c3..47be4a68c82 100644
--- a/estate/views/estate_menus.xml
+++ b/estate/views/estate_menus.xml
@@ -4,4 +4,4 @@
-
\ No newline at end of file
+
diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml
index def2d0ea112..9c08278e011 100644
--- a/estate/views/estate_property_views.xml
+++ b/estate/views/estate_property_views.xml
@@ -9,4 +9,72 @@
+
+
+ estate.property.view.list
+ estate.property
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ estate.property.view.form
+ estate.property
+
+
+
+
+
+
+ estate.property.view.search
+ estate.property
+
+
+
+
+
+
+
+
+
+
From 17de6432be4d98f70b2cbeeff7a9f454193a61b8 Mon Sep 17 00:00:00 2001
From: pimai-odoo
Date: Thu, 30 Oct 2025 17:11:14 +0530
Subject: [PATCH 04/11] [IMP] estate: added property
type,tags,buyer,salesperson & offers in Real Estate
Added Property Type and Tag models, buyer and salesperson fields,
and the Property Offer model in the Real Estate module.
---
estate/__manifest__.py | 10 +++++--
estate/models/__init__.py | 3 ++
estate/models/estate_property.py | 30 +++++++++++---------
estate/models/estate_property_offer.py | 15 ++++++++++
estate/models/estate_property_tag.py | 8 ++++++
estate/models/estate_property_type.py | 8 ++++++
estate/security/ir.model.access.csv | 3 ++
estate/views/estate_menus.xml | 29 +++++++++++++++----
estate/views/estate_property_offer_views.xml | 29 +++++++++++++++++++
estate/views/estate_property_tag_views.xml | 7 +++++
estate/views/estate_property_type_views.xml | 7 +++++
estate/views/estate_property_views.xml | 29 +++++++++++++++++++
12 files changed, 158 insertions(+), 20 deletions(-)
create mode 100644 estate/models/estate_property_offer.py
create mode 100644 estate/models/estate_property_tag.py
create mode 100644 estate/models/estate_property_type.py
create mode 100644 estate/views/estate_property_offer_views.xml
create mode 100644 estate/views/estate_property_tag_views.xml
create mode 100644 estate/views/estate_property_type_views.xml
diff --git a/estate/__manifest__.py b/estate/__manifest__.py
index 0c63df47519..41b6cf0a752 100644
--- a/estate/__manifest__.py
+++ b/estate/__manifest__.py
@@ -4,6 +4,12 @@
'data': [
'security/ir.model.access.csv',
'views/estate_property_views.xml',
- 'views/estate_menus.xml'
- ]
+ 'views/estate_menus.xml',
+ 'views/estate_property_type_views.xml',
+ 'views/estate_property_tag_views.xml',
+ 'views/estate_property_offer_views.xml'
+ ],
+ 'application': True,
+ 'installable':True
}
+
diff --git a/estate/models/__init__.py b/estate/models/__init__.py
index 5e1963c9d2f..2f1821a39c1 100644
--- a/estate/models/__init__.py
+++ b/estate/models/__init__.py
@@ -1 +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 ea35038cf1a..412fa33fb9e 100644
--- a/estate/models/estate_property.py
+++ b/estate/models/estate_property.py
@@ -2,22 +2,22 @@
from dateutil.relativedelta import relativedelta
-class estateproperty(models.Model):
+class EstateProperty(models.Model):
_name = "estate.property"
_description = "Real Estate Property"
name = fields.Char(required=True)
- description = fields.Text("Description")
- postcode = fields.Char("Postcode")
- date_availability = fields.Date("Availability Date", default=fields.Date.today()+relativedelta(months=3))
+ description = fields.Text()
+ postcode = fields.Char()
+ date_availability = fields.Date("Availability Date", default=fields.Date.today() + relativedelta(months=3))
expected_price = fields.Float("Expected Price", required=True)
selling_price = fields.Float("Selling Price", readonly=True)
- bedrooms = fields.Integer("Bedrooms", default=2)
- living_area = fields.Integer("Living Area (sqm)")
- facades = fields.Integer("Facades")
- garage = fields.Boolean("Garage")
- garden = fields.Boolean("Garden")
- garden_area = fields.Integer("Garden Area (sqm)")
+ bedrooms = fields.Integer(default=2)
+ living_area = fields.Integer("Living Area(sqm)")
+ facades = fields.Integer()
+ garage = fields.Boolean()
+ garden = fields.Boolean()
+ garden_area = fields.Integer("Garden Area(sqm)")
garden_orientation = fields.Selection(
selection=[
("north", "North"),
@@ -40,6 +40,10 @@ class estateproperty(models.Model):
copy=False,
default="new",
)
- active = fields.Boolean(string="Active", default=True)
-
-
+ active = fields.Boolean(default=True)
+ 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")
+ 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..788fc12b6c8
--- /dev/null
+++ b/estate/models/estate_property_offer.py
@@ -0,0 +1,15 @@
+from odoo import fields, models
+
+
+class EstatePropertyOffer(models.Model):
+ _name = "estate.property.offer"
+ _description = "Real Estate Property Offer"
+
+ price = fields.Float()
+ status = fields.Selection(
+ [("accepted", "Accepted"), ("refused", "Refused")],
+ copy=False,
+ )
+ partner_id = fields.Many2one("res.partner", string="Partner", required=True)
+ property_id = fields.Many2one("estate.property", string="Property", required=True)
+
\ No newline at end of file
diff --git a/estate/models/estate_property_tag.py b/estate/models/estate_property_tag.py
new file mode 100644
index 00000000000..198c1037c41
--- /dev/null
+++ b/estate/models/estate_property_tag.py
@@ -0,0 +1,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/models/estate_property_type.py b/estate/models/estate_property_type.py
new file mode 100644
index 00000000000..ced40ef01cc
--- /dev/null
+++ b/estate/models/estate_property_type.py
@@ -0,0 +1,8 @@
+from odoo import fields, models
+
+
+class EstatePropertyType(models.Model):
+ _name = "estate.property.type"
+ _description = "Real Estate Property Type"
+
+ name = fields.Char(required=True)
diff --git a/estate/security/ir.model.access.csv b/estate/security/ir.model.access.csv
index 98f4671fb0d..58f5c8d8fe6 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,estate.model_estate_property,base.group_user,1,1,1,1
+estate.access_estate_property_type,access_estate_property_type,estate.model_estate_property_type,base.group_user,1,1,1,1
+access_estate_property_tag,estate.property.tag.access,model_estate_property_tag,base.group_user,1,1,1,1
+access_estate_property_offer,estate.property.offer.access,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 47be4a68c82..61b729e178c 100644
--- a/estate/views/estate_menus.xml
+++ b/estate/views/estate_menus.xml
@@ -1,7 +1,26 @@
-
+
+
+
+
+
+
+
+
+
+
+
diff --git a/estate/views/estate_property_offer_views.xml b/estate/views/estate_property_offer_views.xml
new file mode 100644
index 00000000000..1fbb3be50a0
--- /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
+
+
+
+
+
diff --git a/estate/views/estate_property_tag_views.xml b/estate/views/estate_property_tag_views.xml
new file mode 100644
index 00000000000..70ccfa7ea83
--- /dev/null
+++ b/estate/views/estate_property_tag_views.xml
@@ -0,0 +1,7 @@
+
+
+ Property Tags
+ estate.property.tag
+ list,form
+
+
diff --git a/estate/views/estate_property_type_views.xml b/estate/views/estate_property_type_views.xml
new file mode 100644
index 00000000000..382925bd424
--- /dev/null
+++ b/estate/views/estate_property_type_views.xml
@@ -0,0 +1,7 @@
+
+
+ Property Types
+ estate.property.type
+ list,form
+
+
diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml
index 9c08278e011..f83c441c093 100644
--- a/estate/views/estate_property_views.xml
+++ b/estate/views/estate_property_views.xml
@@ -22,6 +22,8 @@
+
+
@@ -37,6 +39,8 @@
+
+
@@ -54,6 +58,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -68,6 +96,7 @@
+
Date: Fri, 31 Oct 2025 18:12:23 +0530
Subject: [PATCH 05/11] [IMP] estate: add computed fields,inverse funcs,
onchange & Sold/Cancel buttons
Added computed fields with inverse functions and onchange methods,
along with Sold and Cancel buttons to enhance the
Real Estate module functionality.
---
estate/__manifest__.py | 1 -
estate/models/estate_property.py | 48 ++++++++++++++++++--
estate/models/estate_property_offer.py | 21 ++++++++-
estate/models/estate_property_tag.py | 1 +
estate/models/estate_property_type.py | 1 +
estate/views/estate_property_offer_views.xml | 10 ++--
estate/views/estate_property_views.xml | 11 +++++
7 files changed, 83 insertions(+), 10 deletions(-)
diff --git a/estate/__manifest__.py b/estate/__manifest__.py
index 41b6cf0a752..8d29e3c8469 100644
--- a/estate/__manifest__.py
+++ b/estate/__manifest__.py
@@ -12,4 +12,3 @@
'application': True,
'installable':True
}
-
diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py
index 412fa33fb9e..91314582202 100644
--- a/estate/models/estate_property.py
+++ b/estate/models/estate_property.py
@@ -1,5 +1,7 @@
-from odoo import models, fields
+from odoo import models, fields, api
from dateutil.relativedelta import relativedelta
+from datetime import date
+from odoo.exceptions import UserError
class EstateProperty(models.Model):
@@ -35,15 +37,53 @@ class EstateProperty(models.Model):
("sold", "Sold"),
("cancelled", "Cancelled"),
],
- string="Status",
+ "Status",
required=True,
copy=False,
default="new",
)
active = fields.Boolean(default=True)
- property_type_id = fields.Many2one("estate.property.type", string="Property Type")
+ property_type_id = fields.Many2one("estate.property.type", "Property Type")
buyer_id = fields.Many2one("res.partner", string="Buyer", copy=False)
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")
+ offer_ids = fields.One2many("estate.property.offer", "property_id", "Offers")
+ total_area = fields.Integer("Total Area(sqm)", compute="_compute_total_area")
+ best_price = fields.Float("Best Offer", compute="_compute_best_price")
+
+ @api.depends("living_area", "garden_area")
+ def _compute_total_area(self):
+ for record in self:
+ record.total_area = record.living_area + record.garden_area
+
+ @api.depends("offer_ids.price")
+ def _compute_best_price(self):
+ for record in self:
+ if record.offer_ids:
+ record.best_price = max(record.offer_ids.mapped("price"))
+ else:
+ record.best_price = 0
+
+ @api.onchange('garden')
+ def _onchange_garden(self):
+ if self.garden:
+ self.garden_area = 10
+ self.garden_orientation = 'north'
+ else:
+ self.garden_area = 0
+ self.garden_orientation = False
+
+ def action_cancel(self):
+ for record in self:
+ if record.state == 'sold':
+ raise UserError("A sold property cannot be cancelled")
+ else:
+ self.state = 'cancelled'
+
+ def action_sold(self):
+ for record in self:
+ if record.state == 'cancelled':
+ raise UserError("A cancelled property cannot be set as sold")
+ else:
+ self.state = 'sold'
diff --git a/estate/models/estate_property_offer.py b/estate/models/estate_property_offer.py
index 788fc12b6c8..888b2ea13aa 100644
--- a/estate/models/estate_property_offer.py
+++ b/estate/models/estate_property_offer.py
@@ -1,4 +1,5 @@
-from odoo import fields, models
+from odoo import fields, models, api
+from dateutil.relativedelta import relativedelta
class EstatePropertyOffer(models.Model):
@@ -12,4 +13,20 @@ class EstatePropertyOffer(models.Model):
)
partner_id = fields.Many2one("res.partner", string="Partner", required=True)
property_id = fields.Many2one("estate.property", string="Property", required=True)
-
\ No newline at end of file
+ validity = fields.Integer(string="Validity(days)", default=7)
+ date_deadline = fields.Date(string="Deadline Date", compute="_compute_date_deadline", inverse="_inverse_date_deadline")
+
+ @api.depends('validity')
+ def _compute_date_deadline(self):
+ for record in self:
+ if record.create_date:
+ record.date_deadline = fields.Date.to_date(record.create_date) + relativedelta(days=record.validity)
+ else:
+ record.date_deadline = False
+
+ def _inverse_date_deadline(self):
+ for record in self:
+ if record.create_date:
+ record.validity = (record.date_deadline - fields.Date.to_date(record.create_date)).days
+ else:
+ record.validity = False
diff --git a/estate/models/estate_property_tag.py b/estate/models/estate_property_tag.py
index 198c1037c41..380acf06f24 100644
--- a/estate/models/estate_property_tag.py
+++ b/estate/models/estate_property_tag.py
@@ -6,3 +6,4 @@ class EstatePropertyTag(models.Model):
_description = "Real Estate Property Tag"
name = fields.Char(required=True)
+
diff --git a/estate/models/estate_property_type.py b/estate/models/estate_property_type.py
index ced40ef01cc..6e26abafb14 100644
--- a/estate/models/estate_property_type.py
+++ b/estate/models/estate_property_type.py
@@ -6,3 +6,4 @@ class EstatePropertyType(models.Model):
_description = "Real Estate Property Type"
name = fields.Char(required=True)
+
diff --git a/estate/views/estate_property_offer_views.xml b/estate/views/estate_property_offer_views.xml
index 1fbb3be50a0..c67efab427c 100644
--- a/estate/views/estate_property_offer_views.xml
+++ b/estate/views/estate_property_offer_views.xml
@@ -7,6 +7,8 @@
+
+
@@ -18,9 +20,11 @@
diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml
index f83c441c093..ad424b6a79d 100644
--- a/estate/views/estate_property_views.xml
+++ b/estate/views/estate_property_views.xml
@@ -33,6 +33,10 @@
estate.property
From 6f8f97b8f8f616d3e43690ff43aec5d2031d0ef6 Mon Sep 17 00:00:00 2001
From: pimai-odoo
Date: Mon, 3 Nov 2025 15:53:52 +0530
Subject: [PATCH 06/11] [IMP] estate: added Accept,Refuse buttons & related
functionality in Real Estate
Added Accept and Refuse buttons along with the supporting functionality
to improve workflow in the Real Estate module.
---
estate/__manifest__.py | 8 +++++---
estate/models/estate_property.py | 7 +++----
estate/models/estate_property_offer.py | 13 +++++++++++++
estate/models/estate_property_tag.py | 1 -
estate/models/estate_property_type.py | 1 -
estate/views/estate_property_offer_views.xml | 2 ++
estate/views/estate_property_views.xml | 16 ----------------
7 files changed, 23 insertions(+), 25 deletions(-)
diff --git a/estate/__manifest__.py b/estate/__manifest__.py
index 8d29e3c8469..06df611a27f 100644
--- a/estate/__manifest__.py
+++ b/estate/__manifest__.py
@@ -4,11 +4,13 @@
'data': [
'security/ir.model.access.csv',
'views/estate_property_views.xml',
- 'views/estate_menus.xml',
'views/estate_property_type_views.xml',
'views/estate_property_tag_views.xml',
- 'views/estate_property_offer_views.xml'
+ 'views/estate_property_offer_views.xml',
+ 'views/estate_menus.xml'
],
'application': True,
- 'installable':True
+ 'installable': True,
+ 'author': 'Odoo S.A.',
+ 'license': 'LGPL-3'
}
diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py
index 91314582202..87d900e72de 100644
--- a/estate/models/estate_property.py
+++ b/estate/models/estate_property.py
@@ -1,6 +1,5 @@
from odoo import models, fields, api
from dateutil.relativedelta import relativedelta
-from datetime import date
from odoo.exceptions import UserError
@@ -51,12 +50,12 @@ class EstateProperty(models.Model):
offer_ids = fields.One2many("estate.property.offer", "property_id", "Offers")
total_area = fields.Integer("Total Area(sqm)", compute="_compute_total_area")
best_price = fields.Float("Best Offer", compute="_compute_best_price")
-
+
@api.depends("living_area", "garden_area")
def _compute_total_area(self):
for record in self:
record.total_area = record.living_area + record.garden_area
-
+
@api.depends("offer_ids.price")
def _compute_best_price(self):
for record in self:
@@ -64,7 +63,7 @@ def _compute_best_price(self):
record.best_price = max(record.offer_ids.mapped("price"))
else:
record.best_price = 0
-
+
@api.onchange('garden')
def _onchange_garden(self):
if self.garden:
diff --git a/estate/models/estate_property_offer.py b/estate/models/estate_property_offer.py
index 888b2ea13aa..1af415736b1 100644
--- a/estate/models/estate_property_offer.py
+++ b/estate/models/estate_property_offer.py
@@ -30,3 +30,16 @@ def _inverse_date_deadline(self):
record.validity = (record.date_deadline - fields.Date.to_date(record.create_date)).days
else:
record.validity = False
+
+ def action_accept(self):
+ for record in self:
+ record.status = "accepted"
+ record.property_id.selling_price = record.price
+ record.property_id.buyer_id = record.partner_ids
+ record.property_id.state = "offer_accepted"
+ return True
+
+ def action_refuse(self):
+ for record in self:
+ record.status = 'refused'
+ return True
diff --git a/estate/models/estate_property_tag.py b/estate/models/estate_property_tag.py
index 380acf06f24..198c1037c41 100644
--- a/estate/models/estate_property_tag.py
+++ b/estate/models/estate_property_tag.py
@@ -6,4 +6,3 @@ class EstatePropertyTag(models.Model):
_description = "Real Estate Property Tag"
name = fields.Char(required=True)
-
diff --git a/estate/models/estate_property_type.py b/estate/models/estate_property_type.py
index 6e26abafb14..ced40ef01cc 100644
--- a/estate/models/estate_property_type.py
+++ b/estate/models/estate_property_type.py
@@ -6,4 +6,3 @@ class EstatePropertyType(models.Model):
_description = "Real Estate Property Type"
name = fields.Char(required=True)
-
diff --git a/estate/views/estate_property_offer_views.xml b/estate/views/estate_property_offer_views.xml
index c67efab427c..1a6420f98bd 100644
--- a/estate/views/estate_property_offer_views.xml
+++ b/estate/views/estate_property_offer_views.xml
@@ -9,6 +9,8 @@
+
+
diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml
index ad424b6a79d..58c72db14fa 100644
--- a/estate/views/estate_property_views.xml
+++ b/estate/views/estate_property_views.xml
@@ -68,22 +68,6 @@
-
-
-
-
-
-
-
-
From 50a55460bba932feb9bed8807d41389c05f31ccb Mon Sep 17 00:00:00 2001
From: pimai-odoo
Date: Tue, 4 Nov 2025 16:50:17 +0530
Subject: [PATCH 07/11] [IMP] estate: add SQL & Python constraints for price &
uniqueness in Real Estate
Added SQL constraints to ensure positive expected, selling,
and offer prices, and unique names for property tags and types.
Added a Python constraint to ensure the selling price
is not less than 90% of the expected price.
---
estate/models/estate_property.py | 26 +++++++++++++++++---
estate/models/estate_property_offer.py | 33 +++++++++++++++-----------
estate/models/estate_property_tag.py | 5 ++++
estate/models/estate_property_type.py | 5 ++++
4 files changed, 52 insertions(+), 17 deletions(-)
diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py
index 87d900e72de..f59feeec6a0 100644
--- a/estate/models/estate_property.py
+++ b/estate/models/estate_property.py
@@ -1,6 +1,9 @@
-from odoo import models, fields, api
from dateutil.relativedelta import relativedelta
+
+from odoo import models, fields, api
from odoo.exceptions import UserError
+from odoo.exceptions import ValidationError
+from odoo.tools.float_utils import float_compare, float_is_zero
class EstateProperty(models.Model):
@@ -43,10 +46,10 @@ class EstateProperty(models.Model):
)
active = fields.Boolean(default=True)
property_type_id = fields.Many2one("estate.property.type", "Property Type")
- buyer_id = fields.Many2one("res.partner", string="Buyer", copy=False)
+ buyer_id = fields.Many2one("res.partner", "Buyer", copy=False)
salesperson_id = fields.Many2one(
"res.users", string="Salesperson")
- tag_ids = fields.Many2many("estate.property.tag", string="Tags")
+ tag_ids = fields.Many2many("estate.property.tag", "Tags")
offer_ids = fields.One2many("estate.property.offer", "property_id", "Offers")
total_area = fields.Integer("Total Area(sqm)", compute="_compute_total_area")
best_price = fields.Float("Best Offer", compute="_compute_best_price")
@@ -86,3 +89,20 @@ def action_sold(self):
raise UserError("A cancelled property cannot be set as sold")
else:
self.state = 'sold'
+
+ _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.'
+ )
+
+ @api.constrains('selling_price', 'expected_price')
+ def _check_selling_price(self):
+ for record in self:
+ if not float_is_zero(record.selling_price, precision_digits=2):
+ if float_compare(record.selling_price, record.expected_price * 0.9, precision_digits=2) < 0:
+ raise ValidationError("The selling price cannot be lower than 90% of the expected price.")
diff --git a/estate/models/estate_property_offer.py b/estate/models/estate_property_offer.py
index 1af415736b1..a495e6135e2 100644
--- a/estate/models/estate_property_offer.py
+++ b/estate/models/estate_property_offer.py
@@ -1,6 +1,8 @@
-from odoo import fields, models, api
from dateutil.relativedelta import relativedelta
+from odoo import fields, models, api
+from odoo.exceptions import UserError
+
class EstatePropertyOffer(models.Model):
_name = "estate.property.offer"
@@ -19,27 +21,30 @@ class EstatePropertyOffer(models.Model):
@api.depends('validity')
def _compute_date_deadline(self):
for record in self:
- if record.create_date:
- record.date_deadline = fields.Date.to_date(record.create_date) + relativedelta(days=record.validity)
- else:
- record.date_deadline = False
+ creation_date = record.create_date or fields.Date.today()
+ record.date_deadline = relativedelta(days=record.validity) + creation_date
def _inverse_date_deadline(self):
for record in self:
- if record.create_date:
- record.validity = (record.date_deadline - fields.Date.to_date(record.create_date)).days
- else:
- record.validity = False
+ creation_date = record.create_date or fields.Date.today()
+ record.validity = (record.date_deadline - fields.Date.to_date(creation_date)).days
def action_accept(self):
for record in self:
- record.status = "accepted"
- record.property_id.selling_price = record.price
- record.property_id.buyer_id = record.partner_ids
- record.property_id.state = "offer_accepted"
- return True
+ if record.property_id.buyer_id:
+ raise UserError("Property already accepted")
+ else:
+ record.status = 'accepted'
+ record.property_id.selling_price = record.price
+ record.property_id.state = 'offer_accepted'
+ record.property_id.buyer_id= record.partner_id
def action_refuse(self):
for record in self:
record.status = 'refused'
return True
+
+ _check_offer_price = models.Constraint(
+ 'CHECK(price > 0)',
+ 'The price of an offer must be strictly positive.'
+ )
diff --git a/estate/models/estate_property_tag.py b/estate/models/estate_property_tag.py
index 198c1037c41..04b06249ebe 100644
--- a/estate/models/estate_property_tag.py
+++ b/estate/models/estate_property_tag.py
@@ -6,3 +6,8 @@ class EstatePropertyTag(models.Model):
_description = "Real Estate Property Tag"
name = fields.Char(required=True)
+
+ _check_tag_name_unique = models.Constraint(
+ 'UNIQUE(name)',
+ 'The name of the property tag must be unique.'
+ )
diff --git a/estate/models/estate_property_type.py b/estate/models/estate_property_type.py
index ced40ef01cc..9dbc82c2afe 100644
--- a/estate/models/estate_property_type.py
+++ b/estate/models/estate_property_type.py
@@ -6,3 +6,8 @@ class EstatePropertyType(models.Model):
_description = "Real Estate Property Type"
name = fields.Char(required=True)
+
+ _check_type_name_unique = models.Constraint(
+ 'UNIQUE(name)',
+ 'The name of the property type must be unique.'
+ )
From 7e6d80ce32d61450cce80b40d5d8f3706f14112e Mon Sep 17 00:00:00 2001
From: pimai-odoo
Date: Wed, 5 Nov 2025 18:37:08 +0530
Subject: [PATCH 08/11] [IMP] estate: added Inline Views, Widgets, List Order,
Attributes, & Options
Implemented Inline Views and Widgets to enhance the user experience in
Real Estate. Added List Order functionality for improved organization and
sorting of listings. Added Attributes and Options to increase customization
and flexibility for users.
---
estate/models/estate_property.py | 1 +
estate/models/estate_property_offer.py | 5 ++-
estate/models/estate_property_tag.py | 6 ++-
estate/models/estate_property_type.py | 7 +++-
estate/views/estate_property_offer_views.xml | 11 ++++--
estate/views/estate_property_tag_views.xml | 10 +++++
estate/views/estate_property_type_views.xml | 36 +++++++++++++++++
estate/views/estate_property_views.xml | 41 +++++++++++++-------
8 files changed, 94 insertions(+), 23 deletions(-)
diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py
index f59feeec6a0..75a9c079aaf 100644
--- a/estate/models/estate_property.py
+++ b/estate/models/estate_property.py
@@ -9,6 +9,7 @@
class EstateProperty(models.Model):
_name = "estate.property"
_description = "Real Estate Property"
+ _order = "id desc"
name = fields.Char(required=True)
description = fields.Text()
diff --git a/estate/models/estate_property_offer.py b/estate/models/estate_property_offer.py
index a495e6135e2..55596503b82 100644
--- a/estate/models/estate_property_offer.py
+++ b/estate/models/estate_property_offer.py
@@ -7,6 +7,7 @@
class EstatePropertyOffer(models.Model):
_name = "estate.property.offer"
_description = "Real Estate Property Offer"
+ _order = "price desc"
price = fields.Float()
status = fields.Selection(
@@ -22,7 +23,7 @@ class EstatePropertyOffer(models.Model):
def _compute_date_deadline(self):
for record in self:
creation_date = record.create_date or fields.Date.today()
- record.date_deadline = relativedelta(days=record.validity) + creation_date
+ record.date_deadline = relativedelta(days=record.validity) + creation_date
def _inverse_date_deadline(self):
for record in self:
@@ -37,7 +38,7 @@ def action_accept(self):
record.status = 'accepted'
record.property_id.selling_price = record.price
record.property_id.state = 'offer_accepted'
- record.property_id.buyer_id= record.partner_id
+ record.property_id.buyer_id = record.partner_id
def action_refuse(self):
for record in self:
diff --git a/estate/models/estate_property_tag.py b/estate/models/estate_property_tag.py
index 04b06249ebe..f51c0053720 100644
--- a/estate/models/estate_property_tag.py
+++ b/estate/models/estate_property_tag.py
@@ -4,10 +4,12 @@
class EstatePropertyTag(models.Model):
_name = "estate.property.tag"
_description = "Real Estate Property Tag"
+ _order = "name"
name = fields.Char(required=True)
+ color = fields.Integer()
_check_tag_name_unique = models.Constraint(
- 'UNIQUE(name)',
- 'The name of the property tag must be unique.'
+ 'UNIQUE(name)',
+ 'The name of the property tag must be unique.'
)
diff --git a/estate/models/estate_property_type.py b/estate/models/estate_property_type.py
index 9dbc82c2afe..0cf611e3dde 100644
--- a/estate/models/estate_property_type.py
+++ b/estate/models/estate_property_type.py
@@ -4,10 +4,13 @@
class EstatePropertyType(models.Model):
_name = "estate.property.type"
_description = "Real Estate Property Type"
+ _order = "sequence, name"
name = fields.Char(required=True)
+ property_ids = fields.One2many("estate.property", "property_type_id", string="Properties")
+ sequence = fields.Integer('Sequence', default=1)
_check_type_name_unique = models.Constraint(
- 'UNIQUE(name)',
- 'The name of the property type must be unique.'
+ 'UNIQUE(name)',
+ 'The name of the property type must be unique.'
)
diff --git a/estate/views/estate_property_offer_views.xml b/estate/views/estate_property_offer_views.xml
index 1a6420f98bd..e416344f885 100644
--- a/estate/views/estate_property_offer_views.xml
+++ b/estate/views/estate_property_offer_views.xml
@@ -3,14 +3,17 @@
estate.property.offer.list
estate.property.offer
-
+
-
+
-
-
+
+
diff --git a/estate/views/estate_property_tag_views.xml b/estate/views/estate_property_tag_views.xml
index 70ccfa7ea83..dc596271a64 100644
--- a/estate/views/estate_property_tag_views.xml
+++ b/estate/views/estate_property_tag_views.xml
@@ -4,4 +4,14 @@
estate.property.tag
list,form
+
+
+ estate.property.tag.list
+ estate.property.tag
+
+
+
+
+
+
diff --git a/estate/views/estate_property_type_views.xml b/estate/views/estate_property_type_views.xml
index 382925bd424..7b13513eda1 100644
--- a/estate/views/estate_property_type_views.xml
+++ b/estate/views/estate_property_type_views.xml
@@ -4,4 +4,40 @@
estate.property.type
list,form
+
+
+ estate.property.type.form
+ estate.property.type
+
+
+
+
+
+
+ estate.property.type.list
+ estate.property.type
+
+
+
+
+
+
+
diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml
index 58c72db14fa..34131fc6b58 100644
--- a/estate/views/estate_property_views.xml
+++ b/estate/views/estate_property_views.xml
@@ -8,22 +8,26 @@
Create a new property
+ {'search_default_available_properties': 1}
estate.property.view.list
estate.property
-
+
-
+
-
+
@@ -34,8 +38,12 @@
@@ -67,7 +77,8 @@
-
+
@@ -91,13 +102,17 @@
-
+
-
+
+
From 60323346d3b85700e589050c23d6789fc3b1aed1 Mon Sep 17 00:00:00 2001
From: pimai-odoo
Date: Thu, 6 Nov 2025 19:01:07 +0530
Subject: [PATCH 09/11] [IMP] estate: added stat button, related fields &
business logic updates
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Added a stat button to the Property Type model and linked offers through
related fields. Implemented offer count computation and display on button click.
Updated business logic to restrict property deletion unless state is
‘New’ or ‘Cancelled’, set property state to ‘Offer Received’ on offer creation,
and prevent creation of lower offers than existing ones.
---
estate/__manifest__.py | 2 +-
estate/models/estate_property.py | 26 ++++++++++-------
estate/models/estate_property_offer.py | 16 ++++++++++-
estate/models/estate_property_tag.py | 2 +-
estate/models/estate_property_type.py | 13 +++++++--
estate/views/estate_property_offer_views.xml | 12 ++++++--
estate/views/estate_property_type_views.xml | 30 +++++++++++++-------
7 files changed, 72 insertions(+), 29 deletions(-)
diff --git a/estate/__manifest__.py b/estate/__manifest__.py
index 06df611a27f..186164ed1b0 100644
--- a/estate/__manifest__.py
+++ b/estate/__manifest__.py
@@ -4,9 +4,9 @@
'data': [
'security/ir.model.access.csv',
'views/estate_property_views.xml',
+ 'views/estate_property_offer_views.xml',
'views/estate_property_type_views.xml',
'views/estate_property_tag_views.xml',
- 'views/estate_property_offer_views.xml',
'views/estate_menus.xml'
],
'application': True,
diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py
index 75a9c079aaf..cc8e6cc4f24 100644
--- a/estate/models/estate_property.py
+++ b/estate/models/estate_property.py
@@ -55,6 +55,16 @@ class EstateProperty(models.Model):
total_area = fields.Integer("Total Area(sqm)", compute="_compute_total_area")
best_price = fields.Float("Best Offer", compute="_compute_best_price")
+ _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.'
+ )
+
@api.depends("living_area", "garden_area")
def _compute_total_area(self):
for record in self:
@@ -91,19 +101,15 @@ def action_sold(self):
else:
self.state = 'sold'
- _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.'
- )
-
@api.constrains('selling_price', 'expected_price')
def _check_selling_price(self):
for record in self:
if not float_is_zero(record.selling_price, precision_digits=2):
if float_compare(record.selling_price, record.expected_price * 0.9, precision_digits=2) < 0:
raise ValidationError("The selling price cannot be lower than 90% of the expected price.")
+
+ @api.ondelete(at_uninstall=False)
+ def _unlink_if_new_or_cancelled(self):
+ for record in self:
+ if record.state not in ('new', 'cancelled'):
+ raise UserError("Only 'New' or 'Cancelled' property can be deleted.")
diff --git a/estate/models/estate_property_offer.py b/estate/models/estate_property_offer.py
index 55596503b82..3f51c7d51ef 100644
--- a/estate/models/estate_property_offer.py
+++ b/estate/models/estate_property_offer.py
@@ -18,12 +18,13 @@ class EstatePropertyOffer(models.Model):
property_id = fields.Many2one("estate.property", string="Property", required=True)
validity = fields.Integer(string="Validity(days)", default=7)
date_deadline = fields.Date(string="Deadline Date", compute="_compute_date_deadline", inverse="_inverse_date_deadline")
+ property_type_id = fields.Many2one(related="property_id.property_type_id", store=True)
@api.depends('validity')
def _compute_date_deadline(self):
for record in self:
creation_date = record.create_date or fields.Date.today()
- record.date_deadline = relativedelta(days=record.validity) + creation_date
+ record.date_deadline = relativedelta(days=record.validity) + creation_date
def _inverse_date_deadline(self):
for record in self:
@@ -45,6 +46,19 @@ def action_refuse(self):
record.status = 'refused'
return True
+ @api.model
+ def create(self, vals_list):
+ for record in vals_list:
+ property_id = record.get('property_id')
+ offer_price = record.get('price')
+ if property_id and offer_price is not None:
+ estate_property = self.env['estate.property'].browse(property_id)
+ estate_property.state = 'offer_received'
+ existing_offers = self.search([('property_id', '=', property_id), ('price', '>=', offer_price)])
+ if existing_offers:
+ raise UserError("You cannot create an offer with a lower amount than an existing offer for this property.")
+ return super().create(vals_list)
+
_check_offer_price = models.Constraint(
'CHECK(price > 0)',
'The price of an offer must be strictly positive.'
diff --git a/estate/models/estate_property_tag.py b/estate/models/estate_property_tag.py
index f51c0053720..bdb8c392593 100644
--- a/estate/models/estate_property_tag.py
+++ b/estate/models/estate_property_tag.py
@@ -10,6 +10,6 @@ class EstatePropertyTag(models.Model):
color = fields.Integer()
_check_tag_name_unique = models.Constraint(
- 'UNIQUE(name)',
+ 'UNIQUE(name)',
'The name of the property tag must be unique.'
)
diff --git a/estate/models/estate_property_type.py b/estate/models/estate_property_type.py
index 0cf611e3dde..fe058f1d73c 100644
--- a/estate/models/estate_property_type.py
+++ b/estate/models/estate_property_type.py
@@ -1,4 +1,4 @@
-from odoo import fields, models
+from odoo import fields, models, api
class EstatePropertyType(models.Model):
@@ -8,9 +8,16 @@ class EstatePropertyType(models.Model):
name = fields.Char(required=True)
property_ids = fields.One2many("estate.property", "property_type_id", string="Properties")
- sequence = fields.Integer('Sequence', default=1)
+ sequence = fields.Integer("Sequence", default=1)
+ offer_ids = fields.One2many("estate.property.offer", "property_type_id", string="Offers")
+ offer_count = fields.Integer(string="Offer Count", compute="_compute_offer_count")
+
+ @api.depends("offer_ids")
+ def _compute_offer_count(self):
+ for record in self:
+ record.offer_count = len(record.offer_ids)
_check_type_name_unique = models.Constraint(
- 'UNIQUE(name)',
+ 'UNIQUE(name)',
'The name of the property type must be unique.'
)
diff --git a/estate/views/estate_property_offer_views.xml b/estate/views/estate_property_offer_views.xml
index e416344f885..29c7105d318 100644
--- a/estate/views/estate_property_offer_views.xml
+++ b/estate/views/estate_property_offer_views.xml
@@ -1,5 +1,12 @@
-
+
+ Property Offers
+ estate.property.offer
+ list,form
+ [('property_type_id', '=', active_id)]
+
+
+
estate.property.offer.list
estate.property.offer
@@ -7,13 +14,14 @@
decoration-success="status == 'accepted'" editable="bottom">
-
+
+
diff --git a/estate/views/estate_property_type_views.xml b/estate/views/estate_property_type_views.xml
index 7b13513eda1..bfdbbbc2ab9 100644
--- a/estate/views/estate_property_type_views.xml
+++ b/estate/views/estate_property_type_views.xml
@@ -5,11 +5,30 @@
list,form
+
+ estate.property.type.list
+ estate.property.type
+
+
+
+
+
+
+
+
+
+
estate.property.type.form
estate.property.type
-
-
- estate.property.type.list
- estate.property.type
-
-
-
-
-
-
-
From 8d96247357cbbb10b6b1be205138b87d36c647cb Mon Sep 17 00:00:00 2001
From: pimai-odoo
Date: Fri, 7 Nov 2025 18:26:36 +0530
Subject: [PATCH 10/11] [IMP] estate: added user field, new module and start
invoice process
Added a new field in the user form to show linked properties.
Created a new module to connect Real Estate with accounting features.
Began the invoice creation process when a property is marked as sold.
---
estate/__manifest__.py | 1 +
estate/models/__init__.py | 1 +
estate/models/estate_property.py | 3 +--
estate/models/estate_property_offer.py | 8 ++++----
estate/models/res_users.py | 6 ++++++
estate/views/res_users_views.xml | 14 ++++++++++++++
estate_account/__init__.py | 1 +
estate_account/__manifest__.py | 8 ++++++++
estate_account/models/__init__.py | 1 +
estate_account/models/estate_property.py | 11 +++++++++++
10 files changed, 48 insertions(+), 6 deletions(-)
create mode 100644 estate/models/res_users.py
create mode 100644 estate/views/res_users_views.xml
create mode 100644 estate_account/__init__.py
create mode 100644 estate_account/__manifest__.py
create mode 100644 estate_account/models/__init__.py
create mode 100644 estate_account/models/estate_property.py
diff --git a/estate/__manifest__.py b/estate/__manifest__.py
index 186164ed1b0..3a859c91969 100644
--- a/estate/__manifest__.py
+++ b/estate/__manifest__.py
@@ -7,6 +7,7 @@
'views/estate_property_offer_views.xml',
'views/estate_property_type_views.xml',
'views/estate_property_tag_views.xml',
+ 'views/res_users_views.xml',
'views/estate_menus.xml'
],
'application': True,
diff --git a/estate/models/__init__.py b/estate/models/__init__.py
index 2f1821a39c1..9a2189b6382 100644
--- a/estate/models/__init__.py
+++ b/estate/models/__init__.py
@@ -2,3 +2,4 @@
from . import estate_property_type
from . import estate_property_tag
from . import estate_property_offer
+from . import res_users
diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py
index cc8e6cc4f24..d7d36fecb00 100644
--- a/estate/models/estate_property.py
+++ b/estate/models/estate_property.py
@@ -48,8 +48,7 @@ class EstateProperty(models.Model):
active = fields.Boolean(default=True)
property_type_id = fields.Many2one("estate.property.type", "Property Type")
buyer_id = fields.Many2one("res.partner", "Buyer", copy=False)
- salesperson_id = fields.Many2one(
- "res.users", string="Salesperson")
+ salesperson_id = fields.Many2one("res.users", string="Salesperson")
tag_ids = fields.Many2many("estate.property.tag", "Tags")
offer_ids = fields.One2many("estate.property.offer", "property_id", "Offers")
total_area = fields.Integer("Total Area(sqm)", compute="_compute_total_area")
diff --git a/estate/models/estate_property_offer.py b/estate/models/estate_property_offer.py
index 3f51c7d51ef..eeb30881672 100644
--- a/estate/models/estate_property_offer.py
+++ b/estate/models/estate_property_offer.py
@@ -47,8 +47,8 @@ def action_refuse(self):
return True
@api.model
- def create(self, vals_list):
- for record in vals_list:
+ def create(self, value):
+ for record in value:
property_id = record.get('property_id')
offer_price = record.get('price')
if property_id and offer_price is not None:
@@ -57,8 +57,8 @@ def create(self, vals_list):
existing_offers = self.search([('property_id', '=', property_id), ('price', '>=', offer_price)])
if existing_offers:
raise UserError("You cannot create an offer with a lower amount than an existing offer for this property.")
- return super().create(vals_list)
-
+ return super().create(value)
+
_check_offer_price = models.Constraint(
'CHECK(price > 0)',
'The price of an offer must be strictly positive.'
diff --git a/estate/models/res_users.py b/estate/models/res_users.py
new file mode 100644
index 00000000000..f67b9a216eb
--- /dev/null
+++ b/estate/models/res_users.py
@@ -0,0 +1,6 @@
+from odoo import fields, models
+
+class InheritedModel(models.Model):
+ _inherit = "res.users"
+
+ property_ids=fields.One2many("estate.property", "salesperson_id", domain=[("state", "!=", "sold")], string="Properties")
diff --git a/estate/views/res_users_views.xml b/estate/views/res_users_views.xml
new file mode 100644
index 00000000000..9c5f12f58b4
--- /dev/null
+++ b/estate/views/res_users_views.xml
@@ -0,0 +1,14 @@
+
+
+ res.users.view.form.inherit
+ res.users
+
+
+
+
+
+
+
+
+
+
diff --git a/estate_account/__init__.py b/estate_account/__init__.py
new file mode 100644
index 00000000000..0650744f6bc
--- /dev/null
+++ b/estate_account/__init__.py
@@ -0,0 +1 @@
+from . import models
diff --git a/estate_account/__manifest__.py b/estate_account/__manifest__.py
new file mode 100644
index 00000000000..8fa5715ff01
--- /dev/null
+++ b/estate_account/__manifest__.py
@@ -0,0 +1,8 @@
+{
+ 'name': 'Estate Account',
+ 'depends': ['estate', 'account'],
+ 'application': True,
+ 'installable': True,
+ 'author': 'Odoo S.A.',
+ 'license': 'LGPL-3'
+}
diff --git a/estate_account/models/__init__.py b/estate_account/models/__init__.py
new file mode 100644
index 00000000000..5e1963c9d2f
--- /dev/null
+++ b/estate_account/models/__init__.py
@@ -0,0 +1 @@
+from . import estate_property
diff --git a/estate_account/models/estate_property.py b/estate_account/models/estate_property.py
new file mode 100644
index 00000000000..49fba3def14
--- /dev/null
+++ b/estate_account/models/estate_property.py
@@ -0,0 +1,11 @@
+from odoo import fields, models
+
+
+class EstateProperty(models.Model):
+ _inherit="estate.property"
+
+ def action_sold(self):
+ self.env['account.move'].create({
+ 'partner_id': self.buyer_id.id,
+ 'move_type': 'out_invoice'})
+ return super().action_sold()
From adec7555072065ea3b40fe127d79f2d1d8fc2b0e Mon Sep 17 00:00:00 2001
From: pimai-odoo
Date: Tue, 11 Nov 2025 18:44:17 +0530
Subject: [PATCH 11/11] [IMP] estate: added invoice lines for sold properties
and Kanban view
Added two invoice lines during invoice creation for sold properties:
one for 6% of the selling price and another for administrative fees of 100.00.
Added a Kanban view to improve property visualization in the Real Estate module.
---
estate/views/estate_property_views.xml | 19 ++++++++++++++++++-
estate_account/models/estate_property.py | 21 +++++++++++++++++++--
2 files changed, 37 insertions(+), 3 deletions(-)
diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml
index 34131fc6b58..b6ad941139f 100644
--- a/estate/views/estate_property_views.xml
+++ b/estate/views/estate_property_views.xml
@@ -2,7 +2,7 @@
Properties
estate.property
- list,form
+ kanban,list,form
Create a new property
@@ -116,4 +116,21 @@
+
+
+ estate.property.view.kanban
+ estate.property
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/estate_account/models/estate_property.py b/estate_account/models/estate_property.py
index 49fba3def14..5460d8a8ad2 100644
--- a/estate_account/models/estate_property.py
+++ b/estate_account/models/estate_property.py
@@ -1,4 +1,4 @@
-from odoo import fields, models
+from odoo import models, Command
class EstateProperty(models.Model):
@@ -7,5 +7,22 @@ class EstateProperty(models.Model):
def action_sold(self):
self.env['account.move'].create({
'partner_id': self.buyer_id.id,
- 'move_type': 'out_invoice'})
+ 'move_type': 'out_invoice',
+ 'invoice_line_ids': [
+ Command.create(
+ {
+ "name": "6% of the selling price",
+ "quantity": 1,
+ "price_unit": self.selling_price * 0.06 ,
+ }
+ ),
+ Command.create(
+ {
+ "name": "An additional 100 from administrative fees",
+ "quantity": 1,
+ "price_unit": 100,
+ }
+ )
+ ],
+ })
return super().action_sold()