@@ -9,6 +9,8 @@ use App\Models\Investor;
|
||||
use App\Models\Lead;
|
||||
use App\Models\User;
|
||||
use App\Services\ConvertInterestToLead;
|
||||
use App\Services\CountryCatalogue;
|
||||
use App\Services\DeclaredOriginStatistics;
|
||||
use App\Services\PortalAudit;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
@@ -77,7 +79,8 @@ class CrmController extends Controller
|
||||
{
|
||||
$filters = $request->validate(['q' => ['nullable', 'string', 'max:150'],
|
||||
'status' => ['nullable', Rule::in(['new', 'qualified', 'disqualified', 'converted'])],
|
||||
'owner_user_id' => ['nullable', 'integer', 'exists:users,id']]);
|
||||
'owner_user_id' => ['nullable', 'integer', 'exists:users,id'],
|
||||
'origin' => ['nullable', Rule::in(['unknown', ...array_column(app(CountryCatalogue::class)->all(), 'code')])]]);
|
||||
$query = Lead::with(['investor:id,legal_name', 'owner:id,name'])
|
||||
->withCount(['tasks as overdue_tasks_count' => fn ($q) => $q->whereIn('status', ['open', 'in_progress'])->where('due_at', '<', now())]);
|
||||
if ($term = $filters['q'] ?? null) {
|
||||
@@ -92,7 +95,9 @@ class CrmController extends Controller
|
||||
}
|
||||
|
||||
return Inertia::render('crm/leads', ['leads' => $query->latest()->orderBy('id')->paginate(20)->withQueryString(),
|
||||
'filters' => $filters, 'owners' => $this->owners()]);
|
||||
'filters' => $filters, 'owners' => $this->owners(),
|
||||
'countries' => app(CountryCatalogue::class)->all(),
|
||||
'originStatistics' => app(DeclaredOriginStatistics::class)->forManager($request->user(), $filters['origin'] ?? null)]);
|
||||
}
|
||||
|
||||
public function prepare(InterestRequest $interest): Response|RedirectResponse
|
||||
|
||||
@@ -128,7 +128,24 @@ class InvestmentDiscoveryController extends Controller
|
||||
|
||||
public function map(Request $request, InvestmentCatalogue $catalogue): JsonResponse
|
||||
{
|
||||
return response()->json($catalogue->map($catalogue->filters($request)));
|
||||
$filters = $catalogue->filters($request);
|
||||
$context = $this->territorial($filters)->map(fn ($d) => ['id' => 'dossier:'.$d->id, 'title' => $d->title, 'kind' => $d->cluster_id ? 'Cluster' : ($d->corridor_id ? 'Corredor' : 'Projeto âncora'), 'url' => '/carteira/'.$d->id]);
|
||||
$opportunityOnlyFilters = array_filter(\Illuminate\Support\Arr::only($filters, ['education', 'sector', 'facets', 'project_id', 'cluster_id', 'corridor_id', 'min_amount', 'max_amount', 'currency', 'before', 'maturity_level']), fn ($value) => $value !== null && $value !== '' && $value !== false && $value !== []);
|
||||
if ($opportunityOnlyFilters === [] && ! in_array($filters['dimension'] ?? null, ['cluster', 'corridor'], true)) {
|
||||
$zoneFilters = $filters;
|
||||
if (in_array($filters['dimension'] ?? null, ['zee', 'zfi'], true)) {
|
||||
$zoneFilters['category'] = $filters['dimension'];
|
||||
}
|
||||
$zones = app(InvestmentZones::class)->search($zoneFilters);
|
||||
foreach (['zee_id', 'zfi_id', 'park_id'] as $key) {
|
||||
if (! empty($filters[$key])) {
|
||||
$zones->where('id', $filters[$key]);
|
||||
}
|
||||
}
|
||||
$context = $context->concat($zones->orderBy('name')->limit(30)->get(['id', 'name', 'category'])->map(fn ($z) => ['id' => 'zone:'.$z->id, 'title' => $z->name, 'kind' => ['zee' => 'ZEE', 'zfi' => 'ZFI', 'industrial_park' => 'Parque industrial'][$z->category] ?? $z->category, 'url' => '/zonas/'.$z->id]));
|
||||
}
|
||||
|
||||
return response()->json([...$catalogue->map($filters), 'territorial_context' => $context->values(), 'context_limit_per_collection' => 30]);
|
||||
}
|
||||
|
||||
public function compare(Request $request, InvestmentCatalogue $catalogue): JsonResponse
|
||||
|
||||
@@ -49,7 +49,7 @@ class InvestmentSubmissionController extends Controller
|
||||
$company = DB::table('investment_company_declarations')->where('id', $data['company_id'])->where('user_id', $request->user()->id)->first();
|
||||
abort_unless($company !== null, 404);
|
||||
$companyData = json_decode(Crypt::decryptString($company->payload), true, 512, JSON_THROW_ON_ERROR);
|
||||
$payload = [...$payload, 'organisation' => $companyData['name'], 'representative_role' => $companyData['role'], 'country' => $companyData['country']];
|
||||
$payload = [...$payload, 'organisation' => $companyData['name'], 'representative_role' => $companyData['role'], 'incorporation_country_code' => $companyData['incorporation_country_code'] ?? null];
|
||||
}
|
||||
unset($data['company_id']);
|
||||
if (! empty($data['opportunity_id'])) {
|
||||
@@ -111,7 +111,7 @@ class InvestmentSubmissionController extends Controller
|
||||
$target = $row->opportunity_id ? Opportunity::published()->find($row->opportunity_id) : null;
|
||||
$dossier = $row->dossier_id ? InvestmentPortfolioController::visible()->find($row->dossier_id) : null;
|
||||
$zoneOrigin = DB::table('investment_submission_zones')->where('submission_id', $id)->first(['zone_id', 'zone_version', 'intent']);
|
||||
$data = ['submission' => $row->only(['id', 'payload', 'version', 'status', 'opportunity_id', 'dossier_id']), 'zone_origin' => $zoneOrigin,
|
||||
$data = ['countries' => app(\App\Services\CountryCatalogue::class)->all(), 'submission' => $row->only(['id', 'payload', 'version', 'status', 'opportunity_id', 'dossier_id']), 'zone_origin' => $zoneOrigin,
|
||||
'target' => ($target ? $target->title : null) ?? ($dossier ? $dossier->title : null), 'references' => [
|
||||
...$catalogue->references(), 'options' => DB::table('investment_reference_options')->orderBy('label')->get()],
|
||||
'needs' => InvestmentSpecificNeed::where('submission_id', $id)->get(),
|
||||
@@ -144,10 +144,13 @@ class InvestmentSubmissionController extends Controller
|
||||
{
|
||||
$required = $submitting ? 'required' : 'nullable';
|
||||
$data = $request->validate(['version' => ['required', 'integer', 'min:1'],
|
||||
'payload' => ['required', 'array:full_name,email,phone,country,organisation,representative_role,experience,investment_capacity,profile_id,sector,objective,participation_id,partnership_id,estimated_amount,currency,horizon,location,cluster_ids,corridor_ids,project_ids,contact_consent,other_details'],
|
||||
'payload' => ['required', 'array:full_name,email,phone,country,residence_country_code,incorporation_country_code,declared_origin_country_code,organisation,representative_role,experience,investment_capacity,profile_id,sector,objective,participation_id,partnership_id,estimated_amount,currency,horizon,location,cluster_ids,corridor_ids,project_ids,contact_consent,other_details'],
|
||||
'payload.residence_country_code' => ['nullable', 'string', app(\App\Services\CountryCatalogue::class)->rule()],
|
||||
'payload.incorporation_country_code' => ['nullable', 'string', app(\App\Services\CountryCatalogue::class)->rule()],
|
||||
'payload.declared_origin_country_code' => ['nullable', 'string', app(\App\Services\CountryCatalogue::class)->rule()],
|
||||
'payload.other_details' => ['nullable', 'string', 'max:5000'],
|
||||
'payload.full_name' => [$required, 'string', ...($submitting ? ['min:3'] : []), 'max:150'], 'payload.email' => [$required, $submitting ? 'email:rfc' : 'string', 'max:150'],
|
||||
'payload.phone' => ['nullable', 'string', 'max:30'], 'payload.country' => [$required, 'string', 'max:100'],
|
||||
'payload.phone' => ['nullable', 'string', 'max:30'], 'payload.country' => [$submitting ? 'required_without:payload.declared_origin_country_code' : 'nullable', 'nullable', 'string', 'max:100'],
|
||||
'payload.organisation' => ['nullable', 'string', 'max:200'], 'payload.representative_role' => ['nullable', 'string', 'max:150'],
|
||||
'payload.experience' => ['nullable', 'string', 'max:2000'], 'payload.investment_capacity' => ['nullable', 'string', 'max:1000'],
|
||||
'payload.profile_id' => [$required, 'integer'], 'payload.sector' => [$required, 'string', 'max:150'],
|
||||
@@ -257,10 +260,13 @@ class InvestmentSubmissionController extends Controller
|
||||
$interest = $intake->receive(['email' => $p['email'], 'fullName' => $p['full_name'], 'message' => $p['objective'], 'submission_id' => $id], $id,
|
||||
fn () => InterestRequest::create(['reference' => 'IR-'.now()->year.'-'.Str::ulid(), 'opportunity_id' => $opportunity?->id,
|
||||
'opportunity_reference' => $opportunity?->reference, 'full_name' => $p['full_name'], 'email' => $p['email'], 'phone' => $p['phone'] ?? null,
|
||||
'country' => $p['country'], 'investor_type' => empty($p['organisation']) ? 'individual' : 'empresa',
|
||||
'country' => $p['country'] ?? 'Não informado', 'investor_type' => empty($p['organisation']) ? 'individual' : 'empresa',
|
||||
'sector_interest' => $p['sector'], 'message' => $p['objective'], 'privacy_consent' => true, 'consented_at' => now(),
|
||||
'status' => 'received', 'source_page' => '/investidor/manifestacoes/'.$id,
|
||||
'metadata' => ['submission_id' => $id, 'portal_user_id' => $request->user()->id, 'dossier_id' => $row->dossier_id,
|
||||
'declared_origin_country_code' => $p['declared_origin_country_code'] ?? null,
|
||||
'residence_country_code' => $p['residence_country_code'] ?? null,
|
||||
'incorporation_country_code' => $p['incorporation_country_code'] ?? null,
|
||||
...($zoneOrigin ? ['zone_origin' => (array) $zoneOrigin] : [])]]));
|
||||
$row->update(['payload' => $p, 'version' => $row->version + 1, 'interest_request_id' => $interest->id, 'status' => 'submitted']);
|
||||
InvestmentSpecificNeed::where('submission_id', $id)->delete();
|
||||
|
||||
@@ -18,7 +18,7 @@ class InvestmentWorkspaceController extends Controller
|
||||
{
|
||||
public function profile(Request $request, InvestmentWorkspace $workspace): Response|JsonResponse
|
||||
{
|
||||
$data = ['account' => $workspace->account($request->user()->id), 'companies' => DB::table('investment_company_declarations')
|
||||
$data = ['countries' => app(\App\Services\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);
|
||||
|
||||
@@ -31,7 +31,8 @@ class InvestmentWorkspaceController extends Controller
|
||||
public function saveProfile(Request $request): RedirectResponse|JsonResponse
|
||||
{
|
||||
$data = $request->validate(['version' => ['required', 'integer', 'min:0'],
|
||||
'payload' => ['required', 'array:phone,country,sector,experience,investment_capacity'],
|
||||
'payload' => ['required', 'array:phone,country,residence_country_code,sector,experience,investment_capacity'],
|
||||
'payload.residence_country_code' => ['nullable', 'string', app(\App\Services\CountryCatalogue::class)->rule()],
|
||||
'payload.phone' => ['nullable', 'string', 'max:30'], 'payload.country' => ['nullable', 'string', 'max:100'],
|
||||
'payload.sector' => ['nullable', 'string', 'max:150'], 'payload.experience' => ['nullable', 'string', 'max:2000'],
|
||||
'payload.investment_capacity' => ['nullable', 'string', 'max:1000']]);
|
||||
@@ -53,8 +54,9 @@ class InvestmentWorkspaceController extends Controller
|
||||
public function company(Request $request, ?string $id = null): RedirectResponse|JsonResponse
|
||||
{
|
||||
$data = $request->validate(['version' => ['required', 'integer', 'min:0'],
|
||||
'payload' => ['required', 'array:name,country,registration,sector,role,description'],
|
||||
'payload.name' => ['required', 'string', 'min:2', 'max:200'], 'payload.country' => ['required', 'string', 'max:100'],
|
||||
'payload' => ['required', 'array:name,country,incorporation_country_code,registration,sector,role,description'],
|
||||
'payload.incorporation_country_code' => ['nullable', 'string', app(\App\Services\CountryCatalogue::class)->rule()],
|
||||
'payload.name' => ['required', 'string', 'min:2', 'max:200'], 'payload.country' => ['required_without:payload.incorporation_country_code', 'nullable', 'string', 'max:100'],
|
||||
'payload.registration' => ['nullable', 'string', 'max:100'], 'payload.sector' => ['nullable', 'string', 'max:150'],
|
||||
'payload.role' => ['required', 'string', 'max:150'], 'payload.description' => ['nullable', 'string', 'max:2000']]);
|
||||
$result = DB::transaction(function () use ($request, $id, $data) {
|
||||
|
||||
@@ -23,7 +23,8 @@ class OpportunityResourceController extends Controller
|
||||
{
|
||||
public function installation(Request $request, OpportunityResourceInstallation $service): JsonResponse|Response
|
||||
{
|
||||
$preview = [...$service->preview(), 'actionUrl' => '/management/opportunity-resources-install'];
|
||||
$preview = [...$service->preview(), 'imageProcessingAvailable' => app(OpportunityImageProcessor::class)->available(), 'actionUrl' => '/management/opportunity-resources-install'];
|
||||
$preview['changes'] = [...$preview['changes'], 'Código reconciliado: catálogo ISO comum; residência e constituição distintas; origem declarada guardada no payload existente e estatísticas internas autorizadas. Não converte países históricos nem usa IP.', 'Seletores territoriais reutilizados com pesquisa; filtros contextuais do catálogo e fichas territoriais consultáveis no percurso do mapa. Zero geometrias não permite desenhar estruturas sem dados.', 'Não reinstala a gestão básica nem acrescenta associações territoriais. Revisão transversal, cadastro completo em todos os campos e percursos educativos continuam parciais; consultar a matriz por módulo.'];
|
||||
|
||||
return $request->expectsJson() ? response()->json($preview) : Inertia::render('management/opportunity-install', $preview);
|
||||
}
|
||||
@@ -41,6 +42,7 @@ class OpportunityResourceController extends Controller
|
||||
$opportunity = Opportunity::withTrashed()->findOrFail($id);
|
||||
abort_unless($service->installed(), 503, 'Recursos ainda não instalados; consulte o plano autenticado.');
|
||||
$resources = [];
|
||||
Inertia::share('imageProcessingAvailable', app(OpportunityImageProcessor::class)->available());
|
||||
foreach (OpportunityResources::KINDS as $kind) {
|
||||
$resources[$kind] = $service->listing($id, $kind);
|
||||
}
|
||||
|
||||
@@ -48,8 +48,8 @@ class VirtualDeskController extends Controller
|
||||
$data = ['cases' => $cases, 'queue' => $queue, 'notifications' => DB::table('desk_notifications')->where('user_id', $actor->id)->orderByDesc('id')->limit(100)->get(),
|
||||
'legacyRequests' => DB::table('interest_requests')->where('metadata->portal_user_id', $actor->id)->whereNull('deleted_at')->orderByDesc('created_at')->get(['id', 'reference', 'status', 'created_at']),
|
||||
'companies' => DB::table('investment_company_declarations')->where('user_id', $actor->id)->get(['id', 'payload'])->map(fn ($c) => ['id' => $c->id, 'name' => $desk->decode($c->payload)['name'] ?? 'Declaração empresarial']),
|
||||
'submissions' => DB::table('investment_submissions')->where('user_id', $actor->id)->get(['id', 'status']),
|
||||
'journeys' => DB::table('investor_journeys')->where('user_id', $actor->id)->whereNull('deleted_at')->get(['id', 'dossier_id', 'status']),
|
||||
'submissions' => \App\Models\InvestmentSubmission::where('user_id', $actor->id)->get()->map(fn ($s) => ['id' => $s->id, 'status' => $s->status, 'name' => ($s->opportunity_id ? DB::table('opportunities')->where('id', $s->opportunity_id)->value('title') : null) ?? ($s->dossier_id ? DB::table('investment_dossiers')->where('id', $s->dossier_id)->value('title') : null) ?? ($s->payload['organisation'] ?? null)]),
|
||||
'journeys' => DB::table('investor_journeys as j')->leftJoin('investment_dossiers as d', 'd.id', '=', 'j.dossier_id')->where('j.user_id', $actor->id)->whereNull('j.deleted_at')->get(['j.id', 'j.dossier_id', 'j.status', 'd.title as name']),
|
||||
'simulationDrafts' => DB::table('simulation_form_drafts')->where('owner_user_id', $actor->id)->whereNull('deleted_at')->where(fn ($q) => $q->whereNull('expires_at')->orWhere('expires_at', '>', now()))->get(['id', 'version_no']),
|
||||
'guidanceTasks' => app(InvestorGuidance::class)->installed() ? DB::table('tasks as t')->join('desk_cases as c', 'c.id', '=', 't.desk_case_id')
|
||||
->whereNull('t.deleted_at')->when(! $actor->can('manage-portal'), fn ($q) => $q->where(fn ($p) => $p->where('t.assignee_user_id', $actor->id)->orWhere(fn ($s) => $s->where('c.user_id', $actor->id)->whereNull('c.organization_id'))->orWhereIn('c.organization_id', $organizations)))
|
||||
@@ -130,7 +130,7 @@ class VirtualDeskController extends Controller
|
||||
}
|
||||
}
|
||||
$this->auditRead($request, 'desk_requests', $id);
|
||||
$data = ['request' => [...(array) $row, 'payload' => $payload, 'submitted_snapshot' => $desk->decode($row->submitted_snapshot), 'editable_fields' => json_decode($row->editable_fields ?? '[]', true)],
|
||||
$data = ['countries' => app(\App\Services\CountryCatalogue::class)->all(), 'request' => [...(array) $row, 'payload' => $payload, 'submitted_snapshot' => $desk->decode($row->submitted_snapshot), 'editable_fields' => json_decode($row->editable_fields ?? '[]', true)],
|
||||
'case' => ['id' => $case->id, 'title' => $case->title], 'caseVersion' => $case->version, 'definition' => $desk->definition($row),
|
||||
'serviceSource' => json_decode(DB::table('desk_service_versions')->where('id', $row->service_version_id)->value('source'), true),
|
||||
'owner' => $desk->owns($request->user(), $case), 'manager' => $request->user()->can('manage-portal'), 'analyst' => (int) $row->analyst_id === $request->user()->id,
|
||||
|
||||
Reference in New Issue
Block a user