Refactor part pricing page

This commit is contained in:
Oliver 2021-07-15 23:06:37 +10:00
parent 0fc558068f
commit c61fc7c1df
9 changed files with 366 additions and 435 deletions

View File

@ -7,9 +7,8 @@ from __future__ import unicode_literals
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.core.exceptions import ValidationError 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.forms import HiddenInput
from django.urls import reverse
from part.models import Part from part.models import Part
from .models import Build, BuildItem from .models import Build, BuildItem

View File

@ -1,6 +1,8 @@
{% extends "part/part_base.html" %} {% extends "part/part_base.html" %}
{% load static %} {% load static %}
{% load i18n %} {% load i18n %}
{% load inventree_extras %}
{% load crispy_forms_tags %}
{% load markdownify %} {% load markdownify %}
{% block menubar %} {% block menubar %}
@ -92,6 +94,10 @@
</div> </div>
</div> </div>
<div class='panel panel-default panel-inventree panel-hidden' id='panel-pricing'>
{% include "part/prices.html" %}
</div>
<div class='panel panel-default panel-inventree panel-hidden' id='panel-part-notes'> <div class='panel panel-default panel-inventree panel-hidden' id='panel-part-notes'>
<div class='panel-heading'> <div class='panel-heading'>
<div class='row'> <div class='row'>
@ -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({ attachNavCallbacks({
name: 'part', name: 'part',
default: 'part-stock' default: 'part-stock'

View File

@ -56,7 +56,7 @@
</li> </li>
{% endif %} {% endif %}
<li class='list-group-item' title='{% trans "Pricing Information" %}'> <li class='list-group-item' title='{% trans "Pricing Information" %}'>
<a href='#' id='select-part-pricing' class='nav-toggle'> <a href='#' id='select-pricing' class='nav-toggle'>
<span class='menu-tab-icon fas fa-dollar-sign sidebar-icon'></span> <span class='menu-tab-icon fas fa-dollar-sign sidebar-icon'></span>
{% trans "Prices" %} {% trans "Prices" %}
</a> </a>

View File

@ -1,176 +1,168 @@
{% extends "part/part_base.html" %}
{% load static %} {% load static %}
{% load i18n %} {% load i18n %}
{% load crispy_forms_tags %} {% load crispy_forms_tags %}
{% load inventree_extras %} {% load inventree_extras %}
{% block menubar %} <div class='panel-heading'>
{% include 'part/navbar.html' with tab='prices' %} <h4>{% trans "Pricing Information" %}</h4>
{% endblock %} </div>
{% block heading %}
{% trans "General Price Information" %}
{% endblock %}
{% block details %}
{% default_currency as currency %} {% default_currency as currency %}
<div class='panel-content'>
<div class="row"> <div class="row">
<a class="anchor" id="overview"></a> <a class="anchor" id="overview"></a>
<div class="col col-md-6"> <div class="col col-md-6">
<h4>{% trans "Pricing ranges" %}</h4> <h4>{% trans "Pricing ranges" %}</h4>
<table class='table table-striped table-condensed'> <table class='table table-striped table-condensed'>
{% if part.supplier_count > 0 %} {% if part.supplier_count > 0 %}
{% if min_total_buy_price %} {% if min_total_buy_price %}
<tr> <tr>
<td><b>{% trans 'Supplier Pricing' %}</b> <td><b>{% trans 'Supplier Pricing' %}</b>
<a href="#supplier-cost" title='{% trans "Show supplier cost" %}'><span class="fas fa-search-dollar"></span></a> <a href="#supplier-cost" title='{% trans "Show supplier cost" %}'><span class="fas fa-search-dollar"></span></a>
<a href="#purchase-price" title='{% trans "Show purchase price" %}'><span class="fas fa-chart-bar"></span></a> <a href="#purchase-price" title='{% trans "Show purchase price" %}'><span class="fas fa-chart-bar"></span></a>
</td> </td>
<td>{% trans 'Unit Cost' %}</td> <td>{% trans 'Unit Cost' %}</td>
<td>Min: {% include "price.html" with price=min_unit_buy_price %}</td> <td>Min: {% include "price.html" with price=min_unit_buy_price %}</td>
<td>Max: {% include "price.html" with price=max_unit_buy_price %}</td> <td>Max: {% include "price.html" with price=max_unit_buy_price %}</td>
</tr> </tr>
{% if quantity > 1 %} {% if quantity > 1 %}
<tr>
<td></td>
<td>{% trans 'Total Cost' %}</td>
<td>Min: {% include "price.html" with price=min_total_buy_price %}</td>
<td>Max: {% include "price.html" with price=max_total_buy_price %}</td>
</tr>
{% endif %}
{% else %}
<tr>
<td colspan='4'>
<span class='warning-msg'><i>{% trans 'No supplier pricing available' %}</i></span>
</td>
</tr>
{% endif %}
{% endif %}
{% if part.bom_count > 0 %}
{% if min_total_bom_price %}
<tr>
<td><b>{% trans 'BOM Pricing' %}</b>
<a href="#bom-cost" title='{% trans "Show BOM cost" %}'><span class="fas fa-search-dollar"></span></a>
</td>
<td>{% trans 'Unit Cost' %}</td>
<td>Min: {% include "price.html" with price=min_unit_bom_price %}</td>
<td>Max: {% include "price.html" with price=max_unit_bom_price %}</td>
</tr>
{% if quantity > 1 %}
<tr> <tr>
<td></td> <td></td>
<td>{% trans 'Total Cost' %}</td> <td>{% trans 'Total Cost' %}</td>
<td>Min: {% include "price.html" with price=min_total_bom_price %}</td> <td>Min: {% include "price.html" with price=min_total_buy_price %}</td>
<td>Max: {% include "price.html" with price=max_total_bom_price %}</td> <td>Max: {% include "price.html" with price=max_total_buy_price %}</td>
</tr> </tr>
{% endif %} {% endif %}
{% if part.has_complete_bom_pricing == False %} {% else %}
<tr> <tr>
<td colspan='4'> <td colspan='4'>
<span class='warning-msg'><i>{% trans 'Note: BOM pricing is incomplete for this part' %}</i></span> <span class='warning-msg'><i>{% trans 'No supplier pricing available' %}</i></span>
</td> </td>
</tr> </tr>
{% endif %} {% endif %}
{% else %} {% endif %}
{% if part.bom_count > 0 %}
{% if min_total_bom_price %}
<tr>
<td><b>{% trans 'BOM Pricing' %}</b>
<a href="#bom-cost" title='{% trans "Show BOM cost" %}'><span class="fas fa-search-dollar"></span></a>
</td>
<td>{% trans 'Unit Cost' %}</td>
<td>Min: {% include "price.html" with price=min_unit_bom_price %}</td>
<td>Max: {% include "price.html" with price=max_unit_bom_price %}</td>
</tr>
{% if quantity > 1 %}
<tr>
<td></td>
<td>{% trans 'Total Cost' %}</td>
<td>Min: {% include "price.html" with price=min_total_bom_price %}</td>
<td>Max: {% include "price.html" with price=max_total_bom_price %}</td>
</tr>
{% endif %}
{% if part.has_complete_bom_pricing == False %}
<tr>
<td colspan='4'>
<span class='warning-msg'><i>{% trans 'Note: BOM pricing is incomplete for this part' %}</i></span>
</td>
</tr>
{% endif %}
{% else %}
<tr>
<td colspan='4'>
<span class='warning-msg'><i>{% trans 'No BOM pricing available' %}</i></span>
</td>
</tr>
{% endif %}
{% endif %}
{% if show_internal_price and roles.sales_order.view %}
{% if total_internal_part_price %}
<tr> <tr>
<td colspan='4'> <td><b>{% trans 'Internal Price' %}</b></td>
<span class='warning-msg'><i>{% trans 'No BOM pricing available' %}</i></span> <td>{% trans 'Unit Cost' %}</td>
</td> <td colspan='2'>{% include "price.html" with price=unit_internal_part_price %}</td>
</tr>
<tr>
<td></td>
<td>{% trans 'Total Cost' %}</td>
<td colspan='2'>{% include "price.html" with price=total_internal_part_price %}</td>
</tr> </tr>
{% endif %} {% endif %}
{% endif %} {% endif %}
{% if show_internal_price and roles.sales_order.view %} {% if total_part_price %}
{% if total_internal_part_price %} <tr>
<tr> <td><b>{% trans 'Sale Price' %}</b>
<td><b>{% trans 'Internal Price' %}</b></td> <a href="#sale-cost" title='{% trans "Show sale cost" %}'><span class="fas fa-search-dollar"></span></a>
<td>{% trans 'Unit Cost' %}</td> <a href="#sale-price" title='{% trans "Show sale price" %}'><span class="fas fa-chart-bar"></span></a>
<td colspan='2'>{% include "price.html" with price=unit_internal_part_price %}</td> </td>
</tr> <td>{% trans 'Unit Cost' %}</td>
<tr> <td colspan='2'>{% include "price.html" with price=unit_part_price %}</td>
<td></td> </tr>
<td>{% trans 'Total Cost' %}</td> <tr>
<td colspan='2'>{% include "price.html" with price=total_internal_part_price %}</td> <td></td>
</tr> <td>{% trans 'Total Cost' %}</td>
{% endif %} <td colspan='2'>{% include "price.html" with price=total_part_price %}</td>
{% endif %} </tr>
{% endif %}
{% if total_part_price %} </table>
<tr>
<td><b>{% trans 'Sale Price' %}</b>
<a href="#sale-cost" title='{% trans "Show sale cost" %}'><span class="fas fa-search-dollar"></span></a>
<a href="#sale-price" title='{% trans "Show sale price" %}'><span class="fas fa-chart-bar"></span></a>
</td>
<td>{% trans 'Unit Cost' %}</td>
<td colspan='2'>{% include "price.html" with price=unit_part_price %}</td>
</tr>
<tr>
<td></td>
<td>{% trans 'Total Cost' %}</td>
<td colspan='2'>{% include "price.html" with price=total_part_price %}</td>
</tr>
{% endif %}
</table>
{% if min_unit_buy_price or min_unit_bom_price %} {% if min_unit_buy_price or min_unit_bom_price %}
{% else %} {% else %}
<div class='alert alert-danger alert-block'> <div class='alert alert-danger alert-block'>
{% trans 'No pricing information is available for this part.' %} {% trans 'No pricing information is available for this part.' %}
</div> </div>
{% endif %} {% endif %}
</div> </div>
<div class="col col-md-6"> <div class="col col-md-6">
<h4>{% trans "Calculation parameters" %}</h4> <h4>{% trans "Calculation parameters" %}</h4>
<form method="post"> <form method="post">
{% csrf_token %} {% csrf_token %}
{{ form|crispy }} {{ form|crispy }}
<input type="submit" value="{% trans 'Calculate' %}" class="btn btn-primary btn-block"> <input type="submit" value="{% trans 'Calculate' %}" class="btn btn-primary btn-block">
</form> </form>
</div>
</div> </div>
</div> </div>
{% endblock %}
{% block post_content_panel %}
{% default_currency as currency %}
{% settings_value "PART_INTERNAL_PRICE" as show_internal_price %} {% settings_value "PART_INTERNAL_PRICE" as show_internal_price %}
{% if part.purchaseable and roles.purchase_order.view %} {% if part.purchaseable and roles.purchase_order.view %}
<div class='panel panel-default panel-inventree'> <a class="anchor" id="supplier-cost"></a>
<a class="anchor" id="supplier-cost"></a> <div class='panel-heading'>
<div class='panel-heading'> <h4>{% trans "Supplier Cost" %}
<h4>{% trans "Supplier Cost" %} <a href="#overview" title='{% trans "Jump to overview" %}'><span class="fas fa-level-up-alt"></span></a>
<a href="#overview" title='{% trans "Jump to overview" %}'><span class="fas fa-level-up-alt"></span></a> </h4>
</h4> </div>
</div>
<div class='panel-content'><div class="row"> <div class='panel-content'>
<div class="col col-md-6"> <div class="row">
<h4>{% trans "Suppliers" %}</h4> <div class="col col-md-6">
<table class="table table-striped table-condensed" id='supplier-table' data-toolbar='#button-toolbar'></table> <h4>{% trans "Suppliers" %}</h4>
</div> <table class="table table-striped table-condensed" id='supplier-table' data-toolbar='#button-toolbar'></table>
<div class="col col-md-6"> </div>
<h4>{% trans "Manufacturers" %}</h4> <div class="col col-md-6">
<table class="table table-striped table-condensed" id='manufacturer-table' data-toolbar='#button-toolbar'></table> <h4>{% trans "Manufacturers" %}</h4>
</div> <table class="table table-striped table-condensed" id='manufacturer-table' data-toolbar='#button-toolbar'></table>
</div></div> </div>
</div> </div>
</div>
<div class='panel panel-default panel-inventree'> {% if price_history %}
<a class="anchor" id="purchase-price"></a> <a class="anchor" id="purchase-price"></a>
<div class='panel-heading'> <div class='panel-heading'>
<h4>{% trans "Purchase Price" %} <h4>{% trans "Purchase Price" %}
<a href="#overview" title='{% trans "Jump to overview" %}'><span class="fas fa-level-up-alt"></span></a> <a href="#overview" title='{% trans "Jump to overview" %}'><span class="fas fa-level-up-alt"></span></a>
</h4> </h4>
</div> </div>
<div class='panel-content'>
{% if price_history %} <h4>{% trans 'Stock Pricing' %}
<h4>{% trans 'Stock Pricing' %}<i class="fas fa-info-circle" title="Shows the purchase prices of stock for this part. <i class="fas fa-info-circle" title="Shows the purchase prices of stock for this part.
The part single price is the current purchase price for that supplier part."></i></h4> The part single price is the current purchase price for that supplier part."></i>
</h4>
{% if price_history|length > 0 %} {% if price_history|length > 0 %}
<div style="max-width: 99%; min-height: 300px"> <div style="max-width: 99%; min-height: 300px">
<canvas id="StockPriceChart"></canvas> <canvas id="StockPriceChart"></canvas>
@ -180,52 +172,50 @@
{% trans 'No stock pricing history is available for this part.' %} {% trans 'No stock pricing history is available for this part.' %}
</div> </div>
{% endif %} {% endif %}
{% endif %}
</div> </div>
{% endif %} {% endif %}
{% endif %}
{% if show_internal_price and roles.sales_order.view %} {% if show_internal_price and roles.sales_order.view %}
<div class='panel panel-default panel-inventree'> <a class="anchor" id="internal-cost"></a>
<a class="anchor" id="internal-cost"></a> <div class='panel-heading'>
<div class='panel-heading'> <h4>{% trans "Internal Cost" %}
<h4>{% trans "Internal Cost" %} <a href="#overview" title='{% trans "Jump to overview" %}'><span class="fas fa-level-up-alt"></span></a>
<a href="#overview" title='{% trans "Jump to overview" %}'><span class="fas fa-level-up-alt"></span></a> </h4>
</h4> </div>
</div>
<div class='panel-content'><div class="row full-height"> <div class='panel-content'>
<div class="col col-md-8"> <div class="row full-height">
<div style="max-width: 99%; height: 100%;"> <div class="col col-md-8">
<canvas id="InternalPriceBreakChart"></canvas> <div style="max-width: 99%; height: 100%;">
</div> <canvas id="InternalPriceBreakChart"></canvas>
</div> </div>
<div class="col col-md-4"> </div>
<div id='internal-price-break-toolbar' class='btn-group'> <div class="col col-md-4">
<button class='btn btn-primary' id='new-internal-price-break' type='button'> <div id='internal-price-break-toolbar' class='btn-group'>
<span class='fas fa-plus-circle'></span> {% trans "Add Internal Price Break" %} <button class='btn btn-primary' id='new-internal-price-break' type='button'>
</button> <span class='fas fa-plus-circle'></span> {% trans "Add Internal Price Break" %}
</div> </button>
<table class='table table-striped table-condensed' id='internal-price-break-table' data-toolbar='#internal-price-break-toolbar'
data-sort-name="quantity" data-sort-order="asc">
</table>
</div> </div>
</div></div>
<table class='table table-striped table-condensed' id='internal-price-break-table' data-toolbar='#internal-price-break-toolbar'
data-sort-name="quantity" data-sort-order="asc">
</table>
</div>
</div> </div>
</div>
{% endif %} {% endif %}
{% if part.has_bom and roles.sales_order.view %} {% if part.has_bom and roles.sales_order.view %}
<div class='panel panel-default panel-inventree'> <a class="anchor" id="bom-cost"></a>
<a class="anchor" id="bom-cost"></a> <div class='panel-heading'>
<div class='panel-heading'> <h4>{% trans "BOM Cost" %}
<h4>{% trans "BOM Cost" %} <a href="#overview" title='{% trans "Jump to overview" %}'><span class="fas fa-level-up-alt"></span></a>
<a href="#overview" title='{% trans "Jump to overview" %}'><span class="fas fa-level-up-alt"></span></a> </h4>
</h4> </div>
</div>
<div class='panel-content'><div class="row"> <div class='panel-content'>
<div class="row">
<div class="col col-md-6"> <div class="col col-md-6">
<table class='table table-bom table-condensed' data-toolbar="#button-toolbar" id='bom-table'></table> <table class='table table-bom table-condensed' data-toolbar="#button-toolbar" id='bom-table'></table>
</div> </div>
@ -238,251 +228,55 @@
</div> </div>
</div> </div>
{% endif %} {% endif %}
</div></div> </div>
</div> </div>
{% endif %} {% endif %}
{% if part.salable and roles.sales_order.view %} {% if part.salable and roles.sales_order.view %}
<div class='panel panel-default panel-inventree'> <a class="anchor" id="sale-cost"></a>
<a class="anchor" id="sale-cost"></a> <div class='panel-heading'>
<div class='panel-heading'> <h4>{% trans "Sale Cost" %}
<h4>{% trans "Sale Cost" %} <a href="#overview" title='{% trans "Jump to overview" %}'><span class="fas fa-level-up-alt"></span></a>
<a href="#overview" title='{% trans "Jump to overview" %}'><span class="fas fa-level-up-alt"></span></a> </h4>
</h4> </div>
</div>
<div class='panel-content'><div class="row full-height"> <div class='panel-content'>
<div class="col col-md-8"> <div class="row full-height">
<div style="max-width: 99%; height: 100%;"> <div class="col col-md-8">
<canvas id="SalePriceBreakChart"></canvas> <div style="max-width: 99%; height: 100%;">
</div> <canvas id="SalePriceBreakChart"></canvas>
</div> </div>
<div class="col col-md-4"> </div>
<div id='price-break-toolbar' class='btn-group'> <div class="col col-md-4">
<button class='btn btn-primary' id='new-price-break' type='button'> <div id='price-break-toolbar' class='btn-group'>
<span class='fas fa-plus-circle'></span> {% trans "Add Price Break" %} <button class='btn btn-primary' id='new-price-break' type='button'>
</button> <span class='fas fa-plus-circle'></span> {% trans "Add Price Break" %}
</div> </button>
<table class='table table-striped table-condensed' id='price-break-table' data-toolbar='#price-break-toolbar'
data-sort-name="quantity" data-sort-order="asc">
</table>
</div> </div>
</div></div>
<table class='table table-striped table-condensed' id='price-break-table' data-toolbar='#price-break-toolbar'
data-sort-name="quantity" data-sort-order="asc">
</table>
</div>
</div> </div>
</div>
<div class='panel panel-default panel-inventree'> <a class="anchor" id="sale-price"></a>
<a class="anchor" id="sale-price"></a> <div class='panel-heading'>
<div class='panel-heading'> <h4>{% trans "Sale Price" %}
<h4>{% trans "Sale Price" %} <a href="#overview" title='{% trans "Jump to overview" %}'><span class="fas fa-level-up-alt"></span></a>
<a href="#overview" title='{% trans "Jump to overview" %}'><span class="fas fa-level-up-alt"></span></a> </h4>
</h4> </div>
</div>
<div class='panel-content'> <div class='panel-content'>
{% if sale_history|length > 0 %} {% if sale_history|length > 0 %}
<div style="max-width: 99%; min-height: 300px"> <div style="max-width: 99%; min-height: 300px">
<canvas id="SalePriceChart"></canvas> <canvas id="SalePriceChart"></canvas>
</div>
{% else %}
<div class='alert alert-danger alert-block'>
{% trans 'No sale pice history available for this part.' %}
</div>
{% endif %}
</div> </div>
</div> {% else %}
<div class='alert alert-danger alert-block'>
{% trans 'No sale pice history available for this part.' %}
</div>
{% endif %}
</div>
{% 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 %}

View File

@ -47,8 +47,6 @@ part_detail_urls = [
url(r'^bom-upload/?', views.BomUpload.as_view(), name='upload-bom'), url(r'^bom-upload/?', views.BomUpload.as_view(), name='upload-bom'),
url(r'^bom-duplicate/?', views.BomDuplicate.as_view(), name='duplicate-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'), url(r'^qr_code/?', views.PartQRCode.as_view(), name='part-qr'),
# Normal thumbnail with form # Normal thumbnail with form

View File

@ -754,6 +754,7 @@ class PartDetail(InvenTreeRoleMixin, DetailView):
context_object_name = 'part' context_object_name = 'part'
queryset = Part.objects.all().select_related('category') queryset = Part.objects.all().select_related('category')
template_name = 'part/detail.html' template_name = 'part/detail.html'
form_class = part_forms.PartPriceForm
# Add in some extra context information based on query params # Add in some extra context information based on query params
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
@ -774,25 +775,12 @@ class PartDetail(InvenTreeRoleMixin, DetailView):
ctx = part.get_context_data(self.request) ctx = part.get_context_data(self.request)
context.update(**ctx) context.update(**ctx)
return context # Pricing information
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)
ctx = self.get_pricing(self.get_quantity()) ctx = self.get_pricing(self.get_quantity())
ctx['form'] = self.form_class(initial=self.get_initials()) ctx['form'] = self.form_class(initial=self.get_initials())
context.update(ctx) context.update(ctx)
return context return context
def get_quantity(self): def get_quantity(self):

View File

@ -921,7 +921,7 @@ function loadBuildTable(table, options) {
} }
else else
{ {
return '{% trans "No user information" %}'; return `<i>{% trans "No user information" %}</i>`;
} }
} }
}, },

View File

@ -714,7 +714,7 @@ function loadPartTable(table, url, options={}) {
var html = ''; var html = '';
html = `<div class='row'>`; html = `<div class='row full-height'>`;
data.forEach(function(row, index) { data.forEach(function(row, index) {

View File

@ -1495,7 +1495,7 @@ function loadStockTrackingTable(table, options) {
} }
else else
{ {
return '{% trans "No user information" %}'; return `<i>{% trans "No user information" %}</i>`;
} }
} }
}); });