Merge pull request #1688 from SchrodingersGat/supplier-part-parameters

Adds "parameters" for manufacturer parts
This commit is contained in:
Oliver 2021-06-21 16:39:15 +10:00 committed by GitHub
commit 5e5fc35655
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 588 additions and 24 deletions

View File

@ -20,9 +20,12 @@ v4 -> 2021-06-01
- BOM items can now accept "variant stock" to be assigned against them - BOM items can now accept "variant stock" to be assigned against them
- Many slight API tweaks were needed to get this to work properly! - Many slight API tweaks were needed to get this to work properly!
v5 -> 2021-06-21
- Adds API interface for manufacturer part parameters
""" """
INVENTREE_API_VERSION = 4 INVENTREE_API_VERSION = 5
def inventreeInstanceName(): def inventreeInstanceName():

View File

@ -11,6 +11,7 @@ import import_export.widgets as widgets
from .models import Company from .models import Company
from .models import SupplierPart from .models import SupplierPart
from .models import SupplierPriceBreak from .models import SupplierPriceBreak
from .models import ManufacturerPart, ManufacturerPartParameter
from part.models import Part from part.models import Part
@ -71,6 +72,92 @@ class SupplierPartAdmin(ImportExportModelAdmin):
] ]
class ManufacturerPartResource(ModelResource):
"""
Class for managing ManufacturerPart data import/export
"""
part = Field(attribute='part', widget=widgets.ForeignKeyWidget(Part))
part_name = Field(attribute='part__full_name', readonly=True)
manufacturer = Field(attribute='manufacturer', widget=widgets.ForeignKeyWidget(Company))
manufacturer_name = Field(attribute='manufacturer__name', readonly=True)
class Meta:
model = ManufacturerPart
skip_unchanged = True
report_skipped = True
clean_model_instances = True
class ManufacturerPartParameterInline(admin.TabularInline):
"""
Inline for editing ManufacturerPartParameter objects,
directly from the ManufacturerPart admin view.
"""
model = ManufacturerPartParameter
class SupplierPartInline(admin.TabularInline):
"""
Inline for the SupplierPart model
"""
model = SupplierPart
class ManufacturerPartAdmin(ImportExportModelAdmin):
"""
Admin class for ManufacturerPart model
"""
resource_class = ManufacturerPartResource
list_display = ('part', 'manufacturer', 'MPN')
search_fields = [
'manufacturer__name',
'part__name',
'MPN',
]
inlines = [
SupplierPartInline,
ManufacturerPartParameterInline,
]
class ManufacturerPartParameterResource(ModelResource):
"""
Class for managing ManufacturerPartParameter data import/export
"""
class Meta:
model = ManufacturerPartParameter
skip_unchanged = True
report_skipped = True
clean_model_instance = True
class ManufacturerPartParameterAdmin(ImportExportModelAdmin):
"""
Admin class for ManufacturerPartParameter model
"""
resource_class = ManufacturerPartParameterResource
list_display = ('manufacturer_part', 'name', 'value')
search_fields = [
'manufacturer_part__manufacturer__name',
'name',
'value'
]
class SupplierPriceBreakResource(ModelResource): class SupplierPriceBreakResource(ModelResource):
""" Class for managing SupplierPriceBreak data import/export """ """ Class for managing SupplierPriceBreak data import/export """
@ -103,3 +190,6 @@ class SupplierPriceBreakAdmin(ImportExportModelAdmin):
admin.site.register(Company, CompanyAdmin) admin.site.register(Company, CompanyAdmin)
admin.site.register(SupplierPart, SupplierPartAdmin) admin.site.register(SupplierPart, SupplierPartAdmin)
admin.site.register(SupplierPriceBreak, SupplierPriceBreakAdmin) admin.site.register(SupplierPriceBreak, SupplierPriceBreakAdmin)
admin.site.register(ManufacturerPart, ManufacturerPartAdmin)
admin.site.register(ManufacturerPartParameter, ManufacturerPartParameterAdmin)

View File

