Skip to content

Commit 09b81b3

Browse files
committed
Añadido test de descuentos
- Creado `DiscountTest` para probar descuentos y cargos - Corregido fallo de cálculo en líneas de producto con impuestos - Actualizada documentación
1 parent ed64923 commit 09b81b3

File tree

3 files changed

+122
-17
lines changed

3 files changed

+122
-17
lines changed

doc/productos/descuentos-y-cargos.md

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,35 +17,38 @@ $fac->addItem(new FacturaeItem([
1717
["reason" => "5€ de descuento", "amount" => 5]
1818
],
1919
"charges" => [
20-
["reason"=>"Recargo del 1,30%", "rate"=>1.3]
20+
["reason" => "Recargo del 1,30%", "rate" => 1.3]
2121
],
2222
"taxes" => [Facturae::TAX_IVA => 21]
2323
]));
2424
```
2525

2626
## Descuentos y cargos sobre el total con impuestos
27-
Supongamos que vendemos un producto por un importe de 100€ (IVA incluido) y descuento del 20%. Por defecto, Facturae-PHP aplicará el descuento sobre los 100€ **siempre y cuando se indique el campo `unitPrice` en vez de `unitPriceWithoutTax`**:
27+
Supongamos que vendemos un producto por un importe de 100€ (IVA incluido) y descuento de 5€. Por defecto, Facturae-PHP aplicará el descuento sobre los 100€ **siempre y cuando se indique el campo `unitPrice` en vez de `unitPriceWithoutTax`**:
2828
```php
2929
$fac->addItem(new FacturaeItem([
3030
"name" => "Un producto con descuento",
3131
"unitPrice" => 100,
3232
"discounts" => [
33-
["reason" => "Descuento del 20%", "rate" => 20]
33+
["reason" => "Descuento de 5€ (IVA incluído)", "amount" => 5]
3434
],
3535
"taxes" => [Facturae::TAX_IVA => 10]
3636
]));
3737
```
3838

39-
Esto significa que Facturae-PHP ajustará el importe del descuento y su porcentaje para representarlo en función de la base imponible como especifica el estándar.
39+
Esto significa que Facturae-PHP ajustará el importe del descuento para representarlo en función de la base imponible como especifica el estándar.
4040

4141
Si se quisiera evitar este comportamiento y aplicar un descuento a la base imponible de una línea de producto con impuestos incluídos, se deberá usar el flag `hasTaxes`:
4242
```php
4343
$fac->addItem(new FacturaeItem([
4444
"name" => "Un producto con descuento",
4545
"unitPrice" => 100,
4646
"discounts" => [
47-
["reason"=>"5€ de descuento sobre la BI", "amount"=>5, "haxTaxes"=>false]
47+
["reason" => "Descuento de 5€", "amount" => 5, "haxTaxes" => false]
4848
],
4949
"taxes" => [Facturae::TAX_IVA => 10]
5050
]));
5151
```
52+
53+
> #### NOTA
54+
> Consulta el test `DiscountsTest.php` dentro del directorio `tests/` para más información sobre este comportamiento.

src/FacturaeItem.php

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -71,25 +71,17 @@ public function __construct($properties=array()) {
7171
}
7272
}
7373

74-
// Remove discounts and charges
75-
$unitPriceWithoutTax = $this->unitPrice;
74+
// Adjust discounts and charges according to taxes
7675
foreach (['discounts', 'charges'] as $i=>$groupTag) {
77-
$factor = ($i == 0) ? -1 : 1;
7876
foreach ($this->{$groupTag} as &$group) {
77+
if (isset($group['rate'])) continue;
7978
$hasTaxes = isset($group['hasTaxes']) ? $group['hasTaxes'] : true;
80-
if (isset($group['rate'])) {
81-
if ($hasTaxes) $group['rate'] /= $taxesPercent;
82-
$amount = $this->unitPrice * ($group['rate'] / 100);
83-
} else {
84-
if ($hasTaxes) $group['amount'] /= $taxesPercent;
85-
$amount = $group['amount'];
86-
}
87-
$unitPriceWithoutTax += $amount * $factor;
79+
if ($hasTaxes) $group['amount'] /= $taxesPercent;
8880
}
8981
}
9082

9183
// Apply taxes
92-
$this->unitPriceWithoutTax = $unitPriceWithoutTax / $taxesPercent;
84+
$this->unitPriceWithoutTax = $this->unitPrice / $taxesPercent;
9385
}
9486
}
9587

tests/DiscountsTest.php

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
<?php
2+
use josemmo\Facturae\Facturae;
3+
use josemmo\Facturae\FacturaeItem;
4+
use josemmo\Facturae\FacturaeParty;
5+
use PHPUnit\Framework\TestCase;
6+
7+
final class DiscountsTest extends TestCase {
8+
9+
/**
10+
* Get base invoice
11+
* @return Facturae Base invoice
12+
*/
13+
private function _getBaseInvoice() {
14+
$fac = new Facturae();
15+
$fac->setNumber('EMP201712', '0003');
16+
$fac->setIssueDate('2017-12-01');
17+
$fac->setSeller(new FacturaeParty([
18+
"taxNumber" => "A00000000",
19+
"name" => "Perico el de los Palotes S.A.",
20+
"address" => "C/ Falsa, 123",
21+
"postCode" => "12345",
22+
"town" => "Madrid",
23+
"province" => "Madrid"
24+
]));
25+
$fac->setBuyer(new FacturaeParty([
26+
"isLegalEntity" => false,
27+
"taxNumber" => "00000000A",
28+
"name" => "Antonio",
29+
"firstSurname" => "García",
30+
"lastSurname" => "Pérez",
31+
"address" => "Avda. Mayor, 7",
32+
"postCode" => "54321",
33+
"town" => "Madrid",
34+
"province" => "Madrid"
35+
]));
36+
return $fac;
37+
}
38+
39+
40+
/**
41+
* Test invoice item discounts
42+
*/
43+
public function testItemDiscounts() {
44+
$fac = $this->_getBaseInvoice();
45+
$expectedGrossAmounts = [];
46+
47+
// Add first item
48+
$fac->addItem(new FacturaeItem([
49+
"name" => "First item",
50+
"unitPriceWithoutTax" => 100,
51+
"discounts" => [
52+
["reason"=>"Half price", "rate"=>50]
53+
],
54+
"taxes" => [Facturae::TAX_IVA => 10]
55+
]));
56+
$expectedGrossAmounts[] = 50;
57+
58+
// Add second item
59+
$fac->addItem(new FacturaeItem([
60+
"name" => "Second item",
61+
"unitPriceWithoutTax" => 100,
62+
"discounts" => [
63+
["reason"=>"Half price", "rate"=>50],
64+
["reason"=>"5€ off", "amount"=>5]
65+
],
66+
"charges" => [
67+
["reason"=>"Twice as much", "rate"=>50]
68+
],
69+
"taxes" => [Facturae::TAX_IVA => 10]
70+
]));
71+
$expectedGrossAmounts[] = 95;
72+
73+
// Add third item
74+
$fac->addItem(new FacturaeItem([
75+
"name" => "Third item",
76+
"quantity" => 2,
77+
"unitPrice" => 100,
78+
"discounts" => [
79+
["reason"=>"Half price", "rate"=>50]
80+
],
81+
"taxes" => [Facturae::TAX_IVA => 0]
82+
]));
83+
$expectedGrossAmounts[] = 100;
84+
85+
// Add fourth item
86+
$fac->addItem(new FacturaeItem([
87+
"name" => "Fourth item",
88+
"unitPrice" => 100,
89+
"discounts" => [
90+
["reason"=>"Half price", "rate"=>50]
91+
],
92+
"charges" => [
93+
["reason"=>"Extra €5 (tax. included)", "amount"=>5],
94+
["reason"=>"Extra €10", "amount"=>10, "hasTaxes"=>false],
95+
],
96+
"taxes" => [Facturae::TAX_IVA => 25]
97+
]));
98+
$expectedGrossAmounts[] = (100 / 1.25)*0.5 + (5 / 1.25) + 10;
99+
100+
// Generate invoice and validate output
101+
$invoiceXml = new \SimpleXMLElement($fac->export());
102+
$invoiceXml = $invoiceXml->Invoices->Invoice[0];
103+
foreach ($invoiceXml->Items->InvoiceLine as $item) {
104+
$itemGross = floatval($item->GrossAmount);
105+
$expectedGross = array_shift($expectedGrossAmounts);
106+
$this->assertEquals($itemGross, $expectedGross, '', 0.00001);
107+
}
108+
}
109+
110+
}

0 commit comments

Comments
 (0)