Округлення цін

  • Автор темы yura_co
  • Дата начала
yura_co

yura_co

master
#1
Загалом виникла така проблема:
На сайті в валюті гривня прибрано копійки. та виникла така проблема що при замовленні декількох штук товару сума не співпадає.
Причин в тому що округлення не діє на операцію множення чи то додавання, а тільки на кінцеві суми, що, я вважаю, не є вірним.
Виникають незручні ситуації.
Приклад:
c8220207982e9a7eb4a0c656e7c85208-png.212 - opencart

Хто зтикався з такою ситуацією? Як вирішити таку проблему?
 
Baco

Baco

architect
#2
вітання, вважаю таку проблему порушенням бізнес-логіки і досить критичним багом, прошу написати, як можна відтворити (змоделювати) баг на бойовому проекті ?
 
ТС
yura_co

yura_co

master
Топик Стартер #3
так, то на бойовому поекті, відтворити досить просто, взяти повар в якого ціна, наприклад 1 у.о., візьмемо курс 28.15, або ж пропишемо вартість 28.15, якщо базова валюта гривня, та замовимо 20 штук, по ідеї сума при округленні має бути 560 а в реалії 563.
ice_screenshot_20210228-103342-png.213 - opencart
 
Baco

Baco

architect
#4
логічно, бо по налаштуванням. забрано "не показувати десяті", але нема логіки на "не враховувати при округленні", як на мене, все логічно. а як би мало бути правильним на твою думку ?
 
ТС
yura_co

yura_co

master
Топик Стартер #5
логічно при вартості яку я бачу в 1 гривню за куплені мною 100 штук я мав би оплатити 100 гривень а не 101-149 грн.
тобто сума має вираховуватись від фактично вказаної на сайті а не братися з аналів...
Якщо сума виявилась меншою, то можна вважати за знижку а от якщо сума виявиться більшою, то вже якесь ошуканство.
От прийшов я в магазин, купую цукерку, вона коштує 1 гривню, а якщо хочу купити 100 цукерок, то мені вже кажуть 140гривень... чи то є справедливим?
 
ТС
yura_co

yura_co

master
Топик Стартер #6
Можливо навіть я б ввів варіанти округлення ceil , floor та існуючий round як варіанти округлення, але сума має рахуватись саме від кінцевого $value
 
Baco

Baco

architect
#7
@yura_co не погоджусь, те що не виводить 10-ті, не значить, що нівелюються ці дані. по суті я проблему зрозумів і вона випливає саме з "курсу", а от яке б рішення було максимально корректним, мені важко зрозуміти, тому що, наприклад, продаючи за гривню чи рубель на сайті, а вводячи ціну в $ в адмінці, та прибираючи "кількість знаків після коми" я не можу гарантувати наступне:
0. курс $ до гривні = 1 до 28.15, якщо товар коштує 18 долярів, то його ціна повинна бути: 506,7 грн., незважаючи навіть на те, що кількість знаків після коми "0", ми отримуємо 506, якщо ми купляємо 10-ть таких товарів, то нам потрібно оплатити за них: 5 067 грн., по вашій логіці, потрібно нівелювати копійками при конвертуванні чи при сумі товарів, на якому етапі було б корректно ?
 
ТС
yura_co

yura_co

master
Топик Стартер #8
@Baco, тобто виходить щоб зекономити 7 гривень мені потрібно зробити 10 замовлень?
або я наприклад замовляю 20 товарів, вартість яких має дробну частину яка округлюється до меншого, але в сумі вартість буде вищою ніж замовити по одному товару, і то може бути прийняте клієнтом за шахрайство з боку магазину.
Нівелювання копійками відбувається саме тоді коли адміністратор виставляє кількість знаків після коми . і 20х10 має бути 200 а не інша сума.
 
Последнее редактирование:
Baco

Baco

architect
#9
@Baco, тобто виходить щоб зекономити 7 гривень мені потрібно зробити 10 замовлень?
або я наприклад замовляю 20 товарів, вартість яких має дробну частину яка округлюється до меншого, але в сумі вартість буде вищою ніж замовити по одному товару, і то може бути прийняте клієнтом за шахрайство з боку магазину.
Нівелювання копійками відбувається саме тоді коли адміністратор виставляє кількість знаків після коми . і 20х10 має бути 200 а не інша сума.
згідний, що може бути взято за шахрайство, тому тут спочатку обдумати логіку потрібно, ніж одразу лізти фіксити.
 
S

SlaSoft

Maestro
#10
Так называемая проблема копейки

Округлять нужно на самом последнем этапе
потому тип поля digit 8,4

4 после запятой считается money format

Дивись Василю!

