Skip to content

Commit 80be51f

Browse files
committed
[IMP] real_estate: apply restriction to data and add demo data
In this tutorial, learned to restrict access using user groups, access rights, and record rules. bypass security allows only validate user or groups to access some features other can not use that. multi company security secure data between companies other can not access it. Load demo data for sample for user to work on it and user can see and use it by default, also learn data format of csv and xml files.
1 parent 0d37d1f commit 80be51f

18 files changed

+222
-44
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,3 +127,6 @@ dmypy.json
127127

128128
# Pyre type checker
129129
.pyre/
130+
131+
# Ignore VS Code settings
132+
.vscode/

estate_account/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1+
# License LGPL-3
12
from . import models

estate_account/__manifest__.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,9 @@
22
'name': "Account",
33
'version': '1.0',
44
'depends': ['base', 'estate_gasa', 'account'],
5-
'author': "Author Name",
5+
'author': "gasa",
66
'category': 'Category',
77
"license": "LGPL-3",
88
"application": True,
9-
"sequence": 1,
10-
# 'data': [
11-
# 'views/estate_property_views.xml',
12-
# ],
9+
"sequence": 1
1310
}

estate_account/models/estate.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,20 @@ class Estate(models.Model):
66
_inherit = 'estate.property'
77

88
def action_mark_sold(self):
9+
self.check_access_rights('write')
10+
self.check_access_rule('write')
911
res = super().action_mark_sold()
1012

13+
journal = self.env['account.journal'].sudo().search([('type', '=', 'sale')], limit=1)
14+
if not journal:
15+
raise UserError("No sale journal found. Please configure at least one sale journal.")
16+
1117
for record in self:
1218
if not record.buyer:
1319
raise UserError("Please set a Buyer before generating an invoice.")
1420
if not record.selling_price:
1521
raise UserError("Please set a Selling Price before generating an invoice.")
1622

17-
journal = self.env['account.journal'].search([('type', '=', 'sale')], limit=1)
18-
if not journal:
19-
raise UserError("No sale journal found. Please configure at least one sale journal.")
20-
2123
invoice_vals = {
2224
"partner_id": record.buyer.id,
2325
"move_type": "out_invoice",
@@ -36,6 +38,6 @@ def action_mark_sold(self):
3638
]
3739
}
3840

39-
self.env["account.move"].create(invoice_vals)
41+
self.env["account.move"].sudo().create(invoice_vals)
4042

4143
return res

estate_gasa/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1+
# License LGPL-3
12
from . import models

estate_gasa/__manifest__.py

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,24 @@
22
'name': "estate",
33
'version': '1.0',
44
'depends': ['base'],
5-
'author': "Author Name",
6-
'category': 'Category',
5+
'author': "gasa",
6+
'category': 'Real Estate/Brokerage',
77
"license": "LGPL-3",
88
"application": True,
99
"sequence": 1,
1010
'data': [
11-
'security/ir.model.access.csv',
12-
'views/estate_property_views.xml',
13-
'views/estate_property_offer_views.xml',
14-
'views/estate_property_type_views.xml',
15-
'views/estate_tag_views.xml',
16-
'views/inherited_model.xml',
17-
'views/estate_menus.xml',
11+
'security/security.xml',
12+
'security/ir.model.access.csv',
13+
'security/estate_property_rules.xml',
14+
'data/estate.property.type.csv',
15+
'views/estate_property_views.xml',
16+
'views/estate_property_offer_views.xml',
17+
'views/estate_property_type_views.xml',
18+
'views/estate_tag_views.xml',
19+
'views/inherited_model.xml',
20+
'views/estate_menus.xml'
21+
],
22+
"demo": [
23+
'demo/estate_property_demo_data.xml',
1824
],
1925
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
id,name
2+
estate_property_type_residential,Residential
3+
estate_property_type_commercial,Commercial
4+
estate_property_type_industrial,Industrial
5+
estate_property_type_land,Land
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<odoo>
3+
<record id="demo_property_big_villa" model="estate.property">
4+
<field name="name">Big Villa</field>
5+
<field name="state">new</field>
6+
<field name="description">A nice and big villa</field>
7+
<field name="postcode">12345</field>
8+
<field name="date_availability">2025-07-10</field>
9+
<field name="expected_price">97000</field>
10+
<field name="selling_price">97000</field>
11+
<field name="bedrooms">6</field>
12+
<field name="living_area">100</field>
13+
<field name="facades">4</field>
14+
<field name="garage">True</field>
15+
<field name="garden">True</field>
16+
<field name="garden_area">100</field>
17+
<field name="garden_orientation">south</field>
18+
<field name="property_type" ref="estate_property_type_residential" />
19+
</record>
20+
21+
<record id="demo_property_trailer_home" model="estate.property">
22+
<field name="name">Trailer home</field>
23+
<field name="state">cancelled</field>
24+
<field name="description">Home in a trailer park</field>
25+
<field name="postcode">54321</field>
26+
<field name="date_availability">2025-07-10</field>
27+
<field name="expected_price">1000</field>
28+
<field name="selling_price">1000</field>
29+
<field name="bedrooms">2</field>
30+
<field name="living_area">100</field>
31+
<field name="facades">4</field>
32+
<field name="garage">True</field>
33+
<field name="garden">True</field>
34+
<field name="garden_orientation">south</field>
35+
<field name="property_type" ref="estate_property_type_residential" />
36+
</record>
37+
38+
<record id="property_offer_1" model="estate.property.offer">
39+
<field name="partner_id" ref="base.res_partner_2" />
40+
<field name="property_id" ref="demo_property_big_villa" />
41+
<field name="price">90000</field>
42+
<field name="validity">14</field>
43+
<field name="date_deadline" eval="(datetime.now() + timedelta(days=14)).date().isoformat()" />
44+
</record>
45+
46+
<record id="property_offer_2" model="estate.property.offer">
47+
<field name="partner_id" ref="base.res_partner_2" />
48+
<field name="property_id" ref="demo_property_big_villa" />
49+
<field name="price">1500000</field>
50+
<field name="validity">14</field>
51+
<field name="date_deadline" eval="(datetime.now() + timedelta(days=14)).date().isoformat()" />
52+
</record>
53+
54+
<record id="property_offer_3" model="estate.property.offer">
55+
<field name="partner_id" ref="base.res_partner_3" />
56+
<field name="property_id" ref="demo_property_big_villa" />
57+
<field name="price">1500001</field>
58+
<field name="validity">14</field>
59+
<field name="date_deadline" eval="(datetime.now() + timedelta(days=14)).date().isoformat()" />
60+
</record>
61+
<function model="estate.property.offer" name="action_accept">
62+
<value eval="[ref('property_offer_1')]" />
63+
</function>
64+
65+
<function model="estate.property.offer" name="action_refuse">
66+
<value eval="[ref('property_offer_2')]" />
67+
</function>
68+
69+
<function model="estate.property.offer" name="action_refuse">
70+
<value eval="[ref('property_offer_3')]" />
71+
</function>
72+
73+
<record id="demo_property_with_inline_offers" model="estate.property">
74+
<field name="name">Estate with Inline Offers</field>
75+
<field name="state">new</field>
76+
<field name="postcode">44444</field>
77+
<field name="expected_price">120000</field>
78+
<field name="date_availability">2025-07-15</field>
79+
<field name="garden">True</field>
80+
<field name="garage">True</field>
81+
<field name="bedrooms">3</field>
82+
<field name="living_area">95</field>
83+
<field name="property_type" ref="estate_property_type_residential" />
84+
<field name="offer_ids"
85+
eval="[
86+
Command.create({
87+
'partner_id': ref('base.res_partner_2'),
88+
'price': 100000,
89+
'validity': 10,
90+
}),
91+
Command.create({
92+
'partner_id': ref('base.res_partner_3'),
93+
'price': 115000,
94+
'validity': 14,
95+
})
96+
]" />
97+
</record>
98+
</odoo>

