Passed
Push — master ( 3fc31c...585ede )
by Dmitry
06:03
created

src/charge/modifiers/addons/Discount.php (1 issue)

Labels
Severity
1
<?php
2
/**
3
 * PHP Billing Library
4
 *
5
 * @link      https://github.com/hiqdev/php-billing
6
 * @package   php-billing
7
 * @license   BSD-3-Clause
8
 * @copyright Copyright (c) 2017-2018, HiQDev (http://hiqdev.com/)
9
 */
10
11
namespace hiqdev\php\billing\charge\modifiers\addons;
12
13
use hiqdev\php\billing\charge\ChargeInterface;
14
use hiqdev\php\billing\charge\modifiers\AddonInterface;
15
use hiqdev\php\billing\charge\modifiers\PercentPoint;
16
use hiqdev\php\billing\formula\FormulaSemanticsError;
17
use Money\Currencies\ISOCurrencies;
18
use Money\Currency;
19
use Money\Money;
20
use Money\Parser\DecimalMoneyParser;
21
22
/**
23
 * Discount addon.
24
 *
25
 * @author Andrii Vasyliev <[email protected]>
26
 */
27
class Discount implements AddonInterface
28
{
29
    protected static $name = 'discount';
30
31
    /**
32
     * @var string|Money
33
     */
34
    protected $value;
35
36
    protected $moneyParser;
37
38 26
    public function __construct($value)
39
    {
40 26
        $this->moneyParser = new DecimalMoneyParser(new ISOCurrencies());
41 26
        $this->value = $this->ensureValidValue($value);
42 26
    }
43
44 11
    public function getValue()
45
    {
46 11
        return $this->value instanceof PercentPoint ? $this->value->getNumber() : $this->value;
47
    }
48
49 12
    public function isAbsolute()
50
    {
51 12
        return $this->value instanceof Money;
52
    }
53
54 6
    public function isRelative()
55
    {
56 6
        return !$this->isAbsolute();
57
    }
58
59 2
    public function isPercentPoint(): bool
60
    {
61 2
        return $this->value instanceof PercentPoint;
62
    }
63
64 26
    public function ensureValidValue($value)
65
    {
66 26
        if ($value instanceof self) {
67
            return $value->getValue();
68
        }
69
70 26
        if ($value instanceof Money || $value instanceof PercentPoint) {
71 8
            return $value;
72
        }
73
74 25
        if (is_numeric($value)) {
75 10
            return (string) $value;
76
        }
77
78 22
        if (is_string($value) && preg_match('/^(\d{1,5}(\.\d+)?)(%|pp| [A-Z]{3})$/', $value, $ms)) {
79 22
            if ($ms[3] === '%') {
80 20
                return $ms[1];
81
            }
82 16
            if ($ms[3] === 'pp') {
83
                return new PercentPoint($ms[1]);
84
            }
85
86 16
            return $this->moneyParser->parse($ms[1], new Currency(trim($ms[3])));
87
        }
88
89 4
        $name = static::$name;
90 4
        throw new FormulaSemanticsError("invalid $name value: $value");
91
    }
92
93 6
    public function multiply($multiplier)
94
    {
95 6
        if (!is_numeric($multiplier)) {
96 3
            throw new FormulaSemanticsError('multiplier for discount must be numeric');
97
        }
98
99 3
        return new static($this->isAbsolute() ? $this->value->multiply($multiplier) : $this->getValue()*$multiplier);
100
    }
101
102 9
    public function add($addend)
103
    {
104 9
        if (!$addend instanceof self) {
105 7
            $addend = new self($addend);
106
        }
107 5
        $this->ensureSameType($addend, 'addend');
108
109 3
        if ($this->isAbsolute()) {
110 3
            $sum = $this->getValue()->add($addend->getValue());
0 ignored issues
show
It seems like $addend->getValue() can also be of type string; however, parameter $addend of Money\Money::add() does only seem to accept Money\Money, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

110
            $sum = $this->getValue()->add(/** @scrutinizer ignore-type */ $addend->getValue());
Loading history...
111
        } else {
112 1
            $sum = $this->getValue() + $addend->getValue();
113
        }
114
115 3
        return new static($sum);
116
    }
117
118 1
    public function compare($other)
119
    {
120 1
        if (!$other instanceof self) {
121 1
            $other = new self($other);
122
        }
123 1
        $this->ensureSameType($other, 'comparison argument');
124
125 1
        if ($this->isAbsolute()) {
126 1
            return $this->value->compare($other->getValue());
127
        } else {
128 1
            return $this->value - $other->getValue();
129
        }
130
    }
131
132 6
    public function ensureSameType(self $other, $name)
133
    {
134 6
        if ($this->isRelative() && !$other->isRelative()) {
135 1
            throw new FormulaSemanticsError("$name must be relative");
136
        }
137 5
        if ($this->isAbsolute() && !$other->isAbsolute()) {
138 1
            throw new FormulaSemanticsError("$name must be absolute");
139
        }
140 4
    }
141
142 4
    public function calculateSum(ChargeInterface $charge): Money
143
    {
144 4
        return $this->value instanceof Money
145 2
            ? $this->value->multiply($charge->getUsage()->getQuantity())
146 4
            : $charge->getSum()->multiply($this->value * 0.01)
147
        ;
148
    }
149
}
150