From 093a1817518916220722978c74302c92e03e1698 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 23 Jun 2021 01:07:07 +0200 Subject: [PATCH] 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'),