From 2bbbbe9d883cb1f4cdb91bf14744f19c61df14df Mon Sep 17 00:00:00 2001 From: span-odoo Date: Thu, 12 Sep 2024 12:52:25 +0530 Subject: [PATCH 1/6] [ADD] estate: create real estate module with basic property management feature Property Management: Create and maintain detailed property listings, including type, location, features, and pricing. Offer Tracking: Record and track offers from potential buyers, facilitating negotiations and decision-making. Salesperson and Buyer Associations: Link properties to responsible salespersons and interested buyers for better organization. Custom Views: Designed tailored list and form views to improve data visualization and user interaction. Business Logic: Implemented essential business rules and workflows, automating processes and ensuring data consistency. Constraints: Added validation constraints to prevent invalid data entry and maintain data integrity. --- .idea/.gitignore | 8 + .../inspectionProfiles/profiles_settings.xml | 6 + .idea/material_theme_project_new.xml | 12 ++ .idea/misc.xml | 7 + .idea/modules.xml | 8 + .idea/tutorials.iml | 8 + .idea/vcs.xml | 6 + estate/__init__.py | 1 + estate/__manifest__.py | 20 +++ estate/models/__init__.py | 5 + estate/models/estate_property.py | 128 +++++++++++++++ estate/models/estate_property_offer.py | 70 +++++++++ estate/models/estate_property_tag.py | 16 ++ estate/models/estate_property_type.py | 27 ++++ estate/security/ir.model.access.csv | 5 + estate/static/description/realestate.png | Bin 0 -> 24799 bytes estate/views/estate_menu.xml | 15 ++ estate/views/estate_property_offer_view.xml | 68 ++++++++ estate/views/estate_property_tag_view.xml | 20 +++ estate/views/estate_property_type_view.xml | 57 +++++++ estate/views/estate_property_view.xml | 147 ++++++++++++++++++ 21 files changed, 634 insertions(+) create mode 100644 .idea/.gitignore create mode 100644 .idea/inspectionProfiles/profiles_settings.xml create mode 100644 .idea/material_theme_project_new.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/tutorials.iml create mode 100644 .idea/vcs.xml 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/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/security/ir.model.access.csv create mode 100644 estate/static/description/realestate.png create mode 100644 estate/views/estate_menu.xml create mode 100644 estate/views/estate_property_offer_view.xml create mode 100644 estate/views/estate_property_tag_view.xml create mode 100644 estate/views/estate_property_type_view.xml create mode 100644 estate/views/estate_property_view.xml diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000000..13566b81b0 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 0000000000..105ce2da2d --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/material_theme_project_new.xml b/.idea/material_theme_project_new.xml new file mode 100644 index 0000000000..51a1a520fc --- /dev/null +++ b/.idea/material_theme_project_new.xml @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000000..9de286525f --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000000..5c1c888541 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/tutorials.iml b/.idea/tutorials.iml new file mode 100644 index 0000000000..d0876a78d0 --- /dev/null +++ b/.idea/tutorials.iml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000000..35eb1ddfbb --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/estate/__init__.py b/estate/__init__.py new file mode 100644 index 0000000000..0650744f6b --- /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 0000000000..695124bc19 --- /dev/null +++ b/estate/__manifest__.py @@ -0,0 +1,20 @@ +{ + "name": "Real_estate", + "version": "0.1", + "license": "LGPL-3", + "category": "Estate_props", + "author": "sahilpanghal(span)", + "summary": "Real estate module", + "description": "Real estate module", + "installable": True, + "application": True, + "depends": ["base"], + "data": [ + "security/ir.model.access.csv", + "views/estate_property_view.xml", + "views/estate_property_offer_view.xml", + "views/estate_property_type_view.xml", + "views/estate_property_tag_view.xml", + "views/estate_menu.xml", + ], +} diff --git a/estate/models/__init__.py b/estate/models/__init__.py new file mode 100644 index 0000000000..001ba04f23 --- /dev/null +++ b/estate/models/__init__.py @@ -0,0 +1,5 @@ +# imported all the models taht are made inside the model folder +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 new file mode 100644 index 0000000000..7deeb2c7b5 --- /dev/null +++ b/estate/models/estate_property.py @@ -0,0 +1,128 @@ +from odoo import api, models, fields +from dateutil.relativedelta import relativedelta +from odoo.exceptions import UserError, ValidationError + + +class EstateProperty(models.Model): + _name = "estate.property" + _description = "EstateProperty" + _order = "id desc" + # created the field for the estate.property model + name = fields.Char(required=True, default="Unkown") + description = fields.Text() + property_type_id = fields.Many2one("estate.property.type", string="Property Types") + postcode = fields.Char() + date_availablity = fields.Date( + copy=False, default=lambda self: fields.Date.today() + relativedelta(months=3) + ) + expected_price = fields.Float(required=True) + selling_price = fields.Float(readonly=True, copy=False) + bedrooms = fields.Integer(default="2") + living_area = fields.Integer() + facades = fields.Integer() + garage = fields.Boolean() + garden = fields.Boolean() + garden_area = fields.Integer() + garden_orientation = fields.Selection( + [("north", "North"), ("south", "South"), ("east", "East"), ("west", "West")] + ) + state = fields.Selection( + [ + ("new", "New"), + ("offer_recieved", "Offer Recieved"), + ("offer_accepted", "Offer Accepted"), + ("sold", "Sold"), + ("canceled", "Canceled"), + ], + default="new", + ) + active = fields.Boolean(default=True) + saler_id = fields.Many2one( + "res.users", + string="Salesperson", + index=True, + default=lambda self: self.env.user, + ) + buyer_id = fields.Many2one("res.partner", string="Buyer", index=True) + tag_ids = fields.Many2many("estate.property.tag", string="Tags") + offer_ids = fields.One2many("estate.property.offer", "property_id") + total = fields.Integer(compute="_compute_area") + best_price = fields.Integer(compute="_compute_bestprice") + + # created the sql constraints for only accepting positive values for expected price and selling price + _sql_constraints = [ + ( + "check_expected_price", + "CHECK(expected_price > 0.0)", + "A property expected price must be strictly positive and Grater then Zero.", + ), + ( + "check_selling_price", + "CHECK(selling_price > 0.0)", + "A property selling price must be positive and Grater then Zero.", + ), + ] + + # created the area computation function that will compute area from living area and garden area i.e living area + garden area + @api.depends("living_area", "garden_area") + def _compute_area(self): + for record in self: + record.total = record.living_area + record.garden_area + + # created the best price computation function that will compute best price from offers i.e maximum from the offers + @api.depends("offer_ids") + def _compute_bestprice(self): + for record in self: + if record.offer_ids: + for i in record.offer_ids: + max1 = 0 + if i.price > max1: + max1 = i.price + record.best_price = max1 + else: + record.best_price = 0 + + # created the garden onchange function that will change the values of the dependent fiels when the particular field triger is activated i.e turning on the garden field will affect the dependent fields + @api.onchange("garden") + def _onchange_garden(self): + if self.garden: + self.garden_area = 10 + self.garden_orientation = "north" + + # created a python constraint that will check weather the selling price is grater then the 90% of the expected price + @api.constrains("selling_price") + def _check_selling_price(self): + for record in self: + if ( + record.selling_price == 0 + or record.selling_price >= (90 / 100) * record.expected_price + ): + pass + else: + raise ValidationError( + "the selling price cannot be lower than 90'%' of the expected price." + ) + + # created the action for the sold button that will chage the state field of the property to sold + def action_sold(self): + for record in self: + if record.state == "canceled": + raise UserError( + "This Property couldn't be sold Because it is alredy canceled." + ) + elif record.state == "sold": + raise UserError("This property is alredy Sold.") + else: + record.state = "sold" + return True + + # created the action for the cancel button that will change the state of the property to canceled + def action_cancel(self): + for record in self: + if record.state == "sold": + raise UserError("This property is alredy sold. You can't cancel it.") + elif record.state == "canceled": + raise UserError("This property is alredy canceled.") + else: + record.state = "canceled" + return True diff --git a/estate/models/estate_property_offer.py b/estate/models/estate_property_offer.py new file mode 100644 index 0000000000..a867505cf4 --- /dev/null +++ b/estate/models/estate_property_offer.py @@ -0,0 +1,70 @@ +from odoo import api, models, fields +from dateutil.relativedelta import relativedelta +from datetime import date +from odoo.exceptions import UserError + + +class EstateProperty(models.Model): + _name = "estate.property.offer" + _description = "EstatePropertyOffer" + _order = "price desc" + # created the fields for the estate.property.offer model + name = fields.Char() + price = fields.Float() + status = fields.Selection([("accepted", "Accepted"), ("refused", "Refused")]) + partner_id = fields.Many2one("res.partner") + property_id = fields.Many2one("estate.property") + Validity = fields.Integer(default=7) + deadline = fields.Date(compute="_compute_deadline", inverse="_inverse_deadline") + property_type_id = fields.Many2one( + related="property_id.property_type_id", store=True + ) + + # created the sql constraints for only accepting positive value to price + _sql_constraints = [ + ( + "check_price", + "CHECK(price > 0.0)", + "An offer price must be strictly positive and Grater then Zero.", + ) + ] + + # created the compute function to compute deadline as create date + validity + @api.depends("Validity") + def _compute_deadline(self): + for record in self: + if record.create_date: + pass + else: + record.create_date = date.today() + record.deadline = record.create_date + relativedelta(days=record.Validity) + + @api.onchange("name") + def _onchange_offer(self): + if self.name: + self.property_id.state = "offer_recieved" + + # created the inverse function that will compute the validity when we manually update the deadline i.e deadline - creation date + def _inverse_deadline(self): + for record in self: + record.Validity = ( + record.deadline.toordinal() - record.create_date.toordinal() + ) + + # created the action for the accept button that will accept the offer from the offers for a property. + def action_accept(self): + # checks weather any other offer is alredy accepted. if offer is alredy accepted then it will raise an error + if self.property_id.buyer_id: + raise UserError("One of the Offer is alredy selected for this property.") + for record in self: + record.status = "accepted" + record.property_id.buyer_id = record.partner_id.id + record.property_id.selling_price = record.price + record.property_id.state = "offer_accepted" + return True + + # created the action for the refuse button that will refuse the offer from the offers for a property + def action_refuse(self): + for record in self: + record.status = "refused" + record.property_id.buyer_id = "" diff --git a/estate/models/estate_property_tag.py b/estate/models/estate_property_tag.py new file mode 100644 index 0000000000..836ec50284 --- /dev/null +++ b/estate/models/estate_property_tag.py @@ -0,0 +1,16 @@ +from odoo import models, fields + + +class EstateProperty(models.Model): + _name = "estate.property.tag" + _description = "EstatePropertyTag" + _order = "name" + + # created the fields for the estate.property.tag model + name = fields.Char(required=True) + color = fields.Integer(string="Color Index") + + # created the Sql constraints for unique tags + _sql_constraints = [ + ("unique_name", "unique(name)", "A property tag name must be unique.") + ] diff --git a/estate/models/estate_property_type.py b/estate/models/estate_property_type.py new file mode 100644 index 0000000000..03bdcd1a0c --- /dev/null +++ b/estate/models/estate_property_type.py @@ -0,0 +1,27 @@ +from odoo import models, fields, api + + +class EstateProperty(models.Model): + _name = "estate.property.type" + _description = "EstatePropertyType" + _order = "name" + + # created the fields for the estate.property.type model + name = fields.Char(required=True) + property_ids = fields.One2many("estate.property", "property_type_id") + sequence = fields.Integer( + "Sequence", default=1, help="Used to order stages. Lower is better." + ) + offer_ids = fields.One2many("estate.property.offer", "property_type_id") + offer_count = fields.Integer(compute="_compute_offer_count", string="Offer Count") + + # created the compute function to compute the offer count + @api.depends("offer_ids") + def _compute_offer_count(self): + for record in self: + record.offer_count = len(record.offer_ids) + + # created the sql constraints for unique property type + _sql_constraints = [ + ("unique_name", "unique(name)", "A property type name must be unique.") + ] diff --git a/estate/security/ir.model.access.csv b/estate/security/ir.model.access.csv new file mode 100644 index 0000000000..0c0b62b7fe --- /dev/null +++ b/estate/security/ir.model.access.csv @@ -0,0 +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 +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/static/description/realestate.png b/estate/static/description/realestate.png new file mode 100644 index 0000000000000000000000000000000000000000..cf7dd4d86ce1b310528c7ea55c9bb4049cb00a92 GIT binary patch literal 24799 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 literal 0 HcmV?d00001 diff --git a/estate/views/estate_menu.xml b/estate/views/estate_menu.xml new file mode 100644 index 0000000000..383606d387 --- /dev/null +++ b/estate/views/estate_menu.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/estate/views/estate_property_offer_view.xml b/estate/views/estate_property_offer_view.xml new file mode 100644 index 0000000000..e1cb019930 --- /dev/null +++ b/estate/views/estate_property_offer_view.xml @@ -0,0 +1,68 @@ + + + + Estate Property Offer + estate.property.offer + tree,form + [('property_type_id','=',active_id)] + + + estate.property.offer.tree.view + estate.property.offer + + + + + + + + + + + + + + + + + + estate.property.offer.form.view + estate.property.offer + +
+ + + + + + + + +
+ +
+
+
\ No newline at end of file diff --git a/estate/views/estate_property_tag_view.xml b/estate/views/estate_property_tag_view.xml new file mode 100644 index 0000000000..b2622e2bc3 --- /dev/null +++ b/estate/views/estate_property_tag_view.xml @@ -0,0 +1,20 @@ + + + + Property Tags + estate.property.tag + tree,form + + + + estate.property.tag.tree.view + estate.property.tag + + + + + + + \ No newline at end of file diff --git a/estate/views/estate_property_type_view.xml b/estate/views/estate_property_type_view.xml new file mode 100644 index 0000000000..859e6db485 --- /dev/null +++ b/estate/views/estate_property_type_view.xml @@ -0,0 +1,57 @@ + + + + Property Type + estate.property.type + tree,form + + + + estate.property.type.tree.view + estate.property.type + + + + + + + + + + estate.property.type.form.view + estate.property.type + +
+
+ +
+ +
+

