diff --git a/InvenTree/InvenTree/status_codes.py b/InvenTree/InvenTree/status_codes.py index efb76b86fa..2082eac70b 100644 --- a/InvenTree/InvenTree/status_codes.py +++ b/InvenTree/InvenTree/status_codes.py @@ -54,6 +54,10 @@ class StatusCode: return codes + @classmethod + def text(cls, key): + return cls.options.get(key, None) + @classmethod def items(cls): return cls.options.items() @@ -150,6 +154,7 @@ class StockStatus(StatusCode): ATTENTION = 50 # Item requires attention DAMAGED = 55 # Item is damaged DESTROYED = 60 # Item is destroyed + REJECTED = 65 # Item is rejected LOST = 70 # Item has been lost RETURNED = 85 # Item has been returned from a customer @@ -167,6 +172,7 @@ class StockStatus(StatusCode): DAMAGED: _("Damaged"), DESTROYED: _("Destroyed"), LOST: _("Lost"), + REJECTED: _("Rejected"), RETURNED: _("Returned"), SHIPPED: _('Shipped'), ASSIGNED_TO_BUILD: _("Used for Build"), @@ -178,6 +184,7 @@ class StockStatus(StatusCode): ATTENTION: 'yellow', DAMAGED: 'red', DESTROYED: 'red', + REJECTED: 'red', SHIPPED: 'green', ASSIGNED_TO_BUILD: 'blue', ASSIGNED_TO_OTHER_ITEM: 'blue', @@ -195,11 +202,21 @@ class StockStatus(StatusCode): UNAVAILABLE_CODES = [ DESTROYED, LOST, + REJECTED, SHIPPED, ASSIGNED_TO_BUILD, ASSIGNED_TO_OTHER_ITEM, ] + # The following codes are available for receiving goods + RECEIVING_CODES = [ + OK, + ATTENTION, + DAMAGED, + DESTROYED, + REJECTED + ] + class BuildStatus(StatusCode): diff --git a/InvenTree/order/models.py b/InvenTree/order/models.py index 6198eb16bc..56da6c783d 100644 --- a/InvenTree/order/models.py +++ b/InvenTree/order/models.py @@ -209,7 +209,7 @@ class PurchaseOrder(Order): return self.pending_line_items().count() == 0 @transaction.atomic - def receive_line_item(self, line, location, quantity, user): + def receive_line_item(self, line, location, quantity, user, status=StockStatus.OK): """ Receive a line item (or partial line item) against this PO """ @@ -230,7 +230,9 @@ class PurchaseOrder(Order): supplier_part=line.part, location=location, quantity=quantity, - purchase_order=self) + purchase_order=self, + status=status + ) stock.save() diff --git a/InvenTree/order/templates/order/purchase_order_detail.html b/InvenTree/order/templates/order/purchase_order_detail.html index 73c794126c..eb796b2ec1 100644 --- a/InvenTree/order/templates/order/purchase_order_detail.html +++ b/InvenTree/order/templates/order/purchase_order_detail.html @@ -145,7 +145,7 @@ $("#po-table").inventreeTable({ title: '{% trans "Part" %}', formatter: function(value, row, index, field) { if (row.part) { - return imageHoverIcon(row.part_detail.thumbnail) + renderLink(row.part_detail.full_name, `/part/${value}/`); + return imageHoverIcon(row.part_detail.thumbnail) + renderLink(row.part_detail.full_name, `/part/${row.part_detail.pk}/`); } else { return '-'; } diff --git a/InvenTree/order/templates/order/receive_parts.html b/InvenTree/order/templates/order/receive_parts.html index f71959cdc0..082fe083b9 100644 --- a/InvenTree/order/templates/order/receive_parts.html +++ b/InvenTree/order/templates/order/receive_parts.html @@ -1,23 +1,28 @@ {% extends "modal_form.html" %} +{% load i18n %} +{% load inventree_extras %} +{% load status_codes %} {% block form %} -Receive outstanding parts for {{ order }} - {{ order.description }} +{% trans "Receive outstanding parts for" %} {{ order }} - {{ order.description }}
{% csrf_token %} {% load crispy_forms_tags %} - -