estate_gasa/models/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
# License LGPL-3
12
from . import estate
23
from . import estate_property_type
34
from . import estate_property_tag

estate_gasa/models/estate.py

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ class Estate(models.Model):
2020
active = fields.Boolean(default=True)
2121
living_area = fields.Integer(string="Living Area")
2222
facades = fields.Integer(string="Facades")
23-
garden = fields.Boolean(string="Garage")
23+
garage = fields.Boolean(string="Garage")
24+
garden = fields.Boolean(string="Garden")
2425
garden_area = fields.Integer(string="Garden Area")
2526
garden_orientation = fields.Selection(
2627
[
@@ -64,16 +65,23 @@ class Estate(models.Model):
6465
store=True
6566
)
6667

67-
@api.depends('living_area', 'garden_area')
68-
def _compute_total_area(self):
69-
for record in self:
70-
record.total_area = record.living_area + record.garden_area
71-
7268
best_price = fields.Float(
7369
string="Best Offer",
7470
compute="_compute_best_price"
7571
)
72+
7673
selling_price = fields.Float(copy=False)
74+
company_id = fields.Many2one(
75+
'res.company',
76+
string='Company',
77+
required=True,
78+
default=lambda self: self.env.company
79+
)
80+
81+
@api.depends('living_area', 'garden_area')
82+
def _compute_total_area(self):
83+
for record in self:
84+
record.total_area = record.living_area + record.garden_area
7785

7886
@api.depends("offer_ids.price")
7987
def _compute_best_price(self):
@@ -102,11 +110,6 @@ def action_mark_cancelled(self):
102110
raise UserError("Sold properties cannot be canceled.")
103111
record.state = 'cancelled'
104112

105-
_sql_constraints = [
106-
('check_expected_price_positive', 'CHECK(expected_price > 0)', 'The expected price must be strictly positive.'),
107-
('check_selling_price_positive', 'CHECK(selling_price >= 0)', 'The selling price must be positive.'),
108-
]
109-
110113
@api.constrains('selling_price', 'expected_price')
111114
def _check_selling_price_threshold(self):
112115
for record in self:
@@ -127,3 +130,8 @@ def _check_property_state_before_delete(self):
127130
for record in self:
128131
if record.state not in ['new', 'cancelled']:
129132
raise UserError("You can only delete properties that are in 'New' or 'Cancelled' state.")
133+
134+
_sql_constraints = [
135+
('check_expected_price_positive', 'CHECK(expected_price > 0)', 'The expected price must be strictly positive.'),
136+
('check_selling_price_positive', 'CHECK(selling_price >= 0)', 'The selling price must be positive.'),
137+
]

0 commit comments

Comments
 (0)