Skip to content

Commit

Permalink
[ADD] estate: Added UI elements to enhance user experience
Browse files Browse the repository at this point in the history
Chapter 11: Add UI Enhancements and Inline Views

- Improved the user interface of the real estate module with color decorations
  and conditional display of fields and buttons.
- Added inline list view for estate.property.type to display related properties
  with specific fields (name, expected price, and state).
- Implemented statusbar widget to display the state of estate.property with
  specific statuses (New, Offer Received, Offer Accepted, Sold).
- Added ordering to models for deterministic list views and manual ordering for
  estate.property.type.
- Added widget options to prevent creation/editing from the form view and added
  color picker for tags.
- Configured conditional display of buttons based on property state and made
  some fields invisible based on conditions.
- Enhanced list views with color decorations and made lists editable and fields
  optional.
- Added default filters to search views and adjusted filter domains for living
  area searches.
- Implemented a stat button on estate.property.type form view to show related
  offers with appropriate filtering.
Task: Added chatter in the estate property.for that made changes to
estate_property_views.xml, __manifest__.py file and estate_property.py file.
  • Loading branch information
pach-odoo committed Aug 12, 2024
1 parent 82058e9 commit eba3d52
Show file tree
Hide file tree
Showing 9 changed files with 203 additions and 109 deletions.
36 changes: 18 additions & 18 deletions estate/__manifest__.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
{
'name': 'Real Estate',
'version': '1.0',
'license': 'LGPL-3',
'category': 'Real Estate',
'sequence': 15,
'summary': 'Track properties',
'description': "",
'installable': True,
'application': True,
'depends': ['base_setup'],
'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'
]
"name": "Real Estate",
"version": "1.1",
"license": "LGPL-3",
"category": "Tutorials",
"sequence": 15,
"summary": "Track properties",
"description": "",
"installable": True,
"application": True,
"depends": ["base_setup", "mail"],
"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",
],
}
113 changes: 73 additions & 40 deletions estate/models/estate_property.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,23 @@
from odoo import api, models, fields, _
from dateutil.relativedelta import relativedelta
from odoo.exceptions import UserError, ValidationError
from odoo.tools.float_utils import float_compare, float_is_zero


class EstateProperty(models.Model):
_name = "estate.property"
_description = "Table contains estate properties."
_description = "Table contains real estate properties."
_order = "id desc"
_inherit = ["mail.thread", "mail.activity.mixin"]

name = fields.Char(required=True, string="Title")
description = fields.Text()
postcode = fields.Char()
date_availability = fields.Date(string="Available From", copy=False, default=fields.Date.today() + relativedelta(months=3))
date_availability = fields.Date(
string="Available From",
copy=False,
default=fields.Date.today() + relativedelta(months=3),
)
expected_price = fields.Float(required=True)
selling_price = fields.Float(readonly=True, copy=False)
bedrooms = fields.Integer(default=2)
Expand All @@ -19,74 +26,100 @@ class EstateProperty(models.Model):
garage = fields.Boolean()
garden = fields.Boolean()
garden_area = fields.Integer()
garden_orientation = fields.Selection(selection=[
('north', 'North'),
('south', 'South'),
('east', 'East'),
('west', 'West')
])
garden_orientation = fields.Selection(
selection=[
("north", "North"),
("south", "South"),
("east", "East"),
("west", "West"),
]
)
active = fields.Boolean(default=True)
state = fields.Selection(default='new', selection=[
('new', 'New'),
('offer received', 'Offer Received'),
('offer accepted', 'Offer Accepted'),
('sold', 'Sold'),
('canceled', 'Canceled')
])
salesman_id = fields.Many2one('res.users', string='Salesman', default=lambda self: self.env.user)
buyer_id = fields.Many2one('res.partner', string='Buyer', copy=False)
property_type_id = fields.Many2one('estate.property.type', string='Property Type')
tag_ids = fields.Many2many('estate.property.tag')
offer_ids = fields.One2many('estate.property.offer', 'property_id')
total_area = fields.Float(compute='_compute_total_area')
best_price = fields.Float(compute='_compute_best_offer')
state = fields.Selection(
copy=False,
default="new",
selection=[
("new", "New"),
("offer received", "Offer Received"),
("offer accepted", "Offer Accepted"),
("sold", "Sold"),
("canceled", "Canceled"),
],
tracking=True,
)
salesman_id = fields.Many2one(
"res.users", string="Salesman", default=lambda self: self.env.user
)
buyer_id = fields.Many2one("res.partner", string="Buyer", copy=False)
property_type_id = fields.Many2one("estate.property.type", string="Property Type")
tag_ids = fields.Many2many("estate.property.tag")
offer_ids = fields.One2many("estate.property.offer", "property_id")
total_area = fields.Float(compute="_compute_total_area")
best_price = fields.Float(compute="_compute_best_offer")

