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