Add a "sales_order" reference to the Build model

- If a build order is made to fulfil a sales order
- Add sales_order filtering to the Build API
- Pass initial information through to the BuildCreate view
This commit is contained in:
Oliver Walters 2020-04-25 13:15:45 +10:00
parent b351976ae9
commit d5f3498238
8 changed files with 96 additions and 29 deletions

View File

@ -38,6 +38,8 @@ class BuildList(generics.ListCreateAPIView):
]
filter_fields = [
'part',
'sales_order',
]
def get_queryset(self):
@ -48,12 +50,6 @@ class BuildList(generics.ListCreateAPIView):
build_list = super().get_queryset()
# Filter by part
part = self.request.query_params.get('part', None)
if part is not None:
build_list = build_list.filter(part=part)
# Filter by build status?
status = self.request.query_params.get('status', None)

View File

@ -22,6 +22,7 @@ class EditBuildForm(HelperForm):
fields = [
'title',
'part',
'sales_order',
'quantity',
'take_from',
'batch',

View File

@ -0,0 +1,20 @@
# Generated by Django 3.0.5 on 2020-04-24 22:51
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('order', '0029_auto_20200423_1042'),
('build', '0011_auto_20200406_0123'),
]
operations = [
migrations.AddField(
model_name='build',
name='sales_order',
field=models.ForeignKey(blank=True, help_text='SalesOrder to which this build is allocated', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='builds', to='order.SalesOrder'),
),
]

View File

@ -33,6 +33,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
sales_order: References to a SalesOrder object for which this Build is required (e.g. the output of this build will be used to fulfil a sales order)
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)
@ -51,24 +52,37 @@ class Build(models.Model):
title = models.CharField(
blank=False,
max_length=100,
help_text=_('Brief description of the build'))
help_text=_('Brief description of the build')
)
part = models.ForeignKey('part.Part', on_delete=models.CASCADE,
related_name='builds',
limit_choices_to={
'is_template': False,
'assembly': True,
'active': True,
'virtual': False,
},
help_text=_('Select part to build'),
)
part = models.ForeignKey(
'part.Part',
on_delete=models.CASCADE,
related_name='builds',
limit_choices_to={
'is_template': False,
'assembly': True,
'active': True,
'virtual': False,
},
help_text=_('Select part to build'),
)
sales_order = models.ForeignKey(
'order.SalesOrder',
on_delete=models.SET_NULL,
related_name='builds',
null=True, blank=True,
help_text=_('SalesOrder to which this build is allocated')
)
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)')
)
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,

View File

@ -39,6 +39,7 @@ class BuildSerializer(InvenTreeModelSerializer):
'completion_date',
'part',
'part_detail',
'sales_order',
'quantity',
'status',
'status_text',

View File

@ -8,6 +8,14 @@
InvenTree | {% trans "Build" %} - {{ build }}
{% endblock %}
{% block pre_content %}
{% if build.sales_order %}
<div class='alert alert-block alert-info'>
{% trans "This build is allocated to Sales Order" %} <b><a href="{% url 'so-detail' build.sales_order.id %}">{{ build.sales_order }}</a></b>
</div>
{% endif %}
{% endblock %}
{% block thumbnail %}
<img class="part-thumb"
{% if build.part.image %}
@ -54,7 +62,7 @@ src="{% static 'img/blank_image.png' %}"
</tr>
<tr>
<td><span class='fas fa-shapes'></span></td>
<td>Part</td>
<td>{% trans "Part" %}</td>
<td><a href="{% url 'part-detail' build.part.id %}">{{ build.part.full_name }}</a></td>
</tr>
<tr>
@ -67,6 +75,13 @@ src="{% static 'img/blank_image.png' %}"
<td>{% trans "Status" %}</td>
<td>{% build_status_label build.status %}</td>
</tr>
{% if build.sales_order %}
<tr>
<td><span class='fas fa-dolly'></span></td>
<td>{% trans "Sales Order" %}</td>
<td><a href="{% url 'so-detail' build.sales_order.id %}">{{ build.sales_order }}</a></td>
</tr>
{% endif %}
<tr>
<td><span class='fas fa-dollar-sign'></span></td>
<td>{% trans "BOM Price" %}</td>

View File

@ -393,13 +393,13 @@ class BuildCreate(AjaxCreateView):
initials = super(BuildCreate, self).get_initial().copy()
part_id = self.request.GET.get('part', None)
# User has provided a Part ID
initials['part'] = self.request.GET.get('part', None)
if part_id:
try:
initials['part'] = Part.objects.get(pk=part_id)
except Part.DoesNotExist:
pass
# User has provided a SalesOrder ID
initials['sales_order'] = self.request.GET.get('sales_order', None)
initials['quantity'] = self.request.GET.get('quantity', 1)
return initials

View File

@ -6,6 +6,9 @@
{% load static %}
{% block details %}
{% include "order/so_tabs.html" with tab='details' %}
<hr>
<h4>{% trans "Sales Order Items" %}</h4>
@ -45,6 +48,7 @@ $("#so-lines-table").inventreeTable({
part_detail: true,
allocations: true,
},
uniqueId: 'pk',
url: "{% url 'api-so-line-list' %}",
onPostBody: setupCallbacks,
detailViewByClick: true,
@ -242,12 +246,28 @@ function setupCallbacks() {
});
table.find(".button-build").click(function() {
var pk = $(this).attr('pk');
// Extract the row data from the table!
var idx = $(this).closest('tr').attr('data-index');
var row = table.bootstrapTable('getData')[idx];
console.log('Row ' + idx + ' - ' + row.pk + ', ' + row.quantity);
var quantity = 1;
if (row.allocated < row.quantity) {
quantity = row.quantity - row.allocated;
}
launchModalForm(`/build/new/`, {
follow: true,
data: {
part: pk,
sales_order: {{ order.id }},
quantity: quantity,
},
});
});