-
Notifications
You must be signed in to change notification settings - Fork 2.1k
[ADD] estate: it creates a basic estate application #787
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
Open
MaximeNoirhomme229
wants to merge
20
commits into
odoo:18.0
Choose a base branch
from
odoo-dev:18.0-task-tutorial-noma
base: 18.0
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
20 commits
Select commit
Hold shift + click to select a range
a67237e
[ADD] Create a basic estate application
MaximeNoirhomme229 dddec52
[ADD] add menus (chap 5)
MaximeNoirhomme229 49cc7ee
[FIX] estate: resolve time deltas based on timezones
barracudapps d5eecbf
[FIX] add license and date availability 3 mouth instead of 1
MaximeNoirhomme229 1a1ac3a
[ADD] Add form and list view with filter on status and group by on p…
MaximeNoirhomme229 430fb4b
[ADD] add link with user and partner model + create estate property type
MaximeNoirhomme229 4bb21bc
[FIX] style
MaximeNoirhomme229 9d7fc0b
[FIX] give a proper name to the estate property type class and rename…
MaximeNoirhomme229 ff9a522
[ADD] estate: add tags and offer to the estate view
MaximeNoirhomme229 eedcbf4
[ADD] estate: compute best offer, total area, offer deadline (chap 8)
MaximeNoirhomme229 c51ae78
[ADD] estate: it can cancel/sold a property and accept or refuse an o…
MaximeNoirhomme229 d36ea31
[ADD] estate: it creates sql and python contraints
MaximeNoirhomme229 1edc71a
[IMP] estate: it reorganises the imports to make them clearer
MaximeNoirhomme229 2a74e2f
[IMP] estate: it simplifies available filter using in keyword
MaximeNoirhomme229 c4c7c03
[ADD] estate: it adds sprinkle to the interface (chap 11)
MaximeNoirhomme229 759c68e
[FIX] estate: it fixes style
MaximeNoirhomme229 5ede1fe
[FIX] estate: it remove the local config from .gitignore file
MaximeNoirhomme229 e844769
[FIX] estate: it removes white space
MaximeNoirhomme229 63033b3
[FIX] estate: it removes white space
MaximeNoirhomme229 6ad2139
[ADD] estate: chap 12 to chap 14
MaximeNoirhomme229 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
from . import models |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
{ | ||
'name': 'A real state module', | ||
'version': '1.0', | ||
'depends': ['base'], | ||
'application': True, | ||
'license': 'LGPL-3', | ||
'data': [ | ||
'security/ir.model.access.csv', | ||
'views/view_properties.xml', | ||
'views/view_properties_type.xml', | ||
'views/view_properties_tag.xml', | ||
'views/estate_menus.xml', | ||
'views/view_properties_user.xml', | ||
], | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
from . import estate_property | ||
from . import estate_property_offer | ||
from . import estate_property_tag | ||
from . import estate_property_type | ||
from . import estate_user |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
from dateutil.relativedelta import relativedelta | ||
from odoo import api, exceptions, fields, models | ||
from odoo.tools.float_utils import float_compare | ||
|
||
|
||
class EstateProperty(models.Model): | ||
_name = 'estate.property' | ||
_description = 'estate property module' | ||
_order = 'id desc' | ||
|
||
name = fields.Char(required=True) | ||
description = fields.Text("Description") | ||
date_availability = fields.Date( | ||
"Date Availability", default=fields.Date.today() + relativedelta(months=3), copy=False | ||
) | ||
expected_price = fields.Float("Expected Price", required=True) | ||
selling_price = fields.Float("Selling Price", default=0, readonly=True, copy=False) | ||
bedrooms = fields.Integer(required=True, default=2) | ||
living_area = fields.Integer("Living Area (sqm)", required=True) | ||
facades = fields.Integer(default=0) | ||
garage = fields.Boolean(default=False) | ||
garden = fields.Boolean(default=False) | ||
garden_area = fields.Integer("Garden Area (sqm)") | ||
garden_orientation = fields.Selection([('north', 'North'), ('east', 'East'), ('south', 'South'), ('west', 'West')]) | ||
total_area = fields.Integer("Total Area (sqm)", compute='_compute_total_area') | ||
postcode = fields.Integer() | ||
active = fields.Boolean(default=True) | ||
status = fields.Selection( | ||
[ | ||
('new', 'New'), | ||
('offer received', 'Offer Received'), | ||
('offer accepted', 'Offer Accepted'), | ||
('sold', 'Sold'), | ||
('cancelled', 'Cancelled'), | ||
], | ||
default='new', | ||
copy=False, | ||
) | ||
estate_type_id = fields.Many2one(comodel_name='estate.property.type') | ||
estate_tag_ids = fields.Many2many(comodel_name='estate.property.tag') | ||
estate_offer_ids = fields.One2many('estate.property.offer', 'property_id', string='Offers') | ||
partner_id = fields.Many2one(comodel_name='res.partner') | ||
user_id = fields.Many2one(comodel_name='res.users', default=lambda self: self.env.user) | ||
best_offer = fields.Float(compute='_compute_best_price') | ||
_sql_constraints = [ | ||
( | ||
'check_stricly_positive_expected_price', | ||
'CHECK(expected_price > 0)', | ||
'The expected price of an estate property must be strictly positive.', | ||
), | ||
( | ||
'check_positive_selling_price', | ||
'CHECK(selling_price >= 0)', | ||
'The selling price of an estate property must be positive.', | ||
), | ||
] | ||
|
||
@api.constrains('selling_price', 'expected_price') | ||
def _check_valid_selling_price(self): | ||
for record in self: | ||
if not (any(offer.status == 'accepted' for offer in self.estate_offer_ids)): | ||
continue | ||
|
||
if float_compare(record.selling_price, 0.9 * record.expected_price, precision_digits=2) < 0: | ||
raise exceptions.ValidationError("The selling price must not be below 90 % of the expected price") | ||
|
||
@api.depends('garden_area', 'living_area') | ||
def _compute_total_area(self): | ||
for record in self: | ||
record.total_area = record.garden_area + record.living_area | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What happens here if there is no garden ? |
||
|
||
@api.depends('estate_offer_ids.price') | ||
def _compute_best_price(self): | ||
for record in self: | ||
if len(record.estate_offer_ids) == 0: | ||
record.best_offer = 0 | ||
else: | ||
record.best_offer = max(record.estate_offer_ids.mapped('price')) | ||
|
||
@api.onchange('garden') | ||
def _onchange_partner_id(self): | ||
if self.garden: | ||
self.garden_orientation = 'north' | ||
self.garden_area = 10 | ||
else: | ||
self.garden_orientation = None | ||
self.garden_area = 0 | ||
|
||
def action_cancel(self): | ||
if self.status == 'sold': | ||
raise exceptions.UserError('A sold property cannot be cancelled') | ||
|
||
self.status = 'cancelled' | ||
|
||
def action_sold(self): | ||
if self.status == 'cancelled': | ||
raise exceptions.UserError('A cancelled property cannot be sold') | ||
|
||
self.status = 'sold' | ||
|
||
@api.ondelete(at_uninstall=False) | ||
def _unlink_if_estate_is_not_new_nor_cancelled(self): | ||
if any(estate.status!='new' and estate.status!='cancelled' for estate in self): | ||
raise exceptions.UserError('Can only delete new and cancelled records!') |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
from dateutil.relativedelta import relativedelta | ||
from odoo import api, exceptions, fields, models | ||
|
||
|
||
class EstatePropertyOffer(models.Model): | ||
_name = 'estate.property.offer' | ||
_description = 'estate property offer module' | ||
_order = 'price desc' | ||
|
||
name = fields.Char(required=True) | ||
price = fields.Float() | ||
status = fields.Selection( | ||
[ | ||
('accepted', 'Accepted'), | ||
('refused', 'Refused'), | ||
], | ||
copy=False, | ||
) | ||
|
||
validity = fields.Integer(default=7) | ||
create_date = fields.Date(default=fields.Date.today(), readonly=True) | ||
date_deadline = fields.Date(compute='_compute_deadline', inverse='_compute_inverse_deadline') | ||
|
||
partner_id = fields.Many2one(comodel_name='res.partner', required=True) | ||
property_id = fields.Many2one(comodel_name='estate.property', required=True) | ||
property_type_id = fields.Many2one(comodel_name='estate.property.type', related='property_id.estate_type_id') | ||
|
||
_sql_constraints = [ | ||
( | ||
'check_stricly_positive_price', | ||
'CHECK(price > 0)', | ||
'The price of an estate property offer must be strictly positive.', | ||
), | ||
] | ||
|
||
@api.depends('create_date', 'validity') | ||
def _compute_deadline(self): | ||
for record in self: | ||
record.date_deadline = record.create_date + relativedelta(days=record.validity) | ||
|
||
@api.model_create_multi | ||
def create(self, val_list): | ||
for val in val_list: | ||
val_property_id = val.get('property_id') | ||
val_curr_price = val.get('price') | ||
property_id = self.env['estate.property'].browse(val_property_id) | ||
if not property_id: | ||
raise exceptions.ValidationError( | ||
f'Property with id {property_id} does not exist.Please contact the support' | ||
) | ||
|
||
if property_id.best_offer and property_id.best_offer > val_curr_price: | ||
raise exceptions.UserError('The price of a new offer must be bigger than the best_price and' | ||
f' {property_id.best_offer} is bigger than {val_curr_price}') | ||
|
||
if property_id.status == 'new': | ||
property_id.status = 'offer received' | ||
|
||
return super(EstatePropertyOffer, self).create(val_list) | ||
|
||
@api.ondelete(at_uninstall=False) | ||
def _unlink_and_reset_state_if_no_offer_left(self): | ||
for offer in self: | ||
property_id = offer.property_id | ||
if len(property_id.estate_offer_ids) == 1 and property_id.status != 'cancelled': | ||
property_id.status = 'new' | ||
|
||
def _compute_inverse_deadline(self): | ||
for record in self: | ||
record.validity = (record.date_deadline - record.create_date).days | ||
|
||
def action_accepted(self): | ||
if self.status == 'accepted': | ||
return | ||
|
||
if any(offer.status == 'accepted' for offer in self.property_id.estate_offer_ids): | ||
raise exceptions.UserError('You can only accept one offer') | ||
|
||
self.status = 'accepted' | ||
self.property_id.selling_price = self.price | ||
self.property_id.partner_id = self.partner_id | ||
if self.status != 'sold': | ||
self.property_id.status = 'offer accepted' | ||
|
||
def action_refused(self): | ||
if self.status == 'refuse': | ||
return | ||
|
||
if self.status == 'accepted': | ||
self.property_id.selling_price = 0 | ||
self.property_id.partner_id = None | ||
|
||
self.status = 'refused' |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
from odoo import fields, models | ||
|
||
|
||
class EstatePropertyTag(models.Model): | ||
_name = 'estate.property.tag' | ||
_description = 'estate property tag module' | ||
_order = 'name' | ||
|
||
name = fields.Char(required=True) | ||
color = fields.Integer() | ||
|
||
_sql_constraints = [ | ||
('unique_tag_name', 'UNIQUE(name)', | ||
'The tag name must be unique'), | ||
] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
from odoo import api, fields, models | ||
|
||
|
||
class EstatePropertyType(models.Model): | ||
_name = 'estate.property.type' | ||
_description = 'estate property type module' | ||
_order = 'name' | ||
|
||
name = fields.Char(required=True) | ||
sequence = fields.Integer('Sequence', default=1, help="Used to order types. Lower is better.") | ||
property_ids = fields.One2many('estate.property', 'estate_type_id') | ||
offer_ids = fields.One2many('estate.property.offer', 'property_type_id') | ||
offer_count = fields.Integer(compute='_compute_offer_count') | ||
|
||
_sql_constraints = [ | ||
('unique_property_type', 'UNIQUE(name)', | ||
'The property type must be unique'), | ||
] | ||
|
||
@api.depends('offer_ids') | ||
def _compute_offer_count(self): | ||
for record in self: | ||
record.offer_count = len(record.offer_ids) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
from odoo import fields, models | ||
|
||
class AccountMoveLine(models.Model): | ||
_inherit = 'res.users' | ||
|
||
property_ids = fields.One2many('estate.property', 'user_id', domain=[('status', 'not in', ['sold', 'cancelled'])]) | ||
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
access_estate_property,access_estate_property,model_estate_property,base.group_user,1,1,1,1 | ||
access_estate_property_type,access_estate_property_type,model_estate_property_type,base.group_user,1,1,1,1 | ||
estate.access_estate_property_tag,access_estate_property_tag,estate.model_estate_property_tag,base.group_user,1,1,1,1 | ||
estate.access_estate_property_offer,access_estate_property_offer,estate.model_estate_property_offer,base.group_user,1,1,1,1 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
<odoo> | ||
<menuitem id="estate_menu_root" name="Estate"> | ||
<menuitem id="estate_first_level_menu" name="Advertissements"> | ||
<menuitem id="estate_model_menu_action" action="estate_model_action"/> | ||
</menuitem> | ||
<menuitem id="estate_settings_menu" name="Settings"> | ||
<menuitem id="estate_type_model_menu_action" action="estate_property_type_model_action"/> | ||
<menuitem id="estate_tag_model_menu_action" action="estate_property_tag_model_action"/> | ||
</menuitem> | ||
</menuitem> | ||
</odoo> |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Keep double quotes for private properties