Skip to content

Commit 5eb85ca

Browse files
committed
[IMP] estate: add data constraints and UI Changes
Added SQL constraints to ensure: Property expected price is strictly positive, Property selling price is positive, Offer price is strictly positive, Property tag name and property type name are unique, , Added Python constraint to prevent selling price from being set below 90% of expected price, Changes in UI: Added inline list view for properties on property type form, Used statusbar widget for property state display, Defined default ordering for models and enabled manual ordering for property types via sequence field, Applied widget options to restrict creation/editing of property types from property form
1 parent 7e42acb commit 5eb85ca

9 files changed

+203
-16
lines changed

estate/__manifest__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
"installable": True,
66
"data": [
77
"views/estate_property_views.xml",
8+
"views/estate_property_tag_views.xml",
9+
"views/estate_property_offer_views.xml",
10+
"views/estate_property_type_views.xml",
811
"views/estate_menus.xml",
912
"security/ir.model.access.csv",
1013
],

estate/models/estate_property.py

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

46

57
class EstateProperty(models.Model):
@@ -42,7 +44,7 @@ class EstateProperty(models.Model):
4244
copy=False,
4345
)
4446

45-
# property will have Many to one relation with property since many properties can belong to one property
47+
# property will have Many to one relation with property since many properties can belong to one property type
4648

4749
property_type_id = fields.Many2one("estate.property.type", "Property Type")
4850

@@ -69,8 +71,18 @@ class EstateProperty(models.Model):
6971

7072
best_price = fields.Integer(compute="_compute_best_price", string="Best Price")
7173

74+
7275
status = fields.Char(default="new", string="Status")
7376

77+
_order = "id desc"
78+
79+
80+
_sql_constraints = [('check_expected_price', 'CHECK(expected_price > 0)', 'Expected price must be strictly positive'),
81+
('check_selling_price', 'CHECK(selling_price >= 0)', 'Selling price should be positive')
82+
]
83+
84+
85+
7486
@api.depends("garden_area", "living_area")
7587
def _compute_total_property_area(self):
7688
for area in self:
@@ -103,7 +115,6 @@ def action_sell_property(self):
103115
property_sell_status_dict = {"new": True, "sold": True, "cancelled": False}
104116

105117
for record in self:
106-
print("the object on sell action", record.read())
107118
if property_sell_status_dict[record.status]:
108119
record.status = "sold"
109120
else:
@@ -123,3 +134,23 @@ def action_cancel_property_selling(self):
123134
record.status = "cancelled"
124135
else:
125136
raise UserError("Sold property cannot be cancelled.")
137+
138+
# constrains for the selling price
139+
140+
@api.constrains('selling_price', 'expected_price')
141+
142+
def _check_selling_price(self):
143+
144+
for data in self:
145+
# if call will come after selling price change than it will allow updated price to work
146+
if data.selling_price <= 0 :
147+
return
148+
149+
price_float_ratio = (data.selling_price/self.expected_price)
150+
ratio_diffrence = float_compare(price_float_ratio,0.9, precision_digits=2)
151+
if ratio_diffrence == -1 :
152+
data.selling_price = 0
153+
raise ValidationError('The selling price cannot be lower than 90% of the expected price')
154+
155+
156+

estate/models/estate_property_offer.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,21 @@ class EstatePropertyOffer(models.Model):
2525

2626
property_id = fields.Many2one('estate.property', string ='Property')
2727

28+
property_type_id = fields.Many2one("estate.property.type", related="property_id.property_type_id",store=True,readonly=False)
29+
2830
validity = fields.Integer(default= 7)
2931

3032
date_deadline = fields.Date(compute = '_compute_offer_deadline', inverse = '_deadline_update')
3133

3234

35+
# constrains of sql
3336

37+
_sql_constraints = [('check_price', 'CHECK(price > 0)', 'Offered price must be strictly positive')
38+
]
3439

40+
# order in which data is fetched
41+
42+
_order = "price desc"
3543

3644

3745

@@ -64,6 +72,7 @@ def action_offer_confirm(self):
6472
record.status = 'accepted'
6573
record.property_id.selling_price = self.price
6674
record.property_id.partner_id = record.partner_id
75+
record.property_id.state = 'offer_accepted'
6776

