From e0113aefafabd25946a4283fff337c40aa45a467 Mon Sep 17 00:00:00 2001 From: "Abdelrahman Gad (abgad)" Date: Tue, 19 May 2026 16:43:37 +0200 Subject: [PATCH 01/11] [ADD] real estate: add basic module --- estate/__init__.py | 1 + estate/__manifest__.py | 7 +++++++ estate/models/__init__.py | 1 + estate/models/estate_property.py | 21 +++++++++++++++++++++ 4 files changed, 30 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..a9e24cb4495 --- /dev/null +++ b/estate/__manifest__.py @@ -0,0 +1,7 @@ +{ + 'name': 'Real Estate', + 'application': True, + 'depends': [ + 'base' + ] +} \ No newline at end of file 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..d48b1de7d5a --- /dev/null +++ b/estate/models/estate_property.py @@ -0,0 +1,21 @@ +from odoo import fields, models + +class EstateProperty(models.Model): + _name = "estate.property" + _description = "It's free real estate" + + name = fields.Char(required=True) + description = fields.Text() + postcode = fields.Char() + date_availability = fields.Date() + expected_price = fields.Float(required=True) + selling_price = fields.Float() + bedrooms = fields.Integer() + living_area = fields.Integer() + facades = fields.Integer() + garage = fields.Boolean() + garden = fields.Boolean() + garden_area = fields.Integer() + garden_orientation = fields.Selection( + string='Garden Orientation', + selection=[('north', 'North'), ('south', 'South'), ('east', 'East'), ('west', 'West')]) \ No newline at end of file From f4489966c1c6898dcd2aeedfb6cf37ce31c26bad Mon Sep 17 00:00:00 2001 From: "Abdelrahman Gad (abgad)" Date: Wed, 20 May 2026 10:23:58 +0200 Subject: [PATCH 02/11] [IMP] real estate: add access rights on the property model --- estate/__manifest__.py | 5 ++++- estate/security/ir.model.access.csv | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 estate/security/ir.model.access.csv diff --git a/estate/__manifest__.py b/estate/__manifest__.py index a9e24cb4495..a3e03168c4e 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -3,5 +3,8 @@ 'application': True, 'depends': [ 'base' - ] + ], + 'data': [ + 'security/ir.model.access.csv', + ], } \ No newline at end of file diff --git a/estate/security/ir.model.access.csv b/estate/security/ir.model.access.csv new file mode 100644 index 00000000000..ab63520e22b --- /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 \ No newline at end of file From e7cd73262e43f0cc1b7352ae9e8f9a5ee56e1c2e Mon Sep 17 00:00:00 2001 From: "Abdelrahman Gad (abgad)" Date: Wed, 20 May 2026 14:16:20 +0200 Subject: [PATCH 03/11] [IMP] real estate: add action and menus --- estate/__manifest__.py | 2 ++ estate/views/estate_menus.xml | 8 ++++++++ estate/views/estate_property_views.xml | 8 ++++++++ 3 files changed, 18 insertions(+) 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 a3e03168c4e..dcf846c9adc 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -6,5 +6,7 @@ ], 'data': [ 'security/ir.model.access.csv', + 'views/estate_property_views.xml', + 'views/estate_menus.xml' ], } \ No newline at end of file diff --git a/estate/views/estate_menus.xml b/estate/views/estate_menus.xml new file mode 100644 index 00000000000..c8f7c1a6956 --- /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..affda3b8abc --- /dev/null +++ b/estate/views/estate_property_views.xml @@ -0,0 +1,8 @@ + + + + Properties + estate.property + list,form + + \ No newline at end of file From 8ecdce3574a8ae9a8ce240471f2b5f2ee6d45cb3 Mon Sep 17 00:00:00 2001 From: "Abdelrahman Gad (abgad)" Date: Wed, 20 May 2026 14:18:31 +0200 Subject: [PATCH 04/11] [IMP] real estate: add attributes to some fields in estate.property --- estate/models/estate_property.py | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index d48b1de7d5a..71c494dabeb 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -7,10 +7,10 @@ class EstateProperty(models.Model): name = fields.Char(required=True) description = fields.Text() postcode = fields.Char() - date_availability = fields.Date() + date_availability = fields.Date(default=fields.Date.add(fields.Date.today(), months=3), copy=False) 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() @@ -18,4 +18,20 @@ class EstateProperty(models.Model): garden_area = fields.Integer() garden_orientation = fields.Selection( string='Garden Orientation', - selection=[('north', 'North'), ('south', 'South'), ('east', 'East'), ('west', 'West')]) \ No newline at end of file + selection=[('north', 'North'), ('south', 'South'), ('east', 'East'), ('west', 'West')]) + state = fields.Selection( + string='State', + selection=[ + ('new', 'New'), + ('offer_received', 'Offer Received'), + ('offer_accepted', 'Offer Accepted'), + ('sold', 'Sold'), + ('cancelled', 'Cancelled'), + ], + default='new', + required=True, + copy=False + ) + + # Reserved fields + active = fields.Boolean(default=True, string='Active') \ No newline at end of file From 9c11ac65c31fce413d91826d84711baff27183f7 Mon Sep 17 00:00:00 2001 From: "Abdelrahman Gad (abgad)" Date: Wed, 20 May 2026 16:07:07 +0200 Subject: [PATCH 05/11] [IMP] real estate: add basic views (list, search and form) --- estate/views/estate_property_views.xml | 80 ++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index affda3b8abc..466d453cf75 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -5,4 +5,84 @@ estate.property list,form + + + estate.property.list + estate.property + + + + + + + + + + + + + + + estate.property.form + estate.property + +
+ +
+

