From 60f15113ab5fd6543f90ac4f0e9e7023e4435ff7 Mon Sep 17 00:00:00 2001 From: hapt-odoo Date: Wed, 4 Jun 2025 19:02:25 +0530 Subject: [PATCH 01/15] [ADD] estate: created real estate application Created the new module of Real Estate. In this, I created a manifest file and an init file for the configuration of the App. and added the security file to give access for the model estate_property. Created a List and a form view with 3 levels of menu items --- estate/__init__.py | 1 + estate/__manifest__.py | 13 ++++++++ estate/models/__init__.py | 1 + estate/models/estate_property.py | 22 ++++++++++++++ estate/security/ir.model.access.csv | 2 ++ estate/views/estate_menus.xml | 8 +++++ estate/views/estate_property_views.xml | 41 ++++++++++++++++++++++++++ 7 files changed, 88 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 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 new file mode 100644 index 00000000000..0650744f6bc --- /dev/null +++ b/estate/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/estate/__manifest__.py b/estate/__manifest__.py new file mode 100644 index 00000000000..681fb0c4289 --- /dev/null +++ b/estate/__manifest__.py @@ -0,0 +1,13 @@ +{ + 'name': 'Real Estate', + 'category': 'Estate', + 'description': """This module is sale estate module""", + 'depends': ['base'], + 'data': [ + "views/estate_menus.xml", + "views/estate_property_views.xml", + 'security/ir.model.access.csv', + ], + 'application': True, + 'license': 'OEEL-1', +} 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..2bd6dd3b32e --- /dev/null +++ b/estate/models/estate_property.py @@ -0,0 +1,22 @@ +from odoo import fields, models + + +class EstatePropery(models.Model): + _name = "estate.property" + _description = "Estate Property Model" + + + 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(default=True) + garden = fields.Boolean() + garden_area = fields.Integer() + garden_orientation = fields.Selection([('north', 'North'), ('east', 'East'), ('west', 'West'), ('south', 'South')], required=True) + diff --git a/estate/security/ir.model.access.csv b/estate/security/ir.model.access.csv new file mode 100644 index 00000000000..d9d6ba57cc5 --- /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 diff --git a/estate/views/estate_menus.xml b/estate/views/estate_menus.xml new file mode 100644 index 00000000000..4acebdfd3e4 --- /dev/null +++ b/estate/views/estate_menus.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml new file mode 100644 index 00000000000..135e7bd5091 --- /dev/null +++ b/estate/views/estate_property_views.xml @@ -0,0 +1,41 @@ + + + + Properties + estate.property + list,form + + + + estate.property.properties.list.view + estate.property + + + + + + + + + + + estate.property.properties.form.view + estate.property + +
+ + + + + + + + + + + + +
+
+
+
From 862ca08a7e3092507cc2ce843656708dce2576b0 Mon Sep 17 00:00:00 2001 From: hapt-odoo Date: Thu, 5 Jun 2025 18:38:59 +0530 Subject: [PATCH 02/15] [IMP] estate: created new models and applied the relation between models In the real estate module, created new models for property types, tags, and offers. Created compute, inverse, and onchange functions to calculate the fields' values and to set values for specific fields --- estate/__manifest__.py | 5 +- estate/models/__init__.py | 3 + estate/models/estate_property.py | 52 ++++++++++-- estate/models/estate_property_offer.py | 32 ++++++++ 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 | 34 ++++++-- estate/views/estate_property_tag_views.xml | 32 ++++++++ estate/views/estate_property_type_views.xml | 32 ++++++++ estate/views/estate_property_views.xml | 90 +++++++++++++++++---- 11 files changed, 270 insertions(+), 29 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_tag_views.xml create mode 100644 estate/views/estate_property_type_views.xml diff --git a/estate/__manifest__.py b/estate/__manifest__.py index 681fb0c4289..433d4039c58 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -4,10 +4,13 @@ 'description': """This module is sale estate module""", 'depends': ['base'], 'data': [ - "views/estate_menus.xml", + "views/estate_property_tag_views.xml", + "views/estate_property_type_views.xml", "views/estate_property_views.xml", + "views/estate_menus.xml", 'security/ir.model.access.csv', ], 'application': True, 'license': 'OEEL-1', + "sequence": 1, } 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 2bd6dd3b32e..03d174134df 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -1,22 +1,60 @@ -from odoo import fields, models +from odoo import api, fields, models +from dateutil.relativedelta import relativedelta +from datetime import date -class EstatePropery(models.Model): +class EstateProperty(models.Model): _name = "estate.property" _description = "Estate Property Model" - name = fields.Char(required=True) description = fields.Text() postcode = fields.Char() - date_availability = fields.Date() + date_availability = fields.Date( + default=lambda self: (date.today() + relativedelta(months=3)).strftime('%Y-%m-%d'), + 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(default=True) garden = fields.Boolean() garden_area = fields.Integer() - garden_orientation = fields.Selection([('north', 'North'), ('east', 'East'), ('west', 'West'), ('south', 'South')], required=True) + garden_orientation = fields.Selection([('north', 'North'), ('east', 'East'), ('west', 'West'), ('south', 'South')], + required=True) + active = fields.Boolean(default=True) + state = fields.Selection( + [('new', 'New'), ('received', 'Offer Received'), ('accepted', 'Offer Accepted'), ('sold', 'Sold'), + ('cancelled', 'Cancelled')], default='new') + property_type_id = fields.Many2one('estate.property.type', string='Property Type') + user_id = fields.Many2one('res.users', string='Salesman', default=lambda self: self.env.user) + partner_id = fields.Many2one('res.partner', string='Buyer', copy=False) + tag_ids = fields.Many2many('estate.property.tag', string='Tags') + offer_id = fields.One2many('estate.property.offer', 'property_id', string='Offers') + total_area = fields.Integer(compute='_compute_total_area', store=True) + best_price = fields.Float( + string='Best Offer', + compute='_compute_best_price', + store=True, + readonly=True + ) + + @api.depends('living_area', 'garden_area') + def _compute_total_area(self): + for record in self: + record.total_area = record.living_area + record.garden_area + + @api.depends('offer_id.price') + def _compute_best_price(self): + for record in self: + best_offer = max(record.offer_id.mapped('price'), default=0) + record.best_price = best_offer + @api.onchange('garden') + def _onchange_garden(self): + for record in self: + if record.garden: + record.garden_area = 10 + record.garden_orientation = 'north' diff --git a/estate/models/estate_property_offer.py b/estate/models/estate_property_offer.py new file mode 100644 index 00000000000..208fcc432ca --- /dev/null +++ b/estate/models/estate_property_offer.py @@ -0,0 +1,32 @@ +from odoo import api, fields, models +from datetime import date, timedelta + + +class EstatePropertyOffer(models.Model): + _name = "estate.property.offer" + _description = "Estate Property offer Model" + + 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) + validity = fields.Integer(string='Validity(days)', default=7) + date_deadline = fields.Date(compute='_compute_date_deadline', inverse='_inverse_deadline', string='Deadline') + + @api.depends('validity') + def _compute_date_deadline(self): + for record in self: + if record.validity: + record.date_deadline = date.today() + timedelta(days=record.validity) + else: + record.date_deadline = False + + def _inverse_deadline(self): + for record in self: + if record.date_deadline: + record.validity = (record.date_deadline - date.today()).days + else: + record.validity = 7 diff --git a/estate/models/estate_property_tag.py b/estate/models/estate_property_tag.py new file mode 100644 index 00000000000..bd830195315 --- /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 = "Estate Property Tag Model" + + 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..8b14c4b89d4 --- /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 = "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 d9d6ba57cc5..4c593ed42e4 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 \ No newline at end of file diff --git a/estate/views/estate_menus.xml b/estate/views/estate_menus.xml index 4acebdfd3e4..84213aefe1e 100644 --- a/estate/views/estate_menus.xml +++ b/estate/views/estate_menus.xml @@ -1,8 +1,30 @@ - + - - - - - + + + + + + + diff --git a/estate/views/estate_property_tag_views.xml b/estate/views/estate_property_tag_views.xml new file mode 100644 index 00000000000..9f294faff40 --- /dev/null +++ b/estate/views/estate_property_tag_views.xml @@ -0,0 +1,32 @@ + + + + Property Tag + estate.property.tag + list,form + + + estate.property.tag.list.view + estate.property.tag + + + + + + + + estate.property.tag.form.view + estate.property.tag + +
+ + + + + +
+
+
+
+ + diff --git a/estate/views/estate_property_type_views.xml b/estate/views/estate_property_type_views.xml new file mode 100644 index 00000000000..b426667b280 --- /dev/null +++ b/estate/views/estate_property_type_views.xml @@ -0,0 +1,32 @@ + + + + Property Types + estate.property.type + list,form + + + estate.property.type.list.view + estate.property.type + + + + + + + + estate.property.type.form.view + estate.property.type + +
+ + + + + +
+
+
+
+ + diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index 135e7bd5091..8c177a4a8e3 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -1,41 +1,101 @@ - Properties + Properties estate.property list,form - estate.property.properties.list.view estate.property - + + + + + - estate.property.properties.form.view estate.property
+ +

