Skip to content

Commit 881ced2

Browse files
committed
[IMP] estate: chapter 11
1 parent 5349c92 commit 881ced2

8 files changed

+146
-59
lines changed

estate/models/estate_property.py

+15-18
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
class EstateProperty(models.Model):
99
_name = "estate.property"
1010
_description = "Real Estate: Property"
11+
_order = "id desc"
1112

1213
name = fields.Char("Title", required=True)
1314
active = fields.Boolean("Active", default=True)
@@ -36,14 +37,6 @@ class EstateProperty(models.Model):
3637
selling_price = fields.Float("Selling Price", readonly=True, copy=False)
3738
best_price = fields.Float("Best Price", compute="_compute_best_price")
3839

39-
_sql_constraints = [
40-
(
41-
"check_property_prices",
42-
"CHECK(expected_price >= 0 AND selling_price > 0)",
43-
"A property expected price and selling price must be positive",
44-
)
45-
]
46-
4740
# Description
4841
bedrooms = fields.Integer("# of bedrooms", default=2)
4942
living_area = fields.Integer("living area")
@@ -78,6 +71,14 @@ class EstateProperty(models.Model):
7871
default=lambda self: self.env.user,
7972
)
8073

74+
_sql_constraints = [
75+
(
76+
"check_property_prices",
77+
"CHECK(expected_price >= 0 AND selling_price > 0)",
78+
"A property expected price and selling price must be positive",
79+
)
80+
]
81+
8182
@api.depends("garden_area", "living_area")
8283
def _compute_total_area(self) -> None:
8384
for record in self:
@@ -107,17 +108,13 @@ def _check_selling_price(self):
107108
raise ValidationError("The selling price should be atleast 90% of the expected price")
108109

109110
def action_cancel_property(self) -> bool:
110-
for record in self:
111-
if record.state != "sold":
112-
record.state = "cancelled"
113-
else:
114-
raise UserError("Cannot cancel a sold property")
111+
if "sold" in self.mapped("state"):
112+
raise UserError("Cannot cancel a sold property")
113+
self.write({"state": "cancelled"})
115114
return True
116115

117116
def action_sold_property(self) -> bool:
118-
for record in self:
119-
if record.state != "cancelled":
120-
record.state = "sold"
121-
else:
122-
raise UserError("Cannot sold a cancel property")
117+
if "cancelled" in self.mapped("state"):
118+
raise UserError("Cannot sell a cancel property")
119+
self.write({"state": "sold"})
123120
return True

estate/models/estate_property_offer.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
class EstatePropertyOffer(models.Model):
77
_name = "estate.property.offer"
88
_description = "Real Estate: Offer"
9+
_order = "price desc"
910

