Skip to content

Commit 9ac4d89

Browse files
committed
[IMP] estate: auto-refuse other offers when one is marked done and clean up field declarations
- Added functionality to automatically refuse other offers once one offer is marked done - Removed redundant checks and rearranged field declarations to reduce lines and enhance code readability
1 parent 5e50bce commit 9ac4d89

9 files changed

+56
-200
lines changed

estate/models/estate_property.py

Lines changed: 27 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -16,61 +16,23 @@ class EstateProperty(models.Model):
1616
# -----------------------------
1717
# Field Declarations
1818
# -----------------------------
19-
name = fields.Char(
20-
string='Title',
21-
required=True,
22-
help='Title or name of the property.'
23-
)
24-
description = fields.Text(
25-
string='Description',
26-
help='Detailed description of the property.'
27-
)
28-
postcode = fields.Char(
29-
string='Postcode',
30-
help='Postal code of the property location.'
31-
)
19+
name = fields.Char(string='Title', required=True, help='Title or name of the property.')
20+
description = fields.Text(string='Description', help='Detailed description of the property.')
21+
postcode = fields.Char(string='Postcode', help='Postal code of the property location.')
3222
date_availability = fields.Date(
3323
string='Availability From',
3424
copy=False,
3525
default=(date.today() + relativedelta(months=3)),
3626
help='Date from which the property will be available.'
3727
)
38-
expected_price = fields.Float(
39-
string='Expected Price',
40-
required=True,
41-
help='Price expected by the seller for this property.'
42-
)
43-
selling_price = fields.Float(
44-
string='Selling Price',
45-
readonly=True,
46-
copy=False,
47-
help='Final selling price once the property is sold.'
48-
)
49-
bedrooms = fields.Integer(
50-
string='Bedrooms',
51-
default=2,
52-
help='Number of bedrooms in the property.'
53-
)
54-
living_area = fields.Integer(
55-
string='Living Area (sqm)',
56-
help='Living area size in square meters.'
57-
)
58-
facades = fields.Integer(
59-
string='Facades',
60-
help='Number of facades of the property.'
61-
)
62-
garage = fields.Integer(
63-
string='Garage',
64-
help='Number of garage spaces.'
65-
)
66-
garden = fields.Boolean(
67-
string='Garden',
68-
help='Whether the property has a garden.'
69-
)
70-
garden_area = fields.Integer(
71-
string='Garden Area (sqm)',
72-
help='Size of the garden area in square meters.'
73-
)
28+
expected_price = fields.Float(string='Expected Price', required=True, help='Price expected by the seller for this property.')
29+
selling_price = fields.Float(string='Selling Price', readonly=True, copy=False, help='Final selling price once the property is sold.')
30+
bedrooms = fields.Integer(string='Bedrooms', default=2, help='Number of bedrooms in the property.')
31+
living_area = fields.Integer(string='Living Area (sqm)', help='Living area size in square meters.')
32+
facades = fields.Integer(string='Facades', help='Number of facades of the property.')
33+
garage = fields.Integer(string='Garage', help='Number of garage spaces.')
34+
garden = fields.Boolean(string='Garden', help='Whether the property has a garden.')
35+
garden_area = fields.Integer(string='Garden Area (sqm)', help='Size of the garden area in square meters.')
7436
garden_orientation = fields.Selection(
7537
string='Garden Orientation',
7638
selection=[
@@ -79,9 +41,7 @@ class EstateProperty(models.Model):
7941
('east', 'East'),
8042
('west', 'West'),
8143
],
82-
default='north',
83-
help='Direction the garden faces.'
84-
)
44+
default='north', help='Direction the garden faces.')
8545
state = fields.Selection(
8646
string='Status',
8747
selection=[
@@ -91,51 +51,20 @@ class EstateProperty(models.Model):
9151
('sold', 'Sold'),
9252
('cancelled', 'Cancelled'),
9353
],
94-
required=True,
95-
copy=False,
96-
default='new',
97-
help='Current status of the property.'
98-
)
99-
active = fields.Boolean(
100-
string='Active',
101-
default=True,
102-
help='Whether the property is active and visible.'
103-
)
104-
property_type_id = fields.Many2one(
105-
'estate.property.type',
106-
string='Property Type',
107-
help='Type or category of the property.'
108-
)
109-
buyer_id = fields.Many2one(
110-
'res.partner',
111-
string='Buyer',
112-
copy=False,
113-
help='Partner who bought the property.'
114-
)
115-
sales_id = fields.Many2one(
116-
'res.users',
117-
string='Salesman',
118-
default=lambda self: self.env.user,
119-
help='Salesperson responsible for the property.'
120-
)
121-
tag_ids = fields.Many2many(
122-
'estate.property.tag',
123-
string='Tags',
124-
help='Tags to classify the property.'
125-
)
126-
offer_ids = fields.One2many(
127-
'estate.property.offer',
128-
'property_id',
129-
string='Offers',
130-
help='Offers made on this property.'
131-
)
54+
required=True, copy=False, default='new', help='Current status of the property.'
55+
)
56+
active = fields.Boolean(string='Active', default=True, help='Whether the property is active and visible.')
57+
property_type_id = fields.Many2one('estate.property.type', string='Property Type', help='Type or category of the property.')
58+
buyer_id = fields.Many2one('res.partner', string='Buyer', copy=False, help='Partner who bought the property.')
59+
sales_id = fields.Many2one('res.users', string='Salesman', default=lambda self: self.env.user, help='Salesperson responsible for the property.')
60+
tag_ids = fields.Many2many('estate.property.tag', string='Tags', help='Tags to classify the property.')
61+
offer_ids = fields.One2many('estate.property.offer', 'property_id', string='Offers', help='Offers made on this property.')
13262