Select parts to receive against this order.

+ +

{% trans "Select parts to receive against this order" %}

- - - - - + + + + + + + {% for line in lines %} @@ -28,20 +33,29 @@ Receive outstanding parts for {{ order }} - {{ order.description }} {% else %} - + {% endif %} - - + + + diff --git a/InvenTree/order/views.py b/InvenTree/order/views.py index 2459f83abf..02132d34cc 100644 --- a/InvenTree/order/views.py +++ b/InvenTree/order/views.py @@ -29,7 +29,7 @@ from . import forms as order_forms from InvenTree.views import AjaxView, AjaxCreateView, AjaxUpdateView, AjaxDeleteView from InvenTree.helpers import DownloadFile, str2bool -from InvenTree.status_codes import PurchaseOrderStatus +from InvenTree.status_codes import PurchaseOrderStatus, StockStatus logger = logging.getLogger(__name__) @@ -669,6 +669,20 @@ class PurchaseOrderReceive(AjaxUpdateView): except (PurchaseOrderLineItem.DoesNotExist, ValueError): continue + # Check that the StockStatus was set + status_key = 'status-{pk}'.format(pk=pk) + status = request.POST.get(status_key, StockStatus.OK) + + try: + status = int(status) + except ValueError: + status = StockStatus.OK + + if status in StockStatus.RECEIVING_CODES: + line.status_code = status + else: + line.status_code = StockStatus.OK + # Check that line matches the order if not line.order == self.order: # TODO - Display a non-field error? @@ -725,7 +739,13 @@ class PurchaseOrderReceive(AjaxUpdateView): if not line.part: continue - self.order.receive_line_item(line, self.destination, line.receive_quantity, self.request.user) + self.order.receive_line_item( + line, + self.destination, + line.receive_quantity, + self.request.user, + status=line.status_code, + ) class OrderParts(AjaxView): diff --git a/InvenTree/part/templatetags/status_codes.py b/InvenTree/part/templatetags/status_codes.py index 2d4dc77113..ea334970c2 100644 --- a/InvenTree/part/templatetags/status_codes.py +++ b/InvenTree/part/templatetags/status_codes.py @@ -28,6 +28,11 @@ def stock_status_label(key, *args, **kwargs): return mark_safe(StockStatus.render(key, large=kwargs.get('large', False))) +@register.simple_tag +def stock_status_text(key, *args, **kwargs): + return mark_safe(StockStatus.text(key)) + + @register.simple_tag def build_status_label(key, *args, **kwargs): """ Render a Build status label """ diff --git a/InvenTree/stock/migrations/0035_auto_20200502_2308.py b/InvenTree/stock/migrations/0035_auto_20200502_2308.py new file mode 100644 index 0000000000..8cf02481e5 --- /dev/null +++ b/InvenTree/stock/migrations/0035_auto_20200502_2308.py @@ -0,0 +1,19 @@ +# Generated by Django 3.0.5 on 2020-05-02 23:08 + +import django.core.validators +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('stock', '0034_auto_20200426_0602'), + ] + + operations = [ + migrations.AlterField( + model_name='stockitem', + name='status', + field=models.PositiveIntegerField(choices=[(10, 'OK'), (50, 'Attention needed'), (55, 'Damaged'), (60, 'Destroyed'), (70, 'Lost'), (65, 'Rejected'), (85, 'Returned'), (110, 'Shipped'), (120, 'Used for Build'), (130, 'Installed in Stock Item')], default=10, validators=[django.core.validators.MinValueValidator(0)]), + ), + ]
PartOrder CodeOn OrderReceivedReceive{% trans "Part" %}{% trans "Order Code" %}{% trans "On Order" %}{% trans "Received" %}{% trans "Receive" %}{% trans "Status" %}
{{ line.part.SKU }}Referenced part has been removed{% trans "Error: Referenced part has been removed" %}{{ line.quantity }}{{ line.received }}{% decimal line.quantity %}{% decimal line.received %}
- +
+
+ +
+