Passed
Branch develop (5fc816)
by JAIME ELMER
01:58
created

InvoiceItems::getCharges()   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
    const COL_PRODUCT_CODE          = 0;
18
    const COL_UNPSC                 = 1;
19
    const COL_UNIT_CODE             = 2;
20
    const COL_QUANTITY              = 3;
21
    const COL_DESCRIPTION           = 4;
22
    const COL_CURRENCY_CODE         = 5;
23
    const COL_UNIT_VALUE            = 6;
24
    const COL_UNIT_BILLABLE_VALUE   = 7;
25
    const COL_UNIT_TAXED_VALUE      = 8;
26
    // Códigos de catálogos predefinidos
27
    const COL_PRICE_TYPE            = 9;
28
    const COL_TAX_TYPE              = 10;
29
    const COL_IGV_AFFECTATION       = 11;
30
31
    const COL_ITEM_VALUE            = 12;
32
    const COL_ITEM_BILLABLE_VALUE   = 13;
33
    // Cargos y descuentos
34
    const COL_ALLOWANCES_CHARGES    = 14;
35
    const COL_ALLOWANCES_AMOUNT     = 15;
36
    const COL_CHARGES_AMOUNT        = 16;
37
38
    const COL_ITEM_TAXABLE_AMOUNT   = 17;
39
40
    const COL_IGV                   = 18;
41
    const COL_ITEM_PAYABLE_AMOUNT   = 19;
42
43
    protected $columns = [
44
        'COL_PRODUCT_CODE',
45
        'COL_UNPSC',
46
        'COL_UNIT_CODE',
47
        'COL_QUANTITY',
48
        '============COL_DESCRIPTION============',
49
        'COL_CURRENCY_CODE',
50
        'COL_UNIT_VALUE',
51
        'COL_UNIT_BILLABLE_VALUE',
52
        'COL_UNIT_TAXED_VALUE',
53
        'COL_PRICE_TYPE[CAT#16]',
54
        'COL_TAX_TYPE[CAT#5]',
55
        'COL_IGV_AFFECTATION[CAT#7]',
56
        'COL_ITEM_VALUE [COL_UNIT_VALUE*COL_QUANTITY]',
57
        'COL_ITEM_BILLABLE_VALUE [COL_UNIT_BILLABLE_VALUE*COL_QUANTITY]',
58
        'COL_ALLOWANCES_CHARGES',
59
        'COL_ALLOWANCES_AMOUNT [COL_ITEM_BILLABLE_VALUE*k]',
60
        'COL_CHARGES_AMOUNT [COL_ITEM_BILLABLE_VALUE*k]',
61
        'COL_ITEM_TAXABLE_AMOUNT(operacion_gravada) [COL_ITEM_BILLABLE_VALUE-descuentos+cargos]',
62
        'COL_IGV(operacion_gravada) [COL_ITEM_BILLABLE_VALUE*IGV_PERCENT]',
63
        'COL_ITEM_PAYABLE_AMOUNT [base_imponible+IGV]'
64
    ];
