mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Add missing args to docstrings
This commit is contained in:
parent
ff9873f92c
commit
c24882bf66
@ -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
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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 <bom_item> to <output>
|
||||
"""Return all BuildItem objects which allocate stock of <bom_item> to <output>.
|
||||
|
||||
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:
|
||||
|
@ -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(
|
||||
|
@ -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:
|
||||
|
@ -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)
|
||||
|
@ -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():
|
||||
|
@ -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)
|
||||
"""
|
||||
|
@ -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'
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
||||
|
@ -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")
|
||||
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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')
|
||||
|
Loading…
Reference in New Issue
Block a user