Implementa conferencia de facturas e consumo por empresa

This commit is contained in:
2026-10-28 14:26:18 +02:00
parent f86ecef44d
commit 332cb9f1b7
28 changed files with 1356 additions and 8 deletions
+3 -1
View File
@@ -4,6 +4,7 @@ namespace App\Http\Controllers;
use App\Enums\PurchasingRole;
use App\Models\Budget;
use App\Models\BudgetConsumption;
use App\Models\BudgetEntry;
use App\Models\CostCenter;
use App\Services\ConflictGuard;
@@ -31,8 +32,9 @@ class BudgetController extends Controller
$entries = BudgetEntry::query()->where('budget_id', $budget->id);
$reserved = (int) (clone $entries)->sum('reserved_minor');
$committed = (int) $entries->sum('committed_minor');
$consumed = (int) BudgetConsumption::query()->where('budget_id', $budget->id)->sum('amount_minor');
return [...$budget->toArray(), 'reserved_minor' => $reserved, 'committed_minor' => $committed, 'available_minor' => $budget->amount_minor - $reserved - $committed];
return [...$budget->toArray(), 'reserved_minor' => $reserved, 'committed_minor' => $committed, 'consumed_minor' => $consumed, 'available_minor' => $budget->amount_minor - $reserved - $committed - $consumed];
})]);
}
@@ -0,0 +1,39 @@
<?php
namespace App\Http\Controllers;
use App\Enums\PurchasingRole;
use App\Models\Invoice;
use App\Models\PurchaseOrder;
use App\Services\InvoiceWorkflow;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Inertia\Inertia;
use Inertia\Response;
class InvoiceController extends Controller
{
public function index(Request $request): Response
{
abort_unless(in_array($request->user()->purchasing_role, [PurchasingRole::Admin, PurchasingRole::Finance]), 403);
return Inertia::render('invoices/index', ['invoices' => Invoice::query()->with('items')->latest('id')->paginate(15), 'orders' => PurchaseOrder::query()->with('items')->latest('id')->get()->map(fn ($order) => ['id' => $order->id, 'number' => $order->number, 'supplier' => $order->snapshot['supplier']['name'], 'items' => $order->items]), 'canManage' => $request->user()->purchasing_role === PurchasingRole::Finance]);
}
public function store(Request $request, PurchaseOrder $order, InvoiceWorkflow $workflow): RedirectResponse
{
abort_unless($request->user()->purchasing_role === PurchasingRole::Finance, 403);
$decimal = ['required', 'regex:/^\d{1,12}(\.\d{1,2})?$/'];
$data = $request->validate(['number' => 'required|string|max:100', 'issued_on' => 'required|date_format:Y-m-d', 'subtotal' => $decimal, 'tax' => $decimal, 'total' => $decimal, 'items' => 'required|array|min:1|max:50', 'items.*.purchase_order_item_id' => 'required|integer|distinct', 'items.*.quantity' => ['required', 'regex:/^\d{1,9}(\.\d{1,3})?$/'], 'items.*.unit' => 'required|string|max:30', 'items.*.unit_price' => $decimal, 'items.*.tax_rate' => ['required', 'regex:/^\d{1,3}(\.\d{1,2})?$/']]);
$workflow->register($request->user(), $order, $data);
return to_route('invoices.index');
}
public function validateInvoice(Request $request, Invoice $invoice, InvoiceWorkflow $workflow): RedirectResponse
{
$workflow->validate($request->user(), $invoice);
return back();
}
}