/
PriceFacade.php
249 lines (212 loc) · 8.95 KB
/
PriceFacade.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
<?php declare(strict_types=1);
namespace Shopware\Core\Checkout\Cart\Facade;
use Shopware\Core\Checkout\Cart\CartException;
use Shopware\Core\Checkout\Cart\Facade\Traits\PriceFactoryTrait;
use Shopware\Core\Checkout\Cart\LineItem\LineItem;
use Shopware\Core\Checkout\Cart\Price\Struct\CalculatedPrice;
use Shopware\Core\Checkout\Cart\Price\Struct\CartPrice;
use Shopware\Core\Checkout\Cart\Price\Struct\PriceCollection as CalculatedPriceCollection;
use Shopware\Core\Checkout\Cart\Price\Struct\QuantityPriceDefinition;
use Shopware\Core\Checkout\Cart\Tax\Struct\CalculatedTaxCollection;
use Shopware\Core\Checkout\Cart\Tax\Struct\TaxRuleCollection;
use Shopware\Core\Framework\DataAbstractionLayer\Entity;
use Shopware\Core\Framework\DataAbstractionLayer\Pricing\Price;
use Shopware\Core\Framework\DataAbstractionLayer\Pricing\PriceCollection;
use Shopware\Core\Framework\Log\Package;
use Shopware\Core\System\SalesChannel\SalesChannelContext;
/**
* The PriceFacade is a wrapper around a price.
*
* @script-service cart_manipulation
* @script-service product
*/
#[Package('checkout')]
class PriceFacade
{
use PriceFactoryTrait;
/**
* @internal
*/
public function __construct(
protected Entity|LineItem $item,
protected CalculatedPrice $price,
protected ScriptPriceStubs $priceStubs,
protected SalesChannelContext $context
) {
}
/**
* @internal
*/
public function getInner(): CalculatedPrice
{
return $this->price;
}
/**
* `getTotal()` returns the total price for the line-item.
*
* @return float The total price as float.
*/
public function getTotal(): float
{
return $this->price->getTotalPrice();
}
/**
* `getUnit()` returns the unit price for the line-item.
* This is equivalent to the total price of the line-item with the quantity 1.
*
* @return float The price per unit as float.
*/
public function getUnit(): float
{
return $this->price->getUnitPrice();
}
/**
* `getQuantity()` returns the quantity that was used to calculate the total price.
*
* @return int Returns the quantity.
*/
public function getQuantity(): int
{
return $this->price->getQuantity();
}
/**
* `getTaxes()` returns the calculated taxes of the price.
*
* @return CalculatedTaxCollection Returns the calculated taxes.
*/
public function getTaxes(): CalculatedTaxCollection
{
return $this->price->getCalculatedTaxes();
}
/**
* `getRules()` returns the tax rules that were used to calculate the price.
*
* @return TaxRuleCollection Returns the tax rules.
*/
public function getRules(): TaxRuleCollection
{
return $this->price->getTaxRules();
}
/**
* `change()` allows a price overwrite of the current price scope. The provided price will be recalculated
* over the quantity price calculator to consider quantity, tax rule and cash rounding configurations.
*
* @example pricing-cases/product-pricing.twig 40 5 Overwrite prices with a static defined collection
*
* @param PriceCollection $price The provided price can be a fetched price from the database or generated over the `PriceFactory` statically
*/
public function change(PriceCollection $price): void
{
$value = $this->getPriceForTaxState($price, $this->context);
$definition = new QuantityPriceDefinition(
$value,
$this->price->getTaxRules(),
$this->getQuantity()
);
$this->overwrite($definition);
}
/**
* `plus()` allows a price addition of the current price scope. The provided price will be recalculated via the quantity price calculator.
* The provided price is interpreted as a unit price and will be added to the current unit price. The total price
* is calculated afterwards considering quantity, tax rule and cash rounding configurations.
*
* @example pricing-cases/product-pricing.twig 14 5 Plus a static defined price to the existing calculated price
*
* @param PriceCollection $price The provided price can be a fetched price from the database or generated over the `PriceFactory` statically
*/
public function plus(PriceCollection $price): void
{
$value = $this->getPriceForTaxState($price, $this->context);
$definition = new QuantityPriceDefinition(
$this->price->getUnitPrice() + abs($value),
$this->price->getTaxRules(),
$this->getQuantity()
);
$this->overwrite($definition);
}
/**
* `minus()` allows a price subtraction of the current price scope. The provided price will be recalculated via the quantity price calculator.
* The provided price is interpreted as a unit price and will reduce to the current unit price. The total price
* is calculated afterwards considering quantity, tax rule and cash rounding configurations.
*
* @example pricing-cases/product-pricing.twig 22 5 Minus a static defined price to the existing calculated price
*
* @param PriceCollection $price The provided price can be a fetched price from the database or generated over the `PriceFactory` statically
*/
public function minus(PriceCollection $price): void
{
$value = $this->getPriceForTaxState($price, $this->context);
$definition = new QuantityPriceDefinition(
$this->price->getUnitPrice() - abs($value),
$this->price->getTaxRules(),
$this->getQuantity()
);
$this->overwrite($definition);
}
/**
* `discount()` allows a percentage discount calculation of the current price scope. The provided value will be ensured to be negative via `abs(value) * -1`.
* The provided discount is interpreted as a percentage value and will be applied to the unit price and the total price as well.
*
* @example pricing-cases/product-pricing.twig 30 1 Adds a 10% discount to the existing calculated price
*
* @param float $value The percentage value of the discount. The value will be ensured to be negative via `abs(value) * -1`.
*/
public function discount(float $value): void
{
$definition = new QuantityPriceDefinition($this->price->getUnitPrice(), $this->price->getTaxRules());
$definition->setIsCalculated(true);
$unit = $this->priceStubs->calculateQuantity($definition, $this->context);
$discount = $this->priceStubs->calculatePercentage(\abs($value), new CalculatedPriceCollection([$unit]), $this->context);
$definition = new QuantityPriceDefinition(
$this->price->getUnitPrice() - $discount->getUnitPrice(),
$this->price->getTaxRules(),
$this->getQuantity()
);
$this->overwrite($definition);
}
/**
* `surcharge()` allows a percentage surcharge calculation of the current price scope. The provided value will be ensured to be negative via `abs(value)`.
* The provided surcharge is interpreted as a percentage value and will be applied to the unit price and the total price as well.
*
* @example pricing-cases/product-pricing.twig 34 1 Adds a 10% surcharge to the existing calculated price
*
* @param float $value The percentage value of the surcharge. The value will be ensured to be negative via `abs(value)`.
*/
public function surcharge(float $value): void
{
$definition = new QuantityPriceDefinition($this->price->getUnitPrice(), $this->price->getTaxRules());
$definition->setIsCalculated(true);
$unit = $this->priceStubs->calculateQuantity($definition, $this->context);
$discount = $this->priceStubs->calculatePercentage(\abs($value), new CalculatedPriceCollection([$unit]), $this->context);
$definition = new QuantityPriceDefinition(
$this->price->getUnitPrice() + $discount->getUnitPrice(),
$this->price->getTaxRules(),
$this->getQuantity()
);
$this->overwrite($definition);
}
protected function getPriceForTaxState(PriceCollection $price, SalesChannelContext $context): float
{
$currency = $price->getCurrencyPrice($this->context->getCurrencyId());
if (!$currency instanceof Price) {
throw CartException::invalidPriceDefinition();
}
if ($context->getTaxState() === CartPrice::TAX_STATE_GROSS) {
return $currency->getGross();
}
return $currency->getNet();
}
private function overwrite(QuantityPriceDefinition $definition): void
{
if ($this->item instanceof LineItem) {
$this->item->markModifiedByApp();
$this->item->setPriceDefinition($definition);
}
$new = $this->priceStubs->calculateQuantity($definition, $this->context);
$this->price->overwrite(
$new->getUnitPrice(),
$new->getTotalPrice(),
$new->getCalculatedTaxes(),
);
}
}