def test_create_relationships(self): a = Account.objects.get(pk=4) p = Payee.objects.get(pk=5) t = Transaction(account=a, mobile=False, payee=p, amount=Decimal('-50'), date=datetime.date.today()) t.save() TagLink.create_relationships( t, u' food:26.30 toiletries:12.98\n household:51.23') expected = [{ 'name': 'food', 'split': Decimal('-26.30') }, { 'name': 'toiletries', 'split': Decimal('-12.98') }, { 'name': 'household', 'split': Decimal('-50') }] results = TagLink.objects.select_related().filter( transaction=t).order_by('id') self.assertEqual(len(results), len(expected)) for t in range(len(results)): self.assertEqual(results[t].tag.name, expected[t]['name']) self.assertEqual(results[t].split, expected[t]['split'])
def test_add_negative(self): a = Account.objects.get(pk=1) p = Payee.objects.get(pk=1) t = Transaction(account=a, mobile=False, payee=p, amount=Decimal("-512.78"), date=datetime.date.today()) t.save() a = Account.objects.get(pk=1) self.assertEqual(a.balance, Decimal("3188.77"))
def test_add_negative(self): a = Account.objects.get(pk=1) p = Payee.objects.get(pk=1) t = Transaction(account=a, mobile=False, payee=p, amount=Decimal('-512.78'), date=datetime.date.today()) t.save() a = Account.objects.get(pk=1) self.assertEqual(a.balance, Decimal('3188.77'))
def test_create_relationships(self): a = Account.objects.get(pk=4) p = Payee.objects.get(pk=5) t = Transaction(account=a, mobile=False, payee=p, amount=Decimal("-50"), date=datetime.date.today()) t.save() TagLink.create_relationships(t, u" food:26.30 toiletries:12.98\n household:51.23") expected = [ {"name": "food", "split": Decimal("-26.30")}, {"name": "toiletries", "split": Decimal("-12.98")}, {"name": "household", "split": Decimal("-50")}, ] results = TagLink.objects.select_related().filter(transaction=t).order_by("id") self.assertEqual(len(results), len(expected)) for t in range(len(results)): self.assertEqual(results[t].tag.name, expected[t]["name"]) self.assertEqual(results[t].split, expected[t]["split"])
def test_update_balance(self): # Make sure the Transaction model doesn't update the account Transaction.listen_off() # Delete the first transaction (827.59 of credit) t = Transaction.objects.select_related().get(pk=1) t.delete() a = t.account # The balance shouldn't have changed self.assertEqual(a.balance, Decimal('3701.55')) # Update the balance, only taking into account new transactions a.update_balance(all=False) # The balance still shouldn't have changed self.assertEqual(a.balance, Decimal('3701.55')) # Create a new transaction p = Payee.objects.get(pk=1) t = Transaction(account=a, mobile=False, payee=p, amount=Decimal('300'), date=datetime.date.today()) t.save() # Update the balance once more a.update_balance(all=False) # The balance should now be +300 from the added transaction self.assertEqual(a.balance, Decimal('4001.55')) # Update the balance a final time a.update_balance(all=True) # The balance should now be completely up-to-date self.assertEqual(a.balance, Decimal('3173.96')) # Turn transaction listening back on for other tests Transaction.listen_on()
def test_add_lots(self): """ Simulates a synchronisation of a large amount of transactions """ a = Account.objects.get(pk=1) p = Payee.objects.get(pk=1) total = a.balance orig = total.copy_abs() count = Transaction.objects.all().count() # Turn the signal listening off so we can update the account balance afterwards Transaction.listen_off() for i in range(1, 100): amount = Decimal('%.2f' % i) if i > 50: amount = amount.copy_negate() t = Transaction(account=a, mobile=False, payee=p, amount=amount, date=datetime.date.today()) t.save() total += amount count += 1 # Turn signal listening back on Transaction.listen_on() a = Account.objects.get(pk=1) # Make sure the balance hasn't been updated self.assertEqual(a.balance, orig) # Make sure the right number of transactions has been added self.assertEqual(Transaction.objects.all().count(), count)
def test_update_balance(self): # Make sure the Transaction model doesn't update the account Transaction.listen_off() # Delete the first transaction (827.59 of credit) t = Transaction.objects.select_related().get(pk=1) t.delete() a = t.account # The balance shouldn't have changed self.assertEqual(a.balance, Decimal("3701.55")) # Update the balance, only taking into account new transactions a.update_balance(all=False) # The balance still shouldn't have changed self.assertEqual(a.balance, Decimal("3701.55")) # Create a new transaction p = Payee.objects.get(pk=1) t = Transaction(account=a, mobile=False, payee=p, amount=Decimal("300"), date=datetime.date.today()) t.save() # Update the balance once more a.update_balance(all=False) # The balance should now be +300 from the added transaction self.assertEqual(a.balance, Decimal("4001.55")) # Update the balance a final time a.update_balance(all=True) # The balance should now be completely up-to-date self.assertEqual(a.balance, Decimal("3173.96")) # Turn transaction listening back on for other tests Transaction.listen_on()
def test_add_lots(self): """ Simulates a synchronisation of a large amount of transactions """ a = Account.objects.get(pk=1) p = Payee.objects.get(pk=1) total = a.balance orig = total.copy_abs() count = Transaction.objects.all().count() # Turn the signal listening off so we can update the account balance afterwards Transaction.listen_off() for i in range(1, 100): amount = Decimal("%.2f" % i) if i > 50: amount = amount.copy_negate() t = Transaction(account=a, mobile=False, payee=p, amount=amount, date=datetime.date.today()) t.save() total += amount count += 1 # Turn signal listening back on Transaction.listen_on() a = Account.objects.get(pk=1) # Make sure the balance hasn't been updated self.assertEqual(a.balance, orig) # Make sure the right number of transactions has been added self.assertEqual(Transaction.objects.all().count(), count)
def get_transactions(request): # Check the validity of the request if request.method != u'POST' or 'username' not in request.POST.keys() or 'password' not in request.POST.keys() or 'transactions' not in request.POST.keys(): return HttpResponseBadRequest(u"Either you didn't POST or you didn't give your username and your password") try: user = User.objects.get(username=request.POST['username']) except User.DoesNotExist: return HttpResponseForbidden() # Check the username and password given if not check_password(request.POST['password'], user.password): return HttpResponseForbidden() # Set up some variables accounts = {} # errors will contain a list of integers corresponding to the transaction's number # That way, clients can determine which transactions were successfully received and # committed to the database errors = [] try: transactions = json.loads(request.POST['transactions']) except ValueError: # We couldn't get a valid JSON object return HttpResponseBadRequest(u'Could not load the transactions as a JSON object') if type(transactions) is not list: return HttpResponseBadRequest(u'Transactions is not a list') # Turn signal listening off so that account balances aren't updated Transaction.listen_off() # Go through each transaction we've been sent for i in range(len(transactions)): if type(transactions[i]) is not dict: return HttpResponseBadRequest(u'Element %d of transactions is not a dictionary' % i) # If we don't have an account, we can't do anything if 'account' not in transactions[i].keys(): errors.append(i) continue else: # If we already know about this account, we can get if from the 'cache' if transactions[i]['account'] in accounts.keys(): account = accounts[transactions[i]['account']] else: # Otherwise we need to try and find it from the database try: account = Account.objects.get(pk=transactions[i]['account']) accounts[account.id] = account except Account.DoesNotExist: # If it doesn't appear in the database, add the transaction's number # to the list of errors and continue errors.append(i) continue # Make sure we have enough information to continue if 'payee' not in transactions[i].keys() or 'amount' not in transactions[i].keys(): errors.append(i) continue # Set up the new Transaction t = Transaction(mobile=True, account=account, transfer=False) # Get the payee's name and make sure it's UTF8 pname = transactions[i]['payee'] if type(pname) is str: pname = pname.decode('utf-8') # Try to find an existing payee with that name try: payee = Payee.objects.get(name__iexact=pname) except Payee.DoesNotExist: # Create a new payee payee = Payee(name=pname) payee.save() t.payee = payee t.amount = '%s' % transactions[i]['amount'] # If credit doesn't appear in the keys, assume it's a negative value if 'credit' not in transactions[i].keys(): t.amount = '%s' % (-abs(float(t.amount))) else: # Otherwise if credit is anything but 0 assume it's a positive value if transactions[i]['credit'] == 0: t.amount = '%s' % (-abs(float(t.amount))) else: t.amount = '%s' % abs(float(t.amount)) # Try to get the date from the POSTed values try: t.date = datetime.date.fromtimestamp(float(transactions[i]['date'])) except KeyError, ValueError: # KeyError means we don't have one in the POST data # ValueError means we couldn't decode it t.date = datetime.date.today() # If we have a comment, decode it if 'comment' in transactions[i].keys(): comment = transactions[i]['comment'] if type(comment) is str: comment = comment.decode('utf-8') t.comment = comment t.save()
# Otherwise if credit is anything but 0 assume it's a positive value if transactions[i]['credit'] == 0: t.amount = '%s' % (-abs(float(t.amount))) else: t.amount = '%s' % abs(float(t.amount)) # Try to get the date from the POSTed values try: t.date = datetime.date.fromtimestamp(float(transactions[i]['date'])) except KeyError, ValueError: # KeyError means we don't have one in the POST data # ValueError means we couldn't decode it t.date = datetime.date.today() # If we have a comment, decode it if 'comment' in transactions[i].keys(): comment = transactions[i]['comment'] if type(comment) is str: comment = comment.decode('utf-8') t.comment = comment t.save() # Turn signal listening back on Transaction.listen_on() # Now we can update the balance of all the accounts we've dealt with for a in accounts: accounts[a].update_balance() return HttpResponse(json.dumps({'errors': errors}), content_type='application/javascript; charset=utf-8')
def get_transactions(request): # Check the validity of the request if request.method != u'POST' or 'username' not in request.POST.keys( ) or 'password' not in request.POST.keys( ) or 'transactions' not in request.POST.keys(): return HttpResponseBadRequest( u"Either you didn't POST or you didn't give your username and your password" ) try: user = User.objects.get(username=request.POST['username']) except User.DoesNotExist: return HttpResponseForbidden() # Check the username and password given if not check_password(request.POST['password'], user.password): return HttpResponseForbidden() # Set up some variables accounts = {} # errors will contain a list of integers corresponding to the transaction's number # That way, clients can determine which transactions were successfully received and # committed to the database errors = [] try: transactions = json.loads(request.POST['transactions']) except ValueError: # We couldn't get a valid JSON object return HttpResponseBadRequest( u'Could not load the transactions as a JSON object') if type(transactions) is not list: return HttpResponseBadRequest(u'Transactions is not a list') # Turn signal listening off so that account balances aren't updated Transaction.listen_off() # Go through each transaction we've been sent for i in range(len(transactions)): if type(transactions[i]) is not dict: return HttpResponseBadRequest( u'Element %d of transactions is not a dictionary' % i) # If we don't have an account, we can't do anything if 'account' not in transactions[i].keys(): errors.append(i) continue else: # If we already know about this account, we can get if from the 'cache' if transactions[i]['account'] in accounts.keys(): account = accounts[transactions[i]['account']] else: # Otherwise we need to try and find it from the database try: account = Account.objects.get( pk=transactions[i]['account']) accounts[account.id] = account except Account.DoesNotExist: # If it doesn't appear in the database, add the transaction's number # to the list of errors and continue errors.append(i) continue # Make sure we have enough information to continue if 'payee' not in transactions[i].keys( ) or 'amount' not in transactions[i].keys(): errors.append(i) continue # Set up the new Transaction t = Transaction(mobile=True, account=account, transfer=False) # Get the payee's name and make sure it's UTF8 pname = transactions[i]['payee'] if type(pname) is str: pname = pname.decode('utf-8') # Try to find an existing payee with that name try: payee = Payee.objects.get(name__iexact=pname) except Payee.DoesNotExist: # Create a new payee payee = Payee(name=pname) payee.save() t.payee = payee t.amount = '%s' % transactions[i]['amount'] # If credit doesn't appear in the keys, assume it's a negative value if 'credit' not in transactions[i].keys(): t.amount = '%s' % (-abs(float(t.amount))) else: # Otherwise if credit is anything but 0 assume it's a positive value if transactions[i]['credit'] == 0: t.amount = '%s' % (-abs(float(t.amount))) else: t.amount = '%s' % abs(float(t.amount)) # Try to get the date from the POSTed values try: t.date = datetime.date.fromtimestamp(float( transactions[i]['date'])) except KeyError, ValueError: # KeyError means we don't have one in the POST data # ValueError means we couldn't decode it t.date = datetime.date.today() # If we have a comment, decode it if 'comment' in transactions[i].keys(): comment = transactions[i]['comment'] if type(comment) is str: comment = comment.decode('utf-8') t.comment = comment t.save()
else: t.amount = '%s' % abs(float(t.amount)) # Try to get the date from the POSTed values try: t.date = datetime.date.fromtimestamp(float( transactions[i]['date'])) except KeyError, ValueError: # KeyError means we don't have one in the POST data # ValueError means we couldn't decode it t.date = datetime.date.today() # If we have a comment, decode it if 'comment' in transactions[i].keys(): comment = transactions[i]['comment'] if type(comment) is str: comment = comment.decode('utf-8') t.comment = comment t.save() # Turn signal listening back on Transaction.listen_on() # Now we can update the balance of all the accounts we've dealt with for a in accounts: accounts[a].update_balance() return HttpResponse(json.dumps({'errors': errors}), content_type='application/javascript; charset=utf-8')