Adds "stock value" calculation to stock table (#4471)

* Add part pricing data to stockitem list serializer

* Add "stock value" column to stock list table
This commit is contained in:
Oliver 2023-03-09 00:18:39 +11:00 committed by GitHub
parent 34875828d7
commit 2edb7f2b55
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 90 additions and 1 deletions

View File

@ -281,6 +281,8 @@ class PartBriefSerializer(InvenTreeModelSerializer):
'trackable',
'virtual',
'units',
'pricing_min',
'pricing_max',
]
read_only_fields = [
@ -289,6 +291,10 @@ class PartBriefSerializer(InvenTreeModelSerializer):
thumbnail = serializers.CharField(source='get_thumbnail_url', read_only=True)
# Pricing fields
pricing_min = InvenTreeMoneySerializer(source='pricing_data.overall_min', allow_null=True, read_only=True)
pricing_max = InvenTreeMoneySerializer(source='pricing_data.overall_max', allow_null=True, read_only=True)
class DuplicatePartSerializer(serializers.Serializer):
"""Serializer for specifying options when duplicating a Part.

View File

@ -165,6 +165,8 @@ class StockItemSerializer(InvenTree.serializers.InvenTreeModelSerializer):
queryset = queryset.prefetch_related(
'sales_order',
'purchase_order',
'part',
'part__pricing_data',
)
# Annotate the queryset with the total allocated to sales orders

View File

@ -136,7 +136,7 @@ function calculateTotalPrice(dataset, value_func, currency_func, options={}) {
var currency = options.currency;
var rates = getCurrencyConversionRates();
var rates = options.rates || getCurrencyConversionRates();
if (!rates) {
console.error('Could not retrieve currency conversion information from the server');

View File

@ -2065,6 +2065,86 @@ function loadStockTable(table, options) {
}
});
// Total value of stock
// This is not sortable, and may default to the 'price range' for the parent part
columns.push({
field: 'stock_value',
title: '{% trans "Stock Value" %}',
sortable: false,
switchable: true,
formatter: function(value, row) {
let min_price = row.purchase_price;
let max_price = row.purchase_price;
let currency = row.purchase_price_currency;
if (min_price == null && max_price == null && row.part_detail) {
min_price = row.part_detail.pricing_min;
max_price = row.part_detail.pricing_max;
currency = baseCurrency();
}
return formatPriceRange(
min_price,
max_price,
{
quantity: row.quantity,
currency: currency
}
);
},
footerFormatter: function(data) {
// Display overall range of value for the selected items
let rates = getCurrencyConversionRates();
let base = baseCurrency();
let min_price = calculateTotalPrice(
data,
function(row) {
return row.quantity * (row.purchase_price || row.part_detail.pricing_min);
},
function(row) {
if (row.purchase_price) {
return row.purchase_price_currency;
} else {
return base;
}
},
{
currency: base,
rates: rates,
raw: true,
}
);
let max_price = calculateTotalPrice(
data,
function(row) {
return row.quantity * (row.purchase_price || row.part_detail.pricing_max);
},
function(row) {
if (row.purchase_price) {
return row.purchase_price_currency;
} else {
return base;
}
},
{
currency: base,
rates: rates,
raw: true,
}
);
return formatPriceRange(
min_price,
max_price,
{
currency: base,
}
);
}
});
columns.push({
field: 'packaging',
title: '{% trans "Packaging" %}',
@ -2085,6 +2165,7 @@ function loadStockTable(table, options) {
name: 'stock',
original: original,
showColumns: true,
showFooter: true,
columns: columns,
});