Making of a customer report based on sales - python

I am trying to make a customer wise sales report, in it there will customers listed with their total number of sales, total amount, total paid and balance of sales occurred in the selected time period.
models:
class Customer(models.Model):
name = models.CharField(max_length=128)
phone = models.CharField(max_length=128)
email = models.EmailField(blank=True, null=True)
address = models.TextField()
is_deleted = models.BooleanField(default=False)
...
class Sale(models.Model):
auto_id = models.PositiveIntegerField()
sale_id = models.CharField(max_length=128, blank=True, null=True)
sale_date = models.DateTimeField()
customer = models.ForeignKey('customers.Customer', limit_choices_to={'is_deleted': False}, on_delete=models.CASCADE)
customer_address = models.TextField(blank=True, null=True)
sale_category = models.CharField(max_length=128, choices=SALE_CATEGORY, default="intra_state")
subtotal = models.DecimalField(default=0.0, decimal_places=2, max_digits=15, validators=[MinValueValidator(Decimal('0.00'))])
round_off = models.DecimalField(decimal_places=3, default=0.00, max_digits=30)
total = models.DecimalField(default=0.0, decimal_places=2, max_digits=15, validators=[MinValueValidator(Decimal('0.00'))])
paid = models.DecimalField(default=0.0, decimal_places=2, max_digits=15, validators=[MinValueValidator(Decimal('0.00'))])
balance = models.DecimalField(decimal_places=2, default=0.00, max_digits=15)
is_deleted = models.BooleanField(default=False)
...
What I tried is passing customers with sales occurred in the time period and using a template tag getting the sale values of each customer in template
views:
def customer_sales_report(request):
from_date = request.GET.get('from_date')
to_date = request.GET.get('to_date')
filter_form = {
'from_date': from_date,
'to_date': to_date,
}
from_date = datetime.datetime.strptime(from_date, '%d/%m/%Y')
to_date = datetime.datetime.strptime(to_date, '%d/%m/%Y')
sales = Sale.objects.filter(sale_date__date__range=[from_date, to_date], is_deleted=False)
customer_pks = list(sales.values_list('customer_id', flat=True))
customers = Customer.objects.filter(pk__in=customer_pks, is_deleted=False)
filter_string = f"{filter_form['from_date']},{filter_form['to_date']}"
context = {
'customers': customers,
'filter_form': filter_form,
'filter_string': filter_string,
"title": 'Customer sales report',
}
return render(request, 'customers/customer_sales_report.html', context)
template:
...
<table>
<thead>
<tr>
<th style="width: 30px;">ID</th>
<th>Name </th>
<th>Phone </th>
<td>Sales</td>
<td>Total Amount</td>
<td>Paid Amount</td>
<td>Balance Amount</td>
<td>Customer Balance</td>
</tr>
</thead>
<tbody>
{% load el_pagination_tags %}
{% paginate 20 customers %}
{% for instance in customers %}
<tr>
<td>{{ forloop.counter }}</td>
<td>
<a class="" href="{% url 'customers:customer' pk=instance.pk %}" >{{ instance }}</a>
</td>
<td>{{ instance.phone }}</td>
{% with instance.pk|get_customer_sales:filter_string as sales %}
<td>{{ sales.total_count }}</td>
<td>{{ sales.subtotal }}</td>
<td>{{ sales.paid }}</td>
<td>{{ sales.balance }}</td>
<td>{{ sales.current_balance }} ({{ sales.current_balance_type }})</td>
{% endwith %}
</tr>
{% endfor %}
</tbody>
</table>
...
template tag:
#register.filter
def get_customer_sales(pk, data):
list_data = data.split(',')
from_date = list_data[0]
to_date = list_data[1]
from_date = datetime.datetime.strptime(from_date, '%d/%m/%Y').date()
to_date = datetime.datetime.strptime(to_date, '%d/%m/%Y').date()
sales = Sale.objects.filter(customer_id=pk, sale_date__date__range=[from_date, to_date], is_deleted=False)
subtotal_amount = sales.aggregate(Sum('total'))['total__sum']
sale_payment = sales.aggregate(Sum('paid'))['paid__sum']
sale_balance = sales.aggregate(Sum('balance'))['balance__sum']
...
sale_data = {
'total_count': sales.count(),
'paid': sale_payment,
'balance': sale_balance,
'subtotal': subtotal_amount,
"current_balance" : current_balance,
"current_balance_type" : current_balance_type,
}
return sale_data
What I need now is to order by their total amount which as of now I am unable to do. is there a way I can annotate the total amount, paid, balance of sales in to customers queryset which will make it easier or any other ways

