session()->put('search', $request ->has('search') ? strip_tags($request->get('search')) : ($request->session() ->has('search') ? strip_tags($request->session()->get('search')) : '')); $request->session()->put('field', $request ->has('field') ? strip_tags($request->get('field')) : ($request->session() ->has('field') ? strip_tags($request->session()->get('field')) : 'id')); $request->session()->put('sort', $request ->has('sort') ? strip_tags($request->get('sort')) : ($request->session() ->has('sort') ? strip_tags($request->session()->get('sort')) : 'asc')); //$headers = $request->get('header') != '' ? $request->get('header') : -1; $postulations = new Postulations(); $postulations = $postulations->where('id', 'LIKE', '%' . strip_tags($request->session()->get('search')) . '%') ->orwhere('user_id', 'LIKE', '%' . strip_tags($request->session()->get('search')) . '%') ->orwhere('address', 'LIKE', '%' . strip_tags($request->session()->get('search')) . '%') ->orwhere('amount', 'LIKE', '%' . strip_tags($request->session()->get('search')) . '%') ->orderBy(strip_tags($request->session()->get('field')), strip_tags($request->session()->get('sort'))) ->paginate(10); if ($request->ajax()) { return view('postulations.index', compact('postulations')); } else { return view('postulations.ajax', compact('postulations')); } } public function map(Request $request) { // $request->session()->put('search', $request ->has('search') ? strip_tags($request->get('search')) : ($request->session() ->has('search') ? strip_tags($request->session()->get('search')) : '')); $request->session()->put('field', $request ->has('field') ? strip_tags($request->get('field')) : ($request->session() ->has('field') ? strip_tags($request->session()->get('field')) : 'id')); $request->session()->put('sort', $request ->has('sort') ? strip_tags($request->get('sort')) : ($request->session() ->has('sort') ? strip_tags($request->session()->get('sort')) : 'asc')); //$headers = $request->get('header') != '' ? $request->get('header') : -1; $postulations = new Postulations(); $postulations = $postulations->where('id', 'LIKE', '%' . strip_tags($request->session()->get('search')) . '%') ->orwhere('user_id', 'LIKE', '%' . strip_tags($request->session()->get('search')) . '%') ->orwhere('address', 'LIKE', '%' . strip_tags($request->session()->get('search')) . '%') ->orwhere('amount', 'LIKE', '%' . strip_tags($request->session()->get('search')) . '%') ->orderBy(strip_tags($request->session()->get('field')), strip_tags($request->session()->get('sort'))) ->paginate(10); return view('postulations.map', compact('postulations')); } /** * Show the form for creating a new resource. * * @return \Illuminate\Http\Response */ public function create(Request $request) { $rules = [ 'category' => 'required|string', 'property_id' => 'required|numeric', 'references' => 'string|nullable', 'details' => 'string|nullable', 'photos' => 'nullable|array', 'photos.*' => 'nullable|image|max:4096', 'related_postulation_id' => 'nullable|numeric|exists:postulations,id', ]; $validator = Validator::make($request->all(), $rules); if ($validator->fails()) { return response()->json($validator->messages()); } else { $user = Auth::user(); $property = Property::find($request->property_id); if (!$property || $property->user_id !== $user->id) { return response()->json(['message' => 'Propiedad no válida'], 403); } $geometry = new Point($property->lat, $property->lng); $category = Categories::where('name', strip_tags($request->category))->orwhere('en_name', strip_tags($request->category))->first(); $distance = 5000; // metros (5 km) $suppliers = Suppliers::withinDistanceTo('location', $geometry, $distance)->get(); if ($suppliers != '[]') { $postulation = new Postulations(); $postulation->user_id = $user->id; $postulation->category_id = $category->id; $postulation->property_id = $property->id; $postulation->address = $property->address; $postulation->location = $geometry; $postulation->int_number = $property->int_number; $postulation->references = preg_replace('/\d+/', '', strip_tags($request->references)); $postulation->amount = null; $postulation->details = preg_replace('/\d+/', '', strip_tags($request->details)); $postulation->status = 'active'; $postulation->related_postulation_id = $request->related_postulation_id; $postulation->save(); // Subir fotos a storage if ($request->hasFile('photos')) { $disk = Storage::disk(env('STORAGE_DISK', 'gcs')); $photoUrls = []; foreach ($request->file('photos') as $i => $photo) { $filename = time() . '_' . $i . '.' . $photo->getClientOriginalExtension(); $disk->putFileAs('img/postulations/' . $postulation->id . '/', $photo, $filename, 'public'); $photoUrls[] = $disk->url('img/postulations/' . $postulation->id . '/' . $filename); } $postulation->photos = $photoUrls; $postulation->save(); } try { OneSignal::sendNotificationToExternalUser( "Coméntele al Ing. que hay una postulación", "128", null, null, null, null, "Admin: hay nueva postulación" ); } catch (\Exception $e) {} foreach ($suppliers as $supplier) { if (in_array($category->id, $supplier->categories->pluck('id')->toArray())) { try { OneSignal::sendNotificationToExternalUser( "Dirígete a la sección de postulaciones en la app para ver más detalles", (string) $supplier->user_id, null, null, null, null, "Proveedor: hay nueva postulación" ); } catch (\Exception $e) {} // TODO: Configurar WhatsApp cuando esté disponible // Whatsapp::send($supplier->user->phone, Messages\TemplateMessage::create() // ->name('suppplier_new_postulation') // ->language('es_US') // ->body(Messages\Components\Body::create([ // Messages\Components\Parameters\Text::create('Proveedor hay nueva postulación: dirígete a la sección de postulaciones en JobHero para ver más detalles'), // ]))); } } $delay_UTC = Carbon::now()->addMinutes(15)->toString(); try { OneSignal::sendNotificationToExternalUser( "Dirígete a la sección de contratos en la app para ver más detalles", (string) $user->id, null, null, null, $delay_UTC, "Búsqueda Finalizada" ); } catch (\Exception $e) {} return response()->json([ 'message' => 'Servicio solicitado, espere a que un proveedor se postule' ]); } else { return response()->json([ 'message' => 'No Provider' ]); } } } public function postulate(Request $request) { $rules = [ 'postulation_id' => 'required|numeric', 'date_1' => 'required|date', 'date_2' => 'required|date', 'amount' => 'required|numeric|min:0', ]; $validator = Validator::make($request->all(), $rules); if ($validator->fails()) { return response()->json($validator->errors(), 422); } $user = Auth::user(); $postulation = Postulations::where('id', $request->postulation_id)->first(); $time_created = Carbon::parse($postulation->created_at); $time_limit = (1000 - Carbon::now()->diffInMinutes($time_created)); $supplier = Suppliers::where('user_id', $user->id)->first(); if (!$supplier) { return response()->json([ 'success' => false, 'message' => 'No tienes un perfil de proveedor registrado' ], 400); } // Validar cuota mínima del sistema $minFeeParam = iChambaParameter::where('parameter', 'ichamba_fee')->first(); if ($minFeeParam && $request->amount < $minFeeParam->num_value) { return response()->json([ 'message' => 'El monto es menor a la cuota mínima del sistema (' . $minFeeParam->num_value . ')' ], 422); } if ($time_limit > 0) { if (in_array($postulation->category_id, $supplier->categories->pluck('id')->toArray())) { if ($supplier->membership == 1) { try { OneSignal::sendNotificationToExternalUser( "Dirígete a la sección de contratos en la app para ver más detalles", (string) $postulation->user_id, null, null, null, null, "Un proveedor certificado se ha postulado" ); } catch (\Exception $e) {} } $supplier->postulations()->attach($request->postulation_id, [ 'date_1' => Carbon::parse($request->date_1), 'date_2' => Carbon::parse($request->date_2), 'amount' => $request->amount, ]); return response()->json([ 'message' => 'Se ha postulado al servicio exitosamente' ]); } } else { return response()->json([ 'message' => 'La postulación ha caducado' ]); } } public function getpendingcontracts(Request $request) { $user = Auth::user(); $postulations = Postulations::where('user_id', $user->id) ->where('status', 'active') ->orderBy('created_at', 'DESC') ->get(); $pendingcontracts = array(); foreach($postulations as $postulation) { $category = Categories::where('id', $postulation->category_id)->first(); $pendingcontractinfo = array( 'id' => $postulation->id, 'category' => $category->name, 'en_category' => $category->en_name, 'address' => $postulation->address, 'date' => $postulation->appointment, 'amount' => $postulation->amount, 'property_id' => $postulation->property_id, 'references' => $postulation->references, 'details' => $postulation->details, 'time_created' => $postulation->created_at, ); $pendingcontracts[] = $pendingcontractinfo; } return response()->json($pendingcontracts); } public function getfinishedpostulations(Request $request) { $user = Auth::user(); if (!$user->suppliers) { return response()->json([ 'success' => false, 'message' => 'No tienes un perfil de proveedor registrado' ], 400); } $supplier = $user->suppliers; $postulations = FinishedContracts::with(['technician.user']) ->where('supplier_id', $supplier->id)->orderBy('created_at', 'DESC')->get(); $finishedpostulations = array(); foreach($postulations as $postulation) { $category = Categories::where('id', $postulation->category_id)->first(); $technician = $postulation->technical_id ? ($postulation->technician->user->name ?? null) : ($supplier->user->name ?? null); $finishedpostulationinfo = array( 'id' => $postulation->id, 'category' => $category->name, 'en_category' => $category->en_name, 'address' => $postulation->address, 'date' => $postulation->appointment, 'amount' => $postulation->amount, 'technician' => $technician ); $finishedpostulations[] = $finishedpostulationinfo; } return response()->json($finishedpostulations); } public function getpostulants(Request $request) { $rules = [ 'postulation_id' => 'required|numeric', ]; $validator = Validator::make($request->all(), $rules); if ($validator->fails()) { return response()->json($validator->errors(), 422); } $user = Auth::user(); $postulation = Postulations::with('suppliers')->where('id', $request->postulation_id)->first(); if ($postulation->user_id == $user->id) { $category = Categories::where('id', $postulation->category_id)->first(); $suppliers = $postulation->suppliers; $pcontractsuppliers = array(); if ($suppliers->isNotEmpty()) { foreach ($suppliers as $supplier) { $pivot = $supplier->pivot; $pcontractsupplier = array( 'id' => $postulation->id, 'category' => $category->name, 'en_category' => $category->en_name, 'address' => $postulation->address, 'supplier_id' => $supplier->id, 'supplier' => $supplier->company_name, 'tags' => $supplier->tags, 'cover_photo' => $supplier->cover_photo, 'membership' => $supplier->membership, 'fee' => $pivot->amount, 'date_1' => $pivot->date_1, 'date_2' => $pivot->date_2, 'score' => $supplier->finished_jobs > 0 ? round($supplier->total_score / $supplier->finished_jobs, 1) : null, ); $pcontractsuppliers[] = $pcontractsupplier; } $collection = collect($pcontractsuppliers); $sort = $request->get('sort', 'membership'); if ($sort === 'date') { $pcontractsupplier = $collection->sortBy('date_1')->values()->all(); } elseif ($sort === 'amount') { $pcontractsupplier = $collection->sortBy('fee')->values()->all(); } else { $pcontractsupplier = $collection->sortByDesc('membership')->sortByDesc('score')->values()->all(); } } else { $pcontractsupplier = array( 'id' => $postulation->id, 'category' => $category->name, 'en_category' => $category->en_name, 'address' => $postulation->address, 'supplier_id' => null, 'supplier' => null, 'tags' => null, 'cover_photo' => null, 'membership' => null, 'fee' => null, 'date_1' => null, 'date_2' => null, 'score' => null, ); $pcontractsuppliers[] = $pcontractsupplier; $pcontractsupplier = $pcontractsuppliers; } return response()->json($pcontractsupplier); } } public function deleteexpired() { $postulations = Postulations::whereDate('appointment', '<', Carbon::now())->delete(); } public function cancelPostulation($id) { $user = Auth::user(); $postulation = Postulations::find($id); if (!$postulation || $postulation->user_id !== $user->id) { return response()->json(['message' => 'No autorizado'], 403); } $postulation->status = 'perdido'; $postulation->save(); return response()->json(['message' => 'Postulación archivada']); } public function updatePostulation(Request $request, $id) { $user = Auth::user(); $postulation = Postulations::find($id); if (!$postulation || $postulation->user_id !== $user->id) { return response()->json(['message' => 'No autorizado'], 403); } $rules = [ 'references' => 'nullable|string', 'details' => 'nullable|string', 'photos' => 'nullable|array', 'photos.*' => 'nullable|image|max:4096', ]; $validator = Validator::make($request->all(), $rules); if ($validator->fails()) { return response()->json($validator->errors(), 422); } if ($request->has('references')) { $postulation->references = preg_replace('/\d+/', '', strip_tags($request->references)); } if ($request->has('details')) { $postulation->details = preg_replace('/\d+/', '', strip_tags($request->details)); } if ($request->hasFile('photos')) { $disk = Storage::disk(env('STORAGE_DISK', 'gcs')); $photoUrls = []; foreach ($request->file('photos') as $i => $photo) { $filename = time() . '_' . $i . '.' . $photo->getClientOriginalExtension(); $disk->putFileAs('img/postulations/' . $postulation->id . '/', $photo, $filename, 'public'); $photoUrls[] = $disk->url('img/postulations/' . $postulation->id . '/' . $filename); } $postulation->photos = $photoUrls; } $postulation->save(); return response()->json(['message' => 'Postulación actualizada']); } public function destroy($id) { Postulations::destroy($id); return redirect('postulations'); } }