Skip to content
Draft
Show file tree
Hide file tree
Changes from 9 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
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
19 changes: 19 additions & 0 deletions estate/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"name": "Real Estate",
"version": "1.0.0",
"depends": ["base"],
"author": "Amr (amgom)",
Comment thread
amgom-odoo marked this conversation as resolved.
Outdated
"category": "Real Estate",
"description": """
This is a real estate management module that allows users to manage properties, agents, and clients.
""",
"application": True,
"data": [
"security/ir.model.access.csv",
"views/estate_property_menus.xml",
"views/estate_property_offer_views.xml",
"views/estate_property_tag_views.xml",
"views/estate_property_type_views.xml",
"views/estate_property_views.xml",
],
}
1 change: 1 addition & 0 deletions estate/models/__init__.py
Comment thread
amgom-odoo marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import estate, offer, property_tag, property_type
122 changes: 122 additions & 0 deletions estate/models/estate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
from dateutil.relativedelta import relativedelta

from odoo import api, fields, models
from odoo.exceptions import UserError, ValidationError
from odoo.tools.float_utils import float_compare, float_is_zero


class Estate(models.Model):
_name = "estate_property"
Comment thread
amgom-odoo marked this conversation as resolved.
Outdated
_description = "Real Estate"

name = fields.Char(string="Name", required=True)
description = fields.Text(string="Description")
postcode = fields.Char(string="Postcode")
date_availability = fields.Date(
string="Available From",
default=fields.Date.today() + relativedelta(months=3),
copy=False,
)
expected_price = fields.Float(string="Expected Price", required=True)
selling_price = fields.Float(string="Selling price", readonly=True, copy=False)
bedrooms = fields.Integer(string="Bedrooms", default=2)
living_area = fields.Integer(string="Living Area")
facades = fields.Integer(string="Facades")
garage = fields.Boolean(string="Garage")
garden = fields.Boolean(string="Garden")
garden_area = fields.Integer(string="Garden Area")
garden_orientation = fields.Selection(
string="Garden Orientation",
selection=[
("north", "North"),
("east", "East"),
("west", "West"),
("south", "South"),
],
)
active = fields.Boolean(string="Active", default=True)
state = fields.Selection(
string="Status",
selection=[
("new", "New"),
("offer_received", "Offer Received"),
("offer_accepted", "Offer Accepted"),
("sold", "Sold"),
("cancelled", "Cancelled"),
],
required=True,
copy=False,
default="new",
)
type_id = fields.Many2one(
string="Property Type",
comodel_name="estate_property_type",
)
salesman_id = fields.Many2one(
string="Salesman",
comodel_name="res.users",
default=lambda self: self.env.user,
)
buyer_id = fields.Many2one(string="Buyer", comodel_name="res.partner", copy=False)
tag_ids = fields.Many2many(string="Tags", comodel_name="estate_property_tag")
offer_ids = fields.One2many(
string="Offers",
comodel_name="estate_property_offer",
inverse_name="property_id",
)
total_area = fields.Float(string="Total Area", compute="_compute_total_area")
best_offer = fields.Float(string="Best Offer", compute="_compute_best_offer")

_positive_expected_price = models.Constraint(
"CHECK(expected_price > 0)",
"Expected price should be strictly positive",
)
_positive_selling_price = models.Constraint(
"CHECK(selling_price >= 0)",
"Selling price should be positive",
)

@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 _compute_best_offer(self):
self.best_offer = (
max(self.offer_ids.mapped("price")) if len(self.offer_ids) else 0.0
)

@api.onchange("garden")
def _on_change_garden(self):
self.garden_area = 10 if self.garden else 0
self.garden_orientation = "north" if self.garden else None

def action_sell_property(self):
for property in self:
if property.state == "cancelled":
raise UserError("Cancelled properties cannot be sold")
Comment thread
amgom-odoo marked this conversation as resolved.
Outdated
property.state = "sold"

return True
Comment thread
amgom-odoo marked this conversation as resolved.
Outdated

def action_cancel_property(self):
for property in self:
if property.state == "sold":
raise UserError("Sold properties cannot be cancelled")
property.state = "cancelled"

return True

@api.constrains("expected_price", "selling_price")
def _check_selling_price_limit(self):
for property in self:
if not float_is_zero(property.selling_price, precision_digits=5):
limit = 0.9 * property.expected_price
if (
float_compare(property.selling_price, limit, precision_digits=5)
== -1
):
raise ValidationError(
"The selling price should not be less than 90% of the expected price of the property",
)
63 changes: 63 additions & 0 deletions estate/models/offer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
from dateutil.relativedelta import relativedelta

