mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Improve error management for order price calculation (#3075)
* Improve error management for order price calculation - If there are missing exchange rates, it throws an error - Very much an edge case * Style fixes * Add warning message if total order price cannot be calculated * price -> cost
This commit is contained in:
parent
dcffd9a3cf
commit
640a5d0f24
@ -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 %}
|
||||||
|
Loading…
Reference in New Issue
Block a user