We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Error exporting BOM in YAML format
The error is:

![Error](https://user-images.githubusercontent.com/14631/113969183-5db13200-97e9-11eb-918c-c84d23a8eb21.png "Error")

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `InvenTree/part/admin.py`
Content:
```
1 # -*- coding: utf-8 -*-
2 from __future__ import unicode_literals
3 
4 from django.contrib import admin
5 
6 from import_export.admin import ImportExportModelAdmin
7 from import_export.resources import ModelResource
8 from import_export.fields import Field
9 import import_export.widgets as widgets
10 
11 from .models import PartCategory, Part
12 from .models import PartAttachment, PartStar, PartRelated
13 from .models import BomItem
14 from .models import PartParameterTemplate, PartParameter
15 from .models import PartCategoryParameterTemplate
16 from .models import PartTestTemplate
17 from .models import PartSellPriceBreak
18 
19 from InvenTree.helpers import normalize
20 
21 from stock.models import StockLocation
22 from company.models import SupplierPart
23 
24 
25 class PartResource(ModelResource):
26     """ Class for managing Part data import/export """
27 
28     # ForeignKey fields
29     category = Field(attribute='category', widget=widgets.ForeignKeyWidget(PartCategory))
30     
31     default_location = Field(attribute='default_location', widget=widgets.ForeignKeyWidget(StockLocation))
32 
33     default_supplier = Field(attribute='default_supplier', widget=widgets.ForeignKeyWidget(SupplierPart))
34 
35     category_name = Field(attribute='category__name', readonly=True)
36     
37     variant_of = Field(attribute='variant_of', widget=widgets.ForeignKeyWidget(Part))
38 
39     suppliers = Field(attribute='supplier_count', readonly=True)
40 
41     # Extra calculated meta-data (readonly)
42     in_stock = Field(attribute='total_stock', readonly=True, widget=widgets.IntegerWidget())
43 
44     on_order = Field(attribute='on_order', readonly=True, widget=widgets.IntegerWidget())
45 
46     used_in = Field(attribute='used_in_count', readonly=True, widget=widgets.IntegerWidget())
47 
48     allocated = Field(attribute='allocation_count', readonly=True, widget=widgets.IntegerWidget())
49 
50     building = Field(attribute='quantity_being_built', readonly=True, widget=widgets.IntegerWidget())
51 
52     class Meta:
53         model = Part
54         skip_unchanged = True
55         report_skipped = False
56         clean_model_instances = True
57         exclude = [
58             'bom_checksum', 'bom_checked_by', 'bom_checked_date',
59             'lft', 'rght', 'tree_id', 'level',
60         ]
61 
62     def get_queryset(self):
63         """ Prefetch related data for quicker access """
64 
65         query = super().get_queryset()
66         query = query.prefetch_related(
67             'category',
68             'used_in',
69             'builds',
70             'supplier_parts__purchase_order_line_items',
71             'stock_items__allocations'
72         )
73 
74         return query
75 
76 
77 class PartAdmin(ImportExportModelAdmin):
78     
79     resource_class = PartResource
80 
81     list_display = ('full_name', 'description', 'total_stock', 'category')
82 
83     list_filter = ('active', 'assembly', 'is_template', 'virtual')
84 
85     search_fields = ('name', 'description', 'category__name', 'category__description', 'IPN')
86 
87 
88 class PartCategoryResource(ModelResource):
89     """ Class for managing PartCategory data import/export """
90 
91     parent = Field(attribute='parent', widget=widgets.ForeignKeyWidget(PartCategory))
92 
93     parent_name = Field(attribute='parent__name', readonly=True)
94 
95     default_location = Field(attribute='default_location', widget=widgets.ForeignKeyWidget(StockLocation))
96 
97     class Meta:
98         model = PartCategory
99         skip_unchanged = True
100         report_skipped = False
101         clean_model_instances = True
102 
103         exclude = [
104             # Exclude MPTT internal model fields
105             'lft', 'rght', 'tree_id', 'level',
106         ]
107 
108     def after_import(self, dataset, result, using_transactions, dry_run, **kwargs):
109 
110         super().after_import(dataset, result, using_transactions, dry_run, **kwargs)
111 
112         # Rebuild the PartCategory tree(s)
113         PartCategory.objects.rebuild()
114 
115 
116 class PartCategoryAdmin(ImportExportModelAdmin):
117 
118     resource_class = PartCategoryResource
119 
120     list_display = ('name', 'pathstring', 'description')
121 
122     search_fields = ('name', 'description')
123 
124 
125 class PartRelatedAdmin(admin.ModelAdmin):
126     ''' Class to manage PartRelated objects '''
127     pass
128 
129 
130 class PartAttachmentAdmin(admin.ModelAdmin):
131 
132     list_display = ('part', 'attachment', 'comment')
133 
134 
135 class PartStarAdmin(admin.ModelAdmin):
136 
137     list_display = ('part', 'user')
138 
139 
140 class PartTestTemplateAdmin(admin.ModelAdmin):
141 
142     list_display = ('part', 'test_name', 'required')
143 
144 
145 class BomItemResource(ModelResource):
146     """ Class for managing BomItem data import/export """
147 
148     level = Field(attribute='level', readonly=True)
149 
150     bom_id = Field(attribute='pk')
151 
152     # ID of the parent part
153     parent_part_id = Field(attribute='part', widget=widgets.ForeignKeyWidget(Part))
154 
155     # IPN of the parent part
156     parent_part_ipn = Field(attribute='part__IPN', readonly=True)
157 
158     # Name of the parent part
159     parent_part_name = Field(attribute='part__name', readonly=True)
160 
161     # ID of the sub-part
162     part_id = Field(attribute='sub_part', widget=widgets.ForeignKeyWidget(Part))
163 
164     # IPN of the sub-part
165     part_ipn = Field(attribute='sub_part__IPN', readonly=True)
166 
167     # Name of the sub-part
168     part_name = Field(attribute='sub_part__name', readonly=True)
169 
170     # Description of the sub-part
171     part_description = Field(attribute='sub_part__description', readonly=True)
172 
173     # Is the sub-part itself an assembly?
174     sub_assembly = Field(attribute='sub_part__assembly', readonly=True)
175 
176     def dehydrate_quantity(self, item):
177         """
178         Special consideration for the 'quantity' field on data export.
179         We do not want a spreadsheet full of "1.0000" (we'd rather "1")
180 
181         Ref: https://django-import-export.readthedocs.io/en/latest/getting_started.html#advanced-data-manipulation-on-export
182         """
183         return normalize(item.quantity)
184 
185     def before_export(self, queryset, *args, **kwargs):
186 
187         self.is_importing = kwargs.get('importing', False)
188 
189     def get_fields(self, **kwargs):
190         """
191         If we are exporting for the purposes of generating
192         a 'bom-import' template, there are some fields which
193         we are not interested in.
194         """
195 
196         fields = super().get_fields(**kwargs)
197 
198         # If we are not generating an "import" template,
199         # just return the complete list of fields
200         if not self.is_importing:
201             return fields
202 
203         # Otherwise, remove some fields we are not interested in
204 
205         idx = 0
206 
207         to_remove = [
208             'level',
209             'bom_id',
210             'parent_part_id',
211             'parent_part_ipn',
212             'parent_part_name',
213             'part_description',
214             'sub_assembly'
215         ]
216 
217         while idx < len(fields):
218 
219             if fields[idx].column_name.lower() in to_remove:
220                 del fields[idx]
221             else:
222                 idx += 1
223 
224         return fields
225 
226     class Meta:
227         model = BomItem
228         skip_unchanged = True
229         report_skipped = False
230         clean_model_instances = True
231 
232         exclude = [
233             'checksum',
234             'id',
235             'part',
236             'sub_part',
237         ]
238 
239 
240 class BomItemAdmin(ImportExportModelAdmin):
241 
242     resource_class = BomItemResource
243 
244     list_display = ('part', 'sub_part', 'quantity')
245 
246     search_fields = ('part__name', 'part__description', 'sub_part__name', 'sub_part__description')
247 
248 
249 class ParameterTemplateAdmin(ImportExportModelAdmin):
250     list_display = ('name', 'units')
251 
252 
253 class ParameterResource(ModelResource):
254     """ Class for managing PartParameter data import/export """
255 
256     part = Field(attribute='part', widget=widgets.ForeignKeyWidget(Part))
257 
258     part_name = Field(attribute='part__name', readonly=True)
259 
260     template = Field(attribute='template', widget=widgets.ForeignKeyWidget(PartParameterTemplate))
261 
262     template_name = Field(attribute='template__name', readonly=True)
263 
264     class Meta:
265         model = PartParameter
266         skip_unchanged = True
267         report_skipped = False
268         clean_model_instance = True
269 
270 
271 class ParameterAdmin(ImportExportModelAdmin):
272 
273     resource_class = ParameterResource
274 
275     list_display = ('part', 'template', 'data')
276 
277 
278 class PartCategoryParameterAdmin(admin.ModelAdmin):
279 
280     pass
281 
282 
283 class PartSellPriceBreakAdmin(admin.ModelAdmin):
284 
285     class Meta:
286         model = PartSellPriceBreak
287 
288     list_display = ('part', 'quantity', 'price',)
289 
290 
291 admin.site.register(Part, PartAdmin)
292 admin.site.register(PartCategory, PartCategoryAdmin)
293 admin.site.register(PartRelated, PartRelatedAdmin)
294 admin.site.register(PartAttachment, PartAttachmentAdmin)
295 admin.site.register(PartStar, PartStarAdmin)
296 admin.site.register(BomItem, BomItemAdmin)
297 admin.site.register(PartParameterTemplate, ParameterTemplateAdmin)
298 admin.site.register(PartParameter, ParameterAdmin)
299 admin.site.register(PartCategoryParameterTemplate, PartCategoryParameterAdmin)
300 admin.site.register(PartTestTemplate, PartTestTemplateAdmin)
301 admin.site.register(PartSellPriceBreak, PartSellPriceBreakAdmin)
302
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/InvenTree/part/admin.py b/InvenTree/part/admin.py
--- a/InvenTree/part/admin.py
+++ b/InvenTree/part/admin.py
@@ -16,8 +16,6 @@
 from .models import PartTestTemplate
 from .models import PartSellPriceBreak
 
-from InvenTree.helpers import normalize
-
 from stock.models import StockLocation
 from company.models import SupplierPart
 
@@ -180,7 +178,7 @@
 
         Ref: https://django-import-export.readthedocs.io/en/latest/getting_started.html#advanced-data-manipulation-on-export
         """
-        return normalize(item.quantity)
+        return float(item.quantity)
 
     def before_export(self, queryset, *args, **kwargs):

{"golden_diff": "diff --git a/InvenTree/part/admin.py b/InvenTree/part/admin.py\n--- a/InvenTree/part/admin.py\n+++ b/InvenTree/part/admin.py\n@@ -16,8 +16,6 @@\n from .models import PartTestTemplate\n from .models import PartSellPriceBreak\n \n-from InvenTree.helpers import normalize\n-\n from stock.models import StockLocation\n from company.models import SupplierPart\n \n@@ -180,7 +178,7 @@\n \n         Ref: https://django-import-export.readthedocs.io/en/latest/getting_started.html#advanced-data-manipulation-on-export\n         \"\"\"\n-        return normalize(item.quantity)\n+        return float(item.quantity)\n \n     def before_export(self, queryset, *args, **kwargs):\n", "issue": "Error exporting BOM in YAML format\nThe error is:\r\n\r\n![Error](https://user-images.githubusercontent.com/14631/113969183-5db13200-97e9-11eb-918c-c84d23a8eb21.png \"Error\")\n", "before_files": [{"content": "# -*- coding: utf-8 -*-\nfrom __future__ import unicode_literals\n\nfrom django.contrib import admin\n\nfrom import_export.admin import ImportExportModelAdmin\nfrom import_export.resources import ModelResource\nfrom import_export.fields import Field\nimport import_export.widgets as widgets\n\nfrom .models import PartCategory, Part\nfrom .models import PartAttachment, PartStar, PartRelated\nfrom .models import BomItem\nfrom .models import PartParameterTemplate, PartParameter\nfrom .models import PartCategoryParameterTemplate\nfrom .models import PartTestTemplate\nfrom .models import PartSellPriceBreak\n\nfrom InvenTree.helpers import normalize\n\nfrom stock.models import StockLocation\nfrom company.models import SupplierPart\n\n\nclass PartResource(ModelResource):\n    \"\"\" Class for managing Part data import/export \"\"\"\n\n    # ForeignKey fields\n    category = Field(attribute='category', widget=widgets.ForeignKeyWidget(PartCategory))\n    \n    default_location = Field(attribute='default_location', widget=widgets.ForeignKeyWidget(StockLocation))\n\n    default_supplier = Field(attribute='default_supplier', widget=widgets.ForeignKeyWidget(SupplierPart))\n\n    category_name = Field(attribute='category__name', readonly=True)\n    \n    variant_of = Field(attribute='variant_of', widget=widgets.ForeignKeyWidget(Part))\n\n    suppliers = Field(attribute='supplier_count', readonly=True)\n\n    # Extra calculated meta-data (readonly)\n    in_stock = Field(attribute='total_stock', readonly=True, widget=widgets.IntegerWidget())\n\n    on_order = Field(attribute='on_order', readonly=True, widget=widgets.IntegerWidget())\n\n    used_in = Field(attribute='used_in_count', readonly=True, widget=widgets.IntegerWidget())\n\n    allocated = Field(attribute='allocation_count', readonly=True, widget=widgets.IntegerWidget())\n\n    building = Field(attribute='quantity_being_built', readonly=True, widget=widgets.IntegerWidget())\n\n    class Meta:\n        model = Part\n        skip_unchanged = True\n        report_skipped = False\n        clean_model_instances = True\n        exclude = [\n            'bom_checksum', 'bom_checked_by', 'bom_checked_date',\n            'lft', 'rght', 'tree_id', 'level',\n        ]\n\n    def get_queryset(self):\n        \"\"\" Prefetch related data for quicker access \"\"\"\n\n        query = super().get_queryset()\n        query = query.prefetch_related(\n            'category',\n            'used_in',\n            'builds',\n            'supplier_parts__purchase_order_line_items',\n            'stock_items__allocations'\n        )\n\n        return query\n\n\nclass PartAdmin(ImportExportModelAdmin):\n    \n    resource_class = PartResource\n\n    list_display = ('full_name', 'description', 'total_stock', 'category')\n\n    list_filter = ('active', 'assembly', 'is_template', 'virtual')\n\n    search_fields = ('name', 'description', 'category__name', 'category__description', 'IPN')\n\n\nclass PartCategoryResource(ModelResource):\n    \"\"\" Class for managing PartCategory data import/export \"\"\"\n\n    parent = Field(attribute='parent', widget=widgets.ForeignKeyWidget(PartCategory))\n\n    parent_name = Field(attribute='parent__name', readonly=True)\n\n    default_location = Field(attribute='default_location', widget=widgets.ForeignKeyWidget(StockLocation))\n\n    class Meta:\n        model = PartCategory\n        skip_unchanged = True\n        report_skipped = False\n        clean_model_instances = True\n\n        exclude = [\n            # Exclude MPTT internal model fields\n            'lft', 'rght', 'tree_id', 'level',\n        ]\n\n    def after_import(self, dataset, result, using_transactions, dry_run, **kwargs):\n\n        super().after_import(dataset, result, using_transactions, dry_run, **kwargs)\n\n        # Rebuild the PartCategory tree(s)\n        PartCategory.objects.rebuild()\n\n\nclass PartCategoryAdmin(ImportExportModelAdmin):\n\n    resource_class = PartCategoryResource\n\n    list_display = ('name', 'pathstring', 'description')\n\n    search_fields = ('name', 'description')\n\n\nclass PartRelatedAdmin(admin.ModelAdmin):\n    ''' Class to manage PartRelated objects '''\n    pass\n\n\nclass PartAttachmentAdmin(admin.ModelAdmin):\n\n    list_display = ('part', 'attachment', 'comment')\n\n\nclass PartStarAdmin(admin.ModelAdmin):\n\n    list_display = ('part', 'user')\n\n\nclass PartTestTemplateAdmin(admin.ModelAdmin):\n\n    list_display = ('part', 'test_name', 'required')\n\n\nclass BomItemResource(ModelResource):\n    \"\"\" Class for managing BomItem data import/export \"\"\"\n\n    level = Field(attribute='level', readonly=True)\n\n    bom_id = Field(attribute='pk')\n\n    # ID of the parent part\n    parent_part_id = Field(attribute='part', widget=widgets.ForeignKeyWidget(Part))\n\n    # IPN of the parent part\n    parent_part_ipn = Field(attribute='part__IPN', readonly=True)\n\n    # Name of the parent part\n    parent_part_name = Field(attribute='part__name', readonly=True)\n\n    # ID of the sub-part\n    part_id = Field(attribute='sub_part', widget=widgets.ForeignKeyWidget(Part))\n\n    # IPN of the sub-part\n    part_ipn = Field(attribute='sub_part__IPN', readonly=True)\n\n    # Name of the sub-part\n    part_name = Field(attribute='sub_part__name', readonly=True)\n\n    # Description of the sub-part\n    part_description = Field(attribute='sub_part__description', readonly=True)\n\n    # Is the sub-part itself an assembly?\n    sub_assembly = Field(attribute='sub_part__assembly', readonly=True)\n\n    def dehydrate_quantity(self, item):\n        \"\"\"\n        Special consideration for the 'quantity' field on data export.\n        We do not want a spreadsheet full of \"1.0000\" (we'd rather \"1\")\n\n        Ref: https://django-import-export.readthedocs.io/en/latest/getting_started.html#advanced-data-manipulation-on-export\n        \"\"\"\n        return normalize(item.quantity)\n\n    def before_export(self, queryset, *args, **kwargs):\n\n        self.is_importing = kwargs.get('importing', False)\n\n    def get_fields(self, **kwargs):\n        \"\"\"\n        If we are exporting for the purposes of generating\n        a 'bom-import' template, there are some fields which\n        we are not interested in.\n        \"\"\"\n\n        fields = super().get_fields(**kwargs)\n\n        # If we are not generating an \"import\" template,\n        # just return the complete list of fields\n        if not self.is_importing:\n            return fields\n\n        # Otherwise, remove some fields we are not interested in\n\n        idx = 0\n\n        to_remove = [\n            'level',\n            'bom_id',\n            'parent_part_id',\n            'parent_part_ipn',\n            'parent_part_name',\n            'part_description',\n            'sub_assembly'\n        ]\n\n        while idx < len(fields):\n\n            if fields[idx].column_name.lower() in to_remove:\n                del fields[idx]\n            else:\n                idx += 1\n\n        return fields\n\n    class Meta:\n        model = BomItem\n        skip_unchanged = True\n        report_skipped = False\n        clean_model_instances = True\n\n        exclude = [\n            'checksum',\n            'id',\n            'part',\n            'sub_part',\n        ]\n\n\nclass BomItemAdmin(ImportExportModelAdmin):\n\n    resource_class = BomItemResource\n\n    list_display = ('part', 'sub_part', 'quantity')\n\n    search_fields = ('part__name', 'part__description', 'sub_part__name', 'sub_part__description')\n\n\nclass ParameterTemplateAdmin(ImportExportModelAdmin):\n    list_display = ('name', 'units')\n\n\nclass ParameterResource(ModelResource):\n    \"\"\" Class for managing PartParameter data import/export \"\"\"\n\n    part = Field(attribute='part', widget=widgets.ForeignKeyWidget(Part))\n\n    part_name = Field(attribute='part__name', readonly=True)\n\n    template = Field(attribute='template', widget=widgets.ForeignKeyWidget(PartParameterTemplate))\n\n    template_name = Field(attribute='template__name', readonly=True)\n\n    class Meta:\n        model = PartParameter\n        skip_unchanged = True\n        report_skipped = False\n        clean_model_instance = True\n\n\nclass ParameterAdmin(ImportExportModelAdmin):\n\n    resource_class = ParameterResource\n\n    list_display = ('part', 'template', 'data')\n\n\nclass PartCategoryParameterAdmin(admin.ModelAdmin):\n\n    pass\n\n\nclass PartSellPriceBreakAdmin(admin.ModelAdmin):\n\n    class Meta:\n        model = PartSellPriceBreak\n\n    list_display = ('part', 'quantity', 'price',)\n\n\nadmin.site.register(Part, PartAdmin)\nadmin.site.register(PartCategory, PartCategoryAdmin)\nadmin.site.register(PartRelated, PartRelatedAdmin)\nadmin.site.register(PartAttachment, PartAttachmentAdmin)\nadmin.site.register(PartStar, PartStarAdmin)\nadmin.site.register(BomItem, BomItemAdmin)\nadmin.site.register(PartParameterTemplate, ParameterTemplateAdmin)\nadmin.site.register(PartParameter, ParameterAdmin)\nadmin.site.register(PartCategoryParameterTemplate, PartCategoryParameterAdmin)\nadmin.site.register(PartTestTemplate, PartTestTemplateAdmin)\nadmin.site.register(PartSellPriceBreak, PartSellPriceBreakAdmin)\n", "path": "InvenTree/part/admin.py"}], "after_files": [{"content": "# -*- coding: utf-8 -*-\nfrom __future__ import unicode_literals\n\nfrom django.contrib import admin\n\nfrom import_export.admin import ImportExportModelAdmin\nfrom import_export.resources import ModelResource\nfrom import_export.fields import Field\nimport import_export.widgets as widgets\n\nfrom .models import PartCategory, Part\nfrom .models import PartAttachment, PartStar, PartRelated\nfrom .models import BomItem\nfrom .models import PartParameterTemplate, PartParameter\nfrom .models import PartCategoryParameterTemplate\nfrom .models import PartTestTemplate\nfrom .models import PartSellPriceBreak\n\nfrom stock.models import StockLocation\nfrom company.models import SupplierPart\n\n\nclass PartResource(ModelResource):\n    \"\"\" Class for managing Part data import/export \"\"\"\n\n    # ForeignKey fields\n    category = Field(attribute='category', widget=widgets.ForeignKeyWidget(PartCategory))\n    \n    default_location = Field(attribute='default_location', widget=widgets.ForeignKeyWidget(StockLocation))\n\n    default_supplier = Field(attribute='default_supplier', widget=widgets.ForeignKeyWidget(SupplierPart))\n\n    category_name = Field(attribute='category__name', readonly=True)\n    \n    variant_of = Field(attribute='variant_of', widget=widgets.ForeignKeyWidget(Part))\n\n    suppliers = Field(attribute='supplier_count', readonly=True)\n\n    # Extra calculated meta-data (readonly)\n    in_stock = Field(attribute='total_stock', readonly=True, widget=widgets.IntegerWidget())\n\n    on_order = Field(attribute='on_order', readonly=True, widget=widgets.IntegerWidget())\n\n    used_in = Field(attribute='used_in_count', readonly=True, widget=widgets.IntegerWidget())\n\n    allocated = Field(attribute='allocation_count', readonly=True, widget=widgets.IntegerWidget())\n\n    building = Field(attribute='quantity_being_built', readonly=True, widget=widgets.IntegerWidget())\n\n    class Meta:\n        model = Part\n        skip_unchanged = True\n        report_skipped = False\n        clean_model_instances = True\n        exclude = [\n            'bom_checksum', 'bom_checked_by', 'bom_checked_date',\n            'lft', 'rght', 'tree_id', 'level',\n        ]\n\n    def get_queryset(self):\n        \"\"\" Prefetch related data for quicker access \"\"\"\n\n        query = super().get_queryset()\n        query = query.prefetch_related(\n            'category',\n            'used_in',\n            'builds',\n            'supplier_parts__purchase_order_line_items',\n            'stock_items__allocations'\n        )\n\n        return query\n\n\nclass PartAdmin(ImportExportModelAdmin):\n    \n    resource_class = PartResource\n\n    list_display = ('full_name', 'description', 'total_stock', 'category')\n\n    list_filter = ('active', 'assembly', 'is_template', 'virtual')\n\n    search_fields = ('name', 'description', 'category__name', 'category__description', 'IPN')\n\n\nclass PartCategoryResource(ModelResource):\n    \"\"\" Class for managing PartCategory data import/export \"\"\"\n\n    parent = Field(attribute='parent', widget=widgets.ForeignKeyWidget(PartCategory))\n\n    parent_name = Field(attribute='parent__name', readonly=True)\n\n    default_location = Field(attribute='default_location', widget=widgets.ForeignKeyWidget(StockLocation))\n\n    class Meta:\n        model = PartCategory\n        skip_unchanged = True\n        report_skipped = False\n        clean_model_instances = True\n\n        exclude = [\n            # Exclude MPTT internal model fields\n            'lft', 'rght', 'tree_id', 'level',\n        ]\n\n    def after_import(self, dataset, result, using_transactions, dry_run, **kwargs):\n\n        super().after_import(dataset, result, using_transactions, dry_run, **kwargs)\n\n        # Rebuild the PartCategory tree(s)\n        PartCategory.objects.rebuild()\n\n\nclass PartCategoryAdmin(ImportExportModelAdmin):\n\n    resource_class = PartCategoryResource\n\n    list_display = ('name', 'pathstring', 'description')\n\n    search_fields = ('name', 'description')\n\n\nclass PartRelatedAdmin(admin.ModelAdmin):\n    ''' Class to manage PartRelated objects '''\n    pass\n\n\nclass PartAttachmentAdmin(admin.ModelAdmin):\n\n    list_display = ('part', 'attachment', 'comment')\n\n\nclass PartStarAdmin(admin.ModelAdmin):\n\n    list_display = ('part', 'user')\n\n\nclass PartTestTemplateAdmin(admin.ModelAdmin):\n\n    list_display = ('part', 'test_name', 'required')\n\n\nclass BomItemResource(ModelResource):\n    \"\"\" Class for managing BomItem data import/export \"\"\"\n\n    level = Field(attribute='level', readonly=True)\n\n    bom_id = Field(attribute='pk')\n\n    # ID of the parent part\n    parent_part_id = Field(attribute='part', widget=widgets.ForeignKeyWidget(Part))\n\n    # IPN of the parent part\n    parent_part_ipn = Field(attribute='part__IPN', readonly=True)\n\n    # Name of the parent part\n    parent_part_name = Field(attribute='part__name', readonly=True)\n\n    # ID of the sub-part\n    part_id = Field(attribute='sub_part', widget=widgets.ForeignKeyWidget(Part))\n\n    # IPN of the sub-part\n    part_ipn = Field(attribute='sub_part__IPN', readonly=True)\n\n    # Name of the sub-part\n    part_name = Field(attribute='sub_part__name', readonly=True)\n\n    # Description of the sub-part\n    part_description = Field(attribute='sub_part__description', readonly=True)\n\n    # Is the sub-part itself an assembly?\n    sub_assembly = Field(attribute='sub_part__assembly', readonly=True)\n\n    def dehydrate_quantity(self, item):\n        \"\"\"\n        Special consideration for the 'quantity' field on data export.\n        We do not want a spreadsheet full of \"1.0000\" (we'd rather \"1\")\n\n        Ref: https://django-import-export.readthedocs.io/en/latest/getting_started.html#advanced-data-manipulation-on-export\n        \"\"\"\n        return float(item.quantity)\n\n    def before_export(self, queryset, *args, **kwargs):\n\n        self.is_importing = kwargs.get('importing', False)\n\n    def get_fields(self, **kwargs):\n        \"\"\"\n        If we are exporting for the purposes of generating\n        a 'bom-import' template, there are some fields which\n        we are not interested in.\n        \"\"\"\n\n        fields = super().get_fields(**kwargs)\n\n        # If we are not generating an \"import\" template,\n        # just return the complete list of fields\n        if not self.is_importing:\n            return fields\n\n        # Otherwise, remove some fields we are not interested in\n\n        idx = 0\n\n        to_remove = [\n            'level',\n            'bom_id',\n            'parent_part_id',\n            'parent_part_ipn',\n            'parent_part_name',\n            'part_description',\n            'sub_assembly'\n        ]\n\n        while idx < len(fields):\n\n            if fields[idx].column_name.lower() in to_remove:\n                del fields[idx]\n            else:\n                idx += 1\n\n        return fields\n\n    class Meta:\n        model = BomItem\n        skip_unchanged = True\n        report_skipped = False\n        clean_model_instances = True\n\n        exclude = [\n            'checksum',\n            'id',\n            'part',\n            'sub_part',\n        ]\n\n\nclass BomItemAdmin(ImportExportModelAdmin):\n\n    resource_class = BomItemResource\n\n    list_display = ('part', 'sub_part', 'quantity')\n\n    search_fields = ('part__name', 'part__description', 'sub_part__name', 'sub_part__description')\n\n\nclass ParameterTemplateAdmin(ImportExportModelAdmin):\n    list_display = ('name', 'units')\n\n\nclass ParameterResource(ModelResource):\n    \"\"\" Class for managing PartParameter data import/export \"\"\"\n\n    part = Field(attribute='part', widget=widgets.ForeignKeyWidget(Part))\n\n    part_name = Field(attribute='part__name', readonly=True)\n\n    template = Field(attribute='template', widget=widgets.ForeignKeyWidget(PartParameterTemplate))\n\n    template_name = Field(attribute='template__name', readonly=True)\n\n    class Meta:\n        model = PartParameter\n        skip_unchanged = True\n        report_skipped = False\n        clean_model_instance = True\n\n\nclass ParameterAdmin(ImportExportModelAdmin):\n\n    resource_class = ParameterResource\n\n    list_display = ('part', 'template', 'data')\n\n\nclass PartCategoryParameterAdmin(admin.ModelAdmin):\n\n    pass\n\n\nclass PartSellPriceBreakAdmin(admin.ModelAdmin):\n\n    class Meta:\n        model = PartSellPriceBreak\n\n    list_display = ('part', 'quantity', 'price',)\n\n\nadmin.site.register(Part, PartAdmin)\nadmin.site.register(PartCategory, PartCategoryAdmin)\nadmin.site.register(PartRelated, PartRelatedAdmin)\nadmin.site.register(PartAttachment, PartAttachmentAdmin)\nadmin.site.register(PartStar, PartStarAdmin)\nadmin.site.register(BomItem, BomItemAdmin)\nadmin.site.register(PartParameterTemplate, ParameterTemplateAdmin)\nadmin.site.register(PartParameter, ParameterAdmin)\nadmin.site.register(PartCategoryParameterTemplate, PartCategoryParameterAdmin)\nadmin.site.register(PartTestTemplate, PartTestTemplateAdmin)\nadmin.site.register(PartSellPriceBreak, PartSellPriceBreakAdmin)\n", "path": "InvenTree/part/admin.py"}]}