From 326711a137cd59f607f1a8aaf3f99d900bfada35 Mon Sep 17 00:00:00 2001 From: qucol-odoo Date: Mon, 20 Oct 2025 13:35:30 +0200 Subject: [PATCH 01/13] [ADD] estate: Chapter 2 --- estate/__init__.py | 0 estate/__manifest__.py | 9 +++++++++ 2 files changed, 9 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..718d5206054 --- /dev/null +++ b/estate/__manifest__.py @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- + +{ + 'name': 'Estate', + 'depends': [ + 'base' + ], + 'application': True +} \ No newline at end of file From 04fcfac75b3c1d51689f4f0871fdaeff3a71e2d1 Mon Sep 17 00:00:00 2001 From: qucol-odoo Date: Mon, 20 Oct 2025 14:44:04 +0200 Subject: [PATCH 02/13] [IMP] estate: Chapter 3 --- estate/__init__.py | 1 + estate/__manifest__.py | 4 +++- estate/models/__init__.py | 1 + estate/models/estate_properties.py | 27 +++++++++++++++++++++++++++ 4 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 estate/models/__init__.py create mode 100644 estate/models/estate_properties.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/__manifest__.py b/estate/__manifest__.py index 718d5206054..08f2bc5d528 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -5,5 +5,7 @@ 'depends': [ 'base' ], - 'application': True + 'application': True, + 'author': 'Odoo S.A.', + 'license': 'LGPL-3' } \ No newline at end of file diff --git a/estate/models/__init__.py b/estate/models/__init__.py new file mode 100644 index 00000000000..2d6535ae530 --- /dev/null +++ b/estate/models/__init__.py @@ -0,0 +1 @@ +from . import estate_properties \ No newline at end of file diff --git a/estate/models/estate_properties.py b/estate/models/estate_properties.py new file mode 100644 index 00000000000..cbdd205fc84 --- /dev/null +++ b/estate/models/estate_properties.py @@ -0,0 +1,27 @@ +from odoo import fields, models + +class PropertyModel(models.Model): + _name = "estate.property" + _description = "Estate Property model" + + name = fields.Char() + description = fields.Text() + postcode = fields.Char() + date_availability = fields.Date() + expected_price = fields.Float() + 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='Orientation', + selection=[ + ('North', 'north'), + ('South', 'south'), + ('East', 'east'), + ('West', 'west') + ] + ) From a14ca4263c05ae99783b897ac470baa96910aa8e Mon Sep 17 00:00:00 2001 From: qucol-odoo Date: Mon, 20 Oct 2025 16:00:55 +0200 Subject: [PATCH 03/13] [IMP] estate: Chapter 4 --- estate/__init__.py | 2 +- estate/__manifest__.py | 7 ++++--- estate/models/__init__.py | 2 +- estate/models/estate_properties.py | 5 +++-- estate/security/ir.model.access.csv | 2 ++ 5 files changed, 11 insertions(+), 7 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 08f2bc5d528..7e5b811579c 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -1,11 +1,12 @@ -# -*- coding: utf-8 -*- - { 'name': 'Estate', 'depends': [ 'base' ], + 'data': [ + 'security/ir.model.access.csv' + ], 'application': True, 'author': 'Odoo S.A.', 'license': 'LGPL-3' -} \ No newline at end of file +} diff --git a/estate/models/__init__.py b/estate/models/__init__.py index 2d6535ae530..c74fc2fd8eb 100644 --- a/estate/models/__init__.py +++ b/estate/models/__init__.py @@ -1 +1 @@ -from . import estate_properties \ No newline at end of file +from . import estate_properties diff --git a/estate/models/estate_properties.py b/estate/models/estate_properties.py index cbdd205fc84..0244ef316ad 100644 --- a/estate/models/estate_properties.py +++ b/estate/models/estate_properties.py @@ -1,14 +1,15 @@ from odoo import fields, models + class PropertyModel(models.Model): _name = "estate.property" _description = "Estate Property model" - name = fields.Char() + name = fields.Char(required=True) description = fields.Text() postcode = fields.Char() date_availability = fields.Date() - expected_price = fields.Float() + expected_price = fields.Float(required=True) selling_price = fields.Float() bedrooms = fields.Integer() living_area = fields.Integer() 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 792ff40217b84266eb1fadb7e0d88e49c97356bd Mon Sep 17 00:00:00 2001 From: qucol-odoo Date: Tue, 21 Oct 2025 10:04:41 +0200 Subject: [PATCH 04/13] [IMP] estate: Chapter 5 --- estate/__manifest__.py | 5 ++++- estate/models/estate_properties.py | 29 +++++++++++++++++++------- estate/views/estate_menus.xml | 7 +++++++ estate/views/estate_property_views.xml | 7 +++++++ 4 files changed, 40 insertions(+), 8 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 7e5b811579c..27e74401207 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -4,7 +4,10 @@ 'base' ], 'data': [ - 'security/ir.model.access.csv' + 'security/ir.model.access.csv', + + 'views/estate_property_views.xml', + 'views/estate_menus.xml' ], 'application': True, 'author': 'Odoo S.A.', diff --git a/estate/models/estate_properties.py b/estate/models/estate_properties.py index 0244ef316ad..83d07ae85ea 100644 --- a/estate/models/estate_properties.py +++ b/estate/models/estate_properties.py @@ -8,10 +8,10 @@ class PropertyModel(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() @@ -20,9 +20,24 @@ class PropertyModel(models.Model): garden_orientation = fields.Selection( string='Orientation', selection=[ - ('North', 'north'), - ('South', 'south'), - ('East', 'east'), - ('West', 'west') + ('north', 'North'), + ('south', 'South'), + ('east', 'East'), + ('west', 'West') ] ) + + active = fields.Boolean(default=True) + state = fields.Selection( + string="State", + selection=[ + ("new", "New"), + ("received", "Offer Received"), + ("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..9f0859e523c --- /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..4d544c18597 --- /dev/null +++ b/estate/views/estate_property_views.xml @@ -0,0 +1,7 @@ + + + Properties + estate.property + list,form + + From aa31e981371f577cb27ea6b3bc7db4ab7b74a053 Mon Sep 17 00:00:00 2001 From: qucol-odoo Date: Tue, 21 Oct 2025 11:13:52 +0200 Subject: [PATCH 05/13] [IMP] estate: Chapter 6 --- estate/views/estate_property_views.xml | 71 ++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index 4d544c18597..de2c61a3be3 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -4,4 +4,75 @@ estate.property list,form + + + estate.property.list + estate.property + + + + + + + + + + + + + + + estate.property.form + estate.property + +
+ +

