Test Failed
Push — master ( d3c8d8...d4fc42 )
by JAIME ELMER
02:02
created

InvoiceItems::getTotalUnaffectedOperations()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 2
rs 10
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * MÓDULO DE EMISIÓN ELECTRÓNICA F72X
5
 * UBL 2.1
6
 * Version 1.1
7
 * 
8
 * Copyright 2018, Jaime Cruz
9
 */
10
11
namespace F72X\Sunat;
12
13
use F72X\Tools\XMatrix;
14
15
class InvoiceItems extends XMatrix {
16
17
    /** @CAT5 Tipo de impuesto*/
18
    const CAT5_IGV   = '1000';
19
    const CAT5_IVAP  = '1016';
20
    const CAT5_ISC   = '2000';
21
    const CAT5_EXP   = '9995';
22
    const CAT5_GRA   = '9996';
23
    const CAT5_EXO   = '9997';
24
    const CAT5_INA   = '9998';
25
    const CAT5_OTROS = '9999';
26
    
27
    const COL_PRODUCT_CODE          = 0;
28
    const COL_UNPSC                 = 1;
29
    const COL_UNIT_CODE             = 2;
30
    const COL_QUANTITY              = 3;
31
    const COL_DESCRIPTION           = 4;
32
    const COL_CURRENCY_CODE         = 5;
33
    const COL_UNIT_VALUE            = 6;
34
    const COL_UNIT_BILLABLE_VALUE   = 7;
35
    const COL_UNIT_TAXED_VALUE      = 8;
36
    // Códigos de catálogos predefinidos
37
    const COL_PRICE_TYPE            = 9;
38
    const COL_TAX_TYPE              = 10;
39
    const COL_IGV_AFFECTATION       = 11;
40
41
    const COL_ITEM_VALUE            = 12;
42
    const COL_ITEM_BILLABLE_VALUE   = 13;
43
    // Cargos y descuentos
44
    const COL_ALLOWANCES            = 14;
45
    const COL_ALLOWANCES_AMOUNT     = 15;
46
    const COL_CHARGES               = 16;
47
    const COL_CHARGES_AMOUNT        = 17;
48
49
    const COL_ITEM_TAXABLE_AMOUNT   = 18;
50
51
    const COL_IGV                   = 19;
52
    const COL_ITEM_PAYABLE_AMOUNT   = 20;
53
54
    protected $columns = [
55
        'COL_PRODUCT_CODE',
56
        'COL_UNPSC',
57
        'COL_UNIT_CODE',
58
        'COL_QUANTITY',
59
        '============COL_DESCRIPTION============',
60
        'COL_CURRENCY_CODE',
61
        'COL_UNIT_VALUE',
62
        'COL_UNIT_BILLABLE_VALUE',
63
        'COL_UNIT_TAXED_VALUE',
64
        'COL_PRICE_TYPE[CAT#16]',
65
        'COL_TAX_TYPE[CAT#5]',
66
        'COL_IGV_AFFECTATION[CAT#7]',
67
        'COL_ITEM_VALUE [COL_UNIT_VALUE*COL_QUANTITY]',
68
        'COL_ITEM_BILLABLE_VALUE [COL_UNIT_BILLABLE_VALUE*COL_QUANTITY]',
69
        'COL_ALLOWANCES',
70
        'COL_ALLOWANCES_AMOUNT [COL_ITEM_BILLABLE_VALUE*k]',
71
        'COL_CHARGES',
72
        'COL_CHARGES_AMOUNT [COL_ITEM_BILLABLE_VALUE*k]',
73
        'COL_ITEM_TAXABLE_AMOUNT(operacion_gravada) [COL_ITEM_BILLABLE_VALUE-descuentos+cargos]',
74
        'COL_IGV(operacion_gravada) [COL_ITEM_BILLABLE_VALUE*IGV_PERCENT]',
75
        'COL_ITEM_PAYABLE_AMOUNT [base_imponible+IGV]'
76
    ];
77
78
    public function populate($items,  $currencyCode) {
79
        foreach ($items as $idx => $item) {
80
            $allowances     = isset($item['allowances']) ? $item['allowances'] : [];
81
            $charges        = isset($item['charges']) ? $item['charges'] : [];
82
            $igvAffectCode  = $item['igvAffectationCode'];
83
            $priceType      = $item['priceType'];   // Tipo de precio
84
            $grossUnitValue = $item['unitValue'];
85
            $igvIncluded    = $item['igvIncluded'];
86
            
87
            $unitValue         = $this->calcUnitValue($igvAffectCode, $grossUnitValue, $igvIncluded);      // Valor unitario
88
            $unitTaxedValue    = $this->calcUnitTaxedValue($igvAffectCode, $grossUnitValue, $igvIncluded); // Valor unitario incluyendo impuestos si son aplicables
89
            $unitBillableValue = $this->calcUnitBillableValue($unitValue, $priceType);                     // Valor unitario facturable
90
            $quantity          = $item['quantity'];  // Cantidad
91
92
            $itemValue            = $unitValue * $quantity;         // Valor de item
93
            $itemBillableValue    = $unitBillableValue * $quantity; // Valor de item
94
            $itemAllowancesAmount = Operations::getTotalAllowanceCharge($itemBillableValue, $allowances); // Descuentos de item
95
            $itemChargesAmount    = Operations::getTotalAllowanceCharge($itemValue, $charges);            // Cargos de item
96
            $itemTaxableAmount    = $this->calcItemTaxableAmount($itemValue, $priceType, $allowances, $charges);         // Valor de venta del ítem = (Valor del item - Descuentos + Cargos), 0 si el valor del item es referencial!
97
            $igvAmount            = $this->calcIgvAmount($igvAffectCode, $itemTaxableAmount); // Afectación al IGV por item
0 ignored issues
show
Bug introduced by
$itemTaxableAmount of type integer|double is incompatible with the type F72X\Sunat\type expected by parameter $baseAmount of F72X\Sunat\InvoiceItems::calcIgvAmount(). ( Ignorable by Annotation )

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

97
            $igvAmount            = $this->calcIgvAmount($igvAffectCode, /** @scrutinizer ignore-type */ $itemTaxableAmount); // Afectación al IGV por item
Loading history...
98
            
99
            $itemIgvTaxed         = $itemBillableValue + $igvAmount;
100
            
101
            $this->set(self::COL_PRODUCT_CODE,        $idx, $item['productCode']);
102
            $this->set(self::COL_UNPSC,               $idx, $item['sunatProductCode']);
103
            $this->set(self::COL_UNIT_CODE,           $idx, $item['unitCode']);
104
            $this->set(self::COL_QUANTITY,            $idx, $quantity);
105
            $this->set(self::COL_DESCRIPTION,         $idx, $item['description']);
106
            $this->set(self::COL_CURRENCY_CODE,       $idx, $currencyCode);
107
            // Códigos de catálogos predefinidos
108
            $this->set(self::COL_PRICE_TYPE,          $idx, $priceType);
109
            $this->set(self::COL_TAX_TYPE,            $idx, $item['taxType']);
110
            $this->set(self::COL_IGV_AFFECTATION,     $idx, $item['igvAffectationCode']);
111
112
            $this->set(self::COL_UNIT_VALUE,          $idx, $unitValue);
113
            $this->set(self::COL_UNIT_BILLABLE_VALUE, $idx, $unitBillableValue);
114
            $this->set(self::COL_UNIT_TAXED_VALUE,    $idx, $unitTaxedValue);
115
            $this->set(self::COL_ITEM_VALUE,          $idx, $itemValue);
116
            $this->set(self::COL_ITEM_BILLABLE_VALUE, $idx, $itemBillableValue);
117
            $this->set(self::COL_ALLOWANCES,          $idx, $allowances);
118
            $this->set(self::COL_ALLOWANCES_AMOUNT,   $idx, $itemAllowancesAmount);
119
            $this->set(self::COL_CHARGES,             $idx, $charges);
120
            $this->set(self::COL_CHARGES_AMOUNT,      $idx, $itemChargesAmount);
121
            $this->set(self::COL_ITEM_TAXABLE_AMOUNT, $idx, $itemTaxableAmount);
122
            $this->set(self::COL_IGV,                 $idx, $igvAmount);
123
            $this->set(self::COL_ITEM_PAYABLE_AMOUNT, $idx, $itemIgvTaxed);
124
        }
125
    }
126
127
    /**
128
     * Valor unitario: se extrae el IGV si el valor es afectado por este y si se
129
     * encuentra incluido en el monto que se recibe como segundo parametro.
130
     * 
131
     * @param string $igvAffectCode
132
     * @param float $baseAmount
133
     * @param boolean $igvIncluded
134
     * @return float
135
     */
136
    private function calcUnitValue($igvAffectCode, $baseAmount, $igvIncluded) {
137
        $amount = $baseAmount;
138
        if (Operations::isIGVAffected($igvAffectCode) && $igvIncluded) {
0 ignored issues
show
Bug introduced by
$igvAffectCode of type string is incompatible with the type F72X\Sunat\type expected by parameter $igvAffectCode of F72X\Sunat\Operations::isIGVAffected(). ( Ignorable by Annotation )

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

138
        if (Operations::isIGVAffected(/** @scrutinizer ignore-type */ $igvAffectCode) && $igvIncluded) {
Loading history...
139
            $amount = $baseAmount / (1 + SunatVars::IGV);
140
        }
141
        return $amount;
142
    }
143
144
    /**
145
     * Valor unitario pagable: se aplica el IGV si este es aplicable y si aún no
146
     * ha sido incluido en el monto que se recibe como segundo parametro.
147
     * 
148
     * @param string $igvAffectCode
149
     * @param float $baseAmount
150
     * @param boolean $igvIncluded
151
     * @return float
152
     */
153
    private function calcUnitTaxedValue($igvAffectCode, $baseAmount, $igvIncluded) {
154
        $amount = $baseAmount;
155
        if (Operations::isIGVAffected($igvAffectCode) && !$igvIncluded) {
0 ignored issues
show
Bug introduced by
$igvAffectCode of type string is incompatible with the type F72X\Sunat\type expected by parameter $igvAffectCode of F72X\Sunat\Operations::isIGVAffected(). ( Ignorable by Annotation )

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

155
        if (Operations::isIGVAffected(/** @scrutinizer ignore-type */ $igvAffectCode) && !$igvIncluded) {
Loading history...
156
            $amount = $baseAmount * (1 + SunatVars::IGV);
157
        }
158
        return $amount;
159
    }
160
161
    /**
162
     * 
163
     * Valor facturable
164
     * 
165
     * El valor que figurará en el comprobante como valor unitario a pagar
166
     * 
167
     * @param float $baseAmount
168
     * @param boolean $priceType
169
     * @return float
170
     */
171
    private function calcUnitBillableValue($baseAmount, $priceType) {
172
        return ($priceType === Catalogo::CAT16_REF_VALUE) ? 0 : $baseAmount;
0 ignored issues
show
introduced by
The condition $priceType === F72X\Suna...talogo::CAT16_REF_VALUE is always false.
Loading history...
173
    }
174
175
    private function calcItemTaxableAmount($baseAmount, $priceType, $allowances, $charges) {
176
        // Valor de venta del ítem = (Valor del item - Descuentos + Cargos)
177
        if ($priceType === Catalogo::CAT16_UNITARY_PRICE) {
178
            $amount = Operations::applyAllowancesAndCharges($baseAmount, $allowances, $charges);
179
        } else {
180
            // 0 si el valor del item es referencial!
181
            $amount = 0;
182
        }
183
        return $amount;
184
    }
185
186
    /**
187
     * IGV: Calcula el monto del IGV, si este es aplicable.
188
     * @param string $igvAffectCode
189
     * @param type $baseAmount
0 ignored issues
show
Bug introduced by
The type F72X\Sunat\type 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...
190
     * @return float
191
     */
192
    private function calcIgvAmount($igvAffectCode, $baseAmount) {
193
        return Operations::isIGVAffected($igvAffectCode) ? Operations::calcIGV($baseAmount) : 0;
0 ignored issues
show
Bug introduced by
$baseAmount of type F72X\Sunat\type is incompatible with the type double expected by parameter $baseAmount of F72X\Sunat\Operations::calcIGV(). ( Ignorable by Annotation )

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

193
        return Operations::isIGVAffected($igvAffectCode) ? Operations::calcIGV(/** @scrutinizer ignore-type */ $baseAmount) : 0;
Loading history...
Bug introduced by
$igvAffectCode of type string is incompatible with the type F72X\Sunat\type expected by parameter $igvAffectCode of F72X\Sunat\Operations::isIGVAffected(). ( Ignorable by Annotation )

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

193
        return Operations::isIGVAffected(/** @scrutinizer ignore-type */ $igvAffectCode) ? Operations::calcIGV($baseAmount) : 0;
Loading history...
194
    }
195
196
    /**
197
     * Codigo de producto
198
     * 
199
     * @param int $rowIndex
200
     * @return string
201
     */
202
    public function getProductCode($rowIndex) {
203
        return $this->get(self::COL_PRODUCT_CODE, $rowIndex);
204
    }
205
206
    /**
207
     * United Nations Standard Products and Services Code
208
     * Codigo de producto SUNAT de acuerdo con UNSPSC v14_0801
209
     * @param int $rowIndex
210
     * @return string
211
     */
212
    public function getUNPSC($rowIndex) {
213
        return $this->get(self::COL_UNPSC, $rowIndex);
214
    }
215
216
    public function getUnitCode($rowIndex) {
217
        return $this->get(self::COL_UNIT_CODE, $rowIndex);
218
    }
219
220
    public function getQunatity($rowIndex) {
221
        return $this->get(self::COL_QUANTITY, $rowIndex);
222
    }
223
224
    public function getDescription($rowIndex) {
225
        return $this->get(self::COL_DESCRIPTION, $rowIndex);
226
    }
227
228
    public function getCurrencyCode($rowIndex) {
229
        return $this->get(self::COL_CURRENCY_CODE, $rowIndex);
230
    }
231
232
    public function getPriceTypeCode($rowIndex) {
233
        return $this->get(self::COL_PRICE_TYPE, $rowIndex);
234
    }
235
236
    public function getTaxTypeCode($rowIndex) {
237
        return $this->get(self::COL_TAX_TYPE, $rowIndex);
238
    }
239
240
    public function getIgvAffectationCode($rowIndex) {
241
        return $this->get(self::COL_IGV_AFFECTATION, $rowIndex);
242
    }
243
244
    public function getUnitValue($rowIndex) {
245
        return $this->get(self::COL_UNIT_VALUE, $rowIndex);
246
    }
247
248
    public function getUnitBillableValue($rowIndex) {
249
        return $this->get(self::COL_UNIT_BILLABLE_VALUE, $rowIndex);
250
    }
251
252
    public function getUnitTaxedValue($rowIndex) {
253
        return $this->get(self::COL_UNIT_TAXED_VALUE, $rowIndex);
254
    }
255
256
    public function getItemValue($rowIndex) {
257
        return $this->get(self::COL_ITEM_VALUE, $rowIndex);
258
    }
259
260
    public function getItemBillableValue($rowIndex) {
261
        return $this->get(self::COL_ITEM_BILLABLE_VALUE, $rowIndex);
262
    }
263
264
    public function getAllowances($rowIndex) {
265
        return $this->get(self::COL_ALLOWANCES, $rowIndex);
266
    }
267
268
    public function getAllowancesAmount($rowIndex) {
269
        return $this->get(self::COL_ALLOWANCES_AMOUNT, $rowIndex);
270
    }
271
272
    public function getCharges($rowIndex) {
273
        return $this->get(self::COL_CHARGES, $rowIndex);
274
    }
275
276
    public function getChargesAmount($rowIndex) {
277
        return $this->get(self::COL_CHARGES_AMOUNT, $rowIndex);
278
    }
279
280
    public function getTaxableAmount($rowIndex) {
281
        return $this->get(self::COL_ITEM_TAXABLE_AMOUNT, $rowIndex);
282
    }
283
284
    public function getIgv($rowIndex) {
285
        return $this->get(self::COL_IGV, $rowIndex);
286
    }
287
288
    public function getPayableAmount($rowIndex) {
289
        return $this->get(self::COL_ITEM_PAYABLE_AMOUNT, $rowIndex);
290
    }
291
292
    /**
293
     * Total operaciones gravadas
294
     * @return float
295
     */
296
    public function getTotalTaxableOperations() {
297
        return $this->getTotalOperations(Catalogo::CAT5_IGV, self::COL_ITEM_TAXABLE_AMOUNT);
298
    }
299
300
    /**
301
     * Total operaciones gratuitas
302
     * @return float
303
     */
304
    public function getTotalFreeOperations() {
305
        return $this->getTotalOperations(Catalogo::CAT5_GRA, self::COL_ITEM_VALUE);
306
    }
307
308
    /**
309
     * Total operaciones exoneradas
310
     * @return float
311
     */
312
    public function getTotalExemptedOperations() {
313
        return $this->getTotalOperations(Catalogo::CAT5_EXO, self::COL_ITEM_TAXABLE_AMOUNT);
314
    }
315
316
    /**
317
     * Total operaciones inafectas
318
     * @return float
319
     */
320
    public function getTotalUnaffectedOperations() {
321
        return $this->getTotalOperations(Catalogo::CAT5_INA, self::COL_ITEM_VALUE);
322
    }
323
324
    private function getTotalOperations($taxType, $columnIndex) {
325
        $total = 0;
326
        $data = $this->getMatrix();
327
        foreach ($data as $row) {
328
            $total += ($row[self::COL_TAX_TYPE] === $taxType) ? $row[$columnIndex] : 0;
329
        }
330
        return $total;
331
    }
332
333
    /**
334
     * Todos de los descuentos.
335
     * @return float
336
     */
337
    public function getTotalAllowances() {
338
        return $this->sum(self::COL_ALLOWANCES_AMOUNT);
339
    }
340
341
    /**
342
     * Todos de los descuentos.
343
     * @return float
344
     */
345
    public function getTotalCharges() {
346
        return $this->sum(self::COL_CHARGES_AMOUNT);
347
    }
348
349
    /**
350
     * Valor total
351
     * Suma los valores de cada item
352
     * = Cantidad x (Valor unitario | Valor fererencial)
353
     * @return float
354
     */
355
    public function getTotalValue() {
356
        return $this->sum(self::COL_ITEM_VALUE);
357
    }
358
359
    /**
360
     * Suma de valores de cada item sin incluir aquellos por los que no se cobra.
361
     * @return float
362
     */
363
    public function getTotalBillableValue() {
364
        return $this->sum(self::COL_ITEM_BILLABLE_VALUE);
365
    }
366
367
    /**
368
     * Total IGV
369
     * @return float
370
     */
371
    public function getTotalIgv() {
372
        return $this->sum(self::COL_IGV);
373
    }
374
375
    /**
376
     * Base imponible total lista para aplicar impuestos
377
     * @return float
378
     */
379
    public function getTotalTaxableAmount() {
380
        return $this->sum(self::COL_ITEM_TAXABLE_AMOUNT);
381
    }
382
383
    /**
384
     * Total de items
385
     * @return int
386
     */
387
    public function getCount() {
388
        return $this->countRows();
389
    }
390
    /**
391
     * 
392
     * @param float $amount
393
     * @return float
394
     */
395
    private function applyAllowancesAndCharges($amount) {
396
        return Operations::applyAllowancesAndCharges($amount, $this->allowances, $this->charges);
0 ignored issues
show
Bug Best Practice introduced by
The property allowances does not exist on F72X\Sunat\InvoiceItems. Did you maybe forget to declare it?
Loading history...
Bug Best Practice introduced by
The property charges does not exist on F72X\Sunat\InvoiceItems. Did you maybe forget to declare it?
Loading history...
397
    }
398
}
399