Skip to content

Commit 9a4342b

Browse files
author
SurryaT10
committed
[IMP] Chapter 11: Add The Sprinkles
Added ordering for the following: property: Descending by ID offer: Descending by price tag: Name type: Name Added sequence ordering for type Added color field for property tag. Added invisible attribute for Sold, Cancel, Accept, Reject buttons Added color decorations for list view. Added offer count button to show the list of offers for a property type Fixed code review changes.
1 parent 5cb3109 commit 9a4342b

10 files changed

+161
-65
lines changed

estate/__manifest__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,11 @@
55
'website': "https://www.odoo.com",
66

77
'category': 'Tutorials',
8-
'version': '0.1',
8+
'version': '1.0',
99

1010
'depends': ['base', 'web'],
1111
'data': [
12+
'views/estate_property_offer_view.xml',
1213
'views/estate_property_tag_views.xml',
1314
'views/estate_property_type_views.xml',
1415
'views/estate_property_search_view.xml',

estate/models/estate.py

Lines changed: 36 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@
44

55

66
class Estate(models.Model):
7-
_name = 'estate.property'
8-
_description = 'Estate Property'
7+
_name = "estate.property"
8+
_description = "Estate Property"
9+
_order = "id desc"
910

1011
name = fields.Char("Property Name", required=True)
1112
description = fields.Text("Description")
@@ -21,11 +22,22 @@ class Estate(models.Model):
2122
garden_area = fields.Integer("Garden Area (sqm)")
2223
garden_orientation = fields.Selection(
2324
string="Garden Orientation",
24-
selection=[('north', "North"), ('south', "South"), ('east', "East"), ('west', "West")],
25+
selection=[
26+
('north', "North"),
27+
('south', "South"),
28+
('east', "East"),
29+
('west', "West")
30+
],
2531
required=True)
2632
status = fields.Selection(
2733
string="Status",
28-
selection=[('new', "New"), ('offer_received', "Offer Received"), ('offer_accepted', "Offer Accepted"), ('sold', "Sold"), ('canceled', "Canceled")],
34+
selection=[
35+
('new', "New"),
36+
('offer_received', "Offer Received"),
37+
('offer_accepted', "Offer Accepted"),
38+
('sold', "Sold"),
39+
('canceled', "Canceled")
40+
],
2941
default='new',
3042
required=True)
3143
active = fields.Boolean("Active", default=True)
@@ -34,9 +46,19 @@ class Estate(models.Model):
3446
salesperson_id = fields.Many2one('res.users', string="Salesperson", default=lambda self: self.env.user)
3547
tag_ids = fields.Many2many('estate.property.tag', string="Tags")
3648
offer_ids = fields.One2many('estate.property.offer', 'property_id', string="Offers")
37-
total_area = fields.Integer('Total Area (sqm)', compute='_compute_total_area')
49+
total_area = fields.Integer("Total Area (sqm)", compute='_compute_total_area')
3850
best_price = fields.Float("Best Offer", compute='_compute_best_price')
3951

52+
_check_expected_price = models.Constraint(
53+
"CHECK(expected_price > 0)",
54+
"The expected price must be strictly positive"
55+
)
56+
57+
_check_selling_price = models.Constraint(
58+
"CHECK(selling_price >= 0)",
59+
"The selling price must be positive"
60+
)
61+
4062
@api.depends('living_area', 'garden_area')
4163
def _compute_total_area(self):
4264
for record in self:
@@ -52,10 +74,8 @@ def _onchange_property_status(self):
5274
for record in self:
5375
if record.status == 'new' and len(record.offer_ids) > 0:
5476
record.status = 'offer_received'
55-
else:
56-
record.status = record.status
5777

58-
@api.onchange("garden")
78+
@api.onchange('garden')
5979
def _onchange_garden(self):
6080
if self.garden:
6181
self.garden_area = 10
@@ -66,29 +86,18 @@ def _onchange_garden(self):
6686

6787
def action_mark_sold(self):
6888
for record in self:
69-
if record.status == 'canceled':
70-
raise UserError("Canceled properties cannot be sold.")
71-
record.status = 'sold'
89+
if record.status == "canceled":
90+
raise UserError(self.env_("Canceled properties cannot be sold."))
91+
record.status = "sold"
7292

7393
def action_mark_cancel(self):
7494
for record in self:
75-
if record.status == 'sold':
76-
raise UserError("Sold properties cannot be canceled.")
77-
record.status = 'canceled'
78-
79-
_check_expected_price = models.Constraint(
80-
'CHECK(expected_price > 0)',
81-
'The expected price must be strictly positive'
82-
)
83-
84-
_check_selling_price = models.Constraint(
85-
'CHECK(selling_price >= 0)',
86-
'The selling price must be positive'
87-
)
95+
if record.status == "sold":
96+
raise UserError(self.env_("Sold properties cannot be canceled."))
97+
record.status = "canceled"
8898

8999
@api.constrains('selling_price')
90100
def _check_selling_price(self):
91101
for record in self:
92-
if not float_is_zero(record.selling_price, precision_digits=2):
93-
if float_compare(record.selling_price, record.expected_price * 0.9, precision_digits=2) < 0:
94-
raise ValidationError('The selling price must be atleast 90% of the expected price! You must reduce the expected price to accept the offer')
102+
if not float_is_zero(record.selling_price, precision_digits=2) and float_compare(record.selling_price, record.expected_price * 0.9, precision_digits=2) < 0:
103+
raise ValidationError(self._env("The selling price must be atleast 90% of the expected price! You must reduce the expected price to accept the offer"))

estate/models/offer.py

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@
22

33

44
class Offer(models.Model):
5-
_name = 'estate.property.offer'
6-
_description = 'Estate Property Offer'
5+
_name = "estate.property.offer"
6+
_description = "Estate Property Offer"
7+
_order = "price desc"
78

89
price = fields.Float("Offer Price")
910
status = fields.Selection(
@@ -14,6 +15,12 @@ class Offer(models.Model):
1415
property_id = fields.Many2one('estate.property', string="Property", required=True)
1516
validity = fields.Integer('Validity (days)', default=7)
1617
date_deadline = fields.Date("Deadline", compute='_compute_date_deadline', inverse="_inverse_date_deadline")
18+
property_type_id = fields.Many2one(related='property_id.property_type_id', string="Property Type", store=True, readonly=True)
19+
20+
_check_price = models.Constraint(
21+
"CHECK(price > 0)",
22+
"The offer price must be strictly positive"
23+
)
1724

1825
@api.depends('validity', 'create_date')
1926
def _compute_date_deadline(self):
@@ -28,24 +35,19 @@ def _inverse_date_deadline(self):
2835

2936
def action_accept_offer(self):
3037
for record in self:
31-
record.status = 'accepted'
38+
record.status = "accepted"
3239
record.property_id.selling_price = record.price
3340
record.property_id.buyer_id = record.partner_id
34-
record.property_id.status = 'offer_accepted'
41+
record.property_id.status = "offer_accepted"
3542

3643
# Reject other offers
3744
other_offers = record.property_id.offer_ids.filtered(lambda o: o.id != record.id)
3845
other_offers.action_reject_offer()
3946

4047
def action_reject_offer(self):
4148
for record in self:
42-
record.status = 'refused'
43-
if record.property_id.status == 'offer_accepted' and record.property_id.buyer_id == record.partner_id:
49+
record.status = "refused"
50+
if record.property_id.status == "offer_accepted" and record.property_id.buyer_id == record.partner_id:
4451
record.property_id.selling_price = 0.0
4552
record.property_id.buyer_id = False
46-
record.property_id.status = 'offer_received'
47-
48-
_check_price = models.Constraint(
49-
'CHECK(price > 0)',
50-
'The offer price must be strictly positive'
51-
)
53+
record.property_id.status = "offer_received"

estate/models/property_tag.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@
22

33

44
class PropertyTag(models.Model):
5-
_name = 'estate.property.tag'
6-
_description = 'Estate Property Tag'
5+
_name = "estate.property.tag"
6+
_description = "Estate Property Tag"
7+
_order = "name"
78

89
name = fields.Char("Tag Name", required=True)
10+
color = fields.Integer("Color")
911

1012
_unique_name = models.Constraint(
11-
'UNIQUE(name)',
12-
'The property tag name must be unique'
13+
"UNIQUE(name)",
14+
"The property tag name must be unique"
1315
)

estate/models/property_type.py

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,23 @@
1-
from odoo import fields, models
1+
from odoo import api, fields, models
22

33

44
class PropertyType(models.Model):
5-
_name = 'estate.property.type'
6-
_description = 'Estate Property Type'
5+
_name = "estate.property.type"
6+
_description = "Estate Property Type"
7+
_order = "sequence, name"
78

89
name = fields.Char("Property Type", required=True)
10+
property_ids = fields.One2many('estate.property', 'property_type_id', string="Properties")
11+
sequence = fields.Integer("Sequence", default=1, help="Used to order property types")
12+
offer_ids = fields.One2many(related='property_ids.offer_ids', string="Offers", readonly=True)
13+
offer_count = fields.Integer("Offer Count", compute='_compute_offer_count')
914

1015
_unique_name = models.Constraint(
11-
'UNIQUE(name)',
12-
'The property type name must be unique'
16+
"UNIQUE(name)",
17+
"The property type name must be unique"
1318
)
19+
20+
@api.depends('offer_ids')
21+
def _compute_offer_count(self):
22+
for record in self:
23+
record.offer_count = len(record.offer_ids)
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<odoo>
3+
<record id="action_estate_property_offer" model="ir.actions.act_window">
4+
<field name="name">Offers</field>
5+
<field name="res_model">estate.property.offer</field>
6+
<field name="view_mode">list,form</field>
7+
<field name="domain">[('property_type_id', '=', active_id)]</field>
8+
</record>
9+
10+
<record id="estate_property_offer_view_list" model="ir.ui.view">
11+
<field name="name">estate.property.offer.list</field>
12+
<field name="model">estate.property.offer</field>
13+
<field name="arch" type="xml">
14+
<list>
15+
<field name="price"/>
16+
<field name="partner_id"/>
17+
<field name="validity"/>
18+
<field name="date_deadline"/>
19+
</list>
20+
</field>
21+
</record>
22+
</odoo>

estate/views/estate_property_search_view.xml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,12 @@
88
<field name="postcode"/>
99
<field name="expected_price"/>
1010
<field name="bedrooms"/>
11-
<field name="living_area"/>
11+
<field name="living_area" filter_domain="[('living_area', '>=', self)]"/>
1212
<field name="facades"/>
1313
<field name="status"/>
1414
<filter string="Archived" name="inactive" domain="[('active', '=', False)]"/>
1515
<filter string="Status" name="availability" domain="[('status', 'in', ['new', 'offered'])]"/>
16+
<filter string="Available" name="available" domain="[('active', '=', True)]"/>
1617
<group>
1718
<filter string="Postcode" name="postcode" context="{'group_by':'postcode'}"/>
1819
</group>

estate/views/estate_property_tag_views.xml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,14 @@
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 editable="bottom">
14+
<field name="name"/>
15+
</list>
16+
</field>
17+
</record>
818
</odoo>

estate/views/estate_property_type_views.xml

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,43 @@
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>
14+
<field name="sequence" widget="handle"/>
15+
<field name="name" />
16+
</list>
17+
</field>
18+
</record>
19+
20+
<record id="estate_property_type_view_form" model="ir.ui.view">
21+
<field name="name">estate.property.type.form</field>
22+
<field name="model">estate.property.type</field>
23+
<field name="arch" type="xml">
24+
<form string="Properties List">
25+
<button name="%(estate.action_estate_property_offer)d" string="Offers" type="action" class="oe_stat_button" icon="fa-money">
26+
<field name="offer_count" widget="statinfo" string="Offers"/>
27+
</button>
28+
<sheet>
29+
<notebook>
30+
<page string="Properties">
31+
<h1>
32+
<field name="name"/>
33+
</h1>
34+
<field name="property_ids">
35+
<list>
36+
<field name="name" string="Title"/>
37+
<field name="expected_price"/>
38+
<field name="status"/>
39+
</list>
40+
</field>
41+
</page>
42+
</notebook>
43+
</sheet>
44+
</form>
45+
</field>
46+
</record>
847
</odoo>

estate/views/estate_property_views.xml

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,14 @@
44
<field name="name">View Records</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

9-
<record id="estate_property_view_tree" model="ir.ui.view">
10-
<field name="name">estate.property.tree</field>
10+
<record id="estate_property_view_list" model="ir.ui.view">
11+
<field name="name">estate.property.list</field>
1112
<field name="model">estate.property</field>
1213
<field name="arch" type="xml">
13-
<list>
14+
<list decoration-success="status in ('offer_received', 'offer_accepted')" decoration-bf="status == 'offer_accepted'" decoration-muted="status == 'sold'">
1415
<field name="name"/>
1516
<field name="postcode"/>
1617
<field name="bedrooms"/>
@@ -28,8 +29,9 @@
2829
<field name="arch" type="xml">
2930
<form string="My new house">
3031
<header>
31-
<button name="action_mark_sold" type="object" string="Sold" class="oe_highlight"/>
32-
<button name="action_mark_cancel" type="object" string="Cancel"/>
32+
<button name="action_mark_sold" type="object" string="Sold" class="oe_highlight" invisible="status == 'sold'"/>
33+
<button name="action_mark_cancel" type="object" string="Cancel" invisible="status == 'sold'"/>
34+
<field name="status" widget="statusbar" statusbar_visible="new, offer_received, offer_accepted, sold"/>
3335
</header>
3436
<sheet>
3537
<group>
@@ -38,12 +40,11 @@
3840
</h1>
3941
</group>
4042
<group>
41-
<field string="Tags" name="tag_ids" widget="many2many_tags"/>
43+
<field string="Tags" name="tag_ids" widget="many2many_tags" options="{'color_field': 'color'}" />
4244
</group>
4345
<group>
4446
<group>
45-
<field name="status"/>
46-
<field name="property_type_id"/>
47+
<field name="property_type_id" options="{'no_create': true}"/>
4748
<field name="postcode"/>
4849
<field name="date_availability"/>
4950
</group>
@@ -62,21 +63,20 @@
6263
<field name="facades"/>
6364
<field name="garage"/>
6465
<field name="garden"/>
65-
<field name="garden_area"/>
66-
<field name="garden_orientation"/>
66+
<field name="garden_area" invisible="not garden"/>
67+
<field name="garden_orientation" invisible="not garden"/>
6768
<field name="total_area"/>
6869
</group>
6970
</page>
7071
<page string="Offers">
71-
<field name="offer_ids">
72-
<list>
72+
<field name="offer_ids" readonly="status in ('offer_accepted', 'sold', 'canceled')">
73+
<list editable="bottom" decoration-success="status == 'accepted'" decoration-danger="status == 'refused'">
7374
<field name="price"/>
7475
<field name="partner_id"/>
7576
<field name="validity"/>
7677
<field name="date_deadline"/>
77-
<button name="action_accept_offer" type="object" icon="fa-check"/>
78-
<button name="action_reject_offer" type="object" icon="fa-remove"/>
79-
<field name="status"/>
78+
<button name="action_accept_offer" type="object" icon="fa-check" invisible="status"/>
79+
<button name="action_reject_offer" type="object" icon="fa-remove" invisible="status"/>
8080
</list>
8181
</field>
8282
</page>

0 commit comments

Comments
 (0)