2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-06-20 22:06:28 +00:00

removes all lines consisting only of spaces

this really bothers me for some reason - nothing technical
This commit is contained in:
2021-05-06 12:11:38 +02:00
parent ecc9eec084
commit f2b0717d10
91 changed files with 494 additions and 494 deletions

View File

@ -25,13 +25,13 @@ class PartResource(ModelResource):
# ForeignKey fields
category = Field(attribute='category', widget=widgets.ForeignKeyWidget(PartCategory))
default_location = Field(attribute='default_location', widget=widgets.ForeignKeyWidget(StockLocation))
default_supplier = Field(attribute='default_supplier', widget=widgets.ForeignKeyWidget(SupplierPart))
category_name = Field(attribute='category__name', readonly=True)
variant_of = Field(attribute='variant_of', widget=widgets.ForeignKeyWidget(Part))
suppliers = Field(attribute='supplier_count', readonly=True)
@ -73,7 +73,7 @@ class PartResource(ModelResource):
class PartAdmin(ImportExportModelAdmin):
resource_class = PartResource
list_display = ('full_name', 'description', 'total_stock', 'category')

View File

@ -41,7 +41,7 @@ class PartCategoryTree(TreeSerializer):
model = PartCategory
queryset = PartCategory.objects.all()
@property
def root_url(self):
return reverse('part-index')
@ -79,7 +79,7 @@ class CategoryList(generics.ListCreateAPIView):
pass
# Look for top-level categories
elif isNull(cat_id):
if not cascade:
queryset = queryset.filter(parent=None)
@ -166,9 +166,9 @@ class CategoryParameters(generics.ListAPIView):
parent_categories = category.get_ancestors()
for parent in parent_categories:
category_list.append(parent.pk)
queryset = queryset.filter(category__in=category_list)
return queryset
@ -264,7 +264,7 @@ class PartThumbs(generics.ListAPIView):
# Get all Parts which have an associated image
queryset = queryset.exclude(image='')
return queryset
def list(self, request, *args, **kwargs):
@ -301,7 +301,7 @@ class PartDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = Part.objects.all()
serializer_class = part_serializers.PartSerializer
starred_parts = None
def get_queryset(self, *args, **kwargs):
@ -482,7 +482,7 @@ class PartList(generics.ListCreateAPIView):
def get_queryset(self, *args, **kwargs):
queryset = super().get_queryset(*args, **kwargs)
queryset = part_serializers.PartSerializer.prefetch_queryset(queryset)
queryset = part_serializers.PartSerializer.annotate_queryset(queryset)
@ -576,7 +576,7 @@ class PartList(generics.ListCreateAPIView):
if cat_id is None:
# No category filtering if category is not specified
pass
else:
# Category has been specified!
if isNull(cat_id):
@ -780,10 +780,10 @@ class BomList(generics.ListCreateAPIView):
kwargs['sub_part_detail'] = str2bool(self.request.GET.get('sub_part_detail', None))
except AttributeError:
pass
# Ensure the request context is passed through!
kwargs['context'] = self.get_serializer_context()
return self.serializer_class(*args, **kwargs)
def get_queryset(self, *args, **kwargs):
@ -867,7 +867,7 @@ class BomList(generics.ListCreateAPIView):
# Work out which lines have actually been validated
pks = []
for bom_item in queryset.all():
if bom_item.is_line_valid:
pks.append(bom_item.pk)
@ -915,7 +915,7 @@ class BomItemValidate(generics.UpdateAPIView):
valid = request.data.get('valid', False)
instance = self.get_object()
serializer = self.get_serializer(instance, data=request.data, partial=partial)
serializer.is_valid(raise_exception=True)
@ -949,7 +949,7 @@ part_api_urls = [
url(r'^sale-price/', include([
url(r'^.*$', PartSalePriceList.as_view(), name='api-part-sale-price-list'),
])),
# Base URL for PartParameter API endpoints
url(r'^parameter/', include([
url(r'^template/$', PartParameterTemplateList.as_view(), name='api-part-param-template-list'),

View File

@ -43,7 +43,7 @@ class PartConfig(AppConfig):
if part.image:
url = part.image.thumbnail.name
loc = os.path.join(settings.MEDIA_ROOT, url)
if not os.path.exists(loc):
logger.info("InvenTree: Generating thumbnail for Part '{p}'".format(p=part.name))
try:

View File

@ -69,7 +69,7 @@ def ExportBom(part, fmt='csv', cascade=False, max_levels=None, parameter_data=Fa
for item in items:
item.level = str(int(level))
# Avoid circular BOM references
if item.pk in uids:
continue
@ -79,7 +79,7 @@ def ExportBom(part, fmt='csv', cascade=False, max_levels=None, parameter_data=Fa
if item.sub_part.assembly:
if max_levels is None or level < max_levels:
add_items(item.sub_part.bom_items.all().order_by('id'), level + 1)
if cascade:
# Cascading (multi-level) BOM
@ -124,7 +124,7 @@ def ExportBom(part, fmt='csv', cascade=False, max_levels=None, parameter_data=Fa
parameter_cols[name].update({b_idx: value})
except KeyError:
parameter_cols[name] = {b_idx: value}
# Add parameter columns to dataset
parameter_cols_ordered = OrderedDict(sorted(parameter_cols.items(), key=lambda x: x[0]))
add_columns_to_dataset(parameter_cols_ordered, len(bom_items))
@ -185,7 +185,7 @@ def ExportBom(part, fmt='csv', cascade=False, max_levels=None, parameter_data=Fa
# Filter manufacturer parts
manufacturer_parts = ManufacturerPart.objects.filter(part__pk=b_part.pk)
manufacturer_parts = manufacturer_parts.prefetch_related('supplier_parts')
# Process manufacturer part
for manufacturer_idx, manufacturer_part in enumerate(manufacturer_parts):
@ -250,7 +250,7 @@ def ExportBom(part, fmt='csv', cascade=False, max_levels=None, parameter_data=Fa
# Filter supplier parts
manufacturer_parts = ManufacturerPart.objects.filter(part__pk=b_part.pk)
for idx, manufacturer_part in enumerate(manufacturer_parts):
if manufacturer_part:
@ -295,7 +295,7 @@ def ExportBom(part, fmt='csv', cascade=False, max_levels=None, parameter_data=Fa
# Filter supplier parts
supplier_parts = SupplierPart.objects.filter(part__pk=b_part.pk)
for idx, supplier_part in enumerate(supplier_parts):
if supplier_part.supplier:
@ -326,7 +326,7 @@ def ExportBom(part, fmt='csv', cascade=False, max_levels=None, parameter_data=Fa
filename = '{n}_BOM.{fmt}'.format(n=part.full_name, fmt=fmt)
return DownloadFile(data, filename)
class BomUploadManager:
""" Class for managing an uploaded BOM file """
@ -342,7 +342,7 @@ class BomUploadManager:
'Part_IPN',
'Part_ID',
]
# Fields which would be helpful but are not required
OPTIONAL_HEADERS = [
'Reference',
@ -360,7 +360,7 @@ class BomUploadManager:
def __init__(self, bom_file):
""" Initialize the BomUpload class with a user-uploaded file object """
self.process(bom_file)
def process(self, bom_file):
@ -387,7 +387,7 @@ class BomUploadManager:
def guess_header(self, header, threshold=80):
""" Try to match a header (from the file) to a list of known headers
Args:
header - Header name to look for
threshold - Match threshold for fuzzy search
@ -421,7 +421,7 @@ class BomUploadManager:
return matches[0]['header']
return None
def columns(self):
""" Return a list of headers for the thingy """
headers = []

View File

@ -95,11 +95,11 @@ class BomExportForm(forms.Form):
parameter_data = forms.BooleanField(label=_("Include Parameter Data"), required=False, initial=False, help_text=_("Include part parameters data in exported BOM"))
stock_data = forms.BooleanField(label=_("Include Stock Data"), required=False, initial=False, help_text=_("Include part stock data in exported BOM"))
manufacturer_data = forms.BooleanField(label=_("Include Manufacturer Data"), required=False, initial=True, help_text=_("Include part manufacturer data in exported BOM"))
supplier_data = forms.BooleanField(label=_("Include Supplier Data"), required=False, initial=True, help_text=_("Include part supplier data in exported BOM"))
def get_choices(self):
""" BOM export format choices """
@ -324,7 +324,7 @@ class EditCategoryParameterTemplateForm(HelperForm):
add_to_all_categories = forms.BooleanField(required=False,
initial=False,
help_text=_('Add parameter template to all categories'))
class Meta:
model = PartCategoryParameterTemplate
fields = [

View File

@ -349,7 +349,7 @@ class Part(MPTTModel):
context['available'] = self.available_stock
context['on_order'] = self.on_order
context['required'] = context['required_build_order_quantity'] + context['required_sales_order_quantity']
context['allocated'] = context['allocated_build_order_quantity'] + context['allocated_sales_order_quantity']
@ -434,7 +434,7 @@ class Part(MPTTModel):
a) The parent part is the same as this one
b) The parent part is used in the BOM for *this* part
c) The parent part is used in the BOM for any child parts under this one
Failing this check raises a ValidationError!
"""
@ -506,7 +506,7 @@ class Part(MPTTModel):
parts = Part.objects.filter(tree_id=self.tree_id)
stock = StockModels.StockItem.objects.filter(part__in=parts).exclude(serial=None)
# There are no matchin StockItem objects (skip further tests)
if not stock.exists():
return None
@ -578,7 +578,7 @@ class Part(MPTTModel):
if self.IPN:
elements.append(self.IPN)
elements.append(self.name)
if self.revision:
@ -663,7 +663,7 @@ class Part(MPTTModel):
def clean(self):
"""
Perform cleaning operations for the Part model
Update trackable status:
If this part is trackable, and it is used in the BOM
for a parent part which is *not* trackable,
@ -946,7 +946,7 @@ class Part(MPTTModel):
quantity = 0
for build in builds:
bom_item = None
# List the bom lines required to make the build (including inherited ones!)
@ -958,7 +958,7 @@ class Part(MPTTModel):
build_quantity = build.quantity * bom_item.quantity
quantity += build_quantity
return quantity
def requiring_sales_orders(self):
@ -1008,7 +1008,7 @@ class Part(MPTTModel):
def quantity_to_order(self):
"""
Return the quantity needing to be ordered for this part.
Here, an "order" could be one of:
- Build Order
- Sales Order
@ -1019,7 +1019,7 @@ class Part(MPTTModel):
Required for orders = self.required_order_quantity()
Currently on order = self.on_order
Currently building = self.quantity_being_built
"""
# Total requirement
@ -1114,7 +1114,7 @@ class Part(MPTTModel):
if total is None:
total = 0
return max(total, 0)
@property
@ -1238,7 +1238,7 @@ class Part(MPTTModel):
@property
def total_stock(self):
""" Return the total stock quantity for this part.
- Part may be stored in multiple locations
- If this part is a "template" (variants exist) then these are counted too
"""
@ -1463,7 +1463,7 @@ class Part(MPTTModel):
# Start with a list of all parts designated as 'sub components'
parts = Part.objects.filter(component=True)
# Exclude this part
parts = parts.exclude(id=self.id)
@ -1496,7 +1496,7 @@ class Part(MPTTModel):
def get_price_info(self, quantity=1, buy=True, bom=True):
""" Return a simplified pricing string for this part
Args:
quantity: Number of units to calculate price for
buy: Include supplier pricing (default = True)
@ -1519,7 +1519,7 @@ class Part(MPTTModel):
return "{a} - {b}".format(a=min_price, b=max_price)
def get_supplier_price_range(self, quantity=1):
min_price = None
max_price = None
@ -1586,7 +1586,7 @@ class Part(MPTTModel):
return (min_price, max_price)
def get_price_range(self, quantity=1, buy=True, bom=True):
""" Return the price range for this part. This price can be either:
- Supplier price (if purchased from suppliers)
@ -1645,7 +1645,7 @@ class Part(MPTTModel):
@transaction.atomic
def copy_parameters_from(self, other, **kwargs):
clear = kwargs.get('clear', True)
if clear:
@ -1692,7 +1692,7 @@ class Part(MPTTModel):
# Copy the parameters data
if kwargs.get('parameters', True):
self.copy_parameters_from(other)
# Copy the fields that aren't available in the duplicate form
self.salable = other.salable
self.assembly = other.assembly
@ -1722,7 +1722,7 @@ class Part(MPTTModel):
tests = tests.filter(required=required)
return tests
def getRequiredTests(self):
# Return the tests which are required by this part
return self.getTestTemplates(required=True)
@ -1868,7 +1868,7 @@ class PartAttachment(InvenTreeAttachment):
"""
Model for storing file attachments against a Part object
"""
def getSubdir(self):
return os.path.join("part_files", str(self.part.id))
@ -2227,7 +2227,7 @@ class BomItem(models.Model):
def validate_hash(self, valid=True):
""" Mark this item as 'valid' (store the checksum hash).
Args:
valid: If true, validate the hash, otherwise invalidate it (default = True)
"""
@ -2265,7 +2265,7 @@ class BomItem(models.Model):
# Check for circular BOM references
if self.sub_part:
self.sub_part.checkAddToBOM(self.part)
# If the sub_part is 'trackable' then the 'quantity' field must be an integer
if self.sub_part.trackable:
if not self.quantity == int(self.quantity):
@ -2301,7 +2301,7 @@ class BomItem(models.Model):
"""
query = self.sub_part.stock_items.all()
query = query.prefetch_related([
'sub_part__stock_items',
])
@ -2358,7 +2358,7 @@ class BomItem(models.Model):
def get_required_quantity(self, build_quantity):
""" Calculate the required part quantity, based on the supplier build_quantity.
Includes overage estimate in the returned value.
Args:
build_quantity: Number of parts to build

View File

@ -134,7 +134,7 @@ class PartBriefSerializer(InvenTreeModelSerializer):
""" Serializer for Part (brief detail) """
thumbnail = serializers.CharField(source='get_thumbnail_url', read_only=True)
stock = serializers.FloatField(source='total_stock')
class Meta:
@ -232,7 +232,7 @@ class PartSerializer(InvenTreeModelSerializer):
output_field=models.DecimalField(),
)
)
# Filter to limit orders to "open"
order_filter = Q(
order__status__in=PurchaseOrderStatus.OPEN
@ -259,7 +259,7 @@ class PartSerializer(InvenTreeModelSerializer):
output_field=models.DecimalField(),
),
)
return queryset
def get_starred(self, part):
@ -358,7 +358,7 @@ class BomItemSerializer(InvenTreeModelSerializer):
quantity = serializers.FloatField()
part = serializers.PrimaryKeyRelatedField(queryset=Part.objects.filter(assembly=True))
part_detail = PartBriefSerializer(source='part', many=False, read_only=True)
sub_part = serializers.PrimaryKeyRelatedField(queryset=Part.objects.filter(component=True))

View File

@ -47,7 +47,7 @@ def str2bool(x, *args, **kwargs):
def inrange(n, *args, **kwargs):
""" Return range(n) for iterating through a numeric quantity """
return range(n)
@register.simple_tag()
def multiply(x, y, *args, **kwargs):
@ -59,7 +59,7 @@ def multiply(x, y, *args, **kwargs):
def add(x, y, *args, **kwargs):
""" Add two numbers together """
return x + y
@register.simple_tag()
def part_allocation_count(build, part, *args, **kwargs):
@ -177,7 +177,7 @@ def authorized_owners(group):
except TypeError:
# group.get_users returns None
pass
return owners

View File

@ -41,12 +41,12 @@ class PartAPITest(InvenTreeAPITestCase):
Test that we can retrieve list of part categories,
with various filtering options.
"""
url = reverse('api-part-category-list')
# Request *all* part categories
response = self.client.get(url, format='json')
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.data), 8)
@ -95,7 +95,7 @@ class PartAPITest(InvenTreeAPITestCase):
url = reverse('api-part-category-list')
response = self.client.post(url, data, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
parent = response.data['pk']
# Add some sub-categories to the top-level 'Animals' category
@ -289,7 +289,7 @@ class PartAPITest(InvenTreeAPITestCase):
self.assertIn('count', data)
self.assertIn('results', data)
self.assertEqual(len(data['results']), n)
@ -354,7 +354,7 @@ class PartAPIAggregationTest(InvenTreeAPITestCase):
self.assertEqual(data['in_stock'], 600)
self.assertEqual(data['stock_item_count'], 4)
# Add some more stock items!!
for i in range(100):
StockItem.objects.create(part=self.part, quantity=5)
@ -463,7 +463,7 @@ class PartParameterTest(InvenTreeAPITestCase):
response = self.client.patch(url, {'data': '15'}, format='json')
self.assertEqual(response.status_code, 200)
# Check that the data changed!
response = self.client.get(url, format='json')

View File

@ -64,7 +64,7 @@ class BomItemTest(TestCase):
""" Test that BOM line overages are calculated correctly """
item = BomItem.objects.get(part=100, sub_part=50)
q = 300
item.quantity = q
@ -77,7 +77,7 @@ class BomItemTest(TestCase):
item.overage = 'asf234?'
n = item.get_overage_quantity(q)
self.assertEqual(n, 0)
# Test absolute overage
item.overage = '3'
n = item.get_overage_quantity(q)
@ -100,7 +100,7 @@ class BomItemTest(TestCase):
""" Test BOM item hash encoding """
item = BomItem.objects.get(part=100, sub_part=50)
h1 = item.get_item_hash()
# Change data - the hash must change

View File

@ -59,7 +59,7 @@ class CategoryTest(TestCase):
def test_unique_parents(self):
""" Test the 'unique_parents' functionality """
parents = [item.pk for item in self.transceivers.getUniqueParents()]
self.assertIn(self.electronics.id, parents)
@ -128,9 +128,9 @@ class CategoryTest(TestCase):
with self.assertRaises(ValidationError) as err:
cat.full_clean()
cat.save()
self.assertIn('Illegal character in name', str(err.exception.error_dict.get('name')))
cat.name = 'good name'
cat.save()

View File

@ -34,7 +34,7 @@ class TestForwardMigrations(MigratorTestCase):
# Initially some fields are not present
with self.assertRaises(AttributeError):
print(p.has_variants)
with self.assertRaises(AttributeError):
print(p.is_template)

View File

@ -32,7 +32,7 @@ class TestParams(TestCase):
self.assertEqual(str(c1), 'Mechanical | Length | 2.8')
def test_validate(self):
n = PartParameterTemplate.objects.all().count()
t1 = PartParameterTemplate(name='abcde', units='dd')

View File

@ -91,7 +91,7 @@ class PartTest(TestCase):
def test_rename_img(self):
img = rename_part_image(self.r1, 'hello.png')
self.assertEqual(img, os.path.join('part_images', 'hello.png'))
def test_stock(self):
# No stock of any resistors
res = Part.objects.filter(description__contains='resistor')
@ -178,7 +178,7 @@ class PartSettingsTest(TestCase):
Some fields for the Part model can have default values specified by the user.
"""
def setUp(self):
# Create a user for auth
user = get_user_model()
@ -251,7 +251,7 @@ class PartSettingsTest(TestCase):
self.assertEqual(part.trackable, val)
self.assertEqual(part.assembly, val)
self.assertEqual(part.is_template, val)
Part.objects.filter(pk=part.pk).delete()
def test_duplicate_ipn(self):

View File

@ -9,7 +9,7 @@ from .models import Part, PartRelated
class PartViewTestCase(TestCase):
fixtures = [
'category',
'part',
@ -24,7 +24,7 @@ class PartViewTestCase(TestCase):
# Create a user
user = get_user_model()
self.user = user.objects.create_user(
username='username',
email='user@email.com',
@ -52,12 +52,12 @@ class PartListTest(PartViewTestCase):
def test_part_index(self):
response = self.client.get(reverse('part-index'))
self.assertEqual(response.status_code, 200)
keys = response.context.keys()
self.assertIn('csrf_token', keys)
self.assertIn('parts', keys)
self.assertIn('user', keys)
def test_export(self):
""" Export part data to CSV """
@ -153,7 +153,7 @@ class PartDetailTest(PartViewTestCase):
response = self.client.get(reverse('bom-download', args=(1,)), HTTP_X_REQUESTED_WITH='XMLHttpRequest')
self.assertEqual(response.status_code, 200)
self.assertIn('streaming_content', dir(response))
class PartTests(PartViewTestCase):
""" Tests for Part forms """
@ -226,7 +226,7 @@ class PartRelatedTests(PartViewTestCase):
response = self.client.post(reverse('part-related-create'), {'part_1': 1, 'part_2': 1},
HTTP_X_REQUESTED_WITH='XMLHttpRequest')
self.assertContains(response, '"form_valid": false', status_code=200)
# Check final count
n = PartRelated.objects.all().count()
self.assertEqual(n, 1)
@ -266,7 +266,7 @@ class PartQRTest(PartViewTestCase):
def test_valid_part(self):
response = self.client.get(reverse('part-qr', args=(1,)), HTTP_X_REQUESTED_WITH='XMLHttpRequest')
self.assertEqual(response.status_code, 200)
data = str(response.content)
self.assertIn('Part QR Code', data)

View File

@ -30,11 +30,11 @@ sale_price_break_urls = [
]
part_parameter_urls = [
url(r'^template/new/', views.PartParameterTemplateCreate.as_view(), name='part-param-template-create'),
url(r'^template/(?P<pk>\d+)/edit/', views.PartParameterTemplateEdit.as_view(), name='part-param-template-edit'),
url(r'^template/(?P<pk>\d+)/delete/', views.PartParameterTemplateDelete.as_view(), name='part-param-template-edit'),
url(r'^new/', views.PartParameterCreate.as_view(), name='part-param-create'),
url(r'^(?P<pk>\d+)/edit/', views.PartParameterEdit.as_view(), name='part-param-edit'),
url(r'^(?P<pk>\d+)/delete/', views.PartParameterDelete.as_view(), name='part-param-delete'),
@ -49,10 +49,10 @@ part_detail_urls = [
url(r'^duplicate/', views.PartDuplicate.as_view(), name='part-duplicate'),
url(r'^make-variant/', views.MakePartVariant.as_view(), name='make-part-variant'),
url(r'^pricing/', views.PartPricing.as_view(), name='part-pricing'),
url(r'^bom-upload/?', views.BomUpload.as_view(), name='upload-bom'),
url(r'^bom-duplicate/?', views.BomDuplicate.as_view(), name='duplicate-bom'),
url(r'^params/', views.PartDetail.as_view(template_name='part/params.html'), name='part-params'),
url(r'^variants/?', views.PartDetail.as_view(template_name='part/variants.html'), name='part-variants'),
url(r'^stock/?', views.PartDetail.as_view(template_name='part/stock.html'), name='part-stock'),
@ -70,7 +70,7 @@ part_detail_urls = [
url(r'^related-parts/?', views.PartDetail.as_view(template_name='part/related.html'), name='part-related'),
url(r'^attachments/?', views.PartDetail.as_view(template_name='part/attachments.html'), name='part-attachments'),
url(r'^notes/?', views.PartNotes.as_view(), name='part-notes'),
url(r'^qr_code/?', views.PartQRCode.as_view(), name='part-qr'),
# Normal thumbnail with form
@ -104,7 +104,7 @@ category_urls = [
url(r'^subcategory/', views.CategoryDetail.as_view(template_name='part/subcategory.html'), name='category-subcategory'),
url(r'^parametric/', views.CategoryParametric.as_view(), name='category-parametric'),
# Anything else
url(r'^.*$', views.CategoryDetail.as_view(), name='category-detail'),
]))

View File

@ -204,12 +204,12 @@ class PartAttachmentCreate(AjaxCreateView):
class PartAttachmentEdit(AjaxUpdateView):
""" View for editing a PartAttachment object """
model = PartAttachment
form_class = part_forms.EditPartAttachmentForm
ajax_template_name = 'modal_form.html'
ajax_form_title = _('Edit attachment')
def get_data(self):
return {
'success': _('Part attachment updated')
@ -245,7 +245,7 @@ class PartTestTemplateCreate(AjaxCreateView):
model = PartTestTemplate
form_class = part_forms.EditPartTestTemplateForm
ajax_form_title = _("Create Test Template")
def get_initial(self):
initials = super().get_initial()
@ -299,7 +299,7 @@ class PartSetCategory(AjaxUpdateView):
category = None
parts = []
def get(self, request, *args, **kwargs):
""" Respond to a GET request to this view """
@ -364,7 +364,7 @@ class PartSetCategory(AjaxUpdateView):
ctx['category'] = self.category
return ctx
class MakePartVariant(AjaxCreateView):
""" View for creating a new variant based on an existing template Part
@ -501,17 +501,17 @@ class PartDuplicate(AjaxCreateView):
valid = form.is_valid()
name = request.POST.get('name', None)
if name:
matches = match_part_names(name)
if len(matches) > 0:
# Display the first five closest matches
context['matches'] = matches[:5]
# Enforce display of the checkbox
form.fields['confirm_creation'].widget = CheckboxInput()
# Check if the user has checked the 'confirm_creation' input
confirmed = str2bool(request.POST.get('confirm_creation', False))
@ -565,7 +565,7 @@ class PartDuplicate(AjaxCreateView):
initials = super(AjaxCreateView, self).get_initial()
initials['bom_copy'] = str2bool(InvenTreeSetting.get_setting('PART_COPY_BOM', True))
initials['parameters_copy'] = str2bool(InvenTreeSetting.get_setting('PART_COPY_PARAMETERS', True))
return initials
@ -575,7 +575,7 @@ class PartCreate(AjaxCreateView):
""" View for creating a new Part object.
Options for providing initial conditions:
- Provide a category object as initial data
"""
model = Part
@ -636,9 +636,9 @@ class PartCreate(AjaxCreateView):
context = {}
valid = form.is_valid()
name = request.POST.get('name', None)
if name:
matches = match_part_names(name)
@ -646,17 +646,17 @@ class PartCreate(AjaxCreateView):
# Limit to the top 5 matches (to prevent clutter)
context['matches'] = matches[:5]
# Enforce display of the checkbox
form.fields['confirm_creation'].widget = CheckboxInput()
# Check if the user has checked the 'confirm_creation' input
confirmed = str2bool(request.POST.get('confirm_creation', False))
if not confirmed:
msg = _('Possible matches exist - confirm creation of new part')
form.add_error('confirm_creation', msg)
form.pre_form_warning = msg
valid = False
@ -705,7 +705,7 @@ class PartCreate(AjaxCreateView):
initials['keywords'] = category.default_keywords
except (PartCategory.DoesNotExist, ValueError):
pass
# Allow initial data to be passed through as arguments
for label in ['name', 'IPN', 'description', 'revision', 'keywords']:
if label in self.request.GET:
@ -734,7 +734,7 @@ class PartNotes(UpdateView):
def get_success_url(self):
""" Return the success URL for this form """
return reverse('part-notes', kwargs={'pk': self.get_object().id})
def get_context_data(self, **kwargs):
@ -767,7 +767,7 @@ class PartDetail(InvenTreeRoleMixin, DetailView):
- If '?editing=True', set 'editing_enabled' context variable
"""
context = super().get_context_data(**kwargs)
part = self.get_object()
if str2bool(self.request.GET.get('edit', '')):
@ -806,7 +806,7 @@ class PartDetailFromIPN(PartDetail):
pass
except queryset.model.DoesNotExist:
pass
return None
def get(self, request, *args, **kwargs):
@ -1017,7 +1017,7 @@ class BomDuplicate(AjaxUpdateView):
ajax_form_title = _('Duplicate BOM')
ajax_template_name = 'part/bom_duplicate.html'
form_class = part_forms.BomDuplicateForm
def get_form(self):
form = super().get_form()
@ -1218,7 +1218,7 @@ class BomUpload(InvenTreeRoleMixin, FormView):
def handleBomFileUpload(self):
""" Process a BOM file upload form.
This function validates that the uploaded file was valid,
and contains tabulated data that can be extracted.
If the file does not satisfy these requirements,
@ -1299,13 +1299,13 @@ class BomUpload(InvenTreeRoleMixin, FormView):
- If using the Part_ID field, we can do an exact match against the PK field
- If using the Part_IPN field, we can do an exact match against the IPN field
- If using the Part_Name field, we can use fuzzy string matching to match "close" values
We also extract other information from the row, for the other non-matched fields:
- Quantity
- Reference
- Overage
- Note
"""
# Initially use a quantity of zero
@ -1375,7 +1375,7 @@ class BomUpload(InvenTreeRoleMixin, FormView):
# Check if there is a column corresponding to "Note" field
if n_idx >= 0:
row['note'] = row['data'][n_idx]
# Supply list of part options for each row, sorted by how closely they match the part name
row['part_options'] = part_options
@ -1390,7 +1390,7 @@ class BomUpload(InvenTreeRoleMixin, FormView):
try:
if row['part_ipn']:
part_matches = [part for part in self.allowed_parts if part.IPN and row['part_ipn'].lower() == str(part.IPN.lower())]
# Check for single match
if len(part_matches) == 1:
row['part_match'] = part_matches[0]
@ -1464,7 +1464,7 @@ class BomUpload(InvenTreeRoleMixin, FormView):
col_id = int(s[3])
except ValueError:
continue
if row_id not in self.row_data:
self.row_data[row_id] = {}
@ -1530,7 +1530,7 @@ class BomUpload(InvenTreeRoleMixin, FormView):
if col in self.column_selections.values():
part_match_found = True
break
# If not, notify user
if not part_match_found:
for col in BomUploadManager.PART_MATCH_HEADERS:
@ -1546,7 +1546,7 @@ class BomUpload(InvenTreeRoleMixin, FormView):
self.getTableDataFromPost()
valid = len(self.missing_columns) == 0 and not self.duplicates
if valid:
# Try to extract meaningful data
self.preFillSelections()
@ -1557,7 +1557,7 @@ class BomUpload(InvenTreeRoleMixin, FormView):
return self.render_to_response(self.get_context_data(form=None))
def handlePartSelection(self):
# Extract basic table data from POST request
self.getTableDataFromPost()
@ -1595,7 +1595,7 @@ class BomUpload(InvenTreeRoleMixin, FormView):
row['errors']['quantity'] = _('Enter a valid quantity')
row['quantity'] = q
except ValueError:
continue
@ -1648,7 +1648,7 @@ class BomUpload(InvenTreeRoleMixin, FormView):
if key.startswith(field + '_'):
try:
row_id = int(key.replace(field + '_', ''))
row = self.getRowByIndex(row_id)
if row:
@ -1714,7 +1714,7 @@ class BomUpload(InvenTreeRoleMixin, FormView):
return self.render_to_response(ctx)
def getRowByIndex(self, idx):
for row in self.bom_rows:
if row['index'] == idx:
return row
@ -1732,7 +1732,7 @@ class BomUpload(InvenTreeRoleMixin, FormView):
self.form = self.get_form(self.get_form_class())
# Did the user POST a file named bom_file?
form_step = request.POST.get('form_step', None)
if form_step == 'select_file':
@ -1753,7 +1753,7 @@ class PartExport(AjaxView):
def get_parts(self, request):
""" Extract part list from the POST parameters.
Parts can be supplied as:
- Part category
- List of part PK values
"""
@ -1956,10 +1956,10 @@ class PartPricing(AjaxView):
form_class = part_forms.PartPriceForm
role_required = ['sales_order.view', 'part.view']
def get_quantity(self):
""" Return set quantity in decimal format """
return Decimal(self.request.POST.get('quantity', 1))
def get_part(self):
@ -1985,7 +1985,7 @@ class PartPricing(AjaxView):
scaler = Decimal(1.0)
part = self.get_part()
ctx = {
'part': part,
'quantity': quantity,
@ -2039,7 +2039,7 @@ class PartPricing(AjaxView):
if min_bom_price:
ctx['min_total_bom_price'] = min_bom_price
ctx['min_unit_bom_price'] = min_unit_bom_price
if max_bom_price:
ctx['max_total_bom_price'] = max_bom_price
ctx['max_unit_bom_price'] = max_unit_bom_price
@ -2168,7 +2168,7 @@ class PartParameterDelete(AjaxDeleteView):
model = PartParameter
ajax_template_name = 'part/param_delete.html'
ajax_form_title = _('Delete Part Parameter')
class CategoryDetail(InvenTreeRoleMixin, DetailView):
""" Detail view for PartCategory """
@ -2223,7 +2223,7 @@ class CategoryEdit(AjaxUpdateView):
"""
Update view to edit a PartCategory
"""
model = PartCategory
form_class = part_forms.EditCategoryForm
ajax_template_name = 'modal_form.html'
@ -2244,9 +2244,9 @@ class CategoryEdit(AjaxUpdateView):
Limit the choices for 'parent' field to those which make sense
"""
form = super(AjaxUpdateView, self).get_form()
category = self.get_object()
# Remove any invalid choices for the parent category part
@ -2262,7 +2262,7 @@ class CategoryDelete(AjaxDeleteView):
"""
Delete view to delete a PartCategory
"""
model = PartCategory
ajax_template_name = 'part/category_delete.html'
ajax_form_title = _('Delete Part Category')
@ -2346,7 +2346,7 @@ class CategoryParameterTemplateCreate(AjaxCreateView):
"""
form = super(AjaxCreateView, self).get_form()
form.fields['category'].widget = HiddenInput()
if form.is_valid():
@ -2441,7 +2441,7 @@ class CategoryParameterTemplateEdit(AjaxUpdateView):
"""
form = super(AjaxUpdateView, self).get_form()
form.fields['category'].widget = HiddenInput()
form.fields['add_to_all_categories'].widget = HiddenInput()
form.fields['add_to_same_level_categories'].widget = HiddenInput()
@ -2495,7 +2495,7 @@ class BomItemCreate(AjaxCreateView):
"""
Create view for making a new BomItem object
"""
model = BomItem
form_class = part_forms.EditBomItemForm
ajax_template_name = 'modal_form.html'
@ -2523,13 +2523,13 @@ class BomItemCreate(AjaxCreateView):
try:
part = Part.objects.get(id=part_id)
# Hide the 'part' field
form.fields['part'].widget = HiddenInput()
# Exclude the part from its own BOM
sub_part_query = sub_part_query.exclude(id=part.id)
# Eliminate any options that are already in the BOM!
sub_part_query = sub_part_query.exclude(id__in=[item.id for item in part.getRequiredParts()])
@ -2634,7 +2634,7 @@ class PartSalePriceBreakCreate(AjaxCreateView):
model = PartSellPriceBreak
form_class = part_forms.EditPartSalePriceBreakForm
ajax_form_title = _('Add Price Break')
def get_data(self):
return {
'success': _('Added new price break')
@ -2645,7 +2645,7 @@ class PartSalePriceBreakCreate(AjaxCreateView):
part = Part.objects.get(id=self.request.GET.get('part'))
except (ValueError, Part.DoesNotExist):
part = None
if part is None:
try:
part = Part.objects.get(id=self.request.POST.get('part'))
@ -2690,7 +2690,7 @@ class PartSalePriceBreakEdit(AjaxUpdateView):
return form
class PartSalePriceBreakDelete(AjaxDeleteView):
""" View for deleting a sale price break """