1011
price = fields.Float("Price")
1112
status = fields.Selection(
@@ -21,6 +22,8 @@ class EstatePropertyOffer(models.Model):
2122
partner_id = fields.Many2one("res.partner", required=True)
2223
property_id = fields.Many2one("estate.property", required=True)
2324

25+
property_type_id = fields.Many2one(related="property_id.property_type_id", store=True)
26+
2427
_sql_constraints = [
2528
(
2629
"check_offer_price",
@@ -44,7 +47,7 @@ def action_refuse_offer(self) -> bool:
4447
record.status = "refused"
4548
record.property_id.write(
4649
{
47-
"selling_price": None,
50+
"selling_price": record.price,
4851
"buyer_id": None,
4952
}
5053
)

estate/models/estate_property_tag.py

+3
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
class EstatePropertyTag(models.Model):
55
_name = "estate.property.tag"
66
_description = "Real Estate: Tag"
7+
_order = "name asc"
78

89
name = fields.Char("Name", required=True)
10+
color = fields.Integer("Color")
11+
912
_sql_constraints = [("tag_name_unique", "UNIQUE(name)", "A tag with the same name already exists.")]

estate/models/estate_property_type.py

+12-1
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,20 @@
1-
from odoo import fields, models
1+
from odoo import api, fields, models
22

33

44
class EstatePropertyType(models.Model):
55
_name = "estate.property.type"
66
_description = "Real Estate: Type of Property"
7+
_order = "name asc"
78

89
name = fields.Char("Name", required=True)
10+
property_ids = fields.One2many("estate.property", "property_type_id", string="Property")
11+
sequence = fields.Integer("Sequence", default=1, help="Used to order stages.")
12+
offer_ids = fields.One2many("estate.property.offer", "property_type_id")
13+
offer_count = fields.Integer(compute="_compute_offer_count")
14+
15+
@api.depends("offer_ids")
16+
def _compute_offer_count(self) -> None:
17+
for record in self:
18+
record.offer_count = len(record.offer_ids.filtered(lambda x: x.property_type_id.name == record.name))
19+
920
_sql_constraints = [("type_name_unique", "UNIQUE(name)", "A type with the same name already exists.")]

estate/views/estate_property_offer_views.xml

+15-9
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,24 @@
11
<?xml version="1.0"?>
22
<odoo>
3+
<record id="estate_property_offer_action" model="ir.actions.act_window">
4+
<field name="name">Property Offer</field>
5+
<field name="res_model">estate.property.offer</field>
6+
<field name="domain">[('property_type_id', '=', active_id)]</field>
7+
<field name="view_mode">list,form</field>
8+
</record>
9+
310
<record id="estate_property_offer_view_list" model="ir.ui.view">
411
<field name="name">estate.property.offer.list</field>
512
<field name="model">estate.property.offer</field>
613
<field name="arch" type="xml">
7-
<list string="Channel">
8-
<field name="price"/>
14+
<list string="Offers" editable="bottom" decoration-success="status in ('accepted',)"
15+
decoration-danger="status in ('refused',)">
916
<field name="partner_id"/>
10-
<button name="action_refuse_offer" type="object" icon="fa-times" title="refuse"/>
11-
<button name="action_accept_offer" type="object" icon="fa-check" title="accept"/>
12-
<field name="status"/>
17+
<field name="price"/>
18+
<button name="action_refuse_offer" type="object" icon="fa-times" title="refuse"
19+
invisible="status in ('accepted', 'refused',)"/>
20+
<button name="action_accept_offer" type="object" icon="fa-check" title="accept"
21+
invisible="status in ('accepted', 'refused',)"/>
1322
</list>
1423
</field>
1524
</record>
@@ -19,11 +28,8 @@
1928
<field name="model">estate.property.offer</field>
2029
<field name="arch" type="xml">
2130

22-
<form string="Test">
31+
<form string="Offers">
2332
<sheet>
24-
<!-- <h1 >
25-
<field name="name" />
26-
</h1> -->
2733
<group>
2834
<field name="price" string="Price"/>
2935
<field name="status" string="Status"/>

estate/views/estate_property_tag_views.xml

+11
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,15 @@
55
<field name="res_model">estate.property.tag</field>
66
<field name="view_mode">list,form</field>
77
</record>
8+
9+
<record id="estate_property_tag_view_list" model="ir.ui.view">
10+
<field name="name">estate.property.tag.list</field>
11+
<field name="model">estate.property.tag</field>
12+
<field name="arch" type="xml">
13+
<list string="Tags" editable="bottom">
14+
<field name="name"/>
15+
<field name="color"/>
16+
</list>
17+
</field>
18+
</record>
819
</odoo>

estate/views/estate_property_type_views.xml

+47
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,51 @@
55
<field name="res_model">estate.property.type</field>
66
<field name="view_mode">list,form</field>
77
</record>
8+
9+
<record id="estate_property_type_view_list" model="ir.ui.view">
10+
<field name="name">estate.property.type.list</field>
11+
<field name="model">estate.property.type</field>
12+
<field name="arch" type="xml">
13+
<list string="Types">
14+
<field name="sequence" widget="handle"/>
15+
<field name="name" required="True"/>
16+
<field name="offer_count"/>
17+
</list>
18+
</field>
19+
</record>
20+
21+
<record id="estate_property_type_view_form" model="ir.ui.view">
22+
<field name="name">estate.property.type.form</field>
23+
<field name="model">estate.property.type</field>
24+
<field name="arch" type="xml">
25+
26+
<form string="Properties">
27+
<sheet>
28+
<div class="oe_button_box" name="button_box">
29+
<button name="%(estate_property_offer_action)d"
30+
type="action"
31+
class="oe_stat_button"
32+
icon="fa-pencil-square-o"
33+
invisible="offer_count == 0">
34+
<field name="offer_count" widget="statinfo" string="Offers"/>
35+
</button>
36+
</div>
37+
<h1>
38+
<field name="name"/>
39+
</h1>
40+
<notebook>
41+
<page string="Property">
42+
<field name="property_ids">
43+
<list>
44+
<field name="name"/>
45+
<field name="expected_price"/>
46+
<field name="state"/>
47+
</list>
48+
</field>
49+
</page>
50+
</notebook>
51+
</sheet>
52+
</form>
53+
</field>
54+
</record>
855
</odoo>

estate/views/estate_property_views.xml

+39-30
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,17 @@
44
<field name="name">Properties</field>
55
<field name="res_model">estate.property</field>
66
<field name="view_mode">list,form</field>
7+
<field name="context">{'search_default_available': True}</field>
78
</record>
89

910
<record id="real_estate_view_list" model="ir.ui.view">
1011
<field name="name">estate.property.list</field>
1112
<field name="model">estate.property</field>
1213
<field name="arch" type="xml">
13-
<list string="Channel">
14+
<list string="Properties"
15+
decoration-success="state in ('offer_received', 'offer_accepted',)"
16+
decoration-bf="state in ('offer_accepted',)"
17+
decoration-muted="state in ('sold',)">
1418
<field name="name" required="True"/>
1519
<field name="postcode"/>
1620
<field name="bedrooms" string="Bedrooms"/>
@@ -27,54 +31,58 @@
2731
<field name="name">estate.property.form</field>
2832
<field name="model">estate.property</field>
2933
<field name="arch" type="xml">
30-
<form string="Test">
34+
<form string="Properties">
3135
<header>
32-
<button name="action_cancel_property" type="object" string="Cancel"/>
33-
<button name="action_sold_property" type="object" string="Sold"/>
36+
<button name="action_cancel_property" type="object" string="Cancel"
37+
invisible="state in ('cancelled', 'sold',)"/>
38+
<button name="action_sold_property" type="object" string="Sold"
39+
invisible="state in ('cancelled', 'sold',)"/>
40+
<field name="state" widget="statusbar"
41+
statusbar_visible="new,offer_received,offer_accepted,sold"
42+
options="{'clickable': '1'}"/>
3443
</header>
3544
<sheet>
36-
<h1 >
37-
<field name="name" />
45+
<h1>
46+
<field name="name"/>
3847
</h1>
3948
<div class="mb32">
40-
<field name="tag_ids" widget="many2many_tags"/>
49+
<field name="tag_ids" widget="many2many_tags"
50+
options="{'color_field': 'color'}"/>
4151
</div>
4252
<group>
4353
<group>
44-
<field name="postcode" />
45-
<field name="date_availability" />
46-
<field name="state" />
54+
<field name="postcode"/>
55+
<field name="date_availability" optional="hide"/>
56+
<field name="property_type_id" options="{'no_create': true}"/>
4757
</group>
4858
<group>
49-
<field name="expected_price" />
50-
<field name="selling_price" />
51-
<field name="best_price" />
59+
<field name="expected_price"/>
60+
<field name="selling_price"/>
61+
<field name="best_price"/>
5262
</group>
5363
</group>
5464
<notebook>
5565
<page string="Description">
5666
<group>
5767
<group>
58-
<field name="bedrooms" />
59-
<field name="living_area" />
60-
<field name="facades" />
61-
<field name="property_type_id" string="Property Type"/>
62-
<field name="description" />
68+
<field name="bedrooms"/>
69+
<field name="living_area"/>
70+
<field name="facades"/>
71+
<field name="description"/>
6372
</group>
6473
<group>
65-
<field name="garage" />
66-
<field name="garden" />
67-
<field name="garden_area" />
68-
<field name="garden_orientation" />
69-
<field name="total_area" />
70-
<field name="active" invisible="1" />
74+
<field name="garage"/>
75+
<field name="garden"/>
76+
<field name="garden_area" invisible="not garden"/>
77+
<field name="garden_orientation" invisible="not garden"/>
78+
<field name="total_area"/>
79+
<field name="active" invisible="1"/>
7180
</group>
7281
</group>
7382
</page>
7483
<page string="Offers">
75-
<group>
76-
<field name="offer_ids"/>
77-
</group>
84+
<field name="offer_ids"
85+
readonly="state in ('offer_accepted', 'sold', 'cancelled')"/>
7886
</page>
7987
<page string="Other Info">
8088
<group>
@@ -97,11 +105,12 @@
97105
<field name="postcode" string="Postcode"/>
98106
<field name="expected_price" string="Expected Price"/>
99107
<field name="bedrooms" string="Bedrooms"/>
100-
<field name="living_area" string="Living Area (sqm)"/>
108+
<field name="living_area" string="Living Area (sqm)"
109+
filter_domain="[('living_area', '>=', self)]"/>
101110
<field name="facades" string="Facades"/>
102111
<separator/>
103-
<!-- <filter domain="['|', ('state', '=', 'new'), ('state', '=', 'offer_received'),]" name="available" string="Available"/> -->
104-
<filter domain="[('state', 'in', ['new', 'offer_received'])]" name="available" string="Available"/>
112+
<filter domain="[('state', 'in', ['new', 'offer_received'])]" name="available"
113+
string="Available"/>
105114
<separator/>
106115
<group expand="1" string="Group By">
107116
<filter string="Postcode" name="postcode" context="{'group_by':'postcode'}"/>

0 commit comments

Comments
 (0)