Test Failed
Push — master ( bc5469...762165 )
by JAIME ELMER
02:21
created

InvoiceItems::applyAllowancesAndCharges()   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 1
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
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) {
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) {
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 float $baseAmount
190
     * @return float
191
     */
192
    private function calcIgvAmount($igvAffectCode, $baseAmount) {
193
        return Operations::isIGVAffected($igvAffectCode) ? Operations::calcIGV($baseAmount) : 0;
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