Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

17.0 estate azey #125

Closed
wants to merge 23 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
ace946e
[ADD] estate: creation of estate dir
elhayyany Aug 20, 2024
239f6e9
[ADD] estate: creation of estate mainfest
elhayyany Aug 20, 2024
db2623a
[ADD] estate: mv mainfes to manifest
elhayyany Aug 20, 2024
a072d75
[ADD] estate: converting estate to app
elhayyany Aug 20, 2024
a4dfa7e
[ADD] estate: createing the tabe and it fields
elhayyany Aug 21, 2024
4fc79af
[IMP] estate: fixing all style errors in estate directory
elhayyany Aug 23, 2024
36ea210
[IMP] estate: fixing all style errors in TUTORIALS directory
elhayyany Aug 23, 2024
52fb196
[IMP] estate: fixing all style errors in TUTORIALS directory
elhayyany Aug 23, 2024
3737b95
[IMP] estate: fixing all style errors in TUTORIALS directory
elhayyany Aug 23, 2024
316cb51
[ADD] estate: I added access rights to estate_property_type model
elhayyany Aug 23, 2024
b5f7bc5
[ADD] estate: I added license to estate manifest
elhayyany Aug 23, 2024
ad34fad
[ADD] estate: I Buyer and salesman atributes to estate_property model
elhayyany Aug 23, 2024
fc9ce5e
[ADD] estate: I added the Property tag view and link it to estate pro…
elhayyany Aug 23, 2024
6948985
[ADD] estate: saving ..
elhayyany Aug 23, 2024
e41e358
[IMP] estate: Made revisions based on reviewer feedback
elhayyany Aug 26, 2024
b2b7d20
[IMP] estate: Made revisions based on reviewer feedback
elhayyany Aug 26, 2024
c2a1bcd
[IMP] estate: Made revisions based on reviewer feedback
elhayyany Aug 26, 2024
d2f29a1
[ADD] estate: I add What was asked in chapters 'till 11
elhayyany Aug 29, 2024
0b83187
[ADD] estate: I add chapter up to 11
elhayyany Aug 29, 2024
f1eb78b
[IMP] estate: style improving to match runbot requirements
elhayyany Aug 29, 2024
7cc8aef
[IMP] estate: style improving to match runbot requirements
elhayyany Aug 29, 2024
75606fb
[IMP] estate: style improving to match runbot requirements
elhayyany Aug 29, 2024
e79faca
[IMP] estate: style improving to match runbot requirements
elhayyany Aug 29, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -127,3 +127,6 @@ dmypy.json

# Pyre type checker
.pyre/


forme.txt
1 change: 0 additions & 1 deletion awesome_clicker/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +0,0 @@
# -*- coding: utf-8 -*-
2 changes: 1 addition & 1 deletion awesome_clicker/__manifest__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# -*- coding: utf-8 -*-

{
'name': "Awesome Clicker",

Expand Down
2 changes: 1 addition & 1 deletion awesome_dashboard/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
# -*- coding: utf-8 -*-


from . import controllers
2 changes: 1 addition & 1 deletion awesome_dashboard/__manifest__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# -*- coding: utf-8 -*-

{
'name': "Awesome Dashboard",

Expand Down
4 changes: 2 additions & 2 deletions awesome_dashboard/controllers/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
# -*- coding: utf-8 -*-

from . import controllers

from . import controllers
5 changes: 2 additions & 3 deletions awesome_dashboard/controllers/controllers.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
# -*- coding: utf-8 -*-


import logging
import random

from odoo import http
from odoo.http import request

logger = logging.getLogger(__name__)


class AwesomeDashboard(http.Controller):
@http.route('/awesome_dashboard/statistics', type='json', auth='user')
def get_statistics(self):
Expand All @@ -33,4 +33,3 @@ def get_statistics(self):
},
'total_amount': random.randint(100, 1000)
}

2 changes: 1 addition & 1 deletion awesome_gallery/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
# -*- coding: utf-8 -*-

from . import models
2 changes: 1 addition & 1 deletion awesome_gallery/__manifest__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# -*- coding: utf-8 -*-

