-
Notifications
You must be signed in to change notification settings - Fork 3k
[ADD] estate:estate module creation and model implementation. #1194
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
152f232
2ea6100
e2c526f
16566bf
9b08197
3d53ec5
3b65a2b
360f0ab
2429659
9456a9a
176a677
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,21 @@ | ||
| { | ||
| 'name': "Estate", | ||
| 'version': '1.0', | ||
| 'depends': ['base'], | ||
| 'author': "habar", | ||
| 'category': 'Tutorials', | ||
| 'description': """ | ||
| This is the sample module for practise. | ||
| """, | ||
| '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', | ||
| ], | ||
| 'application': True, | ||
| 'license': 'LGPL-3', | ||
| 'installable': True, | ||
| } |
| 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,91 @@ | ||
| from dateutil.relativedelta import relativedelta | ||
|
|
||
| from odoo import api, fields, models | ||
| from odoo.exceptions import UserError | ||
|
|
||
|
|
||
| class EstateProperty(models.Model): | ||
| _name = 'estate.property' | ||
| _description = 'Real Estate Property' | ||
|
|
||
| name = fields.Char(string="Title", required=True) | ||
| description = fields.Text() | ||
| postcode = fields.Char() | ||
| date_availability = fields.Date(string="Available From", copy=False, default=lambda self: fields.Date.today() + relativedelta(months=3)) | ||
| expected_price = fields.Float(string="Expected Price", required=True) | ||
| selling_price = fields.Float(string="Selling Price", copy=False) | ||
| bedrooms = fields.Integer(default=2) | ||
| living_area = fields.Float(string="Living Area (spm)") | ||
| facades = fields.Integer() | ||
| garage = fields.Boolean() | ||
| garden = fields.Boolean() | ||
| garden_area = fields.Float(string="Garden Area (spm)") | ||
| total_area = fields.Float(string="Total Area (sqm)", compute="_computed_total_area") | ||
| garden_orientation = fields.Selection([ | ||
| ('north', "North"), | ||
| ('east', "East"), | ||
| ('west', "West"), | ||
| ('south', "South") | ||
| ]) | ||
| active = fields.Boolean(string="Active", default=True) | ||
| state = fields.Selection([ | ||
| ('new', "New"), | ||
| ('offer_received', "Offer Received"), | ||
| ('offer_accepted', "Offer Accepted"), | ||
| ('sold', "Sold"), | ||
| ('cancelled', "Cancelled") | ||
| ], copy=False, default='new') | ||
| property_type_id = fields.Many2one('estate.property.type', string="Property Type", ondelete="cascade") | ||
| sales_person_id = fields.Many2one('res.users', string='Salesman', ondelete='cascade') | ||
| buyer_id = fields.Many2one('res.partner', string='Buyer', ondelete='cascade') | ||
| property_tag_ids = fields.Many2many('estate.property.tag') | ||
| offer_ids = fields.One2many('estate.property.offer', 'property_id', string="Offers") | ||
| best_price = fields.Float(string="Best Offer", compute="_computed_best_offer", search="_search_best_offer", store=False) | ||
|
|
||
| @api.depends("living_area", "garden_area") | ||
| def _computed_total_area(self): | ||
| for rec in self: | ||
| rec.total_area = rec.living_area + rec.garden_area | ||
|
|
||
| @api.depends("offer_ids.price") | ||
| def _computed_best_offer(self): | ||
| prices = self.offer_ids.mapped("price") | ||
|
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. self may contain multiple properties. |
||
| self.best_price = max(prices) if prices else 0.0 | ||
|
|
||
| def _search_best_offer(self, operator, value): | ||
| return [ | ||
| '&', | ||
| ('offer_ids.price', '>', 10000), | ||
| ('offer_ids.price', operator, value) | ||
| ] | ||
|
Comment on lines
+55
to
+60
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. -> i think it does not search |
||
|
|
||
| @api.onchange("garden") | ||
| def _onchange_garden(self): | ||
| if self.garden: | ||
| self.garden_area = 10 | ||
| self.garden_orientation = 'north' | ||
|
|
||
| return { | ||
| 'warning': { | ||
| 'title': "Garden Enabled", | ||
| 'message': "Default area set to 10 and orientation north", | ||
| 'type': "notification" | ||
| } | ||
| } | ||
| else: | ||
| self.garden_area = 0 | ||
| self.garden_orientation = False | ||
|
|
||
| def set_property_cancelled(self): | ||
| for rec in self: | ||
| if rec.state == 'cancelled': | ||
| raise UserError("Sold property can not be cancelled.") | ||
|
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. Condition is checking state == cancelled, but the error message is Also, can you make this error message translatable? |
||
| rec.state = 'cancelled' | ||
| return True | ||
|
|
||
| def set_property_sold(self): | ||
| for rec in self: | ||
| if rec.state == 'cancelled': | ||
| raise UserError("Cancelled properties can not be sold.") | ||
|
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. Can you make this error message translatable? |
||
| rec.state = 'sold' | ||
| return True | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| from dateutil.relativedelta import relativedelta | ||
|
|
||
| from odoo import api, fields, models | ||
| from odoo.exceptions import UserError | ||
|
|
||
|
|
||
| class EstatePropertyOffer(models.Model): | ||
| _name = "estate.property.offer" | ||
| _description = "Real Estate Offer" | ||
|
|
||
| price = fields.Float(string='Price') | ||
| 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) | ||
| validity = fields.Integer(default=7) | ||
| date_deadline = fields.Date(string="Deadline", compute="_compute_deadline_method", inverse="_inverse_deadline_method") | ||
|
|
||
| @api.depends("create_date", "validity") | ||
| def _compute_deadline_method(self): | ||
| for rec in self: | ||
| create_date = rec.create_date.date() if rec.create_date else fields.Date.today() | ||
| rec.date_deadline = create_date + relativedelta(days=rec.validity) | ||
|
|
||
| def _inverse_deadline_method(self): | ||
| for rec in self: | ||
| create_date = rec.create_date.date() if rec.create_date else fields.Date.today() | ||
| rec.validity = relativedelta(rec.date_deadline, create_date).days | ||
|
|
||
| def action_accept_offer(self): | ||
| for rec in self: | ||
| if any(i.status == "accepted" for i in rec.property_id.offer_ids): | ||
| raise UserError("Offer already accepted for given property.") | ||
|
|
||
| rec.status = "accepted" | ||
| rec.property_id.buyer_id = rec.partner_id.id | ||
| rec.property_id.selling_price = rec.price | ||
| rec.property_id.state = "offer_accepted" | ||
| return True | ||
|
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. It returns inside the loop. The issue is the method return on the first record. |
||
|
|
||
| def action_refuse_offer(self): | ||
| for rec in self: | ||
| rec.status = "refused" | ||
| return True | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| from odoo import fields, models | ||
|
|
||
|
|
||
| class EstatePropertyTag(models.Model): | ||
| _name = 'estate.property.tag' | ||
| _description = 'Real Estate Tag' | ||
|
|
||
| name = fields.Char(string='Tag', 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): | ||
| _name = "estate.property.type" | ||
| _description = "Estate property type" | ||
|
|
||
| name = fields.Char(string="Type", 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,12 @@ | ||
| <?xml version="1.0"?> | ||
| <odoo> | ||
| <menuitem id="estate_property_menu_root" name="Real Estate"> | ||
| <menuitem id="estate_property_menu_advertisment" name="Advertisments"> | ||
| <menuitem id="estate_property_menu_action" action="estate_property_action" name="Properties"/> | ||
| </menuitem> | ||
| <menuitem id="estate_property_type_menu" name="Settings"> | ||
| <menuitem id="estate_property_type_menu_action" action="estate_property_type_action" name="Property Types"/> | ||
| <menuitem id="estate_property_tag_menu_action" action="estate_property_tag_action" name="Property Tags"/> | ||
| </menuitem> | ||
| </menuitem> | ||
| </odoo> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| <?xml version="1.0"?> | ||
| <odoo> | ||
| <record id="estate_property_offer_form_view" 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> | ||
| <field name='price'/> | ||
| <field name='partner_id'/> | ||
| <field name='validity' string="Validity (days)"/> | ||
| <field name='date_deadline'/> | ||
| <field name='status'/> | ||
| </form> | ||
| </field> | ||
| </record> | ||
|
|
||
| <record id="estate_property_offer_list_view" 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"> | ||
| <field name='price'/> | ||
| <field name='partner_id'/> | ||
| <field name='validity' string="Validity (days)"/> | ||
| <field name='date_deadline'/> | ||
| <button name="action_accept_offer" type="object" icon="fa-check" title="Accept"/> | ||
| <button name="action_refuse_offer" type="object" icon="fa-times" title="Refuse"/> | ||
| <field name='status'/> | ||
| </list> | ||
| </field> | ||
| </record> | ||
| </odoo> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| <?xml version="1.0"?> | ||
| <odoo> | ||
| <record id="estate_property_tag_action" model="ir.actions.act_window"> | ||
| <field name="name">Property Tags</field> | ||
| <field name="res_model">estate.property.tag</field> | ||
| <field name="view_mode">list,form</field> | ||
| </record> | ||
| </odoo> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| <?xml version="1.0"?> | ||
| <odoo> | ||
| <record id="estate_property_type_action" model="ir.actions.act_window"> | ||
| <field name="name">Property Type</field> | ||
| <field name="res_model">estate.property.type</field> | ||
| <field name="view_mode">list,form</field> | ||
| </record> | ||
| </odoo> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,100 @@ | ||
| <?xml version="1.0"?> | ||
| <odoo> | ||
| <record id="estate_property_form" model="ir.ui.view"> | ||
| <field name="name">estate.property.form</field> | ||
| <field name="model">estate.property</field> | ||
| <field name="arch" type="xml"> | ||
| <form string="Properties"> | ||
| <header> | ||
| <button name="set_property_sold" type="object">Sold</button> | ||
| <button name="set_property_cancelled" type="object">Cancel</button> | ||
| </header> | ||
| <sheet> | ||
| <h1><field name="name"/></h1> | ||
| <h4><field name='property_tag_ids' widget='many2many_tags'/></h4> | ||
| <group> | ||
| <group> | ||
| <field name="state" string="Status"/> | ||
| <field name='property_type_id'/> | ||
| <field name="postcode"/> | ||
| <field name="date_availability"/> | ||
| </group> | ||
| <group> | ||
| <field name="expected_price"/> | ||
| <field name="best_price"/> | ||
| <field name="selling_price"/> | ||
| </group> | ||
| </group> | ||
| <notebook> | ||
| <page string="Description"> | ||
| <group> | ||
| <field name="description"/> | ||
| <field name="bedrooms"/> | ||
| <field name="living_area"/> | ||
| <field name="facades"/> | ||
| <field name="garage"/> | ||
| <field name="garden"/> | ||
| <field name="garden_area"/> | ||
| <field name="garden_orientation"/> | ||
| <field name="total_area"/> | ||
| <field name="state"/> | ||
| </group> | ||
| </page> | ||
| <page string="Offers"> | ||
| <field name="offer_ids"/> | ||
| </page> | ||
| <page string="Other Info"> | ||
| <group> | ||
| <field name="sales_person_id"/> | ||
| <field name="buyer_id"/> | ||
| </group> | ||
| </page> | ||
| </notebook> | ||
| </sheet> | ||
| </form> | ||
| </field> | ||
| </record> | ||
|
|
||
| <record id="estate_property_list" model="ir.ui.view"> | ||
| <field name="name">estate.property.list</field> | ||
| <field name="model">estate.property</field> | ||
| <field name="arch" type="xml"> | ||
| <list string="Properties" limit="20"> | ||
|
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 is the purpose of adding
Author
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. By default, Odoo typically displays 80 records per page. Setting limit="20" showing only 20 records at a time |
||
| <field name="name"/> | ||
| <field name="postcode"/> | ||
| <field name="bedrooms"/> | ||
| <field name="living_area"/> | ||
| <field name="expected_price"/> | ||
| <field name="selling_price"/> | ||
| <field name="date_availability"/> | ||
| <field name='best_price'/> | ||
| <field name='total_area'/> | ||
| </list> | ||
| </field> | ||
| </record> | ||
|
|
||
| <record id="estate_property_search" model="ir.ui.view"> | ||
| <field name="name">estate.property.search</field> | ||
| <field name="model">estate.property</field> | ||
| <field name="arch" type="xml"> | ||
| <search string="Search Properties"> | ||
| <field name='name'/> | ||
| <field name='bedrooms'/> | ||
| <field name='facades'/> | ||
| <field name='postcode'/> | ||
| <field name='expected_price'/> | ||
| <field name='date_availability'/> | ||
| <field name='total_area'/> | ||
| <field name='best_price'/> | ||
| <filter string="Available" name="available" domain="[('state','in',['new','offer_received'])]"/> | ||
| <filter string="Postcode" name="group_by_postcode" context="{'group_by':'postcode'}"/> | ||
| </search> | ||
| </field> | ||
| </record> | ||
|
|
||
| <record id="estate_property_action" model="ir.actions.act_window"> | ||
| <field name="name">Properties</field> | ||
| <field name="res_model">estate.property</field> | ||
| <field name="view_mode">list,form</field> | ||
| </record> | ||
| </odoo> | ||
Uh oh!
There was an error while loading. Please reload this page.