How to implement customer balance calculation using property

Sorry for the long post. This is my first time posting here.
As a new Django user, I am building a project to manage customer orders of a store. The code is working fine but I would like to know if it is possible to improve it using what I mentioned in the title. And if yes, how to do it? The store employee will login and do operations like:

  • Create new customers
  • Create new products
  • Enter customers’ orders. The customer model has a decimal field called balance. Each time an order is placed for a customer, the order total cost is deducted from the customer balance. There is a Recharge functionality which increases the customer balance using signal. My question is how to implement customer balance calculation using property.

Code:

class Customer(models.Model):
    name = models.CharField(max_length=50, db_index=True)
    email = models.EmailField(max_length=50, unique=True)
    address = models.CharField(max_length=150, null=True, blank=True)
    balance = models.DecimalField(default=0, max_digits=5, decimal_places=2)
    starting_balance = models.DecimalField(default=0, max_digits=5, decimal_places=2)
    created_date = models.DateField(auto_now_add=True)
    updated_date = models.DateField(auto_now=True)

    class Meta:
        ordering = ('name',)
    
    def get_absolute_url(self):
        return reverse('customers:customer_detail', args=[self.id])
        

    def __str__(self):
        return self.name


class Recharge(models.Model):
    customer = models.ForeignKey(Customer, related_name='recharges', on_delete=models.CASCADE)
    amount = models.DecimalField(max_digits=5, decimal_places=2)
    date_created = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return f'{self.customer.name} recharged for {self.amount}'

class Order(models.Model):
    """ Order model. """
    customer = models.ForeignKey(Customer, related_name='orders', null=True, blank=True, on_delete=models.SET_NULL)
    email = models.EmailField()
    address = models.CharField(max_length=200, null=True, blank=True)
    phone_number = models.CharField(max_length=80, null=True, blank=True)
    paid = models.BooleanField(default=False)
    created_date = models.DateField(auto_now_add=True)
    updated_date = models.DateField(auto_now=True)

    class Meta:
        ordering = ('-created_date',)
    
    def __str__(self):
        return f'Order #{self.id}'
    
    @property
    def get_total_cost(self):
        """Returns the order's total amount."""
        return sum(item.get_cost() for item in self.items.all())

Hey there, so you’re saying that instead of having a column balance on your model, you want this value to be calculated?

Hey @leandrodesouzadev thanks for asking.
Yes that’s what I want to implement.
Also what do you think is better, having a column balance or the value calculated ?

I never myself implemented such a feature.
But thinking in terms of Integrity of the data, i would do the bank extract approach, where the current balance is the sum of all transactions. This comes with a downside, it’s gonna load your database. And probably there are some improvements to this approach that i don’t know.
But if this balance does not need all of this Integrity, then your current solution is ok, and probably it’s not the core feature of your application (that’s not the case to your bank account :sweat_smile:), so if works, i would suggest you to keep it like that. If you start facing issues, then you can think on another solution.

A couple of financial institutions that I have had experience with use a hybrid approach. For reporting and regulatory reasons, they need to provide statements as of a specific date. At that date, the current account value is defined as the sum of the previous month value plus all the transactions in that month.

So within that month, the daily balance is just the sum of the current account value plus all the current month’s transactions.

Now, for something like an order, where the total cost at some point in time is finalized and ideally not subject to being changed after being finished, it’s quite ok to calculate a final value and store it. (The mechanisms behind supporting that is also intended to provide for any non-standard adjustments for those situations where the total cost is not directly the cost of the line items such as for a “preferred customer” discount.)

Note: You don’t want to do the summation of the total in your code. You want to use the database to calculate that for you using an aggregation.

Hey thanks for the feedback I really appreciate.

First of all thanks for your feedback.
I am sorry but I do not get your point.
Should I keep my code as is or implement property for customer balance calculation according to you ?
If yes for property, how to go about the logic because once again the Recharge will add to the customer’s balance as soon as the recharge form is submitted and the order cost will get deducted from the customer’s balance as soon as order is placed.
Any suggestion is also very much welcome.

You asked:

@leandrodesouzadev replied in part:

I pointed out a third option, but also stated:

You then ask:

That choice is one you need to make. We can’t make that decision for you.

Why? Because the “best” answer is likely to depend upon a lot of factors involved in the design of your system that we’re not likely to know. You need to make this decision with the understanding of the system as a whole - what your other bookkeeping, accounting, and reporting requirements may be for this system, as well as the (realistically) projected scale of your deployment. (If the system is “small” enough, probably any choice will work equally well. On the other hand, if you’re trying to replace amazon.com, then you’ve got other issues to address.)

About the best we can do is give you your options and highlight what some of the advantages and disadvantages of each of those options.

Ok, I now get your point.
Thank you for your contribution I really appreciate it.
I will try and switch to what @leandrodesouzadev suggested and see how it goes. But thanks a lot.