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\Controller; |
15
|
|
|
|
16
|
|
|
use Eccube\Annotation\ForwardOnly; |
17
|
|
|
use Eccube\Entity\BaseInfo; |
18
|
|
|
use Eccube\Entity\Customer; |
19
|
|
|
use Eccube\Entity\CustomerAddress; |
20
|
|
|
use Eccube\Entity\Master\OrderStatus; |
21
|
|
|
use Eccube\Entity\Order; |
22
|
|
|
use Eccube\Entity\Shipping; |
23
|
|
|
use Eccube\Event\EccubeEvents; |
24
|
|
|
use Eccube\Event\EventArgs; |
25
|
|
|
use Eccube\Exception\CartException; |
26
|
|
|
use Eccube\Exception\ShoppingException; |
27
|
|
|
use Eccube\Form\Type\Front\CustomerLoginType; |
28
|
|
|
use Eccube\Form\Type\Front\ShoppingShippingType; |
29
|
|
|
use Eccube\Form\Type\Shopping\CustomerAddressType; |
30
|
|
|
use Eccube\Form\Type\Shopping\OrderType; |
31
|
|
|
use Eccube\Repository\BaseInfoRepository; |
32
|
|
|
use Eccube\Repository\CustomerAddressRepository; |
33
|
|
|
use Eccube\Repository\OrderRepository; |
34
|
|
|
use Eccube\Service\CartService; |
35
|
|
|
use Eccube\Service\OrderHelper; |
36
|
|
|
use Eccube\Service\Payment\PaymentDispatcher; |
37
|
|
|
use Eccube\Service\ShoppingService; |
38
|
|
|
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method; |
39
|
|
|
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; |
40
|
|
|
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template; |
41
|
|
|
use Symfony\Component\Form\FormError; |
42
|
|
|
use Symfony\Component\Form\FormInterface; |
43
|
|
|
use Symfony\Component\HttpFoundation\ParameterBag; |
44
|
|
|
use Symfony\Component\HttpFoundation\Request; |
45
|
|
|
use Symfony\Component\HttpFoundation\Response; |
46
|
|
|
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; |
47
|
|
|
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils; |
48
|
|
|
|
49
|
|
|
class ShoppingController extends AbstractShoppingController |
50
|
|
|
{ |
51
|
|
|
/** |
52
|
|
|
* @var BaseInfo |
53
|
|
|
*/ |
54
|
|
|
protected $BaseInfo; |
55
|
|
|
|
56
|
|
|
/** |
57
|
|
|
* @var OrderHelper |
58
|
|
|
*/ |
59
|
|
|
protected $orderHelper; |
60
|
|
|
|
61
|
|
|
/** |
62
|
|
|
* @var CartService |
63
|
|
|
*/ |
64
|
|
|
protected $cartService; |
65
|
|
|
|
66
|
|
|
/** |
67
|
|
|
* @var ShoppingService |
68
|
|
|
*/ |
69
|
|
|
protected $shoppingService; |
70
|
|
|
|
71
|
|
|
/** |
72
|
|
|
* @var CustomerAddressRepository |
73
|
|
|
*/ |
74
|
|
|
protected $customerAddressRepository; |
75
|
|
|
|
76
|
|
|
/** |
77
|
|
|
* @var ParameterBag |
78
|
|
|
*/ |
79
|
|
|
protected $parameterBag; |
80
|
|
|
|
81
|
|
|
/** |
82
|
|
|
* ShoppingController constructor. |
83
|
|
|
* |
84
|
|
|
* @param BaseInfoRepository $baseInfoRepository |
85
|
|
|
* @param OrderHelper $orderHelper |
86
|
|
|
* @param CartService $cartService |
87
|
|
|
* @param ShoppingService $shoppingService |
88
|
|
|
* @param CustomerAddressRepository $customerAddressRepository |
89
|
|
|
* @param ParameterBag $parameterBag |
90
|
|
|
*/ |
91
|
59 |
View Code Duplication |
public function __construct( |
|
|
|
|
92
|
|
|
BaseInfoRepository $baseInfoRepository, |
93
|
|
|
OrderHelper $orderHelper, |
94
|
|
|
CartService $cartService, |
95
|
|
|
ShoppingService $shoppingService, |
96
|
|
|
CustomerAddressRepository $customerAddressRepository, |
97
|
|
|
OrderRepository $orderRepository, |
98
|
|
|
ParameterBag $parameterBag |
99
|
|
|
) { |
100
|
59 |
|
$this->BaseInfo = $baseInfoRepository->get(); |
101
|
59 |
|
$this->orderHelper = $orderHelper; |
102
|
59 |
|
$this->cartService = $cartService; |
103
|
59 |
|
$this->shoppingService = $shoppingService; |
104
|
59 |
|
$this->customerAddressRepository = $customerAddressRepository; |
105
|
59 |
|
$this->orderRepository = $orderRepository; |
106
|
59 |
|
$this->parameterBag = $parameterBag; |
107
|
|
|
} |
108
|
|
|
|
109
|
|
|
/** |
110
|
|
|
* 購入画面表示 |
111
|
|
|
* |
112
|
|
|
* @Route("/shopping", name="shopping") |
113
|
|
|
* @Template("Shopping/index.twig") |
114
|
|
|
*/ |
115
|
51 |
|
public function index(Request $request) |
|
|
|
|
116
|
|
|
{ |
117
|
|
|
// カートチェック |
118
|
51 |
|
$response = $this->forwardToRoute('shopping_check_to_cart'); |
119
|
51 |
|
if ($response->isRedirection() || $response->getContent()) { |
120
|
1 |
|
return $response; |
121
|
|
|
} |
122
|
|
|
|
123
|
|
|
// 受注情報を初期化 |
124
|
50 |
|
$response = $this->forwardToRoute('shopping_initialize_order'); |
125
|
50 |
|
if ($response->isRedirection() || $response->getContent()) { |
126
|
|
|
return $response; |
127
|
|
|
} |
128
|
|
|
|
129
|
|
|
/** @var Order $Order */ |
130
|
50 |
|
$Order = $this->parameterBag->get('Order'); |
131
|
|
|
|
132
|
|
|
// 単価集計 |
133
|
50 |
|
$flowResult = $this->validatePurchaseFlow($Order); |
134
|
|
|
|
135
|
|
|
// 明細が丸められる場合に, カートから注文画面へ遷移できなくなるため, 集計の結果を保存する |
136
|
50 |
|
$this->entityManager->flush(); |
137
|
|
|
|
138
|
|
|
// フォームを生成する |
139
|
50 |
|
$this->forwardToRoute('shopping_create_form'); |
140
|
|
|
|
141
|
50 |
|
if ($flowResult->hasWarning() || $flowResult->hasError()) { |
142
|
10 |
|
return $this->redirectToRoute('cart'); |
143
|
|
|
} |
144
|
|
|
|
145
|
45 |
|
$form = $this->parameterBag->get(OrderType::class); |
146
|
|
|
|
147
|
|
|
return [ |
148
|
45 |
|
'form' => $form->createView(), |
149
|
45 |
|
'Order' => $Order, |
150
|
|
|
]; |
151
|
|
|
} |
152
|
|
|
|
153
|
|
|
/** |
154
|
|
|
* 購入確認画面から, 他の画面へのリダイレクト. |
155
|
|
|
* 配送業者や支払方法、お問い合わせ情報をDBに保持してから遷移する. |
156
|
|
|
* |
157
|
|
|
* @Route("/shopping/redirect", name="shopping_redirect_to") |
158
|
|
|
* @Template("Shopping/index.twig") |
159
|
|
|
*/ |
160
|
20 |
|
public function redirectTo(Request $request) |
161
|
|
|
{ |
162
|
|
|
// カートチェック |
163
|
20 |
|
$response = $this->forwardToRoute('shopping_check_to_cart'); |
164
|
20 |
|
if ($response->isRedirection() || $response->getContent()) { |
165
|
|
|
return $response; |
166
|
|
|
} |
167
|
|
|
|
168
|
|
|
// 受注の存在チェック |
169
|
20 |
|
$response = $this->forwardToRoute('shopping_exists_order'); |
170
|
20 |
|
if ($response->isRedirection() || $response->getContent()) { |
171
|
|
|
return $response; |
172
|
|
|
} |
173
|
|
|
|
174
|
|
|
// フォームの生成 |
175
|
20 |
|
$this->forwardToRoute('shopping_create_form'); |
176
|
20 |
|
$form = $this->parameterBag->get(OrderType::class); |
177
|
20 |
|
$form->handleRequest($request); |
178
|
|
|
|
179
|
|
|
// 各種変更ページへリダイレクトする |
180
|
20 |
|
$response = $this->forwardToRoute('shopping_redirect_to_change'); |
181
|
20 |
|
if ($response->isRedirection() || $response->getContent()) { |
182
|
8 |
|
return $response; |
183
|
|
|
} |
184
|
12 |
|
$form = $this->parameterBag->get(OrderType::class); |
185
|
12 |
|
$Order = $this->parameterBag->get('Order'); |
186
|
|
|
|
187
|
|
|
return [ |
188
|
12 |
|
'form' => $form->createView(), |
189
|
12 |
|
'Order' => $Order, |
190
|
|
|
]; |
191
|
|
|
} |
192
|
|
|
|
193
|
|
|
/** |
194
|
|
|
* 購入処理 |
195
|
|
|
* |
196
|
|
|
* @Route("/shopping/confirm", name="shopping_confirm") |
197
|
|
|
* @Method("POST") |
198
|
|
|
* @Template("Shopping/confirm.twig") |
199
|
|
|
*/ |
200
|
5 |
|
public function confirm(Request $request) |
201
|
|
|
{ |
202
|
|
|
// カートチェック |
203
|
5 |
|
$response = $this->forwardToRoute('shopping_check_to_cart'); |
204
|
5 |
|
if ($response->isRedirection() || $response->getContent()) { |
205
|
|
|
return $response; |
206
|
|
|
} |
207
|
|
|
|
208
|
|
|
// 受注の存在チェック |
209
|
5 |
|
$response = $this->forwardToRoute('shopping_exists_order'); |
210
|
5 |
|
if ($response->isRedirection() || $response->getContent()) { |
211
|
|
|
return $response; |
212
|
|
|
} |
213
|
|
|
|
214
|
|
|
// フォームの生成 |
215
|
5 |
|
$this->forwardToRoute('shopping_create_form'); |
216
|
5 |
|
$form = $this->parameterBag->get(OrderType::class); |
217
|
5 |
|
$form->handleRequest($request); |
218
|
|
|
|
219
|
5 |
|
$form = $this->parameterBag->get(OrderType::class); |
220
|
5 |
|
$Order = $this->parameterBag->get('Order'); |
221
|
|
|
|
222
|
5 |
|
$flowResult = $this->validatePurchaseFlow($Order); |
223
|
5 |
|
if ($flowResult->hasWarning() || $flowResult->hasError()) { |
224
|
|
|
return $this->redirectToRoute('shopping_error'); |
225
|
|
|
} |
226
|
|
|
|
227
|
5 |
|
$paymentMethod = $this->createPaymentMethod($Order, $form); |
228
|
|
|
|
229
|
5 |
|
$PaymentResult = $paymentMethod->verify(); |
230
|
|
|
// エラーの場合は注文入力画面に戻す? |
231
|
5 |
|
if ($PaymentResult instanceof PaymentResult) { |
232
|
|
|
if (!$PaymentResult->isSuccess()) { |
233
|
|
|
$this->entityManager->getConnection()->rollback(); |
234
|
|
|
|
235
|
|
|
$this->addError($PaymentResult->getErrors()); |
236
|
|
|
} |
237
|
|
|
|
238
|
|
|
$response = $PaymentResult->getResponse(); |
239
|
|
|
if ($response && ($response->isRedirection() || $response->getContent())) { |
240
|
|
|
$this->entityManager->flush(); |
241
|
|
|
|
242
|
|
|
return $response; |
243
|
|
|
} |
244
|
|
|
} |
245
|
5 |
|
$this->entityManager->flush(); |
246
|
|
|
|
247
|
|
|
return [ |
248
|
5 |
|
'form' => $form->createView(), |
249
|
5 |
|
'Order' => $Order, |
250
|
|
|
]; |
251
|
|
|
} |
252
|
|
|
|
253
|
|
|
/** |
254
|
|
|
* 購入処理 |
255
|
|
|
* |
256
|
|
|
* @Route("/shopping/order", name="shopping_order") |
257
|
|
|
* @Method("POST") |
258
|
|
|
* @Template("Shopping/index.twig") |
259
|
|
|
*/ |
260
|
6 |
|
public function order(Request $request) |
261
|
|
|
{ |
262
|
|
|
// カートチェック |
263
|
6 |
|
$response = $this->forwardToRoute('shopping_check_to_cart'); |
264
|
6 |
|
if ($response->isRedirection() || $response->getContent()) { |
265
|
|
|
return $response; |
266
|
|
|
} |
267
|
|
|
|
268
|
|
|
// 受注の存在チェック |
269
|
6 |
|
$response = $this->forwardToRoute('shopping_exists_order'); |
270
|
6 |
|
if ($response->isRedirection() || $response->getContent()) { |
271
|
|
|
return $response; |
272
|
|
|
} |
273
|
|
|
|
274
|
|
|
// form作成 |
275
|
|
|
// FIXME イベントハンドラを外から渡したい |
276
|
6 |
|
$this->forwardToRoute('shopping_create_form'); |
277
|
|
|
|
278
|
6 |
|
$form = $this->parameterBag->get(OrderType::class); |
279
|
6 |
|
$Order = $this->parameterBag->get('Order'); |
280
|
6 |
|
$usePoint = $Order->getUsePoint(); |
281
|
|
|
|
282
|
6 |
|
$form->handleRequest($request); |
283
|
6 |
|
$Order->setUsePoint($usePoint); |
284
|
|
|
|
285
|
|
|
// 受注処理 |
286
|
6 |
|
$response = $this->forwardToRoute('shopping_complete_order'); |
287
|
6 |
|
if ($response->isRedirection() || $response->getContent()) { |
288
|
6 |
|
return $response; |
289
|
|
|
} |
290
|
|
|
|
291
|
|
|
log_info('購入チェックエラー', [$Order->getId()]); |
292
|
|
|
|
293
|
|
|
return [ |
294
|
|
|
'form' => $form->createView(), |
295
|
|
|
'Order' => $Order, |
296
|
|
|
]; |
297
|
|
|
} |
298
|
|
|
|
299
|
|
|
/** |
300
|
|
|
* 支払方法バーリデト |
301
|
|
|
*/ |
302
|
|
|
private function isValidPayment(Application $app, $form) |
|
|
|
|
303
|
|
|
{ |
304
|
|
|
$data = $form->getData(); |
305
|
|
|
$paymentId = $data['payment']->getId(); |
306
|
|
|
$shippings = $data['shippings']; |
307
|
|
|
$validCount = count($shippings); |
308
|
|
|
foreach ($shippings as $Shipping) { |
309
|
|
|
$payments = $app['eccube.repository.payment']->findPayments($Shipping->getDelivery()); |
310
|
|
|
if ($payments == null) { |
311
|
|
|
continue; |
312
|
|
|
} |
313
|
|
|
foreach ($payments as $payment) { |
314
|
|
|
if ($payment['id'] == $paymentId) { |
315
|
|
|
$validCount--; |
316
|
|
|
continue; |
317
|
|
|
} |
318
|
|
|
} |
319
|
|
|
} |
320
|
|
|
if ($validCount == 0) { |
321
|
|
|
return true; |
322
|
|
|
} |
323
|
|
|
$form->get('payment')->addError(new FormError('front.shopping.payment.error')); |
324
|
|
|
|
325
|
|
|
return false; |
326
|
|
|
} |
327
|
|
|
|
328
|
|
|
/** |
329
|
|
|
* 購入完了画面表示 |
330
|
|
|
* |
331
|
|
|
* @Route("/shopping/complete", name="shopping_complete") |
332
|
|
|
* @Template("Shopping/complete.twig") |
333
|
|
|
*/ |
334
|
1 |
|
public function complete(Request $request) |
335
|
|
|
{ |
336
|
|
|
// 受注IDを取得 |
337
|
1 |
|
$orderId = $this->session->get($this->sessionOrderKey); |
338
|
|
|
|
339
|
1 |
|
if (empty($orderId)) { |
340
|
|
|
return $this->redirectToRoute('homepage'); |
341
|
|
|
} |
342
|
|
|
|
343
|
1 |
|
$Order = $this->orderRepository->find($orderId); |
344
|
|
|
|
345
|
1 |
|
$event = new EventArgs( |
346
|
|
|
[ |
347
|
1 |
|
'Order' => $Order, |
348
|
|
|
], |
349
|
1 |
|
$request |
350
|
|
|
); |
351
|
1 |
|
$this->eventDispatcher->dispatch(EccubeEvents::FRONT_SHOPPING_COMPLETE_INITIALIZE, $event); |
352
|
|
|
|
353
|
1 |
|
if ($event->getResponse() !== null) { |
354
|
|
|
return $event->getResponse(); |
355
|
|
|
} |
356
|
|
|
|
357
|
|
|
// 受注に関連するセッションを削除 |
358
|
1 |
|
$this->session->remove($this->sessionOrderKey); |
359
|
1 |
|
$this->session->remove($this->sessionKey); |
360
|
1 |
|
$this->session->remove($this->sessionCustomerAddressKey); |
361
|
|
|
|
362
|
1 |
|
log_info('購入処理完了', [$Order->getId()]); |
363
|
|
|
|
364
|
1 |
|
$hasNextCart = !empty($this->cartService->getCarts()); |
365
|
|
|
|
366
|
|
|
return [ |
367
|
1 |
|
'Order' => $Order, |
368
|
1 |
|
'hasNextCart' => $hasNextCart, |
369
|
|
|
]; |
370
|
|
|
} |
371
|
|
|
|
372
|
|
|
/** |
373
|
|
|
* お届け先の設定一覧からの選択 |
374
|
|
|
* |
375
|
|
|
* @Route("/shopping/shipping/{id}", name="shopping_shipping", requirements={"id" = "\d+"}) |
376
|
|
|
* @Template("Shopping/shipping.twig") |
377
|
|
|
*/ |
378
|
|
|
public function shipping(Request $request, Shipping $Shipping) |
379
|
|
|
{ |
380
|
|
|
// カートチェック |
381
|
|
|
$response = $this->forwardToRoute('shopping_check_to_cart'); |
382
|
|
|
if ($response->isRedirection() || $response->getContent()) { |
383
|
|
|
return $response; |
384
|
|
|
} |
385
|
|
|
|
386
|
|
|
// 受注の存在チェック |
387
|
|
|
$response = $this->forwardToRoute('shopping_exists_order'); |
388
|
|
|
if ($response->isRedirection() || $response->getContent()) { |
389
|
|
|
return $response; |
390
|
|
|
} |
391
|
|
|
|
392
|
|
|
// 受注に紐づくShippingかどうかのチェック. |
393
|
|
|
/** @var Order $Order */ |
394
|
|
|
$Order = $this->parameterBag->get('Order'); |
395
|
|
|
if (!$Order->findShipping($Shipping->getId())) { |
396
|
|
|
throw new NotFoundHttpException(); |
397
|
|
|
} |
398
|
|
|
|
399
|
|
|
$builder = $this->formFactory->createBuilder(CustomerAddressType::class, null, [ |
400
|
|
|
'customer' => $this->getUser(), |
401
|
|
|
'shipping' => $Shipping, |
402
|
|
|
]); |
403
|
|
|
|
404
|
|
|
$form = $builder->getForm(); |
405
|
|
|
$form->handleRequest($request); |
406
|
|
|
|
407
|
|
|
if ($form->isSubmitted() && $form->isValid()) { |
408
|
|
|
log_info('お届先情報更新開始', [$Shipping->getId()]); |
409
|
|
|
|
410
|
|
|
/** @var CustomerAddress $CustomerAddress */ |
411
|
|
|
$CustomerAddress = $form['addresses']->getData(); |
412
|
|
|
|
413
|
|
|
// お届け先情報を更新 |
414
|
|
|
$Shipping->setFromCustomerAddress($CustomerAddress); |
415
|
|
|
|
416
|
|
|
// 合計金額の再計算 |
417
|
|
|
$flowResult = $this->validatePurchaseFlow($Order); |
418
|
|
|
if ($flowResult->hasWarning() || $flowResult->hasError()) { |
419
|
|
|
return $this->redirectToRoute('shopping_error'); |
420
|
|
|
} |
421
|
|
|
|
422
|
|
|
// 配送先を更新 |
423
|
|
|
$this->entityManager->flush(); |
424
|
|
|
|
425
|
|
|
$event = new EventArgs( |
426
|
|
|
[ |
427
|
|
|
'Order' => $Order, |
428
|
|
|
'Shipping' => $Shipping, |
429
|
|
|
], |
430
|
|
|
$request |
431
|
|
|
); |
432
|
|
|
$this->eventDispatcher->dispatch(EccubeEvents::FRONT_SHOPPING_SHIPPING_COMPLETE, $event); |
433
|
|
|
|
434
|
|
|
log_info('お届先情報更新完了', [$Shipping->getId()]); |
435
|
|
|
|
436
|
|
|
return $this->redirectToRoute('shopping'); |
437
|
|
|
} |
438
|
|
|
|
439
|
|
|
return [ |
440
|
|
|
'form' => $form->createView(), |
441
|
|
|
'Customer' => $this->getUser(), |
442
|
|
|
'shippingId' => $Shipping->getId(), |
443
|
|
|
]; |
444
|
|
|
} |
445
|
|
|
|
446
|
|
|
/** |
447
|
|
|
* お届け先の設定(非会員でも使用する) |
448
|
|
|
* |
449
|
|
|
* @Route("/shopping/shipping_edit/{id}", name="shopping_shipping_edit", requirements={"id" = "\d+"}) |
450
|
|
|
* @Template("Shopping/shipping_edit.twig") |
451
|
|
|
*/ |
452
|
|
|
public function shippingEdit(Request $request, $id) |
453
|
|
|
{ |
454
|
|
|
// 配送先住所最大値判定 |
455
|
|
|
$Customer = $this->getUser(); |
456
|
|
|
if ($this->isGranted('IS_AUTHENTICATED_FULLY')) { |
457
|
|
|
$addressCurrNum = count($this->getUser()->getCustomerAddresses()); |
458
|
|
|
$addressMax = $this->eccubeConfig['eccube_deliv_addr_max']; |
459
|
|
|
if ($addressCurrNum >= $addressMax) { |
460
|
|
|
throw new NotFoundHttpException(trans('shoppingcontroller.text.error.number_of_address')); |
461
|
|
|
} |
462
|
|
|
} |
463
|
|
|
|
464
|
|
|
// カートチェック |
465
|
|
|
$response = $this->forwardToRoute('shopping_check_to_cart'); |
466
|
|
|
if ($response->isRedirection() || $response->getContent()) { |
467
|
|
|
return $response; |
468
|
|
|
} |
469
|
|
|
|
470
|
|
|
// 受注の存在チェック |
471
|
|
|
$response = $this->forwardToRoute('shopping_exists_order'); |
472
|
|
|
if ($response->isRedirection() || $response->getContent()) { |
473
|
|
|
return $response; |
474
|
|
|
} |
475
|
|
|
|
476
|
|
|
/** @var Order $Order */ |
477
|
|
|
$Order = $this->parameterBag->get('Order'); |
478
|
|
|
|
479
|
|
|
$Shipping = $Order->findShipping($id); |
480
|
|
|
if (!$Shipping) { |
481
|
|
|
throw new NotFoundHttpException(trans('shoppingcontroller.text.error.set_address')); |
482
|
|
|
} |
483
|
|
|
if ($this->isGranted('IS_AUTHENTICATED_FULLY')) { |
484
|
|
|
$Shipping->clearCustomerAddress(); |
485
|
|
|
} |
486
|
|
|
|
487
|
|
|
$CustomerAddress = new CustomerAddress(); |
488
|
|
|
if ($this->isGranted('IS_AUTHENTICATED_FULLY')) { |
489
|
|
|
$CustomerAddress->setCustomer($Customer); |
|
|
|
|
490
|
|
|
} else { |
491
|
|
|
$CustomerAddress->setFromShipping($Shipping); |
492
|
|
|
} |
493
|
|
|
|
494
|
|
|
$builder = $this->formFactory->createBuilder(ShoppingShippingType::class, $CustomerAddress); |
495
|
|
|
|
496
|
|
|
$event = new EventArgs( |
497
|
|
|
[ |
498
|
|
|
'builder' => $builder, |
499
|
|
|
'Order' => $Order, |
500
|
|
|
'Shipping' => $Shipping, |
501
|
|
|
'CustomerAddress' => $CustomerAddress, |
502
|
|
|
], |
503
|
|
|
$request |
504
|
|
|
); |
505
|
|
|
$this->eventDispatcher->dispatch(EccubeEvents::FRONT_SHOPPING_SHIPPING_EDIT_INITIALIZE, $event); |
506
|
|
|
|
507
|
|
|
$form = $builder->getForm(); |
508
|
|
|
|
509
|
|
|
$form->handleRequest($request); |
510
|
|
|
|
511
|
|
|
if ($form->isSubmitted() && $form->isValid()) { |
512
|
|
|
log_info('お届け先追加処理開始', ['id' => $Order->getId(), 'shipping' => $id]); |
513
|
|
|
|
514
|
|
|
// 会員の場合、お届け先情報を新規登録 |
515
|
|
|
$Shipping->setFromCustomerAddress($CustomerAddress); |
516
|
|
|
|
517
|
|
|
if ($Customer instanceof Customer) { |
518
|
|
|
$this->entityManager->persist($CustomerAddress); |
519
|
|
|
log_info( |
520
|
|
|
'新規お届け先登録', |
521
|
|
|
[ |
522
|
|
|
'id' => $Order->getId(), |
523
|
|
|
'shipping' => $id, |
524
|
|
|
'customer address' => $CustomerAddress->getId(), |
525
|
|
|
] |
526
|
|
|
); |
527
|
|
|
} |
528
|
|
|
|
529
|
|
|
// 配送料金の設定 |
530
|
|
|
$this->shoppingService->setShippingDeliveryFee($Shipping); |
531
|
|
|
|
532
|
|
|
// 合計金額の再計算 |
533
|
|
|
$flowResult = $this->validatePurchaseFlow($Order); |
534
|
|
|
if ($flowResult->hasWarning() || $flowResult->hasError()) { |
535
|
|
|
return $this->redirectToRoute('shopping_error'); |
536
|
|
|
} |
537
|
|
|
|
538
|
|
|
// 配送先を更新 |
539
|
|
|
$this->entityManager->flush(); |
540
|
|
|
|
541
|
|
|
$event = new EventArgs( |
542
|
|
|
[ |
543
|
|
|
'form' => $form, |
544
|
|
|
'Shipping' => $Shipping, |
545
|
|
|
'CustomerAddress' => $CustomerAddress, |
546
|
|
|
], |
547
|
|
|
$request |
548
|
|
|
); |
549
|
|
|
$this->eventDispatcher->dispatch(EccubeEvents::FRONT_SHOPPING_SHIPPING_EDIT_COMPLETE, $event); |
550
|
|
|
|
551
|
|
|
log_info('お届け先追加処理完了', ['id' => $Order->getId(), 'shipping' => $id]); |
552
|
|
|
|
553
|
|
|
return $this->redirectToRoute('shopping'); |
554
|
|
|
} |
555
|
|
|
|
556
|
|
|
return [ |
557
|
|
|
'form' => $form->createView(), |
558
|
|
|
'shippingId' => $id, |
559
|
|
|
]; |
560
|
|
|
} |
561
|
|
|
|
562
|
|
|
/** |
563
|
|
|
* ログイン |
564
|
|
|
* |
565
|
|
|
* @Route("/shopping/login", name="shopping_login") |
566
|
|
|
* @Template("Shopping/login.twig") |
567
|
|
|
*/ |
568
|
2 |
|
public function login(Request $request, AuthenticationUtils $authenticationUtils) |
569
|
|
|
{ |
570
|
2 |
|
if ($this->isGranted('IS_AUTHENTICATED_FULLY')) { |
571
|
|
|
return $this->redirectToRoute('shopping'); |
572
|
|
|
} |
573
|
|
|
|
574
|
|
|
/* @var $form \Symfony\Component\Form\FormInterface */ |
575
|
2 |
|
$builder = $this->formFactory->createNamedBuilder('', CustomerLoginType::class); |
576
|
|
|
|
577
|
2 |
|
if ($this->isGranted('IS_AUTHENTICATED_REMEMBERED')) { |
578
|
|
|
$Customer = $this->getUser(); |
579
|
|
|
if ($Customer) { |
580
|
|
|
$builder->get('login_email')->setData($Customer->getEmail()); |
581
|
|
|
} |
582
|
|
|
} |
583
|
|
|
|
584
|
2 |
|
$event = new EventArgs( |
585
|
|
|
[ |
586
|
2 |
|
'builder' => $builder, |
587
|
|
|
], |
588
|
2 |
|
$request |
589
|
|
|
); |
590
|
2 |
|
$this->eventDispatcher->dispatch(EccubeEvents::FRONT_SHOPPING_LOGIN_INITIALIZE, $event); |
591
|
|
|
|
592
|
2 |
|
$form = $builder->getForm(); |
593
|
|
|
|
594
|
|
|
return [ |
595
|
2 |
|
'error' => $authenticationUtils->getLastAuthenticationError(), |
596
|
2 |
|
'form' => $form->createView(), |
597
|
|
|
]; |
598
|
|
|
} |
599
|
|
|
|
600
|
|
|
/** |
601
|
|
|
* 購入エラー画面表示 |
602
|
|
|
* |
603
|
|
|
* @Route("/shopping/error", name="shopping_error") |
604
|
|
|
* @Template("Shopping/shopping_error.twig") |
605
|
|
|
*/ |
606
|
1 |
|
public function shoppingError(Request $request) |
607
|
|
|
{ |
608
|
1 |
|
$event = new EventArgs( |
609
|
1 |
|
[], |
610
|
1 |
|
$request |
611
|
|
|
); |
612
|
1 |
|
$this->eventDispatcher->dispatch(EccubeEvents::FRONT_SHOPPING_SHIPPING_ERROR_COMPLETE, $event); |
613
|
|
|
|
614
|
1 |
|
if ($event->getResponse() !== null) { |
615
|
|
|
return $event->getResponse(); |
616
|
|
|
} |
617
|
|
|
|
618
|
1 |
|
return []; |
619
|
|
|
} |
620
|
|
|
|
621
|
|
|
/** |
622
|
|
|
* カート画面のチェック |
623
|
|
|
* |
624
|
|
|
* @ForwardOnly |
625
|
|
|
* @Route("/shopping/check_to_cart", name="shopping_check_to_cart") |
626
|
|
|
*/ |
627
|
55 |
|
public function checkToCart(Request $request) |
628
|
|
|
{ |
629
|
55 |
|
$Cart = $this->cartService->getCart(); |
630
|
55 |
|
if ($Cart && count($Cart->getCartItems()) > 0) { |
631
|
53 |
|
$divide = $request->getSession()->get('cart.divide'); |
632
|
53 |
|
if ($divide) { |
633
|
|
|
log_info('種別が異なる商品がカートと結合されたためカート画面にリダイレクト'); |
634
|
|
|
|
635
|
|
|
return $this->redirectToRoute('cart'); |
636
|
|
|
} |
637
|
|
|
|
638
|
53 |
|
return new Response(); |
639
|
|
|
} |
640
|
2 |
|
log_info('カートに商品が入っていないためショッピングカート画面にリダイレクト'); |
641
|
|
|
|
642
|
|
|
// カートが存在しない時はエラー |
643
|
2 |
|
return $this->redirectToRoute('cart'); |
644
|
|
|
} |
645
|
|
|
|
646
|
|
|
/** |
647
|
|
|
* 受注情報を初期化する. |
648
|
|
|
* |
649
|
|
|
* @ForwardOnly |
650
|
|
|
* @Route("/shopping/initialize_order", name="shopping_initialize_order") |
651
|
|
|
*/ |
652
|
50 |
|
public function initializeOrder(Request $request) |
|
|
|
|
653
|
|
|
{ |
654
|
|
|
// 購入処理中の受注情報を取得 |
655
|
50 |
|
$Order = $this->shoppingService->getOrder(OrderStatus::PROCESSING); |
656
|
|
|
|
657
|
|
|
// 初回アクセス(受注情報がない)の場合は, 受注情報を作成 |
658
|
50 |
|
if (is_null($Order)) { |
659
|
|
|
// 未ログインの場合, ログイン画面へリダイレクト. |
660
|
34 |
|
if (!$this->isGranted('IS_AUTHENTICATED_FULLY')) { |
661
|
|
|
// 非会員でも一度会員登録されていればショッピング画面へ遷移 |
662
|
|
|
$Customer = $this->shoppingService->getNonMember($this->sessionKey); |
663
|
|
|
|
664
|
|
|
if (is_null($Customer)) { |
665
|
|
|
log_info('未ログインのためログイン画面にリダイレクト'); |
666
|
|
|
|
667
|
|
|
return $this->redirectToRoute('shopping_login'); |
668
|
|
|
} |
669
|
|
|
} else { |
670
|
34 |
|
$Customer = $this->getUser(); |
671
|
|
|
} |
672
|
|
|
|
673
|
|
|
try { |
674
|
|
|
// 受注情報を作成 |
675
|
|
|
//$Order = $app['eccube.service.shopping']->createOrder($Customer); |
676
|
34 |
|
$Order = $this->orderHelper->createProcessingOrder( |
677
|
34 |
|
$Customer, |
678
|
34 |
|
$this->cartService->getCart()->getCartItems() |
|
|
|
|
679
|
|
|
); |
680
|
34 |
|
$this->cartService->setPreOrderId($Order->getPreOrderId()); |
681
|
34 |
|
$this->cartService->save(); |
682
|
|
|
} catch (CartException $e) { |
683
|
|
|
log_error('初回受注情報作成エラー', [$e->getMessage()]); |
684
|
|
|
$this->addRequestError($e->getMessage()); |
685
|
|
|
|
686
|
|
|
return $this->redirectToRoute('cart'); |
687
|
|
|
} |
688
|
|
|
|
689
|
|
|
// セッション情報を削除 |
690
|
34 |
|
$this->session->remove($this->sessionOrderKey); |
691
|
|
|
} |
692
|
|
|
|
693
|
|
|
// 受注関連情報を最新状態に更新 |
694
|
50 |
|
$this->entityManager->refresh($Order); |
695
|
|
|
|
696
|
50 |
|
$this->parameterBag->set('Order', $Order); |
697
|
|
|
|
698
|
50 |
|
return new Response(); |
699
|
|
|
} |
700
|
|
|
|
701
|
|
|
/** |
702
|
|
|
* フォームを作成し, イベントハンドラを設定する |
703
|
|
|
* |
704
|
|
|
* @ForwardOnly |
705
|
|
|
* @Route("/shopping/create_form", name="shopping_create_form") |
706
|
|
|
*/ |
707
|
50 |
|
public function createShoppingForm(Request $request) |
708
|
|
|
{ |
709
|
50 |
|
$Order = $this->parameterBag->get('Order'); |
710
|
|
|
// フォームの生成 |
711
|
50 |
|
$builder = $this->formFactory->createBuilder(OrderType::class, $Order); |
712
|
|
|
|
713
|
50 |
|
$event = new EventArgs( |
714
|
|
|
[ |
715
|
50 |
|
'builder' => $builder, |
716
|
50 |
|
'Order' => $Order, |
717
|
|
|
], |
718
|
50 |
|
$request |
719
|
|
|
); |
720
|
50 |
|
$this->eventDispatcher->dispatch(EccubeEvents::FRONT_SHOPPING_INDEX_INITIALIZE, $event); |
721
|
|
|
|
722
|
50 |
|
$form = $builder->getForm(); |
723
|
|
|
|
724
|
50 |
|
$this->parameterBag->set(OrderType::class, $form); |
725
|
|
|
|
726
|
50 |
|
return new Response(); |
727
|
|
|
} |
728
|
|
|
|
729
|
|
|
/** |
730
|
|
|
* mode に応じて各変更ページへリダイレクトする. |
731
|
|
|
* |
732
|
|
|
* @ForwardOnly |
733
|
|
|
* @Route("/shopping/redirect_to_change", name="shopping_redirect_to_change") |
734
|
|
|
*/ |
735
|
20 |
|
public function redirectToChange(Request $request) |
|
|
|
|
736
|
|
|
{ |
737
|
20 |
|
$form = $this->parameterBag->get(OrderType::class); |
738
|
|
|
|
739
|
|
|
// requestのバインド後、Calculatorに再集計させる |
740
|
|
|
//$app['eccube.service.calculate']($Order, $Order->getCustomer())->calculate(); |
741
|
|
|
|
742
|
|
|
// 支払い方法の変更や配送業者の変更があった場合はDBに保持する. |
743
|
20 |
|
if ($form->isSubmitted() && $form->isValid()) { |
744
|
|
|
// POSTされたデータをDBに保持. |
745
|
8 |
|
$this->entityManager->flush(); |
746
|
|
|
|
747
|
8 |
|
$mode = $form['mode']->getData(); |
748
|
8 |
|
switch ($mode) { |
749
|
7 |
|
case 'shipping_change': |
750
|
|
|
// お届け先設定一覧へリダイレクト |
751
|
1 |
|
$param = $form['param']->getData(); |
752
|
|
|
|
753
|
1 |
|
return $this->redirectToRoute('shopping_shipping', ['id' => $param]); |
754
|
7 |
|
case 'shipping_edit_change': |
755
|
|
|
// お届け先設定一覧へリダイレクト |
756
|
|
|
$param = $form['param']->getData(); |
757
|
|
|
|
758
|
|
|
return $this->redirectToRoute('shopping_shipping_edit', ['id' => $param]); |
759
|
7 |
|
case 'shipping_multiple_change': |
760
|
|
|
// 複数配送設定へリダイレクト |
761
|
|
|
return $this->redirectToRoute('shopping_shipping_multiple'); |
762
|
7 |
|
case 'payment': |
763
|
7 |
|
case 'delivery': |
764
|
|
|
default: |
765
|
7 |
|
return $this->redirectToRoute('shopping'); |
766
|
|
|
} |
767
|
|
|
} |
768
|
|
|
|
769
|
12 |
|
return new Response(); |
770
|
|
|
} |
771
|
|
|
|
772
|
|
|
/** |
773
|
|
|
* 受注の存在チェック |
774
|
|
|
* |
775
|
|
|
* @ForwardOnly |
776
|
|
|
* @Route("/shopping/exists_order", name="shopping_exists_order") |
777
|
|
|
*/ |
778
|
26 |
|
public function existsOrder(Request $request) |
|
|
|
|
779
|
|
|
{ |
780
|
26 |
|
$Order = $this->shoppingService->getOrder(OrderStatus::PROCESSING); |
781
|
26 |
|
if (!$Order) { |
782
|
|
|
log_info('購入処理中の受注情報がないため購入エラー'); |
783
|
|
|
$this->addError('front.shopping.order.error'); |
784
|
|
|
|
785
|
|
|
return $this->redirectToRoute('shopping_error'); |
786
|
|
|
} |
787
|
26 |
|
$this->parameterBag->set('Order', $Order); |
788
|
|
|
|
789
|
26 |
|
return new Response(); |
790
|
|
|
} |
791
|
|
|
|
792
|
|
|
/** |
793
|
|
|
* 受注完了処理 |
794
|
|
|
* |
795
|
|
|
* @ForwardOnly |
796
|
|
|
* @Route("/shopping/complete_order", name="shopping_complete_order") |
797
|
|
|
*/ |
798
|
6 |
|
public function completeOrder(Request $request) |
|
|
|
|
799
|
|
|
{ |
800
|
6 |
|
$form = $this->parameterBag->get(OrderType::class); |
801
|
|
|
|
802
|
6 |
|
if ($form->isSubmitted() && $form->isValid()) { |
803
|
|
|
/** @var Order $Order */ |
804
|
6 |
|
$Order = $form->getData(); |
805
|
6 |
|
log_info('購入処理開始', [$Order->getId()]); |
806
|
|
|
|
807
|
|
|
// トランザクション制御 |
808
|
6 |
|
$em = $this->entityManager; |
809
|
6 |
|
$em->getConnection()->beginTransaction(); |
810
|
|
|
try { |
811
|
|
|
// お問い合わせ、配送時間などのフォーム項目をセット |
812
|
|
|
// FormTypeで更新されるため不要 |
813
|
|
|
//$app['eccube.service.shopping']->setFormData($Order, $data); |
814
|
|
|
|
815
|
6 |
|
$flowResult = $this->validatePurchaseFlow($Order); |
816
|
6 |
|
if ($flowResult->hasWarning() || $flowResult->hasError()) { |
817
|
|
|
// TODO エラーメッセージ |
818
|
|
|
throw new ShoppingException(); |
819
|
|
|
} |
820
|
|
|
|
821
|
6 |
|
$paymentMethod = $this->createPaymentMethod($Order, $form); |
822
|
|
|
|
823
|
|
|
// 必要に応じて別のコントローラへ forward or redirect(移譲) |
824
|
6 |
|
$dispatcher = $paymentMethod->apply(); // 決済処理中. |
825
|
|
|
// 一旦、決済処理中になった後は、購入処理中に戻せない。キャンセル or 購入完了の仕様とする |
826
|
|
|
// ステータス履歴も保持しておく? 在庫引き当ての仕様もセットで。 |
827
|
6 |
|
if ($dispatcher instanceof PaymentDispatcher) { |
828
|
|
|
$response = $dispatcher->getResponse(); |
829
|
|
|
$this->entityManager->flush(); |
830
|
|
|
$this->entityManager->commit(); |
831
|
|
|
|
832
|
|
|
if ($response && ($response->isRedirection() || $response->getContent())) { |
833
|
|
|
return $response; |
834
|
|
|
} |
835
|
|
|
|
836
|
|
|
if ($dispatcher->isForward()) { |
837
|
|
|
return $this->forwardToRoute($dispatcher->getRoute(), $dispatcher->getPathParameters(), $dispatcher->getQueryParameters()); |
838
|
|
|
} else { |
839
|
|
|
return $this->redirectToRoute($dispatcher->getRoute(), array_merge($dispatcher->getPathParameters(), $dispatcher->getQueryParameters())); |
840
|
|
|
} |
841
|
|
|
} |
842
|
|
|
|
843
|
|
|
// 決済実行 |
844
|
6 |
|
$response = $this->forwardToRoute('shopping_do_checkout_order'); |
845
|
6 |
|
$this->entityManager->flush(); |
846
|
6 |
|
$this->entityManager->commit(); |
847
|
|
|
|
848
|
6 |
|
if ($response->isRedirection() || $response->getContent()) { |
849
|
|
|
return $response; |
850
|
|
|
} |
851
|
|
|
|
852
|
6 |
|
log_info('購入処理完了', [$Order->getId()]); |
853
|
|
|
} catch (ShoppingException $e) { |
854
|
|
|
log_error('購入エラー', [$e->getMessage()]); |
855
|
|
|
|
856
|
|
|
$this->entityManager->getConnection()->rollback(); |
857
|
|
|
|
858
|
|
|
$this->log($e); |
|
|
|
|
859
|
|
|
$this->addError($e->getMessage()); |
860
|
|
|
|
861
|
|
|
return $this->redirectToRoute('shopping_error'); |
862
|
|
|
} catch (\Exception $e) { |
863
|
|
|
log_error('予期しないエラー', [$e->getMessage()]); |
864
|
|
|
|
865
|
|
|
$this->entityManager->getConnection()->rollback(); |
866
|
|
|
|
867
|
|
|
$this->addError('front.shopping.system.error'); |
868
|
|
|
|
869
|
|
|
return $this->redirectToRoute('shopping_error'); |
870
|
|
|
} |
871
|
|
|
|
872
|
6 |
|
return $this->forwardToRoute('shopping_after_complete'); |
873
|
|
|
} |
874
|
|
|
|
875
|
|
|
return new Response(); |
876
|
|
|
} |
877
|
|
|
|
878
|
|
|
/** |
879
|
|
|
* 決済完了処理 |
880
|
|
|
* |
881
|
|
|
* @ForwardOnly |
882
|
|
|
* @Route("/shopping/do_checkout_order", name="shopping_do_checkout_order") |
883
|
|
|
*/ |
884
|
6 |
|
public function doCheckoutOrder(Request $request) |
|
|
|
|
885
|
|
|
{ |
886
|
6 |
|
$form = $this->parameterBag->get(OrderType::class); |
887
|
6 |
|
$Order = $this->parameterBag->get('Order'); |
888
|
|
|
|
889
|
6 |
|
$paymentMethod = $this->createPaymentMethod($Order, $form); |
890
|
|
|
|
891
|
|
|
// 決済実行 |
892
|
6 |
|
$PaymentResult = $paymentMethod->checkout(); |
893
|
6 |
|
$response = $PaymentResult->getResponse(); |
894
|
6 |
|
if ($response && ($response->isRedirection() || $response->getContent())) { |
895
|
|
|
return $response; |
896
|
|
|
} |
897
|
|
|
|
898
|
6 |
|
if (!$PaymentResult->isSuccess()) { |
899
|
|
|
$this->entityManager->getConnection()->rollback(); |
900
|
|
|
|
901
|
|
|
$this->addError($PaymentResult->getErrors()); |
902
|
|
|
} |
903
|
|
|
|
904
|
6 |
|
return new Response(); |
905
|
|
|
} |
906
|
|
|
|
907
|
|
|
/** |
908
|
|
|
* 受注完了の後処理 |
909
|
|
|
* |
910
|
|
|
* @ForwardOnly |
911
|
|
|
* @Route("/shopping/after_complete", name="shopping_after_complete") |
912
|
|
|
*/ |
913
|
6 |
|
public function afterComplete(Request $request) |
914
|
|
|
{ |
915
|
6 |
|
$form = $this->parameterBag->get(OrderType::class); |
916
|
6 |
|
$Order = $this->parameterBag->get('Order'); |
917
|
|
|
|
918
|
|
|
// カート削除 |
919
|
6 |
|
$this->cartService->clear(); |
920
|
|
|
|
921
|
6 |
|
$event = new EventArgs( |
922
|
|
|
[ |
923
|
6 |
|
'form' => $form, |
924
|
6 |
|
'Order' => $Order, |
925
|
|
|
], |
926
|
6 |
|
$request |
927
|
|
|
); |
928
|
6 |
|
$this->eventDispatcher->dispatch(EccubeEvents::FRONT_SHOPPING_CONFIRM_PROCESSING, $event); |
929
|
|
|
|
930
|
6 |
View Code Duplication |
if ($event->getResponse() !== null) { |
931
|
|
|
log_info('イベントレスポンス返却', [$Order->getId()]); |
932
|
|
|
|
933
|
|
|
return $event->getResponse(); |
934
|
|
|
} |
935
|
|
|
|
936
|
|
|
// 受注IDをセッションにセット |
937
|
6 |
|
$this->session->set($this->sessionOrderKey, $Order->getId()); |
938
|
|
|
|
939
|
|
|
// メール送信 |
940
|
6 |
|
$MailHistory = $this->shoppingService->sendOrderMail($Order); |
941
|
|
|
|
942
|
6 |
|
$event = new EventArgs( |
943
|
|
|
[ |
944
|
6 |
|
'form' => $form, |
945
|
6 |
|
'Order' => $Order, |
946
|
6 |
|
'MailHistory' => $MailHistory, |
947
|
|
|
], |
948
|
6 |
|
$request |
949
|
|
|
); |
950
|
6 |
|
$this->eventDispatcher->dispatch(EccubeEvents::FRONT_SHOPPING_CONFIRM_COMPLETE, $event); |
951
|
|
|
|
952
|
6 |
View Code Duplication |
if ($event->getResponse() !== null) { |
953
|
|
|
log_info('イベントレスポンス返却', [$Order->getId()]); |
954
|
|
|
|
955
|
|
|
return $event->getResponse(); |
956
|
|
|
} |
957
|
|
|
|
958
|
|
|
// 完了画面表示 |
959
|
6 |
|
return $this->redirectToRoute('shopping_complete'); |
960
|
|
|
} |
961
|
|
|
|
962
|
6 |
|
private function createPaymentMethod(Order $Order, FormInterface $form) |
963
|
|
|
{ |
964
|
6 |
|
$PaymentMethod = $this->container->get($Order->getPayment()->getMethodClass()); |
965
|
6 |
|
$PaymentMethod->setOrder($Order); |
966
|
6 |
|
$PaymentMethod->setFormType($form); |
967
|
|
|
|
968
|
6 |
|
return $PaymentMethod; |
969
|
|
|
} |
970
|
|
|
} |
971
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.