fix: complete InvesteMoz validation and error corrections
This commit is contained in:
@@ -15,6 +15,8 @@ use App\Services\InvestmentWorkspace;
|
||||
use App\Services\OpportunityManagement;
|
||||
use App\Services\PortalAudit;
|
||||
use App\Services\PortalProfileAccess;
|
||||
use App\Services\ScopedPortalAdministration;
|
||||
use App\Services\SectorOrganization;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
@@ -55,32 +57,38 @@ class PortalManagementController extends Controller
|
||||
|
||||
private function interestAssignmentEtag(InterestRequest $interest): string
|
||||
{
|
||||
return hash('sha256',json_encode([$interest->id,$interest->status,$interest->assigned_to,$interest->updated_at?->format('Y-m-d H:i:s.u')],JSON_THROW_ON_ERROR));
|
||||
return hash('sha256', json_encode([$interest->id, $interest->status, $interest->assigned_to, $interest->updated_at?->format('Y-m-d H:i:s.u')], JSON_THROW_ON_ERROR));
|
||||
}
|
||||
|
||||
public function interestAssignments(Request $request): Response|JsonResponse
|
||||
{
|
||||
abort_unless(app(\App\Services\ScopedPortalAdministration::class)->administrative($request->user()),403);
|
||||
$rows=InterestRequest::whereNotIn('status',['cancelled','closed','refused','converted'])->orderBy('id')->get(['id','reference','status','assigned_to','updated_at'])
|
||||
->map(fn($row)=>['id'=>$row->id,'protocol'=>$row->reference,'status'=>$row->status,'analyst_id'=>$row->assigned_to,'version'=>0,'etag'=>$this->interestAssignmentEtag($row),'due_at'=>null]);
|
||||
$data=['kind'=>'interest','requests'=>$rows,'analysts'=>$this->interestAssignees()->get(['id','name'])];
|
||||
return $request->expectsJson()?response()->json($data):Inertia::render('management/desk-assignments',$data);
|
||||
abort_unless(app(ScopedPortalAdministration::class)->administrative($request->user()), 403);
|
||||
$rows = InterestRequest::whereNotIn('status', ['cancelled', 'closed', 'refused', 'converted'])->orderBy('id')->get(['id', 'reference', 'status', 'assigned_to', 'updated_at'])
|
||||
->map(fn ($row) => ['id' => $row->id, 'protocol' => $row->reference, 'status' => $row->status, 'analyst_id' => $row->assigned_to, 'version' => 0, 'etag' => $this->interestAssignmentEtag($row), 'due_at' => null]);
|
||||
$data = ['kind' => 'interest', 'requests' => $rows, 'analysts' => $this->interestAssignees()->get(['id', 'name'])];
|
||||
|
||||
return $request->expectsJson() ? response()->json($data) : Inertia::render('management/desk-assignments', $data);
|
||||
}
|
||||
|
||||
public function assignInterest(Request $request, InterestRequest $interest): RedirectResponse|JsonResponse
|
||||
{
|
||||
abort_unless(app(\App\Services\ScopedPortalAdministration::class)->administrative($request->user()),403);
|
||||
$data=$request->validate(['etag'=>'required|string|size:64','analyst_id'=>['required','integer',Rule::in($this->interestAssignees()->pluck('id'))],'reason'=>'required|string|min:10|max:2000'],['reason.required'=>'Explique o motivo da atribuição.','reason.min'=>'Explique o motivo da atribuição com pelo menos 10 caracteres.','analyst_id.required'=>'Seleccione o responsável.','analyst_id.in'=>'Seleccione um responsável autorizado.']);
|
||||
DB::transaction(function()use($request,$interest,$data){
|
||||
$row=InterestRequest::whereKey($interest->id)->lockForUpdate()->firstOrFail();
|
||||
if (!hash_equals($this->interestAssignmentEtag($row),$data['etag'])) throw ValidationException::withMessages(['etag'=>'A atribuição mudou. Recupere a lista actual e tente novamente.']);
|
||||
if (in_array($row->status,['cancelled','closed','refused','converted'],true)) throw ValidationException::withMessages(['status'=>'Este pedido já não permite uma nova atribuição. Recupere a lista actual.']);
|
||||
$owner=(int)($row->metadata['portal_user_id']??0);
|
||||
abort_if($owner===(int)$data['analyst_id'] || InvestmentSubmission::where('interest_request_id',$row->id)->where('user_id',$data['analyst_id'])->exists(),422,'O interessado não pode acompanhar o seu próprio pedido como equipa.');
|
||||
PortalAudit::save($row,['assigned_to'=>(int)$data['analyst_id']],$request->user()->id);
|
||||
app(\App\Services\PortalProfileAccess::class)->audit($request->user(),'interest_assigned',['interest_id'=>$row->id,'assigned_to'=>(int)$data['analyst_id'],'reason'=>$data['reason']]);
|
||||
abort_unless(app(ScopedPortalAdministration::class)->administrative($request->user()), 403);
|
||||
$data = $request->validate(['etag' => 'required|string|size:64', 'analyst_id' => ['required', 'integer', Rule::in($this->interestAssignees()->pluck('id'))], 'reason' => 'required|string|min:10|max:2000'], ['reason.required' => 'Explique o motivo da atribuição.', 'reason.min' => 'Explique o motivo da atribuição com pelo menos 10 caracteres.', 'analyst_id.required' => 'Seleccione o responsável.', 'analyst_id.in' => 'Seleccione um responsável autorizado.']);
|
||||
DB::transaction(function () use ($request, $interest, $data) {
|
||||
$row = InterestRequest::whereKey($interest->id)->lockForUpdate()->firstOrFail();
|
||||
if (! hash_equals($this->interestAssignmentEtag($row), $data['etag'])) {
|
||||
throw ValidationException::withMessages(['etag' => 'A atribuição mudou. Recupere a lista actual e tente novamente.']);
|
||||
}
|
||||
if (in_array($row->status, ['cancelled', 'closed', 'refused', 'converted'], true)) {
|
||||
throw ValidationException::withMessages(['status' => 'Este pedido já não permite uma nova atribuição. Recupere a lista actual.']);
|
||||
}
|
||||
$owner = (int) ($row->metadata['portal_user_id'] ?? 0);
|
||||
abort_if($owner === (int) $data['analyst_id'] || InvestmentSubmission::where('interest_request_id', $row->id)->where('user_id', $data['analyst_id'])->exists(), 422, 'O interessado não pode acompanhar o seu próprio pedido como equipa.');
|
||||
PortalAudit::save($row, ['assigned_to' => (int) $data['analyst_id']], $request->user()->id);
|
||||
app(PortalProfileAccess::class)->audit($request->user(), 'interest_assigned', ['interest_id' => $row->id, 'assigned_to' => (int) $data['analyst_id'], 'reason' => $data['reason']]);
|
||||
});
|
||||
return $request->expectsJson()?response()->json(['ok'=>true]):back()->with('success','Responsável atribuído.');
|
||||
|
||||
return $request->expectsJson() ? response()->json(['ok' => true]) : back()->with('success', 'Responsável atribuído.');
|
||||
}
|
||||
|
||||
public function interests(Request $request): Response
|
||||
@@ -95,7 +103,7 @@ class PortalManagementController extends Controller
|
||||
$query = InterestRequest::query()->with('assignee:id,name')->select([
|
||||
'id', 'reference', 'full_name', 'email', 'country', 'metadata', 'sector_interest', 'existing_business_id', 'modality', 'status', 'assigned_to', 'next_action', 'next_action_due_at', 'created_at',
|
||||
]);
|
||||
app(\App\Services\ScopedPortalAdministration::class)->interestQuery($query, $request->user());
|
||||
app(ScopedPortalAdministration::class)->interestQuery($query, $request->user());
|
||||
if ($country = $filters['country'] ?? null) {
|
||||
$values = app(CountryCatalogue::class)->filterValues($country);
|
||||
$query->whereIn(DB::raw('LOWER(BTRIM(country))'), $values);
|
||||
@@ -142,7 +150,7 @@ class PortalManagementController extends Controller
|
||||
|
||||
public function interest(InterestRequest $interest): Response
|
||||
{
|
||||
abort_unless(app(\App\Services\ScopedPortalAdministration::class)->interest(request()->user(), $interest),403);
|
||||
abort_unless(app(ScopedPortalAdministration::class)->interest(request()->user(), $interest), 403);
|
||||
$history = DB::table('business_workflow_transitions')->where('interest_request_id', $interest->id)
|
||||
->orderByDesc('created_at')->limit(50)->get(['from_status', 'to_status', 'actor_id', 'reason', 'created_at'])
|
||||
->map(function ($row) {
|
||||
@@ -170,7 +178,7 @@ class PortalManagementController extends Controller
|
||||
|
||||
public function review(Request $request, InterestRequest $interest, InterestWorkflow $workflow): RedirectResponse|JsonResponse
|
||||
{
|
||||
abort_unless(app(\App\Services\ScopedPortalAdministration::class)->interest(request()->user(), $interest),403);
|
||||
abort_unless(app(ScopedPortalAdministration::class)->interest(request()->user(), $interest), 403);
|
||||
$validated = $request->validate([
|
||||
'status' => ['required', Rule::in(self::STATES)],
|
||||
'note' => ['nullable', 'string', 'min:10', 'max:2000'],
|
||||
@@ -198,7 +206,7 @@ class PortalManagementController extends Controller
|
||||
|
||||
public function withdrawProponentSharingConsent(Request $request, InterestRequest $interest): RedirectResponse|JsonResponse
|
||||
{
|
||||
abort_unless(app(\App\Services\ScopedPortalAdministration::class)->interest(request()->user(), $interest),403);
|
||||
abort_unless(app(ScopedPortalAdministration::class)->interest(request()->user(), $interest), 403);
|
||||
$data = $request->validate(['reason' => ['required', 'string', 'min:10', 'max:2000']]);
|
||||
$this->withdrawConsent($request, $interest, $data['reason'], 'share_contact_with_proponent');
|
||||
|
||||
@@ -208,7 +216,7 @@ class PortalManagementController extends Controller
|
||||
|
||||
public function withdrawContactConsent(Request $request, InterestRequest $interest): RedirectResponse|JsonResponse
|
||||
{
|
||||
abort_unless(app(\App\Services\ScopedPortalAdministration::class)->interest(request()->user(), $interest),403);
|
||||
abort_unless(app(ScopedPortalAdministration::class)->interest(request()->user(), $interest), 403);
|
||||
$data = $request->validate(['reason' => ['required', 'string', 'min:10', 'max:2000']]);
|
||||
$this->withdrawConsent($request, $interest, $data['reason'], 'respond_to_interest');
|
||||
|
||||
@@ -301,8 +309,8 @@ class PortalManagementController extends Controller
|
||||
}
|
||||
}
|
||||
if ($resource === 'opportunities' && ($filters['sector_id'] ?? null)) {
|
||||
$sector = Sector::find($filters['sector_id']);
|
||||
$query->whereIn('sector_id', $sector ? Sector::whereIn('slug', \App\Services\SectorOrganization::slugs($sector->slug))->pluck('id') : []);
|
||||
$sector = Sector::whereKey($filters['sector_id'])->first();
|
||||
$query->whereIn('sector_id', $sector ? Sector::whereIn('slug', SectorOrganization::slugs($sector->slug))->pluck('id') : []);
|
||||
}
|
||||
|
||||
return Inertia::render('management/catalog', [
|
||||
@@ -319,7 +327,7 @@ class PortalManagementController extends Controller
|
||||
public function edit(string $resource, ?int $id = null): Response
|
||||
{
|
||||
$model = $resource === 'sectors' ? Sector::class : Opportunity::class;
|
||||
$record = $id ? $model::findOrFail($id) : null;
|
||||
$record = $id ? $model::whereKey($id)->firstOrFail() : null;
|
||||
|
||||
return Inertia::render('management/editor', [
|
||||
'resource' => $resource,
|
||||
@@ -335,7 +343,7 @@ class PortalManagementController extends Controller
|
||||
throw ValidationException::withMessages(['status' => 'Use Gestão de Oportunidades para guardar uma versão de rascunho; publicação não é uma edição.']);
|
||||
}
|
||||
$model = $resource === 'sectors' ? Sector::class : Opportunity::class;
|
||||
$record = $id ? $model::findOrFail($id) : new $model;
|
||||
$record = $id ? $model::whereKey($id)->firstOrFail() : new $model;
|
||||
$rules = $resource === 'sectors' ? [
|
||||
'name' => ['required', 'string', 'max:150'],
|
||||
'slug' => ['required', 'alpha_dash:ascii', 'max:180', Rule::unique('sectors')->ignore($record->id)],
|
||||
|
||||
Reference in New Issue
Block a user