diff --git a/InvenTree/build/views.py b/InvenTree/build/views.py index 8d2a8bff80..dfa655f9a4 100644 --- a/InvenTree/build/views.py +++ b/InvenTree/build/views.py @@ -7,9 +7,8 @@ from __future__ import unicode_literals from django.utils.translation import ugettext_lazy as _ from django.core.exceptions import ValidationError -from django.views.generic import DetailView, ListView, UpdateView +from django.views.generic import DetailView, ListView from django.forms import HiddenInput -from django.urls import reverse from part.models import Part from .models import Build, BuildItem diff --git a/InvenTree/part/templates/part/detail.html b/InvenTree/part/templates/part/detail.html index 2cb430ccf8..d9ef212fae 100644 --- a/InvenTree/part/templates/part/detail.html +++ b/InvenTree/part/templates/part/detail.html @@ -1,6 +1,8 @@ {% extends "part/part_base.html" %} {% load static %} {% load i18n %} +{% load inventree_extras %} +{% load crispy_forms_tags %} {% load markdownify %} {% block menubar %} @@ -92,6 +94,10 @@ +
+ {% include "part/prices.html" %} +
+
@@ -893,6 +899,152 @@ }); }); + {% default_currency as currency %} + + // history graphs + {% if price_history %} + 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 %} + ], + 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) + {% 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 %} + + + // Internal pricebreaks + {% settings_value "PART_INTERNAL_PRICE" as show_internal_price %} + {% if show_internal_price and roles.sales_order.view %} + initPriceBreakSet( + $('#internal-price-break-table'), + { + 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' %}', + linkedGraph: $('#InternalPriceBreakChart'), + }, + ); + {% endif %} + + // Sales pricebreaks + {% if part.salable and roles.sales_order.view %} + initPriceBreakSet( + $('#price-break-table'), + { + 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' %}', + linkedGraph: $('#SalePriceBreakChart'), + }, + ); + {% 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 %} + attachNavCallbacks({ name: 'part', default: 'part-stock' diff --git a/InvenTree/part/templates/part/navbar.html b/InvenTree/part/templates/part/navbar.html index 4f0e92a80d..8ea1204bc8 100644 --- a/InvenTree/part/templates/part/navbar.html +++ b/InvenTree/part/templates/part/navbar.html @@ -56,7 +56,7 @@ {% endif %}
  • - + {% trans "Prices" %} diff --git a/InvenTree/part/templates/part/prices.html b/InvenTree/part/templates/part/prices.html index 6e234d3dba..65d36e005a 100644 --- a/InvenTree/part/templates/part/prices.html +++ b/InvenTree/part/templates/part/prices.html @@ -1,176 +1,168 @@ -{% 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 %} +
    +

    {% trans "Pricing Information" %}

    +
    -{% 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 %} +
    + +
    +

    {% trans "Pricing ranges" %}

    +
    {% 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 %}
    + {% if part.supplier_count > 0 %} + {% if min_total_buy_price %} + + + + + + + {% if quantity > 1 %} - - + + - {% endif %} - {% if part.has_complete_bom_pricing == False %} + {% endif %} + {% else %} {% endif %} - {% else %} + {% 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 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_bom_price %}Max: {% include "price.html" with price=max_total_bom_price %}Min: {% include "price.html" with price=min_total_buy_price %}Max: {% include "price.html" with price=max_total_buy_price %}
    - {% trans 'Note: BOM pricing is incomplete for this part' %} + {% 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 '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 '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 %}
    + {% endif %} + + {% if total_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 %} + + {% endif %} + - {% if min_unit_buy_price or min_unit_bom_price %} - {% else %} -
    - {% trans 'No pricing information is available for this part.' %} -
    - {% endif %} -
    + {% if min_unit_buy_price or min_unit_bom_price %} + {% else %} +
    + {% trans 'No pricing information is available for this part.' %} +
    + {% endif %} +
    -
    -

    {% trans "Calculation parameters" %}

    -
    - {% csrf_token %} - {{ form|crispy }} - -
    +
    +

    {% trans "Calculation parameters" %}

    +
    + {% 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" %} - -

    -
    + +
    +

    {% trans "Supplier Cost" %} + +

    +
    -
    -
    -

    {% trans "Suppliers" %}

    -
    -
    -
    -

    {% trans "Manufacturers" %}

    -
    -
    -
    +
    +
    +
    +

    {% trans "Suppliers" %}

    +
    +
    +
    +

    {% trans "Manufacturers" %}

    +
    +
    +
    -
    - -
    -

    {% trans "Purchase Price" %} - -

    -
    - - {% if price_history %} -

    {% trans 'Stock Pricing' %}

    +{% if price_history %} + +
    +

    {% trans "Purchase Price" %} + +

    +
    +
    +

    {% trans 'Stock Pricing' %} + +

    {% if price_history|length > 0 %}
    @@ -180,52 +172,50 @@ {% trans 'No stock pricing history is available for this part.' %}
    {% endif %} - {% endif %}
    {% endif %} - +{% endif %} {% if show_internal_price and roles.sales_order.view %} -
    - -
    -

    {% trans "Internal Cost" %} - -

    -
    + +
    +

    {% trans "Internal Cost" %} + +

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

    {% trans "BOM Cost" %} - -

    -
    + +
    +

    {% trans "BOM Cost" %} + +

    +
    -
    +
    +
    @@ -238,251 +228,55 @@
    {% endif %} -
    +
    {% endif %} - {% if part.salable and roles.sales_order.view %} -
    - -
    -

    {% trans "Sale Cost" %} - -

    -
    + +
    +

    {% trans "Sale Cost" %} + +

    +
    -
    -
    -
    - -
    +
    +
    +
    +
    +
    -
    -
    - -
    - - -
    +
    +
    +
    +
    -
    + + +
    +
    +
    -
    - -
    -

    {% trans "Sale Price" %} - -

    -
    + +
    +

    {% trans "Sale Price" %} + +

    +
    -
    - {% if sale_history|length > 0 %} -
    - -
    - {% else %} -
    - {% trans 'No sale pice history available for this part.' %} -
    - {% endif %} +
    + {% if sale_history|length > 0 %} +
    +
    -
    + {% else %} +
    + {% trans 'No sale pice history available for this part.' %} +
    + {% endif %} +
    {% endif %} - -{% endblock %} - - - -{% block js_ready %} - {{ block.super }} - - {% default_currency as currency %} - - - 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 - {% if price_history %} - 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 %} - ], - 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) - {% 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 %} - - - // Internal pricebreaks - {% settings_value "PART_INTERNAL_PRICE" as show_internal_price %} - {% if show_internal_price and roles.sales_order.view %} - initPriceBreakSet( - $('#internal-price-break-table'), - { - 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' %}', - linkedGraph: $('#InternalPriceBreakChart'), - }, - ); - {% 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 %} - initPriceBreakSet( - $('#price-break-table'), - { - 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' %}', - linkedGraph: $('#SalePriceBreakChart'), - }, - ); - {% 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/urls.py b/InvenTree/part/urls.py index e59ea88a05..2215e14785 100644 --- a/InvenTree/part/urls.py +++ b/InvenTree/part/urls.py @@ -47,8 +47,6 @@ part_detail_urls = [ url(r'^bom-upload/?', views.BomUpload.as_view(), name='upload-bom'), url(r'^bom-duplicate/?', views.BomDuplicate.as_view(), name='duplicate-bom'), - url(r'^prices/', views.PartPricingView.as_view(template_name='part/prices.html'), name='part-prices'), - url(r'^qr_code/?', views.PartQRCode.as_view(), name='part-qr'), # Normal thumbnail with form diff --git a/InvenTree/part/views.py b/InvenTree/part/views.py index cbaa7f3d72..7919b7d412 100644 --- a/InvenTree/part/views.py +++ b/InvenTree/part/views.py @@ -754,6 +754,7 @@ class PartDetail(InvenTreeRoleMixin, DetailView): context_object_name = 'part' queryset = Part.objects.all().select_related('category') template_name = 'part/detail.html' + form_class = part_forms.PartPriceForm # Add in some extra context information based on query params def get_context_data(self, **kwargs): @@ -774,25 +775,12 @@ class PartDetail(InvenTreeRoleMixin, DetailView): ctx = part.get_context_data(self.request) context.update(**ctx) - return context - - -class PartPricingView(PartDetail): - """ Detail view for Part object - """ - context_object_name = 'part' - template_name = 'part/order_prices.html' - form_class = part_forms.PartPriceForm - - # Add in some extra context information based on query params - def get_context_data(self, **kwargs): - """ Provide extra context data to template """ - context = super().get_context_data(**kwargs) - + # Pricing information ctx = self.get_pricing(self.get_quantity()) ctx['form'] = self.form_class(initial=self.get_initials()) context.update(ctx) + return context def get_quantity(self): diff --git a/InvenTree/templates/js/build.js b/InvenTree/templates/js/build.js index e284a0e8c8..f43de6ec2b 100644 --- a/InvenTree/templates/js/build.js +++ b/InvenTree/templates/js/build.js @@ -921,7 +921,7 @@ function loadBuildTable(table, options) { } else { - return '{% trans "No user information" %}'; + return `{% trans "No user information" %}`; } } }, diff --git a/InvenTree/templates/js/part.js b/InvenTree/templates/js/part.js index 7f034682de..169c722d79 100644 --- a/InvenTree/templates/js/part.js +++ b/InvenTree/templates/js/part.js @@ -714,7 +714,7 @@ function loadPartTable(table, url, options={}) { var html = ''; - html = `
    `; + html = `
    `; data.forEach(function(row, index) { diff --git a/InvenTree/templates/js/stock.js b/InvenTree/templates/js/stock.js index f173f868f7..754d3d5b59 100644 --- a/InvenTree/templates/js/stock.js +++ b/InvenTree/templates/js/stock.js @@ -1495,7 +1495,7 @@ function loadStockTrackingTable(table, options) { } else { - return '{% trans "No user information" %}'; + return `{% trans "No user information" %}`; } } });