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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -127,3 +127,5 @@ dmypy.json

# Pyre type checker
.pyre/

.vscode/*
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
27 changes: 27 additions & 0 deletions estate/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
'name': "Real Estate",

'summary': "Cool Real Estate App",
'description': """
Cool Real Estate App
Wow This is a description omgg
""",
'author': "kmhma",
'website': "https://www.odoo.com/",
'category': 'Tutorials',
'version': '0.1',
'application': True,
'installable': True,
'depends': ['base'],

'data': [
"security/ir.model.access.csv",
"views/property_type_views.xml",
"views/property_tag_views.xml",
"views/property_offer_views.xml",
"views/estate_property_views.xml",
"views/res_users_views.xml",
"views/estate_menus_views.xml",
],
'license': 'LGPL-3',
}
5 changes: 5 additions & 0 deletions estate/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from . import estate_property
from . import estate_property_type
from . import estate_property_tag
from . import estate_property_offer
from . import res_users
125 changes: 125 additions & 0 deletions estate/models/estate_property.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
from odoo import models, fields, api, exceptions
from odoo.tools import float_compare


class EstateProperty(models.Model):
_name = "estate.property"
_description = "Real Estate model"
_order = "id desc"

name = fields.Char(required=True)
description = fields.Char()
postcode = fields.Char()
available_from = fields.Date(
copy=False, default=fields.Date.add(fields.Date.today(), 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(string="Living Area (sqm)")
facades = fields.Integer()
garage = fields.Boolean()
garden = fields.Boolean()
active = fields.Boolean(default=True)
garden_area = fields.Integer(string="Garden Area (sqm)")
garden_orientation = fields.Selection(
string="Garden Orientation",
selection=[
("north", "North"),
("south", "South"),
("east", "East"),
("west", "West"),
],
)
state = fields.Selection(
string="State",
selection=[
("new", "New"),
("offer_received", "Offer Received"),
("offer_accepted", "Offer Accepted"),
("sold", "Sold"),
("cancelled", "Cancelled"),
],
required=True,
copy=False,
default="new",
)
property_type_id = fields.Many2one(
comodel_name="estate.property.type", string="Property Type"
)
buyer_id = fields.Many2one(comodel_name="res.partner", copy=False)
salesman_id = fields.Many2one(
comodel_name="res.users", default=lambda self: self.env.uid
)
tag_ids = fields.Many2many(comodel_name="estate.property.tag", string="Tags")
offer_ids = fields.One2many(
comodel_name="estate.property.offer", inverse_name="property_id"
)
total_area = fields.Integer(compute="_compute_area", string="Total Area (sqm)")
best_price = fields.Float(compute="_compute_best_price")

_postive_expected_price = models.Constraint(
"CHECK (expected_price > 0)", "The expected price must be strictly positive"
)

_postive_selling_price = models.Constraint(
"CHECK (selling_price > 0)", "The selling price must be strictly positive"
)

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

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

@api.onchange("offer_ids")
def _onchange_offer_receieved(self):
if len(self.offer_ids):
self.state = "offer_received"

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

def sell_property(self):
for property in self:
if property.state == "cancelled":
raise exceptions.UserError("You cannot sell a cancelled property")
property.state = "sold"
return True

def cancel_property(self):
for property in self:
if property.state == "sold":
raise exceptions.UserError("You cannot cancel a sold property")
else:
property.state = "cancelled"
return True

@api.constrains("selling_price")
def _check_selling_price(self):
for property in self:
if (
float_compare(property.selling_price, 0.9 * property.expected_price, 2)
== -1
):
raise exceptions.ValidationError(
"The selling price must be at least 90% of the expected price."
)

@api.ondelete(at_uninstall=False)
def _unlink_property_with_offer(self):
for property in self:
if property.state not in ["new", "cancelled"]:
raise exceptions.UserError(
"You cannot delete a property with existing offers!"
)
73 changes: 73 additions & 0 deletions estate/models/estate_property_offer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
from odoo import models, fields, api, exceptions


class EstatePropertyOffer(models.Model):
_name = "estate.property.offer"
_description = "property offer"
_order = "price desc"

price = fields.Float()
status = fields.Selection(
copy=False,
selection=[
("accepted", "Accepted"),
("refused", "Refused"),
],
)
partner_id = fields.Many2one("res.partner", required=True, string="Partner")
property_id = fields.Many2one(
"estate.property", required=True, string="Property", ondelete="cascade"
)
validity = fields.Integer(default=7)
deadline_date = fields.Date(
compute="_compute_deadline_date", inverse="_inverse_deadline_date"
)
property_type_id = fields.Many2one(related="property_id.property_type_id", store=True)

_postive_price = models.Constraint(
"CHECK (price > 0)",
"The expected price must be strictly positive"
)

@api.depends("create_date", "validity")
def _compute_deadline_date(self):
for record in self:
create_date = record.create_date or fields.Date.today()
record.deadline_date = fields.Date.add(create_date, days=record.validity)

def _inverse_deadline_date(self):
for record in self:
create_date = record.create_date or fields.Date.today()
record.validity = (record.deadline_date - create_date.date()).days

def button_accept_offer(self):
for offer in self:
if offer.property_id.state == "sold":
raise exceptions.UserError(
f"Property '{offer.property_id.name}' is already sold"
)
offer.property_id.write({
"buyer_id": offer.partner_id,
"selling_price": offer.price,
"state": "offer_accepted"
})
offer.status = "accepted"
return True

def button_refuse_offer(self):
for offer in self:
if offer.property_id.state == "sold" and offer.status == "accepted":
raise exceptions.UserError(
f"Property '{offer.property_id.name}' is already sold to '{offer.partner_id.name}'"
)
offer.status = "refused"
return True

@api.model_create_multi
def create(self, vals_list):
for val in vals_list:
property_id = self.env['estate.property'].browse(val['property_id'])
if property_id.offer_ids and max(offer.price for offer in property_id.offer_ids) > val['price']:
raise exceptions.UserError("An offer with a higher price exists")
property_id.state = "offer_received"
return super().create(vals_list)
12 changes: 12 additions & 0 deletions estate/models/estate_property_tag.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from odoo import models, fields


class EstatePropertyTag(models.Model):
_name = "estate.property.tag"
_description = "property tags"
_order = "name asc"

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

_unique_tag = models.Constraint("unique(name)", "This tag already exists")
23 changes: 23 additions & 0 deletions estate/models/estate_property_type.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from odoo import models, fields, api


class EstatePropertyType(models.Model):
_name = "estate.property.type"
_description = "property types"
_order = "sequence, name asc"

name = fields.Char(required=True)
sequence = fields.Integer('Sequence', default=1)

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

_unique_type = models.Constraint("unique(name)", "This type already exists")

@api.depends("offer_ids")
def _compute_offer_count(self):
for type in self:
type.offer_count = len(type.offer_ids)
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 models, fields


class Users(models.Model):
_inherit = "res.users"
property_ids = fields.One2many(comodel_name="estate.property", inverse_name="salesman_id",
string="Properties")
6 changes: 6 additions & 0 deletions estate/security/ir.model.access.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
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
access_res_users,access.res.users,model_res_users,base.group_user,1,1,1,1
14 changes: 14 additions & 0 deletions estate/views/estate_menus_views.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?xml version="1.0"?>
<odoo>
<menuitem id="estate_menu_root" name="Estate Property">
<menuitem id="advertisement_menu" name="Advertisments">
<menuitem id="properties_action" action="estate_property_view"/>
</menuitem>

<menuitem id="settings_menu" name="Settings">
<menuitem id="property_type_action" action="property_type_view"/>
<menuitem id="property_tag_action" action="property_tag_view"/>
<menuitem id="users_and_companies" action="user_and_companies_view"/>
</menuitem>
</menuitem>
</odoo>
Loading