Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
31.91% covered (danger)
31.91%
15 / 47
0.00% covered (danger)
0.00%
0 / 3
CRAP
0.00% covered (danger)
0.00%
0 / 1
StockOutputFinishService
31.91% covered (danger)
31.91%
15 / 47
0.00% covered (danger)
0.00%
0 / 3
49.19
0.00% covered (danger)
0.00%
0 / 1
 processOutputFinish
57.69% covered (warning)
57.69%
15 / 26
0.00% covered (danger)
0.00%
0 / 1
10.71
 allProductsSeparated
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 allProductsCoveredByPickingOrders
0.00% covered (danger)
0.00%
0 / 17
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2
3namespace Domain\StockIO\Services;
4
5use App\Models\PickingOrder\PickingOrderProduct;
6use App\Models\PickingOrder\PickingOrder;
7use App\Models\StockIO\StockIO;
8use Domain\PickingOrder\Enums\PickingOrderStatusEnum;
9use Domain\Shared\Enums\SectorEnum;
10use Domain\StockIO\Enums\StockProductStatusEnum;
11use Domain\StockIO\Enums\StockStatusEnum;
12use Illuminate\Support\Collection;
13
14readonly class StockOutputFinishService
15{
16    public function processOutputFinish(PickingOrder $pickingOrder): void
17    {
18        $output = $pickingOrder->stockIO;
19
20        if (!$output?->isOutput()) {
21            return;
22        }
23
24        $doesntExistUnfinishedPickingOrders = $output->pickingOrders()
25            ->whereNotIn('status', [
26                PickingOrderStatusEnum::CONCLUDED->value,
27                PickingOrderStatusEnum::SEPARATED->value,
28                PickingOrderStatusEnum::CANCELED->value,
29            ])
30            ->where('id', '!=', $pickingOrder->getKey())
31            ->doesntExist();
32
33        $statuses = $output->stockIOProducts()
34            ->where('status', '<>', StockProductStatusEnum::CANCELED->value)
35            ->pluck('status');
36
37        if (!$doesntExistUnfinishedPickingOrders || !$statuses->isEmpty()) {
38            return;
39        }
40
41        if ($this->allProductsSeparated($statuses) && $this->allProductsCoveredByPickingOrders($output)) {
42            (new StockIOFinishService())->finish($output);
43
44            return;
45        }
46
47        $output->setAttribute(
48            'status',
49            $output->isRequest()
50                ? StockStatusEnum::SEPARATED_PARTIAL
51                : StockStatusEnum::STARTED
52        );
53        $output->save();
54    }
55
56    private function allProductsSeparated(Collection $statuses): bool
57    {
58        return $statuses->every(function ($status) {
59            $value = $status instanceof StockProductStatusEnum ? $status->value : (int) $status;
60
61            return $value === StockProductStatusEnum::SEPARATED->value;
62        });
63    }
64
65    private function allProductsCoveredByPickingOrders(StockIO $output): bool
66    {
67        $products = $output->stockIOProducts()
68            ->where('status', '<>', StockProductStatusEnum::CANCELED->value)
69            ->get(['id', 'amount']);
70
71        if ($products->isEmpty()) {
72            return false;
73        }
74
75        $coveredAmounts = PickingOrderProduct::query()
76            ->join('picking_orders', 'picking_orders.id', '=', 'picking_order_products.picking_order_id')
77            ->whereIn('picking_order_products.stock_io_product_id', $products->pluck('id'))
78            ->where('picking_orders.status', '<>', PickingOrderStatusEnum::CANCELED->value)
79            ->where('picking_order_products.status', '<>', PickingOrderStatusEnum::CANCELED->value)
80            ->selectRaw('picking_order_products.stock_io_product_id, SUM(picking_order_products.amount) as total')
81            ->groupBy('picking_order_products.stock_io_product_id')
82            ->pluck('total', 'stock_io_product_id');
83
84        return $products->every(function ($product) use ($coveredAmounts) {
85            $covered = (float) ($coveredAmounts[$product->getKey()] ?? 0);
86
87            return round($covered, 2) >= round((float) $product->getAttribute('amount'), 2);
88        });
89    }
90}