Skip to content
Open
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
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,
"installable": True,
"data": [
"security/ir.model.access.csv",
"views/estate_property_views.xml",
"views/estate_menus.xml",
],
"license": "LGPL-3",
"author": "Abdallah",
}
3 changes: 3 additions & 0 deletions estate/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from . import estate_property
from . import estate_property_misc
from . import res_user
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 fields, models, api, exceptions
from odoo.tools import float_compare


class Property(models.Model):
_name = "estate.property"
_description = "Estate Property Info"
_order = "id desc"

name = fields.Char(required=True, translate=True, string="Title")
description = fields.Text(translate=True)
postcode = fields.Char()
date_availability = fields.Date(
copy=False,
default=fields.Datetime.add(fields.Datetime.today(), months=3),
string="Available From",
)
expected_price = fields.Float(required=True)
selling_price = fields.Float(readonly=True, copy=False)
bedrooms = fields.Integer(default=2)
living_area = fields.Integer(string="Living Area(sqm)")
facades = fields.Integer()
garage = fields.Boolean()
garden = fields.Boolean()
garden_area = fields.Integer(string="Garden Area(sqm)")
garden_orientation = fields.Selection(
string="Garden Orientation",
selection=[
("north", "North"),
("east", "East"),
("west", "West"),
("south", "South"),
],
)
active = fields.Boolean(default=True)
state = fields.Selection(
required=True,
copy=False,
default="new",
selection=[
("new", "New"),
("offer_recieved", "Offer Recieved"),
("offer_accepted", "Offer Accepted"),
("sold", "Sold"),
("cancelled", "Cancelled"),
],
)

property_type_id = fields.Many2one(
"estate.property.type",
string="Property Type",
)

buyer_id = fields.Many2one("res.partner", string="Buyer", copy="False")
salesperson_id = fields.Many2one(
"res.users",
string="Salesman",
default=lambda self: self.env.user,
)

tag_ids = fields.Many2many(
"estate.property.tags",
string="Tags",
)
offer_ids = fields.One2many("estate.property.offer", "property_id", string="Offers")

total_area = fields.Float(
compute="_compute_total_area",
string="Total Area(sqm)",
)
best_offer = fields.Float(
compute="_compute_best_price",
string="Best Offer",
)

_check_positive_expected_price = models.Constraint(
"CHECK(expected_price > 0)",
"Prices Must Be Positive",
)

_check_positive_selling_price = models.Constraint(
"CHECK(selling_price > 0)",
"Prices Must Be Positive",
)

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

@api.depends("offer_ids")
def _compute_best_price(self):
for record in self:
record.best_offer = max(record.offer_ids.mapped("price"), default=0.0)

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

def button_sell_property(self):
for record in self:
if record.state == "cancelled":
raise exceptions.UserError("Property Cancelled")
record.state = "sold"
return True

def button_cancel_property(self):
for record in self:
if record.state == "sold":
raise exceptions.UserError("Property Sold")
record.state = "cancelled"
return True

@api.constrains("selling_price")
def _check_selling_price(self):
for record in self:
if float_compare(record.selling_price, record.expected_price * 0.90, 2) < 0:
raise exceptions.ValidationError("The selling price is too low")

@api.ondelete(at_uninstall=False)
def _check_ondelete(self):
if any((property.state not in ("new", "cancelled")) for property in self):
raise exceptions.UserError(
"Can't delete, one or more property has some action done, mark as cancelled first"
)
113 changes: 113 additions & 0 deletions estate/models/estate_property_misc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
from odoo import fields, models, api, exceptions


class PropertyType(models.Model):
_name = "estate.property.type"
_description = "Estate Property Type"
_order = "sequence, name"

name = fields.Char(required=True, string="Type Name")
description = fields.Char()
sequence = fields.Integer("Sequence", default=1)

property_ids = fields.One2many("estate.property", "property_type_id")
offer_ids = fields.One2many("estate.property.offer", "property_type_id")
offer_count = fields.Integer(compute="_compute_offer_count", default=0)

_check_unique_name = models.Constraint(
"unique(name)",
"Type must be unique",
)

@api.depends("offer_ids")
def _compute_offer_count(self):
for type in self:
count = 0
for offer in type.offer_ids:
count += 1
type.offer_count = count


class PropertyTags(models.Model):
_name = "estate.property.tags"
_description = "Estate Property Tags"
_order = "name"

name = fields.Char(required=True)
color = fields.Integer()

_check_unique_name = models.Constraint(
"unique(name)",
"Tag must be unique",
)


class PropertyOffer(models.Model):
_name = "estate.property.offer"
_description = "Estate Property Offer"
_order = "price desc"

price = fields.Float(required=True)
state = fields.Selection(
string="State",
copy=False,
selection=[
("accepted", "Accepted"),
("refused", "Refused"),
],
)
validity = fields.Integer(default=7)

buyer_id = fields.Many2one("res.partner", string="Buyer", required=True)
property_id = fields.Many2one("estate.property", string="Property", required=True)
property_type_id = fields.Many2one(
related="property_id.property_type_id", store=True
)

date_deadline = fields.Datetime(
compute="_compute_date_deadline", inverse="_inverse_date_deadline"
)

_check_positive_price = models.Constraint(
"CHECK(price > 0.001)",
"Prices Must Be Positive",
)

@api.depends("validity")
def _compute_date_deadline(self):
for property in self:
create_date = property.create_date or fields.Datetime.now()
property.date_deadline = fields.Datetime.add(
create_date,
days=property.validity,
)

def _inverse_date_deadline(self):
for property in self:
create_date = property.create_date or fields.Datetime.now()
property.validity = (property.date_deadline - create_date).days

def accept_offer(self):
self.ensure_one()
if self.property_id.state == "sold":
raise exceptions.UserError("Property is already sold")
self.property_id.buyer_id = self.buyer_id
self.property_id.selling_price = self.price
self.state = "accepted"
return True

def refuse_offer(self):
self.ensure_one()
if self.property_id.state == "sold":
raise exceptions.UserError("Property is already sold")
self.state = "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_recieved"
if vals["price"] < property.best_offer:
raise exceptions.UserError("Property is already sold")
return super().create(vals_list)
11 changes: 11 additions & 0 deletions estate/models/res_user.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from odoo import fields, models


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

property_ids = fields.One2many(
"estate.property",
"salesperson_id",
domain=[("state", "in", ("new", "offer_received"))],
)
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
access_estate_model,access_estate_model,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_tags,access_estate_property_tags,model_estate_property_tags,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
22 changes: 22 additions & 0 deletions estate/views/estate_menus.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?xml version="1.0"?>
<odoo>
<menuitem id="estate_menu_root" name="Real Estate"/>
<menuitem id="estate_ads_menu"
name="Ads"
parent="estate_menu_root"/>
<menuitem id="estate_ads_properties"
name="Properties"
action="estate_property_action"
parent="estate_ads_menu"/>
<menuitem id="estate_settings_menu"
name="Settings"
parent="estate_menu_root"/>
<menuitem id="estate_settings_types"
name="Property Types"
action="estate_property_type_action"
parent="estate_settings_menu"/>
<menuitem id="estate_settings_tags"
name="Property Tags"
action="estate_property_tags_action"
parent="estate_settings_menu"/>
</odoo>
Loading