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
23 changes: 23 additions & 0 deletions estate/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
'name': "Estate",
'version': '0.1',
'summary': "Real Estate Advertisement",
'description': """
This module allows you to manage real estate advertisements, including
properties, agents, and customer inquiries.
""",
'author': "aykhu",
'license': 'LGPL-3',
'website': "https://www.odoo.com/app/estate",
'category': 'Tutorials',
'application': True,
'depends': ['base'],
'data': [
'security/ir.model.access.csv',
'views/estate_property_views.xml',
'views/estate_property_type_views.xml',
'views/estate_property_tag_views.xml',
'views/estate_property_offer_views.xml',
'views/estate_menus.xml',
],
}
4 changes: 4 additions & 0 deletions estate/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from . import estate_property
from . import estate_property_type
from . import estate_property_tag
from . import estate_property_offer
99 changes: 99 additions & 0 deletions estate/models/estate_property.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
from dateutil.relativedelta import relativedelta

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


class EstateProperty(models.Model):
_name = 'estate.property'
_description = 'Estate Property'

name = fields.Char(string='Title', required=True, default='Unknown')
description = fields.Text(string='Description')
postcode = fields.Char(string='Postal Code')
date_availability = fields.Date(
string='Available From', copy=False, default=fields.Date.today() + relativedelta(months=+3)
)
expected_price = fields.Float(string='Expected Price', required=True)
selling_price = fields.Float(
string='Selling Price', readonly=True, copy=False
)
living_area = fields.Float(string='Living Area (sq m)')
bedrooms = fields.Integer(string='Bedrooms', default=2)
facades = fields.Integer(string='Facades')
has_garage = fields.Boolean(string="Has Garage ?")
has_garden = fields.Boolean(string="Has Garden ?")
garden_area = fields.Integer(string="Garden Area (sq m)")
active = fields.Boolean(string="Is 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")
],
required=True, default="new", copy=False
)
property_type_id = fields.Many2one(
"estate.property.type", ondelete='Cascade', string="Property Type"
)
salesperson_id = fields.Many2one(
"res.users", string="Sales Person", default=lambda self: self.env.user
)
buyer_id = fields.Many2one("res.partner", string="Buyer", copy=False)
property_tags_ids = fields.Many2many(
"estate.property.tag", string="Property Tags"
)
offer_ids = fields.One2many("estate.property.offer", "property_id")
total_area = fields.Float(compute="_compute_total_area")
best_price = fields.Float(
string="Best Offer", compute="_compute_best_price"
)

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

@api.onchange('has_garden')
def _onchange_garden(self):
if self.has_garden:
self.garden_area = 10
self.garden_orientation = 'north'
else:
self.garden_area = None
self.garden_orientation = None

def action_property_sold(self):
for record in self:
if record.state == "cancelled":
raise UserError("Cancelled property cannot be set as sold.")
else:
record.state = "sold"
return True

def action_property_cancelled(self):
for record in self:
if record.state == "sold":
raise UserError("Sold property cannot be set as cancelled")
else:
record.state = "cancelled"
return True
57 changes: 57 additions & 0 deletions estate/models/estate_property_offer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
from dateutil.relativedelta import relativedelta

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


class EstatePropertyOffer(models.Model):
_name = "estate.property.offer"
_description = "Estate Property Offer"

price = fields.Float(string="Offer Price")
status = fields.Selection(
selection=[
("accepted", "Accepted"),
("refused", "Refused"),
],
string="Status",
copy=False,
)
partner_id = fields.Many2one("res.partner", required=True)
property_id = fields.Many2one("estate.property", required=True)
validity = fields.Integer(string="Validity (days)", default=7)
date_deadline = fields.Date(
string="Deadline", compute='_compute_date_deadline', inverse="_inverse_deadline",
)

@api.depends('create_date', 'validity')
def _compute_date_deadline(self):
for record in self:
start_date = (
record.create_date.date() if record.create_date else fields.Date.today()
)
record.date_deadline = start_date + relativedelta(days=record.validity)

def _inverse_deadline(self):
for record in self:
start_date = (
record.create_date.date() if record.create_date else fields.Date.today()
)
record.validity = (record.date_deadline - start_date).days

