Skip to content

Commit d2ac217

Browse files
committed
[IMP] estate: added computed fields,inverse fun & onchange
-Added computed fields with inverse functions and onchange methods, -Improved real estate module with sold and cancel buttons to enhance the module functionality.
1 parent dacf930 commit d2ac217

10 files changed

+154
-31
lines changed

estate/__init__.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1 @@
11
from . import models
2-

estate/__manifest__.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
{
22
'name': 'Real Estate',
33
'depends': ['base'],
4-
'license': 'AGPL-3',
54
'application': True,
65
'installable': True,
76
'author': 'Odoo S.A.',
8-
'category':'Tutorials',
7+
'license': 'LGPL-3',
98
'data': [
109
'security/ir.model.access.csv',
1110
'views/estate_property_views.xml',
@@ -14,4 +13,6 @@
1413
'views/estate_property_offer_views.xml',
1514
'views/estate_menus.xml'
1615
],
16+
1717
}
18+

estate/models/estate_property.py

Lines changed: 85 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1-
from odoo import models, fields
21
from dateutil.relativedelta import relativedelta
2+
from odoo import models, fields, api
3+
from odoo.exceptions import UserError, ValidationError
4+
from odoo.tools.float_utils import float_compare, float_is_zero
35

46

57
class EstateProperty(models.Model):
@@ -9,15 +11,18 @@ class EstateProperty(models.Model):
911
name = fields.Char(required=True)
1012
description = fields.Text()
1113
postcode = fields.Char()
12-
date_availability = fields.Date("Availability Date", default=fields.Date.today() + relativedelta(months=3))
14+
date_availability = fields.Date(
15+
"Availability Date",
16+
default=lambda self: fields.Date.today() + relativedelta(months=3)
17+
)
1318
expected_price = fields.Float("Expected Price", required=True)
1419
selling_price = fields.Float("Selling Price", readonly=True)
1520
bedrooms = fields.Integer(default=2)
16-
living_area = fields.Integer("Living Area(sqm)")
21+
living_area = fields.Integer("Living Area (sqm)")
1722
facades = fields.Integer()
1823
garage = fields.Boolean()
1924
garden = fields.Boolean()
20-
garden_area = fields.Integer("Garden Area(sqm)")
25+
garden_area = fields.Integer("Garden Area (sqm)")
2126
garden_orientation = fields.Selection(
2227
selection=[
2328
("north", "North"),
@@ -41,9 +46,81 @@ class EstateProperty(models.Model):
4146
default="new",
4247
)
4348
active = fields.Boolean(default=True)
44-
property_type_id = fields.Many2one("estate.property.type", string="Property Type")
45-
buyer_id = fields.Many2one("res.partner", string="Buyer", copy=False)
46-
salesperson_id = fields.Many2one(
47-
"res.users", string="Salesperson")
49+
property_type_id = fields.Many2one("estate.property.type", "Property Type")
50+
buyer_id = fields.Many2one("res.partner", "Buyer", copy=False)
51+
salesperson_id = fields.Many2one("res.users", string="Salesperson")
4852
tag_ids = fields.Many2many("estate.property.tag", string="Tags")
4953
offer_ids = fields.One2many("estate.property.offer", "property_id", string="Offers")
54+
total_area = fields.Integer("Total Area (sqm)", compute="_compute_total_area")
55+
best_price = fields.Float("Best Offer", compute="_compute_best_price")
56+
57+
@api.depends("living_area", "garden_area")
58+
def _compute_total_area(self):
59+
for record in self:
60+
record.total_area = (record.living_area or 0) + (record.garden_area or 0)
61+
62+
@api.depends("offer_ids.price")
63+
def _compute_best_price(self):
64+
for record in self:
65+
record.best_price = max(record.offer_ids.mapped("price") or [0])
66+
67+
@api.onchange("garden")
68+
def _onchange_garden(self):
69+
for record in self:
70+
if record.garden:
71+
if record.garden_area == 10:
72+
record.garden_orientation = "north"
73+
elif record.garden_area == 20:
74+
record.garden_orientation = "south"
75+
elif record.garden_area == 30:
76+
record.garden_orientation = "east"
77+
elif record.garden_area == 40:
78+
record.garden_orientation = "west"
79+
else:
80+
record.garden_area = 0
81+
record.garden_orientation = False
82+
else:
83+
record.garden_area = 0
84+
record.garden_orientation = False
85+
86+
def action_cancel(self):
87+
for record in self:
88+
if record.state == "sold":
89+
raise UserError("A sold property cannot be cancelled")
90+
record.state = "cancelled"
91+
92+
def action_sold(self):
93+
for record in self:
94+
if record.state == "cancelled":
95+
raise UserError("A cancelled property cannot be set as sold")
96+
record.state = "sold"
97+
98+
_sql_constraints = [
99+
(
100+
"check_expected_price",
101+
"CHECK(expected_price > 0)",
102+
"The expected price must be strictly positive.",
103+
),
104+
(
105+
"check_selling_price_positive",
106+
"CHECK(selling_price >= 0)",
107+
"The selling price must be positive.",
108+
),
109+
]
110+
111+
@api.constrains("selling_price", "expected_price")
112+
def _check_selling_price_ratio(self):
113+
for record in self:
114+
if not float_is_zero(record.selling_price, precision_digits=2):
115+
if (
116+
float_compare(
117+
record.selling_price,
118+
record.expected_price * 0.9,
119+
precision_digits=2,
120+
)
121+
< 0
122+
):
123+
raise ValidationError(
124+
"The selling price cannot be lower than 90% of the expected price."
125+
)
126+

estate/models/estate_property_offer.py

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
from odoo import fields, models
1+
from dateutil.relativedelta import relativedelta
2+
3+
from odoo import fields, models, api
4+
from odoo.exceptions import UserError
25

36

47
class EstatePropertyOffer(models.Model):
@@ -12,4 +15,36 @@ class EstatePropertyOffer(models.Model):
1215
)
1316
partner_id = fields.Many2one("res.partner", string="Partner", required=True)
1417
property_id = fields.Many2one("estate.property", string="Property", required=True)
15-
18+
validity = fields.Integer(string="Validity(days)", default=7)
19+
date_deadline = fields.Date(string="Deadline Date", compute="_compute_date_deadline", inverse="_inverse_date_deadline")
20+
21+
@api.depends('validity')
22+
def _compute_date_deadline(self):
23+
for record in self:
24+
creation_date = record.create_date or fields.Date.today()
25+
record.date_deadline = relativedelta(days=record.validity) + creation_date
26+
27+
def _inverse_date_deadline(self):
28+
for record in self:
29+
creation_date = record.create_date or fields.Date.today()
30+
record.validity = (record.date_deadline - fields.Date.to_date(creation_date)).days
31+
32+
def action_accept(self):
33+
for record in self:
34+
if record.property_id.buyer_id:
35+
raise UserError("Property already accepted")
36+
else:
37+
record.status = 'accepted'
38+
record.property_id.selling_price = record.price
39+
record.property_id.state = 'offer_accepted'
40+
record.property_id.buyer_id= record.partner_id
41+
42+
def action_refuse(self):
43+
for record in self:
44+
record.status = 'refused'
45+
return True
46+
47+
_check_offer_price = models.Constraint(
48+
'CHECK(price > 0)',
49+
'The price of an offer must be strictly positive.'
50+
)

