Skip to content

Commit d0a617c

Browse files
committed
[IMP] estate: Chapter 11
1 parent 19b9db7 commit d0a617c

8 files changed

+134
-43
lines changed

estate/models/estate_properties.py

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
class PropertyModel(models.Model):
1010
_name = "estate.property"
1111
_description = "Estate Property model"
12+
_order = "id desc"
1213
_check_positive_expected_price = models.Constraint(
1314
"CHECK(expected_price >= 0)",
1415
"The expected price must be positive."
@@ -18,23 +19,23 @@ class PropertyModel(models.Model):
1819
"The selling price must be positive"
1920
)
2021

21-
name = fields.Char(required=True)
22+
name = fields.Char("Title", required=True)
2223
description = fields.Text()
2324
postcode = fields.Char()
2425
date_availability = fields.Date(default=fields.Date.add(fields.Date.today(), months=3), copy=False)
2526
expected_price = fields.Float(required=True)
2627
best_offer = fields.Float(compute="_get_highest_price")
2728
selling_price = fields.Float(readonly=True, copy=False)
2829
bedrooms = fields.Integer(default=2)
29-
living_area = fields.Integer()
30+
living_area = fields.Integer("Living Area (sqm)")
3031
facades = fields.Integer()
3132
garage = fields.Boolean()
3233
garden = fields.Boolean()
33-
garden_area = fields.Integer()
34+
garden_area = fields.Integer("Garden Area (sqm)")
3435
garden_orientation = fields.Selection(
3536
selection=[('north', 'North'), ('south', 'South'), ('east', 'East'), ('west', 'West')]
3637
)
37-
total_living_area = fields.Integer(compute="_compute_total_area")
38+
total_living_area = fields.Integer("Total Area (sqm)", compute="_compute_total_area")
3839

3940
active = fields.Boolean(default=True)
4041
state = fields.Selection(
@@ -45,6 +46,7 @@ class PropertyModel(models.Model):
4546
("sold", "Sold"),
4647
("cancelled", "Cancelled")
4748
],
49+
string="Status",
4850
required=True,
4951
copy=False,
5052
default="new"
@@ -67,26 +69,25 @@ def _get_highest_price(self):
6769

6870
@api.onchange("garden")
6971
def _update_garden_area_and_orientation(self):
70-
for record in self:
71-
if record.garden:
72-
record.garden_area = DEFAULT_GARDEN_AREA
73-
record.garden_orientation = DEFAULT_GARDEN_ORIENTATION
74-
else:
75-
record.garden_area = 0
76-
record.garden_orientation = None
72+
if self.garden:
73+
self.garden_area = DEFAULT_GARDEN_AREA
74+
self.garden_orientation = DEFAULT_GARDEN_ORIENTATION
75+
else:
76+
self.garden_area = 0
77+
self.garden_orientation = None
7778

7879
def mark_as_sold(self):
79-
for record in self:
80-
if record.state == "cancelled":
81-
raise UserError("A cancelled property cannot be set as sold.")
82-
record.state = "sold"
80+
self.ensure_one()
81+
if self.state == "cancelled":
82+
raise UserError("A cancelled property cannot be set as sold.")
83+
self.state = "sold"
8384
return True
8485

8586
def mark_as_cancelled(self):
86-
for record in self:
87-
if record.state == "sold":
88-
raise UserError("A sold property cannot be set as cancelled.")
89-
record.state = "cancelled"
87+
self.ensure_one()
88+
if self.state == "sold":
89+
raise UserError("A sold property cannot be set as cancelled.")
90+
self.state = "cancelled"
9091
return True
9192

9293
@api.constrains("selling_price", "expected_price")

estate/models/estate_property_offers.py

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
class PropertyOfferModel(models.Model):
55
_name = "estate.property.offer"
66
_description = "Estate Property Offer model"
7+
_order = "price desc"
78

