Passed
Push — master ( 714cf3...b030f5 )
by Florian
01:16 queued 19s
created

Invoice::addInvoicePosition()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 10
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 8
c 1
b 0
f 0
nc 1
nop 6
dl 0
loc 10
rs 10
1
<?php
2
3
/**
4
 * PAYONE Magento 2 Connector is free software: you can redistribute it and/or modify
5
 * it under the terms of the GNU Lesser General Public License as published by
6
 * the Free Software Foundation, either version 3 of the License, or
7
 * (at your option) any later version.
8
 *
9
 * PAYONE Magento 2 Connector is distributed in the hope that it will be useful,
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
 * GNU Lesser General Public License for more details.
13
 *
14
 * You should have received a copy of the GNU Lesser General Public License
15
 * along with PAYONE Magento 2 Connector. If not, see <http://www.gnu.org/licenses/>.
16
 *
17
 * PHP version 5
18
 *
19
 * @category  Payone
20
 * @package   Payone_Magento2_Plugin
21
 * @author    FATCHIP GmbH <[email protected]>
22
 * @copyright 2003 - 2016 Payone GmbH
23
 * @license   <http://www.gnu.org/licenses/> GNU Lesser General Public License
24
 * @link      http://www.payone.de
25
 */
26
27
namespace Payone\Core\Model\Api;
28
29
use Payone\Core\Model\Api\Request\Base;
30
use Magento\Sales\Model\Order;
0 ignored issues
show
Bug introduced by
The type Magento\Sales\Model\Order was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
31
use Magento\Quote\Model\Quote\Item as QuoteItem;
0 ignored issues
show
Bug introduced by
The type Magento\Quote\Model\Quote\Item was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
32
use Magento\Quote\Model\Quote;
0 ignored issues
show
Bug introduced by
The type Magento\Quote\Model\Quote was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
33
34
/**
35
 * Collect all invoice parameters
36
 *
37
 * @category  Payone
38
 * @package   Payone_Magento2_Plugin
39
 * @author    FATCHIP GmbH <[email protected]>
40
 * @copyright 2003 - 2016 Payone GmbH
41
 * @license   <http://www.gnu.org/licenses/> GNU Lesser General Public License
42
 * @link      http://www.payone.de
43
 */
