-
Notifications
You must be signed in to change notification settings - Fork 2.8k
19.0 tutorial dhvag #1110
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?
19.0 tutorial dhvag #1110
Changes from all commits
32a250b
ca94372
f8ae7c6
41b6a55
48db2c6
def4d3d
fa2ac6b
0c7c7f7
332bd11
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': 'Dhrudeep', | ||
| 'description': """ | ||
| This module provides functionality to manage real estate properties. | ||
| """, | ||
| 'data': [ | ||
| 'security/ir.model.access.csv', | ||
| 'views/estate_property_offer_views.xml', | ||
| 'views/estate_property_tag_views.xml', | ||
| 'views/estate_property_type_views.xml', | ||
| 'views/estate_property_views.xml', | ||
| 'views/estate_property_maintenance_requests_view.xml', | ||
| 'views/estate_menus.xml', | ||
| ], | ||
| 'category': 'Tutorial', | ||
| 'license': 'LGPL-3', | ||
| 'application': True, | ||
| } |
| 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 estate_property_maintenance_requests |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,122 @@ | ||
| from odoo import api, fields, models | ||
| from odoo.exceptions import UserError, ValidationError | ||
|
|
||
|
|
||
| class EstateProperty(models.Model): | ||
| _name = 'estate.property' | ||
| _description = 'Estate Property Planning' | ||
| _order = 'id desc' # defaultvalue = asc | ||
|
|
||
| name = fields.Char(required=True, default="Unknown") | ||
| description = fields.Text() | ||
| postcode = fields.Float() | ||
| date_availability = fields.Date( | ||
| "Available From", copy=False, default=fields.Datetime.now | ||
| ) | ||
| 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( | ||
| selection=[ | ||
| ('north', "North"), | ||
| ('west', "West"), | ||
| ('east', "East"), | ||
| ('south', "South"), | ||
| ], | ||
| help="Direction for the garden" | ||
| ) | ||
| active = fields.Boolean(default=True) | ||
| state = fields.Selection( | ||
| selection=[ | ||
| ('new', "New"), | ||
| ('offer_received', "Offer Received"), | ||
| ('offer_accepted', "Offer Accepted"), | ||
| ('sold', "Sold"), | ||
| ('cancelled', "Cancelled"), | ||
| ], | ||
| string="Status", | ||
| default='new', | ||
| ) | ||
| property_type_id = fields.Many2one( | ||
| 'estate.property.type', string="Property Type") | ||
| seller_ids = fields.Many2one( | ||
| 'res.users', string="Salesman", default=lambda self: self.env.user | ||
|
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 does this lambda function do? |
||
| ) | ||
| buyer_ids = fields.Many2one('res.partner', string="Buyer", copy=False) | ||
| tags_ids = fields.Many2many('estate.property.tag') | ||
| offer_ids = fields.One2many('estate.property.offer', 'property_id') | ||
| total_area = fields.Float(compute='_compute_total_area') | ||
| best_price = fields.Float( | ||
| "Best offer", compute='_compute_best_price', store=True) | ||
|
|
||
| property_maintenance_requests = fields.One2many( | ||
| 'estate.property.maintenance.requests', 'property_id') | ||
| total_maintenance_cost = fields.Float( | ||
| compute='_compute_total_maintenance_cost', string="Total Maintenance Cost") | ||
|
|
||
| @api.depends('property_maintenance_requests.cost') | ||
| def _compute_total_maintenance_cost(self): | ||
| for record in self: | ||
| record.total_maintenance_cost = ( | ||
| sum(record.property_maintenance_requests.mapped('cost') | ||
| ) if record.property_maintenance_requests else 0.0 | ||
| ) | ||
|
|
||
| # SQL Constraint | ||
| _check_expected_price = models.Constraint( | ||
| 'CHECK(expected_price > 0)', "The expected price must be strictly positive") | ||
| _check_selling_price = models.Constraint( | ||
| 'CHECK(selling_price >= 0)', "The selling price must be positive") | ||
|
|
||
| # Python Constriant | ||
| @api.constrains('selling_price', 'expected_price') | ||
| def _check_price(self): | ||
| if self.buyer_ids and self.selling_price < (self.expected_price * 0.9): | ||
| raise ValidationError( | ||
| "The selling price must be at least 90% of the expected price! You must reduce the expected price if you want to accept this offer.") | ||
|
|
||
| # Compute Methods | ||
| # Depends Decorator | ||
| @api.depends('living_area', 'garden_area') | ||
| def _compute_total_area(self): | ||
| for record in self: | ||
| record.total_area = record.living_area + record.garden_area | ||
|
|
||
| @api.depends('offer_ids') | ||
| def _compute_best_price(self): | ||
| for record in self: | ||
| record.best_price = ( | ||
| max(record.offer_ids.mapped('price') | ||
| ) if record.offer_ids else 0.0 | ||
| ) | ||
|
|
||
| # Onchange Decorator | ||
| @api.onchange('garden') | ||
| def _onchange_garden(self): | ||
| if self.garden: | ||
| self.garden_area = "10" | ||
| self.garden_orientation = "north" | ||
| else: | ||
| self.garden_area = None | ||
| self.garden_orientation = None | ||
|
|
||
| # Action Methods | ||
| def action_sold(self): | ||
| if 'cancelled' in self.mapped('state'): | ||
| raise UserError("Cancelled properties cannot be sold.") | ||
| else: | ||
| for record in self: | ||
| if record.property_maintenance_requests.status != 'done': | ||
| raise UserError( | ||
| "Property cannot be sold if there is any maintenance request not done.") | ||
| return self.write({'state': 'sold'}) | ||
|
|
||
| def action_cancel(self): | ||
| if 'sold' in self.mapped('state'): | ||
| raise UserError("Sold properties cannot be cancelled.") | ||
| return self.write({'state': 'cancelled'}) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| from odoo import api, fields, models | ||
| from odoo.exceptions import ValidationError | ||
|
|
||
|
|
||
| class EstatePropertyMaintenanceRequests(models.Model): | ||
| _name = 'estate.property.maintenance.requests' | ||
| _description = 'Estate Property Maintenance Requests' | ||
|
|
||
| title = fields.Char() | ||
| cost = fields.Float() | ||
| status = fields.Selection( | ||
| default='new', | ||
| copy=False, | ||
| selection=[('new', "New"), ('approved', "Approved"), ('done', "Done")], | ||
| ) | ||
| property_id = fields.Many2one('estate.property', required=True) | ||
|
|
||
| @api.constrains('status', 'cost') | ||
| def _check_cost(self): | ||
| if self.status == 'approved' and self.cost <= 0: | ||
| raise ValidationError( | ||
| "The cost must be greater than zero.") |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,61 @@ | ||
| 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 = 'Estate Property offer' | ||
| _order = 'price desc' | ||
|
|
||
| price = fields.Float("Price") | ||
| status = fields.Selection( | ||
| copy=False, | ||
| selection=[('accepted', "Accepted"), ('refused', "Refused")], | ||
| ) | ||
| partner_id = fields.Many2one( | ||
| 'res.partner', string="Partner", required=True) | ||
| property_id = fields.Many2one('estate.property', required=True) | ||
| validity = fields.Integer(default=7) | ||
| date_deadline = fields.Date( | ||
| compute='_compute_date_deadline', inverse='_inverse_date_deadline') | ||
| property_type_id = fields.Many2one( | ||
| related='property_id.property_type_id', store=True) | ||
|
|
||
| # SQL Constraint | ||
| _check_offer_price = models.Constraint( | ||
| 'CHECK(price > 0)', "The selling price must be positive") | ||
|
|
||
| # Compute Methods | ||
| # Depends Decorator | ||
| @api.depends('create_date', 'validity') | ||
| def _compute_date_deadline(self): | ||
| for offer in self: | ||
| date = offer.create_date.date() if offer.create_date else fields.Date.today() | ||
| offer.date_deadline = date + relativedelta(days=offer.validity) | ||
|
|
||
| def _inverse_date_deadline(self): | ||
| for record in self: | ||
| start_date = ( | ||
| record.create_date.date() if record.create_date else fields.Date.today() | ||
| ) | ||
| record.validity = (record.date_deadline - start_date).days | ||
|
|
||
| # Action Funcitons | ||
| def action_accept(self): | ||
| for record in self: | ||
| if record.property_id.state != 'offer_accepted': | ||
| record.status = 'accepted' | ||
| record.property_id.selling_price = record.price | ||
| record.property_id.buyer_ids = self.partner_id | ||
| record.property_id.state = 'offer_accepted' | ||
| else: | ||
| raise UserError('One offer has already been accepted') | ||
|
Comment on lines
+46
to
+54
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 optimize this? |
||
|
|
||
| def action_refuse(self): | ||
| for record in self: | ||
| record.status = 'refused' | ||
| record.property_id.state = 'new' | ||
| record.property_id.selling_price = '0' | ||
| record.property_id.buyer_ids = None | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| from odoo import api, fields, models | ||
|
|
||
|
|
||
| class EstatePropertyTag(models.Model): | ||
| _name = 'estate.property.tag' | ||
| _description = 'Estate Property Tag' | ||
| _order = 'name' | ||
|
|
||
| name = fields.Char(required=True) | ||
| color = fields.Integer("Color Index") | ||
|
|
||
| # SQL Constraint | ||
| _check_name = models.Constraint('unique(name)', "The name must be unique") | ||
|
|
||
| local_datetime = fields.Char( | ||
| compute='_compute_local_datetime', store=True) | ||
|
|
||
| # Copmute Method - Depends Decorator | ||
| @api.depends('create_date') | ||
| def _compute_local_datetime(self): | ||
| for record in self: | ||
| if record.create_date: | ||
| # user_tz = self.env.user.tz or 'Asia/Kolkata' | ||
| local_dt = fields.Datetime.context_timestamp( | ||
| record, record.create_date | ||
| ) | ||
| record.local_datetime = local_dt.strftime("%Y-%m-%d %H:%M:%S") | ||
| else: | ||
| record.local_datetime = False |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| from odoo import api, 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. Follow this conventions while declaring fields, constraints, methods or CRUD operations. |
||
| _name = 'estate.property.type' | ||
| _description = 'Estate Property Type' | ||
| _order = 'name, sequence' | ||
|
|
||
| name = fields.Char(required=True) | ||
| sequence = fields.Integer() | ||
| offer_ids = fields.One2many('estate.property.offer', 'property_type_id') | ||
| offer_count = fields.Integer(compute='_compute_offer_count') | ||
|
|
||
| # Depends Decorator | ||
| @api.depends('offer_ids') | ||
| def _compute_offer_count(self): | ||
| for record in self: | ||
| record.offer_count = len(record.offer_ids) | ||
|
|
||
| # SQL Constarint | ||
| _check_name = models.Constraint('unique(name)', "The name must be unique") | ||
|
|
||
| # For Inline View | ||
| property_ids = fields.One2many( | ||
| "estate.property", "property_type_id", string="Properties") | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| id,name,model_id/id,group_id/id,perm_read,perm_write,perm_create,perm_unlink | ||
| access_estate_model,access_estate_model,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 | ||
| access_estate_property_maintenance_requests,access_estate_property_maintenance_requests,model_estate_property_maintenance_requests,base.group_user,1,1,1,1 | ||
|
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. Add a line at the end of file |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| <?xml version='1.0' encoding='UTF-8'?> | ||
| <odoo> | ||
| <menuitem | ||
| id="estate_menu_root" | ||
| name="estate"/> | ||
|
|
||
| <!-- Advertisments --> | ||
| <menuitem | ||
| id="estate_menu_advertisements" | ||
| parent="estate_menu_root" | ||
| name="Advertisements"/> | ||
|
|
||
| <menuitem | ||
| id="estate_advertisements_properties" | ||
| parent="estate_menu_advertisements" | ||
| action="estate_model_action"/> | ||
|
|
||
| <!-- Settings --> | ||
| <menuitem | ||
| id="estate_menu_settings" | ||
| parent="estate_menu_root" | ||
| name="Settings"/> | ||
|
|
||
| <menuitem | ||
| id="estate_view_property_type" | ||
| parent="estate_menu_settings" | ||
| action="estate_property_type_action"/> | ||
|
|
||
| <menuitem | ||
| id="estate_view_property_tag" | ||
| parent="estate_menu_settings" | ||
| action="estate_property_tag_action"/> | ||
|
|
||
| <menuitem | ||
| id="estate_view_property_maintenance_requests" | ||
| parent="estate_menu_settings" | ||
| action="estate_property_maintenance_requests_action"/> | ||
| </odoo> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| <?xml version='1.0' encoding='UTF-8'?> | ||
| <odoo> | ||
|
|
||
| <!-- Form View --> | ||
| <record id="estate_property_maintenance_requests_view_form" model="ir.ui.view"> | ||
| <field name="name">estate.property.maintenance.requests.form</field> | ||
| <field name="model">estate.property.maintenance.requests</field> | ||
| <field name="arch" type="xml"> | ||
| <form> | ||
| <sheet> | ||
| <group> | ||
| <field name="title"/> | ||
| <field name="cost"/> | ||
| <field name="status"/> | ||
| <field name='property_id'/> | ||
| </group> | ||
| </sheet> | ||
| </form> | ||
| </field> | ||
| </record> | ||
|
|
||
| <!-- List View --> | ||
| <record id="estate_property_maintenance_requests_view_list" model="ir.ui.view"> | ||
| <field name="name">estate.property.maintenance.requests.list</field> | ||
| <field name="model">estate.property.maintenance.requests</field> | ||
| <field name="arch" type="xml"> | ||
| <list> | ||
| <field name="title"/> | ||
| <field name="cost"/> | ||
| <field name="status"/> | ||
| <field name='property_id' string="Property Name"/> | ||
| </list> | ||
| </field> | ||
| </record> | ||
|
|
||
| <!-- Action window --> | ||
| <record id="estate_property_maintenance_requests_action" model="ir.actions.act_window"> | ||
| <field name="name">Maintenance Requests</field> | ||
| <field name="res_model">estate.property.maintenance.requests</field> | ||
| <field name="view_mode">list,form</field> | ||
| </record> | ||
| </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.