Skip to content

Commit f14ce88

Browse files
committed
[ADD] estate, estate_account: Add the estate module
Blabla task-#######
1 parent fbf9ee9 commit f14ce88

19 files changed

+672
-0
lines changed

estate/__init__.py

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

estate/__manifest__.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
'name': "Estate",
3+
'version': '1.0',
4+
'depends': ['base'],
5+
'author': "Odoo",
6+
'category': 'Category',
7+
'description': """
8+
Just a dummy estate app
9+
""",
10+
'data' : [
11+
'security/ir.model.access.csv',
12+
13+
'views/estate_property_views.xml',
14+
'views/estate_property_offer_views.xml',
15+
'views/estate_property_type_views.xml',
16+
'views/estate_property_tag_views.xml',
17+
'views/res_users_views.xml',
18+
19+
'views/estate_menus.xml',
20+
],
21+
'installable': True,
22+
'application': True,
23+
'license': 'LGPL-3',
24+
}

estate/models/__init__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
from . import estate_property
2+
from . import estate_property_type
3+
from . import estate_property_tag
4+
from . import estate_property_offer
5+
from . import res_users

estate/models/estate_property.py

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
from odoo import api,fields,models
2+
from dateutil.relativedelta import relativedelta
3+
from odoo.exceptions import UserError, ValidationError
4+
from odoo.tools import float_compare, float_is_zero
5+
6+
7+
class estate_property(models.Model):
8+
# Private attributes
9+
_name = "estate.property"
10+
_description = "Estate property file"
11+
_order = "id desc"
12+
13+
_sql_constraints = [
14+
('check_expected_price', 'CHECK(expected_price > 0)',
15+
'The expected price should be strictly positive.'),
16+
('check_property_selling_price','CHECK(selling_price>=0)',
17+
'Property selling price should be posiive')
18+
]
19+
20+
# Default methods
21+
def _default_date_availability(self,number_of_months):
22+
return fields.Date.context_today(self) + relativedelta(months=number_of_months)
23+
24+
# Fields declaration
25+
name = fields.Char('Name',required=True, translate=True, default='Unknown')
26+
description = fields.Text("Description",required=True, translate=True)
27+
postcode = fields.Char("Post code",required=False, translate=True)
28+
date_avaibility = fields.Date("Avaibility date",required=False, copy=False, default=lambda self: self._default_date_availability(3))
29+
expected_price = fields.Float("Expected Price",required=False)
30+
selling_price = fields.Float("Selling Price",readonly=True, copy=False, required=False)
31+
bedrooms = fields.Integer("Bedrooms numbers",required=False, default=2)
32+
living_area = fields.Integer("Living Area",required=False)
33+
facades = fields.Integer("Facades",required=False)
34+
garage = fields.Boolean("Garage",required=False)
35+
garden = fields.Boolean("Garden",required=False)
36+
garden_area = fields.Integer("garden_area",required=False)
37+
orientation = fields.Selection(
38+
string="Garden Orientation",
39+
selection=[('North','North'),
40+
('East','East'),
41+
('South','South'),
42+
('West','West')]
43+
)
44+
estate_state = fields.Selection(
45+
string="Estate State",
46+
selection=[('New','New'),
47+
('Offer_Received','Offer Received'),
48+
('Offer_Accepted','Offer Accepted'),
49+
('Sold','Sold'),
50+
("Cancelled","Cancelled")],
51+
default="New",
52+
copy=False
53+
)
54+
active = fields.Boolean("active",required=True,default=True)
55+
total_area = fields.Float(compute="_compute_total_area")
56+
best_offer = fields.Float(compute="_compute_best_offer")
57+
58+
property_type_id = fields.Many2one("estate.property.type", string="Property Type")
59+
property_salesman_id = fields.Many2one("res.users", string="Salesman", default=lambda self: self.env.user)
60+
property_buyer_id = fields.Many2one("res.partner", string="Buyer")
61+
62+
property_tag_ids = fields.Many2many("estate.property.tag", string="Tags")
63+
64+
property_offer_ids = fields.One2many("estate.property.offer","property_id", string="Offers")
65+
66+
note = fields.Text("Special mentions about the house.")
67+
68+
# compute and search fields
69+
@api.depends("living_area","garden_area")
70+
def _compute_total_area(self):
71+
for prop in self:
72+
prop.total_area = prop.living_area+prop.garden_area
73+
74+
@api.depends("property_offer_ids.price")
75+
def _compute_best_offer(self):
76+
for prop in self:
77+
prop.best_offer = max(prop.property_offer_ids.mapped("price")) if prop.property_offer_ids else 0.0
78+
79+
# Constraints and onchanges# Constraints and onchanges
80+
@api.constrains("selling_price")
81+
def _check_selling_price(self):
82+
for record in self:
83+
if float_compare(record.selling_price, record.expected_price * 90.0 / 100.0, precision_rounding=0.01) < 0:
84+
raise ValidationError("The selling price is to low")
85+
86+
@api.onchange("garden")
87+
def _onchange_garden(self):
88+
if self.garden == True:
89+
self.garden_area = 100
90+
self.orientation = "North"
91+
else:
92+
self.garden_area = 0
93+
self.orientation = False
94+
95+
# CRUD methods
96+
@api.ondelete(at_uninstall=False)
97+
def _delete_house(self):
98+
for record in self:
99+
if record.estate_state not in ["New","Cancelled"]:
100+
raise UserError("Can't delete an active house!")
101+
102+
# Action methods
103+
def action_cancel_property(self):
104+
for record in self:
105+
if record.estate_state == "Sold":
106+
raise UserError('Unable to Cancel a sold estate, please change estate state before continuing.')
107+
record.estate_state = "Cancelled"
108+
109+
def action_sold_property(self):
110+
for record in self:
111+
if record.estate_state == "Cancelled":
112+
raise UserError('Unable to sold a cancelled estate, please change estate state before continuing.')
113+
record.estate_state = "Sold"
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
from odoo import fields,models,api
2+
from dateutil.relativedelta import relativedelta
3+
from odoo.exceptions import UserError
4+
5+
6+
class estate_property_offer(models.Model):
7+
# Private attributes
8+
_name = "estate.property.offer"
9+
_description = "Estate property offer file"
10+
_order = "price desc"
11+
12+
_sql_constraints = [
13+
('check_offer_price', 'CHECK(price > 0)',
14+
'The offer price should be strictly positive.')
15+
]
16+
17+
# Fields declaration
18+
price = fields.Integer('Price',required=True, default=100000)
19+
validity = fields.Integer("validity days", default=7)
20+
date_dateline = fields.Date("Date deadline",compute="_compute_date_deadline", inverse="_inverse_date_deadline")
21+
status = fields.Selection(
22+
string="Offer Status",
23+
selection=[('Accepted','Acccepted'),
24+
('In Waiting','In Waiting'),
25+
('Refused','Refused')],
26+
default="In Waiting",
27+
copy=False
28+
)
29+
partner_id = fields.Many2one("res.partner", string="Partner",required=True)
30+
property_id = fields.Many2one("estate.property", string="Property",required=True)
31+
property_type_id = fields.Many2one("estate.property.type", related="property_id.property_type_id", string="Property Type", store=True)
32+
33+
# compute and search fields
34+
@api.depends("validity")
35+
def _compute_date_deadline(self):
36+
for record in self:
37+
record.date_dateline=fields.Date.context_today(self)+relativedelta(days=record.validity)
38+
39+
def _inverse_date_deadline(self):
40+
for record in self:
41+
record.validity = (record.date_dateline - fields.Date.context_today(self)).days
42+
43+
# CRUD methods
44+
@api.model
45+
def create(self, vals):
46+
if vals.get("property_id") and vals.get("price"):
47+
prop = self.env["estate.property"].browse(vals["property_id"])
48+
if prop.property_offer_ids:
49+
max_offer = max(prop.property_offer_ids.mapped("price"))
50+
print(max_offer)
51+
if vals["price"]<max_offer:
52+
raise UserError("offer with better price already exist")
53+
else:
54+
prop.estate_state="Offer_Received"
55+
return super().create(vals)
56+
57+
#Action methods
58+
def action_refuse_offer(self):
59+
for record in self:
60+
if record.status == "Accepted":
61+
raise UserError('Unable to refuse offer, please change offer status before continuing.')
62+
record.status = "Refused"
63+
64+
def action_accept_offer(self):
65+
for record in self:
66+
if record.property_id.estate_state=="Cancelled" or record.property_id.estate_state=="Sold":
67+
raise UserError("Unable to accept offer (estate can't be sold), please change estate status before continuing.")
68+
if record.status == "Refused":
69+
raise UserError('Unable to accept offer, please change offer status before continuing.')
70+
record.status = "Accepted"
71+
record.property_id.estate_state="Offer_Accepted"
72+
record.property_id.property_buyer_id = record.partner_id
73+
record.property_id.selling_price =record.price

