Skip to content

Commit 00f4b88

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 8cd5541 commit 00f4b88

9 files changed

+212
-34
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: 42 additions & 4 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="_best_property_offer", 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:
@@ -120,6 +132,32 @@ def action_cancel_property_selling(self):
120132
}
121133
for record in self:
122134
if property_cancel_status_dict[record.status]:
123-
record.status = "cancelled"
135+
record.status = 'cancelled'
136+
return
137+
raise UserError('Sold property cannot be cancelled.')
138+
139+
140+
141+
# constrains for the selling price
142+
143+
@api.constrains('selling_price', 'expected_price')
144+
145+
def _check_selling_price(self):
146+
147+
for data in self:
148+
# if call will come after selling price change than it will allow updated price to work
149+
if data.selling_price <= 0 :
150+
return
151+
152+
price_float_ratio = (data.selling_price/self.expected_price)
153+
ratio_diffrence = float_compare(price_float_ratio,0.9, precision_digits=2)
154+
if ratio_diffrence == -1 :
155+
data.selling_price = 0
156+
raise ValidationError('The selling price cannot be lower than 90% of the expected price')
124157
return
125-
raise UserError("Sold property cannot be cancelled.")
158+
159+
160+
161+
162+
163+

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: 25 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -5,39 +5,27 @@
55
<field name="name">Properties</field>
66
<field name="res_model">estate.property</field>
77
<field name="view_mode">list,form</field>
8-
</record>
9-
8+
<field name="context">{'search_default_available': 1}</field>
9+
1010

11-
<record id="estate_property_type_id" model="ir.actions.act_window">
12-
<field name="name">Property Type</field>
13-
<field name="res_model">estate.property.type</field>
14-
<field name="view_mode">list,form</field>
1511
</record>
1612

17-
18-
<record id="estate_property_tag_id" model="ir.actions.act_window">
19-
20-
<field name="name">Property Tag</field>
21-
<field name="res_model">estate.property.tag</field>
22-
<field name="view_mode">list,form</field>
23-
</record>
24-
25-
26-
2713
<!-- *********************** End of Actions ******** -->
2814

2915
<record id="estate_property_detail_view" model="ir.ui.view">
3016
<field name="name">estate.property.list</field>
3117
<field name="model">estate.property</field>
3218
<field name="arch" type="xml">
33-
<list string="Channel">
19+
<list string="Channel" decoration-success="state in ('offer_received', 'offer_accepted')"
20+
decoration-bf="state == 'offer_accepted'"
21+
decoration-muted="state == 'sold'">
3422
<field name="name"/>
3523
<field name="postcode"/>
3624
<field name="bedrooms"/>
3725
<field name="living_area"/>
3826
<field name="expected_price"/>
3927
<field name="selling_price"/>
40-
<field name="date_availability"/>
28+
<field name="date_availability" optional="hidden"/>
4129
</list>
4230
</field>
4331
</record>
@@ -53,6 +41,7 @@
5341
<header>
5442
<button name="action_sell_property" type="object" string="Sold"/>
5543
<button name="action_cancel_property_selling" type="object" string="Cancel"/>
44+
<field name="state" widget="statusbar" statusbar_visible="new,offer_received,offer_accepted, sold"/>
5645
</header>
5746
<sheet>
5847
<div class="oe_title">
@@ -62,7 +51,7 @@
6251
</div>
6352

6453
<group>
65-
<field name="tag_ids" widget="many2many_tags"/>
54+
<field name="tag_ids" widget="many2many_tags" options="{'color_field': 'color'}"/>
6655
</group>
6756

6857
<group>
@@ -100,8 +89,8 @@
10089
<field name="facades"/>
10190
<field name="garage"/>
10291
<field name="garden"/>
103-
<field name="garden_area"/>
104-
<field name="garden_orientation"/>
92+
<field name="garden_area" invisible="not garden"/>
93+
<field name="garden_orientation" invisible="not garden"/>
10594
<field name="active"/>
10695
<field name="state"/>
10796
<field name="total_area"/>
@@ -111,13 +100,19 @@
111100

112101

113102
<page string="Offer">
114-
<field name="offer_ids">
115-
<list>
103+
<field name="offer_ids" readonly="state in ('offer_accepted', 'sold', 'cancelled')" >
104+
<list editable="bottom" decoration-red="status in ('refused')"
105+
decoration-success="status in ('accepted')"
106+
>
116107
<field name="price"/>
117108
<field name="partner_id"/>
118-
<field name="status"/>
119-
<button name="action_offer_confirm" string="Accept" states="draft" type="object" icon="fa-check"/>
120-
<button name="action_offer_refuse" string="Refuse" states="draft" type="object" icon="fa-times"/>
109+
<!-- <field name="status" invisible="True"/> -->
110+
<button name="action_offer_confirm" string="Accept" states="draft" type="object" icon="fa-check"
111+
invisible="status in ('accepted','refused')"
112+
/>
113+
<button name="action_offer_refuse" string="Refuse" states="draft" type="object" icon="fa-times"
114+
invisible="status in ('accepted','refused')"
115+
/>
121116
</list>
122117
</field>
123118
</page>
@@ -135,18 +130,20 @@
135130
</field>
136131
</record>
137132

138-
139133
<!-- **************************************** search view ******************************-->
140134

141135
<record id="estate_property_detail_view_search" model="ir.ui.view">
142136
<field name="name">estate.property.search</field>
143137
<field name="model">estate.property</field>
144138
<field name="arch" type="xml">
145139
<search string="Property Search">
140+
146141
<field name="name"/>
147142
<field name="postcode"/>
148143
<field name="expected_price"/>
149-
<field name="living_area"/>
144+
<field name="living_area"
145+
filter_domain="[ ('living_area', '>=', self)]"
146+
/>
150147
<field name="facades"/>
151148
<separator/>
152149
<filter string="Available" name="available" domain="

0 commit comments

Comments
 (0)