+ +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ + estate.property.search.view + estate.property + + + + + + + + + - - - - - - - - - - + - +
+ From 8e4a45520c93a292b1ad407eed6839e2a8b00a9b Mon Sep 17 00:00:00 2001 From: hapt-odoo Date: Fri, 6 Jun 2025 17:56:31 +0530 Subject: [PATCH 03/15] [IMP] estate: added button for actions and added constraints In the real estate module, added action using button type object, Also added user error and validation error while applying constraints on the fields --- estate/models/estate_property.py | 28 +++++++++++++++- estate/models/estate_property_offer.py | 37 +++++++++++++++++++++ estate/models/estate_property_tag.py | 4 +++ estate/models/estate_property_type.py | 4 +++ estate/views/estate_menus.xml | 36 +++++--------------- estate/views/estate_property_tag_views.xml | 2 -- estate/views/estate_property_type_views.xml | 2 -- estate/views/estate_property_views.xml | 9 ++++- 8 files changed, 89 insertions(+), 33 deletions(-) diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 03d174134df..acb55ad5c80 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -1,6 +1,9 @@ from odoo import api, fields, models from dateutil.relativedelta import relativedelta from datetime import date +from odoo.exceptions import UserError +from odoo.exceptions import ValidationError +from odoo.tools import float_compare class EstateProperty(models.Model): @@ -27,7 +30,7 @@ class EstateProperty(models.Model): active = fields.Boolean(default=True) state = fields.Selection( [('new', 'New'), ('received', 'Offer Received'), ('accepted', 'Offer Accepted'), ('sold', 'Sold'), - ('cancelled', 'Cancelled')], default='new') + ('cancelled', 'Cancelled')], default='new', string='Status') property_type_id = fields.Many2one('estate.property.type', string='Property Type') user_id = fields.Many2one('res.users', string='Salesman', default=lambda self: self.env.user) partner_id = fields.Many2one('res.partner', string='Buyer', copy=False) @@ -41,6 +44,11 @@ class EstateProperty(models.Model): readonly=True ) + _sql_constraints = [ + ('check_expected_price_positive', 'CHECK(expected_price > 0)', 'Expected price must be positive!'), + ('check_selling_price_positive', 'CHECK(selling_price > 0)', 'Selling price must be positive!'), + ] + @api.depends('living_area', 'garden_area') def _compute_total_area(self): for record in self: @@ -58,3 +66,21 @@ def _onchange_garden(self): if record.garden: record.garden_area = 10 record.garden_orientation = 'north' + + def action_sold(self): + for record in self: + if record.state == 'cancelled': + raise UserError("Property is cancelled. No further actions can be taken on this property.") + record.state = 'sold' + + def action_cancel(self): + for record in self: + if record.state == 'sold': + raise UserError("Property is already sold. No further actions can be taken on this property.") + record.state = 'cancelled' + + @api.constrains('selling_price') + def _check_selling_price(self): + for record in self: + if float_compare(record.expected_price * .90, record.selling_price, 2): + raise ValidationError("The selling price not be lowed than 90% of expected price") diff --git a/estate/models/estate_property_offer.py b/estate/models/estate_property_offer.py index 208fcc432ca..22e2d230042 100644 --- a/estate/models/estate_property_offer.py +++ b/estate/models/estate_property_offer.py @@ -1,5 +1,6 @@ from odoo import api, fields, models from datetime import date, timedelta +from odoo.exceptions import UserError class EstatePropertyOffer(models.Model): @@ -16,6 +17,10 @@ class EstatePropertyOffer(models.Model): validity = fields.Integer(string='Validity(days)', default=7) date_deadline = fields.Date(compute='_compute_date_deadline', inverse='_inverse_deadline', string='Deadline') + _sql_constraints = [ + ('check_offer_price_positive', 'CHECK(price > 0)', 'Offer price must be positive!'), + ] + @api.depends('validity') def _compute_date_deadline(self): for record in self: @@ -30,3 +35,35 @@ def _inverse_deadline(self): record.validity = (record.date_deadline - date.today()).days else: record.validity = 7 + + def action_accept_offer(self): + for record in self: + if record.status == 'accepted': + raise UserError("This offer has already been accepted.") + check_other_accepted = self.search([ + ('status', '=', 'accepted') + ]) + if check_other_accepted: + raise UserError("Another offer has already been accepted for this property.") + record.status = 'accepted' + record.property_id.selling_price = record.price + record.property_id.partner_id = record.partner_id + record.property_id.state = 'accepted' + return True + + def action_refuse_offer(self): + for record in self: + record.status = 'refused' + check_other_accepted = self.search([ + ('status', '=', 'accepted') + ]) + if not check_other_accepted: + record.property_id.selling_price = 0 + record.property_id.partner_id = False + return True + + @api.ondelete(at_uninstall=False) + def _reset_selling_price(self): + for offer in self: + if offer.status == 'accepted': + offer.property_id.selling_price = 0.0 diff --git a/estate/models/estate_property_tag.py b/estate/models/estate_property_tag.py index bd830195315..db4804a253a 100644 --- a/estate/models/estate_property_tag.py +++ b/estate/models/estate_property_tag.py @@ -6,3 +6,7 @@ class EstatePropertyTag(models.Model): _description = "Estate Property Tag Model" name = fields.Char(required=True) + + _sql_constraints = [ + ('check_property_tag', 'UNIQUE(name)', 'Property Tag must be unique'), + ] diff --git a/estate/models/estate_property_type.py b/estate/models/estate_property_type.py index 8b14c4b89d4..acaa6d0ebdf 100644 --- a/estate/models/estate_property_type.py +++ b/estate/models/estate_property_type.py @@ -6,3 +6,7 @@ class EstatePropertyType(models.Model): _description = "Estate Property Type Model" name = fields.Char(required=True) + + _sql_constraints = [ + ('check_property_type', 'UNIQUE(name)', 'Property Type must be unique'), + ] diff --git a/estate/views/estate_menus.xml b/estate/views/estate_menus.xml index 84213aefe1e..d90669a1142 100644 --- a/estate/views/estate_menus.xml +++ b/estate/views/estate_menus.xml @@ -1,30 +1,12 @@ - - - - - - + + + + + + + + + - diff --git a/estate/views/estate_property_tag_views.xml b/estate/views/estate_property_tag_views.xml index 9f294faff40..6fb59a26142 100644 --- a/estate/views/estate_property_tag_views.xml +++ b/estate/views/estate_property_tag_views.xml @@ -28,5 +28,3 @@ - - diff --git a/estate/views/estate_property_type_views.xml b/estate/views/estate_property_type_views.xml index b426667b280..7ea17356444 100644 --- a/estate/views/estate_property_type_views.xml +++ b/estate/views/estate_property_type_views.xml @@ -28,5 +28,3 @@ - - diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index 8c177a4a8e3..471dd1c5f58 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -25,6 +25,10 @@ estate.property
+
+

