mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Refactor part pricing page
This commit is contained in:
parent
0fc558068f
commit
c61fc7c1df
@ -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
|
||||||
|
@ -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'
|
||||||
|
@ -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>
|
||||||
|
@ -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 %}
|
|
||||||
|
@ -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
|
||||||
|
@ -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):
|
||||||
|
@ -921,7 +921,7 @@ function loadBuildTable(table, options) {
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return '{% trans "No user information" %}';
|
return `<i>{% trans "No user information" %}</i>`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -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) {
|
||||||
|
|
||||||
|
@ -1495,7 +1495,7 @@ function loadStockTrackingTable(table, options) {
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return '{% trans "No user information" %}';
|
return `<i>{% trans "No user information" %}</i>`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user