13363
# -----------------------------
13464
# SQL Constraints
13565
# -----------------------------
13666
_sql_constraints = [
137-
('check_expected_price', 'CHECK(expected_price > 0)', 'Expected price cannot be negative.'),
138-
('check_selling_price', 'CHECK(selling_price > 0)', 'Selling price cannot be negative.'),
67+
('check_expected_price', 'CHECK(expected_price > 0)', 'Expected price cannot be negative.')
13968
]
14069

14170
# -----------------------------
@@ -191,23 +120,6 @@ def action_cancel(self):
191120
# -----------------------------
192121
# Constraints
193122
# -----------------------------
194-
@api.constrains('selling_price', 'expected_price')
195-
def _check_selling_price(self):
196-
"""
197-
Ensure selling price is at least 90% of expected price.
198-
Raises ValidationError if condition is not met.
199-
"""
200-
for record in self:
201-
if record.selling_price and record.selling_price < 0.9 * record.expected_price:
202-
raise ValidationError(
203-
_("The selling price must be at least 90%% of the expected price.\n"
204-
"Expected Price: %(expected).2f\nSelling Price: %(selling).2f")
205-
% {
206-
'expected': record.expected_price,
207-
'selling': record.selling_price
208-
}
209-
)
210-
211123
@api.constrains('selling_price', 'expected_price')
212124
def _check_selling_price_above_90_percent(self):
213125
"""
@@ -219,14 +131,14 @@ def _check_selling_price_above_90_percent(self):
219131
continue
220132
min_acceptable_price = 0.9 * record.expected_price
221133
if float_compare(record.selling_price, min_acceptable_price, precision_digits=2) < 0:
222-
raise ValidationError(
223-
_("The selling price must be at least 90%% of the expected price.\n"
224-
"Expected Price: %(expected_price).2f\nSelling Price: %(selling_price).2f")
225-
% {
134+
raise ValidationError(_(
135+
"The selling price must be at least 90%% of the expected price.\n"
136+
"Expected Price: %(expected_price).2f\nSelling Price: %(selling_price).2f",
137+
{
226138
'expected_price': record.expected_price,
227139
'selling_price': record.selling_price
228140
}
229-
)
141+
))
230142

231143
@api.ondelete(at_uninstall=False)
232144
def _check_can_be_deleted(self):
@@ -236,4 +148,4 @@ def _check_can_be_deleted(self):
236148
"""
237149
for record in self:
238150
if record.state not in ['new', 'cancelled']:
239-
raise UserError("You can only delete properties that are New or Cancelled.")
151+
raise UserError('You can only delete properties that are New or Cancelled.')

