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
25 changes: 25 additions & 0 deletions estate/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
'name': 'Real Estate',
'category': 'tutorials',
'summary': 'Real estate module to sell/rent houses and offices',
'description': """
immoweb alias xd
""",
'website': 'https://www.odoo.com/app/real_estate',
'depends': [
'base_setup',
'base'
],
'data': [
'security/ir.model.access.csv',
'views/estate_property_offer_view.xml',
'views/estate_property_type_views.xml',
'views/estate_property_tag_views.xml',
'views/estate_property_views.xml',
'views/estate_menus.xml',
],
'installable': True,
'application': True,
'author': 'ibrha',
'license': 'LGPL-3',
}
1 change: 1 addition & 0 deletions estate/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import estate_property, estate_property_type, estate_property_tag, estate_property_offer
107 changes: 107 additions & 0 deletions estate/models/estate_property.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
from dateutil.relativedelta import relativedelta
from odoo import models, fields, api, exceptions
from odoo.tools.float_utils import float_compare


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

# computed fields
total_area = fields.Float(string="Total Area", compute="_compute_total_area")
best_price = fields.Float(compute="_compute_best_price")

# constraints
_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'
)

name = fields.Char(string="Title", required=True)
description = fields.Text(string="Description")
postcode = fields.Char(string="Postcode")
property_type_id = fields.Many2one("estate.property.type", string="Property Type")
offer_ids = fields.One2many("estate.property.offer", string="Offers", inverse_name="property_id")
tag_ids = fields.Many2many("estate.property.tag", string="Tags")
salesman = fields.Many2one("res.users", string="Salesman", default=lambda self: self.env.uid)
buyer = fields.Many2one("res.partner", string="Buyer", copy=False)
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")
active = fields.Boolean(string="Active", default=True)
garden_orientation = fields.Selection(
string="Garden Orientation",
selection=[
("north", "North"),
("south", "South"),
("east", "East"),
("west", "West")
]
)
state = fields.Selection(
string="Status",
selection=[
("new", "New"),
("offer_received", "Offer Received"),
("offer_accepted", "Offer Accepted"),
("sold", "Sold"),
("cancelled", "Cancelled")
],
readonly=True,
default="new"
)

@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")
def _compute_best_price(self):
for record in self:
record.best_price = max(record.offer_ids.mapped("price")) if record.offer_ids else 0

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

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

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

@api.constrains("state", "offer_ids")
def _check_selling_price(self):
for record in self:
for offer in record.offer_ids:
if offer.status == "accepted" and float_compare(offer.price, 0.9 * record.expected_price, 2) == -1:
raise exceptions.UserError("Selling price should be at least 90 percent of the expected price")
59 changes: 59 additions & 0 deletions estate/models/estate_property_offer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
from odoo import models, fields, api, exceptions


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

# constraints
_positive_price = models.Constraint(
'CHECK(price > 0)',
'Offer price should be (strictly) positive'
)

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

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

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

def 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")
elif offer.property_id.state == "offer_accepted":
raise exceptions.UserError(f"Property {offer.property_id.name} is already accepted")
elif offer.property_id.state == "cancelled":
raise exceptions.UserError(f"Property {offer.property_id.name} is cancelled")
else:
offer.property_id.buyer = offer.partner_id
offer.property_id.selling_price = offer.price
offer.property_id.state = "offer_accepted"
offer.status = "accepted"
return True

def refuse_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.status = "refused"
return True
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 PropertyTag(models.Model):
_name = "estate.property.tag"
_description = "Estate Property Tag"
_order = "name"

# constraints
_unique_tag = models.Constraint(
'UNIQUE(name)',
'Tag name should be unique'
)

name = fields.Char(string="Name", required=True)
color = fields.Integer('Color Index', default=0)
24 changes: 24 additions & 0 deletions estate/models/estate_property_type.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from odoo import models, fields, api


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

# constraints
_unique_type = models.Constraint(
'UNIQUE(name)',
'Type name should be unique'
)

name = fields.Char(string="Name", required=True)
property_ids = fields.One2many("estate.property", inverse_name="property_type_id", string="Properties")
sequence = fields.Integer(string="Sequence", default=1)
offer_ids = fields.One2many("estate.property.offer", inverse_name="property_type_id")
offer_count = fields.Integer(string="Offer Count", compute="_compute_offer_count")

@api.depends("offer_ids")
def _compute_offer_count(self):
for type in self:
type.offer_count = len(type.offer_ids)
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_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
12 changes: 12 additions & 0 deletions estate/views/estate_menus.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<odoo>
<menuitem id="estate_menu_root" name="Real Estate">
<menuitem id="estate_first_level_menu" name="Properties">
<menuitem id="estate_property_menu_action" name="Properties" action="estate_property_action"/>
</menuitem>
<menuitem id="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 id="estate_property_offer_menu" name="Property Offers" action="property_offer_view"/>
</menuitem>
</menuitem>
</odoo>
21 changes: 21 additions & 0 deletions estate/views/estate_property_offer_view.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?xml version="1.0"?>
<odoo>
<record id="property_offer_view" model="ir.actions.act_window">
<field name="name">Property Offer</field>
<field name="res_model">estate.property.offer</field>
<field name="view_mode">list,form</field>
</record>

<record id="property_offer_list_view" model="ir.ui.view">
<field name="name">property.offer.list.view</field>
<field name="model">estate.property.offer</field>
<field name="arch" type="xml">
<list string="Property Offers">
<field name="price"/>
<field name="partner_id"/>
<field name="validity"/>
<field name="deadline_date"/>
</list>
</field>
</record>
</odoo>
32 changes: 32 additions & 0 deletions estate/views/estate_property_tag_views.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?xml version="1.0"?>
<odoo>
<record id="estate_property_tag_view_list" model="ir.ui.view">
<field name="name">estate.property.tag.view.list</field>
<field name="model">estate.property.tag</field>
<field name="arch" type="xml">
<list string="Property Tags" editable="bottom">
<field name="name"/>
</list>
</field>
</record>

<record id="estate_property_tag_view_form" model="ir.ui.view">
<field name="name">estate.property.tag.view.form</field>
<field name="model">estate.property.tag</field>
<field name="arch" type="xml">
<form string="Property Tag">
<sheet>
<group>
<field name="name"/>
</group>
</sheet>
</form>
</field>
</record>

<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>
57 changes: 57 additions & 0 deletions estate/views/estate_property_type_views.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?xml version="1.0"?>
<odoo>
<record id="estate_property_type_view_list" model="ir.ui.view">
<field name="name">estate.property.type.view.list</field>
<field name="model">estate.property.type</field>
<field name="arch" type="xml">
<list string="Property Types">
<field name="sequence" widget="handle"/>
<field name="name"/>
</list>
</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="Property Type">
<sheet>
<div class="oe_button_box" name="button_box">
<button class="oe_stat_button"
name="%(estate.property_offer_view)d"
icon="fa-money"
type="action">
<div class="o_field_widget o_stat_info">
<span class="o_stat_value"><field name="offer_count"/></span>
<span class="o_stat_text">Offers</span>
</div>
</button>
</div>
<div class="oe_title">
<h1>
<field name="name" placeholder="Property Type"/>
</h1>
</div>
<notebook>
<page string="Properties" name="properties">
<field name="property_ids">
<list>
<field name="name" string="Title"/>
<field name="expected_price"/>
<field name="state"/>
</list>
</field>
</page>
</notebook>
</sheet>
</form>
</field>
</record>

<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