Trying to build a purchase order app - django

I've been working on a Purchase Order app but I'm getting a little confused how I'm going to put it all together.
I have 3 models -
class PurchaseOrder(models.Model):
po_number = models.IntegerField(default=get_po_number, unique=True)
po_date = models.DateField()
invoice_number = models.ForeignKey(Invoice, on_delete=models.CASCADE)
....
class PurchaseOrderItem(models.Model):
po_number_fk = models.ForeignKey(PurchaseOrder, on_delete=models.CASCADE)
qty = models.IntegerField()
unit = models.CharField(max_length=100, blank=True, null=True)
description = models.CharField(max_length=255)
unit_price = models.DecimalField(max_digits=6, decimal_places=2)
amount = models.DecimalField(max_digits=6, decimal_places=2)
class PurchaseOrderTotal(models.Model):
po_number_fk = models.ForeignKey(PurchaseOrder, on_delete=models.CASCADE)
subtotal = models.DecimalField(max_digits=6, decimal_places=2)
tax = models.DecimalField(max_digits=6, decimal_places=2, default="7.82")
shipping = models.DecimalField(max_digits=6, decimal_places=2)
other = models.DecimalField(max_digits=6, decimal_places=2)
total = models.DecimalField(max_digits=6, decimal_places=2)
the first (PurchaseOrder) holds information about the purchase order itself. ie. what the invoice number is, the vendor, etc.
the second (PurchaseOrderItem) lists items in the purchase order to purchase
the third (PurchaseOrderTotal) totals up the amounts from the items and adds tax etc. (I may not need this model.. I can probably put this info in the first model?)
Does it look like I'm going about this in the right way or should I take away the third model and put those fields from the third model into the first model? How do I total up all prices for all items? I'm sure I'll need to do some sort of loop to total up all prices but where do I do that? In the form_valid fucntion? or do I override the save function and do it there? Thanks!

I would combine model 1 and 3. You can total up the numbers when you submit the form and then save it to the same model. Its better to try and keep all related model information on the same table. If you find out after testing that for some reason one model doesn't work well you can always change it.
There are multiple ways to total up the totals:
Do it in your view when you submit the form and save it to the DB.
This would be my recommended choice for your situation since this data while not likely
change very often.
Do not save the totals in the DB and
sum them up in your view before you display that information.
Do not save the totals in the DB and sum them up in your
template.
I would highly discourage the third option since your sounds like your data is fairly static and there is no need to do that processing in your template. I added it to illustrate all the possibilities. This also applies to the second option, though in this situation option two is better than three because it is done in the view instead the template but is still not the best option since it would not make since to do that calculation over and over again for data that never changed.
Example:
def your_view(request):
if request.method == 'POST':
# create a form instance and populate it with data from the request:
form = InvoiceForm(request.POST)
# check whether it's valid:
if form.is_valid():
tax = form.cleaned_data.get('tax')
sub = form.cleaned_data.get('sub_total')
fee = form.cleaned_data.get('fee')
form.total = tax + sub + fee
form.save()
return HttpResponseRedirect('/thanks/')
# if a GET (or any other method) we'll create a blank form
else:
form = InvoiceForm()
return render(request, 'name.html', {'form': form})

Related

Annotating values from filtered related objects -- Case, Subquery, or another method?

I have some models in Django:
# models.py, simplified here
class Category(models.Model):
"""The category an inventory item belongs to. Examples: car, truck, airplane"""
name = models.CharField(max_length=255)
class UserInterestCategory(models.Model):
"""
How interested is a user in a given category. `interest` can be set by any method, maybe a neural network or something like that
"""
user = models.ForeignKey(User, on_delete=models.CASCADE) # user is the stock Django user
category = models.ForeignKey(Category, on_delete=models.CASCADE)
interest = models.PositiveIntegerField(default=0, validators=[MinValueValidator(0)])
class Item(models.Model):
"""This is a product that we have in stock, which we are trying to get a User to buy"""
model_number = models.CharField(max_length=40, default="New inventory item")
product_category = models.ForeignKey(Category, null=True, blank=True, on_delete=models.SET_NULL, verbose_name="Category")
I have a list view showing items, and I'm trying to sort by user_interest_category for the currently logged in user.
I have tried a couple different querysets and I'm not thrilled with them:
primary_queryset = Item.objects.all()
# this one works, and it's fast, but only finds items the users ALREADY has an interest in --
primary_queryset = primary_queryset.filter(product_category__userinterestcategory__user=self.request.user).annotate(
recommended = F('product_category__userinterestcategory__interest')
)
# this one works great but the baby jesus weeps at its slowness
# probably because we are iterating through every user, item, and userinterestcategory in the db
primary_queryset = primary_queryset.annotate(
recommended = Case(
When(product_category__userinterestcategory__user=self.request.user, then=F('product_category__userinterestcategory__interest')),
default=Value(0),
output_field=IntegerField(),
)
)
# this one works, but it's still a bit slow -- 2-3 seconds per query:
interest = Subquery(UserInterestCategory.objects.filter(category=OuterRef('product_category'), user=self.request.user).values('interest'))
primary_queryset = primary_queryset.annotate(interest)
The third method is workable, but it doesn't seem like the most efficient way to do things. Isn't there a better method than this?

Django model field dependent on another model field