_sql_constraints = [
('positive_expected_price', 'CHECK(expected_price > 0.0)', 'A property expected price must be strictly positive'),
('positive_selling_price', 'CHECK(selling_price >= 0.0)', 'A property selling price must be positive')
(
"positive_expected_price",
"CHECK(expected_price > 0.0)",
"A property expected price must be strictly positive",
),
(
"positive_selling_price",
"CHECK(selling_price >= 0.0)",
"A property selling price must be positive",
),
]

@api.constrains('selling_price', 'expected_price')
@api.constrains("selling_price", "expected_price")
def _check_selling_price(self):
for record in self:
if record.selling_price > 0:
if record.selling_price < 0.9 * record.expected_price:
raise ValidationError('Selling price cannot be lower than 90% of the expected price')
if not float_is_zero(record.selling_price, precision_digits=2):
if (
float_compare(
record.selling_price,
0.9 * record.expected_price,
precision_digits=2,
)
== -1
):
raise ValidationError(
"Selling price cannot be lower than 90% of the expected price"
)

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

@api.depends("offer_ids.price")
def _compute_best_offer(self):
for record in self:
if record.offer_ids:
record.best_price = max(record.offer_ids.mapped('price'))
record.best_price = max(record.offer_ids.mapped("price"))
else:
record.best_price = 0

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

def action_cancel_property(self):
for record in self:
if record.state == 'sold':
raise UserError(_('Sold property can not be canceled.'))
if record.state == "sold":
raise UserError(_("Sold property can not be canceled."))
else:
self.write({'state': 'canceled'})
self.write({"state": "canceled"})
return True

def action_sold_property(self):
for record in self:
if record.state == 'canceled':
raise UserError(_('Canceled property can not be sold.'))
if record.state == "canceled":
raise UserError(_("Canceled property can not be sold."))
else:
self.write({'state': 'sold'})
self.write({"state": "sold"})
return True
39 changes: 23 additions & 16 deletions estate/models/estate_property_offer.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,45 +3,52 @@


class EstatePropertyOffer(models.Model):
_name = 'estate.property.offer'
_description = 'Offers that propeties receive.'
_name = "estate.property.offer"
_description = "Offers that propeties receive."
_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)
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)
validity = fields.Integer(default=7)
date_deadline = fields.Date(compute='_compute_date_deadline', inverse='_inverse_date_deadline')

date_deadline = fields.Date(
compute="_compute_date_deadline", inverse="_inverse_date_deadline"
)
#chap -11
property_type_id = fields.Many2one(related="property_id.property_type_id", store=True)
_sql_constraints = [
('positive_offer_price', 'CHECK(price > 0)', 'Offer Price Must be Positive')
("positive_offer_price", "CHECK(price > 0)", "Offer Price Must be Positive")
]

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

def _inverse_date_deadline(self):
for record in self:
if record.date_deadline and record.property_id.create_date:
delta = record.date_deadline - fields.Date.to_date(record.property_id.create_date)
delta = record.date_deadline - fields.Date.to_date(
record.property_id.create_date
)
record.validity = delta.days
else:
record.validity = 7

def action_accept_offer(self):
self.write({'status': 'accepted'})
self.write({"status": "accepted"})
self.property_id.selling_price = self.price
self.property_id.buyer_id = self.partner_id
return True

