1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* |
5
|
|
|
* |
6
|
|
|
* @category Mygento |
7
|
|
|
* @package Mygento_Payture |
8
|
|
|
* @copyright 2017 NKS LLC. (https://www.mygento.ru) |
9
|
|
|
*/ |
10
|
|
|
class Mygento_Payture_Helper_Discount extends Mage_Core_Helper_Abstract |
11
|
|
|
{ |
12
|
|
|
const VERSION = '1.0.3'; |
13
|
|
|
|
14
|
|
|
protected $_code = 'payture'; |
15
|
|
|
|
16
|
|
|
/** Returns item's data as array with properly calculated discount |
17
|
|
|
* @param $entity Mage_Sales_Model_Order | Mage_Sales_Model_Order_Invoice | Mage_Sales_Model_Order_Creditmemo |
18
|
|
|
* @param $itemSku |
19
|
|
|
* @param string $taxValue |
20
|
|
|
* @param string $taxAttributeCode |
21
|
|
|
* @param string $shippingTaxValue |
22
|
|
|
* @return array|mixed |
23
|
|
|
*/ |
24
|
|
|
public function getItemWithDiscount($entity, $itemSku, $taxValue = '', $taxAttributeCode = '', $shippingTaxValue = '') |
25
|
|
|
{ |
26
|
|
|
$items = $this->getRecalculated($entity, $taxValue, $taxAttributeCode, $shippingTaxValue)['items']; |
27
|
|
|
|
28
|
|
|
return isset($items[$itemSku]) ? $items[$itemSku] : []; |
29
|
|
|
} |
30
|
|
|
|
31
|
|
|
/** Returns all items of the entity (order|invoice|creditmemo) with properly calculated discount and properly calculated Sum |
32
|
|
|
* @param $entity Mage_Sales_Model_Order | Mage_Sales_Model_Order_Invoice | Mage_Sales_Model_Order_Creditmemo |
33
|
|
|
* @param string $taxValue |
34
|
|
|
* @param string $taxAttributeCode Set it if info about tax is stored in product in certain attr |
35
|
|
|
* @param string $shippingTaxValue |
36
|
|
|
* @return array with calculated items and sum |
37
|
|
|
*/ |
38
|
|
|
public function getRecalculated($entity, $taxValue = '', $taxAttributeCode = '', $shippingTaxValue = '') |
39
|
|
|
{ |
40
|
|
|
$generalHelper = Mage::helper($this->_code); |
41
|
|
|
$generalHelper->addLog("== START == Recalculation of entity prices. Entity class: " . get_class($entity) . ". Entity id: {$entity->getId()}"); |
42
|
|
|
|
43
|
|
|
$subTotal = $entity->getData('subtotal'); |
44
|
|
|
$shippingAmount = $entity->getData('shipping_amount'); |
45
|
|
|
$grandTotal = $entity->getData('grand_total'); |
46
|
|
|
$grandDiscount = $grandTotal-$subTotal-$shippingAmount; |
47
|
|
|
|
48
|
|
|
$percentageSum = 0; |
49
|
|
|
|
50
|
|
|
$items = $entity->getAllVisibleItems() ? $entity->getAllVisibleItems() : $entity->getAllItems(); |
51
|
|
|
$itemsFinal = []; |
52
|
|
|
$itemsSum = 0.00; |
53
|
|
|
foreach ($items as $item) { |
54
|
|
|
if (!$this->isValidItem($item)) { |
55
|
|
|
continue; |
56
|
|
|
} |
57
|
|
|
|
58
|
|
|
$taxValue = $taxAttributeCode ? $this->addTaxValue($taxAttributeCode, $entity, $item) : $taxValue; |
59
|
|
|
|
60
|
|
|
$price = $item->getData('price'); |
61
|
|
|
$qty = $item->getQty() ?: $item->getQtyOrdered(); |
62
|
|
|
$rowTotal = $item->getData('row_total'); |
63
|
|
|
|
64
|
|
|
//Calculate Percentage. The heart of logic. |
65
|
|
|
$rowPercentage = $rowTotal / $subTotal; |
66
|
|
|
$percentageSum += $rowPercentage; |
67
|
|
|
|
68
|
|
|
$discountPerUnit = $rowPercentage * $grandDiscount / $qty; |
69
|
|
|
$priceWithDiscount = $this->slyFloor($price + $discountPerUnit); |
70
|
|
|
|
71
|
|
|
$entityItem = $this->_buildItem($item, $priceWithDiscount, $taxValue); |
72
|
|
|
|
73
|
|
|
$itemsFinal[$item->getId()] = $entityItem; |
74
|
|
|
$itemsSum += $entityItem['sum']; |
75
|
|
|
} |
76
|
|
|
|
77
|
|
|
$generalHelper->addLog("Sum of all percentages: {$percentageSum}"); |
78
|
|
|
|
79
|
|
|
//Calculate DIFF! |
80
|
|
|
$itemsSumDiff = round($this->slyFloor($grandTotal - $itemsSum - $shippingAmount, 3), 2); |
81
|
|
|
|
82
|
|
|
$generalHelper->addLog("Items sum: {$itemsSum}. All Discounts: {$grandDiscount} Diff value: {$itemsSumDiff}"); |
83
|
|
|
if (bccomp($itemsSumDiff, 0.00, 2) < 0) { |
84
|
|
|
//if: $itemsSumDiff < 0 |
|
|
|
|
85
|
|
|
$generalHelper->addLog("Notice: Sum of all items is greater than sumWithAllDiscount of entity. ItemsSumDiff: {$itemsSumDiff}"); |
86
|
|
|
$itemsSumDiff = 0.0; |
87
|
|
|
} |
88
|
|
|
|
89
|
|
|
$receipt = [ |
90
|
|
|
'sum' => $itemsSum, |
91
|
|
|
'origGrandTotal' => floatval($grandTotal) |
92
|
|
|
]; |
93
|
|
|
|
94
|
|
|
$shippingItem = [ |
95
|
|
|
'name' => $this->getShippingName($entity), |
96
|
|
|
'price' => $entity->getShippingAmount() + $itemsSumDiff, |
97
|
|
|
'quantity' => 1.0, |
98
|
|
|
'sum' => $entity->getShippingAmount() + $itemsSumDiff, |
99
|
|
|
'tax' => $shippingTaxValue, |
100
|
|
|
]; |
101
|
|
|
|
102
|
|
|
$itemsFinal['shipping'] = $shippingItem; |
103
|
|
|
$receipt['items'] = $itemsFinal; |
104
|
|
|
|
105
|
|
|
if (!$this->_checkReceipt($receipt)) { |
106
|
|
|
$generalHelper->addLog("WARNING: Calculation error! Sum of items is not equal to grandTotal!"); |
107
|
|
|
} |
108
|
|
|
$generalHelper->addLog("Final result of recalculation:"); |
109
|
|
|
$generalHelper->addLog($receipt); |
110
|
|
|
$generalHelper->addLog("== STOP == Recalculation of entity prices. "); |
111
|
|
|
|
112
|
|
|
return $receipt; |
113
|
|
|
} |
114
|
|
|
|
115
|
|
|
protected function _buildItem($item, $price, $taxValue = '') |
116
|
|
|
{ |
117
|
|
|
$generalHelper = Mage::helper($this->_code); |
118
|
|
|
|
119
|
|
|
$qty = $item->getQty() ?: $item->getQtyOrdered(); |
120
|
|
|
if (!$qty) { |
121
|
|
|
throw new Exception('Divide by zero. Qty of the item is equal to zero! Item: ' . $item->getId()); |
122
|
|
|
} |
123
|
|
|
|
124
|
|
|
$entityItem = [ |
125
|
|
|
'price' => round($price, 2), |
126
|
|
|
'name' => $item->getName(), |
127
|
|
|
'quantity' => round($qty, 2), |
128
|
|
|
'sum' => round($price * $qty, 2), |
129
|
|
|
'tax' => $taxValue, |
130
|
|
|
]; |
131
|
|
|
|
132
|
|
|
$generalHelper->addLog("Item calculation details:"); |
133
|
|
|
$generalHelper->addLog("Item id: {$item->getId()}. Orig price: {$price} Item rowTotal: {$item->getRowTotal()} Price of 1 piece: {$price}. Result of calc:"); |
134
|
|
|
$generalHelper->addLog($entityItem); |
135
|
|
|
|
136
|
|
|
return $entityItem; |
137
|
|
|
} |
138
|
|
|
|
139
|
|
|
public function getShippingName($entity) |
140
|
|
|
{ |
141
|
|
|
return $entity->getShippingDescription() |
142
|
|
|
?: ($entity->getOrder() ? $entity->getOrder()->getShippingDescription() : ''); |
143
|
|
|
} |
144
|
|
|
|
145
|
|
|
/**Validation method. It sums up all items and compares it to grandTotal. |
146
|
|
|
* @param array $receipt |
147
|
|
|
* @return bool True if all items price equal to grandTotal. False - if not. |
148
|
|
|
*/ |
149
|
|
|
protected function _checkReceipt(array $receipt) |
150
|
|
|
{ |
151
|
|
|
$sum = array_reduce($receipt['items'], function ($carry, $item) { |
152
|
|
|
$carry += $item['sum']; |
153
|
|
|
return $carry; |
154
|
|
|
}); |
155
|
|
|
|
156
|
|
|
return bcsub($sum, $receipt['origGrandTotal'], 2) === '0.00'; |
157
|
|
|
} |
158
|
|
|
|
159
|
|
|
public function isValidItem($item) |
160
|
|
|
{ |
161
|
|
|
return $item->getRowTotal() && $item->getRowTotal() !== '0.0000'; |
162
|
|
|
} |
163
|
|
|
|
164
|
|
|
public function slyFloor($val, $precision = 2) |
165
|
|
|
{ |
166
|
|
|
$factor = 1.00; |
167
|
|
|
$divider = pow(10, $precision); |
168
|
|
|
|
169
|
|
|
if ($val < 0) { |
170
|
|
|
$factor = -1.00; |
171
|
|
|
} |
172
|
|
|
|
173
|
|
|
return (floor(abs($val) * $divider) / $divider) * $factor; |
174
|
|
|
} |
175
|
|
|
|
176
|
|
|
protected function addTaxValue($taxAttributeCode, $entity, $item) |
177
|
|
|
{ |
178
|
|
|
if (!$taxAttributeCode) { |
179
|
|
|
return ''; |
180
|
|
|
} |
181
|
|
|
$storeId = $entity->getStoreId(); |
182
|
|
|
$store = $storeId ? Mage::app()->getStore($storeId) : Mage::app()->getStore(); |
183
|
|
|
|
184
|
|
|
$taxValue = Mage::getResourceModel('catalog/product')->getAttributeRawValue( |
185
|
|
|
$item->getProductId(), |
186
|
|
|
$taxAttributeCode, |
187
|
|
|
$store |
188
|
|
|
); |
189
|
|
|
|
190
|
|
|
$attributeModel = Mage::getModel('eav/entity_attribute')->loadByCode('catalog_product', $taxAttributeCode); |
191
|
|
|
if ($attributeModel->getData('frontend_input') == 'select') { |
192
|
|
|
$taxValue = $attributeModel->getSource()->getOptionText($taxValue); |
193
|
|
|
} |
194
|
|
|
return $taxValue; |
195
|
|
|
} |
196
|
|
|
} |
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.
The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.
This check looks for comments that seem to be mostly valid code and reports them.