11from datetime import date
22from dateutil .relativedelta import relativedelta
3- from odoo .exceptions import UserError
3+ from odoo .exceptions import UserError , ValidationError
44from odoo import api , fields , models
5+ from odoo .tools .float_utils import float_compare , float_is_zero
56
67
78class EstateProperty (models .Model ):
89 _name = "estate.property"
910 _description = "Estate property"
10- _order = ""
11+ _order = "id desc "
1112
12- name = fields .Text ('Title' , required = True )
13+ name = fields .Text ('Title' , required = True , default = 'Unknown' , translate = True )
1314 description = fields .Text ('Description' )
1415 post_code = fields .Char ('Postcode' )
1516 date_availability = fields .Date (
@@ -53,14 +54,34 @@ class EstateProperty(models.Model):
5354 user_id = fields .Many2one (
5455 'res.users' , string = 'Salesman' , default = lambda self : self .env .user
5556 )
56- buyer_id = fields .Many2one ("res.partner" , string = "Buyer" , readonly = True , copy = False )
57+ buyer_id = fields .Many2one (
58+ "res.partner" ,
59+ string = "Buyer" ,
60+ copy = False ,
61+ readonly = True ,
62+ domain = [('is_company' , '=' , False )],
63+ )
5764 tag_ids = fields .Many2many ("estate.property.tag" , string = "Tags" )
5865 offer_ids = fields .One2many ("estate.property.offer" , "property_id" , string = "Offers" )
5966 total_area = fields .Integer ("Total Area (sqm)" , compute = "_compute_total_area" )
6067 best_price = fields .Float (
6168 "Best Offer" , compute = "_compute_best_price" , readonly = True
6269 )
6370
71+ # 💡 REPLACED deprecated models.Constraint with _sql_constraints
72+ _sql_constraints = [
73+ (
74+ 'check_expected_price' ,
75+ 'CHECK(expected_price > 0)' ,
76+ 'The expected price must be strictly positive.' ,
77+ ),
78+ (
79+ 'check_selling_price' ,
80+ 'CHECK(selling_price >= 0)' ,
81+ 'The selling price must be positive or zero.' ,
82+ ),
83+ ]
84+
6485 @api .depends ('living_area' , 'garden_area' )
6586 def _compute_total_area (self ):
6687 for rec in self :
@@ -70,12 +91,7 @@ def _compute_total_area(self):
7091 def _compute_best_price (self ):
7192 for rec in self :
7293 prices = rec .offer_ids .mapped ('price' )
73- rec .best_price = max (prices ) if prices else 0.0
74-
75- @api .depends ("living_area" , "garden_area" )
76- def _compute_total_area (self ):
77- for prop in self :
78- prop .total_area = prop .living_area + prop .garden_area
94+ rec .best_price = max (prices , default = 0.0 )
7995
8096 @api .onchange ("garden" )
8197 def _onchange_garden (self ):
@@ -87,11 +103,35 @@ def _onchange_garden(self):
87103 self .garden_orientation = False
88104
89105 def action_sold (self ):
90- if "cancelled" in self . mapped ( "state" ):
106+ if any ([ prop . state == "cancelled" for prop in self ] ):
91107 raise UserError ("Canceled property cannot be sold !" )
92- return self .write ({"state" : "sold" })
108+ self .state = 'sold'
109+ return True
93110
94111 def action_cancel (self ):
95112 if "sold" in self .mapped ("state" ):
96113 raise UserError ("Sold property cannot be canceled !" )
97114 return self .write ({"state" : "cancelled" })
115+
116+ @api .constrains ('selling_price' , 'expected_price' )
117+ def _check_selling_price_constraint (self ):
118+ for rec in self :
119+ if float_is_zero (rec .selling_price or 0.0 , precision_digits = 2 ):
120+ continue
121+ if not rec .expected_price :
122+ raise ValidationError (
123+ "Expected price must be set to validate selling price."
124+ )
125+ threshold = 0.9 * rec .expected_price
126+ if float_compare (rec .selling_price , threshold , precision_digits = 2 ) < 0 :
127+ raise ValidationError (
128+ "The selling price cannot be lower than 90% of the expected price."
129+ )
130+
131+ _check_expected_price = models .Constraint (
132+ 'CHECK(expected_price > 0)' , 'The expected price must be strictly positive.'
133+ )
134+
135+ _check_selling_price = models .Constraint (
136+ 'CHECK(selling_price >= 0)' , 'The selling price must be positive or zero.'
137+ )
0 commit comments