-
Notifications
You must be signed in to change notification settings - Fork 2.8k
[ADD] estate: adding new estate module #727
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
Closed
Closed
Changes from 10 commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
0b23021
[ADD] estate: adding new estate module
amry-odoo d4fd942
[ADD] estate: Added first estate module
amry-odoo ff2ed32
[IMP] estate: Added different views
amry-odoo 74d2b8e
[IMP] estate: Property Types, tags, offers added
amry-odoo 43a27fe
[IMP] estate: Added computed fields
amry-odoo 3a9a5a2
[IMP] estate: Added actions, some refactoring
amry-odoo 2390818
[IMP] estate: Added constraints
amry-odoo 6316118
[ADD] estate: added views, action buttons and widgets
amry-odoo 6f9cc29
[ADD] estate: Added an inherited module
amry-odoo 3c56016
[ADD] estate: Created Link module between account and estate
amry-odoo 203b4b9
[IMP] estate: Added Kanban View
amry-odoo bbd1d95
[REF] estate: Code refactored following last comments
amry-odoo 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,3 @@ | ||
| from . import models | ||
| from . import security | ||
| from . import views |
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,17 @@ | ||
| { | ||
| "name": "estate", | ||
| "license": "LGPL-3", | ||
| "application": True, | ||
| "depends": [ | ||
| "base", | ||
| ], | ||
| "data": [ | ||
| "views/estate_offer_views.xml", | ||
| "views/estate_property_type_views.xml", | ||
| "views/estate_property_tags_views.xml", | ||
| "views/estate_property_views.xml", | ||
| "views/res_users_views.xml", | ||
| "views/estate_menus.xml", | ||
| "security/ir.model.access.csv", | ||
| ], | ||
| } |
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_type | ||
| from . import estate_property_tag | ||
| from . import estate_property_offer | ||
| from . import res_users |
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,137 @@ | ||
| from datetime import date | ||
|
|
||
| from odoo import fields, models, api | ||
| from odoo.tools import date_utils, float_utils | ||
| from odoo.exceptions import UserError, ValidationError | ||
|
|
||
|
|
||
| class EstateProperty(models.Model): | ||
| _name = "estate.property" | ||
| _description = "A property module that adds the property as a listing" | ||
| _sql_constraints = [ | ||
| ( | ||
| "check_expected_price", | ||
| "CHECK(expected_price > 0)", | ||
| "Expected price of a property should be only positive", | ||
| ), | ||
| ( | ||
| "check_selling_price", | ||
| "CHECK(selling_price >= 0)", | ||
| "Selling price of a property should be positive", | ||
| ), | ||
| ] | ||
| _order = "id desc" | ||
|
|
||
| name = fields.Char(required=True) | ||
| description = fields.Text() | ||
| postcode = fields.Char() | ||
| date_availability = fields.Date( | ||
| copy=False, default=lambda _: date_utils.add(date.today(), months=3) | ||
| ) | ||
| expected_price = fields.Float(required=True) | ||
| selling_price = fields.Float(readonly=True, copy=False) | ||
| bedrooms = fields.Integer(default=2) | ||
| living_area = fields.Integer() | ||
| facades = fields.Integer() | ||
| garage = fields.Boolean() | ||
| garden = fields.Boolean() | ||
| garden_area = fields.Integer() | ||
| garden_orientation = fields.Selection( | ||
| string="Orientation", | ||
| selection=[ | ||
| ("north", "North"), | ||
| ("south", "South"), | ||
| ("east", "East"), | ||
| ("west", "West"), | ||
| ], | ||
| ) | ||
| total_area = fields.Float(compute="_compute_total_area") | ||
| active = fields.Boolean(default=True) | ||
| state = fields.Selection( | ||
| string="State", | ||
| selection=[ | ||
| ("new", "New"), | ||
| ("offer-received", "Offer Received"), | ||
| ("offer-accepted", "Offer Accepted"), | ||
| ("sold", "Sold"), | ||
| ("cancelled", "Cancelled"), | ||
| ], | ||
| required=True, | ||
| copy=False, | ||
| default="new", | ||
| ) | ||
| property_type_id = fields.Many2one("estate.property.type") | ||
| buyer_id = fields.Many2one("res.partner", copy=False) | ||
| seller_id = fields.Many2one( | ||
| "res.users", name="Salesperson", default=lambda self: self.env.user | ||
| ) | ||
| tag_ids = fields.Many2many("estate.property.tag", string="Tags") | ||
| offers_ids = fields.One2many( | ||
| "estate.property.offer", "property_id", string="Offers" | ||
| ) | ||
| best_price = fields.Float( | ||
| compute="_compute_best_price", readonly=True, string="Best Offer" | ||
| ) | ||
|
|
||
| @api.depends("living_area", "garden_area") | ||
| def _compute_total_area(self): | ||
| for single_property in self: | ||
| single_property.total_area = ( | ||
| single_property.living_area + single_property.garden_area | ||
| ) | ||
|
|
||
| @api.depends("offers_ids.price") | ||
| def _compute_best_price(self): | ||
| for single_property in self: | ||
| if single_property.offers_ids: | ||
| single_property.best_price = max( | ||
| single_property.offers_ids.mapped("price") | ||
| ) | ||
| else: | ||
| single_property.best_price = 0 | ||
amry-odoo marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| @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 = None | ||
|
|
||
| def action_property_cancel(self): | ||
| for single_property in self: | ||
| if single_property.state == "sold": | ||
| raise UserError("Sold properties cannot be cancelled!") | ||
amry-odoo marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| single_property.state = "cancelled" | ||
| return True | ||
|
|
||
| def action_property_sold(self): | ||
| for single_property in self: | ||
| if single_property.state == "cancelled": | ||
| raise UserError("Cancelled properties cannot be sold!") | ||
| single_property.state = "sold" | ||
| return True | ||
|
|
||
| @api.constrains("selling_price", "expected_price") | ||
| def check_selling_price_in_range(self): | ||
| for single_property in self: | ||
| if not float_utils.float_is_zero( | ||
| single_property.selling_price, precision_rounding=0.1 | ||
| ): | ||
| if single_property.selling_price < ( | ||
| 0.9 * single_property.expected_price | ||
| ): | ||
| raise ValidationError( | ||
| "Selling price cannot be lower than 90%% of Expected price" | ||
| ) | ||
amry-odoo marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| return True | ||
|
|
||
| @api.ondelete(at_uninstall=False) | ||
| def _unlink_check_property_state(self): | ||
| for single_property in self: | ||
| if single_property.state not in ["new", "cancelled"]: | ||
| raise UserError( | ||
| "Property cannot be deleted unless it is new or cancelled" | ||
| ) | ||
| return True | ||
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,89 @@ | ||
| from datetime import date | ||
|
|
||
| from odoo import fields, models, api | ||
| from odoo.tools import date_utils | ||
| from odoo.exceptions import UserError | ||
|
|
||
|
|
||
| class EstatePropertyOffer(models.Model): | ||
| _name = "estate.property.offer" | ||
| _description = "Offers made on a listing" | ||
| _sql_constraints = [ | ||
| ( | ||
| "check_price", | ||
| "CHECK(price > 0)", | ||
| "Price of an offer should be only positive", | ||
| ), | ||
| ] | ||
| _order = "price desc" | ||
|
|
||
| price = fields.Float() | ||
| status = fields.Selection( | ||
| string="Status", | ||
| selection=[ | ||
| ("accepted", "Accepted"), | ||
| ("refused", "Refused"), | ||
| ], | ||
| copy=False, | ||
| ) | ||
| partner_id = fields.Many2one("res.partner", string="Buyer", required=True) | ||
| property_id = fields.Many2one("estate.property", string="Property", required=True) | ||
| property_type_id = fields.Many2one( | ||
| related="property_id.property_type_id", store=True | ||
| ) | ||
| validity = fields.Integer(default=7, string="Validity (days)") | ||
| date_deadline = fields.Date( | ||
| compute="_compute_date_deadline", | ||
| inverse="_inverse_date_deadline", | ||
| string="Deadline", | ||
| ) | ||
|
|
||
| @api.depends("validity") | ||
| def _compute_date_deadline(self): | ||
| for offer in self: | ||
| create_date_actual = ( | ||
| date.today() if not offer.create_date else offer.create_date.date() | ||
| ) | ||
| offer.date_deadline = date_utils.add( | ||
| create_date_actual, days=offer.validity | ||
| ) | ||
|
|
||
| def _inverse_date_deadline(self): | ||
| for offer in self: | ||
| create_date_actual = ( | ||
| date.today() if not offer.create_date else offer.create_date.date() | ||
| ) | ||
| offer.validity = (offer.date_deadline - create_date_actual).days | ||
|
|
||
| def action_offer_accept(self): | ||
| for offer in self: | ||
| offer.status = "accepted" | ||
| offer._action_check_offers() | ||
amry-odoo marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| return True | ||
|
|
||
| @api.depends("status") | ||
| def _action_check_offers(self): | ||
amry-odoo marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| if self.status == "accepted": | ||
amry-odoo marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| if self.property_id.state in ("offer-accepted", "sold"): | ||
| self.status = False | ||
| raise UserError("An offer has already been accepted!") | ||
| else: | ||
| self.property_id.state = "offer-accepted" | ||
| self.property_id.selling_price = self.price | ||
| self.property_id.buyer_id = self.partner_id | ||
amry-odoo marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| return True | ||
|
|
||
| @api.depends("status") | ||
| def action_offer_refuse(self): | ||
| for offer in self: | ||
| offer.status = "refused" | ||
amry-odoo marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| return True | ||
|
|
||
| @api.model_create_multi | ||
| def create(self, vals): | ||
| for val in vals: | ||
amry-odoo marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| single_property = self.env["estate.property"].browse(val["property_id"]) | ||
| if val["price"] < single_property.best_price: | ||
|
||
| raise UserError("An offer cannot be lower than an existing offer") | ||
| single_property.state = "offer-received" | ||
| return super().create(vals) | ||
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,17 @@ | ||
| from odoo import fields, models | ||
|
|
||
|
|
||
| class EstatePropertyTag(models.Model): | ||
| _name = "estate.property.tag" | ||
| _description = "Adds different property tags" | ||
| _sql_constraints = [ | ||
| ( | ||
| "property_tag_unique", | ||
| "UNIQUE (name)", | ||
| "Property Tag already exists.", | ||
| ), | ||
| ] | ||
| _order = "name" | ||
|
|
||
| name = fields.Char(required=True) | ||
| color = fields.Integer() |
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,33 @@ | ||
| from odoo import fields, models, api | ||
|
|
||
|
|
||
| class EstatePropertyType(models.Model): | ||
| _name = "estate.property.type" | ||
| _description = "Adds different property types" | ||
| _sql_constraints = [ | ||
| ( | ||
| "property_type_unique", | ||
| "UNIQUE (name)", | ||
| "Property Type already exists.", | ||
| ), | ||
| ] | ||
| _order = "sequence, name" | ||
|
|
||
| name = fields.Char(required=True) | ||
| property_ids = fields.One2many( | ||
| "estate.property", "property_type_id", string="Properties" | ||
| ) | ||
| sequence = fields.Integer( | ||
| "Sequence", default=1, help="Used to order stages. Lower is better." | ||
| ) | ||
| offer_ids = fields.One2many( | ||
| "estate.property.offer", "property_type_id", string="Offers" | ||
| ) | ||
| offer_count = fields.Integer( | ||
| compute="_compute_offer_count", string="Number of Offers" | ||
| ) | ||
|
|
||
| @api.depends("offer_ids") | ||
| def _compute_offer_count(self): | ||
| for property_type in self: | ||
| property_type.offer_count = len(property_type.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,12 @@ | ||
| from odoo import fields, models | ||
|
|
||
|
|
||
| class ResUsers(models.Model): | ||
| _inherit = "res.users" | ||
|
|
||
| property_ids = fields.One2many( | ||
| "estate.property", | ||
| "seller_id", | ||
| string="Properties", | ||
| domain=[("state", "in", ("new", "offer-received"))], | ||
| ) |
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 | ||
| estate.access_estate_property,access_estate_property,estate.model_estate_property,base.group_user,1,1,1,1 | ||
| estate.access_estate_property_type,access_estate_property_type,estate.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,12 @@ | ||
| <?xml version="1.0" encoding="UTF-8"?> | ||
| <odoo> | ||
| <menuitem id="estate_menu_root" name="Real Estate"> | ||
| <menuitem id="estate_ads_menu" name="Advertisements"> | ||
| <menuitem id="estate_property_menu_action" action="estate_property_action"/> | ||
| </menuitem> | ||
| <menuitem id="estate_settings_menu" name="Settings"> | ||
| <menuitem id="estate_property_type_menu_action" action="estate_property_type_action"/> | ||
| <menuitem id="estate_property_tag_menu_action" action="estate_property_tag_action"/> | ||
| </menuitem> | ||
| </menuitem> | ||
| </odoo> |
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,40 @@ | ||
| <?xml version="1.0" encoding="UTF-8"?> | ||
| <odoo> | ||
| <record id="estate_property_offer_view_form" model="ir.ui.view"> | ||
| <field name="name">estate.property.offer.form</field> | ||
| <field name="model">estate.property.offer</field> | ||
| <field name="arch" type="xml"> | ||
| <form string="Type"> | ||
| <group> | ||
| <field name="price"/> | ||
| <field name="partner_id" string="Partner"/> | ||
| <field name="validity"/> | ||
| <field name="date_deadline"/> | ||
| </group> | ||
| </form> | ||
| </field> | ||
| </record> | ||
|
|
||
| <record id="estate_property_offer_view_list" model="ir.ui.view"> | ||
| <field name="name">estate.property.offer.list</field> | ||
| <field name="model">estate.property.offer</field> | ||
| <field name="arch" type="xml"> | ||
| <list string="Offers" editable="top" decoration-success="status == 'accepted'" decoration-danger="status == 'refused'"> | ||
| <field name="price"/> | ||
| <field name="partner_id" string="Partner"/> | ||
| <field name="validity"/> | ||
| <field name="date_deadline"/> | ||
| <button name="action_offer_accept" type="object" icon="fa-check" title="accept" invisible="status in ['accepted', 'refused']"/> | ||
| <button name="action_offer_refuse" type="object" icon="fa-times" title="refuse" invisible="status in ['accepted', 'refused']"/> | ||
| <field name="status" optional="hidden" invisible="1"/> | ||
| </list> | ||
| </field> | ||
| </record> | ||
|
|
||
| <record id="estate_property_offer_action" model="ir.actions.act_window"> | ||
| <field name="name">Offers</field> | ||
| <field name="res_model">estate.property.offer</field> | ||
| <field name="view_mode">list,form</field> | ||
| <field name="domain">[('property_type_id', '=', active_id)]</field> | ||
| </record> | ||
| </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.
Uh oh!
There was an error while loading. Please reload this page.