1$ = 25,2
товар на сайте 25
купили 3 товар
75,6
загальна сума - 76


Якщо виникле таке питання, то сумировати треба вже опрацьовані ціні, за одиницю

25 * 3
 
Baco

Baco

architect
#11
отут і питання, на якому етапі потрібно нівелювати копійкою ? при формуванні суми на рівні library/cart ? чи на рівні currency->format ? чи взагалі, при формуванні кінцевого сумарного чеку ? на мою скромну думку, потрібно саме на рівні library\cart\cart.php|currency.php
 
Baco

Baco

architect
#13
@yura_co як твої міркування щодо цього моменту ? Слава корректно запропонував обробку на рівні total робить, як по твоєму, є ще місця, де потрібно підкорректувати даний недолік ?
 
ТС
yura_co

yura_co

master
Топик Стартер #14
@Baco, так, є, якщо тільки тотал, то всерівно будуть місця не сума не збігається. наприклад в випадаючій корзині, хоча можливо я не про той тотал думаюякщо за цей:
$total = $this->currency->format($unit_price * $product['quantity'], $this->session->data['currency']);
то він не вирішує проблеми, плюс в кого стоїть сімпла, також матимуть проблему, там також потрібно правити але то вже інша проблема. і я не зрозумів як формується тотал для декількох товарів, там потрібно також дивитись.
Можливо потрібно зробити модом, для можливих подальших доповнень.
 
Последнее редактирование:
Baco

Baco

architect
#15
@yura_co рішення для Вашого випадку:
відкрийте файл: system/library/cart/currency.php
знайдіть метод public function format(
а в ньому рядок:
PHP:
$amount = $value ? (float)$number * $value : (float)$number;
перед ним додайте рядочок з наступною логікою:
PHP:
$value = round($value, (int)$decimal_place);
зберігаєте, оновлюєте, насолоджуетесь рішенням.
наразі хочу спитати у спільноти, чи є доцільним таке рішення, для інтеграції в бро ?
 
rifle

rifle

Шопмейкер
#16
@yura_co рішення для Вашого випадку:
відкрийте файл: system/library/cart/currency.php
знайдіть метод public function format(
а в ньому рядок:
PHP:
$amount = $value ? (float)$number * $value : (float)$number;
перед ним додайте рядочок з наступною логікою:
PHP:
$value = round($value, (int)$decimal_place);
зберігаєте, оновлюєте, насолоджуетесь рішенням.
наразі хочу спитати у спільноти, чи є доцільним таке рішення, для інтеграції в бро ?
Да, удобно
 
Baco

Baco

architect
#17
є халепа, по факту данного фіксу, які продемонстрував @yura_co на своєму сайті:
коли базова валюта, від котрої розрахунок ведеться ціни в адмінці, має 0 знаків та валюта на котру конвертується значення, в результаті неправильні цифри. тому тут потрібно подумати більш детально, як би то грамотно знайти рішення.
 
Baco

Baco

architect
#18
давайте розберемось разом з таким ребусом:
дано:
0. товар по ціні 14 USD
1. ціна основна магазину USD (та, що прописується в валютах як 1.00000000)
2. конвертація 1 USD до UAH = 28,15
3. валюта по замовчуванню для вітрини UAH
4. приховування символів після коми, в момент конвертації на вітрині
знайти:
яким чином, ми можемо корректно конвертувати товари, якщо в налаштуванні прописано "нівелювати * символів після коми", в результаті чого, на вітрині повинна бути відображена сума (14 * 28,15) = 394 , а ось така, більш корректна логіка (14 * 28) = 392 UAH ?

p.s. рішення з
PHP:
$value = round($value, (int)$decimal_place);
вирішує частково проблему, оскільки є ще одна змінна в задачі, наприклад:
основна валюта RUB, котра до гривні має конвертацію в 1.124 (для прикладу), тобто товар, поставлений за 400 RUB, на вітрині корректно повинен бути відображеним в сумі 450 UAH, але при "нівелюванні * символів після коми" ми отримуємо не 450, а 400 UAH
 
Последнее редактирование:
ТС
yura_co

yura_co

master
Топик Стартер #19
проблема в тому що округлення валюти не припустиме, потрібно округлювати саме ціну але є проблема, скрізь береться $product['price'] саме базової валюти ,потім множиться на кількість а потім множиться на курс, звідси і трабли.
Правильним рішенням було б спочатку множити на курс а потім на кількість. як приклад:
Замість існуючої стрічки

$total = $this->currency->format($unit_price* $product['quantity'] , $this->session->data['currency']);

я виніс множення за курс

$total = $this->currency->format($unit_price , $this->session->data['currency'])* $product['quantity']; ,

то є правильним рішенням, але проблема в тому що таких місць просто уйма і моїх знань просто не вистачає то виправити.
От наприклад стрічка с сімпли:

$total = $this->simplecheckout->formatCurrency($this->tax->calculate($product['price'], $product['tax_class_id'], $this->config->get('config_tax')) * $product['quantity']);

тут також потрібно виносити множення на кількість за дужки.
Можливо це наштовхне на роздуми
 
Baco

Baco

architect
#20
це вже Дмитра персональний метод, він до ОС і до бро в цілому не має відношення, але торкає цю проблему, тому можно тему залишити, для лінкування на неї при такій же проблемі у інших розробників.
Щодо вашої ситуації, то давайте змоделюємо, що вийде в результаті такої логіки:
PHP:
 $this->currency->format($unit_price , $this->session->data['currency'])* $product['quantity'];
тобто кінцевою ціною, по формулі: ціна в сконвертованій валюті множиться на кількість товару, і ТОДІ тільки округляється чи округляється в момент конвертації ? чи округляється після того, як множиться на кількість ?
а) ((14 * 28,15)) * 1
б) (14 * (28)) * 1
в) ((14 * 28,15) * 1)
[логіку округлення виділив червоними дужками з підкресленням]
 
