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:
Oliver 2022-05-27 13:26:37 +10:00 committed by GitHub
parent dcffd9a3cf
commit 640a5d0f24
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 85 additions and 10 deletions

View File

@ -4,7 +4,10 @@ Order model definitions
# -*- coding: utf-8 -*-
import logging
import os
import sys
import traceback
from datetime import datetime
from decimal import Decimal
@ -20,7 +23,9 @@ from django.urls import reverse
from django.utils.translation import gettext_lazy as _
from djmoney.contrib.exchange.models import convert_money
from djmoney.contrib.exchange.exceptions import MissingRate
from djmoney.money import Money
from error_report.models import Error
from markdownx.models import MarkdownxField
from mptt.models import TreeForeignKey
@ -39,6 +44,9 @@ from stock import models as stock_models
from users import models as UserModels
logger = logging.getLogger('inventree')
def get_next_po_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'))
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)
# gather name reference
price_ref = '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))
price_ref_tag = 'sale_price' if isinstance(self, SalesOrder) else 'purchase_price'
# extra lines
total += sum(a.quantity * convert_money(a.price, target_currency) for a in self.extra_lines.all() if a.price)
# order items
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
total.decimal_places = 4
return total

View File

@ -181,7 +181,15 @@ src="{% static 'img/blank_image.png' %}"
<tr>
<td><span class='fas fa-dollar-sign'></span></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>
</table>
{% endblock %}

View File

@ -188,7 +188,15 @@ src="{% static 'img/blank_image.png' %}"
<tr>
<td><span class='fas fa-dollar-sign'></span></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>
</table>
{% endblock %}