def action_accepted(self):
for record in self:
for offer in record.property_id.offer_ids:
if offer.status == "accepted":
raise UserError(
"Only 1 offer can be accepted for each property")
record.status = "accepted"
record.property_id.selling_price = record.price
record.property_id.buyer_id = record.partner_id
record.property_id.state = "offer_accepted"
return True

def action_refused(self):
for record in self:
record.status = "refused"
return True
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 EstatePropertyTags(models.Model):
_name = "estate.property.tag"
_description = "Estate Property Tag"

name = fields.Char(string="Name", 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 = "Estate Property Type"

name = fields.Char(string="Name", required=True)
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
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
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 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<menuitem id = "estate_menu_root" name = "Real Estate">
<menuitem id = "estate_menu_advertisements" name = "Advertisements">
<menuitem id = "estate_menu_advertisements_properties" name = "Properties" action="estate_property_action"/>
</menuitem>
<menuitem id = "estate_menu_settings" name="Settings">
<menuitem id = "estate_menu_settings_type" name = "Property Types" action="estate.estate_property_type_action"/>
<menuitem id = "estate_menu_settings_tag" name="Property Tags" action="estate_property_tag_action"/>
</menuitem>
</menuitem>
</odoo>
34 changes: 34 additions & 0 deletions estate/views/estate_property_offer_views.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?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>
<field name="price"/>
<field name="partner_id"/>
<field name="validity" width="100"/>
<field name="date_deadline"/>
<button name="action_accepted" type="object" string="Accept" icon="fa-check"/>
<button name="action_refused" type="object" string="Refuse" icon="fa-times"/>
<field name="status"/>
</list>
</field>
</record>

<record id="estate_property_offer_view_form" model="ir.ui.view">
<field name="name">estate.property.offer.form</field>
<field name="model">estate.property.offer</field>
<field name="arch" type="xml">
<form>
<group>
<field name="price"/>
<field name="partner_id"/>
<field name="validity"/>
<field name="status"/>
<field name="date_deadline"/>
</group>
</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">Estate Property Tag</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">Estate Property Types</field>
<field name="res_model">estate.property.type</field>
<field name="view_mode">list,form</field>
</record>
</odoo>
102 changes: 102 additions & 0 deletions estate/views/estate_property_views.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="estate_property_action" model="ir.actions.act_window">
<field name="name">Estate Property</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>
<field name="name" width="200"/>
<field name="postcode" width="80"/>
<field name="property_type_id"/>
<field name="property_tags_ids" widget="many2many_tags" width="300"/>
<field name="bedrooms"/>
<field name="living_area"/>
<field name="expected_price"/>
<field name="selling_price"/>
<field name="date_availability"/>
</list>
</field>
</record>

<record id="estate_property_view_search" model="ir.ui.view">
<field name="name">estate.property.search</field>
<field name="model">estate.property</field>
<field name="arch" type="xml">
<search>
<field name="name"/>
<field name="postcode"/>
<field name="expected_price"/>
<field name="bedrooms"/>
<field name="living_area"/>
<field name="facades"/>
<field name="property_type_id"/>
<filter name="state" string="Status" domain="[('state', 'in', ('new', 'offer_received'))]"/>
<filter name="groupby_postcode" string="Postcode" context="{'group_by': 'postcode'}"/>
</search>
</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>
<header>
<button name="action_property_sold" string="Sold" type="object"/>
<button name="action_property_cancelled" string="Cancel" type="object"/>
</header>
<sheet>
<h1>
<field name="name"/>
</h1>
<group>
<group>
<field name="property_tags_ids" widget="many2many_tags"/>
<field name="postcode"/>
<field name="date_availability"/>
<field name="property_type_id"/>
<field name="state"/>
</group>
<group>
<field name="expected_price"/>
<field name="best_price"/>
<field name="selling_price"/>
</group>
</group>
<notebook>
<page string="Description">
<group>
<field name="description"/>
<field name="bedrooms"/>
<field name="living_area"/>
<field name="facades"/>
<field name="has_garage"/>
</group>
<group>
<field name="has_garden"/>
<field name="garden_area" invisible="not has_garden"/>
<field name="garden_orientation" invisible="not has_garden"/>
<field name="total_area"/>
</group>
</page>
<page string="Offers">
<field name="offer_ids"/>
</page>
<page string="Other Info">
<group>
<field name="salesperson_id"/>
<field name="buyer_id"/>
</group>
</page>
</notebook>
</sheet>
</form>
</field>
</record>
</odoo>