AsArrayObject::class]; //------------------------------------------------------------ // -- RELATED TO RELATIONSHIP /// HAS ONE public function new_tv_request(): HasOne { return $this->hasOne(NewTvRequest::class, 'tv_fk', 'id'); } public function tv_app_info(): HasOne { return $this->hasOne(TvAppInfo::class, 'tv_fk', 'id'); } /// HAS MANY public function tv_connect_logs(): HasMany { return $this->hasMany(TvConnectLog::class, 'tv_fk', 'id'); } public function tv_sessions(): HasMany { return $this->hasMany(TvSession::class, 'tv_fk', 'id'); } /// BELONGS TO public function outlet(): BelongsTo { return $this->belongsTo(Outlet::class, 'outlet_fk', 'id'); } // -- END RELATED TO RELATIONSHIP //------------------------------------------------------------ //------------------------------------------------------------ // -- RELATED TO DATA FUNCTION public static function generateUniqueCode() { // init $countSuffixDigit = 5; date_default_timezone_set('Asia/Jakarta'); $prefixCode = 'TV' . date('Ym'); // try to get unique suffix $uniqueCode = ''; while(true) { $suffixCode = strtoupper(Str::random($countSuffixDigit)); $uniqueCode = $prefixCode . $suffixCode; $isUnique = TV::where('code', $uniqueCode)->first(); if(!$isUnique) break; } return $uniqueCode; } // -- END RELATED TO DATA FUNCTION //------------------------------------------------------------ //------------------------------------------------------------ // -- RELATED TO REQUEST public static function validateAndGetEloquentFromRequest(Request $request) { $request->validate([ ...DatabaseHelper::getOrderBysValidations(), 'search' => DatabaseHelper::getSearchValidation(), 'apkVersionCode' => 'nullable|integer', 'tz' => 'required|timezone', 'isActive' => 'nullable|boolean', 'installedAt' => 'nullable|array', 'installedAt.from' => 'nullable|date', 'installedAt.to' => 'nullable|date', 'lastConnectedAt' => 'nullable|array', 'lastConnectedAt.from' => 'nullable|date', 'lastConnectedAt.to' => 'nullable|date', 'lastConnectedDay' => 'nullable|int' ]); Common::setTimezone($request->tz); return Tv::when($request->isActive != null, function($q) use($request) { $q->where('is_active', $request->isActive); }) ->when($request->apkVersionCode, function($q, $apkVersionCode) { $q->where('apk_version_code', $apkVersionCode); }) ->when($request->installedAt['from'] ?? '', function($q, $from) { $q->where(DB::raw('DATE(installed_at)'), '>=', $from); }) ->when($request->installedAt['to'] ?? '', function($q, $to) { $q->where(DB::raw('DATE(installed_at)'), '<=', $to); }) ->when($request->lastConnectedAt['from'] ?? '', function($q, $from) { $q->where(DB::raw('DATE(last_connected_at)'), '>=', $from); }) ->when($request->lastConnectedAt['to'] ?? '', function($q, $to) { $q->where(DB::raw('DATE(last_connected_at)'), '<=', $to); }) ->multiSearch($request->search, ['code', 'company_name']) ->multiOrderBy($request->orderBys, 'created_at desc'); } public static function updateFromRequest(Request $request) { if($request->code) $request->merge(['code' => strtoupper($request->code)]); $request->validate([ 'id' => 'required|integer|exists:App\Models\Tv', 'code' => ['required','string',Rule::unique('tvs', 'code') ->when($request->id, function($q, $id) { $q->whereNot('id', $id);})], 'company_name' => 'nullable|string', 'address' => 'nullable|string', 'street_address' => 'nullable|string', 'col1' => 'nullable|string', 'col2' => 'nullable|string', 'col3' => 'nullable|string', 'col4' => 'nullable|string', 'col5' => 'nullable|string', 'col6' => 'nullable|string', 'col7' => 'nullable|string', 'col8' => 'nullable|string', 'col9' => 'nullable|string', 'col10' => 'nullable|string', 'notes' => 'nullable|string', ]); try { DB::beginTransaction(); $tv = TV::findOrFail($request->id); $oldTv = $tv->replicate(); $tv->company_name = $request->company_name; $tv->address = $request->address; $tv->street_address = $request->street_address; $tv->code = $request->code; $tv->col1 = $request->col1; $tv->col2 = $request->col2; $tv->col3 = $request->col3; $tv->col4 = $request->col4; $tv->col5 = $request->col5; $tv->col6 = $request->col6; $tv->col7 = $request->col7; $tv->col8 = $request->col8; $tv->col9 = $request->col9; $tv->col10 = $request->col10; $tv->notes = $request->notes; $tv->update(); TvLog::historyUpdate($request->user(), $tv->id, $oldTv, $tv); DB::commit(); return JSONResponse::Success(['message'=>'Success to update tv data']); // TODO: waiting from ops workflow (Dont forget to resync co_name, address, street address in ik) // // try to sys_to_sys with indokargo // $jsonResponse = Indokargo::updateTVAddress($request, $tv->id, $tv->ik_address_id); // DB::commit(); // return $jsonResponse; } catch(\Throwable $th) { DB::rollback(); throw $th; } } public static function changeStatusFromRequest(Request $request) { $request->validate(['id' => 'required|integer|exists:App\Models\TV,id']); try { DB::beginTransaction(); $tv = Tv::findOrFail($request->id); $tv->is_active = !$tv->is_active; $tv->save(); DB::commit(); return JSONResponse::Success(['message'=>'Success to change tv status']); // TODO: waiting from ops workflow (Dont forget to resync co_name, address, street address in ik) // // try to sys_to_sys with indokargo // $jsonResponse = Indokargo::changeStatusAddress(new Request(['is_active' => $tv->is_active]), // $tv->id, $tv->ik_address_id); // DB::commit(); // return $jsonResponse; } catch(\Throwable $th) { DB::rollback(); throw $th; } } // -- END RELATED TO REQUEST //------------------------------------------------------------ //------------------------------------------------------------ // -- RELATED TO EXCEL public static function getExcelDetail(Request $request) { return JSONResponse::Success(['rows' => TV::validateAndGetEloquentFromRequest($request)->get()]); } // ---- RELATED TO EXPORT IMPORT const EXCEL_SUCCESS = 'success'; const EXCEL_FAILED = 'failed'; const EXCEL_NO_CHANGE = 'no_change'; const EXCEL_UPDATE = 'update'; const EXCEL_TEMPLATE_COLS = ['code', 'company_name', 'address', 'street_address', 'notes', 'col1', 'col2', 'col3', 'col4', 'col5', 'col6', 'col7', 'col8', 'col9', 'col10', 'is_active']; public static function getExcelTemplate() { $row = []; foreach(self::EXCEL_TEMPLATE_COLS as $col) { $row[$col] = ''; } $rows = [$row]; return JSONResponse::Success(['rows' => $rows]); } public static function getExportData(Request $request) { $rows = TV::validateAndGetEloquentFromRequest($request)->get(); return JSONResponse::Success(['rows' => $rows, 'cols' => self::EXCEL_TEMPLATE_COLS]); } // ------ RELATED TO IMPORT private static function _checkExcelFormat($tvRows) { if(!$tvRows) throw new \Exception("No Data"); $firstTvRow = $tvRows[0]; if(!$firstTvRow) throw new \Exception('Column not found'); // check if excel is empty or not $firstTvRow['row'] = null; if(!array_filter($firstTvRow)) throw new \Exception("Excel is empty"); // check is header col is exists $errors = []; foreach(self::EXCEL_TEMPLATE_COLS as $col) { if(!array_key_exists($col, $firstTvRow)) $errors[$col] = "Column $col is Required"; } if($errors) throw ValidationException::withMessages($errors); } private static function _getCountTvCodes($tvRows, $tvCodes) { if(!$tvCodes) $tvCodes = array_column($tvRows, 'code'); $tvCodes = array_map(function($code) { return DatabaseHelper::trimUpperNull($code) ?? ''; }, $tvCodes); return array_count_values($tvCodes); } private static function _changeModelFromTvRow(Tv $tv, $tvRow) { $tv->company_name = $tvRow['company_name']; $tv->address = $tvRow['address']; $tv->street_address = $tvRow['street_address']; $tv->code = $tvRow['code']; $tv->col1 = $tvRow['col1']; $tv->col2 = $tvRow['col2']; $tv->col3 = $tvRow['col3']; $tv->col4 = $tvRow['col4']; $tv->col5 = $tvRow['col5']; $tv->col6 = $tvRow['col6']; $tv->col7 = $tvRow['col7']; $tv->col8 = $tvRow['col8']; $tv->col9 = $tvRow['col9']; $tv->col10 = $tvRow['col10']; $tv->notes = $tvRow['notes']; return $tv; } public static function validateExcel($tvRows, $tvCodes = []) { self::_checkExcelFormat($tvRows); $countTvCodes = self::_getCountTvCodes($tvRows, $tvCodes); $endStatus = self::EXCEL_SUCCESS; $results = []; foreach($tvRows as $tvRow) { $status = self::EXCEL_NO_CHANGE; $message = ''; $tvRow['code'] = strtoupper($tvRow['code'] ?? ''); try { // STEP 1: check validation $validator = Validator::make($tvRow, [ 'row' => 'required|integer', 'code' => 'required|string', 'company_name' => 'nullable|string', 'address' => 'nullable|string', 'street_address' => 'nullable|string', 'col1' => 'nullable|string', 'col2' => 'nullable|string', 'col3' => 'nullable|string', 'col4' => 'nullable|string', 'col5' => 'nullable|string', 'col6' => 'nullable|string', 'col7' => 'nullable|string', 'col8' => 'nullable|string', 'col9' => 'nullable|string', 'col10' => 'nullable|string', 'notes' => 'nullable|string', ]); if($validator->fails()) { $errors = $validator->errors()->toArray(); $messages = []; foreach($errors as $eMessages) { $messages = array_merge($messages, $eMessages); } throw new \Exception(implode(', ', $messages)); } // STEP 2: check code is duplicate or not or not $code = $tvRow['code']; if(($countTvCodes[$code] ?? 0) > 1) throw new \Exception('Code is Duplicate in Excel'); // STEP 3: check code existing in database or not $tvCheck = Tv::where('code', 'ilike', $code)->first(); if(!$tvCheck) throw new \Exception("TV Code '$code' not found in database"); // STEP 4: check has update data or not $tvCheck = self::_changeModelFromTvRow($tvCheck, $tvRow); if($tvCheck->isDirty()) { $status = self::EXCEL_UPDATE; $message = DatabaseHelper::compileDirtyEloquentToArrMessage($tvCheck); } } catch (\Throwable $th) { $endStatus = self::EXCEL_FAILED; $status = self::EXCEL_FAILED; $message = $th->getMessage(); } $results[] = ['row' => $tvRow['row'], 'status' => $status, 'message' => $message]; } return ['status' => $endStatus, 'results' => $results]; } public static function uploadExcel($tvRows, $oValidation, User $user) { $validationResults = $oValidation['results']; $countUploads = [ self::EXCEL_UPDATE => 0, self::EXCEL_NO_CHANGE => 0, self::EXCEL_FAILED => 0 ]; $additionalErrors = []; foreach($validationResults as $result) { $validateStatus = $result['status']; try { switch($validateStatus) { case self::EXCEL_FAILED: throw new \Exception($result['message']); break; case self::EXCEL_UPDATE: // get sparepart data $idxTvRow = array_search($result['row'], array_column($tvRows, 'row')); if($idxTvRow === false) throw new \Exception('Row Not Found'); $tvRow = $tvRows[$idxTvRow]; $tvRow['code'] = strtoupper($tvRow['code'] ?? ''); // try to upsert DB::beginTransaction(); $tv = TV::where('code', 'ilike', $tvRow['code'])->firstOrFail(); $oldTV = $tv->replicate(); $newTv = self::_changeModelFromTvRow($tv, $tvRow); if(!$newTv->isDirty()) throw new \Exception('No Change'); $newTv->save(); // save data log TvLog::historyUpdateExcel($user, $newTv->id, $oldTV, $newTv); DB::commit(); break; case self::EXCEL_NO_CHANGE: default: break; } } catch (\Throwable $th) { DB::rollBack(); $validateStatus = self::EXCEL_FAILED; $additionalErrors[] = 'row ' . ($result['row'] ?? '-') . ' => ' . $th->getMessage(); } $countUploads[$validateStatus]++; } return ['countUploads' => $countUploads, 'additionalErrors' => $additionalErrors]; } // ------ END RELATED TO IMPORT // ---- END RELATED TO EXPORT IMPORT // -- END RELATED TO EXCEL //------------------------------------------------------------ }