Все дороги ведут в Checkout Андрей Церкус Magento Developer, Magento Core Team, Magento Inc.
Три кита Чекаута Сбор данных Сумма Оплата
Расчет итоговой суммы Стоимость товаров Скидки Налоги Бонусные программы Сложно. Но лишь на первый взгляд.
1 - Термины
Value / Base Value $virtualAmount += $item->getRowTotal(); $baseVirtualAmount += $item->getBaseRowTotal(); … $subtotal = $price * $qty; $baseSubtotal = $basePrice * $qty; Всегда вместе Base = value во внутренней валюте
Виртуальные товары Виртуальная квота присоединяется к Billing- адресу. Virtual Downloadable (наследник Virtual) Configurable, к которому подключен Virtual Bundle со всеми Virtual Enterprise GiftCard, виртуальный тип
Nominal item Служит для Recurring Profiles – автоматических платежей с определенным периодом Бета-версия – есть ограничения (только 1 Nominal item в Quote) Собственная система обсчета, базирующаяся на обычной с небольшими отличиями
Subtotal / Grand Total Subtotal – это стоимость позиций товара Grand Total = Subtotals + все скидки и налоги. То есть, итоговая цена заказа. Не пара, абсолютно разные вещи
Тax rate Налоговая ставка для комбинации Налогов может быть несколько покупатель тип товара адрес покупателя
Weee / FPT Фиксированный (не процентный) налог на единицу продукции Модуль Magento – Weee. В интерфейсе – FPT (Fixed Price Tax)
Цена товаров включает налоги В интерфейсе – Catalog prices including tax Необходимо определять чистую цену товара перед вычислением налогов X = ценаY = налог $100 (задана суммарная стоимость)
Метод расчета налогов Unit price: [(Product Price * Tax Rate) * Qty] Row total: [(Product Price * Qty) * Tax Rate] Total: (Tax Class Total * Tax Class Rate] – все товары бьются по классам налогообложения, после чего к ним применяются ставки налогов Наличие разных методов – из-за законодательства стран и личных предпочтений владельцев магазинов.
Ура! Теперь общие термины понятны и не страшны! :)
2 - Процесс
Алгоритм Quote Billing & shipping addresses collectTotals() collector 1 collector 2 collector 3 … collector N collectTotals($a)
1.Квота перебирает адреса и у каждого вызывает метод collectTotals() 2.Каждый адрес перебирает список моделей- коллекторов и у каждого вызывает метод collectTotals($address) 3.Каждый коллектор выполняет необходимые ему действия. Обычно: перебирает список продуктов и считает значения. 4.Результаты коллектора – внутри себя (временно), в адресе или в продукте.
Список коллекторов Настраивается в config.xml, как обычно это делается в Magento Можно управлять порядком, так как некоторые основываются на результатах других коллекторов Для построения списка – служебная модель Quote_Address_Total_Collector На данный момент 10 коллекторов в CE и +3 в EE
Порядок коллекторов 1. Sales/Nominal 2. Sales/Address_Subtotal 3. SalesRule/Freeshipping 4. Sales/Quote_Subtotal 5. Sales/Shipping 6. Weee 7. Tax/Shipping 8. SalesRule/Discount 9. Tax/Tax 10. Sales/Grand 11. EE_Reward 12. EE_GiftCardAccount 13. EE_CustomerBalance
Mage_Sales_Model_Quote_Address _Total_Nominal Коллектор специально для Nominal Items Реализует свой собственный стек коллекторов, такой же как общий стек квоты Все коллекторы собственного стека – унаследованы от аналогичных коллекторов квоты. Только результаты не выдают наружу во Frontend. Другие коллекторы квоты с Nominal items в основном не работают
Mage_Sales_Mode_Quote_Address_ Total_Subtotal Общая цена позиций 1 телевизор * $100 = $100 2 телефона * $200 = $400 Subtotal: $500
Mage_SalesRule_Model_Quote _Freeshipping Freeshipping – Да/Нет Правила бесплатной доставки
Mage_Tax_Model_Sales_Total_ Quote_Subtotal Телевизор: $100 incl. tax, $90 excl. tax Телефоны: $400 incl. tax, $310 excl. tax Subtotal: $500 incl.tax, $400 excl.tax Вычисление налогов на subtotal, чтобы разделить subtotal incl. tax и subtotal excl. tax и на них потом правильно начислить скидки
Mage_Sales_Model_Quote_Address _Total_Shipping Доставка выбранным методом UPS: $20 Вариант UPS Very Quick: $40 Вариант FedEx: $30 Вычисление стоимости доставки, в том числе и вариантов
Mage_Weee_Model_Total_Quote_ Weee 1 телевизор * $10 = $10 Weee Tax 2 телефона * $15 = $30 Weee Tax Вычисление фиксированных налогов
Mage_Tax_Model_Sales_Total_Quote _Shipping Налог на доставку = стоимость UPS $20 * 10% = $2 Начисление налогов на доставку. Используется Shipping Tax Class, который задается в админке в конфигурации системы.
Mage_SalesRule_Model_Quote_Discount Скидка по акции за 2 телефона = $400 * -10% = -$40 Подключение всех скидок и купонов
Mage_Tax_Model_Sales_Total_Quote _Tax Налог на телевизор: ($90 excl. tax + $10 weee) * 10% = $10 Налог на телефоны: ($310 excl. tax + $30 weee - $40 discount) * 20 % = $60 Вычисление всех налогов после применения скидок и Weee
Mage_Sales_Model_Quote_Address_ Total_Grand Телевизор: $90 Телефоны: $310 Доставка: $20 Weee: $40 Discount: -$40 Налоги: $ Grand Total (Итого): $572 Суммирование всех величин для показа итога суммы покупателю
Enterprise: Rewards, GiftCard, CustomerBalance GrandTotal: $572 Rewards: -$22 GiftCard: -$20 Balance: -$ Grand Total (Итого): $499 Возможность оплаты бонусами, подарочными картами и со своего счета в магазине
Ура! Теперь и процесс понятен! :)
2. P.S. – Спецтермины налогов
Rounding Deltas
$delta = 0; foreach ($items as $item) { $realTax = $item->getPrice() * $taxRate; $tax = round($realTax + $delta, 2); $delta = $realTax - $tax; } Стабилизация ошибки округления
Чистая цена товара при Catalog Prices incl. tax X = ценаY = налог $100 (задана суммарная стоимость) Как найти цену из стоимости с налогом?
1.Magento считает, что стоимость товара указана включая налог, который действует на Retail Customer, находящегося в Shipping Origin. 2.Имея связку покупатель-класс продукта – адрес Magento находит соответствующие ставки налогов. 3.Налоги вычитаются и получается чистая цена. С ней можно работать уже дальше. * Оптимизация – если покупатель такой же, то налог сразу берется как разница стоимости и чистой цены.
3 – Сам!
Идея Хотим красиво упаковывать товары! платит за упаковку покупатель каждому товару добавим атрибут wrapping_price. Сумму выставляет админ в зависимости от оценки сложности работы и количества расходуемого материала.
Реализация Помним основы Magento: a)свой неймспейс (Zerkella) b)наш модуль – в локальном codepool: app/code/local Итого модуль Zerkella_Wrapping в app/code/local/Zerkella_Wrapping
Подключение в коллекторы – в config.xml. Путь ноды: config/global/sales/quote/totals zerkella_wrapping/total_wrapping grand_total
wrapping – код коллектора Можно использовать и Можно в них через запятую перечислять коды коллекторов Наша модель коллектора: class Zerkella_Wrapping_Model_Total_Wrapping extends Mage_Sales_Model_Quote_Address_Total_Abstract
Обязательный метод collect(): $totalWPrice = 0; $totalWBasePrice = 0; foreach ($this->_getAddressItems($address) as $item) { if ($item->getProduct()->isVirtual()) { continue; } $wrappingBasePrice = $item->getProduct()->getWrappingPrice() * $item->getQty(); $totalWBasePrice += $wrappingBasePrice; $totalWPrice += $address->getQuote()->getStore()- >convertPrice($wrappingBasePrice, false); } $this->_setAmount($totalWPrice)->setBaseAmount($totalWBasePrice);
Пусть будет видно на фронте, метод fetch(): $amount = $address->getWrappingAmount(); if ($amount != 0) { $title = Mage::helper('zerkella_wrapping')->__('Wrapping'); $address->addTotal(array( 'code' => $this->getCode(), 'title' => $title, 'value' => $amount )); } Иии...
Напоследок Легко, но лишь начало: модели для nominals, инвойсов, shipping, refunds (creditmemo) и генерации pdf Только цельный $quote->collectTotals() гарантирует правильность расчета. Но не $address->collectTotals() Помнить, что может быть много collectTotals() – кешировать ресурсоемкие результаты
Спасибо Андрей Церкус, skype: andrey.tserkus Magento Developer, Magento Core Team, Magento Inc.