Merge remote-tracking branch 'inventree/master'

This commit is contained in:
Oliver Walters 2021-01-03 23:34:50 +11:00
commit d5fcb83ad1
11 changed files with 947 additions and 763 deletions

View File

@ -38,7 +38,9 @@ class CompanyAdmin(ImportExportModelAdmin):
class SupplierPartResource(ModelResource):
""" Class for managing SupplierPart data import/export """
"""
Class for managing SupplierPart data import/export
"""
part = Field(attribute='part', widget=widgets.ForeignKeyWidget(Part))
@ -51,7 +53,7 @@ class SupplierPartResource(ModelResource):
class Meta:
model = SupplierPart
skip_unchanged = True
report_skipped = False
report_skipped = True
clean_model_instances = True

View File

@ -0,0 +1,61 @@
# Generated by Django 3.0.7 on 2021-01-03 11:15
import InvenTree.fields
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('part', '0060_merge_20201112_1722'),
('company', '0030_auto_20201112_1112'),
]
operations = [
migrations.AlterField(
model_name='supplierpart',
name='MPN',
field=models.CharField(blank=True, help_text='Manufacturer part number', max_length=100, null=True, verbose_name='MPN'),
),
migrations.AlterField(
model_name='supplierpart',
name='SKU',
field=models.CharField(help_text='Supplier stock keeping unit', max_length=100, verbose_name='SKU'),
),
migrations.AlterField(
model_name='supplierpart',
name='description',
field=models.CharField(blank=True, help_text='Supplier part description', max_length=250, null=True, verbose_name='Description'),
),
migrations.AlterField(
model_name='supplierpart',
name='link',
field=InvenTree.fields.InvenTreeURLField(blank=True, help_text='URL for external supplier part link', null=True, verbose_name='Link'),
),
migrations.AlterField(
model_name='supplierpart',
name='manufacturer',
field=models.ForeignKey(blank=True, help_text='Select manufacturer', limit_choices_to={'is_manufacturer': True}, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='manufactured_parts', to='company.Company', verbose_name='Manufacturer'),
),
migrations.AlterField(
model_name='supplierpart',
name='note',
field=models.CharField(blank=True, help_text='Notes', max_length=100, null=True, verbose_name='Note'),
),
migrations.AlterField(
model_name='supplierpart',
name='packaging',
field=models.CharField(blank=True, help_text='Part packaging', max_length=50, null=True),
),
migrations.AlterField(
model_name='supplierpart',
name='part',
field=models.ForeignKey(help_text='Select part', limit_choices_to={'purchaseable': True}, on_delete=django.db.models.deletion.CASCADE, related_name='supplier_parts', to='part.Part', verbose_name='Base Part'),
),
migrations.AlterField(
model_name='supplierpart',
name='supplier',
field=models.ForeignKey(help_text='Select supplier', limit_choices_to={'is_supplier': True}, on_delete=django.db.models.deletion.CASCADE, related_name='supplied_parts', to='company.Company', verbose_name='Supplier'),
),
]

View File

@ -313,7 +313,6 @@ class SupplierPart(models.Model):
verbose_name=_('Base Part'),
limit_choices_to={
'purchaseable': True,
'is_template': False,
},
help_text=_('Select part'),
)
@ -321,31 +320,55 @@ class SupplierPart(models.Model):
supplier = models.ForeignKey(Company, on_delete=models.CASCADE,
related_name='supplied_parts',
limit_choices_to={'is_supplier': True},
verbose_name=_('Supplier'),
help_text=_('Select supplier'),
)
SKU = models.CharField(max_length=100, help_text=_('Supplier stock keeping unit'))
SKU = models.CharField(
max_length=100,
verbose_name=_('SKU'),
help_text=_('Supplier stock keeping unit')
)
manufacturer = models.ForeignKey(
Company,
on_delete=models.SET_NULL,
related_name='manufactured_parts',
limit_choices_to={'is_manufacturer': True},
limit_choices_to={
'is_manufacturer': True
},
verbose_name=_('Manufacturer'),
help_text=_('Select manufacturer'),
null=True, blank=True
)
MPN = models.CharField(max_length=100, blank=True, help_text=_('Manufacturer part number'))
MPN = models.CharField(
max_length=100, blank=True, null=True,
verbose_name=_('MPN'),
help_text=_('Manufacturer part number')
)
link = InvenTreeURLField(blank=True, help_text=_('URL for external supplier part link'))
link = InvenTreeURLField(
blank=True, null=True,
verbose_name=_('Link'),
help_text=_('URL for external supplier part link')
)
description = models.CharField(max_length=250, blank=True, help_text=_('Supplier part description'))
description = models.CharField(
max_length=250, blank=True, null=True,
verbose_name=_('Description'),
help_text=_('Supplier part description')
)
note = models.CharField(max_length=100, blank=True, help_text=_('Notes'))
note = models.CharField(
max_length=100, blank=True, null=True,
verbose_name=_('Note'),
help_text=_('Notes')
)
base_cost = models.DecimalField(max_digits=10, decimal_places=3, default=0, validators=[MinValueValidator(0)], help_text=_('Minimum charge (e.g. stocking fee)'))
packaging = models.CharField(max_length=50, blank=True, help_text=_('Part packaging'))
packaging = models.CharField(max_length=50, blank=True, null=True, help_text=_('Part packaging'))
multiple = models.PositiveIntegerField(default=1, validators=[MinValueValidator(1)], help_text=('Order multiple'))

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -319,7 +319,9 @@ class EditBomItemForm(HelperForm):
]
# Prevent editing of the part associated with this BomItem
widgets = {'part': forms.HiddenInput()}
widgets = {
'part': forms.HiddenInput()
}
class PartPriceForm(forms.Form):