+ +

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ + + estate.property.search + estate.property + + + + + + + + + + + + + + + + + + \ No newline at end of file From 3409e686cb800132f7d5704a99f467fcff648b0c Mon Sep 17 00:00:00 2001 From: "Abdelrahman Gad (abgad)" Date: Thu, 21 May 2026 10:56:57 +0200 Subject: [PATCH 06/11] [IMP] real estate: add estate.property.type model and some relations to estate.property --- estate/__manifest__.py | 1 + estate/models/__init__.py | 3 ++- estate/models/estate_property.py | 5 ++++- estate/models/estate_property_type.py | 7 +++++++ estate/security/ir.model.access.csv | 3 ++- estate/views/estate_menus.xml | 3 +++ estate/views/estate_property_type_views.xml | 8 ++++++++ estate/views/estate_property_views.xml | 9 +++++++++ 8 files changed, 36 insertions(+), 3 deletions(-) create mode 100644 estate/models/estate_property_type.py create mode 100644 estate/views/estate_property_type_views.xml diff --git a/estate/__manifest__.py b/estate/__manifest__.py index dcf846c9adc..ea7a0110b97 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -6,6 +6,7 @@ ], 'data': [ 'security/ir.model.access.csv', + 'views/estate_property_type_views.xml', 'views/estate_property_views.xml', 'views/estate_menus.xml' ], diff --git a/estate/models/__init__.py b/estate/models/__init__.py index f4c8fd6db6d..97aee757823 100644 --- a/estate/models/__init__.py +++ b/estate/models/__init__.py @@ -1 +1,2 @@ -from . import estate_property \ No newline at end of file +from . import estate_property +from . import estate_property_type \ No newline at end of file diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 71c494dabeb..0fd78369b7a 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -32,6 +32,9 @@ class EstateProperty(models.Model): required=True, copy=False ) - + property_type_id = fields.Many2one("estate.property.type") + salesperson_id = fields.Many2one('res.users', string='Salesman', default=lambda self: self.env.user) + buyer_id = fields.Many2one('res.partner', copy=False) + # Reserved fields active = fields.Boolean(default=True, string='Active') \ No newline at end of file diff --git a/estate/models/estate_property_type.py b/estate/models/estate_property_type.py new file mode 100644 index 00000000000..b8ea436064c --- /dev/null +++ b/estate/models/estate_property_type.py @@ -0,0 +1,7 @@ +from odoo import fields, models + +class EstatePropertyType(models.Model): + _name = "estate.property.type" + _description = "Property's type" + + name = fields.Char(required=True) \ No newline at end of file diff --git a/estate/security/ir.model.access.csv b/estate/security/ir.model.access.csv index ab63520e22b..7d4c1fcdb86 100644 --- a/estate/security/ir.model.access.csv +++ b/estate/security/ir.model.access.csv @@ -1,2 +1,3 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink -estate.access_estate_property,access_estate_property,estate.model_estate_property,base.group_user,1,1,1,1 \ No newline at end of file +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 diff --git a/estate/views/estate_menus.xml b/estate/views/estate_menus.xml index c8f7c1a6956..dd172f8d307 100644 --- a/estate/views/estate_menus.xml +++ b/estate/views/estate_menus.xml @@ -4,5 +4,8 @@ + + + \ No newline at end of file diff --git a/estate/views/estate_property_type_views.xml b/estate/views/estate_property_type_views.xml new file mode 100644 index 00000000000..8d8012a78aa --- /dev/null +++ b/estate/views/estate_property_type_views.xml @@ -0,0 +1,8 @@ + + + + Property Types + estate.property.type + list,form + + \ No newline at end of file diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index 466d453cf75..7033244ccab 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -12,6 +12,7 @@ + @@ -36,6 +37,7 @@ + @@ -58,6 +60,12 @@ + + + + + + @@ -70,6 +78,7 @@ + From f049a8c232ae0cfa790fcf5b913199aaba79d260 Mon Sep 17 00:00:00 2001 From: "Abdelrahman Gad (abgad)" Date: Thu, 21 May 2026 11:43:04 +0200 Subject: [PATCH 07/11] [IMP] real estate: add estate.property.tag model and it's corresponding relations/menus/actions/views --- estate/__manifest__.py | 1 + estate/models/__init__.py | 3 ++- estate/models/estate_property.py | 3 ++- estate/models/estate_property_tag.py | 7 +++++++ estate/security/ir.model.access.csv | 1 + estate/views/estate_menus.xml | 1 + estate/views/estate_property_tag_views.xml | 8 ++++++++ estate/views/estate_property_views.xml | 5 +++++ 8 files changed, 27 insertions(+), 2 deletions(-) create mode 100644 estate/models/estate_property_tag.py create mode 100644 estate/views/estate_property_tag_views.xml diff --git a/estate/__manifest__.py b/estate/__manifest__.py index ea7a0110b97..6d988c7621c 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -7,6 +7,7 @@ 'data': [ 'security/ir.model.access.csv', 'views/estate_property_type_views.xml', + 'views/estate_property_tag_views.xml', 'views/estate_property_views.xml', 'views/estate_menus.xml' ], diff --git a/estate/models/__init__.py b/estate/models/__init__.py index 97aee757823..fdab6f99948 100644 --- a/estate/models/__init__.py +++ b/estate/models/__init__.py @@ -1,2 +1,3 @@ from . import estate_property -from . import estate_property_type \ No newline at end of file +from . import estate_property_type +from . import estate_property_tag \ No newline at end of file diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 0fd78369b7a..2cb384e4741 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -33,8 +33,9 @@ class EstateProperty(models.Model): copy=False ) property_type_id = fields.Many2one("estate.property.type") + property_tag_ids = fields.Many2many("estate.property.tag") salesperson_id = fields.Many2one('res.users', string='Salesman', default=lambda self: self.env.user) buyer_id = fields.Many2one('res.partner', copy=False) - + # Reserved fields active = fields.Boolean(default=True, string='Active') \ 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..961493a44b0 --- /dev/null +++ b/estate/models/estate_property_tag.py @@ -0,0 +1,7 @@ +from odoo import fields, models + +class EstatePropertyTag(models.Model): + _name = "estate.property.tag" + _description = "Property's tag" + + name = fields.Char(required=True) \ No newline at end of file diff --git a/estate/security/ir.model.access.csv b/estate/security/ir.model.access.csv index 7d4c1fcdb86..5558d0fbc60 100644 --- a/estate/security/ir.model.access.csv +++ b/estate/security/ir.model.access.csv @@ -1,3 +1,4 @@ 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 +estate.access_estate_property_tag,access_estate_property_tag,estate.model_estate_property_tag,base.group_user,1,1,1,1 diff --git a/estate/views/estate_menus.xml b/estate/views/estate_menus.xml index dd172f8d307..bde9f8e66eb 100644 --- a/estate/views/estate_menus.xml +++ b/estate/views/estate_menus.xml @@ -6,6 +6,7 @@ + \ No newline at end of file diff --git a/estate/views/estate_property_tag_views.xml b/estate/views/estate_property_tag_views.xml new file mode 100644 index 00000000000..4c1901b199a --- /dev/null +++ b/estate/views/estate_property_tag_views.xml @@ -0,0 +1,8 @@ + + + + Property Tag + estate.property.tag + list,form + + \ No newline at end of file diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index 7033244ccab..a2051318e5e 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -13,6 +13,7 @@ + @@ -34,6 +35,10 @@ +
+ +
+ From ac8d9e6f575357bd2a65cf86a5bef74cd930d1d7 Mon Sep 17 00:00:00 2001 From: "Abdelrahman Gad (abgad)" Date: Thu, 21 May 2026 13:53:46 +0200 Subject: [PATCH 08/11] [IMP] real estate: add estate.property.offer model and it's corresponding relations/views --- estate/__manifest__.py | 3 +- estate/models/__init__.py | 3 +- estate/models/estate_property.py | 1 + estate/models/estate_property_offer.py | 16 ++++++++++ estate/security/ir.model.access.csv | 1 + estate/views/estate_property_offer_views.xml | 32 ++++++++++++++++++++ estate/views/estate_property_views.xml | 3 ++ 7 files changed, 57 insertions(+), 2 deletions(-) create mode 100644 estate/models/estate_property_offer.py create mode 100644 estate/views/estate_property_offer_views.xml diff --git a/estate/__manifest__.py b/estate/__manifest__.py index 6d988c7621c..3f5edb333be 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -6,8 +6,9 @@ ], 'data': [ 'security/ir.model.access.csv', - 'views/estate_property_type_views.xml', + 'views/estate_property_offer_views.xml', 'views/estate_property_tag_views.xml', + 'views/estate_property_type_views.xml', 'views/estate_property_views.xml', 'views/estate_menus.xml' ], diff --git a/estate/models/__init__.py b/estate/models/__init__.py index fdab6f99948..09b2099fe84 100644 --- a/estate/models/__init__.py +++ b/estate/models/__init__.py @@ -1,3 +1,4 @@ from . import estate_property from . import estate_property_type -from . import estate_property_tag \ No newline at end of file +from . import estate_property_tag +from . import estate_property_offer \ No newline at end of file diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 2cb384e4741..5e56b0bc61b 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -36,6 +36,7 @@ class EstateProperty(models.Model): property_tag_ids = fields.Many2many("estate.property.tag") salesperson_id = fields.Many2one('res.users', string='Salesman', default=lambda self: self.env.user) buyer_id = fields.Many2one('res.partner', copy=False) + offer_ids = fields.One2many("estate.property.offer", "property_id", string="Offers") # Reserved fields active = fields.Boolean(default=True, string='Active') \ No newline at end of file diff --git a/estate/models/estate_property_offer.py b/estate/models/estate_property_offer.py new file mode 100644 index 00000000000..3ffe3bb46e0 --- /dev/null +++ b/estate/models/estate_property_offer.py @@ -0,0 +1,16 @@ +from odoo import fields, models + +class EstatePropertyOffer(models.Model): + _name = "estate.property.offer" + _description = "Real Estate Property Offer" + + price = fields.Float() + status = fields.Selection( + selection=[ + ('accepted', 'Accepted'), + ('refused', 'Refused') + ], + copy=False + ) + partner_id = fields.Many2one("res.partner", required=True) + property_id = fields.Many2one("estate.property", required=True) \ No newline at end of file diff --git a/estate/security/ir.model.access.csv b/estate/security/ir.model.access.csv index 5558d0fbc60..0db13e578ec 100644 --- a/estate/security/ir.model.access.csv +++ b/estate/security/ir.model.access.csv @@ -2,3 +2,4 @@ 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 estate.access_estate_property_tag,access_estate_property_tag,estate.model_estate_property_tag,base.group_user,1,1,1,1 +estate.access_estate_property_offer,access_estate_property_offer,estate.model_estate_property_offer,base.group_user,1,1,1,1 \ No newline at end of file diff --git a/estate/views/estate_property_offer_views.xml b/estate/views/estate_property_offer_views.xml new file mode 100644 index 00000000000..07503154dfd --- /dev/null +++ b/estate/views/estate_property_offer_views.xml @@ -0,0 +1,32 @@ + + + + + estate.property.offer.list + estate.property.offer + + + + + + + + + + + estate.property.offer.form + estate.property.offer + +
+ + + + + + + +
+
+
+ +
\ No newline at end of file diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index a2051318e5e..a6aceae4e79 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -65,6 +65,9 @@
+ + + From 0ccf101018cc49bb6385cdb3ee6a7dc1e5ce1a31 Mon Sep 17 00:00:00 2001 From: "Abdelrahman Gad (abgad)" Date: Thu, 21 May 2026 16:37:19 +0200 Subject: [PATCH 09/11] [IMP] real estate: add computed fields and onchange methods to estate.property and estate.property.offer --- estate/models/estate_property.py | 53 ++++++++++++++++++-- estate/models/estate_property_offer.py | 23 ++++++++- estate/views/estate_property_offer_views.xml | 4 ++ estate/views/estate_property_views.xml | 2 + 4 files changed, 77 insertions(+), 5 deletions(-) diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 5e56b0bc61b..b782cab25d8 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -1,4 +1,4 @@ -from odoo import fields, models +from odoo import api, fields, models class EstateProperty(models.Model): _name = "estate.property" @@ -32,11 +32,58 @@ class EstateProperty(models.Model): required=True, copy=False ) + + # ------------------------------------------------------------------------- + # RELATIONS + # ------------------------------------------------------------------------- property_type_id = fields.Many2one("estate.property.type") property_tag_ids = fields.Many2many("estate.property.tag") salesperson_id = fields.Many2one('res.users', string='Salesman', default=lambda self: self.env.user) buyer_id = fields.Many2one('res.partner', copy=False) offer_ids = fields.One2many("estate.property.offer", "property_id", string="Offers") - # Reserved fields - active = fields.Boolean(default=True, string='Active') \ No newline at end of file + # ------------------------------------------------------------------------- + # COMPUTED FIELDS + # ------------------------------------------------------------------------- + total_area = fields.Integer( + string="Total Area (sqm)", + compute="_compute_total_area" + ) + + best_price = fields.Float( + string="Best Offer", + compute="_compute_best_price" + ) + + # ------------------------------------------------------------------------- + # RESERVED FIELDS + # ------------------------------------------------------------------------- + active = fields.Boolean(default=True, string='Active') + + # ------------------------------------------------------------------------- + # COMPUTE METHODS + # ------------------------------------------------------------------------- + @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.0 + + # ------------------------------------------------------------------------- + # ONCHANGE METHODS + # ------------------------------------------------------------------------- + @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 \ No newline at end of file diff --git a/estate/models/estate_property_offer.py b/estate/models/estate_property_offer.py index 3ffe3bb46e0..77fdba606d9 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 datetime import timedelta +from odoo import api, fields, models class EstatePropertyOffer(models.Model): _name = "estate.property.offer" @@ -12,5 +13,23 @@ class EstatePropertyOffer(models.Model): ], copy=False ) + validity = fields.Integer(default=7) + date_deadline = fields.Date( + compute="_compute_date_deadline", + inverse="_inverse_date_deadline" + ) partner_id = fields.Many2one("res.partner", required=True) - property_id = fields.Many2one("estate.property", required=True) \ No newline at end of file + property_id = fields.Many2one("estate.property", required=True) + + + @api.depends("create_date", "validity") + def _compute_date_deadline(self): + for offer in self: + base_date = offer.create_date.date() if offer.create_date else fields.Date.today() + offer.date_deadline = base_date + timedelta(days=offer.validity) + + def _inverse_date_deadline(self): + for offer in self: + base_date = offer.create_date.date() if offer.create_date else fields.Date.today() + if offer.date_deadline: + offer.validity = (offer.date_deadline - base_date).days \ No newline at end of file diff --git a/estate/views/estate_property_offer_views.xml b/estate/views/estate_property_offer_views.xml index 07503154dfd..3ff89d8566e 100644 --- a/estate/views/estate_property_offer_views.xml +++ b/estate/views/estate_property_offer_views.xml @@ -8,6 +8,8 @@ + + @@ -22,6 +24,8 @@ + + diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index a6aceae4e79..8d5350dd938 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -48,6 +48,7 @@ +
@@ -63,6 +64,7 @@ + From d3391707fbb4c59b7eb9588f01bf286bca5f3038 Mon Sep 17 00:00:00 2001 From: "Abdelrahman Gad (abgad)" Date: Fri, 22 May 2026 09:48:27 +0200 Subject: [PATCH 10/11] [IMP] real estate: allow selling/canceling a property and accepting/refusing an offer --- estate/models/estate_property.py | 26 ++++++++++++++++---- estate/models/estate_property_offer.py | 21 +++++++++++++++- estate/views/estate_property_offer_views.xml | 2 ++ estate/views/estate_property_views.xml | 5 ++++ 4 files changed, 48 insertions(+), 6 deletions(-) diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index b782cab25d8..8d7d9abee72 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -1,4 +1,5 @@ from odoo import api, fields, models +from odoo.exceptions import UserError class EstateProperty(models.Model): _name = "estate.property" @@ -26,16 +27,14 @@ class EstateProperty(models.Model): ('offer_received', 'Offer Received'), ('offer_accepted', 'Offer Accepted'), ('sold', 'Sold'), - ('cancelled', 'Cancelled'), + ('canceled', 'Cancelled'), ], default='new', required=True, copy=False ) - # ------------------------------------------------------------------------- - # RELATIONS - # ------------------------------------------------------------------------- + # Relations property_type_id = fields.Many2one("estate.property.type") property_tag_ids = fields.Many2many("estate.property.tag") salesperson_id = fields.Many2one('res.users', string='Salesman', default=lambda self: self.env.user) @@ -86,4 +85,21 @@ def _onchange_garden(self): self.garden_orientation = "north" else: self.garden_area = 0 - self.garden_orientation = False \ No newline at end of file + self.garden_orientation = False + + # ------------------------------------------------------------------------- + # ACTIONS + # ------------------------------------------------------------------------- + def action_sold(self): + for record in self: + if record.state == 'canceled': + raise UserError("You cannot sell a canceled property.") + record.state = 'sold' + return True + + def action_cancel(self): + for record in self: + if record.state == 'sold': + raise UserError("You cannot cancel a sold property.") + record.state = 'canceled' + return True \ No newline at end of file diff --git a/estate/models/estate_property_offer.py b/estate/models/estate_property_offer.py index 77fdba606d9..2ec97d07504 100644 --- a/estate/models/estate_property_offer.py +++ b/estate/models/estate_property_offer.py @@ -1,5 +1,6 @@ from datetime import timedelta from odoo import api, fields, models +from odoo.exceptions import UserError class EstatePropertyOffer(models.Model): _name = "estate.property.offer" @@ -32,4 +33,22 @@ def _inverse_date_deadline(self): for offer in self: base_date = offer.create_date.date() if offer.create_date else fields.Date.today() if offer.date_deadline: - offer.validity = (offer.date_deadline - base_date).days \ No newline at end of file + offer.validity = (offer.date_deadline - base_date).days + + # ------------------------------------------------------------------------- + # ACTIONS + # ------------------------------------------------------------------------- + def action_accept(self): + for record in self: + if "accepted" in record.property_id.offer_ids.mapped("status"): + raise UserError("An offer has already been accepted for this property.") + + record.status = 'accepted' + record.property_id.selling_price = record.price + record.property_id.buyer_id = record.partner_id + return True + + def action_refuse(self): + for record in self: + record.status = 'refused' + return True \ No newline at end of file diff --git a/estate/views/estate_property_offer_views.xml b/estate/views/estate_property_offer_views.xml index 3ff89d8566e..6bbbb175125 100644 --- a/estate/views/estate_property_offer_views.xml +++ b/estate/views/estate_property_offer_views.xml @@ -10,6 +10,8 @@ +