Yes, that's possible to do via annotation and will even be more efficient since all calculations are made in a single query instead of a 3 * NRows.
We can achieve that with the Filtering on annotations.
BTW, you don't need the list() for customer_pks, it should be more efficient to allow DB to work directly on a query.
sales = Sale.objects.filter(sale_date__date__range=[from_date, to_date], is_deleted=False)
customers = Customer.objects.filter(pk__in=sales.values('customer_id'), is_deleted=False)
sales_q = Q(sales__sale_date__date__range=[from_date, to_date], sales__is_deleted=False)
customers = customers.annotate(
subtotal_amount=Sum('sales__total', filter=sales_q),
sale_payment=Sum('sales__paid', filter=sales_q),
sale_balance=Sum('sales__balance', filter=sales_q),
)
I don't know what is current_balance and current_balance_type, so you'll need to figure it out by yourself or amend the question.
P.S. you don't have to covert dates to the filter_string - you can pass any type of objects to the template and then to the filter.

Maybe you are looking for this:
order_by()
order_by(*fields)
By default, results returned by a QuerySet are ordered by the ordering tuple given by the ordering option in the model’s Meta. You can override this on a per-QuerySet basis by using the order_by method.
Example:
Entry.objects.filter(pub_date__year=2005).order_by('-pub_date', 'headline')
Read more about it here

Related

Django template regorup

I want to regroup this on the basis of ingredients.
this is my sample result which I want
Just like the below picture as there is more than one certificate so I am showing them in one row.
Ingredient_name, all the stop_name associated with that ingredient,
all the stop_longitude associated with that ingredient and so on in one table row.
Right now its showing like this and you can see that the ingredient name is repeating .
model
class SupplyChainStops(models.Model):
ingredient = models.ForeignKey(Ingredients, null=True, on_delete=models.CASCADE)
stop_name = models.CharField(max_length=1024, null=True, blank=True)
stop_longitude = models.CharField(max_length=500, null=True, blank=True)
stop_latitude = models.CharField(max_length=500, null=True, blank=True)
is_supplier = models.BooleanField(default=False, blank=True, null=True)
def __str__(self):
return f'{self.stop_name}'
query
items = SupplyChainStops.objects.all()
template
{% for item in items %}
<tr class="text-black">
<td>{{ item.ingredient }}</td>
<td>{{ item.stop_name }}
<td>{{ item.stop_longitude }}
<td>{{ item.stop_latitude }}
This is my DB structure
This is my desired output
You can set the related_name for ForeignKey field.
class SupplyChainStops(models.Model):
ingredient = models.ForeignKey(Ingredients, null=True, on_delete=models.CASCADE, related_name="supply_chain_stops")
in view:
each_row = []
ing_ids = []
sup = SupplyChainStops.objects.all()
for each_sup in sup:
ing_ids.append(each_sup.ingredient.id)
ing_ids = list(set(ing_ids))
sch = []
for each_ing_id in ing_ids:
sch.append(SupplyChainStops.objects.filter(ingredient__id= each_ing_id).last())
for each in sch:
stop_names_list = []
stop_longitude_list = []
stop_latitude_list = []
mi_list = each.ingredient.supply_chain_stops.all()
for each_mi in mi_list:
stop_names_list.append(each_mi.stop_name)
stop_longitude_list.append(each_mi.stop_longitude)
stop_latitude_list.append(each_mi.stop_latitude)
row_list = [each.ingredient, stop_names_list, stop_longitude_list, stop_latitude_list]
each_row.append(row_list)
context = {
"items": each_row
}
in template:
{% for item in items %}
<tr class="text-black">
<td>{{ item.0 }}</td>
<td>{{ item.1|join:", " }}</td>
<td>{{ item.2|join:", " }}</td>
<td>{{ item.3|join:", " }}</td>
</tr>
{% endfor %}

