From f865573e487029cf8e5658c9b2c855e2e8b77f1b Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Tue, 11 Feb 2020 22:32:36 +1100 Subject: [PATCH] Implement cascading export of BOM --- InvenTree/part/admin.py | 10 ++++++---- InvenTree/part/bom.py | 33 ++++++++++++++++++++++++++++++--- InvenTree/part/views.py | 6 ++++-- 3 files changed, 40 insertions(+), 9 deletions(-) diff --git a/InvenTree/part/admin.py b/InvenTree/part/admin.py index 20ad569a12..6f9d2369e5 100644 --- a/InvenTree/part/admin.py +++ b/InvenTree/part/admin.py @@ -129,15 +129,17 @@ class PartStarAdmin(admin.ModelAdmin): class BomItemResource(ModelResource): """ Class for managing BomItem data import/export """ + level = Field(attribute='level', readonly=True) + part = Field(attribute='part', widget=widgets.ForeignKeyWidget(Part)) - part_name = Field(attribute='part__full_name', readonly=True) + parent_part_name = Field(attribute='part__full_name', readonly=True) - sub_part = Field(attribute='sub_part', widget=widgets.ForeignKeyWidget(Part)) + id = Field(attribute='sub_part', widget=widgets.ForeignKeyWidget(Part)) sub_part_name = Field(attribute='sub_part__full_name', readonly=True) - stock = Field(attribute='sub_part__total_stock', readonly=True) + sub_assembly = Field(attribute='sub_part__assembly', readonly=True) class Meta: model = BomItem @@ -145,7 +147,7 @@ class BomItemResource(ModelResource): report_skipped = False clean_model_instances = True - exclude = ('checksum') + exclude = ['checksum', ] class BomItemAdmin(ImportExportModelAdmin): diff --git a/InvenTree/part/bom.py b/InvenTree/part/bom.py index 394314d461..a1f8b5ec6a 100644 --- a/InvenTree/part/bom.py +++ b/InvenTree/part/bom.py @@ -40,16 +40,43 @@ def MakeBomTemplate(fmt): return DownloadFile(data, filename) -def ExportBom(part, fmt='csv'): +def ExportBom(part, fmt='csv', cascade=False): """ Export a BOM (Bill of Materials) for a given part. + + Args: + fmt: File format (default = 'csv') + cascade: If True, multi-level BOM output is supported. Otherwise, a flat top-level-only BOM is exported. """ if not IsValidBOMFormat(fmt): fmt = 'csv' - bom_items = part.bom_items.all().order_by('id') + bom_items = [] - dataset = BomItemResource().export(queryset=bom_items) + def add_items(items, level): + # Add items at a given layer + for item in items: + + item.level = '-' * level + + bom_items.append(item) + + if item.sub_part.assembly: + add_items(item.sub_part.bom_items.all().order_by('id'), level + 1) + + if cascade: + # Cascading (multi-level) BOM + + # Start with the top level + items_to_process = part.bom_items.all().order_by('id') + + add_items(items_to_process, 1) + + else: + # No cascading needed - just the top-level items + bom_items = [item for item in part.bom_items.all().order_by('id')] + + dataset = BomItemResource().export(queryset=bom_items, cascade=cascade) data = dataset.export(fmt) filename = '{n}_BOM.{fmt}'.format(n=part.full_name, fmt=fmt) diff --git a/InvenTree/part/views.py b/InvenTree/part/views.py index c894c6ba99..584c8fe62f 100644 --- a/InvenTree/part/views.py +++ b/InvenTree/part/views.py @@ -1334,10 +1334,12 @@ class BomDownload(AjaxView): export_format = request.GET.get('file_format', 'csv') + cascade = str2bool(request.GET.get('cascade', False)) + if not IsValidBOMFormat(export_format): export_format = 'csv' - return ExportBom(part, fmt=export_format) + return ExportBom(part, fmt=export_format, cascade=cascade) def get_data(self): return { @@ -1354,7 +1356,7 @@ class BomExport(AjaxView): ajax_form_title = _("Export Bill of Materials") def get(self, request, *args, **kwargs): - return self.renderJsonResponse(request, self.form_class()) + return self.renderJsonResponse(request, self.form_class()) def post(self, request, *args, **kwargs):