Skip to content
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
20 changes: 20 additions & 0 deletions estate/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"name": "estate",
"version": "1.0",
"depends": ["base"],
"author": "Odoo S.A.",
"category": "Real Estate",
"description": """
Description text
""",
"application": True,
"installable": True,
"data": [
"data/ir.model.access.csv",
"views/estate_property_views.xml",
"views/estate_property_type.xml",
"views/estate_property_tag.xml",
"views/estate_property_offer.xml",
"views/estate_menus.xml",
],
}
5 changes: 5 additions & 0 deletions estate/data/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,access_estate_property,estate.model_estate_property,base.group_user,1,1,1,1
estate.access_estate_property_type,access_estate_property_type,estate.model_estate_property_type,base.group_user,1,1,1,1
estate.access_estate_property_tag,access_estate_property_tag,estate.model_estate_property_tag,base.group_user,1,1,1,1
estate.access_estate_property_offer,access_estate_property_offer,estate.model_estate_property_offer,base.group_user,1,1,1,1
2 changes: 2 additions & 0 deletions estate/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from . import (estate_property, estate_property_offer, estate_property_tag,
estate_property_type)
93 changes: 93 additions & 0 deletions estate/models/estate_property.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
from datetime import datetime

from dateutil.relativedelta import relativedelta
from odoo import api, exceptions, fields, models


class EstateProperty(models.Model):
_name = "estate.property"
_description = "Estate Property data model"

name = fields.Char(required=True)
description = fields.Text()
active = fields.Boolean(default=True)
postcode = fields.Char()
date_availability = fields.Date(
copy=False, default=datetime.now() + relativedelta(month=3)
)
expected_price = fields.Float(required=True)
selling_price = fields.Float(readonly=True, copy=False)
bedrooms = fields.Integer(default=2)
living_area = fields.Integer()
facades = fields.Integer()
garage = fields.Boolean()
garden = fields.Boolean()
garden_area = fields.Integer()
garden_orientation = fields.Selection(
[
("north", "North"),
("south", "South"),
("east", "East"),
("west", "West"),
],
help="Indicates the direction the garden is facing with respect to the house",
)
state = fields.Selection(
[
("new", "New"),
("offer_received", "Offer Received"),
("offer_accepted", "Offer Accepted"),
("sold", "Sold"),
("cancelled", "Cancelled"),
],
required=True,
copy=False,
default="new",
help="State of the proprty listing lifecycle",
)
property_type_id = fields.Many2one("estate.property.type", string="Property Type")
buyer_id = fields.Many2one("res.partner", string="Buyer")
salesperson_id = fields.Many2one(
"res.users", string="Salesman", default=lambda self: self.env.user
)
tag_ids = fields.Many2many("estate.property.tag", string="Property Tags")
offer_ids = fields.One2many("estate.property.offer", "property_id")
total_area = fields.Integer(compute="_compute_total_area")
best_price = fields.Float(compute="_compute_best_price")

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

@api.depends("offer_ids.price")
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):
self.garden_area = 10 if self.garden else 0
self.garden_orientation = "north" if self.garden else None

def sold_estate_property(self):
for record in self:
if record.state != "cancelled":
record.state = "sold"
else:
raise exceptions.UserError("can't sell a cancelled property")

def cancel_estate_property(self):
for record in self:
if record.state != "sold":
record.state = "cancelled"
else:
raise exceptions.UserError("can't cancel a sold property")

def reject_other_offers(self, winning_offer):
for record in self:
for offer in record.offer_ids:
if offer != winning_offer:
offer.status = "refused"
43 changes: 43 additions & 0 deletions estate/models/estate_property_offer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import datetime

from odoo import api, fields, models


class EstatePropertyOffer(models.Model):
_name = "estate.property.offer"
_description = "Real estate property offer"

price = fields.Float()
status = fields.Selection(
[("accepted", "Accepted"), ("refused", "Refused")], copy=False
)
partner_id = fields.Many2one("res.partner", required=True, string="Sales")
property_id = fields.Many2one("estate.property", required=True)
validity = fields.Integer(default=7)
date_deadline = fields.Date(
compute="_compute_date_deadline", inverse="_inverse_date_deadline"
)

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

def _inverse_date_deadline(self):
for record in self:
record.validity = (record.date_deadline - record.create_date.date()).days

def action_confirm(self):
for record in self:
record.status = "accepted"
record.property_id.selling_price = record.price
record.property_id.buyer_id = record.partner_id
record.property_id.reject_other_offers(record)

def action_cancel(self):
for record in self:
record.status = "refused"
8 changes: 8 additions & 0 deletions estate/models/estate_property_tag.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from odoo import fields, models


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

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


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

