Merger::mergeBills()   A
last analyzed

Complexity

Conditions 3
Paths 3

Size

Total Lines 13
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 8
c 1
b 0
f 0
nc 3
nop 1
dl 0
loc 13
ccs 0
cts 11
cp 0
crap 12
rs 10
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-2020, HiQDev (http://hiqdev.com/)
9
 */
10
11
namespace hiqdev\php\billing\tools;
12
13
use hiqdev\php\billing\bill\Bill;
14
use hiqdev\php\billing\bill\BillInterface;
15
use hiqdev\php\billing\charge\ChargeInterface;
16
use hiqdev\php\units\QuantityInterface;
17
use Money\Money;
18
19
/**
20
 * @author Andrii Vasyliev <[email protected]>
21
 */
22
class Merger implements MergerInterface
23
{
24
    /**
25
     * {@inheritdoc}
26
     */
27
    public function mergeBills(array $bills): array
28
    {
29
        $res = [];
30
        foreach ($bills as $bill) {
31
            $uid = $bill->getUniqueString();
32
            if (empty($res[$uid])) {
33
                $res[$uid] = $bill;
34
            } else {
35
                $res[$uid] = $this->mergeBill($res[$uid], $bill);
36
            }
37
        }
38
39
        return $res;
40
    }
41
42
    /**
43
     * {@inheritdoc}
44
     */
45
    public function mergeBill(BillInterface $first, BillInterface $other): BillInterface
46
    {
47
        $charges = $this->mergeCharges(array_merge($first->getCharges(), $other->getCharges()));
48
49
        $bill = new Bill(
50
            $this->mergeId($first, $other),
51
            $first->getType(),
52
            $first->getTime(),
53
            $this->mergeSum($first, $other, $charges),
54
            $this->mergeQuantity($first, $other, $charges),
55
            $first->getCustomer(),
56
            $first->getTarget(),
57
            $first->getPlan(),
58
            $charges
59
        );
60
        $bill->setUsageInterval($first->getUsageInterval());
61
        return $bill;
62
    }
63
64
    /**
65
     * Merge
66
     * @param ChargeInterface[] $charges
67
     * @return ChargeInterface[]
68
     */
69
    protected function mergeCharges(array $charges): array
70
    {
71
        $res = [];
72
        foreach ($charges as $charge) {
73
            $uid = $charge->getUniqueString();
74
            if (empty($res[$uid])) {
75
                $res[$uid] = $charge;
76
            } else {
77
                $res[$uid] = $this->mergeCharge($res[$uid], $charge);
78
            }
79
        }
80
81
        return $res;
82
    }
83
84
    /**
85
     * Merges two charges.
86
     * Simple implementation just returns latest charge.
87
     */
88
    protected function mergeCharge(ChargeInterface $first, ChargeInterface $other): ChargeInterface
89
    {
90
        if (!$first->hasId()) {
91
            $first->setId($other->getId());
92
        }
93
94
        return $first;
95
    }
96
97
    /**
98
     * @return string|int|null
99
     */
100
    protected function mergeId(BillInterface $first, BillInterface $other)
101
    {
102
        if ($first->getId() === null) {
0 ignored issues
show
introduced by
The condition $first->getId() === null is always false.
Loading history...
103
            return $other->getId();
104
        }
105
        if ($other->getId() === null) {
0 ignored issues
show
introduced by
The condition $other->getId() === null is always false.
Loading history...
106
            return $first->getId();
107
        }
108
        if ($first->getId() === $other->getId()) {
109
            return $other->getId();
110
        }
111
112
        throw new AggregationException('cannot merge bills with different IDs');
113
    }
114
115
    /**
116
     * @param ChargeInterface[] $charges
117
     */
118
    protected function mergeSum(BillInterface $first, BillInterface $other, array $charges): Money
119
    {
120
        if (empty($charges)) {
121
            return $first->getSum()->add($other->getSum());
122
        }
123
124
        $sum = array_shift($charges)->getSum();
125
        foreach ($charges as $charge) {
126
            $sum = $sum->add($charge->getSum());
127
        }
128
129
        return $sum->negative();
130
    }
131
132
    /**
133
     * @param ChargeInterface[] $charges
134
     */
135
    protected function mergeQuantity(BillInterface $first, BillInterface $other, array $charges): QuantityInterface
136
    {
137
        if (empty($charges)) {
138
            return $first->getQuantity()->add($other->getQuantity());
139
        }
140
141
        $usage = array_shift($charges)->getUsage();
142
        foreach ($charges as $charge) {
143
            if (!$charge->getUsage()->isConvertible($usage->getUnit())) {
144
                continue;
145
            }
146
            $usage = $usage->add($charge->getUsage());
147
        }
148
149
        return $usage;
150
    }
151
}
152