ReceiptSplitAI / algorithm /receipt_calculation.py
valentynliubchenko
merging
eba303d
import math
import copy
from algorithm.product import Product
def calculate_dish_price_with_taxes(_products, taxes, grand_total):
payment_total = round(grand_total - taxes, 2)
grand_total = round(payment_total, 2) + round(taxes, 2)
_product_with_taxes = copy.deepcopy(_products)
for _product in _product_with_taxes:
_product.price = round(((_product.price / payment_total) * grand_total), 5)
return _product_with_taxes, grand_total
def round_up_two_decimals(_products_total):
_product_with_taxes_rounded = copy.deepcopy(_products_total)
for _product in _product_with_taxes_rounded:
_product.price = math.ceil(_product.price * 100) / 100
return _product_with_taxes_rounded
def first_algorithm(_products_total_rounded, receipt_subtotal):
current_total = 0
for _product in _products_total_rounded:
current_total += _product.price
current_total = round(current_total, 2)
difference = current_total - receipt_subtotal
corrections = copy.deepcopy(_products_total_rounded)
for _product in corrections:
_product.price = round((_product.price / current_total) * difference, 2)
for i in range(len(_products_total_rounded)):
_products_total_rounded[i].price = round(_products_total_rounded[i].price - corrections[i].price, 2)
_final_total = 0
for _product in _products_total_rounded:
_final_total += _product.price
return _products_total_rounded, _final_total
def fractional_part_rest(value):
fraction_str = f"{value:.10f}".split('.')[1]
rest_of_digits = fraction_str[2:]
return float(rest_of_digits)
def second_algorithm(_products_total, receipt_total):
_products_total_rounded = round_up_two_decimals(_products_total)
current_total = 0
for _product in _products_total_rounded:
current_total += _product.price
if current_total == receipt_total:
return _products_total_rounded, receipt_total
difference = current_total - receipt_total
difference = round(difference, 2)
fractional_parts = copy.deepcopy(_products_total)
for _product in fractional_parts:
_product.price = fractional_part_rest(_product.price) - math.ceil(_product.price)
fractional_parts = sorted(fractional_parts, key=lambda p: p.price, reverse=False)
for i in range(len(fractional_parts)):
if difference <= 0:
break
_products_total_rounded[i].price -= 0.01
difference -= 0.01
_final_total = 0
for _product in _products_total_rounded:
_final_total += _product.price
for _product in _products_total_rounded:
_product.price = round(_product.price, 2)
return _products_total_rounded, _final_total
def clean_and_convert_to_float(price):
if price == "": return 0.0
clean_price = ''.join(c for c in str(price) if c.isdigit() or c in ",.")
return float(clean_price.replace(",", "."))
def calculate_tips_and_taxes(items_table, total_amount, tax, tips):
products = []
if items_table[0][0] == "No items":
return products, 0
if total_amount == "Not specified" or total_amount == "unknown" or total_amount is None:
total_amount = "0.0"
if tax == "Not specified" or tax == "unknown" or tax is None:
tax = "0.0"
if tips == "Not specified" or tips == "unknown" or tips is None:
tips = "0.0"
for item in items_table:
price = item[5]
if price == "Not specified" or price == "unknown":
price = "0.0"
item_value = clean_and_convert_to_float(price) if item[5] is not None else 0.0
products.append(Product(item[0], item_value))
sum_of_product_prices = 0
for _product in products:
sum_of_product_prices += _product.price
sum_of_product_prices = round(float(sum_of_product_prices), 2)
total_amount = round(clean_and_convert_to_float(total_amount), 2)
tips = round(clean_and_convert_to_float(tips), 2)
tax = round(tips + round(clean_and_convert_to_float(tax), 2), 2)
if round(float(total_amount), 2) != round(float(sum_of_product_prices) + float(tax), 2):
return products, sum_of_product_prices
products_total, subtotal = calculate_dish_price_with_taxes(products, taxes=float(tax),
grand_total=float(total_amount))
final_prices, final_total = second_algorithm(products_total, subtotal)
final_total = round(final_total, 2)
return final_prices, final_total