+ +

+ + + + + + + + + + + + + + + +
+
+
+
+
+
\ No newline at end of file diff --git a/estate/views/estate_property_view.xml b/estate/views/estate_property_view.xml new file mode 100644 index 0000000000..2222ae21a7 --- /dev/null +++ b/estate/views/estate_property_view.xml @@ -0,0 +1,147 @@ + + + + + Properties + estate.property + tree,form + {'search_default_available': True} + + + + + + estate.property.tree.view + estate.property + + + + + + + + + + + + + + + + + + estate.property.form.view + estate.property + +
+
+ + + + +
+ +
+

+ +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+
+ + + estate.property.search + estate.property + + + + + + + + + + + + + + + + +
+ \ No newline at end of file From 8d72692572492e0f96cd96981798fd3b9fe2724e Mon Sep 17 00:00:00 2001 From: span-odoo Date: Wed, 18 Sep 2024 18:53:52 +0530 Subject: [PATCH 2/6] [ADD] real_estate: added inheritance in the module This commit extends the Real Estate module by incorporating three types of inheritance: Python inheritance: Applied Python inheritance to streamline code structure, promoting reusability and maintainability. Model inheritance: Utilized Odoo's model inheritance features to extend existing models, adding new fields and behaviors while preserving core functionalities. View inheritance: Employed view inheritance to customize and enrich user interfaces, ensuring seamless integration with existing elements. --- estate/__manifest__.py | 1 + estate/models/__init__.py | 1 + estate/models/estate_property.py | 9 +++++++- estate/models/estate_property_offer.py | 18 ++++++++++------ estate/models/res_users.py | 7 +++++++ estate/views/estate_property_type_view.xml | 14 +++++++------ estate/views/res_users.xml | 24 ++++++++++++++++++++++ estate_account/__init__.py | 1 + estate_account/__manifest__.py | 14 +++++++++++++ 9 files changed, 76 insertions(+), 13 deletions(-) create mode 100644 estate/models/res_users.py create mode 100644 estate/views/res_users.xml create mode 100644 estate_account/__init__.py create mode 100644 estate_account/__manifest__.py diff --git a/estate/__manifest__.py b/estate/__manifest__.py index 695124bc19..a6ce1f3d17 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -16,5 +16,6 @@ "views/estate_property_type_view.xml", "views/estate_property_tag_view.xml", "views/estate_menu.xml", + "views/res_users.xml", ], } diff --git a/estate/models/__init__.py b/estate/models/__init__.py index 001ba04f23..ba1107d1bd 100644 --- a/estate/models/__init__.py +++ b/estate/models/__init__.py @@ -3,3 +3,4 @@ from . import estate_property_type from . import estate_property_tag from . import estate_property_offer +from . import res_users diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 7deeb2c7b5..e12807534e 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -35,6 +35,7 @@ class EstateProperty(models.Model): ("canceled", "Canceled"), ], default="new", + readonly=True, ) active = fields.Boolean(default=True) saler_id = fields.Many2one( @@ -74,8 +75,8 @@ def _compute_area(self): def _compute_bestprice(self): for record in self: if record.offer_ids: + max1 = 0 for i in record.offer_ids: - max1 = 0 if i.price > max1: max1 = i.price record.best_price = max1 @@ -103,6 +104,12 @@ def _check_selling_price(self): "the selling price cannot be lower than 90'%' of the expected price." ) + # creating a delection check function that will check if the property is sold or not + @api.ondelete(at_uninstall=False) + def _check_property(self): + if any(user.state not in ("new", "canceled") for user in self): + raise UserError("You can't delete a property that is in process.") + # created the action for the sold button that will chage the state field of the property to sold def action_sold(self): for record in self: diff --git a/estate/models/estate_property_offer.py b/estate/models/estate_property_offer.py index a867505cf4..e9af23fa68 100644 --- a/estate/models/estate_property_offer.py +++ b/estate/models/estate_property_offer.py @@ -1,7 +1,7 @@ from odoo import api, models, fields from dateutil.relativedelta import relativedelta from datetime import date -from odoo.exceptions import UserError +from odoo.exceptions import UserError, ValidationError class EstateProperty(models.Model): @@ -39,11 +39,6 @@ def _compute_deadline(self): record.create_date = date.today() record.deadline = record.create_date + relativedelta(days=record.Validity) - @api.onchange("name") - def _onchange_offer(self): - if self.name: - self.property_id.state = "offer_recieved" - # created the inverse function that will compute the validity when we manually update the deadline i.e deadline - creation date def _inverse_deadline(self): for record in self: @@ -51,6 +46,17 @@ def _inverse_deadline(self): record.deadline.toordinal() - record.create_date.toordinal() ) + # create a create model that will make status as offer recieved when we create the offer + @api.model + def create(self, vals): + record = super().create(vals) + if record.price < record.property_id.best_price: + raise ValidationError( + "One of the offer is present which is best than your offer." + ) + record.property_id.state = "offer_recieved" + return record + # created the action for the accept button that will accept the offer from the offers for a property. def action_accept(self): # checks weather any other offer is alredy accepted. if offer is alredy accepted then it will raise an error diff --git a/estate/models/res_users.py b/estate/models/res_users.py new file mode 100644 index 0000000000..d8039f3d33 --- /dev/null +++ b/estate/models/res_users.py @@ -0,0 +1,7 @@ +from odoo import fields, models + + +class resuserinherit(models.Model): + _inherit = "res.users" + + property_ids = fields.One2many("estate.property", "saler_id") diff --git a/estate/views/estate_property_type_view.xml b/estate/views/estate_property_type_view.xml index 859e6db485..0f6b926eb0 100644 --- a/estate/views/estate_property_type_view.xml +++ b/estate/views/estate_property_type_view.xml @@ -23,13 +23,15 @@ estate.property.type
-
- -
+
+
+ +
+

