Failed Conditions
Pull Request — experimental/sf (#3385)
by chihiro
62:56 queued 51:03
created

StockDiffProcessor::validate()   B

Complexity

Conditions 7
Paths 6

Size

Total Lines 32

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 12.2913

Importance

Changes 0
Metric Value
cc 7
nc 6
nop 2
dl 0
loc 32
rs 8.4746
c 0
b 0
f 0
ccs 11
cts 21
cp 0.5238
crap 12.2913
1
<?php
2
3
/*
4
 * This file is part of EC-CUBE
5
 *
6
 * Copyright(c) LOCKON CO.,LTD. All Rights Reserved.
7
 *
8
 * http://www.lockon.co.jp/
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 * file that was distributed with this source code.
12
 */
13
14
namespace Eccube\Service\PurchaseFlow\Processor;
15
16
use Eccube\Entity\ItemHolderInterface;
17
use Eccube\Entity\Master\OrderStatus;
18
use Eccube\Entity\ProductClass;
19
use Eccube\Entity\ProductStock;
20
use Eccube\Repository\ProductClassRepository;
21
use Eccube\Service\PurchaseFlow\ItemHolderValidator;
22
use Eccube\Service\PurchaseFlow\PurchaseContext;
23
use Eccube\Service\PurchaseFlow\PurchaseException;
24
use Eccube\Service\PurchaseFlow\PurchaseProcessor;
25
26
/**
27
 * 編集前/編集後の個数の差分にもとづいて在庫を更新します.
28
 */
29
class StockDiffProcessor extends ItemHolderValidator implements PurchaseProcessor
30
{
31
    /**
32
     * @var ProductClassRepository
33
     */
34
    protected $productClassRepository;
35
36
    /**
37
     * StockProcessor constructor.
38
     *
39
     * @param ProductClassRepository $productClassRepository
40
     */
41 654
    public function __construct(ProductClassRepository $productClassRepository)
42
    {
43 654
        $this->productClassRepository = $productClassRepository;
44
    }
45
46
    /**
47
     * @param ItemHolderInterface $itemHolder
48
     * @param PurchaseContext $context
0 ignored issues
show
introduced by
Expected 5 spaces after parameter type; 1 found
Loading history...
49
     *
50
     * @throws \Eccube\Service\PurchaseFlow\InvalidItemException
51
     */
52 146
    public function validate(ItemHolderInterface $itemHolder, PurchaseContext $context)
53
    {
54 146
        $From = $context->getOriginHolder();
55 146
        $To = $itemHolder;
56 146
        $diff = $this->getDiffOfQuantities($From, $To);
0 ignored issues
show
Bug introduced by
It seems like $From defined by $context->getOriginHolder() on line 54 can be null; however, Eccube\Service\PurchaseF...::getDiffOfQuantities() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
57
58 146
        foreach ($diff as $id => $quantity) {
59
            /** @var ProductClass $ProductClass */
60 146
            $ProductClass = $this->productClassRepository->find($id);
61 146
            if ($ProductClass->isStockUnlimited()) {
62
                continue;
63
            }
64 146
            $stock = $ProductClass->getStock();
65
            // 更新後ステータスがキャンセルの場合は, 差分ではなく更新後の個数で確認.
66 146
            if ($To->getOrderStatus() && $To->getOrderStatus()->getId() == OrderStatus::CANCEL) {
67
                $Items = $To->getProductOrderItems();
68
                $Items = array_filter($Items, function ($Item) use ($id) {
69
                    return $Item->getProductClass()->getId() == $id;
70
                });
71
                $toQuantity = array_reduce($Items, function ($quantity, $Item) {
72
                    return $quantity += $Item->getQuantity();
73
                }, 0);
74
                if ($stock - $toQuantity < 0) {
75
                    $this->throwInvalidItemException(sprintf('%s: 在庫がたりません', $ProductClass->formatedProductName()));
76
                }
77
            } else {
78 146
                if ($stock - $quantity < 0) {
79 146
                    $this->throwInvalidItemException(sprintf('%s: 在庫がたりません', $ProductClass->formatedProductName()));
80
                }
81
            }
82
        }
83
    }
84
85 146
    protected function getDiffOfQuantities(ItemHolderInterface $From, ItemHolderInterface $To)
86
    {
87 146
        $FromItems = $this->getQuantityByProductClass($From);
88 146
        $ToItems = $this->getQuantityByProductClass($To);
89 146
        $ids = array_unique(array_merge(array_keys($FromItems), array_keys($ToItems)));
90
91 146
        $diff = [];
92 146
        foreach ($ids as $id) {
93
            // 更新された明細
94 146
            if (isset($FromItems[$id]) && isset($ToItems[$id])) {
95
                // 2 -> 3 = +1
96
                // 2 -> 1 = -1
97 144
                $diff[$id] = $ToItems[$id] - $FromItems[$id];
98
            } // 削除された明細
99 5 View Code Duplication
            elseif (isset($FromItems[$id]) && empty($ToItems[$id])) {
100
                // 2 -> 0 = -2
101 3
                $diff[$id] = $FromItems[$id] * -1;
102
            } // 追加された明細
103 5 View Code Duplication
            elseif (!isset($FromItems[$id]) && isset($ToItems[$id])) {
104
                // 0 -> 2 = +2
105 146
                $diff[$id] = $ToItems[$id];
106
            }
107
        }
108
109 146
        return $diff;
110
    }
111
112 146
    protected function getQuantityByProductClass(ItemHolderInterface $ItemHolder)
113
    {
114 146
        $ItemsByProductClass = [];
115 146
        foreach ($ItemHolder->getItems() as $Item) {
116 146
            if ($Item->isProduct()) {
117 146
                $id = $Item->getProductClass()->getId();
118 146
                if (isset($ItemsByProductClass[$id])) {
119
                    $ItemsByProductClass[$id] += $Item->getQuantity();
120
                } else {
121 146
                    $ItemsByProductClass[$id] = $Item->getQuantity();
122
                }
123
            }
124
        }
125
126 146
        return $ItemsByProductClass;
127
    }
128
129
    /**
130
     * 受注の仮確定処理を行います。
131
     *
132
     * @param ItemHolderInterface $target
133
     * @param PurchaseContext $context
0 ignored issues
show
introduced by
Expected 5 spaces after parameter type; 1 found
Loading history...
134
     *
135
     * @throws PurchaseException
136
     */
137 5
    public function prepare(ItemHolderInterface $target, PurchaseContext $context)
0 ignored issues
show
introduced by
Declare public methods first, then protected ones and finally private ones
Loading history...
138
    {
139 5
        $diff = $this->getDiffOfQuantities($context->getOriginHolder(), $target);
0 ignored issues
show
Bug introduced by
It seems like $context->getOriginHolder() can be null; however, getDiffOfQuantities() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
140
141 5
        foreach ($diff as $id => $quantity) {
142
            /** @var ProductClass $ProductClass */
143 5
            $ProductClass = $this->productClassRepository->find($id);
144 5
            if ($ProductClass->isStockUnlimited()) {
145
                continue;
146
            }
147
148 5
            $stock = $ProductClass->getStock() - $quantity;
149 5
            $ProductStock = $ProductClass->getProductStock();
150 5
            if (!$ProductStock) {
151
                $ProductStock = new ProductStock();
152
                $ProductStock->setProductClass($ProductClass);
153
                $ProductClass->setProductStock($ProductStock);
154
            }
155 5
            $ProductClass->setStock($stock);
156 5
            $ProductStock->setStock($stock);
157
        }
158
    }
159
160
    /**
161
     * 受注の確定処理を行います。
162
     *
163
     * @param ItemHolderInterface $target
164
     * @param PurchaseContext $context
0 ignored issues
show
introduced by
Expected 5 spaces after parameter type; 1 found
Loading history...
165
     *
166
     * @throws PurchaseException
167
     */
168 5
    public function commit(ItemHolderInterface $target, PurchaseContext $context)
169
    {
170
        // 何もしない.
171 5
        return;
0 ignored issues
show
Coding Style introduced by
Empty return statement not required here
Loading history...
172
    }
173
174
    /**
175
     * 仮確定した受注データの取り消し処理を行います。
176
     *
177
     * @param ItemHolderInterface $itemHolder
178
     * @param PurchaseContext $context
0 ignored issues
show
introduced by
Expected 5 spaces after parameter type; 1 found
Loading history...
179
     */
180
    public function rollback(ItemHolderInterface $itemHolder, PurchaseContext $context)
181
    {
182
        // 何もしない.
183
        return;
0 ignored issues
show
Coding Style introduced by
Empty return statement not required here
Loading history...
184
    }
185
}
186