Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add an 'equity' method to ALIncome class #247

Merged
merged 5 commits into from
Jan 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 100 additions & 11 deletions docassemble/ALToolbox/al_income.py
Original file line number Diff line number Diff line change
Expand Up @@ -588,23 +588,55 @@ class ALAsset(ALIncome):

def total(self, times_per_year: float = 1) -> Decimal:
"""
Returns the .value attribute divided by the times per year you want to
calculate. The value defaults to 0.
Returns the .value attribute divided by the times per year you want to calculate. The value defaults to 0.

`times_per_year` is some denominator of a year. E.g, to express a weekly
period, use 52. The default is 1 (a year).
`times_per_year` is some denominator of a year. E.g, to express a weekly period, use 52. The default is 1 (a year).

Args:
times_per_year (float, optional): The number of times per year to calculate. Defaults to 1.

Returns:
Decimal: The .value attribute divided by the times per year.
"""
if not hasattr(self, "value") or self.value == "":
return Decimal(0)
else:
return super(ALAsset, self).total(times_per_year=times_per_year)

def equity(self, loan_attribute="balance") -> Decimal:
"""
Returns the total equity in the asset (e.g., market value minus balance).

Args:
loan_attribute (str, optional): The attribute of the asset to use as the loan value. Defaults to "balance".

Returns:
Decimal: The total equity in the asset.
"""
if getattr(self, loan_attribute, None) is None:
return Decimal(self.market_value)
return Decimal(self.market_value) - Decimal(getattr(self, loan_attribute))


class ALAssetList(ALIncomeList):
"""
A list of ALAssets. The `total()` of the list will be the total income
earned, which may not be what you want for a list of assets. To get the
total value of all assets, use the `market_value()` method.

Attributes:
market_value (float | Decimal): Market value of the asset.
balance (float | Decimal): Current balance of the account, e.g., like
the balance in a checking account, but could also represent a loan
amount.
value (float | Decimal, optional): Represents the income the asset earns
for a given `times_per_year`, such as interest earned in a checking
account. If not defined, the income will be set to 0, to simplify
representing the many common assets that do not earn any income.
times_per_year (float, optional): Number of times per year the asset
earns the income listed in the `value` attribute.
owner (str, optional): Full name of the asset owner as a single string.
source (str, optional): The "source" of the asset, like "vase".
"""

def init(self, *pargs, **kwargs):
Expand All @@ -617,8 +649,18 @@ def market_value(
exclude_source: Optional[SourceType] = None,
) -> Decimal:
"""
Returns the total `.market_value` of assets in the list. You can filter
the assets by `source`. `source` can be a string or a list.
Returns the total `.market_value` of assets in the list.

You can filter the assets by `source`. `source` can be a string or a list.

Args:
source (Optional[SourceType]): The source of the assets to include in the calculation.
If None, all sources are included. Can be a string or a list.
exclude_source (Optional[SourceType]): The source of the assets to exclude from the calculation.
If None, no sources are excluded.

Returns:
Decimal: The total market value of the assets.
"""
result = Decimal(0)
satisfies_sources = _source_to_callable(source, exclude_source)
Expand All @@ -635,11 +677,18 @@ def balance(
exclude_source: Optional[SourceType] = None,
) -> Decimal:
"""
Returns the total `.balance` of assets in the list,
which typically corresponds to the available funds
in a financial account.
Returns the total `.balance` of assets in the list, which typically corresponds to the available funds in a financial account.

You can filter the assets by `source`. `source` can be a string or a list.

Args:
source (Optional[SourceType]): The source of the assets to include in the calculation.
If None, all sources are included. Can be a string or a list.
exclude_source (Optional[SourceType]): The source of the assets to exclude from the calculation.
If None, no sources are excluded.

Returns:
Decimal: The total balance of the assets.
"""
self._trigger_gather()
result = Decimal(0)
Expand All @@ -651,14 +700,54 @@ def balance(
result += _currency_float_to_decimal(asset.balance)
return result

def equity(
self,
source: Optional[SourceType] = None,
exclude_source: Optional[SourceType] = None,
loan_attribute: str = "balance",
) -> Decimal:
"""
Calculates and returns the total equity in the assets.

This method triggers the gathering of assets, then iterates over each asset. If a source or exclude_source is not
specified, or if the asset's source satisfies the source criteria, the equity of the asset is added to the total.

Args:
source (Optional[SourceType]): The source of the assets to include in the calculation. If None, all sources are included.
exclude_source (Optional[SourceType]): The source of the assets to exclude from the calculation. If None, no sources are excluded.
loan_attribute (str, optional): The attribute of the asset to use as the loan value. Defaults to "balance".

Returns:
Decimal: The total equity in the assets.
"""
self._trigger_gather()
result = Decimal(0)
satisfies_sources = _source_to_callable(source, exclude_source)
for asset in self.elements:
if (source is None and exclude_source is None) or (
satisfies_sources(asset.source)
):
result += asset.equity(loan_attribute=loan_attribute)
return result

def owners(
self,
source: Optional[SourceType] = None,
exclude_source: Optional[SourceType] = None,
) -> Set[str]:
"""
Returns a set of the unique owners of the assets. You can filter the
assets by `source`. `source` can be a string or a list.
Returns a set of the unique owners of the assets.

You can filter the assets by `source`. `source` can be a string or a list.

Args:
source (Optional[SourceType]): The source of the assets to include in the calculation.
If None, all sources are included. Can be a string or a list.
exclude_source (Optional[SourceType]): The source of the assets to exclude from the calculation.
If None, no sources are excluded.

Returns:
Set[str]: A set of the unique owners of the assets.
"""
owners = set()
if source is None and exclude_source is None:
Expand Down
10 changes: 8 additions & 2 deletions docassemble/ALToolbox/data/questions/al_income.yml
Original file line number Diff line number Diff line change
Expand Up @@ -846,15 +846,21 @@ fields:
asset_terms_ordered
exclude: |
['vehicle']
- How often do you get this income?: x.times_per_year
- Does this asset earn any income (like rent or interest)?: x.has_income
datatype: yesnoradio
- How often do you get the income?: x.times_per_year
input type: radio
code: |
times_per_year_list
datatype: integer
- Interest or other income from the asset: x.value
- Amount of income: x.value
datatype: currency
- Who owns this?: x.owner
required: False
validation code: |
if not x.has_income:
x.times_per_year = 1
x.value = 0
---
# UNIQUE FOR ALAssetList
---
Expand Down