11# Part of Odoo. See LICENSE file for full copyright and licensing details.
22
33from odoo import api , fields , models
4- from odoo .exceptions import UserError
4+ from odoo .exceptions import UserError , ValidationError
5+ from odoo .tools .float_utils import float_compare , float_is_zero
56
67
78class EstateProperty (models .Model ):
8- _name = " estate.property"
9+ _name = ' estate.property'
910 _description = "Real Estate Property"
11+ _order = 'id desc'
12+ _check_expected_price = models .Constraint ('Check(expected_price > 0)' , "The expected price must be strictly positive." )
13+ _check_selling_price = models .Constraint ('Check(selling_price >= 0)' , "The selling price must be positive." )
1014
11- name = fields .Char (required = True )
12- description = fields .Text ()
13- postcode = fields .Char ()
14- date_availability = fields .Date ()
15- expected_price = fields .Float ()
16- selling_price = fields .Float ()
17- bedrooms = fields .Integer ()
18- living_area = fields .Integer ()
19- facades = fields .Integer ()
20- garage = fields .Boolean ()
21- garden = fields .Boolean ()
22- garden_area = fields .Integer ()
15+ name = fields .Char (required = True , string = "Name" )
16+ description = fields .Text (string = "Description" )
17+ postcode = fields .Char (string = "Postcode" )
18+ date_availability = fields .Date (string = "Available From" )
19+ expected_price = fields .Float (required = True , string = "Expected Price" )
20+ selling_price = fields .Float (string = "Selling Price" )
21+ bedrooms = fields .Integer (string = "Bedrooms" )
22+ living_area = fields .Integer (string = "Living Area (sqm)" )
23+ facades = fields .Integer (string = "Facades" )
24+ garage = fields .Boolean (string = "Garage" )
25+ garden = fields .Boolean (string = "Garden" )
26+ garden_area = fields .Integer (string = "Garden Area (sqm)" )
2327 garden_orientation = fields .Selection (
2428 selection = [
25- ('north' , 'North' ),
26- ('south' , 'South' ),
27- ('east' , 'East' ),
28- ('west' , 'West' )
29- ]
29+ ('north' , "North" ),
30+ ('south' , "South" ),
31+ ('east' , "East" ),
32+ ('west' , "West" ),
33+ ],
34+ string = "Garden Orientation" ,
3035 )
3136 state = fields .Selection (
3237 selection = [
33- ('new' , ' New' ),
34- ('offer_received' , ' Offer Received' ),
35- ('offer_accepted' , ' Offer Accepted' ),
36- ('sold' , ' Sold' ),
37- ('cancelled' , ' Cancelled' )
38+ ('new' , " New" ),
39+ ('offer_received' , " Offer Received" ),
40+ ('offer_accepted' , " Offer Accepted" ),
41+ ('sold' , " Sold" ),
42+ ('cancelled' , " Cancelled" ),
3843 ],
3944 default = 'new' ,
40- required = True
45+ required = True ,
46+ string = "Status" ,
47+ )
48+ buyer_id = fields .Many2one (comodel_name = 'res.partner' , string = "Buyer" )
49+ property_type_id = fields .Many2one (comodel_name = 'estate.property.type' , string = "Property Type" )
50+ tag_ids = fields .Many2many (comodel_name = 'estate.property.tag' , string = "Tags" )
51+ offer_ids = fields .One2many (
52+ comodel_name = 'estate.property.offer' ,
53+ inverse_name = 'property_id' ,
54+ string = "Offers" ,
4155 )
42- buyer_id = fields .Many2one ('res.partner' , string = 'Buyer' )
43- property_type_id = fields .Many2one ("estate.property.type" , string = "Property Type" )
44- tag_ids = fields .Many2many ("estate.property.tag" , string = "Tags" )
45- offer_ids = fields .One2many ("estate.property.offer" , "property_id" , string = "Offers" )
46- total_area = fields .Integer (compute = "_compute_total_area" )
47- best_price = fields .Float (compute = "_compute_best_price" , string = "Best Offer Price" )
56+ total_area = fields .Integer (compute = '_compute_total_area' , string = "Total Area (sqm)" )
57+ best_price = fields .Float (compute = '_compute_best_price' , string = "Best Offer Price" )
4858
4959 @api .depends ('living_area' , 'garden_area' )
5060 def _compute_total_area (self ):
51- for record in self :
52- record .total_area = record .living_area + record .garden_area
61+ for property in self :
62+ property .total_area = property .living_area + property .garden_area
5363
5464 @api .depends ('offer_ids.price' )
5565 def _compute_best_price (self ):
56- for record in self :
57- if record .offer_ids :
58- record .best_price = max (record .offer_ids .mapped ('price' ))
66+ for property in self :
67+ if property .offer_ids :
68+ property .best_price = max (property .offer_ids .mapped ('price' ))
5969 else :
60- record .best_price = 0.0
70+ property .best_price = 0.0
6171
6272 @api .onchange ('garden' )
6373 def _onchange_garden (self ):
@@ -68,16 +78,23 @@ def _onchange_garden(self):
6878 self .garden_area = 0
6979 self .garden_orientation = False
7080
81+ @api .constrains ('selling_price' , 'expected_price' )
82+ def _check_selling_price (self ):
83+ for property in self :
84+ if not float_is_zero (property .selling_price , precision_digits = 2 ):
85+ if float_compare (property .selling_price , property .expected_price * 0.9 , precision_digits = 2 ) < 0 :
86+ raise ValidationError (self .env ._ ("The selling price cannot be lower than 90% of the expected price." ))
87+
7188 def action_sold (self ):
72- for record in self :
73- if record .state == 'cancelled' :
74- raise UserError ("Cancelled property cannot be sold." )
75- record .state = 'sold'
89+ for property in self :
90+ if property .state == 'cancelled' :
91+ raise UserError (self . env . _ ( "Cancelled property cannot be sold." ) )
92+ property .state = 'sold'
7693 return True
7794
7895 def action_cancel (self ):
79- for record in self :
80- if record .state == 'sold' :
81- raise UserError ("Sold property cannot be cancelled." )
82- record .state = 'cancelled'
96+ for property in self :
97+ if property .state == 'sold' :
98+ raise UserError (self . env . _ ( "Sold property cannot be cancelled." ) )
99+ property .state = 'cancelled'
83100 return True
0 commit comments