-
Notifications
You must be signed in to change notification settings - Fork 3.2k
[ADD] Estate: Add real estate module #1331
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
base: 19.0
Are you sure you want to change the base?
Changes from all commits
bd57451
97ddcbf
07925fe
7a49261
12071e7
4f67725
54c1253
6f3701f
e7a4d1b
3b1b076
0aed7c4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| from . import models |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| { | ||
| 'name': 'Real Estate', | ||
| 'depends': ['base'], | ||
| 'application': True, | ||
| '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_property_offer_views.xml', | ||
| 'views/estate_menus.xml', | ||
| 'data/data_type.xml', | ||
| 'data/data_tags.xml', | ||
| 'data/data.xml', | ||
| ], | ||
| } |
| 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_list_view_demo_1" model="estate.property"> | ||
| <field name="name">ABC Ltd</field> | ||
| <field name="property_type_id" ref="estate_property_type_1"/> | ||
| <field name="tag_ids" eval="[(6, 0, [ref('estate_property_tag_1')])]"/> | ||
| <field name="postcode">123345</field> | ||
| <field name="bedrooms">2</field> | ||
| <field name="living_area">135765</field> | ||
| <field name="expected_price">5820000</field> | ||
| <field name="selling_price">0</field> | ||
| <field name="date_availability" eval="(datetime.today() + relativedelta(months=3)).strftime('%Y-%m-%d')"/> | ||
| </record> | ||
|
|
||
| <record id="estate_property_list_view_demo_2" model="estate.property"> | ||
| <field name="name">XYZ Pvt Ltd</field> | ||
| <field name="property_type_id" ref="estate_property_type_2"/> | ||
| <field name="tag_ids" eval="[(6, 0, [ref('estate_property_tag_1')])]"/> | ||
| <field name="postcode">7865445</field> | ||
| <field name="bedrooms">4</field> | ||
| <field name="living_area">100000</field> | ||
| <field name="expected_price">200000</field> | ||
| <field name="selling_price">0</field> | ||
| <field name="date_availability" eval="(datetime.today() + relativedelta(months=3)).strftime('%Y-%m-%d')"/> | ||
| </record> | ||
|
|
||
| <record id="estate_property_list_view_demo_3" model="estate.property"> | ||
| <field name="name">Umbrella Corp</field> | ||
| <field name="property_type_id" ref="estate_property_type_4"/> | ||
| <field name="tag_ids" eval="[(6, 0, [ref('estate_property_tag_3')])]"/> | ||
| <field name="postcode">908765</field> | ||
| <field name="bedrooms">3</field> | ||
| <field name="living_area">100000</field> | ||
| <field name="expected_price">700000</field> | ||
| <field name="selling_price">0</field> | ||
| <field name="date_availability" eval="(datetime.today() + relativedelta(months=3)).strftime('%Y-%m-%d')"/> | ||
| </record> | ||
|
|
||
| </odoo> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| <?xml version="1.0" encoding="utf-8"?> | ||
| <odoo> | ||
| <record id="estate_property_tag_1" model="estate.property.tag"> | ||
| <field name="name">Cozy</field> | ||
| </record> | ||
| <record id="estate_property_tag_2" model="estate.property.tag"> | ||
| <field name="name">Furnished</field> | ||
| </record> | ||
| <record id="estate_property_tag_3" model="estate.property.tag"> | ||
| <field name="name">Renovated</field> | ||
| </record> | ||
| <record id="estate_property_tag_4" model="estate.property.tag"> | ||
| <field name="name">Unfurnished</field> | ||
| </record> | ||
| <record id="estate_property_tag_5" model="estate.property.tag"> | ||
| <field name="name">Semi-furnished</field> | ||
| </record> | ||
| </odoo> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| <?xml version="1.0" encoding="utf-8"?> | ||
| <odoo> | ||
| <record id="estate_property_type_1" model="estate.property.type"> | ||
| <field name="name">House</field> | ||
| </record> | ||
| <record id="estate_property_type_2" model="estate.property.type"> | ||
| <field name="name">Apartment</field> | ||
| </record> | ||
| <record id="estate_property_type_3" model="estate.property.type"> | ||
| <field name="name">Penthouse</field> | ||
| </record> | ||
| <record id="estate_property_type_4" model="estate.property.type"> | ||
| <field name="name">Villa</field> | ||
| </record> | ||
| <record id="estate_property_type_5" model="estate.property.type"> | ||
| <field name="name">Boat House</field> | ||
| </record> | ||
| <record id="estate_property_type_6" model="estate.property.type"> | ||
| <field name="name">Mansion</field> | ||
| </record> | ||
| </odoo> |
| 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 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,129 @@ | ||
| from datetime import timedelta | ||
|
|
||
| from odoo import api, fields, models | ||
| from odoo.exceptions import UserError | ||
|
|
||
|
|
||
| class EstateProperty(models.Model): | ||
| _name = "estate.property" | ||
| _description = "Real Estate Property" | ||
|
|
||
| def _default_availability_date(self): | ||
| return fields.Date.today() + timedelta(days=90) | ||
|
|
||
| name = fields.Char(required=True) | ||
| description = fields.Text() | ||
| postcode = fields.Char() | ||
| property_type_id = fields.Many2one( | ||
| "estate.property.type", | ||
| string="Property Type", | ||
| ) | ||
| tag_ids = fields.Many2many( | ||
| "estate.property.tag", | ||
| string="Tags", | ||
| ) | ||
| buyer_id = fields.Many2one( | ||
| "res.partner", | ||
| string="Buyer", | ||
| copy=False, | ||
| ) | ||
| salesperson_id = fields.Many2one( | ||
| "res.users", | ||
| string="Salesperson", | ||
| default=lambda self: self.env.user, | ||
| ) | ||
| offer_ids = fields.One2many( | ||
| "estate.property.offer", | ||
| "property_id", | ||
| string="Offers", | ||
| ) | ||
| date_availability = fields.Date( | ||
| copy=False, | ||
| default=lambda self: fields.Datetime.now() + timedelta(days=90), | ||
| ) | ||
| expected_price = fields.Float(required=True) | ||
| selling_price = fields.Float( | ||
| readonly=True, | ||
| copy=False, | ||
| ) | ||
| bedrooms = fields.Integer( | ||
| default=2, | ||
| ) | ||
| facades = fields.Integer() | ||
| garage = fields.Boolean() | ||
| living_area = fields.Integer() | ||
| garden = fields.Boolean() | ||
| garden_area = fields.Integer() | ||
| total_area = fields.Integer( | ||
| compute="_compute_total_area", | ||
| ) | ||
| best_price = fields.Float( | ||
| compute="_compute_best_price", | ||
| ) | ||
| garden_orientation = fields.Selection([ | ||
| ('north', 'North'), | ||
| ('south', 'South'), | ||
| ('east', 'East'), | ||
| ('west', 'West'), | ||
| ]) | ||
| active = fields.Boolean( | ||
| default=True, | ||
| ) | ||
| state = fields.Selection( | ||
| [ | ||
| ('new', 'New'), | ||
| ('offer_received', 'Offer Received'), | ||
| ('offer_accepted', 'Offer Accepted'), | ||
| ('sold', 'Sold'), | ||
| ('cancelled', 'Cancelled'), | ||
| ], | ||
| required=True, | ||
| copy=False, | ||
| default='new', | ||
| readonly=True, | ||
| ) | ||
|
|
||
| @api.depends("living_area", "garden_area") | ||
| def _compute_total_area(self): | ||
| for property in self: | ||
| property.total_area = ( | ||
| property.living_area + | ||
| property.garden_area | ||
| ) | ||
|
|
||
| @api.depends("offer_ids.price") | ||
| def _compute_best_price(self): | ||
| for property in self: | ||
| prices = property.offer_ids.mapped("price") | ||
| property.best_price = max(prices) if prices else 0.0 | ||
|
|
||
| @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 | ||
|
|
||
| # Mark the property as sold | ||
| def action_sold(self): | ||
| for property in self: | ||
| # A cancelled property cannot be sold | ||
| if property.state == "cancelled": | ||
| raise UserError( | ||
| "Cancelled properties cannot be sold." | ||
| ) | ||
| property.state = "sold" | ||
| return True | ||
|
|
||
| # Mark the property as cancelled | ||
| def action_cancel(self): | ||
| for property in self: | ||
| # A sold property cannot be cancelled | ||
| if property.state == "sold": | ||
| raise UserError( | ||
| "Sold properties cannot be cancelled." | ||
| ) | ||
| property.state = "cancelled" | ||
| return True |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,93 @@ | ||
| from datetime import timedelta | ||
|
|
||
| from odoo import api, fields, models | ||
| from odoo.exceptions import UserError | ||
|
|
||
|
|
||
| class EstatePropertyOffer(models.Model): | ||
| _name = "estate.property.offer" | ||
| _description = "Real Estate Property Offer" | ||
|
|
||
| price = fields.Float() | ||
| validity = fields.Integer( | ||
| default=7, | ||
| ) | ||
| date_deadline = fields.Date( | ||
| compute="_compute_date_deadline", | ||
| inverse="_inverse_date_deadline", | ||
| ) | ||
| status = fields.Selection( | ||
| [ | ||
| ("accepted", "Accepted"), | ||
| ("refused", "Refused"), | ||
| ], | ||
| copy=False, | ||
| ) | ||
| partner_id = fields.Many2one( | ||
| "res.partner", | ||
| required=True, | ||
| ) | ||
| property_id = fields.Many2one( | ||
| "estate.property", | ||
| required=True, | ||
| ) | ||
|
|
||
| @api.depends("create_date", "validity") | ||
| def _compute_date_deadline(self): | ||
| for offer in self: | ||
| create_date = ( | ||
| offer.create_date.date() | ||
| if offer.create_date | ||
| else fields.Date.today() | ||
| ) | ||
| offer.date_deadline = ( | ||
| create_date + | ||
| timedelta(days=offer.validity) | ||
| ) | ||
|
|
||
| def _inverse_date_deadline(self): | ||
| for offer in self: | ||
| create_date = ( | ||
| offer.create_date.date() | ||
| if offer.create_date | ||
| else fields.Date.today() | ||
| ) | ||
| if offer.date_deadline: | ||
| offer.validity = ( | ||
| offer.date_deadline - | ||
| create_date | ||
| ).days | ||
|
|
||
| # Accept the offer | ||
| def action_accept(self): | ||
| for offer in self: | ||
| # Check if another offer for this property has already been accpted | ||
| if offer.property_id.offer_ids.filtered( | ||
| lambda o: o.status == "accepted" and o.id != offer.id | ||
| ): | ||
| raise UserError( | ||
| "Another offer for this property has already been accepted." | ||
| ) | ||
| # Set the status to "accepted" | ||
| offer.status = "accepted" | ||
| # Updating the related property fields | ||
| offer.property_id.write({ | ||
| "buyer_id": offer.partner_id.id, | ||
| "selling_price": offer.price, | ||
| "state": "offer_accepted", | ||
| }) | ||
| return True | ||
|
|
||
| # Refuse the offer and set the status to "refused" | ||
| def action_refuse(self): | ||
| for offer in self: | ||
| offer.status = "refused" | ||
| return True | ||
|
|
||
| @api.model_create_multi | ||
| def create(self, vals_list): | ||
| offers = super().create(vals_list) | ||
| for offer in offers: | ||
| if offer.property_id.state == "new": | ||
| offer.property_id.state = "offer_received" | ||
| return offers | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| from odoo import fields, models | ||
|
|
||
|
|
||
| class EstatePropertyTag(models.Model): | ||
|
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. should be 2 blank lines before it. |
||
| _name = "estate.property.tag" | ||
| _description = "Real Estate Property Tag" | ||
|
|
||
| name = fields.Char(required=True) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| from odoo import fields, models | ||
|
|
||
|
|
||
| class EstatePropertyType(models.Model): | ||
|
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. should be 2 blank lines before it. |
||
| _name = "estate.property.type" | ||
| _description = "Real Estate Property Type" | ||
|
|
||
| name = fields.Char(required=True) | ||
| 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 | ||
| access_estate_property_tag,access_estate_property_tag,model_estate_property_tag,base.group_user,1,1,1,1 | ||
| access_estate_property_offer,access_estate_property_offer,model_estate_property_offer,base.group_user,1,1,1,1 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| <?xml version="1.0" encoding="utf-8"?> | ||
| <odoo> | ||
|
|
||
| <menuitem | ||
| id="estate_menu_root" | ||
| name="Real Estate" | ||
| /> | ||
|
|
||
|
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. unneccary diff. |
||
| <menuitem | ||
| id="estate_advertisements_menu" | ||
| name="Advertisements" | ||
| parent="estate_menu_root" | ||
| /> | ||
|
|
||
|
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. unneccary diff. |
||
| <menuitem | ||
| id="estate_settings_menu" | ||
| name="Settings" | ||
| parent="estate_menu_root" | ||
| /> | ||
|
|
||
| <menuitem | ||
| id="estate_property_type_menu_action" | ||
| name="Property Types" | ||
| parent="estate_settings_menu" | ||
| action="estate_property_type_action" | ||
| /> | ||
|
|
||
| <menuitem | ||
| id="estate_property_tag_menu_action" | ||
| name="Property Tags" | ||
| parent="estate_settings_menu" | ||
| action="estate_property_tag_action" | ||
| /> | ||
|
|
||
| <menuitem | ||
| id="estate_property_menu_action" | ||
| parent="estate_advertisements_menu" | ||
| action="estate_property_action" | ||
| /> | ||
|
|
||
| </odoo> | ||
|
|
||
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.
should be 2 blank lines before it.