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: adding a new real estate module #317

Draft
wants to merge 8 commits into
base: 18.0
Choose a base branch
from
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
13 changes: 13 additions & 0 deletions estate/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
'name': "Real Estate",
'depends': ['base'],
'application': True,
'data':[
'security/ir.model.access.csv',
'views/estate_property_views.xml',
'views/estate_property_type_menu.xml',
'views/estate_property_tag_menu.xml',
'views/estate_menus.xml',
],
'license': 'LGPL-3'
}
4 changes: 4 additions & 0 deletions estate/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from . import estate_property_offer
from . import estate_property_tag
from . import estate_property_type
from . import estate_property
126 changes: 126 additions & 0 deletions estate/models/estate_property.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
from odoo import api, fields, models
from odoo.exceptions import ValidationError
from dateutil.relativedelta import relativedelta
from odoo.tools import float_compare, float_is_zero

class PropertyPlan(models.Model):
_name = "estate.property"
_description = "Estate property tables"

@api.depends("living_area", "garden_area")
def _compute_total_area(self):
for record in self:
record.total_area = record.living_area + record.garden_area

@api.depends("offer_ids.price")
def _computer_best_price(self):
for record in self:
if record.offer_ids:
record.best_price = max(record.offer_ids.mapped("price"))
else:
record.best_price = 0.0

@api.onchange("garden")
def _onchange_garden(self):
if self.garden:
self.garden_area = 10
self.garden_orientation = "north"
else:
self.garden_area = 00
self.garden_orientation = ""

def action_property_cancelled(self):
for record in self:
if record.state == "cancelled":
raise ValidationError("Already cancelled.")
elif record.state == "sold":
raise ValidationError("Cannot cancel a sold property!")
else:
record.state = "cancelled"
return True

def action_property_sold(self):
for record in self:
if record.state == "sold":
raise ValidationError("Already sold.")
elif record.state == "cancelled":
raise ValidationError("Cannot sell a cancelled property!")
else:
record.state = "sold"
return True

_sql_constraints = [
(
"check_expected_price",
"CHECK(expected_price > 0)",
"Expected price must be strictly positive!",
),
(
"check_selling_price",
"CHECK(selling_price > 0)",
"Selling price must be positive!",
),
]

@api.constrains("selling_price", "expected_price")
def check_selling_price(self):
for price in self:
if float_is_zero(price.selling_price, 2) != 1:
if (
float_compare(price.expected_price * 0.9, price.selling_price, 2)
== 1
):
raise ValidationError(
"The selling price must be at least 90% of the expected price!You must reduce the expected price if you want to accept this offer."
)

name = fields.Char(string="Title", required=True)
description = fields.Text(string="Description")
postcode = fields.Char(string="Postcode")
date_availability = fields.Date(
string="Available From",
copy=False,
default=(fields.Date.today() + relativedelta(months=+3)),
)
expected_price = fields.Float(string="Expected Price", required=True)
selling_price = fields.Float(string="Selling Price", readonly=True)
bedrooms = fields.Integer(string="Bedrooms", default=2)
living_area = fields.Integer(string="Living Area (sqm)")
facades = fields.Integer(string="Facades")
garage = fields.Boolean(string="Garage")
garden = fields.Boolean(string="Garden")
garden_area = fields.Integer(string="Garden Area (sqm)")
active = fields.Boolean(string="Active", default=True)
state = fields.Selection(
string="State",
required=True,
default="new",
copy=False,
selection=[
("new", "New"),
("offer_received", "Offer Received"),
("offer_accepted", "Offer Accepted"),
("sold", "Sold"),
("cancelled", "Cancelled"),
],
help="Used to decide the state of Garden",
)
garden_orientation = fields.Selection(
string="Garden Orientation",
selection=[
("north", "North"),
("south", "South"),
("east", "East"),
("west", "West"),
],
help="Used to decide the direction of Garden",
)
property_type_id = fields.Many2one("estate.property.type", string="Property Type")
tag = fields.Many2many("estate.property.tag", string="Property Tag")
partner_id = fields.Many2one("res.partner", string="Buyer", copy=False)
sales_person = fields.Many2one(
"res.users", string="Salesman", default=lambda self: self.env.user
)
offer_ids = fields.One2many("estate.property.offer", "property_id", string="Price")
total_area = fields.Float(string="Total Area (sqm)", compute="_compute_total_area")
best_price = fields.Float(string="Best Offer", compute="_computer_best_price")
75 changes: 75 additions & 0 deletions estate/models/estate_property_offer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
from odoo import api, fields, models
from odoo.exceptions import ValidationError
from dateutil.relativedelta import relativedelta

