Passed
Push — 6.5.0.0 ( dae7c8...14f5ba )
by Christian
19:12 queued 07:22
created

TaxAdjustment::applyCartPriceTaxes()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 28
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 19
c 0
b 0
f 0
nc 4
nop 3
dl 0
loc 28
rs 9.6333
1
<?php declare(strict_types=1);
2
3
namespace Shopware\Core\Checkout\Cart\TaxProvider;
4
5
use Shopware\Core\Checkout\Cart\Cart;
6
use Shopware\Core\Checkout\Cart\CartException;
7
use Shopware\Core\Checkout\Cart\Delivery\Struct\DeliveryCollection;
8
use Shopware\Core\Checkout\Cart\LineItem\LineItemCollection;
9
use Shopware\Core\Checkout\Cart\Price\AmountCalculator;
10
use Shopware\Core\Checkout\Cart\Price\CashRounding;
11
use Shopware\Core\Checkout\Cart\Price\Struct\CartPrice;
12
use Shopware\Core\Checkout\Cart\Tax\Struct\CalculatedTax;
13
use Shopware\Core\Checkout\Cart\Tax\Struct\CalculatedTaxCollection;
14
use Shopware\Core\Checkout\Cart\TaxProvider\Struct\TaxProviderResult;
15
use Shopware\Core\Framework\Log\Package;
16
use Shopware\Core\System\SalesChannel\SalesChannelContext;
17
18
#[Package('checkout')]
19
class TaxAdjustment
20
{
21
    /**
22
     * @internal
23
     */
24
    public function __construct(
25
        private readonly AmountCalculator $amountCalculator,
26
        private readonly CashRounding $rounding
27
    ) {
28
    }
29
30
    public function adjust(Cart $cart, TaxProviderResult $result, SalesChannelContext $context): void
31
    {
32
        $lineItems = $cart->getLineItems();
33
        $deliveries = $cart->getDeliveries();
34
35
        $this->applyLineItemTaxes($lineItems, $result->getLineItemTaxes());
36
        $this->applyDeliveryTaxes($deliveries, $result->getDeliveryTaxes());
37
38
        $price = $this->amountCalculator->calculate(
39
            $cart->getLineItems()->getPrices(),
40
            $cart->getDeliveries()->getShippingCosts(),
41
            $context
42
        );
43
44
        // either take the price from the tax provider result or take the calculated taxes
45
        $taxes = $price->getCalculatedTaxes()->filter(fn (CalculatedTax $tax) => $tax->getTax() > 0.0);
46
        $price->setCalculatedTaxes($taxes);
47
48
        if ($result->getCartPriceTaxes()) {
49
            $price = $this->applyCartPriceTaxes($price, $result->getCartPriceTaxes(), $context);
50
        }
51
52
        $cart->setPrice($price);
53
    }
54
55
    private function applyCartPriceTaxes(CartPrice $price, CalculatedTaxCollection $taxes, SalesChannelContext $context): CartPrice
56
    {
57
        $netPrice = $price->getNetPrice();
58
        $grossPrice = $price->getTotalPrice();
59
        $taxSum = $taxes->getAmount();
60
61
        if ($context->getTaxState() === CartPrice::TAX_STATE_NET) {
62
            $grossPrice = $this->rounding->cashRound(
63
                $netPrice + $taxSum,
64
                $context->getTotalRounding()
65
            );
66
        }
67
68
        if ($context->getTaxState() === CartPrice::TAX_STATE_GROSS) {
69
            $netPrice = $this->rounding->cashRound(
70
                $grossPrice - $taxSum,
71
                $context->getTotalRounding()
72
            );
73
        }
74
75
        return new CartPrice(
76
            $netPrice,
77
            $grossPrice,
78
            $price->getPositionPrice(),
79
            $taxes,
80
            $price->getTaxRules(),
81
            $context->getTaxState(),
82
            $grossPrice
83
        );
84
    }
85
86
    /**
87
     * @param array<string, CalculatedTaxCollection>|null $taxes
88
     */
89
    private function applyLineItemTaxes(LineItemCollection $lineItems, ?array $taxes): void
90
    {
91
        if (!$taxes) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $taxes of type array<string,Shopware\Co...alculatedTaxCollection> is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
92
            return;
93
        }
94
95
        foreach ($lineItems as $lineItem) {
96
            if (!$lineItem->getPrice()) {
97
                throw CartException::missingLineItemPrice($lineItem->getUniqueIdentifier());
98
            }
99
100
            // trickle down for nested line items
101
            if ($lineItem->hasChildren()) {
102
                $this->applyLineItemTaxes($lineItem->getChildren(), $taxes);
103
            }
104
105
            // line item has no tax sum provided
106
            if (!\array_key_exists($lineItem->getUniqueIdentifier(), $taxes)) {
107
                continue;
108
            }
109
110
            // apply provided taxes
111
            $tax = $taxes[$lineItem->getUniqueIdentifier()];
112
            $lineItem->getPrice()->setCalculatedTaxes($tax);
113
        }
114
    }
115
116
    /**
117
     * @param array<string, CalculatedTaxCollection>|null $taxes
118
     */
119
    private function applyDeliveryTaxes(DeliveryCollection $deliveries, ?array $taxes): void
120
    {
121
        if (!$taxes) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $taxes of type array<string,Shopware\Co...alculatedTaxCollection> is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
122
            return;
123
        }
124
125
        foreach ($taxes as $deliveryPositionId => $deliveryTax) {
126
            foreach ($deliveries as $delivery) {
127
                $position = $delivery->getPositions()->get($deliveryPositionId);
128
129
                if (!$position) {
130
                    continue;
131
                }
132
133
                $position->getPrice()->setCalculatedTaxes($deliveryTax);
134
                $delivery->getShippingCosts()->setCalculatedTaxes($deliveryTax);
135
            }
136
        }
137
    }
138
}
139