estate/models/estate_property_tag.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
from odoo import fields,models
2+
3+
4+
class estate_property_tag(models.Model):
5+
# Private attributes
6+
_name = "estate.property.tag"
7+
_description = "Estate property tag file"
8+
_order = "name"
9+
10+
_sql_constraints = [
11+
("check_name", "UNIQUE(name)", "The name must be unique"),
12+
]
13+
14+
# Fields declaration
15+
name = fields.Char('Name',required=True, translate=True, default='Unknown')
16+
color = fields.Integer("couleur")

estate/models/estate_property_type.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
from odoo import fields,models,api
2+
3+
4+
class estate_property_type(models.Model):
5+
# Private attributes
6+
_name = "estate.property.type"
7+
_description = "Estate property type file"
8+
_order = "sequence, name"
9+
10+
_sql_constraints = [
11+
("check_name", "UNIQUE(name)", "The name must be unique"),
12+
]
13+
14+
# Fields declaration
15+
name = fields.Char('Name',required=True, translate=True, default='Unknown')
16+
property_ids = fields.One2many("estate.property","property_type_id",string="Property")
17+
offer_ids = fields.One2many("estate.property.offer","property_type_id",string="Property")
18+
offer_count = fields.Integer("Offer counted",compute="_compute_offer_count")
19+
20+
sequence = fields.Integer('Sequence', default=1)
21+
22+
# compute and search fields
23+
@api.depends("offer_ids.price")
24+
def _compute_offer_count(self):
25+
for record in self:
26+
record.offer_count=len(record.offer_ids.mapped("price")) if record.offer_ids else 0

