Merge pull request #780 from SchrodingersGat/rejected-stock

Rejected stock
This commit is contained in:
Oliver 2020-05-04 08:41:34 +10:00 committed by GitHub
commit 779c1e1d58
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 95 additions and 18 deletions

View File

@ -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):

View File

@ -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()

View File

@ -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 '-';
}

View File

@ -1,23 +1,28 @@
{% extends "modal_form.html" %}
{% load i18n %}
{% load inventree_extras %}
{% load status_codes %}
{% block form %}
Receive outstanding parts for <b>{{ order }}</b> - <i>{{ order.description }}</i>
{% trans "Receive outstanding parts for" %} <b>{{ order }}</b> - <i>{{ order.description }}</i>
<form method='post' action='' class='js-modal-form' enctype='multipart/form-data'>
{% csrf_token %}
{% load crispy_forms_tags %}
<label class='control-label'>Parts</label>
<p class='help-block'>Select parts to receive against this order.</p>
<label class='control-label'>{% trans "Parts" %}</label>
<p class='help-block'>{% trans "Select parts to receive against this order" %}</p>
<table class='table table-striped'>
<tr>
<th>Part</th>
<th>Order Code</th>
<th>On Order</th>
<th>Received</th>
<th>Receive</th>
<th>{% trans "Part" %}</th>
<th>{% trans "Order Code" %}</th>
<th>{% trans "On Order" %}</th>
<th>{% trans "Received" %}</th>
<th>{% trans "Receive" %}</th>
<th>{% trans "Status" %}</th>
<th></th>
</tr>
{% for line in lines %}
<tr id='line_row_{{ line.id }}'>
@ -28,20 +33,29 @@ Receive outstanding parts for <b>{{ order }}</b> - <i>{{ order.description }}</i
</td>
<td>{{ line.part.SKU }}</td>
{% else %}
<td colspan='2'>Referenced part has been removed</td>
<td colspan='2'>{% trans "Error: Referenced part has been removed" %}</td>
{% endif %}
<td>{{ line.quantity }}</td>
<td>{{ line.received }}</td>
<td>{% decimal line.quantity %}</td>
<td>{% decimal line.received %}</td>
<td>
<div class='control-group'>
<div class='controls'>
<input class='numberinput' type='number' min='0' value='{{ line.receive_quantity }}' name='line-{{ line.id }}'/>
<input class='numberinput' type='number' min='0' value='{% decimal line.receive_quantity %}' name='line-{{ line.id }}'/>
</div>
</div>
</td>
<td>
<div class='control-group'>
<select class='select' name='status-{{ line.id }}'>
{% for code in StockStatus.RECEIVING_CODES %}
<option value="{{ code }}" {% if code|add:"0" == line.status_code|add:"0" %}selected="selected"{% endif %}>{% stock_status_text code %}</option>
{% endfor %}
</select>
</div>
</td>
<td>
<button class='btn btn-default btn-remove' onClick="removeOrderRowFromOrderWizard()" id='del_item_{{ line.id }}' title='Remove line' type='button'>
<span row='line_row_{{ line.id }}' class='fas fa-trash-alt icon-red'></span>
<span row='line_row_{{ line.id }}' class='fas fa-times-circle icon-red'></span>
</button>
</td>
</tr>

View File

@ -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):

View File

@ -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 """

View File

@ -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)]),
),
]