diff --git a/estate/__init__.py b/estate/__init__.py new file mode 100644 index 00000000000..0650744f6bc --- /dev/null +++ b/estate/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/estate/__manifest__.py b/estate/__manifest__.py new file mode 100644 index 00000000000..69814356e7f --- /dev/null +++ b/estate/__manifest__.py @@ -0,0 +1,17 @@ +{ + 'name': 'Estate', + 'version': '1.0', + 'summary': 'Tutorial module for managing real estate properties', + 'description': 'A starter module to learn Odoo development by managing estate properties.', + 'author': '', + 'depends': ['base'], + 'data': [ + "views/estate_property_offer.xml", + "views/estate_property_type.xml", + "views/estate_property_tag.xml", + "views/estate_property.xml", + "security/ir.model.access.csv" + ], + 'installable': True, + 'application': True, +} diff --git a/estate/models/__init__.py b/estate/models/__init__.py new file mode 100644 index 00000000000..e5f89221b05 --- /dev/null +++ b/estate/models/__init__.py @@ -0,0 +1 @@ +from . import estate_property, estate_property_type, estate_property_tag, 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 00000000000..8af4dcef753 --- /dev/null +++ b/estate/models/estate_property.py @@ -0,0 +1,84 @@ +from odoo import api, models, fields, exceptions + + +class EstatePropertyModel(models.Model): # Inheritence -> This class inherits from models.Model + _name = "estate_property_model" # Name of the table in database + _description = "Estate Property Model" # user-friendly name + _order = "id desc" + + name = fields.Char(required=True) # VARCHAR & NOT NULL + expected_price = fields.Float(required=True) # NUMERIC & NOT NULL + description = fields.Char() + + living_area = fields.Integer() + garden = fields.Boolean() + garden_area = fields.Integer() + garden_orientation = fields.Selection([("north", 'North'), ('south', 'South'), ('east', "East"), ("west", 'West')]) + total_area = fields.Integer(compute="_compute_total_area") + + best_price = fields.Float(compute="_compute_best_offer_price", string="Best Accepted Offer") + + sold = fields.Boolean() + cancelled = fields.Boolean() + property_status = fields.Char(default="New", string="Property Status") + + @api.depends("living_area", "garden_area") + def _compute_total_area(self): + for record in self: + record.total_area = record.living_area + record.garden_area + + property_type_id = fields.Many2one( + comodel_name="estate_property_type_model", + string="Property Type", + ondelete="set null" + ) + property_tag_ids = fields.Many2many( + comodel_name="estate_property_tag_model", + relation="estate_property_tag_rel", + column1="estate_property_id", + column2="estate_property_tag_id", + string="Tag" + ) + property_offer_ids = fields.One2many( + comodel_name="estate_property_offer_model", + inverse_name="property_id", + string="Property Offers" + ) + + @api.depends("property_offer_ids") + def _compute_best_offer_price(self): + for record in self: + accepted_offers= record.property_offer_ids.filtered(lambda rec: rec.status == "accepted") + prices = accepted_offers.mapped("price") + record.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 = "" + + def action_property_sold(self): + if(self.cancelled==False): + self.sold = True + self.property_status = "Sold" + return True + else: + raise exceptions.UserError("Cancelled properties can't be sold") + + def action_property_cancelled(self): + if (self.sold==False): + self.cancelled = True + self.property_status = "Cancelled" + return True + else: + raise exceptions.UserError("Sold properties can't be cancelled") + + @api.constrains("expected_price") + def _check_expected_price_positive(self): + for record in self: + if(record.expected_price<=0): + raise exceptions.ValidationError("The price must be a positive number") \ 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 00000000000..0d1c1ee6675 --- /dev/null +++ b/estate/models/estate_property_offer.py @@ -0,0 +1,20 @@ +from odoo import models, fields + +class EstatePropertyOffer(models.Model): + _name = "estate_property_offer_model" + _order = "price desc" + + sequence = fields.Integer('Sequence', default=1, help="Used to order stages. Lower is better.") + + price = fields.Float(required=True) + status = fields.Selection([('recieved', 'Recieved'), ('accepted', 'Accepted'), ('refused', 'Refused')], string="Offer Status", default="recieved") + partner_id = fields.Many2one(comodel_name="res.partner", string="Partner") + property_id = fields.Many2one(comodel_name="estate_property_model", string="Estate Property") + + def action_accept_offer(self): + self.status = "accepted" + return True + + def action_refuse_offer(self): + self.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 00000000000..13415e70b66 --- /dev/null +++ b/estate/models/estate_property_tag.py @@ -0,0 +1,14 @@ +from odoo import models, fields + +class EstatePropertyTag(models.Model): + _name = "estate_property_tag_model" + _order = "name" + + name = fields.Char(required=True) + property_ids = fields.Many2many( + comodel_name="estate_property_model", + relation="estate_property_tag_rel", + column1="estate_property_tag_id", + column2="estate_property_id", + string="Properties" + ) \ 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 00000000000..f32ddd1c10b --- /dev/null +++ b/estate/models/estate_property_type.py @@ -0,0 +1,13 @@ +from odoo import models, fields + + +class PropertyType(models.Model): + _name = "estate_property_type_model" + _order = "name" + + name = fields.Char(required=True) + property_ids = fields.One2many( + comodel_name="estate_property_model", + inverse_name="property_type_id", + string="Properties" + ) \ 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 00000000000..20dc10709b5 --- /dev/null +++ b/estate/security/ir.model.access.csv @@ -0,0 +1,5 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_estate_property_model_user,estate.property.user,model_estate_property_model,,1,1,1,1 +access_estate_property_type_user,estate.property.type.user,model_estate_property_type_model,base.group_user,1,1,1,1 +access_estate_property_tag_user,estate.property.tag.user,model_estate_property_tag_model,base.group_user,1,1,1,1 +access_estate_property_offer_user,estate.property.offer.user,model_estate_property_offer_model,base.group_user,1,1,1,1 \ No newline at end of file diff --git a/estate/views/estate_property.xml b/estate/views/estate_property.xml new file mode 100644 index 00000000000..7f8c686ab6d --- /dev/null +++ b/estate/views/estate_property.xml @@ -0,0 +1,123 @@ + + + + + + Estate Property action + estate_property_model + list,form + + + + + + + + + + + + + + + + estate.property.model.list + estate_property_model + + + + + + + + + + + + + + + + + + + estate.property.model.form + estate_property_model + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ + + + estate.property.model.search + estate_property_model + + + + + + + + + + + +
+
diff --git a/estate/views/estate_property_offer.xml b/estate/views/estate_property_offer.xml new file mode 100644 index 00000000000..e42820fe7e2 --- /dev/null +++ b/estate/views/estate_property_offer.xml @@ -0,0 +1,70 @@ + + + + + + Estate Property Offer + estate_property_offer_model + list,form + + + + + estate.property.offer.model.list + estate_property_offer_model + + + + + + +