6877
# action for the refusal of the status
6978
def action_offer_refuse(self):

estate/models/estate_property_tag.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,8 @@ class EstatePropertyTag(models.Model):
1212
_name = "estate.property.tag"
1313
_description = "Tag model for the estate properties"
1414
name = fields.Char(required=True)
15+
color = fields.Integer(default=1)
16+
_sql_constraints = [('check_uniquness', ' UNIQUE(name)', 'Tag name must be unique')]
17+
18+
# order on which data will be fetched
19+
_order = "name desc"

estate/models/estate_property_type.py

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,33 @@
1-
from odoo import fields, models
1+
from odoo import fields, models, api
22

33
# Property type model for properties
44
# prpoerties can be of type house, penthouse, etc.
55
class EstatePropertyType(models.Model):
66
_name = "estate.property.type"
77
_description = "Type of properties of estate model"
8-
name = fields.Char(required=True)
8+
name = fields.Char(required=True)
9+
10+
property_ids = fields.One2many('estate.property', 'property_type_id')
11+
12+
offer_ids = fields.One2many("estate.property.offer", "property_type_id", "Offer For Property Type")
13+
14+
offer_count = fields.Integer(compute = "_compute_offer_count")
15+
sequence = fields.Integer('Sequence', default=1)
16+
17+
# sql constrains :::
18+
19+
_sql_constraints = [('check_uniquness', ' UNIQUE(name)', 'Type of property name must be unique')]
20+
21+
# order on which data will be fetched
22+
23+
_order = "sequence, name desc"
24+
25+
26+
@api.depends("offer_ids")
27+
28+
def _compute_offer_count(self):
29+
for data in self:
30+
data.offer_count = len(data.offer_ids)
31+
32+
33+
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
2+
3+
<odoo>
4+
<record id="estate_property_offer_view" model="ir.actions.act_window">
5+
<field name="name">Offers</field>
6+
<field name="res_model">estate.property.offer</field>
7+
<field name="view_mode">list,form</field>
8+
</record>
9+
10+
<record id="estate_property_offer_list_view" model="ir.ui.view">
11+
<field name="name">estate.property.list</field>
12+
<field name="model">estate.property.offer</field>
13+
<field name="arch" type="xml">
14+
<list string="offer_chanel" >
15+
<field name="price"/>
16+
<field name="partner_id"/>
17+
<field name="validity"/>
18+
<field name="date_deadline"/>
19+
20+
</list>
21+
</field>
22+
</record>
23+
</odoo>
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
2+
3+
<odoo>
4+
5+
<record id="estate_property_tag_id" model="ir.actions.act_window">
6+
<field name="name">Property Tag</field>
7+
<field name="res_model">estate.property.tag</field>
8+
<field name="view_mode">list,form</field>
9+
</record>
10+
11+
12+
</odoo>
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
2+
3+
4+
5+
6+
<odoo>
7+
8+
<record id="estate_property_type_id" model="ir.actions.act_window">
9+
<field name="name">Property Type</field>
10+
<field name="res_model">estate.property.type</field>
11+
<field name="view_mode">list,form</field>
12+
</record>
13+
14+
15+
<record id="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="Property Types">
20+
<field name="sequence" widget="handle"/>
21+
<field name="name"/>
22+
</list >
23+
</field>
24+
</record>
25+
26+
27+
<record id="estate_property_type_view_form" model="ir.ui.view">
28+
<field name="name">estate.property.type.form</field>
29+
<field name="model">estate.property.type</field>
30+
<field name="arch" type="xml">
31+
<form string = 'Property Type'>
32+
<sheet>
33+
<div class="oe_button_box" name="button_box">
34+
<button name="%(estate_property_offer_view)d"
35+
type="action"
36+
string="Offers"
37+
class="oe_stat_button"
38+
icon="fa-envelope"
39+
context="{'default_property_type_id': id}"
40+
>
41+
<field name="offer_count" widget="statinfo" string="Offers"/>
42+
</button>
43+
</div>
44+
45+
<div class="oe_title">
46+
<h1>
47+
<field name="name" />
48+
</h1>
49+
</div>
50+
<notebook>
51+
<page string='Properties'>
52+
<field name="property_ids" options="{'no_create': True, 'no_edit': True}">
53+
<list >
54+
<field name="name"/>
55+
<field name="expected_price"/>
56+
<field name="status"/>
57+
</list>
58+
</field>
59+
</page>
60+
</notebook>
61+
</sheet>
62+
</form>
63+
</field>
64+
</record>
65+
66+
</odoo>

