mirror of
				https://github.com/inventree/InvenTree.git
				synced 2025-10-31 13:15:43 +00:00 
			
		
		
		
	Add "label" app
This commit is contained in:
		| @@ -383,3 +383,56 @@ def ExtractSerialNumbers(serials, expected_quantity): | |||||||
|         raise ValidationError([_("Number of unique serial number ({s}) must match quantity ({q})".format(s=len(numbers), q=expected_quantity))]) |         raise ValidationError([_("Number of unique serial number ({s}) must match quantity ({q})".format(s=len(numbers), q=expected_quantity))]) | ||||||
|  |  | ||||||
|     return numbers |     return numbers | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def validateFilterString(value): | ||||||
|  |     """ | ||||||
|  |     Validate that a provided filter string looks like a list of comma-separated key=value pairs | ||||||
|  |  | ||||||
|  |     These should nominally match to a valid database filter based on the model being filtered. | ||||||
|  |  | ||||||
|  |     e.g. "category=6, IPN=12" | ||||||
|  |     e.g. "part__name=widget" | ||||||
|  |  | ||||||
|  |     The ReportTemplate class uses the filter string to work out which items a given report applies to. | ||||||
|  |     For example, an acceptance test report template might only apply to stock items with a given IPN, | ||||||
|  |     so the string could be set to: | ||||||
|  |  | ||||||
|  |     filters = "IPN = ACME0001" | ||||||
|  |  | ||||||
|  |     Returns a map of key:value pairs | ||||||
|  |     """ | ||||||
|  |  | ||||||
|  |     # Empty results map | ||||||
|  |     results = {} | ||||||
|  |  | ||||||
|  |     value = str(value).strip() | ||||||
|  |  | ||||||
|  |     if not value or len(value) == 0: | ||||||
|  |         return results | ||||||
|  |  | ||||||
|  |     groups = value.split(',') | ||||||
|  |  | ||||||
|  |     for group in groups: | ||||||
|  |         group = group.strip() | ||||||
|  |  | ||||||
|  |         pair = group.split('=') | ||||||
|  |  | ||||||
|  |         if not len(pair) == 2: | ||||||
|  |             raise ValidationError( | ||||||
|  |                 "Invalid group: {g}".format(g=group) | ||||||
|  |             ) | ||||||
|  |  | ||||||
|  |         k, v = pair | ||||||
|  |  | ||||||
|  |         k = k.strip() | ||||||
|  |         v = v.strip() | ||||||
|  |  | ||||||
|  |         if not k or not v: | ||||||
|  |             raise ValidationError( | ||||||
|  |                 "Invalid group: {g}".format(g=group) | ||||||
|  |             ) | ||||||
|  |  | ||||||
|  |         results[k] = v | ||||||
|  |  | ||||||
|  |     return results | ||||||
| @@ -130,6 +130,7 @@ INSTALLED_APPS = [ | |||||||
|     'build.apps.BuildConfig', |     'build.apps.BuildConfig', | ||||||
|     'common.apps.CommonConfig', |     'common.apps.CommonConfig', | ||||||
|     'company.apps.CompanyConfig', |     'company.apps.CompanyConfig', | ||||||
|  |     'label.apps.LabelConfig', | ||||||
|     'order.apps.OrderConfig', |     'order.apps.OrderConfig', | ||||||
|     'part.apps.PartConfig', |     'part.apps.PartConfig', | ||||||
|     'report.apps.ReportConfig', |     'report.apps.ReportConfig', | ||||||
|   | |||||||
							
								
								
									
										0
									
								
								InvenTree/label/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								InvenTree/label/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										3
									
								
								InvenTree/label/admin.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								InvenTree/label/admin.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | |||||||
|  | from django.contrib import admin | ||||||
|  |  | ||||||
|  | # Register your models here. | ||||||
							
								
								
									
										5
									
								
								InvenTree/label/apps.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								InvenTree/label/apps.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | |||||||
|  | from django.apps import AppConfig | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class LabelConfig(AppConfig): | ||||||
|  |     name = 'label' | ||||||
							
								
								
									
										30
									
								
								InvenTree/label/migrations/0001_initial.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								InvenTree/label/migrations/0001_initial.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,30 @@ | |||||||
|  | # Generated by Django 3.0.7 on 2020-08-15 23:27 | ||||||
|  |  | ||||||
|  | import InvenTree.helpers | ||||||
|  | import django.core.validators | ||||||
|  | from django.db import migrations, models | ||||||
|  | import label.models | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class Migration(migrations.Migration): | ||||||
|  |  | ||||||
|  |     initial = True | ||||||
|  |  | ||||||
|  |     dependencies = [ | ||||||
|  |     ] | ||||||
|  |  | ||||||
|  |     operations = [ | ||||||
|  |         migrations.CreateModel( | ||||||
|  |             name='StockItemLabel', | ||||||
|  |             fields=[ | ||||||
|  |                 ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), | ||||||
|  |                 ('name', models.CharField(help_text='Label name', max_length=100, unique=True)), | ||||||
|  |                 ('description', models.CharField(blank=True, help_text='Label description', max_length=250, null=True)), | ||||||
|  |                 ('label', models.FileField(help_text='Label template file', upload_to=label.models.rename_label, validators=[django.core.validators.FileExtensionValidator(allowed_extensions=['html'])])), | ||||||
|  |                 ('filters', models.CharField(blank=True, help_text='Query filters (comma-separated list of key=value pairs', max_length=250, validators=[InvenTree.helpers.validateFilterString])), | ||||||
|  |             ], | ||||||
|  |             options={ | ||||||
|  |                 'abstract': False, | ||||||
|  |             }, | ||||||
|  |         ), | ||||||
|  |     ] | ||||||
							
								
								
									
										0
									
								
								InvenTree/label/migrations/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								InvenTree/label/migrations/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										93
									
								
								InvenTree/label/models.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								InvenTree/label/models.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,93 @@ | |||||||
