diff --git a/InvenTree/InvenTree/settings.py b/InvenTree/InvenTree/settings.py index 2f5572a5f8..f73a5034ca 100644 --- a/InvenTree/InvenTree/settings.py +++ b/InvenTree/InvenTree/settings.py @@ -227,6 +227,10 @@ USE_L10N = True USE_TZ = True +DATE_INPUT_FORMATS = [ + "%Y-%m-%d", +] + # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/1.10/howto/static-files/ diff --git a/InvenTree/common/admin.py b/InvenTree/common/admin.py index 1628f811a7..e0db0a7136 100644 --- a/InvenTree/common/admin.py +++ b/InvenTree/common/admin.py @@ -1,9 +1,14 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + from django.contrib import admin +from import_export.admin import ImportExportModelAdmin + from .models import Currency -class CurrencyAdmin(admin.ModelAdmin): +class CurrencyAdmin(ImportExportModelAdmin): list_display = ('symbol', 'suffix', 'description', 'value', 'base') diff --git a/InvenTree/company/admin.py b/InvenTree/company/admin.py index 607f773198..ada60c7975 100644 --- a/InvenTree/company/admin.py +++ b/InvenTree/company/admin.py @@ -1,20 +1,74 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + from django.contrib import admin + from import_export.admin import ImportExportModelAdmin +from import_export.resources import ModelResource +from import_export.fields import Field +import import_export.widgets as widgets from .models import Company from .models import SupplierPart from .models import SupplierPriceBreak +from part.models import Part +from common.models import Currency + + +class CompanyResource(ModelResource): + """ Class for managing Company data import/export """ + + class Meta: + model = Company + skip_unchanged = True + report_skipped = False + class CompanyAdmin(ImportExportModelAdmin): + + resource_class = CompanyResource + list_display = ('name', 'website', 'contact') +class SupplierPartResource(ModelResource): + """ Class for managing SupplierPart data import/export """ + + part = Field(attribute='part', widget=widgets.ForeignKeyWidget(Part)) + + supplier = Field(attribute='supplier', widget=widgets.ForeignKeyWidget(Company)) + + class Meta: + model = SupplierPart + skip_unchanged = True + report_skipped = False + + class SupplierPartAdmin(ImportExportModelAdmin): + + resource_class = SupplierPartResource + list_display = ('part', 'supplier', 'SKU') +class SupplierPriceBreakResource(ModelResource): + """ Class for managing SupplierPriceBreak data import/export """ + + part = Field(attribute='part', widget=widgets.ForeignKeyWidget(SupplierPart)) + + currency = Field(attribute='currency', widget=widgets.ForeignKeyWidget(Currency)) + + class Meta: + model = SupplierPriceBreak + skip_unchanged = True + report_skipped = False + + class SupplierPriceBreakAdmin(ImportExportModelAdmin): + + resource_class = SupplierPriceBreakResource + list_display = ('part', 'quantity', 'cost') diff --git a/InvenTree/order/admin.py b/InvenTree/order/admin.py index bbd95cd42a..44d26ea3f2 100644 --- a/InvenTree/order/admin.py +++ b/InvenTree/order/admin.py @@ -2,11 +2,12 @@ from __future__ import unicode_literals from django.contrib import admin +from import_export.admin import ImportExportModelAdmin from .models import PurchaseOrder, PurchaseOrderLineItem -class PurchaseOrderAdmin(admin.ModelAdmin): +class PurchaseOrderAdmin(ImportExportModelAdmin): list_display = ( 'reference', @@ -17,7 +18,7 @@ class PurchaseOrderAdmin(admin.ModelAdmin): ) -class PurchaseOrderLineItemAdmin(admin.ModelAdmin): +class PurchaseOrderLineItemAdmin(ImportExportModelAdmin): list_display = ( 'order', diff --git a/InvenTree/part/admin.py b/InvenTree/part/admin.py index e7489c9810..7a9def473f 100644 --- a/InvenTree/part/admin.py +++ b/InvenTree/part/admin.py @@ -1,21 +1,116 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + from django.contrib import admin + from import_export.admin import ImportExportModelAdmin +from import_export.resources import ModelResource +from import_export.fields import Field +import import_export.widgets as widgets from .models import PartCategory, Part from .models import PartAttachment, PartStar from .models import BomItem from .models import PartParameterTemplate, PartParameter +from stock.models import StockLocation +from company.models import SupplierPart + + +class PartResource(ModelResource): + """ Class for managing Part data import/export """ + + # 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)) + + # Extra calculated meta-data (readonly) + in_stock = Field(attribute='total_stock', readonly=True, widget=widgets.IntegerWidget()) + + on_order = Field(attribute='on_order', readonly=True, widget=widgets.IntegerWidget()) + + used_in = Field(attribute='used_in_count', readonly=True, widget=widgets.IntegerWidget()) + + allocated = Field(attribute='allocation_count', readonly=True, widget=widgets.IntegerWidget()) + + building = Field(attribute='quantity_being_built', readonly=True, widget=widgets.IntegerWidget()) + + class Meta: + model = Part + skip_unchanged = True + report_skipped = False + exclude = [ + 'bom_checksum', 'bom_checked_by', 'bom_checked_date' + ] + + def get_queryset(self): + """ Prefetch related data for quicker access """ + + query = super().get_queryset() + query = query.prefetch_related( + 'category', + 'used_in', + 'builds', + 'supplier_parts__purchase_order_line_items', + 'stock_items__allocations' + ) + + return query + class PartAdmin(ImportExportModelAdmin): + resource_class = PartResource + list_display = ('full_name', 'description', 'total_stock', 'category') + list_filter = ('active', 'assembly', 'is_template', 'virtual') + + search_fields = ('name', 'description', 'category__name', 'category__description', 'IPN') + + +class PartCategoryResource(ModelResource): + """ Class for managing PartCategory data import/export """ + + parent = Field(attribute='parent', widget=widgets.ForeignKeyWidget(PartCategory)) + + parent_name = Field(attribute='parent__name', readonly=True) + + default_location = Field(attribute='default_location', widget=widgets.ForeignKeyWidget(StockLocation)) + + class Meta: + model = PartCategory + skip_unchanged = True + report_skipped = False + + exclude = [ + # Exclude MPTT internal model fields + 'lft', 'rght', 'tree_id', 'level', + ] + + def after_import(self, dataset, result, using_transactions, dry_run, **kwargs): + + super().after_import(dataset, result, using_transactions, dry_run, **kwargs) + + # Rebuild the PartCategory tree(s) + PartCategory.objects.rebuild() + class PartCategoryAdmin(ImportExportModelAdmin): + resource_class = PartCategoryResource + list_display = ('name', 'pathstring', 'description') + search_fields = ('name', 'description') + class PartAttachmentAdmin(admin.ModelAdmin): @@ -27,15 +122,53 @@ class PartStarAdmin(admin.ModelAdmin): list_display = ('part', 'user') +class BomItemResource(ModelResource): + """ Class for managing BomItem data import/export """ + + part = Field(attribute='part', widget=widgets.ForeignKeyWidget(Part)) + + sub_part = Field(attribute='part', widget=widgets.ForeignKeyWidget(Part)) + + class Meta: + model = BomItem + skip_unchanged = True + report_skipped = False + + class BomItemAdmin(ImportExportModelAdmin): + + resource_class = BomItemResource + list_display = ('part', 'sub_part', 'quantity') + search_fields = ('part__name', 'part__description', 'sub_part__name', 'sub_part__description') + class ParameterTemplateAdmin(ImportExportModelAdmin): list_display = ('name', 'units') +class ParameterResource(ModelResource): + """ Class for managing PartParameter data import/export """ + + part = Field(attribute='part', widget=widgets.ForeignKeyWidget(Part)) + + part_name = Field(attribute='part__name', readonly=True) + + template = Field(attribute='template', widget=widgets.ForeignKeyWidget(PartParameterTemplate)) + + template_name = Field(attribute='template__name', readonly=True) + + class Meta: + model = PartParameter + skip_unchanged = True + report_skipped = False + + class ParameterAdmin(ImportExportModelAdmin): + + resource_class = ParameterResource + list_display = ('part', 'template', 'data') diff --git a/InvenTree/stock/admin.py b/InvenTree/stock/admin.py index 8f4e18461e..ef84b0890e 100644 --- a/InvenTree/stock/admin.py +++ b/InvenTree/stock/admin.py @@ -1,15 +1,93 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + from django.contrib import admin + from import_export.admin import ImportExportModelAdmin +from import_export.resources import ModelResource +from import_export.fields import Field +import import_export.widgets as widgets from .models import StockLocation, StockItem from .models import StockItemTracking +from build.models import Build +from company.models import Company, SupplierPart +from order.models import PurchaseOrder +from part.models import Part + + +class LocationResource(ModelResource): + """ Class for managing StockLocation data import/export """ + + parent = Field(attribute='parent', widget=widgets.ForeignKeyWidget(StockLocation)) + + parent_name = Field(attribute='parent__name', readonly=True) + + class Meta: + model = StockLocation + skip_unchanged = True + report_skipped = False + + exclude = [ + # Exclude MPTT internal model fields + 'lft', 'rght', 'tree_id', 'level', + ] + + def after_import(self, dataset, result, using_transactions, dry_run, **kwargs): + + super().after_import(dataset, result, using_transactions, dry_run, **kwargs) + + # Rebuild the StockLocation tree(s) + StockLocation.objects.rebuild() + class LocationAdmin(ImportExportModelAdmin): + + resource_class = LocationResource + list_display = ('name', 'pathstring', 'description') + search_fields = ('name', 'description') + + +class StockItemResource(ModelResource): + """ Class for managing StockItem data import/export """ + + # Custom manaegrs for ForeignKey fields + part = Field(attribute='part', widget=widgets.ForeignKeyWidget(Part)) + + part_name = Field(attribute='part__name', readonly=True) + + supplier_part = Field(attribute='supplier_part', widget=widgets.ForeignKeyWidget(SupplierPart)) + + location = Field(attribute='location', widget=widgets.ForeignKeyWidget(StockLocation)) + + location_name = Field(attribute='location__name', readonly=True) + + belongs_to = Field(attribute='belongs_to', widget=widgets.ForeignKeyWidget(StockItem)) + + customer = Field(attribute='customer', widget=widgets.ForeignKeyWidget(Company)) + + build = Field(attribute='build', widget=widgets.ForeignKeyWidget(Build)) + + purchase_order = Field(attribute='purchase_order', widget=widgets.ForeignKeyWidget(PurchaseOrder)) + + # Date management + updated = Field(attribute='updated', widget=widgets.DateWidget()) + + stocktake_date = Field(attribute='stocktake_date', widget=widgets.DateWidget()) + + class Meta: + model = StockItem + skip_unchanged = True + report_skipped = False + class StockItemAdmin(ImportExportModelAdmin): + + resource_class = StockItemResource + list_display = ('part', 'quantity', 'location', 'status', 'updated')