@@ -64,6 +68,10 @@ + + + + + + + + + diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index 471dd1c5f58..7404575af40 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -4,19 +4,25 @@ Properties estate.property list,form + {'search_default_state': True, 'search_default_current': True} estate.property.properties.list.view estate.property - + + + - + @@ -26,17 +32,20 @@
-

- + - + @@ -55,24 +64,25 @@ - - + + - - + + +

diff --git a/estate_account/__manifest__.py b/estate_account/__manifest__.py index 8ab43d6ee2b..b2564e68c10 100644 --- a/estate_account/__manifest__.py +++ b/estate_account/__manifest__.py @@ -5,6 +5,7 @@ 'depends': ['estate', 'account'], 'data': [ "security/ir.model.access.csv", + "report/estate_property_inherit_template.xml", ], 'application': True, 'license': 'OEEL-1', diff --git a/estate_account/report/estate_property_inherit_template.xml b/estate_account/report/estate_property_inherit_template.xml new file mode 100644 index 00000000000..da37985b19a --- /dev/null +++ b/estate_account/report/estate_property_inherit_template.xml @@ -0,0 +1,15 @@ + + + + From 4624a8d7899321e3d9481d2c434dd4e4cfc81b88 Mon Sep 17 00:00:00 2001 From: hapt-odoo Date: Tue, 17 Jun 2025 18:48:36 +0530 Subject: [PATCH 10/15] [IMP] estate: Inherited Template and Created Unit Test Cases In the estate_account module, inherited template, and using xpath In the estate module, I created 3 test cases for estate_property_offer model and estate_property model --- estate/__manifest__.py | 4 +- estate/models/estate_property.py | 3 + estate/tests/__init__.py | 1 + estate/tests/test_estate_property.py | 78 +++++++++++++++++++ .../estate_property_inherit_template.xml | 19 ++--- 5 files changed, 92 insertions(+), 13 deletions(-) create mode 100644 estate/tests/__init__.py create mode 100644 estate/tests/test_estate_property.py diff --git a/estate/__manifest__.py b/estate/__manifest__.py index 47d13f11c9d..7a55038c8ac 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -11,10 +11,10 @@ "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", "report/estate_property_templates.xml", "report/estate_property_reports.xml", + "views/estate_property_views.xml", + "views/estate_menus.xml", ], 'demo': [ "demo/mail_message_subtype_data.xml", diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index fa6ce1ce52d..4db41206577 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -72,6 +72,9 @@ def action_sold(self): for record in self: if record.state == 'cancelled': raise UserError("Property is cancelled. No further actions can be taken on this property.") + accepted_offers = record.offer_ids.filtered(lambda o: o.status == 'accepted') + if not accepted_offers: + raise UserError("Property must be in 'Accepted' state before it can be sold.") record.state = 'sold' def action_cancel(self): diff --git a/estate/tests/__init__.py b/estate/tests/__init__.py new file mode 100644 index 00000000000..576617cccff --- /dev/null +++ b/estate/tests/__init__.py @@ -0,0 +1 @@ +from . import test_estate_property diff --git a/estate/tests/test_estate_property.py b/estate/tests/test_estate_property.py new file mode 100644 index 00000000000..3ce9c645552 --- /dev/null +++ b/estate/tests/test_estate_property.py @@ -0,0 +1,78 @@ +from odoo.tests.common import TransactionCase +from odoo.exceptions import UserError +from odoo.tests import Form + + +class TestEstateProperty(TransactionCase): + + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.properties = cls.env['estate.property'] + + def test_creation_offer(self): + property = self.properties.create({ + 'name': 'Test Property', + 'expected_price': 1000, + 'garden': True, + 'garden_area': 20, + 'garden_orientation': 'south', + 'state': 'sold', + }) + + with self.assertRaises( + UserError, msg="Cannot create an offer for a sold property" + ): + self.env["estate.property.offer"].create( + { + "price": 1500.00, + "partner_id": self.env.ref("base.res_partner_1").id, + "date_deadline": "2025-09-14", + "property_id": property.id, + } + ) + + def test_sold_property(self): + property = self.properties.create({ + 'name': 'Test Property Demo', + 'expected_price': 100, + 'garden': True, + 'garden_area': 20, + 'garden_orientation': 'south', + 'state': 'new', + }) + + self.env['estate.property.offer'].create({ + 'property_id': property.id, + 'partner_id': self.env.ref('base.res_partner_2').id, + "validity": 7, + 'price': 1200, + }) + + with self.assertRaises(UserError): + property.action_sold() + + def test_reset_garden_area_and_orientation(self): + property = self.env["estate.property"].create( + { + "name": "Garden Test Property", + "expected_price": "789", + "garden": True, + "garden_area": 50, + "garden_orientation": "north", + } + ) + + with Form(property) as form: + form.garden = False + form.save() + + self.assertFalse(property.garden, "Garden checkbox should be unchecked.") + self.assertFalse( + property.garden_area, + "Garden area should be reset when the garden checkbox is unchecked.", + ) + self.assertFalse( + property.garden_orientation, + "Orientation should be reset when the garden checkbox is unchecked.", + ) diff --git a/estate_account/report/estate_property_inherit_template.xml b/estate_account/report/estate_property_inherit_template.xml index da37985b19a..e2ee33be1e0 100644 --- a/estate_account/report/estate_property_inherit_template.xml +++ b/estate_account/report/estate_property_inherit_template.xml @@ -1,15 +1,12 @@ -