I have general question. Let’s say I have a model field that is the sum of two other fields in a different model.
I’m having a hard time to implement this.
Let’s take the following example
model1
field1
field2
model2
field3 (dependent on field1 and field2) in model1
If I do it as part of specific page in my webapp. It means that if field1 or field2 has changed but the person didn’t visit the page that sum up the value and update it in field3 then field3 will carry incorrect value.
The only way to takle such a problem that I managed to identify is to never create field3. everytime a sum(or any other operation that had dependency on other fields) take a place is to be done in a variable inside the view.py
This means that value to be calculated everytime it is needed.
This way I won’t get myself in a position where I forget to recalculate the value of field3.
My question is this the best way to do it? Is there a way that whenever a depedent field change such as field1 that automatically change field3 without the need to visit a specific page?
I tried something with foriegn keys for field3 and try to add the value of two foriegn key inside the model.py but I don’t think it is allowed.
field3 = field1+ field2
any suggestions?
**
added the following example per request to further clarify question
If you notice that the totalPrice under transaction table is based on the price for the item and shipping. However, this require visiting order.html page.
My question if someone changed the item that resulted in a different price. Then without visiting the order.html page the totalprice in transaction table won't reflect the new price. Is there a way to build the "transaction" model in a way that updates the totalprice if any other field it depends on was updated without the need to visit the order.html page?
**
models.py
class Item(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, null=True)
price = models.FloatField(null=True)
class Shipping(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, null=True)
price = models.FloatField(null=True)
class Transaction(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, null=True)
total_price = models.FloatField(null=True)
views.py
def order(request):
item_obj = item.object.get(user=self.user)
ship_obj = shipping.object.get(user=self.user)
trans_obj = transaction.object.get(user=self.user)
trans_obj.total_price = item_obj.price + ship_obj.price
trans_obj.save()
return render(request, 'order.html')
You can override save function in Shipping and Item models.
def save(self, *args, **kwargs):
#change total_price
# Transaction.save(update_fields['total_price'])
super(Item, self).save(*args, **kwargs)
However "#change total_price" will depend on how you are going to relate your models and how you're going to find proper Shipping object

Django querysets select average and best result

I have a model like this:
class Quiz(models.Model):
user = models.ForeignKey(User, unique=True)
school_class = models.CharField(max_length=3, choices=klasa_choices)
points = models.DecimalField(max_digits=5, decimal_places=2,default=0)
date = models.DateTimeField(null=True,blank=True)
active = models.BooleanField(default=False)
Basically I'd like to get the average score (points) and the best result with the corresponding user for each school_class . Can this be done easily? (i.e without additional computing?)
So far I've come to:
Quiz.objects.values('school_class').annotate(avg=Avg('points'),max=Max('points')).order_by('-avg')
but how do I also get the user with the best score?
You need a second query. You can use the results of the first one to save on ordering your tables:
quizzes = Quiz.objects.values('school_class').annotate(avg=Avg('points'),max=Max('points')).order_by('-avg')
Quiz.objects.values('user','school_class').filter(points=quizzes.max)
If you only want one, add .latest('date'), .latest('-date'), or [0] to get just one.

Django models question: does not return model string representation name

Hello I seem to have a trouble with my models. Seems to me that I cannot name a string representation name. In python shell, I get an Payment object instead.
[<Payment: Payment object>, <Payment: Payment object>]
Here is my models. It should be more simpler if I could remove where I defined line_total but as you already know, it is not so simple to edit the models once you have generated the tables.
class Payment(models.Model):
unit_price = models.DecimalField(max_digits=12, decimal_places=2)
discount = models.DecimalField(max_digits=12, decimal_places=2)
payment_terms = models.CharField(max_length=80)
amount = models.DecimalField(max_digits=12, decimal_places=2)
line_total = models.DecimalField(max_digits=12, decimal_places=2)
def line_total():
unit_price -= discount
return line_total
def __unicode__(self):
return self.line_total
The indentation on __unicode__() is incorrect. It should be at the same level as line_total().
When your model is printed within a list, it's not __unicode__ method that is called but __repr__ (that you can also implement with usually a simpler output than __unicode__)
OK, look like I am going to keep it this way. I have removed line_total field because 1st reason, I do not really need it & 2nd reason is that somehow, there was no column that was created for accounts_payment.line_total. So in my mysql database. I have also now made it so it returns a unit_price rather than an object. Anyway thaks for everyone for helping me anyway.

Sumproduct using Django's aggregation

Question
Is it possible using Django's aggregation capabilities to calculate a sumproduct?
Background
I am modeling an invoice, which can contain multiple items. The many-to-many relationship between the Invoice and Item models is handled through the InvoiceItem intermediary table.
The total amount of the invoice—amount_invoiced—is calculated by summing the product of unit_price and quantity for each item on a given invoice. Below is the code that I'm currently using to accomplish this, but I was wondering if there is a better way to handle this using Django's aggregation capabilities.
Current Code
class Item(models.Model):
item_num = models.SlugField(unique=True)
description = models.CharField(blank=True, max_length=100)
class InvoiceItem(models.Model):
item = models.ForeignKey(Item)
invoice = models.ForeignKey('Invoice')
unit_price = models.DecimalField(max_digits=10, decimal_places=2)
quantity = models.DecimalField(max_digits=10, decimal_places=4)
class Invoice(models.Model):
invoice_num = models.SlugField(max_length=25)
invoice_items = models.ManyToManyField(Item,through='InvoiceItem')
def _get_amount_invoiced(self):
invoice_items = self.invoiceitem_set.all()
amount_invoiced = 0
for invoice_item in invoice_items:
amount_invoiced += (invoice_item.unit_price *
invoice_item.quantity)
return amount_invoiced
amount_invoiced = property(_get_amount_invoiced)
Yes, it is possible since Django 1.1 where aggregate functions were introduced. Here's a solution for your models:
def _get_amount_invoiced(self):
self.invoiceitem_set.extra(select=("item_total": "quantity * unit_price")
).aggregate(total=Sum("item_total")["total"]
It is, however, highly recommended to store item_total in a database, because it may be subject to discounts, taxes and other changes that make calculating it evety time impractical or even impossible.