mirror of
https://github.com/inventree/InvenTree.git
synced 2025-12-17 20:35:01 -06:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f48bd62534 | ||
|
|
bd92ff1290 | ||
|
|
3b3238f762 | ||
|
|
81d29efc12 | ||
|
|
044315afbe |
@@ -12,7 +12,7 @@ import common.models
|
|||||||
from InvenTree.api_version import INVENTREE_API_VERSION
|
from InvenTree.api_version import INVENTREE_API_VERSION
|
||||||
|
|
||||||
# InvenTree software version
|
# InvenTree software version
|
||||||
INVENTREE_SW_VERSION = "0.7.0 dev"
|
INVENTREE_SW_VERSION = "0.7.1"
|
||||||
|
|
||||||
|
|
||||||
def inventreeInstanceName():
|
def inventreeInstanceName():
|
||||||
|
|||||||
@@ -309,7 +309,9 @@ $('#new-price-break').click(function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
loadPurchaseOrderTable($("#purchase-order-table"), {
|
loadPurchaseOrderTable($("#purchase-order-table"), {
|
||||||
url: "{% url 'api-po-list' %}?supplier_part={{ part.id }}",
|
params: {
|
||||||
|
supplier_part: {{ part.id }},
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
loadStockTable($("#stock-table"), {
|
loadStockTable($("#stock-table"), {
|
||||||
|
|||||||
@@ -4,7 +4,10 @@ Order model definitions
|
|||||||
|
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import logging
|
||||||
import os
|
import os
|
||||||
|
import sys
|
||||||
|
import traceback
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
|
|
||||||
@@ -20,7 +23,9 @@ from django.urls import reverse
|
|||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from djmoney.contrib.exchange.models import convert_money
|
from djmoney.contrib.exchange.models import convert_money
|
||||||
|
from djmoney.contrib.exchange.exceptions import MissingRate
|
||||||
from djmoney.money import Money
|
from djmoney.money import Money
|
||||||
|
from error_report.models import Error
|
||||||
from markdownx.models import MarkdownxField
|
from markdownx.models import MarkdownxField
|
||||||
from mptt.models import TreeForeignKey
|
from mptt.models import TreeForeignKey
|
||||||
|
|
||||||
@@ -39,6 +44,9 @@ from stock import models as stock_models
|
|||||||
from users import models as UserModels
|
from users import models as UserModels
|
||||||
|
|
||||||
|
|
||||||
|
logger = logging.getLogger('inventree')
|
||||||
|
|
||||||
|
|
||||||
def get_next_po_number():
|
def get_next_po_number():
|
||||||
"""
|
"""
|
||||||
Returns the next available PurchaseOrder reference number
|
Returns the next available PurchaseOrder reference number
|
||||||
@@ -151,23 +159,74 @@ class Order(MetadataMixin, ReferenceIndexingMixin):
|
|||||||
|
|
||||||
notes = MarkdownxField(blank=True, verbose_name=_('Notes'), help_text=_('Order notes'))
|
notes = MarkdownxField(blank=True, verbose_name=_('Notes'), help_text=_('Order notes'))
|
||||||
|
|
||||||
def get_total_price(self):
|
def get_total_price(self, target_currency=currency_code_default()):
|
||||||
"""
|
"""
|
||||||
Calculates the total price of all order lines
|
Calculates the total price of all order lines, and converts to the specified target currency.
|
||||||
|
|
||||||
|
If not specified, the default system currency is used.
|
||||||
|
|
||||||
|
If currency conversion fails (e.g. there are no valid conversion rates),
|
||||||
|
then we simply return zero, rather than attempting some other calculation.
|
||||||
"""
|
"""
|
||||||
target_currency = currency_code_default()
|
|
||||||
total = Money(0, target_currency)
|
total = Money(0, target_currency)
|
||||||
|
|
||||||
# gather name reference
|
# gather name reference
|
||||||
price_ref = 'sale_price' if isinstance(self, SalesOrder) else 'purchase_price'
|
price_ref_tag = 'sale_price' if isinstance(self, SalesOrder) else 'purchase_price'
|
||||||
# order items
|
|
||||||
total += sum(a.quantity * convert_money(getattr(a, price_ref), target_currency) for a in self.lines.all() if getattr(a, price_ref))
|
|
||||||
|
|
||||||
# extra lines
|
# order items
|
||||||
total += sum(a.quantity * convert_money(a.price, target_currency) for a in self.extra_lines.all() if a.price)
|
for line in self.lines.all():
|
||||||
|
|
||||||
|
price_ref = getattr(line, price_ref_tag)
|
||||||
|
|
||||||
|
if not price_ref:
|
||||||
|
continue
|
||||||
|
|
||||||
|
try:
|
||||||
|
total += line.quantity * convert_money(price_ref, target_currency)
|
||||||
|
except MissingRate:
|
||||||
|
# Record the error, try to press on
|
||||||
|
kind, info, data = sys.exc_info()
|
||||||
|
|
||||||
|
Error.objects.create(
|
||||||
|
kind=kind.__name__,
|
||||||
|
info=info,
|
||||||
|
data='\n'.join(traceback.format_exception(kind, info, data)),
|
||||||
|
path='order.get_total_price',
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.error(f"Missing exchange rate for '{target_currency}'")
|
||||||
|
|
||||||
|
# Return None to indicate the calculated price is invalid
|
||||||
|
return None
|
||||||
|
|
||||||
|
# extra items
|
||||||
|
for line in self.extra_lines.all():
|
||||||
|
|
||||||
|
if not line.price:
|
||||||
|
continue
|
||||||
|
|
||||||
|
try:
|
||||||
|
total += line.quantity * convert_money(line.price, target_currency)
|
||||||
|
except MissingRate:
|
||||||
|
# Record the error, try to press on
|
||||||
|
kind, info, data = sys.exc_info()
|
||||||
|
|
||||||
|
Error.objects.create(
|
||||||
|
kind=kind.__name__,
|
||||||
|
info=info,
|
||||||
|
data='\n'.join(traceback.format_exception(kind, info, data)),
|
||||||
|
path='order.get_total_price',
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.error(f"Missing exchange rate for '{target_currency}'")
|
||||||
|
|
||||||
|
# Return None to indicate the calculated price is invalid
|
||||||
|
return None
|
||||||
|
|
||||||
# set decimal-places
|
# set decimal-places
|
||||||
total.decimal_places = 4
|
total.decimal_places = 4
|
||||||
|
|
||||||
return total
|
return total
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -181,7 +181,15 @@ src="{% static 'img/blank_image.png' %}"
|
|||||||
<tr>
|
<tr>
|
||||||
<td><span class='fas fa-dollar-sign'></span></td>
|
<td><span class='fas fa-dollar-sign'></span></td>
|
||||||
<td>{% trans "Total cost" %}</td>
|
<td>{% trans "Total cost" %}</td>
|
||||||
<td id="poTotalPrice">{{ order.get_total_price }}</td>
|
<td id="poTotalPrice">
|
||||||
|
{% with order.get_total_price as tp %}
|
||||||
|
{% if tp == None %}
|
||||||
|
<span class='badge bg-warning'>{% trans "Total cost could not be calculated" %}</span>
|
||||||
|
{% else %}
|
||||||
|
{{ tp }}
|
||||||
|
{% endif %}
|
||||||
|
{% endwith %}
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -188,7 +188,15 @@ src="{% static 'img/blank_image.png' %}"
|
|||||||
<tr>
|
<tr>
|
||||||
<td><span class='fas fa-dollar-sign'></span></td>
|
<td><span class='fas fa-dollar-sign'></span></td>
|
||||||
<td>{% trans "Total cost" %}</td>
|
<td>{% trans "Total cost" %}</td>
|
||||||
<td id="soTotalPrice">{{ order.get_total_price }}</td>
|
<td id="soTotalPrice">
|
||||||
|
{% with order.get_total_price as tp %}
|
||||||
|
{% if tp == None %}
|
||||||
|
<span class='badge bg-warning'>{% trans "Total cost could not be calculated" %}</span>
|
||||||
|
{% else %}
|
||||||
|
{{ tp }}
|
||||||
|
{% endif %}
|
||||||
|
{% endwith %}
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -17,6 +17,41 @@ function closeSearchPanel() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Keep track of the roles / permissions available to the current user
|
||||||
|
var search_user_roles = null;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check if the user has the specified role and permission
|
||||||
|
*/
|
||||||
|
function checkPermission(role, permission='view') {
|
||||||
|
|
||||||
|
if (!search_user_roles) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(role in search_user_roles)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var roles = search_user_roles[role];
|
||||||
|
|
||||||
|
if (!roles) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var found = false;
|
||||||
|
|
||||||
|
search_user_roles[role].forEach(function(p) {
|
||||||
|
if (String(p).valueOf() == String(permission).valueOf()) {
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Callback when the search panel is opened.
|
* Callback when the search panel is opened.
|
||||||
* Ensure the panel is in a known state
|
* Ensure the panel is in a known state
|
||||||
@@ -27,6 +62,16 @@ function openSearchPanel() {
|
|||||||
|
|
||||||
clearSearchResults();
|
clearSearchResults();
|
||||||
|
|
||||||
|
// Request user roles if we do not have them
|
||||||
|
if (search_user_roles == null) {
|
||||||
|
inventreeGet('{% url "api-user-roles" %}', {}, {
|
||||||
|
success: function(response) {
|
||||||
|
search_user_roles = response.roles || {};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Callback for text input changed
|
||||||
panel.find('#search-input').on('keyup change', searchTextChanged);
|
panel.find('#search-input').on('keyup change', searchTextChanged);
|
||||||
|
|
||||||
// Callback for "clear search" button
|
// Callback for "clear search" button
|
||||||
@@ -84,7 +129,7 @@ function updateSearch() {
|
|||||||
// Show the "searching" text
|
// Show the "searching" text
|
||||||
$('#offcanvas-search').find('#search-pending').show();
|
$('#offcanvas-search').find('#search-pending').show();
|
||||||
|
|
||||||
if (user_settings.SEARCH_PREVIEW_SHOW_PARTS) {
|
if (checkPermission('part') && user_settings.SEARCH_PREVIEW_SHOW_PARTS) {
|
||||||
|
|
||||||
var params = {};
|
var params = {};
|
||||||
|
|
||||||
@@ -106,7 +151,7 @@ function updateSearch() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (user_settings.SEARCH_PREVIEW_SHOW_CATEGORIES) {
|
if (checkPermission('part_category') && user_settings.SEARCH_PREVIEW_SHOW_CATEGORIES) {
|
||||||
// Search for matching part categories
|
// Search for matching part categories
|
||||||
addSearchQuery(
|
addSearchQuery(
|
||||||
'category',
|
'category',
|
||||||
@@ -120,7 +165,7 @@ function updateSearch() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (user_settings.SEARCH_PREVIEW_SHOW_STOCK) {
|
if (checkPermission('stock') && user_settings.SEARCH_PREVIEW_SHOW_STOCK) {
|
||||||
// Search for matching stock items
|
// Search for matching stock items
|
||||||
|
|
||||||
var filters = {
|
var filters = {
|
||||||
@@ -146,7 +191,7 @@ function updateSearch() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (user_settings.SEARCH_PREVIEW_SHOW_LOCATIONS) {
|
if (checkPermission('stock_location') && user_settings.SEARCH_PREVIEW_SHOW_LOCATIONS) {
|
||||||
// Search for matching stock locations
|
// Search for matching stock locations
|
||||||
addSearchQuery(
|
addSearchQuery(
|
||||||
'location',
|
'location',
|
||||||
@@ -160,7 +205,7 @@ function updateSearch() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (user_settings.SEARCH_PREVIEW_SHOW_COMPANIES) {
|
if ((checkPermission('sales_order') || checkPermission('purchase_order')) && user_settings.SEARCH_PREVIEW_SHOW_COMPANIES) {
|
||||||
// Search for matching companies
|
// Search for matching companies
|
||||||
addSearchQuery(
|
addSearchQuery(
|
||||||
'company',
|
'company',
|
||||||
@@ -174,7 +219,7 @@ function updateSearch() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (user_settings.SEARCH_PREVIEW_SHOW_PURCHASE_ORDERS) {
|
if (checkPermission('purchase_order') && user_settings.SEARCH_PREVIEW_SHOW_PURCHASE_ORDERS) {
|
||||||
|
|
||||||
var filters = {
|
var filters = {
|
||||||
supplier_detail: true,
|
supplier_detail: true,
|
||||||
@@ -197,7 +242,7 @@ function updateSearch() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (user_settings.SEARCH_PREVIEW_SHOW_SALES_ORDERS) {
|
if (checkPermission('sales_order') && user_settings.SEARCH_PREVIEW_SHOW_SALES_ORDERS) {
|
||||||
|
|
||||||
var filters = {
|
var filters = {
|
||||||
customer_detail: true,
|
customer_detail: true,
|
||||||
|
|||||||
Reference in New Issue
Block a user