Explore/ismail #12

Merged
afrina merged 10 commits from explore/ismail into main 2026-09-09 14:47:34 +08:00
9 changed files with 298 additions and 2 deletions
Showing only changes of commit 22279af86c - Show all commits
+7 -1
View File
@@ -21,4 +21,10 @@ Tebus=penyelesaian penuh/ansuran
docker exec -i api_arrahn_staging_mysql mysql -uroot -proot erahn_test < erahn_test.sql
```
## Disable the cawangan in arrahn_master_db
## Disable the cawangan in arrahn_master_db
## Feature Flag
1. Register the flag in api_arrahn/app/Feature.php
2. Block the real work in the API
3. Hide the UI in kaunter from the
4. Enable a pilot from Admin → Ciri Cawangan (or SQL):
+21
View File
@@ -0,0 +1,21 @@
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class CawanganFeature extends Model
{
protected $table = 'cawangan_feature';
protected $connection = 'mysql7';
protected $fillable = [
'kodcaw',
'feature',
'enabled',
];
protected $casts = [
'enabled' => 'integer',
];
}
+147
View File
@@ -0,0 +1,147 @@
<?php
namespace App;
class Feature
{
const PEMBIAYAAN_ONLINE = 'pembiayaan_online';
/**
* Known flag keys. New features: add a constant here, then INSERT rows for pilot kodcaw.
*
* @return string[]
*/
public static function keys()
{
return [
self::PEMBIAYAAN_ONLINE,
];
}
/**
* @return array<string,int>
*/
public static function defaults()
{
$map = [];
foreach (self::keys() as $key) {
$map[$key] = 0;
}
return $map;
}
/**
* Display labels for admin UI. New features: add a label here.
*
* @return array<string,string>
*/
public static function labels()
{
return [
self::PEMBIAYAAN_ONLINE => 'Pembiayaan Online',
];
}
/**
* Missing row is off (fail closed).
*
* @param string $kodcaw
* @param string $feature
* @return bool
*/
public static function enabled($kodcaw, $feature)
{
return CawanganFeature::on('mysql7')
->where('kodcaw', $kodcaw)
->where('feature', $feature)
->where('enabled', 1)
->exists();
}
/**
* @param string $kodcaw
* @return array<string,int>
*/
public static function mapFor($kodcaw)
{
$map = self::defaults();
$rows = CawanganFeature::on('mysql7')
->where('kodcaw', $kodcaw)
->get(['feature', 'enabled']);
foreach ($rows as $row) {
$map[$row->feature] = ((int) $row->enabled) === 1 ? 1 : 0;
}
return $map;
}
/**
* @param string[] $kodcaws
* @return array<string,array<string,int>>
*/
public static function mapForMany(array $kodcaws)
{
$defaults = self::defaults();
$result = [];
foreach ($kodcaws as $kodcaw) {
$result[$kodcaw] = $defaults;
}
if (count($kodcaws) === 0) {
return $result;
}
$rows = CawanganFeature::on('mysql7')
->whereIn('kodcaw', $kodcaws)
->get(['kodcaw', 'feature', 'enabled']);
foreach ($rows as $row) {
$result[$row->kodcaw][$row->feature] = ((int) $row->enabled) === 1 ? 1 : 0;
}
return $result;
}
/**
* @param string $kodcaw
* @param string $feature
* @param bool|int $enabled
* @return bool
*/
public static function setEnabled($kodcaw, $feature, $enabled)
{
if (!in_array($feature, self::keys(), true)) {
return false;
}
CawanganFeature::on('mysql7')->updateOrCreate(
[
'kodcaw' => $kodcaw,
'feature' => $feature,
],
[
'enabled' => $enabled ? 1 : 0,
]
);
return true;
}
/**
* @param string|null $message
* @return array{ok:bool,status:int,payload:array}
*/
public static function denied($message = null)
{
return [
'ok' => false,
'status' => 403,
'payload' => [
'message' => $message ?: 'Ciri ini tidak diaktifkan untuk cawangan ini',
],
];
}
}
+10 -1
View File
@@ -3,6 +3,7 @@
namespace App\Http\Controllers;
use App\Cawangan;
use App\Feature;
use Illuminate\Http\Request;
class CawanganController extends Controller
@@ -18,7 +19,15 @@ class CawanganController extends Controller
$cawangan = Cawangan::on('mysql7')
->where('kodcaw','=',$code_cawangan)
->first();
return response()->json($cawangan);
if (!$cawangan) {
return response()->json($cawangan);
}
$data = $cawangan->toArray();
$data['features'] = Feature::mapFor($code_cawangan);
return response()->json($data);
}
public function create(Request $request)
@@ -0,0 +1,59 @@
<?php
namespace App\Http\Controllers;
use App\Cawangan;
use App\Feature;
use Illuminate\Http\Request;
class FeatureController extends Controller
{
public function index()
{
return response()->json([
'keys' => Feature::keys(),
'labels' => Feature::labels(),
]);
}
public function cawangan()
{
$cawangan = Cawangan::on('mysql7')
->orderBy('kodcaw')
->get(['kodcaw', 'details_caw']);
$maps = Feature::mapForMany($cawangan->pluck('kodcaw')->all());
$data = $cawangan->map(function ($c) use ($maps) {
return [
'kodcaw' => $c->kodcaw,
'details_caw' => $c->nama_cawangan,
'features' => $maps[$c->kodcaw] ?? Feature::defaults(),
];
});
return response()->json($data);
}
public function update($kodcaw, $feature, Request $request)
{
$cawangan = Cawangan::on('mysql7')->where('kodcaw', $kodcaw)->first();
if (!$cawangan) {
return response()->json(['message' => 'Cawangan tidak dijumpai'], 404);
}
if (!in_array($feature, Feature::keys(), true)) {
return response()->json(['message' => 'Ciri tidak sah'], 422);
}
$enabled = (int) $request->input('enabled') ? 1 : 0;
Feature::setEnabled($kodcaw, $feature, $enabled);
return response()->json([
'kodcaw' => $kodcaw,
'feature' => $feature,
'enabled' => $enabled,
'features' => Feature::mapFor($kodcaw),
]);
}
}
@@ -3,6 +3,7 @@
namespace App\Services\PembiayaanOnline;
use App\Cawangan;
use App\Feature;
use App\ModalOnline;
use App\RequestModalOnline;
use Carbon\Carbon;
@@ -208,6 +209,10 @@ class ModalOnlineService
return $resolved;
}
if (!Feature::enabled($kodcaw, Feature::PEMBIAYAAN_ONLINE)) {
return Feature::denied('Pembiayaan online tidak diaktifkan untuk cawangan ini');
}
$mysql = $resolved['mysql'];
$amaun = round((float) $amaun, 2);
@@ -294,6 +299,10 @@ class ModalOnlineService
}
$mysql = $resolved['mysql'];
if (!Feature::enabled($kodcaw, Feature::PEMBIAYAAN_ONLINE)) {
return Feature::denied('Pembiayaan online tidak diaktifkan untuk cawangan ini');
}
$amaun = round((float) $amaun, 2);
$dimohonOleh = trim((string) $dimohonOleh);
@@ -7,6 +7,7 @@ use App\Bank;
use App\BatchPembiayaanOnline;
use App\Cawangan;
use App\Customer;
use App\Feature;
use App\Gadai;
use App\PembiayaanOnline;
use Carbon\Carbon;
@@ -67,6 +68,10 @@ class PembiayaanOnlineService
}
$mysql = $resolved['mysql'];
if (!Feature::enabled($kodcaw, Feature::PEMBIAYAAN_ONLINE)) {
return Feature::denied('Pembiayaan online tidak diaktifkan untuk cawangan ini');
}
$norujukan = trim((string) ($data['norujukan'] ?? ''));
$nokp = trim((string) ($data['nokp'] ?? ''));
$pinjaman = round((float) ($data['pinjaman'] ?? 0), 2);
@@ -0,0 +1,36 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateCawanganFeatureTable extends Migration
{
/**
* Master DB (mysql7) — per-cawangan feature flags.
*
* Missing row = off. Enable a pilot after migrate:
*
* INSERT INTO cawangan_feature (kodcaw, feature, enabled, created_at, updated_at)
* VALUES ('X0301', 'pembiayaan_online', 1, NOW(), NOW())
* ON DUPLICATE KEY UPDATE enabled = 1;
*/
public function up()
{
if (!Schema::connection('mysql7')->hasTable('cawangan_feature')) {
Schema::connection('mysql7')->create('cawangan_feature', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('kodcaw', 20);
$table->string('feature', 64);
$table->tinyInteger('enabled')->default(0);
$table->timestamps();
$table->unique(['kodcaw', 'feature'], 'uniq_cawangan_feature');
});
}
}
public function down()
{
Schema::connection('mysql7')->dropIfExists('cawangan_feature');
}
}
+4
View File
@@ -240,6 +240,10 @@ $router->group(['prefix'=>'api'], function () use ($router){
$router->get('cawangan/{code_cawangan}',['uses'=>'CawanganController@showOneCawangan']);
$router->get('cawangan/detail/{kodcaw}',['uses'=>'CawanganDetailController@getCawanganDetail']);
$router->get('features',['uses'=>'FeatureController@index']);
$router->get('features/cawangan',['uses'=>'FeatureController@cawangan']);
$router->put('features/{kodcaw}/{feature}',['uses'=>'FeatureController@update']);
//Poskod API
$router->post('poskod',['uses'=>'PoskodController@store']);
$router->put('poskod/{poskod}/update',['uses'=>'PoskodController@update']);