Skip to content

Commit

Permalink
save state using transactions with balance
Browse files Browse the repository at this point in the history
  • Loading branch information
mrmurilo75 committed Mar 15, 2024
1 parent 9edb502 commit 3c28eca
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 17 deletions.
29 changes: 29 additions & 0 deletions development_notebook/2024-03-07.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Current State

See [[2024-03-06]] for previous log.

Deployed to production at [negligentoctopus.pythonanywhere.com](negligentoctopus.pythonanywhere.com).

Project has been fully configured from cookiecutter-django using MySQL. As development environment we are remotely accesing a linux system and using tmux and nvim, with the relevant plugins for code review.

Core models are being defined and added to the django-admin interface to be tested.

# Today

Define automatic balance tracking in account.

## Work Log
__InProgress__
* Use django's signal post-save to update accounts balance.

__ToDo__
* Migrate.
* Test on admin.

__Done__

__Discarded__
~~Define balance property, as well as relevant new db fields - assuming fields defined in Transaction. Define fields and functions in Transaction. Check FieldTracker in model\_utils.~~ This is overcomplicated. Strategy changed to above.

# To Do
[[Future Work]] Consider that it would be a high usability information to have current balance on each transaction. One possibility would be same as balance in account (We could send a event to account and use it as a manager). Another way would be doing it lazily from our current balance backwards (but this assumes end of timeframe is last transaction - which might not be true).
1 change: 1 addition & 0 deletions development_notebook/tools.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ tmux attach-session -t \<session name\>
`Ctrl + b` then `"`
? Split vertical

`Ctrl + b` then `Alt+[Arrow UDLR]`
`Ctrl + b` then `:resize-pane -[UDLR] N`
? Resize window by `N` to [Up, Down, Left, Right]

Expand Down
10 changes: 7 additions & 3 deletions negligent_octopus/core/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,15 @@
class AccountAdmin(admin.ModelAdmin):
list_display = ["name", "owner", "balance", "modified", "created", "is_removed"]
search_fields = ["name", "owner__username"]
list_filter = ["is_removed", "modified"]
list_filter = [
"owner",
"is_removed",
"modified",
] # TODO: Check modified catches transaction changes


@admin.register(Transaction)
class TransactionAdmin(admin.ModelAdmin):
list_display = ["title", "account", "get_account_owner", "date", "amount"]
list_display = ["title", "account", "get_account_owner", "amount", "timestamp"]
search_fields = ["account__owner__username", "account__name", "title"]
list_filter = ["account__owner", "account", "date"]
list_filter = ["account__owner", "account", "timestamp"]
58 changes: 44 additions & 14 deletions negligent_octopus/core/models.py
Original file line number Diff line number Diff line change
@@ -1,30 +1,23 @@
from django.db import models
from django.db import transaction as db_transaction
from django.utils import timezone
from model_utils.models import SoftDeletableModel
from model_utils.models import TimeStampedModel
from model_utils.tracker import FieldTracker

from negligent_octopus.users.models import User


class Account(TimeStampedModel, SoftDeletableModel):
owner = models.ForeignKey(User, on_delete=models.CASCADE)
name = models.CharField(max_length=255)
calculated_balance = models.FloatField(default=0.0, editable=False)
calculated_at = models.DateTimeField(auto_now_add=True)

@property
def balance(self):
# Set calculated_at to now (now_at)
# Get relevant transaction (Get creation or modified after now_at)
# For each do a running sum
# taking in consideration delta of all modifications after now_at
# Check for any modification on is_removed (just negative of last calculation?)
# Update calculated_balance and return it
# #IMPROVE : Evaluate using atomic or another techinique to prevent conflicts
pass
return self.transaction_set.last().balance

def __str__(self):
return self.name
return str(self.name)

class Meta(TimeStampedModel.Meta, SoftDeletableModel.Meta):
verbose_name = "Account"
Expand All @@ -33,19 +26,56 @@ class Meta(TimeStampedModel.Meta, SoftDeletableModel.Meta):


class Transaction(TimeStampedModel):
account = models.ForeignKey(Account, on_delete=models.CASCADE)
amount = models.FloatField()
date = models.DateField(default=timezone.now)
timestamp = models.DateTimeField(default=timezone.now)
account = models.ForeignKey(Account, on_delete=models.CASCADE)
balance = models.FloatField(default=0.0, editable=False)
title = models.CharField(max_length=127)
description = models.TextField(blank=True)

tracker = FieldTracker(
fields=["amount", "timestamp"],
) # We need to consider changes to the timestamp
# and how to update balances based on these

def get_account_owner(self):
return str(self.account.owner)

@db_transaction.atomic
def save(self, *args, **kwargs):
universe = self.account.transaction_set

last_transaction = (
universe.filter(
timestamp__lte=self.timestamp,
)
.order_by("-created")
.first()
)
self.balance = last_transaction.balance + self.amount
super().save(*args, **kwargs)
last_transaction = self

to_update = universe.filter(
timestamp__gte=self.timestamp,
).exclude(
created__lt=self.created,
)
for transaction in to_update:
transaction.balance = last_transaction.balance + transaction.amount
transaction.save()
last_transaction = transaction

def delete(self, *args, **kwargs):
self.amount = -self.amount
self.save()
super().delete(*args, **kwargs)

def __str__(self):
return self.title

class Meta(TimeStampedModel.Meta):
verbose_name = "Transaction"
verbose_name_plural = "Transactions"
ordering = ["date", "-modified"]
ordering = ["timestamp", "-modified"]
unique_together = ["timestamp", "created"]

0 comments on commit 3c28eca

Please sign in to comment.