class EstatePropertyOffer(models.Model):
_name = "estate.property.offer"
_description = "Offer list for estate properties"

@api.depends("validity", "create_date")
def _compute_date_deadline(self):
for record in self:
if record.create_date:
record.date_deadline = record.create_date + relativedelta(
days=record.validity
)
else:
record.date_deadline = fields.Date.today() + relativedelta(
days=record.validity
)

def _inverse_date_deadline(self):
for record in self:
if record.date_deadline:
record.validity = (record.date_deadline - fields.Date.today()).days
else:
record.validity = 7

def action_confirm(self):
for record in self:
if record.status == "accepted":
raise ValidationError("Already accepted.")
else:
record.status = "accepted"
record.property_id.partner_id = record.partner_id
record.property_id.selling_price = record.price
other_offers = self.search(
[
("property_id", "=", record.property_id.id),
("id", "!=", record.id),
"|",
("status", "=", ""),
("status", "=", "accepted"),
]
)
other_offers.write({"status": "refused"})
return True

def action_refuse(self):
for record in self:
if record.status == "refused":
raise ValidationError("Already refused.")
else:
record.status = "refused"
return True

_sql_constraints = [
("check_price", "CHECK(price > 0)", "Price must be positive."),
]

price = fields.Float(string="Price")
status = fields.Selection(
string="Status",
copy=False,
selection=[("accepted", "Accepted"), ("refused", "Refused")],
readonly=True,
help="Status of the offer",
)
partner_id = fields.Many2one("res.partner", string="Partner", required=True)
property_id = fields.Many2one("estate.property", string="Property", required=True)
validity = fields.Integer(string="Validity", default="7")
date_deadline = fields.Date(
string="Deadline",
compute="_compute_date_deadline",
inverse="_inverse_date_deadline",
)
11 changes: 11 additions & 0 deletions estate/models/estate_property_tag.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from odoo import fields, models

class EstatePropertyTag(models.Model):
_name = "estate.property.tag"
_description = "Estate Property Tags"

_sql_constraints = [
("name_unique", "unique(name)", "Name must be unique!"),
]

name = fields.Char(string="Tag", required=True)
7 changes: 7 additions & 0 deletions estate/models/estate_property_type.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from odoo import fields, models

class EstatePropertyType(models.Model):
_name = "estate.property.type"
_description = "Estate Property Types"

name = fields.Char(string="Property Type", required=True)
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,estate.access_estate_property,estate.model_estate_property,base.group_user,1,1,1,1
estate.access_estate_property_type,estate.access_estate_property_type,estate.model_estate_property_type,base.group_user,1,1,1,1
estate.access_estate_property_tag,estate.access_estate_property_tag,estate.model_estate_property_tag,base.group_user,1,1,1,1
estate.access_estate_property_offer,estate.access_estate_property_offer,estate.model_estate_property_offer,base.group_user,1,1,1,1
11 changes: 11 additions & 0 deletions estate/views/estate_menus.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<odoo>
<menuitem id="estate_property_root_menu" name="Estate Property">
<menuitem id="estate_property_advertisements_menu" name="Advertisements">
<menuitem id="estate_property_menu_action" name="Advertisements" action="estate_property_action"/>
</menuitem>
<menuitem id="estate_property_settings_menu" name="Settings">
<menuitem id="estate_property_type_menu" name="Property Types" action="estate_property_type_action"/>
<menuitem id="estate_property_tag_menu" name="Property Tags" action="estate_property_tag_action"/>
</menuitem>
</menuitem>
</odoo>
8 changes: 8 additions & 0 deletions estate/views/estate_property_tag_menu.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="estate_property_tag_action" model="ir.actions.act_window">
<field name="name">Estate Property Tag</field>
<field name="res_model">estate.property.tag</field>
<field name="view_mode">list,form</field>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the purpose of adding view_mode here?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The "view_mode" parameter specifies the views to be displayed for the model. In this case, it includes both the list and form views, through which the model will be presented to the user.

</record>
</odoo>
25 changes: 25 additions & 0 deletions estate/views/estate_property_type_menu.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="estate_property_type_action" model="ir.actions.act_window">
<field name="name">Estate Property Type</field>
<field name="res_model">estate.property.type</field>
<field name="view_mode">list,form</field>
</record>
<record id="estate_property_type_view_form" model="ir.ui.view">
<field name="name">estate.property.type.view.form</field>
<field name="model">estate.property.type</field>
<field name="arch" type="xml">
<form string="Estate Property Type Form">
<sheet>
<group>
<group>
<h1 class="mb32">
<field name="name" class="mb16" />
</h1>
</group>
</group>
</sheet>
</form>
</field>
</record>
</odoo>
Loading