Basic handling of multiple commodities
This commit is contained in:
parent
f76e0d3dcb
commit
5d13e5cbef
@ -20,7 +20,7 @@ from .. import AMOUNT_DPS
|
||||
from ..database import db_session
|
||||
from ..models import TrialBalancer
|
||||
from ..webapp import all_transactions, app
|
||||
from .models import BalanceAssertion, GeneralJournalPosting, GeneralJournalTransaction
|
||||
from .models import Amount, BalanceAssertion, GeneralJournalPosting, GeneralJournalTransaction
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
@ -40,15 +40,15 @@ def general_journal_new():
|
||||
postings=[]
|
||||
)
|
||||
|
||||
for account, sign, amount in zip(request.form.getlist('account'), request.form.getlist('sign'), request.form.getlist('amount')):
|
||||
quantity = round(float(amount) * (10**AMOUNT_DPS))
|
||||
for account, sign, amount_str in zip(request.form.getlist('account'), request.form.getlist('sign'), request.form.getlist('amount')):
|
||||
amount = Amount.parse(amount_str)
|
||||
if sign == 'cr':
|
||||
quantity = -quantity
|
||||
amount = -amount
|
||||
|
||||
posting = GeneralJournalPosting(
|
||||
account=account,
|
||||
quantity=quantity,
|
||||
commodity='$' # TODO: Commodities
|
||||
quantity=amount.quantity,
|
||||
commodity=amount.commodity
|
||||
)
|
||||
transaction.postings.append(posting)
|
||||
|
||||
|
@ -25,8 +25,7 @@ class Transaction:
|
||||
def assert_valid(self):
|
||||
"""Assert that debits equal credits, and commodities are compatible"""
|
||||
|
||||
if any(p.commodity != '$' for p in self.postings):
|
||||
# FIXME: Allow non-$ commodities
|
||||
if any(p.commodity != self.postings[0].commodity for p in self.postings[1:]):
|
||||
raise AssertionError('Transaction contains multiple commodities')
|
||||
|
||||
if sum(p.quantity for p in self.postings) != 0:
|
||||
@ -49,15 +48,51 @@ class Amount:
|
||||
self.quantity = quantity
|
||||
self.commodity = commodity
|
||||
|
||||
@classmethod
|
||||
def parse(self, amount_str):
|
||||
if ' ' not in amount_str:
|
||||
# Default commodity
|
||||
quantity = round(float(amount_str) * (10**AMOUNT_DPS))
|
||||
return Amount(quantity, '$') # TODO: Customisable default commodity
|
||||
|
||||
quantity_str = amount_str[:amount_str.index(' ')]
|
||||
quantity = round(float(quantity_str) * (10**AMOUNT_DPS))
|
||||
|
||||
commodity = amount_str[amount_str.index(' ')+1:]
|
||||
return Amount(quantity, commodity)
|
||||
|
||||
def __abs__(self):
|
||||
return Amount(abs(self.quantity), self.commodity)
|
||||
|
||||
def __neg__(self):
|
||||
return Amount(-self.quantity, self.commodity)
|
||||
|
||||
def format(self):
|
||||
if len(self.commodity) == 1:
|
||||
return '{0}{1:,.{dps}f}'.format(self.commodity, self.quantity / (10**AMOUNT_DPS), dps=AMOUNT_DPS)
|
||||
else:
|
||||
return '{1:,.{dps}f} {0}'.format(self.commodity, self.quantity / (10**AMOUNT_DPS), dps=AMOUNT_DPS)
|
||||
|
||||
def quantity_string(self):
|
||||
return '{:.{dps}f}'.format(self.quantity / (10**AMOUNT_DPS), dps=AMOUNT_DPS)
|
||||
|
||||
def as_cost(self):
|
||||
"""Convert commodity to reporting currency in cost basis"""
|
||||
|
||||
if self.commodity == '$':
|
||||
return self
|
||||
|
||||
# TODO: Refactor this
|
||||
|
||||
if '{{' in self.commodity:
|
||||
cost = float(self.commodity[self.commodity.index('{{')+2:self.commodity.index('}}')])
|
||||
return Amount(round(cost * (10**AMOUNT_DPS)), '$')
|
||||
elif '{' in self.commodity:
|
||||
cost = float(self.commodity[self.commodity.index('{')+1:self.commodity.index('}')])
|
||||
return Amount(round(cost * self.quantity), '$') # FIXME: Custom reporting currency
|
||||
else:
|
||||
raise Exception('No cost base for commodity {}'.format(self.commodity))
|
||||
|
||||
class TrialBalancer:
|
||||
"""
|
||||
Applies transactions to generate a trial balance
|
||||
@ -70,10 +105,10 @@ class TrialBalancer:
|
||||
for transaction in transactions:
|
||||
for posting in transaction.postings:
|
||||
if posting.account not in self.accounts:
|
||||
self.accounts[posting.account] = Amount(0, '$') # FIXME: Other commodities
|
||||
self.accounts[posting.account] = Amount(0, '$')
|
||||
|
||||
# FIXME: Handle commodities
|
||||
self.accounts[posting.account].quantity += posting.quantity
|
||||
# FIXME: Handle commodities better
|
||||
self.accounts[posting.account].quantity += posting.amount().as_cost().quantity
|
||||
|
||||
def transfer_balance(self, source_account, destination_account, description=None):
|
||||
"""Transfer the balance of the source account to the destination account"""
|
||||
|
@ -54,7 +54,7 @@
|
||||
<td>
|
||||
<div class="input-group">
|
||||
<div class="input-group-text">$</div>
|
||||
<input type="number" name="amount" step="0.01" value="" class="form-control">
|
||||
<input type="text" name="amount" value="" class="form-control">
|
||||
</div>
|
||||
<td></td>
|
||||
</tr>
|
||||
@ -74,7 +74,7 @@
|
||||
<td>
|
||||
<div class="input-group">
|
||||
<div class="input-group-text">$</div>
|
||||
<input type="number" name="amount" step="0.01" value="" class="form-control">
|
||||
<input type="text" name="amount" value="" class="form-control">
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -37,7 +37,7 @@
|
||||
{% for transaction in transactions %}
|
||||
{% if transaction.postings|length == 2 %}
|
||||
{% for posting in transaction.postings if posting.account == account %}
|
||||
{% set _ = running_total.__setattr__('quantity', running_total.quantity + posting.quantity) %}
|
||||
{% set _ = running_total.__setattr__('quantity', running_total.quantity + posting.amount().as_cost().quantity) %}
|
||||
<tr>
|
||||
<td>{{ transaction.dt.strftime('%Y-%m-%d') }}</td>
|
||||
<td>{{ transaction.description }}</td>
|
||||
@ -58,7 +58,7 @@
|
||||
<td></td>
|
||||
</tr>
|
||||
{% for posting in transaction.postings if posting.account == account %}
|
||||
{% set _ = running_total.__setattr__('quantity', running_total.quantity + posting.quantity) %}
|
||||
{% set _ = running_total.__setattr__('quantity', running_total.quantity + posting.amount().as_cost().quantity) %}
|
||||
<tr>
|
||||
<td></td>
|
||||
<td class="text-end"><i>{{ 'Dr' if posting.quantity >= 0 else 'Cr' }}</i></td>
|
||||
|
Loading…
Reference in New Issue
Block a user