2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-08-09 21:30:54 +00:00

Feature: Supplier part pack size (#3644)

* Adds 'pack_size' field to SupplierPart model

* Edit pack_size for SupplierPart via API

* Display pack size in supplier part page template

* Improve table ordering for SupplierPart table

* Fix for API filtering

- Need to use custom filter class

* Adds functionality to duplicate an existing SupplierPart

* Bump API version number

* Display annotation of pack size in purchase order line item table

* Display additional information in part purchase order table

* Add UOM to purchase order table

* Improve receive items functionality

* Indicate quantity which will be received in modal form

* Update the received quantity as the user changes the value

* Take  the pack_size into account when receiving line items

* Take supplierpart pack size into account when receiving line items

* Add "pack size" column to purchase order line item table

* Tweak supplier part table

* Update 'on_order' queryset annotation to take pack_size into account

- May god have mercy on my soul

* Adds a unit test to validate that the on_order queryset annotation is working as expected

* Update Part.on_order method to take pack_size into account

- Check in existing unit test also

* Fix existing unit tests

- Previous unit test was actually in error
- Logic for calculating "on_order" was broked

* More unit tests for receiving items against a purchase order

* Allow pack_size < 1

* Display pack size when adding / editing PurchaseOrderLineItem

* Fix bug in part purchase order table

* Update part purchase order table again

* Exclude notificationmessage when exporting dataset

* Also display pack size when ordering parts from secondary form

* javascript linting

* Change user facing strings to "Pack Quantity"
This commit is contained in:
Oliver
2022-09-08 09:49:14 +10:00
committed by GitHub
parent 890c998420
commit 198ac9b275
17 changed files with 567 additions and 60 deletions

View File

@@ -8,6 +8,7 @@ from django_filters.rest_framework import DjangoFilterBackend
from rest_framework import filters
from InvenTree.api import AttachmentMixin, ListCreateDestroyAPIView
from InvenTree.filters import InvenTreeOrderingFilter
from InvenTree.helpers import str2bool
from InvenTree.mixins import ListCreateAPI, RetrieveUpdateDestroyAPI
@@ -338,12 +339,30 @@ class SupplierPartList(ListCreateDestroyAPIView):
filter_backends = [
DjangoFilterBackend,
filters.SearchFilter,
filters.OrderingFilter,
InvenTreeOrderingFilter,
]
filterset_fields = [
]
ordering_fields = [
'SKU',
'part',
'supplier',
'manufacturer',
'MPN',
'packaging',
'pack_size',
'in_stock',
]
ordering_field_aliases = {
'part': 'part__name',
'supplier': 'supplier__name',
'manufacturer': 'manufacturer_part__manufacturer__name',
'MPN': 'manufacturer_part__MPN',
}
search_fields = [
'SKU',
'supplier__name',

View File

@@ -0,0 +1,20 @@
# Generated by Django 3.2.15 on 2022-09-05 04:21
import InvenTree.fields
import django.core.validators
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('company', '0046_alter_company_image'),
]
operations = [
migrations.AddField(
model_name='supplierpart',
name='pack_size',
field=InvenTree.fields.RoundingDecimalField(decimal_places=5, default=1, help_text='Unit quantity supplied in a single pack', max_digits=15, validators=[django.core.validators.MinValueValidator(0.001)], verbose_name='Pack Quantity'),
),
]

View File

@@ -20,7 +20,7 @@ import InvenTree.fields
import InvenTree.helpers
import InvenTree.validators
from common.settings import currency_code_default
from InvenTree.fields import InvenTreeURLField
from InvenTree.fields import InvenTreeURLField, RoundingDecimalField
from InvenTree.models import InvenTreeAttachment
from InvenTree.status_codes import PurchaseOrderStatus
@@ -406,6 +406,7 @@ class SupplierPart(models.Model):
multiple: Multiple that the part is provided in
lead_time: Supplier lead time
packaging: packaging that the part is supplied in, e.g. "Reel"
pack_size: Quantity of item supplied in a single pack (e.g. 30ml in a single tube)
"""
objects = SupplierPartManager()
@@ -527,6 +528,14 @@ class SupplierPart(models.Model):
packaging = models.CharField(max_length=50, blank=True, null=True, verbose_name=_('Packaging'), help_text=_('Part packaging'))
pack_size = RoundingDecimalField(
verbose_name=_('Pack Quantity'),
help_text=_('Unit quantity supplied in a single pack'),
default=1,
max_digits=15, decimal_places=5,
validators=[MinValueValidator(0.001)],
)
multiple = models.PositiveIntegerField(default=1, validators=[MinValueValidator(1)], verbose_name=_('multiple'), help_text=_('Order multiple'))
# TODO - Reimplement lead-time as a charfield with special validation (pattern matching).

View File

@@ -239,6 +239,8 @@ class SupplierPartSerializer(InvenTreeModelSerializer):
pretty_name = serializers.CharField(read_only=True)
pack_size = serializers.FloatField()
def __init__(self, *args, **kwargs):
"""Initialize this serializer with extra detail fields as required"""
@@ -273,6 +275,8 @@ class SupplierPartSerializer(InvenTreeModelSerializer):
manufacturer_part_detail = ManufacturerPartSerializer(source='manufacturer_part', read_only=True)
url = serializers.CharField(source='get_absolute_url', read_only=True)
class Meta:
"""Metaclass options."""
@@ -291,12 +295,14 @@ class SupplierPartSerializer(InvenTreeModelSerializer):
'note',
'pk',
'packaging',
'pack_size',
'part',
'part_detail',
'pretty_name',
'SKU',
'supplier',
'supplier_detail',
'url',
]
read_only_fields = [

View File

@@ -49,6 +49,11 @@
<span class='fas fa-edit icon-green'></span> {% trans "Edit Supplier Part" %}
</a></li>
{% endif %}
{% if roles.purchase_order.add %}
<li><a class='dropdown-item' href='#' id='duplicate-part' title='{% trans "Duplicate Supplier Part" %}'>
<span class='fas fa-clone'></span> {% trans "Duplicate Supplier Part" %}
</a></li>
{% endif %}
{% if roles.purchase_order.delete %}
<li><a class='dropdown-item' href='#' id='delete-part' title='{% trans "Delete Supplier Part" %}'>
<span class='fas fa-trash-alt icon-red'></span> {% trans "Delete Supplier Part" %}
@@ -140,6 +145,13 @@ src="{% static 'img/blank_image.png' %}"
<td>{{ part.packaging }}{% include "clip.html"%}</td>
</tr>
{% endif %}
{% if part.pack_size != 1.0 %}
<tr>
<td><span class='fas fa-box'></span></td>
<td>{% trans "Pack Quantity" %}</td>
<td>{% decimal part.pack_size %}{% if part.part.units %} {{ part.part.units }}{% endif %}</td>
</tr>
{% endif %}
{% if part.note %}
<tr>
<td><span class='fas fa-sticky-note'></span></td>
@@ -386,6 +398,12 @@ $('#update-part-availability').click(function() {
});
});
$('#duplicate-part').click(function() {
duplicateSupplierPart({{ part.pk }}, {
follow: true
});
});
$('#edit-part').click(function () {
editSupplierPart({{ part.pk }}, {