65
66
    public function populate($items, $currencyCode) {
67
        foreach ($items as $idx => $item) {
68
            $ac             = isset($item['allowancesCharges']) ? $item['allowancesCharges'] : [];
69
            $igvAffectCode  = $item['igvAffectationCode'];
70
            $priceType      = $item['priceType']; // Tipo de precio
71
            $grossUnitValue = $item['unitValue'];
72
            $igvIncluded    = $item['igvIncluded'];
73
            
74
            $unitValue         = $this->calcUnitValue($igvAffectCode, $grossUnitValue, $igvIncluded);      // Valor unitario
75
            $unitTaxedValue    = $this->calcUnitTaxedValue($igvAffectCode, $grossUnitValue, $igvIncluded); // Valor unitario incluyendo impuestos si son aplicables
76
            $unitBillableValue = $this->calcUnitBillableValue($unitValue, $priceType);                     // Valor unitario facturable
77
            $quantity          = $item['quantity']; // Cantidad
78
79
            $itemValue            = $unitValue * $quantity;         // Valor de item
80
            $itemBillableValue    = $unitBillableValue * $quantity; // Valor de item
81
            $itemAllowancesAmount = Operations::getTotalAllowances($itemBillableValue, $ac); // Descuentos de item
82
            $itemChargesAmount    = Operations::getTotalCharges($itemValue, $ac);            // Cargos de item
83
            $itemTaxableAmount    = $this->calcItemTaxableAmount($itemValue, $priceType, $ac);         // Valor de venta del ítem = (Valor del item - Descuentos + Cargos), 0 si el valor del item es referencial!
84
            $igvAmount            = $this->calcIgvAmount($igvAffectCode, $itemTaxableAmount); // Afectación al IGV por item
85
            
86
            $itemIgvTaxed         = $itemBillableValue + $igvAmount;
87
            
88
            $this->set(self::COL_PRODUCT_CODE,        $idx, $item['productCode']);
89
            $this->set(self::COL_UNPSC,               $idx, $item['sunatProductCode']);
90
            $this->set(self::COL_UNIT_CODE,           $idx, $item['unitCode']);
91
            $this->set(self::COL_QUANTITY,            $idx, $quantity);
92
            $this->set(self::COL_DESCRIPTION,         $idx, $item['description']);
93
            $this->set(self::COL_CURRENCY_CODE,       $idx, $currencyCode);
94
            // Códigos de catálogos predefinidos
95
            $this->set(self::COL_PRICE_TYPE,          $idx, $priceType);
96
            $this->set(self::COL_TAX_TYPE,            $idx, $item['taxType']);
97
            $this->set(self::COL_IGV_AFFECTATION,     $idx, $item['igvAffectationCode']);
98
99
            $this->set(self::COL_UNIT_VALUE,          $idx, $unitValue);
100
            $this->set(self::COL_UNIT_BILLABLE_VALUE, $idx, $unitBillableValue);
101
            $this->set(self::COL_UNIT_TAXED_VALUE,    $idx, $unitTaxedValue);
102
            $this->set(self::COL_ITEM_VALUE,          $idx, $itemValue);
103
            $this->set(self::COL_ITEM_BILLABLE_VALUE, $idx, $itemBillableValue);
104
            $this->set(self::COL_ALLOWANCES_CHARGES,  $idx, $ac);
105
            $this->set(self::COL_ALLOWANCES_AMOUNT,   $idx, $itemAllowancesAmount);
106
            $this->set(self::COL_CHARGES_AMOUNT,      $idx, $itemChargesAmount);
107
            $this->set(self::COL_ITEM_TAXABLE_AMOUNT, $idx, $itemTaxableAmount);
108
            $this->set(self::COL_IGV,                 $idx, $igvAmount);
109
            $this->set(self::COL_ITEM_PAYABLE_AMOUNT, $idx, $itemIgvTaxed);
110
        }
111
    }
112
113
    /**
114
     * Valor unitario: se extrae el IGV si el valor es afectado por este y si se
115
     * encuentra incluido en el monto que se recibe como segundo parametro.
116
     * 
117
     * @param string $igvAffectCode
118
     * @param float $baseAmount
119
     * @param boolean $igvIncluded
120
     * @return float
121
     */
122
    private function calcUnitValue($igvAffectCode, $baseAmount, $igvIncluded) {
123
        $amount = $baseAmount;
124
        if (Operations::isIGVAffected($igvAffectCode) && $igvIncluded) {
125
            $amount = $baseAmount / (1 + SunatVars::IGV);
126
        }
127
        return $amount;
128
    }
129
130
    /**
131
     * Valor unitario pagable: se aplica el IGV si este es aplicable y si aún no
132
     * ha sido incluido en el monto que se recibe como segundo parametro.
133
     * 
134
     * @param string $igvAffectCode
135
     * @param float $baseAmount
136
     * @param boolean $igvIncluded
137
     * @return float
138
     */