|  | """ | ||||||
|  | Label printing models | ||||||
|  | """ | ||||||
|  |  | ||||||
|  | # -*- coding: utf-8 -*- | ||||||
|  | from __future__ import unicode_literals | ||||||
|  |  | ||||||
|  | import os | ||||||
|  |  | ||||||
|  | from blabel import LabelWriter | ||||||
|  |  | ||||||
|  | from django.db import models | ||||||
|  | from django.core.validators import FileExtensionValidator | ||||||
|  |  | ||||||
|  | from django.utils.translation import gettext_lazy as _ | ||||||
|  |  | ||||||
|  | from InvenTree.helpers import validateFilterString | ||||||
|  |  | ||||||
|  | from stock.models import StockItem | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def rename_label(instance, filename): | ||||||
|  |     """ Place the label file into the correct subdirectory """ | ||||||
|  |  | ||||||
|  |     filename = os.path.basename(filename) | ||||||
|  |  | ||||||
|  |     return os.path.join('label', 'template', instance.SUBDIR, filename) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class LabelTemplate(models.Model): | ||||||
|  |     """ | ||||||
|  |     Base class for generic, filterable labels. | ||||||
|  |     """ | ||||||
|  |  | ||||||
|  |     class Meta: | ||||||
|  |         abstract = True | ||||||
|  |  | ||||||
|  |     # Each class of label files will be stored in a separate subdirectory | ||||||
|  |     SUBDIR = "label" | ||||||
|  |  | ||||||
|  |     name = models.CharField( | ||||||
|  |         unique=True, | ||||||
|  |         blank=False, max_length=100, | ||||||
|  |         help_text=_('Label name'), | ||||||
|  |     ) | ||||||
|  |  | ||||||
|  |     description = models.CharField(max_length=250, help_text=_('Label description'), blank=True, null=True) | ||||||
|  |  | ||||||
|  |     label = models.FileField( | ||||||
|  |         upload_to=rename_label, | ||||||
|  |         blank=False, null=False, | ||||||
|  |         help_text=_('Label template file'), | ||||||
|  |         validators=[FileExtensionValidator(allowed_extensions=['html'])], | ||||||
|  |     ) | ||||||
|  |  | ||||||
|  |     filters = models.CharField( | ||||||
|  |         blank=True, max_length=250, | ||||||
|  |         help_text=_('Query filters (comma-separated list of key=value pairs'), | ||||||
|  |         validators=[validateFilterString] | ||||||
|  |     ) | ||||||
|  |  | ||||||
|  |     def get_record_data(self, items): | ||||||
|  |  | ||||||
|  |         return [] | ||||||
|  |  | ||||||
|  |     def render(self, items, **kwargs): | ||||||
|  |  | ||||||
|  |         records = self.get_record_data(items) | ||||||
|  |  | ||||||
|  |         writer = LabelWriter(self.label.filename) | ||||||
|  |  | ||||||
|  |         writer.write_label(records, 'out.pdf') | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class StockItemLabel(LabelTemplate): | ||||||
|  |     """ | ||||||
|  |     Template for printing StockItem labels | ||||||
|  |     """ | ||||||
|  |  | ||||||
|  |     SUBDIR = "stockitem" | ||||||
|  |  | ||||||
|  |     def matches_stock_item(self, item): | ||||||
|  |         """ | ||||||
|  |         Test if this label template matches a given StockItem object | ||||||
|  |         """ | ||||||
|  |  | ||||||
|  |         filters = validateFilterString(self.filters) | ||||||
|  |  | ||||||
|  |         items = StockItem.objects.filter(**filters) | ||||||
|  |  | ||||||
|  |         items = items.filter(pk=item.pk) | ||||||
|  |  | ||||||
|  |         return items.exists() | ||||||
							
								
								
									
										3
									
								
								InvenTree/label/tests.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								InvenTree/label/tests.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | |||||||
|  | from django.test import TestCase | ||||||
|  |  | ||||||
|  | # Create your tests here. | ||||||
							
								
								
									
										3
									
								
								InvenTree/label/views.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								InvenTree/label/views.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | |||||||
|  | from django.shortcuts import render | ||||||
|  |  | ||||||
|  | # Create your views here. | ||||||
							
								
								
									
										4
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								Makefile
									
									
									
									
									
								
							| @@ -51,12 +51,12 @@ style: | |||||||
| # Run unit tests | # Run unit tests | ||||||
| test: | test: | ||||||
| 	cd InvenTree && python3 manage.py check | 	cd InvenTree && python3 manage.py check | ||||||
| 	cd InvenTree && python3 manage.py test barcode build common company order part report stock InvenTree | 	cd InvenTree && python3 manage.py test barcode build common company label order part report stock InvenTree | ||||||
|  |  | ||||||
| # Run code coverage | # Run code coverage | ||||||
| coverage: | coverage: | ||||||
| 	cd InvenTree && python3 manage.py check | 	cd InvenTree && python3 manage.py check | ||||||
| 	coverage run InvenTree/manage.py test barcode build common company order part report stock InvenTree | 	coverage run InvenTree/manage.py test barcode build common company label order part report stock InvenTree | ||||||
| 	coverage html | 	coverage html | ||||||
|  |  | ||||||
| # Install packages required to generate code docs | # Install packages required to generate code docs | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user