Skip to content

Commit f2bff30

Browse files
committed
[IMP] estate: Enhance UI and property management features
• Enable inline editing for property tags and offers without opening form views • Add widgets and visual decorations to property and offer records • Introduce a status bar to visualize property states • Add a stat button on property types to view all related offers
1 parent 5784c4f commit f2bff30

8 files changed

+250
-195
lines changed

estate/models/estate_property.py

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,14 @@
11
from dateutil.relativedelta import relativedelta
2+
23
from odoo import models, api, fields, exceptions
34
from odoo.exceptions import ValidationError
45

56

67
class EstateProperty(models.Model):
78
_name = "estate.property"
89
_description = "Real Estate Property"
9-
_check_expected_price = models.Constraint(
10-
"CHECK(expected_price > 0)", "Expected price must be strictly positive."
11-
)
12-
_check_selling_price = models.Constraint(
13-
"CHECK(selling_price >= 0)", "Selling price must be positive."
14-
)
10+
_order = "id desc"
11+
1512
name = fields.Char(required=True)
1613
description = fields.Text()
1714
postcode = fields.Char()
@@ -44,24 +41,32 @@ class EstateProperty(models.Model):
4441
string="Status",
4542
default="new",
4643
)
47-
active = fields.Boolean(default=False)
44+
active = fields.Boolean(default=True)
4845
property_type_id = fields.Many2one("estate.property.type", string="Property Type", required=True)
4946
customer = fields.Many2one("res.partner", string="Customer", copy=False)
5047
salesperson = fields.Many2one(
5148
"res.users", string="Salesperson", default=lambda self: self.env.user
5249
)
5350
tag_ids = fields.Many2many("estate.property.tag", string="Property Tags")
5451
offer_ids = fields.One2many("estate.property.offer", "property_id", string="Offer")
55-
total_area = fields.Integer(compute="_total_area")
56-
best_price = fields.Integer(compute="_best_price")
52+
total_area = fields.Integer(compute="_compute_total_area")
53+
best_price = fields.Integer(compute="_compute_best_price")
54+
55+
_check_expected_price = models.Constraint(
56+
"CHECK(expected_price > 0)", "Expected price must be strictly positive."
57+
)
58+
59+
_check_selling_price = models.Constraint(
60+
"CHECK(selling_price >= 0)", "Selling price must be positive."
61+
)
5762

5863
@api.depends("living_area", "garden_area")
59-
def _total_area(self):
64+
def _compute_total_area(self):
6065
for record in self:
6166
record.total_area = record.living_area + record.garden_area
6267

6368
@api.depends("offer_ids.price")
64-
def _best_price(self):
69+
def _compute_best_price(self):
6570
for record in self:
6671
if not record.mapped("offer_ids.price"):
6772
record.best_price = 0

estate/models/estate_property_offer.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,27 @@
11
from dateutil.relativedelta import relativedelta
2+
23
from odoo import models, api, fields
34

45

56
class EstatePropertyOffer(models.Model):
67
_name = "estate.property.offer"
78
_description = "Estate Property Offer"
8-
_check_price = models.Constraint(
9-
"CHECK(price > 0)", "Price of an offer must be positive"
10-
)
9+
_order = "price desc"
10+
1111
price = fields.Float()
1212
status = fields.Selection(
1313
selection=[("accepted", "Accepted"), ("refused", "Refused")], copy=False
1414
)
1515
partner_id = fields.Many2one("res.partner", string="Partner", required=True)
1616
property_id = fields.Many2one("estate.property", string="Property", required=True)
17+
property_type_id = fields.Many2one(related="property_id.property_type_id", store=True)
1718
validity = fields.Integer(default=7)
1819
date_deadline = fields.Date(compute="_compute_deadline", inverse="_inverse_date")
1920

21+
_check_price = models.Constraint(
22+
"CHECK(price > 0)", "Price of an offer must be positive"
23+
)
24+
2025
@api.depends("validity")
2126
def _compute_deadline(self):
2227
for record in self:
@@ -31,6 +36,7 @@ def _inverse_date(self):
3136
def action_accept(self):
3237
for record in self:
3338
record.status = "accepted"
39+
record.property_id.state = "offer_accepted"
3440
record.property_id.selling_price = record.price
3541
record.property_id.customer = record.partner_id
3642
return True

