def get_account(self, name, bucket=None): """Return the account called 'name' from this group Args: name (:obj:`str`): Name of account to retrieve bucket (:obj:`dict`): Bucket to load data from Returns: :obj:`Account`: Account object """ self._assert_is_readable() if bucket is None: from Acquire.Service import get_service_account_bucket \ as _get_service_account_bucket bucket = _get_service_account_bucket() try: from Acquire.ObjectStore import ObjectStore as _ObjectStore account_uid = _ObjectStore.get_string_object( bucket, self._account_key(name)) except: account_uid = None if account_uid is None: # ensure that the user always has a "main" account if name == "main": return self.create_account("main", "primary user account", overdraft_limit=0, bucket=bucket) from Acquire.Accounting import AccountError raise AccountError("There is no account called '%s' in the " "group '%s'" % (name, self.group())) from Acquire.Accounting import Account as _Account return _Account(uid=account_uid, bucket=bucket)
def refund(refund, bucket=None): """Create and record a new transaction from the passed refund. This applies the refund, thereby transferring value from the credit account to the debit account of the corresponding transaction. Note that you can only refund a transaction once! This returns the (already recorded) TransactionRecord for the refund """ from Acquire.Accounting import Refund as _Refund from Acquire.Accounting import Account as _Account from Acquire.Accounting import DebitNote as _DebitNote from Acquire.Accounting import CreditNote as _CreditNote from Acquire.Accounting import PairedNote as _PairedNote from Acquire.Accounting import TransactionRecord as _TransactionRecord if not isinstance(refund, _Refund): raise TypeError("The Refund must be of type Refund") if refund.is_null(): return _TransactionRecord() if bucket is None: from Acquire.Service import get_service_account_bucket \ as _get_service_account_bucket bucket = _get_service_account_bucket() # return value from the credit to debit accounts debit_account = _Account(uid=refund.debit_account_uid(), bucket=bucket) credit_account = _Account(uid=refund.credit_account_uid(), bucket=bucket) # remember that a refund debits from the original credit account... # (and can only refund completed (DIRECT) transactions) debit_note = _DebitNote(refund=refund, account=credit_account, bucket=bucket) # now create the credit note to return the value into the debit account try: credit_note = _CreditNote(debit_note=debit_note, refund=refund, account=debit_account, bucket=bucket) except Exception as e: # delete the debit note try: debit_account._delete_note(debit_note, bucket=bucket) except: pass # reset the transaction to its original state try: _TransactionRecord.load_test_and_set( refund.transaction_uid(), _TransactionState.REFUNDING, _TransactionState.DIRECT, bucket=bucket) except: pass raise e try: paired_notes = _PairedNote.create(debit_note, credit_note) except Exception as e: # delete all records...! try: debit_account._delete_note(debit_note, bucket=bucket) except: pass try: credit_account._delete_note(credit_note, bucket=bucket) except: pass # reset the transaction to the pending state try: _TransactionRecord.load_test_and_set( refund.transaction_uid(), _TransactionState.REFUNDING, _TransactionState.DIRECT, bucket=bucket) except: pass raise e # now record the two entries to the ledger. The below function # is guaranteed not to raise an exception return Ledger._record_to_ledger(paired_notes, refund=refund, bucket=bucket)
def receipt(receipt, bucket=None): """Create and record a new transaction from the passed receipt. This applies the receipt, thereby actually transferring value from the debit account to the credit account of the corresponding transaction. Note that you can only receipt a transaction once! This returns the (already recorded) TransactionRecord for the receipt Args: receipt (Receipt): Receipt to use for transaction bucket (default=None): Bucket to load data from Returns: list: List of TransactionRecords """ from Acquire.Accounting import Receipt as _Receipt from Acquire.Accounting import Account as _Account from Acquire.Accounting import DebitNote as _DebitNote from Acquire.Accounting import CreditNote as _CreditNote from Acquire.Accounting import TransactionRecord as _TransactionRecord from Acquire.Accounting import PairedNote as _PairedNote if not isinstance(receipt, _Receipt): raise TypeError("The Receipt must be of type Receipt") if receipt.is_null(): return _TransactionRecord() if bucket is None: from Acquire.Service import get_service_account_bucket \ as _get_service_account_bucket bucket = _get_service_account_bucket() # extract value into the debit note debit_account = _Account(uid=receipt.debit_account_uid(), bucket=bucket) credit_account = _Account(uid=receipt.credit_account_uid(), bucket=bucket) debit_note = _DebitNote(receipt=receipt, account=debit_account, bucket=bucket) # now create the credit note to put the value into the credit account try: credit_note = _CreditNote(debit_note=debit_note, receipt=receipt, account=credit_account, bucket=bucket) except Exception as e: # delete the debit note try: debit_account._delete_note(debit_note, bucket=bucket) except: pass # reset the transaction to the pending state try: _TransactionRecord.load_test_and_set( receipt.transaction_uid(), _TransactionState.RECEIPTING, _TransactionState.PENDING, bucket=bucket) except: pass raise e try: paired_notes = _PairedNote.create(debit_note, credit_note) except Exception as e: # delete all records...! try: debit_account._delete_note(debit_note, bucket=bucket) except: pass try: credit_account._delete_note(credit_note, bucket=bucket) except: pass # reset the transaction to the pending state try: _TransactionRecord.load_test_and_set( receipt.transaction_uid(), _TransactionState.RECEIPTING, _TransactionState.PENDING, bucket=bucket) except: pass raise e # now record the two entries to the ledger. The below function # is guaranteed not to raise an exception return Ledger._record_to_ledger(paired_notes, receipt=receipt, bucket=bucket)
def _create_from_receipt(self, debit_note, receipt, account, bucket): """Internal function used to create the credit note from the passed receipt. This will actually transfer value from the debit note to the credited account debit_note (DebitNote): DebitNote from which to take value receipt (Receipt): Receipt to create CreditNote from account (Account): Account to credit bucket (Bucket): Bucket to load data from Returns: None """ from Acquire.Accounting import DebitNote as _DebitNote from Acquire.Accounting import Refund as _Refund from Acquire.Accounting import TransactionRecord as _TransactionRecord from Acquire.Accounting import TransactionState as _TransactionState from Acquire.Accounting import Account as _Account from Acquire.Accounting import Receipt as _Receipt if not isinstance(debit_note, _DebitNote): raise TypeError("You can only create a CreditNote " "with a DebitNote") if not isinstance(receipt, _Receipt): raise TypeError("You can only receipt a Receipt object: %s" % str(receipt.__class__)) # get the transaction behind this receipt and ensure it is in the # receipting state... transaction = _TransactionRecord.load_test_and_set( receipt.transaction_uid(), _TransactionState.RECEIPTING, _TransactionState.RECEIPTING, bucket=bucket) # ensure that the receipt matches the transaction... transaction.assert_matching_receipt(receipt) if account is None: account = _Account(transaction.credit_account_uid(), bucket) elif account.uid() != receipt.credit_account_uid(): raise ValueError("The accounts do not match when crediting " "the receipt: %s versus %s" % (account.uid(), receipt.credit_account_uid())) (uid, datetime) = account._credit_receipt(debit_note, receipt, bucket) self._account_uid = account.uid() self._debit_account_uid = debit_note.account_uid() self._datetime = datetime self._uid = uid self._debit_note_uid = debit_note.uid() self._value = debit_note.value() self._is_provisional = debit_note.is_provisional() if debit_note.is_provisional(): self._receipt_by = debit_note.receipt_by() # finally(!) move the transaction into the receipted state _TransactionRecord.load_test_and_set(receipt.transaction_uid(), _TransactionState.RECEIPTING, _TransactionState.RECEIPTED, bucket=bucket)
def create_account(self, name, description=None, overdraft_limit=None, bucket=None, authorisation=None): """Create a new account called 'name' in this group. This will return the existing account if it already exists Args: name (str): Name of account to create description (default=None): Description of account overdraft_limit (int, default=None): Limit of overdraft bucket (dict, default=None): Bucket to load data from Returns: Account: New Account object """ if name is None: raise ValueError("You must pass a name of the new account") from Acquire.Identity import Authorisation as _Authorisation if authorisation is not None: if not isinstance(authorisation, _Authorisation): raise TypeError("The authorisation must be type Authorisation") if self._user_guid is not None: if self._user_guid != authorisation.user_guid(): raise PermissionError( "The same user who opened this accounts group (%s) " "must create accounts in this group (%s)" % (self._user_guid, authorisation.user_guid())) authorisation.verify("create_account %s" % name) self._assert_is_writeable() account_key = self._account_key(name) if bucket is None: from Acquire.Service import get_service_account_bucket \ as _get_service_account_bucket bucket = _get_service_account_bucket() from Acquire.ObjectStore import ObjectStore as _ObjectStore from Acquire.Accounting import Account as _Account try: account_uid = _ObjectStore.get_string_object(bucket, account_key) except: account_uid = None if account_uid is not None: # this account already exists - just return it account = _Account(uid=account_uid, bucket=bucket) if account.group_name() != self.name(): account.set_group(self) if overdraft_limit is not None: account.set_overdraft_limit(overdraft_limit, bucket=bucket) return account # make sure that no-one has created this account before from Acquire.ObjectStore import Mutex as _Mutex m = _Mutex(account_key, timeout=600, lease_time=600, bucket=bucket) try: account_uid = _ObjectStore.get_string_object(bucket, account_key) except: account_uid = None if account_uid is not None: m.unlock() # this account already exists - just return it account = _Account(uid=account_uid, bucket=bucket) if account.group_name() != self.name(): account.set_group(self) if overdraft_limit is not None: account.set_overdraft_limit(overdraft_limit, bucket=bucket) return account # write a temporary UID to the object store so that we # can ensure we are the only function to create it try: _ObjectStore.set_string_object(bucket, account_key, "under_construction") except: m.unlock() raise m.unlock() # ok - we are the only function creating this account. Let's try # to create it properly. The account is created with the same # ACLRules of the group. try: from Acquire.Identity import ACLRules as _ACLRules account = _Account(name=name, description=description, group_name=self.name(), aclrules=_ACLRules.inherit(), bucket=bucket) except: try: _ObjectStore.delete_object(bucket, account_key) except: pass raise if overdraft_limit is not None: account.set_overdraft_limit(overdraft_limit, bucket=bucket) _ObjectStore.set_string_object(bucket, account_key, account.uid()) return account
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 Args: receipt (Receipt): Receipt to create DebitNote from account (Account): Account to take value from bucket (dict): Bucket to read data from """ 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