Django : querying data from related models

I use Django 3.2.7 and I would like to query all orders from a customer.
Models.py
class Order(models.Model):
STATUS=(
('Pending','Pending'),
('Out for Delivery','Out for delivery'),
('Delivered','Delivered'),
)
customer = models.ForeignKey(Customer,null=True, on_delete=models.SET_NULL)
product = models.ForeignKey(Product,null=True, on_delete=models.SET_NULL)
date_created = models.DateTimeField(auto_now_add=True)
status = models.CharField(max_length=200, null=True, choices=STATUS)
Views.py
def customer(request,pk):
customer = Customer.objects.get(id=pk)
orders = customer.order_set.all()
my_context={
'customer':customer,
'orders':orders
}
return render(request, 'accounts/customer.html', context=my_context)
Urls.py
path('customer/<str:pk>/', views.customer),
Template
{% for order in orders %}
<tr>
<td>
{order.product}
</td>
<td>
{order.product.category}
</td>
<td>
{order.date_created}
</td>
<td>
{order.status}
</td>
<td>Update</td>
<td>Delete</td>
</tr>
{% endfor %}
My problem is that instead of printing the actual data on the template is prints the query data.
I think the problem is at
orders = customer.order_set.all()
What am I doing wrong?

how to call property method from model class to html in django

Im making a django app, and its basically an admin site, i have an app called calculator, inisde it i have 3 models Transaction, FamilyGroup and FamilyMember, each model has some property methods for calculation purposes. here are the models for more clearness :
class Transaction(models.Model):
chp_reference = models.CharField(max_length=50, unique=True)
rent_effective_date = models.DateField(null=True, blank=True)
income_period = models.CharField(max_length=11)
property_market_rent = models.DecimalField(max_digits=7)
#property
def ftb_combined(self):
ftb_combined = 0
for family_group in self.familygroup_set.all():
ftb_combined += family_group.ftb_combined
return ftb_combined
class FamilyGroup(models.Model):
name = models.CharField(max_length=10)
transaction = models.ForeignKey(Transaction, on_delete=models.CASCADE)
last_rent = models.DecimalField(max_digits=7)
#property
def additional_child_combined(self):
return (self.number_of_additional_children
or 0) * self.maintenance_rate_additional_child
class FamilyMember(models.Model):
transaction = models.ForeignKey(Transaction, on_delete=models.CASCADE)
family_group = models.ForeignKey(FamilyGroup, on_delete=models.CASCADE, null=True, blank=True)
name = models.CharField(max_length=100, null=True, blank=True)
date_of_birth = models.DateField(null=True, blank=True)
income = models.DecimalField(max_digits=6)
#property
def weekly_income(self):
if self.transaction.income_period == 'Weekly':
return self.income
return (self.income or 0) / 2
this is how my models are connected, now i made a method in views.py as below:
def transaction_print(request, transaction_id):
transaction = Transaction.objects.get(id=transaction_id)
return render(request, 'report.html', {'transaction':transaction})
I want to make a report in report.html, 1 report for each transaction, and the transaction can have many FamilyGroups and FamilyMember, and will include almost all the data from the models and the property methods inside it.
here what i thought in the report.html
<table class="table">
<thead class="thead-dark">
<tr>
<th>CHP Reference </th>
<th>Rent Effective From (dd/mm/yyyy)</th>
<th>CRA Fortnightly Rates valid for 6 months from</th>
<th>Market Rent of the Property </th>
<th>Number of Family Groups </th>
</tr>
</thead>
<tbody>
<tr>
<td>{{ transaction.chp_reference }} </td>
<td>{{ transaction.rent_effective_date }} </td>
<td>0</td>
<td>{{ transaction.property_market_rent }}</td>
<td>{{ transaction.number_of_family_group }}</td>
</tr>
</tbody>
{% for family_group in transaction.family_group_set.all %} ??
{% for m in family_group.transaction.family_group_set.all %} ??
</table>
Im really not sure how to perform the nested loop to iterate through the FamilyGroup and FamilyMember inside the transaction report.html would appreciate a hint how this be done.
According to the documentation Django sets the name to MODELNAME_set. However you can still use the related_name property to set a name for your backward reference (you will still be able to use MODELNAME_set as well).
Here's how to achieve it using related_name:
models.py
class FamilyGroup(models.Model):
name = models.CharField(max_length=10)
transaction = models.ForeignKey(Transaction, on_delete=models.CASCADE, related_name="family_groups") # Notice the related_name here as it will be used later on
last_rent = models.DecimalField(max_digits=7)
# ...
class FamilyMember(models.Model):
transaction = models.ForeignKey(Transaction, on_delete=models.CASCADE, related_name="family_members") # Notice the related_name
family_group = models.ForeignKey(FamilyGroup, on_delete=models.CASCADE, null=True, blank=True)
name = models.CharField(max_length=100, null=True, blank=True)
date_of_birth = models.DateField(null=True, blank=True)
income = models.DecimalField(max_digits=6)
# ...
Now you can loop through them like so:
report.html
{% for family_group in transaction.family_groups.all %}
{{ family_group.name }}
{% endfor %}
{% for family_member in transaction.family_members.all %}
{{ family_member.name }}
{% endfor %}

