Skip to content

Commit

Permalink
[PUI] Sales order actions (#7837)
Browse files Browse the repository at this point in the history
* Create build order from sales order table

* Allow creation of child build order from build page

* Add production and purcahse order quantitres to sales order item serializer

* Bump API version

* Fix playwright test
  • Loading branch information
SchrodingersGat authored Aug 8, 2024
1 parent a556409 commit 21f623e
Show file tree
Hide file tree
Showing 10 changed files with 173 additions and 80 deletions.
6 changes: 5 additions & 1 deletion src/backend/InvenTree/InvenTree/api_version.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
"""InvenTree API version information."""

# InvenTree API version
INVENTREE_API_VERSION = 234
INVENTREE_API_VERSION = 235

"""Increment this API version number whenever there is a significant change to the API that any clients need to know about."""


INVENTREE_API_TEXT = """
v235 - 2024-08-08 : https://github.com/inventree/InvenTree/pull/7837
- Adds "on_order" quantity to SalesOrderLineItem serializer
- Adds "building" quantity to SalesOrderLineItem serializer
v234 - 2024-08-08 : https://github.com/inventree/InvenTree/pull/7829
- Fixes bug in the plugin metadata endpoint
Expand Down
36 changes: 26 additions & 10 deletions src/backend/InvenTree/order/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
from sql_util.utils import SubqueryCount

import order.models
import part.filters
import part.filters as part_filters
import part.models as part_models
import stock.models
Expand Down Expand Up @@ -1030,8 +1029,6 @@ class Meta:
'pk',
'allocated',
'allocations',
'available_stock',
'available_variant_stock',
'customer_detail',
'quantity',
'reference',
Expand All @@ -1046,6 +1043,11 @@ class Meta:
'shipped',
'target_date',
'link',
# Annotated fields for part stocking information
'available_stock',
'available_variant_stock',
'building',
'on_order',
]

def __init__(self, *args, **kwargs):
Expand Down Expand Up @@ -1078,6 +1080,8 @@ def annotate_queryset(queryset):
- "overdue" status (boolean field)
- "available_quantity"
- "building"
- "on_order"
"""
queryset = queryset.annotate(
overdue=Case(
Expand All @@ -1093,11 +1097,11 @@ def annotate_queryset(queryset):
# Annotate each line with the available stock quantity
# To do this, we need to look at the total stock and any allocations
queryset = queryset.alias(
total_stock=part.filters.annotate_total_stock(reference='part__'),
allocated_to_sales_orders=part.filters.annotate_sales_order_allocations(
total_stock=part_filters.annotate_total_stock(reference='part__'),
allocated_to_sales_orders=part_filters.annotate_sales_order_allocations(
reference='part__'
),
allocated_to_build_orders=part.filters.annotate_build_order_allocations(
allocated_to_build_orders=part_filters.annotate_build_order_allocations(
reference='part__'
),
)
Expand All @@ -1112,19 +1116,19 @@ def annotate_queryset(queryset):
)

# Filter for "variant" stock: Variant stock items must be salable and active
variant_stock_query = part.filters.variant_stock_query(
variant_stock_query = part_filters.variant_stock_query(
reference='part__'
).filter(part__salable=True, part__active=True)

# Also add in available "variant" stock
queryset = queryset.alias(
variant_stock_total=part.filters.annotate_variant_quantity(
variant_stock_total=part_filters.annotate_variant_quantity(
variant_stock_query, reference='quantity'
),
variant_bo_allocations=part.filters.annotate_variant_quantity(
variant_bo_allocations=part_filters.annotate_variant_quantity(
variant_stock_query, reference='sales_order_allocations__quantity'
),
variant_so_allocations=part.filters.annotate_variant_quantity(
variant_so_allocations=part_filters.annotate_variant_quantity(
variant_stock_query, reference='allocations__quantity'
),
)
Expand All @@ -1138,6 +1142,16 @@ def annotate_queryset(queryset):
)
)

# Add information about the quantity of parts currently on order
queryset = queryset.annotate(
on_order=part_filters.annotate_on_order_quantity(reference='part__')
)

# Add information about the quantity of parts currently in production
queryset = queryset.annotate(
building=part_filters.annotate_in_production_quantity(reference='part__')
)

return queryset

customer_detail = CompanyBriefSerializer(
Expand All @@ -1153,6 +1167,8 @@ def annotate_queryset(queryset):
overdue = serializers.BooleanField(required=False, read_only=True)
available_stock = serializers.FloatField(read_only=True)
available_variant_stock = serializers.FloatField(read_only=True)
on_order = serializers.FloatField(label=_('On Order'), read_only=True)
building = serializers.FloatField(label=_('In Production'), read_only=True)

quantity = InvenTreeDecimalField()

Expand Down
63 changes: 28 additions & 35 deletions src/backend/InvenTree/part/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
import InvenTree.helpers
import InvenTree.serializers
import InvenTree.status
import part.filters
import part.filters as part_filters
import part.helpers as part_helpers
import part.stocktake
import part.tasks
Expand Down Expand Up @@ -107,12 +107,12 @@ def annotate_queryset(queryset):
"""Annotate extra information to the queryset."""
# Annotate the number of 'parts' which exist in each category (including subcategories!)
queryset = queryset.annotate(
part_count=part.filters.annotate_category_parts(),
subcategories=part.filters.annotate_sub_categories(),
part_count=part_filters.annotate_category_parts(),
subcategories=part_filters.annotate_sub_categories(),
)

queryset = queryset.annotate(
parent_default_location=part.filters.annotate_default_location('parent__')
parent_default_location=part_filters.annotate_default_location('parent__')
)

return queryset
Expand Down Expand Up @@ -164,7 +164,7 @@ class Meta:
@staticmethod
def annotate_queryset(queryset):
"""Annotate the queryset with the number of subcategories."""
return queryset.annotate(subcategories=part.filters.annotate_sub_categories())
return queryset.annotate(subcategories=part_filters.annotate_sub_categories())


@register_importer()
Expand Down Expand Up @@ -781,10 +781,10 @@ def annotate_queryset(queryset):
queryset = queryset.annotate(stock_item_count=SubqueryCount('stock_items'))

# Annotate with the total variant stock quantity
variant_query = part.filters.variant_stock_query()
variant_query = part_filters.variant_stock_query()

queryset = queryset.annotate(
variant_stock=part.filters.annotate_variant_quantity(
variant_stock=part_filters.annotate_variant_quantity(
variant_query, reference='quantity'
)
)
Expand Down Expand Up @@ -814,10 +814,10 @@ def annotate_queryset(queryset):
# TODO: Note that BomItemSerializer and BuildLineSerializer have very similar code

queryset = queryset.annotate(
ordering=part.filters.annotate_on_order_quantity(),
in_stock=part.filters.annotate_total_stock(),
allocated_to_sales_orders=part.filters.annotate_sales_order_allocations(),
allocated_to_build_orders=part.filters.annotate_build_order_allocations(),
ordering=part_filters.annotate_on_order_quantity(),
in_stock=part_filters.annotate_total_stock(),
allocated_to_sales_orders=part_filters.annotate_sales_order_allocations(),
allocated_to_build_orders=part_filters.annotate_build_order_allocations(),
)

# Annotate the queryset with the 'total_in_stock' quantity
Expand All @@ -829,7 +829,7 @@ def annotate_queryset(queryset):
)

queryset = queryset.annotate(
external_stock=part.filters.annotate_total_stock(
external_stock=part_filters.annotate_total_stock(
filter=Q(location__external=True)
)
)
Expand All @@ -847,12 +847,12 @@ def annotate_queryset(queryset):

# Annotate with the total 'required for builds' quantity
queryset = queryset.annotate(
required_for_build_orders=part.filters.annotate_build_order_requirements(),
required_for_sales_orders=part.filters.annotate_sales_order_requirements(),
required_for_build_orders=part_filters.annotate_build_order_requirements(),
required_for_sales_orders=part_filters.annotate_sales_order_requirements(),
)

queryset = queryset.annotate(
category_default_location=part.filters.annotate_default_location(
category_default_location=part_filters.annotate_default_location(
'category__'
)
)
Expand Down Expand Up @@ -1684,30 +1684,23 @@ def annotate_queryset(queryset):

# Annotate with the total "on order" amount for the sub-part
queryset = queryset.annotate(
on_order=part.filters.annotate_on_order_quantity(ref)
on_order=part_filters.annotate_on_order_quantity(ref)
)

# Annotate with the total "building" amount for the sub-part
queryset = queryset.annotate(
building=Coalesce(
SubquerySum(
'sub_part__builds__quantity',
filter=Q(status__in=BuildStatusGroups.ACTIVE_CODES),
),
Decimal(0),
output_field=models.DecimalField(),
)
building=part_filters.annotate_in_production_quantity(ref)
)

# Calculate "total stock" for the referenced sub_part
# Calculate the "build_order_allocations" for the sub_part
# Note that these fields are only aliased, not annotated
queryset = queryset.alias(
total_stock=part.filters.annotate_total_stock(reference=ref),
allocated_to_sales_orders=part.filters.annotate_sales_order_allocations(
total_stock=part_filters.annotate_total_stock(reference=ref),
allocated_to_sales_orders=part_filters.annotate_sales_order_allocations(
reference=ref
),
allocated_to_build_orders=part.filters.annotate_build_order_allocations(
allocated_to_build_orders=part_filters.annotate_build_order_allocations(
reference=ref
),
)
Expand All @@ -1724,7 +1717,7 @@ def annotate_queryset(queryset):

# Calculate 'external_stock'
queryset = queryset.annotate(
external_stock=part.filters.annotate_total_stock(
external_stock=part_filters.annotate_total_stock(
reference=ref, filter=Q(location__external=True)
)
)
Expand All @@ -1733,11 +1726,11 @@ def annotate_queryset(queryset):

# Extract similar information for any 'substitute' parts
queryset = queryset.alias(
substitute_stock=part.filters.annotate_total_stock(reference=ref),
substitute_build_allocations=part.filters.annotate_build_order_allocations(
substitute_stock=part_filters.annotate_total_stock(reference=ref),
substitute_build_allocations=part_filters.annotate_build_order_allocations(
reference=ref
),
substitute_sales_allocations=part.filters.annotate_sales_order_allocations(
substitute_sales_allocations=part_filters.annotate_sales_order_allocations(
reference=ref
),
)
Expand All @@ -1753,16 +1746,16 @@ def annotate_queryset(queryset):
)

# Annotate the queryset with 'available variant stock' information
variant_stock_query = part.filters.variant_stock_query(reference='sub_part__')
variant_stock_query = part_filters.variant_stock_query(reference='sub_part__')

queryset = queryset.alias(
variant_stock_total=part.filters.annotate_variant_quantity(
variant_stock_total=part_filters.annotate_variant_quantity(
variant_stock_query, reference='quantity'
),
variant_bo_allocations=part.filters.annotate_variant_quantity(
variant_bo_allocations=part_filters.annotate_variant_quantity(
variant_stock_query, reference='sales_order_allocations__quantity'
),
variant_so_allocations=part.filters.annotate_variant_quantity(
variant_so_allocations=part_filters.annotate_variant_quantity(
variant_stock_query, reference='allocations__quantity'
),
)
Expand Down
6 changes: 1 addition & 5 deletions src/frontend/src/pages/build/BuildDetail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -247,11 +247,7 @@ export default function BuildDetail() {
label: t`Line Items`,
icon: <IconListNumbers />,
content: build?.pk ? (
<BuildLineTable
params={{
build: id
}}
/>
<BuildLineTable buildId={build.pk} />
) : (
<Skeleton />
)
Expand Down
2 changes: 1 addition & 1 deletion src/frontend/src/pages/part/PartDetail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -543,7 +543,7 @@ export default function PartDetail() {
label: t`Variants`,
icon: <IconVersions />,
hidden: !part.is_template,
content: <PartVariantTable partId={String(id)} />
content: <PartVariantTable part={part} />
},
{
name: 'allocations',
Expand Down
Loading

0 comments on commit 21f623e

Please sign in to comment.