@ -15,11 +15,11 @@ from django.db.models import Q
from InvenTree.helpers import str2bool from InvenTree.helpers import str2bool
from .models import Company from .models import Company
from .models import ManufacturerPart from .models import ManufacturerPart, ManufacturerPartParameter
from .models import SupplierPart, SupplierPriceBreak from .models import SupplierPart, SupplierPriceBreak
from .serializers import CompanySerializer from .serializers import CompanySerializer
from .serializers import ManufacturerPartSerializer from .serializers import ManufacturerPartSerializer, ManufacturerPartParameterSerializer
from .serializers import SupplierPartSerializer, SupplierPriceBreakSerializer from .serializers import SupplierPartSerializer, SupplierPriceBreakSerializer
@ -175,6 +175,86 @@ class ManufacturerPartDetail(generics.RetrieveUpdateDestroyAPIView):
serializer_class = ManufacturerPartSerializer serializer_class = ManufacturerPartSerializer
class ManufacturerPartParameterList(generics.ListCreateAPIView):
"""
API endpoint for list view of ManufacturerPartParamater model.
"""
queryset = ManufacturerPartParameter.objects.all()
serializer_class = ManufacturerPartParameterSerializer
def get_serializer(self, *args, **kwargs):
# Do we wish to include any extra detail?
try:
params = self.request.query_params
optional_fields = [
'manufacturer_part_detail',
]
for key in optional_fields:
kwargs[key] = str2bool(params.get(key, None))
except AttributeError:
pass
kwargs['context'] = self.get_serializer_context()
return self.serializer_class(*args, **kwargs)
def filter_queryset(self, queryset):
"""
Custom filtering for the queryset
"""
queryset = super().filter_queryset(queryset)
params = self.request.query_params
# Filter by manufacturer?
manufacturer = params.get('manufacturer', None)
if manufacturer is not None:
queryset = queryset.filter(manufacturer_part__manufacturer=manufacturer)
# Filter by part?
part = params.get('part', None)
if part is not None:
queryset = queryset.filter(manufacturer_part__part=part)
return queryset
filter_backends = [
DjangoFilterBackend,
filters.SearchFilter,
filters.OrderingFilter,
]
filter_fields = [
'name',
'value',
'units',
'manufacturer_part',
]
search_fields = [
'name',
'value',
'units',
]
class ManufacturerPartParameterDetail(generics.RetrieveUpdateDestroyAPIView):
"""
API endpoint for detail view of ManufacturerPartParameter model
"""
queryset = ManufacturerPartParameter.objects.all()
serializer_class = ManufacturerPartParameterSerializer
class SupplierPartList(generics.ListCreateAPIView): class SupplierPartList(generics.ListCreateAPIView):
""" API endpoint for list view of SupplierPart object """ API endpoint for list view of SupplierPart object
@ -249,7 +329,7 @@ class SupplierPartList(generics.ListCreateAPIView):
params = self.request.query_params params = self.request.query_params
kwargs['part_detail'] = str2bool(params.get('part_detail', None)) kwargs['part_detail'] = str2bool(params.get('part_detail', None))
kwargs['supplier_detail'] = str2bool(params.get('supplier_detail', None)) kwargs['supplier_detail'] = str2bool(params.get('supplier_detail', None))
kwargs['manufacturer_detail'] = str2bool(self.params.get('manufacturer_detail', None)) kwargs['manufacturer_detail'] = str2bool(params.get('manufacturer_detail', None))
kwargs['pretty'] = str2bool(params.get('pretty', None)) kwargs['pretty'] = str2bool(params.get('pretty', None))
except AttributeError: except AttributeError:
pass pass
@ -316,6 +396,13 @@ class SupplierPriceBreakList(generics.ListCreateAPIView):
manufacturer_part_api_urls = [ manufacturer_part_api_urls = [
url(r'^parameter/', include([
url(r'^(?P<pk>\d+)/', ManufacturerPartParameterDetail.as_view(), name='api-manufacturer-part-parameter-detail'),
# Catch anything else
url(r'^.*$', ManufacturerPartParameterList.as_view(), name='api-manufacturer-part-parameter-list'),
])),
url(r'^(?P<pk>\d+)/?', ManufacturerPartDetail.as_view(), name='api-manufacturer-part-detail'), url(r'^(?P<pk>\d+)/?', ManufacturerPartDetail.as_view(), name='api-manufacturer-part-detail'),
# Catch anything else # Catch anything else

View File

@ -16,7 +16,7 @@ from djmoney.forms.fields import MoneyField
from common.settings import currency_code_default from common.settings import currency_code_default
from .models import Company from .models import Company, ManufacturerPartParameter
from .models import ManufacturerPart from .models import ManufacturerPart
from .models import SupplierPart from .models import SupplierPart
from .models import SupplierPriceBreak from .models import SupplierPriceBreak
@ -105,6 +105,21 @@ class EditManufacturerPartForm(HelperForm):
] ]
class EditManufacturerPartParameterForm(HelperForm):
"""
Form for creating / editing a ManufacturerPartParameter object
"""
class Meta:
model = ManufacturerPartParameter
fields = [
'manufacturer_part',
'name',
'value',
'units',
]
class EditSupplierPartForm(HelperForm): class EditSupplierPartForm(HelperForm):
""" Form for editing a SupplierPart object """ """ Form for editing a SupplierPart object """