estate/models/estate_property_offer.py

Lines changed: 10 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -16,60 +16,30 @@ class EstatePropertyOffer(models.Model):
1616
# SQL Constraints
1717
# -----------------------------
1818
_sql_constraints = [
19-
('check_offer_price', 'CHECK(price >= 0)', 'Offer price cannot be negative or zero.'),
19+
('check_offer_price', 'CHECK(price > 0)', 'Offer price cannot be negative or zero.'),
2020
]
2121

2222
# -----------------------------
2323
# Field Declarations
2424
# -----------------------------
25-
price = fields.Float(
26-
string='Price',
27-
required=True,
28-
help='The offer price proposed by the partner.'
29-
)
30-
25+
price = fields.Float(string='Price', required=True, help='The offer price proposed by the partner.')
3126
status = fields.Selection(
3227
string='Status',
3328
selection=[
3429
('accepted', 'Accepted'),
3530
('refused', 'Refused'),
3631
],
37-
copy=False,
38-
help='Current status of the offer: Accepted or Refused.'
39-
)
40-
41-
validity = fields.Integer(
42-
string='Validity (days)',
43-
default=7,
44-
help='Number of days this offer remains valid from the creation date.'
32+
copy=False, help='Current status of the offer: Accepted or Refused.'
4533
)
46-
34+
validity = fields.Integer(string='Validity (days)', default=7, help='Number of days this offer remains valid from the creation date.')
4735
date_deadline = fields.Date(
48-
string='Deadline',
49-
compute='_compute_date_deadline',
36+
string='Deadline', compute='_compute_date_deadline',
5037
inverse='_inverse_date_deadline',
5138
help='Deadline date until which the offer is valid.'
5239
)
53-
54-
partner_id = fields.Many2one(
55-
'res.partner',
56-
string='Partner',
57-
required=True,
58-
help='The partner who made this offer.'
59-
)
60-
61-
property_id = fields.Many2one(
62-
'estate.property',
63-
string='Property',
64-
required=True,
65-
help='The property this offer is related to.'
66-
)
67-
68-
property_type_id = fields.Many2one(
69-
related='property_id.property_type_id',
70-
store=True,
71-
help='Type of the related property.'
72-
)
40+
partner_id = fields.Many2one('res.partner', string='Partner', required=True, help='The partner who made this offer.')
41+
property_id = fields.Many2one('estate.property', string='Property', required=True, help='The property this offer is related to.')
42+
property_type_id = fields.Many2one(related='property_id.property_type_id', store=True, help='Type of the related property.')
7343

7444
# -----------------------------
7545
# Compute / Inverse Methods
@@ -144,6 +114,8 @@ def action_confirm(self):
144114
record.property_id.selling_price = record.price
145115
record.property_id.buyer_id = record.partner_id
146116