diff --git a/estate/views/res_users.xml b/estate/views/res_users.xml new file mode 100644 index 0000000000..646693610a --- /dev/null +++ b/estate/views/res_users.xml @@ -0,0 +1,24 @@ + + + inherited.res.user + res.users + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/estate_account/__init__.py b/estate_account/__init__.py new file mode 100644 index 0000000000..0650744f6b --- /dev/null +++ b/estate_account/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/estate_account/__manifest__.py b/estate_account/__manifest__.py new file mode 100644 index 0000000000..f9d9f0c03f --- /dev/null +++ b/estate_account/__manifest__.py @@ -0,0 +1,14 @@ +{ + "name": "Estate Account", + "version": "0.1", + "license": "LGPL-3", + "category": "Estate_props", + "author": "sahilpanghal(span)", + "summary": "Estate Account", + "description": "Estate Account", + "application": True, + "installable": True, + "auto_install": True, + "depends": ["base", "account", "estate"], + "data": [], +} From 00416dd8830e2d0b51c5cbd1157981924592a4e6 Mon Sep 17 00:00:00 2001 From: span-odoo Date: Fri, 20 Sep 2024 14:55:31 +0530 Subject: [PATCH 3/6] [ADD] real_estate: added kanban view and polished the structure of the code refactor(real_estate): Enhance module with Kanban view and code optimizations. This commit introduces a Kanban view for improved visualization and workflow management in the Real Estate module. Additionally, the codebase has been polished for better readability and maintainability. --- estate/__manifest__.py | 1 + .../description/{realestate.png => icon.png} | Bin estate/views/estate_menu.xml | 2 +- estate/views/estate_property_view.xml | 49 +++++++++++++++++- estate_account/__manifest__.py | 1 - estate_account/models/__init__.py | 1 + estate_account/models/estate_property.py | 31 +++++++++++ estate_account/static/description/icon.png | Bin 0 -> 35666 bytes 8 files changed, 82 insertions(+), 3 deletions(-) rename estate/static/description/{realestate.png => icon.png} (100%) create mode 100644 estate_account/models/__init__.py create mode 100644 estate_account/models/estate_property.py create mode 100644 estate_account/static/description/icon.png diff --git a/estate/__manifest__.py b/estate/__manifest__.py index a6ce1f3d17..f449b1bd8f 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -8,6 +8,7 @@ "description": "Real estate module", "installable": True, "application": True, + "icons": ["static/description/realestate.png"], "depends": ["base"], "data": [ "security/ir.model.access.csv", diff --git a/estate/static/description/realestate.png b/estate/static/description/icon.png similarity index 100% rename from estate/static/description/realestate.png rename to estate/static/description/icon.png diff --git a/estate/views/estate_menu.xml b/estate/views/estate_menu.xml index 383606d387..b79af9975d 100644 --- a/estate/views/estate_menu.xml +++ b/estate/views/estate_menu.xml @@ -2,7 +2,7 @@ + web_icon="estate,static/description/icon.png"> diff --git a/estate/views/estate_property_view.xml b/estate/views/estate_property_view.xml index 2222ae21a7..ecdd39f1e6 100644 --- a/estate/views/estate_property_view.xml +++ b/estate/views/estate_property_view.xml @@ -5,7 +5,7 @@ Properties estate.property - tree,form + tree,form,kanban {'search_default_available': True} @@ -121,6 +121,53 @@ + + + + estate.property.view.kanban + estate.property + + + + + + +
+ + + +
+ Expected Price: + +
+
+ +
+
+ + + Best Price: + + +
+
+ + + Selling Price: + + +
+ +
+ +
+
+
+
+
+
+ estate.property.search diff --git a/estate_account/__manifest__.py b/estate_account/__manifest__.py index f9d9f0c03f..39536e7b4f 100644 --- a/estate_account/__manifest__.py +++ b/estate_account/__manifest__.py @@ -6,7 +6,6 @@ "author": "sahilpanghal(span)", "summary": "Estate Account", "description": "Estate Account", - "application": True, "installable": True, "auto_install": True, "depends": ["base", "account", "estate"], diff --git a/estate_account/models/__init__.py b/estate_account/models/__init__.py new file mode 100644 index 0000000000..5e1963c9d2 --- /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 0000000000..6e6968bfa1 --- /dev/null +++ b/estate_account/models/estate_property.py @@ -0,0 +1,31 @@ +from odoo import models, Command + + +class EstateProperty(models.Model): + _inherit = "estate.property" + + def action_sold(self): + for record in self: + values_property = { + "partner_id": record.buyer_id.id, + "name": record.name, + "move_type": "out_invoice", + "line_ids": [ + Command.create( + { + "name": record.name, + "quantity": 1, + "price_unit": 0.06 * record.selling_price, + } + ), + Command.create( + { + "name": "administrative_fees", + "quantity": 1, + "price_unit": 100, + } + ), + ], + } + self.env["account.move"].create(values_property) + return super().action_sold() diff --git a/estate_account/static/description/icon.png b/estate_account/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..8342f39e95a6a53ea3330816c5cca2fee9b6a07f GIT binary patch literal 35666 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 Date: Mon, 23 Sep 2024 18:41:25 +0530 Subject: [PATCH 4/6] [ADD] estate: defined the module data feat(real_estate): Add essential master and demo data for module setup and testing. This commit populates the Real Estate module with crucial master data, such as property types, offer states, and default settings. Additionally, demo data is included to facilitate testing and showcase the module's functionalities in action. --- estate/__manifest__.py | 4 ++ estate/data/estate.property.type.csv | 5 ++ estate/demo/estate_property_demo.xml | 86 +++++++++++++++++++++++++++ estate/views/estate_property_view.xml | 2 +- 4 files changed, 96 insertions(+), 1 deletion(-) create mode 100644 estate/data/estate.property.type.csv create mode 100644 estate/demo/estate_property_demo.xml diff --git a/estate/__manifest__.py b/estate/__manifest__.py index f449b1bd8f..bc4407ef27 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -12,6 +12,7 @@ "depends": ["base"], "data": [ "security/ir.model.access.csv", + "data/estate.property.type.csv", "views/estate_property_view.xml", "views/estate_property_offer_view.xml", "views/estate_property_type_view.xml", @@ -19,4 +20,7 @@ "views/estate_menu.xml", "views/res_users.xml", ], + "demo": [ + "demo/estate_property_demo.xml", + ], } diff --git a/estate/data/estate.property.type.csv b/estate/data/estate.property.type.csv new file mode 100644 index 0000000000..83b427f5ae --- /dev/null +++ b/estate/data/estate.property.type.csv @@ -0,0 +1,5 @@ +id,name +id1,Residential +id2,Commercial +id3,Industrial +id4,Land \ No newline at end of file diff --git a/estate/demo/estate_property_demo.xml b/estate/demo/estate_property_demo.xml new file mode 100644 index 0000000000..e4ff9fdb88 --- /dev/null +++ b/estate/demo/estate_property_demo.xml @@ -0,0 +1,86 @@ + + + + + Big Villa + new + A nice and big villa + 12345 + 2020-02-02 + 1600000 + 6 + 100 + 4 + True + True + 100000 + south + + + + + Trailer home + canceled + Home in a trailer park + 54321 + 1970-01-01 + 100000 + 120000 + 1 + 10 + 4 + False + + + + + Luxury villa + new + A very nice villa + 12345 + 2020-02-02 + 100 + 6 + 100 + True + True + 100000 + south + 4 + + + + + + + + 10000 + + + + + + + 1500000 + 14 + + + + + + 1500001 + 14 + + + + + + \ No newline at end of file diff --git a/estate/views/estate_property_view.xml b/estate/views/estate_property_view.xml index ecdd39f1e6..94f3bdeb2d 100644 --- a/estate/views/estate_property_view.xml +++ b/estate/views/estate_property_view.xml @@ -43,7 +43,7 @@ invisible="state in ['sold','canceled']"> - +

From 99a0673fcd4b2ee482d39acc0d1d2ab77401e279 Mon Sep 17 00:00:00 2001 From: span-odoo Date: Wed, 25 Sep 2024 18:21:22 +0530 Subject: [PATCH 5/6] [ADD] estate: created the reports for properties and created the report template feat(real_estate): Implement property reports with inheritance for enhanced functionality. This commit introduces a new reporting system for properties in the Real Estate module. It includes a base report template for displaying property details and utilizes inheritance to create specialized reports for different property types. This approach improves code organization and allows for flexible report customization in the future. --- estate/__manifest__.py | 3 + .../report/estate_property_offer_report.xml | 26 ++++++++ .../estate_property_offer_report_template.xml | 61 +++++++++++++++++++ .../estate_property_offer_subTemplate.xml | 47 ++++++++++++++ estate/views/estate_property_view.xml | 3 +- estate_account/__manifest__.py | 2 +- .../estate_account_property_invoice.xml | 15 +++++ 7 files changed, 155 insertions(+), 2 deletions(-) create mode 100644 estate/report/estate_property_offer_report.xml create mode 100644 estate/report/estate_property_offer_report_template.xml create mode 100644 estate/report/estate_property_offer_subTemplate.xml create mode 100644 estate_account/report/estate_account_property_invoice.xml diff --git a/estate/__manifest__.py b/estate/__manifest__.py index bc4407ef27..dfc947537b 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -13,6 +13,9 @@ "data": [ "security/ir.model.access.csv", "data/estate.property.type.csv", + "report/estate_property_offer_report.xml", + "report/estate_property_offer_subTemplate.xml", + "report/estate_property_offer_report_template.xml", "views/estate_property_view.xml", "views/estate_property_offer_view.xml", "views/estate_property_type_view.xml", diff --git a/estate/report/estate_property_offer_report.xml b/estate/report/estate_property_offer_report.xml new file mode 100644 index 0000000000..05d73aa2df --- /dev/null +++ b/estate/report/estate_property_offer_report.xml @@ -0,0 +1,26 @@ + + + + Estate Reports + estate.property + qweb-pdf + estate.report_property_offers + estate.report_property_offers + 'Estate Property - %s' % (object.name or + 'Attendee').replace('/','') + + report + + + Sales Person Property offers + res.users + qweb-pdf + estate.report_property_offers_users + estate.report_property_offers_users + 'Estate Property - %s' % (object.name or + 'Attendee').replace('/','') + + report + + + \ No newline at end of file diff --git a/estate/report/estate_property_offer_report_template.xml b/estate/report/estate_property_offer_report_template.xml new file mode 100644 index 0000000000..db34d3c9d2 --- /dev/null +++ b/estate/report/estate_property_offer_report_template.xml @@ -0,0 +1,61 @@ + + + + + \ No newline at end of file diff --git a/estate/report/estate_property_offer_subTemplate.xml b/estate/report/estate_property_offer_subTemplate.xml new file mode 100644 index 0000000000..c209de2ee0 --- /dev/null +++ b/estate/report/estate_property_offer_subTemplate.xml @@ -0,0 +1,47 @@ + + + + + + \ No newline at end of file diff --git a/estate/views/estate_property_view.xml b/estate/views/estate_property_view.xml index 94f3bdeb2d..f97c0b08ad 100644 --- a/estate/views/estate_property_view.xml +++ b/estate/views/estate_property_view.xml @@ -43,7 +43,8 @@ invisible="state in ['sold','canceled']"> - +
diff --git a/estate_account/__manifest__.py b/estate_account/__manifest__.py index 39536e7b4f..9e175de13a 100644 --- a/estate_account/__manifest__.py +++ b/estate_account/__manifest__.py @@ -9,5 +9,5 @@ "installable": True, "auto_install": True, "depends": ["base", "account", "estate"], - "data": [], + "data": ["report/estate_account_property_invoice.xml"], } diff --git a/estate_account/report/estate_account_property_invoice.xml b/estate_account/report/estate_account_property_invoice.xml new file mode 100644 index 0000000000..6813c979e8 --- /dev/null +++ b/estate_account/report/estate_account_property_invoice.xml @@ -0,0 +1,15 @@ + + + + \ No newline at end of file From bdf19312328cc7c1342daa7c731da8f6d58278de Mon Sep 17 00:00:00 2001 From: span-odoo Date: Fri, 27 Sep 2024 17:41:30 +0530 Subject: [PATCH 6/6] [ADD] estate: added access rights in real estate module feat(real_estate): Implement robust access control mechanisms for enhanced security. This commit introduces a refined access control system to the Real Estate module, ensuring data protection and appropriate user permissions. It includes: Granular access rights for different user groups, controlling actions like property creation, modification, and deletion. Clear separation of responsibilities between salespersons and managers, limiting access based on roles. Integration with Odoo's existing security features to leverage established access control mechanisms. --- estate/__manifest__.py | 3 ++- estate/demo/estate_property_demo.xml | 2 +- estate/models/estate_property.py | 15 ++++--------- estate/models/estate_property_offer.py | 6 ++---- estate/models/estate_property_tag.py | 2 +- estate/models/estate_property_type.py | 2 +- .../report/estate_property_offer_report.xml | 2 +- .../estate_property_offer_report_template.xml | 2 +- .../estate_property_offer_subTemplate.xml | 2 +- estate/security/ir.model.access.csv | 16 ++++++++++---- estate/security/security.xml | 21 +++++++++++++++++++ estate/views/estate_menu.xml | 3 +-- estate/views/estate_property_offer_view.xml | 9 +------- estate/views/estate_property_tag_view.xml | 2 +- estate/views/estate_property_type_view.xml | 13 ++++++------ estate/views/estate_property_view.xml | 3 ++- estate/views/res_users.xml | 2 +- estate_account/models/estate_property.py | 2 +- .../estate_account_property_invoice.xml | 2 +- 19 files changed, 61 insertions(+), 48 deletions(-) create mode 100644 estate/security/security.xml diff --git a/estate/__manifest__.py b/estate/__manifest__.py index dfc947537b..b120b9454b 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -2,7 +2,7 @@ "name": "Real_estate", "version": "0.1", "license": "LGPL-3", - "category": "Estate_props", + "category": "Real Estate/Brokerage", "author": "sahilpanghal(span)", "summary": "Real estate module", "description": "Real estate module", @@ -11,6 +11,7 @@ "icons": ["static/description/realestate.png"], "depends": ["base"], "data": [ + "security/security.xml", "security/ir.model.access.csv", "data/estate.property.type.csv", "report/estate_property_offer_report.xml", diff --git a/estate/demo/estate_property_demo.xml b/estate/demo/estate_property_demo.xml index e4ff9fdb88..d6b7683089 100644 --- a/estate/demo/estate_property_demo.xml +++ b/estate/demo/estate_property_demo.xml @@ -83,4 +83,4 @@ - \ No newline at end of file + diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index e12807534e..d10f2aba0a 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -1,5 +1,5 @@ -from odoo import api, models, fields from dateutil.relativedelta import relativedelta +from odoo import api, fields, models from odoo.exceptions import UserError, ValidationError @@ -94,12 +94,7 @@ def _onchange_garden(self): @api.constrains("selling_price") def _check_selling_price(self): for record in self: - if ( - record.selling_price == 0 - or record.selling_price >= (90 / 100) * record.expected_price - ): - pass - else: + if record.selling_price <= (90 / 100) * record.expected_price: raise ValidationError( "the selling price cannot be lower than 90'%' of the expected price." ) @@ -113,12 +108,10 @@ def _check_property(self): # created the action for the sold button that will chage the state field of the property to sold def action_sold(self): for record in self: - if record.state == "canceled": + if record.state in ["sold", "canceled"]: raise UserError( - "This Property couldn't be sold Because it is alredy canceled." + "This property is alredy sold or canceled. You can't sell it." ) - elif record.state == "sold": - raise UserError("This property is alredy Sold.") else: record.state = "sold" return True diff --git a/estate/models/estate_property_offer.py b/estate/models/estate_property_offer.py index e9af23fa68..e8ec2d166b 100644 --- a/estate/models/estate_property_offer.py +++ b/estate/models/estate_property_offer.py @@ -1,6 +1,6 @@ -from odoo import api, models, fields from dateutil.relativedelta import relativedelta from datetime import date +from odoo import api, fields, models from odoo.exceptions import UserError, ValidationError @@ -33,9 +33,7 @@ class EstateProperty(models.Model): @api.depends("Validity") def _compute_deadline(self): for record in self: - if record.create_date: - pass - else: + if not record.create_date: record.create_date = date.today() record.deadline = record.create_date + relativedelta(days=record.Validity) diff --git a/estate/models/estate_property_tag.py b/estate/models/estate_property_tag.py index 836ec50284..1f34087747 100644 --- a/estate/models/estate_property_tag.py +++ b/estate/models/estate_property_tag.py @@ -1,4 +1,4 @@ -from odoo import models, fields +from odoo import fields, models class EstateProperty(models.Model): diff --git a/estate/models/estate_property_type.py b/estate/models/estate_property_type.py index 03bdcd1a0c..0a32c3bab3 100644 --- a/estate/models/estate_property_type.py +++ b/estate/models/estate_property_type.py @@ -1,4 +1,4 @@ -from odoo import models, fields, api +from odoo import api, fields, models class EstateProperty(models.Model): diff --git a/estate/report/estate_property_offer_report.xml b/estate/report/estate_property_offer_report.xml index 05d73aa2df..5bb6c1e2e4 100644 --- a/estate/report/estate_property_offer_report.xml +++ b/estate/report/estate_property_offer_report.xml @@ -23,4 +23,4 @@ report - \ No newline at end of file + diff --git a/estate/report/estate_property_offer_report_template.xml b/estate/report/estate_property_offer_report_template.xml index db34d3c9d2..f97dc8853a 100644 --- a/estate/report/estate_property_offer_report_template.xml +++ b/estate/report/estate_property_offer_report_template.xml @@ -58,4 +58,4 @@ - \ No newline at end of file + diff --git a/estate/report/estate_property_offer_subTemplate.xml b/estate/report/estate_property_offer_subTemplate.xml index c209de2ee0..eb88457eda 100644 --- a/estate/report/estate_property_offer_subTemplate.xml +++ b/estate/report/estate_property_offer_subTemplate.xml @@ -44,4 +44,4 @@ - \ No newline at end of file + diff --git a/estate/security/ir.model.access.csv b/estate/security/ir.model.access.csv index 0c0b62b7fe..e2ea7ee912 100644 --- a/estate/security/ir.model.access.csv +++ b/estate/security/ir.model.access.csv @@ -1,5 +1,13 @@ 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 +estate.access_estate_property_user,access_estate_property.user,estate.model_estate_property,estate.estate_group_user,1,1,1,0 +estate.access_estate_property_tag_user,access_estate_property_tag_user,estate.model_estate_property_tag,estate.estate_group_user,1,0,0,0 +estate.access_estate_property_type_user,access_estate_property_type_user,estate.model_estate_property_type,estate.estate_group_user,1,0,0,0 +estate.access_estate_property_offer_user,access_estate_property_offer_user,estate.model_estate_property_offer,estate.estate_group_user,1,1,1,1 +estate.access_estate_property_manager,access_estate_property.manager,estate.model_estate_property,estate.estate_group_manager,1,1,1,1 +estate.access_estate_property_tag_manager,access_estate_property_tag_manager,estate.model_estate_property_tag,estate.estate_group_manager,1,1,1,1 +estate.access_estate_property_type_manager,access_estate_property_type_manager,estate.model_estate_property_type,estate.estate_group_manager,1,1,1,1 +estate.access_estate_property_offer_manager,access_estate_property_offer_manager,estate.model_estate_property_offer,estate.estate_group_manager,1,1,1,1 +estate.access_estate_property,access_estate_property,estate.model_estate_property,base.group_user,1,0,0,0 +estate.access_estate_property_type,access_estate_property_type,estate.model_estate_property_type,base.group_user,1,0,0,0 +estate.access_estate_property_tag,access_estate_property_tag,estate.model_estate_property_tag,base.group_user,1,0,0,0 +estate.access_estate_property_offer,access_estate_property_offer,estate.model_estate_property_offer,base.group_user,1,0,0,0 \ No newline at end of file diff --git a/estate/security/security.xml b/estate/security/security.xml new file mode 100644 index 0000000000..1f891d9b9e --- /dev/null +++ b/estate/security/security.xml @@ -0,0 +1,21 @@ + + + Agent + + + + Manager + + + + + Limit estate user + + + + + + + ['|',('saler_id.id','=',user.id),('saler_id.id','=',False)] + + diff --git a/estate/views/estate_menu.xml b/estate/views/estate_menu.xml index b79af9975d..5404cbbde2 100644 --- a/estate/views/estate_menu.xml +++ b/estate/views/estate_menu.xml @@ -11,5 +11,4 @@ - - \ No newline at end of file + diff --git a/estate/views/estate_property_offer_view.xml b/estate/views/estate_property_offer_view.xml index e1cb019930..07af305387 100644 --- a/estate/views/estate_property_offer_view.xml +++ b/estate/views/estate_property_offer_view.xml @@ -47,13 +47,6 @@ estate.property.offer
- @@ -65,4 +58,4 @@ - \ No newline at end of file + diff --git a/estate/views/estate_property_tag_view.xml b/estate/views/estate_property_tag_view.xml index b2622e2bc3..131d30753c 100644 --- a/estate/views/estate_property_tag_view.xml +++ b/estate/views/estate_property_tag_view.xml @@ -17,4 +17,4 @@ - \ No newline at end of file + diff --git a/estate/views/estate_property_type_view.xml b/estate/views/estate_property_type_view.xml index 0f6b926eb0..62bd062f72 100644 --- a/estate/views/estate_property_type_view.xml +++ b/estate/views/estate_property_type_view.xml @@ -24,13 +24,12 @@ -
-
- -
+
+

diff --git a/estate/views/estate_property_view.xml b/estate/views/estate_property_view.xml index f97c0b08ad..14dc1e5157 100644 --- a/estate/views/estate_property_view.xml +++ b/estate/views/estate_property_view.xml @@ -192,4 +192,5 @@ - \ No newline at end of file + + \ No newline at end of file diff --git a/estate/views/res_users.xml b/estate/views/res_users.xml index 646693610a..abe481122a 100644 --- a/estate/views/res_users.xml +++ b/estate/views/res_users.xml @@ -21,4 +21,4 @@ - \ No newline at end of file + diff --git a/estate_account/models/estate_property.py b/estate_account/models/estate_property.py index 6e6968bfa1..57dc8340f5 100644 --- a/estate_account/models/estate_property.py +++ b/estate_account/models/estate_property.py @@ -1,4 +1,4 @@ -from odoo import models, Command +from odoo import Command, models class EstateProperty(models.Model): diff --git a/estate_account/report/estate_account_property_invoice.xml b/estate_account/report/estate_account_property_invoice.xml index 6813c979e8..bfb2220a5f 100644 --- a/estate_account/report/estate_account_property_invoice.xml +++ b/estate_account/report/estate_account_property_invoice.xml @@ -12,4 +12,4 @@ - \ No newline at end of file +