From 0f35aaa6098dde227f91327558101ceea893f0b5 Mon Sep 17 00:00:00 2001 From: kdes-odoo Date: Thu, 3 Jul 2025 10:37:53 +0530 Subject: [PATCH 1/7] [ADD] Estate: Initial module creation Created a new 'estate' module as per Chapters 2 and 3. Defined the module structure, added the business object 'estate.property' in a Python class, and configured the __manifest__.py and __init__.py to make the app visible in the Apps page. --- estate/__init__.py | 1 + estate/__manifest__.py | 13 +++++++++++++ estate/models/__init__.py | 1 + estate/models/estate_property.py | 23 +++++++++++++++++++++++ 4 files changed, 38 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..acf068b7f2c --- /dev/null +++ b/estate/__manifest__.py @@ -0,0 +1,13 @@ +{ + 'name': "Estate", + 'version': '1.0', + 'depends': ['base'], + 'author': "Kalpan Desai", + 'category': 'Estate/sales', + 'description': """ + Module specifically designed for real estate buisness case. + """, + 'installable': True, + 'application': True, + +} \ 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..294d04bbe56 --- /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 = "Details of all the properties will be stored over here." + + + name = fields.Char('Name', required=True) + description = fields.Text('Description') + postcode = fields.Char('Postcode') + date_availability = fields.Date('Available From') + expected_price = fields.Float('Expected Price', required=True) + selling_price = fields.Float('Selling Price', readonly=True, copy=False) + bedrooms = fields.Integer('Bedrooms', default=2) + living_area = fields.Integer('Living Area (sqm)') + facades = fields.Integer('Facades') + garage = fields.Boolean('Garage') + garden = fields.Boolean('Garden') + garden_area = fields.Integer('Garden Area (sqm)') + garden_orientation = fields.Selection( + [('north', 'North'), ('south', 'South'), ('east', 'East'), ('west', 'West')], + string='Garden Orientation' + ) From 9920ca20460ea2d7be7fef4551adebe0e3ef8e56 Mon Sep 17 00:00:00 2001 From: kdes-odoo Date: Thu, 3 Jul 2025 11:02:39 +0530 Subject: [PATCH 2/7] [ADD] estate: initial module creation Created a new 'estate' module as per Chapters 2 and 3. Defined the module structure added the business object 'estate.property' in a Python class, and configured the __manifest__.py and __init__.py to make the app visible in the Apps page. --- estate/models/estate_property.py | 1 - 1 file changed, 1 deletion(-) diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 294d04bbe56..1aeb96488ae 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -1,5 +1,4 @@ from odoo import models , fields - class EstateProperty(models.Model): _name = "estate.property" _description = "Details of all the properties will be stored over here." From 3021c7b29f4bd01681279fba53601a2238f930bd Mon Sep 17 00:00:00 2001 From: kdes-odoo Date: Fri, 4 Jul 2025 08:28:42 +0530 Subject: [PATCH 3/7] [ADD] estate: added access rights, field attributes and custom Views Applied the security access to base users group using CSV. Started working on UI: Create the estate_property_views.xml file in the Views folder Created an action for the model estate.property. Created the estate_menus.xml Created the three levels of menus for the estate.property Added the appropriate default attributes so that: the default number of bedrooms is 2 the default availability date is in 3 months Added active and state field. Defined a custom list view for the estate.property Defined a custom form view for the estate.property Defined a custom search view for the estate.property --- .vscode/settings.json | 3 + estate/__manifest__.py | 6 ++ estate/models/estate_property.py | 17 ++++- estate/security/ir.model.access.csv | 2 + estate/views/estate_menus.xml | 13 ++++ estate/views/estate_property_views.xml | 86 ++++++++++++++++++++++++++ 6 files changed, 124 insertions(+), 3 deletions(-) create mode 100644 .vscode/settings.json 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/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000000..ff5300ef481 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "python.languageServer": "None" +} \ No newline at end of file diff --git a/estate/__manifest__.py b/estate/__manifest__.py index acf068b7f2c..87034b0bd83 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -10,4 +10,10 @@ 'installable': True, 'application': True, + '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/models/estate_property.py b/estate/models/estate_property.py index 1aeb96488ae..729877aa5b3 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -1,13 +1,18 @@ -from odoo import models , fields +from odoo import models, fields +from dateutil.relativedelta import relativedelta + class EstateProperty(models.Model): _name = "estate.property" _description = "Details of all the properties will be stored over here." - name = fields.Char('Name', required=True) + name = fields.Char('Name', required=True,default='Unknown Property') description = fields.Text('Description') postcode = fields.Char('Postcode') - date_availability = fields.Date('Available From') + date_availability = fields.Date( + "Date Availability", + default=lambda self: fields.Date.to_string(fields.Date.context_today(self) + relativedelta(months=3)) + ) expected_price = fields.Float('Expected Price', required=True) selling_price = fields.Float('Selling Price', readonly=True, copy=False) bedrooms = fields.Integer('Bedrooms', default=2) @@ -20,3 +25,9 @@ class EstateProperty(models.Model): [('north', 'North'), ('south', 'South'), ('east', 'East'), ('west', 'West')], string='Garden Orientation' ) + active = fields.Boolean('Active', default=True) + state = fields.Selection( + [('new', 'New'), ('offer_received', 'Offer Received'), ('offer_accepted', 'Offer Accepted'), + ('sold', 'Sold'), ('canceled', 'Canceled')], + string='Status', default='new', required=True, copy=False + ) \ 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..45a7de697cd --- /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_base_group,estate.property.basegroup,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..8df75853d8f --- /dev/null +++ b/estate/views/estate_menus.xml @@ -0,0 +1,13 @@ + + + + + + + + \ 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..a166e653001 --- /dev/null +++ b/estate/views/estate_property_views.xml @@ -0,0 +1,86 @@ + + + + Properties + estate.property + list,form + [('active', '=', True)] + + + + estate.property.list.view + estate.property + + + + + + + + + + + + + + + estate.property.search.view + estate.property + + + + + + + + + + + + + + + + + + + estate.property.properties.form.view + estate.property + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ +
\ No newline at end of file From aba5754d055d22a511ebb3f4305340098603b862 Mon Sep 17 00:00:00 2001 From: kdes-odoo Date: Mon, 7 Jul 2025 10:24:42 +0530 Subject: [PATCH 4/7] [ADD] estate: added property type, Tags, offers and States MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added Property type model, created view and added it to menu Added filed to Buyer and salesperson field to property model Added Property tag model, created view and added it to menu Created many2many relation between property tags and property model Added Property offer model, created view and added property form page. Added the total_area field to estate.property. It is defined as the sum of the living_area and the garden_area. Added the best_price field to estate.property. It is defined as the highest (i.e. maximum) of the offers’ price. Added validity and date_deadline to property offer Defined inverse function so that the user can set either the date or the validity. Created an onchange in the estate.property model in order to set values for the garden area (10) and orientation (North) when garden is set to True. When unset, clear the fields. Added the buttons ‘Cancel’ and ‘Sold’ to the estate.property model. A cancelled property cannot be set as sold, and a sold property cannot be cancelled. Added the buttons ‘Accept’ and ‘Refuse’ to the estate.property.offer model. When an offer is accepted, set the buyer and the selling price for the corresponding property. --- estate/__manifest__.py | 3 + estate/models/__init__.py | 5 +- estate/models/estate_property.py | 59 +++++++++++++++++++- estate/models/estate_property_offer.py | 51 +++++++++++++++++ estate/models/estate_property_tag.py | 10 ++++ estate/models/estate_property_type.py | 11 ++++ estate/security/ir.model.access.csv | 3 + estate/views/estate_menus.xml | 34 ++++++++--- estate/views/estate_property_offer_views.xml | 43 ++++++++++++++ estate/views/estate_property_tag_views.xml | 32 +++++++++++ estate/views/estate_property_type_views.xml | 42 ++++++++++++++ estate/views/estate_property_views.xml | 31 +++++++++- 12 files changed, 309 insertions(+), 15 deletions(-) create mode 100644 estate/models/estate_property_offer.py create mode 100644 estate/models/estate_property_tag.py create mode 100644 estate/models/estate_property_type.py create mode 100644 estate/views/estate_property_offer_views.xml create mode 100644 estate/views/estate_property_tag_views.xml create mode 100644 estate/views/estate_property_type_views.xml diff --git a/estate/__manifest__.py b/estate/__manifest__.py index 87034b0bd83..640faf5b9cd 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -13,6 +13,9 @@ 'data': [ 'security/ir.model.access.csv', 'views/estate_property_views.xml', + 'views/estate_property_type_views.xml', + 'views/estate_property_tag_views.xml', + 'views/estate_property_offer_views.xml', 'views/estate_menus.xml', ] diff --git a/estate/models/__init__.py b/estate/models/__init__.py index f4c8fd6db6d..2f1821a39c1 100644 --- a/estate/models/__init__.py +++ b/estate/models/__init__.py @@ -1 +1,4 @@ -from . import estate_property \ No newline at end of file +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 729877aa5b3..f525a448365 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -1,4 +1,4 @@ -from odoo import models, fields +from odoo import models, fields, api from dateutil.relativedelta import relativedelta class EstateProperty(models.Model): @@ -30,4 +30,59 @@ class EstateProperty(models.Model): [('new', 'New'), ('offer_received', 'Offer Received'), ('offer_accepted', 'Offer Accepted'), ('sold', 'Sold'), ('canceled', 'Canceled')], string='Status', default='new', required=True, copy=False - ) \ No newline at end of file + ) + property_type_id = fields.Many2one("estate.property.type", string="Property Type", required=True) + buyer_id = fields.Many2one("res.partner", string="Buyer", copy=False) + salesperson_id = fields.Many2one("res.users", string="Salesperson", default=lambda self: self.env.user) + tags_ids = fields.Many2many("estate.property.tag", string="Tags") + offer_ids = fields.One2many("estate.property.offer", "property_id", string="Offers") + total_area = fields.Float(compute="_compute_total_area", string="Total Area", readonly=True) + best_price = fields.Float(compute="_compute_best_price", string="Best Price", readonly=True) + state = fields.Selection( + string="State", + selection=[ + ("new", "New"), + ("offer_received", "Offer Received"), + ("offer_accepted", "Offer Accepted"), + ("sold", "Sold"), + ("canceled", "Canceled"), + ], + default="new", + copy=False, + ) + + + @api.depends("garden_area", "living_area") + def _compute_total_area(self): + for record in self: + record.total_area = record.garden_area + record.living_area + + @api.depends("offer_ids.price") + def _compute_best_price(self): + for record in self: + prices = record.offer_ids.mapped("price") + record.best_price = max(prices, default=0) + + @api.onchange("garden") + def _onchange_garden(self): + if self.garden: + self.garden_area = 10 + self.garden_orientation = "north" + else: + self.garden_area = 0 + self.garden_orientation = False + + def action_sold(self): + if self.state != "canceled": + self.state = "sold" + else: + raise UserError("Canceled properties can't be sold") + return True + + def action_cancel(self): + for record in self: + if record.state != "sold": + record.state = "canceled" + else: + raise UserError("Sold properties can't be sold") + return True diff --git a/estate/models/estate_property_offer.py b/estate/models/estate_property_offer.py new file mode 100644 index 00000000000..08fd9634502 --- /dev/null +++ b/estate/models/estate_property_offer.py @@ -0,0 +1,51 @@ +from odoo import api, fields, models +from odoo.exceptions import UserError + + +class EstatePropertyOffer(models.Model): + _name = "estate.property.offer" + _description = "Property Offer" + _order = "price asc" + + price = fields.Float() + status = fields.Selection( + string="Type", + selection=[("accepted", "Accepted"), ("refused", "Refused")], + copy=False, + ) + 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_date_deadline", inverse="_inverse_date_deadline" + ) + + + + @api.depends("validity") + def _compute_date_deadline(self): + for record in self: + if not record.create_date: + today = fields.Datetime.today() + record.date_deadline = fields.Datetime.add(today, days=record.validity) + else: + record.date_deadline = fields.Datetime.add( + record.create_date, days=record.validity + ) + + def _inverse_date_deadline(self): + for record in self: + record.validity = (record.date_deadline - record.create_date.date()).days + + def action_accept_offer(self): + self.ensure_one() + self.status = "accepted" + self.property_id.selling_price = self.price + self.property_id.buyer_id = self.partner_id + self.property_id.state = "offer_accepted" + return True + + def action_refuse_offer(self): + self.ensure_one() + self.status = "refused" + return True \ No newline at end of file diff --git a/estate/models/estate_property_tag.py b/estate/models/estate_property_tag.py new file mode 100644 index 00000000000..61614859623 --- /dev/null +++ b/estate/models/estate_property_tag.py @@ -0,0 +1,10 @@ +from odoo import fields, models + + +class EstatePropertyTag(models.Model): + _name = "estate.property.tag" + _description = "Property Tags" + _order = "name" + + name = fields.Char() + \ 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..b5a054f34ca --- /dev/null +++ b/estate/models/estate_property_type.py @@ -0,0 +1,11 @@ +from odoo import models, fields + +class EstatePropertyType(models.Model): + _name = "estate.property.type" + _description = "Types of properties available in the estate module." + + + name = fields.Char('Name', required=True) + property_ids = fields.One2many( + "estate.property", "property_type_id", string="Property" + ) diff --git a/estate/security/ir.model.access.csv b/estate/security/ir.model.access.csv index 45a7de697cd..81bca172f47 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_base_group,estate.property.basegroup,model_estate_property,base.group_user,1,1,1,1 +access_estate_property_type_base_group,estate.property.type.basegroup,model_estate_property_type,base.group_user,1,1,1,1 +access_estate_property_tag_base_group,estate.property.tag.basegroup,model_estate_property_tag,base.group_user,1,1,1,1 +access_estate_property_offer_base_group,estate.property.offer.basegroup,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 8df75853d8f..5e9da54ff4e 100644 --- a/estate/views/estate_menus.xml +++ b/estate/views/estate_menus.xml @@ -1,13 +1,29 @@ - + - - - + + + + + + + + + + + \ 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..1d0229cbfb2 --- /dev/null +++ b/estate/views/estate_property_offer_views.xml @@ -0,0 +1,43 @@ + + + + Property Offers + estate.property.offer + list,form + [('property_type_id', '=', active_id)] + + + + estate.property.offer.list + estate.property.offer + + + + + + + + diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index f43daf6a369..82d80238912 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -5,13 +5,17 @@ estate.property list,form [('active', '=', True)] - + {'search_default_state': True} estate.property.list.view estate.property - + + @@ -68,7 +72,7 @@ - + @@ -81,8 +85,8 @@ - - + + @@ -91,7 +95,7 @@ - + From 5cc2dadf8c50f37436f32c8741b5263f63b7a37a Mon Sep 17 00:00:00 2001 From: kdes-odoo Date: Wed, 9 Jul 2025 10:21:57 +0530 Subject: [PATCH 6/7] [IMP] estate: added constrains and made changes in all views Added the following constraints to their corresponding models: A property expected price must be strictly positive A property selling price must be positive An offer price must be strictly positive A property tag name and property type name must be unique Added a python constraint so that the selling price cannot be lower than 90% of the expected price. Used the statusbar widget in order to display the state of the estate.property Added ordering to each model as per requirement. Added manual ordering to property type field. Added widget options to color the tag. Added conditional display of buttons Using the invisible attribute Made property tag and offers list view editable Made the field date_availability on the estate.property list view optional and hidden by default Added conditional decoration to property and offer list view. Added availability to the default search filter Added a stat button to the property type to view offers for each property. Added kanban view to the estate property --- estate/__manifest__.py | 5 +- estate/models/__init__.py | 1 + estate/models/estate_property.py | 21 +++----- estate/models/estate_property_offer.py | 22 ++++---- estate/models/estate_property_tag.py | 2 +- estate/models/estate_property_type.py | 5 +- estate/models/estate_res_user.py | 8 +++ estate/views/estate_menus.xml | 2 +- estate/views/estate_property_offer_views.xml | 2 +- estate/views/estate_property_tag_views.xml | 2 +- estate/views/estate_property_type_views.xml | 3 +- estate/views/estate_property_views.xml | 53 ++++++++++++++++---- estate/views/estate_res_user_views.xml | 15 ++++++ estate_account/__init__.py | 1 + estate_account/__manifest__.py | 21 ++++++++ estate_account/models/__init__.py | 1 + estate_account/models/estate_property.py | 32 ++++++++++++ estate_account/security/ir.model.access.csv | 2 + 18 files changed, 151 insertions(+), 47 deletions(-) create mode 100644 estate/models/estate_res_user.py create mode 100644 estate/views/estate_res_user_views.xml create mode 100644 estate_account/__init__.py create mode 100644 estate_account/__manifest__.py create mode 100644 estate_account/models/__init__.py create mode 100644 estate_account/models/estate_property.py create mode 100644 estate_account/security/ir.model.access.csv diff --git a/estate/__manifest__.py b/estate/__manifest__.py index bb39c50c365..05e6361e0fb 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -14,10 +14,11 @@ 'data': [ 'security/ir.model.access.csv', 'views/estate_property_views.xml', + 'views/estate_property_offer_views.xml', 'views/estate_property_type_views.xml', 'views/estate_property_tag_views.xml', - 'views/estate_property_offer_views.xml', + 'views/estate_res_user_views.xml', 'views/estate_menus.xml', ] -} \ No newline at end of file +} diff --git a/estate/models/__init__.py b/estate/models/__init__.py index 2f1821a39c1..ff7189569b6 100644 --- a/estate/models/__init__.py +++ b/estate/models/__init__.py @@ -2,3 +2,4 @@ from . import estate_property_type from . import estate_property_tag from . import estate_property_offer +from . import estate_res_user diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index b0a4c5a2371..265f4dbdabd 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -7,9 +7,7 @@ class EstateProperty(models.Model): _name = "estate.property" _description = "Details of all the properties will be stored over here." _order = "id desc" - - - name = fields.Char('Name', required=True,default='Unknown Property') + name = fields.Char('Name', required=True, default='Unknown Property') description = fields.Text('Description') postcode = fields.Char('Postcode') date_availability = fields.Date( @@ -41,18 +39,6 @@ class EstateProperty(models.Model): offer_ids = fields.One2many("estate.property.offer", "property_id", string="Offers") total_area = fields.Float(compute="_compute_total_area", string="Total Area", readonly=True) best_price = fields.Float(compute="_compute_best_price", string="Best Price", readonly=True) - state = fields.Selection( - string="State", - selection=[ - ("new", "New"), - ("offer_received", "Offer Received"), - ("offer_accepted", "Offer Accepted"), - ("sold", "Sold"), - ("canceled", "Canceled"), - ], - default="new", - copy=False, - ) _sql_constraints = [ ('check_expected_price', 'CHECK(expected_price > 0)', 'The expected price must strictly Positive.'), @@ -82,6 +68,7 @@ def _onchange_garden(self): def action_sold(self): if self.state != "canceled": self.state = "sold" + print("Log by KDES", flush=True) else: raise UserError("Canceled properties can't be sold") return True @@ -103,3 +90,7 @@ def _check_price(self): if float_utils.float_compare(record.selling_price, record.expected_price * 0.9, precision_rounding=3) == -1: raise ValidationError(_('The selling cannot be lower than 90% of the expected price.')) + @api.ondelete(at_uninstall=False) + def _unlink_if_state_check(self): + if any(record.state not in ('new', 'canceled') for record in self): + raise UserError(_("You cannot delete a property that is not in the 'New' or 'Canceled' state.")) diff --git a/estate/models/estate_property_offer.py b/estate/models/estate_property_offer.py index a80e6e653c6..565cfe4592f 100644 --- a/estate/models/estate_property_offer.py +++ b/estate/models/estate_property_offer.py @@ -1,4 +1,4 @@ -from odoo import api, fields, models, api +from odoo import api, fields, models, _ from odoo.exceptions import UserError @@ -23,7 +23,7 @@ class EstatePropertyOffer(models.Model): _sql_constraints = [ ("check_price", "CHECK(price > 0)", "The price must be strictly positive.") ] - + @api.depends("validity") def _compute_date_deadline(self): for record in self: @@ -39,15 +39,6 @@ def _inverse_date_deadline(self): for record in self: record.validity = (record.date_deadline - record.create_date.date()).days - def action_accept_offer(self): - self.ensure_one() - self.status = "accepted" - self.property_id.selling_price = self.price - self.property_id.buyer_id = self.partner_id - self.property_id.state = "offer_accepted" - return True - - def action_accept_offer(self): if self.property_id.state in {'accepted', 'sold'}: raise UserError(_('An offer as already been accepted.')) @@ -63,3 +54,12 @@ def action_refuse_offer(self): self.ensure_one() self.status = "refused" return True + + @api.model_create_multi + def create(self, vals_list): + for vals in vals_list: + property = self.env["estate.property"].browse(vals["property_id"]) + property.state = "offer_received" + if property.best_price > vals["price"]: + raise UserError("The offer must be higher than the existing offer") + return super().create(vals_list) diff --git a/estate/models/estate_property_tag.py b/estate/models/estate_property_tag.py index 0e3bb422e55..562bff2799d 100644 --- a/estate/models/estate_property_tag.py +++ b/estate/models/estate_property_tag.py @@ -11,4 +11,4 @@ class EstatePropertyTag(models.Model): _sql_constraints = [ ('check_name', 'UNIQUE(name)', 'A tag must be unique.'), - ] \ No newline at end of file + ] diff --git a/estate/models/estate_property_type.py b/estate/models/estate_property_type.py index 8ee40976280..f908feef1cc 100644 --- a/estate/models/estate_property_type.py +++ b/estate/models/estate_property_type.py @@ -1,12 +1,11 @@ from odoo import models, fields, api + class EstatePropertyType(models.Model): _name = "estate.property.type" _description = "Types of properties available in the estate module." _order = "name" - - name = fields.Char('Name', required=True) property_ids = fields.One2many("estate.property", "property_type_id", string="Property") sequence = fields.Integer("Sequence", default=1, help="Used to order types. Lower is better.") @@ -20,4 +19,4 @@ class EstatePropertyType(models.Model): @api.depends('offer_ids') def compute_offer_count(self): for record in self: - record.offer_count = len(record.offer_ids) \ No newline at end of file + record.offer_count = len(record.offer_ids) diff --git a/estate/models/estate_res_user.py b/estate/models/estate_res_user.py new file mode 100644 index 00000000000..107f5957082 --- /dev/null +++ b/estate/models/estate_res_user.py @@ -0,0 +1,8 @@ +from odoo import fields, models + + +class User(models.Model): + _inherit = "res.users" + + property_ids = fields.One2many("estate.property", inverse_name="salesperson_id") + domain = ["|", ("state", "=", "new"), ("state", "=", "offer_received")] diff --git a/estate/views/estate_menus.xml b/estate/views/estate_menus.xml index 5e9da54ff4e..dd6deeefe2a 100644 --- a/estate/views/estate_menus.xml +++ b/estate/views/estate_menus.xml @@ -26,4 +26,4 @@ name="Properties Tags" parent="menu_estate_settings" action="action_estate_property_tag"/> - \ 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 647c0eb6285..861ee7760c7 100644 --- a/estate/views/estate_property_offer_views.xml +++ b/estate/views/estate_property_offer_views.xml @@ -41,4 +41,4 @@ - \ No newline at end of file + diff --git a/estate/views/estate_property_tag_views.xml b/estate/views/estate_property_tag_views.xml index 5a5a93c9377..e5aa8ab8f1a 100644 --- a/estate/views/estate_property_tag_views.xml +++ b/estate/views/estate_property_tag_views.xml @@ -29,4 +29,4 @@ - \ No newline at end of file + diff --git a/estate/views/estate_property_type_views.xml b/estate/views/estate_property_type_views.xml index 11ee5739043..0a6913b7ccc 100644 --- a/estate/views/estate_property_type_views.xml +++ b/estate/views/estate_property_type_views.xml @@ -49,5 +49,4 @@ - - \ No newline at end of file + diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index 82d80238912..c906418698a 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -1,13 +1,6 @@ - - Properties - estate.property - list,form - [('active', '=', True)] - {'search_default_state': True} - - + estate.property.list.view estate.property @@ -51,7 +44,6 @@ - estate.property.properties.form.view estate.property @@ -112,4 +104,45 @@ - \ No newline at end of file + + estate.property.kanban + estate.property + + + + +
+ +
+ Expected Price: + +
+
+ Best Price: + +
+
+ Selling Price: + +
+ +
+
+
+
+
+
+ + + Properties + estate.property + list,form,kanban + [('active', '=', True)] + {'search_default_state': True} + + + diff --git a/estate/views/estate_res_user_views.xml b/estate/views/estate_res_user_views.xml new file mode 100644 index 00000000000..8b2cc83490c --- /dev/null +++ b/estate/views/estate_res_user_views.xml @@ -0,0 +1,15 @@ + + + + estate.res.user.form.inherit + res.users + + + + + + + + + + diff --git a/estate_account/__init__.py b/estate_account/__init__.py new file mode 100644 index 00000000000..9a7e03eded3 --- /dev/null +++ b/estate_account/__init__.py @@ -0,0 +1 @@ +from . import models \ No newline at end of file diff --git a/estate_account/__manifest__.py b/estate_account/__manifest__.py new file mode 100644 index 00000000000..b45806be921 --- /dev/null +++ b/estate_account/__manifest__.py @@ -0,0 +1,21 @@ +{ + 'name': "Estate_Account", + 'version': '1.0', + 'license': 'LGPL-3', + 'depends': ['estate', 'account'], + 'author': "Kalpan Desai", + 'category': 'Estate/Accounting', + 'description': """ + Module specifically designed for real estate accounting case. + This module extends the estate module to include accounting features. + It allows users to manage financial transactions related to properties, + such as tracking payments, managing invoices, and handling financial reports. + """, + 'installable': True, + 'application': True, + + 'data': [ + 'security/ir.model.access.csv', + ] + +} diff --git a/estate_account/models/__init__.py b/estate_account/models/__init__.py new file mode 100644 index 00000000000..5e1963c9d2f --- /dev/null +++ b/estate_account/models/__init__.py @@ -0,0 +1 @@ +from . import estate_property diff --git a/estate_account/models/estate_property.py b/estate_account/models/estate_property.py new file mode 100644 index 00000000000..4ce04517dc6 --- /dev/null +++ b/estate_account/models/estate_property.py @@ -0,0 +1,32 @@ +from odoo import models, fields, Command + + +class EstateProperty(models.Model): + _inherit = "estate.property" + + def action_sold(self): + print("Log by kdes inside Child model", flush=True) + self.ensure_one() + self.env["account.move"].create( + { + "partner_id": self.buyer_id.id, + "move_type": "out_invoice", + "invoice_line_ids": [ + Command.create( + { + "name": self.name, + "quantity": 1, + "price_unit": 0.06 * self.selling_price, + } + ), + Command.create( + { + "name": "administrative fees", + "quantity": 1, + "price_unit": 100.00, + } + ), + ], + } + ) + return super().action_sold() \ No newline at end of file diff --git a/estate_account/security/ir.model.access.csv b/estate_account/security/ir.model.access.csv new file mode 100644 index 00000000000..ab63520e22b --- /dev/null +++ b/estate_account/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 fe2ca9a528d5339ed022faee499f125a89b839af Mon Sep 17 00:00:00 2001 From: kdes-odoo Date: Wed, 9 Jul 2025 12:06:30 +0530 Subject: [PATCH 7/7] [FIX] estate: fixed all Runbot Errors Modified code to meet coding guidelines. Solved all Runbot Errors. --- estate/__init__.py | 2 +- estate/models/estate_property.py | 3 ++- estate_account/__init__.py | 2 +- estate_account/models/estate_property.py | 6 +++--- estate_account/security/ir.model.access.csv | 1 - 5 files changed, 7 insertions(+), 7 deletions(-) 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/models/estate_property.py b/estate/models/estate_property.py index 265f4dbdabd..53fe9ac5bec 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -3,6 +3,7 @@ from odoo.exceptions import UserError, ValidationError from odoo.tools import float_utils, _ + class EstateProperty(models.Model): _name = "estate.property" _description = "Details of all the properties will be stored over here." @@ -68,7 +69,7 @@ def _onchange_garden(self): def action_sold(self): if self.state != "canceled": self.state = "sold" - print("Log by KDES", flush=True) + # print("Log by KDES", flush=True) else: raise UserError("Canceled properties can't be sold") return True diff --git a/estate_account/__init__.py b/estate_account/__init__.py index 9a7e03eded3..0650744f6bc 100644 --- a/estate_account/__init__.py +++ b/estate_account/__init__.py @@ -1 +1 @@ -from . import models \ No newline at end of file +from . import models diff --git a/estate_account/models/estate_property.py b/estate_account/models/estate_property.py index 4ce04517dc6..5b38e6b3e00 100644 --- a/estate_account/models/estate_property.py +++ b/estate_account/models/estate_property.py @@ -1,11 +1,11 @@ -from odoo import models, fields, Command +from odoo import models, Command class EstateProperty(models.Model): _inherit = "estate.property" def action_sold(self): - print("Log by kdes inside Child model", flush=True) + # print("Log by kdes inside Child model", flush=True) self.ensure_one() self.env["account.move"].create( { @@ -29,4 +29,4 @@ def action_sold(self): ], } ) - return super().action_sold() \ No newline at end of file + return super().action_sold() diff --git a/estate_account/security/ir.model.access.csv b/estate_account/security/ir.model.access.csv index ab63520e22b..97dd8b917b8 100644 --- a/estate_account/security/ir.model.access.csv +++ b/estate_account/security/ir.model.access.csv @@ -1,2 +1 @@ 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