From ddad857015508f30b064e44cd2bf55de6dc6c425 Mon Sep 17 00:00:00 2001 From: Egor fureunoir Gorbunov Date: Tue, 17 Jun 2025 09:54:03 +0300 Subject: [PATCH] Features: 1) Implement marketological rounding method to handle psychological pricing; 2) Enhance rounding logic to consider price magnitude using logarithmic calculation; Fixes: None; Extra: 1) Update import to include `log10` from `math`; 2) Add detailed docstring explaining marketological rounding method logic. --- core/vendors/__init__.py | 36 +++++++++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/core/vendors/__init__.py b/core/vendors/__init__.py index 8e35dec6..3ae2ec56 100644 --- a/core/vendors/__init__.py +++ b/core/vendors/__init__.py @@ -1,6 +1,6 @@ import json from contextlib import suppress -from math import ceil +from math import ceil, log10 from django.db import IntegrityError @@ -174,10 +174,36 @@ class AbstractVendor: @staticmethod def round_price_marketologically(price: float) -> float: - up_int = ceil(price) - s = str(up_int) - s = (s[:-1] if len(s) > 1 else "0") + "9" - return float(f"{int(s):.2f}") + """ + Marketological rounding with no cents: + + - Prices < 1: leave exactly as-is. + - Prices ≥ 1: drop any fractional part, then + bump to the next 'psychological' threshold + at the correct order of magnitude and subtract 1. + + E.g. 2.34 → 2 → 3 – 1 = 2 + 12.34 → 12 → 13 – 1 = 12 + 123.45 → 123 → 130 – 1 = 129 + """ + if price < 1: + # sub-currency prices stay as they are + return round(price, 2) + + # strip off any cents + whole = int(price) + + # figure out the magnitude: + # 10**0 = 1 for [1–9], 10**1 = 10 for [10–99], 10**2 = 100 for [100–999], etc. + magnitude = 10 ** max(int(log10(whole)) - 1, 0) + + # next multiple of that magnitude + next_threshold = ceil(whole / magnitude) * magnitude + + # step back 1 to land on a “9” ending + psychological = next_threshold - 1 + + return float(psychological) def get_vendor_instance(self): try: