diff --git a/InvenTree/order/forms.py b/InvenTree/order/forms.py
new file mode 100644
index 0000000000..627b5f130e
--- /dev/null
+++ b/InvenTree/order/forms.py
@@ -0,0 +1,23 @@
+"""
+Django Forms for interacting with Order objects
+"""
+
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from InvenTree.forms import HelperForm
+
+from .models import PurchaseOrder, PurchaseOrderLineItem
+
+
+class EditPurchaseOrderLineItemForm(HelperForm):
+
+ class Meta:
+ model = PurchaseOrderLineItem
+ fields = [
+ 'order',
+ 'part',
+ 'quantity',
+ 'reference',
+ 'received'
+ ]
\ No newline at end of file
diff --git a/InvenTree/order/migrations/0005_purchaseorderlineitem_part.py b/InvenTree/order/migrations/0005_purchaseorderlineitem_part.py
new file mode 100644
index 0000000000..61f0944156
--- /dev/null
+++ b/InvenTree/order/migrations/0005_purchaseorderlineitem_part.py
@@ -0,0 +1,20 @@
+# Generated by Django 2.2 on 2019-06-05 10:36
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('company', '0005_auto_20190525_2356'),
+ ('order', '0004_purchaseorder_status'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='purchaseorderlineitem',
+ name='part',
+ field=models.ForeignKey(blank=True, help_text='Supplier part', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='orders', to='company.SupplierPart'),
+ ),
+ ]
diff --git a/InvenTree/order/migrations/0006_auto_20190605_2056.py b/InvenTree/order/migrations/0006_auto_20190605_2056.py
new file mode 100644
index 0000000000..e938343d01
--- /dev/null
+++ b/InvenTree/order/migrations/0006_auto_20190605_2056.py
@@ -0,0 +1,18 @@
+# Generated by Django 2.2 on 2019-06-05 10:56
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('company', '0005_auto_20190525_2356'),
+ ('order', '0005_purchaseorderlineitem_part'),
+ ]
+
+ operations = [
+ migrations.AlterUniqueTogether(
+ name='purchaseorderlineitem',
+ unique_together={('order', 'part')},
+ ),
+ ]
diff --git a/InvenTree/order/models.py b/InvenTree/order/models.py
index e9188dc869..56ef5952fb 100644
--- a/InvenTree/order/models.py
+++ b/InvenTree/order/models.py
@@ -4,7 +4,7 @@ from django.contrib.auth.models import User
from django.utils.translation import ugettext as _
-from company.models import Company
+from company.models import Company, SupplierPart
from InvenTree.status_codes import OrderStatus
@@ -108,11 +108,24 @@ class PurchaseOrderLineItem(OrderLineItem):
"""
- order = models.ForeignKey(PurchaseOrder, on_delete=models.CASCADE,
- related_name='lines',
- help_text=_('Purchase Order')
- )
+ class Meta:
+ unique_together = (
+ ('order', 'part')
+ )
+
+ order = models.ForeignKey(
+ PurchaseOrder, on_delete=models.CASCADE,
+ related_name='lines',
+ help_text=_('Purchase Order')
+ )
# TODO - foreign key references to part and stockitem objects
+ part = models.ForeignKey(
+ SupplierPart, on_delete=models.SET_NULL,
+ blank=True, null=True,
+ related_name='orders',
+ help_text=_("Supplier part"),
+ )
+
received = models.PositiveIntegerField(default=0, help_text=_('Number of items received'))
diff --git a/InvenTree/order/templates/order/purchase_order_detail.html b/InvenTree/order/templates/order/purchase_order_detail.html
index bea88a21ba..2d40de6459 100644
--- a/InvenTree/order/templates/order/purchase_order_detail.html
+++ b/InvenTree/order/templates/order/purchase_order_detail.html
@@ -1,9 +1,106 @@
{% extends "base.html" %}
-{% block content %}
+{% load static %}
-Purchase Order: {{ order.reference }}
-
-Description: {{ order.description }}
+{% block page_title %}
+InvenTree | {{ order }}
+{% endblock %}
+
+{% block content %}
+
+
+
+
+
+ Status |
+ {% include "order/order_status.html" %} |
+
+
+ Created |
+ {{ order.creation_date }} |
+
+
+ Created By |
+ {{ order.created_by }} |
+
+ {% if order.issue_date %}
+
+ Issued |
+ {{ order.issue_date }} |
+
+ {% endif %}
+
+
+
+
+
+
+Order Items
+
+
+
+
+
+ Line |
+ Part |
+ Reference |
+ Quantity |
+ Received |
+
+ {% for line in order.lines.all %}
+
+ {{ forloop.counter }} |
+ |
+ {{ line.reference }} |
+ {{ line.quantity }} |
+ {{ line.received }} |
+
+ {% endfor %}
+
+
+{% if order.notes %}
+
+
+
Notes
+
{{ order.notes }}
+
+{% endif %}
+
+{% endblock %}
+
+{% block js_ready %}
+
+$('#new-po-line').click(function() {
+ launchModalForm("{% url 'po-line-item-create' %}",
+ {
+ reload: true,
+ data: {
+ order: {{ order.id }},
+ },
+ }
+ );
+});
+
+$("#po-lines-table").bootstrapTable({
+});
{% endblock %}
\ No newline at end of file
diff --git a/InvenTree/order/templates/order/purchase_orders.html b/InvenTree/order/templates/order/purchase_orders.html
index f85de6c962..4e48effa59 100644
--- a/InvenTree/order/templates/order/purchase_orders.html
+++ b/InvenTree/order/templates/order/purchase_orders.html
@@ -2,6 +2,10 @@
{% load static %}
+{% block page_title %}
+InvenTree | Purchase Orders
+{% endblock %}
+
{% block content %}
Purchase Orders
diff --git a/InvenTree/order/urls.py b/InvenTree/order/urls.py
index 961cbb6857..9aa350fc54 100644
--- a/InvenTree/order/urls.py
+++ b/InvenTree/order/urls.py
@@ -14,11 +14,18 @@ purchase_order_detail_urls = [
url(r'^.*$', views.PurchaseOrderDetail.as_view(), name='purchase-order-detail'),
]
+po_line_urls = [
+
+ url(r'^new/', views.POLineItemCreate.as_view(), name='po-line-item-create'),
+]
+
purchase_order_urls = [
# Display detail view for a single purchase order
url(r'^(?P\d+)/', include(purchase_order_detail_urls)),
+ url(r'^line/', include(po_line_urls)),
+
# Display complete list of purchase orders
url(r'^.*$', views.PurchaseOrderIndex.as_view(), name='purchase-order-index'),
]
diff --git a/InvenTree/order/views.py b/InvenTree/order/views.py
index add27967d7..ac84ee1f4f 100644
--- a/InvenTree/order/views.py
+++ b/InvenTree/order/views.py
@@ -7,7 +7,10 @@ from __future__ import unicode_literals
from django.views.generic import DetailView, ListView
-from .models import PurchaseOrder
+from .models import PurchaseOrder, PurchaseOrderLineItem
+from .forms import EditPurchaseOrderLineItemForm
+
+from InvenTree.views import AjaxCreateView, AjaxUpdateView, AjaxDeleteView
from InvenTree.status_codes import OrderStatus
@@ -19,8 +22,8 @@ class PurchaseOrderIndex(ListView):
template_name = 'order/purchase_orders.html'
context_object_name = 'orders'
- def get_context_data(self):
- ctx = super().get_context_data()
+ def get_context_data(self, **kwargs):
+ ctx = super().get_context_data(**kwargs)
ctx['OrderStatus'] = OrderStatus
@@ -33,3 +36,76 @@ class PurchaseOrderDetail(DetailView):
context_object_name = 'order'
queryset = PurchaseOrder.objects.all().prefetch_related('lines')
template_name = 'order/purchase_order_detail.html'
+
+ def get_context_data(self, **kwargs):
+ ctx = super().get_context_data(**kwargs)
+
+ ctx['OrderStatus'] = OrderStatus
+
+ return ctx
+
+
+class POLineItemCreate(AjaxCreateView):
+ """ AJAX view for creating a new PurchaseOrderLineItem object
+ """
+
+ model = PurchaseOrderLineItem
+ context_object_name = 'line'
+ form_class = EditPurchaseOrderLineItemForm
+ ajax_template_name = 'modal_form.html'
+ ajax_form_action = 'Add Line Item'
+
+ def get_form(self):
+ """ Limit choice options based on the selected order, etc
+ """
+
+ form = super().get_form()
+
+ order_id = form['order'].value()
+
+ try:
+ order = PurchaseOrder.objects.get(id=order_id)
+
+ query = form.fields['part'].queryset
+
+ # Only allow parts from the selected supplier
+ query = query.filter(supplier=order.supplier.id)
+
+ print('limiting queryset')
+
+ form.fields['part'].queryset = query
+ except PurchaseOrder.DoesNotExist:
+ print('error')
+ pass
+
+ return form
+
+
+ def get_initial(self):
+ """ Extract initial data for the line item.
+
+ - The 'order' will be passed as a query parameter
+ - Use this to set the 'order' field and limit the options for 'part'
+ """
+
+ initials = super().get_initial().copy()
+
+ order_id = self.request.GET.get('order', None)
+
+ if order_id:
+ try:
+ order = PurchaseOrder.objects.get(id=order_id)
+ initials['order'] = order
+
+ except PurchaseOrder.DoesNotExist:
+ pass
+
+ return initials
+
+
+class POLineItemEdit(AjaxUpdateView):
+
+ model = PurchaseOrderLineItem
+ form_class = EditPurchaseOrderLineItemForm
+ ajax_template_name = 'modal_form.html'
+ ajax_form_action = 'Edit Line Item'
diff --git a/InvenTree/static/css/inventree.css b/InvenTree/static/css/inventree.css
index da7d38d250..794676ca68 100644
--- a/InvenTree/static/css/inventree.css
+++ b/InvenTree/static/css/inventree.css
@@ -297,6 +297,10 @@
margin-bottom: 5px;
}
+.panel-body {
+ padding: 10px;
+}
+
.panel-group .panel {
border-radius: 2px;
}