feat: consolidar desenvolvimentos do portal InvestMoz

This commit is contained in:
2026-11-03 23:32:32 +02:00
parent 1da273f1f3
commit 36c8cadb49
345 changed files with 110714 additions and 2799 deletions
@@ -10,6 +10,7 @@ use App\Services\CountryCatalogue;
use App\Services\FundingProgrammes;
use App\Services\InvestmentGeography;
use App\Services\InvestmentOpportunityAlerts;
use App\Services\InvestmentProfileCommands;
use App\Services\InvestmentWorkspace;
use App\Services\OpportunityManagement;
use App\Services\OrganizationCountry;
@@ -25,9 +26,9 @@ use Inertia\Response;
class InvestmentWorkspaceController extends Controller
{
public function profile(Request $request, InvestmentWorkspace $workspace): Response|JsonResponse
public function profile(Request $request, InvestmentWorkspace $workspace): \Symfony\Component\HttpFoundation\Response
{
$data = ['organizationCountryAvailable' => app(OrganizationCountry::class)->available(), 'organizations' => app(OrganizationCountry::class)->represented($request->user()), 'countries' => app(CountryCatalogue::class)->all(), 'account' => $workspace->account($request->user()->id), 'companies' => DB::table('investment_company_declarations')
$data = ['actorId' => $request->user()->id, 'organizationCountryAvailable' => app(OrganizationCountry::class)->available(), 'organizations' => app(OrganizationCountry::class)->represented($request->user()), 'countries' => app(CountryCatalogue::class)->all(), 'account' => $workspace->account($request->user()->id), 'companies' => DB::table('investment_company_declarations')
->where('user_id', $request->user()->id)->orderByDesc('created_at')->get(['id', 'version', 'payload'])->map(function ($company) {
$company->payload = json_decode(Crypt::decryptString($company->payload), true, 512, JSON_THROW_ON_ERROR);
$company->countrySource = $company->payload['organization_country_snapshot'] ?? null;
@@ -35,12 +36,17 @@ class InvestmentWorkspaceController extends Controller
return $company;
})];
return $request->expectsJson() ? response()->json($data) : Inertia::render('investment/profile', $data);
Inertia::encryptHistory();
$response = $request->expectsJson() ? response()->json($data) : Inertia::render('investment/profile', $data)->toResponse($request);
$response->headers->set('Cache-Control', 'no-store, private');
return $response;
}
public function saveProfile(Request $request): RedirectResponse|JsonResponse
{
$data = $request->validate(['version' => ['required', 'integer', 'min:0'],
$data = $request->validate(['version' => ['required', 'integer', 'min:0'], 'operation_key' => ['sometimes', 'required', 'uuid'],
'payload' => ['required', 'array:phone,country,residence_country_code,sector,experience,investment_capacity'],
'payload.residence_country_code' => ['nullable', 'string', app(CountryCatalogue::class)->rule()],
'payload.phone' => ['nullable', 'string', 'max:30'], 'payload.country' => ['nullable', 'string', 'max:100'],
@@ -48,12 +54,19 @@ class InvestmentWorkspaceController extends Controller
'payload.investment_capacity' => ['nullable', 'string', 'max:1000']]);
$version = DB::transaction(function () use ($request, $data) {
DB::table('users')->where('id', $request->user()->id)->lockForUpdate()->firstOrFail();
$commands = app(InvestmentProfileCommands::class);
$command = ['kind' => 'account', 'target_id' => null, 'version' => (int) $data['version'], 'payload' => $data['payload']];
$replay = $commands->replay($request->user(), $data['operation_key'] ?? null, $command);
if ($replay !== null) {
return $replay['version'];
}
$row = DB::table('investment_accounts')->where('user_id', $request->user()->id)->first();
abort_unless((int) ($row->version ?? 0) === (int) $data['version'], 409, 'Perfil alterado. Recarregue.');
$version = (int) $data['version'] + 1;
DB::table('investment_accounts')->updateOrInsert(['user_id' => $request->user()->id], ['version' => $version,
'payload' => Crypt::encryptString(json_encode($data['payload'], JSON_THROW_ON_ERROR)), 'created_at' => $row->created_at ?? now(), 'updated_at' => now()]);
$this->audit($request, 'profile_saved', 'investment_accounts', null, ['version' => $version]);
$commands->record($request->user(), $data['operation_key'] ?? null, $command, ['version' => $version]);
return $version;
});
@@ -63,7 +76,7 @@ class InvestmentWorkspaceController extends Controller
public function company(Request $request, ?string $id = null): RedirectResponse|JsonResponse
{
$data = $request->validate(['version' => ['required', 'integer', 'min:0'],
$data = $request->validate(['version' => ['required', 'integer', 'min:0'], 'operation_key' => ['sometimes', 'required', 'uuid'],
'payload' => ['required', 'array:name,country,incorporation_country_code,registration,sector,role,description'],
'payload.incorporation_country_code' => ['nullable', 'string', app(CountryCatalogue::class)->rule()],
'payload.name' => ['required', 'string', 'min:2', 'max:200'], 'payload.country' => ['required_without:payload.incorporation_country_code', 'nullable', 'string', 'max:100'],
@@ -73,6 +86,12 @@ class InvestmentWorkspaceController extends Controller
DB::table('users')->where('id', $request->user()->id)->lockForUpdate()->firstOrFail();
$row = $id ? DB::table('investment_company_declarations')->where('id', $id)->where('user_id', $request->user()->id)->first() : null;
abort_if($id && ! $row, 404);
$commands = app(InvestmentProfileCommands::class);
$command = ['kind' => 'company', 'target_id' => $id, 'version' => (int) $data['version'], 'payload' => $data['payload']];
$replay = $commands->replay($request->user(), $data['operation_key'] ?? null, $command);
if ($replay !== null) {
return $replay;
}
abort_unless((int) ($row->version ?? 0) === (int) $data['version'], 409, 'Declaração alterada. Recarregue.');
abort_if(! $id && DB::table('investment_company_declarations')->where('user_id', $request->user()->id)->count() >= 20, 422, 'Máximo de vinte empresas declaradas.');
$id ??= (string) Str::uuid();
@@ -86,6 +105,7 @@ class InvestmentWorkspaceController extends Controller
DB::table('investment_company_declarations')->updateOrInsert(['id' => $id], ['user_id' => $request->user()->id, 'version' => $version,
'payload' => $encrypted, 'created_at' => $row->created_at ?? now(), 'updated_at' => now()]);
$this->audit($request, 'company_declared', 'investment_company_declarations', $id, ['version' => $version, 'before_payload' => $row?->payload, 'after_payload' => $encrypted]);
$commands->record($request->user(), $data['operation_key'] ?? null, $command, ['id' => $id, 'version' => $version]);
return ['id' => $id, 'version' => $version];
});
@@ -102,13 +122,25 @@ class InvestmentWorkspaceController extends Controller
public function reuseOrganizationCountry(Request $request, string $id, OrganizationCountry $service): RedirectResponse|JsonResponse
{
$data = $request->validate(['version' => 'required|integer|min:1', 'organization_id' => 'required|uuid', 'organization_version' => 'required|integer|min:1']);
$data = $request->validate(['version' => 'required|integer|min:1', 'organization_id' => 'required|uuid', 'organization_version' => 'required|integer|min:1', 'operation_key' => ['sometimes', 'required', 'uuid']]);
$result = DB::transaction(function () use ($request, $id, $data, $service) {
DB::table('users')->where('id', $request->user()->id)->lockForUpdate()->firstOrFail();
$row = DB::table('investment_company_declarations')->where('id', $id)->where('user_id', $request->user()->id)->lockForUpdate()->first();
abort_unless($row !== null, 404);
$snapshot = $service->readableSnapshot($request->user(), $data['organization_id']);
$commands = app(InvestmentProfileCommands::class);
$command = ['kind' => 'organization_country_reuse', 'target_id' => $row->id, 'version' => (int) $data['version'], 'payload' => ['organization_id' => $snapshot['organization_id'], 'organization_version' => (int) $data['organization_version']]];
$replay = $commands->replay($request->user(), $data['operation_key'] ?? null, $command);
if ($replay !== null) {
if (! isset($replay['country'])) {
throw new \LogicException('Stored country reuse receipt unavailable.');
}
return ['version' => $replay['version'], 'country' => $replay['country']];
}
abort_unless((int) $row->version === (int) $data['version'], 409, 'Declaração alterada. Recarregue.');
$snapshot = $service->snapshot($request->user(), $data['organization_id']);
abort_unless((int) $snapshot['organization_version'] === (int) $data['organization_version'], 409, 'País mestre alterado. Recarregue.');
abort_unless($snapshot['country'] !== null, 422, 'País de constituição não informado no cadastro mestre.');
$payload = json_decode(Crypt::decryptString($row->payload), true, 512, JSON_THROW_ON_ERROR);
$payload['incorporation_country_code'] = $snapshot['country'];
$payload['organization_country_snapshot'] = $snapshot;
@@ -116,11 +148,12 @@ class InvestmentWorkspaceController extends Controller
DB::table('investment_company_declarations')->where('id', $id)->update(['payload' => $encrypted, 'version' => $row->version + 1, 'updated_at' => now()]);
$this->audit($request, 'organization_country_reused', 'investment_company_declarations', $id, ['version' => $row->version + 1,
'before_payload' => $row->payload, 'after_payload' => $encrypted, 'organization_id' => $data['organization_id'], 'organization_version' => $snapshot['organization_version']]);
$commands->record($request->user(), $data['operation_key'] ?? null, $command, ['version' => $row->version + 1, 'country' => $snapshot['country']]);
return ['version' => $row->version + 1, 'country' => $snapshot['country']];
});
return $request->expectsJson() ? response()->json($result) : back()->with('success', 'País mestre reutilizado nesta declaração; manifestações anteriores não alteradas.');
return $request->expectsJson() ? response()->json($result)->header('Cache-Control', 'no-store, private') : back()->with('success', 'País mestre reutilizado nesta declaração; manifestações anteriores não alteradas.');
}
public function notifications(Request $request): Response|JsonResponse