Последнее редактирование:
Baco

Baco

architect
#22
почув, перевірю все ретельно на локальному сервері, та відпишусь тут по тріггерним містам, де то все є і обраховується і якщо логіка максимально прийнятна, то реалізую в синглі
 
Последнее редактирование:
ТС
yura_co

yura_co

master
Топик Стартер #23
чесно кажучи я не розумію чому саме так зроблено. але якщо фіксувати тотали, то вони просто скрізь, в швидкому замовленні, в кошиках, в випадаючих корзинах...... просто жах якийсь. інший шаблон не дай боже зі своєю корзиною і все.... Я переглянув пару корзин, в них все перекочувало ще з 2ї версії (а може й раніше), невже не виникало ні в кого запитань по округленню цін?
Чому б не зробити єдиний тотал для всьго?
Але то може моє бачення того ,мені аби все простіше, бо я вже голову зламав вивчаючи звідки яка перемінна береться та що вона означає.
 
Baco

Baco

architect
#24
як на мене, то логіка вірна, як не крути... чому ? ось:
на сайті я торгую мандаринами, купляю за ларі, один кг. мандаринів беру за 2.7 лар, базова валюта в мене - ларі і я не хочу показувати копійки і на вітрині відображення в гривні, конвертація лар ло гривні: 1 лар = 10,42, в адмінці я вказую ціну: 2.7,\за кг., і на вітрині виходить: 2.7 * 10.42 = 28.13, округлюємо і маємо 28, як на мене - все вірно, думаю що пес закопаний саме в налаштуваннях базова ціна до конвертованої і валюта, у котрої округляти залишок.
 
ТС
yura_co

yura_co

master
Топик Стартер #25
@Baco, тут 2 логіки виходить, одна округлюю, бо не хочу показувати копійки, але торгую всерівно з копійками, а інша то табличка множення. ну не може бути, щоб 2 Х 2 було 5-ть. Зрозуміло що я як продавець хочу щоб 2 х 2 було якомога більше а от як покупець, я хочу щоб все було чесно. Тим паче в Україні є закон, який забороняє продавати дорожче ніж вказано на ціннику.
А враховуючи що в Україні деякі номінали копійок виведені з обігу, не можна вказувати ціну, яка суперечить номіналам, що досить тяжко зробити виходячи з ситуації.
З валютами я прораховував купу варіантів, не виходить, все ж таки все зводиться до тоталів.
 
Baco

Baco

architect
#26
податкова поможе)) жартую звісно, я теж за чесний рахунок, тому пробую зрозуміти і вашу логіку, опишіть будь ласка, чому не можна базову ціну зробити ціною закупки, в адмінці вказувати ціну закупки, а на вітрині виводити конвертаційну ціну, залишик заокругливши ?
тобто: є товар, по закупочній ціні в 400 р., то і вказати базову в р., а конвертувати як і виходить, і тільки в кінці округлювати.
або опишіть якось з цифрами більш аргументовано, бо я поки що абстракцію не можу привести до порядку.
 
Последнее редактирование:
ТС
yura_co

yura_co

