Completed
Push — master ( a7b6c6...c81e64 )
by Joachim
14:42
created

StockMovementSubscriber::update()   C

Complexity

Conditions 7
Paths 8

Size

Total Lines 42
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 56

Importance

Changes 0
Metric Value
dl 0
loc 42
ccs 0
cts 19
cp 0
rs 6.7272
c 0
b 0
f 0
cc 7
eloc 20
nc 8
nop 1
crap 56
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Loevgaard\DandomainStockBundle\EventListener;
6
7
use Doctrine\Common\EventSubscriber;
8
use Doctrine\Common\Persistence\Event\LifecycleEventArgs;
9
use Doctrine\ORM\EntityManager;
10
use Doctrine\ORM\Event\OnFlushEventArgs;
11
use Doctrine\ORM\Events;
12
use Doctrine\ORM\Mapping\ClassMetadata;
13
use Doctrine\ORM\ORMException;
14
use Doctrine\ORM\UnitOfWork;
15
use Loevgaard\DandomainFoundation\Entity\Generated\OrderInterface;
16
use Loevgaard\DandomainFoundation\Entity\Generated\OrderLineInterface;
17
use Loevgaard\DandomainStock\Entity\Generated\StockMovementInterface;
18
use Loevgaard\DandomainStock\Entity\StockMovement;
19
use Loevgaard\DandomainStock\Exception\CurrencyMismatchException;
20
use Loevgaard\DandomainStock\Exception\StockMovementProductMismatchException;
21
use Loevgaard\DandomainStock\Exception\UndefinedPriceForCurrencyException;
22
use Loevgaard\DandomainStock\Exception\UnsetCurrencyException;
23
use Loevgaard\DandomainStock\Exception\UnsetProductException;
24
25
class StockMovementSubscriber implements EventSubscriber
26
{
27
    /**
28
     * @var array
29
     */
30
    private $orderStateIds;
31
32
    /**
33
     * @param array $orderStateIds An array of external ids for order states (use the id in the Dandomain interface)
34
     */
35 1
    public function __construct(array $orderStateIds)
36
    {
37 1
        $this->orderStateIds = $orderStateIds;
38 1
    }
39
40 1
    public function getSubscribedEvents()
41
    {
42
        return [
43 1
            Events::onFlush,
44
        ];
45
    }
46
47
    /**
48
     * @param OnFlushEventArgs $args
49
     * @throws CurrencyMismatchException
50
     * @throws ORMException
51
     * @throws StockMovementProductMismatchException
52
     * @throws UndefinedPriceForCurrencyException
53
     * @throws UnsetCurrencyException
54
     * @throws UnsetProductException
55
     */
56
    public function onFlush(OnFlushEventArgs $args)
57
    {
58
        $em = $args->getEntityManager();
59
        $uow = $em->getUnitOfWork();
60
61
        foreach ($uow->getScheduledEntityInsertions() as $entity) {
62
            /** @var OrderLineInterface $entity */
63
            if(!$this->isValidOrderLine($entity)) {
64
                continue;
65
            }
66
67
            $stockMovement = $this->stockMovementFromOrderLine($entity);
68
69
            $this->persistStockMovement($stockMovement);
0 ignored issues
show
Bug introduced by
The call to persistStockMovement() misses some required arguments starting with $entityManager.
Loading history...
70
        }
71
72
        foreach ($uow->getScheduledEntityUpdates() as $entity) {
73
            /** @var OrderLineInterface $entity */
74
            if(!$this->isValidOrderLine($entity)) {
75
                continue;
76
            }
77
78
            $stockMovement = $this->stockMovementFromOrderLine($entity);
79
            $effectiveStockMovement = $entity->computeEffectiveStockMovement();
80
            if ($effectiveStockMovement) {
81
                $stockMovement = $effectiveStockMovement->diff($stockMovement);
82
                $stockMovement->setType(StockMovement::TYPE_REGULATION);
83
            }
84
85
            $this->persistStockMovement($stockMovement);
0 ignored issues
show
Bug introduced by
The call to persistStockMovement() misses some required arguments starting with $entityManager.
Loading history...
86
        }
87
88
        foreach ($uow->getScheduledEntityDeletions() as $entity) {
89
            /** @var OrderLineInterface $entity */
90
            if(!$this->isValidOrderLine($entity)) {
91
                continue;
92
            }
93
94
            $effectiveStockMovement = $entity->computeEffectiveStockMovement();
95
96
            // if the quantity is 0 we don't want to add a stock movement since this will just pollute the stock movement table
97
            if ($effectiveStockMovement && $effectiveStockMovement->getQuantity() !== 0) {
98
                $stockMovement = $effectiveStockMovement->inverse();
99
                $stockMovement
100
                    ->setType(StockMovement::TYPE_REGULATION) // we set the type as regulation since it is not a sale now
101
                    ->setOrderLineRemoved(true)
102
                    ->setOrderLine(null);
103
104
                $this->persistStockMovement($stockMovement);
0 ignored issues
show
Bug introduced by
The call to persistStockMovement() misses some required arguments starting with $entityManager.
Loading history...
105
            }
106
107
            foreach ($entity->getStockMovements() as $stockMovement) {
108
                $stockMovement
109
                    ->setOrderLineRemoved(true)
110
                    ->setOrderLine(null);
111
112
                $md = $this->metaData($em, $stockMovement);
113
                $uow->recomputeSingleEntityChangeSet($md, $stockMovement);
114
            }
115
        }
116
    }
117
118
    private function isValidOrderLine($entity) : bool
119
    {
120
        return $entity instanceof OrderLineInterface
0 ignored issues
show
Bug introduced by
The class Loevgaard\DandomainFound...ated\OrderLineInterface does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
121
            && $entity->getOrder()
122
            && $entity->getOrder()->getState()
123
            && in_array($entity->getOrder()->getState()->getExternalId(), $this->orderStateIds);
124
    }
125
126
    /**
127
     * @param OrderLineInterface $orderLine
128
     * @return StockMovement
129
     * @throws CurrencyMismatchException
130
     * @throws UndefinedPriceForCurrencyException
131
     * @throws UnsetProductException
132
     */
133
    private function stockMovementFromOrderLine(OrderLineInterface $orderLine) : StockMovement
134
    {
135
        $stockMovement = new StockMovement();
136
        $stockMovement->populateFromOrderLine($orderLine);
137
138
        return $stockMovement;
139
    }
140
141
    private function metaData(EntityManager $entityManager, $entity) : ClassMetadata
142
    {
143
        return $entityManager->getClassMetadata(get_class($entity));
144
    }
145
146
    /**
147
     * @param StockMovementInterface $stockMovement
148
     * @param EntityManager $entityManager
149
     * @param UnitOfWork $unitOfWork
150
     * @throws ORMException
151
     */
152
    private function persistStockMovement(StockMovementInterface $stockMovement, EntityManager $entityManager, UnitOfWork $unitOfWork)
153
    {
154
        $stockMovement->validate();
155
        $entityManager->persist($stockMovement);
156
        $md = $this->metaData($entityManager, $stockMovement);
157
        $unitOfWork->computeChangeSet($md, $stockMovement);
158
    }
159
160
161
    /**
162
     * @param LifecycleEventArgs $args
163
     *
164
     * @return bool
165
     *
166
     * @throws CurrencyMismatchException
167
     * @throws StockMovementProductMismatchException
168
     * @throws UndefinedPriceForCurrencyException
169
     * @throws UnsetCurrencyException
170
     * @throws UnsetProductException
171
     */
172
    private function update(LifecycleEventArgs $args)
0 ignored issues
show
Unused Code introduced by
This method is not used, and could be removed.
Loading history...
173
    {
174
        /** @var OrderInterface $entity */
175
        $entity = $args->getObject();
176
177
        if (!($entity instanceof OrderInterface)) {
0 ignored issues
show
Bug introduced by
The class Loevgaard\DandomainFound...enerated\OrderInterface does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
178
            return false;
179
        }
180
181
        // only log a stock movement when the order state is in the specified order states, typically 'completed'
182
        if (!in_array($entity->getState()->getExternalId(), $this->orderStateIds)) {
183
            return false;
184
        }
185
186
        $i = 0;
187
        foreach ($entity->getOrderLines() as $orderLine) {
188
            // if the order line does not have a valid product, we wont add it to the stock movements table
189
            // examples of products like this are discounts
190
            if (!$orderLine->getProduct()) {
191
                continue;
192
            }
193
194
            $stockMovement = new StockMovement();
195
            $stockMovement->populateFromOrderLine($orderLine);
196
197
            $effectiveStockMovement = $orderLine->computeEffectiveStockMovement();
198
            if ($effectiveStockMovement) {
199
                $stockMovement = $effectiveStockMovement->diff($stockMovement);
200
            }
201
202
            // if the quantity is 0 we don't want to add a stock movement since this will just pollute the stock movement table
203
            if($stockMovement->getQuantity() === 0) {
204
                continue;
205
            }
206
207
            $orderLine->addStockMovement($stockMovement);
208
209
            ++$i;
210
        }
211
212
        return true;
213
    }
214
}
215