66 from report .nk .contract import NkContract
77 from report .nk .generator import NkReportGenerator
88 from report .nk .rental_unit import NkRentalUnit
9+ from report .nk .section import NkSection
910
1011
1112class NkCostValueType (Enum ):
@@ -50,6 +51,7 @@ def __init__(self, report_generator: "NkReportGenerator", cost_config: dict):
5051 self .rental_unit_values : dict [int , dict [NkCostValueType , NkCostValue ]] = {}
5152 self .section_weights = cost_config .get ("section_weights" , "default" )
5253 self .add_value_type (NkCostValueType .COST , "Kosten" , "CHF" )
54+ self .add_value_type (NkCostValueType .WEIGHT , "Gewichtung" , "" )
5355 self .warnings = []
5456
5557 def add_value_type (self , kind : NkCostValueType , name : str , unit : str ):
@@ -95,7 +97,7 @@ def _normalize_monthly_amounts_for_dict(self, container: dict, value_required=Fa
9597 if value .amount :
9698 if total :
9799 # Annual and monthly values are given, check consistency
98- if total != value .amount :
100+ if abs ( total - value .amount ) > 0.00001 :
99101 raise ValueError (
100102 f"Inkonsistente Angaben für Totalbetrag { value .amount } und "
101103 f"Summe der Monatswerte { total } für { value .name } /{ _kind } "
@@ -177,9 +179,9 @@ def _calculate_weights(self):
177179 def _calculate_weights_for_type (
178180 self , value_type : NkCostValueType , rental_unit_weights_function_name : str
179181 ):
180- self .add_value_type (value_type , "Gewichtung" , "" )
182+ self ._zero_values (value_type )
181183 monthly_weights = self .get_monthly_weights ()
182- section_weights = self .get_section_weights ()
184+ section_weights = self .get_section_weights (value_type )
183185 total = self .total_values [value_type ]
184186 rental_unit_weights_function = getattr (self , rental_unit_weights_function_name )
185187 if not callable (rental_unit_weights_function ):
@@ -202,6 +204,20 @@ def _calculate_weights_for_type(
202204 )
203205 total .amount = sum (total .monthly_amounts )
204206
207+ def _zero_values (self , value_type : NkCostValueType ):
208+ for ru in self .generator .rental_units :
209+ self .rental_unit_values [ru .id ][value_type ].amount = 0
210+ self .rental_unit_values [ru .id ][value_type ].monthly_amounts = (
211+ self .generator .num_months * [0 ]
212+ )
213+ for section in self .generator .sections :
214+ self .section_values [section .id ][value_type ].amount = 0
215+ self .section_values [section .id ][value_type ].monthly_amounts = (
216+ self .generator .num_months * [0 ]
217+ )
218+ self .total_values [value_type ].amount = 0
219+ self .total_values [value_type ].monthly_amounts = self .generator .num_months * [0 ]
220+
205221 def _aggregate_monthly_amounts (self , value_type : NkCostValueType = NkCostValueType .COST ):
206222 """Aggregate pre-calculated monthly per-rental-unit costs up to sections and total."""
207223 for ru in self .generator .rental_units :
@@ -220,13 +236,10 @@ def get_monthly_weights(self):
220236 """Default with equal weights for all months."""
221237 return self .generator .num_months * [1.0 ]
222238
223- def get_section_weights (self ) :
239+ def get_section_weights (self , value_type : NkCostValueType ) -> dict [ int , float ] :
224240 """Return weights per section, using the configured section_weights profile if available."""
225- weight_profile = (
226- self .generator .section_weights .get (self .section_weights )
227- if self .section_weights
228- else None
229- )
241+
242+ weight_profile = self ._get_weight_profile (value_type )
230243 weights = {}
231244 for section in self .generator .sections :
232245 if weight_profile is not None :
@@ -235,6 +248,11 @@ def get_section_weights(self):
235248 weights [section .id ] = 1.0
236249 return weights
237250
251+ def _get_weight_profile (self , value_type : NkCostValueType ):
252+ if self .section_weights :
253+ return self .generator .section_weights .get (self .section_weights )
254+ return None
255+
238256 def get_rental_unit_weights (self , ru ):
239257 """Default with equal weights for all rental units."""
240258 if ru .is_virtual :
@@ -308,9 +326,24 @@ def _get_assigned_sum(
308326 ret += amount
309327 return ret
310328
311- def get_building_amount (self , value_type : NkCostValueType ):
329+ def get_building_cost (self ):
330+ return self ._get_building_amount (NkCostValueType .COST )
331+
332+ def _get_building_amount (self , value_type : NkCostValueType ):
312333 return self .total_values [value_type ].amount
313334
335+ def get_section_cost (self , section ):
336+ return self ._get_section_amount (section , NkCostValueType .COST )
337+
338+ def _get_section_amount (self , section : "NkSection" , value_type : NkCostValueType ):
339+ return self .section_values [section .id ][value_type ].amount
340+
341+ def get_rental_unit_cost (self , rental_unit ):
342+ return self ._get_rental_unit_amount (rental_unit , NkCostValueType .COST )
343+
344+ def _get_rental_unit_amount (self , rental_unit : "NkRentalUnit" , value_type : NkCostValueType ):
345+ return self .rental_unit_values [rental_unit .id ][value_type ].amount
346+
314347 def _get_context (self , ru : "NkRentalUnit" , contract : "NkContract" ) -> dict :
315348 """Return extra context variables for ODT template rendering. Override in subclasses."""
316349 return {}
@@ -351,7 +384,11 @@ class NkCommonCostMixin:
351384
352385 def __init__ (self , report_generator : "NkReportGenerator" , cost_config : dict ):
353386 super ().__init__ (report_generator , cost_config )
387+ self .common_cost_section_weights = cost_config .get (
388+ "common_cost_section_weights" , "default"
389+ )
354390 self .add_value_type (NkCostValueType .COMMON_COST , "Allgemeinkosten" , "CHF" )
391+ self .add_value_type (NkCostValueType .COMMON_WEIGHT , "Gewichtung" , "" )
355392
356393 def get_assigned_cost (self , contract : "NkContract" , rental_unit : "NkRentalUnit | None" = None ):
357394 ret = super ().get_assigned_cost (contract , rental_unit )
@@ -374,6 +411,7 @@ def set_common_costs(self, cost: float | list[float], usage: float | list[float]
374411 self .total_values [NkCostValueType .COMMON_USAGE ].monthly_amounts = usage
375412 else :
376413 self .total_values [NkCostValueType .COMMON_USAGE ].amount = usage
414+ self .normalize_monthly_amounts ()
377415
378416 def _split_common_costs (self ):
379417 self ._calculate_common_weights ()
@@ -384,3 +422,15 @@ def _calculate_common_weights(self):
384422 self ._calculate_weights_for_type (
385423 NkCostValueType .COMMON_WEIGHT , "get_rental_unit_common_weights"
386424 )
425+
426+ def _get_weight_profile (self , value_type : NkCostValueType ):
427+ if value_type in (
428+ NkCostValueType .COMMON_COST ,
429+ NkCostValueType .COMMON_USAGE ,
430+ NkCostValueType .COMMON_WEIGHT ,
431+ ):
432+ if self .common_cost_section_weights :
433+ return self .generator .section_weights .get (self .common_cost_section_weights )
434+ else :
435+ return None
436+ return super ()._get_weight_profile (value_type )
0 commit comments