estate/models/estate_property_tag.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@
44
class EstatePropertyTags(models.Model):
55
_name = "estate.property.tag"
66
_description = "Estate Property Tag"
7+
_order = "name"
8+
9+
name = fields.Char(required=True)
10+
color = fields.Integer()
711
_check_tag_name = models.Constraint(
812
"UNIQUE(name)", "Property tag should be unique."
913
)
10-
name = fields.Char(required=True)
Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,22 @@
1-
from odoo import models, fields
1+
from odoo import models, api, fields
22

33

44
class EstatePropertyType(models.Model):
55
_name = "estate.property.type"
66
_description = "Estate Property Type"
7+
_order = "sequence,name"
8+
9+
name = fields.Char(required=True)
10+
property_ids = fields.One2many("estate.property", "property_type_id")
11+
sequence = fields.Integer("Sequence", default=1)
12+
offer_ids = fields.One2many("estate.property.offer", "property_type_id")
13+
offer_count = fields.Integer(compute="_compute_offer_count")
14+
715
_check_type_name = models.Constraint(
816
"UNIQUE(name)", "Property type should be unique."
917
)
10-
name = fields.Char(required=True)
18+
19+
@api.depends("offer_ids")
20+
def _compute_offer_count(self):
21+
for record in self:
22+
record.offer_count = len(record.property_ids.mapped('offer_ids'))
Lines changed: 38 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,42 @@
11
<?xml version="1.0"?>
22
<odoo>
3-
<record id="action_estate_property_offer" model="ir.actions.act_window">
4-
<field name="name">Property Offers</field>
5-
<field name="res_model">estate.property.offer</field>
6-
<field name="view_mode">list,form</field>
7-
</record>
8-
9-
<record id="action_estate_offer_view_list" model="ir.ui.view">
10-
<field name="name">estate.property.offer.list</field>
11-
<field name="model">estate.property.offer</field>
12-
<field name="arch" type="xml">
13-
<list string="Channel">
14-
<field name="price"/>
15-
<field name="partner_id"/>
16-
<field name="validity" string="Validity(days)"/>
17-
<field name="date_deadline"/>
18-
<button name="action_accept" string="Accept" type="object" icon="fa-check" />
19-
<button name="action_refuse" string="Refuse" type="object" icon="fa-times" />
20-
<field name="status"/>
21-
</list>
22-
</field>
23-
</record>
24-
25-
<record id="action_estate_offer_view_form" model="ir.ui.view">
26-
<field name="name">estate.property.offer.form</field>
27-
<field name="model">estate.property.offer</field>
28-
<field name="arch" type="xml">
29-
<form string="Offer">
30-
<sheet>
31-
<group>
32-
<field name="price" />
33-
<field name="partner_id" />
34-
<field name="status" />
35-
<field name="validity" string="Validity(days)"/>
36-
<field name="date_deadline"/>
37-
</group>
38-
</sheet>
39-
</form>
40-
</field>
41-
</record>
3+
<record id="action_estate_property_offer" model="ir.actions.act_window">
4+
<field name="name">Property Offers</field>
5+
<field name="res_model">estate.property.offer</field>
6+
<field name="view_mode">list,form</field>
7+
<field name="domain">[('property_type_id', '=', active_id)]</field>
8+
</record>
9+
<record id="action_estate_offer_view_list" model="ir.ui.view">
10+
<field name="name">estate.property.offer.list</field>
11+
<field name="model">estate.property.offer</field>
12+
<field name="arch" type="xml">
13+
<list string="Channel" editable="bottom" decoration-danger="status=='refused'" decoration-success="status=='accepted'">
14+
<field name="price"/>
15+
<field name="partner_id"/>
16+
<field name="validity" string="Validity(days)"/>
17+
<field name="date_deadline"/>
18+
<button name="action_accept" string="Accept" type="object" icon="fa-check" invisible="status in ('accepted', 'refused')"/>
19+
<button name="action_refuse" string="Refuse" type="object" icon="fa-times" invisible="status in ('accepted', 'refused')"/>
20+
<field name="status"/>
21+
</list>
22+
</field>
23+
</record>
24+
<record id="action_estate_offer_view_form" model="ir.ui.view">
25+
<field name="name">estate.property.offer.form</field>
26+
<field name="model">estate.property.offer</field>
27+
<field name="arch" type="xml">
28+
<form string="Offer">
29+
<sheet>
30+
<group>
31+
<field name="price" />
32+
<field name="partner_id" />
33+
<field name="status" />
34+
<field name="validity" string="Validity(days)"/>
35+
<field name="date_deadline"/>
36+
</group>
37+
</sheet>
38+
</form>
39+
</field>
40+
</record>
4241
</odoo>
4342

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,24 @@
11
<?xml version="1.0"?>
22
<odoo>
3-
<record id="action_estate_property_tag" model="ir.actions.act_window">
4-
<field name="name">Properties Tags</field>
5-
<field name="res_model">estate.property.tag</field>
6-
<field name="view_mode">list,form</field>
7-
<field name="help" type="html">
8-
<p class="o_view_nocontent_smiling_face">
3+
<record id="action_estate_property_tag" model="ir.actions.act_window">
4+
<field name="name">Properties Tags</field>
5+
<field name="res_model">estate.property.tag</field>
6+
<field name="view_mode">list,form</field>
7+
<field name="help" type="html">
8+
<p class="o_view_nocontent_smiling_face">
99
Create your first Property Tag!
1010
</p>
11-
</field>
12-
</record>
13-
14-
<record id="action_estate_property_tag_view_list" model="ir.ui.view">
15-
<field name="name">estate.property.tag.list</field>
16-
<field name="model">estate.property.tag</field>
17-
<field name="arch" type="xml">
18-
<list string="Tag">
19-
<field name="name"/>
20-
</list>
21-
</field>
22-
</record>
11+
</field>
12+
</record>
13+
<record id="action_estate_property_tag_view_list" model="ir.ui.view">
14+
<field name="name">estate.property.tag.list</field>
15+
<field name="model">estate.property.tag</field>
16+
<field name="arch" type="xml">
17+
<list string="Tags" editable="bottom">
18+
<field name="name"/>
19+
<field name="color" widget="color_picker"/>
20+
</list>
21+
</field>
22+
</record>
2323
</odoo>
2424