Django: loop all the records in a table and then get field from another table

I got two class models called Buy and Sell:
class Buy(models.Model):
category = models.ForeignKey(Category, on_delete=models.CASCADE)
image = models.FileField()
date = models.DateField()
buy_price = models.DecimalField(max_digits=6, decimal_places=2)
sell_price = models.DecimalField(max_digits=6, decimal_places=2)
def __str__(self):
return str(self.id)
class Sell(models.Model):
item = models.OneToOneField(Buy, on_delete=models.CASCADE)
date = models.DateField()
discount = models.DecimalField(max_digits=6, decimal_places=2)
total_paid = models.DecimalField(max_digits=6, decimal_places=2)
buyer = models.ForeignKey(Buyer, on_delete=models.CASCADE)
def __str__(self):
return str(self.item)
In a template, I'd like to display all the records from the Sell table and also the image class-attribute from Buy table.
Below, my view.py:
def sell(request):
buys = []
sells = Sell.objects.order_by('-id')
for sell in sells:
buys.append(Buy.objects.filter(id=str(sell.item)))
context = {'sells': sells, 'buys': buys}
return render(request, 'sell.html', context)
Template:
{% for sell in sells %}
<tr>
<td>{{ sell.item }}</td>
<td>{{ sell.date|date:'d-m-Y' }}</td>
<td>{{ buys[0].image}}</td>
<td>R$ {{ sell.discount }}</td>
<td>R$ {{ sell.total_paid }}</td>
<td>{{ sell.buyer }}</td>
</tr>
{% endfor %}
I'm wondering if there is a simple way to do that. I need some help in my view.py!
You can access the Buy entity related to the Sell inside the template
{{sell.item.image}}
If the image field is as the name suggests a image you might want to change the field to a ImageField.
Then the code to display the image would be:
<img src="{{ sell.item.image.url }}">
(found here)

Django: Sum Total value over Foreign Key Data