master
Топик Стартер #27
податкова поможе)) жартую звісно, я теж за чесний рахунок, тому пробую зрозуміти і вашу логіку, опишіть будь ласка, чому не можна базову ціну зробити ціною закупки, в адмінці вказувати ціну закупки, а на вітрині виводити конвертаційну ціну, залишик заокругливши ?
тобто: є товар, по закупочній ціні в 400 р., то і вказати базову в р., а конвертувати як і виходить, і тільки в кінці округлювати.
або опишіть якось з цифрами більш аргументовано, бо я оки що абстракцію не можу привести до порядку.
Ну якщо виходячи з конкретного прикладу, то базова валюта обиралась як тест, але якщо взяти загалом, то більшість товарів імпортного виробництва та ціни в них прив'язані до якоїсь валюти. частіше то долар або євро, і залежно від курсу валют буде коливатись ціна нац. валюти продажу. Можливо зробити інакше, вести облік в потрібній валюті, а при зміні курсу просто завантажувати нові ціни в нац. валюті, зробивши її базовою. Але я зараз підняв питання саме порушення математичної логіки, коли в магазині за основу взята логіка саме продавця, тим самим покресливши математику, бо наразі виходить: 2х2= а скільки потрібно? а інфляцію враховувати?
 
ТС
yura_co

yura_co

master
Топик Стартер #28
з приводу математичної логіки:
Як зроблено зараз:
((базова ціна *кількість)округлена по умові валюти)*курс
Я вважаю потрібно :
((базова ціна*курс )округлена по умові валюти)*кількість
З моєї точки зору то є більш математично правильним і не порушує округлення до будь-якого знаку.
 
Baco

Baco

architect
#29
повертаючись до нашої розмови по тел. все ж аргументую цифрами свою позицію:
0. базова валюта в $
1. курс грн. 28.55
2. ціна товару 5$
___
в результаті маємо:
з 2-ма символами після коми: ціна товару: 142.75 ціна в корзині 11 штук: 1570.25
з 0 символами після коми: ціна товару: 143 ціна в корзині 11 штук: 1570 (що на 3 гривні менше 143 * 11 = 1573), проте корректно, по відношенню до валюти і конвертації
 
Baco

Baco

architect
#30
все ж знайшов логічне вирішення проблеми, дякуючи Даніелю чи тому розробнику, хто створив класс валюти, в котрому маємо ось такі аргументи в методі format:
PHP:
$number, $currency, $value = '', $format = true
$number - базова цифра, котра конвертується відповідно до курсу (зазвичай множиться на кількість і тоді конвертується (з чим і є проблема в поточній темі)
$currency - сюди передається параметр коду валюти вибраної користувачем, по замовчуванню, це валюта з налаштувань магазину, по факту - вибрана з налаштувань користувача
$value - кастомна конвертація, розписувати не буду, покажу фокус, котрий закладено в опенкарті
$format - маркер, котрий дозволяє виводити числовий формат ціни, якщо в положенні false, або ж повний (з символами зліва, справа)
___
а тепер сама магія, що як і до чого комбінується, щоб отримати корректні цифри:
крок 0: отримуємо ціну за товар без форматування:
PHP:
$clear_price = $this->currency->format($unit_price, $this->session->data['currency'], false, false);
по факту, отримуємо округлену ціну за 1-ну одиницю товару, без додаткових символів (зліва\справа)
крок 1:
перераховуємо загальну сумму по сконвертованій (корректно) сумі за одиницю:
PHP:
$total = $this->currency->format($product['quantity'], $this->session->data['currency'], $clear_price);
ось і все вирішення цієї проблеми. тему вважаю закритою, та знову звернусь до спільноти, щодо внесення змін до збірки, дякую @rifle та @yura_co за фідбєк, тема досить важлива в рамках корректного обрахування товару.
 
Последнее редактирование:
ALarik

ALarik

expert
#31
вот взяли и разобрались, без меня :cool: если моё мнение учитывается, то я бы не просто внёс данное изменение в бро, но и дал инструкцию Димону и всем, кто использует $total при подсчете в своих модификаторах.
 
Baco

Baco

architect
#32
перевірив кілька місць, потрібно все ж напильником обробляти деякі місця, не всюди корректно обраховує, хоча логіка вірна побудови такого підходу, багато місць, як вже описав @yura_co, де потрібно корректувати з (базова ціна * кількість) * курс конвертації, на (базова ціна * курс конвертації) * кількість, де перша половина логічної операції округлює дані і тільки потім, корректну суму множити на кількість.
 
ТС
yura_co

yura_co

master
Топик Стартер #33
Щож, рішення знайдене, і воно може стати однією з фішок збірки. Впевнено можна сказати що в нас округлення цін відбувається правильно, скільки знаків після коми не постав. Але то є фактична відмінність абсолютно з усіма модулями які мені відомі і доведеться робити фікси під популярні модулі. або ж запропонувати наше рішення для всієї спільноти опенкарт, так сказати масовий фікс.
 
Твой e-mail адрес не будет опубликован. Он нужен для подтверждения сообщения !
Вверх