Implemented cost framework

This commit is contained in:
Khalim Conn-Kowlessar 2023-12-20 14:55:04 +00:00
parent 3426629418
commit 0b52dc8097
4 changed files with 63 additions and 22 deletions

View file

@ -771,19 +771,38 @@ class Costs:
:return:
"""
number_of_windows = 7
windows_cost = 345
material_cost = material["material_cost"] * number_of_windows * self.SASH_WINDOW_INFLATION_FACTOR
labour_cost = (
material["labour_cost"] * number_of_windows * self.SASH_WINDOW_INFLATION_FACTOR *
self.labour_adjustment_factor
)
material_cost = windows_cost * number_of_windows * self.SASH_WINDOW_INFLATION_FACTOR
subtotal = material_cost + labour_cost
subtotal = material_cost
contingency_cost = subtotal * 0.2
preliminaries_cost = subtotal * 0.2
profit_cost = subtotal * 0.2
contingency_cost = subtotal * self.CONTINGENCY
preliminaries_cost = subtotal * self.PRELIMINARIES
profit_cost = subtotal * self.PROFIT_MARGIN
subtotal_before_vat = subtotal + contingency_cost + preliminaries_cost + profit_cost
vat_cost = subtotal_before_vat * 0.2
vat_cost = subtotal_before_vat * self.VAT_RATE
total_cost = subtotal_before_vat + vat_cost
labour_hours = material["labour_hours_per_unit"] * number_of_windows
# Assume a team of 2
labour_days = (labour_hours / 8) / 2
return {
"total": total_cost,
"subtotal": subtotal_before_vat,
"vat": vat_cost,
"contingency": contingency_cost,
"preliminaries": preliminaries_cost,
"material": material_cost,
"profit": profit_cost,
"labour_hours": labour_hours,
"labour_cost": labour_cost,
"labour_days": labour_days
}

View file

@ -11,14 +11,13 @@ class WindowsRecommendations:
self.recommendation = []
self.glazing_materials = [
material for material in materials if material["type"] == "window_glazing"
self.glazing_material = [
material for material in materials if material["type"] == "windows_glazing"
]
# TODO: This will be populated with the works associated to upgrading glazing. This will involve removal of
# previous glazing and installation of new glazing. This will not include the cost of the windows material
# themselfs
self.glazing_works_materials = []
if len(self.glazing_material) != 1:
raise ValueError("There should only be one window glazing material")
self.glazing_material = self.glazing_material[0]
def recommend(self):
"""
@ -34,12 +33,14 @@ class WindowsRecommendations:
if not number_of_windows:
raise ValueError("Number of windows not specified")
if self.property.windows["has_glazing"] & self.property.windows["glazing_coverage"] == "full":
if self.property.windows["has_glazing"] & (self.property.windows["glazing_coverage"] == "full"):
return
# We then price the job based on the number of windows that there are
cost_result = {}
cost_result = self.costs.window_glazing(
number_of_windows=number_of_windows,
material=self.glazing_material,
)
description = None

View file

@ -942,8 +942,24 @@ materials = [
'https://www.hamuch.com/cost/led-spot-light#:~:text=It%20costs%20an%20average%20of,'
'will%20drive%20up%20the%20cost.',
'created_at': datetime.datetime(2023, 11, 28, 22, 49, 12, 244907), 'is_active': True, 'prime_material_cost': None,
'material_cost': 20.0, 'labour_cost': 46.0, 'labour_hours_per_unit': 0.8, 'plant_cost': 0.0, 'total_cost': 66.0,
'material_cost': 20.0, 'labour_cost': 15.0, 'labour_hours_per_unit': 0.8, 'plant_cost': 0.0, 'total_cost': 66.0,
'notes': 'We estimate the unit economics from the checkatrade article. We assume that the average job consists '
'of installing 6 lights based on the hamuch article. We use the median value of 400 for a job of 6 '
'lights'}
'lights'},
{'id': 1235, 'type': 'windows_glazing',
'description': 'uPVC windows; Profile 22 or other equal and approved; reinforced where appropriate with '
'aluminium alloy; in refurbishment work, including standard ironmongery; sills and factory glazed '
'with low-e 24 mm double glazing; removing existing windows and fixing new in position; including '
'lugs plugged and screwed to brickwork or blockwork; Casement/fixed light; including vents; '
'e.p.d.m. glazing gaskets and weather seals; 1770 mm × 1200 mm; ref P312WW',
'depth': 0.0, 'depth_unit': None, 'cost': None, 'cost_unit': 'gbp_per_unit', 'r_value_per_mm': None,
'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': None, 'thermal_conductivity_unit': None,
'link': 'SPONs',
'created_at': datetime.datetime(2023, 11, 28, 22, 49, 12, 244907),
'is_active': True, 'prime_material_cost': 176.55,
'material_cost': 182.25, 'labour_cost': 163.36, 'labour_hours_per_unit': 6.5, 'plant_cost': 0.0,
'total_cost': 345.61,
'notes': 'This is the cost of removal of existing windows and installation of new windows. This is a casement '
'style window, which is the most common but also the cheapest style. In the cost estimation framework, '
'we can inflate prices for different finishes, to be conservative on price.'}
]

View file

@ -1,6 +1,7 @@
from recommendations.WindowsRecommendations import WindowsRecommendations
from backend.Property import Property
from unittest.mock import Mock
from recommendations.tests.test_data.materials import materials
class TestWindowRecommendations:
@ -25,6 +26,10 @@ class TestWindowRecommendations:
'glazing_type': 'single',
'no_data': False
}
property_1.number_of_windows = 5
property_1.number_of_windows = 7
recommender = WindowsRecommendations(property_instance=property_1, materials=[])
recommender = WindowsRecommendations(property_instance=property_1, materials=materials)
assert not recommender.recommendation
recommender.recommend()