diff --git a/InvenTree/build/forms.py b/InvenTree/build/forms.py index 57c31ff1db..66ec98ac77 100644 --- a/InvenTree/build/forms.py +++ b/InvenTree/build/forms.py @@ -21,6 +21,7 @@ class EditBuildForm(HelperForm): 'title', 'part', 'quantity', + 'take_from', 'batch', 'URL', 'notes', diff --git a/InvenTree/build/migrations/0013_build_take_from.py b/InvenTree/build/migrations/0013_build_take_from.py new file mode 100644 index 0000000000..9945da2be1 --- /dev/null +++ b/InvenTree/build/migrations/0013_build_take_from.py @@ -0,0 +1,20 @@ +# Generated by Django 2.2 on 2019-05-10 08:50 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('stock', '0015_stockitem_delete_on_deplete'), + ('build', '0012_auto_20190508_2332'), + ] + + operations = [ + migrations.AddField( + model_name='build', + name='take_from', + field=models.ForeignKey(blank=True, help_text='Select location to take stock from for this build (leave blank to take from any stock location', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='sourcing_builds', to='stock.StockLocation'), + ), + ] diff --git a/InvenTree/build/models.py b/InvenTree/build/models.py index 0b658dee1a..028f6b0f4c 100644 --- a/InvenTree/build/models.py +++ b/InvenTree/build/models.py @@ -27,6 +27,7 @@ class Build(models.Model): part: The part to be built (from component BOM items) title: Brief title describing the build (required) quantity: Number of units to be built + take_from: Location to take stock from to make this build (if blank, can take from anywhere) status: Build status code batch: Batch code transferred to build parts (optional) creation_date: Date the build was created (auto) @@ -41,6 +42,11 @@ class Build(models.Model): def get_absolute_url(self): return reverse('build-detail', kwargs={'pk': self.id}) + title = models.CharField( + blank=False, + max_length=100, + help_text='Brief description of the build') + part = models.ForeignKey('part.Part', on_delete=models.CASCADE, related_name='builds', limit_choices_to={ @@ -50,10 +56,11 @@ class Build(models.Model): help_text='Select part to build', ) - title = models.CharField( - blank=False, - max_length=100, - help_text='Brief description of the build') + take_from = models.ForeignKey('stock.StockLocation', on_delete=models.SET_NULL, + related_name='sourcing_builds', + null=True, blank=True, + help_text='Select location to take stock from for this build (leave blank to take from any stock location' + ) quantity = models.PositiveIntegerField( default=1, @@ -139,6 +146,11 @@ class Build(models.Model): stock = StockItem.objects.filter(part=item.sub_part) + # Ensure that the available stock items are in the correct location + if self.take_from is not None: + # Filter for stock that is located downstream of the designated location + stock = stock.filter(location__in=[loc for loc in self.take_from.getUniqueChildren()]) + # Only one StockItem to choose from? Default to that one! if len(stock) == 1: stock_item = stock[0] diff --git a/InvenTree/build/templates/build/detail.html b/InvenTree/build/templates/build/detail.html index fef67f77c1..b4de8787b1 100644 --- a/InvenTree/build/templates/build/detail.html +++ b/InvenTree/build/templates/build/detail.html @@ -45,6 +45,16 @@ InvenTree | Build - {{ build }} Quantity{{ build.quantity }} + + Stock Source + + {% if build.take_from %} + {{ build.take_from }} + {% else %} + Stock can be taken from any available location. + {% endif %} + + Status{% include "build_status.html" with build=build %} diff --git a/InvenTree/build/views.py b/InvenTree/build/views.py index 79e3819df5..85d07858fb 100644 --- a/InvenTree/build/views.py +++ b/InvenTree/build/views.py @@ -374,6 +374,16 @@ class BuildItemCreate(AjaxCreateView): query = query.filter(part=part_id) if build_id is not None: + try: + build = Build.objects.get(id=build_id) + + if build.take_from is not None: + # Limit query to stock items that are downstream of the 'take_from' location + query = query.filter(location__in=[loc for loc in build.take_from.getUniqueChildren()]) + + except Build.DoesNotExist: + pass + # Exclude StockItem objects which are already allocated to this build and part query = query.exclude(id__in=[item.stock_item.id for item in BuildItem.objects.filter(build=build_id, stock_item__part=part_id)])