91 lines
6.2 KiB
PHP
91 lines
6.2 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers;
|
|
|
|
use App\Models\CostCenter;
|
|
use App\Models\Equipment;
|
|
use App\Models\SupplyRecord;
|
|
use App\Models\User;
|
|
use App\Services\AutomaticPurchases;
|
|
use App\Services\OrganizationAccess;
|
|
use App\Services\ReplenishmentQueue;
|
|
use App\Services\SupplyPlanning;
|
|
use Illuminate\Http\RedirectResponse;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Facades\Schema;
|
|
use Illuminate\Validation\Rule;
|
|
use Inertia\Inertia;
|
|
use Symfony\Component\HttpFoundation\Response;
|
|
|
|
class AutomaticPurchaseController extends Controller
|
|
{
|
|
public function index(Request $request, AutomaticPurchases $service): Response
|
|
{
|
|
$actor = $request->user('web');
|
|
abort_unless($actor instanceof User && $actor->hasFunctions(['admin', 'supply_read', 'supply_prepare', 'supply_review']), 403);
|
|
$prepare = app(SupplyPlanning::class)->allows($actor, 'supply_prepare');
|
|
$ready = $service->ready();
|
|
$planning = app(SupplyPlanning::class);
|
|
$filters = $request->validate(['rule_id' => ['nullable', 'integer', 'min:1', Rule::exists('supply_records', 'id')->where('tenant_id', $actor->tenant_id)->where('kind', 'auto_rule')], 'need_state' => 'nullable|in:all,pending,created,covered,satisfied', 'rules_page' => 'nullable|integer|min:1|max:1000000', 'needs_page' => 'nullable|integer|min:1|max:1000000', 'history_page' => 'nullable|integer|min:1|max:1000000', 'work_page' => 'nullable|integer|min:1|max:1000000']);
|
|
$focus = isset($filters['rule_id']) ? SupplyRecord::query()->where('kind', 'auto_rule')->findOrFail((int) $filters['rule_id']) : null;
|
|
$rules = $planning->latest('auto_rule')->when($focus !== null, fn ($query) => $query->where('record_key', $focus->record_key));
|
|
$latestFocus = $focus === null ? null : (clone $rules)->first();
|
|
$needs = $planning->latest('auto_need');
|
|
if (($filters['need_state'] ?? 'all') !== 'all') {
|
|
$needs->where('state', $filters['need_state']);
|
|
}
|
|
Inertia::encryptHistory();
|
|
|
|
$response = Inertia::render('stock/replenishment', [
|
|
'ready' => $ready, 'canPrepare' => $prepare, 'canReview' => $planning->allows($actor, 'supply_review'),
|
|
'filters' => ['need_state' => $filters['need_state'] ?? 'all'],
|
|
'focusRuleId' => $focus->id ?? null, 'focusChanged' => $focus !== null && $latestFocus?->id !== $focus->id,
|
|
'rules' => $ready ? $rules->latest('id')->paginate(20, ['*'], 'rules_page')->withQueryString()->through(fn (SupplyRecord $rule) => [...$rule->toArray(), 'canApprove' => $service->canReview($actor, $rule), 'canSuspend' => $service->canReview($actor, $rule, 'suspend')]) : null,
|
|
'needs' => $ready ? $needs->orderByRaw("CASE WHEN state = 'pending' THEN 0 ELSE 1 END")->oldest('id')->paginate(20, ['*'], 'needs_page')->withQueryString() : null,
|
|
'history' => $ready ? SupplyRecord::query()->whereIn('kind', ['auto_rule', 'auto_need'])->latest('id')->paginate(30, ['*'], 'history_page')->withQueryString() : null,
|
|
...app(ReplenishmentQueue::class)->status($actor),
|
|
'workItems' => app(ReplenishmentQueue::class)->ready() ? DB::table('replenishment_work as w')->join('stock_items as i', 'i.id', '=', 'w.stock_item_id')->join('warehouses as a', 'a.id', '=', 'w.warehouse_id')->where('w.tenant_id', $actor->tenant_id)->where('i.tenant_id', $actor->tenant_id)->where('a.tenant_id', $actor->tenant_id)->where(fn ($q) => $q->whereColumn('w.generation', '>', 'w.processed_generation')->orWhere('w.failures', '>', 0))->orderBy('w.due_at')->orderBy('w.id')->paginate(20, ['w.id', 'i.code', 'i.name', 'a.name as warehouse', 'w.last_error', 'w.due_at', 'w.failures'], 'work_page')->withQueryString() : null,
|
|
'policies' => Schema::hasTable('supply_records') && $prepare ? $planning->latest('policy')->where('state', 'approved')->where('data->purpose', 'internal')->get() : [],
|
|
'requesters' => $prepare ? User::query()->where('tenant_id', $actor->tenant_id)->get(['id', 'name', 'department_id', 'purchasing_role', 'tenant_id'])->filter(fn ($u) => $service->eligibleRequester($u))->map(fn ($u) => $u->only(['id', 'name', 'department_id']))->values() : [],
|
|
'centers' => $prepare ? CostCenter::query()->orderBy('code')->get(['id', 'department_id', 'code', 'name']) : [],
|
|
'equipment' => $prepare ? Equipment::query()->orderBy('code')->get(['id', 'code', 'description', 'department_id']) : [],
|
|
'catalogueTypes' => $prepare && Schema::hasTable('requisition_catalogue') ? DB::table('requisition_catalogue')->where('tenant_id', $actor->tenant_id)->where('published', true)->pluck('purchase_type', 'stock_item_id') : [],
|
|
])->toResponse($request);
|
|
$response->headers->set('Cache-Control', 'private, no-store, max-age=0');
|
|
|
|
return $response;
|
|
}
|
|
|
|
public function save(Request $request, AutomaticPurchases $service): RedirectResponse
|
|
{
|
|
$service->save($request->user('web'), $request->all());
|
|
|
|
return redirect('/stock/reposicao');
|
|
}
|
|
|
|
public function evaluate(Request $request, int $id, AutomaticPurchases $service): RedirectResponse
|
|
{
|
|
$actor = $request->user('web');
|
|
abort_unless($actor instanceof User && ($actor->hasFunctions(['supply_prepare', 'supply_review'])) && ! app(OrganizationAccess::class)->maintenanceOnly($actor), 403);
|
|
$need = SupplyRecord::query()->where('kind', 'auto_need')->findOrFail($id);
|
|
if (app(ReplenishmentQueue::class)->ready()) {
|
|
$service->signal($actor, $need->stock_item_id, $need->warehouse_id);
|
|
} else {
|
|
$service->evaluate($actor, $need->stock_item_id, $need->warehouse_id);
|
|
}
|
|
|
|
return redirect('/stock/reposicao');
|
|
}
|
|
|
|
public function recover(Request $request, int $id, AutomaticPurchases $service): RedirectResponse
|
|
{
|
|
$actor = $request->user('web');
|
|
abort_unless($actor instanceof User && $actor->hasFunctions(['supply_prepare', 'supply_review']) && ! app(OrganizationAccess::class)->maintenanceOnly($actor), 403);
|
|
$work = DB::table('replenishment_work')->where('tenant_id', $actor->tenant_id)->where('id', $id)->firstOrFail();
|
|
$service->signal($actor, $work->stock_item_id, $work->warehouse_id);
|
|
|
|
return redirect('/stock/reposicao');
|
|
}
|
|
}
|