estate/models/estate_property_tag.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,8 @@ class EstatePropertyTag(models.Model):
66
_description = "Real Estate Property Tag"
77

88
name = fields.Char(required=True)
9+
10+
_check_tag_name_unique = models.Constraint(
11+
'UNIQUE(name)',
12+
'The name of the property tag must be unique.'
13+
)

estate/models/estate_property_type.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,8 @@ class EstatePropertyType(models.Model):
66
_description = "Real Estate Property Type"
77

88
name = fields.Char(required=True)
9+
10+
_check_type_name_unique = models.Constraint(
11+
'UNIQUE(name)',
12+
'The name of the property type must be unique.'
13+
)

estate/security/ir.model.access.csv

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
22
estate.access_estate_property,access_estate_property,estate.model_estate_property,base.group_user,1,1,1,1
33
estate.access_estate_property_type,access_estate_property_type,estate.model_estate_property_type,base.group_user,1,1,1,1
44
access_estate_property_tag,estate.property.tag.access,model_estate_property_tag,base.group_user,1,1,1,1
5-
access_estate_property_offer,estate.property.offer.access,model_estate_property_offer,base.group_user,1,1,1,1
5+
access_estate_property_offer,estate.property.offer.access,model_estate_property_offer,base.group_user,1,1,1,1

estate/views/estate_property_offer_views.xml

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77
<field name="price" />
88
<field name="partner_id" />
99
<field name="status" />
10+
<field name="validity" />
11+
<field name="date_deadline" />
12+
<button name="action_accept" type="object" string="Accept" icon="fa-check"/>
13+
<button name="action_refuse" type="object" string="Refuse" icon="fa-times"/>
1014
</list>
1115
</field>
1216
</record>
@@ -18,12 +22,15 @@
1822
<form>
1923
<sheet>
2024
<group>
21-
<field name="price"/>
22-
<field name="partner_id"/>
23-
<field name="status"/>
25+
<field name="price" />
26+
<field name="partner_id" />
27+
<field name="status" />
28+
<field name="validity" />
29+
<field name="date_deadline" />
2430
</group>
2531
</sheet>
2632
</form>
2733
</field>
2834
</record>
2935
</odoo>
36+

estate/views/estate_property_tag_views.xml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,4 @@
44
<field name="res_model">estate.property.tag</field>
55
<field name="view_mode">list,form</field>
66
</record>
7-
</odoo>
8-
7+
</odoo>

estate/views/estate_property_views.xml

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,20 @@
3333
<field name="model">estate.property</field>
3434
<field name="arch" type="xml">
3535
<form string="form">
36+
<header>
37+
<button name="action_sold" type="object" string="Sold" />
38+
<button name="action_cancel" type="object" string="Cancel" />
39+
</header>
3640
<sheet>
3741
<group>
3842
<group>
3943
<field name="name" />
4044
<field name="postcode" />
4145
<field name="date_availability" />
4246
<field name="property_type_id" />
47+
<field name="state" />
4348
<field name="tag_ids" widget="many2many_tags" />
49+
<field name="best_price" />
4450
</group>
4551
<group>
4652
<field name="expected_price" />
@@ -52,6 +58,7 @@
5258
<field name="garden" />
5359
<field name="garden_area" />
5460
<field name="garden_orientation" />
61+
<field name="total_area" />
5562
</group>
5663
</group>
5764
<notebook>
@@ -61,18 +68,6 @@
6168

6269
<page string="Offers">
6370
<field name="offer_ids">
64-
<list>
65-
<field name="price" />
66-
<field name="partner_id" />
67-
<field name="status" />
68-
</list>
69-
<form>
70-
<group>
71-
<field name="price" />
72-
<field name="partner_id" />
73-
<field name="status" />
74-
</group>
75-
</form>
7671
</field>
7772
</page>
7873

0 commit comments

Comments
 (0)