33"""
44
55import datetime
6+ import math
67import numbers
78
89from django .conf import settings
@@ -1356,7 +1357,7 @@ def validate_quantity_consumed(self):
13561357 )
13571358
13581359 # Check if the actual quantity_consumed matches the expected quantity_consumed
1359- if self .quantity_consumed and round (self .quantity_consumed , 6 ) != round ( expected_quantity_consumed , 6 ):
1360+ if self .quantity_consumed and not math . isclose (self .quantity_consumed , expected_quantity_consumed ):
13601361 if quantity_butter_production :
13611362 message = "Quantity consumed for Milk Production must be quantity produced + quantity purchased - quantity sold - quantity used for butter production - quantity used for other things" # NOQA: E501
13621363 else :
@@ -1365,7 +1366,7 @@ def validate_quantity_consumed(self):
13651366
13661367 def validate_income (self ):
13671368 if self .income and self .quantity_sold is not None and self .price is not None :
1368- if self .income != self .quantity_sold * self .price :
1369+ if not math . isclose ( self .income , self .quantity_sold * self .price ) :
13691370 raise ValidationError (_ ("Income for a Livelihood Activity must be quantity sold multiplied by price" ))
13701371
13711372 def validate_expenditure (self ):
@@ -1384,7 +1385,7 @@ def validate_expenditure(self):
13841385 price = self .price or 0
13851386 expenditure = self .expenditure or 0
13861387
1387- if self .expenditure and expenditure != quantity_produced * price :
1388+ if self .expenditure and not math . isclose ( expenditure , quantity_produced * price ) :
13881389 raise ValidationError (
13891390 _ ("Expenditure for a Livelihood Activity must be quantity produced multiplied by price" )
13901391 )
@@ -1407,9 +1408,9 @@ def validate_kcals_consumed(self):
14071408 "product" : self .livelihood_strategy .product ,
14081409 }
14091410 )
1410- if (
1411- self .kcals_consumed
1412- != self .quantity_consumed * conversion_factor * self .livelihood_strategy .product .kcals_per_unit
1411+ if not math . isclose (
1412+ self .kcals_consumed ,
1413+ self .quantity_consumed * conversion_factor * self .livelihood_strategy .product .kcals_per_unit ,
14131414 ):
14141415 raise ValidationError (
14151416 _ (
@@ -1635,7 +1636,7 @@ class MilkType(models.TextChoices):
16351636 # https://unstats.un.org/unsd/classifications/Econ/Detail/EN/1074/22110. That LivelihoodActivity would have a
16361637 # `quantity_produced` equal to the amount of whole milk that was used for ButterProduction, and then the
16371638 # quantity_sold and the quantity_consumed could be tracked separately.
1638- quantity_butter_production = models .PositiveIntegerField (
1639+ quantity_butter_production = models .FloatField (
16391640 blank = True , null = True , verbose_name = _ ("Quantity used for Butter Production" )
16401641 ) # NOQA: E501
16411642 type_of_milk_consumed = models .CharField (
@@ -1734,7 +1735,7 @@ def clean(self):
17341735
17351736 def validate_quantity_produced (self ):
17361737 if self .quantity_produced is not None and self .animals_slaughtered and self .carcass_weight is not None :
1737- if self .quantity_produced != self .animals_slaughtered * self .carcass_weight :
1738+ if not math . isclose ( self .quantity_produced , self .animals_slaughtered * self .carcass_weight ) :
17381739 raise ValidationError (
17391740 _ (
17401741 "Quantity Produced for a Meat Production must be animals slaughtered multiplied by carcass weight"
@@ -1787,7 +1788,7 @@ class FoodPurchase(LivelihoodActivity):
17871788 # NIO93 Row B422 tia = 2.5kg
17881789
17891790 # unit_multiple has names like wt_of_measure in the BSS.
1790- unit_multiple = models .PositiveSmallIntegerField (
1791+ unit_multiple = models .FloatField (
17911792 blank = True ,
17921793 null = True ,
17931794 verbose_name = _ ("Unit Multiple" ),
@@ -1816,7 +1817,9 @@ def validate_quantity_purchased(self):
18161817 and self .times_per_month is not None
18171818 and self .months_per_year is not None
18181819 ):
1819- if self .quantity_purchased != self .unit_multiple * self .times_per_month * self .months_per_year :
1820+ if not math .isclose (
1821+ self .quantity_purchased , self .unit_multiple * self .times_per_month * self .months_per_year
1822+ ):
18201823 raise ValidationError (
18211824 _ (
18221825 "Quantity purchased for a Food Purchase must be purchase amount * purchases per month * months per year" # NOQA: E501
@@ -1828,7 +1831,7 @@ def validate_expenditure(self):
18281831 price = self .price or 0
18291832 expenditure = self .expenditure or 0
18301833
1831- if self .expenditure and expenditure != quantity_purchased * price :
1834+ if self .expenditure and not math . isclose ( expenditure , quantity_purchased * price ) :
18321835 raise ValidationError (_ ("Expenditure for a Food Purchase must be quantity purchased multiplied by price" ))
18331836
18341837 class Meta :
@@ -1910,9 +1913,9 @@ def validate_quantity_produced(self):
19101913 and self .times_per_month is not None
19111914 and self .months_per_year is not None
19121915 ):
1913- if (
1914- self .quantity_produced
1915- != self .payment_per_time * self .people_per_household * self .times_per_month * self .months_per_year
1916+ if not math . isclose (
1917+ self .quantity_produced ,
1918+ self .payment_per_time * self .people_per_household * self .times_per_month * self .months_per_year ,
19161919 ):
19171920 raise ValidationError (
19181921 _ (
@@ -1935,7 +1938,7 @@ class ReliefGiftOther(LivelihoodActivity):
19351938
19361939 # Production calculation /validation is `unit_of_measure * unit_multiple * times_per_year`
19371940 # Also used for the number of children receiving school meals.
1938- unit_multiple = models .PositiveSmallIntegerField (
1941+ unit_multiple = models .FloatField (
19391942 blank = True ,
19401943 null = True ,
19411944 verbose_name = _ ("Unit Multiple" ),
@@ -1961,7 +1964,7 @@ class ReliefGiftOther(LivelihoodActivity):
19611964
19621965 def validate_quantity_produced (self ):
19631966 if self .quantity_produced is not None and self .unit_multiple is not None and self .times_per_year is not None :
1964- if self .quantity_produced != self .unit_multiple * self .times_per_year :
1967+ if not math . isclose ( self .quantity_produced , self .unit_multiple * self .times_per_year ) :
19651968 raise ValidationError (
19661969 _ ("Quantity produced for Relief, Gifts, Other must be amount received * times per year" )
19671970 )
@@ -2070,16 +2073,17 @@ def validate_income(self):
20702073 and self .times_per_month is not None
20712074 and self .months_per_year is not None
20722075 ):
2073- if round (self .income , 6 ) != round (
2074- self .payment_per_time * self .people_per_household * self .times_per_month * self .months_per_year , 6
2076+ if not math .isclose (
2077+ self .income ,
2078+ self .payment_per_time * self .people_per_household * self .times_per_month * self .months_per_year ,
20752079 ):
20762080 raise ValidationError (
20772081 _ (
20782082 "Quantity produced for Other Cash Income must be payment per time * number of people * labor per month * months per year" # NOQA: E501
20792083 )
20802084 )
20812085 elif self .income is not None and self .payment_per_time is not None and self .times_per_year is not None :
2082- if round (self .income , 6 ) != round ( self .payment_per_time * self .times_per_year , 6 ):
2086+ if not math . isclose (self .income , self .payment_per_time * self .times_per_year ):
20832087 raise ValidationError (_ ("Income for 'Other Cash Income' must be payment per time * times per year" ))
20842088
20852089 def calculate_fields (self ):
@@ -2103,7 +2107,7 @@ class OtherPurchase(LivelihoodActivity):
21032107 # individual fields must be nullable
21042108 # Do we need this, or can we use combined units of measure like FDW, e.g. 5kg
21052109 # NIO93 Row B422 tia = 2.5kg
2106- unit_multiple = models .PositiveSmallIntegerField (
2110+ unit_multiple = models .FloatField (
21072111 blank = True ,
21082112 null = True ,
21092113 verbose_name = _ ("Unit Multiple" ),
@@ -2129,7 +2133,7 @@ def validate_expenditure(self):
21292133 errors = []
21302134 if self .times_per_month is not None and self .months_per_year is not None :
21312135 expected_times_per_year = self .times_per_month * self .months_per_year
2132- if self .times_per_year is not None and self .times_per_year != expected_times_per_year :
2136+ if self .times_per_year is not None and not math . isclose ( self .times_per_year , expected_times_per_year ) :
21332137 errors .append (
21342138 _ (
21352139 "Times per year must be times per month * months per year. Expected: %(expected)s, Found: %(found)s"
@@ -2141,7 +2145,7 @@ def validate_expenditure(self):
21412145 )
21422146 if self .price is not None and self .unit_multiple is not None and self .times_per_year is not None :
21432147 expected_expenditure = self .price * self .unit_multiple * self .times_per_year
2144- if self .expenditure is not None and self .expenditure != expected_expenditure :
2148+ if self .expenditure is not None and not math . isclose ( self .expenditure , expected_expenditure ) :
21452149 errors .append (
21462150 _ (
21472151 "Expenditure for Other Purchases must be price * unit multiple * purchases per year. Expected: %(expected)s, Found: %(found)s"
0 commit comments