diff --git a/InvenTree/part/templates/part/prices.html b/InvenTree/part/templates/part/prices.html
index b1403b3157..9c216eb534 100644
--- a/InvenTree/part/templates/part/prices.html
+++ b/InvenTree/part/templates/part/prices.html
@@ -281,7 +281,15 @@
- PLACEHOLDER FOR SALE HISTORY
+ {% if sale_history|length > 0 %}
+
+
+
+ {% else %}
+
+ {% trans 'No sale pice history available for this part.' %}
+
+ {% endif %}
{% endif %}
@@ -444,4 +452,35 @@
);
{% endif %}
+ // Sale price history
+ {% if sale_history %}
+ var salepricedata = {
+ labels: [
+ {% for line in sale_history %}'{{ line.date }}',{% endfor %}
+ ],
+ datasets: [{
+ label: '{% blocktrans %}Unit Price - {{currency}}{% endblocktrans %}',
+ backgroundColor: 'rgba(255, 99, 132, 0.2)',
+ borderColor: 'rgb(255, 99, 132)',
+ yAxisID: 'y',
+ data: [
+ {% for line in sale_history %}{{ line.price|stringformat:".2f" }},{% endfor %}
+ ],
+ borderWidth: 1,
+ },
+ {
+ label: '{% trans "Quantity" %}',
+ backgroundColor: 'rgba(255, 206, 86, 0.2)',
+ borderColor: 'rgb(255, 206, 86)',
+ yAxisID: 'y1',
+ data: [
+ {% for line in sale_history %}{{ line.qty|stringformat:"f" }},{% endfor %}
+ ],
+ borderWidth: 1,
+ type: 'bar',
+ }]
+ }
+ var SalePriceChart = loadSellPricingChart($('#SalePriceChart'), salepricedata)
+ {% endif %}
+
{% endblock %}
diff --git a/InvenTree/part/views.py b/InvenTree/part/views.py
index 7a683a29b5..30e95535d6 100644
--- a/InvenTree/part/views.py
+++ b/InvenTree/part/views.py
@@ -50,6 +50,7 @@ import common.settings as inventree_settings
from . import forms as part_forms
from .bom import MakeBomTemplate, BomUploadManager, ExportBom, IsValidBOMFormat
+from order.models import PurchaseOrderLineItem
from .admin import PartResource
@@ -1038,6 +1039,36 @@ class PartPricingView(PartDetail):
# add to global context
ctx['bom_parts'] = ctx_bom_parts
+ # Sale price history
+ sale_items = PurchaseOrderLineItem.objects.filter(part__part=part).order_by('order__issue_date').\
+ prefetch_related('order', ).all()
+
+ if sale_items:
+ sale_history = []
+
+ for sale_item in sale_items:
+ # check for not fully defined elements
+ if None in [sale_item.purchase_price, sale_item.quantity]:
+ continue
+
+ price = convert_money(sale_item.purchase_price, default_currency)
+ line = {
+ 'price': price.amount if price else 0,
+ 'qty': sale_item.quantity,
+ }
+
+ # set date for graph labels
+ if sale_item.order.issue_date:
+ line['date'] = sale_item.order.issue_date.strftime('%d.%m.%Y')
+ elif sale_item.order.creation_date:
+ line['date'] = sale_item.order.creation_date.strftime('%d.%m.%Y')
+ else:
+ line['date'] = _('None')
+
+ sale_history.append(line)
+
+ ctx['sale_history'] = sale_history
+
return ctx
def get_initials(self):
diff --git a/InvenTree/templates/js/part.js b/InvenTree/templates/js/part.js
index 888f1d245a..7fa63098e1 100644
--- a/InvenTree/templates/js/part.js
+++ b/InvenTree/templates/js/part.js
@@ -977,3 +977,36 @@ function loadBomChart(context, data) {
}
});
}
+
+function loadSellPricingChart(context, data) {
+ return new Chart(context, {
+ type: 'line',
+ data: data,
+ options: {
+ responsive: true,
+ maintainAspectRatio: false,
+ plugins: {legend: {position: 'bottom'}},
+ scales: {
+ y: {
+ type: 'linear',
+ position: 'left',
+ grid: {display: false},
+ title: {
+ display: true,
+ text: '{% trans "Unit Price" %}'
+ }
+ },
+ y1: {
+ type: 'linear',
+ position: 'right',
+ grid: {display: false},
+ titel: {
+ display: true,
+ text: '{% trans "Quantity" %}',
+ position: 'right'
+ }
+ },
+ },
+ }
+ });
+}