-
Notifications
You must be signed in to change notification settings - Fork 2.7k
[ADD] estate: init #1031
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?
[ADD] estate: init #1031
Changes from all commits
3458fe5
d929277
6b5c153
d851030
2b74661
f8f582c
9803b98
4bd2716
98dc4fc
b5aaaa7
9b190ce
5d4a3ff
9637ec0
1d3d53e
dd14df9
3f82fdb
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,30 @@ | ||
| # Part of Odoo. See LICENSE file for full copyright and licensing details. | ||
|
|
||
| { | ||
| 'name': 'Estate', | ||
| 'version': '1.0', | ||
| 'category': 'Sales/Estate', | ||
| 'sequence': 15, | ||
| 'description': """ | ||
| This module is here to help you manage your real estate business. | ||
| """, | ||
| 'summary': 'Track all the properties you own', | ||
| 'website': 'https://www.odoo.com/app/estate', | ||
| 'depends': [ | ||
| 'base' | ||
| ], | ||
| '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' | ||
| ], | ||
| 'demo': [ | ||
| ], | ||
| 'installable': True, | ||
| 'application': True, | ||
| 'author': 'Odoo S.A.', | ||
| 'license': 'LGPL-3', | ||
| } | ||
| 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,106 @@ | ||
| from odoo import models, fields, api | ||
| from odoo.exceptions import ValidationError, UserError | ||
| from odoo.tools.float_utils import float_compare, float_is_zero | ||
|
|
||
|
|
||
| class EstateProperty(models.Model): | ||
| _name = "estate.property" | ||
| _description = "Estate properties" | ||
| _order = "id desc" | ||
| name = fields.Char('Name', required=True, translate=True) | ||
| description = fields.Text('Description', required=True) | ||
| postcode = fields.Char('Postcode') | ||
| date_availability = fields.Date('Available From', copy=False, default=fields.Date.add(fields.Date.today(), months=3)) | ||
| expected_price = fields.Float('Expected Price') | ||
| selling_price = fields.Float('Selling Price', readonly=True) | ||
| bedrooms = fields.Integer(default=2) | ||
| active = fields.Boolean(default=True) | ||
| living_area = fields.Integer('Living Area (sqm)') | ||
| facades = fields.Integer('Facades') | ||
| garage = fields.Boolean('Garage') | ||
| garden = fields.Boolean('Garden') | ||
| garden_area = fields.Float('Garden Area (sqm)') | ||
| garden_orientation = fields.Selection( | ||
| string='Garden Orientation', | ||
| selection=[ | ||
| ('north', 'North'), | ||
| ('south', 'South'), | ||
| ('east', 'East'), | ||
| ('west', 'West') | ||
| ]) | ||
| state = fields.Selection( | ||
| string='State', | ||
| required=True, | ||
| default='new', | ||
| copy=False, | ||
| selection=[ | ||
| ('new', 'New'), | ||
| ('offer_received','Offer Received'), | ||
| ('offer_accepted', 'Offer Accepted'), | ||
| ('sold', 'Sold'), | ||
| ('cancelled', 'Cancelled') | ||
| ]) | ||
| property_type_id = fields.Many2one("estate.property.type", "Type") | ||
| property_tags_ids = fields.Many2many("estate.property.tag", "Tags") | ||
| sales_person_id = fields.Many2one('res.users', string='Salesperson', default=lambda self: self.env.user) | ||
| buyer_id = fields.Many2one('res.partner', string='Buyer', copy=False) | ||
| offer_ids = fields.One2many('estate.property.offer', 'property_id', string="Offers") | ||
| total_area = fields.Float('Total Area', compute='_compute_total_area') | ||
| best_price = fields.Float('Best Offer', compute='_compute_best_price') | ||
|
|
||
| _check_positive_selling_price = models.Constraint( | ||
| 'CHECK(selling_price >= 0)', | ||
| 'The selling price must be positive.', | ||
| ) | ||
| _check_positive_expected_price = models.Constraint( | ||
| 'CHECK(expected_price > 0)', | ||
| 'The expected price must be positive.', | ||
| ) | ||
|
|
||
| @api.depends('garden_area', 'living_area') | ||
| def _compute_total_area(self): | ||
| for record in self: | ||
| record.total_area = record.garden_area + record.living_area | ||
|
|
||
| @api.depends('offer_ids.price') | ||
| def _compute_best_price(self): | ||
| for record in self: | ||
| record.best_price = max(record.offer_ids.mapped('price'), default=0.0) | ||
| @api.onchange('garden') | ||
| def _onchange_garden(self): | ||
| if not self.garden: | ||
| self.garden_area = None | ||
| self.garden_orientation = None | ||
| else: | ||
| self.garden_area = 10 | ||
| self.garden_orientation = 'north' | ||
|
|
||
| def action_cancel_property(self): | ||
| for record in self: | ||
| if record.state == 'sold': | ||
| raise UserError('Sold properties cannot be cancelled') | ||
| else: | ||
| record.state = 'cancelled' | ||
| return True | ||
|
|
||
| def action_sold_property(self): | ||
| for record in self: | ||
| if record.state == 'cancelled': | ||
| raise UserError('Cancelled properties cannot be sold') | ||
| else: | ||
| record.state = 'sold' | ||
| return True | ||
|
|
||
| @api.constrains('selling_price', 'expected_price') | ||
| def _check_selling_price(self): | ||
| for record in self: | ||
| if float_is_zero(record.selling_price, precision_rounding=0.0001): | ||
| continue | ||
| if float_compare( | ||
| record.selling_price, | ||
| record.expected_price * 0.9, | ||
| precision_rounding=0.01, | ||
| ) == -1: | ||
| raise ValidationError( | ||
| "The selling price cannot be lower than 90 precent of the expected price." | ||
| ) |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,48 @@ | ||||||
| from odoo import models, fields, api, exceptions | ||||||
|
|
||||||
|
|
||||||
| class EstatePropertyOffer(models.Model): | ||||||
| _name = "estate.property.offer" | ||||||
| _description = "Estate properties Offers" | ||||||
|
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. Better to have the description here as title case
Suggested change
|
||||||
| _order = "price desc" | ||||||
| price = fields.Float('Price') | ||||||
| status = fields.Selection( | ||||||
| string='Status', | ||||||
| default='new', | ||||||
| copy=False, | ||||||
| selection=[('accepted', 'Accepted'), ('refused', 'Refused'), ('new', 'New')]) | ||||||
|
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. Same as above to have in multiple lines |
||||||
| partner_id = fields.Many2one('res.partner', string='Buyer', required=True) | ||||||
| property_id = fields.Many2one('estate.property', string='Estate Property', required=True) | ||||||
| validity = fields.Integer('Validity', default=7) | ||||||
| date_deadline = fields.Datetime('Deadline', compute='_compute_date_deadline', inverse='_inverse_date_deadline') | ||||||
|
|
||||||
| _check_positive_price = models.Constraint( | ||||||
| 'CHECK(price >= 0)', | ||||||
| 'The price must be positive.', | ||||||
| ) | ||||||
|
|
||||||
| @api.depends('validity', 'create_date') | ||||||
| def _compute_date_deadline(self): | ||||||
| for record in self: | ||||||
| if record.create_date: | ||||||
| record.date_deadline = fields.Date.add(record.create_date, days=record.validity) | ||||||
|
|
||||||
| def _inverse_date_deadline(self): | ||||||
| for record in self: | ||||||
| record.validity = (record.date_deadline - record.create_date).days | ||||||
|
|
||||||
| def action_accept_offer(self): | ||||||
| if any(record.status == 'accepted' for record in self.property_id.offer_ids): | ||||||
| raise exceptions.UserError('An offer is already accepted for this property') | ||||||
| for record in self: | ||||||
| record.status = 'accepted' | ||||||
| record.property_id.buyer_id = record.partner_id | ||||||
| record.property_id.selling_price = record.price | ||||||
| return True | ||||||
|
|
||||||
| def action_refuse_offer(self): | ||||||
| for record in self: | ||||||
| if record.status == 'accepted': | ||||||
| raise exceptions.UserError('You cant refuse an already accepted offer') | ||||||
| record.status = 'refused' | ||||||
| return True | ||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| from odoo import models, fields | ||
|
|
||
|
|
||
| class EstatePropertyTag(models.Model): | ||
| _name = "estate.property.tag" | ||
| _description = "Estate properties Tags" | ||
|
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. Same about the title-case convention we should use in description |
||
| _order = "name" | ||
| name = fields.Char('Name', required=True, translate=True) | ||
|
|
||
| _tags_uniq = models.Constraint( | ||
| 'unique(name)', | ||
| "The tag name already exists", | ||
| ) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| from odoo import models, fields | ||
|
|
||
|
|
||
| class EstatePropertyType(models.Model): | ||
| _name = "estate.property.type" | ||
| _description = "Estate properties Types" | ||
|
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. Same |
||
| _order = "name" | ||
| name = fields.Char('Name', required=True, translate=True) | ||
| properties_ids = fields.One2many("estate.property", "property_type_id", "Properties") | ||
| sequence = fields.Integer('Sequence', default=1, help="Used to order stages. Lower is better.") | ||
| _types_uniq = models.Constraint( | ||
| 'unique(name)', | ||
| "The type name already exists", | ||
| ) | ||
| 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_main_menu" name="Real Estate"> | ||
| <menuitem id="estate_advertisement_menu" name="Advertisment"> | ||
| <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> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| <?xml version="1.0"?> | ||
| <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="Estate Property Offer"> | ||
| <group> | ||
| <field name="price" /> | ||
| <field name="status" /> | ||
| <field name="partner_id" /> | ||
| <field name="validity" /> | ||
| <field name="date_deadline" /> | ||
| </group> | ||
| </form> | ||
| </field> | ||
| </record> | ||
|
|
||
| <record id="estate_property_offer_view_tree" 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" /> | ||
| <field name="date_deadline" /> | ||
| <button name="action_accept_offer" string="Accept" type="object" icon="fa-check" /> | ||
| <button name="action_refuse_offer" string="Refuse" type="object" icon="fa-times" /> | ||
| <field name="status" /> | ||
| </list> | ||
| </field> | ||
| </record> | ||
| </odoo> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| <?xml version="1.0"?> | ||
| <odoo> | ||
| <record id="estate_property_tag_view_form" model="ir.ui.view"> | ||
| <field name="name">estate.property.tag.form</field> | ||
| <field name="model">estate.property.tag</field> | ||
| <field name="arch" type="xml"> | ||
| <form string="Estate Property Tag"> | ||
| <sheet> | ||
| <div class="oe_title"> | ||
| <h1 class="mb32"> | ||
| <field name="name" placeholder="Under Option" class="mb16" /> | ||
| </h1> | ||
| </div> | ||
| </sheet> | ||
| </form> | ||
| </field> | ||
| </record> | ||
|
|
||
| <record id="estate_property_tag_action" model="ir.actions.act_window"> | ||
| <field name="name">Estate Property Tags</field> | ||
| <field name="res_model">estate.property.tag</field> | ||
| <field name="view_mode">list,form</field> | ||
| <field name="help" type="html"> | ||
| <p class="o_view_nocontent_smiling_face"> | ||
| Property Tags. | ||
| </p> | ||
| </field> | ||
| </record> | ||
| </odoo> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
| <?xml version="1.0"?> | ||
| <odoo> | ||
| <record id="estate_property_type_view_form" model="ir.ui.view"> | ||
| <field name="name">estate.property.type.form</field> | ||
| <field name="model">estate.property.type</field> | ||
| <field name="arch" type="xml"> | ||
| <form string="Estate Property Type"> | ||
| <sheet> | ||
| <div class="oe_title"> | ||
| <h1 class="mb32"> | ||
| <field name="name" placeholder="House" class="mb16" /> | ||
| </h1> | ||
| </div> | ||
| <notebook> | ||
| <page string="Properties"> | ||
| <field name="properties_ids"> | ||
| <list> | ||
| <field name="name" string="Title" /> | ||
| <field name="state" /> | ||
| <field name="expected_price" /> | ||
| </list> | ||
| </field> | ||
| </page> | ||
| </notebook> | ||
| </sheet> | ||
| </form> | ||
| </field> | ||
| </record> | ||
|
|
||
| <record id="estate_property_view_list" model="ir.ui.view"> | ||
| <field name="name">estate.property.type.list</field> | ||
| <field name="model">estate.property.type</field> | ||
| <field name="arch" type="xml"> | ||
| <list string="Types" multi_edit="1"> | ||
| <field name="name" /> | ||
| <field name="sequence" widget="handle" /> | ||
| </list> | ||
| </field> | ||
| </record> | ||
|
|
||
| <record id="estate_property_type_action" model="ir.actions.act_window"> | ||
| <field name="name">Estate Property Types</field> | ||
| <field name="res_model">estate.property.type</field> | ||
| <field name="view_mode">list,form</field> | ||
| <field name="help" type="html"> | ||
| <p class="o_view_nocontent_smiling_face"> | ||
| A property type is, for example, a house or an apartment. | ||
| </p> | ||
| </field> | ||
| </record> | ||
| </odoo> |
Uh oh!
There was an error while loading. Please reload this page.