Lines changed: 46 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,52 @@
11
<?xml version="1.0"?>
22
<odoo>
3-
<record id="action_estate_property_type" model="ir.actions.act_window">
4-
<field name="name">Properties Types</field>
5-
<field name="res_model">estate.property.type</field>
6-
<field name="view_mode">list,form</field>
7-
<field name="help" type="html">
8-
<p class="o_view_nocontent_smiling_face">
3+
<record id="action_estate_property_type" model="ir.actions.act_window">
4+
<field name="name">Properties Types</field>
5+
<field name="res_model">estate.property.type</field>
6+
<field name="view_mode">list,form</field>
7+
<field name="help" type="html">
8+
<p class="o_view_nocontent_smiling_face">
99
Create your first Property Type!
1010
</p>
11-
</field>
12-
</record>
13-
14-
15-
<record id="action_estate_property_type_view_list" model="ir.ui.view">
16-
<field name="name">estate.property.type.list</field>
17-
<field name="model">estate.property.type</field>
18-
<field name="arch" type="xml">
19-
<list string="Types">
20-
<field name="name" string="Type"/>
21-
</list>
22-
</field>
23-
</record>
11+
</field>
12+
</record>
13+
<record id="action_estate_property_type_form" model="ir.ui.view">
14+
<field name="name">estate.property.type.form</field>
15+
<field name="model">estate.property.type</field>
16+
<field name="arch" type="xml">
17+
<form name="List">
18+
<header>
19+
<button class="oe_stat_button" type="action" name="estate.action_estate_property_offer" icon="fa-list" string="offer"/>
20+
<field name="offer_count"/>
21+
</header>
22+
<sheet>
23+
<h1>
24+
<field name="name"/>
25+
</h1>
26+
<notebook>
27+
<page string="Properties">
28+
<field name="property_ids">
29+
<list>
30+
<field name="name" string="Title" />
31+
<field name="expected_price" />
32+
<field name="state" widget="badge" />
33+
</list>
34+
</field>
35+
</page>
36+
</notebook>
37+
</sheet>
38+
</form>
39+
</field>
40+
</record>
41+
<record id="action_estate_property_type_view_list" model="ir.ui.view">
42+
<field name="name">estate.property.type.list</field>
43+
<field name="model">estate.property.type</field>
44+
<field name="arch" type="xml">
45+
<list string="Types">
46+
<field name="sequence" widget="handle"/>
47+
<field name="name" string="Type"/>
48+
</list>
49+
</field>
50+
</record>
2451
</odoo>
2552

0 commit comments

Comments
 (0)