estate/models/res_users.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
from odoo import api,fields,models
2+
3+
class res_users(models.Model):
4+
# Private attributes
5+
_inherit = "res.users"
6+
7+
# Fields declaration
8+
property_ids = fields.One2many("estate.property","property_salesman_id",string="Properties",domain=[("estate_state","in",["New","Offer_Received"])])
9+
10+
# @api.onchange("property_ids")
11+
# def _onchange_garden(self):
12+
# print("####################################################")
13+
# for record in self:
14+
# print(record.property_ids)

estate/security/ir.model.access.csv

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
2+
estate.access_estate_property,access_estate_property,estate.model_estate_property,base.group_user,1,1,1,1
3+
estate.access_estate_property_type,access_estate_property_type,estate.model_estate_property_type,base.group_user,1,1,1,1
4+
estate.access_estate_property_tag,access_estate_property_tag,estate.model_estate_property_tag,base.group_user,1,1,1,1
5+
estate.access_estate_property_offer,access_estate_property_offer,estate.model_estate_property_offer,base.group_user,1,1,1,1

estate/views/estate_menus.xml

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<odoo>
3+
<menuitem
4+
id="estate_menu_root"
5+
name="Estate"
6+
sequence="5"
7+
/>
8+
9+
<menuitem
10+
id="estate_menu_advertissement"
11+
name="Advertissement"
12+
parent="estate_menu_root"
13+
sequence="10"
14+
/>
15+
<menuitem
16+
id="estate_menu_advertissement_property"
17+
name="Property"
18+
parent="estate_menu_advertissement"
19+
action="estate_property_view"
20+
sequence="15"
21+
/>
22+
23+
<menuitem
24+
id="estate_menu_settings"
25+
name="Settings"
26+
parent="estate_menu_root"
27+
sequence="10"
28+
/>
29+
<menuitem
30+
id="estate_menu_settings_type"
31+
name="Property Types"
32+
parent="estate_menu_settings"
33+
action="estate_property_type_view"
34+
sequence="15"
35+
/>
36+
<menuitem
37+
id="estate_menu_settings_tag"
38+
name="Property Tags"
39+
parent="estate_menu_settings"
40+
action="estate_property_tag_view"
41+
sequence="15"
42+
/>
43+
</odoo>
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<?xml version="1.0"?>
2+
<odoo>
3+
<record id="estate_property_offer_view_form" model="ir.ui.view">
4+
<field name="name">estate.property.offer.view.form</field>
5+
<field name="model">estate.property.offer</field>
6+
<field name="arch" type="xml">
7+
<form string="Estate Property Offers">
8+
<sheet>
9+
<group>
10+
<h1>
11+
<field name="price"/>
12+
</h1>
13+
<field name="status"/>
14+
<field name="partner_id"/>
15+
<field name="property_id"/>
16+
<field name="date_dateline"/>
17+
<field name="validity"/>
18+
</group>
19+
</sheet>
20+
</form>
21+
</field>
22+
</record>
23+
24+
<record id="estate_property_offer_view_tree" model="ir.ui.view">
25+
<field name="name">estate.property.offer.view.list</field>
26+
<field name="model">estate.property.offer</field>
27+
<field name="arch" type="xml">
28+
<list string="Channel" editable ='bottom' decoration-success="status=='Accepted'" decoration-danger="status=='Refused'">
29+
<field name="price"/>
30+
<field name="partner_id"/>
31+
<field name="validity"/>
32+
<field name="date_dateline" optional="True"/>
33+
<field name="property_type_id"/>
34+
<button name="action_accept_offer" type="object" string="Accept" icon="fa-check" invisible="status!='In Waiting'"/>
35+
<button name="action_refuse_offer" type="object" string="Refuse" icon="fa-times" invisible="status!='In Waiting'"/>
36+
</list>
37+
</field>
38+
</record>
39+
40+
<record id="estate_property_offer_view" model="ir.actions.act_window">
41+
<field name="name">Estate Properties Offers</field>
42+
<field name="res_model">estate.property.offer</field>
43+
<field name="domain">[('property_type_id','=', active_id)]</field>
44+
<field name="view_mode">list,form</field>
45+
</record>
46+
</odoo>

0 commit comments

Comments
 (0)