UNDERSTOCK_THRESHOLD = 0.5  # months
OVERSTOCK_THRESHOLD = 2.  # months


def months_of_stock_remaining(stock, daily_consumption):
    try:
        return stock / (daily_consumption * 30)
    except (TypeError, ZeroDivisionError):
        return None


def stock_category(stock, daily_consumption):
    if stock is None:
        return 'nodata'
    elif stock == 0:
        return 'stockout'
    elif daily_consumption is None:
        return 'nodata'
    elif daily_consumption == 0:
        return 'overstock'

    months_left = months_of_stock_remaining(stock, daily_consumption)

    if months_left is None:
        return 'nodata'
    elif months_left < UNDERSTOCK_THRESHOLD:
        return 'understock'
    elif months_left > OVERSTOCK_THRESHOLD:
        return 'overstock'
    else:
        return 'adequate'

from decimal import Decimal

UNDERSTOCK_THRESHOLD = 0.5  # months
OVERSTOCK_THRESHOLD = 2.  # months


def months_of_stock_remaining(stock, daily_consumption):
    if daily_consumption:
        return stock / Decimal((daily_consumption * 30))
    else:
        return None


def stock_category(stock, daily_consumption):
    if stock is None:
        return 'nodata'
    elif stock == 0:
        return 'stockout'
    elif daily_consumption is None:
        return 'nodata'
    elif daily_consumption == 0:
        return 'overstock'

    months_left = months_of_stock_remaining(stock, daily_consumption)

    if months_left is None:
        return 'nodata'
    elif months_left < UNDERSTOCK_THRESHOLD:
        return 'understock'
    elif months_left > OVERSTOCK_THRESHOLD:
        return 'overstock'
    else:
        return 'adequate'

Fix error handling on aggregate status report
Fix error handling on aggregate status report

Previously the catch block was a little too aggressive. It was swallowing a
real error (since aggregate reports pass a float, not a decimal). Now we
prevent the original possible errors by converting no matter what the type is
and checking for zero/null values first.
