From 967fc30d13cfe7687cac5f4f858e28c1e80d3fb2 Mon Sep 17 00:00:00 2001 From: KarimH537 Date: Tue, 18 Nov 2025 14:10:33 +0100 Subject: [PATCH 01/15] [ADD] estate module --- estate/__init__.py | 0 estate/__manifest__.py | 25 +++++++++++++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 estate/__init__.py create mode 100644 estate/__manifest__.py diff --git a/estate/__init__.py b/estate/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/estate/__manifest__.py b/estate/__manifest__.py new file mode 100644 index 00000000000..9d570a86a8b --- /dev/null +++ b/estate/__manifest__.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +{ + 'name': "Real Estate", + + 'summary': """ + Cool Real Estate App + """, + + 'description': """ + Cool Real Estate App + """, + + 'author': "kmhma", + 'website': "https://www.odoo.com/", + 'category': 'Tutorials', + 'version': '0.1', + 'application': True, + 'installable': True, + 'depends': ['base'], + + 'data': [], + 'assets': { + }, + 'license': 'AGPL-3' +} From ba2124fbe14aaf7a2aaa72d4877f57e378ff8557 Mon Sep 17 00:00:00 2001 From: KarimH537 Date: Tue, 18 Nov 2025 15:06:36 +0100 Subject: [PATCH 02/15] [ADD] estate: add estate_property fields --- estate/__init__.py | 1 + estate/models/__init__.py | 1 + estate/models/estate_property.py | 23 +++++++++++++++++++++++ 3 files changed, 25 insertions(+) create mode 100644 estate/models/__init__.py create mode 100644 estate/models/estate_property.py diff --git a/estate/__init__.py b/estate/__init__.py index e69de29bb2d..9a7e03eded3 100644 --- a/estate/__init__.py +++ b/estate/__init__.py @@ -0,0 +1 @@ +from . import models \ No newline at end of file diff --git a/estate/models/__init__.py b/estate/models/__init__.py new file mode 100644 index 00000000000..5e1963c9d2f --- /dev/null +++ b/estate/models/__init__.py @@ -0,0 +1 @@ +from . import estate_property diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py new file mode 100644 index 00000000000..ae41f7e1c72 --- /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 model" + + name = fields.Char(required=True) + postcode= fields.Char() + date_availability = fields.Date() + expected_price = fields.Float(required=True) + selling_price = fields.Float() + bedrooms = fields.Integer() + living_area = fields.Integer() + facades = fields.Integer() + garage = fields.Boolean() + garden = fields.Boolean() + garden_area = fields.Integer() + garden_orientation = fields.Selection( + string='Type', + selection=[('north', 'North'), ('south', 'South'), ('east', 'East'), ('west', 'West')] + ) + + From f920c0a9c9c4e993b257777c4907971da0b8db4d Mon Sep 17 00:00:00 2001 From: KarimH537 Date: Tue, 18 Nov 2025 15:51:52 +0100 Subject: [PATCH 03/15] [ADD] estate: add access rights --- estate/__init__.py | 2 +- estate/__manifest__.py | 7 +++---- estate/models/estate_property.py | 16 ++++++++++------ estate/security/ir.model.access.csv | 2 ++ 4 files changed, 16 insertions(+), 11 deletions(-) create mode 100644 estate/security/ir.model.access.csv 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 9d570a86a8b..1c248e12315 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -1,15 +1,12 @@ -# -*- coding: utf-8 -*- { 'name': "Real Estate", 'summary': """ Cool Real Estate App """, - 'description': """ Cool Real Estate App """, - 'author': "kmhma", 'website': "https://www.odoo.com/", 'category': 'Tutorials', @@ -18,7 +15,9 @@ 'installable': True, 'depends': ['base'], - 'data': [], + 'data': [ + "security/ir.model.access.csv" + ], 'assets': { }, 'license': 'AGPL-3' diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index ae41f7e1c72..ec365b17ee5 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -1,11 +1,12 @@ from odoo import models, fields + class estateProperty(models.Model): _name = "estate.property" _description = "Real Estate model" name = fields.Char(required=True) - postcode= fields.Char() + postcode = fields.Char() date_availability = fields.Date() expected_price = fields.Float(required=True) selling_price = fields.Float() @@ -16,8 +17,11 @@ class estateProperty(models.Model): garden = fields.Boolean() garden_area = fields.Integer() garden_orientation = fields.Selection( - string='Type', - selection=[('north', 'North'), ('south', 'South'), ('east', 'East'), ('west', 'West')] - ) - - + string="Type", + selection=[ + ("north", "North"), + ("south", "South"), + ("east", "East"), + ("west", "West"), + ], + ) diff --git a/estate/security/ir.model.access.csv b/estate/security/ir.model.access.csv new file mode 100644 index 00000000000..c6d251ec585 --- /dev/null +++ b/estate/security/ir.model.access.csv @@ -0,0 +1,2 @@ +id,name,model_id/id,group_id/id,perm_read,perm_write,perm_create,perm_unlink +access_estate_property,access.estate.property,model_estate_property,base.group_user,1,1,1,1 \ No newline at end of file From e722790a0e0c076c89476200abff12f5e22770cf Mon Sep 17 00:00:00 2001 From: KarimH537 Date: Tue, 18 Nov 2025 17:14:24 +0100 Subject: [PATCH 04/15] [ADD] estate: add action, menu, and active and state fields --- estate/__manifest__.py | 8 +++++--- estate/models/estate_property.py | 26 ++++++++++++++++++++++---- estate/views/estate_menus.xml | 8 ++++++++ estate/views/estate_property_views.xml | 8 ++++++++ 4 files changed, 43 insertions(+), 7 deletions(-) create mode 100644 estate/views/estate_menus.xml create mode 100644 estate/views/estate_property_views.xml diff --git a/estate/__manifest__.py b/estate/__manifest__.py index 1c248e12315..a20084bae9e 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -2,10 +2,10 @@ 'name': "Real Estate", 'summary': """ - Cool Real Estate App + Cool Real Estate App """, 'description': """ - Cool Real Estate App + Cool Real Estate App """, 'author': "kmhma", 'website': "https://www.odoo.com/", @@ -16,7 +16,9 @@ 'depends': ['base'], 'data': [ - "security/ir.model.access.csv" + "security/ir.model.access.csv", + "views/estate_property_views.xml", + "views/estate_menus.xml" ], 'assets': { }, diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index ec365b17ee5..5ada62a0c3d 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -1,4 +1,5 @@ from odoo import models, fields +from dateutil.relativedelta import relativedelta class estateProperty(models.Model): @@ -7,17 +8,20 @@ class estateProperty(models.Model): name = fields.Char(required=True) postcode = fields.Char() - date_availability = fields.Date() + date_availability = fields.Date( + copy=False, default=fields.Datetime.today() + relativedelta(months=3) + ) expected_price = fields.Float(required=True) - selling_price = fields.Float() - bedrooms = fields.Integer() + selling_price = fields.Float(readonly=True, copy=False) + bedrooms = fields.Integer(default=2) living_area = fields.Integer() facades = fields.Integer() garage = fields.Boolean() garden = fields.Boolean() + active = fields.Boolean(default=True) garden_area = fields.Integer() garden_orientation = fields.Selection( - string="Type", + string="Orientation", selection=[ ("north", "North"), ("south", "South"), @@ -25,3 +29,17 @@ class estateProperty(models.Model): ("west", "West"), ], ) + state = fields.Selection( + string="State", + selection=[ + ("new", "New"), + ("offer", "Offer"), + ("received", "Received"), + ("offer accepted", "Offer Accepted"), + ("sold", "Sold"), + ("cancelled", "Cancelled"), + ], + required=True, + copy=False, + default="new", + ) diff --git a/estate/views/estate_menus.xml b/estate/views/estate_menus.xml new file mode 100644 index 00000000000..36f8848f8e4 --- /dev/null +++ b/estate/views/estate_menus.xml @@ -0,0 +1,8 @@ + + + + + \ 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..228c66c8f46 --- /dev/null +++ b/estate/views/estate_property_views.xml @@ -0,0 +1,8 @@ + + + + Estate Property + estate.property + list,form + + \ No newline at end of file From 815f4b0b36836b8de5363a159d5423bd9024db57 Mon Sep 17 00:00:00 2001 From: KarimH537 Date: Wed, 19 Nov 2025 11:12:58 +0100 Subject: [PATCH 05/15] [ADD] estate: add attrs to list view, restructured form view, add search view --- estate/__manifest__.py | 5 ++- estate/models/estate_property.py | 12 +++---- estate/views/estate_form_view.xml | 44 ++++++++++++++++++++++++++ estate/views/estate_list_view.xml | 18 +++++++++++ estate/views/estate_property_views.xml | 2 +- estate/views/estate_search_view.xml | 21 ++++++++++++ 6 files changed, 94 insertions(+), 8 deletions(-) create mode 100644 estate/views/estate_form_view.xml create mode 100644 estate/views/estate_list_view.xml create mode 100644 estate/views/estate_search_view.xml diff --git a/estate/__manifest__.py b/estate/__manifest__.py index a20084bae9e..26d106e1454 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -18,7 +18,10 @@ 'data': [ "security/ir.model.access.csv", "views/estate_property_views.xml", - "views/estate_menus.xml" + "views/estate_menus.xml", + "views/estate_list_view.xml", + "views/estate_form_view.xml", + "views/estate_search_view.xml", ], 'assets': { }, diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 5ada62a0c3d..a98c905d9b1 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -7,21 +7,22 @@ class estateProperty(models.Model): _description = "Real Estate model" name = fields.Char(required=True) + description = fields.Char() postcode = fields.Char() - date_availability = fields.Date( + available_from = fields.Date( copy=False, default=fields.Datetime.today() + relativedelta(months=3) ) expected_price = fields.Float(required=True) selling_price = fields.Float(readonly=True, copy=False) bedrooms = fields.Integer(default=2) - living_area = fields.Integer() + living_area = fields.Integer(string="Living Area (sqm)") facades = fields.Integer() garage = fields.Boolean() garden = fields.Boolean() active = fields.Boolean(default=True) - garden_area = fields.Integer() + garden_area = fields.Integer(string="Garden Area (sqm)") garden_orientation = fields.Selection( - string="Orientation", + string="Garden Orientation", selection=[ ("north", "North"), ("south", "South"), @@ -33,8 +34,7 @@ class estateProperty(models.Model): string="State", selection=[ ("new", "New"), - ("offer", "Offer"), - ("received", "Received"), + ("offer received", "Offer Received"), ("offer accepted", "Offer Accepted"), ("sold", "Sold"), ("cancelled", "Cancelled"), diff --git a/estate/views/estate_form_view.xml b/estate/views/estate_form_view.xml new file mode 100644 index 00000000000..dd592fef525 --- /dev/null +++ b/estate/views/estate_form_view.xml @@ -0,0 +1,44 @@ + + + + estate.property.form + estate.property + +
+ + +

+ +

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+
\ No newline at end of file diff --git a/estate/views/estate_list_view.xml b/estate/views/estate_list_view.xml new file mode 100644 index 00000000000..968d0fdcb17 --- /dev/null +++ b/estate/views/estate_list_view.xml @@ -0,0 +1,18 @@ + + + + estate.property.list + estate.property + + + + + + + + + + + + + \ No newline at end of file diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index 228c66c8f46..b4ecd555e96 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -1,7 +1,7 @@ - Estate Property + estate.property.view estate.property list,form diff --git a/estate/views/estate_search_view.xml b/estate/views/estate_search_view.xml new file mode 100644 index 00000000000..7e7db70fea5 --- /dev/null +++ b/estate/views/estate_search_view.xml @@ -0,0 +1,21 @@ + + + + estate.property.search + estate.property + + + + + + + + + + + + + + + + \ No newline at end of file From 783aac29c176ec47398cdf028cd68a37a4bcb03a Mon Sep 17 00:00:00 2001 From: KarimH537 Date: Wed, 19 Nov 2025 11:26:25 +0100 Subject: [PATCH 06/15] [FIX] estate: add missing EOLs, trailing commas --- estate/__manifest__.py | 11 ++++------- estate/models/estate_property.py | 5 ++--- estate/security/ir.model.access.csv | 2 +- estate/views/estate_form_view.xml | 2 +- estate/views/estate_list_view.xml | 2 +- estate/views/estate_menus.xml | 2 +- estate/views/estate_property_views.xml | 2 +- estate/views/estate_search_view.xml | 2 +- 8 files changed, 12 insertions(+), 16 deletions(-) diff --git a/estate/__manifest__.py b/estate/__manifest__.py index 26d106e1454..026c1bdf78d 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -1,11 +1,10 @@ { 'name': "Real Estate", - 'summary': """ - Cool Real Estate App - """, + 'summary': "Cool Real Estate App", 'description': """ - Cool Real Estate App +Cool Real Estate App +Wow This is a description omgg """, 'author': "kmhma", 'website': "https://www.odoo.com/", @@ -23,7 +22,5 @@ "views/estate_form_view.xml", "views/estate_search_view.xml", ], - 'assets': { - }, - 'license': 'AGPL-3' + 'license': 'LGPL-3', } diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index a98c905d9b1..495afc5fd92 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -1,8 +1,7 @@ from odoo import models, fields -from dateutil.relativedelta import relativedelta -class estateProperty(models.Model): +class EstateProperty(models.Model): _name = "estate.property" _description = "Real Estate model" @@ -10,7 +9,7 @@ class estateProperty(models.Model): description = fields.Char() postcode = fields.Char() available_from = fields.Date( - copy=False, default=fields.Datetime.today() + relativedelta(months=3) + copy=False, default=fields.Date.add(fields.Date.today(), months=3) ) expected_price = fields.Float(required=True) selling_price = fields.Float(readonly=True, copy=False) diff --git a/estate/security/ir.model.access.csv b/estate/security/ir.model.access.csv index c6d251ec585..f0cdfdcac54 100644 --- a/estate/security/ir.model.access.csv +++ b/estate/security/ir.model.access.csv @@ -1,2 +1,2 @@ id,name,model_id/id,group_id/id,perm_read,perm_write,perm_create,perm_unlink -access_estate_property,access.estate.property,model_estate_property,base.group_user,1,1,1,1 \ No newline at end of file +access_estate_property,access.estate.property,model_estate_property,base.group_user,1,1,1,1 diff --git a/estate/views/estate_form_view.xml b/estate/views/estate_form_view.xml index dd592fef525..3c30a636425 100644 --- a/estate/views/estate_form_view.xml +++ b/estate/views/estate_form_view.xml @@ -41,4 +41,4 @@ - \ No newline at end of file + diff --git a/estate/views/estate_list_view.xml b/estate/views/estate_list_view.xml index 968d0fdcb17..b07492282a6 100644 --- a/estate/views/estate_list_view.xml +++ b/estate/views/estate_list_view.xml @@ -15,4 +15,4 @@ - \ No newline at end of file + diff --git a/estate/views/estate_menus.xml b/estate/views/estate_menus.xml index 36f8848f8e4..598ea192cb7 100644 --- a/estate/views/estate_menus.xml +++ b/estate/views/estate_menus.xml @@ -5,4 +5,4 @@ - \ No newline at end of file + diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index b4ecd555e96..36f3cae2342 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -5,4 +5,4 @@ estate.property list,form - \ No newline at end of file + diff --git a/estate/views/estate_search_view.xml b/estate/views/estate_search_view.xml index 7e7db70fea5..329f7767647 100644 --- a/estate/views/estate_search_view.xml +++ b/estate/views/estate_search_view.xml @@ -18,4 +18,4 @@ - \ No newline at end of file + From a64bdd2699a61211b4f842b286906b94ddddd326 Mon Sep 17 00:00:00 2001 From: KarimH537 Date: Wed, 19 Nov 2025 15:52:20 +0100 Subject: [PATCH 07/15] [ADD] estate: add relations with salesman, buyer, tags, and offers --- estate/__manifest__.py | 10 ++++--- estate/models/__init__.py | 7 ++++- estate/models/estate_property.py | 9 ++++++ estate/models/estate_property_offer.py | 17 +++++++++++ estate/models/estate_property_tag.py | 8 +++++ estate/models/estate_property_type.py | 8 +++++ estate/security/ir.model.access.csv | 3 ++ ...te_form_view.xml => estate_form_views.xml} | 30 ++++++++++++++++--- ...te_list_view.xml => estate_list_views.xml} | 1 + ...state_menus.xml => estate_menus_views.xml} | 5 ++++ estate/views/estate_property_views.xml | 2 +- ...earch_view.xml => estate_search_views.xml} | 1 + estate/views/property_tag_views.xml | 8 +++++ estate/views/property_type_views.xml | 8 +++++ 14 files changed, 107 insertions(+), 10 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 rename estate/views/{estate_form_view.xml => estate_form_views.xml} (62%) rename estate/views/{estate_list_view.xml => estate_list_views.xml} (92%) rename estate/views/{estate_menus.xml => estate_menus_views.xml} (54%) rename estate/views/{estate_search_view.xml => estate_search_views.xml} (94%) create mode 100644 estate/views/property_tag_views.xml create mode 100644 estate/views/property_type_views.xml diff --git a/estate/__manifest__.py b/estate/__manifest__.py index 026c1bdf78d..f8d4fdfb4c2 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -17,10 +17,12 @@ 'data': [ "security/ir.model.access.csv", "views/estate_property_views.xml", - "views/estate_menus.xml", - "views/estate_list_view.xml", - "views/estate_form_view.xml", - "views/estate_search_view.xml", + "views/property_type_views.xml", + "views/property_tag_views.xml", + "views/estate_menus_views.xml", + "views/estate_list_views.xml", + "views/estate_form_views.xml", + "views/estate_search_views.xml", ], 'license': 'LGPL-3', } diff --git a/estate/models/__init__.py b/estate/models/__init__.py index 5e1963c9d2f..a0a8b8c501c 100644 --- a/estate/models/__init__.py +++ b/estate/models/__init__.py @@ -1 +1,6 @@ -from . import estate_property +from . import ( + estate_property, + estate_property_type, + estate_property_tag, + estate_property_offer, +) diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 495afc5fd92..75e5c9a3368 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -42,3 +42,12 @@ class EstateProperty(models.Model): copy=False, default="new", ) + property_type_id = fields.Many2one( + comodel_name="estate.property.type", string="Property Type" + ) + buyer = fields.Many2one(comodel_name="res.partner", copy=False) + salesman = fields.Many2one( + comodel_name="res.users", default=lambda self: self.env.uid + ) + tag_ids = fields.Many2many(comodel_name="estate.property.tag", string="Tags") + offer_ids = fields.One2many(comodel_name="estate.property.offer", inverse_name="property_id") diff --git a/estate/models/estate_property_offer.py b/estate/models/estate_property_offer.py new file mode 100644 index 00000000000..6e17fc2d869 --- /dev/null +++ b/estate/models/estate_property_offer.py @@ -0,0 +1,17 @@ +from odoo import models, fields + + +class EstatePropertyOffer(models.Model): + _name = "estate.property.offer" + _description = "property offer" + + price = fields.Float() + status = fields.Selection( + copy=False, + selection=[ + ("accepted", "Accepted"), + ("refused", "Refused"), + ], + ) + partner_id = fields.Many2one("res.partner", required=True, string="Partner") + property_id = fields.Many2one("estate.property", required=True, string="Property") diff --git a/estate/models/estate_property_tag.py b/estate/models/estate_property_tag.py new file mode 100644 index 00000000000..6f4569f5954 --- /dev/null +++ b/estate/models/estate_property_tag.py @@ -0,0 +1,8 @@ +from odoo import models, fields + + +class EstatePropertyTag(models.Model): + _name = "estate.property.tag" + _description = "property tags" + + 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..2e07a19bf05 --- /dev/null +++ b/estate/models/estate_property_type.py @@ -0,0 +1,8 @@ +from odoo import models, fields + + +class EstatePropertyType(models.Model): + _name = "estate.property.type" + _description = "property types" + + name = fields.Char(required=True) diff --git a/estate/security/ir.model.access.csv b/estate/security/ir.model.access.csv index f0cdfdcac54..e20ec4dd90b 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 access_estate_property,access.estate.property,model_estate_property,base.group_user,1,1,1,1 +access_estate_property_type,access.estate.property.type,model_estate_property_type,base.group_user,1,1,1,1 +access_estate_property_tag,access.estate.property.tag,model_estate_property_tag,base.group_user,1,1,1,1 +access_estate_property_offer,access.estate.property.offer,model_estate_property_offer,base.group_user,1,1,1,1 diff --git a/estate/views/estate_form_view.xml b/estate/views/estate_form_views.xml similarity index 62% rename from estate/views/estate_form_view.xml rename to estate/views/estate_form_views.xml index 3c30a636425..ee6906ba221 100644 --- a/estate/views/estate_form_view.xml +++ b/estate/views/estate_form_views.xml @@ -7,22 +7,26 @@
-

+

-

+
+ + - + - + + + @@ -36,7 +40,25 @@ + + + + + + + + + + + + + + + + + +
diff --git a/estate/views/estate_list_view.xml b/estate/views/estate_list_views.xml similarity index 92% rename from estate/views/estate_list_view.xml rename to estate/views/estate_list_views.xml index b07492282a6..34a9d57d9b7 100644 --- a/estate/views/estate_list_view.xml +++ b/estate/views/estate_list_views.xml @@ -7,6 +7,7 @@ + diff --git a/estate/views/estate_menus.xml b/estate/views/estate_menus_views.xml similarity index 54% rename from estate/views/estate_menus.xml rename to estate/views/estate_menus_views.xml index 598ea192cb7..1721ca49a12 100644 --- a/estate/views/estate_menus.xml +++ b/estate/views/estate_menus_views.xml @@ -4,5 +4,10 @@ From 3ffa891d53f9021427a303a8de73e5441eb17db9 Mon Sep 17 00:00:00 2001 From: KarimH537 Date: Thu, 20 Nov 2025 11:48:34 +0100 Subject: [PATCH 10/15] [ADD] estate: add sold/cancel buttons on property; add accept/refuse buttons on offers --- estate/models/estate_property.py | 21 ++++++++++++-- estate/models/estate_property_offer.py | 39 ++++++++++++++++++++++---- estate/views/estate_property_views.xml | 12 ++++++-- 3 files changed, 60 insertions(+), 12 deletions(-) diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 3b708a33823..6754ab3da84 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -1,4 +1,4 @@ -from odoo import models, fields, api +from odoo import models, fields, api, exceptions class EstateProperty(models.Model): @@ -64,8 +64,7 @@ def _compute_area(self): @api.depends("offer_ids.price") def _compute_best_price(self): for record in self: - if record.best_price: - record.best_price = max(record.offer_ids.mapped('price')) + record.best_price = max(record.offer_ids.mapped('price'), default=0.0) @api.onchange("garden") def _onchange_garden(self): @@ -75,3 +74,19 @@ def _onchange_garden(self): else: self.garden_area = 0 self.garden_orientation = '' + + def sell_property(self): + for record in self: + if record.state == "cancelled": + raise exceptions.UserError("You cannot sell a cancelled property") + else: + record.state = "sold" + return True + + def cancel_property(self): + for record in self: + if record.state == "sold": + raise exceptions.UserError("You cannot cancel a sold property") + else: + record.state = "cancelled" + return True diff --git a/estate/models/estate_property_offer.py b/estate/models/estate_property_offer.py index a5980989918..05867c7021a 100644 --- a/estate/models/estate_property_offer.py +++ b/estate/models/estate_property_offer.py @@ -1,4 +1,4 @@ -from odoo import models, fields, api +from odoo import models, fields, api, exceptions class EstatePropertyOffer(models.Model): @@ -16,14 +16,41 @@ class EstatePropertyOffer(models.Model): partner_id = fields.Many2one("res.partner", required=True, string="Partner") property_id = fields.Many2one("estate.property", required=True, string="Property") validity = fields.Integer(default=7) - deadline_date = fields.Date(compute="_compute_deadline_date", inverse="_inverse_deadline_date") + deadline_date = fields.Date( + compute="_compute_deadline_date", inverse="_inverse_deadline_date" + ) @api.depends("create_date", "validity") def _compute_deadline_date(self): for record in self: - record.deadline_date = fields.Date.add((record.create_date or fields.Date.today()), days=record.validity) - + create_date = record.create_date or fields.Date.today() + record.deadline_date = fields.Date.add(create_date, days=record.validity) + def _inverse_deadline_date(self): for record in self: - record.validity = (record.deadline_date - fields.Date.today()).days - \ No newline at end of file + create_date = record.create_date or fields.Date.today() + record.validity = (record.deadline_date - (create_date.date())).days + + def accept_offer(self): + for offer in self: + if offer.property_id.state == "sold": + raise exceptions.UserError( + f"Property '{offer.property_id.name}' is already sold" + ) + offer.property_id.write( + { + "buyer": offer.partner_id, + "selling_price": offer.price, + } + ) + offer.status = "accepted" + return True + + def refuse_offer(self): + for offer in self: + if offer.property_id.state == "sold" and offer.status == "accepted": + raise exceptions.UserError( + f"Property '{offer.property_id.name}' is already sold to '{offer.partner_id.name}'" + ) + offer.status = "refused" + return True diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index 4f04ee1d670..721c65388e3 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -28,6 +28,10 @@ estate.property
+
+

@@ -38,16 +42,16 @@ + - + - @@ -71,9 +75,11 @@ - + + + + + + + + + + + + + + + + + + From a68f98c4fec420c91f88242c89a0c3095bd83035 Mon Sep 17 00:00:00 2001 From: KarimH537 Date: Mon, 24 Nov 2025 15:13:03 +0100 Subject: [PATCH 13/15] [ADD] estate: add properties to user, and properites in user profile --- estate/__manifest__.py | 1 + estate/models/__init__.py | 3 ++- estate/models/estate_property.py | 6 +++++ estate/models/estate_property_offer.py | 9 +++++++ estate/models/res_users.py | 7 +++++ estate/security/ir.model.access.csv | 1 + estate/views/estate_menus_views.xml | 1 + estate/views/res_users_views.xml | 36 ++++++++++++++++++++++++++ 8 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 estate/models/res_users.py create mode 100644 estate/views/res_users_views.xml diff --git a/estate/__manifest__.py b/estate/__manifest__.py index f855c7f9635..e4efa83f70b 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -20,6 +20,7 @@ "views/property_tag_views.xml", "views/property_offer_views.xml", "views/estate_property_views.xml", + "views/res_users_views.xml", "views/estate_menus_views.xml", ], 'license': 'LGPL-3', diff --git a/estate/models/__init__.py b/estate/models/__init__.py index 09b2099fe84..9a2189b6382 100644 --- a/estate/models/__init__.py +++ b/estate/models/__init__.py @@ -1,4 +1,5 @@ from . import estate_property from . import estate_property_type from . import estate_property_tag -from . import estate_property_offer \ No newline at end of file +from . import estate_property_offer +from . import res_users diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index c1e8b09c528..de46d1861cc 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -112,3 +112,9 @@ def _check_selling_price(self): for property in self: if float_compare(property.selling_price, 0.9 * property.expected_price, 2) == -1: raise exceptions.ValidationError("The selling price must be at least 90% of the expected price.") + + @api.ondelete(at_uninstall=False) + def _unlink_property_with_offer(self): + for property in self: + if property.state not in ["new", "cancelled"]: + raise exceptions.UserError("You cannot delete a property with existing offers!") diff --git a/estate/models/estate_property_offer.py b/estate/models/estate_property_offer.py index 4e213a81b9d..a7ea9d5e10c 100644 --- a/estate/models/estate_property_offer.py +++ b/estate/models/estate_property_offer.py @@ -62,3 +62,12 @@ def button_refuse_offer(self): ) offer.status = "refused" return True + + @api.model_create_multi + def create(self, vals_list): + for val in vals_list: + property_id = self.env['estate.property'].browse(val['property_id']) + if property_id.offer_ids and max(offer.price for offer in property_id.offer_ids) > val['price']: + raise exceptions.UserError("An offer with a higher price exists") + property_id.state = "offer_received" + return super().create(vals_list) diff --git a/estate/models/res_users.py b/estate/models/res_users.py new file mode 100644 index 00000000000..61e580dc963 --- /dev/null +++ b/estate/models/res_users.py @@ -0,0 +1,7 @@ +from odoo import models, fields + + +class Users(models.Model): + _inherit="res.users" + property_ids = fields.One2many(comodel_name="estate.property", inverse_name="salesman_id", + string="Properties") diff --git a/estate/security/ir.model.access.csv b/estate/security/ir.model.access.csv index e20ec4dd90b..be6afd74af1 100644 --- a/estate/security/ir.model.access.csv +++ b/estate/security/ir.model.access.csv @@ -3,3 +3,4 @@ access_estate_property,access.estate.property,model_estate_property,base.group_u access_estate_property_type,access.estate.property.type,model_estate_property_type,base.group_user,1,1,1,1 access_estate_property_tag,access.estate.property.tag,model_estate_property_tag,base.group_user,1,1,1,1 access_estate_property_offer,access.estate.property.offer,model_estate_property_offer,base.group_user,1,1,1,1 +access_res_users,access.res.users,model_res_users,base.group_user,1,1,1,1 diff --git a/estate/views/estate_menus_views.xml b/estate/views/estate_menus_views.xml index 1721ca49a12..412a4657d0b 100644 --- a/estate/views/estate_menus_views.xml +++ b/estate/views/estate_menus_views.xml @@ -8,6 +8,7 @@ + diff --git a/estate/views/res_users_views.xml b/estate/views/res_users_views.xml new file mode 100644 index 00000000000..ea14ea580f4 --- /dev/null +++ b/estate/views/res_users_views.xml @@ -0,0 +1,36 @@ + + + + + res.users.view.form.inherit + res.users + + + + + + + + + + + + + + + + + + + + + + + + + Users & Companies + res.users + list,form + + + From bc04c280cd2c3105919a32a2609b01686ee8c04f Mon Sep 17 00:00:00 2001 From: KarimH537 Date: Mon, 24 Nov 2025 17:12:16 +0100 Subject: [PATCH 14/15] [ADD] estate: create estate_account module --- estate/models/estate_property.py | 29 ++++++++++++--------- estate/models/estate_property_tag.py | 5 +--- estate_account/__init__.py | 1 + estate_account/__manifest__.py | 20 +++++++++++++++ estate_account/models/__init__.py | 1 + estate_account/models/estate_property.py | 32 ++++++++++++++++++++++++ 6 files changed, 72 insertions(+), 16 deletions(-) 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/models/estate_property.py b/estate/models/estate_property.py index de46d1861cc..5b4d1145d77 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -59,13 +59,11 @@ class EstateProperty(models.Model): best_price = fields.Float(compute="_compute_best_price") _postive_expected_price = models.Constraint( - "CHECK (expected_price > 0)", - "The expected price must be strictly positive" + "CHECK (expected_price > 0)", "The expected price must be strictly positive" ) _postive_selling_price = models.Constraint( - "CHECK (selling_price > 0)", - "The selling price must be strictly positive" + "CHECK (selling_price > 0)", "The selling price must be strictly positive" ) @api.depends("living_area", "garden_area") @@ -76,7 +74,7 @@ def _compute_area(self): @api.depends("offer_ids.price") def _compute_best_price(self): for property in self: - property.best_price = max(property.offer_ids.mapped('price'), default=0.0) + property.best_price = max(property.offer_ids.mapped("price"), default=0.0) @api.onchange("offer_ids") def _onchange_offer_receieved(self): @@ -87,10 +85,10 @@ def _onchange_offer_receieved(self): def _onchange_garden(self): if self.garden: self.garden_area = 10 - self.garden_orientation = 'north' + self.garden_orientation = "north" else: self.garden_area = 0 - self.garden_orientation = '' + self.garden_orientation = "" def sell_property(self): for property in self: @@ -106,15 +104,22 @@ def cancel_property(self): else: property.state = "cancelled" return True - + @api.constrains("selling_price") def _check_selling_price(self): for property in self: - if float_compare(property.selling_price, 0.9 * property.expected_price, 2) == -1: - raise exceptions.ValidationError("The selling price must be at least 90% of the expected price.") - + if ( + float_compare(property.selling_price, 0.9 * property.expected_price, 2) + == -1 + ): + raise exceptions.ValidationError( + "The selling price must be at least 90% of the expected price." + ) + @api.ondelete(at_uninstall=False) def _unlink_property_with_offer(self): for property in self: if property.state not in ["new", "cancelled"]: - raise exceptions.UserError("You cannot delete a property with existing offers!") + raise exceptions.UserError( + "You cannot delete a property with existing offers!" + ) diff --git a/estate/models/estate_property_tag.py b/estate/models/estate_property_tag.py index 502ace7b04b..5d62ae9ceb8 100644 --- a/estate/models/estate_property_tag.py +++ b/estate/models/estate_property_tag.py @@ -9,7 +9,4 @@ class EstatePropertyTag(models.Model): name = fields.Char(required=True) color = fields.Integer() - _unique_tag = models.Constraint( - "unique(name)", - "This tag already exists" - ) + _unique_tag = models.Constraint("unique(name)", "This tag already exists") 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..8806a6ccce8 --- /dev/null +++ b/estate_account/__manifest__.py @@ -0,0 +1,20 @@ +{ + 'name': "Real Estate Account", + + 'summary': "Cool Real Estate For Invoicing ", + 'description': """ +Cool Real Estate App +Wow This is a description omgg + """, + 'author': "kmhma", + 'website': "https://www.odoo.com/", + 'category': 'Tutorials', + 'version': '0.1', + 'application': True, + 'installable': True, + 'depends': ['base', 'estate', 'account'], + + 'data': [ + ], + 'license': 'LGPL-3', +} diff --git a/estate_account/models/__init__.py b/estate_account/models/__init__.py new file mode 100644 index 00000000000..f4c8fd6db6d --- /dev/null +++ b/estate_account/models/__init__.py @@ -0,0 +1 @@ +from . import estate_property \ No newline at end of file diff --git a/estate_account/models/estate_property.py b/estate_account/models/estate_property.py new file mode 100644 index 00000000000..1c6611b61e2 --- /dev/null +++ b/estate_account/models/estate_property.py @@ -0,0 +1,32 @@ +from odoo import models, Command + + +class EstateProperty(models.Model): + _inherit = "estate.property" + + def sell_property(self): + partner_id = self.buyer_id.id + move_type = "out_invoice" + self.env["account.move"].create( + { + "partner_id": partner_id, + "move_type": move_type, + "invoice_line_ids": [ + Command.create( + { + "name": "Selling Price", + "quantity": 1, + "price_unit": 0.06 * self.selling_price, + } + ), + Command.create( + { + "name": "Administrative Fees", + "quantity": 1, + "price_unit": 100, + } + ), + ], + } + ) + return super().sell_property() From 0959dc371ca796c95e990013126a0ad8f54d240f Mon Sep 17 00:00:00 2001 From: KarimH537 Date: Tue, 25 Nov 2025 09:36:35 +0100 Subject: [PATCH 15/15] [ADD] estate: add properties kanban view --- estate/models/estate_property_offer.py | 2 +- estate/models/res_users.py | 2 +- estate/views/estate_property_views.xml | 27 +++++++++++++++++++++++++- estate_account/models/__init__.py | 2 +- 4 files changed, 29 insertions(+), 4 deletions(-) diff --git a/estate/models/estate_property_offer.py b/estate/models/estate_property_offer.py index a7ea9d5e10c..b25f99ba7fa 100644 --- a/estate/models/estate_property_offer.py +++ b/estate/models/estate_property_offer.py @@ -62,7 +62,7 @@ def button_refuse_offer(self): ) offer.status = "refused" return True - + @api.model_create_multi def create(self, vals_list): for val in vals_list: diff --git a/estate/models/res_users.py b/estate/models/res_users.py index 61e580dc963..9912be284b6 100644 --- a/estate/models/res_users.py +++ b/estate/models/res_users.py @@ -2,6 +2,6 @@ class Users(models.Model): - _inherit="res.users" + _inherit = "res.users" property_ids = fields.One2many(comodel_name="estate.property", inverse_name="salesman_id", string="Properties") diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index b919a2e8455..79ce14c26a9 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -121,10 +121,35 @@ + + estate.property.kanban + estate.property + + + + + +

+
+ Expected Price: +
+
+ Best Offer: +
+
+ Selling Price: +
+ +
+
+
+
+
+ Estate Property estate.property - list,form + list,form,kanban {'search_default_available': True} diff --git a/estate_account/models/__init__.py b/estate_account/models/__init__.py index f4c8fd6db6d..5e1963c9d2f 100644 --- a/estate_account/models/__init__.py +++ b/estate_account/models/__init__.py @@ -1 +1 @@ -from . import estate_property \ No newline at end of file +from . import estate_property