89
price = fields.Float()
910
status = fields.Selection(
@@ -15,8 +16,10 @@ class PropertyOfferModel(models.Model):
1516
)
1617
partner_id = fields.Many2one("res.partner", required=True)
1718
property_id = fields.Many2one("estate.property", required=True)
19+
property_type_id = fields.Many2one(related="property_id.property_type_id", store=True)
1820
validity = fields.Integer(default=7)
1921
date_deadline = fields.Date(compute="_compute_deadline", inverse="_inverse_deadline")
22+
property_state = fields.Selection(related="property_id.state")
2023
_check_price = models.Constraint(
2124
"CHECK(price >= 0)",
2225
"The price of the offer must be positive."
@@ -32,11 +35,18 @@ def _inverse_deadline(self):
3235
record.validity = (record.date_deadline - fields.Date.to_date(record.create_date)).days if record.date_deadline else record.validity
3336

3437
def accept_offer(self):
35-
for record in self:
36-
record.status = "accepted"
37-
record.property_id.selling_price = record.price
38-
record.property_id.buyer = record.partner_id
38+
self.ensure_one()
39+
self.status = "accepted"
40+
self.property_id.selling_price = self.price
41+
self.property_id.buyer = self.partner_id
42+
self.property_id.state = "accepted"
43+
self.refuse_all_other_offers()
44+
45+
def refuse_all_other_offers(self):
46+
for offer in self.property_id.offer_ids:
47+
if offer != self:
48+
offer.status = "refused"
3949

4050
def refuse_offer(self):
41-
for record in self:
42-
record.status = "refused"
51+
self.ensure_one()
52+
self.status = "refused"

estate/models/estate_property_tags.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@
44
class PropertyTagModel(models.Model):
55
_name = "estate.property.tag"
66
_description = "Estate Property Tag model"
7+
_order = "name"
78
_check_tag_uniqueness = models.Constraint(
89
"UNIQUE(name)",
910
"Each tag should have a unique name."
1011
)
1112

1213
name = fields.Char(required=True)
14+
color = fields.Integer()
Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,22 @@
1-
from odoo import fields, models
1+
from odoo import api, fields, models
22

33

44
class PropertyTypeModel(models.Model):
55
_name = "estate.property.type"
66
_description = "Estate Property Type model"
7+
_order = "sequence"
78
_check_type_uniqueness = models.Constraint(
89
"UNIQUE(name)",
910
"Each type should have a unique name."
1011
)
1112

1213
name = fields.Char(required=True)
14+
property_ids = fields.One2many("estate.property", "property_type_id")
15+
offer_ids = fields.One2many("estate.property.offer", "property_type_id")
16+
offer_count = fields.Integer("Offer Count", compute="_compute_offer_count")
17+
sequence = fields.Integer("Sequence")
18+
19+
@api.depends("offer_ids")
20+
def _compute_offer_count(self):
21+
for record in self:
22+
record.offer_count = len(record.offer_ids)

estate/views/estate_property_offer_views.xml

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,26 @@
11
<odoo>
2+
<record id="estate_property_offer_action" model="ir.actions.act_window">
3+
<field name="name">Property Offers</field>
4+
<field name="res_model">estate.property.offer</field>
5+
<field name="view_mode">list,form</field>
6+
<field name="domain">[('property_type_id', '=', active_id)]</field>
7+
</record>
8+
29
<record id="estate_property_offer_list_view" model="ir.ui.view">
310
<field name="name">estate.property.offer.list</field>
411
<field name="model">estate.property.offer</field>
512
<field name="arch" type="xml">
6-
<list string="Channel">
13+
<list string="Channel" editable="bottom"
14+
decoration-danger="status == 'refused'"
15+
decoration-success="status == 'accepted'">
716
<field name="price"/>
817
<field name="partner_id"/>
918
<field name="validity"/>
1019
<field name="date_deadline" string="Deadline"/>
11-
<button name="accept_offer" string="Accept" type="object" icon="fa-check"/>
12-
<button name="refuse_offer" string="Refuse" type="object" icon="oi-close"/>
13-
<field name="status"/>
20+
<button name="accept_offer" string="Accept" type="object" icon="fa-check" invisible="property_state in ('sold', 'cancelled')"/>
21+
<button name="refuse_offer" string="Refuse" type="object" icon="oi-close" invisible="property_state in ('sold', 'cancelled')"/>
22+
<field name="property_id" optional="hide"/>
23+
<field name="status" optional="hide"/>
1424
</list>
1525
</field>
1626
</record>

estate/views/estate_property_tag_views.xml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,15 @@
44
<field name="res_model">estate.property.tag</field>
55
<field name="view_mode">list,form</field>
66
</record>
7+
8+
<record id="estate_property_tag_list_view" model="ir.ui.view">
9+
<field name="name">estate.property.tag.list</field>
10+
<field name="model">estate.property.tag</field>
11+
<field name="arch" type="xml">
12+
<list string="Channel" editable="bottom">
13+
<field name="name"/>
14+
<field name="color"/>
15+
</list>
16+
</field>
17+
</record>
718
</odoo>

estate/views/estate_property_type_views.xml

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,45 @@
44
<field name="res_model">estate.property.type</field>
55
<field name="view_mode">list,form</field>
66
</record>
7+
8+
<record id="estate_property_type_form_view" model="ir.ui.view">
9+
<field name="name">estate.property.type.form</field>
10+
<field name="model">estate.property.type</field>
11+
<field name="arch" type="xml">
12+
<form string="Property type">
13+
<sheet>
14+
<button name="%(estate.estate_property_offer_action)d" string="Offers" type="action" title="Offers list" invisible="not offer_count > 0"/>
15+
<h1>
16+
<field name="name"/>
17+
</h1>
18+
<group>
19+
<field name="offer_count"/>
20+
</group>
21+
<notebook>
22+
<page string="Properties">
23+
<field name="property_ids">
24+
<list create="false" edit="false">
25+
<field name="name"/>
26+
<field name="expected_price"/>
27+
<field name="state"/>
28+
<field name="property_type_id" optional="hide"/>
29+
</list>
30+
</field>
31+
</page>
32+
</notebook>
33+
</sheet>
34+
</form>
35+
</field>
36+
</record>
37+
38+
<record id="estate_property_type_list_view" model="ir.ui.view">
39+
<field name="name">estate.property.type.list</field>
40+
<field name="model">estate.property.type</field>
41+
<field name="arch" type="xml">
42+
<list string="Channel">
43+
<field name="sequence" widget="handle"/>
44+
<field name="name"/>
45+
</list>
46+
</field>
47+
</record>
748
</odoo>

estate/views/estate_property_views.xml

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,24 @@
33
<field name="name">Properties</field>
44
<field name="res_model">estate.property</field>
55
<field name="view_mode">list,form</field>
6+
<field name="context">{'search_default_available': True}</field>
67
</record>
78

89
<record id="estate_property_action_list_view" model="ir.ui.view">
910
<field name="name">estate.property.list</field>
1011
<field name="model">estate.property</field>
1112
<field name="arch" type="xml">
12-
<list string="Channel">
13+
<list string="Channel"
14+
decoration-success="state in ('received', 'accepted')"
15+
decoration-bf="state == 'accepted'"
16+
decoration-muted="state == 'sold'">
1317
<field name="name"/>
1418
<field name="postcode"/>
1519
<field name="bedrooms"/>
1620
<field name="living_area"/>
1721
<field name="expected_price"/>
1822
<field name="selling_price"/>
19-
<field name="date_availability"/>
23+
<field name="date_availability" optional="hide"/>
2024
</list>
2125
</field>
2226
</record>
@@ -27,18 +31,20 @@
2731
<field name="arch" type="xml">
2832
<form string="Property">
2933
<header>
30-
<button name="mark_as_sold" type="object" string="Sold"/>
31-
<button name="mark_as_cancelled" type="object" string="Cancel"/>
34+
<button name="mark_as_sold" type="object" string="Sold" class="btn-primary" invisible="state != 'accepted'"/>
35+
<button name="mark_as_sold" type="object" string="Sold" invisible="state in ('sold', 'accepted', 'cancelled')"/>
36+
<button name="mark_as_cancelled" type="object" string="Cancel" invisible="state in ('sold', 'cancelled')"/>
3237
</header>
38+
<field name="state" widget="statusbar" statusbar_visible="new,received,accepted,sold"/>
3339
<sheet>
3440
<h1>
3541
<field name="name"/>
3642
</h1>
37-
<field name="tag_ids" widget="many2many_tags"/>
43+
<field name="tag_ids" widget="many2many_tags" options="{'color_field': 'color'}"/>
3844
<group>
3945
<group>
4046
<field name="state"/>
41-
<field name="property_type_id"/>
47+
<field name="property_type_id" options="{'no_create': true}"/>
4248
<field name="postcode"/>
4349
<field name="date_availability"/>
4450
</group>
@@ -53,18 +59,18 @@
5359
<group>
5460
<field name="description"/>
5561
<field name="bedrooms"/>
56-
<field name="living_area" string="Living Area (sqm)"/>
62+
<field name="living_area"/>
5763
<field name="facades"/>
5864
<field name="garage"/>
5965
<field name="garden"/>
60-
<field name="garden_area" string="Garden Area (sqm)"/>
61-
<field name="garden_orientation"/>
62-
<field name="total_living_area" string="Total Living Area (sqm)"/>
66+
<field name="garden_area" invisible="not garden"/>
67+
<field name="garden_orientation" invisible="not garden"/>
68+
<field name="total_living_area"/>
6369
</group>
6470
</page>
6571
<page string="Offers">
6672
<group>
67-
<field name="offer_ids"/>
73+
<field name="offer_ids" readonly="state in ('accepted', 'sold', 'cancelled')"/>
6874
</group>
6975
</page>
7076
<page string="Other info">
@@ -88,9 +94,9 @@
8894
<field name="postcode"/>
8995
<field name="expected_price"/>
9096
<field name="bedrooms"/>
91-
<field name="living_area"/>
97+
<field name="living_area" filter_domain="['|', ('living_area', '=', self), ('living_area', '&gt;', self)]"/>
9298
<field name="facades"/>
93-
<filter name="State" domain="[('state', 'in', ('new', 'received'))]"/>
99+
<filter name="available" string="Available" domain="[('state', 'in', ('new', 'received'))]"/>
94100
<filter name="postcode" context="{'group_by': 'postcode'}"/>
95101
</search>
96102
</field>

0 commit comments

Comments
 (0)