Skip to content

Commit 756fc9e

Browse files
committed
[IMP] estate: Added Inheritance to module and created estate account module
Applied model and view inheritance patterns to extend core functionality Enabled interactions between modules for enhanced business workflows Updated business logic to support new integration features
1 parent c67201f commit 756fc9e

15 files changed

+264
-120
lines changed

awesome_gallery/models/ir_action.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,4 @@ class ActWindowView(models.Model):
77

88
view_mode = fields.Selection(selection_add=[
99
('gallery', "Awesome Gallery")
10-
], ondelete={'gallery': 'cascade'})
10+
], ondelete={'gallery': 'cascade'})

estate/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
from . import models
1+
from . import models

estate/__manifest__.py

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
11
{
2-
'name':"Real Estate",
3-
'summary':"This is real estate module",
4-
'category':"Tutorials",
5-
'description':"This is real estate module",
6-
'author':"Dhruvrajsinh Zala (zadh)",
2+
'name': "Real Estate",
3+
'summary': "This is real estate module",
4+
'category': "Tutorials",
5+
'description': "This is real estate module",
6+
'author': "Dhruvrajsinh Zala (zadh)",
77
'installable': True,
88
'application': True,
9-
'data':['security/ir.model.access.csv', 'views/estate_property_offers.xml',
10-
'views/estate_property_views.xml',
11-
'views/estate_menus.xml',
12-
'views/estate_property_type_views.xml','views/estate_property_tags.xml']
13-
}
9+
'data': ['security/ir.model.access.csv', 'views/estate_property_views.xml', 'views/estate_property_offers.xml', 'views/estate_property_type_views.xml', 'views/estate_property_tags.xml', 'views/res_users_views.xml', 'views/estate_menus.xml'],
10+
'license': 'AGPL-3'
11+
}

estate/models/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@
22
from . import esatate_property_type
33
from . import estate_property_tags
44
from . import estate_property_offer
5+
from . import inherited_res_users
Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,30 @@
1-
from odoo import models,fields,api
1+
from odoo import models, fields, api
22

33

44
class EstatePropertyTyeps(models.Model):
5-
_name="estate.property.types"
6-
_description="Types of Estate Property"
7-
_order="sequence,name"
5+
_name = "estate.property.types"
6+
_description = "Types of Estate Property"
7+
_order = "sequence, name"
88

9-
_unique_type_name = models.Constraint('UNIQUE(name)','Property type name must be unique.')
9+
_sql_constraints = [
10+
("_unique_type_name", "UNIQUE(name)", "Property type name must be unique.")
11+
]
1012

11-
name=fields.Char(required=True)
12-
property_ids=fields.One2many('estate.property','property_type_id',string="Properties")
13+
name = fields.Char(required=True)
14+
property_ids = fields.One2many(
15+
"estate.property", "property_type_id", string="Properties"
16+
)
1317
sequence = fields.Integer(default=1)
1418

1519
offer_ids = fields.One2many(
16-
'estate.property.offer',
17-
'property_type_id',
18-
string="Offers"
20+
"estate.property.offer", "property_type_id", string="Offers"
1921
)
2022

21-
2223
offer_count = fields.Integer(
23-
string="Offer Count",
24-
compute='_compute_offer_count',
25-
store=True
24+
string="Offer Count", compute="_compute_offer_count", store=True
2625
)
2726

28-
@api.depends('offer_ids')
27+
@api.depends("offer_ids")
2928
def _compute_offer_count(self):
3029
for record in self:
31-
record.offer_count = len(record.offer_ids)
30+
record.offer_count = len(record.offer_ids)

estate/models/estate_property.py

Lines changed: 81 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,110 +1,131 @@
11
from dateutil.relativedelta import relativedelta
2-
from odoo import models,fields,api,_
3-
from odoo.tools.float_utils import float_compare,float_is_zero
4-
from odoo.exceptions import UserError,ValidationError
2+
from odoo import models, fields, api, _
3+
from odoo.tools.float_utils import float_compare, float_is_zero
4+
from odoo.exceptions import UserError, ValidationError
55

6-
class EstateProperty(models.Model):
7-
_name = 'estate.property'
8-
_description = 'Real Estate Property'
9-
_order = 'id desc'
106

7+
class EstateProperty(models.Model):
8+
_name = "estate.property"
9+
_description = "Real Estate Property"
10+
_order = "id desc"
1111

12-
_check_expected_price = models.Constraint('CHECK(expected_price > 0)','The expected price must be positive.')
13-
_check_selling_price = models.Constraint('CHECK(selling_price >= 0)','The selling price must be positive.')
12+
_sql_constraints = [
13+
(
14+
"_check_expected_price",
15+
"CHECK(expected_price > 0)",
16+
"The expected price must be positive.",
17+
),
18+
(
19+
"_check_selling_price",
20+
"CHECK(selling_price >= 0)",
21+
"The selling price must be positive.",
22+
),
23+
]
1424

