2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-10-03 15:52:51 +00:00

Improve typing (#10408)

* Improve typing

- build/models.py

* More typing
This commit is contained in:
Oliver
2025-09-27 14:14:50 +10:00
committed by GitHub
parent 6fdc6b3a8c
commit 1279001d8e

View File

@@ -1,6 +1,7 @@
"""Build database model definitions.""" """Build database model definitions."""
import decimal import decimal
from typing import Optional
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
@@ -422,14 +423,14 @@ class Build(
help_text=_('Project code for this build order'), help_text=_('Project code for this build order'),
) )
def sub_builds(self, cascade=True): def sub_builds(self, cascade: bool = True) -> QuerySet:
"""Return all Build Order objects under this one.""" """Return all Build Order objects under this one."""
if cascade: if cascade:
return self.get_descendants(include_self=False) return self.get_descendants(include_self=False)
else: else:
return self.get_children() return self.get_children()
def sub_build_count(self, cascade=True): def sub_build_count(self, cascade: bool = True) -> int:
"""Return the number of sub builds under this one. """Return the number of sub builds under this one.
Args: Args:
@@ -438,14 +439,14 @@ class Build(
return self.sub_builds(cascade=cascade).count() return self.sub_builds(cascade=cascade).count()
@property @property
def has_open_child_builds(self): def has_open_child_builds(self) -> bool:
"""Return True if this build order has any open child builds.""" """Return True if this build order has any open child builds."""
return ( return (
self.sub_builds().filter(status__in=BuildStatusGroups.ACTIVE_CODES).exists() self.sub_builds().filter(status__in=BuildStatusGroups.ACTIVE_CODES).exists()
) )
@property @property
def is_overdue(self): def is_overdue(self) -> bool:
"""Returns true if this build is "overdue". """Returns true if this build is "overdue".
Makes use of the OVERDUE_FILTER to avoid code duplication Makes use of the OVERDUE_FILTER to avoid code duplication
@@ -459,30 +460,30 @@ class Build(
return query.exists() return query.exists()
@property @property
def active(self): def active(self) -> bool:
"""Return True if this build is active.""" """Return True if this build is active."""
return self.status in BuildStatusGroups.ACTIVE_CODES return self.status in BuildStatusGroups.ACTIVE_CODES
@property @property
def tracked_line_items(self): def tracked_line_items(self) -> QuerySet:
"""Returns the "trackable" BOM lines for this BuildOrder.""" """Returns the "trackable" BOM lines for this BuildOrder."""
return self.build_lines.filter(bom_item__sub_part__trackable=True) return self.build_lines.filter(bom_item__sub_part__trackable=True)
def has_tracked_line_items(self): def has_tracked_line_items(self) -> bool:
"""Returns True if this BuildOrder has trackable BomItems.""" """Returns True if this BuildOrder has trackable BomItems."""
return self.tracked_line_items.count() > 0 return self.tracked_line_items.count() > 0
@property @property
def untracked_line_items(self): def untracked_line_items(self) -> bool:
"""Returns the "non trackable" BOM items for this BuildOrder.""" """Returns the "non trackable" BOM items for this BuildOrder."""
return self.build_lines.filter(bom_item__sub_part__trackable=False) return self.build_lines.filter(bom_item__sub_part__trackable=False)
@property @property
def are_untracked_parts_allocated(self): def are_untracked_parts_allocated(self) -> bool:
"""Returns True if all untracked parts are allocated for this BuildOrder.""" """Returns True if all untracked parts are allocated for this BuildOrder."""
return self.is_fully_allocated(tracked=False) return self.is_fully_allocated(tracked=False)
def has_untracked_line_items(self): def has_untracked_line_items(self) -> bool:
"""Returns True if this BuildOrder has non trackable BomItems.""" """Returns True if this BuildOrder has non trackable BomItems."""
return self.has_untracked_line_items.count() > 0 return self.has_untracked_line_items.count() > 0
@@ -492,15 +493,15 @@ class Build(
return max(0, self.quantity - self.completed) return max(0, self.quantity - self.completed)
@property @property
def output_count(self): def output_count(self) -> int:
"""Return the number of build outputs (StockItem) associated with this build order.""" """Return the number of build outputs (StockItem) associated with this build order."""
return self.build_outputs.count() return self.build_outputs.count()
def has_build_outputs(self): def has_build_outputs(self) -> bool:
"""Returns True if this build has more than zero build outputs.""" """Returns True if this build has more than zero build outputs."""
return self.output_count > 0 return self.output_count > 0
def get_build_outputs(self, **kwargs): def get_build_outputs(self, **kwargs) -> QuerySet:
"""Return a list of build outputs. """Return a list of build outputs.
kwargs: kwargs:
@@ -530,7 +531,7 @@ class Build(
return outputs return outputs
@property @property
def complete_outputs(self): def complete_outputs(self) -> bool:
"""Return all the "completed" build outputs.""" """Return all the "completed" build outputs."""
outputs = self.get_build_outputs(complete=True) outputs = self.get_build_outputs(complete=True)
@@ -546,12 +547,12 @@ class Build(
return quantity return quantity
def is_partially_allocated(self): def is_partially_allocated(self) -> bool:
"""Test is this build order has any stock allocated against it.""" """Test is this build order has any stock allocated against it."""
return self.allocated_stock.count() > 0 return self.allocated_stock.count() > 0
@property @property
def incomplete_outputs(self): def incomplete_outputs(self) -> QuerySet:
"""Return all the "incomplete" build outputs.""" """Return all the "incomplete" build outputs."""
outputs = self.get_build_outputs(complete=False) outputs = self.get_build_outputs(complete=False)
@@ -605,7 +606,7 @@ class Build(
return new_ref return new_ref
@property @property
def can_complete(self): def can_complete(self) -> bool:
"""Returns True if this BuildOrder is ready to be completed. """Returns True if this BuildOrder is ready to be completed.
- Must not have any outstanding build outputs - Must not have any outstanding build outputs
@@ -630,7 +631,7 @@ class Build(
return self.is_fully_allocated(tracked=False) return self.is_fully_allocated(tracked=False)
@transaction.atomic @transaction.atomic
def complete_allocations(self, user): def complete_allocations(self, user) -> None:
"""Complete all stock allocations for this build order. """Complete all stock allocations for this build order.
- This function is called when a build order is completed - This function is called when a build order is completed
@@ -752,7 +753,7 @@ class Build(
) )
@property @property
def can_issue(self): def can_issue(self) -> bool:
"""Returns True if this BuildOrder can be issued.""" """Returns True if this BuildOrder can be issued."""
return self.status in [BuildStatus.PENDING.value, BuildStatus.ON_HOLD.value] return self.status in [BuildStatus.PENDING.value, BuildStatus.ON_HOLD.value]
@@ -779,7 +780,7 @@ class Build(
) )
@property @property
def can_hold(self): def can_hold(self) -> bool:
"""Returns True if this BuildOrder can be placed on hold.""" """Returns True if this BuildOrder can be placed on hold."""
return self.status in [BuildStatus.PENDING.value, BuildStatus.PRODUCTION.value] return self.status in [BuildStatus.PENDING.value, BuildStatus.PRODUCTION.value]
@@ -1094,13 +1095,13 @@ class Build(
BuildItem.objects.filter(pk__in=[item.pk for item in items_to_delete]).delete() BuildItem.objects.filter(pk__in=[item.pk for item in items_to_delete]).delete()
@property @property
def allocated_stock(self): def allocated_stock(self) -> QuerySet:
"""Returns a QuerySet object of all BuildItem objects which point back to this Build.""" """Returns a QuerySet object of all BuildItem objects which point back to this Build."""
return BuildItem.objects.filter(build_line__build=self) return BuildItem.objects.filter(build_line__build=self)
@transaction.atomic @transaction.atomic
def subtract_allocated_stock(self, user): def subtract_allocated_stock(self, user) -> None:
"""Called when the Build is marked as "complete", this function removes the allocated untracked items from stock.""" """Removes the allocated untracked items from stock."""
# Find all BuildItem objects which point to this build # Find all BuildItem objects which point to this build
items = self.allocated_stock.filter( items = self.allocated_stock.filter(
build_line__bom_item__sub_part__trackable=False build_line__bom_item__sub_part__trackable=False
@@ -1369,7 +1370,7 @@ class Build(
# Bulk-create the new BuildItem objects # Bulk-create the new BuildItem objects
BuildItem.objects.bulk_create(new_items) BuildItem.objects.bulk_create(new_items)
def unallocated_lines(self, tracked=None): def unallocated_lines(self, tracked: Optional[bool] = None) -> QuerySet:
"""Returns a list of BuildLine objects which have not been fully allocated.""" """Returns a list of BuildLine objects which have not been fully allocated."""
lines = self.build_lines.all() lines = self.build_lines.all()
@@ -1390,11 +1391,9 @@ class Build(
return lines return lines
def is_fully_allocated(self, tracked=None): def is_fully_allocated(self, tracked: Optional[bool] = None) -> bool:
"""Test if the BuildOrder has been fully allocated. """Test if the BuildOrder has been fully allocated.
This is *true* if *all* associated BuildLine items have sufficient allocation
Arguments: Arguments:
tracked: If True, only consider tracked BuildLine items. If False, only consider untracked BuildLine items. tracked: If True, only consider tracked BuildLine items. If False, only consider untracked BuildLine items.
@@ -1403,10 +1402,10 @@ class Build(
""" """
return self.unallocated_lines(tracked=tracked).count() == 0 return self.unallocated_lines(tracked=tracked).count() == 0
def is_output_fully_allocated(self, output): def is_output_fully_allocated(self, output) -> bool:
"""Determine if the specified output (StockItem) has been fully allocated for this build. """Determine if the specified output (StockItem) has been fully allocated for this build.
Args: Arguments:
output: StockItem object (the "in production" output to test against) output: StockItem object (the "in production" output to test against)
To determine if the output has been fully allocated, To determine if the output has been fully allocated,
@@ -1431,7 +1430,7 @@ class Build(
# At this stage, we can assume that the output is fully allocated # At this stage, we can assume that the output is fully allocated
return True return True
def is_overallocated(self): def is_overallocated(self) -> bool:
"""Test if the BuildOrder has been over-allocated. """Test if the BuildOrder has been over-allocated.
Returns: Returns:
@@ -1450,7 +1449,7 @@ class Build(
return lines.count() > 0 return lines.count() > 0
@property @property
def is_active(self): def is_active(self) -> bool:
"""Is this build active? """Is this build active?
An active build is either: An active build is either:
@@ -1460,12 +1459,12 @@ class Build(
return self.status in BuildStatusGroups.ACTIVE_CODES return self.status in BuildStatusGroups.ACTIVE_CODES
@property @property
def is_complete(self): def is_complete(self) -> bool:
"""Returns True if the build status is COMPLETE.""" """Returns True if the build status is COMPLETE."""
return self.status == BuildStatus.COMPLETE.value return self.status == BuildStatus.COMPLETE.value
@transaction.atomic @transaction.atomic
def create_build_line_items(self, prevent_duplicates=True): def create_build_line_items(self, prevent_duplicates: bool = True) -> None:
"""Create BuildLine objects for each BOM line in this BuildOrder.""" """Create BuildLine objects for each BOM line in this BuildOrder."""
lines = [] lines = []
@@ -1500,7 +1499,7 @@ class Build(
logger.info('Created %s BuildLine objects for BuildOrder', len(lines)) logger.info('Created %s BuildLine objects for BuildOrder', len(lines))
@transaction.atomic @transaction.atomic
def update_build_line_items(self): def update_build_line_items(self) -> None:
"""Rebuild required quantity field for each BuildLine object.""" """Rebuild required quantity field for each BuildLine object."""
lines_to_update = [] lines_to_update = []
@@ -1660,7 +1659,7 @@ class BuildLine(report.mixins.InvenTreeReportMixin, InvenTree.models.InvenTreeMo
""" """
return max(self.quantity - self.consumed - self.allocated_quantity(), 0) return max(self.quantity - self.consumed - self.allocated_quantity(), 0)
def is_fully_allocated(self): def is_fully_allocated(self) -> bool:
"""Return True if this BuildLine is fully allocated.""" """Return True if this BuildLine is fully allocated."""
if self.bom_item.consumable: if self.bom_item.consumable:
return True return True
@@ -1675,7 +1674,7 @@ class BuildLine(report.mixins.InvenTreeReportMixin, InvenTree.models.InvenTreeMo
return self.allocated_quantity() > required return self.allocated_quantity() > required
def is_fully_consumed(self): def is_fully_consumed(self) -> bool:
"""Return True if this BuildLine is fully consumed.""" """Return True if this BuildLine is fully consumed."""
return self.consumed >= self.quantity return self.consumed >= self.quantity
@@ -1846,7 +1845,7 @@ class BuildItem(InvenTree.models.InvenTreeMetadataModel):
return self.build_line.bom_item if self.build_line else None return self.build_line.bom_item if self.build_line else None
@transaction.atomic @transaction.atomic
def complete_allocation(self, quantity=None, notes='', user=None): def complete_allocation(self, quantity=None, notes='', user=None) -> None:
"""Complete the allocation of this BuildItem into the output stock item. """Complete the allocation of this BuildItem into the output stock item.
Arguments: Arguments: