From cbb59ff3dfc880809974a7b560b1e27c7273aa8c Mon Sep 17 00:00:00 2001 From: Dhruvkumar Nagar Date: Fri, 22 Nov 2024 18:41:28 +0530 Subject: [PATCH 01/16] [ADD] estate: Added new models related to property model Learned about how to make our Application and model and all things around that basic and complex fields, things related to security fields, how to make UI changes, how to create relationships between models and fields, and things related to computed fields and onchange methods. --- estate/__init__.py | 1 + estate/__manifest__.py | 14 +++ estate/models/__init__.py | 4 + estate/models/estate_property.py | 97 +++++++++++++++ estate/models/estate_property_offer.py | 40 ++++++ estate/models/estate_property_tag.py | 11 ++ estate/models/estate_property_type.py | 7 ++ estate/security/ir.model.access.csv | 6 + estate/views/estate_menus.xml | 18 +++ estate/views/estate_property_type_views.xml | 27 ++++ estate/views/estate_property_views.xml | 131 ++++++++++++++++++++ 11 files changed, 356 insertions(+) create mode 100644 estate/__init__.py create mode 100644 estate/__manifest__.py create mode 100644 estate/models/__init__.py create mode 100644 estate/models/estate_property.py create mode 100644 estate/models/estate_property_offer.py create mode 100644 estate/models/estate_property_tag.py create mode 100644 estate/models/estate_property_type.py create mode 100644 estate/security/ir.model.access.csv create mode 100644 estate/views/estate_menus.xml create mode 100644 estate/views/estate_property_type_views.xml create mode 100644 estate/views/estate_property_views.xml diff --git a/estate/__init__.py b/estate/__init__.py new file mode 100644 index 0000000000..9a7e03eded --- /dev/null +++ b/estate/__init__.py @@ -0,0 +1 @@ +from . import models \ No newline at end of file diff --git a/estate/__manifest__.py b/estate/__manifest__.py new file mode 100644 index 0000000000..ff5c2f635c --- /dev/null +++ b/estate/__manifest__.py @@ -0,0 +1,14 @@ +{ + 'name': 'estate', + 'version': '1.0', + 'depends': [ + 'base', + ], + 'installable': True, + 'application': True, + 'data': [ + 'security/ir.model.access.csv', + 'views/estate_property_views.xml', + 'views/estate_menus.xml', + ] +} \ No newline at end of file diff --git a/estate/models/__init__.py b/estate/models/__init__.py new file mode 100644 index 0000000000..09b2099fe8 --- /dev/null +++ b/estate/models/__init__.py @@ -0,0 +1,4 @@ +from . import estate_property +from . import estate_property_type +from . import estate_property_tag +from . import estate_property_offer \ No newline at end of file diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py new file mode 100644 index 0000000000..bc88bd399e --- /dev/null +++ b/estate/models/estate_property.py @@ -0,0 +1,97 @@ +from odoo.fields import float_compare, float_is_zero +from odoo import fields, models, api +from odoo.exceptions import UserError, ValidationError + +class estate_property(models.Model): + _name = "estate.property" + _description = "real estate property" + name = fields.Char(required=True) + active = fields.Boolean(default=True) + description = fields.Text() + postcode = fields.Char() + date_availability = fields.Date(copy=False,default=fields.Date.add(fields.Date.today(), months=+3)) + expected_price = fields.Float(required=True) + selling_price = fields.Float(readonly=True,copy=False,default=0.0) + bedrooms = fields.Integer(default=2) + living_area = fields.Integer() + facades = fields.Integer() + state = fields.Selection( + selection=[('New', 'New'), ('Offer Received', 'Offer Received'), ('Offer Accepted', 'Offer Accepted'), ('Sold', 'Sold'), ('Cancelled', 'Cancelled')], + copy=False, + required=True, + default='New') + garage = fields.Boolean() + garden = fields.Boolean() + garden_area = fields.Integer() + garden_orientation = fields.Selection( + selection=[('north', 'North'), ('south', 'South'), ('east', 'East'), ('west', 'West')]) + salesman = fields.Many2one("res.users", string='Salesperson', default=lambda self: self.env.user) + buyer = fields.Many2one("res.partner", copy=False) + property_type_id = fields.Many2one("estate.property.type") + tag_ids = fields.Many2many("estate.property.tag") + offer_ids = fields.One2many(comodel_name="estate.property.offer", inverse_name="property_id", string= "Offers") + total_area = fields.Integer(compute="_compute_total_area") + best_offer = fields.Float(compute="_compute_best_offer") + + _sql_constraints = [ + ('check_expected_price', 'CHECK(expected_price >= 0 AND selling_price >= 0 AND offer_ids.price > 0)', + 'The expected price and selling price should be greater than equal to zero.') + ] + + @api.depends('living_area', 'garden_area') + def _compute_total_area(self): + for area in self: + area.total_area = area.living_area + area.garden_area + + @api.depends('offer_ids') + def _compute_best_offer(self): + for record in self: + all_sequences = record.offer_ids.mapped('price') + record.best_offer = max(all_sequences) if all_sequences else 0.0 + + @api.onchange("garden") + def _onchange_garden(self): + for record in self: + if record.garden: + record.garden_area = 1000 + record.garden_orientation = "north" + else: + record.garden_area = 0 + record.garden_orientation = "" + + + def action_set_property_as_cancle(self): + if(self.state == 'Sold'): + raise UserError(("Sold property can't be cancelled")) + else: + self.state = 'Cancelled' + + def action_set_property_as_sold(self): + if(self.state == 'Cancelled'): + raise UserError(("Cancelled property can't be sold")) + else: + self.state = 'Sold' + + def action_accept_offer(self): + # Logic to accept the offer for the property + for offer in self.offer_ids: + offer.action_accept_offer() + return True + + def action_refuse_offer(self): + # Logic to refuse the offer for the property + for offer in self.offer_ids: + offer.action_refuse_offer() + 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=record.env.company.currency_id.rounding): + continue + min_price = 0.9 * record.expected_price + if float_compare(record.selling_price, min_price, precision_rounding=record.env.company.currency_id.rounding) < 0: + raise ValidationError( + _("The selling price (%.2f) cannot be lower than 90%% of the expected price (%.2f).") + % (record.selling_price, min_price) + ) \ No newline at end of file diff --git a/estate/models/estate_property_offer.py b/estate/models/estate_property_offer.py new file mode 100644 index 0000000000..7c28dee056 --- /dev/null +++ b/estate/models/estate_property_offer.py @@ -0,0 +1,40 @@ +from odoo import fields, models, api + +class estate_property_offer(models.Model): + _name = "estate.property.offer" + _description = "real estate property offers" + + price = fields.Float() + status = fields.Selection( + 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(compute="_compute_date_deadline", inverse="_inverse_date_deadline") + + _sql_constraints = [ + ('check_price', 'CHECK(price > 0)', + 'The Offer price should be greater than equal to zero.') + ] + + @api.depends("validity") + def _compute_date_deadline(self): + for record in self: + record.date_deadline = (fields.Date.add(fields.Date.today(), days=+record.validity)) + + def _inverse_date_deadline(self): + for record in self: + record.validity = fields.Date.add(fields.Date.today(), days=+record.validity) + + def action_accept_offer(self): + for record in self: + record.status = 'accepted' + record.property_id.buyer = record.partner_id + record.property_id.selling_price = record.price + return True + + def action_refuse_offer(self): + for record in self: + record.status = 'refused' + return True \ No newline at end of file diff --git a/estate/models/estate_property_tag.py b/estate/models/estate_property_tag.py new file mode 100644 index 0000000000..531ec5f64e --- /dev/null +++ b/estate/models/estate_property_tag.py @@ -0,0 +1,11 @@ +from odoo import models, fields # type: ignore + +class estate_property_tag(models.Model): + _name = "estate.property.tag" + _description = "real estate property tags" + + name = fields.Char(required=True) + + _sql_constraints = [ + ('code_tag_uniq', 'unique (name)', 'Tag name must be unique.'), + ] \ No newline at end of file diff --git a/estate/models/estate_property_type.py b/estate/models/estate_property_type.py new file mode 100644 index 0000000000..02fe099421 --- /dev/null +++ b/estate/models/estate_property_type.py @@ -0,0 +1,7 @@ +from odoo import fields, models + +class estate_property_type(models.Model): + _name = "estate.property.type" + _description = "real estate property types" + property_type = fields.Text() + name = fields.Char(required=True) \ No newline at end of file diff --git a/estate/security/ir.model.access.csv b/estate/security/ir.model.access.csv new file mode 100644 index 0000000000..f04c59826c --- /dev/null +++ b/estate/security/ir.model.access.csv @@ -0,0 +1,6 @@ +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 \ No newline at end of file diff --git a/estate/views/estate_menus.xml b/estate/views/estate_menus.xml new file mode 100644 index 0000000000..bd194399cf --- /dev/null +++ b/estate/views/estate_menus.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/estate/views/estate_property_type_views.xml b/estate/views/estate_property_type_views.xml new file mode 100644 index 0000000000..3b7d43ed29 --- /dev/null +++ b/estate/views/estate_property_type_views.xml @@ -0,0 +1,27 @@ + + + + + estate.property.type.form + estate.property.type + +
+ +

+ +

+
+
+
+
+ + + estate.property.type.tree + estate.property.type + + + + + + +
\ No newline at end of file diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml new file mode 100644 index 0000000000..15fc7c9dc6 --- /dev/null +++ b/estate/views/estate_property_views.xml @@ -0,0 +1,131 @@ + + + + + estate.property.action + estate.property + list,form + + + + estate.property.type.action + estate.property.type + list,form + + + + estate.property.tag.action + estate.property.tag + list,form + + + + estate.property.tree + estate.property + + + + + + + + + + + + + + + + estate.property.form + estate.property + +
+
+
+ +

+ +

+ + + + + + + + estate.property.tag + + + + + + + + + + + + + + + + + + + + + + + + + + + + +