Completed
Push — master ( d79f58...e0b171 )
by Jan
03:56 queued 10s
created

PricedetailHelper::calculateAvgPrice()   A

Complexity

Conditions 6
Paths 14

Size

Total Lines 33
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 16
c 0
b 0
f 0
nc 14
nop 3
dl 0
loc 33
rs 9.1111
1
<?php
2
/**
3
 *
4
 * part-db version 0.1
5
 * Copyright (C) 2005 Christoph Lechner
6
 * http://www.cl-projects.de/
7
 *
8
 * part-db version 0.2+
9
 * Copyright (C) 2009 K. Jacobs and others (see authors.php)
10
 * http://code.google.com/p/part-db/
11
 *
12
 * Part-DB Version 0.4+
13
 * Copyright (C) 2016 - 2019 Jan Böhmer
14
 * https://github.com/jbtronics
15
 *
16
 * This program is free software; you can redistribute it and/or
17
 * modify it under the terms of the GNU General Public License
18
 * as published by the Free Software Foundation; either version 2
19
 * of the License, or (at your option) any later version.
20
 *
21
 * This program is distributed in the hope that it will be useful,
22
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24
 * GNU General Public License for more details.
25
 *
26
 * You should have received a copy of the GNU General Public License
27
 * along with this program; if not, write to the Free Software
28
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
29
 *
30
 */
31
32
namespace App\Services;
33
34
use App\Entity\Parts\Part;
35
use App\Entity\PriceInformations\Currency;
36
use App\Entity\PriceInformations\Pricedetail;
37
use Locale;
38
39
class PricedetailHelper
40
{
41
    protected $base_currency;
42
    protected $locale;
43
44
    public function __construct(string $base_currency)
45
    {
46
        $this->base_currency = $base_currency;
47
        $this->locale = Locale::getDefault();
48
    }
49
50
    /**
51
     * Determines the highest amount, for which you get additional discount.
52
     * This function determines the highest min_discount_quantity for the given part.
53
     * @param Part $part
54
     * @return float|null
55
     */
56
    public function getMaxDiscountAmount(Part $part) : ?float
57
    {
58
        $orderdetails = $part->getOrderdetails(true);
59
60
        $max = 0;
61
62
        foreach ($orderdetails as $orderdetail) {
63
            $pricedetails = $orderdetail->getPricedetails();
64
            //The orderdetail must have pricedetails, otherwise this will not work!
65
            if (empty($pricedetails)) {
66
                continue;
67
            }
68
69
            /* Pricedetails in orderdetails are ordered by min discount quantity,
70
                so our first object is our min order amount for the current orderdetail */
71
            $max_amount = $pricedetails->last()->getMinDiscountQuantity();
72
73
            if ($max_amount > $max) {
74
                $max = $max_amount;
75
            }
76
        }
77
78
        if ($max > 0) {
79
            return $max;
80
        }
81
82
        return null;
83
    }
84
85
    /**
86
     * Determines the minimum amount of the part that can be ordered
87
     * @param Part $part The part for which the minimum order amount should be determined.
88
     * @return float
89
     */
90
    public function getMinOrderAmount(Part $part) : ?float
91
    {
92
        $orderdetails = $part->getOrderdetails(true);
93
94
        $min = INF;
95
96
        foreach ($orderdetails as $orderdetail) {
97
            $pricedetails = $orderdetail->getPricedetails();
98
            //The orderdetail must have pricedetails, otherwise this will not work!
99
            if (count($pricedetails) === 0) {
100
                continue;
101
            }
102
103
            /* Pricedetails in orderdetails are ordered by min discount quantity,
104
                so our first object is our min order amount for the current orderdetail */
105
            $min_amount = $pricedetails[0]->getMinDiscountQuantity();
106
107
            if ($min_amount < $min) {
108
                $min = $min_amount;
109
            }
110
        }
111
112
        if ($min < INF) {
113
            return $min;
114
        }
115
116
        return null;
117
    }
118
119
    /**
120
     * Calculates the average price of a part, when ordering the amount $amount.
121
     * @param Part $part The part for which the average price should be calculated.
122
     * @param float $amount The order amount for which the average price should be calculated.
123
     * If set to null, the mininmum order amount for the part is used.
124
     * @param Currency|null $currency The currency in which the average price should be calculated
125
     * @return string|null The Average price as bcmath string. Returns null, if it was not possible to calculate the
126
     * price for the given
127
     */
128
    public function calculateAvgPrice(Part $part, ?float $amount = null, ?Currency $currency = null) : ?string
129
    {
130
        if ($amount === null) {
131
            $amount = $this->getMinOrderAmount($part);
132
        }
133
134
        if ($amount === null) {
135
            return null;
136
        }
137
138
        $orderdetails = $part->getOrderdetails(true);
139
140
        $avg = "0";
141
        $count = 0;
142
143
        //Find the price for the amount, for the given
144
        foreach ($orderdetails as $orderdetail) {
145
            $pricedetail = $orderdetail->getPrice($amount);
146
147
            //When we dont have informations about this amount, ignore it
148
            if ($pricedetail === null) {
149
                continue;
150
            }
151
152
            $avg = bcadd($avg, $this->convertMoneyToCurrency($pricedetail->getPricePerUnit(), $pricedetail->getCurrency(), $currency), Pricedetail::PRICE_PRECISION);
153
            $count++;
154
        }
155
156
        if ($count === 0) {
157
            return null;
158
        }
159
160
        return bcdiv($avg, (string) $count, Pricedetail::PRICE_PRECISION);
161
    }
162
163
    /**
164
     * Converts the given value in origin currency to the choosen target currency
165
     * @param $value float|string The value that should be converted
166
     * @param Currency|null $originCurrency The currency the $value is given in.
167
     * Set to null, to use global base currency.
168
     * @param Currency|null $targetCurrency The target currency, to which $value should be converted.
169
     * Set to null, to use global base currency.
170
     * @return string|null The value in $targetCurrency given as bcmath string.
171
     * Returns null, if it was not possible to convert between both values (e.g. when the exchange rates are missing)
172
     */
173
    public function convertMoneyToCurrency($value, ?Currency $originCurrency = null, ?Currency $targetCurrency = null) : ?string
174
    {
175
        //Skip conversion, if both currencies are same
176
        if ($originCurrency === $targetCurrency) {
177
            return $value;
178
        }
179
180
        $value = (string) $value;
181
182
        //Convert value to base currency
183
        $val_base = $value;
184
        if ($originCurrency !== null) {
185
            //Without an exchange rate we can not calculate the exchange rate
186
            if ((float) $originCurrency->getExchangeRate() === 0) {
0 ignored issues
show
introduced by
The condition (double)$originCurrency->getExchangeRate() === 0 is always false.
Loading history...
187
                return null;
188
            }
189
190
            $val_base = bcmul($value, $originCurrency->getExchangeRate(), Pricedetail::PRICE_PRECISION);
191
        }
192
193
        //Convert value in base currency to target currency
194
        $val_target = $val_base;
195
        if ($targetCurrency !== null) {
196
            //Without an exchange rate we can not calculate the exchange rate
197
            if ($targetCurrency->getExchangeRate() === null) {
198
                return null;
199
            }
200
201
            $val_target = bcmul($val_base, $targetCurrency->getInverseExchangeRate(), Pricedetail::PRICE_PRECISION);
202
        }
203
204
        return $val_target;
205
    }
206
}