You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tivi_kemana_saja_laravel/app/Models/Tv.php

365 lines
15 KiB
PHP

<?php
namespace App\Models;
use App\Helper\DatabaseHelper;
use App\Helper\JSONResponse;
use App\Helper\STS\Indokargo;
use App\Helper\Traits\Models\CanMultiOrderBy;
use App\Helper\Traits\Models\CanMultiSearch;
use Illuminate\Database\Eloquent\Casts\AsArrayObject;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;
use Illuminate\Support\Str;
use Illuminate\Validation\ValidationException;
class Tv extends Model {
use HasFactory;
use CanMultiSearch;
use CanMultiOrderBy;
protected $table = 'tvs';
protected $hidden = ['ik_cust_id', 'ik_address_id'];
protected $casts = ['device_info'=>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',
'isActive' => 'nullable|boolean',
'lastConnectedAt' => 'nullable|array',
'lastConnectedAt.from' => 'nullable|date',
'lastConnectedAt.to' => 'nullable|date'
]);
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->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) {
$cols = ['code', 'apk_version_code', 'apk_version_name', 'last_connected_at',
'company_name', 'address', 'street_address', 'notes',
'col1', 'col2', 'col3', 'col4', 'col5',
'col6', 'col7', 'col8', 'col9', 'col10',
'is_active'];
return JSONResponse::Success(['rows' => TV::validateAndGetEloquentFromRequest($request)->select(...$cols)->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)->select(...self::EXCEL_TEMPLATE_COLS)->get();
return JSONResponse::Success(['rows' => $rows]);
}
// ------ 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
//------------------------------------------------------------
}