mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
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:
parent
b351976ae9
commit
d5f3498238
@ -38,6 +38,8 @@ class BuildList(generics.ListCreateAPIView):
|
|||||||
]
|
]
|
||||||
|
|
||||||
filter_fields = [
|
filter_fields = [
|
||||||
|
'part',
|
||||||
|
'sales_order',
|
||||||
]
|
]
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
@ -48,12 +50,6 @@ class BuildList(generics.ListCreateAPIView):
|
|||||||
|
|
||||||
build_list = super().get_queryset()
|
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?
|
# Filter by build status?
|
||||||
status = self.request.query_params.get('status', None)
|
status = self.request.query_params.get('status', None)
|
||||||
|
|
||||||
|
@ -22,6 +22,7 @@ class EditBuildForm(HelperForm):
|
|||||||
fields = [
|
fields = [
|
||||||
'title',
|
'title',
|
||||||
'part',
|
'part',
|
||||||
|
'sales_order',
|
||||||
'quantity',
|
'quantity',
|
||||||
'take_from',
|
'take_from',
|
||||||
'batch',
|
'batch',
|
||||||
|
20
InvenTree/build/migrations/0012_build_sales_order.py
Normal file
20
InvenTree/build/migrations/0012_build_sales_order.py
Normal 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'),
|
||||||
|
),
|
||||||
|
]
|
@ -33,6 +33,7 @@ class Build(models.Model):
|
|||||||
part: The part to be built (from component BOM items)
|
part: The part to be built (from component BOM items)
|
||||||
title: Brief title describing the build (required)
|
title: Brief title describing the build (required)
|
||||||
quantity: Number of units to be built
|
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)
|
take_from: Location to take stock from to make this build (if blank, can take from anywhere)
|
||||||
status: Build status code
|
status: Build status code
|
||||||
batch: Batch code transferred to build parts (optional)
|
batch: Batch code transferred to build parts (optional)
|
||||||
@ -51,24 +52,37 @@ class Build(models.Model):
|
|||||||
title = models.CharField(
|
title = models.CharField(
|
||||||
blank=False,
|
blank=False,
|
||||||
max_length=100,
|
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,
|
part = models.ForeignKey(
|
||||||
related_name='builds',
|
'part.Part',
|
||||||
limit_choices_to={
|
on_delete=models.CASCADE,
|
||||||
'is_template': False,
|
related_name='builds',
|
||||||
'assembly': True,
|
limit_choices_to={
|
||||||
'active': True,
|
'is_template': False,
|
||||||
'virtual': False,
|
'assembly': True,
|
||||||
},
|
'active': True,
|
||||||
help_text=_('Select part to build'),
|
'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,
|
take_from = models.ForeignKey(
|
||||||
related_name='sourcing_builds',
|
'stock.StockLocation',
|
||||||
null=True, blank=True,
|
on_delete=models.SET_NULL,
|
||||||
help_text=_('Select location to take stock from for this build (leave blank to take from any stock location)')
|
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(
|
quantity = models.PositiveIntegerField(
|
||||||
default=1,
|
default=1,
|
||||||
|
@ -39,6 +39,7 @@ class BuildSerializer(InvenTreeModelSerializer):
|
|||||||
'completion_date',
|
'completion_date',
|
||||||
'part',
|
'part',
|
||||||
'part_detail',
|
'part_detail',
|
||||||
|
'sales_order',
|
||||||
'quantity',
|
'quantity',
|
||||||
'status',
|
'status',
|
||||||
'status_text',
|
'status_text',
|
||||||
|
@ -8,6 +8,14 @@
|
|||||||
InvenTree | {% trans "Build" %} - {{ build }}
|
InvenTree | {% trans "Build" %} - {{ build }}
|
||||||
{% endblock %}
|
{% 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 %}
|
{% block thumbnail %}
|
||||||
<img class="part-thumb"
|
<img class="part-thumb"
|
||||||
{% if build.part.image %}
|
{% if build.part.image %}
|
||||||
@ -54,7 +62,7 @@ src="{% static 'img/blank_image.png' %}"
|
|||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><span class='fas fa-shapes'></span></td>
|
<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>
|
<td><a href="{% url 'part-detail' build.part.id %}">{{ build.part.full_name }}</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
@ -67,6 +75,13 @@ src="{% static 'img/blank_image.png' %}"
|
|||||||
<td>{% trans "Status" %}</td>
|
<td>{% trans "Status" %}</td>
|
||||||
<td>{% build_status_label build.status %}</td>
|
<td>{% build_status_label build.status %}</td>
|
||||||
</tr>
|
</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>
|
<tr>
|
||||||
<td><span class='fas fa-dollar-sign'></span></td>
|
<td><span class='fas fa-dollar-sign'></span></td>
|
||||||
<td>{% trans "BOM Price" %}</td>
|
<td>{% trans "BOM Price" %}</td>
|
||||||
|
@ -393,13 +393,13 @@ class BuildCreate(AjaxCreateView):
|
|||||||
|
|
||||||
initials = super(BuildCreate, self).get_initial().copy()
|
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:
|
# User has provided a SalesOrder ID
|
||||||
try:
|
initials['sales_order'] = self.request.GET.get('sales_order', None)
|
||||||
initials['part'] = Part.objects.get(pk=part_id)
|
|
||||||
except Part.DoesNotExist:
|
initials['quantity'] = self.request.GET.get('quantity', 1)
|
||||||
pass
|
|
||||||
|
|
||||||
return initials
|
return initials
|
||||||
|
|
||||||
|
@ -6,6 +6,9 @@
|
|||||||
{% load static %}
|
{% load static %}
|
||||||
|
|
||||||
{% block details %}
|
{% block details %}
|
||||||
|
|
||||||
|
{% include "order/so_tabs.html" with tab='details' %}
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
<h4>{% trans "Sales Order Items" %}</h4>
|
<h4>{% trans "Sales Order Items" %}</h4>
|
||||||
@ -45,6 +48,7 @@ $("#so-lines-table").inventreeTable({
|
|||||||
part_detail: true,
|
part_detail: true,
|
||||||
allocations: true,
|
allocations: true,
|
||||||
},
|
},
|
||||||
|
uniqueId: 'pk',
|
||||||
url: "{% url 'api-so-line-list' %}",
|
url: "{% url 'api-so-line-list' %}",
|
||||||
onPostBody: setupCallbacks,
|
onPostBody: setupCallbacks,
|
||||||
detailViewByClick: true,
|
detailViewByClick: true,
|
||||||
@ -242,12 +246,28 @@ function setupCallbacks() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
table.find(".button-build").click(function() {
|
table.find(".button-build").click(function() {
|
||||||
|
|
||||||
var pk = $(this).attr('pk');
|
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/`, {
|
launchModalForm(`/build/new/`, {
|
||||||
follow: true,
|
follow: true,
|
||||||
data: {
|
data: {
|
||||||
part: pk,
|
part: pk,
|
||||||
|
sales_order: {{ order.id }},
|
||||||
|
quantity: quantity,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user