View File

@ -1074,7 +1074,7 @@ class Part(MPTTModel):
self.bom_items.all().delete()
def getRequiredParts(self, recursive=False, parts=set()):
def getRequiredParts(self, recursive=False, parts=None):
"""
Return a list of parts required to make this part (i.e. BOM items).
@ -1083,7 +1083,10 @@ class Part(MPTTModel):
parts: Set of parts already found (to prevent recursion issues)
"""
items = self.bom_items.all().prefetch_related('sub_part')
if parts is None:
parts = set()
items = BomItem.objects.filter(part=self.pk)
for bom_item in items:

View File

@ -32,7 +32,7 @@
<div class="btn-group" role="group" aria-label="...">
{% if editing_enabled %}
<button class='btn btn-default' type='button' title='{% trans "Remove selected BOM items" %}' id='bom-item-delete'>
<span class='fas fa-trash-alt'></span>
<span class='fas fa-trash-alt icon-red'></span>
</button>
<button class='btn btn-primary' type='button' title='{% trans "Import BOM data" %}' id='bom-upload'>
<span class='fas fa-file-upload'></span> {% trans "Import from File" %}

View File

@ -1307,7 +1307,8 @@ class StockItemEdit(AjaxUpdateView):
# If the part cannot be purchased, hide the supplier_part field
if not item.part.purchaseable:
form.fields['supplier_part'].widget = HiddenInput()
form.fields['purchase_price'].widget = HiddenInput()
form.fields.pop('purchase_price')
else:
query = form.fields['supplier_part'].queryset
query = query.filter(part=item.part.id)
@ -1522,8 +1523,8 @@ class StockItemCreate(AjaxCreateView):
form.rebuild_layout()
if not part.purchaseable:
form.fields['purchase_price'].widget = HiddenInput()
form.fields.pop('purchase_price')
# Hide the 'part' field (as a valid part is selected)
# form.fields['part'].widget = HiddenInput()

View File

@ -278,7 +278,7 @@ function loadStockTable(table, options) {
if (row.is_building && row.build) {
// StockItem is currently being built!
text = "{% trans "In production" %}";
text = '{% trans "In production" %}';
url = `/build/${row.build}/`;
} else if (row.belongs_to) {
// StockItem is installed inside a different StockItem
@ -286,17 +286,17 @@ function loadStockTable(table, options) {
url = `/stock/item/${row.belongs_to}/installed/`;
} else if (row.customer) {
// StockItem has been assigned to a customer
text = "{% trans "Shipped to customer" %}";
text = '{% trans "Shipped to customer" %}';
url = `/company/${row.customer}/assigned-stock/`;
} else if (row.sales_order) {
// StockItem has been assigned to a sales order
text = "{% trans "Assigned to Sales Order" %}";
text = '{% trans "Assigned to Sales Order" %}';
url = `/order/sales-order/${row.sales_order}/`;
} else if (row.location) {
text = row.location_detail.pathstring;
url = `/stock/location/${row.location}/`;
} else {
text = "<i>{% trans "No stock location set" %}</i>";
text = '<i>{% trans "No stock location set" %}</i>';
url = '';
}
@ -336,7 +336,13 @@ function loadStockTable(table, options) {
return html;
}
else if (field == 'part_detail.IPN') {
return row.part_detail.IPN;
var ipn = row.part_detail.IPN;
if (ipn) {
return ipn;
} else {
return '-';
}
}
else if (field == 'part_detail.description') {
return row.part_detail.description;