139
    private function calcUnitTaxedValue($igvAffectCode, $baseAmount, $igvIncluded) {
140
        $amount = $baseAmount;
141
        if (Operations::isIGVAffected($igvAffectCode) && !$igvIncluded) {
142
            $amount = $baseAmount * (1 + SunatVars::IGV);
143
        }
144
        return $amount;
145
    }
146
147
    /**
148
     * 
149
     * Valor facturable
150
     * 
151
     * El valor que figurará en el comprobante como valor unitario a pagar
152
     * 
153
     * @param float $baseAmount
154
     * @param boolean $priceType
155
     * @return float
156
     */
157
    private function calcUnitBillableValue($baseAmount, $priceType) {
158
        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...
159
    }
160
161
    private function calcItemTaxableAmount($baseAmount, $priceType, $ac) {
162
        // Valor de venta del ítem = (Valor del item - Descuentos + Cargos)
163
        if ($priceType === Catalogo::CAT16_UNITARY_PRICE) {
164
            $amount = Operations::applyAllowancesAndCharges($baseAmount, $ac);
165
        } else {
166
            // 0 si el valor del item es referencial!
167
            $amount = 0;
168
        }
169
        return $amount;
170
    }
171
172
    /**
173
     * IGV: Calcula el monto del IGV, si este es aplicable.
174
     * @param string $igvAffectCode
175
     * @param float $baseAmount
176
     * @return float
177
     */
178
    private function calcIgvAmount($igvAffectCode, $baseAmount) {
179
        return Operations::isIGVAffected($igvAffectCode) ? Operations::calcIGV($baseAmount) : 0;
180
    }
181
182
    /**
183
     * Codigo de producto
184
     * 
185
     * @param int $rowIndex
186
     * @return string
187
     */
188
    public function getProductCode($rowIndex) {
189
        return $this->get(self::COL_PRODUCT_CODE, $rowIndex);
190
    }
191
192
    /**
193
     * United Nations Standard Products and Services Code
194
     * Codigo de producto SUNAT de acuerdo con UNSPSC v14_0801
195
     * @param int $rowIndex
196
     * @return string
197
     */
198
    public function getUNPSC($rowIndex) {
199
        return $this->get(self::COL_UNPSC, $rowIndex);
200
    }
201
202
    public function getUnitCode($rowIndex) {
203
        return $this->get(self::COL_UNIT_CODE, $rowIndex);
204
    }
205
206
    public function getQunatity($rowIndex) {
207
        return $this->get(self::COL_QUANTITY, $rowIndex);
208
    }
209
210
    public function getDescription($rowIndex) {
211
        return $this->get(self::COL_DESCRIPTION, $rowIndex);
212
    }
213
214
    public function getCurrencyCode($rowIndex) {
215
        return $this->get(self::COL_CURRENCY_CODE, $rowIndex);
216
    }
217
218
    public function getPriceTypeCode($rowIndex) {
219
        return $this->get(self::COL_PRICE_TYPE, $rowIndex);
220
    }
221
222
    public function getTaxTypeCode($rowIndex) {
223
        return $this->get(self::COL_TAX_TYPE, $rowIndex);
224
    }
225
226
    public function getIgvAffectationCode($rowIndex) {
227
        return $this->get(self::COL_IGV_AFFECTATION, $rowIndex);
228
    }
229
230
    public function getUnitValue($rowIndex) {
231
        return $this->get(self::COL_UNIT_VALUE, $rowIndex);
232
    }
233
234
    public function getUnitBillableValue($rowIndex) {
235
        return $this->get(self::COL_UNIT_BILLABLE_VALUE, $rowIndex);
236
    }
237
238
    public function getUnitTaxedValue($rowIndex) {
239
        return $this->get(self::COL_UNIT_TAXED_VALUE, $rowIndex);
240
    }
241
242
    public function getItemValue($rowIndex) {
243
        return $this->get(self::COL_ITEM_VALUE, $rowIndex);
244
    }
245
246
    public function getItemBillableValue($rowIndex) {
247
        return $this->get(self::COL_ITEM_BILLABLE_VALUE, $rowIndex);
248
    }