def action_refuse_offer(self):
self.write({'status': 'refused'})
self.write({"status": "refused"})
return True
11 changes: 5 additions & 6 deletions estate/models/estate_property_tag.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,10 @@


class EstatePropertyTag(models.Model):
_name = 'estate.property.tag'
_description = 'It describes how property is i.e renovated, cozy'

_name = "estate.property.tag"
_description = "It describes how property is i.e renovated, cozy"
_order = "name"
name = fields.Char(required=True)
color = fields.Integer()

_sql_constraints = [
('tag_uniq', 'unique(name)', 'Property Tag must be unique')
]
_sql_constraints = [("tag_uniq", "unique(name)", "Property Tag must be unique")]
18 changes: 14 additions & 4 deletions estate/models/estate_property_type.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,22 @@
from odoo import fields, models
from odoo import fields, models, api


class EstatePropertyType(models.Model):
_name = "estate.property.type"
_description = "Which type of property it is i.e apartment, house "
_order = "sequence, name"

name = fields.Char(required=True)
property_ids = fields.One2many("estate.property", "property_type_id")
sequence = fields.Integer("Sequence")
_sql_constraints = [("type_uniq", "unique(name)", "Property Type should be unique")]
offer_ids = fields.One2many(
"estate.property.offer",
"property_type_id",
)
offer_count = fields.Integer(compute="_compute_offer_count")

_sql_constraints = [
('type_uniq', 'unique(name)', 'Property Type should be unique')
]
@api.depends("offer_ids")
def _compute_offer_count(self):
for record in self:
record.offer_count = len(record.offer_ids)
14 changes: 10 additions & 4 deletions estate/views/estate_property_offer_views.xml
Original file line number Diff line number Diff line change
@@ -1,18 +1,24 @@
<?xml version="1.0"?>
<odoo>
<record id="estate_property_offer_actions" model="ir.actions.act_window">
<field name="name">Offers</field>
<field name="res_model">estate.property.offer</field>
<field name="view_mode">tree,form</field>
<field name="domain">[('property_type_id', '=', active_id)]</field>
</record>

<record id="estate_property_offer_tree" model="ir.ui.view">
<field name="name">estate.property.offer.tree</field>
<field name="model">estate.property.offer</field>
<field name="arch" type="xml">
<tree string="Property Type" sample="1">
<tree string="Property Type" sample="1" editable='bottom' decoration-danger="status == 'refused'" decoration-success="status == 'accepted'">
<field name="price"/>
<field name="partner_id"/>
<field name="validity"/>
<field name="date_deadline"/>
<button name="action_accept_offer" type="object" icon="fa-check" title="Accept Offer" />
<button name="action_refuse_offer" type="object" icon="fa-times" title="Refuse Offer" />
<field name="status"/>
<button name="action_accept_offer" type="object" icon="fa-check" title="Accept Offer" invisible="status == 'accepted' or status == 'refused'"/>
<button name="action_refuse_offer" type="object" icon="fa-times" title="Refuse Offer" invisible="status == 'accepted' or status == 'refused'"/>
<field name="status" invisible="1"/>
</tree>
</field>
</record>
Expand Down
8 changes: 4 additions & 4 deletions estate/views/estate_property_tag_views.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,21 @@
</field>
</record>

<record id="estate_property_type_tree" model="ir.ui.view">
<record id="estate_property_tag_tree" model="ir.ui.view">
<field name="name">estate.property.tag.tree</field>
<field name="model">estate.property.tag</field>
<field name="arch" type="xml">
<tree string="Property Type" sample="1">
<tree string="Property Type" sample="1" editable='bottom'>
<field name="name"/>
</tree>
</field>
</record>

<record id="estate_property__type_form" model="ir.ui.view">
<record id="estate_property_tag_form" model="ir.ui.view">
<field name="name">estate.property.tag.form</field>
<field name="model">estate.property.tag</field>
<field name="arch" type="xml">
<form string="Properties Form">
<form string="Properties Tags">
<sheet>
<group>
<field name="name" />
Expand Down
Loading

0 comments on commit eba3d52

Please sign in to comment.