Source code for Acquire.Accounting._debitnote


__all__ = ["DebitNote"]


[docs]class DebitNote: """This class holds all of the information about a completed debit. This is combined with credit note of equal value to form a transaction record """ def __init__(self, transaction=None, account=None, authorisation=None, is_provisional=False, receipt_by=None, receipt=None, refund=None, bucket=None): """Create a debit note for the passed transaction will debit value from the passed account. The note will create a unique ID (uid) for the debit, plus the datetime of the time that value was drawn from the debited account. This debit note will be paired with a corresponding credit note from the account that received the value from the transaction so that a balanced TransactionRecord can be written to the ledger. If the note is provisional, then the value of the transaction will be held until the corresponding CreditNote has been receipted. This must be receipted before 'receipt_by', else the value will be returned to the DebitNote account (it will be automatically refunded) """ self._transaction = None nargs = (transaction is not None) + (refund is not None) + \ (receipt is not None) if nargs > 1: raise ValueError("You can only choose to create a debit note " "from a transaction, receipt or refund!") if refund is not None: self._create_from_refund(refund, account, bucket) elif receipt is not None: self._create_from_receipt(receipt, account, bucket) elif (transaction is not None): if account is None: raise ValueError("You need to supply the account from " "which the transaction will be taken") self._create_from_transaction(transaction, account, authorisation, is_provisional, receipt_by, bucket) def __str__(self): if self.is_null(): return "DebitNote::null" else: return "DebitNote:%s>>%s" % (self.account_uid(), self.value()) def __eq__(self, other): if isinstance(other, self.__class__): return self._uid == other._uid else: return False def __ne__(self, other): return not self.__eq__(other)
[docs] def is_null(self): """Return whether or not this is a null note""" return self._transaction is None
[docs] def uid(self): """Return the UID for this note. This has the format dd:mm:yyyy/unique_string """ if self.is_null(): return None else: return self._uid
[docs] def datetime(self): """Return the datetime for when value was debited from the account""" if self.is_null(): return None else: return self._datetime
[docs] def account_uid(self): """Return the UID of the account that was debited""" if self.is_null(): return None else: return self._account_uid
[docs] def transaction(self): """Return the transaction related to this debit note""" if self.is_null(): return None else: return self._transaction
[docs] def value(self): """Return the value of this note""" if self.is_null(): return 0 else: return self.transaction().value()
[docs] def authorisation(self): """Return the authorisation that was used successfully to withdraw value from the debited account """ if self.is_null(): return None else: return self._authorisation
[docs] def is_provisional(self): """Return whether or not the debit was provisional. Provisional debits are listed as liabilities """ if self.is_null(): return False else: return self._is_provisional
[docs] def needs_receipting(self): """Return whether or not this DebitNote transaction needs receipting - if it does, then it must be receipted by the CreditNote before DebitNote.receipt_by(). If this does """ return self.is_provisional()
[docs] def receipt_by(self): """Return the datetime by which this DebitNote must be receipted via the CreditNote, else the transaction will be automatically refunded. This will return 'None' if the transaction has already been receipted or it wasn't provisional """ if self.is_provisional(): return self._receipt_by else: return None
def _create_from_refund(self, refund, account, bucket): """Function used to construct a debit note by extracting the value specified in the passed refund from the specified account. This is authorised using the authorisation held in the refund. Note that the refund must match up with a prior existing provisional transaction, and this must not have already been refunded. This will actually take value out of the passed account, with that value residing in this debit note until it is credited to another account """ from Acquire.Accounting import Refund as _Refund if not isinstance(refund, _Refund): raise TypeError("You can only create a DebitNote with a " "Refund") if refund.is_null(): return if bucket is None: from Acquire.Service import get_service_account_bucket \ as _get_service_account_bucket bucket = _get_service_account_bucket() from Acquire.Accounting import TransactionRecord as _TransactionRecord from Acquire.Accounting import TransactionState as _TransactionState from Acquire.Accounting import Account as _Account # get the transaction behind this refund and move it into # the "refunding" state transaction = _TransactionRecord.load_test_and_set( refund.transaction_uid(), _TransactionState.DIRECT, _TransactionState.REFUNDING, bucket=bucket) try: # ensure that the receipt matches the transaction... transaction.assert_matching_refund(refund) if account is None: account = _Account(transaction.credit_account_uid(), bucket) elif account.uid() != refund.credit_account_uid(): raise ValueError("The accounts do not match when debiting " "the refund: %s versus %s" % (account.uid(), refund.credit_account_uid())) # now move the refund from the credit account back to the # debit note (uid, datetime) = account._debit_refund(refund, bucket) self._transaction = refund.transaction() self._account_uid = refund.credit_account_uid() self._authorisation = refund.authorisation() self._is_provisional = False self._datetime = datetime self._uid = str(uid) except: # move the transaction back to its original state... _TransactionRecord.load_test_and_set( refund.transaction_uid(), _TransactionState.REFUNDING, _TransactionState.DIRECT) raise def _create_from_receipt(self, receipt, account, bucket): """Function used to construct a debit note by extracting the value specified in the passed receipt from the specified account. This is authorised using the authorisation held in the receipt, based on the original authorisation given in the provisional transaction. Note that the receipt must match up with a prior existing provisional transaction, and this must not have already been receipted or refunded. This will actually take value out of the passed account, with that value residing in this debit note until it is credited to another account """ from Acquire.Accounting import Receipt as _Receipt if not isinstance(receipt, _Receipt): raise TypeError("You can only create a DebitNote with a " "Receipt") if receipt.is_null(): return if bucket is None: from Acquire.Service import get_service_account_bucket \ as _get_service_account_bucket bucket = _get_service_account_bucket() from Acquire.Accounting import TransactionRecord as _TransactionRecord from Acquire.Accounting import TransactionState as _TransactionState from Acquire.Accounting import Account as _Account # get the transaction behind this receipt and move it into # the "receipting" state transaction = _TransactionRecord.load_test_and_set( receipt.transaction_uid(), _TransactionState.PROVISIONAL, _TransactionState.RECEIPTING, bucket=bucket) try: # ensure that the receipt matches the transaction... transaction.assert_matching_receipt(receipt) if account is None: account = _Account(transaction.debit_account_uid(), bucket) elif account.uid() != receipt.debit_account_uid(): raise ValueError("The accounts do not match when debiting " "the receipt: %s versus %s" % (account.uid(), receipt.debit_account_uid())) # now move value from liability to debit, and then into this # debit note (uid, datetime) = account._debit_receipt(receipt, bucket) self._transaction = receipt.transaction() self._account_uid = receipt.debit_account_uid() self._authorisation = receipt.authorisation() self._is_provisional = False self._datetime = datetime self._uid = str(uid) except: # move the transaction back to its original state... _TransactionRecord.load_test_and_set( receipt.transaction_uid(), _TransactionState.RECEIPTING, _TransactionState.PROVISIONAL) raise def _create_from_transaction(self, transaction, account, authorisation, is_provisional, receipt_by, bucket): """Function used to construct a debit note by extracting the specified transaction value from the passed account. This is authorised using the passed authorisation, and can be a provisional debit if 'is_provisional' is true. This will actually take value out of the passed account, with that value residing in this debit note until it is credited to another account """ from Acquire.Accounting import Transaction as _Transaction from Acquire.Accounting import Account as _Account if not isinstance(transaction, _Transaction): raise TypeError("You can only create a DebitNote with a " "Transaction") if not isinstance(account, _Account): raise TypeError("You can only create a DebitNote with a valid " "Account") if authorisation is not None: from Acquire.Identity import Authorisation as _Authorisation if not isinstance(authorisation, _Authorisation): raise TypeError("Authorisation must be of type Authorisation") self._transaction = transaction self._account_uid = account.uid() self._authorisation = authorisation self._is_provisional = is_provisional (uid, datetime, receipt_by) = account._debit( transaction, authorisation, is_provisional, receipt_by, bucket=bucket) from Acquire.ObjectStore import datetime_to_datetime \ as _datetime_to_datetime self._datetime = _datetime_to_datetime(datetime) self._uid = str(uid) if is_provisional: assert(receipt_by is not None) self._receipt_by = receipt_by else: assert(receipt_by is None)
[docs] def to_data(self): """Return this DebitNote as a dictionary that can be encoded as json""" data = {} if not self.is_null(): from Acquire.ObjectStore import datetime_to_string \ as _datetime_to_string data["transaction"] = self._transaction.to_data() data["account_uid"] = self._account_uid data["authorisation"] = self._authorisation.to_data() data["is_provisional"] = self._is_provisional data["datetime"] = _datetime_to_string(self._datetime) data["uid"] = self._uid if self._is_provisional: data["receipt_by"] = _datetime_to_string(self._receipt_by) return data
[docs] @staticmethod def from_data(data): """Return a DebitNote that has been extracted from the passed json-decoded dictionary """ d = DebitNote() if (data and len(data) > 0): from Acquire.Accounting import Transaction as _Transaction from Acquire.Identity import Authorisation as _Authorisation from Acquire.ObjectStore import string_to_datetime \ as _string_to_datetime d._transaction = _Transaction.from_data(data["transaction"]) d._account_uid = data["account_uid"] d._authorisation = _Authorisation.from_data(data["authorisation"]) d._is_provisional = data["is_provisional"] d._datetime = _string_to_datetime(data["datetime"]) d._uid = data["uid"] if d._is_provisional: d._receipt_by = _string_to_datetime(data["receipt_by"]) return d