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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ estate.property.offer.model.form
+ estate_property_offer_model
+
+
+
+
+
+
+
+ estate.property.offer.model.search
+ estate_property_offer_model
+
+
+
+
+
+
+
+
+
diff --git a/estate/views/estate_property_tag.xml b/estate/views/estate_property_tag.xml
new file mode 100644
index 00000000000..66cbf3e69e8
--- /dev/null
+++ b/estate/views/estate_property_tag.xml
@@ -0,0 +1,54 @@
+
+
+
+
+
+ Estate Property Tag
+ estate_property_tag_model
+ list,form
+
+
+
+
+ estate.property.tag.model.list
+ estate_property_tag_model
+
+
+
+
+
+
+
+
+
+
+ estate.property.tag.model.form
+ estate_property_tag_model
+
+
+
+
+
+
+
+ estate.property.tag.model.search
+ estate_property_tag_model
+
+
+
+
+
+
+
+
diff --git a/estate/views/estate_property_type.xml b/estate/views/estate_property_type.xml
new file mode 100644
index 00000000000..824ec2b4170
--- /dev/null
+++ b/estate/views/estate_property_type.xml
@@ -0,0 +1,62 @@
+
+
+
+
+
+ Estate Property Type action
+ estate_property_type_model
+ list,form
+
+
+
+
+ estate.property.type.model.list
+ estate_property_type_model
+
+
+
+
+
+
+
+
+
+
+ estate.property.type.model.form
+ estate_property_type_model
+
+
+
+
+
+
+
+ estate.property.type.model.search
+ estate_property_type_model
+
+
+
+
+
+
+
+