reports: Balance has built-in tolerance for zero comparisons.
This commit is contained in:
		
							parent
							
								
									f8f57428aa
								
							
						
					
					
						commit
						4cba2b2681
					
				
					 2 changed files with 26 additions and 8 deletions
				
			
		| 
						 | 
					@ -80,10 +80,17 @@ class Balance(Mapping[str, data.Amount]):
 | 
				
			||||||
    Each key is a Beancount currency string, and each value represents the
 | 
					    Each key is a Beancount currency string, and each value represents the
 | 
				
			||||||
    balance in that currency.
 | 
					    balance in that currency.
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    __slots__ = ('_currency_map',)
 | 
					    __slots__ = ('_currency_map', 'tolerance')
 | 
				
			||||||
 | 
					    TOLERANCE = Decimal('0.01')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __init__(self, source: Iterable[data.Amount]=()) -> None:
 | 
					    def __init__(self,
 | 
				
			||||||
 | 
					                 source: Iterable[data.Amount]=(),
 | 
				
			||||||
 | 
					                 tolerance: Optional[Decimal]=None,
 | 
				
			||||||
 | 
					    ) -> None:
 | 
				
			||||||
 | 
					        if tolerance is None:
 | 
				
			||||||
 | 
					            tolerance = self.TOLERANCE
 | 
				
			||||||
        self._currency_map = {amount.currency: amount for amount in source}
 | 
					        self._currency_map = {amount.currency: amount for amount in source}
 | 
				
			||||||
 | 
					        self.tolerance = tolerance
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _add_amount(self,
 | 
					    def _add_amount(self,
 | 
				
			||||||
                    currency_map: MutableMapping[str, data.Amount],
 | 
					                    currency_map: MutableMapping[str, data.Amount],
 | 
				
			||||||
| 
						 | 
					@ -147,19 +154,24 @@ class Balance(Mapping[str, data.Amount]):
 | 
				
			||||||
    ) -> bool:
 | 
					    ) -> bool:
 | 
				
			||||||
        return all(op_func(amt.number, operand) for amt in self.values())
 | 
					        return all(op_func(amt.number, operand) for amt in self.values())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @staticmethod
 | 
				
			||||||
 | 
					    def within_tolerance(dec: DecimalCompat, tolerance: DecimalCompat) -> bool:
 | 
				
			||||||
 | 
					        dec = cast(Decimal, dec)
 | 
				
			||||||
 | 
					        return abs(dec) < tolerance
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def eq_zero(self) -> bool:
 | 
					    def eq_zero(self) -> bool:
 | 
				
			||||||
        """Returns true if all amounts in the balance == 0."""
 | 
					        """Returns true if all amounts in the balance == 0, within tolerance."""
 | 
				
			||||||
        return self._all_amounts(operator.eq, 0)
 | 
					        return self._all_amounts(self.within_tolerance, self.tolerance)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    is_zero = eq_zero
 | 
					    is_zero = eq_zero
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def ge_zero(self) -> bool:
 | 
					    def ge_zero(self) -> bool:
 | 
				
			||||||
        """Returns true if all amounts in the balance >= 0."""
 | 
					        """Returns true if all amounts in the balance >= 0, within tolerance."""
 | 
				
			||||||
        return self._all_amounts(operator.ge, 0)
 | 
					        return self._all_amounts(operator.ge, -self.tolerance)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def le_zero(self) -> bool:
 | 
					    def le_zero(self) -> bool:
 | 
				
			||||||
        """Returns true if all amounts in the balance <= 0."""
 | 
					        """Returns true if all amounts in the balance <= 0, within tolerance."""
 | 
				
			||||||
        return self._all_amounts(operator.le, 0)
 | 
					        return self._all_amounts(operator.le, self.tolerance)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def format(self,
 | 
					    def format(self,
 | 
				
			||||||
               fmt: Optional[str]='#,#00.00 ¤¤',
 | 
					               fmt: Optional[str]='#,#00.00 ¤¤',
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -92,6 +92,8 @@ def test_mixed_balance():
 | 
				
			||||||
    ({'JPY': 10}, False),
 | 
					    ({'JPY': 10}, False),
 | 
				
			||||||
    ({'JPY': 10, 'BRL': 0}, False),
 | 
					    ({'JPY': 10, 'BRL': 0}, False),
 | 
				
			||||||
    ({'JPY': 10, 'BRL': 20}, False),
 | 
					    ({'JPY': 10, 'BRL': 20}, False),
 | 
				
			||||||
 | 
					    ({'USD': '0.00015'}, True),
 | 
				
			||||||
 | 
					    ({'EUR': '-0.00052'}, True),
 | 
				
			||||||
])
 | 
					])
 | 
				
			||||||
def test_eq_zero(mapping, expected):
 | 
					def test_eq_zero(mapping, expected):
 | 
				
			||||||
    balance = core.Balance(amounts_from_map(mapping))
 | 
					    balance = core.Balance(amounts_from_map(mapping))
 | 
				
			||||||
| 
						 | 
					@ -108,6 +110,8 @@ def test_eq_zero(mapping, expected):
 | 
				
			||||||
    ({'JPY': 10}, True),
 | 
					    ({'JPY': 10}, True),
 | 
				
			||||||
    ({'JPY': 10, 'BRL': 0}, True),
 | 
					    ({'JPY': 10, 'BRL': 0}, True),
 | 
				
			||||||
    ({'JPY': 10, 'BRL': 20}, True),
 | 
					    ({'JPY': 10, 'BRL': 20}, True),
 | 
				
			||||||
 | 
					    ({'USD': '0.00015'}, True),
 | 
				
			||||||
 | 
					    ({'EUR': '-0.00052'}, True),
 | 
				
			||||||
])
 | 
					])
 | 
				
			||||||
def test_ge_zero(mapping, expected):
 | 
					def test_ge_zero(mapping, expected):
 | 
				
			||||||
    balance = core.Balance(amounts_from_map(mapping))
 | 
					    balance = core.Balance(amounts_from_map(mapping))
 | 
				
			||||||
| 
						 | 
					@ -123,6 +127,8 @@ def test_ge_zero(mapping, expected):
 | 
				
			||||||
    ({'JPY': 10}, False),
 | 
					    ({'JPY': 10}, False),
 | 
				
			||||||
    ({'JPY': 10, 'BRL': 0}, False),
 | 
					    ({'JPY': 10, 'BRL': 0}, False),
 | 
				
			||||||
    ({'JPY': 10, 'BRL': 20}, False),
 | 
					    ({'JPY': 10, 'BRL': 20}, False),
 | 
				
			||||||
 | 
					    ({'USD': '0.00015'}, True),
 | 
				
			||||||
 | 
					    ({'EUR': '-0.00052'}, True),
 | 
				
			||||||
])
 | 
					])
 | 
				
			||||||
def test_le_zero(mapping, expected):
 | 
					def test_le_zero(mapping, expected):
 | 
				
			||||||
    balance = core.Balance(amounts_from_map(mapping))
 | 
					    balance = core.Balance(amounts_from_map(mapping))
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		
		Reference in a new issue