{
'name': "Gallery View",
'summary': """
Expand Down
2 changes: 1 addition & 1 deletion awesome_gallery/models/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# -*- coding: utf-8 -*-

# import filename_python_file_within_folder_or_subfolder
from . import ir_action
from . import ir_ui_view
4 changes: 2 additions & 2 deletions awesome_gallery/models/ir_action.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# -*- coding: utf-8 -*-

from odoo import fields, models


Expand All @@ -7,4 +7,4 @@ class ActWindowView(models.Model):

view_mode = fields.Selection(selection_add=[
('gallery', "Awesome Gallery")
], ondelete={'gallery': 'cascade'})
], ondelete={'gallery': 'cascade'})
2 changes: 1 addition & 1 deletion awesome_gallery/models/ir_ui_view.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# -*- coding: utf-8 -*-

from odoo import fields, models


Expand Down
1 change: 0 additions & 1 deletion awesome_kanban/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +0,0 @@
# -*- coding: utf-8 -*-
2 changes: 1 addition & 1 deletion awesome_kanban/__manifest__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# -*- coding: utf-8 -*-

{
'name': "Awesome Kanban",
'summary': """
Expand Down
4 changes: 2 additions & 2 deletions awesome_owl/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
# -*- coding: utf-8 -*-

from . import controllers

from . import controllers
4 changes: 2 additions & 2 deletions awesome_owl/__manifest__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# -*- coding: utf-8 -*-

{
'name': "Awesome Owl",

Expand Down Expand Up @@ -36,7 +36,7 @@

# required for fa icons
'web/static/src/libs/fontawesome/css/font-awesome.css',

# include base files from framework
('include', 'web._assets_core'),

Expand Down
4 changes: 2 additions & 2 deletions awesome_owl/controllers/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
# -*- coding: utf-8 -*-

from . import controllers

from . import controllers
3 changes: 2 additions & 1 deletion awesome_owl/controllers/controllers.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from odoo import http
from odoo.http import request, route
from odoo.http import request


class OwlPlayground(http.Controller):
@http.route(['/awesome_owl'], type='http', auth='public')
Expand Down
1 change: 1 addition & 0 deletions estate/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import models
18 changes: 18 additions & 0 deletions estate/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
'name': 'Estate',
'description': 'A module to manage real estate advertisments',
'depends': ['base'],

elhayyany marked this conversation as resolved.
Show resolved Hide resolved
'data': [
'security/ir.model.access.csv',
'views/estate_property_views.xml',
'views/estate_property_type_views.xml',
'views/estate_property_tag_views.xml',
'views/estate_menus.xml',
'views/estate_property_offer_view.xml'
],

'installable': True,
'application': True,
'license': 'AGPL-3',
}
4 changes: 4 additions & 0 deletions estate/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from . import estate_property
from . import estate_property_type
from . import estate_property_tag
from . import estate_property_offer
102 changes: 102 additions & 0 deletions estate/models/estate_property.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
from odoo import api, fields, models
from odoo.exceptions import UserError


class EstateProperty(models.Model):
_name = 'estate.property'
_description = 'The Real Estate Advertisement module'
_order = "id desc"
_sql_constraints = [
('check_expected_price', 'CHECK(expected_price > 0)',
'A property expected price must be strictly positive'),
('check_selling_price', 'CHECK(selling_price >= 0)',
'A property selling price must be positive'),
]

name = fields.Char('Name', required=True)
description = fields.Text('Description')
postcode = fields.Char('Postcode')
bedrooms = fields.Integer('Bedrooms', default=2)
living_area = fields.Integer('Living Area (sqrm)')
facades = fields.Integer('Fcades')
garage = fields.Boolean(string="Garage", default=False)
garden = fields.Boolean(string="Garden", default=False)
garden_area = fields.Integer('Garden Area', default=0)
expected_price = fields.Float('Expected Price', required=True)
active = fields.Boolean(default=True)
sales_man_id = fields.Many2one("res.users", string="Salesmam", default=lambda self: self._uid)
buyer_id = fields.Many2one("res.partner", string="Buyer", copy=False)
type_id = fields.Many2one("estate.property.type", string="Type")
tag_ids = fields.Many2many("estate.property.tag", string="Tags")
offer_ids = fields.One2many("estate.property.offer", "property_id", string="Offer")
total_area = fields.Integer(compute="_compute_total_area", string="Total Area (sqrm)")
best_price = fields.Float(compute="_compute_best_price", string="Best Offer")
date_availability = fields.Date(
'Availability Date',
copy=False,
default=fields.Datetime.add(fields.Datetime.today(), months=3),
)

selling_price = fields.Float(
'Selling Price',
readonly=True,
copy=False,
)

garden_orientation = fields.Selection(
[
('north', 'North'),
('south', 'South'),
('east', 'East'),
('west', 'West'),
],
string='Garden Orientation',
)
state = fields.Selection(
[
("new", "New"),
("offer_received", "Offer Received"),
("offer_accepted", "Offer Accepted"),
("sold", "Sold"),
("canceled", "Canceled"),
],
default="new",
required=True,
copy=False,
string="Status",
)

@api.depends("living_area", "garden_area")
def _compute_total_area(self):
for record in self:
record.total_area = record.living_area + record.garden_area
Comment on lines +71 to +72

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know they use record in the tutorial, but something more meaningful, like estate will make your code much more readable in the long run


@api.depends("offer_ids")
def _compute_best_price(self):
for record in self:
record.best_price = max(record.offer_ids.mapped('price'), default=0)
Comment on lines +76 to +77

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here

Suggested change
for record in self:
record.best_price = max(record.offer_ids.mapped('price'), default=0)
for estate in self:
estate.best_price = max(estate.offer_ids.mapped('price'), default=0)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Btw, I didn't know max took a default value :o Thanks for showing me :D


@api.onchange("garden")
def _onchange_garden(self):
if self.garden:
self.garden_area = 10
self.garden_orientation = "north"
else:
self.garden_area = 0
self.garden_orientation = False

def action_sold_property(self):
if "canceled" in self.mapped("state"):

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method may be called on several records (self is multiple records). There are two ways to deal with this:
1 - either like you've done in the compute methods with a for estate in self: loop.
2 - or, by adding self.ensure_one() at the beginning of your method. This triggers an error if there are more or less than one record.

Usually, we tend to prefer the 1st option when possible to improve performances of the server.

raise UserError("Canceled properties cannot be sold.")

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Like for the string parameter in the fields, we try to make all strings translatable. Usually, it is done automatically, like for the fields name. In exceptions, it is not done automatically yet. To set a manual string as translatable, you have to import _ -> from odoo import _ and use it as such

Suggested change
raise UserError("Canceled properties cannot be sold.")
raise UserError(_("Canceled properties cannot be sold."))

p.s: could you do it for all the other error messages as well

return self.write({"state": "sold", "active": False})

def action_cancel_property(self):

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some for this method about the several records

if "sold" in self.mapped("state"):
raise UserError("Sold properties cannot be canceled.")
return self.write({"state": "canceled", "active": False})

def unlink(self):
for record in self:
if not (record.state in ('new', 'canceled')):
raise UserError("It's not possible to delete a property which is not new or canceled!")
return super().unlink()
75 changes: 75 additions & 0 deletions estate/models/estate_property_offer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
from odoo import api, fields, models
from odoo.exceptions import UserError, ValidationError


class estatePropertyOffer(models.Model):
_name = "estate.property.offer"
_description = "The offer of estate property"
_order = "price desc"
_sql_constraints = [
('check_offer_price', 'CHECK(price > 0)',
'A property offer price must be strictly positive'),
]

price = fields.Float("Price")
partner_id = fields.Many2one("res.partner", required=True)
validity = fields.Integer(string="Validity (days)", default=7)
property_id = fields.Many2one('estate.property', required=True)
property_type_id = fields.Many2one("estate.property.type", related="property_id.type_id", store=True)

state = fields.Selection(
selection=[
("accepted", "Accepted"),
("refused", "Refused"),
],
string="Status",
copy=False,
default=False,
)
date_deadline = fields.Date(
string="Deadline (days)",
compute="_compute_date_deadline",
inverse="_inverse_date_deadline",
)

@api.depends('validity')
def _compute_date_deadline(self):
for record in self:

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
for record in self:
for offer in self:

if record.create_date:
record.date_deadline = fields.Datetime.add(record.create_date, days=record.validity)
else:
record.date_deadline = fields.Datetime.add(fields.Datetime.today(), days=record.validity)

def _inverse_date_deadline(self):
for offer in self:
date = offer.create_date.date() if offer.create_date else fields.Date.today()
offer.validity = (offer.date_deadline - date).days

def action_accepted(self):

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment about the multiple records in this method

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, shouldn't this method refuse all other offers if one has been accepted ?

if "Accepted" in self.mapped("state"):

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if "Accepted" in self.mapped("state"):
if "accepted" in self.mapped("state"):

? I believe your key from the state field is accepted without capital letter

raise UserError("An offer as already been accepted.")
self.write(
{
"state": "accepted",
}
)
Comment on lines +51 to +55

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
self.write(
{
"state": "accepted",
}
)
self.write({"state": "accepted"})

return self.mapped("property_id").write(
{
"state": "offer_accepted",
"selling_price": self.price,
"buyer_id": self.partner_id.id,
}
)

def action_refused(self):

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment about multiple records

return self.write(
{
"state": "refused",
}
)
Comment on lines +65 to +69

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return self.write(
{
"state": "refused",
}
)
return self.write({"state": "refused"})


@api.constrains("price", "property_id.expected_price")
def check_price(self):
for record in self:

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
for record in self:
for estate in self:

if record.price < (0.9 * record.property_id.expected_price):
raise ValidationError("The selling price must be at least 90%% of the expected price")
14 changes: 14 additions & 0 deletions estate/models/estate_property_tag.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from odoo import fields, models


class estatePropertyTag(models.Model):
_name = "estate.property.tag"
_description = "This module introduces property tags to the real estate model, allowing properties to be categorized with multiple descriptive tags like 'cozy' or 'renovated' using a many-to-many relationship."

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
_description = "This module introduces property tags to the real estate model, allowing properties to be categorized with multiple descriptive tags like 'cozy' or 'renovated' using a many-to-many relationship."
_description = """
This module introduces property tags to the real estate model, allowing
properties to be categorized with multiple descriptive tags like 'cozy'
or 'renovated' using a many-to-many relationship.
"""

_order = "name"
_sql_constraints = [
('check_tag', 'UNIQUE(name)',
'A property tag name must be unique'),
]

name = fields.Char("Name", required=True)
color = fields.Integer("Color")
22 changes: 22 additions & 0 deletions estate/models/estate_property_type.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from odoo import api, fields, models


class estatePropertyType(models.Model):
_name = "estate.property.type"
_description = "the type of the property ..."
elhayyany marked this conversation as resolved.
Show resolved Hide resolved
_order = "sequence, name"
_sql_constraints = [
('check_tag', 'UNIQUE(name)',
'A property type name must be unique'),
]

name = fields.Char("Name")
property_ids = fields.One2many("estate.property", "type_id", string="Properties")
sequence = fields.Integer("Secuence", default=1)
offer_ids = fields.One2many("estate.property.offer", "property_type_id", string="offers")
offer_count = fields.Integer(string="Offers Count", compute="_compute_offers")

@api.depends("offer_ids")
def _compute_offers(self):
for record in self:

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
for record in self:
for property_type in self:

record.offer_count = len(record.offer_ids)
5 changes: 5 additions & 0 deletions estate/security/ir.model.access.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
estate_properry_model,access_model,model_estate_property,base.group_user,1,1,1,1
elhayyany marked this conversation as resolved.
Show resolved Hide resolved
estate_properry_type_model,access_model,model_estate_property_type,base.group_user,1,1,1,1
estate_properry_tag_model,access_model,model_estate_property_tag,base.group_user,1,1,1,1
estate_properry_offer_model,access_model,model_estate_property_offer,base.group_user,1,1,1,1
11 changes: 11 additions & 0 deletions estate/views/estate_menus.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<odoo>
<menuitem id="root_menu_estate" name="Real Estate">
<menuitem id="estate_first_level_menu" name="Advertisements">
<menuitem id="model_menu_action" action="estate_property_action"/>
</menuitem>
<menuitem id="property_type_menu" name="Settings">
<menuitem id="estate_property_tag_menu" action="estate_property_tag_action"/>
<menuitem id="estate_property_type_menu" action="estate_property_type_action"/>
</menuitem>
</menuitem>
</odoo>
Loading