diff --git a/estate.zip b/estate.zip
new file mode 100644
index 0000000000..5446237138
Binary files /dev/null and b/estate.zip differ
diff --git a/estate/__manifest__.py b/estate/__manifest__.py
index ab40a80f40..bddb9a613a 100644
--- a/estate/__manifest__.py
+++ b/estate/__manifest__.py
@@ -8,11 +8,16 @@
'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_property_type_views.xml',
'views/estate_menus.xml',
- 'views/res_users_views.xml'
+ 'views/res_users_views.xml',
+ ],
+ 'demo': [
+ 'demo/estate.property.type.csv',
+ 'demo/estate_property.xml',
+ 'demo/estate_property_offer.xml'
],
'installable': True,
'application': True,
diff --git a/estate/demo/estate.property.type.csv b/estate/demo/estate.property.type.csv
new file mode 100644
index 0000000000..17f4695bb6
--- /dev/null
+++ b/estate/demo/estate.property.type.csv
@@ -0,0 +1,5 @@
+"id","name"
+"data_property_type_residential","Residential"
+"data_property_type_commerial","Commerial"
+"data_property_type_industrial","Industrial"
+"data_property_type_land","Land"
diff --git a/estate/demo/estate_property.xml b/estate/demo/estate_property.xml
new file mode 100644
index 0000000000..8359e8af29
--- /dev/null
+++ b/estate/demo/estate_property.xml
@@ -0,0 +1,62 @@
+
+
+ Big Villa
+ new
+ A nice and big villa
+ 12345
+ 2020-02-02
+ 1600000
+ 6
+ 100
+ 4
+ True
+ True
+ 100000
+ south
+
+
+
+
+ Trailer home
+ cancelled
+ Home in a trailer park
+ 54321
+ 1970-01-01
+ 100000
+ 120000
+ 1
+ 10
+ 4
+ False
+
+
+
+
+ Tutorial House
+ offer_received
+ X2many tutorial
+ 13579
+ 2011-11-11
+ 123456
+ 135791
+ 2
+ 30
+ 4
+ False
+
+
+
+
+
\ No newline at end of file
diff --git a/estate/demo/estate_property_offer.xml b/estate/demo/estate_property_offer.xml
new file mode 100644
index 0000000000..87c01d263e
--- /dev/null
+++ b/estate/demo/estate_property_offer.xml
@@ -0,0 +1,29 @@
+
+
+
+
+ 10000
+ 14
+
+
+
+
+
+
+ 1500000
+ 14
+
+
+
+
+
+
+ 1500001
+ 14
+
+
+
+
+
+
+
diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py
index a816fd33d3..6971e731d1 100644
--- a/estate/models/estate_property.py
+++ b/estate/models/estate_property.py
@@ -57,19 +57,21 @@ def _compute_best_price(self):
record.state = 'offer_received'
@api.onchange("garden")
- def _onchange_partner_id(self):
+ def _onchange_garden(self):
for record in self:
if record.garden:
record.garden_area = 10
record.garden_orientation = 'north'
else:
- record.garden_area = None
- record.garden_orientation = None
+ record.garden_area = 0
+ record.garden_orientation = False
def action_property_sold(self):
for record in self:
if record.state == 'cancelled':
raise UserError("Cancelled properties cannot be sold.")
+ elif record.state != 'offer_accepted':
+ raise UserError("You cannot sell a property without an accepted offer.")
else:
record.state = 'sold'
return True
@@ -92,6 +94,6 @@ def _check_selling_price(self):
def unlink_if_new_or_cancelled(self):
for record in self:
if record.state not in ('new', 'cancelled'):
- raise UserError("Only new and cancelled properties can be sold.")
+ raise UserError("Only new and cancelled properties can be deleted.")
if record.offer_ids:
record.offer_ids.unlink()
diff --git a/estate/models/estate_property_offer.py b/estate/models/estate_property_offer.py
index 8df062241a..3334e5a0ce 100644
--- a/estate/models/estate_property_offer.py
+++ b/estate/models/estate_property_offer.py
@@ -55,6 +55,9 @@ def action_offer_refuse(self):
@api.model_create_multi
def create(self, vals_list):
for vals in vals_list:
+ property = self.env['estate.property'].browse(vals.get('property_id'))
+ if property.state in ('sold', 'cancelled'):
+ raise UserError(f"Cannot create an offer in a {property.state} property.")
price = vals.get('price')
estate_property = self.env['estate.property'].browse(vals.get('property_id'))
if estate_property.best_price and price <= estate_property.best_price:
diff --git a/estate/tests/__init__.py b/estate/tests/__init__.py
new file mode 100644
index 0000000000..576617cccf
--- /dev/null
+++ b/estate/tests/__init__.py
@@ -0,0 +1 @@
+from . import test_estate_property
diff --git a/estate/tests/test_estate_property.py b/estate/tests/test_estate_property.py
new file mode 100644
index 0000000000..2ac0c5e682
--- /dev/null
+++ b/estate/tests/test_estate_property.py
@@ -0,0 +1,105 @@
+from odoo.tests.common import TransactionCase
+from odoo.exceptions import UserError
+from odoo.tests import Form, tagged
+
+
+@tagged('post_install', '-at_install')
+class EstateTestCase(TransactionCase):
+
+ @classmethod
+ def setUpClass(cls):
+ # add env on cls and many other things
+ super().setUpClass()
+
+ cls.properties = cls.env['estate.property'].create([{'name': 'test_house'}])
+ cls.partner = cls.env['res.partner'].create([{
+ 'id': 'test_partner',
+ 'name': 'test_person',
+ 'company_name': 'test_company',
+ 'street': 'test_street',
+ 'city': 'test_city',
+ 'zip': '12345',
+ 'country_id': cls.env.ref('base.us').id,
+ 'state_id': cls.env.ref('base.state_us_39').id,
+ 'phone': '+1 555-555-5555',
+ 'email': 'test@testing.example.com',
+ 'tz': 'Europe/Brussels',
+ }])
+
+ def test_creation_area(self):
+ """Test that the total_area is computed like it should."""
+ self.properties.living_area = 20
+ self.properties.garden = True
+ self.properties.garden_area = 15
+ self.properties.garden_orientation = 'north'
+
+ self.assertRecordValues(self.properties, [
+ {'total_area': 35},
+ ])
+
+ def test_action_sell_without_accepted_offer(self):
+ """Test that everything behaves like it should when selling an invalid property."""
+
+ self.assertRecordValues(self.properties, [
+ {'state': 'new'},
+ ])
+
+ with self.assertRaises(UserError):
+ self.properties.action_property_sold()
+
+ def test_action_sell_with_accepted_offer(self):
+ """Test that everything behaves like it should when selling a valid property."""
+
+ self.properties.offer_ids.create({
+ 'property_id': self.properties.id,
+ 'partner_id': self.partner.id,
+ 'price': 124,
+ 'validity': 14,
+ })
+ self.properties.offer_ids.action_offer_accept()
+ self.properties.action_property_sold()
+
+ self.assertRecordValues(self.properties, [
+ {'state': 'sold'},
+ ])
+
+ def test_creation_offer_for_sold_property(self):
+ """Test that everything behaves like it should when property is sold."""
+
+ self.properties.offer_ids.create({
+ 'property_id': self.properties.id,
+ 'partner_id': self.partner.id,
+ 'price': 124,
+ 'validity': 14,
+ })
+ self.properties.offer_ids.action_offer_accept()
+ self.properties.action_property_sold()
+
+ with self.assertRaises(UserError):
+ self.properties.offer_ids.create({
+ 'property_id': self.properties.id,
+ 'partner_id': self.partner.id,
+ 'price': 130,
+ 'validity': 14,
+ })
+
+ def test_enable_garden(self):
+ """Test that default values are assigned to garden area and orientation when garden is enabled"""
+
+ form = Form(self.env['estate.property'])
+ form.garden = True
+
+ self.assertEqual(form.garden_area, 10)
+ self.assertEqual(form.garden_orientation, 'north')
+
+ def test_disable_garden(self):
+ """Test that values are removed from garden area and orientation when garden is disabled"""
+
+ form = Form(self.env['estate.property'])
+ form.garden = True
+ form.garden_area = 15
+ form.garden_orientation = 'south'
+ form.garden = False
+
+ self.assertEqual(form.garden_area, 0)
+ self.assertEqual(form.garden_orientation, False)