249
250
    public function getAllowancesAndCharges($rowIndex) {
251
        return $this->get(self::COL_ALLOWANCES_CHARGES, $rowIndex);
252
    }
253
254
    public function getAllowancesAmount($rowIndex) {
255
        return $this->get(self::COL_ALLOWANCES_AMOUNT, $rowIndex);
256
    }
257
258
    public function getChargesAmount($rowIndex) {
259
        return $this->get(self::COL_CHARGES_AMOUNT, $rowIndex);
260
    }
261
262
    public function getTaxableAmount($rowIndex) {
263
        return $this->get(self::COL_ITEM_TAXABLE_AMOUNT, $rowIndex);
264
    }
265
266
    public function getIgv($rowIndex) {
267
        return $this->get(self::COL_IGV, $rowIndex);
268
    }
269
270
    public function getPayableAmount($rowIndex) {
271
        return $this->get(self::COL_ITEM_PAYABLE_AMOUNT, $rowIndex);
272
    }
273
274
    /**
275
     * Total operaciones gravadas
276
     * @return float
277
     */
278
    public function getTotalTaxableOperations() {
279
        return $this->getTotalOperations(Catalogo::CAT5_IGV, self::COL_ITEM_TAXABLE_AMOUNT);
280
    }
281
282
    /**
283
     * Total operaciones gratuitas
284
     * @return float
285
     */
286
    public function getTotalFreeOperations() {
287
        return $this->getTotalOperations(Catalogo::CAT5_GRA, self::COL_ITEM_VALUE);
288
    }
289
290
    /**
291
     * Total operaciones exoneradas
292
     * @return float
293
     */
294
    public function getTotalExemptedOperations() {
295
        return $this->getTotalOperations(Catalogo::CAT5_EXO, self::COL_ITEM_TAXABLE_AMOUNT);
296
    }
297
298
    /**
299
     * Total operaciones inafectas
300
     * @return float
301
     */
302
    public function getTotalUnaffectedOperations() {
303
        return $this->getTotalOperations(Catalogo::CAT5_INA, self::COL_ITEM_VALUE);
304
    }
305
306
    private function getTotalOperations($taxType, $columnIndex) {
307
        $total = 0;
308
        $data = $this->getMatrix();
309
        foreach ($data as $row) {
310
            $total += ($row[self::COL_TAX_TYPE] === $taxType) ? $row[$columnIndex] : 0;
311
        }
312
        return $total;
313
    }
314
315
    /**
316
     * Todos de los descuentos.
317
     * @return float
318
     */
319
    public function getTotalAllowances() {
320
        return $this->sum(self::COL_ALLOWANCES_AMOUNT);
321
    }
322
323
    /**
324
     * Todos de los descuentos.
325
     * @return float
326
     */
327
    public function getTotalCharges() {
328
        return $this->sum(self::COL_CHARGES_AMOUNT);
329
    }
330
331
    /**
332
     * Valor total
333
     * Suma los valores de cada item
334
     * = Cantidad x (Valor unitario | Valor fererencial)
335
     * @return float
336
     */
337
    public function getTotalValue() {
338
        return $this->sum(self::COL_ITEM_VALUE);
339
    }
340
341
    /**
342
     * Suma de valores de cada item sin incluir aquellos por los que no se cobra.
343
     * @return float
344
     */
345
    public function getTotalBillableValue() {
346
        return $this->sum(self::COL_ITEM_BILLABLE_VALUE);
347
    }
348
349
    /**
350
     * Total IGV
351
     * @return float
352
     */
353
    public function getTotalIgv() {
354
        return $this->sum(self::COL_IGV);
355
    }
356
357
    /**
358
     * Base imponible total lista para aplicar impuestos
359
     * @return float
360
     */
361
    public function getTotalTaxableAmount() {
362
        return $this->sum(self::COL_ITEM_TAXABLE_AMOUNT);
363
    }
364
365
    /**
366
     * Total de items
367
     * @return int
368
     */
369
    public function getCount() {
370
        return $this->countRows();
371
    }
372
373
}
374