How to make a Purchase Order app?

274 views
Skip to first unread message

Alexander Joseph

unread,
Aug 2, 2018, 1:27:21 PM8/2/18
to Django users
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!

Andréas Kühne

unread,
Aug 3, 2018, 3:44:15 AM8/3/18
to django...@googlegroups.com
Hi,

I think you are doing it right. I would remove the PurchaseOrderTotal model however. The reason for this is that the purchase order itself should know it's total and so on. Either you can add a signal on the save of the PurchaseOrderItem - and update the PurchaseOrder total or the PurchaseOrder could calculate the total when required via a method.

I think the easiest way would be to add a method that calculates the total:

@property
def subtotal(self):
    order_total = 0
    for item in self.purchaseorderitem_set.all():
        order_total += item.qty * item.unit_price
    return order_total

Now it is a property on the purchase order. So calling: purchase_order.subtotal will get the total for the purchase order.

2 other things:
1. You can remove the amount in the PurchaseOrderItem and calculate the total for the row when required (see my example above) - this of course depends on WHEN you want to show the total. The same thing goes for calculating the total on the PurchaseOrder. You could add a property the same way I did for the PurchaseOrder in the PurchaseOrderItem class.
2. The names you have given your properties that are foreign keys are not that good from an object oriented perspective. I would for example call the invoice_number field on the PurchaseOrder as "invoice". The same goes for the po_number_fk fields. Because when you assign them or when you want to get a value from the invoice it would be easier to read - purchase_order.invoice.id than purchase_order.invoice_number.id.

Hope that helps somewhat.

If you don't want to calculate the total each time (it all depends on the usage), you can use signals to see when a model is saved/created and then update the subtotal in the PurchaseOrder. See here for information about signals: https://docs.djangoproject.com/en/2.0/topics/signals/

Andréas

--
You received this message because you are subscribed to the Google Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-users+unsubscribe@googlegroups.com.
To post to this group, send email to django...@googlegroups.com.
Visit this group at https://groups.google.com/group/django-users.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-users/dc6a1cb9-0770-49d9-9c89-92d1f947e718%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Alexander Joseph

unread,
Aug 3, 2018, 4:58:52 PM8/3/18
to Django users
Thanks Andréas, very helpful!
One more question - 
Since I'm using class based views and this view requires 2 models I'm unsure how to fetch the data from both the models to use in the same view. It looks like I need to modify the get_context_data() method but I'm unsure how to filter this way. Here is my view now

class PurchaseOrderDetailView(LoginRequiredMixin, TemplateView):
   #context_object_name = "po"
   #model = PurchaseOrder
   template_name = 'financial/purchase_orders/purchase_order_detail.html'

    def get_context_data(self, **kwargs):
       context = super(PurchaseOrderDetailView, self).get_context_data(**kwargs)
       context['purchase_order'] = PurchaseOrder.objects.get()
       context['item'] = PurchaseOrderItem.objects.all()
       return context


So basically what I'm trying to do is filter the PurchaseOrder.objects.get() so that the purchase_order.pk = the pk in the url, and then filter the PurchaseOrderItem.objects.get() so that the item.po_number_fk = purchase_order.pk

Thanks again for all your help!



Andréas

To unsubscribe from this group and stop receiving emails from it, send an email to django-users...@googlegroups.com.

Andréas Kühne

unread,
Aug 4, 2018, 5:56:27 AM8/4/18
to django...@googlegroups.com
Ok.

First of all you don't really need to get both. What you need to do is get the PurchaseOrder connected to the url - then you get the items from the purchase order.

So what I would do is the following:

First make sure that you hvae the right url definition, something like this would work:

path('purchase_order/<int:pk>/', views.PurchaseOrderDetailView.as_view(), name='purchase-order-detail'),
Then use the DetailView from django to extend - this does a lot of magic under the hood, and is much better to use.

class PurchaseOrderDetailView(LoginRequiredMixin, DetailView):

model = PurchaseOrder
template_name = 'financial/purchase_orders/purchase_order_detail.html'

    def get_object(self, queryset=None):
return self.get_queryset().get(po_number=self.kwargs.get('pk'))
Now the reason for this is that you need to write a lot less code. Another question is why you don't use the default id field on the purchase order? Because if you did, you wouldn't need to add the get_object() method to the detail view. Also if you follow the correct layout of your templates (in this case it should be called purchaseorders/purchaseorder_defailt.html and you don't need to add a template name either). Essentially the code for your detail view could be only:
class PurchaseOrderDetailView(LoginRequiredMixin, DetailView):
model = PurchaseOrder

If you follow the guidelines that the detail view requires :-)

If you use my method here you will get the purchase order from the context object: purchaseorder so your template would be able to access the information in the purchase order by writing:

{{ purchaseorder.info }}
Then to get the items of the purchase order all you need to do is iterate over them:

{% for item in purchaseorder.purchaseorderitem_set %}
{{ item.qty }}
{% endfor %}

You can look at the following tutorial that explains the usage of generic class based views:
https://developer.mozilla.org/en-US/docs/Learn/Server-side/Django/Generic_views

I find woring WITH the framework is much easier than reinventing the wheel :-)

Regards,

Andréas

To unsubscribe from this group and stop receiving emails from it, send an email to django-users+unsubscribe@googlegroups.com.

To post to this group, send email to django...@googlegroups.com.
Visit this group at https://groups.google.com/group/django-users.
Reply all
Reply to author
Forward
0 new messages