name = fields.Char(required=True)
14 changes: 14 additions & 0 deletions estate/views/estate_menus.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?xml version="1.0"?>
<odoo>
<menuitem id="estate_property_menu_root" name="Real Estate">
<menuitem id="estate_property_first_level_menu" name="Advertisements">
<menuitem id="estate_property_model_menu_action" action="estate_property_action" />
</menuitem>
<menuitem id="estate_property_settings_menu" name="Settings">
<menuitem id="estate_property_type_model_menu_action" action="estate_property_type_action"
name="Property Types" />
<menuitem id="estate_property_tag_model_menu_action" action="estate_property_tag_action"
name="Property Tags" />
</menuitem>
</menuitem>
</odoo>
19 changes: 19 additions & 0 deletions estate/views/estate_property_offer.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<record id="estate_property_offer_view_list" model="ir.ui.view">
<field name="name">estate.property.offer.list</field>
<field name="model">estate.property.offer</field>
<field name="arch" type="xml">
<list string="Properties">
<field name="price" string="Price" />
<field name="partner_id" string="Partner" />
<field name="validity" string="Validity (days)" />
<field name="date_deadline" string="Deadline" />
<field name="status" />
<button name="action_confirm" title="Accept" type="object" icon="fa-check" />
<button name="action_cancel" title="Refuse" type="object" icon="fa-times" />
</list>
</field>
</record>

</odoo>
8 changes: 8 additions & 0 deletions estate/views/estate_property_tag.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.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>
111 changes: 111 additions & 0 deletions estate/views/estate_property_views.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
<?xml version="1.0"?>
<odoo>
<record id="estate_property_action" model="ir.actions.act_window">
<field name="name">estate property action</field>
<field name="res_model">estate.property</field>
<field name="view_mode">list,form</field>
</record>

<record id="estate_property_view_list" model="ir.ui.view">
<field name="name">estate.property.list</field>
<field name="model">estate.property</field>
<field name="arch" type="xml">
<list string="Properties">
<field name="name" string="Title" />
<field name="postcode" string="Postcode" />
<field name="bedrooms" string="Bedrooms" />
<field name="living_area" string="Living Area (sqm)" />
<field name="expected_price" />
<field name="selling_price" />
<field name="date_availability" string="Available From" />
</list>
</field>
</record>

<record id="estate_property_view_form" model="ir.ui.view">
<field name="name">estate.property.form</field>
<field name="model">estate.property</field>
<field name="arch" type="xml">
<form string="Test">
<header>
<button name="sold_estate_property" type="object" string="Sold" />
<button name="cancel_estate_property" type="object" string="Cancel" />
</header>
<sheet>
<group>
<h1 class="mb32">
<field name="name" placeholder="TITLE" />
</h1>
</group>
<field name="tag_ids" widget="many2many_tags" />
<separator />
<group>
<group>
<field name="state" string="Status" />
<field name="property_type_id" string="Property Type" />
<field name="postcode" string="Postcode" />
<field name="date_availability" string="Available From" />
</group>
<group>
<field name="expected_price" />
<field name="best_price" string="Best Offer" />
<field name="selling_price" />
</group>
</group>
<group>
</group>
<notebook>
<page string="Description">
<group>
<field name="description" />
<field name="bedrooms" string="Bedrooms" />
<field name="living_area" string="Living Area (sqm)" />
<field name="facades" />
<field name="garage" />
<field name="garden" />
<field name="garden_area" />
<field name="garden_orientation" />
<field name="total_area" string="Total Area (sqm)" />
</group>
</page>
<page string="Offers">
<group>
<field name="offer_ids" />
</group>
</page>
<page string="Other Info">
<group>
<field name="salesperson_id" />
<field name="buyer_id" />
</group>
</page>
</notebook>
</sheet>
</form>
</field>
</record>

<record id="estate_property_search" model="ir.ui.view">
<field name="name">estate.property.search</field>
<field name="model">estate.property</field>
<field name="arch" type="xml">
<search string="Search Properties">
<field name="name" string="Title" />
<field name="postcode" />
<field name="expected_price" />
<field name="bedrooms" />
<field name="living_area" />
<field name="facades" />
<separator />
<filter string="Available" name="available" domain="[('active', '=', True)]" />
<filter string="New" name="new" domain="[('state', '=', 'new')]" />
<filter string="Offer Received" name="offer_received"
domain="[('state', '=', 'offer_received')]" />
<group>
<filter string="Postcode" name="postcode"
context="{'group_by':'postcode'}" />
</group>
</search>
</field>
</record>
</odoo>