117+
(self.property_id.offer_ids - record).write({'status': 'refused'})
118+
147119
def action_refuse(self):
148120
"""
149121
Refuse the offer by setting its status to 'refused'.

estate/models/estate_property_tag.py

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,8 @@ class EstatePropertyTag(models.Model):
1010
# -----------------------------
1111
# Fields
1212
# -----------------------------
13-
name = fields.Char(
14-
string='Property Tag',
15-
required=True,
16-
help='Name of the tag used to categorize or label properties.'
17-
)
18-
color = fields.Integer(
19-
string='Color',
20-
help='Color code used to visually distinguish this tag.'
21-
)
13+
name = fields.Char(string='Property Tag', required=True, help='Name of the tag used to categorize or label properties.')
14+
color = fields.Integer(string='Color', help='Color code used to visually distinguish this tag.')
2215

2316
# -----------------------------
2417
# SQL Constraints

estate/models/estate_property_type.py

Lines changed: 5 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -10,33 +10,11 @@ class EstatePropertyType(models.Model):
1010
# -----------------------------
1111
# Fields
1212
# -----------------------------
13-
name = fields.Char(
14-
string='Property Type',
15-
required=True,
16-
help='Name of the property type (e.g., Apartment, House).'
17-
)
18-
property_ids = fields.One2many(
19-
'estate.property',
20-
'property_type_id',
21-
string='Properties',
22-
help='Properties categorized under this type.'
23-
)
24-
offer_ids = fields.One2many(
25-
'estate.property.offer',
26-
'property_type_id',
27-
string='Offers',
28-
help='Offers associated with properties of this type.'
29-
)
30-
sequence = fields.Integer(
31-
string='Sequence',
32-
default=10,
33-
help='Used to order property types in lists and views.'
34-
)
35-
offer_count = fields.Integer(
36-
string='Number of Offers',
37-
compute='_compute_offer_count',
38-
help='Total number of offers made on properties of this type.'
39-
)
13+
name = fields.Char(string='Property Type', required=True, help='Name of the property type (e.g., Apartment, House).')
14+
property_ids = fields.One2many('estate.property', 'property_type_id', string='Properties', help='Properties categorized under this type.')
15+
offer_ids = fields.One2many('estate.property.offer', 'property_type_id', string='Offers', help='Offers associated with properties of this type.')
16+
sequence = fields.Integer(string='Sequence', default=10, help='Used to order property types in lists and views.')
17+
offer_count = fields.Integer(string='Number of Offers', compute='_compute_offer_count', help='Total number of offers made on properties of this type.')
4018

4119
# -----------------------------
4220
# SQL Constraints

estate/views/estate_property_offer_view.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444

4545
<!-- Property Offer Actions -->
4646
<record id="estate_property_offer_action" model="ir.actions.act_window">
47-
<field name="name">estate.property.offer.action</field>
47+
<field name="name">Property Offers</field>
4848
<field name="res_model">estate.property.offer</field>
4949
<field name="view_mode">list,form</field>
5050
</record>

estate/views/estate_property_tag_view.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040

4141
<!-- Property Tag Actions -->
4242
<record id="estate_property_tag_action" model="ir.actions.act_window">
43-
<field name="name">estate.property.tag.action</field>
43+
<field name="name">Property Tags</field>
4444
<field name="res_model">estate.property.tag</field>
4545
<field name="view_mode">list,form</field>
4646
</record>

estate/views/estate_property_type_view.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,9 +66,9 @@
6666

6767
<!-- Property Type Actions -->
6868
<record id="estate_property_type_action" model="ir.actions.act_window">
69-
<field name="name">estate.property.type.action</field>
69+
<field name="name">Properties Types</field>
7070
<field name="res_model">estate.property.type</field>
71-
<field name="view_mode">form,list</field>
71+
<field name="view_mode">list,form</field>
7272
</record>
7373
</data>
7474
</odoo>

estate/views/estate_property_view.xml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
<kanban default_group_by="property_type_id" groups_draggable="1" records_draggable="1">
99
<field name="state" />
1010
<templates>
11-
<t t-name="kanban-box">
11+
<t t-name="card">
1212
<div>
1313
<h4>
1414
<field name="name" />
@@ -53,7 +53,6 @@
5353

5454
<!-- Optional fields (can be toggled in UI) -->
5555
<field name="description" optional="hide" />
56-
<field name="date_availability" optional="hide" invisible="1" />
5756
<field name="selling_price" optional="hide" />
5857
<field name="bedrooms" optional="hide" />
5958
<field name="living_area" optional="hide" />
@@ -100,11 +99,13 @@
10099
type="object"
101100
string="Sold"
102101
class="btn-primary"
102+
confirm="Are you sure you want to sold this?"
103103
invisible="state == 'sold' or state == 'cancelled'" />
104104

105105
<button name="action_cancel"
106106
type="object"
107107
string="Cancel"
108+
confirm="Are you sure you want to cancel it?"
108109
invisible="state == 'sold' or state == 'cancelled'" />
109110

110111

@@ -185,7 +186,7 @@
185186

186187
<!-- Estate Property Actions -->
187188
<record id="estate_property_action" model="ir.actions.act_window">
188-
<field name="name">estate.property.action</field>
189+
<field name="name">Properties</field>
189190
<field name="res_model">estate.property</field>
190191
<field name="view_mode">kanban,list,form</field>
191192
<field name="context">{'search_default_available_properties': 1}</field>

0 commit comments

Comments
 (0)