20
20
use Contao \StringUtil ;
21
21
use Isotope \Interfaces \IsotopeProduct ;
22
22
use Isotope \Interfaces \IsotopeProductCollection ;
23
+ use Isotope \Model \Address ;
23
24
use Isotope \Model \Config as IsotopeConfig ;
24
25
use Isotope \Model \Product ;
25
26
use Isotope \Model \ProductCollectionItem ;
27
+ use Isotope \Model \ProductCollectionSurcharge ;
26
28
use Isotope \Model \ProductCollectionSurcharge \Rule ;
29
+ use Isotope \Model \ProductPrice ;
27
30
use Isotope \Model \ProductType ;
31
+ use Isotope \Model \TaxClass ;
32
+ use Isotope \Model \TaxRate ;
28
33
29
34
final class Item extends AbstractOrderLine
30
35
{
31
- public function __construct (ProductCollectionItem $ item , IsotopeProductCollection $ collection )
36
+ public function __construct (ProductCollectionItem $ item , IsotopeProductCollection $ collection, Address $ billingAddress , Address $ shippingAddress )
32
37
{
33
38
$ this ->reference = $ item ->getSku ();
34
39
$ this ->name = $ item ->getName ();
@@ -38,18 +43,69 @@ public function __construct(ProductCollectionItem $item, IsotopeProductCollectio
38
43
$ this ->unit_price = (int ) round ($ item ->getPrice () * 100 );
39
44
$ this ->total_amount = (int ) round (($ item ->getTotalPrice () - $ this ->total_discount_amount / 100 ) * 100 );
40
45
41
- // If config shows net prices, tax_rate stays 0 because sales taxes are added as surcharges.
42
- // If config shows gross prices, calculate tax_amount and tax_rate.
43
- if (IsotopeConfig::PRICE_DISPLAY_GROSS === $ collection ->getConfig ()->priceDisplay ) {
44
- $ this ->total_tax_amount = (int ) round (($ item ->getTotalPrice () - $ item ->getTaxFreeTotalPrice ()) * 100 );
45
- $ this ->tax_rate = (int ) round (($ this ->total_tax_amount / ($ this ->total_amount - $ this ->total_tax_amount ) * 10000 ));
46
- }
46
+ $ this ->addTaxRate ($ item , $ collection , $ billingAddress , $ shippingAddress );
47
+ $ this ->addTotalTaxAmount ($ item , $ collection );
47
48
48
49
$ this ->addType ($ item );
49
50
$ this ->addProductUrl ($ item );
50
51
$ this ->addImageUrl ($ item );
51
52
}
52
53
54
+ private function addTaxRate (ProductCollectionItem $ item , IsotopeProductCollection $ collection , Address $ billingAddress , Address $ shippingAddress )
55
+ {
56
+ // If config shows net prices, tax_rate stays 0 because sales taxes are added as surcharges.
57
+ // If config shows gross prices, calculate tax_amount and tax_rate.
58
+ if (IsotopeConfig::PRICE_DISPLAY_GROSS !== $ collection ->getConfig ()->priceDisplay ) {
59
+ return ;
60
+ }
61
+
62
+ /** @var ProductPrice&Model $price */
63
+ $ price = $ item ->getProduct ()->getPrice ();
64
+ /** @var TaxClass&Model $taxClass */
65
+ $ taxClass = $ price ->getRelated ('tax_class ' );
66
+ if (null === $ taxClass ) {
67
+ return ;
68
+ }
69
+
70
+ /** @var TaxRate&Model $includedRate */
71
+ $ includedRate = $ taxClass ->getRelated ('includes ' );
72
+ $ addresses = ['billing ' => $ billingAddress , 'shipping ' => $ shippingAddress ];
73
+
74
+ // Use the tax rate that is included in the price, if applicable
75
+ if (null !== $ includedRate
76
+ && $ includedRate ->isApplicable ($ item ->getTotalPrice (), $ addresses )
77
+ && $ includedRate ->isPercentage ()) {
78
+ $ this ->tax_rate = (int ) round ($ includedRate ->getAmount () * 100 );
79
+
80
+ return ;
81
+ }
82
+
83
+ // Use the tax rate that is added to price, first applicable wins.
84
+ // We purposely do not support cases when multiple tax rates are applicable to a single product,
85
+ // or non-percantage tax_rates apply.
86
+ if (null !== ($ addedRates = $ taxClass ->getRelated ('rates ' ))) {
87
+ /** @var TaxRate $taxRate */
88
+ foreach ($ addedRates as $ taxRate ) {
89
+ if ($ taxRate ->isApplicable ($ item ->getTotalPrice (), $ addresses ) && $ taxRate ->isPercentage ()) {
90
+ $ this ->tax_rate = (int ) round ($ taxRate ->getAmount () * 100 );
91
+
92
+ return ;
93
+ }
94
+ }
95
+ }
96
+ }
97
+
98
+ private function addTotalTaxAmount (ProductCollectionItem $ item , IsotopeProductCollection $ collection )
99
+ {
100
+ // If config shows net prices, tax_rate stays 0 because sales taxes are added as surcharges.
101
+ // If config shows gross prices, calculate tax_amount and tax_rate.
102
+ if (IsotopeConfig::PRICE_DISPLAY_GROSS !== $ collection ->getConfig ()->priceDisplay ) {
103
+ return ;
104
+ }
105
+
106
+ $ this ->total_tax_amount = (int ) ($ this ->total_amount - $ this ->total_amount * 10000 / (10000 + $ this ->tax_rate ));
107
+ }
108
+
53
109
private function addType (ProductCollectionItem $ item )
54
110
{
55
111
$ this ->type = self ::TYPE_PHYSICAL ;
@@ -65,11 +121,13 @@ private function addType(ProductCollectionItem $item)
65
121
66
122
private function addProductUrl (ProductCollectionItem $ item )
67
123
{
124
+ /** @var ProductCollectionItem&Model $item */
68
125
$ product = $ item ->getProduct ();
69
126
$ jumpTo = $ item ->getRelated ('jumpTo ' );
70
- if (null !== $ jumpTo && null !== $ product
71
- && $ item ->hasProduct ()
72
- && $ product ->isAvailableInFrontend ()) {
127
+ if (null !== $ jumpTo
128
+ && null !== $ product
129
+ && $ item ->hasProduct ()
130
+ && $ product ->isAvailableInFrontend ()) {
73
131
$ this ->product_url = Environment::get ('url ' ).'/ ' .$ product ->generateUrl ($ jumpTo );
74
132
}
75
133
}
@@ -100,6 +158,7 @@ private function addImageUrl(ProductCollectionItem $item)
100
158
private function addTotalDiscountAmount (array $ surcharges , ProductCollectionItem $ item ): void
101
159
{
102
160
foreach ($ surcharges as $ surcharge ) {
161
+ /** @var ProductCollectionSurcharge&Model $surcharge */
103
162
if (!$ surcharge instanceof Rule || ('cart ' === $ surcharge ->type && 'subtotal ' === $ surcharge ->applyTo )) {
104
163
continue ;
105
164
}
0 commit comments