Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
46.05% covered (danger)
46.05%
35 / 76
50.00% covered (danger)
50.00%
4 / 8
CRAP
0.00% covered (danger)
0.00%
0 / 1
BillElement
46.05% covered (danger)
46.05%
35 / 76
50.00% covered (danger)
50.00%
4 / 8
39.53
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
21 / 21
100.00% covered (success)
100.00%
1 / 1
1
 getId
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setQuantity
66.67% covered (warning)
66.67%
2 / 3
0.00% covered (danger)
0.00%
0 / 1
2.15
 getQuantity
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setItem
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 fromItem
0.00% covered (danger)
0.00%
0 / 38
0.00% covered (danger)
0.00%
0 / 1
30
 toArray
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
1
 jsonSerialize
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2/**
3 * Jingga
4 *
5 * PHP Version 8.1
6 *
7 * @package   Modules\Billing\Models
8 * @copyright Dennis Eichhorn
9 * @license   OMS License 2.0
10 * @version   1.0.0
11 * @link      https://jingga.app
12 */
13declare(strict_types=1);
14
15namespace Modules\Billing\Models;
16
17use Modules\Finance\Models\TaxCode;
18use Modules\ItemManagement\Models\Item;
19use phpOMS\Stdlib\Base\FloatInt;
20use phpOMS\Stdlib\Base\SmartDateTime;
21
22/**
23 * Bill class.
24 *
25 * @package Modules\Billing\Models
26 * @license OMS License 2.0
27 * @link    https://jingga.app
28 * @since   1.0.0
29 */
30class BillElement implements \JsonSerializable
31{
32    /**
33     * ID.
34     *
35     * @var int
36     * @since 1.0.0
37     */
38    public int $id = 0;
39
40    public int $order = 0;
41
42    /** @todo: consider to reference the model instead of the int, this would make it much easier in other places like the shop */
43    public ?int $item = null;
44
45    public string $itemNumber = '';
46
47    public string $itemName = '';
48
49    public string $itemDescription = '';
50
51    protected int $quantity = 0;
52
53    public ?Subscription $subscription = null;
54
55    public FloatInt $singleSalesPriceNet;
56
57    public FloatInt $singleSalesPriceGross;
58
59    public FloatInt $totalSalesPriceNet;
60
61    public FloatInt $totalSalesPriceGross;
62
63    public FloatInt $singleDiscountP;
64
65    public FloatInt $totalDiscountP;
66
67    public ?FloatInt $singleDiscountR = null;
68
69    public ?FloatInt $discountQ = null;
70
71    public FloatInt $singleListPriceNet;
72
73    public FloatInt $singleListPriceGross;
74
75    public FloatInt $totalListPriceNet;
76
77    public FloatInt $totalListPriceGross;
78
79    public FloatInt $singlePurchasePriceNet;
80
81    public FloatInt $singlePurchasePriceGross;
82
83    public FloatInt $totalPurchasePriceNet;
84
85    public FloatInt $totalPurchasePriceGross;
86
87    public FloatInt $singleProfitNet;
88
89    public FloatInt $singleProfitGross;
90
91    public FloatInt $totalProfitNet;
92
93    public FloatInt $totalProfitGross;
94
95    /**
96     * Tax amount
97     *
98     * @var FloatInt
99     * @since 1.0.0
100     */
101    public FloatInt $taxP;
102
103    /**
104     * Tax percentage
105     *
106     * @var FloatInt
107     * @since 1.0.0
108     */
109    public FloatInt $taxR;
110
111    public string $taxCode = '';
112
113    /**
114     * Event assigned to this element.
115     *
116     * @var int
117     * @since 1.0.0
118     */
119    public int $event = 0;
120
121    /**
122     * Promotion assigned to this element.
123     *
124     * @var int
125     * @since 1.0.0
126     */
127    public int $promotion = 0;
128
129    public Bill $bill;
130
131    /**
132     * Constructor.
133     *
134     * @since 1.0.0
135     */
136    public function __construct()
137    {
138        $this->bill = new NullBill();
139
140        $this->singleListPriceNet   = new FloatInt();
141        $this->singleListPriceGross = new FloatInt();
142
143        $this->totalListPriceNet   = new FloatInt();
144        $this->totalListPriceGross = new FloatInt();
145
146        $this->singleSalesPriceNet   = new FloatInt();
147        $this->singleSalesPriceGross = new FloatInt();
148
149        $this->totalSalesPriceNet   = new FloatInt();
150        $this->totalSalesPriceGross = new FloatInt();
151
152        $this->singlePurchasePriceNet   = new FloatInt();
153        $this->singlePurchasePriceGross = new FloatInt();
154
155        $this->totalPurchasePriceNet   = new FloatInt();
156        $this->totalPurchasePriceGross = new FloatInt();
157
158        $this->singleProfitNet   = new FloatInt();
159        $this->singleProfitGross = new FloatInt();
160
161        $this->totalProfitNet   = new FloatInt();
162        $this->totalProfitGross = new FloatInt();
163
164        $this->singleDiscountP = new FloatInt();
165        $this->totalDiscountP  = new FloatInt();
166
167        $this->taxP = new FloatInt();
168        $this->taxR = new FloatInt();
169    }
170
171    /**
172     * Get id.
173     *
174     * @return int Model id
175     *
176     * @since 1.0.0
177     */
178    public function getId() : int
179    {
180        return $this->id;
181    }
182
183    /**
184     * Set the element quantity.
185     *
186     * @param int $quantity Quantity
187     *
188     * @return void
189     *
190     * @since 1.0.0
191     */
192    public function setQuantity(int $quantity) : void
193    {
194        if ($this->quantity === $quantity) {
195            return;
196        }
197
198        $this->quantity = $quantity;
199        // @todo: recalculate all the prices!!!
200    }
201
202    /**
203     * Get quantity.
204     *
205     * @return int
206     *
207     * @since 1.0.0
208     */
209    public function getQuantity() : int
210    {
211        return $this->quantity;
212    }
213
214    /**
215     * Set item.
216     *
217     * @param int $item Item
218     *
219     * @return void
220     *
221     * @since 1.0.0
222     */
223    public function setItem(int $item) : void
224    {
225        $this->item = $item;
226    }
227
228    /**
229     * Create element from item
230     *
231     * @param Item    $item     Item
232     * @param TaxCode $code     Tax code used for gross amount calculation
233     * @param int     $quantity Quantity
234     * @param int     $bill     Bill
235     *
236     * @return self
237     *
238     * @since 1.0.0
239     */
240    public static function fromItem(Item $item, TaxCode $code, int $quantity = 1, int $bill = 0) : self
241    {
242        $element                  = new self();
243        $element->bill            = new NullBill($bill);
244        $element->item            = empty($item->id) ? null : $item->id;
245        $element->itemNumber      = $item->number;
246        $element->itemName        = $item->getL11n('name1')->content;
247        $element->itemDescription = $item->getL11n('description_short')->content;
248        $element->quantity        = $quantity;
249
250        // @todo: Use pricing instead of the default sales price
251        // @todo: discounts might be in quantities
252        $element->singleListPriceNet->setInt($item->salesPrice->getInt());
253        $element->totalListPriceNet->setInt($element->quantity * $item->salesPrice->getInt());
254        $element->singleSalesPriceNet->setInt($item->salesPrice->getInt());
255        $element->totalSalesPriceNet->setInt($element->quantity * $item->salesPrice->getInt());
256        $element->singlePurchasePriceNet->setInt($item->purchasePrice->getInt());
257        $element->totalPurchasePriceNet->setInt($element->quantity * $item->purchasePrice->getInt());
258
259        $element->singleProfitNet->setInt($element->singleSalesPriceNet->getInt() - $element->singlePurchasePriceNet->getInt());
260        $element->totalProfitNet->setInt($element->quantity * ($element->totalSalesPriceNet->getInt() - $element->totalPurchasePriceNet->getInt()));
261
262        $element->taxP    = new FloatInt((int) (($code->percentageInvoice * $element->totalSalesPriceNet->getInt()) / 10000));
263        $element->taxR    = new FloatInt($code->percentageInvoice);
264        $element->taxCode = $code->abbr;
265
266        $element->singleListPriceGross->setInt((int) ($element->singleListPriceNet->getInt() + $element->singleListPriceNet->getInt() * $element->taxR->getInt() / 10000));
267        $element->totalListPriceGross->setInt((int) ($element->totalListPriceNet->getInt() + $element->totalListPriceNet->getInt() * $element->taxR->getInt() / 10000));
268        $element->singleSalesPriceGross->setInt((int) ($element->singleSalesPriceNet->getInt() + $element->singleSalesPriceNet->getInt() * $element->taxR->getInt() / 10000));
269        $element->totalSalesPriceGross->setInt((int) ($element->totalSalesPriceNet->getInt() + $element->totalSalesPriceNet->getInt() * $element->taxR->getInt() / 10000));
270        $element->singlePurchasePriceGross->setInt((int) ($element->singlePurchasePriceNet->getInt() + $element->singlePurchasePriceNet->getInt() * $element->taxR->getInt() / 10000));
271        $element->totalPurchasePriceGross->setInt((int) ($element->totalPurchasePriceNet->getInt() + $element->totalPurchasePriceNet->getInt() * $element->taxR->getInt() / 10000));
272
273        $element->singleProfitGross->setInt($element->singleSalesPriceGross->getInt() - $element->singlePurchasePriceGross->getInt());
274        $element->totalProfitGross->setInt($element->quantity * ($element->totalSalesPriceGross->getInt() - $element->totalPurchasePriceGross->getInt()));
275
276        if ($element->bill->id !== 0
277            && $item->getAttribute('subscription')->value->getValue() === 1
278            && $element->item !== null
279        ) {
280            $element->subscription        = new Subscription();
281            $element->subscription->bill  = $element->bill->id;
282            $element->subscription->item  = $element->item;
283            $element->subscription->start = new \DateTime('now'); // @todo: change to bill performanceDate
284            $element->subscription->end   = new SmartDateTime('now'); // @todo: depends on subscription type
285            $element->subscription->end->smartModify(m: 1);
286
287            $element->subscription->quantity  = $element->quantity;
288            $element->subscription->autoRenew = $item->getAttribute('subscription_renewal_type')->value->getValue() === 1;
289        }
290
291        return $element;
292    }
293
294    /**
295     * {@inheritdoc}
296     */
297    public function toArray() : array
298    {
299        return [
300            'id'              => $this->id,
301            'order'           => $this->order,
302            'item'            => $this->item,
303            'itemNumber'      => $this->itemNumber,
304            'itemName'        => $this->itemName,
305            'itemDescription' => $this->itemDescription,
306            'quantity'        => $this->quantity,
307            'bill'            => $this->bill,
308        ];
309    }
310
311    /**
312     * {@inheritdoc}
313     */
314    public function jsonSerialize() : mixed
315    {
316        return $this->toArray();
317    }
318}