From 640a5d0f243129ac4a91034ccbcef9f5a90b27be Mon Sep 17 00:00:00 2001 From: Oliver Date: Fri, 27 May 2022 13:26:37 +1000 Subject: [PATCH] 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 --- InvenTree/order/models.py | 75 +++++++++++++++++-- .../order/templates/order/order_base.html | 10 ++- .../templates/order/sales_order_base.html | 10 ++- 3 files changed, 85 insertions(+), 10 deletions(-) diff --git a/InvenTree/order/models.py b/InvenTree/order/models.py index 0a5f05ba0f..e4ad0b881f 100644 --- a/InvenTree/order/models.py +++ b/InvenTree/order/models.py @@ -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 diff --git a/InvenTree/order/templates/order/order_base.html b/InvenTree/order/templates/order/order_base.html index 1e0de4d193..a14fea16f7 100644 --- a/InvenTree/order/templates/order/order_base.html +++ b/InvenTree/order/templates/order/order_base.html @@ -181,7 +181,15 @@ src="{% static 'img/blank_image.png' %}" {% trans "Total cost" %} - {{ order.get_total_price }} + + {% with order.get_total_price as tp %} + {% if tp == None %} + {% trans "Total cost could not be calculated" %} + {% else %} + {{ tp }} + {% endif %} + {% endwith %} + {% endblock %} diff --git a/InvenTree/order/templates/order/sales_order_base.html b/InvenTree/order/templates/order/sales_order_base.html index 1e7ddc168f..131280ac7a 100644 --- a/InvenTree/order/templates/order/sales_order_base.html +++ b/InvenTree/order/templates/order/sales_order_base.html @@ -188,7 +188,15 @@ src="{% static 'img/blank_image.png' %}" {% trans "Total cost" %} - {{ order.get_total_price }} + + {% with order.get_total_price as tp %} + {% if tp == None %} + {% trans "Total cost could not be calculated" %} + {% else %} + {{ tp }} + {% endif %} + {% endwith %} + {% endblock %}