From 093a1817518916220722978c74302c92e03e1698 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 23 Jun 2021 01:07:07 +0200 Subject: [PATCH 01/19] initial structure for single pricing view --- InvenTree/part/templates/part/navbar.html | 8 +- InvenTree/part/templates/part/prices.html | 490 ++++++++++++++++++++++ InvenTree/part/urls.py | 2 +- 3 files changed, 495 insertions(+), 5 deletions(-) create mode 100644 InvenTree/part/templates/part/prices.html diff --git a/InvenTree/part/templates/part/navbar.html b/InvenTree/part/templates/part/navbar.html index c0bc4c96a3..b123c458e9 100644 --- a/InvenTree/part/templates/part/navbar.html +++ b/InvenTree/part/templates/part/navbar.html @@ -70,13 +70,13 @@ {% endif %} - {% if part.purchaseable and roles.purchase_order.view %} -
  • - +
  • + - {% trans "Order Price" %} + {% trans "Prices" %}
  • + {% if part.purchaseable and roles.purchase_order.view %}
  • diff --git a/InvenTree/part/templates/part/prices.html b/InvenTree/part/templates/part/prices.html new file mode 100644 index 0000000000..e71179284a --- /dev/null +++ b/InvenTree/part/templates/part/prices.html @@ -0,0 +1,490 @@ +{% extends "part/part_base.html" %} +{% load static %} +{% load i18n %} +{% load crispy_forms_tags %} +{% load inventree_extras %} + +{% block menubar %} +{% include 'part/navbar.html' with tab='prices' %} +{% endblock %} + +{% block heading %} +{% trans "General Price Information" %} +{% endblock %} + + +{% block details %} +{% default_currency as currency %} + +
    +
    +

    {% trans "Pricing ranges" %}

    + + {% if part.supplier_count > 0 %} + {% if min_total_buy_price %} + + + + + + + {% if quantity > 1 %} + + + + + + + {% endif %} + {% else %} + + + + {% endif %} + {% endif %} + + {% if part.bom_count > 0 %} + {% if min_total_bom_price %} + + + + + + + {% if quantity > 1 %} + + + + + + + {% endif %} + {% if part.has_complete_bom_pricing == False %} + + + + {% endif %} + {% else %} + + + + {% endif %} + {% endif %} + + {% if show_internal_price and roles.sales_order.view %} + {% if total_internal_part_price %} + + + + + + + + + + + {% endif %} + {% endif %} + + {% if total_part_price %} + + + + + + + + + + + {% endif %} +
    {% trans 'Supplier Pricing' %}{% trans 'Unit Cost' %}Min: {% include "price.html" with price=min_unit_buy_price %}Max: {% include "price.html" with price=max_unit_buy_price %}
    {% trans 'Total Cost' %}Min: {% include "price.html" with price=min_total_buy_price %}Max: {% include "price.html" with price=max_total_buy_price %}
    + {% trans 'No supplier pricing available' %} +
    {% trans 'BOM Pricing' %}{% trans 'Unit Cost' %}Min: {% include "price.html" with price=min_unit_bom_price %}Max: {% include "price.html" with price=max_unit_bom_price %}
    {% trans 'Total Cost' %}Min: {% include "price.html" with price=min_total_bom_price %}Max: {% include "price.html" with price=max_total_bom_price %}
    + {% trans 'Note: BOM pricing is incomplete for this part' %} +
    + {% trans 'No BOM pricing available' %} +
    {% trans 'Internal Price' %}{% trans 'Unit Cost' %}{% include "price.html" with price=unit_internal_part_price %}
    {% trans 'Total Cost' %}{% include "price.html" with price=total_internal_part_price %}
    {% trans 'Sale Price' %}{% trans 'Unit Cost' %}{% include "price.html" with price=unit_part_price %}
    {% trans 'Total Cost' %}{% include "price.html" with price=total_part_price %}
    + + {% if min_unit_buy_price or min_unit_bom_price %} + {% else %} +
    + {% trans 'No pricing information is available for this part.' %} +
    + {% endif %} +
    + +
    +
    + {% csrf_token %} + {{ form|crispy }} + +
    +
    +
    +{% endblock %} + +{% block post_content_panel %} +{% default_currency as currency %} +{% settings_value "PART_INTERNAL_PRICE" as show_internal_price %} + + +{% if part.purchaseable and roles.purchase_order.view %} +
    +
    +

    {% trans "Supplier Cost" %}

    +
    + +
    + PLACEHOLDER FOR SUPPLIER COST TABLE +
    +
    + + +
    +
    +

    {% trans "Purchase Price" %}

    +
    + + {% if price_history %} +

    {% trans 'Stock Pricing' %}

    + {% if price_history|length > 0 %} +
    + +
    + {% else %} +
    + {% trans 'No stock pricing history is available for this part.' %} +
    + {% endif %} + {% endif %} +
    +{% endif %} + + +{% if show_internal_price and roles.sales_order.view %} +
    +
    +

    {% trans "Internal Cost" %}

    +
    + +
    +
    + +
    + + +
    +
    +
    +{% endif %} + + +
    +
    +

    {% trans "BOM Cost" %}

    +
    + +
    +
    + PLACEHOLDER FOR BOM ITEM COST TABLE +
    +
    + + {% if part.bom_count > 0 %} +
    +

    {% trans 'BOM Pricing' %}

    +
    + +
    +
    + {% endif %} +
    +
    + +{% if part.salable and roles.sales_order.view %} +
    +
    +

    {% trans "Sale Cost" %}

    +
    + +
    +
    + +
    + + +
    +
    +
    + +
    +
    +

    {% trans "Sale Price" %}

    +
    + +
    + PLACEHOLDER FOR SALE HISTORY +
    +
    +{% endif %} + +{% endblock %} + + + +{% block js_ready %} + {{ block.super }} + + // history graphs + {% default_currency as currency %} + {% if price_history %} + var pricedata = { + labels: [ + {% for line in price_history %}'{{ line.date }}',{% endfor %} + ], + datasets: [{ + label: '{% blocktrans %}Single Price - {{currency}}{% endblocktrans %}', + backgroundColor: 'rgba(255, 99, 132, 0.2)', + borderColor: 'rgb(255, 99, 132)', + yAxisID: 'y', + data: [ + {% for line in price_history %}{{ line.price|stringformat:".2f" }},{% endfor %} + ], + borderWidth: 1, + type: 'line' + }, + {% if 'price_diff' in price_history.0 %} + { + label: '{% blocktrans %}Single Price Difference - {{currency}}{% endblocktrans %}', + backgroundColor: 'rgba(68, 157, 68, 0.2)', + borderColor: 'rgb(68, 157, 68)', + yAxisID: 'y2', + data: [ + {% for line in price_history %}{{ line.price_diff|stringformat:".2f" }},{% endfor %} + ], + borderWidth: 1, + type: 'line', + hidden: true, + }, + { + label: '{% blocktrans %}Part Single Price - {{currency}}{% endblocktrans %}', + backgroundColor: 'rgba(70, 127, 155, 0.2)', + borderColor: 'rgb(70, 127, 155)', + yAxisID: 'y', + data: [ + {% for line in price_history %}{{ line.price_part|stringformat:".2f" }},{% endfor %} + ], + borderWidth: 1, + type: 'line', + hidden: true, + }, + {% endif %} + { + label: '{% trans "Quantity" %}', + backgroundColor: 'rgba(255, 206, 86, 0.2)', + borderColor: 'rgb(255, 206, 86)', + yAxisID: 'y1', + data: [ + {% for line in price_history %}{{ line.qty|stringformat:"f" }},{% endfor %} + ], + borderWidth: 1 + }] + } + var StockPriceChart = loadStockPricingChart(document.getElementById('StockPriceChart'), pricedata) + var bom_colors = randomColor({hue: 'green', count: {{ bom_parts|length }} }) + var bomdata = { + labels: [{% for line in bom_parts %}'{{ line.name }}',{% endfor %}], + datasets: [ + { + label: 'Price', + data: [{% for line in bom_parts %}{{ line.min_price }},{% endfor %}], + backgroundColor: bom_colors, + }, + {% if bom_pie_max %} + { + label: 'Max Price', + data: [{% for line in bom_parts %}{{ line.max_price }},{% endfor %}], + backgroundColor: bom_colors, + }, + {% endif %} + ] + }; + var BomChart = loadBomChart(document.getElementById('BomChart'), bomdata) + + {% endif %} + + + // Internal pricebreaks + {% settings_value "PART_INTERNAL_PRICE" as show_internal_price %} + {% if show_internal_price and roles.sales_order.view %} + function reloadPriceBreaks() { + $("#internal-price-break-table").bootstrapTable("refresh"); + } + + $('#new-internal-price-break').click(function() { + launchModalForm("{% url 'internal-price-break-create' %}", + { + success: reloadPriceBreaks, + data: { + part: {{ part.id }}, + } + } + ); + }); + + $('#internal-price-break-table').inventreeTable({ + name: 'internalprice', + formatNoMatches: function() { return "{% trans 'No internal price break information found' %}"; }, + queryParams: { + part: {{ part.id }}, + }, + url: "{% url 'api-part-internal-price-list' %}", + onPostBody: function() { + var table = $('#internal-price-break-table'); + + table.find('.button-internal-price-break-delete').click(function() { + var pk = $(this).attr('pk'); + + launchModalForm( + `/part/internal-price/${pk}/delete/`, + { + success: reloadPriceBreaks + } + ); + }); + + table.find('.button-internal-price-break-edit').click(function() { + var pk = $(this).attr('pk'); + + launchModalForm( + `/part/internal-price/${pk}/edit/`, + { + success: reloadPriceBreaks + } + ); + }); + }, + columns: [ + { + field: 'pk', + title: 'ID', + visible: false, + switchable: false, + }, + { + field: 'quantity', + title: '{% trans "Quantity" %}', + sortable: true, + }, + { + field: 'price', + title: '{% trans "Price" %}', + sortable: true, + formatter: function(value, row, index) { + var html = value; + + html += `
    ` + + html += makeIconButton('fa-edit icon-blue', 'button-internal-price-break-edit', row.pk, '{% trans "Edit internal price break" %}'); + html += makeIconButton('fa-trash-alt icon-red', 'button-internal-price-break-delete', row.pk, '{% trans "Delete internal price break" %}'); + + html += `
    `; + + return html; + } + }, + ] + }) + {% endif %} + + + // Sales pricebreaks + {% if part.salable and roles.sales_order.view %} + function reloadPriceBreaks() { + $("#price-break-table").bootstrapTable("refresh"); + } + + $('#new-price-break').click(function() { + launchModalForm("{% url 'sale-price-break-create' %}", + { + success: reloadPriceBreaks, + data: { + part: {{ part.id }}, + } + } + ); + }); + + $('#price-break-table').inventreeTable({ + name: 'saleprice', + formatNoMatches: function() { return "{% trans 'No price break information found' %}"; }, + queryParams: { + part: {{ part.id }}, + }, + url: "{% url 'api-part-sale-price-list' %}", + onPostBody: function() { + var table = $('#price-break-table'); + + table.find('.button-price-break-delete').click(function() { + var pk = $(this).attr('pk'); + + launchModalForm( + `/part/sale-price/${pk}/delete/`, + { + success: reloadPriceBreaks + } + ); + }); + + table.find('.button-price-break-edit').click(function() { + var pk = $(this).attr('pk'); + + launchModalForm( + `/part/sale-price/${pk}/edit/`, + { + success: reloadPriceBreaks + } + ); + }); + }, + columns: [ + { + field: 'pk', + title: 'ID', + visible: false, + switchable: false, + }, + { + field: 'quantity', + title: '{% trans "Quantity" %}', + sortable: true, + }, + { + field: 'price', + title: '{% trans "Price" %}', + sortable: true, + formatter: function(value, row, index) { + var html = value; + + html += `
    ` + + html += makeIconButton('fa-edit icon-blue', 'button-price-break-edit', row.pk, '{% trans "Edit price break" %}'); + html += makeIconButton('fa-trash-alt icon-red', 'button-price-break-delete', row.pk, '{% trans "Delete price break" %}'); + + html += `
    `; + + return html; + } + }, + ] + }) + + {% endif %} + +{% endblock %} diff --git a/InvenTree/part/urls.py b/InvenTree/part/urls.py index d12612c604..7bd58f52f2 100644 --- a/InvenTree/part/urls.py +++ b/InvenTree/part/urls.py @@ -65,7 +65,7 @@ part_detail_urls = [ url(r'^bom/?', views.PartDetail.as_view(template_name='part/bom.html'), name='part-bom'), url(r'^build/?', views.PartDetail.as_view(template_name='part/build.html'), name='part-build'), url(r'^used/?', views.PartDetail.as_view(template_name='part/used_in.html'), name='part-used-in'), - url(r'^order-prices/', views.PartPricingView.as_view(template_name='part/order_prices.html'), name='part-order-prices'), + url(r'^prices/', views.PartPricingView.as_view(template_name='part/prices.html'), name='part-prices'), url(r'^manufacturers/?', views.PartDetail.as_view(template_name='part/manufacturer.html'), name='part-manufacturers'), url(r'^suppliers/?', views.PartDetail.as_view(template_name='part/supplier.html'), name='part-suppliers'), url(r'^orders/?', views.PartDetail.as_view(template_name='part/orders.html'), name='part-orders'), From c8ff6ee0e248959063c37204bef5f62adcaf9131 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 23 Jun 2021 01:11:25 +0200 Subject: [PATCH 02/19] removing old views --- .../part/templates/part/internal_prices.html | 122 --------- InvenTree/part/templates/part/navbar.html | 14 +- .../part/templates/part/order_prices.html | 235 ------------------ .../part/templates/part/sale_prices.html | 108 -------- InvenTree/part/urls.py | 2 - 5 files changed, 1 insertion(+), 480 deletions(-) delete mode 100644 InvenTree/part/templates/part/internal_prices.html delete mode 100644 InvenTree/part/templates/part/order_prices.html delete mode 100644 InvenTree/part/templates/part/sale_prices.html diff --git a/InvenTree/part/templates/part/internal_prices.html b/InvenTree/part/templates/part/internal_prices.html deleted file mode 100644 index 2f54f3bb64..0000000000 --- a/InvenTree/part/templates/part/internal_prices.html +++ /dev/null @@ -1,122 +0,0 @@ -{% extends "part/part_base.html" %} -{% load static %} -{% load i18n %} -{% load inventree_extras %} - -{% block menubar %} -{% include 'part/navbar.html' with tab='internal-prices' %} -{% endblock %} - -{% block heading %} -{% trans "Internal Price Information" %} -{% endblock %} - -{% block details %} -{% settings_value "PART_INTERNAL_PRICE" as show_internal_price %} -{% if show_internal_price and roles.sales_order.view %} -
    - -
    - - -
    - -{% else %} -
    -

    {% trans "Permission Denied" %}

    - -
    - {% trans "You do not have permission to view this page." %} -
    -
    -{% endif %} -{% endblock %} - -{% block js_ready %} -{{ block.super }} - -{% settings_value "PART_INTERNAL_PRICE" as show_internal_price %} -{% if show_internal_price and roles.sales_order.view %} -function reloadPriceBreaks() { - $("#internal-price-break-table").bootstrapTable("refresh"); -} - -$('#new-internal-price-break').click(function() { - launchModalForm("{% url 'internal-price-break-create' %}", - { - success: reloadPriceBreaks, - data: { - part: {{ part.id }}, - } - } - ); -}); - -$('#internal-price-break-table').inventreeTable({ - name: 'internalprice', - formatNoMatches: function() { return "{% trans 'No internal price break information found' %}"; }, - queryParams: { - part: {{ part.id }}, - }, - url: "{% url 'api-part-internal-price-list' %}", - onPostBody: function() { - var table = $('#internal-price-break-table'); - - table.find('.button-internal-price-break-delete').click(function() { - var pk = $(this).attr('pk'); - - launchModalForm( - `/part/internal-price/${pk}/delete/`, - { - success: reloadPriceBreaks - } - ); - }); - - table.find('.button-internal-price-break-edit').click(function() { - var pk = $(this).attr('pk'); - - launchModalForm( - `/part/internal-price/${pk}/edit/`, - { - success: reloadPriceBreaks - } - ); - }); - }, - columns: [ - { - field: 'pk', - title: 'ID', - visible: false, - switchable: false, - }, - { - field: 'quantity', - title: '{% trans "Quantity" %}', - sortable: true, - }, - { - field: 'price', - title: '{% trans "Price" %}', - sortable: true, - formatter: function(value, row, index) { - var html = value; - - html += `
    ` - - html += makeIconButton('fa-edit icon-blue', 'button-internal-price-break-edit', row.pk, '{% trans "Edit internal price break" %}'); - html += makeIconButton('fa-trash-alt icon-red', 'button-internal-price-break-delete', row.pk, '{% trans "Delete internal price break" %}'); - - html += `
    `; - - return html; - } - }, - ] -}) - -{% endif %} -{% endblock %} \ No newline at end of file diff --git a/InvenTree/part/templates/part/navbar.html b/InvenTree/part/templates/part/navbar.html index b123c458e9..6df4a5505f 100644 --- a/InvenTree/part/templates/part/navbar.html +++ b/InvenTree/part/templates/part/navbar.html @@ -96,19 +96,7 @@
  • {% endif %} - {% if show_internal_price and roles.sales_order.view %} -
  • - - - {% trans "Internal Price" %} - -
  • -
  • - - - {% trans "Sale Price" %} - -
  • + {% if roles.sales_order.view %}
  • diff --git a/InvenTree/part/templates/part/order_prices.html b/InvenTree/part/templates/part/order_prices.html deleted file mode 100644 index 5e5552c5f9..0000000000 --- a/InvenTree/part/templates/part/order_prices.html +++ /dev/null @@ -1,235 +0,0 @@ -{% extends "part/part_base.html" %} -{% load static %} -{% load i18n %} -{% load crispy_forms_tags %} -{% load inventree_extras %} - -{% block menubar %} -{% include 'part/navbar.html' with tab='order-prices' %} -{% endblock %} - -{% block heading %} -{% trans "Order Price Information" %} -{% endblock %} - -{% block details %} -{% default_currency as currency %} -{% settings_value "PART_INTERNAL_PRICE" as show_internal_price %} - -
    - {% csrf_token %} -
    -
    {{ form|crispy }}
    -
    - -
    -
    -
    -
    - -
    -

    {% trans "Pricing ranges" %}

    - -{% if part.supplier_count > 0 %} - {% if min_total_buy_price %} - - - - - - - {% if quantity > 1 %} - - - - - - - {% endif %} - {% else %} - - - - {% endif %} -{% endif %} - -{% if part.bom_count > 0 %} - {% if min_total_bom_price %} - - - - - - - {% if quantity > 1 %} - - - - - - - {% endif %} - {% if part.has_complete_bom_pricing == False %} - - - - {% endif %} - {% else %} - - - - {% endif %} -{% endif %} - -{% if show_internal_price and roles.sales_order.view %} -{% if total_internal_part_price %} - - - - - - - - - - -{% endif %} -{% endif %} - -{% if total_part_price %} - - - - - - - - - - -{% endif %} -
    {% trans 'Supplier Pricing' %}{% trans 'Unit Cost' %}Min: {% include "price.html" with price=min_unit_buy_price %}Max: {% include "price.html" with price=max_unit_buy_price %}
    {% trans 'Total Cost' %}Min: {% include "price.html" with price=min_total_buy_price %}Max: {% include "price.html" with price=max_total_buy_price %}
    - {% trans 'No supplier pricing available' %} -
    {% trans 'BOM Pricing' %}{% trans 'Unit Cost' %}Min: {% include "price.html" with price=min_unit_bom_price %}Max: {% include "price.html" with price=max_unit_bom_price %}
    {% trans 'Total Cost' %}Min: {% include "price.html" with price=min_total_bom_price %}Max: {% include "price.html" with price=max_total_bom_price %}
    - {% trans 'Note: BOM pricing is incomplete for this part' %} -
    - {% trans 'No BOM pricing available' %} -
    {% trans 'Internal Price' %}{% trans 'Unit Cost' %}{% include "price.html" with price=unit_internal_part_price %}
    {% trans 'Total Cost' %}{% include "price.html" with price=total_internal_part_price %}
    {% trans 'Sale Price' %}{% trans 'Unit Cost' %}{% include "price.html" with price=unit_part_price %}
    {% trans 'Total Cost' %}{% include "price.html" with price=total_part_price %}
    - -{% if min_unit_buy_price or min_unit_bom_price %} -{% else %} -
    - {% trans 'No pricing information is available for this part.' %} -
    -{% endif %} -
    -{% if part.bom_count > 0 %} -
    -

    {% trans 'BOM Pricing' %}

    -
    - -
    -
    -{% endif %} -
    - -{% if price_history %} -
    -

    {% trans 'Stock Pricing' %}

    - {% if price_history|length > 0 %} -
    - -
    - {% else %} -
    - {% trans 'No stock pricing history is available for this part.' %} -
    - {% endif %} -{% endif %} -{% endblock %} - - - - -{% block js_ready %} - {{ block.super }} - - {% default_currency as currency %} - {% if price_history %} - var pricedata = { - labels: [ - {% for line in price_history %}'{{ line.date }}',{% endfor %} - ], - datasets: [{ - label: '{% blocktrans %}Single Price - {{currency}}{% endblocktrans %}', - backgroundColor: 'rgba(255, 99, 132, 0.2)', - borderColor: 'rgb(255, 99, 132)', - yAxisID: 'y', - data: [ - {% for line in price_history %}{{ line.price|stringformat:".2f" }},{% endfor %} - ], - borderWidth: 1, - type: 'line' - }, - {% if 'price_diff' in price_history.0 %} - { - label: '{% blocktrans %}Single Price Difference - {{currency}}{% endblocktrans %}', - backgroundColor: 'rgba(68, 157, 68, 0.2)', - borderColor: 'rgb(68, 157, 68)', - yAxisID: 'y2', - data: [ - {% for line in price_history %}{{ line.price_diff|stringformat:".2f" }},{% endfor %} - ], - borderWidth: 1, - type: 'line', - hidden: true, - }, - { - label: '{% blocktrans %}Part Single Price - {{currency}}{% endblocktrans %}', - backgroundColor: 'rgba(70, 127, 155, 0.2)', - borderColor: 'rgb(70, 127, 155)', - yAxisID: 'y', - data: [ - {% for line in price_history %}{{ line.price_part|stringformat:".2f" }},{% endfor %} - ], - borderWidth: 1, - type: 'line', - hidden: true, - }, - {% endif %} - { - label: '{% trans "Quantity" %}', - backgroundColor: 'rgba(255, 206, 86, 0.2)', - borderColor: 'rgb(255, 206, 86)', - yAxisID: 'y1', - data: [ - {% for line in price_history %}{{ line.qty|stringformat:"f" }},{% endfor %} - ], - borderWidth: 1 - }] - } - var StockPriceChart = loadStockPricingChart(document.getElementById('StockPriceChart'), pricedata) - var bom_colors = randomColor({hue: 'green', count: {{ bom_parts|length }} }) - var bomdata = { - labels: [{% for line in bom_parts %}'{{ line.name }}',{% endfor %}], - datasets: [ - { - label: 'Price', - data: [{% for line in bom_parts %}{{ line.min_price }},{% endfor %}], - backgroundColor: bom_colors, - }, - {% if bom_pie_max %} - { - label: 'Max Price', - data: [{% for line in bom_parts %}{{ line.max_price }},{% endfor %}], - backgroundColor: bom_colors, - }, - {% endif %} - ] - }; - var BomChart = loadBomChart(document.getElementById('BomChart'), bomdata) - - {% endif %} - -{% endblock %} diff --git a/InvenTree/part/templates/part/sale_prices.html b/InvenTree/part/templates/part/sale_prices.html deleted file mode 100644 index 4ec826e2a6..0000000000 --- a/InvenTree/part/templates/part/sale_prices.html +++ /dev/null @@ -1,108 +0,0 @@ -{% extends "part/part_base.html" %} -{% load static %} -{% load i18n %} - -{% block menubar %} -{% include 'part/navbar.html' with tab='sales-prices' %} -{% endblock %} - -{% block heading %} -{% trans "Sell Price Information" %} -{% endblock %} - -{% block details %} - -
    - -
    - - -
    - -{% endblock %} - -{% block js_ready %} -{{ block.super }} - -function reloadPriceBreaks() { - $("#price-break-table").bootstrapTable("refresh"); -} - -$('#new-price-break').click(function() { - launchModalForm("{% url 'sale-price-break-create' %}", - { - success: reloadPriceBreaks, - data: { - part: {{ part.id }}, - } - } - ); -}); - -$('#price-break-table').inventreeTable({ - name: 'saleprice', - formatNoMatches: function() { return "{% trans 'No price break information found' %}"; }, - queryParams: { - part: {{ part.id }}, - }, - url: "{% url 'api-part-sale-price-list' %}", - onPostBody: function() { - var table = $('#price-break-table'); - - table.find('.button-price-break-delete').click(function() { - var pk = $(this).attr('pk'); - - launchModalForm( - `/part/sale-price/${pk}/delete/`, - { - success: reloadPriceBreaks - } - ); - }); - - table.find('.button-price-break-edit').click(function() { - var pk = $(this).attr('pk'); - - launchModalForm( - `/part/sale-price/${pk}/edit/`, - { - success: reloadPriceBreaks - } - ); - }); - }, - columns: [ - { - field: 'pk', - title: 'ID', - visible: false, - switchable: false, - }, - { - field: 'quantity', - title: '{% trans "Quantity" %}', - sortable: true, - }, - { - field: 'price', - title: '{% trans "Price" %}', - sortable: true, - formatter: function(value, row, index) { - var html = value; - - html += `
    ` - - html += makeIconButton('fa-edit icon-blue', 'button-price-break-edit', row.pk, '{% trans "Edit price break" %}'); - html += makeIconButton('fa-trash-alt icon-red', 'button-price-break-delete', row.pk, '{% trans "Delete price break" %}'); - - html += `
    `; - - return html; - } - }, - ] -}) - -{% endblock %} \ No newline at end of file diff --git a/InvenTree/part/urls.py b/InvenTree/part/urls.py index 7bd58f52f2..1b11bd16ff 100644 --- a/InvenTree/part/urls.py +++ b/InvenTree/part/urls.py @@ -70,8 +70,6 @@ part_detail_urls = [ url(r'^suppliers/?', views.PartDetail.as_view(template_name='part/supplier.html'), name='part-suppliers'), url(r'^orders/?', views.PartDetail.as_view(template_name='part/orders.html'), name='part-orders'), url(r'^sales-orders/', views.PartDetail.as_view(template_name='part/sales_orders.html'), name='part-sales-orders'), - url(r'^sale-prices/', views.PartDetail.as_view(template_name='part/sale_prices.html'), name='part-sale-prices'), - url(r'^internal-prices/', views.PartDetail.as_view(template_name='part/internal_prices.html'), name='part-internal-prices'), url(r'^tests/', views.PartDetail.as_view(template_name='part/part_tests.html'), name='part-test-templates'), url(r'^track/?', views.PartDetail.as_view(template_name='part/track.html'), name='part-track'), url(r'^related-parts/?', views.PartDetail.as_view(template_name='part/related.html'), name='part-related'), From 522ca161d6e60a769ca7313203c082e173618f4d Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 23 Jun 2021 01:26:07 +0200 Subject: [PATCH 03/19] added permissions-check to bom --- InvenTree/part/templates/part/prices.html | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/InvenTree/part/templates/part/prices.html b/InvenTree/part/templates/part/prices.html index e71179284a..1789b6ddff 100644 --- a/InvenTree/part/templates/part/prices.html +++ b/InvenTree/part/templates/part/prices.html @@ -140,7 +140,6 @@ -

    {% trans "Purchase Price" %}

    @@ -183,6 +182,7 @@ {% endif %} +{% if part.has_bom and roles.sales_order.view %}

    {% trans "BOM Cost" %}

    @@ -204,6 +204,8 @@ {% endif %}
    +{% endif %} + {% if part.salable and roles.sales_order.view %}
    From 332c0a43fd298593afb217125eb77e3ab7abffc2 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 23 Jun 2021 12:16:04 +0200 Subject: [PATCH 04/19] clearer headings --- InvenTree/part/templates/part/prices.html | 1 + 1 file changed, 1 insertion(+) diff --git a/InvenTree/part/templates/part/prices.html b/InvenTree/part/templates/part/prices.html index 1789b6ddff..e2828470b8 100644 --- a/InvenTree/part/templates/part/prices.html +++ b/InvenTree/part/templates/part/prices.html @@ -115,6 +115,7 @@
    +

    {% trans "Calculation parameters" %}

    {% csrf_token %} {{ form|crispy }} From 761aa04aba0882b4d0d59a592869551c65c7b898 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 23 Jun 2021 12:16:33 +0200 Subject: [PATCH 05/19] added bom-table --- InvenTree/part/templates/part/prices.html | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/InvenTree/part/templates/part/prices.html b/InvenTree/part/templates/part/prices.html index e2828470b8..ac4565b8b1 100644 --- a/InvenTree/part/templates/part/prices.html +++ b/InvenTree/part/templates/part/prices.html @@ -191,8 +191,7 @@
    - PLACEHOLDER FOR BOM ITEM COST TABLE -
    +
    {% if part.bom_count > 0 %} @@ -406,6 +405,16 @@ }) {% endif %} + + // Load the BOM table data + loadBomTable($("#bom-table"), { + editable: {{ editing_enabled }}, + bom_url: "{% url 'api-bom-list' %}", + part_url: "{% url 'api-part-list' %}", + parent_id: {{ part.id }} , + sub_part_detail: true, + }); + // Sales pricebreaks {% if part.salable and roles.sales_order.view %} From d7d080fb963d371a460ea54e463e8c7c74023c2c Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 23 Jun 2021 15:30:04 +0200 Subject: [PATCH 06/19] adding in manufacturer and supplier tables --- InvenTree/part/templates/part/prices.html | 42 ++++++++++++++++++++--- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/InvenTree/part/templates/part/prices.html b/InvenTree/part/templates/part/prices.html index ac4565b8b1..21bc8d405a 100644 --- a/InvenTree/part/templates/part/prices.html +++ b/InvenTree/part/templates/part/prices.html @@ -136,9 +136,16 @@

    {% trans "Supplier Cost" %}

    -
    - PLACEHOLDER FOR SUPPLIER COST TABLE -
    +
    +
    +

    {% trans "Suppliers" %}

    +
    +
    +
    +

    {% trans "Manufacturers" %}

    +
    +
    +
    @@ -243,6 +250,33 @@ {% block js_ready %} {{ block.super }} + + loadSupplierPartTable( + "#supplier-table", + "{% url 'api-supplier-part-list' %}", + { + params: { + part: {{ part.id }}, + part_detail: false, + supplier_detail: true, + manufacturer_detail: true, + }, + } + ); + + loadManufacturerPartTable( + "#manufacturer-table", + "{% url 'api-manufacturer-part-list' %}", + { + params: { + part: {{ part.id }}, + part_detail: false, + manufacturer_detail: true, + }, + } + ); + + // history graphs {% default_currency as currency %} {% if price_history %} @@ -405,7 +439,7 @@ }) {% endif %} - + // Load the BOM table data loadBomTable($("#bom-table"), { editable: {{ editing_enabled }}, From 9f27a77689904901f1d57e55f911af1e65e8762c Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 24 Jun 2021 01:19:09 +0200 Subject: [PATCH 07/19] price break js refactor --- InvenTree/part/templates/part/prices.html | 171 ++-------------------- InvenTree/templates/js/part.js | 99 +++++++++++++ 2 files changed, 115 insertions(+), 155 deletions(-) diff --git a/InvenTree/part/templates/part/prices.html b/InvenTree/part/templates/part/prices.html index 21bc8d405a..749b5cb928 100644 --- a/InvenTree/part/templates/part/prices.html +++ b/InvenTree/part/templates/part/prices.html @@ -359,84 +359,15 @@ // Internal pricebreaks {% settings_value "PART_INTERNAL_PRICE" as show_internal_price %} {% if show_internal_price and roles.sales_order.view %} - function reloadPriceBreaks() { - $("#internal-price-break-table").bootstrapTable("refresh"); - } - - $('#new-internal-price-break').click(function() { - launchModalForm("{% url 'internal-price-break-create' %}", - { - success: reloadPriceBreaks, - data: { - part: {{ part.id }}, - } - } + initPriceBreakSet( + $('#internal-price-break-table'), + {{part.id}}, + 'internal price break', + 'internal-price', + "{% url 'api-part-internal-price-list' %}", + $('#new-internal-price-break'), + '{% url 'internal-price-break-create' %}' ); - }); - - $('#internal-price-break-table').inventreeTable({ - name: 'internalprice', - formatNoMatches: function() { return "{% trans 'No internal price break information found' %}"; }, - queryParams: { - part: {{ part.id }}, - }, - url: "{% url 'api-part-internal-price-list' %}", - onPostBody: function() { - var table = $('#internal-price-break-table'); - - table.find('.button-internal-price-break-delete').click(function() { - var pk = $(this).attr('pk'); - - launchModalForm( - `/part/internal-price/${pk}/delete/`, - { - success: reloadPriceBreaks - } - ); - }); - - table.find('.button-internal-price-break-edit').click(function() { - var pk = $(this).attr('pk'); - - launchModalForm( - `/part/internal-price/${pk}/edit/`, - { - success: reloadPriceBreaks - } - ); - }); - }, - columns: [ - { - field: 'pk', - title: 'ID', - visible: false, - switchable: false, - }, - { - field: 'quantity', - title: '{% trans "Quantity" %}', - sortable: true, - }, - { - field: 'price', - title: '{% trans "Price" %}', - sortable: true, - formatter: function(value, row, index) { - var html = value; - - html += `
    ` - - html += makeIconButton('fa-edit icon-blue', 'button-internal-price-break-edit', row.pk, '{% trans "Edit internal price break" %}'); - html += makeIconButton('fa-trash-alt icon-red', 'button-internal-price-break-delete', row.pk, '{% trans "Delete internal price break" %}'); - - html += `
    `; - - return html; - } - }, - ] - }) {% endif %} @@ -452,85 +383,15 @@ // Sales pricebreaks {% if part.salable and roles.sales_order.view %} - function reloadPriceBreaks() { - $("#price-break-table").bootstrapTable("refresh"); - } - - $('#new-price-break').click(function() { - launchModalForm("{% url 'sale-price-break-create' %}", - { - success: reloadPriceBreaks, - data: { - part: {{ part.id }}, - } - } + initPriceBreakSet( + $('#price-break-table'), + {{part.id}}, + 'sale price break', + 'sale-price', + "{% url 'api-part-sale-price-list' %}", + $('#new-price-break'), + '{% url 'sale-price-break-create' %}' ); - }); - - $('#price-break-table').inventreeTable({ - name: 'saleprice', - formatNoMatches: function() { return "{% trans 'No price break information found' %}"; }, - queryParams: { - part: {{ part.id }}, - }, - url: "{% url 'api-part-sale-price-list' %}", - onPostBody: function() { - var table = $('#price-break-table'); - - table.find('.button-price-break-delete').click(function() { - var pk = $(this).attr('pk'); - - launchModalForm( - `/part/sale-price/${pk}/delete/`, - { - success: reloadPriceBreaks - } - ); - }); - - table.find('.button-price-break-edit').click(function() { - var pk = $(this).attr('pk'); - - launchModalForm( - `/part/sale-price/${pk}/edit/`, - { - success: reloadPriceBreaks - } - ); - }); - }, - columns: [ - { - field: 'pk', - title: 'ID', - visible: false, - switchable: false, - }, - { - field: 'quantity', - title: '{% trans "Quantity" %}', - sortable: true, - }, - { - field: 'price', - title: '{% trans "Price" %}', - sortable: true, - formatter: function(value, row, index) { - var html = value; - - html += `
    ` - - html += makeIconButton('fa-edit icon-blue', 'button-price-break-edit', row.pk, '{% trans "Edit price break" %}'); - html += makeIconButton('fa-trash-alt icon-red', 'button-price-break-delete', row.pk, '{% trans "Delete price break" %}'); - - html += `
    `; - - return html; - } - }, - ] - }) - {% endif %} {% endblock %} diff --git a/InvenTree/templates/js/part.js b/InvenTree/templates/js/part.js index 66174e2f15..82fd416e15 100644 --- a/InvenTree/templates/js/part.js +++ b/InvenTree/templates/js/part.js @@ -769,6 +769,105 @@ function loadPartTestTemplateTable(table, options) { } +function loadPriceBreakTable(table, options) { + /* + * Load PriceBreak table. + */ + + var name = options.name || 'pricebreak'; + var human_name = options.human_name || 'price break'; + + table.inventreeTable({ + name: name, + method: 'get', + formatNoMatches: function() { + return `{% trans "No ${human_name} information found" %}`; + }, + url: options.url, + columns: [ + { + field: 'pk', + title: 'ID', + visible: false, + switchable: false, + }, + { + field: 'quantity', + title: '{% trans "Quantity" %}', + sortable: true, + }, + { + field: 'price', + title: '{% trans "Price" %}', + sortable: true, + formatter: function(value, row, index) { + var html = value; + + html += `
    ` + + html += makeIconButton('fa-edit icon-blue', `button-${name}-edit`, row.pk, `{% trans "Edit ${human_name}" %}`); + html += makeIconButton('fa-trash-alt icon-red', `button-${name}-delete`, row.pk, `{% trans "Delete ${human_name}" %}`); + + html += `
    `; + + return html; + } + }, + ] + }); +} + + +function initPriceBreakSet(table, part_id, pb_human_name, pb_url_slug, pb_url, pb_new_btn, pb_new_url) { + + loadPriceBreakTable( + table, + { + name: pb_url_slug, + human_name: pb_human_name, + url: pb_url, + } + ); + + function reloadPriceBreakTable(){ + table.bootstrapTable("refresh"); + } + + pb_new_btn.click(function() { + launchModalForm(pb_new_url, + { + success: reloadPriceBreakTable, + data: { + part: part_id, + } + } + ); + }); + + table.on('click', `.button-${pb_url_slug}-delete`, function() { + var pk = $(this).attr('pk'); + + launchModalForm( + `/part/${pb_url_slug}/${pk}/delete/`, + { + success: reloadPriceBreakTable + } + ); + }); + + table.on('click', `.button-${pb_url_slug}-edit`, function() { + var pk = $(this).attr('pk'); + + launchModalForm( + `/part/${pb_url_slug}/${pk}/edit/`, + { + success: reloadPriceBreakTable + } + ); + }); +} + + function loadStockPricingChart(context, data) { return new Chart(context, { type: 'bar', From b99af16bfd2fc815121e22ed299e5cbe02a03bfd Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 24 Jun 2021 22:13:56 +0200 Subject: [PATCH 08/19] preparing for price breaks diagrams --- InvenTree/part/templates/part/prices.html | 44 +++++++++++++---------- 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/InvenTree/part/templates/part/prices.html b/InvenTree/part/templates/part/prices.html index 749b5cb928..1b8b3959cf 100644 --- a/InvenTree/part/templates/part/prices.html +++ b/InvenTree/part/templates/part/prices.html @@ -176,16 +176,20 @@

    {% trans "Internal Cost" %}

    -
    -
    - +
    +
    - - -
    -
    +
    +
    + +
    + + +
    +
    +
    {% endif %} @@ -220,16 +224,20 @@

    {% trans "Sale Cost" %}

    -
    -
    - +
    +
    - - -
    -
    +
    +
    + +
    + + +
    +
    +
    From 4921cd47f9f8caa7fab488200dbe6476a70868e1 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 25 Jun 2021 07:40:01 +0200 Subject: [PATCH 09/19] refactor for better readabilty --- InvenTree/part/templates/part/prices.html | 30 +++++++++++++---------- InvenTree/templates/js/part.js | 8 ++++++ 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/InvenTree/part/templates/part/prices.html b/InvenTree/part/templates/part/prices.html index 1b8b3959cf..cf719711dc 100644 --- a/InvenTree/part/templates/part/prices.html +++ b/InvenTree/part/templates/part/prices.html @@ -340,7 +340,7 @@ borderWidth: 1 }] } - var StockPriceChart = loadStockPricingChart(document.getElementById('StockPriceChart'), pricedata) + var StockPriceChart = loadStockPricingChart($('#StockPriceChart'), pricedata) var bom_colors = randomColor({hue: 'green', count: {{ bom_parts|length }} }) var bomdata = { labels: [{% for line in bom_parts %}'{{ line.name }}',{% endfor %}], @@ -369,12 +369,14 @@ {% if show_internal_price and roles.sales_order.view %} initPriceBreakSet( $('#internal-price-break-table'), - {{part.id}}, - 'internal price break', - 'internal-price', - "{% url 'api-part-internal-price-list' %}", - $('#new-internal-price-break'), - '{% url 'internal-price-break-create' %}' + { + part_id: {{part.id}}, + pb_human_name: 'internal price break', + pb_url_slug: 'internal-price', + pb_url: '{% url 'api-part-internal-price-list' %}', + pb_new_btn: $('#new-internal-price-break'), + pb_new_url: '{% url 'internal-price-break-create' %}', + }, ); {% endif %} @@ -393,12 +395,14 @@ {% if part.salable and roles.sales_order.view %} initPriceBreakSet( $('#price-break-table'), - {{part.id}}, - 'sale price break', - 'sale-price', - "{% url 'api-part-sale-price-list' %}", - $('#new-price-break'), - '{% url 'sale-price-break-create' %}' + { + part_id: {{part.id}}, + pb_human_name: 'sale price break', + pb_url_slug: 'sale-price', + pb_url: "{% url 'api-part-sale-price-list' %}", + pb_new_btn: $('#new-price-break'), + pb_new_url: '{% url 'sale-price-break-create' %}', + }, ); {% endif %} diff --git a/InvenTree/templates/js/part.js b/InvenTree/templates/js/part.js index 82fd416e15..37fd36486b 100644 --- a/InvenTree/templates/js/part.js +++ b/InvenTree/templates/js/part.js @@ -817,6 +817,14 @@ function loadPriceBreakTable(table, options) { }); } +function initPriceBreakSet(table, options) { + + var part_id = options.part_id; + var pb_human_name = options.pb_human_name; + var pb_url_slug = options.pb_url_slug; + var pb_url = options.pb_url; + var pb_new_btn = options.pb_new_btn; + var pb_new_url = options.pb_new_url; function initPriceBreakSet(table, part_id, pb_human_name, pb_url_slug, pb_url, pb_new_btn, pb_new_url) { From d28d66795d87327536d97bc479ec4179fcb5a6d3 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 25 Jun 2021 07:41:00 +0200 Subject: [PATCH 10/19] linked price break graphs --- InvenTree/InvenTree/static/css/inventree.css | 12 ++++- InvenTree/part/templates/part/prices.html | 12 ++++- InvenTree/templates/js/part.js | 46 +++++++++++++++++++- 3 files changed, 65 insertions(+), 5 deletions(-) diff --git a/InvenTree/InvenTree/static/css/inventree.css b/InvenTree/InvenTree/static/css/inventree.css index eed6c6ad21..7bdc6e8a9b 100644 --- a/InvenTree/InvenTree/static/css/inventree.css +++ b/InvenTree/InvenTree/static/css/inventree.css @@ -960,4 +960,14 @@ input[type="date"].form-control, input[type="time"].form-control, input[type="da .sidebar-icon { min-width: 19px; -} \ No newline at end of file +} + +.row.full-height { + display: flex; + flex-wrap: wrap; + } + +.row.full-height > [class*='col-'] { + display: flex; + flex-direction: column; + } diff --git a/InvenTree/part/templates/part/prices.html b/InvenTree/part/templates/part/prices.html index cf719711dc..1214239fe4 100644 --- a/InvenTree/part/templates/part/prices.html +++ b/InvenTree/part/templates/part/prices.html @@ -176,8 +176,11 @@

    {% trans "Internal Cost" %}

    -
    +
    +
    + +
    @@ -224,8 +227,11 @@

    {% trans "Sale Cost" %}

    -
    +
    +
    + +
    @@ -376,6 +382,7 @@ pb_url: '{% url 'api-part-internal-price-list' %}', pb_new_btn: $('#new-internal-price-break'), pb_new_url: '{% url 'internal-price-break-create' %}', + linkedGraph: $('#InternalPriceBreakChart'), }, ); {% endif %} @@ -402,6 +409,7 @@ pb_url: "{% url 'api-part-sale-price-list' %}", pb_new_btn: $('#new-price-break'), pb_new_url: '{% url 'sale-price-break-create' %}', + linkedGraph: $('#SalePriceBreakChart'), }, ); {% endif %} diff --git a/InvenTree/templates/js/part.js b/InvenTree/templates/js/part.js index 37fd36486b..75e925266a 100644 --- a/InvenTree/templates/js/part.js +++ b/InvenTree/templates/js/part.js @@ -776,6 +776,8 @@ function loadPriceBreakTable(table, options) { var name = options.name || 'pricebreak'; var human_name = options.human_name || 'price break'; + var linkedGraph = options.linkedGraph || null; + var chart = null; table.inventreeTable({ name: name, @@ -784,6 +786,31 @@ function loadPriceBreakTable(table, options) { return `{% trans "No ${human_name} information found" %}`; }, url: options.url, + onLoadSuccess: function(tableData) { + if (linkedGraph) { + var labels = Array.from(tableData, x => x.quantity); + var data = Array.from(tableData, x => parseFloat(x.price)); + + // destroy chart if exists + if (chart){ + chart.destroy(); + } + chart = loadLineChart(linkedGraph, + { + labels: labels, + datasets: [ + { + label: '{% trans "Unit Price" %}', + data: data, + backgroundColor: 'rgba(255, 206, 86, 0.2)', + borderColor: 'rgb(255, 206, 86)', + stepped: true, + fill: true, + },] + } + ); + } + }, columns: [ { field: 'pk', @@ -817,6 +844,20 @@ function loadPriceBreakTable(table, options) { }); } +function loadLineChart(context, data) { + return new Chart(context, { + type: 'line', + data: data, + options: { + responsive: true, + maintainAspectRatio: false, + plugins: { + legend: {position: 'bottom'}, + } + } + }); +} + function initPriceBreakSet(table, options) { var part_id = options.part_id; @@ -826,14 +867,15 @@ function initPriceBreakSet(table, options) { var pb_new_btn = options.pb_new_btn; var pb_new_url = options.pb_new_url; -function initPriceBreakSet(table, part_id, pb_human_name, pb_url_slug, pb_url, pb_new_btn, pb_new_url) { - + var linkedGraph = options.linkedGraph || null; + loadPriceBreakTable( table, { name: pb_url_slug, human_name: pb_human_name, url: pb_url, + linkedGraph: linkedGraph, } ); From 16f25f54d4797b2cfbd89d79a950fbeb91cf4783 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 27 Jun 2021 17:45:31 +0200 Subject: [PATCH 11/19] sorting price-breaks on start --- InvenTree/part/templates/part/prices.html | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/InvenTree/part/templates/part/prices.html b/InvenTree/part/templates/part/prices.html index 1214239fe4..31732d3d94 100644 --- a/InvenTree/part/templates/part/prices.html +++ b/InvenTree/part/templates/part/prices.html @@ -189,7 +189,8 @@
    - +
    @@ -240,7 +241,8 @@
    - +
    From 984efd7493cad851e52ad2582cd639ec1d82eb8a Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 27 Jun 2021 17:51:49 +0200 Subject: [PATCH 12/19] sort graph-data as well --- InvenTree/templates/js/part.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/InvenTree/templates/js/part.js b/InvenTree/templates/js/part.js index 75e925266a..bab8a914db 100644 --- a/InvenTree/templates/js/part.js +++ b/InvenTree/templates/js/part.js @@ -788,6 +788,10 @@ function loadPriceBreakTable(table, options) { url: options.url, onLoadSuccess: function(tableData) { if (linkedGraph) { + // sort array + tableData = tableData.sort((a,b)=>a.quantity-b.quantity); + + // split up for graph definition var labels = Array.from(tableData, x => x.quantity); var data = Array.from(tableData, x => parseFloat(x.price)); From d71aee00cd34f3bbe059edd2aae154f922b19e03 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 27 Jun 2021 17:54:33 +0200 Subject: [PATCH 13/19] refactor of variable names --- InvenTree/templates/js/part.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/InvenTree/templates/js/part.js b/InvenTree/templates/js/part.js index bab8a914db..888f1d245a 100644 --- a/InvenTree/templates/js/part.js +++ b/InvenTree/templates/js/part.js @@ -792,8 +792,8 @@ function loadPriceBreakTable(table, options) { tableData = tableData.sort((a,b)=>a.quantity-b.quantity); // split up for graph definition - var labels = Array.from(tableData, x => x.quantity); - var data = Array.from(tableData, x => parseFloat(x.price)); + var graphLabels = Array.from(tableData, x => x.quantity); + var graphData = Array.from(tableData, x => parseFloat(x.price)); // destroy chart if exists if (chart){ @@ -801,11 +801,11 @@ function loadPriceBreakTable(table, options) { } chart = loadLineChart(linkedGraph, { - labels: labels, + labels: graphLabels, datasets: [ { label: '{% trans "Unit Price" %}', - data: data, + data: graphData, backgroundColor: 'rgba(255, 206, 86, 0.2)', borderColor: 'rgb(255, 206, 86)', stepped: true, From ef07c93634ce202df4665145e8ce77f19f39cb37 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 27 Jun 2021 18:31:40 +0200 Subject: [PATCH 14/19] section anchors --- InvenTree/InvenTree/static/css/inventree.css | 11 ++++- InvenTree/part/templates/part/prices.html | 45 ++++++++++++++++---- 2 files changed, 45 insertions(+), 11 deletions(-) diff --git a/InvenTree/InvenTree/static/css/inventree.css b/InvenTree/InvenTree/static/css/inventree.css index a0030ee7e6..41e0937a8b 100644 --- a/InvenTree/InvenTree/static/css/inventree.css +++ b/InvenTree/InvenTree/static/css/inventree.css @@ -965,9 +965,16 @@ input[type="date"].form-control, input[type="time"].form-control, input[type="da .row.full-height { display: flex; flex-wrap: wrap; - } +} .row.full-height > [class*='col-'] { display: flex; flex-direction: column; - } +} + +a.anchor { + display: block; + position: relative; + top: -60px; + visibility: hidden; +} \ No newline at end of file diff --git a/InvenTree/part/templates/part/prices.html b/InvenTree/part/templates/part/prices.html index 31732d3d94..0d5b3e9d91 100644 --- a/InvenTree/part/templates/part/prices.html +++ b/InvenTree/part/templates/part/prices.html @@ -17,13 +17,17 @@ {% default_currency as currency %}
    +

    {% trans "Pricing ranges" %}

    {% if part.supplier_count > 0 %} {% if min_total_buy_price %} - + @@ -48,7 +52,9 @@ {% if part.bom_count > 0 %} {% if min_total_bom_price %} - + @@ -94,7 +100,10 @@ {% if total_part_price %} - + @@ -132,8 +141,11 @@ {% if part.purchaseable and roles.purchase_order.view %}
    +
    -

    {% trans "Supplier Cost" %}

    +

    {% trans "Supplier Cost" %} + +

    @@ -149,8 +161,11 @@
    +
    -

    {% trans "Purchase Price" %}

    +

    {% trans "Purchase Price" %} + +

    {% if price_history %} @@ -172,8 +187,11 @@ {% if show_internal_price and roles.sales_order.view %}
    +
    -

    {% trans "Internal Cost" %}

    +

    {% trans "Internal Cost" %} + +

    @@ -200,8 +218,11 @@ {% if part.has_bom and roles.sales_order.view %}
    +
    -

    {% trans "BOM Cost" %}

    +

    {% trans "BOM Cost" %} + +

    @@ -224,8 +245,11 @@ {% if part.salable and roles.sales_order.view %}
    +
    -

    {% trans "Sale Cost" %}

    +

    {% trans "Sale Cost" %} + +

    @@ -249,8 +273,11 @@
    +
    -

    {% trans "Sale Price" %}

    +

    {% trans "Sale Price" %} + +

    From f479c0cd27d6589d519d27660786b85b5a1b987c Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 27 Jun 2021 20:46:52 +0200 Subject: [PATCH 15/19] naming refactor --- InvenTree/part/templates/part/prices.html | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/InvenTree/part/templates/part/prices.html b/InvenTree/part/templates/part/prices.html index 0d5b3e9d91..b1403b3157 100644 --- a/InvenTree/part/templates/part/prices.html +++ b/InvenTree/part/templates/part/prices.html @@ -293,6 +293,8 @@ {% block js_ready %} {{ block.super }} + {% default_currency as currency %} + loadSupplierPartTable( "#supplier-table", @@ -321,9 +323,8 @@ // history graphs - {% default_currency as currency %} {% if price_history %} - var pricedata = { + var purchasepricedata = { labels: [ {% for line in price_history %}'{{ line.date }}',{% endfor %} ], @@ -375,7 +376,7 @@ borderWidth: 1 }] } - var StockPriceChart = loadStockPricingChart($('#StockPriceChart'), pricedata) + var StockPriceChart = loadStockPricingChart($('#StockPriceChart'), purchasepricedata) var bom_colors = randomColor({hue: 'green', count: {{ bom_parts|length }} }) var bomdata = { labels: [{% for line in bom_parts %}'{{ line.name }}',{% endfor %}], From e06397adc1da181e3ad281958a4df8411dab503e Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 27 Jun 2021 21:31:10 +0200 Subject: [PATCH 16/19] refactor --- InvenTree/part/views.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/InvenTree/part/views.py b/InvenTree/part/views.py index 2f129dd30b..cb89f67de3 100644 --- a/InvenTree/part/views.py +++ b/InvenTree/part/views.py @@ -979,6 +979,8 @@ class PartPricingView(PartDetail): """ returns context with pricing information """ ctx = PartPricing.get_pricing(self, quantity, currency) part = self.get_part() + default_currency = inventree_settings.currency_code_default() + # Stock history if part.total_stock > 1: price_history = [] @@ -990,7 +992,7 @@ class PartPricingView(PartDetail): continue # convert purchase price to current currency - only one currency in the graph - price = convert_money(stock_item.purchase_price, inventree_settings.currency_code_default()) + price = convert_money(stock_item.purchase_price, default_currency) line = { 'price': price.amount, 'qty': stock_item.quantity From 4462b1e25064a0bca7ac4c415d57e30f8d3cb023 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 27 Jun 2021 21:31:34 +0200 Subject: [PATCH 17/19] order stock histroy items --- InvenTree/part/views.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/InvenTree/part/views.py b/InvenTree/part/views.py index cb89f67de3..7a683a29b5 100644 --- a/InvenTree/part/views.py +++ b/InvenTree/part/views.py @@ -984,8 +984,8 @@ class PartPricingView(PartDetail): # Stock history if part.total_stock > 1: price_history = [] - stock = part.stock_entries(include_variants=False, in_stock=True) # .order_by('purchase_order__date') - stock = stock.prefetch_related('purchase_order', 'supplier_part') + stock = part.stock_entries(include_variants=False, in_stock=True).\ + order_by('purchase_order__issue_date').prefetch_related('purchase_order', 'supplier_part') for stock_item in stock: if None in [stock_item.purchase_price, stock_item.quantity]: From 5598f7fad15ef481f52de11d9bb2623a3ab8d1e8 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 27 Jun 2021 21:32:27 +0200 Subject: [PATCH 18/19] added sale price history --- InvenTree/part/templates/part/prices.html | 41 ++++++++++++++++++++++- InvenTree/part/views.py | 31 +++++++++++++++++ InvenTree/templates/js/part.js | 33 ++++++++++++++++++ 3 files changed, 104 insertions(+), 1 deletion(-) 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' + } + }, + }, + } + }); +} From 628e365c6a0f141831b94ed467a7d23b75b10f8c Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 2 Jul 2021 16:44:25 +0200 Subject: [PATCH 19/19] fix for bom-pricing chart not showing up --- InvenTree/part/templates/part/prices.html | 140 +++++++++++----------- 1 file changed, 71 insertions(+), 69 deletions(-) diff --git a/InvenTree/part/templates/part/prices.html b/InvenTree/part/templates/part/prices.html index 9c216eb534..6e234d3dba 100644 --- a/InvenTree/part/templates/part/prices.html +++ b/InvenTree/part/templates/part/prices.html @@ -332,79 +332,81 @@ // history graphs {% if price_history %} - var purchasepricedata = { - labels: [ - {% for line in price_history %}'{{ line.date }}',{% endfor %} + var purchasepricedata = { + labels: [ + {% for line in price_history %}'{{ line.date }}',{% endfor %} + ], + datasets: [{ + label: '{% blocktrans %}Single Price - {{currency}}{% endblocktrans %}', + backgroundColor: 'rgba(255, 99, 132, 0.2)', + borderColor: 'rgb(255, 99, 132)', + yAxisID: 'y', + data: [ + {% for line in price_history %}{{ line.price|stringformat:".2f" }},{% endfor %} ], - datasets: [{ - label: '{% blocktrans %}Single Price - {{currency}}{% endblocktrans %}', - backgroundColor: 'rgba(255, 99, 132, 0.2)', - borderColor: 'rgb(255, 99, 132)', - yAxisID: 'y', - data: [ - {% for line in price_history %}{{ line.price|stringformat:".2f" }},{% endfor %} - ], - borderWidth: 1, - type: 'line' - }, - {% if 'price_diff' in price_history.0 %} - { - label: '{% blocktrans %}Single Price Difference - {{currency}}{% endblocktrans %}', - backgroundColor: 'rgba(68, 157, 68, 0.2)', - borderColor: 'rgb(68, 157, 68)', - yAxisID: 'y2', - data: [ - {% for line in price_history %}{{ line.price_diff|stringformat:".2f" }},{% endfor %} - ], - borderWidth: 1, - type: 'line', - hidden: true, - }, - { - label: '{% blocktrans %}Part Single Price - {{currency}}{% endblocktrans %}', - backgroundColor: 'rgba(70, 127, 155, 0.2)', - borderColor: 'rgb(70, 127, 155)', - yAxisID: 'y', - data: [ - {% for line in price_history %}{{ line.price_part|stringformat:".2f" }},{% endfor %} - ], - borderWidth: 1, - type: 'line', - hidden: true, - }, - {% endif %} - { - label: '{% trans "Quantity" %}', - backgroundColor: 'rgba(255, 206, 86, 0.2)', - borderColor: 'rgb(255, 206, 86)', - yAxisID: 'y1', - data: [ - {% for line in price_history %}{{ line.qty|stringformat:"f" }},{% endfor %} - ], - borderWidth: 1 - }] - } - var StockPriceChart = loadStockPricingChart($('#StockPriceChart'), purchasepricedata) - var bom_colors = randomColor({hue: 'green', count: {{ bom_parts|length }} }) - var bomdata = { - labels: [{% for line in bom_parts %}'{{ line.name }}',{% endfor %}], - datasets: [ - { - label: 'Price', - data: [{% for line in bom_parts %}{{ line.min_price }},{% endfor %}], - backgroundColor: bom_colors, + borderWidth: 1, + type: 'line' }, - {% if bom_pie_max %} + {% if 'price_diff' in price_history.0 %} { - label: 'Max Price', - data: [{% for line in bom_parts %}{{ line.max_price }},{% endfor %}], - backgroundColor: bom_colors, - }, + label: '{% blocktrans %}Single Price Difference - {{currency}}{% endblocktrans %}', + backgroundColor: 'rgba(68, 157, 68, 0.2)', + borderColor: 'rgb(68, 157, 68)', + yAxisID: 'y2', + data: [ + {% for line in price_history %}{{ line.price_diff|stringformat:".2f" }},{% endfor %} + ], + borderWidth: 1, + type: 'line', + hidden: true, + }, + { + label: '{% blocktrans %}Part Single Price - {{currency}}{% endblocktrans %}', + backgroundColor: 'rgba(70, 127, 155, 0.2)', + borderColor: 'rgb(70, 127, 155)', + yAxisID: 'y', + data: [ + {% for line in price_history %}{{ line.price_part|stringformat:".2f" }},{% endfor %} + ], + borderWidth: 1, + type: 'line', + hidden: true, + }, {% endif %} - ] - }; - var BomChart = loadBomChart(document.getElementById('BomChart'), bomdata) - + { + label: '{% trans "Quantity" %}', + backgroundColor: 'rgba(255, 206, 86, 0.2)', + borderColor: 'rgb(255, 206, 86)', + yAxisID: 'y1', + data: [ + {% for line in price_history %}{{ line.qty|stringformat:"f" }},{% endfor %} + ], + borderWidth: 1 + }] + } + var StockPriceChart = loadStockPricingChart($('#StockPriceChart'), purchasepricedata) + {% endif %} + + {% if bom_parts %} + var bom_colors = randomColor({hue: 'green', count: {{ bom_parts|length }} }) + var bomdata = { + labels: [{% for line in bom_parts %}'{{ line.name }}',{% endfor %}], + datasets: [ + { + label: 'Price', + data: [{% for line in bom_parts %}{{ line.min_price }},{% endfor %}], + backgroundColor: bom_colors, + }, + {% if bom_pie_max %} + { + label: 'Max Price', + data: [{% for line in bom_parts %}{{ line.max_price }},{% endfor %}], + backgroundColor: bom_colors, + }, + {% endif %} + ] + }; + var BomChart = loadBomChart(document.getElementById('BomChart'), bomdata) {% endif %}
    {% trans 'Supplier Pricing' %}{% trans 'Supplier Pricing' %} + + + {% trans 'Unit Cost' %} Min: {% include "price.html" with price=min_unit_buy_price %} Max: {% include "price.html" with price=max_unit_buy_price %}
    {% trans 'BOM Pricing' %}{% trans 'BOM Pricing' %} + + {% trans 'Unit Cost' %} Min: {% include "price.html" with price=min_unit_bom_price %} Max: {% include "price.html" with price=max_unit_bom_price %}
    {% trans 'Sale Price' %}{% trans 'Sale Price' %} + + + {% trans 'Unit Cost' %} {% include "price.html" with price=unit_part_price %}