from odoo import api, fields, models
from odoo.exceptions import UserError


class Offer(models.Model):
_name = "estate_property_offer"
_description = "Offer made to some estate (property)"

price = fields.Float(string="Price")
status = fields.Selection(
string="Status",
selection=[("accepted", "Accepted"), ("refused", "Refused")],
copy=False,
)
property_id = fields.Many2one(
string="Property",
comodel_name="estate_property",
required=True,
)
partner_id = fields.Many2one(
string="Partner",
comodel_name="res.partner",
required=True,
)
validity = fields.Integer(string="Validity", default=7)
date_deadline = fields.Date(
string="Deadline",
compute="_compute_deadline",
inverse="_inverse_deadline",
)

_positive_offer_price = models.Constraint(
"CHECK(price > 0)",
"Offer price should be strictly positive",
)

@api.depends("validity")
def _compute_deadline(self):
for offer in self:
curr_date = offer.create_date if offer.create_date else fields.Date.today()
offer.date_deadline = curr_date + relativedelta(days=offer.validity)
Comment thread
amgom-odoo marked this conversation as resolved.
Outdated

def _inverse_deadline(self):
for offer in self:
date_diff = offer.date_deadline - (offer.create_date.date())
offer.validity = date_diff.days

def action_accept_offer(self):
# check if there is no other accepted offers
if "accepted" in self.property_id.offer_ids.mapped("status"):
raise UserError("Only one offer can be accepted for a given property!")

# update status, selling price, buyer
self.status = "accepted"
self.property_id.selling_price = self.price
self.property_id.buyer_id = self.partner_id

def action_refuse_offer(self):
self.status = "refused"
self.property_id.selling_price = 0
self.property_id.buyer_id = None
Comment thread
amgom-odoo marked this conversation as resolved.
Outdated
13 changes: 13 additions & 0 deletions estate/models/property_tag.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from odoo import fields, models


class PropertyTag(models.Model):
_name = "estate_property_tag"
_description = "A list of tags that categorize the properities"

name = fields.Char(string="Name", required=True)

_unique_name = models.Constraint(
"UNIQUE(name)",
"Property tag names must be unique!",
)
13 changes: 13 additions & 0 deletions estate/models/property_type.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from odoo import fields, models


class PropertyType(models.Model):
_name = "estate_property_type"
_description = "The type of the property to be sold such as House, apartment, ..."

name = fields.Char(string="Name", required=True)

_unique_name = models.Constraint(
"UNIQUE(name)",
"Property type names must be unique!",
)
5 changes: 5 additions & 0 deletions estate/security/ir.model.access.csv
Comment thread
amgom-odoo marked this conversation as resolved.
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"
access_estate_property,access_estate_property,model_estate_property,base.group_user,1,1,1,1
access_estate_property_type,access_estate_property_type,model_estate_property_type,base.group_user,1,1,1,1
access_estate_property_tag,access_estate_property_tag,model_estate_property_tag,base.group_user,1,1,1,1
access_estate_property_offer,access_estate_property_offer,model_estate_property_offer,base.group_user,1,1,1,1
10 changes: 10 additions & 0 deletions estate/views/estate_property_menus.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<menuitem id="menu_estate_root" name="Real Estate">
<menuitem id="menu_estate_property" name="Advertisements" action="estate_property_action"/>
<menuitem id="menu_estate_property_types" name="Settings">
Comment thread
amgom-odoo marked this conversation as resolved.
Outdated
<menuitem id="menu_estate_property_types_list" name="Property Types" action="estate_property_type_action"/>
<menuitem id="menu_estate_property_tags_list" name="Property Tags" action="estate_property_tag_action"/>
</menuitem>
</menuitem>
</odoo>
23 changes: 23 additions & 0 deletions estate/views/estate_property_offer_views.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="estate_property_offer_form_view" model="ir.ui.view">
<field name="name">estate.property.offer.form</field>
<field name="model">estate_property_offer</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Create Offers">
<sheet>
<group>
<group>
<field name="price"/>
<field name="partner_id"/>
<field name="validity"/>
<field name="date_deadline"/>
<field name="status"/>
</group>
</group>
</sheet>
</form>
</field>
</record>
</odoo>
8 changes: 8 additions & 0 deletions estate/views/estate_property_tag_views.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">Property Tags</field>
<field name="res_model">estate_property_tag</field>
<field name="view_mode">list,form</field>
</record>
</odoo>
8 changes: 8 additions & 0 deletions estate/views/estate_property_type_views.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_type_action" model="ir.actions.act_window">
<field name="name">Property Types</field>
<field name="res_model">estate_property_type</field>
<field name="view_mode">list,form</field>
</record>
</odoo>
Loading