From 5769befb04d6af73458e0e9b4faf5af9c8b2965c Mon Sep 17 00:00:00 2001 From: Oliver Date: Wed, 18 Apr 2018 00:22:25 +1000 Subject: [PATCH] Add model for supplier order (PO) - Added create page - Added detail page --- InvenTree/supplier/forms.py | 23 ++++++++++ .../migrations/0009_auto_20180417_1411.py | 42 +++++++++++++++++ .../migrations/0010_auto_20180417_1420.py | 20 +++++++++ InvenTree/supplier/models.py | 45 +++++++++++++++++++ .../templates/supplier/order_create.html | 5 +++ .../templates/supplier/order_detail.html | 7 +++ InvenTree/supplier/urls.py | 26 +++++++++-- InvenTree/supplier/views.py | 15 +++++++ 8 files changed, 180 insertions(+), 3 deletions(-) create mode 100644 InvenTree/supplier/migrations/0009_auto_20180417_1411.py create mode 100644 InvenTree/supplier/migrations/0010_auto_20180417_1420.py create mode 100644 InvenTree/supplier/templates/supplier/order_create.html create mode 100644 InvenTree/supplier/templates/supplier/order_detail.html diff --git a/InvenTree/supplier/forms.py b/InvenTree/supplier/forms.py index 10167ee8d0..b154d209ea 100644 --- a/InvenTree/supplier/forms.py +++ b/InvenTree/supplier/forms.py @@ -3,6 +3,29 @@ from crispy_forms.helper import FormHelper from crispy_forms.layout import Submit from .models import Supplier, SupplierPart +from .models import SupplierOrder + + +class EditSupplierOrderForm(forms.ModelForm): + + def __init__(self, *args, **kwargs): + super(EditSupplierOrderForm, self).__init__(*args, **kwargs) + self.helper = FormHelper() + + self.helper.form_id = 'id-edit-part-form' + self.helper.form_class = 'blueForms' + self.helper.form_method = 'post' + + self.helper.add_input(Submit('submit', 'Submit')) + + class Meta: + model = SupplierOrder + fields = [ + 'internal_ref', + 'supplier', + 'notes', + 'issued_date', + ] class EditSupplierForm(forms.ModelForm): diff --git a/InvenTree/supplier/migrations/0009_auto_20180417_1411.py b/InvenTree/supplier/migrations/0009_auto_20180417_1411.py new file mode 100644 index 0000000000..b89f2adcf6 --- /dev/null +++ b/InvenTree/supplier/migrations/0009_auto_20180417_1411.py @@ -0,0 +1,42 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.12 on 2018-04-17 14:11 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('supplier', '0008_delete_customer'), + ] + + operations = [ + migrations.CreateModel( + name='SupplierOrder', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('internal_ref', models.CharField(max_length=25, unique=True)), + ('created_date', models.DateField(auto_now_add=True)), + ('issued_date', models.DateField(blank=True, help_text='Date the purchase order was issued')), + ('notes', models.TextField(blank=True, help_text='Order notes')), + ('supplier', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='supplier.Supplier')), + ], + ), + migrations.CreateModel( + name='SupplierOrderLineItem', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('line_number', models.PositiveIntegerField(default=1)), + ('quantity', models.PositiveIntegerField(default=1)), + ('notes', models.TextField(blank=True)), + ('order', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='supplier.SupplierOrder')), + ('part', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='supplier.SupplierPart')), + ], + ), + migrations.AlterUniqueTogether( + name='supplierorderlineitem', + unique_together=set([('order', 'line_number'), ('order', 'part')]), + ), + ] diff --git a/InvenTree/supplier/migrations/0010_auto_20180417_1420.py b/InvenTree/supplier/migrations/0010_auto_20180417_1420.py new file mode 100644 index 0000000000..a8e8a8436c --- /dev/null +++ b/InvenTree/supplier/migrations/0010_auto_20180417_1420.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.12 on 2018-04-17 14:20 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('supplier', '0009_auto_20180417_1411'), + ] + + operations = [ + migrations.AlterField( + model_name='supplierorder', + name='issued_date', + field=models.DateField(blank=True, help_text='Date the purchase order was issued', null=True), + ), + ] diff --git a/InvenTree/supplier/models.py b/InvenTree/supplier/models.py index c3ef7a57f3..83c3a4b850 100644 --- a/InvenTree/supplier/models.py +++ b/InvenTree/supplier/models.py @@ -103,3 +103,48 @@ class SupplierPriceBreak(models.Model): cost=self.cost, currency=self.currency if self.currency else '', quan=self.quantity) + + +class SupplierOrder(models.Model): + """ + An order of parts from a supplier, made up of multiple line items + """ + + def get_absolute_url(self): + return "/supplier/order/{id}/".format(id=self.id) + + # Interal reference for this order + internal_ref = models.CharField(max_length=25, unique=True) + + supplier = models.ForeignKey(Supplier, on_delete=models.CASCADE) + + created_date = models.DateField(auto_now_add=True, editable=False) + + issued_date = models.DateField(blank=True, null=True, help_text="Date the purchase order was issued") + + notes = models.TextField(blank=True, help_text="Order notes") + + def __str__(self): + return "PO {ref}".format(ref=self.internal_ref) + + +class SupplierOrderLineItem(models.Model): + """ + A line item in a supplier order, corresponding to some quantity of part + """ + + class Meta: + unique_together = [ + ('order', 'line_number'), + ('order', 'part'), + ] + + order = models.ForeignKey(SupplierOrder, on_delete=models.CASCADE) + + line_number = models.PositiveIntegerField(default=1) + + part = models.ForeignKey(SupplierPart, null=True, blank=True, on_delete=models.SET_NULL) + + quantity = models.PositiveIntegerField(default=1) + + notes = models.TextField(blank=True) diff --git a/InvenTree/supplier/templates/supplier/order_create.html b/InvenTree/supplier/templates/supplier/order_create.html new file mode 100644 index 0000000000..d401662ea3 --- /dev/null +++ b/InvenTree/supplier/templates/supplier/order_create.html @@ -0,0 +1,5 @@ +{% extends "create_edit_obj.html" %} + +{% block obj_title %} +Create a new supplier purchase order +{% endblock %} \ No newline at end of file diff --git a/InvenTree/supplier/templates/supplier/order_detail.html b/InvenTree/supplier/templates/supplier/order_detail.html new file mode 100644 index 0000000000..1a9f650410 --- /dev/null +++ b/InvenTree/supplier/templates/supplier/order_detail.html @@ -0,0 +1,7 @@ +{% include "base.html" %} + +{% block content %} + +Details for supplier order {{ order.id }}. + +{% endblock %} \ No newline at end of file diff --git a/InvenTree/supplier/urls.py b/InvenTree/supplier/urls.py index 94321f734e..16b870a086 100644 --- a/InvenTree/supplier/urls.py +++ b/InvenTree/supplier/urls.py @@ -61,10 +61,30 @@ supplier_part_detail_urls = [ url('^.*$', views.SupplierPartDetail.as_view(), name='supplier-part-detail'), ] -supplier_urls = [ - url(r'part/(?P\d+)/', include(supplier_part_detail_urls)), +supplier_part_urls = [ + url(r'^new/?', views.SupplierPartCreate.as_view(), name='supplier-part-create'), - url(r'part/new/?', views.SupplierPartCreate.as_view(), name='supplier-part-create'), + url(r'^(?P\d+)/', include(supplier_part_detail_urls)), +] + +supplier_order_detail_urls = [ + + + url('^.*$', views.SupplierOrderDetail.as_view(), name='supplier-order-detail'), +] + +supplier_order_urls = [ + url(r'^new/?', views.SupplierOrderCreate.as_view(), name='supplier-order-create'), + + url(r'^(?P\d+)/', include(supplier_order_detail_urls)), +] + +supplier_urls = [ + + + url(r'part/', include(supplier_part_urls)), + + url(r'order/', include(supplier_order_urls)), url(r'new/?', views.SupplierCreate.as_view(), name='supplier-create'), diff --git a/InvenTree/supplier/views.py b/InvenTree/supplier/views.py index c64a0b07dc..c49b18586e 100644 --- a/InvenTree/supplier/views.py +++ b/InvenTree/supplier/views.py @@ -6,9 +6,24 @@ from django.views.generic.edit import UpdateView, DeleteView, CreateView from part.models import Part from .models import Supplier, SupplierPart +from .models import SupplierOrder from .forms import EditSupplierForm from .forms import EditSupplierPartForm +from .forms import EditSupplierOrderForm + +class SupplierOrderDetail(DetailView): + context_object_name = 'order' + model = SupplierOrder + template_name = 'supplier/order_detail.html' + queryset = SupplierOrder.objects.all() + + +class SupplierOrderCreate(CreateView): + model = SupplierOrder + form_class = EditSupplierOrderForm + context_object_name = 'supplier' + template_name = 'supplier/order_create.html' class SupplierIndex(ListView):