44
class Invoice
45
{
46
    /**
47
     * Index of added invoice items
48
     *
49
     * @var integer
50
     */
51
    protected $iIndex = 1;
52
53
    /**
54
     * Invoice amount
55
     *
56
     * @var integer
57
     */
58
    protected $dAmount = 0;
59
60
    /**
61
     * Vat rate for following entities which may not have the vat attached to it
62
     *
63
     * @var double
64
     */
65
    protected $dTax = false;
66
67
    /**
68
     * PAYONE toolkit helper
69
     *
70
     * @var \Payone\Core\Helper\Toolkit
71
     */
72
    protected $toolkitHelper;
73
74
    /**
75
     * PAYONE amasty helper
76
     *
77
     * @var \Payone\Core\Helper\AmastyGiftcard
78
     */
79
    protected $amastyHelper;
80
81
    /**
82
     * Request object
83
     *
84
     * @var Base
85
     */
86
    protected $oRequest;
87
88
    /**
89
     * Constructor
90
     *
91
     * @param \Payone\Core\Helper\Toolkit $toolkitHelper Toolkit helper
92
     */
93
    public function __construct(\Payone\Core\Helper\Toolkit $toolkitHelper, \Payone\Core\Helper\AmastyGiftcard $amastyHelper)
94
    {
95
        $this->toolkitHelper = $toolkitHelper;
96
        $this->amastyHelper = $amastyHelper;
97
    }
98
99
    /**
100
     * Add parameters for a invoice position
101
     *
102
     * @param  string $sId       item identification
103
     * @param  double $dPrice    item price
104
     * @param  string $sItemType item type
105
     * @param  int    $iAmount   item amount
106
     * @param  string $sDesc     item description
107
     * @param  double $dVat      item tax rate
108
     * @return void
109
     */
110
    protected function addInvoicePosition($sId, $dPrice, $sItemType, $iAmount, $sDesc, $dVat)
111
    {
112
        $this->oRequest->addParameter('id['.$this->iIndex.']', $sId); // add invoice item id
113
        $this->oRequest->addParameter('pr['.$this->iIndex.']', $this->toolkitHelper->formatNumber($dPrice) * 100); // expected in smallest unit of currency
114
        $this->oRequest->addParameter('it['.$this->iIndex.']', $sItemType); // add invoice item type
115
        $this->oRequest->addParameter('no['.$this->iIndex.']', $iAmount); // add invoice item amount
116
        $this->oRequest->addParameter('de['.$this->iIndex.']', $sDesc); // add invoice item description
117
        $this->oRequest->addParameter('va['.$this->iIndex.']', $this->toolkitHelper->formatNumber($dVat * 100, 0)); // expected * 100 to also handle vats with decimals
118
        $this->dAmount += $dPrice * $iAmount; // needed for return of the main method
119
        $this->iIndex++; // increase index for next item
120
    }
121
122
    /**
123
     * Add invoicing data to the request and return the summed invoicing amount
124
     *
125
     * @param  Base     $oRequest       Request object
126
     * @param  object   $oOrder         Order object
127
     * @param  array    $aPositions     Is given with non-complete captures or debits
128
     * @param  bool     $blDebit        Is the call coming from a debit request
129
     * @param  double   $dShippingCosts Shipping costs - needed for Klarna start_session
130
     * @return integer
131
     */
132
    public function addProductInfo(Base $oRequest, $oOrder, $aPositions = false, $blDebit = false, $dShippingCosts = false)
133
    {
134
        $this->oRequest = $oRequest; // write request to property for manipulation of the object
135
        if ($oOrder instanceof Order) {
136
            $sInvoiceAppendix = $this->toolkitHelper->getInvoiceAppendix($oOrder); // get invoice appendix
137
            if (!empty($sInvoiceAppendix)) { // invoice appendix existing?
138
                $this->oRequest->addParameter('invoiceappendix', $sInvoiceAppendix); // add appendix to request
139
            }
140
        }
141
142
        $iQtyInvoiced = 0;
143
        foreach ($oOrder->getAllItems() as $oItem) { // add invoice items for all order items
144
            if (($oOrder instanceof Order && $oItem->isDummy() === false) || ($oOrder instanceof Quote && $oItem->getParentItemId() === null)) { // prevent variant-products of adding 2 items
145
                $this->addProductItem($oItem, $aPositions); // add product invoice params to request
0 ignored issues
show
Bug introduced by
It seems like $aPositions can also be of type false; however, parameter $aPositions of Payone\Core\Model\Api\Invoice::addProductItem() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

145
                $this->addProductItem($oItem, /** @scrutinizer ignore-type */ $aPositions); // add product invoice params to request
Loading history...
146
            }
147
            $iQtyInvoiced += $oItem->getOrigData('qty_invoiced'); // get data pre-capture
148
        }
149
150
        $blFirstCapture = true; // Is first capture?
151
        if ($iQtyInvoiced > 0) {
152
            $blFirstCapture = false;
153
        }
154
155
        if ($aPositions === false || $blFirstCapture === true || $blDebit === true) {
156
            $this->addShippingItem($oOrder, $aPositions, $blDebit, $dShippingCosts); // add shipping invoice params to request
0 ignored issues
show
Bug introduced by
It seems like $dShippingCosts can also be of type false; however, parameter $dShippingCosts of Payone\Core\Model\Api\Invoice::addShippingItem() does only seem to accept double, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

156
            $this->addShippingItem($oOrder, $aPositions, $blDebit, /** @scrutinizer ignore-type */ $dShippingCosts); // add shipping invoice params to request
Loading history...
Bug introduced by
It seems like $aPositions can also be of type false; however, parameter $aPositions of Payone\Core\Model\Api\Invoice::addShippingItem() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

156
            $this->addShippingItem($oOrder, /** @scrutinizer ignore-type */ $aPositions, $blDebit, $dShippingCosts); // add shipping invoice params to request
Loading history...
157
            $this->addDiscountItem($oOrder, $aPositions, $blDebit); // add discount invoice params to request
0 ignored issues
show
Bug introduced by
It seems like $aPositions can also be of type false; however, parameter $aPositions of Payone\Core\Model\Api\Invoice::addDiscountItem() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

157
            $this->addDiscountItem($oOrder, /** @scrutinizer ignore-type */ $aPositions, $blDebit); // add discount invoice params to request
Loading history...
158
            $this->addGiftCardItem($oOrder);  // add gift card invoice params to request
159
            $this->addAmastyGiftcards($oOrder, $aPositions, $blDebit); // add amasty giftcard invoice params to request
0 ignored issues
show
Bug introduced by
It seems like $aPositions can also be of type false; however, parameter $aPositions of Payone\Core\Model\Api\In...e::addAmastyGiftcards() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

159
            $this->addAmastyGiftcards($oOrder, /** @scrutinizer ignore-type */ $aPositions, $blDebit); // add amasty giftcard invoice params to request
Loading history...
160
        }
161
        return $this->dAmount;
162
    }
163
164
    /**
165
     * Add invoicing item for a product
166
     *
167
     * @param  \Magento\Sales\Model\Order\Item $oItem
0 ignored issues
show
Bug introduced by
The type Magento\Sales\Model\Order\Item was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
168
     * @param  array $aPositions
169
     * @return void
170
     */
171
    protected function addProductItem($oItem, $aPositions)
172
    {
173
        $sPositionKey = $oItem->getProductId().$oItem->getSku();
174
        if ($aPositions === false || array_key_exists($sPositionKey, $aPositions) !== false) { // full or single-invoice?
175
            $dItemAmount = $oItem->getQtyOrdered(); // get ordered item amount
176
            if ($oItem instanceof QuoteItem) {
177
                $dItemAmount = $oItem->getQty();
178
            }
179
            if ($aPositions !== false && array_key_exists($sPositionKey, $aPositions) !== false) { // product existing in single-invoice?
180
                $dItemAmount = $aPositions[$sPositionKey]; // use amount from single-invoice
181
            }
182
            $iAmount = $this->convertItemAmount($dItemAmount);
183
            $dPrice = $oItem->getBasePriceInclTax();
184
            if ($this->toolkitHelper->getConfigParam('currency') == 'display') {
185
                $dPrice = $oItem->getPriceInclTax();
186
            }
187
            $this->addInvoicePosition($oItem->getSku(), $dPrice, 'goods', $iAmount, $oItem->getName(), $oItem->getTaxPercent()); // add invoice params to request
188
            if ($this->dTax === false) { // is dTax not set yet?
0 ignored issues
show
introduced by
The condition $this->dTax === false is always false.
Loading history...
189
                $this->dTax = $oItem->getTaxPercent(); // set the tax for following entities which dont have the vat attached to it
190
            }
191
        }
192
    }
193
194
    protected function addGiftCardItem($oOrder)
195
    {
196
        $giftCards = json_decode($oOrder->getData('gift_cards'), true);
197
198
        if(empty($giftCards) || !is_array($giftCards)) {
199
            return;
200
        }
201
202
        foreach($giftCards as $giftCard) {
203
            $this->addInvoicePosition($giftCard['c'], -$giftCard['authorized'], 'voucher', 1, 'Giftcard', 0);
204
        }
205
    }
206
207
    /**
208
     * Add invoicing item for shipping
209
     *
210
     * @param  Order    $oOrder
211
     * @param  array    $aPositions
212
     * @param  bool     $blDebit
213
     * @param  double   $dShippingCosts
214
     * @return void
215
     */
216
    protected function addShippingItem($oOrder, $aPositions, $blDebit, $dShippingCosts = false)
217
    {
218
        $dPrice = $dShippingCosts;
219
        if ($dPrice === false) {
220
            $dPrice = $oOrder->getBaseShippingInclTax();
221
            if ($this->toolkitHelper->getConfigParam('currency') == 'display') {
222
                $dPrice = $oOrder->getShippingInclTax();
223
            }
224
        }
225
226
        // shipping costs existing or given for partial captures/debits?
227
        if ($dPrice != 0 && ($aPositions === false || ($blDebit === false || array_key_exists('delcost', $aPositions) !== false))) {
228
            if ($aPositions !== false && array_key_exists('delcost', $aPositions) !== false) { // product existing in single-invoice?
229
                $dPrice = $aPositions['delcost'];
230
            }
231
            $sDelDesc = __('Surcharge').' '.__('Shipping Costs'); // default description
0 ignored issues
show
Bug introduced by
The function __ was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

231
            $sDelDesc = /** @scrutinizer ignore-call */ __('Surcharge').' '.__('Shipping Costs'); // default description
Loading history...
232
            if ($dPrice < 0) { // negative shipping cost
233
                $sDelDesc = __('Deduction').' '.__('Shipping Costs'); // change item description to deduction
234
            }
235
            $sShippingSku = $this->toolkitHelper->getConfigParam('sku', 'costs', 'payone_misc'); // get configured shipping SKU
236
            $this->addInvoicePosition($sShippingSku, $dPrice, 'shipment', 1, $sDelDesc, $this->dTax); // add invoice params to request
237
        }
238
    }
239
240
    /**
241
     * Add invoicing item for discounts
242
     *
243
     * @param  Order $oOrder
244
     * @param  array $aPositions
245
     * @param  bool  $blDebit
246
     * @return void
247
     */
248
    protected function addDiscountItem($oOrder, $aPositions, $blDebit)
249
    {
250
        // discount costs existing or given for partial captures/debit?
251
        $dTransmitDiscount = $oOrder->getBaseDiscountAmount();
252
        if ($this->toolkitHelper->getConfigParam('currency') == 'display') {
253
            $dTransmitDiscount = $oOrder->getDiscountAmount();
254
        }
255
256
        if ($oOrder instanceof Quote) {
257
            $dTransmitDiscount = $oOrder->getBaseSubtotal() - $oOrder->getBaseSubtotalWithDiscount();
258
            if ($this->toolkitHelper->getConfigParam('currency') == 'display') {
259
                $dTransmitDiscount = $oOrder->getSubtotal() - $oOrder->getSubtotalWithDiscount();
260
            }
261
        }
262
263
        if ($dTransmitDiscount != 0 && ($aPositions === false || ($blDebit === false || array_key_exists('discount', $aPositions) !== false))) {
264
            if ($aPositions !== false && array_key_exists('discount', $aPositions) !== false) {
265
                $dTransmitDiscount = $aPositions['discount'];
266
            }
267
            $dDiscount = $this->toolkitHelper->formatNumber($dTransmitDiscount); // format discount
268
            if ($aPositions === false && $this->amastyHelper->hasAmastyGiftcards($oOrder->getQuoteId()) === false) {
0 ignored issues
show
introduced by
The condition $aPositions === false is always false.
Loading history...
269
                // The calculations broken down to single items of Magento2 are unprecise and the Payone API will send an error if
270
                // the calculated positions don't match, so we compensate for rounding-problems here
271
                $dTotal = $oOrder->getBaseGrandTotal();
272
                if ($this->toolkitHelper->getConfigParam('currency') == 'display') {
273
                    $dTotal = $oOrder->getGrandTotal();
274
                }
275
                $dDiff = ($this->dAmount + $dTransmitDiscount - $dTotal); // calc rounding discrepancy
276
                $dDiscount -= $dDiff; // subtract difference from discount
277
            }
278
            $sDiscountSku = $this->toolkitHelper->getConfigParam('sku', 'discount', 'payone_misc'); // get configured discount SKU
279
            $sDesc = (string)__('Discount'); // default description
0 ignored issues
show
Bug introduced by
The function __ was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

279
            $sDesc = (string)/** @scrutinizer ignore-call */ __('Discount'); // default description
Loading history...
280
            if ($oOrder->getCouponCode()) {// was a coupon code used?
281
                $sDiscountSku = $this->toolkitHelper->getConfigParam('sku', 'voucher', 'payone_misc'); // get configured voucher SKU
282
                $sDesc = (string)__('Coupon').' - '.$oOrder->getCouponCode(); // add counpon code to description
283
            }
284
            $this->addInvoicePosition($sDiscountSku, $dDiscount, 'voucher', 1, $sDesc, $this->dTax); // add invoice params to request
0 ignored issues
show
Bug introduced by
$dDiscount of type string is incompatible with the type double expected by parameter $dPrice of Payone\Core\Model\Api\In...e::addInvoicePosition(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

284
            $this->addInvoicePosition($sDiscountSku, /** @scrutinizer ignore-type */ $dDiscount, 'voucher', 1, $sDesc, $this->dTax); // add invoice params to request
Loading history...
285
        }
286
    }
287
288
    /**
289
     * Adding amasty giftcards to request
290
     *
291
     * @param  Order $oOrder
292
     * @param  array $aPositions
293
     * @param  bool  $blDebit
294
     * @return void
295
     */
296
    protected function addAmastyGiftcards($oOrder, $aPositions, $blDebit)
297
    {
298
        $aGiftCards = $this->amastyHelper->getAmastyGiftCards($oOrder->getQuoteId());
299
        for ($i = 0; $i < count($aGiftCards); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
300
            $aGiftCard = $aGiftCards[$i];
301
            $blIsLastGiftcard = false;
302
            if ($i + 1 == count($aGiftCards)) {
303
                $blIsLastGiftcard = true;
304
            }
305
306
            $dTransmitDiscount = $aGiftCard['base_gift_amount'];
307
            if ($this->toolkitHelper->getConfigParam('currency') == 'display') {
308
                $dTransmitDiscount = $aGiftCard['gift_amount'];
309
            }
310
            if ($dTransmitDiscount != 0 && ($aPositions === false || ($blDebit === false || array_key_exists('discount', $aPositions) !== false))) {
311
                $dTransmitDiscount = $dTransmitDiscount * -1;
312
                $dDiscount = $this->toolkitHelper->formatNumber($dTransmitDiscount); // format discount
313
                if ($aPositions === false && $blIsLastGiftcard === true) {
314
                    // The calculations broken down to single items of Magento2 are unprecise and the Payone API will send an error if
315
                    // the calculated positions don't match, so we compensate for rounding-problems here
316
                    $dTotal = $oOrder->getBaseGrandTotal();
317
                    if ($this->toolkitHelper->getConfigParam('currency') == 'display') {
318
                        $dTotal = $oOrder->getGrandTotal();
319
                    }
320
                    $dDiff = ($this->dAmount + $dTransmitDiscount - $dTotal); // calc rounding discrepancy
321
                    $dDiscount -= $dDiff; // subtract difference from discount
322
                }
323
324
                if ($dDiscount != 0) {
325
                    $sDiscountSku = $this->toolkitHelper->getConfigParam('sku', 'voucher', 'payone_misc'); // get configured voucher SKU
326
                    $sDesc = (string)__('Amasty Coupon').' - '.$aGiftCard['code']; // add counpon code to description
0 ignored issues
show
Bug introduced by
The function __ was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

326
                    $sDesc = (string)/** @scrutinizer ignore-call */ __('Amasty Coupon').' - '.$aGiftCard['code']; // add counpon code to description
Loading history...
327
                    $this->addInvoicePosition($sDiscountSku, $dDiscount, 'voucher', 1, $sDesc, $this->dTax); // add invoice params to request
0 ignored issues
show
Bug introduced by
$dDiscount of type string is incompatible with the type double expected by parameter $dPrice of Payone\Core\Model\Api\In...e::addInvoicePosition(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

327
                    $this->addInvoicePosition($sDiscountSku, /** @scrutinizer ignore-type */ $dDiscount, 'voucher', 1, $sDesc, $this->dTax); // add invoice params to request
Loading history...
328
                }
329
            }
330
        }
331
    }
332
333
    /**
334
     * Check if item amount has decimal places
335
     * Throw exception if given amount is no integer
336
     *
337
     * @param  double $dItemAmount
338
     * @throws \InvalidArgumentException
339
     * @return int
340
     */
341
    protected function convertItemAmount($dItemAmount)
342
    {
343
        if (fmod(floatval($dItemAmount), 1.0) > 0) { // input does not represent an integer
344
            $sErrorMessage = "Unable to use floating point values for item amounts! Parameter was: ";
345
            throw new \InvalidArgumentException($sErrorMessage . strval($dItemAmount), 1);
346
        } else { // return the integer value
347
            return intval($dItemAmount);
348
        }
349
    }
350
}
351