Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
86.57% covered (warning)
86.57%
58 / 67
50.00% covered (danger)
50.00%
2 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
GenerateItemService
86.57% covered (warning)
86.57%
58 / 67
50.00% covered (danger)
50.00%
2 / 4
15.55
0.00% covered (danger)
0.00%
0 / 1
 generate
86.96% covered (warning)
86.96%
40 / 46
0.00% covered (danger)
0.00%
0 / 1
8.14
 getStockIOProduct
66.67% covered (warning)
66.67%
6 / 9
0.00% covered (danger)
0.00%
0 / 1
2.15
 generatePickedItem
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
1
 getPriorityField
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
4
1<?php
2
3namespace Domain\Item\Services;
4
5use Throwable;
6
7use App\Events\ServiceProcessed;
8use App\Models\Item\Item;
9use App\Models\Product\Product;
10use App\Models\StockIO\StockIO;
11use App\Models\StockIO\StockIOProduct;
12use App\Models\User;
13
14use Domain\Item\DTOs\GenerateItemDTO;
15use Domain\Item\DTOs\ItemProductDTO;
16use Domain\Item\Exceptions\ItemAmountExceededStockIOProduct;
17use Domain\Item\Exceptions\ItemExistsStockIO;
18use Domain\Item\Exceptions\ItemPriorityValueNullException;
19use Domain\PickingOrder\DTOs\PickedItemDTO;
20use Domain\PickingOrder\Services\PickedItemStoreService;
21use Domain\Product\Exceptions\ProductDoesntHavePriorityException;
22use Domain\StockIO\Exceptions\StockIOProductNotFoundException;
23
24readonly class GenerateItemService
25{
26    /**
27     * @param GenerateItemDTO $generateItemDTO
28     * @param StockIO $stockIO
29     * @param User $user
30     * @return Item
31     * @throws Throwable
32     */
33    public function generate(GenerateItemDTO $generateItemDTO, StockIO $stockIO, User $user): Item
34    {
35        $productField  = $this->getPriorityField($generateItemDTO->product);
36        $priorityValue = $generateItemDTO->priority_value;
37        $product       = $generateItemDTO->product;
38
39        $stockIOProduct = $this->getStockIOProduct($stockIO, $product);
40
41        if (!is_null($productField) && is_null($priorityValue)) {
42            throw new ItemPriorityValueNullException(
43                __("The product has a priority setting, but the priority value was not specified.")
44            );
45        }
46
47        if (is_null($productField)) {
48            $priorityValue = null;
49        }
50
51        if($stockIOProduct->hasAmountExceededLimit($generateItemDTO->amount)){
52            throw new ItemAmountExceededStockIOProduct(
53                __("The informed amount exceeded stock product amount.")
54            );
55        }
56
57        $item = Item::whereHas('integration', function ($query) use ($productField, $priorityValue) {
58            if (is_null($productField)) {
59                $query->whereRaw("(parameters::jsonb ?? 'priority_value')")
60                    ->whereRaw("(parameters->>'priority_value') is null")
61                    ->whereRaw("(parameters::jsonb ?? 'priority_field')")
62                    ->whereRaw("(parameters->>'priority_field') is null");
63
64                return;
65            }
66
67            $query->whereRaw("parameters->>'priority_field' = ?", [(string) $productField])
68                ->whereRaw("parameters->>'priority_value' = ?", [(string) $priorityValue]);
69            })
70            ->where('itemable_type', StockIO::class)
71            ->where('itemable_id', $stockIO->id)
72            ->where('product_id', $product->getKey())
73            ->first();
74
75        $itemAmount = $item ? $item->amount : 0;
76
77        $itemDTO = new ItemProductDTO([
78                'target' => $product,
79                'amount' => $generateItemDTO->amount + $itemAmount,
80                'itemIntegrationDTO' => [
81                    'priority_field' => $productField,
82                    'priority_value' => $priorityValue
83                ]
84        ]);
85
86        $item = $item ?
87            (new ItemUpdateService)->update($itemDTO, $item, $user) :
88            (new ItemStoreService)->store($itemDTO, $stockIO, $user);
89
90        $item->load(
91            ['integration']
92        );
93
94        $this->generatePickedItem($item, $stockIOProduct, $generateItemDTO->amount, $user);
95
96        ServiceProcessed::dispatch(self::class, $user);
97        return $item;
98    }
99
100    /**
101     * @param StockIO $stockIO
102     * @param Product $product
103     * @return StockIOProduct
104     */
105    private function getStockIOProduct(StockIO $stockIO, Product $product): StockIOProduct
106    {
107        $stockIOProduct = StockIOProduct::query()
108            ->where('stock_io_id', $stockIO->getKey())
109            ->where('product_id', $product->getKey())
110            ->first();
111
112        if(!$stockIOProduct){
113            throw new StockIOProductNotFoundException(
114                __("StockIO product not found.")
115            );
116        }
117
118        return $stockIOProduct;
119    }
120
121    /**
122     * @param Item $item
123     * @param StockIOProduct $stockIOProduct
124     * @param User $user
125     */
126    private function generatePickedItem(Item $item, StockIOProduct $stockIOProduct, float $amount, User $user)
127    {
128        $pickedItemDTO = new PickedItemDTO([
129            'stock_io_product' => $stockIOProduct,
130            'item' => $item,
131            'amount' => $amount,
132        ]);
133
134        (new PickedItemStoreService())->store($pickedItemDTO, $user);
135    }
136
137    private function getPriorityField(Product $product): ?int
138    {
139        $priorityField = $product->getAttribute('priority_field');
140
141        if(is_null($priorityField)) {
142            $priorityField = $product->family->getAttribute('priority_field');
143
144            if(is_null($priorityField)){
145                $priorityField = $product->family->origin->getAttribute('priority_field');
146            }
147        }
148
149        return $priorityField ? $priorityField->value : null;
150    }
151}