From 4bde317a233ea70c28d43dd2451d3b9fdb4c8eca Mon Sep 17 00:00:00 2001 From: ricky rx Date: Sat, 18 May 2024 11:24:44 +0700 Subject: [PATCH 01/27] feat: migration for v2.0.0 --- .../2014_10_11_000000_V1.0.1_MARKER | 4 + .../2024_05_15_000000_V2.0.0_MARKER | 2 + .../migrations/2024_05_18_033101_outlet.php | 43 ++++++++++ database/migrations/2024_05_18_033105_tv.php | 79 +++++++++++++++++++ .../migrations/2024_05_18_033128_sts_log.php | 37 +++++++++ 5 files changed, 165 insertions(+) create mode 100644 database/migrations/2014_10_11_000000_V1.0.1_MARKER create mode 100644 database/migrations/2024_05_15_000000_V2.0.0_MARKER create mode 100644 database/migrations/2024_05_18_033101_outlet.php create mode 100644 database/migrations/2024_05_18_033105_tv.php create mode 100644 database/migrations/2024_05_18_033128_sts_log.php diff --git a/database/migrations/2014_10_11_000000_V1.0.1_MARKER b/database/migrations/2014_10_11_000000_V1.0.1_MARKER new file mode 100644 index 0000000..c9e720f --- /dev/null +++ b/database/migrations/2014_10_11_000000_V1.0.1_MARKER @@ -0,0 +1,4 @@ +Notes: +- Auto Update app +- Auto Update video +- Laravel Sanctum \ No newline at end of file diff --git a/database/migrations/2024_05_15_000000_V2.0.0_MARKER b/database/migrations/2024_05_15_000000_V2.0.0_MARKER new file mode 100644 index 0000000..f2532ed --- /dev/null +++ b/database/migrations/2024_05_15_000000_V2.0.0_MARKER @@ -0,0 +1,2 @@ +Notes: +- TV information & Logs \ No newline at end of file diff --git a/database/migrations/2024_05_18_033101_outlet.php b/database/migrations/2024_05_18_033101_outlet.php new file mode 100644 index 0000000..923f94e --- /dev/null +++ b/database/migrations/2024_05_18_033101_outlet.php @@ -0,0 +1,43 @@ +id(); + $table->string('code', 50)->index(); + $table->string('name', 255)->nullable()->index(); + $table->string('address', 255)->nullable(); + $table->string('street_address', 255)->nullable(); + $table->string('phone', 50)->nullable()->index(); + $table->double('lat')->default(0); + $table->double('long')->default(0); + $table->string('notes', 255)->nullable(); + $table->string('country', 255); + $table->string('ik_country_id', 255); + $table->string('state', 255); + $table->string('ik_state_id', 255); + $table->string('region', 255); + $table->string('ik_region_id', 255); + $table->string('city', 255); + $table->string('ik_city_id', 255); + $table->string('zipcode', 255)->nullable(); + $table->timestampTz('last_visited_at')->nullable(); + $table->timestampsTz(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void { + Schema::drop('outlets'); + } +}; diff --git a/database/migrations/2024_05_18_033105_tv.php b/database/migrations/2024_05_18_033105_tv.php new file mode 100644 index 0000000..1092337 --- /dev/null +++ b/database/migrations/2024_05_18_033105_tv.php @@ -0,0 +1,79 @@ +id(); + $table->foreignId('outlet_fk')->nullable(); + $table->string('code', 50)->unique(); + $table->string('ik_address_id', 255); + $table->string('notes', 255)->nullable(); + $table->timestampTz('last_connected_at')->nullable(); + $table->string('col1', 255)->nullable(); + $table->string('col2', 255)->nullable(); + $table->string('col3', 255)->nullable(); + $table->string('col4', 255)->nullable(); + $table->string('col5', 255)->nullable(); + $table->string('col6', 255)->nullable(); + $table->string('col7', 255)->nullable(); + $table->string('col8', 255)->nullable(); + $table->string('col9', 255)->nullable(); + $table->string('col10', 255)->nullable(); + $table->bool('is_acctive')->default(true); + $table->timestampsTz(); + }); + + Schema::create('tv_sessions', function (Blueprint $table) { + $table->id(); + $table->foreignId('tv_fk')->index(); + $table->timestampTz('started_at')->index(); + $table->timestampTz('finished_at')->nullable()->index(); + $table->boolean('is_playing_video'); + $table->jsonb('current_videos')->nullable(); + $table->timestampsTz(); + + $table->foreign('tv_fk')->references('id')->on('tvs'); + }); + + Schema::create('tv_app_infos', function (Blueprint $table) { + $table->foreignId('tv_fk')->unique()->index(); + $table->timestampTz('installed_at')->nullable()->index(); + $table->jsonb('current_videos')->nullable(); + $table->timestampTz('last_updated_video')->nullable()->index(); + $table->timestampsTz(); + + $table->foreign('tv_fk')->references('id')->on('tvs'); + }); + + Schema::create('tv_app_logs', function (Blueprint $table) { + $table->id(); + $table->foreignId('tv_fk')->index(); + $table->string('requested_to'); + $table->string('ip', 50); + $table->jsonb('request_data')->nullable(); + $table->enum('result', ['success', 'failed']); + $table->jsonb('response_data')->nullable(); + $table->timestampsTz(); + + $table->foreign('tv_fk')->references('id')->on('tvs'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void { + Schema::drop('tv_app_logs'); + Schema::drop('tv_app_infos'); + Schema::drop('tv_sessions'); + Schema::drop('tvs'); + } +}; diff --git a/database/migrations/2024_05_18_033128_sts_log.php b/database/migrations/2024_05_18_033128_sts_log.php new file mode 100644 index 0000000..caa949b --- /dev/null +++ b/database/migrations/2024_05_18_033128_sts_log.php @@ -0,0 +1,37 @@ +id(); + $table->string('partner', 255)->nullable(); + $table->boolean('is_outgoing'); + $table->string('module', 255)->nullable(); + $table->string('service_name', 255)->nullable(); + $table->string('local_id', 255)->nullable(); + $table->string('partner_id', 255)->nullable(); + $table->enum('result', ['success', 'failed', 'warning']); + $table->jsonb('error_info')->nullable(); + $table->tinyInteger('seq'); + $table->jsonb('request_data')->nullable(); + $table->timestampTz('request_time')->nullable(); + $table->boolean('is_retry'); + $table->timestampsTz(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void { + Schema::drop('sts_logs'); + } +}; From 2a7a0b32f38de9cd6866b06d8eb8296439842189 Mon Sep 17 00:00:00 2001 From: ricky rx Date: Sat, 18 May 2024 11:25:09 +0700 Subject: [PATCH 02/27] feat: model for v2.0.0 --- app/Models/Outlet.php | 20 ++++++++++++++++++++ app/Models/StsLog.php | 12 ++++++++++++ app/Models/Tv.php | 28 ++++++++++++++++++++++++++++ app/Models/TvAppInfo.php | 22 ++++++++++++++++++++++ app/Models/TvConnectLog.php | 18 ++++++++++++++++++ app/Models/TvSession.php | 18 ++++++++++++++++++ 6 files changed, 118 insertions(+) create mode 100644 app/Models/Outlet.php create mode 100644 app/Models/StsLog.php create mode 100644 app/Models/Tv.php create mode 100644 app/Models/TvAppInfo.php create mode 100644 app/Models/TvConnectLog.php create mode 100644 app/Models/TvSession.php diff --git a/app/Models/Outlet.php b/app/Models/Outlet.php new file mode 100644 index 0000000..4349332 --- /dev/null +++ b/app/Models/Outlet.php @@ -0,0 +1,20 @@ +hasOne(Tv::class, 'outlet_fk', 'id'); } + // -- END RELATED TO MIGRATION2 + //------------------------------------------------------------ +} diff --git a/app/Models/StsLog.php b/app/Models/StsLog.php new file mode 100644 index 0000000..b32f0b6 --- /dev/null +++ b/app/Models/StsLog.php @@ -0,0 +1,12 @@ +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 MIGRATION + //------------------------------------------------------------ +} diff --git a/app/Models/TvAppInfo.php b/app/Models/TvAppInfo.php new file mode 100644 index 0000000..edb3d23 --- /dev/null +++ b/app/Models/TvAppInfo.php @@ -0,0 +1,22 @@ +belongsTo(Tv::class, 'tv_fk', 'id'); } + // -- END RELATED TO MIGRATION + //------------------------------------------------------------ +} diff --git a/app/Models/TvConnectLog.php b/app/Models/TvConnectLog.php new file mode 100644 index 0000000..9c9e961 --- /dev/null +++ b/app/Models/TvConnectLog.php @@ -0,0 +1,18 @@ +belongsTo(Tv::class, 'tv_fk', 'id'); } + // -- END RELATED TO MIGRATION + //------------------------------------------------------------ +} diff --git a/app/Models/TvSession.php b/app/Models/TvSession.php new file mode 100644 index 0000000..4eccdd5 --- /dev/null +++ b/app/Models/TvSession.php @@ -0,0 +1,18 @@ +belongsTo(Tv::class, 'tv_fk', 'id'); } + // -- END RELATED TO MIGRATION + //------------------------------------------------------------ +} From 546df1abf65dff4a11e023ba8649ace002ed214f Mon Sep 17 00:00:00 2001 From: ricky rx Date: Mon, 20 May 2024 13:28:29 +0700 Subject: [PATCH 03/27] feat: api for frontend --- .../{TvAppInfo.php => KIV_TvAppInfo.php} | 4 +- ...{TvConnectLog.php => KIV_TvConnectLog.php} | 4 +- .../{TvSession.php => KIV_TvSession.php} | 4 +- app/Models/NewTvRequest.php | 12 +++ app/Models/Tv.php | 39 +++++++++ ...01_outlet.php => 2024_05_18_033101_outlet} | 0 database/migrations/2024_05_18_033105_tv.php | 84 +++++++++++-------- .../migrations/2024_05_18_033128_sts_log.php | 4 +- 8 files changed, 108 insertions(+), 43 deletions(-) rename app/Models/{TvAppInfo.php => KIV_TvAppInfo.php} (89%) rename app/Models/{TvConnectLog.php => KIV_TvConnectLog.php} (87%) rename app/Models/{TvSession.php => KIV_TvSession.php} (86%) create mode 100644 app/Models/NewTvRequest.php rename database/migrations/{2024_05_18_033101_outlet.php => 2024_05_18_033101_outlet} (100%) diff --git a/app/Models/TvAppInfo.php b/app/Models/KIV_TvAppInfo.php similarity index 89% rename from app/Models/TvAppInfo.php rename to app/Models/KIV_TvAppInfo.php index edb3d23..c832606 100644 --- a/app/Models/TvAppInfo.php +++ b/app/Models/KIV_TvAppInfo.php @@ -14,9 +14,9 @@ class TvAppInfo extends Model { public $incrementing = false; //------------------------------------------------------------ - // -- RELATED TO MIGRATION + // -- RELATED TO RELATIONSHIP /// BELONGS TO public function tv(): BelongsTo { return $this->belongsTo(Tv::class, 'tv_fk', 'id'); } - // -- END RELATED TO MIGRATION + // -- END RELATED TO RELATIONSHIP //------------------------------------------------------------ } diff --git a/app/Models/TvConnectLog.php b/app/Models/KIV_TvConnectLog.php similarity index 87% rename from app/Models/TvConnectLog.php rename to app/Models/KIV_TvConnectLog.php index 9c9e961..faede24 100644 --- a/app/Models/TvConnectLog.php +++ b/app/Models/KIV_TvConnectLog.php @@ -10,9 +10,9 @@ class TvConnectLog extends Model { use HasFactory; //------------------------------------------------------------ - // -- RELATED TO MIGRATION + // -- RELATED TO RELATIONSHIP /// BELONGS TO public function tv(): BelongsTo { return $this->belongsTo(Tv::class, 'tv_fk', 'id'); } - // -- END RELATED TO MIGRATION + // -- END RELATED TO RELATIONSHIP //------------------------------------------------------------ } diff --git a/app/Models/TvSession.php b/app/Models/KIV_TvSession.php similarity index 86% rename from app/Models/TvSession.php rename to app/Models/KIV_TvSession.php index 4eccdd5..d5d0741 100644 --- a/app/Models/TvSession.php +++ b/app/Models/KIV_TvSession.php @@ -10,9 +10,9 @@ class TvSession extends Model { use HasFactory; //------------------------------------------------------------ - // -- RELATED TO MIGRATION + // -- RELATED TO RELATIONSHIP /// BELONGS TO public function tv(): BelongsTo { return $this->belongsTo(Tv::class, 'tv_fk', 'id'); } - // -- END RELATED TO MIGRATION + // -- END RELATED TO RELATIONSHIP //------------------------------------------------------------ } diff --git a/app/Models/NewTvRequest.php b/app/Models/NewTvRequest.php new file mode 100644 index 0000000..abb20aa --- /dev/null +++ b/app/Models/NewTvRequest.php @@ -0,0 +1,12 @@ +belongsTo(Outlet::class, 'outlet_fk', 'id'); } // -- END RELATED TO MIGRATION //------------------------------------------------------------ + + //------------------------------------------------------------ + // -- RELATED TO DATA FUNCTION + public static function generateRandomCode(): string { + $totalDigit = 6; + $randomDigit = ''; + while(true) { + $randomDigit = strtoupper(Str::random($totalDigit)); + $isExist = Tv::where('code', $randomDigit)->first(); + if(!$isExist) break; + } + return $randomDigit; + } + // -- END RELATED TO DATA FUNCTION + //------------------------------------------------------------ + + //------------------------------------------------------------ + // -- RELATED TO DATA FUNCTION + public static function createAnonymousData() :Tv { + $newTv = new self; + $newTv->code = self::generateRandomCode(); + $newTv->is_active = true; + $newTv->save(); + return $newTv; + } + // -- END RELATED TO DATA FUNCTION + //------------------------------------------------------------ + + + //------------------------------------------------------------ + // -- RELATED TO REQUEST + public static function renewTvInformation(Request $request) { + + } + // -- END RELATED TO REQUEST + //------------------------------------------------------------ } diff --git a/database/migrations/2024_05_18_033101_outlet.php b/database/migrations/2024_05_18_033101_outlet similarity index 100% rename from database/migrations/2024_05_18_033101_outlet.php rename to database/migrations/2024_05_18_033101_outlet diff --git a/database/migrations/2024_05_18_033105_tv.php b/database/migrations/2024_05_18_033105_tv.php index 1092337..cdc5316 100644 --- a/database/migrations/2024_05_18_033105_tv.php +++ b/database/migrations/2024_05_18_033105_tv.php @@ -4,8 +4,7 @@ use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; -return new class extends Migration -{ +return new class extends Migration { /** * Run the migrations. */ @@ -14,9 +13,7 @@ return new class extends Migration $table->id(); $table->foreignId('outlet_fk')->nullable(); $table->string('code', 50)->unique(); - $table->string('ik_address_id', 255); - $table->string('notes', 255)->nullable(); - $table->timestampTz('last_connected_at')->nullable(); + $table->string('ik_address_id', 255)->nullable(); $table->string('col1', 255)->nullable(); $table->string('col2', 255)->nullable(); $table->string('col3', 255)->nullable(); @@ -27,53 +24,68 @@ return new class extends Migration $table->string('col8', 255)->nullable(); $table->string('col9', 255)->nullable(); $table->string('col10', 255)->nullable(); - $table->bool('is_acctive')->default(true); + $table->string('notes', 255)->nullable(); + $table->boolean('is_active')->default(true); + $table->jsonb('device_info')->nullable(); + $table->timestampTz('installed_at')->nullable(); + $table->timestampTz('last_connected_at')->nullable(); $table->timestampsTz(); }); - Schema::create('tv_sessions', function (Blueprint $table) { + Schema::create('new_tv_requests', function (Blueprint $table) { $table->id(); - $table->foreignId('tv_fk')->index(); - $table->timestampTz('started_at')->index(); - $table->timestampTz('finished_at')->nullable()->index(); - $table->boolean('is_playing_video'); - $table->jsonb('current_videos')->nullable(); + $table->string('code', 50); + $table->jsonb('device_info')->nullable(); + $table->foreignId('tv_fk')->nullable(); + $table->timestampTz('activated_at')->nullable(); + $table->timestampTz('responded_at')->nullable(); $table->timestampsTz(); - - $table->foreign('tv_fk')->references('id')->on('tvs'); }); - Schema::create('tv_app_infos', function (Blueprint $table) { - $table->foreignId('tv_fk')->unique()->index(); - $table->timestampTz('installed_at')->nullable()->index(); - $table->jsonb('current_videos')->nullable(); - $table->timestampTz('last_updated_video')->nullable()->index(); - $table->timestampsTz(); + // Schema::create('tv_sessions', function (Blueprint $table) { + // $table->id(); + // $table->foreignId('tv_fk')->index(); + // $table->timestampTz('started_at')->index(); + // $table->timestampTz('finished_at')->nullable()->index(); + // $table->boolean('is_playing_video'); + // $table->jsonb('current_videos')->nullable(); + // $table->timestampsTz(); - $table->foreign('tv_fk')->references('id')->on('tvs'); - }); + // $table->foreign('tv_fk')->references('id')->on('tvs'); + // }); - Schema::create('tv_app_logs', function (Blueprint $table) { - $table->id(); - $table->foreignId('tv_fk')->index(); - $table->string('requested_to'); - $table->string('ip', 50); - $table->jsonb('request_data')->nullable(); - $table->enum('result', ['success', 'failed']); - $table->jsonb('response_data')->nullable(); - $table->timestampsTz(); + // Schema::create('tv_app_infos', function (Blueprint $table) { + // $table->foreignId('tv_fk')->unique()->index(); + // $table->timestampTz('installed_at')->nullable()->index(); + // $table->jsonb('current_videos')->nullable(); + // $table->timestampTz('last_updated_video')->nullable()->index(); + // $table->timestampsTz(); - $table->foreign('tv_fk')->references('id')->on('tvs'); - }); + // $table->foreign('tv_fk')->references('id')->on('tvs'); + // }); + + // Schema::create('tv_app_logs', function (Blueprint $table) { + // $table->id(); + // $table->foreignId('tv_fk')->index(); + // $table->string('requested_to'); + // $table->string('ip', 50); + // $table->jsonb('request_data')->nullable(); + // $table->enum('result', ['success', 'failed']); + // $table->jsonb('response_data')->nullable(); + // $table->timestampsTz(); + + // $table->foreign('tv_fk')->references('id')->on('tvs'); + // }); } /** * Reverse the migrations. */ public function down(): void { - Schema::drop('tv_app_logs'); - Schema::drop('tv_app_infos'); - Schema::drop('tv_sessions'); + // Schema::drop('tv_app_logs'); + // Schema::drop('tv_app_infos'); + // Schema::drop('tv_sessions'); + Schema::drop('new_tv_requests'); Schema::drop('tvs'); } }; diff --git a/database/migrations/2024_05_18_033128_sts_log.php b/database/migrations/2024_05_18_033128_sts_log.php index caa949b..8677641 100644 --- a/database/migrations/2024_05_18_033128_sts_log.php +++ b/database/migrations/2024_05_18_033128_sts_log.php @@ -23,7 +23,9 @@ return new class extends Migration $table->tinyInteger('seq'); $table->jsonb('request_data')->nullable(); $table->timestampTz('request_time')->nullable(); - $table->boolean('is_retry'); + $table->jsonb('response_data')->nullable(); + $table->timestampTz('response_time')->nullable(); + $table->boolean('is_retry')->default('false'); $table->timestampsTz(); }); } From fb281607d1842f56f547d6e53d243c3bb2e8a240 Mon Sep 17 00:00:00 2001 From: ricky rx Date: Mon, 27 May 2024 12:22:50 +0700 Subject: [PATCH 04/27] feat: api for frontend> tv > new tv request --- .env.example | 5 + app/Helper/STS/Indokargo.php | 112 ++++++++++++ .../superadmin/tv/NewTvRequestController.php | 31 ++++ app/Models/NewTvRequest.php | 171 ++++++++++++++++++ app/Models/StsLog.php | 35 ++++ app/Models/Tv.php | 23 +-- app/Providers/AppServiceProvider.php | 14 +- routes/api/superadmin.php | 6 + 8 files changed, 376 insertions(+), 21 deletions(-) create mode 100644 app/Helper/STS/Indokargo.php create mode 100644 app/Http/Controllers/api/superadmin/tv/NewTvRequestController.php diff --git a/.env.example b/.env.example index 4885627..ff84703 100644 --- a/.env.example +++ b/.env.example @@ -8,6 +8,11 @@ MOBILE_TOKEN=Gaobd5OKPdGARLGTD03vSFStrADAxmQ9 # for CORS SANCTUM_STATEFUL_DOMAINS=[http://localhost:3000] +# for Indokargo +IK_BE_DOMAIN= +IK_SESSION_ID= +IK_OAUTH_ID= + LOG_CHANNEL=stack LOG_DEPRECATIONS_CHANNEL=null LOG_LEVEL=debug diff --git a/app/Helper/STS/Indokargo.php b/app/Helper/STS/Indokargo.php new file mode 100644 index 0000000..597777f --- /dev/null +++ b/app/Helper/STS/Indokargo.php @@ -0,0 +1,112 @@ +partner = StsLog::PARTNER_INDOKARGO; + $stsLog->is_outgoing = false; + $stsLog->module = StsLog::MODULE_TV; + $stsLog->service_name = StsLog::SERVICE_CREATE_TV_ADDRESS; + $stsLog->local_id = $tvFk; + $stsLog->partner_id = null; + $stsLog->seq = $seq; + $stsLog->request_data = $request->all(); + $stsLog->request_time = Carbon::now(); + + try { + $request->validate(['code' => 'required|string']); + + $result = Http::indokargo()->post('tv/address/create', $request->all()); + $res = $result->throw()->json(); + + // update stsLog + $stsLog->response_data = $res; + $stsLog->response_time = Carbon::now(); + + self::_checkIkResponse($res); + + $ikAddress = $res['data']['0']; + $ikAddressId = $ikAddress['id']; + $stsLog->partner_id = $ikAddressId; + + // check ik address id is exist (almost impossible to happen, 0.01%) + $isDuplicateIkAddressId = TV::where([ + ['ik_address_id', '=', $ikAddressId], + ['id', '!=', $tvFk] + ])->first(); + if($isDuplicateIkAddressId) throw new \Exception("IK Address ID Already exist in current db ($ikAddressId)"); + + DB::beginTransaction(); + $tv = Tv::findOrFail($tvFk); + $tv->ik_address_id = $ikAddressId; + $tv->save(); + + $stsLog->result = StsLog::STATUS_SUCCESS; + $stsLog->save(); + DB::commit(); + return JSONResponse::Success(['is_warning' => false, 'message' => 'Success To Save Data']); + } catch(ConnectionException $e) { + // if error cause by connection error, try again later + $stsLog->error_info = [ + 'message' => $e->getMessage(), + 'errors' => $e->getTrace() + ]; + $stsLog->result = StsLog::STATUS_FAILED; + $stsLog->is_retry = true; + $stsLog->save(); + return JSONResponse::Success(['is_warning' => true, + 'message' => "Failed to sync data with indokargo data, don't worry, we will sync data later"]); + } catch(\Throwable $th) { + DB::rollBack(); + $stsLog->error_info = [ + 'message' => $th->getMessage(), + 'errors' => $th->getTrace() + ]; + $stsLog->result = StsLog::STATUS_FAILED; + $stsLog->save(); + throw $th; + } + } +} + +?> \ No newline at end of file diff --git a/app/Http/Controllers/api/superadmin/tv/NewTvRequestController.php b/app/Http/Controllers/api/superadmin/tv/NewTvRequestController.php new file mode 100644 index 0000000..8eca953 --- /dev/null +++ b/app/Http/Controllers/api/superadmin/tv/NewTvRequestController.php @@ -0,0 +1,31 @@ +validate([ + 'perPage' => 'nullable|integer|min:1', + ...DatabaseHelper::getOrderBysValidations(), + 'search' => DatabaseHelper::getSearchValidation() + ]); + + $newTvRequests = NewTvRequest::addColumnCanApprove() + ->addColumnCanReject() + ->multiSearch($request->search, ['code']) + ->multiOrderBy($request->orderBys, 'created_at desc') + ->paginate($request->perPage ?? 10); + + return JSONResponse::Success(['data' => $newTvRequests ]); + } + + public function approve(Request $request) { return NewTvRequest::approveFromRequest($request); } + public function reject(Request $request) { return NewTvRequest::rejectFromRequest($request); } +} diff --git a/app/Models/NewTvRequest.php b/app/Models/NewTvRequest.php index abb20aa..aeb1e13 100644 --- a/app/Models/NewTvRequest.php +++ b/app/Models/NewTvRequest.php @@ -2,11 +2,182 @@ namespace App\Models; +use App\Helper\JSONResponse; +use App\Helper\STS\Indokargo; +use App\Helper\Traits\Models\CanMultiOrderBy; +use App\Helper\Traits\Models\CanMultiSearch; +use Carbon\Carbon; +use Illuminate\Database\Eloquent\Builder; +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\HasOne; +use Illuminate\Http\Request; +use Illuminate\Support\Facades\DB; +use Illuminate\Support\Str; class NewTvRequest extends Model { use HasFactory; + use CanMultiSearch; + use CanMultiOrderBy; protected $table = 'new_tv_requests'; + protected $casts = ['device_info'=>AsArrayObject::class]; + + //------------------------------------------------------------ + // -- RELATED TO RELATIONSHIP + // HAS ONE + public function self(): HasOne { return $this->hasOne(self::class, 'id', 'id'); } + /// BELONGS TO + public function tv(): BelongsTo { return $this->belongsTo(Tv::class, 'tv_fk', 'id'); } + // -- END RELATED TO RELATIONSHIP + //------------------------------------------------------------ + + //------------------------------------------------------------ + // -- RELATED TO SCOPE + public function scopeAddColumnCanApprove(Builder $query) { + $query->withCount(['self as can_approve' => function($q) { + $q->whereNull('activated_at'); + }]); + } + public function scopeAddColumnCanReject(Builder $query) { + $query->withCount(['self as can_reject' => function($q) { + $q->whereNull('activated_at'); + }]); + } + // -- END RELATED TO SCOPE + //------------------------------------------------------------ + + //------------------------------------------------------------ + // -- RELATED TO DATA FUNCTION + private static function _generateRandomCode(): string { + $totalDigit = 6; + $randomDigit = ''; + while(true) { + $randomDigit = strtoupper(Str::random($totalDigit)); + $isExist = NewTvRequest::where('code', $randomDigit)->first(); + if(!$isExist) break; + } + return $randomDigit; + } + // -- END RELATED TO DATA FUNCTION + //------------------------------------------------------------ + + //------------------------------------------------------------ + // -- RELATED TO FROM REQUEST FUNCTION + private static function _checkOrCreateResponse(NewTvRequest $newTvReq) { + if($newTvReq->is_activated) { + // make sure waiting tv_device responded before deleted + if(empty($newTvReq->responded_at)) { + $newTvReq->responded_at = now(); + $newTvReq->save(); + } + + return JSONResponse::Success([ + 'message' => 'activated', + 'is_active' => true, + 'new_tv_request' => $newTvReq, + 'tv' => $newTvReq->tv + ]); + } + + return JSONResponse::Success([ + 'message' => 'not activate', + 'is_active' => false, + 'new_tv_request' => $newTvReq, + 'tv' => null + ]); + } + + public static function approveFromRequest(Request $request) { + $request->validate([ + 'id' => 'required|integer|exists:App\Models\NewTvRequest', + 'tv' => 'required|array', + ]); + + $tvRequest = new Request($request->tv); + if($tvRequest->filled('code')) $tvRequest->code = strtoupper($tvRequest->code); + + $tvRequest->validate([ + 'code' => 'required|string|unique:App\Models\Tv,code', + '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', + ]); + + $newTvReq = NewTvRequest::addColumnCanApprove() + ->findOrFail($request->id); + if(!$newTvReq->can_approve) throw new \Exception('Cannot approve current request'); + + try { + DB::beginTransaction(); + $tv = new Tv(); + $tv->code = $tvRequest->code; + $tv->col1 = $tvRequest->col1; + $tv->col2 = $tvRequest->col2; + $tv->col3 = $tvRequest->col3; + $tv->col4 = $tvRequest->col4; + $tv->col5 = $tvRequest->col5; + $tv->col6 = $tvRequest->col6; + $tv->col7 = $tvRequest->col7; + $tv->col8 = $tvRequest->col8; + $tv->col9 = $tvRequest->col9; + $tv->col10 = $tvRequest->col10; + $tv->notes = $tvRequest->notes; + $tv->device_info = $newTvReq->device_info; + $tv->save(); + + $newTvReq->activated_at = now(); + $newTvReq->tv_fk = $tv->id; + $newTvReq->save(); + + // try to sys_to_sys with indokargo + $jsonResponse = Indokargo::createTVAddress($tvRequest, $tv->id); + DB::commit(); + return $jsonResponse; + } catch(\Throwable $th) { + DB::rollback(); + throw $th; + } + } + + public static function rejectFromRequest(Request $request) { + $request->validate([ + 'id' => 'required|integer|exists:App\Models\NewTvRequest', + ]); + $newTvRequest = NewTvRequest::addColumnCanReject() + ->findOrFail($request->id); + if(!$newTvRequest->can_reject) throw new \Exception('Cannot reject current request'); + $newTvRequest->delete(); + + return JSONResponse::Success(['data' => $newTvRequest ]); + } + + /** + * Rule: + * 1. if it has not been activated for more than the expiry date, deleted it + * 2. if it has been activated & has been responded more than expiry date, delete it + * - case: when has been activated, but intenet connection missing, how can device know + * that the new request tv has been activated? + */ + private static function _getMaxExpiredTime() :Carbon { return Carbon::now()->subHour(); } + public static function deleteExpiredRequests() { + $expiredTime = self::_getMaxExpiredTime()->toString(); + NewTvRequest::where(function($q) use ($expiredTime) { + $q->whereNull('activated_at')->where('created_at', '<=', $expiredTime); + })->orWhere(function($q) use ($expiredTime) { + $q->where('responded_at', '<=', $expiredTime ); + })->delete(); + } + // -- END RELATED TO FROM REQUEST FUNCTION + //------------------------------------------------------------ } diff --git a/app/Models/StsLog.php b/app/Models/StsLog.php index b32f0b6..458ab7f 100644 --- a/app/Models/StsLog.php +++ b/app/Models/StsLog.php @@ -2,6 +2,7 @@ namespace App\Models; +use Illuminate\Database\Eloquent\Casts\AsArrayObject; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; @@ -9,4 +10,38 @@ class StsLog extends Model { use HasFactory; protected $table = 'sts_logs'; + + protected $fillable = [ + 'partner', + 'is_outgoing', + 'module', + 'service_name', + 'local_id', + 'partner_id', + 'seq', + 'result', + 'error_info', + 'request_data', + 'request_time', + 'response_data', + 'response_time', + 'is_retry', + 'created_at', + 'updated_at' + ]; + protected $casts = [ + 'error_info' => AsArrayObject::class, + 'request_data' => AsArrayObject::class, + 'response_data' => AsArrayObject::class, + ]; + + const STATUS_SUCCESS = 'success'; + const STATUS_FAILED = 'failed'; + const STATUS_WARNING = 'warning'; + + const PARTNER_INDOKARGO = 'indokargo'; + + // related to indokargo + const MODULE_TV = 'tv'; + const SERVICE_CREATE_TV_ADDRESS = 'create-tv-addrass'; } diff --git a/app/Models/Tv.php b/app/Models/Tv.php index 4af51b5..f116808 100644 --- a/app/Models/Tv.php +++ b/app/Models/Tv.php @@ -4,44 +4,31 @@ namespace App\Models; use App\Helper\JSONResponse; use AWS\CRT\HTTP\Request; +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\Support\Str; class Tv extends Model { use HasFactory; protected $table = 'tvs'; protected $hidden = ['ik_cust_id', 'ik_address_id']; + protected $casts = ['device_info'=>AsArrayObject::class]; //------------------------------------------------------------ - // -- RELATED TO MIGRATION + // -- 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 MIGRATION - //------------------------------------------------------------ - - //------------------------------------------------------------ - // -- RELATED TO DATA FUNCTION - public static function generateRandomCode(): string { - $totalDigit = 6; - $randomDigit = ''; - while(true) { - $randomDigit = strtoupper(Str::random($totalDigit)); - $isExist = Tv::where('code', $randomDigit)->first(); - if(!$isExist) break; - } - return $randomDigit; - } - // -- END RELATED TO DATA FUNCTION + // -- END RELATED TO RELATIONSHIP //------------------------------------------------------------ //------------------------------------------------------------ diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 452e6b6..a8cc324 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -2,6 +2,7 @@ namespace App\Providers; +use Illuminate\Support\Facades\Http; use Illuminate\Support\ServiceProvider; class AppServiceProvider extends ServiceProvider @@ -17,8 +18,15 @@ class AppServiceProvider extends ServiceProvider /** * Bootstrap any application services. */ - public function boot(): void - { - // + public function boot(): void { + Http::macro('indokargo', function() { + $headers = [ + 'oauth' => env('INDOKARGO_API_OAUTH', ''), + 'session' => env('INDOKARGO_API_SESSION_ID', '') + ]; + + return Http::withHeaders($headers) + ->baseUrl(env('INDOKARGO_API_URL', '').'/v1/'); + }); } } diff --git a/routes/api/superadmin.php b/routes/api/superadmin.php index 4f74481..2c383d4 100644 --- a/routes/api/superadmin.php +++ b/routes/api/superadmin.php @@ -1,6 +1,7 @@ group(function() { Route::post('/user-management/delete', 'delete'); }); +Route::controller(NewTvRequestController::class)->group(function() { + Route::post('/tv/new-tv-request', 'init'); + Route::post('/tv/new-tv-request/approve', 'approve'); + Route::post('/tv/new-tv-request/reject', 'reject'); +}); ?> \ No newline at end of file From 4b56d762f65a4180edcdd28d598f2db0f5757fbc Mon Sep 17 00:00:00 2001 From: ricky rx Date: Tue, 28 May 2024 15:58:52 +0700 Subject: [PATCH 05/27] feat: api for mobile > tv > new tv request --- .../Controllers/api/mobile/TvController.php | 23 ++++++++ app/Models/NewTvRequest.php | 58 ++++++++++++------- app/Models/Tv.php | 11 +--- routes/api/mobile.php | 8 ++- 4 files changed, 68 insertions(+), 32 deletions(-) create mode 100644 app/Http/Controllers/api/mobile/TvController.php diff --git a/app/Http/Controllers/api/mobile/TvController.php b/app/Http/Controllers/api/mobile/TvController.php new file mode 100644 index 0000000..6543acb --- /dev/null +++ b/app/Http/Controllers/api/mobile/TvController.php @@ -0,0 +1,23 @@ +validate(['id' => 'nullable|integer']); + + $tv = null; + if($request->id) { $tv = Tv::find($request->id); } + return JSONResponse::Success(['tv' => $tv]); + } +} diff --git a/app/Models/NewTvRequest.php b/app/Models/NewTvRequest.php index aeb1e13..d1305bb 100644 --- a/app/Models/NewTvRequest.php +++ b/app/Models/NewTvRequest.php @@ -61,13 +61,30 @@ class NewTvRequest extends Model { } return $randomDigit; } + + /** + * Rule: + * 1. if it has not been activated for more than the expiry date, deleted it + * 2. if it has been activated & has been responded more than expiry date, delete it + * - case: when has been activated, but intenet connection missing, how can device know + * that the new request tv has been activated? + */ + private static function _getMaxExpiredTime() :Carbon { return Carbon::now()->subHour(); } + public static function deleteExpiredRequests() { + $expiredTime = self::_getMaxExpiredTime()->toDateTimeString(); + NewTvRequest::where(function($q) use ($expiredTime) { + $q->whereNull('activated_at')->where('created_at', '<=', $expiredTime); + })->orWhere(function($q) use ($expiredTime) { + $q->where('responded_at', '<=', $expiredTime ); + })->delete(); + } // -- END RELATED TO DATA FUNCTION //------------------------------------------------------------ //------------------------------------------------------------ // -- RELATED TO FROM REQUEST FUNCTION private static function _checkOrCreateResponse(NewTvRequest $newTvReq) { - if($newTvReq->is_activated) { + if($newTvReq->activated_at) { // make sure waiting tv_device responded before deleted if(empty($newTvReq->responded_at)) { $newTvReq->responded_at = now(); @@ -76,7 +93,6 @@ class NewTvRequest extends Model { return JSONResponse::Success([ 'message' => 'activated', - 'is_active' => true, 'new_tv_request' => $newTvReq, 'tv' => $newTvReq->tv ]); @@ -84,11 +100,28 @@ class NewTvRequest extends Model { return JSONResponse::Success([ 'message' => 'not activate', - 'is_active' => false, 'new_tv_request' => $newTvReq, 'tv' => null ]); } + public static function checkOrCreateFromRequest(Request $request) { + $request->validate([ + 'id' => 'nullable|string|required_with:code|integer', + 'code' => 'nullable|string|required_with:id', + 'device_info' => 'required|array' + ]); + + if(!empty($request->id)) { + $checkTvReq = NewTvRequest::where('code', $request->code)->find($request->id); + if($checkTvReq) return self::_checkOrCreateResponse($checkTvReq); + } + + $newTvReq = new self; + $newTvReq->code = self::_generateRandomCode(); + $newTvReq->device_info = $request->device_info; + $newTvReq->save(); + return self::_checkOrCreateResponse($newTvReq); + } public static function approveFromRequest(Request $request) { $request->validate([ @@ -97,7 +130,7 @@ class NewTvRequest extends Model { ]); $tvRequest = new Request($request->tv); - if($tvRequest->filled('code')) $tvRequest->code = strtoupper($tvRequest->code); + if($tvRequest->code) $tvRequest->merge(['code' => strtoupper($tvRequest->code)]); $tvRequest->validate([ 'code' => 'required|string|unique:App\Models\Tv,code', @@ -161,23 +194,6 @@ class NewTvRequest extends Model { return JSONResponse::Success(['data' => $newTvRequest ]); } - - /** - * Rule: - * 1. if it has not been activated for more than the expiry date, deleted it - * 2. if it has been activated & has been responded more than expiry date, delete it - * - case: when has been activated, but intenet connection missing, how can device know - * that the new request tv has been activated? - */ - private static function _getMaxExpiredTime() :Carbon { return Carbon::now()->subHour(); } - public static function deleteExpiredRequests() { - $expiredTime = self::_getMaxExpiredTime()->toString(); - NewTvRequest::where(function($q) use ($expiredTime) { - $q->whereNull('activated_at')->where('created_at', '<=', $expiredTime); - })->orWhere(function($q) use ($expiredTime) { - $q->where('responded_at', '<=', $expiredTime ); - })->delete(); - } // -- END RELATED TO FROM REQUEST FUNCTION //------------------------------------------------------------ } diff --git a/app/Models/Tv.php b/app/Models/Tv.php index f116808..5eff49a 100644 --- a/app/Models/Tv.php +++ b/app/Models/Tv.php @@ -3,13 +3,13 @@ namespace App\Models; use App\Helper\JSONResponse; -use AWS\CRT\HTTP\Request; 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; class Tv extends Model { use HasFactory; @@ -42,13 +42,4 @@ class Tv extends Model { } // -- END RELATED TO DATA FUNCTION //------------------------------------------------------------ - - - //------------------------------------------------------------ - // -- RELATED TO REQUEST - public static function renewTvInformation(Request $request) { - - } - // -- END RELATED TO REQUEST - //------------------------------------------------------------ } diff --git a/routes/api/mobile.php b/routes/api/mobile.php index 0a5c364..7946ca2 100644 --- a/routes/api/mobile.php +++ b/routes/api/mobile.php @@ -1,10 +1,16 @@ group(function() { Route::post('/check-update/video-update/latest', 'videoUpdateLatest'); Route::post('/check-update/apk-update/latest', 'apkUpdateLatest'); -}) +}); + +Route::controller(TvController::class)->group(function() { + Route::post('/tv/new-request', 'newRequest'); + Route::post('/tv/update-condition', 'updateCondition'); +}); ?> \ No newline at end of file From 5e46b2072099676fe009a4c93d996400ad08ca3d Mon Sep 17 00:00:00 2001 From: ricky rx Date: Thu, 6 Jun 2024 11:17:13 +0700 Subject: [PATCH 06/27] adjust: api for background service --- .../api/mobile/CheckUpdateController.php | 4 ++-- app/Http/Controllers/api/mobile/TvController.php | 15 ++++++++++++--- app/Models/ApkUpdate.php | 6 ++++++ app/Models/VideoUpdate.php | 6 ++++++ 4 files changed, 26 insertions(+), 5 deletions(-) diff --git a/app/Http/Controllers/api/mobile/CheckUpdateController.php b/app/Http/Controllers/api/mobile/CheckUpdateController.php index 73e7c42..e040116 100644 --- a/app/Http/Controllers/api/mobile/CheckUpdateController.php +++ b/app/Http/Controllers/api/mobile/CheckUpdateController.php @@ -10,11 +10,11 @@ use Illuminate\Http\Request; class CheckUpdateController extends Controller { public function videoUpdateLatest(Request $request) { - $lastestVideoUpdate = VideoUpdate::selected()->latest('updated_at')->first(); + $lastestVideoUpdate = VideoUpdate::getLatestSelected(); return JSONResponse::Success(['latestVideoUpload' => $lastestVideoUpdate]); } public function apkUpdateLatest(Request $request) { - $latestApkUpdate = ApkUpdate::latest('version_code')->first(); + $latestApkUpdate = ApkUpdate::getLatest(); return JSONResponse::Success(['latestApkUpdate' => $latestApkUpdate]); } } diff --git a/app/Http/Controllers/api/mobile/TvController.php b/app/Http/Controllers/api/mobile/TvController.php index 6543acb..bc28aa5 100644 --- a/app/Http/Controllers/api/mobile/TvController.php +++ b/app/Http/Controllers/api/mobile/TvController.php @@ -4,20 +4,29 @@ namespace App\Http\Controllers\api\mobile; use App\Helper\JSONResponse; use App\Http\Controllers\Controller; +use App\Models\ApkUpdate; use App\Models\NewTvRequest; use App\Models\Tv; +use App\Models\VideoUpdate; use Illuminate\Http\Request; class TvController extends Controller { public function newRequest(Request $request) { return NewTvRequest::checkOrCreateFromRequest($request); } - + public function updateCondition(Request $request) { $request->validate(['id' => 'nullable|integer']); $tv = null; - if($request->id) { $tv = Tv::find($request->id); } - return JSONResponse::Success(['tv' => $tv]); + $latestApkUpdate = null; + $latestVideoUpdate = null; + if($request->id) { + $tv = Tv::find($request->id); + $latestApkUpdate = ApkUpdate::getLatest(); + $latestVideoUpdate = VideoUpdate::getLatestSelected(); + } + + return JSONResponse::Success(['tv' => $tv, 'latestApkUpdate' => $latestApkUpdate, 'latestVideoUpdate' => $latestVideoUpdate]); } } diff --git a/app/Models/ApkUpdate.php b/app/Models/ApkUpdate.php index da582b0..693d36f 100644 --- a/app/Models/ApkUpdate.php +++ b/app/Models/ApkUpdate.php @@ -34,6 +34,12 @@ class ApkUpdate extends Model { // -- END RELATED TO SCOPE // --------------------------------------------------------------------------------------- + // --------------------------------------------------------------------------------------- + // -- RELATED TO GET DATA + public static function getLatest(){ return self::latest('version_code')->first(); } + // -- END RELATED TO GET DATA + // --------------------------------------------------------------------------------------- + // --------------------------------------------------------------------------------------- // -- RELATED TO MODIFICATION DATA FROM REQUEST public static function upsertFromRequest(Request $request) { diff --git a/app/Models/VideoUpdate.php b/app/Models/VideoUpdate.php index 30b480c..1430bdb 100644 --- a/app/Models/VideoUpdate.php +++ b/app/Models/VideoUpdate.php @@ -34,6 +34,12 @@ class VideoUpdate extends Model { // -- END RELATED TO SCOPE // --------------------------------------------------------------------------------------- + // --------------------------------------------------------------------------------------- + // -- RELATED TO GET DATA + public static function getLatestSelected(){ return self::selected()->latest('updated_at')->first(); } + // -- END RELATED TO GET DATA + // --------------------------------------------------------------------------------------- + // --------------------------------------------------------------------------------------- // -- RELATED TO MODIFICATION DATA FROM REQUEST public static function upsertFromRequest(Request $request) { From cbbe61265877630440d81eb3d82a22b44c23ed43 Mon Sep 17 00:00:00 2001 From: ricky rx Date: Sat, 8 Jun 2024 16:00:26 +0700 Subject: [PATCH 07/27] refactor: TV Controller change update condition to check update --- app/Http/Controllers/api/mobile/TvController.php | 2 +- routes/api/mobile.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/Http/Controllers/api/mobile/TvController.php b/app/Http/Controllers/api/mobile/TvController.php index bc28aa5..5b6e028 100644 --- a/app/Http/Controllers/api/mobile/TvController.php +++ b/app/Http/Controllers/api/mobile/TvController.php @@ -15,7 +15,7 @@ class TvController extends Controller { return NewTvRequest::checkOrCreateFromRequest($request); } - public function updateCondition(Request $request) { + public function checkUpdate(Request $request) { $request->validate(['id' => 'nullable|integer']); $tv = null; diff --git a/routes/api/mobile.php b/routes/api/mobile.php index 7946ca2..20a37c7 100644 --- a/routes/api/mobile.php +++ b/routes/api/mobile.php @@ -11,6 +11,6 @@ Route::controller(CheckUpdateController::class)->group(function() { Route::controller(TvController::class)->group(function() { Route::post('/tv/new-request', 'newRequest'); - Route::post('/tv/update-condition', 'updateCondition'); + Route::post('/tv/check-update', 'checkUpdate'); }); ?> \ No newline at end of file From e2e6c2b6f81e1f4fa8efc128b2ff8677f7232428 Mon Sep 17 00:00:00 2001 From: ricky rx Date: Tue, 11 Jun 2024 10:51:38 +0700 Subject: [PATCH 08/27] feat: video_update with file_size information --- app/Models/VideoUpdate.php | 21 ++++++++++++--- ..._add_col_total_size_kb_to_video_update.php | 26 +++++++++++++++++++ 2 files changed, 44 insertions(+), 3 deletions(-) create mode 100644 database/migrations/2024_06_11_095119_add_col_total_size_kb_to_video_update.php diff --git a/app/Models/VideoUpdate.php b/app/Models/VideoUpdate.php index 1430bdb..fe42082 100644 --- a/app/Models/VideoUpdate.php +++ b/app/Models/VideoUpdate.php @@ -24,7 +24,7 @@ class VideoUpdate extends Model { protected $table = 'video_updates'; protected $hidden = ['file']; - protected $appends = ['file_url']; + protected $appends = ['file_url', 'file_size_mb']; // --------------------------------------------------------------------------------------- // -- RELATED TO SCOPE @@ -40,6 +40,16 @@ class VideoUpdate extends Model { // -- END RELATED TO GET DATA // --------------------------------------------------------------------------------------- + // --------------------------------------------------------------------------------------- + // -- RELATED TO ATTRIBUTE + protected function fileSizeMb(): Attribute { + return Attribute::make( + fn() => $this->file_size_kb != null ? round($this->file_size_kb / 1024, 2) : null + ); + } + // -- END RELATED TO ATTRIBUTE + // --------------------------------------------------------------------------------------- + // --------------------------------------------------------------------------------------- // -- RELATED TO MODIFICATION DATA FROM REQUEST public static function upsertFromRequest(Request $request) { @@ -54,9 +64,13 @@ class VideoUpdate extends Model { $delOldDbFileLocation = ''; $newDbFileLocation = ''; + $newFileSizeKb = 0; try { - // save photo - if($request->file) $newDbFileLocation = self::saveFile($request->file)['db_url']; + // save video to storage & get file size + if($request->file) { + $newDbFileLocation = self::saveFile($request->file)['db_url']; + $newFileSizeKb = round($request->file('file')->getSize() / 1024, 2); + } // try to upsert data DB::beginTransaction(); @@ -68,6 +82,7 @@ class VideoUpdate extends Model { if($newDbFileLocation) { if($videoUpdate->file) $delOldDbFileLocation = $videoUpdate->file; $videoUpdate->file = $newDbFileLocation; + $videoUpdate->file_size_kb = $newFileSizeKb; } $videoUpdate->file_name = $request->file_name; diff --git a/database/migrations/2024_06_11_095119_add_col_total_size_kb_to_video_update.php b/database/migrations/2024_06_11_095119_add_col_total_size_kb_to_video_update.php new file mode 100644 index 0000000..4674d5c --- /dev/null +++ b/database/migrations/2024_06_11_095119_add_col_total_size_kb_to_video_update.php @@ -0,0 +1,26 @@ +double('file_size_kb')->default(0); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void { + Schema::table('video_updates', function(Blueprint $table) { + $table->dropColumn('file_size_kb'); + }); + } +}; From d1c4f395918f9a634abe775af9b3c5dc73e9b213 Mon Sep 17 00:00:00 2001 From: ricky rx Date: Tue, 11 Jun 2024 10:52:07 +0700 Subject: [PATCH 09/27] fix: validation for bool & custom message --- app/Models/User.php | 6 +++--- app/Models/VideoUpdate.php | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/Models/User.php b/app/Models/User.php index cdb2f7e..52e4a00 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -63,10 +63,10 @@ class User extends Authenticatable 'username' => 'required|string', 'email' => 'required|email', 'password' => 'required_without:id|string|min:8', - 'is_active' => 'required_with:id|in:true,false', + 'is_active' => 'required_with:id|boolean', ], [ - 'password' => ['required_with' => 'The password field is required.'], - 'is_active' => ['required_with' => 'The is active field is required.'] + 'password.required_without' => 'The password field is required.', + 'is_active.required_with' => 'The is active field is required.' ]); try { diff --git a/app/Models/VideoUpdate.php b/app/Models/VideoUpdate.php index fe42082..bf8eb74 100644 --- a/app/Models/VideoUpdate.php +++ b/app/Models/VideoUpdate.php @@ -55,7 +55,7 @@ class VideoUpdate extends Model { public static function upsertFromRequest(Request $request) { $request->validate([ 'id' => 'nullable|integer|exists:App\Models\VideoUpdate,id', - 'is_selected' => 'nullable|in:true,false', + 'is_selected' => 'nullable|boolean', 'file' => 'required_without:id|file|' . FileHelper::convertToStrLaraValidation(FileHelper::$allowedVideoExtensions), 'file_name' => 'required|string', ], [ From 7118fea23757c3fb4e78328aad5222665002d36d Mon Sep 17 00:00:00 2001 From: ricky rx Date: Wed, 12 Jun 2024 09:40:54 +0700 Subject: [PATCH 10/27] feat: api for tv > tv page --- app/Helper/STS/Indokargo.php | 104 ++++++++++++++++++ .../api/superadmin/tv/TvController.php | 28 +++++ app/Models/StsLog.php | 4 +- app/Models/Tv.php | 101 +++++++++++++++++ routes/api/superadmin.php | 7 ++ 5 files changed, 243 insertions(+), 1 deletion(-) create mode 100644 app/Http/Controllers/api/superadmin/tv/TvController.php diff --git a/app/Helper/STS/Indokargo.php b/app/Helper/STS/Indokargo.php index 597777f..6662f3b 100644 --- a/app/Helper/STS/Indokargo.php +++ b/app/Helper/STS/Indokargo.php @@ -107,6 +107,110 @@ class Indokargo { throw $th; } } + + public static function updateTVAddress(Request $request, int $tvFk, String $ikAddressId, int $lastSeq = 0) { + $seq = $lastSeq + 1; + + $request->merge(['ik_address_id' => $ikAddressId]); + $stsLog = new StsLog(); + $stsLog->partner = StsLog::PARTNER_INDOKARGO; + $stsLog->is_outgoing = false; + $stsLog->module = StsLog::MODULE_TV; + $stsLog->service_name = StsLog::SERVICE_UPDATE_TV_ADDRESS; + $stsLog->local_id = $tvFk; + $stsLog->partner_id = $ikAddressId; + $stsLog->seq = $seq; + $stsLog->request_data = $request->all(); + $stsLog->request_time = Carbon::now(); + + try { + $request->validate(['code' => 'required|string', 'ik_address_id' => 'required|string']); + $result = Http::indokargo()->post('tv/address/update-tv', $request->all()); + $res = $result->throw()->json(); + + // update stsLog + $stsLog->response_data = $res; + $stsLog->response_time = Carbon::now(); + self::_checkIkResponse($res); + $stsLog->result = StsLog::STATUS_SUCCESS; + $stsLog->save(); + DB::commit(); + + return JSONResponse::Success(['is_warning' => false, 'message' => 'Success To Save Data']); + } catch(ConnectionException $e) { + // if error cause by connection error, try again later + $stsLog->error_info = [ + 'message' => $e->getMessage(), + 'errors' => $e->getTrace() + ]; + $stsLog->result = StsLog::STATUS_FAILED; + $stsLog->is_retry = true; + $stsLog->save(); + return JSONResponse::Success(['is_warning' => true, + 'message' => "Failed to sync data with indokargo data, don't worry, we will sync data later"]); + } catch(\Throwable $th) { + DB::rollBack(); + $stsLog->error_info = [ + 'message' => $th->getMessage(), + 'errors' => $th->getTrace() + ]; + $stsLog->result = StsLog::STATUS_FAILED; + $stsLog->save(); + throw $th; + } + } + + public static function changeStatusAddress(Request $request, int $tvFk, String $ikAddressId, int $lastSeq = 0) { + $seq = $lastSeq + 1; + + $request->merge(['ik_address_id' => $ikAddressId]); + $stsLog = new StsLog(); + $stsLog->partner = StsLog::PARTNER_INDOKARGO; + $stsLog->is_outgoing = false; + $stsLog->module = StsLog::MODULE_TV; + $stsLog->service_name = StsLog::SERVICE_CHANGE_STATUS_TV_ADDRESS; + $stsLog->local_id = $tvFk; + $stsLog->partner_id = $request->ik_address_id; + $stsLog->seq = $seq; + $stsLog->request_data = $request->all(); + $stsLog->request_time = Carbon::now(); + + try { + $request->validate(['ik_address_id' => 'required|string', 'is_active'=> 'required|boolean']); + $result = Http::indokargo()->post('tv/address/change-status', $request->all()); + $res = $result->throw()->json(); + + // update stsLog + $stsLog->response_data = $res; + $stsLog->response_time = Carbon::now(); + self::_checkIkResponse($res); + $stsLog->result = StsLog::STATUS_SUCCESS; + $stsLog->save(); + DB::commit(); + + return JSONResponse::Success(['is_warning' => false, 'message' => 'Success To Change Status']); + } catch(ConnectionException $e) { + // if error cause by connection error, try again later + $stsLog->error_info = [ + 'message' => $e->getMessage(), + 'errors' => $e->getTrace() + ]; + $stsLog->result = StsLog::STATUS_FAILED; + $stsLog->is_retry = true; + $stsLog->save(); + return JSONResponse::Success(['is_warning' => true, + 'message' => "Failed to sync data with indokargo data, don't worry, we will sync data later"]); + } catch(\Throwable $th) { + DB::rollBack(); + $stsLog->error_info = [ + 'message' => $th->getMessage(), + 'errors' => $th->getTrace() + ]; + $stsLog->result = StsLog::STATUS_FAILED; + $stsLog->save(); + throw $th; + } + } } ?> \ No newline at end of file diff --git a/app/Http/Controllers/api/superadmin/tv/TvController.php b/app/Http/Controllers/api/superadmin/tv/TvController.php new file mode 100644 index 0000000..41c3544 --- /dev/null +++ b/app/Http/Controllers/api/superadmin/tv/TvController.php @@ -0,0 +1,28 @@ +validate([ + 'perPage' => 'nullable|integer|min:1', + ...DatabaseHelper::getOrderBysValidations(), + 'search' => DatabaseHelper::getSearchValidation() + ]); + + $newTvRequests = Tv::multiSearch($request->search, ['code']) + ->multiOrderBy($request->orderBys, 'created_at desc') + ->paginate($request->perPage ?? 10); + + return JSONResponse::Success(['data' => $newTvRequests ]); + } + + public function update(Request $request) { return Tv::updateFromRequest($request); } + public function changeStatus(Request $request) { return Tv::changeStatusFromRequest($request); } +} diff --git a/app/Models/StsLog.php b/app/Models/StsLog.php index 458ab7f..26ef4e4 100644 --- a/app/Models/StsLog.php +++ b/app/Models/StsLog.php @@ -43,5 +43,7 @@ class StsLog extends Model { // related to indokargo const MODULE_TV = 'tv'; - const SERVICE_CREATE_TV_ADDRESS = 'create-tv-addrass'; + const SERVICE_CREATE_TV_ADDRESS = 'create-tv-address'; + const SERVICE_UPDATE_TV_ADDRESS = 'update-tv-address'; + const SERVICE_CHANGE_STATUS_TV_ADDRESS = 'change-statustv-address'; } diff --git a/app/Models/Tv.php b/app/Models/Tv.php index 5eff49a..031dd84 100644 --- a/app/Models/Tv.php +++ b/app/Models/Tv.php @@ -3,6 +3,9 @@ namespace App\Models; 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; @@ -10,9 +13,13 @@ 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\Validation\Rule; class Tv extends Model { use HasFactory; + use CanMultiSearch; + use CanMultiOrderBy; protected $table = 'tvs'; protected $hidden = ['ik_cust_id', 'ik_address_id']; @@ -42,4 +49,98 @@ class Tv extends Model { } // -- END RELATED TO DATA FUNCTION //------------------------------------------------------------ + + //------------------------------------------------------------ + // -- RELATED TO REQUEST + public static function modifyAndValidateUpsertRequest(Request $request, $isUpdate = false) { + if($request->code) $request->merge(['code' => strtoupper($request->code)]); + $validationRules = [ + 'code' => ['required','string',Rule::unique('tvs', 'code') + ->when($request->id, function($q, $id) { + $q->whereNot('id', $id); + })], + '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($isUpdate) $validationRules['id'] = 'nullable|integer|exists:App\Models\Tv'; + $request->validate(); + return $request; + } + 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); + })], + '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); + $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(); + + // 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 { + $tv = Tv::findOrFail($request->id); + $tv->is_active = !$tv->is_active; + $tv->save(); + + // 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 REQUES + //------------------------------------------------------------ } diff --git a/routes/api/superadmin.php b/routes/api/superadmin.php index 2c383d4..c9c9565 100644 --- a/routes/api/superadmin.php +++ b/routes/api/superadmin.php @@ -2,6 +2,7 @@ use App\Http\Controllers\api\superadmin\ApkUploadController; use App\Http\Controllers\api\superadmin\tv\NewTvRequestController; +use App\Http\Controllers\api\superadmin\tv\TvController; use App\Http\Controllers\api\superadmin\UserManagementController; use App\Http\Controllers\api\superadmin\VideoUploadController; use Illuminate\Support\Facades\Route; @@ -30,6 +31,12 @@ Route::controller(UserManagementController::class)->group(function() { Route::post('/user-management/delete', 'delete'); }); +Route::controller(TvController::class)->group(function() { + Route::post('/tv/tv', 'init'); + Route::post('/tv/tv/update', 'update'); + Route::post('/tv/tv/change-status', 'changeStatus'); +}); + Route::controller(NewTvRequestController::class)->group(function() { Route::post('/tv/new-tv-request', 'init'); Route::post('/tv/new-tv-request/approve', 'approve'); From d947df81d394b8ac0191f488089f2b96e7e00657 Mon Sep 17 00:00:00 2001 From: ricky rx Date: Wed, 12 Jun 2024 11:03:47 +0700 Subject: [PATCH 11/27] fix: add info last_connected_at & installed_at --- app/Http/Controllers/api/mobile/TvController.php | 2 ++ app/Models/NewTvRequest.php | 2 ++ app/Models/Tv.php | 7 ------- database/migrations/2024_05_18_033105_tv.php | 2 +- 4 files changed, 5 insertions(+), 8 deletions(-) diff --git a/app/Http/Controllers/api/mobile/TvController.php b/app/Http/Controllers/api/mobile/TvController.php index 5b6e028..a5caae8 100644 --- a/app/Http/Controllers/api/mobile/TvController.php +++ b/app/Http/Controllers/api/mobile/TvController.php @@ -23,6 +23,8 @@ class TvController extends Controller { $latestVideoUpdate = null; if($request->id) { $tv = Tv::find($request->id); + $tv->last_connected_at = now(); + $tv->save(); $latestApkUpdate = ApkUpdate::getLatest(); $latestVideoUpdate = VideoUpdate::getLatestSelected(); } diff --git a/app/Models/NewTvRequest.php b/app/Models/NewTvRequest.php index d1305bb..0e30957 100644 --- a/app/Models/NewTvRequest.php +++ b/app/Models/NewTvRequest.php @@ -167,6 +167,8 @@ class NewTvRequest extends Model { $tv->col10 = $tvRequest->col10; $tv->notes = $tvRequest->notes; $tv->device_info = $newTvReq->device_info; + $tv->installed_at = now(); + $tv->last_connected_at = now(); $tv->save(); $newTvReq->activated_at = now(); diff --git a/app/Models/Tv.php b/app/Models/Tv.php index 031dd84..f7824c8 100644 --- a/app/Models/Tv.php +++ b/app/Models/Tv.php @@ -40,13 +40,6 @@ class Tv extends Model { //------------------------------------------------------------ // -- RELATED TO DATA FUNCTION - public static function createAnonymousData() :Tv { - $newTv = new self; - $newTv->code = self::generateRandomCode(); - $newTv->is_active = true; - $newTv->save(); - return $newTv; - } // -- END RELATED TO DATA FUNCTION //------------------------------------------------------------ diff --git a/database/migrations/2024_05_18_033105_tv.php b/database/migrations/2024_05_18_033105_tv.php index cdc5316..3822d0b 100644 --- a/database/migrations/2024_05_18_033105_tv.php +++ b/database/migrations/2024_05_18_033105_tv.php @@ -27,7 +27,7 @@ return new class extends Migration { $table->string('notes', 255)->nullable(); $table->boolean('is_active')->default(true); $table->jsonb('device_info')->nullable(); - $table->timestampTz('installed_at')->nullable(); + $table->timestampTz('installed_at'); $table->timestampTz('last_connected_at')->nullable(); $table->timestampsTz(); }); From 158106c758c2c1764d1fe2a8282715bad1c19aa5 Mon Sep 17 00:00:00 2001 From: ricky rx Date: Wed, 12 Jun 2024 11:55:11 +0700 Subject: [PATCH 12/27] chore: change code for ik http transaction & timeout --- app/Helper/STS/Indokargo.php | 100 +++++++++------------------ app/Providers/AppServiceProvider.php | 2 +- 2 files changed, 33 insertions(+), 69 deletions(-) diff --git a/app/Helper/STS/Indokargo.php b/app/Helper/STS/Indokargo.php index 6662f3b..445b9de 100644 --- a/app/Helper/STS/Indokargo.php +++ b/app/Helper/STS/Indokargo.php @@ -31,6 +31,31 @@ class Indokargo { throw new \Exception("Indokargo status is not valid (ik status = $status)" ); } } + private static function _HttpTransaction(StsLog $stsLog, $callback) { + try { + return $callback(); + } catch(ConnectionException $e) { + // if error cause by connection error, try again later + $stsLog->error_info = [ + 'message' => $e->getMessage(), + 'errors' => $e->getTrace() + ]; + $stsLog->result = StsLog::STATUS_FAILED; + $stsLog->is_retry = true; + $stsLog->save(); + return JSONResponse::Success(['is_warning' => true, + 'message' => "Failed to sync data with indokargo data, don't worry, we will sync data later"]); + } catch(\Throwable $th) { + DB::rollBack(); + $stsLog->error_info = [ + 'message' => $th->getMessage(), + 'errors' => $th->getTrace() + ]; + $stsLog->result = StsLog::STATUS_FAILED; + $stsLog->save(); + throw $th; + } + } //-- END UTILITIES //------------------------------------------------------------------------ @@ -53,7 +78,7 @@ class Indokargo { $stsLog->request_data = $request->all(); $stsLog->request_time = Carbon::now(); - try { + return self::_HttpTransaction($stsLog, function() use($tvFk, $request, $stsLog) { $request->validate(['code' => 'required|string']); $result = Http::indokargo()->post('tv/address/create', $request->all()); @@ -85,27 +110,7 @@ class Indokargo { $stsLog->save(); DB::commit(); return JSONResponse::Success(['is_warning' => false, 'message' => 'Success To Save Data']); - } catch(ConnectionException $e) { - // if error cause by connection error, try again later - $stsLog->error_info = [ - 'message' => $e->getMessage(), - 'errors' => $e->getTrace() - ]; - $stsLog->result = StsLog::STATUS_FAILED; - $stsLog->is_retry = true; - $stsLog->save(); - return JSONResponse::Success(['is_warning' => true, - 'message' => "Failed to sync data with indokargo data, don't worry, we will sync data later"]); - } catch(\Throwable $th) { - DB::rollBack(); - $stsLog->error_info = [ - 'message' => $th->getMessage(), - 'errors' => $th->getTrace() - ]; - $stsLog->result = StsLog::STATUS_FAILED; - $stsLog->save(); - throw $th; - } + }); } public static function updateTVAddress(Request $request, int $tvFk, String $ikAddressId, int $lastSeq = 0) { @@ -123,7 +128,7 @@ class Indokargo { $stsLog->request_data = $request->all(); $stsLog->request_time = Carbon::now(); - try { + return self::_HttpTransaction($stsLog, function() use($stsLog, $request) { $request->validate(['code' => 'required|string', 'ik_address_id' => 'required|string']); $result = Http::indokargo()->post('tv/address/update-tv', $request->all()); $res = $result->throw()->json(); @@ -135,29 +140,8 @@ class Indokargo { $stsLog->result = StsLog::STATUS_SUCCESS; $stsLog->save(); DB::commit(); - - return JSONResponse::Success(['is_warning' => false, 'message' => 'Success To Save Data']); - } catch(ConnectionException $e) { - // if error cause by connection error, try again later - $stsLog->error_info = [ - 'message' => $e->getMessage(), - 'errors' => $e->getTrace() - ]; - $stsLog->result = StsLog::STATUS_FAILED; - $stsLog->is_retry = true; - $stsLog->save(); - return JSONResponse::Success(['is_warning' => true, - 'message' => "Failed to sync data with indokargo data, don't worry, we will sync data later"]); - } catch(\Throwable $th) { - DB::rollBack(); - $stsLog->error_info = [ - 'message' => $th->getMessage(), - 'errors' => $th->getTrace() - ]; - $stsLog->result = StsLog::STATUS_FAILED; - $stsLog->save(); - throw $th; - } + return JSONResponse::Success(['message' => "Success to save data"]); + }); } public static function changeStatusAddress(Request $request, int $tvFk, String $ikAddressId, int $lastSeq = 0) { @@ -175,7 +159,7 @@ class Indokargo { $stsLog->request_data = $request->all(); $stsLog->request_time = Carbon::now(); - try { + return self::_HttpTransaction($stsLog, function() use($stsLog, $request) { $request->validate(['ik_address_id' => 'required|string', 'is_active'=> 'required|boolean']); $result = Http::indokargo()->post('tv/address/change-status', $request->all()); $res = $result->throw()->json(); @@ -189,27 +173,7 @@ class Indokargo { DB::commit(); return JSONResponse::Success(['is_warning' => false, 'message' => 'Success To Change Status']); - } catch(ConnectionException $e) { - // if error cause by connection error, try again later - $stsLog->error_info = [ - 'message' => $e->getMessage(), - 'errors' => $e->getTrace() - ]; - $stsLog->result = StsLog::STATUS_FAILED; - $stsLog->is_retry = true; - $stsLog->save(); - return JSONResponse::Success(['is_warning' => true, - 'message' => "Failed to sync data with indokargo data, don't worry, we will sync data later"]); - } catch(\Throwable $th) { - DB::rollBack(); - $stsLog->error_info = [ - 'message' => $th->getMessage(), - 'errors' => $th->getTrace() - ]; - $stsLog->result = StsLog::STATUS_FAILED; - $stsLog->save(); - throw $th; - } + }); } } diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index a8cc324..0f630e2 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -25,7 +25,7 @@ class AppServiceProvider extends ServiceProvider 'session' => env('INDOKARGO_API_SESSION_ID', '') ]; - return Http::withHeaders($headers) + return Http::withHeaders($headers)->timeout(120) ->baseUrl(env('INDOKARGO_API_URL', '').'/v1/'); }); } From 2c1a4159790123ad4b559de852ae52b209f0a9d7 Mon Sep 17 00:00:00 2001 From: ricky rx Date: Wed, 12 Jun 2024 14:01:31 +0700 Subject: [PATCH 13/27] feat: sts reschedule --- app/Console/Commands/StsReschedule.php | 86 ++++++++++++++++++++++++++ app/Helper/STS/Indokargo.php | 6 +- 2 files changed, 89 insertions(+), 3 deletions(-) create mode 100644 app/Console/Commands/StsReschedule.php diff --git a/app/Console/Commands/StsReschedule.php b/app/Console/Commands/StsReschedule.php new file mode 100644 index 0000000..54fed10 --- /dev/null +++ b/app/Console/Commands/StsReschedule.php @@ -0,0 +1,86 @@ +info('---------------------------'); + $this->info('reschedule sts:' . Carbon::now()->toDateTimeString()); + $this->info('---------------------------'); + + // get reschedu;e sts logs + $success = 0; + $failed = 0; + $rescheduleStsLogs = StsLog::where(['is_retry' => true])->get(); + foreach($rescheduleStsLogs as $rescheduleStsLog) { + try { + $partner = $rescheduleStsLog->partner; + $module = $rescheduleStsLog->module; + $serviceName = $rescheduleStsLog->service_name; + $request = new Request((array) $rescheduleStsLog->request_data ?? []); + $localId = $rescheduleStsLog->local_id; + $partnerId = $rescheduleStsLog->partner_id; + $lastSeq = $rescheduleStsLog->seq; + + if($partner == StsLog::PARTNER_INDOKARGO) { + if($module == StsLog::MODULE_TV) { + if($serviceName == StsLog::SERVICE_CREATE_TV_ADDRESS) { + Indokargo::createTVAddress($request, $localId, $lastSeq); + } else if($serviceName == StsLog::SERVICE_UPDATE_TV_ADDRESS) { + Indokargo::updateTVAddress($request, $localId, $partnerId, $lastSeq); + } else if($serviceName == StsLog::SERVICE_CHANGE_STATUS_TV_ADDRESS) { + Indokargo::changeStatusAddress($request, $localId, $partnerId, $lastSeq); + } else { + throw new \Exception("Service name '$partner' > '$module' > '$serviceName' not found service"); + } + } else { + throw new \Exception("Module name '$partner' > '$module' not found service"); + } + } else { + throw new \Exception("Partner name '$partner' not found service"); + } + $success++; + } catch(\Throwable $th) { + $failed++; + $this->info("stsLog id: ". $rescheduleStsLog->id . " => " . $th->getMessage()); + } + } + + // save all retry request is retry false + $rescheduleStsLogIds = $rescheduleStsLogs->pluck('id')->toArray(); + StsLog::whereIn('id', $rescheduleStsLogIds)->update(['is_retry' => false]); + + $this->info('---------------------------'); + $this->info("result: $success success, $failed failed" ); + $this->info("Sleep in $sleep seconds" ); + $this->info('---------------------------'); + sleep($sleep); + } + } +} diff --git a/app/Helper/STS/Indokargo.php b/app/Helper/STS/Indokargo.php index 445b9de..563e95f 100644 --- a/app/Helper/STS/Indokargo.php +++ b/app/Helper/STS/Indokargo.php @@ -69,7 +69,7 @@ class Indokargo { */ $stsLog = new StsLog(); $stsLog->partner = StsLog::PARTNER_INDOKARGO; - $stsLog->is_outgoing = false; + $stsLog->is_outgoing = true; $stsLog->module = StsLog::MODULE_TV; $stsLog->service_name = StsLog::SERVICE_CREATE_TV_ADDRESS; $stsLog->local_id = $tvFk; @@ -119,7 +119,7 @@ class Indokargo { $request->merge(['ik_address_id' => $ikAddressId]); $stsLog = new StsLog(); $stsLog->partner = StsLog::PARTNER_INDOKARGO; - $stsLog->is_outgoing = false; + $stsLog->is_outgoing = true; $stsLog->module = StsLog::MODULE_TV; $stsLog->service_name = StsLog::SERVICE_UPDATE_TV_ADDRESS; $stsLog->local_id = $tvFk; @@ -150,7 +150,7 @@ class Indokargo { $request->merge(['ik_address_id' => $ikAddressId]); $stsLog = new StsLog(); $stsLog->partner = StsLog::PARTNER_INDOKARGO; - $stsLog->is_outgoing = false; + $stsLog->is_outgoing = true; $stsLog->module = StsLog::MODULE_TV; $stsLog->service_name = StsLog::SERVICE_CHANGE_STATUS_TV_ADDRESS; $stsLog->local_id = $tvFk; From b6e3edecde34ff537637399b105fb1323c973ba2 Mon Sep 17 00:00:00 2001 From: ricky rx Date: Wed, 12 Jun 2024 15:04:16 +0700 Subject: [PATCH 14/27] refactor: change mobile-token to mobile-auth-token --- .env.example | 2 +- app/Http/Middleware/MobileMiddleware.php | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/.env.example b/.env.example index ff84703..5d6a826 100644 --- a/.env.example +++ b/.env.example @@ -3,7 +3,7 @@ APP_ENV=local APP_KEY= APP_DEBUG=true APP_URL=http://localhost -MOBILE_TOKEN=Gaobd5OKPdGARLGTD03vSFStrADAxmQ9 +MOBILE_AUTH_TOKEN=Gaobd5OKPdGARLGTD03vSFStrADAxmQ9 # for CORS SANCTUM_STATEFUL_DOMAINS=[http://localhost:3000] diff --git a/app/Http/Middleware/MobileMiddleware.php b/app/Http/Middleware/MobileMiddleware.php index faddb3b..6ef57ff 100644 --- a/app/Http/Middleware/MobileMiddleware.php +++ b/app/Http/Middleware/MobileMiddleware.php @@ -14,11 +14,12 @@ class MobileMiddleware * @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next */ public function handle(Request $request, Closure $next): Response { - $serverMobileToken = env('MOBILE_TOKEN', ''); - if(!$serverMobileToken) throw new \Exception('Mobile token in server is not found'); + $serverMobileAuthToken = env('MOBILE_AUTH_TOKEN', ''); + if(!$serverMobileAuthToken) throw new \Exception('Mobile Auth token in server is not found'); - $clientMobileToken = $request->header('mobile-token', ''); - if($serverMobileToken != $clientMobileToken) throw new \Exception('Invalid Mobile Token'); + $clientMobileAuthToken = $request->header('mobile-token', ''); + if(!$clientMobileAuthToken) $clientMobileAuthToken = $request->header('mobile-auth-token', ''); + if($serverMobileAuthToken != $clientMobileAuthToken) throw new \Exception('Invalid Mobile Auth Token'); return $next($request); } } From 363f11136ebacae710c225fc684a5a5fb60c7180 Mon Sep 17 00:00:00 2001 From: ricky rx Date: Fri, 14 Jun 2024 16:59:56 +0700 Subject: [PATCH 15/27] chore: remove indokargo resync data feature --- app/Models/NewTvRequest.php | 11 +++++---- app/Models/Tv.php | 46 +++++++++++-------------------------- 2 files changed, 21 insertions(+), 36 deletions(-) diff --git a/app/Models/NewTvRequest.php b/app/Models/NewTvRequest.php index 0e30957..3422543 100644 --- a/app/Models/NewTvRequest.php +++ b/app/Models/NewTvRequest.php @@ -174,11 +174,14 @@ class NewTvRequest extends Model { $newTvReq->activated_at = now(); $newTvReq->tv_fk = $tv->id; $newTvReq->save(); - - // try to sys_to_sys with indokargo - $jsonResponse = Indokargo::createTVAddress($tvRequest, $tv->id); DB::commit(); - return $jsonResponse; + return JSONResponse::Success(['message'=>'Success to update tv data']); + + // TODO: waiting execution until update from ops + // // try to sys_to_sys with indokargo + // DB::commit(); + // $jsonResponse = Indokargo::createTVAddress($tvRequest, $tv->id); + // return $jsonResponse; } catch(\Throwable $th) { DB::rollback(); throw $th; diff --git a/app/Models/Tv.php b/app/Models/Tv.php index f7824c8..bd07db1 100644 --- a/app/Models/Tv.php +++ b/app/Models/Tv.php @@ -45,29 +45,6 @@ class Tv extends Model { //------------------------------------------------------------ // -- RELATED TO REQUEST - public static function modifyAndValidateUpsertRequest(Request $request, $isUpdate = false) { - if($request->code) $request->merge(['code' => strtoupper($request->code)]); - $validationRules = [ - 'code' => ['required','string',Rule::unique('tvs', 'code') - ->when($request->id, function($q, $id) { - $q->whereNot('id', $id); - })], - '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($isUpdate) $validationRules['id'] = 'nullable|integer|exists:App\Models\Tv'; - $request->validate(); - return $request; - } public static function updateFromRequest(Request $request) { if($request->code) $request->merge(['code' => strtoupper($request->code)]); $request->validate([ @@ -106,10 +83,13 @@ class Tv extends Model { $tv->notes = $request->notes; $tv->update(); - // try to sys_to_sys with indokargo - $jsonResponse = Indokargo::updateTVAddress($request, $tv->id, $tv->ik_address_id); - DB::commit(); - return $jsonResponse; + return JSONResponse::Success(['message'=>'Success to update tv data']); + + // TODO: waiting from ops workflow + // // 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; @@ -123,12 +103,14 @@ class Tv extends Model { $tv = Tv::findOrFail($request->id); $tv->is_active = !$tv->is_active; $tv->save(); + return JSONResponse::Success(['message'=>'Success to change tv status']); - // 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; + // TODO: waiting from ops workflow + // // 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; From 9ae7e9f7b3be430a318db8ee7287d9b06f46d05f Mon Sep 17 00:00:00 2001 From: ricky rx Date: Fri, 14 Jun 2024 17:33:51 +0700 Subject: [PATCH 16/27] feat: add version_code & name on tv --- app/Http/Controllers/api/mobile/TvController.php | 6 +++++- database/migrations/2024_05_18_033105_tv.php | 2 ++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/app/Http/Controllers/api/mobile/TvController.php b/app/Http/Controllers/api/mobile/TvController.php index a5caae8..721e46c 100644 --- a/app/Http/Controllers/api/mobile/TvController.php +++ b/app/Http/Controllers/api/mobile/TvController.php @@ -16,7 +16,9 @@ class TvController extends Controller { } public function checkUpdate(Request $request) { - $request->validate(['id' => 'nullable|integer']); + $request->validate(['id' => 'nullable|integer', + 'version_code' => 'required|integer', + 'version_name' => 'required|string']); $tv = null; $latestApkUpdate = null; @@ -24,6 +26,8 @@ class TvController extends Controller { if($request->id) { $tv = Tv::find($request->id); $tv->last_connected_at = now(); + $tv->version_code = $request->version_code; + $tv->version_name = $request->version_name; $tv->save(); $latestApkUpdate = ApkUpdate::getLatest(); $latestVideoUpdate = VideoUpdate::getLatestSelected(); diff --git a/database/migrations/2024_05_18_033105_tv.php b/database/migrations/2024_05_18_033105_tv.php index 3822d0b..b3f1eb6 100644 --- a/database/migrations/2024_05_18_033105_tv.php +++ b/database/migrations/2024_05_18_033105_tv.php @@ -14,6 +14,8 @@ return new class extends Migration { $table->foreignId('outlet_fk')->nullable(); $table->string('code', 50)->unique(); $table->string('ik_address_id', 255)->nullable(); + $table->integer('version_code')->nullable(); + $table->integer('version_name')->nullable(); $table->string('col1', 255)->nullable(); $table->string('col2', 255)->nullable(); $table->string('col3', 255)->nullable(); From d50cde11892f8edb2fdf29bd773e298c07584052 Mon Sep 17 00:00:00 2001 From: ricky rx Date: Fri, 14 Jun 2024 17:38:48 +0700 Subject: [PATCH 17/27] fix: forget db commit --- app/Models/Tv.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/Models/Tv.php b/app/Models/Tv.php index bd07db1..1a55959 100644 --- a/app/Models/Tv.php +++ b/app/Models/Tv.php @@ -82,7 +82,7 @@ class Tv extends Model { $tv->col10 = $request->col10; $tv->notes = $request->notes; $tv->update(); - + DB::commit(); return JSONResponse::Success(['message'=>'Success to update tv data']); // TODO: waiting from ops workflow @@ -103,6 +103,7 @@ class Tv extends Model { $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 From eee7c263ab0f989b82199c8f904fa3e1070c3b3c Mon Sep 17 00:00:00 2001 From: ricky rx Date: Sat, 15 Jun 2024 13:46:12 +0700 Subject: [PATCH 18/27] chore: TV > add col company name, address, street address --- app/Models/NewTvRequest.php | 6 ++++++ app/Models/Tv.php | 6 ++++++ database/migrations/2024_05_18_033105_tv.php | 5 ++++- 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/app/Models/NewTvRequest.php b/app/Models/NewTvRequest.php index 3422543..7daaab4 100644 --- a/app/Models/NewTvRequest.php +++ b/app/Models/NewTvRequest.php @@ -134,6 +134,9 @@ class NewTvRequest extends Model { $tvRequest->validate([ 'code' => 'required|string|unique:App\Models\Tv,code', + 'company_name' => 'nullable|string', + 'address' => 'nullable|string', + 'street_address' => 'nullable|string', 'col1' => 'nullable|string', 'col2' => 'nullable|string', 'col3' => 'nullable|string', @@ -155,6 +158,9 @@ class NewTvRequest extends Model { DB::beginTransaction(); $tv = new Tv(); $tv->code = $tvRequest->code; + $tv->company_name = $tvRequest->company_name; + $tv->address = $tvRequest->address; + $tv->street_address = $tvRequest->street_address; $tv->col1 = $tvRequest->col1; $tv->col2 = $tvRequest->col2; $tv->col3 = $tvRequest->col3; diff --git a/app/Models/Tv.php b/app/Models/Tv.php index 1a55959..1ca1128 100644 --- a/app/Models/Tv.php +++ b/app/Models/Tv.php @@ -53,6 +53,9 @@ class Tv extends Model { ->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', @@ -69,6 +72,9 @@ class Tv extends Model { try { DB::beginTransaction(); $tv = TV::findOrFail($request->id); + $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; diff --git a/database/migrations/2024_05_18_033105_tv.php b/database/migrations/2024_05_18_033105_tv.php index b3f1eb6..0803d30 100644 --- a/database/migrations/2024_05_18_033105_tv.php +++ b/database/migrations/2024_05_18_033105_tv.php @@ -15,7 +15,10 @@ return new class extends Migration { $table->string('code', 50)->unique(); $table->string('ik_address_id', 255)->nullable(); $table->integer('version_code')->nullable(); - $table->integer('version_name')->nullable(); + $table->string('version_name')->nullable(); + $table->string('company_name')->nullable(); + $table->string('address')->nullable(); + $table->string('street_address')->nullable(); $table->string('col1', 255)->nullable(); $table->string('col2', 255)->nullable(); $table->string('col3', 255)->nullable(); From 52b7e914053c762de3323cdf7a9b6a66970ec01d Mon Sep 17 00:00:00 2001 From: ricky rx Date: Sat, 15 Jun 2024 14:00:53 +0700 Subject: [PATCH 19/27] feat: TV code with autogenerate code --- app/Models/NewTvRequest.php | 3 +-- app/Models/Tv.php | 17 +++++++++++++++++ database/migrations/2024_05_18_033105_tv.php | 1 - 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/app/Models/NewTvRequest.php b/app/Models/NewTvRequest.php index 7daaab4..92ec5f7 100644 --- a/app/Models/NewTvRequest.php +++ b/app/Models/NewTvRequest.php @@ -133,7 +133,6 @@ class NewTvRequest extends Model { if($tvRequest->code) $tvRequest->merge(['code' => strtoupper($tvRequest->code)]); $tvRequest->validate([ - 'code' => 'required|string|unique:App\Models\Tv,code', 'company_name' => 'nullable|string', 'address' => 'nullable|string', 'street_address' => 'nullable|string', @@ -157,7 +156,7 @@ class NewTvRequest extends Model { try { DB::beginTransaction(); $tv = new Tv(); - $tv->code = $tvRequest->code; + $tv->code = TV::generateUniqueCode(); $tv->company_name = $tvRequest->company_name; $tv->address = $tvRequest->address; $tv->street_address = $tvRequest->street_address; diff --git a/app/Models/Tv.php b/app/Models/Tv.php index 1ca1128..a71c664 100644 --- a/app/Models/Tv.php +++ b/app/Models/Tv.php @@ -15,6 +15,7 @@ use Illuminate\Database\Eloquent\Relations\HasOne; use Illuminate\Http\Request; use Illuminate\Support\Facades\DB; use Illuminate\Validation\Rule; +use Illuminate\Support\Str; class Tv extends Model { use HasFactory; @@ -40,6 +41,22 @@ class Tv extends Model { //------------------------------------------------------------ // -- RELATED TO DATA FUNCTION + public static function generateUniqueCode() { + // init + $totalSuffixDigit = 5; + date_default_timezone_set('Asia/Jakarta'); + $prefixCode = 'TV' . date('Ym'); + + // try to get unique suffix + $uniqueCode = ''; + while(true) { + $suffixCode = strtoupper(Str::random($totalSuffixDigit)); + $uniqueCode = $prefixCode . $suffixCode; + $isUnique = TV::where('code', $uniqueCode)->first(); + if(!$isUnique) break; + } + return $uniqueCode; + } // -- END RELATED TO DATA FUNCTION //------------------------------------------------------------ diff --git a/database/migrations/2024_05_18_033105_tv.php b/database/migrations/2024_05_18_033105_tv.php index 0803d30..e9e9750 100644 --- a/database/migrations/2024_05_18_033105_tv.php +++ b/database/migrations/2024_05_18_033105_tv.php @@ -11,7 +11,6 @@ return new class extends Migration { public function up(): void { Schema::create('tvs', function (Blueprint $table) { $table->id(); - $table->foreignId('outlet_fk')->nullable(); $table->string('code', 50)->unique(); $table->string('ik_address_id', 255)->nullable(); $table->integer('version_code')->nullable(); From 22f99b29fa976d650682ae765f561ed77fc92783 Mon Sep 17 00:00:00 2001 From: ricky rx Date: Tue, 18 Jun 2024 14:59:34 +0700 Subject: [PATCH 20/27] refactor: add prefix apk_ tv>version_code & tv_version_name --- app/Http/Controllers/api/mobile/TvController.php | 8 ++++---- database/migrations/2024_05_18_033105_tv.php | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/Http/Controllers/api/mobile/TvController.php b/app/Http/Controllers/api/mobile/TvController.php index 721e46c..093fde4 100644 --- a/app/Http/Controllers/api/mobile/TvController.php +++ b/app/Http/Controllers/api/mobile/TvController.php @@ -17,8 +17,8 @@ class TvController extends Controller { public function checkUpdate(Request $request) { $request->validate(['id' => 'nullable|integer', - 'version_code' => 'required|integer', - 'version_name' => 'required|string']); + 'apk_version_code' => 'required|integer', + 'apk_version_name' => 'required|string']); $tv = null; $latestApkUpdate = null; @@ -26,8 +26,8 @@ class TvController extends Controller { if($request->id) { $tv = Tv::find($request->id); $tv->last_connected_at = now(); - $tv->version_code = $request->version_code; - $tv->version_name = $request->version_name; + $tv->apk_version_code = $request->apk_version_code; + $tv->apk_version_name = $request->apk_version_name; $tv->save(); $latestApkUpdate = ApkUpdate::getLatest(); $latestVideoUpdate = VideoUpdate::getLatestSelected(); diff --git a/database/migrations/2024_05_18_033105_tv.php b/database/migrations/2024_05_18_033105_tv.php index e9e9750..31cbfcb 100644 --- a/database/migrations/2024_05_18_033105_tv.php +++ b/database/migrations/2024_05_18_033105_tv.php @@ -13,8 +13,8 @@ return new class extends Migration { $table->id(); $table->string('code', 50)->unique(); $table->string('ik_address_id', 255)->nullable(); - $table->integer('version_code')->nullable(); - $table->string('version_name')->nullable(); + $table->integer('apk_version_code')->nullable(); + $table->string('apk_version_name')->nullable(); $table->string('company_name')->nullable(); $table->string('address')->nullable(); $table->string('street_address')->nullable(); From b82fb64dd3cfb53a0b3a3ce3934ac6071b44fc0e Mon Sep 17 00:00:00 2001 From: ricky rx Date: Tue, 18 Jun 2024 15:36:00 +0700 Subject: [PATCH 21/27] feat: add col version_name to app_update --- app/Models/ApkUpdate.php | 2 ++ ...619_add_col_version_name_to_apk_update.php | 28 +++++++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 database/migrations/2024_06_18_152619_add_col_version_name_to_apk_update.php diff --git a/app/Models/ApkUpdate.php b/app/Models/ApkUpdate.php index 693d36f..ee8d556 100644 --- a/app/Models/ApkUpdate.php +++ b/app/Models/ApkUpdate.php @@ -48,6 +48,7 @@ class ApkUpdate extends Model { 'name' => 'required|string', 'file' => 'required_without:id|file|' . FileHelper::convertToStrLaraValidation(FileHelper::$allowedApkExtensions), 'version_code' => 'required|integer|min:1', + 'version_name' => 'required|string', 'change_note' => 'nullable|string' ], [ 'file' => ['required_without' => 'The file field is required.'] @@ -72,6 +73,7 @@ class ApkUpdate extends Model { } $apkUpdate->name = $request->name; $apkUpdate->version_code = $request->version_code; + $apkUpdate->version_name = $request->version_name; $apkUpdate->change_note = $request->change_note; $apkUpdate->save(); diff --git a/database/migrations/2024_06_18_152619_add_col_version_name_to_apk_update.php b/database/migrations/2024_06_18_152619_add_col_version_name_to_apk_update.php new file mode 100644 index 0000000..237eea8 --- /dev/null +++ b/database/migrations/2024_06_18_152619_add_col_version_name_to_apk_update.php @@ -0,0 +1,28 @@ +string('version_name', 50)->nullable(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('apk_updates', function (Blueprint $table) { + $table->dropColumn('version_name'); + }); + } +}; From 56384c1fa9f4d62e2e1d43dce5a268b442d85550 Mon Sep 17 00:00:00 2001 From: ricky rx Date: Fri, 21 Jun 2024 16:43:56 +0700 Subject: [PATCH 22/27] feat: tv with import export excel --- app/Helper/DatabaseHelper.php | 8 + app/Helper/Frontend/ApiUtilities.php | 48 ++++ .../api/superadmin/tv/TvController.php | 19 +- app/Models/ApkUpdate.php | 2 +- app/Models/Tv.php | 231 +++++++++++++++++- app/Models/TvLog.php | 33 +++ database/migrations/2024_05_18_033105_tv.php | 10 + routes/api/superadmin.php | 1 + 8 files changed, 336 insertions(+), 16 deletions(-) create mode 100644 app/Helper/Frontend/ApiUtilities.php create mode 100644 app/Models/TvLog.php diff --git a/app/Helper/DatabaseHelper.php b/app/Helper/DatabaseHelper.php index 9c2d82e..7145de8 100644 --- a/app/Helper/DatabaseHelper.php +++ b/app/Helper/DatabaseHelper.php @@ -25,5 +25,13 @@ ]; } public static function getSearchValidation() { return 'nullable|string'; } + + public static function compileDirtyEloquentToArrMessage($model) { + $dirties = $model->getDirty(); + foreach($dirties as $key => $dirty) { + $dirties[$key] = $model->getOriginal($key) . ' => ' . $dirty; + } + return $dirties; + } } ?> \ No newline at end of file diff --git a/app/Helper/Frontend/ApiUtilities.php b/app/Helper/Frontend/ApiUtilities.php new file mode 100644 index 0000000..6f70f42 --- /dev/null +++ b/app/Helper/Frontend/ApiUtilities.php @@ -0,0 +1,48 @@ +validate(['a' => 'nullable|string']); + + switch($request->a) { + case 'excelTemplate': + return Tv::getExcelTemplate(); + break; + case 'validateData': + $tvCodes = $request->tvCodes ?? []; + $tvs = $request->tvs ?? []; + $oValidation = TV::validateExcel($tvs, $tvCodes); + return JSONResponse::Success(['oValidation' => $oValidation]); + break; + case 'uploadExcel': + $tvCodes = $request->tvCodes ?? []; + $tvs = $request->tvs ?? []; + $oValidation = TV::validateExcel($tvs, $tvCodes); + $result = TV::uploadExcel($tvs, $oValidation); + return JSONResponse::Success($result); + break; + case 'exportData': + return Tv::getExportData($request); + break; + case 'excelDetail': + return Tv::getExcelDetail($request); + break; + + } + throw new \Exception('Invalid Request Command'); + } +} +?> \ No newline at end of file diff --git a/app/Http/Controllers/api/superadmin/tv/TvController.php b/app/Http/Controllers/api/superadmin/tv/TvController.php index 41c3544..5ddb537 100644 --- a/app/Http/Controllers/api/superadmin/tv/TvController.php +++ b/app/Http/Controllers/api/superadmin/tv/TvController.php @@ -3,8 +3,10 @@ namespace App\Http\Controllers\api\superadmin\tv; use App\Helper\DatabaseHelper; +use App\Helper\Frontend\ApiUtilities; use App\Helper\JSONResponse; use App\Http\Controllers\Controller; +use App\Models\ApkUpdate; use App\Models\Tv; use Illuminate\Http\Request; @@ -12,17 +14,20 @@ class TvController extends Controller { public function init(Request $request) { $request->validate([ 'perPage' => 'nullable|integer|min:1', - ...DatabaseHelper::getOrderBysValidations(), - 'search' => DatabaseHelper::getSearchValidation() + 'isFirstTime' => 'nullable|boolean', ]); - $newTvRequests = Tv::multiSearch($request->search, ['code']) - ->multiOrderBy($request->orderBys, 'created_at desc') - ->paginate($request->perPage ?? 10); - - return JSONResponse::Success(['data' => $newTvRequests ]); + $additionalData = []; + if($request->isFirstTime) { + $additionalData['apkUpdates'] = ApkUpdate::select('version_code', 'version_name') + ->orderBy('version_code', 'desc') + ->get(); + } + $newTvRequests = TV::validateAndGetEloquentFromRequest($request)->paginate($request->perPage ?? 10); + return JSONResponse::Success(['data' => $newTvRequests, ...$additionalData ]); } + public function excel(Request $request) { return ApiUtilities::tvExcel($request); } public function update(Request $request) { return Tv::updateFromRequest($request); } public function changeStatus(Request $request) { return Tv::changeStatusFromRequest($request); } } diff --git a/app/Models/ApkUpdate.php b/app/Models/ApkUpdate.php index ee8d556..05cdf84 100644 --- a/app/Models/ApkUpdate.php +++ b/app/Models/ApkUpdate.php @@ -47,7 +47,7 @@ class ApkUpdate extends Model { 'id' => 'nullable|integer|exists:App\Models\ApkUpdate,id', 'name' => 'required|string', 'file' => 'required_without:id|file|' . FileHelper::convertToStrLaraValidation(FileHelper::$allowedApkExtensions), - 'version_code' => 'required|integer|min:1', + 'version_code' => 'required|integer|min:1|unique:apk_updates,version_code', 'version_name' => 'required|string', 'change_note' => 'nullable|string' ], [ diff --git a/app/Models/Tv.php b/app/Models/Tv.php index a71c664..61d8bbf 100644 --- a/app/Models/Tv.php +++ b/app/Models/Tv.php @@ -2,6 +2,7 @@ namespace App\Models; +use App\Helper\DatabaseHelper; use App\Helper\JSONResponse; use App\Helper\STS\Indokargo; use App\Helper\Traits\Models\CanMultiOrderBy; @@ -14,8 +15,10 @@ 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; @@ -43,14 +46,14 @@ class Tv extends Model { // -- RELATED TO DATA FUNCTION public static function generateUniqueCode() { // init - $totalSuffixDigit = 5; + $countSuffixDigit = 5; date_default_timezone_set('Asia/Jakarta'); $prefixCode = 'TV' . date('Ym'); // try to get unique suffix $uniqueCode = ''; while(true) { - $suffixCode = strtoupper(Str::random($totalSuffixDigit)); + $suffixCode = strtoupper(Str::random($countSuffixDigit)); $uniqueCode = $prefixCode . $suffixCode; $isUnique = TV::where('code', $uniqueCode)->first(); if(!$isUnique) break; @@ -62,14 +65,39 @@ class Tv extends Model { //------------------------------------------------------------ // -- 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); - })], + ->when($request->id, function($q, $id) { $q->whereNot('id', $id);})], 'company_name' => 'nullable|string', 'address' => 'nullable|string', 'street_address' => 'nullable|string', @@ -108,7 +136,7 @@ class Tv extends Model { DB::commit(); return JSONResponse::Success(['message'=>'Success to update tv data']); - // TODO: waiting from ops workflow + // 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(); @@ -129,7 +157,7 @@ class Tv extends Model { DB::commit(); return JSONResponse::Success(['message'=>'Success to change tv status']); - // TODO: waiting from ops workflow + // 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); @@ -140,6 +168,193 @@ class Tv extends Model { throw $th; } } - // -- END RELATED TO REQUES + // -- 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) { + $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::saveHistory(TvLog::TYPE_CREATE, $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 //------------------------------------------------------------ } diff --git a/app/Models/TvLog.php b/app/Models/TvLog.php new file mode 100644 index 0000000..7fb78bf --- /dev/null +++ b/app/Models/TvLog.php @@ -0,0 +1,33 @@ +'object', 'to'=>'object']; + + const TYPE_CREATE = 'create'; + const TYPE_UPDATE = 'update'; + const TYPES = ['create', 'update']; + public static function saveHistory(String $type, int $tvFk, ?Tv $oldTv, ?Tv $newTv) { + if(!in_array($type, self::TYPES)) throw new \Exception("Type '$type' No Valid"); + $tvLog = new TvLog(); + $tvLog->tv_fk = $tvFk; + $tvLog->type = $type; + if($oldTv) { + $oldTv = $oldTv->toArray(); + $tvLog->from =$oldTv; + } + if($newTv) { + $newTv = $newTv->toArray(); + $tvLog->from =$newTv; + } + $tvLog->save(); + } +} diff --git a/database/migrations/2024_05_18_033105_tv.php b/database/migrations/2024_05_18_033105_tv.php index 31cbfcb..be94026 100644 --- a/database/migrations/2024_05_18_033105_tv.php +++ b/database/migrations/2024_05_18_033105_tv.php @@ -46,6 +46,15 @@ return new class extends Migration { $table->timestampsTz(); }); + Schema::create('tv_logs', function (Blueprint $table) { + $table->id(); + $table->foreignId('tv_fk'); + $table->string('type'); + $table->json('from')->nullable(); + $table->json('to')->nullable(); + $table->timestampsTz(); + }); + // Schema::create('tv_sessions', function (Blueprint $table) { // $table->id(); // $table->foreignId('tv_fk')->index(); @@ -89,6 +98,7 @@ return new class extends Migration { // Schema::drop('tv_app_logs'); // Schema::drop('tv_app_infos'); // Schema::drop('tv_sessions'); + Schema::drop('tv_logs'); Schema::drop('new_tv_requests'); Schema::drop('tvs'); } diff --git a/routes/api/superadmin.php b/routes/api/superadmin.php index c9c9565..4421764 100644 --- a/routes/api/superadmin.php +++ b/routes/api/superadmin.php @@ -35,6 +35,7 @@ Route::controller(TvController::class)->group(function() { Route::post('/tv/tv', 'init'); Route::post('/tv/tv/update', 'update'); Route::post('/tv/tv/change-status', 'changeStatus'); + Route::post('/tv/tv/excel', 'excel'); }); Route::controller(NewTvRequestController::class)->group(function() { From 163d5a048613d64f92856483d70e32a501cf25cc Mon Sep 17 00:00:00 2001 From: ricky rx Date: Fri, 21 Jun 2024 17:31:54 +0700 Subject: [PATCH 23/27] chore: write tvlogs for upsert action --- app/Helper/Frontend/ApiUtilities.php | 2 +- app/Models/NewTvRequest.php | 2 ++ app/Models/Tv.php | 8 ++++++-- app/Models/TvLog.php | 16 +++++++++++----- database/migrations/2024_05_18_033105_tv.php | 4 ++++ 5 files changed, 24 insertions(+), 8 deletions(-) diff --git a/app/Helper/Frontend/ApiUtilities.php b/app/Helper/Frontend/ApiUtilities.php index 6f70f42..cc7101c 100644 --- a/app/Helper/Frontend/ApiUtilities.php +++ b/app/Helper/Frontend/ApiUtilities.php @@ -31,7 +31,7 @@ class ApiUtilities { $tvCodes = $request->tvCodes ?? []; $tvs = $request->tvs ?? []; $oValidation = TV::validateExcel($tvs, $tvCodes); - $result = TV::uploadExcel($tvs, $oValidation); + $result = TV::uploadExcel($tvs, $oValidation, $request->user()); return JSONResponse::Success($result); break; case 'exportData': diff --git a/app/Models/NewTvRequest.php b/app/Models/NewTvRequest.php index 92ec5f7..aaf9922 100644 --- a/app/Models/NewTvRequest.php +++ b/app/Models/NewTvRequest.php @@ -175,10 +175,12 @@ class NewTvRequest extends Model { $tv->installed_at = now(); $tv->last_connected_at = now(); $tv->save(); + TVLog::historyCreate($request->user(), $tv->id, $tv); $newTvReq->activated_at = now(); $newTvReq->tv_fk = $tv->id; $newTvReq->save(); + DB::commit(); return JSONResponse::Success(['message'=>'Success to update tv data']); diff --git a/app/Models/Tv.php b/app/Models/Tv.php index 61d8bbf..d97fd2d 100644 --- a/app/Models/Tv.php +++ b/app/Models/Tv.php @@ -117,6 +117,7 @@ class Tv extends Model { 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; @@ -133,6 +134,8 @@ class Tv extends Model { $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']); @@ -151,6 +154,7 @@ class Tv extends Model { $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(); @@ -303,7 +307,7 @@ class Tv extends Model { } return ['status' => $endStatus, 'results' => $results]; } - public static function uploadExcel($tvRows, $oValidation) { + 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 @@ -334,7 +338,7 @@ class Tv extends Model { $newTv->save(); // save data log - TvLog::saveHistory(TvLog::TYPE_CREATE, $newTv->id, $oldTV, $newTv); + TvLog::historyUpdateExcel($user, $newTv->id, $oldTV, $newTv); DB::commit(); break; diff --git a/app/Models/TvLog.php b/app/Models/TvLog.php index 7fb78bf..2af7207 100644 --- a/app/Models/TvLog.php +++ b/app/Models/TvLog.php @@ -12,22 +12,28 @@ class TvLog extends Model { protected $hidden = ['ik_cust_id', 'ik_address_id']; protected $casts = ['from'=>'object', 'to'=>'object']; - const TYPE_CREATE = 'create'; - const TYPE_UPDATE = 'update'; - const TYPES = ['create', 'update']; - public static function saveHistory(String $type, int $tvFk, ?Tv $oldTv, ?Tv $newTv) { + const TYPES = ['create', 'update', 'update-excel']; + + public static function historyCreate(User $user, int $tvFk, Tv $newTv) { self::saveHistory('create', $user, $tvFk, null, $newTv); } + public static function historyUpdate(User $user, int $tvFk, Tv $oldTv, Tv $newTv) { self::saveHistory('update', $user, $tvFk, $oldTv, $newTv); } + public static function historyUpdateExcel(User $user, int $tvFk, Tv $oldTv, Tv $newTv) { self::saveHistory('update-excel', $user, $tvFk, $oldTv, $newTv); } + + private static function saveHistory(String $type, ?User $user, int $tvFk, ?Tv $oldTv, ?Tv $newTv) { if(!in_array($type, self::TYPES)) throw new \Exception("Type '$type' No Valid"); $tvLog = new TvLog(); $tvLog->tv_fk = $tvFk; $tvLog->type = $type; if($oldTv) { $oldTv = $oldTv->toArray(); + unset($oldTv['id']); $tvLog->from =$oldTv; } if($newTv) { $newTv = $newTv->toArray(); - $tvLog->from =$newTv; + unset($newTv['id']); + $tvLog->to = $newTv; } + if($user) { $tvLog->user_fk = $user->id; } $tvLog->save(); } } diff --git a/database/migrations/2024_05_18_033105_tv.php b/database/migrations/2024_05_18_033105_tv.php index be94026..98f8b12 100644 --- a/database/migrations/2024_05_18_033105_tv.php +++ b/database/migrations/2024_05_18_033105_tv.php @@ -52,7 +52,11 @@ return new class extends Migration { $table->string('type'); $table->json('from')->nullable(); $table->json('to')->nullable(); + $table->foreignId('user_fk')->nullable(); $table->timestampsTz(); + + $table->foreign('tv_fk')->references('id')->on('tvs')->cascadeOnDelete(); + $table->foreign('user_fk')->references('id')->on('users'); }); // Schema::create('tv_sessions', function (Blueprint $table) { From 3322055ac55553ab8146330d6be9d9ae31a320ce Mon Sep 17 00:00:00 2001 From: ricky rx Date: Sat, 22 Jun 2024 13:45:38 +0700 Subject: [PATCH 24/27] chore: adjustment related to tv > instaleed at & last connected at --- app/Models/NewTvRequest.php | 1 - app/Models/Tv.php | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/app/Models/NewTvRequest.php b/app/Models/NewTvRequest.php index aaf9922..68fcde1 100644 --- a/app/Models/NewTvRequest.php +++ b/app/Models/NewTvRequest.php @@ -173,7 +173,6 @@ class NewTvRequest extends Model { $tv->notes = $tvRequest->notes; $tv->device_info = $newTvReq->device_info; $tv->installed_at = now(); - $tv->last_connected_at = now(); $tv->save(); TVLog::historyCreate($request->user(), $tv->id, $tv); diff --git a/app/Models/Tv.php b/app/Models/Tv.php index d97fd2d..042157c 100644 --- a/app/Models/Tv.php +++ b/app/Models/Tv.php @@ -178,7 +178,7 @@ class Tv extends Model { //------------------------------------------------------------ // -- RELATED TO EXCEL public static function getExcelDetail(Request $request) { - $cols = ['code', 'apk_version_code', 'apk_version_name', 'last_connected_at', + $cols = ['code', 'apk_version_code', 'apk_version_name', 'installed_at', 'last_connected_at', 'company_name', 'address', 'street_address', 'notes', 'col1', 'col2', 'col3', 'col4', 'col5', 'col6', 'col7', 'col8', 'col9', 'col10', From 3ff470bc554f02bddf49772a402bdce6db509d30 Mon Sep 17 00:00:00 2001 From: ricky rx Date: Sat, 22 Jun 2024 14:05:06 +0700 Subject: [PATCH 25/27] refactor: new_tv_req > activated_at change to approved_at --- .../superadmin/tv/NewTvRequestController.php | 3 ++- app/Models/NewTvRequest.php | 20 +++++++++---------- database/migrations/2024_05_18_033105_tv.php | 2 +- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/app/Http/Controllers/api/superadmin/tv/NewTvRequestController.php b/app/Http/Controllers/api/superadmin/tv/NewTvRequestController.php index 8eca953..0052c55 100644 --- a/app/Http/Controllers/api/superadmin/tv/NewTvRequestController.php +++ b/app/Http/Controllers/api/superadmin/tv/NewTvRequestController.php @@ -17,7 +17,8 @@ class NewTvRequestController extends Controller { 'search' => DatabaseHelper::getSearchValidation() ]); - $newTvRequests = NewTvRequest::addColumnCanApprove() + $newTvRequests = NewTvRequest::with('tv') + ->addColumnCanApprove() ->addColumnCanReject() ->multiSearch($request->search, ['code']) ->multiOrderBy($request->orderBys, 'created_at desc') diff --git a/app/Models/NewTvRequest.php b/app/Models/NewTvRequest.php index 68fcde1..cfce1be 100644 --- a/app/Models/NewTvRequest.php +++ b/app/Models/NewTvRequest.php @@ -38,12 +38,12 @@ class NewTvRequest extends Model { // -- RELATED TO SCOPE public function scopeAddColumnCanApprove(Builder $query) { $query->withCount(['self as can_approve' => function($q) { - $q->whereNull('activated_at'); + $q->whereNull('approved_at'); }]); } public function scopeAddColumnCanReject(Builder $query) { $query->withCount(['self as can_reject' => function($q) { - $q->whereNull('activated_at'); + $q->whereNull('approved_at'); }]); } // -- END RELATED TO SCOPE @@ -64,16 +64,16 @@ class NewTvRequest extends Model { /** * Rule: - * 1. if it has not been activated for more than the expiry date, deleted it - * 2. if it has been activated & has been responded more than expiry date, delete it - * - case: when has been activated, but intenet connection missing, how can device know - * that the new request tv has been activated? + * 1. if it has not been approved for more than the expiry date, deleted it + * 2. if it has been approved & has been responded more than expiry date, delete it + * - case: when has been approved, but intenet connection missing, how can device know + * that the new request tv has been approved? */ private static function _getMaxExpiredTime() :Carbon { return Carbon::now()->subHour(); } public static function deleteExpiredRequests() { $expiredTime = self::_getMaxExpiredTime()->toDateTimeString(); NewTvRequest::where(function($q) use ($expiredTime) { - $q->whereNull('activated_at')->where('created_at', '<=', $expiredTime); + $q->whereNull('approved_at')->where('created_at', '<=', $expiredTime); })->orWhere(function($q) use ($expiredTime) { $q->where('responded_at', '<=', $expiredTime ); })->delete(); @@ -84,7 +84,7 @@ class NewTvRequest extends Model { //------------------------------------------------------------ // -- RELATED TO FROM REQUEST FUNCTION private static function _checkOrCreateResponse(NewTvRequest $newTvReq) { - if($newTvReq->activated_at) { + if($newTvReq->approved_at) { // make sure waiting tv_device responded before deleted if(empty($newTvReq->responded_at)) { $newTvReq->responded_at = now(); @@ -92,7 +92,7 @@ class NewTvRequest extends Model { } return JSONResponse::Success([ - 'message' => 'activated', + 'message' => 'approved', 'new_tv_request' => $newTvReq, 'tv' => $newTvReq->tv ]); @@ -176,7 +176,7 @@ class NewTvRequest extends Model { $tv->save(); TVLog::historyCreate($request->user(), $tv->id, $tv); - $newTvReq->activated_at = now(); + $newTvReq->approved_at = now(); $newTvReq->tv_fk = $tv->id; $newTvReq->save(); diff --git a/database/migrations/2024_05_18_033105_tv.php b/database/migrations/2024_05_18_033105_tv.php index 98f8b12..c3aedad 100644 --- a/database/migrations/2024_05_18_033105_tv.php +++ b/database/migrations/2024_05_18_033105_tv.php @@ -41,7 +41,7 @@ return new class extends Migration { $table->string('code', 50); $table->jsonb('device_info')->nullable(); $table->foreignId('tv_fk')->nullable(); - $table->timestampTz('activated_at')->nullable(); + $table->timestampTz('approved_at')->nullable(); $table->timestampTz('responded_at')->nullable(); $table->timestampsTz(); }); From 2ed57016bc790822f29a228850902a7614b8a9d6 Mon Sep 17 00:00:00 2001 From: ricky rx Date: Mon, 24 Jun 2024 15:34:20 +0700 Subject: [PATCH 26/27] chore: adjust for filter TV --- app/Helper/Common.php | 7 +++++++ app/Models/Tv.php | 26 +++++++++++++++++--------- 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/app/Helper/Common.php b/app/Helper/Common.php index 89c4f6c..f1c18e8 100644 --- a/app/Helper/Common.php +++ b/app/Helper/Common.php @@ -2,6 +2,8 @@ namespace App\Helper; +use Illuminate\Support\Facades\DB; + class Common { public static function convertRequestConfig(?array $requestConfig): array { $config = []; @@ -26,4 +28,9 @@ class Common { public static function trueOrFalse(mixed $value): bool { return ($value === "true" || $value === "1" || $value === 1 || $value === true) ? true : false; } + + public static function setTimezone($timezone) { + date_default_timezone_set($timezone); + DB::statement("SET TIME ZONE '$timezone'"); + } } \ No newline at end of file diff --git a/app/Models/Tv.php b/app/Models/Tv.php index 042157c..51231c0 100644 --- a/app/Models/Tv.php +++ b/app/Models/Tv.php @@ -2,6 +2,7 @@ namespace App\Models; +use App\Helper\Common; use App\Helper\DatabaseHelper; use App\Helper\JSONResponse; use App\Helper\STS\Indokargo; @@ -70,11 +71,17 @@ class Tv extends Model { ...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' + '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); @@ -82,6 +89,12 @@ class Tv extends Model { ->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); }) @@ -178,12 +191,7 @@ class Tv extends Model { //------------------------------------------------------------ // -- RELATED TO EXCEL public static function getExcelDetail(Request $request) { - $cols = ['code', 'apk_version_code', 'apk_version_name', 'installed_at', '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()]); + return JSONResponse::Success(['rows' => TV::validateAndGetEloquentFromRequest($request)->get()]); } // ---- RELATED TO EXPORT IMPORT @@ -202,8 +210,8 @@ class Tv extends Model { 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]); + $rows = TV::validateAndGetEloquentFromRequest($request)->get(); + return JSONResponse::Success(['rows' => $rows, 'cols' => self::EXCEL_TEMPLATE_COLS]); } // ------ RELATED TO IMPORT private static function _checkExcelFormat($tvRows) { From e2ab453d66f1730f8cfc740b9906ac7dd7d4f6ce Mon Sep 17 00:00:00 2001 From: ricky rx Date: Tue, 25 Jun 2024 14:36:25 +0700 Subject: [PATCH 27/27] feat: api for new tv request > approve existing tv --- app/Helper/Frontend/ApiUtilities.php | 3 + .../api/superadmin/GeneralController.php | 16 +++ app/Models/NewTvRequest.php | 115 ++++++++++-------- routes/api/superadmin.php | 6 + 4 files changed, 90 insertions(+), 50 deletions(-) create mode 100644 app/Http/Controllers/api/superadmin/GeneralController.php diff --git a/app/Helper/Frontend/ApiUtilities.php b/app/Helper/Frontend/ApiUtilities.php index cc7101c..9055dc3 100644 --- a/app/Helper/Frontend/ApiUtilities.php +++ b/app/Helper/Frontend/ApiUtilities.php @@ -9,6 +9,9 @@ use Illuminate\Http\Request; * Note: * if any of frontend component need specific api query / data specific. * Please add function to get data here. + * + * But, if the parent component still needs to pass the url to the + * child component, the url api can be placed in the app/Http/Controllers/api/superadmin/GeneralController */ class ApiUtilities { diff --git a/app/Http/Controllers/api/superadmin/GeneralController.php b/app/Http/Controllers/api/superadmin/GeneralController.php new file mode 100644 index 0000000..24bfd28 --- /dev/null +++ b/app/Http/Controllers/api/superadmin/GeneralController.php @@ -0,0 +1,16 @@ +validate(['search' => 'nullable|string']); + $tvs = Tv::multiSearch($request->search, ['code'])->orderBy('code', 'asc')->limit(10)->get(); + return JSONResponse::Success(['tvs' => $tvs ]); + } +} diff --git a/app/Models/NewTvRequest.php b/app/Models/NewTvRequest.php index cfce1be..775338a 100644 --- a/app/Models/NewTvRequest.php +++ b/app/Models/NewTvRequest.php @@ -126,68 +126,83 @@ class NewTvRequest extends Model { public static function approveFromRequest(Request $request) { $request->validate([ 'id' => 'required|integer|exists:App\Models\NewTvRequest', - 'tv' => 'required|array', - ]); - - $tvRequest = new Request($request->tv); - if($tvRequest->code) $tvRequest->merge(['code' => strtoupper($tvRequest->code)]); - - $tvRequest->validate([ - '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', + 'tv' => 'nullable|array', + 'existingTv' => 'nullable|array', + 'action' => 'required|string', ]); $newTvReq = NewTvRequest::addColumnCanApprove() - ->findOrFail($request->id); + ->findOrFail($request->id); if(!$newTvReq->can_approve) throw new \Exception('Cannot approve current request'); try { - DB::beginTransaction(); - $tv = new Tv(); - $tv->code = TV::generateUniqueCode(); - $tv->company_name = $tvRequest->company_name; - $tv->address = $tvRequest->address; - $tv->street_address = $tvRequest->street_address; - $tv->col1 = $tvRequest->col1; - $tv->col2 = $tvRequest->col2; - $tv->col3 = $tvRequest->col3; - $tv->col4 = $tvRequest->col4; - $tv->col5 = $tvRequest->col5; - $tv->col6 = $tvRequest->col6; - $tv->col7 = $tvRequest->col7; - $tv->col8 = $tvRequest->col8; - $tv->col9 = $tvRequest->col9; - $tv->col10 = $tvRequest->col10; - $tv->notes = $tvRequest->notes; - $tv->device_info = $newTvReq->device_info; - $tv->installed_at = now(); - $tv->save(); - TVLog::historyCreate($request->user(), $tv->id, $tv); + $tv = null; + if($request->action == 'existing') { + $existingTvRequest = new Request($request->existingTv); + $existingTvRequest->validate([ + 'id' => 'required|integer|exists:App\Models\Tv', + ]); + + $tv = TV::findOrFail($existingTvRequest->id); + } else if ($request->action == 'new') { + $tvRequest = new Request($request->tv); + if($tvRequest->code) $tvRequest->merge(['code' => strtoupper($tvRequest->code)]); + + $tvRequest->validate([ + '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', + ]); + + DB::beginTransaction(); + $tv = new Tv(); + $tv->code = TV::generateUniqueCode(); + $tv->company_name = $tvRequest->company_name; + $tv->address = $tvRequest->address; + $tv->street_address = $tvRequest->street_address; + $tv->col1 = $tvRequest->col1; + $tv->col2 = $tvRequest->col2; + $tv->col3 = $tvRequest->col3; + $tv->col4 = $tvRequest->col4; + $tv->col5 = $tvRequest->col5; + $tv->col6 = $tvRequest->col6; + $tv->col7 = $tvRequest->col7; + $tv->col8 = $tvRequest->col8; + $tv->col9 = $tvRequest->col9; + $tv->col10 = $tvRequest->col10; + $tv->notes = $tvRequest->notes; + $tv->device_info = $newTvReq->device_info; + $tv->installed_at = now(); + $tv->save(); + TVLog::historyCreate($request->user(), $tv->id, $tv); + + // TODO: waiting execution until update from ops + // NEED TO REFACTOR (cause by add existing tv code) + // // try to sys_to_sys with indokargo + // DB::commit(); + // $jsonResponse = Indokargo::createTVAddress($tvRequest, $tv->id); + // return $jsonResponse; + } else { + throw new \Exception('INVALID FORM ACTION'); + } $newTvReq->approved_at = now(); $newTvReq->tv_fk = $tv->id; $newTvReq->save(); DB::commit(); - return JSONResponse::Success(['message'=>'Success to update tv data']); - - // TODO: waiting execution until update from ops - // // try to sys_to_sys with indokargo - // DB::commit(); - // $jsonResponse = Indokargo::createTVAddress($tvRequest, $tv->id); - // return $jsonResponse; + return JSONResponse::Success(['message'=>'Success to approve new tv request']); } catch(\Throwable $th) { DB::rollback(); throw $th; diff --git a/routes/api/superadmin.php b/routes/api/superadmin.php index 4421764..0dd9d48 100644 --- a/routes/api/superadmin.php +++ b/routes/api/superadmin.php @@ -1,12 +1,17 @@ group(function() { + Route::post('/general/tv/search', 'tvSearch'); +}); + Route::controller(VideoUploadController::class)->group(function() { Route::post('/video-upload', 'init'); Route::post('/video-upload/save', 'save'); @@ -42,5 +47,6 @@ Route::controller(NewTvRequestController::class)->group(function() { Route::post('/tv/new-tv-request', 'init'); Route::post('/tv/new-tv-request/approve', 'approve'); Route::post('/tv/new-tv-request/reject', 'reject'); + Route::post('/tv/new-tv-request/tv/search', 'tvSearch'); }); ?> \ No newline at end of file