Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
| Total | |
95.52% |
64 / 67 |
|
50.00% |
2 / 4 |
CRAP | |
0.00% |
0 / 1 |
| StockOutputFinishService | |
95.52% |
64 / 67 |
|
50.00% |
2 / 4 |
15 | |
0.00% |
0 / 1 |
| processOutputFinish | |
94.29% |
33 / 35 |
|
0.00% |
0 / 1 |
7.01 | |||
| allProductsSeparated | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 | |||
| allProductsCoveredByPickingOrders | |
94.12% |
16 / 17 |
|
0.00% |
0 / 1 |
2.00 | |||
| processInvoiceLog | |
100.00% |
11 / 11 |
|
100.00% |
1 / 1 |
4 | |||
| 1 | <?php |
| 2 | |
| 3 | namespace Domain\StockIO\Services\V2; |
| 4 | |
| 5 | use App\Models\PickingOrder\PickingOrder; |
| 6 | use App\Models\PickingOrder\PickingOrderProduct; |
| 7 | use App\Models\StockIO\StockIO; |
| 8 | use App\Models\User; |
| 9 | |
| 10 | use Domain\PickingOrder\Enums\PickingOrderStatusEnum; |
| 11 | use Domain\Request\DTOs\RequestLogStoreDTO; |
| 12 | use Domain\Request\Enums\RequestLogOperationEnum; |
| 13 | use Domain\Request\Services\RequestLogStoreService; |
| 14 | use Domain\Shared\Enums\SectorEnum; |
| 15 | use Domain\StockIO\Enums\StockProductStatusEnum; |
| 16 | use Domain\StockIO\Enums\StockStatusEnum; |
| 17 | use Domain\StockIO\Services\StockIOFinishService; |
| 18 | use Exception; |
| 19 | use Illuminate\Support\Collection; |
| 20 | use Throwable; |
| 21 | |
| 22 | class StockOutputFinishService |
| 23 | { |
| 24 | private array $stockIOs; |
| 25 | |
| 26 | /** |
| 27 | * @throws Exception |
| 28 | * @throws Throwable |
| 29 | */ |
| 30 | public function processOutputFinish(PickingOrder $pickingOrder, User $user): void |
| 31 | { |
| 32 | $this->stockIOs = []; |
| 33 | $pickingOrder->stockIOProducts |
| 34 | ->map(fn ($stockIOProduct) => $stockIOProduct->stockIO) |
| 35 | ->filter() |
| 36 | ->unique('id') |
| 37 | ->each(function (StockIO $stockIO) use ($pickingOrder, $user) { |
| 38 | if (! $stockIO->isOutput()) { |
| 39 | return; |
| 40 | } |
| 41 | |
| 42 | $doesntExistUnfinishedPickingOrders = $stockIO->pickingOrders() |
| 43 | ->whereNotIn('status', [ |
| 44 | PickingOrderStatusEnum::CONCLUDED->value, |
| 45 | PickingOrderStatusEnum::SEPARATED->value, |
| 46 | PickingOrderStatusEnum::SEPARATED_PARTIAL->value, |
| 47 | PickingOrderStatusEnum::CANCELED->value, |
| 48 | ]) |
| 49 | ->where('id', '!=', $pickingOrder->getKey()) |
| 50 | ->doesntExist(); |
| 51 | |
| 52 | if (! $doesntExistUnfinishedPickingOrders) { |
| 53 | return; |
| 54 | } |
| 55 | |
| 56 | $statuses = $stockIO->stockIOProducts() |
| 57 | ->where('status', '<>', StockProductStatusEnum::CANCELED->value) |
| 58 | ->pluck('status'); |
| 59 | |
| 60 | if ($statuses->isEmpty()) { |
| 61 | return; |
| 62 | } |
| 63 | |
| 64 | if ($this->allProductsSeparated($statuses) && $this->allProductsCoveredByPickingOrders($stockIO)) { |
| 65 | (new StockIOFinishService())->finish($stockIO); |
| 66 | } else { |
| 67 | $stockIO->setAttribute( |
| 68 | 'status', |
| 69 | $stockIO->sector === SectorEnum::EXPEDITION |
| 70 | ? StockStatusEnum::STARTED |
| 71 | : StockStatusEnum::SEPARATED_PARTIAL |
| 72 | ); |
| 73 | $stockIO->save(); |
| 74 | } |
| 75 | |
| 76 | $this->processInvoiceLog($stockIO, $pickingOrder, $user); |
| 77 | }); |
| 78 | } |
| 79 | |
| 80 | private function allProductsSeparated(Collection $statuses): bool |
| 81 | { |
| 82 | return $statuses->every(function ($status) { |
| 83 | $value = $status instanceof StockProductStatusEnum ? $status->value : (int) $status; |
| 84 | |
| 85 | return $value === StockProductStatusEnum::SEPARATED->value; |
| 86 | }); |
| 87 | } |
| 88 | |
| 89 | private function allProductsCoveredByPickingOrders(StockIO $stockIO): bool |
| 90 | { |
| 91 | $products = $stockIO->stockIOProducts() |
| 92 | ->where('status', '<>', StockProductStatusEnum::CANCELED->value) |
| 93 | ->get(['id', 'amount']); |
| 94 | |
| 95 | if ($products->isEmpty()) { |
| 96 | return false; |
| 97 | } |
| 98 | |
| 99 | $coveredAmounts = PickingOrderProduct::query() |
| 100 | ->join('picking_orders', 'picking_orders.id', '=', 'picking_order_products.picking_order_id') |
| 101 | ->whereIn('picking_order_products.stock_io_product_id', $products->pluck('id')) |
| 102 | ->where('picking_orders.status', '<>', PickingOrderStatusEnum::CANCELED->value) |
| 103 | ->where('picking_order_products.status', '<>', PickingOrderStatusEnum::CANCELED->value) |
| 104 | ->selectRaw('picking_order_products.stock_io_product_id, SUM(picking_order_products.amount) as total') |
| 105 | ->groupBy('picking_order_products.stock_io_product_id') |
| 106 | ->pluck('total', 'stock_io_product_id'); |
| 107 | |
| 108 | return $products->every(function ($product) use ($coveredAmounts) { |
| 109 | $covered = (float) ($coveredAmounts[$product->getKey()] ?? 0); |
| 110 | |
| 111 | return round($covered, 2) >= round((float) $product->getAttribute('amount'), 2); |
| 112 | }); |
| 113 | } |
| 114 | |
| 115 | /** |
| 116 | * @param StockIO $stockIO |
| 117 | * @param PickingOrder $pickingOrder |
| 118 | * @param User $user |
| 119 | */ |
| 120 | private function processInvoiceLog(StockIO $stockIO, PickingOrder $pickingOrder, User $user): void |
| 121 | { |
| 122 | if($this->stockIOs == [] || !in_array($stockIO->getKey(), $this->stockIOs)) { |
| 123 | $requestLogStoreDTO = new RequestLogStoreDTO([ |
| 124 | 'request' => $stockIO->request ? $stockIO->request : null, |
| 125 | 'stockIO' => $stockIO, |
| 126 | 'operation' => RequestLogOperationEnum::PICKING_ORDER_CONCLUDED, |
| 127 | 'user' => $user, |
| 128 | 'detail' => $pickingOrder->toJson(), |
| 129 | 'observation' => "Tarefa de separação concluída: ". $pickingOrder->getKey(), |
| 130 | ]); |
| 131 | |
| 132 | (new RequestLogStoreService())->store($requestLogStoreDTO, $user); |
| 133 | |
| 134 | $this->stockIOs[] = $stockIO->getKey(); |
| 135 | } |
| 136 | } |
| 137 | } |