I am using Django 1.11 and Python3.5
I have created a table. This is a screenshot.
When I got a table from my query database, it is showing 10+2+3... but I want to get total sum value for every customer within Due Taka update column in table like this 10+2+4+2 = 18
this is my model.py file
class CustomerInfo(models.Model):
customer_name = models.CharField('Customer Name', max_length=100)
customer_mobile_no = models.CharField(
'Mobile No', null=True, blank=True, max_length=12)
customer_price=models.IntegerField('Customer Price',default=1)
customer_product_warrenty = models.CharField('Product Warrenty',null=True, blank=True,max_length=10)
customer_sell_date = models.DateTimeField('date-published', auto_now_add=True)
customer_product_id=models.CharField('Product ID',max_length=300,null=True, blank=True)
customer_product_name=models.TextField('Product Name')
customer_product_quantity=models.IntegerField('Quantity',default=1)
customer_uid = models.CharField(max_length=6, blank=True, null=True)
customer_info=models.TextField('Customer Details Informations', blank=True, null=True)
customer_conditions=models.CharField('Conditions',blank=True, null=True, max_length=300)
customer_due_taka_info=models.IntegerField(default=0)
customer_discount_taka=models.IntegerField(default=0)
customer_first_time_payment=models.IntegerField('First Time Payment',default=0)
customer_first_due_info = models.CharField('First Due Info',default='No due info', max_length=200, blank=True, null=True)
customer_product_mrp=models.IntegerField('Products MRP', default=0)
customers_refrence=models.CharField(max_length=100)
customer_updated = models.DateTimeField(auto_now=True)
customer_type=models.CharField('Customer Type', default='MobilePhone', max_length=50)
def __str__(self):
return self.customer_name
def remainBalance(self):
if self.customer_price > self.customer_due_taka_info:
remain=self.customer_price - self.customer_due_taka_info
return remain
def totalRetalsPerSingle(self):
return self.customer_product_quantity * self.customer_product_mrp
#def product_warrenty(self):
#expire_date_oneyr =self.customer_sell_date+ datetime.timedelta(days=365)
#return 'This product expire on this date ' + str(expire_date_oneyr)
class Meta:
verbose_name = ("গ্রাহকের তথ্য")
verbose_name_plural = ("গ্রাহকের তথ্যসমূহ")
#intregated with Customerinfo Model (Using foreignKey)
class DueTaka(models.Model):
customer_due = models.IntegerField('Due Taka', default=0)
customer_due_date=models.DateTimeField(auto_now_add=True)
customer_due_info=models.CharField('Due Info', max_length=200, blank=True, null=True)
customerinfo = models.ForeignKey(CustomerInfo, on_delete=models.CASCADE)
due_customer_updated = models.DateTimeField(auto_now=True)
def __int__(self):
return self.customer_due
def sum_total(self):
return self.customer_due
this is my views.py file
due_update_info = CustomerInfo.objects.filter(duetaka__customer_due_date__range=(starts_date, tomorrow)).order_by('-customer_updated')
I want to get total price customer_due fields within DueTaka model for per single customer. if a customer name is 'asad'. He has some multple due taka and he paid multiple times like this 10+20+20 Now I want to get total value like 50
If I update my customer's profile, same customers name come again with in loop my table. But I don't want this.
and this is my html template file
<h1>Due Paid Book</h1>
<table class="table table-hover table-bordered">
<p style="font-size:16px;">Due Paid Tody</p>
<thead>
<tr>
<th>No</th>
<th>Name</th>
<th>Invoice ID</th>
<th>Mobile</th>
<th>Product</th>
<th>Product MRP</th>
<th>Customer Paid (TK)</th>
<th>Due Amount</th>
<th>Total Price</th>
<th>Warrenty</th>
<th>Purchase Date</th>
<th>Due Taka update</th>
<th>Update Date</th>
<th>QN</th>
</tr>
</thead>
{% for x in due_update_info %}
<tbody>
<tr>
.......code in here.......
<td>
{% for y in x.duetaka_set.all %}
{{y.customer_due | intcomma}}+
{% endfor %}
{{x.duetaka_set }}
<!--i want to toatal value in here, now nothing showing-->
{{total_price}}
</td>
</tr>
<tr>
</tr>
</tbody>
{% endfor %}
<td colspan="10"></td>
<td> Total Du Paid <br> <i>{{starts_date}} to {{end_date}}</i> </td>
<td> <b>{{dupaid_today|intcomma}} TK</b> </td>
</table>
Now How can I implement in views file?
Maybe change the filter query like this this:
CustomerInfo.objects.filter(duetaka__customer_due_date__range=(starts_date, tomorrow)).annotate(due_taka_total=Sum('duetaka__customer_due')).order_by('-customer_updated')
which will give you additional field 'due_taka_total', which can be used in template, like:
{{ y.due_taka_total }}
assuming y is an object of CustomerInfo
OR
You could write a custom template tag and find out the total using it:
from django import template
register = template.Library()
#register.simple_tag
def get_total_taka(customer_info_object):
return sum([each.customer_due for each in customer_info_object.duetaka_set.all()])
and use it in template like this:
{% get_total_taka y %}
assuming y is an object of Customerinfo
For writing the custom template tag refer: Writing Custom Tags

Categories