15-
name = fields.Char(required=True,string="Title")
25+
name = fields.Char(required=True, string="Title")
1626
description = fields.Text()
1727
postcode = fields.Char()
1828
date_availability = fields.Date(
19-
default=lambda self: fields.Date.today() + relativedelta(months=3),
20-
copy=False,string="Available From")
29+
default=lambda self: fields.Date.today() + relativedelta(months=3),
30+
copy=False,
31+
string="Available From",
32+
)
2133
expected_price = fields.Float(required=True)
22-
selling_price = fields.Float(readonly=True,copy=False)
34+
selling_price = fields.Float(readonly=True, copy=False)
2335
bedrooms = fields.Integer(default=2)
2436
living_area = fields.Integer(string="Living Area (sqm)")
25-
total_area = fields.Float(compute='_compute_total_area',store=True)
37+
total_area = fields.Float(compute="_compute_total_area", store=True)
2638
facades = fields.Integer()
27-
garage = fields.Boolean()
39+
garage = fields.Boolean()
2840
garden = fields.Boolean()
2941
garden_area = fields.Integer(string="Garden Area (sqm)")
3042
active = fields.Boolean(default=True)
31-
garden_orientation = fields.Selection([
32-
('north','North'),
33-
('south','South'),
34-
('east','East'),
35-
('west','West')
36-
],string="Garden Orientation")
37-
state = fields.Selection(
38-
[
39-
('new', 'New'),
40-
('offer_received', 'Offer Received'),
41-
('offer_accepted', 'Offer Accepted'),
42-
('sold', 'Sold'),
43-
('cancelled', 'Cancelled')
44-
],
45-
required=True,
46-
copy=False,
47-
default='new')
48-
property_type_id = fields.Many2one(
49-
"estate.property.types", string="Property Type"
43+
garden_orientation = fields.Selection(
44+
[("north", "North"), ("south", "South"), ("east", "East"), ("west", "West")],
45+
string="Garden Orientation",
5046
)
51-
buyer_id = fields.Many2one(
52-
"res.partner", string="Buyer", copy=False
47+
state = fields.Selection(
48+
[
49+
("new", "New"),
50+
("offer_received", "Offer Received"),
51+
("offer_accepted", "Offer Accepted"),
52+
("sold", "Sold"),
53+
("cancelled", "Cancelled"),
54+
],
55+
required=True,
56+
copy=False,
57+
default="new",
5358
)
59+
property_type_id = fields.Many2one("estate.property.types", string="Property Type")
60+
buyer_id = fields.Many2one("res.partner", string="Buyer", copy=False)
5461
salesperson_id = fields.Many2one(
5562
"res.users", string="Salesperson", default=lambda self: self.env.user
5663
)
57-
tag_ids = fields.Many2many('estate.property.tags',string="Tags")
64+
tag_ids = fields.Many2many("estate.property.tags", string="Tags")
5865

59-
offer_ids = fields.One2many('estate.property.offer','property_id',string="Offers")
66+
offer_ids = fields.One2many("estate.property.offer", "property_id", string="Offers")
6067

61-
best_price = fields.Float(compute='_get_best_offer_price',store=True)
68+
best_price = fields.Float(compute="_get_best_offer_price", store=True)
6269

63-
@api.depends("living_area","garden_area")
70+
@api.ondelete(at_uninstall=False)
71+
def _unlink_except_state_new_or_cancelled(self):
72+
for record in self:
73+
if record.state not in ["new", "cancelled"]:
74+
raise UserError(
75+
_("You can only delete properties in 'New' or 'Cancelled' state.")
76+
)
77+
78+
@api.depends("living_area", "garden_area")
6479
def _compute_total_area(self):
6580
for record in self:
6681
record.total_area = record.living_area + record.garden_area
67-
68-
@api.depends('offer_ids.price')
82+
83+
@api.depends("offer_ids.price")
6984
def _get_best_offer_price(self):
7085
for record in self:
71-
prices=record.offer_ids.mapped('price')
86+
prices = record.offer_ids.mapped("price")
7287
record.best_price = max(prices) if prices else 0.0
7388

74-
@api.onchange('garden')
89+
@api.onchange("garden")
7590
def _onchange_garden(self):
7691
for record in self:
7792
if record.garden:
7893
record.garden_area = 10
79-
record.garden_orientation = 'north'
94+
record.garden_orientation = "north"
8095
else:
8196
record.garden_area = 0
82-
record.garden_orientation = ''
97+
record.garden_orientation = ""
8398

8499
def action_set_state_sold(self):
85100
for record in self:
86-
if(record.state == 'cancelled'):
101+
if record.state == "cancelled":
87102
raise UserError(_("Cancelled property cannot be sold."))
88103
else:
89-
record.state = 'sold'
104+
record.state = "sold"
90105
return True
91106

92107
def action_set_state_cancel(self):
93108
for record in self:
94-
if(record.state == 'sold'):
109+
if record.state == "sold":
95110
raise UserError(_("Sold property cannot be cancelled."))
96111
else:
97-
record.state = 'cancelled'
112+
record.state = "cancelled"
98113
return True
99-
100-
@api.constrains('selling_price','expected_price')
114+
115+
@api.constrains("selling_price", "expected_price")
101116
def _check_selling_price(self):
102117
for record in self:
103-
if not float_is_zero(record.selling_price,precision_digits=2):
104-
if float_compare(
105-
record.selling_price,
106-
record.expected_price * 0.9,
107-
precision_digits=2) < 0 :
108-
raise ValidationError(_("The selling price cannot be lower than 90% of the expected price"))
109-
110-
118+
if not float_is_zero(record.selling_price, precision_digits=2):
119+
if (
120+
float_compare(
121+
record.selling_price,
122+
record.expected_price * 0.9,
123+
precision_digits=2,
124+
)
125+
< 0
126+
):
127+
raise ValidationError(
128+
_(
129+
"The selling price cannot be lower than 90% of the expected price"
130+
)
131+
)

estate/models/estate_property_offer.py

Lines changed: 64 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,93 @@
1-
from odoo import fields,models,api,_
1+
from odoo import fields, models, api, _
22
from odoo.exceptions import UserError
3-
from datetime import timedelta,datetime
3+
from datetime import timedelta, datetime
44

5-
class EstatePropertyOffer(models.Model):
6-
_name="estate.property.offer"
7-
_description="Offers of Estate Property"
8-
_order = 'price desc'
95

10-
_check_offer_price = models.Constraint('CHECK(price > 0)','The Offer price must be positive.')
6+
class EstatePropertyOffer(models.Model):
7+
_name = "estate.property.offer"
8+
_description = "Offers of Estate Property"
9+
_order = "price desc"
10+
_sql_constraints = [
11+
("_check_offer_price", "CHECK(price > 0)", "The Offer price must be positive.")
12+
]
1113

1214
price = fields.Float()
13-
status = fields.Selection([('accepted','Accepted'),('refused','Refused')],copy=False)
14-
partner_id = fields.Many2one('res.partner',string="Partner",required=True)
15-
property_id = fields.Many2one('estate.property',string="Property",required=True)
15+
status = fields.Selection(
16+
[("accepted", "Accepted"), ("refused", "Refused")], copy=False
17+
)
18+
partner_id = fields.Many2one("res.partner", string="Partner", required=True)
19+
property_id = fields.Many2one("estate.property", string="Property", required=True)
1620
property_type_id = fields.Many2one(
17-
'estate.property.types',
18-
related="property_id.property_type_id",
19-
string="Property Type",
20-
required=True)
21-
21+
"estate.property.types",
22+
related="property_id.property_type_id",
23+
string="Property Type",
24+
required=True,
25+
)
2226
validity = fields.Integer(default=7)
23-
date_deadline = fields.Date(compute="_compute_deadline",inverse="_inverse_deadline",store=True)
24-
@api.depends('validity')
27+
date_deadline = fields.Date(
28+
compute="_compute_deadline", inverse="_inverse_deadline", store=True
29+
)
30+
31+
@api.model_create_multi
32+
def create(self, vals):
33+
for val in vals:
34+
property_id = val["property_id"]
35+
offer_price = val["price"]
36+
is_state_new = (
37+
self.env["estate.property"].browse(property_id).state == "new"
38+
)
39+
40+
result = self.read_group(
41+
domain=[("property_id", "=", property_id)],
42+
fields=["price:max"],
43+
groupby=[],
44+
)
45+
46+
max_price = result[0]["price"] if result else 0
47+
48+
if max_price >= offer_price:
49+
raise UserError(
50+
_(
51+
"You cannot create an offer with a lower or equal amount than an existing offer for this property."
52+
)
53+
)
54+
55+
if is_state_new:
56+
self.env["estate.property"].browse(property_id).state = "offer_received"
57+
58+
return super().create(vals)
59+
60+
@api.depends("validity")
2561
def _compute_deadline(self):
2662
for record in self:
2763
create_date = record.create_date or datetime.now()
2864
record.date_deadline = create_date + timedelta(days=record.validity)
65+
2966
def _inverse_deadline(self):
3067
for record in self:
31-
create_date = record.create_date or datetime.now()
68+
create_date = record.create_date or datetime.now()
3269
if record.date_deadline:
3370
delta = record.date_deadline - create_date.date()
3471
record.validity = delta.days
35-
72+
3673
def action_accept_offer(self):
3774
for record in self:
38-
existing = self.search([
39-
('property_id','=',record.property_id.id),
40-
('status','=','accepted')
41-
])
75+
existing = self.search(
76+
[
77+
("property_id", "=", record.property_id.id),
78+
("status", "=", "accepted"),
79+
]
80+
)
4281
if existing:
4382
raise UserError(_("Another offer has been already accepted."))
4483
record.status = "accepted"
4584
record.property_id.state = "offer_accepted"
4685
record.property_id.selling_price = record.price
4786
record.property_id.buyer_id = record.partner_id.id
87+
4888
def action_refuse_offer(self):
4989
for record in self:
50-
if(record.status == 'accepted'):
90+
if record.status == "accepted":
5191
raise UserError(_("Accepted Offer cannot be Refused"))
5292
else:
5393
record.status = "refused"
54-

0 commit comments

Comments
 (0)