+ +

+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ + + estate.property.search + estate.property + + + + + + + + + + + + + From 23db79f02000bb47c2e2d48fee8cf9d22dfa05e4 Mon Sep 17 00:00:00 2001 From: qucol-odoo Date: Tue, 21 Oct 2025 14:17:06 +0200 Subject: [PATCH 06/13] [IMP] estate: Chapter 7 --- estate/__manifest__.py | 5 ++- estate/models/__init__.py | 3 ++ estate/models/estate_properties.py | 7 +++-- estate/models/estate_property_offers.py | 17 +++++++++++ estate/models/estate_property_tags.py | 8 +++++ estate/models/estate_property_types.py | 8 +++++ estate/security/ir.model.access.csv | 5 ++- estate/views/estate_menus.xml | 8 +++-- estate/views/estate_property_offer_views.xml | 32 ++++++++++++++++++++ estate/views/estate_property_tag_views.xml | 7 +++++ estate/views/estate_property_type_views.xml | 7 +++++ estate/views/estate_property_views.xml | 13 ++++++++ 12 files changed, 114 insertions(+), 6 deletions(-) create mode 100644 estate/models/estate_property_offers.py create mode 100644 estate/models/estate_property_tags.py create mode 100644 estate/models/estate_property_types.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 27e74401207..58fff9bc2b0 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -7,7 +7,10 @@ '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_menus.xml', ], 'application': True, 'author': 'Odoo S.A.', diff --git a/estate/models/__init__.py b/estate/models/__init__.py index c74fc2fd8eb..5300991529d 100644 --- a/estate/models/__init__.py +++ b/estate/models/__init__.py @@ -1 +1,4 @@ from . import estate_properties +from . import estate_property_types +from . import estate_property_tags +from . import estate_property_offers diff --git a/estate/models/estate_properties.py b/estate/models/estate_properties.py index 83d07ae85ea..4e7cfefbad8 100644 --- a/estate/models/estate_properties.py +++ b/estate/models/estate_properties.py @@ -18,7 +18,6 @@ class PropertyModel(models.Model): garden = fields.Boolean() garden_area = fields.Integer() garden_orientation = fields.Selection( - string='Orientation', selection=[ ('north', 'North'), ('south', 'South'), @@ -29,7 +28,6 @@ class PropertyModel(models.Model): active = fields.Boolean(default=True) state = fields.Selection( - string="State", selection=[ ("new", "New"), ("received", "Offer Received"), @@ -41,3 +39,8 @@ class PropertyModel(models.Model): copy=False, default="new" ) + property_type_id = fields.Many2one("estate.property.type") + buyer = fields.Many2one("res.partner", copy=False) + salesperson = fields.Many2one("res.users", default=lambda self: self.env.user) + tag_ids = fields.Many2many("estate.property.tag") + offer_ids = fields.One2many("estate.property.offer", "property_id") diff --git a/estate/models/estate_property_offers.py b/estate/models/estate_property_offers.py new file mode 100644 index 00000000000..8b25236bcf2 --- /dev/null +++ b/estate/models/estate_property_offers.py @@ -0,0 +1,17 @@ +from odoo import fields, models + + +class PropertyOfferModel(models.Model): + _name = "estate.property.offer" + _description = "Estate Property Offer model" + + 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) diff --git a/estate/models/estate_property_tags.py b/estate/models/estate_property_tags.py new file mode 100644 index 00000000000..96cdb0f913f --- /dev/null +++ b/estate/models/estate_property_tags.py @@ -0,0 +1,8 @@ +from odoo import fields, models + + +class PropertyTagModel(models.Model): + _name = "estate.property.tag" + _description = "Estate Property Tag model" + + name = fields.Char(required=True) diff --git a/estate/models/estate_property_types.py b/estate/models/estate_property_types.py new file mode 100644 index 00000000000..2eece5cdcbc --- /dev/null +++ b/estate/models/estate_property_types.py @@ -0,0 +1,8 @@ +from odoo import fields, models + + +class PropertyTypeModel(models.Model): + _name = "estate.property.type" + _description = "Estate Property Type model" + + name = fields.Char(required=True) diff --git a/estate/security/ir.model.access.csv b/estate/security/ir.model.access.csv index ab63520e22b..0c0b62b7fee 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 \ 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 +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 diff --git a/estate/views/estate_menus.xml b/estate/views/estate_menus.xml index 9f0859e523c..95337d8bf62 100644 --- a/estate/views/estate_menus.xml +++ b/estate/views/estate_menus.xml @@ -1,7 +1,11 @@ - + + + + + - \ 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..935adcb598b --- /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 + +
+ +

+ +

+ + + + + +
+
+
+
+
diff --git a/estate/views/estate_property_tag_views.xml b/estate/views/estate_property_tag_views.xml new file mode 100644 index 00000000000..bbb5013739c --- /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 de2c61a3be3..01528ab12a7 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -30,8 +30,10 @@

+ + @@ -53,6 +55,17 @@ + + + + + + + + + + + From 5563be0ac47806bab68cee63714e925a09954972 Mon Sep 17 00:00:00 2001 From: qucol-odoo Date: Tue, 21 Oct 2025 15:56:00 +0200 Subject: [PATCH 07/13] [IMP] estate: Chapter 8 --- estate/models/estate_properties.py | 34 ++++++++++++++++---- estate/models/estate_property_offers.py | 13 +++++++- estate/views/estate_property_offer_views.xml | 4 +++ estate/views/estate_property_views.xml | 6 ++-- 4 files changed, 47 insertions(+), 10 deletions(-) diff --git a/estate/models/estate_properties.py b/estate/models/estate_properties.py index 4e7cfefbad8..df0a3743fcd 100644 --- a/estate/models/estate_properties.py +++ b/estate/models/estate_properties.py @@ -1,4 +1,7 @@ -from odoo import fields, models +from odoo import api, fields, models + +DEFAULT_GARDEN_AREA = 10 +DEFAULT_GARDEN_ORIENTATION = "north" class PropertyModel(models.Model): @@ -10,6 +13,7 @@ class PropertyModel(models.Model): postcode = fields.Char() date_availability = fields.Date(default=fields.Date.add(fields.Date.today(), months=3), copy=False) expected_price = fields.Float(required=True) + best_offer = fields.Float(compute="_get_highest_price") selling_price = fields.Float(readonly=True, copy=False) bedrooms = fields.Integer(default=2) living_area = fields.Integer() @@ -18,13 +22,9 @@ class PropertyModel(models.Model): garden = fields.Boolean() garden_area = fields.Integer() garden_orientation = fields.Selection( - selection=[ - ('north', 'North'), - ('south', 'South'), - ('east', 'East'), - ('west', 'West') - ] + selection=[('north', 'North'), ('south', 'South'), ('east', 'East'), ('west', 'West')] ) + total_living_area = fields.Integer(compute="_compute_total_area") active = fields.Boolean(default=True) state = fields.Selection( @@ -44,3 +44,23 @@ class PropertyModel(models.Model): salesperson = fields.Many2one("res.users", default=lambda self: self.env.user) tag_ids = fields.Many2many("estate.property.tag") offer_ids = fields.One2many("estate.property.offer", "property_id") + + @api.depends("living_area", "garden_area") + def _compute_total_area(self): + for record in self: + record.total_living_area = record.living_area + record.garden_area + + @api.depends("offer_ids") + def _get_highest_price(self): + for record in self: + record.best_offer = max(record.offer_ids.mapped("price")) if record.offer_ids else 0 + + @api.onchange("garden") + def _update_garden_area_and_orientation(self): + for record in self: + if record.garden: + record.garden_area = DEFAULT_GARDEN_AREA + record.garden_orientation = DEFAULT_GARDEN_ORIENTATION + else: + record.garden_area = 0 + record.garden_orientation = None diff --git a/estate/models/estate_property_offers.py b/estate/models/estate_property_offers.py index 8b25236bcf2..2637687f321 100644 --- a/estate/models/estate_property_offers.py +++ b/estate/models/estate_property_offers.py @@ -1,4 +1,4 @@ -from odoo import fields, models +from odoo import api, fields, models class PropertyOfferModel(models.Model): @@ -15,3 +15,14 @@ class PropertyOfferModel(models.Model): ) partner_id = fields.Many2one("res.partner", required=True) property_id = fields.Many2one("estate.property", required=True) + validity = fields.Integer(default=7) + date_deadline = fields.Date(compute="_compute_deadline", inverse="_inverse_deadline") + + @api.depends("validity") + def _compute_deadline(self): + for record in self: + record.date_deadline = fields.Date.add(record.create_date, days=record.validity) if record.create_date else None + + def _inverse_deadline(self): + for record in self: + record.validity = (record.date_deadline - fields.Date.to_date(record.create_date)).days if record.date_deadline else record.validity diff --git a/estate/views/estate_property_offer_views.xml b/estate/views/estate_property_offer_views.xml index 935adcb598b..cc2d3bbe8c1 100644 --- a/estate/views/estate_property_offer_views.xml +++ b/estate/views/estate_property_offer_views.xml @@ -6,6 +6,8 @@ + + @@ -23,6 +25,8 @@ + + diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index 01528ab12a7..3fd02ffd1ac 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -39,6 +39,7 @@ + @@ -47,12 +48,13 @@ - + - + + From 42d58417db65b722f794366c88fbb3740ac1c310 Mon Sep 17 00:00:00 2001 From: qucol-odoo Date: Tue, 21 Oct 2025 16:48:11 +0200 Subject: [PATCH 08/13] [IMP] estate: Chapter 9 --- estate/models/estate_properties.py | 15 +++++++++++++++ estate/models/estate_property_offers.py | 10 ++++++++++ estate/views/estate_property_offer_views.xml | 2 ++ estate/views/estate_property_views.xml | 5 +++++ 4 files changed, 32 insertions(+) diff --git a/estate/models/estate_properties.py b/estate/models/estate_properties.py index df0a3743fcd..53fd984344d 100644 --- a/estate/models/estate_properties.py +++ b/estate/models/estate_properties.py @@ -1,4 +1,5 @@ from odoo import api, fields, models +from odoo.exceptions import UserError DEFAULT_GARDEN_AREA = 10 DEFAULT_GARDEN_ORIENTATION = "north" @@ -64,3 +65,17 @@ def _update_garden_area_and_orientation(self): else: record.garden_area = 0 record.garden_orientation = None + + def mark_as_sold(self): + for record in self: + if record.state == "cancelled": + raise UserError("A cancelled property cannot be set as sold.") + record.state = "sold" + return True + + def mark_as_cancelled(self): + for record in self: + if record.state == "sold": + raise UserError("A sold property cannot be set as cancelled.") + record.state = "cancelled" + return True diff --git a/estate/models/estate_property_offers.py b/estate/models/estate_property_offers.py index 2637687f321..4e38638ff7e 100644 --- a/estate/models/estate_property_offers.py +++ b/estate/models/estate_property_offers.py @@ -26,3 +26,13 @@ def _compute_deadline(self): def _inverse_deadline(self): for record in self: record.validity = (record.date_deadline - fields.Date.to_date(record.create_date)).days if record.date_deadline else record.validity + + def accept_offer(self): + for record in self: + record.status = "accepted" + record.property_id.selling_price = record.price + record.property_id.buyer = record.partner_id + + def refuse_offer(self): + for record in self: + record.status = "refused" diff --git a/estate/views/estate_property_offer_views.xml b/estate/views/estate_property_offer_views.xml index cc2d3bbe8c1..d83477fc40a 100644 --- a/estate/views/estate_property_offer_views.xml +++ b/estate/views/estate_property_offer_views.xml @@ -8,6 +8,8 @@ +