mirror of
				https://github.com/inventree/InvenTree.git
				synced 2025-11-03 22:55:43 +00:00 
			
		
		
		
	Merge remote-tracking branch 'inventree/master'
This commit is contained in:
		@@ -167,10 +167,6 @@ class StockStatus(StatusCode):
 | 
				
			|||||||
    # This can be used as a quick check for filtering
 | 
					    # This can be used as a quick check for filtering
 | 
				
			||||||
    NOT_IN_STOCK = 100
 | 
					    NOT_IN_STOCK = 100
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    SHIPPED = 110  # Item has been shipped to a customer
 | 
					 | 
				
			||||||
    ASSIGNED_TO_BUILD = 120
 | 
					 | 
				
			||||||
    ASSIGNED_TO_OTHER_ITEM = 130
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    options = {
 | 
					    options = {
 | 
				
			||||||
        OK: _("OK"),
 | 
					        OK: _("OK"),
 | 
				
			||||||
        ATTENTION: _("Attention needed"),
 | 
					        ATTENTION: _("Attention needed"),
 | 
				
			||||||
@@ -179,9 +175,6 @@ class StockStatus(StatusCode):
 | 
				
			|||||||
        LOST: _("Lost"),
 | 
					        LOST: _("Lost"),
 | 
				
			||||||
        REJECTED: _("Rejected"),
 | 
					        REJECTED: _("Rejected"),
 | 
				
			||||||
        RETURNED: _("Returned"),
 | 
					        RETURNED: _("Returned"),
 | 
				
			||||||
        SHIPPED: _('Shipped'),
 | 
					 | 
				
			||||||
        ASSIGNED_TO_BUILD: _("Used for Build"),
 | 
					 | 
				
			||||||
        ASSIGNED_TO_OTHER_ITEM: _("Installed in Stock Item")
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    colors = {
 | 
					    colors = {
 | 
				
			||||||
@@ -190,9 +183,6 @@ class StockStatus(StatusCode):
 | 
				
			|||||||
        DAMAGED: 'red',
 | 
					        DAMAGED: 'red',
 | 
				
			||||||
        DESTROYED: 'red',
 | 
					        DESTROYED: 'red',
 | 
				
			||||||
        REJECTED: 'red',
 | 
					        REJECTED: 'red',
 | 
				
			||||||
        SHIPPED: 'green',
 | 
					 | 
				
			||||||
        ASSIGNED_TO_BUILD: 'blue',
 | 
					 | 
				
			||||||
        ASSIGNED_TO_OTHER_ITEM: 'blue',
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # The following codes correspond to parts that are 'available' or 'in stock'
 | 
					    # The following codes correspond to parts that are 'available' or 'in stock'
 | 
				
			||||||
@@ -208,9 +198,6 @@ class StockStatus(StatusCode):
 | 
				
			|||||||
        DESTROYED,
 | 
					        DESTROYED,
 | 
				
			||||||
        LOST,
 | 
					        LOST,
 | 
				
			||||||
        REJECTED,
 | 
					        REJECTED,
 | 
				
			||||||
        SHIPPED,
 | 
					 | 
				
			||||||
        ASSIGNED_TO_BUILD,
 | 
					 | 
				
			||||||
        ASSIGNED_TO_OTHER_ITEM,
 | 
					 | 
				
			||||||
    ]
 | 
					    ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # The following codes are available for receiving goods
 | 
					    # The following codes are available for receiving goods
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -21,7 +21,7 @@ from markdownx.models import MarkdownxField
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
from mptt.models import MPTTModel, TreeForeignKey
 | 
					from mptt.models import MPTTModel, TreeForeignKey
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from InvenTree.status_codes import BuildStatus, StockStatus
 | 
					from InvenTree.status_codes import BuildStatus
 | 
				
			||||||
from InvenTree.fields import InvenTreeURLField
 | 
					from InvenTree.fields import InvenTreeURLField
 | 
				
			||||||
from InvenTree.helpers import decimal2string
 | 
					from InvenTree.helpers import decimal2string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -501,7 +501,6 @@ class BuildItem(models.Model):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        # TODO - If the item__part object is not trackable, delete the stock item here
 | 
					        # TODO - If the item__part object is not trackable, delete the stock item here
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        item.status = StockStatus.ASSIGNED_TO_BUILD
 | 
					 | 
				
			||||||
        item.build_order = self.build
 | 
					        item.build_order = self.build
 | 
				
			||||||
        item.save()
 | 
					        item.save()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -211,15 +211,12 @@ class BuildTest(TestCase):
 | 
				
			|||||||
        # New stock items created and assigned to the build
 | 
					        # New stock items created and assigned to the build
 | 
				
			||||||
        self.assertEqual(StockItem.objects.get(pk=4).quantity, 50)
 | 
					        self.assertEqual(StockItem.objects.get(pk=4).quantity, 50)
 | 
				
			||||||
        self.assertEqual(StockItem.objects.get(pk=4).build_order, self.build)
 | 
					        self.assertEqual(StockItem.objects.get(pk=4).build_order, self.build)
 | 
				
			||||||
        self.assertEqual(StockItem.objects.get(pk=4).status, status.StockStatus.ASSIGNED_TO_BUILD)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.assertEqual(StockItem.objects.get(pk=5).quantity, 50)
 | 
					        self.assertEqual(StockItem.objects.get(pk=5).quantity, 50)
 | 
				
			||||||
        self.assertEqual(StockItem.objects.get(pk=5).build_order, self.build)
 | 
					        self.assertEqual(StockItem.objects.get(pk=5).build_order, self.build)
 | 
				
			||||||
        self.assertEqual(StockItem.objects.get(pk=5).status, status.StockStatus.ASSIGNED_TO_BUILD)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.assertEqual(StockItem.objects.get(pk=6).quantity, 250)
 | 
					        self.assertEqual(StockItem.objects.get(pk=6).quantity, 250)
 | 
				
			||||||
        self.assertEqual(StockItem.objects.get(pk=6).build_order, self.build)
 | 
					        self.assertEqual(StockItem.objects.get(pk=6).build_order, self.build)
 | 
				
			||||||
        self.assertEqual(StockItem.objects.get(pk=6).status, status.StockStatus.ASSIGNED_TO_BUILD)
 | 
					 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        # And a new stock item created for the build output
 | 
					        # And a new stock item created for the build output
 | 
				
			||||||
        self.assertEqual(StockItem.objects.get(pk=7).quantity, 1)
 | 
					        self.assertEqual(StockItem.objects.get(pk=7).quantity, 1)
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										18
									
								
								InvenTree/part/migrations/0046_auto_20200804_0107.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								InvenTree/part/migrations/0046_auto_20200804_0107.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,18 @@
 | 
				
			|||||||
 | 
					# Generated by Django 3.0.7 on 2020-08-04 01:07
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from django.db import migrations, models
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Migration(migrations.Migration):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    dependencies = [
 | 
				
			||||||
 | 
					        ('part', '0045_auto_20200605_0932'),
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    operations = [
 | 
				
			||||||
 | 
					        migrations.AlterField(
 | 
				
			||||||
 | 
					            model_name='partcategory',
 | 
				
			||||||
 | 
					            name='default_keywords',
 | 
				
			||||||
 | 
					            field=models.CharField(blank=True, help_text='Default keywords for parts in this category', max_length=250, null=True),
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
@@ -65,7 +65,7 @@ class PartCategory(InvenTreeTree):
 | 
				
			|||||||
        help_text=_('Default location for parts in this category')
 | 
					        help_text=_('Default location for parts in this category')
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    default_keywords = models.CharField(blank=True, max_length=250, help_text=_('Default keywords for parts in this category'))
 | 
					    default_keywords = models.CharField(null=True, blank=True, max_length=250, help_text=_('Default keywords for parts in this category'))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get_absolute_url(self):
 | 
					    def get_absolute_url(self):
 | 
				
			||||||
        return reverse('category-detail', kwargs={'pk': self.id})
 | 
					        return reverse('category-detail', kwargs={'pk': self.id})
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -46,6 +46,18 @@ class AssignStockItemToCustomerForm(HelperForm):
 | 
				
			|||||||
        ]
 | 
					        ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ReturnStockItemForm(HelperForm):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Form for manually returning a StockItem into stock
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class Meta:
 | 
				
			||||||
 | 
					        model = StockItem
 | 
				
			||||||
 | 
					        fields = [
 | 
				
			||||||
 | 
					            'location',
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class EditStockItemTestResultForm(HelperForm):
 | 
					class EditStockItemTestResultForm(HelperForm):
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    Form for creating / editing a StockItemTestResult object.
 | 
					    Form for creating / editing a StockItemTestResult object.
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										19
									
								
								InvenTree/stock/migrations/0048_auto_20200807_2344.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								InvenTree/stock/migrations/0048_auto_20200807_2344.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,19 @@
 | 
				
			|||||||
 | 
					# Generated by Django 3.0.7 on 2020-08-07 23:44
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import django.core.validators
 | 
				
			||||||
 | 
					from django.db import migrations, models
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Migration(migrations.Migration):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    dependencies = [
 | 
				
			||||||
 | 
					        ('stock', '0047_auto_20200605_0932'),
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    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')], default=10, validators=[django.core.validators.MinValueValidator(0)]),
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
@@ -140,6 +140,7 @@ class StockItem(MPTTModel):
 | 
				
			|||||||
        sales_order=None,
 | 
					        sales_order=None,
 | 
				
			||||||
        build_order=None,
 | 
					        build_order=None,
 | 
				
			||||||
        belongs_to=None,
 | 
					        belongs_to=None,
 | 
				
			||||||
 | 
					        customer=None,
 | 
				
			||||||
        status__in=StockStatus.AVAILABLE_CODES
 | 
					        status__in=StockStatus.AVAILABLE_CODES
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -219,12 +220,6 @@ class StockItem(MPTTModel):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        super().clean()
 | 
					        super().clean()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if self.status == StockStatus.ASSIGNED_TO_OTHER_ITEM and self.belongs_to is None:
 | 
					 | 
				
			||||||
            raise ValidationError({
 | 
					 | 
				
			||||||
                'belongs_to': "Belongs_to field must be specified as statis is marked as ASSIGNED_TO_OTHER_ITEM",
 | 
					 | 
				
			||||||
                'status': 'Status cannot be marked as ASSIGNED_TO_OTHER_ITEM if the belongs_to field is not set',
 | 
					 | 
				
			||||||
            })
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            if self.part.trackable:
 | 
					            if self.part.trackable:
 | 
				
			||||||
                # Trackable parts must have integer values for quantity field!
 | 
					                # Trackable parts must have integer values for quantity field!
 | 
				
			||||||
@@ -477,7 +472,6 @@ class StockItem(MPTTModel):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        # Update StockItem fields with new information
 | 
					        # Update StockItem fields with new information
 | 
				
			||||||
        item.sales_order = order
 | 
					        item.sales_order = order
 | 
				
			||||||
        item.status = StockStatus.SHIPPED
 | 
					 | 
				
			||||||
        item.customer = customer
 | 
					        item.customer = customer
 | 
				
			||||||
        item.location = None
 | 
					        item.location = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -495,6 +489,23 @@ class StockItem(MPTTModel):
 | 
				
			|||||||
        # Return the reference to the stock item
 | 
					        # Return the reference to the stock item
 | 
				
			||||||
        return item
 | 
					        return item
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def returnFromCustomer(self, location, user=None):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Return stock item from customer, back into the specified location.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.addTransactionNote(
 | 
				
			||||||
 | 
					            _("Returned from customer") + " " + self.customer.name,
 | 
				
			||||||
 | 
					            user,
 | 
				
			||||||
 | 
					            notes=_("Returned to location") + " " + location.name,
 | 
				
			||||||
 | 
					            system=True
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.customer = None
 | 
				
			||||||
 | 
					        self.location = location
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.save()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # If stock item is incoming, an (optional) ETA field
 | 
					    # If stock item is incoming, an (optional) ETA field
 | 
				
			||||||
    # expected_arrival = models.DateField(null=True, blank=True)
 | 
					    # expected_arrival = models.DateField(null=True, blank=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -599,6 +610,10 @@ class StockItem(MPTTModel):
 | 
				
			|||||||
        if self.build_order is not None:
 | 
					        if self.build_order is not None:
 | 
				
			||||||
            return False
 | 
					            return False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Not 'in stock' if it has been assigned to a customer
 | 
				
			||||||
 | 
					        if self.customer is not None:
 | 
				
			||||||
 | 
					            return False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Not 'in stock' if the status code makes it unavailable
 | 
					        # Not 'in stock' if the status code makes it unavailable
 | 
				
			||||||
        if self.status in StockStatus.UNAVAILABLE_CODES:
 | 
					        if self.status in StockStatus.UNAVAILABLE_CODES:
 | 
				
			||||||
            return False
 | 
					            return False
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -86,30 +86,34 @@ InvenTree | {% trans "Stock Item" %} - {{ item }}
 | 
				
			|||||||
            {% endif %}
 | 
					            {% endif %}
 | 
				
			||||||
        </ul>
 | 
					        </ul>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
    {% if item.in_stock %}
 | 
					 | 
				
			||||||
    <!-- Stock adjustment menu -->
 | 
					    <!-- Stock adjustment menu -->
 | 
				
			||||||
    <div class='dropdown dropdown-buttons'>
 | 
					    <div class='dropdown dropdown-buttons'>
 | 
				
			||||||
        <button id='stock-options' title='{% trans "Stock adjustment actions" %}' class='btn btn-default dropdown-toggle' type='button' data-toggle='dropdown'><span class='fas fa-boxes'></span> <span class='caret'></span></button>
 | 
					        <button id='stock-options' title='{% trans "Stock adjustment actions" %}' class='btn btn-default dropdown-toggle' type='button' data-toggle='dropdown'><span class='fas fa-boxes'></span> <span class='caret'></span></button>
 | 
				
			||||||
        <ul class='dropdown-menu' role='menu'>
 | 
					        <ul class='dropdown-menu' role='menu'>
 | 
				
			||||||
 | 
					            {% if item.in_stock %}
 | 
				
			||||||
            {% if not item.serialized %}
 | 
					            {% if not item.serialized %}
 | 
				
			||||||
            <li><a href='#' id='stock-count' title='{% trans "Count stock" %}'><span class='fas fa-clipboard-list'></span> {% trans "Count stock" %}</a></li>
 | 
					            <li><a href='#' id='stock-count' title='{% trans "Count stock" %}'><span class='fas fa-clipboard-list'></span> {% trans "Count stock" %}</a></li>
 | 
				
			||||||
            <li><a href='#' id='stock-add' title='{% trans "Add stock" %}'><span class='fas fa-plus-circle icon-green'></span> {% trans "Add stock" %}</a></li>
 | 
					            <li><a href='#' id='stock-add' title='{% trans "Add stock" %}'><span class='fas fa-plus-circle icon-green'></span> {% trans "Add stock" %}</a></li>
 | 
				
			||||||
            <li><a href='#' id='stock-remove' title='{% trans "Remove stock" %}'><span class='fas fa-minus-circle icon-red'></span> {% trans "Remove stock" %}</a></li>
 | 
					            <li><a href='#' id='stock-remove' title='{% trans "Remove stock" %}'><span class='fas fa-minus-circle icon-red'></span> {% trans "Remove stock" %}</a></li>
 | 
				
			||||||
            {% endif %}
 | 
					            {% endif %}
 | 
				
			||||||
            <li><a href='#' id='stock-move' title='{% trans "Transfer stock" %}'><span class='fas fa-exchange-alt icon-blue'></span> {% trans "Transfer stock" %}</a></li>
 | 
					            <li><a href='#' id='stock-move' title='{% trans "Transfer stock" %}'><span class='fas fa-exchange-alt icon-blue'></span> {% trans "Transfer stock" %}</a></li>
 | 
				
			||||||
        </ul>
 | 
					 | 
				
			||||||
    </div>
 | 
					 | 
				
			||||||
    {% endif %}
 | 
					 | 
				
			||||||
    <!-- Edit stock item -->
 | 
					 | 
				
			||||||
    <div class='dropdown dropdown-buttons'>
 | 
					 | 
				
			||||||
        <button id='stock-edit-actions' title='{% trans "Stock actions" %}' class='btn btn-default dropdown-toggle' type='button' data-toggle='dropdown'><span class='fas fa-tools'></span> <span class='caret'></span></button>
 | 
					 | 
				
			||||||
        <ul class='dropdown-menu' role='menu'>
 | 
					 | 
				
			||||||
            {% if item.part.trackable and not item.serialized %}
 | 
					            {% if item.part.trackable and not item.serialized %}
 | 
				
			||||||
            <li><a href='#' id='stock-serialize' title='{% trans "Serialize stock" %}'><span class='fas fa-hashtag'></span> {% trans "Serialize stock" %}</a> </li>
 | 
					            <li><a href='#' id='stock-serialize' title='{% trans "Serialize stock" %}'><span class='fas fa-hashtag'></span> {% trans "Serialize stock" %}</a> </li>
 | 
				
			||||||
            {% endif %}
 | 
					            {% endif %}
 | 
				
			||||||
 | 
					            {% endif %}
 | 
				
			||||||
            {% if item.part.salable and not item.customer %}
 | 
					            {% if item.part.salable and not item.customer %}
 | 
				
			||||||
            <li><a href='#' id='stock-assign-to-customer' title='{% trans "Assign to customer" %}'><span class='fas fa-user-tie'></span> {% trans "Assign to customer" %}</a></li>
 | 
					            <li><a href='#' id='stock-assign-to-customer' title='{% trans "Assign to customer" %}'><span class='fas fa-user-tie'></span> {% trans "Assign to customer" %}</a></li>
 | 
				
			||||||
            {% endif %}
 | 
					            {% endif %}
 | 
				
			||||||
 | 
					            {% if item.customer %}
 | 
				
			||||||
 | 
					            <li><a href='#' id='stock-return-from-customer' title='{% trans "Return to stock" %}'><span class='fas fa-undo'></span> {% trans "Return to stock" %}</a></li>
 | 
				
			||||||
 | 
					            {% endif %}
 | 
				
			||||||
 | 
					        </ul>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					    <!-- Edit stock item -->
 | 
				
			||||||
 | 
					    <div class='dropdown dropdown-buttons'>
 | 
				
			||||||
 | 
					        <button id='stock-edit-actions' title='{% trans "Stock actions" %}' class='btn btn-default dropdown-toggle' type='button' data-toggle='dropdown'><span class='fas fa-tools'></span> <span class='caret'></span></button>
 | 
				
			||||||
 | 
					        <ul class='dropdown-menu' role='menu'>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            {% if item.part.has_variants %}
 | 
					            {% if item.part.has_variants %}
 | 
				
			||||||
            <li><a href='#' id='stock-convert' title='{% trans "Convert to variant" %}'><span class='fas fa-screwdriver'></span> {% trans "Convert to variant" %}</a></li>
 | 
					            <li><a href='#' id='stock-convert' title='{% trans "Convert to variant" %}'><span class='fas fa-screwdriver'></span> {% trans "Convert to variant" %}</a></li>
 | 
				
			||||||
            {% endif %}
 | 
					            {% endif %}
 | 
				
			||||||
@@ -157,7 +161,7 @@ InvenTree | {% trans "Stock Item" %} - {{ item }}
 | 
				
			|||||||
    <tr>
 | 
					    <tr>
 | 
				
			||||||
        <td><span class='fas fa-user-tie'></span></td>
 | 
					        <td><span class='fas fa-user-tie'></span></td>
 | 
				
			||||||
        <td>{% trans "Customer" %}</td>
 | 
					        <td>{% trans "Customer" %}</td>
 | 
				
			||||||
        <td><a href="{% url 'company-detail' item.customer.id %}">{{ item.customer.name }}</a></td>
 | 
					        <td><a href="{% url 'company-detail-assigned-stock' item.customer.id %}">{{ item.customer.name }}</a></td>
 | 
				
			||||||
    </tr>
 | 
					    </tr>
 | 
				
			||||||
    {% endif %}
 | 
					    {% endif %}
 | 
				
			||||||
    {% if item.belongs_to %}
 | 
					    {% if item.belongs_to %}
 | 
				
			||||||
@@ -349,7 +353,6 @@ $("#unlink-barcode").click(function() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
{% if item.in_stock %}
 | 
					{% if item.in_stock %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
{% if item.part.salable %}
 | 
					 | 
				
			||||||
$("#stock-assign-to-customer").click(function() {
 | 
					$("#stock-assign-to-customer").click(function() {
 | 
				
			||||||
    launchModalForm("{% url 'stock-item-assign' item.id %}",
 | 
					    launchModalForm("{% url 'stock-item-assign' item.id %}",
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
@@ -357,7 +360,6 @@ $("#stock-assign-to-customer").click(function() {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
{% endif %}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
function itemAdjust(action) {
 | 
					function itemAdjust(action) {
 | 
				
			||||||
    launchModalForm("/stock/adjust/", 
 | 
					    launchModalForm("/stock/adjust/", 
 | 
				
			||||||
@@ -398,6 +400,16 @@ $('#stock-add').click(function() {
 | 
				
			|||||||
    itemAdjust('add');
 | 
					    itemAdjust('add');
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% else %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					$("#stock-return-from-customer").click(function() {
 | 
				
			||||||
 | 
					    launchModalForm("{% url 'stock-item-return' item.id %}",
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            reload: true,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
{% endif %}
 | 
					{% endif %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
$("#stock-delete").click(function () {
 | 
					$("#stock-delete").click(function () {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -24,6 +24,7 @@ stock_item_detail_urls = [
 | 
				
			|||||||
    url(r'^qr_code/', views.StockItemQRCode.as_view(), name='stock-item-qr'),
 | 
					    url(r'^qr_code/', views.StockItemQRCode.as_view(), name='stock-item-qr'),
 | 
				
			||||||
    url(r'^delete_test_data/', views.StockItemDeleteTestData.as_view(), name='stock-item-delete-test-data'),
 | 
					    url(r'^delete_test_data/', views.StockItemDeleteTestData.as_view(), name='stock-item-delete-test-data'),
 | 
				
			||||||
    url(r'^assign/', views.StockItemAssignToCustomer.as_view(), name='stock-item-assign'),
 | 
					    url(r'^assign/', views.StockItemAssignToCustomer.as_view(), name='stock-item-assign'),
 | 
				
			||||||
 | 
					    url(r'^return/', views.StockItemReturnToStock.as_view(), name='stock-item-return'),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    url(r'^add_tracking/', views.StockItemTrackingCreate.as_view(), name='stock-tracking-create'),
 | 
					    url(r'^add_tracking/', views.StockItemTrackingCreate.as_view(), name='stock-tracking-create'),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -260,6 +260,41 @@ class StockItemAssignToCustomer(AjaxUpdateView):
 | 
				
			|||||||
        return self.renderJsonResponse(request, self.get_form(), data)
 | 
					        return self.renderJsonResponse(request, self.get_form(), data)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class StockItemReturnToStock(AjaxUpdateView):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    View for returning a stock item (which is assigned to a customer) to stock.
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    model = StockItem
 | 
				
			||||||
 | 
					    ajax_form_title = _("Return to Stock")
 | 
				
			||||||
 | 
					    context_object_name = "item"
 | 
				
			||||||
 | 
					    form_class = StockForms.ReturnStockItemForm
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def post(self, request, *args, **kwargs):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        location = request.POST.get('location', None)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if location:
 | 
				
			||||||
 | 
					            try:
 | 
				
			||||||
 | 
					                location = StockLocation.objects.get(pk=location)
 | 
				
			||||||
 | 
					            except (ValueError, StockLocation.DoesNotExist):
 | 
				
			||||||
 | 
					                location = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if location:
 | 
				
			||||||
 | 
					            stock_item = self.get_object()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            stock_item.returnFromCustomer(location, request.user)
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            raise ValidationError({'location': _("Specify a valid location")})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        data = {
 | 
				
			||||||
 | 
					            'form_valid': True,
 | 
				
			||||||
 | 
					            'success': _("Stock item returned from customer")
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return self.renderJsonResponse(request, self.get_form(), data)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class StockItemDeleteTestData(AjaxUpdateView):
 | 
					class StockItemDeleteTestData(AjaxUpdateView):
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    View for deleting all test data
 | 
					    View for deleting all test data
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -425,16 +425,10 @@ function loadStockTable(table, options) {
 | 
				
			|||||||
                sortable: true,
 | 
					                sortable: true,
 | 
				
			||||||
                formatter: function(value, row, index, field) {
 | 
					                formatter: function(value, row, index, field) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    var url = '';
 | 
					                    var url = `/stock/item/${row.pk}/`;
 | 
				
			||||||
                    var thumb = row.part_detail.thumbnail;
 | 
					                    var thumb = row.part_detail.thumbnail;
 | 
				
			||||||
                    var name = row.part_detail.full_name;
 | 
					                    var name = row.part_detail.full_name;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    if (row.supplier_part) {
 | 
					 | 
				
			||||||
                        url = `/supplier-part/${row.supplier_part}/`;
 | 
					 | 
				
			||||||
                    } else {
 | 
					 | 
				
			||||||
                        url = `/part/${row.part}/`;
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    html = imageHoverIcon(thumb) + renderLink(name, url);
 | 
					                    html = imageHoverIcon(thumb) + renderLink(name, url);
 | 
				
			||||||
                    
 | 
					                    
 | 
				
			||||||
                    return html;
 | 
					                    return html;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -18,14 +18,18 @@ function {{ label }}StatusDisplay(key) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    key = String(key);
 | 
					    key = String(key);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    var value = {{ label }}Codes[key].value;
 | 
					    var value = null;
 | 
				
			||||||
 | 
					    var label = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (key in {{ label }}Codes) {
 | 
				
			||||||
 | 
					        value = {{ label }}Codes[key].value;
 | 
				
			||||||
 | 
					        label = {{ label }}Codes[key].label;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (value == null || value.length == 0) {
 | 
					    if (value == null || value.length == 0) {
 | 
				
			||||||
        value = key;
 | 
					        value = key;
 | 
				
			||||||
 | 
					        label = '';
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Select the label color
 | 
					 | 
				
			||||||
    var label = {{ label }}Codes[key].label ?? '';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return `<span class='label ${label}'>${value}</span>`;
 | 
					    return `<span class='label ${label}'>${value}</span>`;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,4 @@
 | 
				
			|||||||
[](https://opensource.org/licenses/MIT) [](https://travis-ci.org/inventree/InvenTree) [](https://inventree.readthedocs.io/en/latest/?badge=latest) [](https://coveralls.io/github/inventree/InvenTree)
 | 
					[](https://opensource.org/licenses/MIT) [](https://travis-ci.org/inventree/InvenTree) [](https://coveralls.io/github/inventree/InvenTree)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<img src="images/logo/inventree.png" alt="InvenTree" width="128"/>
 | 
					<img src="images/logo/inventree.png" alt="InvenTree" width="128"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -15,7 +15,7 @@ Refer to the [getting started guide](https://inventree.github.io/docs/start/inst
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
## Documentation
 | 
					## Documentation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
For InvenTree documentation, refer to the [InvenTre documentation website](https://inventree.github.io).
 | 
					For InvenTree documentation, refer to the [InvenTree documentation website](https://inventree.github.io).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Integration
 | 
					## Integration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -15,7 +15,7 @@ django-crispy-forms==1.8.1      # Form helpers
 | 
				
			|||||||
django-import-export==2.0.0     # Data import / export for admin interface
 | 
					django-import-export==2.0.0     # Data import / export for admin interface
 | 
				
			||||||
django-cleanup==4.0.0           # Manage deletion of old / unused uploaded files
 | 
					django-cleanup==4.0.0           # Manage deletion of old / unused uploaded files
 | 
				
			||||||
django-qr-code==1.2.0           # Generate QR codes
 | 
					django-qr-code==1.2.0           # Generate QR codes
 | 
				
			||||||
flake8==3.3.0                   # PEP checking
 | 
					flake8==3.8.3                   # PEP checking
 | 
				
			||||||
coverage==4.0.3                 # Unit test coverage
 | 
					coverage==4.0.3                 # Unit test coverage
 | 
				
			||||||
python-coveralls==2.9.1         # Coveralls linking (for Travis)
 | 
					python-coveralls==2.9.1         # Coveralls linking (for Travis)
 | 
				
			||||||
rapidfuzz==0.7.6                # Fuzzy string matching
 | 
					rapidfuzz==0.7.6                # Fuzzy string matching
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user