Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ADD] estate: create real estate module with basic property managemen… #136

Draft
wants to merge 3 commits into
base: 17.0
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .idea/.gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions .idea/inspectionProfiles/profiles_settings.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions .idea/material_theme_project_new.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions .idea/modules.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions .idea/tutorials.iml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions .idea/vcs.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions estate/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import models
22 changes: 22 additions & 0 deletions estate/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"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,
"icons": ["static/description/realestate.png"],
"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",
"views/res_users.xml",
],
}
6 changes: 6 additions & 0 deletions estate/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# 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
from . import res_users
135 changes: 135 additions & 0 deletions estate/models/estate_property.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
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",
readonly=True,
)
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:
max1 = 0
for i in record.offer_ids:
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."
)

# 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:
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
76 changes: 76 additions & 0 deletions estate/models/estate_property_offer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
from odoo import api, models, fields
from dateutil.relativedelta import relativedelta
from datetime import date
from odoo.exceptions import UserError, ValidationError


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)

# 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()
)

# 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
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 = ""
16 changes: 16 additions & 0 deletions estate/models/estate_property_tag.py
Original file line number Diff line number Diff line change
@@ -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.")
]
27 changes: 27 additions & 0 deletions estate/models/estate_property_type.py
Original file line number Diff line number Diff line change
@@ -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.")
]
7 changes: 7 additions & 0 deletions estate/models/res_users.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from odoo import fields, models


class resuserinherit(models.Model):
_inherit = "res.users"

property_ids = fields.One2many("estate.property", "saler_id")
5 changes: 5 additions & 0 deletions estate/security/ir.model.access.csv
Original file line number Diff line number Diff line change
@@ -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
Binary file added estate/static/description/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
15 changes: 15 additions & 0 deletions estate/views/estate_menu.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- created the menu items for the model to show inside the module -->
<menuitem id="test_menu_root" name="Real Estate"
web_icon="estate,static/description/icon.png">
<menuitem id="test_first_level_menu" name="Advertisments">
<menuitem id="test_model_menu_action" action="estate_property_action" />
</menuitem>
<menuitem id="test_second_level_menu" name="Settings">
<menuitem id="test_model_menu_action2" action="estate_property_type_action" />
<menuitem id="test_model_menu_action3" action="estate_property_tag_action" />
</menuitem>
</menuitem>

</odoo>
Loading