View File

@ -0,0 +1,27 @@
# Generated by Django 3.2.4 on 2021-06-20 07:48
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('company', '0037_supplierpart_update_3'),
]
operations = [
migrations.CreateModel(
name='ManufacturerPartParameter',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(help_text='Parameter name', max_length=500, verbose_name='Name')),
('value', models.CharField(help_text='Parameter value', max_length=500, verbose_name='Value')),
('units', models.CharField(blank=True, help_text='Parameter units', max_length=64, null=True, verbose_name='Units')),
('manufacturer_part', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='parameters', to='company.manufacturerpart', verbose_name='Manufacturer Part')),
],
options={
'unique_together': {('manufacturer_part', 'name')},
},
),
]

View File

@ -371,6 +371,47 @@ class ManufacturerPart(models.Model):
return s return s
class ManufacturerPartParameter(models.Model):
"""
A ManufacturerPartParameter represents a key:value parameter for a MnaufacturerPart.
This is used to represent parmeters / properties for a particular manufacturer part.
Each parameter is a simple string (text) value.
"""
class Meta:
unique_together = ('manufacturer_part', 'name')
manufacturer_part = models.ForeignKey(
ManufacturerPart,
on_delete=models.CASCADE,
related_name='parameters',
verbose_name=_('Manufacturer Part'),
)
name = models.CharField(
max_length=500,
blank=False,
verbose_name=_('Name'),
help_text=_('Parameter name')
)
value = models.CharField(
max_length=500,
blank=False,
verbose_name=_('Value'),
help_text=_('Parameter value')
)
units = models.CharField(
max_length=64,
blank=True, null=True,
verbose_name=_('Units'),
help_text=_('Parameter units')
)
class SupplierPart(models.Model): class SupplierPart(models.Model):
""" Represents a unique part as provided by a Supplier """ Represents a unique part as provided by a Supplier
Each SupplierPart is identified by a SKU (Supplier Part Number) Each SupplierPart is identified by a SKU (Supplier Part Number)

View File

@ -7,7 +7,7 @@ from rest_framework import serializers
from sql_util.utils import SubqueryCount from sql_util.utils import SubqueryCount
from .models import Company from .models import Company
from .models import ManufacturerPart from .models import ManufacturerPart, ManufacturerPartParameter
from .models import SupplierPart, SupplierPriceBreak from .models import SupplierPart, SupplierPriceBreak
from InvenTree.serializers import InvenTreeModelSerializer from InvenTree.serializers import InvenTreeModelSerializer
@ -124,6 +124,35 @@ class ManufacturerPartSerializer(InvenTreeModelSerializer):
] ]
class ManufacturerPartParameterSerializer(InvenTreeModelSerializer):
"""
Serializer for the ManufacturerPartParameter model
"""
manufacturer_part_detail = ManufacturerPartSerializer(source='manufacturer_part', many=False, read_only=True)
def __init__(self, *args, **kwargs):
man_detail = kwargs.pop('manufacturer_part_detail', False)
super(ManufacturerPartParameterSerializer, self).__init__(*args, **kwargs)
if not man_detail:
self.fields.pop('manufacturer_part_detail')
class Meta:
model = ManufacturerPartParameter
fields = [
'pk',
'manufacturer_part',
'manufacturer_part_detail',
'name',
'value',
'units',
]
class SupplierPartSerializer(InvenTreeModelSerializer): class SupplierPartSerializer(InvenTreeModelSerializer):
""" Serializer for SupplierPart object """ """ Serializer for SupplierPart object """

