mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Merge pull request #780 from SchrodingersGat/rejected-stock
Rejected stock
This commit is contained in:
commit
779c1e1d58
@ -54,6 +54,10 @@ class StatusCode:
|
|||||||
|
|
||||||
return codes
|
return codes
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def text(cls, key):
|
||||||
|
return cls.options.get(key, None)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def items(cls):
|
def items(cls):
|
||||||
return cls.options.items()
|
return cls.options.items()
|
||||||
@ -150,6 +154,7 @@ class StockStatus(StatusCode):
|
|||||||
ATTENTION = 50 # Item requires attention
|
ATTENTION = 50 # Item requires attention
|
||||||
DAMAGED = 55 # Item is damaged
|
DAMAGED = 55 # Item is damaged
|
||||||
DESTROYED = 60 # Item is destroyed
|
DESTROYED = 60 # Item is destroyed
|
||||||
|
REJECTED = 65 # Item is rejected
|
||||||
LOST = 70 # Item has been lost
|
LOST = 70 # Item has been lost
|
||||||
RETURNED = 85 # Item has been returned from a customer
|
RETURNED = 85 # Item has been returned from a customer
|
||||||
|
|
||||||
@ -167,6 +172,7 @@ class StockStatus(StatusCode):
|
|||||||
DAMAGED: _("Damaged"),
|
DAMAGED: _("Damaged"),
|
||||||
DESTROYED: _("Destroyed"),
|
DESTROYED: _("Destroyed"),
|
||||||
LOST: _("Lost"),
|
LOST: _("Lost"),
|
||||||
|
REJECTED: _("Rejected"),
|
||||||
RETURNED: _("Returned"),
|
RETURNED: _("Returned"),
|
||||||
SHIPPED: _('Shipped'),
|
SHIPPED: _('Shipped'),
|
||||||
ASSIGNED_TO_BUILD: _("Used for Build"),
|
ASSIGNED_TO_BUILD: _("Used for Build"),
|
||||||
@ -178,6 +184,7 @@ class StockStatus(StatusCode):
|
|||||||
ATTENTION: 'yellow',
|
ATTENTION: 'yellow',
|
||||||
DAMAGED: 'red',
|
DAMAGED: 'red',
|
||||||
DESTROYED: 'red',
|
DESTROYED: 'red',
|
||||||
|
REJECTED: 'red',
|
||||||
SHIPPED: 'green',
|
SHIPPED: 'green',
|
||||||
ASSIGNED_TO_BUILD: 'blue',
|
ASSIGNED_TO_BUILD: 'blue',
|
||||||
ASSIGNED_TO_OTHER_ITEM: 'blue',
|
ASSIGNED_TO_OTHER_ITEM: 'blue',
|
||||||
@ -195,11 +202,21 @@ class StockStatus(StatusCode):
|
|||||||
UNAVAILABLE_CODES = [
|
UNAVAILABLE_CODES = [
|
||||||
DESTROYED,
|
DESTROYED,
|
||||||
LOST,
|
LOST,
|
||||||
|
REJECTED,
|
||||||
SHIPPED,
|
SHIPPED,
|
||||||
ASSIGNED_TO_BUILD,
|
ASSIGNED_TO_BUILD,
|
||||||
ASSIGNED_TO_OTHER_ITEM,
|
ASSIGNED_TO_OTHER_ITEM,
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# The following codes are available for receiving goods
|
||||||
|
RECEIVING_CODES = [
|
||||||
|
OK,
|
||||||
|
ATTENTION,
|
||||||
|
DAMAGED,
|
||||||
|
DESTROYED,
|
||||||
|
REJECTED
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
class BuildStatus(StatusCode):
|
class BuildStatus(StatusCode):
|
||||||
|
|
||||||
|
@ -209,7 +209,7 @@ class PurchaseOrder(Order):
|
|||||||
return self.pending_line_items().count() == 0
|
return self.pending_line_items().count() == 0
|
||||||
|
|
||||||
@transaction.atomic
|
@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
|
""" Receive a line item (or partial line item) against this PO
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@ -230,7 +230,9 @@ class PurchaseOrder(Order):
|
|||||||
supplier_part=line.part,
|
supplier_part=line.part,
|
||||||
location=location,
|
location=location,
|
||||||
quantity=quantity,
|
quantity=quantity,
|
||||||
purchase_order=self)
|
purchase_order=self,
|
||||||
|
status=status
|
||||||
|
)
|
||||||
|
|
||||||
stock.save()
|
stock.save()
|
||||||
|
|
||||||
|
@ -145,7 +145,7 @@ $("#po-table").inventreeTable({
|
|||||||
title: '{% trans "Part" %}',
|
title: '{% trans "Part" %}',
|
||||||
formatter: function(value, row, index, field) {
|
formatter: function(value, row, index, field) {
|
||||||
if (row.part) {
|
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 {
|
} else {
|
||||||
return '-';
|
return '-';
|
||||||
}
|
}
|
||||||
|
@ -1,23 +1,28 @@
|
|||||||
{% extends "modal_form.html" %}
|
{% extends "modal_form.html" %}
|
||||||
|
{% load i18n %}
|
||||||
|
{% load inventree_extras %}
|
||||||
|
{% load status_codes %}
|
||||||
|
|
||||||
{% block form %}
|
{% 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'>
|
<form method='post' action='' class='js-modal-form' enctype='multipart/form-data'>
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
{% load crispy_forms_tags %}
|
{% load crispy_forms_tags %}
|
||||||
|
|
||||||
<label class='control-label'>Parts</label>
|
<label class='control-label'>{% trans "Parts" %}</label>
|
||||||
<p class='help-block'>Select parts to receive against this order.</p>
|
<p class='help-block'>{% trans "Select parts to receive against this order" %}</p>
|
||||||
|
|
||||||
<table class='table table-striped'>
|
<table class='table table-striped'>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Part</th>
|
<th>{% trans "Part" %}</th>
|
||||||
<th>Order Code</th>
|
<th>{% trans "Order Code" %}</th>
|
||||||
<th>On Order</th>
|
<th>{% trans "On Order" %}</th>
|
||||||
<th>Received</th>
|
<th>{% trans "Received" %}</th>
|
||||||
<th>Receive</th>
|
<th>{% trans "Receive" %}</th>
|
||||||
|
<th>{% trans "Status" %}</th>
|
||||||
|
<th></th>
|
||||||
</tr>
|
</tr>
|
||||||
{% for line in lines %}
|
{% for line in lines %}
|
||||||
<tr id='line_row_{{ line.id }}'>
|
<tr id='line_row_{{ line.id }}'>
|
||||||
@ -28,20 +33,29 @@ Receive outstanding parts for <b>{{ order }}</b> - <i>{{ order.description }}</i
|
|||||||
</td>
|
</td>
|
||||||
<td>{{ line.part.SKU }}</td>
|
<td>{{ line.part.SKU }}</td>
|
||||||
{% else %}
|
{% else %}
|
||||||
<td colspan='2'>Referenced part has been removed</td>
|
<td colspan='2'>{% trans "Error: Referenced part has been removed" %}</td>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<td>{{ line.quantity }}</td>
|
<td>{% decimal line.quantity %}</td>
|
||||||
<td>{{ line.received }}</td>
|
<td>{% decimal line.received %}</td>
|
||||||
<td>
|
<td>
|
||||||
<div class='control-group'>
|
<div class='control-group'>
|
||||||
<div class='controls'>
|
<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>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</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>
|
<td>
|
||||||
<button class='btn btn-default btn-remove' onClick="removeOrderRowFromOrderWizard()" id='del_item_{{ line.id }}' title='Remove line' type='button'>
|
<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>
|
</button>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -29,7 +29,7 @@ from . import forms as order_forms
|
|||||||
from InvenTree.views import AjaxView, AjaxCreateView, AjaxUpdateView, AjaxDeleteView
|
from InvenTree.views import AjaxView, AjaxCreateView, AjaxUpdateView, AjaxDeleteView
|
||||||
from InvenTree.helpers import DownloadFile, str2bool
|
from InvenTree.helpers import DownloadFile, str2bool
|
||||||
|
|
||||||
from InvenTree.status_codes import PurchaseOrderStatus
|
from InvenTree.status_codes import PurchaseOrderStatus, StockStatus
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -669,6 +669,20 @@ class PurchaseOrderReceive(AjaxUpdateView):
|
|||||||
except (PurchaseOrderLineItem.DoesNotExist, ValueError):
|
except (PurchaseOrderLineItem.DoesNotExist, ValueError):
|
||||||
continue
|
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
|
# Check that line matches the order
|
||||||
if not line.order == self.order:
|
if not line.order == self.order:
|
||||||
# TODO - Display a non-field error?
|
# TODO - Display a non-field error?
|
||||||
@ -725,7 +739,13 @@ class PurchaseOrderReceive(AjaxUpdateView):
|
|||||||
if not line.part:
|
if not line.part:
|
||||||
continue
|
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):
|
class OrderParts(AjaxView):
|
||||||
|
@ -28,6 +28,11 @@ def stock_status_label(key, *args, **kwargs):
|
|||||||
return mark_safe(StockStatus.render(key, large=kwargs.get('large', False)))
|
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
|
@register.simple_tag
|
||||||
def build_status_label(key, *args, **kwargs):
|
def build_status_label(key, *args, **kwargs):
|
||||||
""" Render a Build status label """
|
""" Render a Build status label """
|
||||||
|
19
InvenTree/stock/migrations/0035_auto_20200502_2308.py
Normal file
19
InvenTree/stock/migrations/0035_auto_20200502_2308.py
Normal 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)]),
|
||||||
|
),
|
||||||
|
]
|
Loading…
Reference in New Issue
Block a user