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))]) | ||||
|  | ||||
|     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', | ||||
|     'common.apps.CommonConfig', | ||||
|     'company.apps.CompanyConfig', | ||||
|     'label.apps.LabelConfig', | ||||
|     'order.apps.OrderConfig', | ||||
|     'part.apps.PartConfig', | ||||
|     '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 | ||||
| test: | ||||
| 	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 | ||||
| coverage: | ||||
| 	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 | ||||
|  | ||||
| # Install packages required to generate code docs | ||||
|   | ||||
		Reference in New Issue
	
	Block a user