View File

@ -7,7 +7,7 @@
{% endblock %} {% endblock %}
{% block heading %} {% block heading %}
{% trans "Supplier Parts" %} {% trans "Suppliers" %}
{% endblock %} {% endblock %}
{% block details %} {% block details %}
@ -30,9 +30,44 @@
{% endblock %} {% endblock %}
{% block post_content_panels %}
<div class='panel panel-default panel-inventree'>
<div class='panel-heading'>
<h4>{% trans "Parameters" %}</h4>
</div>
<div class='panel-content'>
<div id='parameter-toolbar'>
<div class='btn-group'>
<button class='btn btn-success' id='parameter-create'>
<span class='fas fa-plus-circle'></span> {% trans "New Parameter" %}
</button>
<div id='param-dropdown' class='btn-group'>
<!-- TODO -->
</div>
</div>
</div>
<table class='table table-striped table-condensed' id='parameter-table' data-toolbar='#parameter-toolbar'></table>
</div>
</div>
{% endblock %}
{% block js_ready %} {% block js_ready %}
{{ block.super }} {{ block.super }}
$('#parameter-create').click(function() {
launchModalForm(
"{% url 'manufacturer-part-parameter-create' %}",
{
data: {
manufacturer_part: {{ part.id }},
}
}
);
});
$('#supplier-create').click(function () { $('#supplier-create').click(function () {
launchModalForm( launchModalForm(
"{% url 'supplier-part-create' %}", "{% url 'supplier-part-create' %}",
@ -84,6 +119,16 @@ loadSupplierPartTable(
} }
); );
loadManufacturerPartParameterTable(
"#parameter-table",
"{% url 'api-manufacturer-part-parameter-list' %}",
{
params: {
manufacturer_part: {{ part.id }},
}
}
);
linkButtonsToSelection($("#supplier-table"), ['#supplier-part-options']) linkButtonsToSelection($("#supplier-table"), ['#supplier-part-options'])
{% endblock %} {% endblock %}

View File

@ -53,20 +53,25 @@ price_break_urls = [
url(r'^(?P<pk>\d+)/delete/', views.PriceBreakDelete.as_view(), name='price-break-delete'), url(r'^(?P<pk>\d+)/delete/', views.PriceBreakDelete.as_view(), name='price-break-delete'),
] ]
manufacturer_part_detail_urls = [
url(r'^edit/?', views.ManufacturerPartEdit.as_view(), name='manufacturer-part-edit'),
url(r'^suppliers/', views.ManufacturerPartDetail.as_view(template_name='company/manufacturer_part_suppliers.html'), name='manufacturer-part-suppliers'),
url('^.*$', views.ManufacturerPartDetail.as_view(template_name='company/manufacturer_part_suppliers.html'), name='manufacturer-part-detail'),
]
manufacturer_part_urls = [ manufacturer_part_urls = [
url(r'^new/?', views.ManufacturerPartCreate.as_view(), name='manufacturer-part-create'), url(r'^new/?', views.ManufacturerPartCreate.as_view(), name='manufacturer-part-create'),
url(r'delete/', views.ManufacturerPartDelete.as_view(), name='manufacturer-part-delete'), url(r'^delete/', views.ManufacturerPartDelete.as_view(), name='manufacturer-part-delete'),
url(r'^(?P<pk>\d+)/', include(manufacturer_part_detail_urls)), # URLs for ManufacturerPartParameter views (create / edit / delete)
url(r'^parameter/', include([
url(r'^new/', views.ManufacturerPartParameterCreate.as_view(), name='manufacturer-part-parameter-create'),
url(r'^(?P<pk>\d)/', include([
url(r'^edit/', views.ManufacturerPartParameterEdit.as_view(), name='manufacturer-part-parameter-edit'),
url(r'^delete/', views.ManufacturerPartParameterDelete.as_view(), name='manufacturer-part-parameter-delete'),
])),
])),
url(r'^(?P<pk>\d+)/', include([
url(r'^edit/?', views.ManufacturerPartEdit.as_view(), name='manufacturer-part-edit'),
url(r'^suppliers/', views.ManufacturerPartDetail.as_view(template_name='company/manufacturer_part_suppliers.html'), name='manufacturer-part-suppliers'),
url('^.*$', views.ManufacturerPartDetail.as_view(template_name='company/manufacturer_part_suppliers.html'), name='manufacturer-part-detail'),
])),
] ]
supplier_part_detail_urls = [ supplier_part_detail_urls = [

View File

@ -23,14 +23,14 @@ from InvenTree.views import AjaxCreateView, AjaxUpdateView, AjaxDeleteView
from InvenTree.helpers import str2bool from InvenTree.helpers import str2bool
from InvenTree.views import InvenTreeRoleMixin from InvenTree.views import InvenTreeRoleMixin
from .models import Company from .models import Company, ManufacturerPartParameter
from .models import ManufacturerPart from .models import ManufacturerPart
from .models import SupplierPart from .models import SupplierPart
from .models import SupplierPriceBreak from .models import SupplierPriceBreak
from part.models import Part from part.models import Part
from .forms import EditCompanyForm from .forms import EditCompanyForm, EditManufacturerPartParameterForm
from .forms import CompanyImageForm from .forms import CompanyImageForm
from .forms import EditManufacturerPartForm from .forms import EditManufacturerPartForm
from .forms import EditSupplierPartForm from .forms import EditSupplierPartForm
@ -504,6 +504,66 @@ class ManufacturerPartDelete(AjaxDeleteView):
return self.renderJsonResponse(self.request, data=data, form=self.get_form()) return self.renderJsonResponse(self.request, data=data, form=self.get_form())
class ManufacturerPartParameterCreate(AjaxCreateView):
"""
View for creating a new ManufacturerPartParameter object
"""
model = ManufacturerPartParameter
form_class = EditManufacturerPartParameterForm
ajax_form_title = _('Add Manufacturer Part Parameter')
def get_form(self):
form = super().get_form()
# Hide the manufacturer_part field if specified
if form.initial.get('manufacturer_part', None):
form.fields['manufacturer_part'].widget = HiddenInput()
return form
def get_initial(self):
initials = super().get_initial().copy()
manufacturer_part = self.get_param('manufacturer_part')
if manufacturer_part:
try:
initials['manufacturer_part'] = ManufacturerPartParameter.objects.get(pk=manufacturer_part)
except (ValueError, ManufacturerPartParameter.DoesNotExist):
pass
return initials
class ManufacturerPartParameterEdit(AjaxUpdateView):
"""
View for editing a ManufacturerPartParameter object
"""
model = ManufacturerPartParameter
form_class = EditManufacturerPartParameterForm
ajax_form_title = _('Edit Manufacturer Part Parameter')
def get_form(self):
form = super().get_form()
form.fields['manufacturer_part'].widget = HiddenInput()
return form
class ManufacturerPartParameterDelete(AjaxDeleteView):
"""
View for deleting a ManufacturerPartParameter object
"""
model = ManufacturerPartParameter
class SupplierPartDetail(DetailView): class SupplierPartDetail(DetailView):
""" Detail view for SupplierPart """ """ Detail view for SupplierPart """
model = SupplierPart model = SupplierPart

View File

@ -111,6 +111,13 @@ class PartCategoryResource(ModelResource):
PartCategory.objects.rebuild() PartCategory.objects.rebuild()
class PartCategoryInline(admin.TabularInline):
"""
Inline for PartCategory model
"""
model = PartCategory
class PartCategoryAdmin(ImportExportModelAdmin): class PartCategoryAdmin(ImportExportModelAdmin):
resource_class = PartCategoryResource resource_class = PartCategoryResource
@ -119,6 +126,10 @@ class PartCategoryAdmin(ImportExportModelAdmin):
search_fields = ('name', 'description') search_fields = ('name', 'description')
inlines = [
PartCategoryInline,
]
class PartRelatedAdmin(admin.ModelAdmin): class PartRelatedAdmin(admin.ModelAdmin):
''' Class to manage PartRelated objects ''' ''' Class to manage PartRelated objects '''

View File

@ -380,7 +380,6 @@ class Part(MPTTModel):
previous.image.delete(save=False) previous.image.delete(save=False)
self.clean() self.clean()
self.validate_unique()
super().save(*args, **kwargs) super().save(*args, **kwargs)
@ -672,6 +671,8 @@ class Part(MPTTModel):
super().clean() super().clean()
self.validate_unique()
if self.trackable: if self.trackable:
for part in self.get_used_in().all(): for part in self.get_used_in().all():

View File

@ -5,6 +5,7 @@ over and above the built-in Django tags.
""" """
import os import os
import sys
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.conf import settings as djangosettings from django.conf import settings as djangosettings
@ -114,6 +115,14 @@ def inventree_title(*args, **kwargs):
return version.inventreeInstanceTitle() return version.inventreeInstanceTitle()
@register.simple_tag()
def python_version(*args, **kwargs):
"""
Return the current python version
"""
return sys.version.split(' ')[0]
@register.simple_tag() @register.simple_tag()
def inventree_version(*args, **kwargs): def inventree_version(*args, **kwargs):
""" Return InvenTree version string """ """ Return InvenTree version string """

View File

@ -44,6 +44,13 @@ class LocationResource(ModelResource):
StockLocation.objects.rebuild() StockLocation.objects.rebuild()
class LocationInline(admin.TabularInline):
"""
Inline for sub-locations
"""
model = StockLocation
class LocationAdmin(ImportExportModelAdmin): class LocationAdmin(ImportExportModelAdmin):
resource_class = LocationResource resource_class = LocationResource
@ -52,6 +59,10 @@ class LocationAdmin(ImportExportModelAdmin):
search_fields = ('name', 'description') search_fields = ('name', 'description')
inlines = [
LocationInline,
]
class StockItemResource(ModelResource): class StockItemResource(ModelResource):
""" Class for managing StockItem data import/export """ """ Class for managing StockItem data import/export """

View File

@ -34,6 +34,11 @@
<td>{% trans "API Version" %}</td> <td>{% trans "API Version" %}</td>
<td>{% inventree_api_version %}{% include "clip.html" %}</td> <td>{% inventree_api_version %}{% include "clip.html" %}</td>
</tr> </tr>
<tr>
<td><span class='fas fa-hashtag'></span></td>
<td>{% trans "Python Version" %}</td>
<td>{% python_version %}</td>
</tr>
<tr> <tr>
<td><span class='fas fa-hashtag'></span></td> <td><span class='fas fa-hashtag'></span></td>
<td>{% trans "Django Version" %}</td> <td>{% trans "Django Version" %}</td>

View File

@ -126,7 +126,7 @@ function loadManufacturerPartTable(table, url, options) {
queryParams: filters, queryParams: filters,
name: 'manufacturerparts', name: 'manufacturerparts',
groupBy: false, groupBy: false,
formatNoMatches: function() { return "{% trans "No manufacturer parts found" %}"; }, formatNoMatches: function() { return '{% trans "No manufacturer parts found" %}'; },
columns: [ columns: [
{ {
checkbox: true, checkbox: true,
@ -199,6 +199,107 @@ function loadManufacturerPartTable(table, url, options) {
} }
function loadManufacturerPartParameterTable(table, url, options) {
/*
* Load table of ManufacturerPartParameter objects
*/
var params = options.params || {};
// Load filters
var filters = loadTableFilters("manufacturer-part-parameters");
// Overwrite explicit parameters
for (var key in params) {
filters[key] = params[key];
}
// setupFilterList("manufacturer-part-parameters", $(table));
$(table).inventreeTable({
url: url,
method: 'get',
original: params,
queryParams: filters,
name: 'manufacturerpartparameters',
groupBy: false,
formatNoMatches: function() { return '{% trans "No parameters found" %}'; },
columns: [
{
checkbox: true,
switchable: false,
visible: false,
},
{
field: 'name',
title: '{% trans "Name" %}',
switchable: false,
sortable: true,
},
{
field: 'value',
title: '{% trans "Value" %}',
switchable: false,
sortable: true,
},
{
field: 'units',
title: '{% trans "Units" %}',
switchable: true,
sortable: true,
},
{
field: 'actions',
title: '',
switchable: false,
sortable: false,
formatter: function(value, row) {
var pk = row.pk;
var html = `<div class='btn-group float-right' role='group'>`;
html += makeIconButton('fa-edit icon-blue', 'button-parameter-edit', pk, '{% trans "Edit parameter" %}');
html += makeIconButton('fa-trash-alt icon-red', 'button-parameter-delete', pk, '{% trans "Delete parameter" %}');
html += `</div>`;
return html;
}
}
],
onPostBody: function() {
// Setup callback functions
$(table).find('.button-parameter-edit').click(function() {
var pk = $(this).attr('pk');
launchModalForm(
`/manufacturer-part/parameter/${pk}/edit/`,
{
success: function() {
$(table).bootstrapTable('refresh');
}
}
);
});
$(table).find('.button-parameter-delete').click(function() {
var pk = $(this).attr('pk');
launchModalForm(
`/manufacturer-part/parameter/${pk}/delete/`,
{
success: function() {
$(table).bootstrapTable('refresh');
}
}
);
});
}
});
}
function loadSupplierPartTable(table, url, options) { function loadSupplierPartTable(table, url, options) {
/* /*
* Load supplier part table * Load supplier part table
@ -224,7 +325,7 @@ function loadSupplierPartTable(table, url, options) {
queryParams: filters, queryParams: filters,
name: 'supplierparts', name: 'supplierparts',
groupBy: false, groupBy: false,
formatNoMatches: function() { return "{% trans "No supplier parts found" %}"; }, formatNoMatches: function() { return '{% trans "No supplier parts found" %}'; },
columns: [ columns: [
{ {
checkbox: true, checkbox: true,
@ -260,7 +361,7 @@ function loadSupplierPartTable(table, url, options) {
{ {
sortable: true, sortable: true,
field: 'supplier', field: 'supplier',
title: "{% trans "Supplier" %}", title: '{% trans "Supplier" %}',
formatter: function(value, row, index, field) { formatter: function(value, row, index, field) {
if (value) { if (value) {
var name = row.supplier_detail.name; var name = row.supplier_detail.name;
@ -276,7 +377,7 @@ function loadSupplierPartTable(table, url, options) {
{ {
sortable: true, sortable: true,
field: 'SKU', field: 'SKU',
title: "{% trans "Supplier Part" %}", title: '{% trans "Supplier Part" %}',
formatter: function(value, row, index, field) { formatter: function(value, row, index, field) {
return renderLink(value, `/supplier-part/${row.pk}/`); return renderLink(value, `/supplier-part/${row.pk}/`);
} }

View File

@ -43,6 +43,9 @@
</div> </div>
{% endblock %} {% endblock %}
{% block pre_content_panels %}
{% endblock %}
{% block content_panels %} {% block content_panels %}
<div class='panel panel-default panel-inventree'> <div class='panel panel-default panel-inventree'>
<div class='panel-heading'> <div class='panel-heading'>
@ -63,6 +66,9 @@
</div> </div>
{% endblock %} {% endblock %}
{% block post_content_panels %}
{% endblock %}
{% endblock %} {% endblock %}
{% block js_ready %} {% block js_ready %}

View File

@ -85,6 +85,7 @@ class RuleSet(models.Model):
'part_partstar', 'part_partstar',
'company_supplierpart', 'company_supplierpart',
'company_manufacturerpart', 'company_manufacturerpart',
'company_manufacturerpartparameter',
], ],
'stock_location': [ 'stock_location': [
'stock_stocklocation', 'stock_stocklocation',
@ -116,6 +117,8 @@ class RuleSet(models.Model):
'order_purchaseorderattachment', 'order_purchaseorderattachment',
'order_purchaseorderlineitem', 'order_purchaseorderlineitem',
'company_supplierpart', 'company_supplierpart',
'company_manufacturerpart',
'company_manufacturerpartparameter',
], ],
'sales_order': [ 'sales_order': [
'company_company', 'company_company',

View File

@ -365,6 +365,21 @@ def import_records(c, filename='data.json'):
print("Data import completed") print("Data import completed")
@task
def delete_data(c, force=False):
"""
Delete all database records!
Warning: This will REALLY delete all records in the database!!
"""
if force:
manage(c, 'flush --noinput')
else:
manage(c, 'flush')
@task(post=[rebuild]) @task(post=[rebuild])
def import_fixtures(c): def import_fixtures(c):
""" """