|
| 1 | +# Demonstrate ability to use totals models |
| 2 | + |
| 3 | +sales.xml |
| 4 | +```xml |
| 5 | +<section name="quote"> |
| 6 | + <group name="totals"> |
| 7 | + <item name="tax" instance="Magento\Tax\Model\Sales\Total\Quote\Tax" sort_order="450"> |
| 8 | + <renderer name="adminhtml" instance="Magento\Sales\Block\Adminhtml\Order\Create\Totals\Tax"/> |
| 9 | + <renderer name="frontend" instance="Magento\Tax\Block\Checkout\Tax"/> |
| 10 | + </item> |
| 11 | + </group> |
| 12 | +</section> |
| 13 | +``` |
| 14 | + |
| 15 | +Classes: |
| 16 | +- Quote\TotalsCollector - does the job, `collect` and `collectAddressTotals`. (collectQuoteTotals not used) |
| 17 | + |
| 18 | +## Quote totals |
| 19 | +- quote.collectTotals |
| 20 | +- total = totalsCollector.collect - crunch numbers, return data to set on quote |
| 21 | + * create *quote total object* quote\address\total. all you set here will be copied to quote |
| 22 | + * event `sales_quote_collect_totals_before` |
| 23 | + * collect item qtys - quote.itemsCount, quote.itemsQty, quote.virtualItemsQty |
| 24 | + * zero total object amounts - subtotal, grand total etc. |
| 25 | + * collect each *address totals*: |
| 26 | + + *collect address totals* into new address total object |
| 27 | + - address total = quote.*collectAddressTotals* _(see below)_ |
| 28 | + + *add* to quote total object: shipping amount, shipping description, subtotal, subtotal with discount, grant total |
| 29 | + (+ base versions when applicable) |
| 30 | + * validate max grand total amount 99999999 |
| 31 | + * validate coupon code - check that at least one address has it, otherwise reset to '' - code is invalid |
| 32 | + * event `sales_quote_collect_totals_after` |
| 33 | +- quote.addData(total.getData) |
| 34 | + |
| 35 | +## Address totals |
| 36 | +quote.collectAddressTotals for each address (billing, shipping) - set address fields, return some fields for quote |
| 37 | +- new shipping assignment obj: |
| 38 | + + shipping = [method, address] |
| 39 | + + items = address items. empty for normal checkout? |
| 40 | +- create new *address total object* quote\address\total. all you set here will be copied to address |
| 41 | +- event `sales_quote_address_collect_totals_before` |
| 42 | +- get collectors: |
| 43 | + + _initModels -> _initModelInstance -> model.processConfigArray -- tax collector can change its sort programmaticaly |
| 44 | + + sales/totals_sort/* - admin configurable *totals retrievers* ordering - display order |
| 45 | +- *every collector[].collect* |
| 46 | + + all data set on address total object will be copied to address. Main amounts will also copy to quote total object. |
| 47 | + + has direct access to quote |
| 48 | + + has access to address via shipping assignment.getShipping.getAddress |
| 49 | +- event `sales_quote_address_collect_totals_after` |
| 50 | +- address.addData(address total.getData) |
| 51 | + |
| 52 | +## Display totals |
| 53 | +Totals rendered in cart in UI-component fashion with JS configuration. |
| 54 | + |
| 55 | +Totals JS models extend `Magento_Checkout/js/view/summary/abstract-total` and implement: |
| 56 | +- getPureValue - raw float |
| 57 | +- getValue - formatted via `Magento_Catalog/js/price-utils` in format `window.checkoutConfig.priceFormat`. |
| 58 | + |
| 59 | +Models get their values from model quote.getTotals(), e.g. `quote.getTotals()().subtotal`. |
| 60 | +Initial totals values are in `window.checkoutConfig.totalsData`, any extended attributes are merged into main totals. |
| 61 | + |
| 62 | +`window.checkoutConfig` values are provided by `\Magento\Checkout\Model\CompositeConfigProvider`. |
| 63 | +Default config provider `\Magento\Checkout\Model\DefaultConfigProvider`. |
| 64 | +Register your classes via arguments. |
| 65 | + |
| 66 | +Default config provider.getTotalsData and REST API reuse same repository. |
| 67 | +\Magento\Quote\Model\Cart\CartTotalRepository::get: |
| 68 | +- billing or shipping address.getTotals |
| 69 | + * \Magento\Quote\Model\Quote\TotalsReader::fetch |
| 70 | + * each collector[].*fetch*(quote, data) |
| 71 | + + can return \Magento\Quote\Model\Quote\Address\Total or array [code, title, value] |
| 72 | + + title can be object with method *render()* - by default this is just __ Phrase translation |
| 73 | + + can return multiple totals as once |
| 74 | + + can overwrite other totals by code |
| 75 | +- convert address data to interface Cart\Totals - totals by code + quote visible items |
| 76 | +- *totals segments* - totals by code as return from fetch: |
| 77 | + * \Magento\Quote\Model\Cart\TotalsConverter::process - convert address totals to total segment |
| 78 | + * title = titleRenderer.render |
| 79 | +- add other info - coupon code, grand total, items, items qty, currency code |
| 80 | + |
| 81 | +js model quote.getTotals: |
| 82 | +``` |
| 83 | +[ |
| 84 | + {address data fields = populate \Magento\Quote\Api\Data\TotalsInterface with address.getData}, |
| 85 | + items = quote visible items, |
| 86 | + total_segments = as returned from totals collector.fetch |
| 87 | +] |
| 88 | +``` |
| 89 | + |
| 90 | +## Invoice totals, credit memo totals |
| 91 | +```xml |
| 92 | +<section name="order_invoice"> |
| 93 | + <group name="totals"> |
| 94 | + <item name="subtotal" instance="Magento\Sales\Model\Order\Invoice\Total\Subtotal" sort_order="50"/> |
| 95 | + </group> |
| 96 | +</section> |
| 97 | +<section name="order_creditmemo"> |
| 98 | + <group name="totals"> |
| 99 | + <item name="subtotal" instance="Magento\Sales\Model\Order\Creditmemo\Total\Subtotal" sort_order="50"/> |
| 100 | + </group> |
| 101 | +</section> |
| 102 | +``` |
| 103 | + |
| 104 | +Invoice totals: |
| 105 | +- \Magento\Sales\Model\Order\Invoice::collectTotals |
| 106 | +- every total - \Magento\Sales\Model\Order\Invoice\Total\AbstractTotal::collect(invoice) |
| 107 | + + directly update invoice object |
| 108 | + |
| 109 | +Credit memo totals: |
| 110 | +- \Magento\Sales\Model\Order\Creditmemo::collectTotals |
| 111 | +- every total - \Magento\Sales\Model\Order\Creditmemo\Total\AbstractTotal::collect |
| 112 | + + directly update credit memo object |
| 113 | + |
| 114 | +## Render totals in admin area: |
| 115 | +Example - admin invoice totals: |
| 116 | + |
| 117 | +handle `sales_order_invoice_view`: |
| 118 | +```xml |
| 119 | +<block class="Magento\Sales\Block\Adminhtml\Order\Invoice\Totals" name="invoice_totals" template="Magento_Sales::order/totals.phtml"> |
| 120 | +``` |
| 121 | + |
| 122 | +- block totals.`_beforeToHtml` |
| 123 | +- block order totals.`_initTotals` - add hardcoded totals - subtotal, shipping, discount, grand_total, base_grandtotal |
| 124 | +- every child block.initTotals |
| 125 | +- child blocks call block order totals.addTotal(total, after={'first'|'last'|$code}) or addTotalBefore(total, before) |
| 126 | + + total = DataObject [code, value, base_value, label, optional: {area, block_name, is_formated, strong}] |
| 127 | +- block order totals template |
| 128 | + + totals with area = 'footer' |
| 129 | + + totals with empty area |
| 130 | + + if 'block_name', getChildHtml - simply renders child block |
| 131 | + + otherwise print [label, value], value will be price formated unless flag 'is_formated' |
| 132 | + |
| 133 | + |
| 134 | +## Describe how to modify the price calculation process in the shopping cart. |
| 135 | +- when preparing product in product type model, can set `product.custom_price` |
| 136 | +- register quote totals collector in sales.xml. Edit given address_total. |
| 137 | + Examples: |
| 138 | + * `$total->setGrandTotal($total->getGrandTotal() - $pointsCurrencyAmountUsed);` |
| 139 | + * call `$total->addTotalAmount(code, value)`. Grand total will then be sum of all total amounts - they can be negative. |
| 140 | + |
| 141 | +## How can you add a custom totals model or modify existing totals models? |
| 142 | +- add quote totals collector - declare in sales.xml, implement `collect` and `fetch` |
| 143 | + + in collect can modify address totals object (preferred), or edit directly quote or quote address via shipping assignment |
| 144 | + + fetch can return null, or one or many declarations in array format or ready Address\Total objects |
| 145 | + + fetch can rewrite other totals with same 'code' |
| 146 | + + in cart block JS config, add view model and implement getPureValue, getValue. Can read values from model quote.getTotals().totals_segments |
| 147 | +- Render totals in admin: |
| 148 | + + sales_invoice_view, referenceBlock 'invoice_totals', add block with method `initTotals`, call parentBlock.`addTotal` |
0 commit comments