diff --git a/InvenTree/InvenTree/helpers.py b/InvenTree/InvenTree/helpers.py index 929a1ff708..79a7ccd53a 100644 --- a/InvenTree/InvenTree/helpers.py +++ b/InvenTree/InvenTree/helpers.py @@ -162,7 +162,7 @@ def normalize(d): def increment(n): - """Attempt to increment an integer (or a string that looks like an integer!) + """Attempt to increment an integer (or a string that looks like an integer). e.g. @@ -332,7 +332,7 @@ def GetExportFormats(): ] -def DownloadFile(data, filename, content_type='application/text', inline=False): +def DownloadFile(data, filename, content_type='application/text', inline=False) -> StreamingHttpResponse: """Create a dynamic file for the user to download. Args: @@ -362,7 +362,7 @@ def DownloadFile(data, filename, content_type='application/text', inline=False): def extract_serial_numbers(serials, expected_quantity, next_number: int): - """Attempt to extract serial numbers from an input string: + """Attempt to extract serial numbers from an input string. Requirements: - Serial numbers can be either strings, or integers diff --git a/InvenTree/InvenTree/views.py b/InvenTree/InvenTree/views.py index 325ffa9242..9eef22d494 100644 --- a/InvenTree/InvenTree/views.py +++ b/InvenTree/InvenTree/views.py @@ -211,7 +211,7 @@ class AjaxMixin(InvenTreeRoleMixin): ajax_form_title = '' def get_form_title(self): - """Default implementation - return the ajax_form_title variable""" + """Default implementation - return the ajax_form_title variable.""" return self.ajax_form_title def get_param(self, name, method='GET'): @@ -230,7 +230,7 @@ class AjaxMixin(InvenTreeRoleMixin): return self.request.GET.get(name, None) def get_data(self): - """Get extra context data (default implementation is empty dict) + """Get extra context data (default implementation is empty dict). Returns: dict object (empty) @@ -444,6 +444,9 @@ class AjaxUpdateView(AjaxMixin, UpdateView): Args: object - The current object, to be updated form - The validated form + + Returns: + object instance for supplied form """ self.object = form.save() diff --git a/InvenTree/build/models.py b/InvenTree/build/models.py index 3a960c5019..76be632e10 100644 --- a/InvenTree/build/models.py +++ b/InvenTree/build/models.py @@ -340,9 +340,12 @@ class Build(MPTTModel, ReferenceIndexingMixin): @property def is_overdue(self): - """Returns true if this build is "overdue": + """Returns true if this build is "overdue". Makes use of the OVERDUE_FILTER to avoid code duplication + + Returns: + bool: Is the build overdue """ query = Build.objects.filter(pk=self.pk) query = query.filter(Build.OVERDUE_FILTER) @@ -574,9 +577,9 @@ class Build(MPTTModel, ReferenceIndexingMixin): def unallocateStock(self, bom_item=None, output=None): """Unallocate stock from this Build. - Arguments: - - bom_item: Specify a particular BomItem to unallocate stock against - - output: Specify a particular StockItem (output) to unallocate stock against + Args: + bom_item: Specify a particular BomItem to unallocate stock against + output: Specify a particular StockItem (output) to unallocate stock against """ allocations = BuildItem.objects.filter( build=self, @@ -693,8 +696,9 @@ class Build(MPTTModel, ReferenceIndexingMixin): @transaction.atomic def delete_output(self, output): - """Remove a build output from the database: + """Remove a build output from the database. + Executes: - Unallocate any build items against the output - Delete the output StockItem """ @@ -882,8 +886,8 @@ class Build(MPTTModel, ReferenceIndexingMixin): """Get the quantity of a part required to complete the particular build output. Args: - part: The Part object - output - The particular build output (StockItem) + bom_item: The Part object + output: The particular build output (StockItem) """ quantity = bom_item.quantity @@ -895,14 +899,14 @@ class Build(MPTTModel, ReferenceIndexingMixin): return quantity def allocated_bom_items(self, bom_item, output=None): - """Return all BuildItem objects which allocate stock of to + """Return all BuildItem objects which allocate stock of to . Note that the bom_item may allow variants, or direct substitutes, making things difficult. Args: - bom_item - The BomItem object - output - Build output (StockItem). + bom_item: The BomItem object + output: Build output (StockItem). """ allocations = BuildItem.objects.filter( build=self, @@ -991,7 +995,7 @@ class Build(MPTTModel, ReferenceIndexingMixin): @property def required_parts(self): - """Returns a list of parts required to build this part (BOM)""" + """Returns a list of parts required to build this part (BOM).""" parts = [] for item in self.bom_items: diff --git a/InvenTree/build/test_build.py b/InvenTree/build/test_build.py index a7a5f7106b..298fbb583a 100644 --- a/InvenTree/build/test_build.py +++ b/InvenTree/build/test_build.py @@ -1,3 +1,4 @@ +from ctypes import Union from django.test import TestCase from django.core.exceptions import ValidationError @@ -193,12 +194,12 @@ class BuildTest(BuildTestBase): quantity=99 ) - def allocate_stock(self, output, allocations): + def allocate_stock(self, output: Union[StockItem, None], allocations: dict[StockItem, int]) -> None: """Allocate stock to this build, against a particular output. Args: - output - StockItem object (or None) - allocations - Map of {StockItem: quantity} + output (Union[StockItem, None]): StockItem object or None + allocations (dict[StockItem, int]): Map of `{StockItem: quantity}` """ for item, quantity in allocations.items(): BuildItem.objects.create( diff --git a/InvenTree/common/files.py b/InvenTree/common/files.py index a1817747aa..a9326b406e 100644 --- a/InvenTree/common/files.py +++ b/InvenTree/common/files.py @@ -94,8 +94,11 @@ class FileManager: """Try to match a header (from the file) to a list of known headers. Args: - header - Header name to look for - threshold - Match threshold for fuzzy search + header (Any): Header name to look for + threshold (int, optional): Match threshold for fuzzy search. Defaults to 80. + + Returns: + Any: Matched headers """ # Replace null values with empty string if header is None: diff --git a/InvenTree/common/models.py b/InvenTree/common/models.py index 10610d1b48..cf9cd72df7 100644 --- a/InvenTree/common/models.py +++ b/InvenTree/common/models.py @@ -477,7 +477,7 @@ class BaseInvenTreeSetting(models.Model): pass def choices(self): - """Return the available choices for this setting (or None if no choices are defined)""" + """Return the available choices for this setting (or None if no choices are defined).""" return self.__class__.get_setting_choices(self.key, **self.get_kwargs()) def valid_options(self): @@ -1519,7 +1519,7 @@ class PriceBreak(models.Model): """Convert the unit-price at this price break to the specified currency code. Args: - currency_code - The currency code to convert to (e.g "USD" or "AUD") + currency_code: The currency code to convert to (e.g "USD" or "AUD") """ try: converted = convert_money(self.price, currency_code) diff --git a/InvenTree/company/models.py b/InvenTree/company/models.py index acdeb9c737..ec9935d706 100644 --- a/InvenTree/company/models.py +++ b/InvenTree/company/models.py @@ -231,7 +231,7 @@ class Company(models.Model): return self.purchase_orders.filter(status__in=PurchaseOrderStatus.OPEN) def pending_purchase_orders(self): - """Return purchase orders which are PENDING (not yet issued)""" + """Return purchase orders which are PENDING (not yet issued).""" return self.purchase_orders.filter(status=PurchaseOrderStatus.PENDING) def closed_purchase_orders(self): @@ -598,12 +598,12 @@ class SupplierPart(models.Model): def unit_pricing(self): return self.get_price(1) - def add_price_break(self, quantity, price): + def add_price_break(self, quantity, price) -> None: """Create a new price break for this part. Args: - quantity - Numerical quantity - price - Must be a Money object + quantity: Numerical quantity + price: Must be a Money object """ # Check if a price break at that quantity already exists... if self.price_breaks.filter(quantity=quantity, part=self.pk).exists(): diff --git a/InvenTree/order/models.py b/InvenTree/order/models.py index 4fd45aa09f..9851b7e771 100644 --- a/InvenTree/order/models.py +++ b/InvenTree/order/models.py @@ -329,16 +329,24 @@ class PurchaseOrder(Order): return reverse('po-detail', kwargs={'pk': self.id}) @transaction.atomic - def add_line_item(self, supplier_part, quantity, group=True, reference='', purchase_price=None): - """Add a new line item to this purchase order. This function will check that: + def add_line_item(self, supplier_part, quantity, group: bool = True, reference: str = '', purchase_price=None): + """Add a new line item to this purchase order. + This function will check that: * The supplier part matches the supplier specified for this purchase order * The quantity is greater than zero Args: - supplier_part - The supplier_part to add - quantity - The number of items to add - group - If True, this new quantity will be added to an existing line item for the same supplier_part (if it exists) + supplier_part: The supplier_part to add + quantity : The number of items to add + group (bool, optional): If True, this new quantity will be added to an existing line item for the same supplier_part (if it exists). Defaults to True. + reference (str, optional): Reference to item. Defaults to ''. + purchase_price (optional): Price of item. Defaults to None. + + Raises: + ValidationError: quantity is smaller than 0 + ValidationError: quantity is not type int + ValidationError: supplier is not supplier of purchase order """ try: quantity = int(quantity) @@ -416,7 +424,11 @@ class PurchaseOrder(Order): return query.exists() def can_cancel(self): - """A PurchaseOrder can only be cancelled under the following circumstances:""" + """A PurchaseOrder can only be cancelled under the following circumstances. + + - Status is PLACED + - Status is PENDING + """ return self.status in [ PurchaseOrderStatus.PLACED, PurchaseOrderStatus.PENDING @@ -655,7 +667,7 @@ class SalesOrder(Order): @property def is_overdue(self): - """Returns true if this SalesOrder is "overdue": + """Returns true if this SalesOrder is "overdue". Makes use of the OVERDUE_FILTER to avoid code duplication. """ @@ -692,7 +704,7 @@ class SalesOrder(Order): return False def is_completed(self): - """Check if this order is "shipped" (all line items delivered)""" + """Check if this order is "shipped" (all line items delivered).""" return self.lines.count() > 0 and all([line.is_completed() for line in self.lines.all()]) def can_complete(self, raise_error=False): @@ -749,8 +761,9 @@ class SalesOrder(Order): @transaction.atomic def cancel_order(self): - """Cancel this order (only if it is "pending") + """Cancel this order (only if it is "pending"). + Executes: - Mark the order as 'cancelled' - Delete any StockItems which have been allocated """ @@ -1110,7 +1123,7 @@ class SalesOrderLineItem(OrderLineItem): return self.allocated_quantity() > self.quantity def is_completed(self): - """Return True if this line item is completed (has been fully shipped)""" + """Return True if this line item is completed (has been fully shipped).""" return self.shipped >= self.quantity @@ -1222,8 +1235,9 @@ class SalesOrderShipment(models.Model): @transaction.atomic def complete_shipment(self, user, **kwargs): - """Complete this particular shipment: + """Complete this particular shipment. + Executes: 1. Update any stock items associated with this shipment 2. Update the "shipped" quantity of all associated line items 3. Set the "shipment_date" to now @@ -1295,8 +1309,9 @@ class SalesOrderAllocation(models.Model): return reverse('api-so-allocation-list') def clean(self): - """Validate the SalesOrderAllocation object: + """Validate the SalesOrderAllocation object. + Executes: - Cannot allocate stock to a line item without a part reference - The referenced part must match the part associated with the line item - Allocated quantity cannot exceed the quantity of the stock item @@ -1385,8 +1400,9 @@ class SalesOrderAllocation(models.Model): return self.item.purchase_order def complete_allocation(self, user): - """Complete this allocation (called when the parent SalesOrder is marked as "shipped"): + """Complete this allocation (called when the parent SalesOrder is marked as "shipped"). + Executes: - Determine if the referenced StockItem needs to be "split" (if allocated quantity != stock quantity) - Mark the StockItem as belonging to the Customer (this will remove it from stock) """ diff --git a/InvenTree/part/bom.py b/InvenTree/part/bom.py index 69d7baba5d..2e1018060d 100644 --- a/InvenTree/part/bom.py +++ b/InvenTree/part/bom.py @@ -11,7 +11,7 @@ from company.models import ManufacturerPart, SupplierPart from InvenTree.helpers import DownloadFile, GetExportFormats, normalize from .admin import BomItemResource -from .models import BomItem +from .models import BomItem, Part def IsValidBOMFormat(fmt): @@ -20,7 +20,7 @@ def IsValidBOMFormat(fmt): def MakeBomTemplate(fmt): - """Generate a Bill of Materials upload template file (for user download)""" + """Generate a Bill of Materials upload template file (for user download).""" fmt = fmt.strip().lower() if not IsValidBOMFormat(fmt): @@ -42,12 +42,21 @@ def MakeBomTemplate(fmt): return DownloadFile(data, filename) -def ExportBom(part, fmt='csv', cascade=False, max_levels=None, parameter_data=False, stock_data=False, supplier_data=False, manufacturer_data=False): +def ExportBom(part: Part, fmt='csv', cascade: bool = False, max_levels: int = None, parameter_data=False, stock_data=False, supplier_data=False, manufacturer_data=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. + part (Part): Part for which the BOM should be exported + fmt (str, optional): file format. Defaults to 'csv'. + cascade (bool, optional): If True, multi-level BOM output is supported. Otherwise, a flat top-level-only BOM is exported.. Defaults to False. + max_levels (int, optional): Levels of items that should be included. None for np sublevels. Defaults to None. + parameter_data (bool, optional): Additonal data that should be added. Defaults to False. + stock_data (bool, optional): Additonal data that should be added. Defaults to False. + supplier_data (bool, optional): Additonal data that should be added. Defaults to False. + manufacturer_data (bool, optional): Additonal data that should be added. Defaults to False. + + Returns: + StreamingHttpResponse: Response that can be passed to the endpoint """ if not IsValidBOMFormat(fmt): fmt = 'csv' diff --git a/InvenTree/part/models.py b/InvenTree/part/models.py index 8c8c6eb428..8834756b29 100644 --- a/InvenTree/part/models.py +++ b/InvenTree/part/models.py @@ -1,5 +1,7 @@ """Part database model definitions.""" +from __future__ import annotations + import decimal import hashlib import logging @@ -110,11 +112,14 @@ class PartCategory(MetadataMixin, InvenTreeTree): verbose_name = _("Part Category") verbose_name_plural = _("Part Categories") - def get_parts(self, cascade=True): + def get_parts(self, cascade=True) -> set[Part]: """Return a queryset for all parts under this category. Args: - cascade - If True, also look under subcategories (default = True) + cascade (bool, optional): If True, also look under subcategories. Defaults to True. + + Returns: + set[Part]: All matching parts """ if cascade: """Select any parts which exist in this category or any child categories.""" @@ -129,7 +134,7 @@ class PartCategory(MetadataMixin, InvenTreeTree): return self.partcount() def partcount(self, cascade=True, active=False): - """Return the total part count under this category (including children of child categories)""" + """Return the total part count under this category (including children of child categories).""" query = self.get_parts(cascade=cascade) if active: @@ -1071,8 +1076,9 @@ class Part(MetadataMixin, MPTTModel): @property def net_stock(self): - """Return the 'net' stock. It takes into account: + """Return the 'net' stock. + It takes into account: - Stock on hand (total_stock) - Stock on order (on_order) - Stock allocated (allocation_count) @@ -1370,12 +1376,12 @@ class Part(MetadataMixin, MPTTModel): return queryset.prefetch_related('sub_part') - def get_installed_part_options(self, include_inherited=True, include_variants=True): + def get_installed_part_options(self, include_inherited: bool = True, include_variants: bool = True): """Return a set of all Parts which can be "installed" into this part, based on the BOM. Arguments: - include_inherited - If set, include BomItem entries defined for parent parts - include_variants - If set, include variant parts for BomItems which allow variants + include_inherited (bool): If set, include BomItem entries defined for parent parts + include_variants (bool): If set, include variant parts for BomItems which allow variants """ parts = set() @@ -1480,7 +1486,7 @@ class Part(MetadataMixin, MPTTModel): return str(result_hash.digest()) def is_bom_valid(self): - """Check if the BOM is 'valid' - if the calculated checksum matches the stored value""" + """Check if the BOM is 'valid' - if the calculated checksum matches the stored value.""" return self.get_bom_hash() == self.bom_checksum or not self.has_bom @transaction.atomic @@ -1728,8 +1734,8 @@ class Part(MetadataMixin, MPTTModel): """Create a new price break for this part. Args: - quantity - Numerical quantity - price - Must be a Money object + quantity: Numerical quantity + price: Must be a Money object """ # Check if a price break at that quantity already exists... if self.price_breaks.filter(quantity=quantity, part=self.pk).exists(): @@ -1774,8 +1780,8 @@ class Part(MetadataMixin, MPTTModel): """Copy the BOM from another part. Args: - other - The part to copy the BOM from - clear - Remove existing BOM items first (default=True) + other: The part to copy the BOM from + clear (bool, optional): Remove existing BOM items first. Defaults to True. """ # Ignore if the other part is actually this part? if other == self: @@ -2017,10 +2023,9 @@ class Part(MetadataMixin, MPTTModel): @property def can_convert(self): - """Check if this Part can be "converted" to a different variant: + """Check if this Part can be "converted" to a different variant. It can be converted if: - a) It has non-virtual variant parts underneath it b) It has non-virtual template parts above it c) It has non-virtual sibling variants @@ -2064,8 +2069,9 @@ class Part(MetadataMixin, MPTTModel): return filtered_parts def get_related_parts(self): - """Return list of tuples for all related parts: + """Return list of tuples for all related parts. + Includes: - first value is PartRelated object - second value is matching Part object """ @@ -2408,7 +2414,7 @@ class PartCategoryParameterTemplate(models.Model): ] def __str__(self): - """String representation of a PartCategoryParameterTemplate (admin interface)""" + """String representation of a PartCategoryParameterTemplate (admin interface).""" if self.default_value: return f'{self.category.name} | {self.parameter_template.name} | {self.default_value}' else: @@ -2489,11 +2495,12 @@ class BomItem(models.Model, DataImportMixin): return reverse('api-bom-list') def get_valid_parts_for_allocation(self, allow_variants=True, allow_substitutes=True): - """Return a list of valid parts which can be allocated against this BomItem: + """Return a list of valid parts which can be allocated against this BomItem. - - Include the referenced sub_part - - Include any directly specvified substitute parts - - If allow_variants is True, allow all variants of sub_part + Includes: + - The referenced sub_part + - Any directly specvified substitute parts + - If allow_variants is True, all variants of sub_part """ # Set of parts we will allow parts = set() @@ -2591,10 +2598,9 @@ class BomItem(models.Model, DataImportMixin): ) def get_item_hash(self): - """Calculate the checksum hash of this BOM line item: + """Calculate the checksum hash of this BOM line item. The hash is calculated from the following fields: - - Part.full_name (if the part name changes, the BOM checksum is invalidated) - Quantity - Reference field @@ -2794,8 +2800,9 @@ class BomItemSubstitute(models.Model): super().save(*args, **kwargs) def validate_unique(self, exclude=None): - """Ensure that this BomItemSubstitute is "unique": + """Ensure that this BomItemSubstitute is "unique". + Ensure: - It cannot point to the same "part" as the "sub_part" of the parent "bom_item" """ super().validate_unique(exclude=exclude) @@ -2830,7 +2837,7 @@ class BomItemSubstitute(models.Model): class PartRelated(models.Model): - """Store and handle related parts (eg. mating connector, crimps, etc.)""" + """Store and handle related parts (eg. mating connector, crimps, etc.).""" part_1 = models.ForeignKey(Part, related_name='related_parts_1', verbose_name=_('Part 1'), on_delete=models.DO_NOTHING) diff --git a/InvenTree/plugin/base/barcodes/mixins.py b/InvenTree/plugin/base/barcodes/mixins.py index 371157b2eb..e57d67d5fd 100644 --- a/InvenTree/plugin/base/barcodes/mixins.py +++ b/InvenTree/plugin/base/barcodes/mixins.py @@ -52,7 +52,7 @@ class BarcodeMixin: """Initialize the BarcodePlugin instance. Args: - barcode_data - The raw barcode data + barcode_data: The raw barcode data """ self.data = barcode_data diff --git a/InvenTree/plugin/base/label/label.py b/InvenTree/plugin/base/label/label.py index 6a13e616cd..54dd0db475 100644 --- a/InvenTree/plugin/base/label/label.py +++ b/InvenTree/plugin/base/label/label.py @@ -10,16 +10,17 @@ from plugin.registry import registry logger = logging.getLogger('inventree') -def print_label(plugin_slug, label_image, label_instance=None, user=None): +def print_label(plugin_slug: str, label_image, label_instance=None, user=None): """Print label with the provided plugin. This task is nominally handled by the background worker. - If the printing fails (throws an exception) then the user is notified. - Arguments: - plugin_slug: The unique slug (key) of the plugin - label_image: A PIL.Image image object to be printed + Args: + plugin_slug (str): The unique slug (key) of the plugin + label_image (_type_): A PIL.Image image object to be printed + label_instance (Union[LabelTemplate, None], optional): The template instance that should be printed. Defaults to None. + user (Union[User, None], optional): User that should be informed of errors. Defaults to None. """ logger.info(f"Plugin '{plugin_slug}' is printing a label") diff --git a/InvenTree/plugin/models.py b/InvenTree/plugin/models.py index bd8bd5306e..0bed6ece8d 100644 --- a/InvenTree/plugin/models.py +++ b/InvenTree/plugin/models.py @@ -46,13 +46,13 @@ class MetadataMixin(models.Model): return self.metadata.get(key, backup_value) - def set_metadata(self, key: str, data, commit=True): + def set_metadata(self, key: str, data, commit: bool = True): """Save the provided metadata under the provided key. Args: - key: String key for saving metadata - data: Data object to save - must be able to be rendered as a JSON string - overwrite: If true, existing metadata with the provided key will be overwritten. If false, a merge will be attempted + key (str): Key for saving metadata + data (Any): Data object to save - must be able to be rendered as a JSON string + commit (bool, optional): If true, existing metadata with the provided key will be overwritten. If false, a merge will be attempted. Defaults to True. """ if self.metadata is None: # Handle a null field value diff --git a/InvenTree/stock/models.py b/InvenTree/stock/models.py index 23ccbe7f6b..761ae9aafd 100644 --- a/InvenTree/stock/models.py +++ b/InvenTree/stock/models.py @@ -1,5 +1,7 @@ """Stock database model definitions.""" +from __future__ import annotations + import os from datetime import datetime, timedelta from decimal import Decimal, InvalidOperation @@ -129,8 +131,12 @@ class StockLocation(MetadataMixin, InvenTreeTree): ) @property - def barcode(self): - """Brief payload data (e.g. for labels)""" + def barcode(self) -> str: + """Get Brief payload data (e.g. for labels). + + Returns: + str: Brief pyload data + """ return self.format_barcode(brief=True) def get_stock_items(self, cascade=True): @@ -335,8 +341,9 @@ class StockItem(MetadataMixin, MPTTModel): return None def save(self, *args, **kwargs): - """Save this StockItem to the database. Performs a number of checks: + """Save this StockItem to the database. + Performs a number of checks: - Unique serial number requirement - Adds a transaction note when the item is first created. """ @@ -433,10 +440,9 @@ class StockItem(MetadataMixin, MPTTModel): raise ValidationError({"serial": _("StockItem with this serial number already exists")}) def clean(self): - """Validate the StockItem object (separate to field validation) + """Validate the StockItem object (separate to field validation). The following validation checks are performed: - - The 'part' and 'supplier_part.part' fields cannot point to the same Part object - The 'part' does not belong to itself - Quantity must be 1 if the StockItem has a serial number @@ -558,7 +564,11 @@ class StockItem(MetadataMixin, MPTTModel): @property def barcode(self): - """Brief payload data (e.g. for labels)""" + """Get Brief payload data (e.g. for labels). + + Returns: + str: Brief pyload data + """ return self.format_barcode(brief=True) uid = models.CharField(blank=True, max_length=128, help_text=("Unique identifier field")) @@ -825,8 +835,9 @@ class StockItem(MetadataMixin, MPTTModel): return self.expiry_date < today def clearAllocations(self): - """Clear all order allocations for this StockItem: + """Clear all order allocations for this StockItem. + Clears: - SalesOrder allocations - Build allocations """ @@ -966,8 +977,9 @@ class StockItem(MetadataMixin, MPTTModel): return max(self.quantity - self.allocation_count(), 0) def can_delete(self): - """Can this stock item be deleted? It can NOT be deleted under the following circumstances: + """Can this stock item be deleted? + It can NOT be deleted under the following circumstances: - Has installed stock items - Is installed inside another StockItem - It has been assigned to a SalesOrder @@ -981,13 +993,16 @@ class StockItem(MetadataMixin, MPTTModel): return True - def get_installed_items(self, cascade=False): + def get_installed_items(self, cascade: bool = False) -> set[StockItem]: """Return all stock items which are *installed* in this one! - Args: - cascade - Include items which are installed in items which are installed in items - Note: This function is recursive, and may result in a number of database hits! + + Args: + cascade (bool, optional): Include items which are installed in items which are installed in items. Defaults to False. + + Returns: + set[StockItem]: Sll stock items which are installed """ installed = set() @@ -1157,15 +1172,14 @@ class StockItem(MetadataMixin, MPTTModel): def has_tracking_info(self): return self.tracking_info_count > 0 - def add_tracking_entry(self, entry_type, user, deltas=None, notes='', **kwargs): + def add_tracking_entry(self, entry_type: int, user: User, deltas: dict = None, notes: str = '', **kwargs): """Add a history tracking entry for this StockItem. Args: - entry_type - Integer code describing the "type" of historical action (see StockHistoryCode) - user - The user performing this action - deltas - A map of the changes made to the model - notes - User notes associated with this tracking entry - url - Optional URL associated with this tracking entry + entry_type (int): Code describing the "type" of historical action (see StockHistoryCode) + user (User): The user performing this action + deltas (dict, optional): A map of the changes made to the model. Defaults to None. + notes (str, optional): URL associated with this tracking entry. Defaults to ''. """ if deltas is None: deltas = {} @@ -1915,7 +1929,7 @@ class StockItemAttachment(InvenTreeAttachment): class StockItemTracking(models.Model): - """Stock tracking entry - used for tracking history of a particular StockItem + """Stock tracking entry - used for tracking history of a particular StockItem. Note: 2021-05-11 The legacy StockTrackingItem model contained very litle information about the "history" of the item. diff --git a/InvenTree/users/models.py b/InvenTree/users/models.py index 6346757441..0e990854ca 100644 --- a/InvenTree/users/models.py +++ b/InvenTree/users/models.py @@ -348,12 +348,12 @@ def update_group_roles(group, debug=False): permissions_to_delete = set() def add_model(name, action, allowed): - """Add a new model to the pile: + """Add a new model to the pile. Args: - name - The name of the model e.g. part_part - action - The permission action e.g. view - allowed - Whether or not the action is allowed + name: The name of the model e.g. part_part + action: The permission action e.g. view + allowed: Whether or not the action is allowed """ if action not in ['view', 'add', 'change', 'delete']: # pragma: no cover raise ValueError("Action {a} is invalid".format(a=action)) @@ -400,7 +400,7 @@ def update_group_roles(group, debug=False): """Find the permission object in the database, from the simplified permission string. Args: - permission_string - a simplified permission_string e.g. 'part.view_partcategory' + permission_string: a simplified permission_string e.g. 'part.view_partcategory' Returns the permission object in the database associated with the permission string """ @@ -521,10 +521,11 @@ class Owner(models.Model): @classmethod def get_owners_matching_user(cls, user): - """Return all "owner" objects matching the provided user: + """Return all "owner" objects matching the provided user. - A) An exact match for the user - B) Any groups that the user is a part of + Includes: + - An exact match for the user + - Any groups that the user is a part of """ user_type = ContentType.objects.get(app_label='auth', model='user') group_type = ContentType.objects.get(app_label='auth', model='group')