estate/views/estate_property_views.xml

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
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': 1}</field>
67
</record>
78

89
<record id="estate_property_type_id" model="ir.actions.act_window">
@@ -22,14 +23,16 @@
2223
<field name="name">estate.property.list</field>
2324
<field name="model">estate.property</field>
2425
<field name="arch" type="xml">
25-
<list string="Channel">
26+
<list string="Channel" decoration-success="state in ('offer_received', 'offer_accepted')"
27+
decoration-bf="state == 'offer_accepted'"
28+
decoration-muted="state == 'sold'">
2629
<field name="name"/>
2730
<field name="postcode"/>
2831
<field name="bedrooms"/>
2932
<field name="living_area"/>
3033
<field name="expected_price"/>
3134
<field name="selling_price"/>
32-
<field name="date_availability"/>
35+
<field name="date_availability" optional="hidden"/>
3336
</list>
3437
</field>
3538
</record>
@@ -42,6 +45,7 @@
4245
<header>
4346
<button name="action_sell_property" type="object" string="Sold"/>
4447
<button name="action_cancel_property_selling" type="object" string="Cancel"/>
48+
<field name="state" widget="statusbar" statusbar_visible="new,offer_received,offer_accepted, sold"/>
4549
</header>
4650

4751
<sheet>
@@ -52,7 +56,7 @@
5256
</div>
5357

5458
<group>
55-
<field name="tag_ids" widget="many2many_tags"/>
59+
<field name="tag_ids" widget="many2many_tags" options="{'color_field': 'color'}"/>
5660
</group>
5761

5862
<group>
@@ -90,8 +94,8 @@
9094
<field name="facades"/>
9195
<field name="garage"/>
9296
<field name="garden"/>
93-
<field name="garden_area"/>
94-
<field name="garden_orientation"/>
97+
<field name="garden_area" invisible="not garden"/>
98+
<field name="garden_orientation" invisible="not garden"/>
9599
<field name="active"/>
96100
<field name="state"/>
97101
<field name="total_area"/>
@@ -101,13 +105,19 @@
101105

102106

103107
<page string="Offer">
104-
<field name="offer_ids">
105-
<list>
108+
<field name="offer_ids" readonly="state in ('offer_accepted', 'sold', 'cancelled')" >
109+
<list editable="bottom" decoration-red="status in ('refused')"
110+
decoration-success="status in ('accepted')"
111+
>
106112
<field name="price"/>
107113
<field name="partner_id"/>
108-
<field name="status"/>
109-
<button name="action_offer_confirm" string="Accept" states="draft" type="object" icon="fa-check"/>
110-
<button name="action_offer_refuse" string="Refuse" states="draft" type="object" icon="fa-times"/>
114+
<!-- <field name="status" invisible="True"/> -->
115+
<button name="action_offer_confirm" string="Accept" states="draft" type="object" icon="fa-check"
116+
invisible="status in ('accepted','refused')"
117+
/>
118+
<button name="action_offer_refuse" string="Refuse" states="draft" type="object" icon="fa-times"
119+
invisible="status in ('accepted','refused')"
120+
/>
111121
</list>
112122
</field>
113123
</page>
@@ -130,10 +140,13 @@
130140
<field name="model">estate.property</field>
131141
<field name="arch" type="xml">
132142
<search string="Property Search">
143+
133144
<field name="name"/>
134145
<field name="postcode"/>
135146
<field name="expected_price"/>
136-
<field name="living_area"/>
147+
<field name="living_area"
148+
filter_domain="[ ('living_area', '>=', self)]"
149+
/>
137150
<field name="facades"/>
138151
<separator/>
139152
<filter string="Available" name="available" domain="

0 commit comments

Comments
 (0)