Compare commits
5 Commits
7a72e6674f
...
8cd1385a21
| Author | SHA1 | Date | |
|---|---|---|---|
| 8cd1385a21 | |||
| 46ac5c8651 | |||
| 4fea68e542 | |||
| 4027d6f687 | |||
| f343f31075 |
182
AGENTS.md
Normal file
182
AGENTS.md
Normal file
@@ -0,0 +1,182 @@
|
|||||||
|
# AGENTS.md
|
||||||
|
|
||||||
|
File ini berisi instruksi untuk AI coding agent yang bekerja di repository ini.
|
||||||
|
|
||||||
|
## Project Overview
|
||||||
|
|
||||||
|
Project name: ASO (Laravel + Modules)
|
||||||
|
Purpose: Platform operasional health insurance/managed care untuk pengelolaan member, corporate policy, claim, monitoring, livechat, dan pelaporan.
|
||||||
|
Main users: Tim internal operasional, portal client corporate, portal hospital.
|
||||||
|
Business domain: Healthtech / asuransi kesehatan / manajemen klaim dan layanan kesehatan.
|
||||||
|
|
||||||
|
## Tech Stack
|
||||||
|
|
||||||
|
Backend: PHP 8+, Laravel 9, `nwidart/laravel-modules`.
|
||||||
|
Frontend: React + TypeScript + Vite (utama di `frontend/dashboard` dan `frontend/client-portal`), plus aset root Laravel (`resources/`, Vite/Mix).
|
||||||
|
Database: MySQL/MariaDB (via Eloquent ORM + Laravel migrations).
|
||||||
|
Queue/background jobs: Laravel Queue (`jobs` table tersedia; default testing `sync`).
|
||||||
|
Cache: Laravel cache (driver mengikuti `.env`, testing pakai `array`).
|
||||||
|
Auth: Laravel Sanctum, Spatie Permission (`role`/`permission` middleware), sebagian flow JWT untuk integrasi tertentu.
|
||||||
|
Deployment: Build frontend ke `public/dashboard` dan `public/client-portal`; backend Laravel standar.
|
||||||
|
Package manager: Composer (PHP), npm/yarn/pnpm (frontend, mixed antar subproject).
|
||||||
|
|
||||||
|
## Local Setup
|
||||||
|
|
||||||
|
Install dependencies:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
composer install
|
||||||
|
npm install
|
||||||
|
cd frontend/dashboard && yarn install
|
||||||
|
cd ../client-portal && yarn install
|
||||||
|
```
|
||||||
|
|
||||||
|
Run app:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
php artisan serve
|
||||||
|
npm run dev
|
||||||
|
cd frontend/dashboard && yarn start
|
||||||
|
cd ../client-portal && yarn start
|
||||||
|
```
|
||||||
|
|
||||||
|
Run tests:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
php artisan test
|
||||||
|
```
|
||||||
|
|
||||||
|
Run lint:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd frontend/dashboard && yarn lint
|
||||||
|
cd ../client-portal && yarn lint
|
||||||
|
```
|
||||||
|
|
||||||
|
Run typecheck:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Belum ada script typecheck standar di root/subproject.
|
||||||
|
# Jika diperlukan, gunakan tsc manual per frontend setelah konfirmasi tim.
|
||||||
|
```
|
||||||
|
|
||||||
|
Run build:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run production
|
||||||
|
cd frontend/dashboard && yarn build
|
||||||
|
cd ../client-portal && yarn build
|
||||||
|
```
|
||||||
|
|
||||||
|
Run migrations:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
php artisan migrate
|
||||||
|
```
|
||||||
|
|
||||||
|
## Repository Structure
|
||||||
|
|
||||||
|
```text
|
||||||
|
app/ # Core Laravel app (models, services, middleware, controllers)
|
||||||
|
routes/ # Root web/api routes
|
||||||
|
Modules/ # Domain/module-based backend (Client, Internal, Linksehat, Primaya, HospitalPortal)
|
||||||
|
Modules/*/Routes/ # Route entry point tiap module
|
||||||
|
Modules/*/Http/Controllers/Api/
|
||||||
|
database/migrations/ # Root migrations
|
||||||
|
frontend/dashboard/ # React TS admin/internal dashboard
|
||||||
|
frontend/client-portal/ # React TS client portal
|
||||||
|
resources/ # Blade/views/assets untuk app Laravel utama
|
||||||
|
public/ # Public assets + output build frontend
|
||||||
|
tests/ # PHPUnit tests (Feature/Unit)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Architecture Rules
|
||||||
|
|
||||||
|
- Tempatkan business logic di layer service/domain yang sudah ada (`app/Services`, `Modules/*/Services`) dan jaga controller tetap tipis.
|
||||||
|
- API layer mengikuti pemisahan module: endpoint module didefinisikan di `Modules/<ModuleName>/Routes/api.php` dengan controller module terkait.
|
||||||
|
- Untuk perubahan schema, gunakan migration baru; jangan ubah migration lama kecuali ada instruksi eksplisit.
|
||||||
|
- Untuk frontend, ikuti pattern existing per app (`src/pages`, `src/components`, `src/hooks`, `src/sections`) dan hindari cross-import antar app dashboard/client-portal.
|
||||||
|
- Testing backend utama menggunakan PHPUnit (`tests/Feature`, `tests/Unit`); tambah regression test untuk perubahan behavior penting.
|
||||||
|
|
||||||
|
## Coding Standards
|
||||||
|
|
||||||
|
- Ikuti pattern existing project sebelum membuat pattern baru.
|
||||||
|
- Buat perubahan kecil dan fokus.
|
||||||
|
- Jangan lakukan refactor yang tidak berhubungan.
|
||||||
|
- Jangan hardcode secrets.
|
||||||
|
- Jangan bypass validation.
|
||||||
|
- Jangan bypass auth/permission checks.
|
||||||
|
- Pilih code yang eksplisit dan mudah dibaca dibanding code yang terlalu clever.
|
||||||
|
- Tambahkan atau update test jika behavior berubah.
|
||||||
|
|
||||||
|
## Git Rules
|
||||||
|
|
||||||
|
- Jangan commit kecuali user secara eksplisit meminta.
|
||||||
|
- Jangan push kecuali user secara eksplisit meminta.
|
||||||
|
- Jangan rewrite git history kecuali user secara eksplisit meminta.
|
||||||
|
- Tampilkan file yang berubah di final response.
|
||||||
|
|
||||||
|
## Security Rules
|
||||||
|
|
||||||
|
- Jangan pernah print secrets.
|
||||||
|
- Jangan ubah nilai `.env` asli kecuali diminta secara eksplisit.
|
||||||
|
- Gunakan `.env-example`/`.env.example` untuk dokumentasi environment variables.
|
||||||
|
- Validasi auth, permissions, dan scoping corporate/member untuk protected data.
|
||||||
|
- Untuk endpoint API, pertahankan middleware auth yang ada (`auth:sanctum`, role/permission, middleware khusus module).
|
||||||
|
- Minta konfirmasi sebelum destructive data changes.
|
||||||
|
|
||||||
|
## Database Rules
|
||||||
|
|
||||||
|
- Gunakan migration system project ini.
|
||||||
|
- Jangan edit historical migrations kecuali project ini memang mengizinkan.
|
||||||
|
- Sertakan rollback/safety notes untuk schema changes.
|
||||||
|
- Pertimbangkan existing production data.
|
||||||
|
- Perhatikan migrasi dapat berasal dari root `database/migrations` dan modul tertentu.
|
||||||
|
|
||||||
|
## Testing Rules
|
||||||
|
|
||||||
|
Sebelum selesai, jalankan command yang relevan:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
php artisan test
|
||||||
|
cd frontend/dashboard && yarn lint
|
||||||
|
cd frontend/client-portal && yarn lint
|
||||||
|
```
|
||||||
|
|
||||||
|
Jika command tidak bisa dijalankan, jelaskan alasannya dan command apa yang harus dijalankan manual.
|
||||||
|
|
||||||
|
## AI Agent Workflow
|
||||||
|
|
||||||
|
Untuk feature work:
|
||||||
|
|
||||||
|
1. Baca file ini.
|
||||||
|
2. Inspect pattern yang sudah ada.
|
||||||
|
3. Tulis plan singkat untuk pekerjaan non-trivial.
|
||||||
|
4. Tanya klarifikasi jika requirement kurang jelas.
|
||||||
|
5. Implement perubahan kecil dan fokus.
|
||||||
|
6. Jalankan test/lint/typecheck/build yang relevan.
|
||||||
|
7. Rangkum file yang berubah, command yang dijalankan, dan risiko.
|
||||||
|
|
||||||
|
Untuk debugging:
|
||||||
|
|
||||||
|
1. Reproduce atau inspect issue.
|
||||||
|
2. Identifikasi root cause sebelum mengubah code.
|
||||||
|
3. Buat fix minimal yang aman.
|
||||||
|
4. Tambahkan regression test jika memungkinkan.
|
||||||
|
5. Jalankan verification.
|
||||||
|
|
||||||
|
## Final Response Format
|
||||||
|
|
||||||
|
```text
|
||||||
|
Summary:
|
||||||
|
- ...
|
||||||
|
|
||||||
|
Files changed:
|
||||||
|
- ...
|
||||||
|
|
||||||
|
Commands run:
|
||||||
|
- ...
|
||||||
|
|
||||||
|
Risks / notes:
|
||||||
|
- ...
|
||||||
|
```
|
||||||
@@ -0,0 +1,661 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Modules\HospitalPortal\Http\Controllers\Api;
|
||||||
|
|
||||||
|
use App\Models\Benefit;
|
||||||
|
use App\Models\Claim;
|
||||||
|
use App\Models\CorporateBenefit;
|
||||||
|
use App\Models\Member;
|
||||||
|
use App\Models\MemberPlan;
|
||||||
|
use App\Models\Organization;
|
||||||
|
use App\Models\Provider;
|
||||||
|
use App\Models\RequestLog;
|
||||||
|
use App\Models\RequestLogBenefit;
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Routing\Controller;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Illuminate\Support\Facades\Hash;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
use PDF;
|
||||||
|
use Throwable;
|
||||||
|
|
||||||
|
class ProviderOnlineController extends Controller
|
||||||
|
{
|
||||||
|
public function addHeaderKey(Request $request)
|
||||||
|
{
|
||||||
|
$request->validate([
|
||||||
|
'kodeprovider' => 'required|string',
|
||||||
|
'username' => 'required|string',
|
||||||
|
'password' => 'required|string',
|
||||||
|
'status' => 'required|string',
|
||||||
|
'header_token' => 'required|string',
|
||||||
|
'namaprovider' => 'required|string',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$organization = Organization::query()->firstOrCreate(
|
||||||
|
['code' => $request->kodeprovider],
|
||||||
|
[
|
||||||
|
'name' => $request->namaprovider,
|
||||||
|
'status' => 'active',
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
if ((string) $organization->name !== (string) $request->namaprovider) {
|
||||||
|
$organization->name = $request->namaprovider;
|
||||||
|
$organization->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
$provider = Provider::query()->firstOrNew([
|
||||||
|
'organization_id' => $organization->id,
|
||||||
|
'username' => $request->username,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$provider->password = Hash::make($request->password);
|
||||||
|
$provider->code = $request->kodeprovider;
|
||||||
|
$provider->status = $request->status;
|
||||||
|
$provider->header_token = $request->header_token;
|
||||||
|
$provider->token = $provider->token ?: Str::random(64);
|
||||||
|
$provider->save();
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'header-token' => $provider->header_token,
|
||||||
|
'userid' => $provider->id,
|
||||||
|
'usertoken' => $provider->token,
|
||||||
|
'kodeprovider' => $organization->code,
|
||||||
|
'namaprovider' => $organization->name,
|
||||||
|
'errornumber' => 0,
|
||||||
|
'messagestring' => 'Success',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getHeaderKey(Request $request)
|
||||||
|
{
|
||||||
|
$request->validate([
|
||||||
|
'username' => 'required|string',
|
||||||
|
'password' => 'required|string',
|
||||||
|
'kodeprovider' => 'required|string',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$organization = Organization::query()->where('code', $request->kodeprovider)->first();
|
||||||
|
if (!$organization) {
|
||||||
|
return $this->headerError('Kode provider tidak ditemukan');
|
||||||
|
}
|
||||||
|
|
||||||
|
$provider = Provider::query()->firstOrNew([
|
||||||
|
'organization_id' => $organization->id,
|
||||||
|
'username' => $request->username,
|
||||||
|
]);
|
||||||
|
|
||||||
|
if ($provider->exists) {
|
||||||
|
$isPasswordValid = Hash::check($request->password, $provider->password) || $provider->password === $request->password;
|
||||||
|
if (!$isPasswordValid) {
|
||||||
|
return $this->headerError('Username atau password tidak valid');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$provider->password = Hash::make($request->password);
|
||||||
|
}
|
||||||
|
|
||||||
|
$provider->code = $request->kodeprovider;
|
||||||
|
$provider->status = 'active';
|
||||||
|
$provider->header_token = Str::random(64);
|
||||||
|
$provider->token = Str::random(64);
|
||||||
|
$provider->save();
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'header-token' => $provider->header_token,
|
||||||
|
'userid' => $provider->id,
|
||||||
|
'usertoken' => $provider->token,
|
||||||
|
'kodeprovider' => $organization->code,
|
||||||
|
'namaprovider' => $organization->name,
|
||||||
|
'errornumber' => 0,
|
||||||
|
'messagestring' => 'Success',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function checkEligibilitasPeserta(Request $request)
|
||||||
|
{
|
||||||
|
$request->validate([
|
||||||
|
'nokartu' => 'required|string',
|
||||||
|
'kodeprovider' => 'required|string',
|
||||||
|
'p_user_no' => 'required',
|
||||||
|
'p_token' => 'required|string',
|
||||||
|
]);
|
||||||
|
|
||||||
|
[$provider, $organization, $authError] = $this->resolveProvider($request->kodeprovider, $request->p_user_no, $request->p_token);
|
||||||
|
if ($authError) {
|
||||||
|
return $authError;
|
||||||
|
}
|
||||||
|
|
||||||
|
$member = Member::query()
|
||||||
|
->with(['person', 'currentCorporate', 'currentPolicy', 'memberPlans.plan'])
|
||||||
|
->where('member_id', $request->nokartu)
|
||||||
|
->first();
|
||||||
|
|
||||||
|
if (!$member) {
|
||||||
|
return $this->statusError('Peserta tidak ditemukan');
|
||||||
|
}
|
||||||
|
|
||||||
|
$planIds = $member->memberPlans
|
||||||
|
->pluck('plan_id')
|
||||||
|
->filter()
|
||||||
|
->unique()
|
||||||
|
->values();
|
||||||
|
|
||||||
|
$benefits = collect();
|
||||||
|
if ($planIds->isNotEmpty()) {
|
||||||
|
$benefits = CorporateBenefit::query()
|
||||||
|
->with(['benefit', 'plan'])
|
||||||
|
->whereIn('plan_id', $planIds)
|
||||||
|
->get()
|
||||||
|
->map(function (CorporateBenefit $corporateBenefit) {
|
||||||
|
return [
|
||||||
|
// Keep response keys unchanged, but fill with sub-benefit data
|
||||||
|
'kodebenefit' => optional($corporateBenefit->benefit)->code,
|
||||||
|
'namabenefit' => optional($corporateBenefit->benefit)->description,
|
||||||
|
'planid' => optional($corporateBenefit->plan)->code,
|
||||||
|
];
|
||||||
|
})
|
||||||
|
->filter(function (array $item) {
|
||||||
|
return !empty($item['kodebenefit']);
|
||||||
|
})
|
||||||
|
->values();
|
||||||
|
}
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'Status' => $this->okStatus(),
|
||||||
|
'Data' => [
|
||||||
|
'nokartu' => $member->member_id,
|
||||||
|
'memberid' => (string) $member->id,
|
||||||
|
'namapeserta' => $member->full_name,
|
||||||
|
'nomorbpjs' => $member->bpjs_id,
|
||||||
|
'jeniskelamin' => $member->gender_code,
|
||||||
|
'tanggallahir' => $this->isoDate($member->birth_date),
|
||||||
|
'hubungankeluarga' => $member->relation_with_principal,
|
||||||
|
'namaperusahaan' => optional($member->currentCorporate)->name,
|
||||||
|
'pesertavip' => 'N',
|
||||||
|
'namapenjamin' => optional($organization)->name,
|
||||||
|
'nomorpolis' => optional($member->currentPolicy)->code,
|
||||||
|
'tglmulaipolis' => $this->isoDate(optional($member->currentPolicy)->start),
|
||||||
|
'tglberakhirpolis' => $this->isoDate(optional($member->currentPolicy)->end),
|
||||||
|
'phone' => optional($member->person)->phone,
|
||||||
|
'email' => $member->email,
|
||||||
|
],
|
||||||
|
'Benefit' => $benefits,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function createPendaftaran(Request $request)
|
||||||
|
{
|
||||||
|
$request->validate([
|
||||||
|
'kodeprovider' => 'required|string',
|
||||||
|
'kodebenefit' => 'required|string',
|
||||||
|
'statusrujukan' => 'nullable|string',
|
||||||
|
'nomorrujukan' => 'nullable|string',
|
||||||
|
'keterangan' => 'nullable|string',
|
||||||
|
'nomorsep' => 'nullable|string',
|
||||||
|
'nokartu' => 'required|string',
|
||||||
|
'kelaskamar' => 'nullable|string',
|
||||||
|
'cobbpjs' => 'nullable|numeric',
|
||||||
|
'notransaksiprovider' => 'nullable|string',
|
||||||
|
'inacbgscode' => 'nullable|string',
|
||||||
|
'inacbgsamount' => 'nullable|numeric',
|
||||||
|
'p_user_no' => 'required',
|
||||||
|
'p_token' => 'required|string',
|
||||||
|
]);
|
||||||
|
|
||||||
|
[$provider, $organization, $authError] = $this->resolveProvider($request->kodeprovider, $request->p_user_no, $request->p_token);
|
||||||
|
if ($authError) {
|
||||||
|
return $authError;
|
||||||
|
}
|
||||||
|
|
||||||
|
$member = Member::query()
|
||||||
|
->with(['person', 'currentCorporate', 'currentPolicy'])
|
||||||
|
->where('member_id', $request->nokartu)
|
||||||
|
->first();
|
||||||
|
if (!$member) {
|
||||||
|
return $this->statusError('Peserta tidak ditemukan');
|
||||||
|
}
|
||||||
|
|
||||||
|
$plan = $this->resolvePlan($member, $request->kodebenefit);
|
||||||
|
$generatedLogCode = $this->generateNextRequestLogCode($organization, $member);
|
||||||
|
|
||||||
|
$requestLog = RequestLog::query()->create([
|
||||||
|
'code' => $generatedLogCode,
|
||||||
|
'organization_id' => $organization->id,
|
||||||
|
'member_id' => $member->id,
|
||||||
|
'plan_id' => optional($plan)->id,
|
||||||
|
'policy_id' => optional($member->currentPolicy)->id ?? 0,
|
||||||
|
'payment_type' => 'cashless',
|
||||||
|
'service_code' => $request->kodebenefit,
|
||||||
|
'type_of_member' => 'member',
|
||||||
|
'status' => 'approved',
|
||||||
|
'source' => 'api',
|
||||||
|
'keterangan' => $request->keterangan,
|
||||||
|
'hak_kamar_pasien' => $request->kelaskamar ?? '',
|
||||||
|
'penempatan_kamar' => $request->kelaskamar,
|
||||||
|
'total_cob' => $request->cobbpjs,
|
||||||
|
'nominal' => 0,
|
||||||
|
'import_system' => 0,
|
||||||
|
'nomor_sep' => $request->nomorsep,
|
||||||
|
'inacbgs_code' => $request->inacbgscode,
|
||||||
|
'inacbgs_amount' => $request->inacbgsamount,
|
||||||
|
'no_transaksi_provider' => $request->notransaksiprovider,
|
||||||
|
'diagnosis' => '',
|
||||||
|
'reason' => '',
|
||||||
|
'reason_final' => '',
|
||||||
|
'catatan' => '',
|
||||||
|
'nomor_rujukan' => $request->nomorrujukan,
|
||||||
|
'status_rujukan' => $request->statusrujukan,
|
||||||
|
'submission_date' => now(),
|
||||||
|
'admission_date' => now(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
$limitSubBenefit = collect();
|
||||||
|
if ($plan) {
|
||||||
|
$limitSubBenefit = CorporateBenefit::query()
|
||||||
|
->with('benefit')
|
||||||
|
->where('plan_id', $plan->id)
|
||||||
|
->get()
|
||||||
|
->map(function (CorporateBenefit $corporateBenefit) {
|
||||||
|
return [
|
||||||
|
'kodesubbenefit' => optional($corporateBenefit->benefit)->code,
|
||||||
|
'namasubbenefit' => optional($corporateBenefit->benefit)->description,
|
||||||
|
'batasan' => (string) ($corporateBenefit->limit_amount ?? 0),
|
||||||
|
];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'Status' => $this->okStatus(),
|
||||||
|
'Data' => [[
|
||||||
|
'noklaim' => $requestLog->code,
|
||||||
|
'namapeserta' => $member->full_name,
|
||||||
|
'tanggallahir' => $this->isoDate($member->birth_date),
|
||||||
|
'nokartu' => $member->member_id,
|
||||||
|
'nopolis' => optional($member->currentPolicy)->code,
|
||||||
|
'nobpjs' => $member->bpjs_id,
|
||||||
|
'nosep' => $requestLog->nomor_sep,
|
||||||
|
'nomorrujukan' => $requestLog->nomor_rujukan,
|
||||||
|
'planid' => optional($plan)->code,
|
||||||
|
'masapolis' => optional($member->currentPolicy)->end ? Carbon::parse($member->currentPolicy->end)->format('d/m/Y') : null,
|
||||||
|
'namaperusahaan' => optional($member->currentCorporate)->name,
|
||||||
|
'namapenjamin' => $organization->name,
|
||||||
|
'tanggalmasuk' => $this->isoDate($requestLog->admission_date),
|
||||||
|
'tanggalkeluar' => $this->isoDate($requestLog->discharge_date),
|
||||||
|
'hakkamar' => $requestLog->penempatan_kamar,
|
||||||
|
'hakicu' => null,
|
||||||
|
'nosuratjaminan' => $requestLog->code,
|
||||||
|
'namapegawai' => null,
|
||||||
|
'namabenefit' => optional($plan)->corporate_plan_id,
|
||||||
|
'kodediagnosa' => $requestLog->diagnosis,
|
||||||
|
'keterangan' => $requestLog->keterangan,
|
||||||
|
'catatanTC1' => null,
|
||||||
|
'catatanTC2' => null,
|
||||||
|
'catatanTC3' => null,
|
||||||
|
'catatanTC4' => null,
|
||||||
|
'catatanTC5' => null,
|
||||||
|
'catatanTC6' => null,
|
||||||
|
'catatanTC7' => null,
|
||||||
|
'catatanTC8' => null,
|
||||||
|
'catatanTC9' => null,
|
||||||
|
'catatanTC10' => null,
|
||||||
|
'statusrujukan' => $requestLog->status_rujukan,
|
||||||
|
'statusklaim' => 0,
|
||||||
|
'notransaksiprovider' => $requestLog->no_transaksi_provider,
|
||||||
|
'inacbgscode' => $requestLog->inacbgs_code,
|
||||||
|
'inacbgsamount' => (float) ($requestLog->inacbgs_amount ?? 0),
|
||||||
|
]],
|
||||||
|
'LimitSubBenefit' => $limitSubBenefit->values(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function createPengesahan(Request $request)
|
||||||
|
{
|
||||||
|
$request->validate([
|
||||||
|
'noklaim' => 'required|string',
|
||||||
|
'kodeprovider' => 'required|string',
|
||||||
|
'tanggalkeluar' => 'nullable|date',
|
||||||
|
'kodediagnosa' => 'nullable|string',
|
||||||
|
'inacbgscode' => 'nullable|string',
|
||||||
|
'inacbgsamount' => 'nullable|numeric',
|
||||||
|
'daftarbiaya' => 'nullable|array',
|
||||||
|
'daftarbiaya.*.kodesubbenefit' => 'required_with:daftarbiaya|string',
|
||||||
|
'daftarbiaya.*.biayaaju' => 'required_with:daftarbiaya|numeric',
|
||||||
|
'p_user_no' => 'required',
|
||||||
|
'p_token' => 'required|string',
|
||||||
|
]);
|
||||||
|
|
||||||
|
[$provider, $organization, $authError] = $this->resolveProvider($request->kodeprovider, $request->p_user_no, $request->p_token);
|
||||||
|
if ($authError) {
|
||||||
|
return $authError;
|
||||||
|
}
|
||||||
|
|
||||||
|
$requestLog = RequestLog::query()
|
||||||
|
->with(['member.person', 'member.currentCorporate', 'member.currentPolicy', 'plan'])
|
||||||
|
->where('code', $request->noklaim)
|
||||||
|
->where('organization_id', $organization->id)
|
||||||
|
->first();
|
||||||
|
if (!$requestLog) {
|
||||||
|
return $this->statusError('Nomor klaim tidak ditemukan');
|
||||||
|
}
|
||||||
|
|
||||||
|
$requestLog->update([
|
||||||
|
'status_final_log' => 'approved',
|
||||||
|
'final_log' => true,
|
||||||
|
'discharge_date' => $request->tanggalkeluar,
|
||||||
|
'diagnosis' => $request->kodediagnosa,
|
||||||
|
'inacbgs_code' => $request->inacbgscode ?? $requestLog->inacbgs_code,
|
||||||
|
'inacbgs_amount' => $request->inacbgsamount ?? $requestLog->inacbgs_amount,
|
||||||
|
]);
|
||||||
|
|
||||||
|
foreach (($request->daftarbiaya ?? []) as $biaya) {
|
||||||
|
$benefit = Benefit::query()->where('code', $biaya['kodesubbenefit'])->first();
|
||||||
|
if (!$benefit) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
RequestLogBenefit::query()->updateOrCreate(
|
||||||
|
[
|
||||||
|
'request_log_id' => $requestLog->id,
|
||||||
|
'benefit_id' => $benefit->id,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'amount_incurred' => $biaya['biayaaju'],
|
||||||
|
'amount_approved' => 0,
|
||||||
|
'amount_not_approved' => 0,
|
||||||
|
'excess_paid' => 0,
|
||||||
|
'keterangan' => null,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$requestLog->load(['requestLogBenefits.benefit']);
|
||||||
|
|
||||||
|
$biayaResponse = $requestLog->requestLogBenefits->map(function (RequestLogBenefit $requestLogBenefit) use ($requestLog) {
|
||||||
|
return [
|
||||||
|
'noklaim' => $requestLog->code,
|
||||||
|
'kodesubbenefit' => optional($requestLogBenefit->benefit)->code,
|
||||||
|
'namasubbenefit' => optional($requestLogBenefit->benefit)->description,
|
||||||
|
'kodebenefit' => optional($requestLog->plan)->code,
|
||||||
|
'namabenefit' => optional($requestLog->plan)->corporate_plan_id,
|
||||||
|
'biayaaju' => (float) ($requestLogBenefit->amount_incurred ?? 0),
|
||||||
|
'jaminanasuransi' => (float) ($requestLogBenefit->amount_approved ?? 0),
|
||||||
|
'jaminanpeserta' => (float) ($requestLogBenefit->excess_paid ?? 0),
|
||||||
|
'keterangan' => $requestLogBenefit->keterangan,
|
||||||
|
];
|
||||||
|
})->values();
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'Status' => $this->okStatus(),
|
||||||
|
'Data' => [[
|
||||||
|
'noklaim' => $requestLog->code,
|
||||||
|
'namapeserta' => optional($requestLog->member)->full_name,
|
||||||
|
'tanggallahir' => $this->isoDate(optional($requestLog->member)->birth_date),
|
||||||
|
'nokartu' => optional($requestLog->member)->member_id,
|
||||||
|
'nopolis' => optional(optional($requestLog->member)->currentPolicy)->code,
|
||||||
|
'nobpjs' => optional($requestLog->member)->bpjs_id,
|
||||||
|
'nosep' => $requestLog->nomor_sep,
|
||||||
|
'nomorrujukan' => $requestLog->nomor_rujukan,
|
||||||
|
'planid' => optional($requestLog->plan)->code,
|
||||||
|
'masapolis' => optional(optional($requestLog->member)->currentPolicy)->end ? Carbon::parse($requestLog->member->currentPolicy->end)->format('d/m/Y') : null,
|
||||||
|
'namaperusahaan' => optional(optional($requestLog->member)->currentCorporate)->name,
|
||||||
|
'namapenjamin' => $organization->name,
|
||||||
|
'tanggalmasuk' => $this->isoDate($requestLog->admission_date),
|
||||||
|
'tanggalkeluar' => $this->isoDate($requestLog->discharge_date),
|
||||||
|
'hakkamar' => $requestLog->penempatan_kamar,
|
||||||
|
'hakicu' => null,
|
||||||
|
'nosuratjaminan' => $requestLog->code,
|
||||||
|
'namapegawai' => null,
|
||||||
|
'namabenefit' => optional($requestLog->plan)->corporate_plan_id,
|
||||||
|
'kodediagnosa' => $requestLog->diagnosis,
|
||||||
|
'keterangan' => $requestLog->keterangan,
|
||||||
|
'catatanTC1' => null,
|
||||||
|
'catatanTC2' => null,
|
||||||
|
'catatanTC3' => null,
|
||||||
|
'catatanTC4' => null,
|
||||||
|
'catatanTC5' => null,
|
||||||
|
'catatanTC6' => null,
|
||||||
|
'catatanTC7' => null,
|
||||||
|
'catatanTC8' => null,
|
||||||
|
'catatanTC9' => null,
|
||||||
|
'catatanTC10' => null,
|
||||||
|
'statusrujukan' => $requestLog->status_rujukan,
|
||||||
|
'statusklaim' => 0,
|
||||||
|
'notransaksiprovider' => $requestLog->no_transaksi_provider,
|
||||||
|
'inacbgscode' => $requestLog->inacbgs_code,
|
||||||
|
'inacbgsamount' => (float) ($requestLog->inacbgs_amount ?? 0),
|
||||||
|
]],
|
||||||
|
'Biaya' => $biayaResponse,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRincianBiayaKlaim(Request $request)
|
||||||
|
{
|
||||||
|
$request->validate([
|
||||||
|
'noklaim' => 'required|string',
|
||||||
|
'kodeprovider' => 'required|string',
|
||||||
|
'p_user_no' => 'required',
|
||||||
|
'p_token' => 'required|string',
|
||||||
|
]);
|
||||||
|
|
||||||
|
[$provider, $organization, $authError] = $this->resolveProvider($request->kodeprovider, $request->p_user_no, $request->p_token);
|
||||||
|
if ($authError) {
|
||||||
|
return $authError;
|
||||||
|
}
|
||||||
|
|
||||||
|
$requestLog = RequestLog::query()
|
||||||
|
->with(['plan', 'requestLogBenefits.benefit'])
|
||||||
|
->where('code', $request->noklaim)
|
||||||
|
->where('organization_id', $organization->id)
|
||||||
|
->first();
|
||||||
|
if (!$requestLog) {
|
||||||
|
return $this->statusError('Nomor klaim tidak ditemukan');
|
||||||
|
}
|
||||||
|
|
||||||
|
$biaya = $requestLog->requestLogBenefits->map(function (RequestLogBenefit $requestLogBenefit) use ($requestLog) {
|
||||||
|
return [
|
||||||
|
'noklaim' => $requestLog->code,
|
||||||
|
'kodesubbenefit' => optional($requestLogBenefit->benefit)->code,
|
||||||
|
'namasubbenefit' => optional($requestLogBenefit->benefit)->description,
|
||||||
|
'kodebenefit' => optional($requestLog->plan)->code,
|
||||||
|
'namabenefit' => optional($requestLog->plan)->corporate_plan_id,
|
||||||
|
'biayaaju' => (float) ($requestLogBenefit->amount_incurred ?? 0),
|
||||||
|
'jaminanasuransi' => (float) ($requestLogBenefit->amount_approved ?? 0),
|
||||||
|
'jaminanpeserta' => (float) ($requestLogBenefit->excess_paid ?? 0),
|
||||||
|
'keterangan' => $requestLogBenefit->keterangan,
|
||||||
|
];
|
||||||
|
})->values();
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'Status' => $this->okStatus(),
|
||||||
|
'Data' => [
|
||||||
|
'noklaim' => $requestLog->code,
|
||||||
|
'kodeprovider' => $organization->code,
|
||||||
|
'nosep' => $requestLog->nomor_sep,
|
||||||
|
'keterangan' => $requestLog->keterangan,
|
||||||
|
'statusklaim' => $requestLog->status,
|
||||||
|
],
|
||||||
|
'Biaya' => $biaya,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function downloadStrukPendaftaran(Request $request)
|
||||||
|
{
|
||||||
|
$request->validate([
|
||||||
|
'noklaim' => 'required|string',
|
||||||
|
'kodeprovider' => 'required|string',
|
||||||
|
'p_user_no' => 'required',
|
||||||
|
'p_token' => 'required|string',
|
||||||
|
]);
|
||||||
|
|
||||||
|
[$provider, $organization, $authError] = $this->resolveProvider($request->kodeprovider, $request->p_user_no, $request->p_token);
|
||||||
|
if ($authError) {
|
||||||
|
return $authError;
|
||||||
|
}
|
||||||
|
|
||||||
|
$requestLog = RequestLog::query()->where('code', $request->noklaim)->where('organization_id', $organization->id)->first();
|
||||||
|
if (!$requestLog) {
|
||||||
|
return $this->statusError('Nomor klaim tidak ditemukan');
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$requestLogController = new RequestLogController();
|
||||||
|
return $requestLogController->downlodLog($requestLog->id);
|
||||||
|
} catch (Throwable $e) {
|
||||||
|
$fallbackUrl = url('api/v1/hospitalportal/download-log/' . $requestLog->id);
|
||||||
|
return $this->statusError(
|
||||||
|
'Gagal generate PDF struk pendaftaran: ' . $e->getMessage() . '. request_log_id=' . $requestLog->id . ', fallback_url=' . $fallbackUrl,
|
||||||
|
500
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function downloadStrukPengesahan(Request $request)
|
||||||
|
{
|
||||||
|
$request->validate([
|
||||||
|
'noklaim' => 'required|string',
|
||||||
|
'kodeprovider' => 'required|string',
|
||||||
|
'p_user_no' => 'required',
|
||||||
|
'p_token' => 'required|string',
|
||||||
|
]);
|
||||||
|
|
||||||
|
[$provider, $organization, $authError] = $this->resolveProvider($request->kodeprovider, $request->p_user_no, $request->p_token);
|
||||||
|
if ($authError) {
|
||||||
|
return $authError;
|
||||||
|
}
|
||||||
|
|
||||||
|
$requestLog = RequestLog::query()->where('code', $request->noklaim)->where('organization_id', $organization->id)->first();
|
||||||
|
if (!$requestLog) {
|
||||||
|
return $this->statusError('Nomor klaim tidak ditemukan');
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$requestLogController = new RequestLogController();
|
||||||
|
return $requestLogController->downlodFinalLog($requestLog->id);
|
||||||
|
} catch (Throwable $e) {
|
||||||
|
$fallbackUrl = url('api/v1/hospitalportal/download-final-log/' . $requestLog->id);
|
||||||
|
return $this->statusError(
|
||||||
|
'Gagal generate PDF struk pengesahan: ' . $e->getMessage() . '. request_log_id=' . $requestLog->id . ', fallback_url=' . $fallbackUrl,
|
||||||
|
500
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function resolveProvider(string $kodeProvider, $userId, string $token): array
|
||||||
|
{
|
||||||
|
$organization = Organization::query()->where('code', $kodeProvider)->first();
|
||||||
|
if (!$organization) {
|
||||||
|
return [null, null, $this->statusError('Kode provider tidak ditemukan')];
|
||||||
|
}
|
||||||
|
|
||||||
|
$provider = Provider::query()
|
||||||
|
->where('id', $userId)
|
||||||
|
->where('organization_id', $organization->id)
|
||||||
|
->where('code', $kodeProvider)
|
||||||
|
->where('status', 'active')
|
||||||
|
->first();
|
||||||
|
if (!$provider) {
|
||||||
|
return [null, null, $this->statusError('User provider tidak ditemukan')];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((string) $provider->token !== (string) $token) {
|
||||||
|
return [null, null, $this->statusError('Token tidak valid')];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [$provider, $organization, null];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function resolvePlan(Member $member, string $serviceCode)
|
||||||
|
{
|
||||||
|
$memberPlan = $member->memberPlans()->with('plan')
|
||||||
|
->where('status', 'active')
|
||||||
|
->orderByDesc('id')
|
||||||
|
->get()
|
||||||
|
->first(function (MemberPlan $item) use ($serviceCode) {
|
||||||
|
return optional($item->plan)->service_code === $serviceCode;
|
||||||
|
});
|
||||||
|
|
||||||
|
if ($memberPlan && $memberPlan->plan) {
|
||||||
|
return $memberPlan->plan;
|
||||||
|
}
|
||||||
|
|
||||||
|
return optional($member->memberPlans()->with('plan')->orderByDesc('id')->first())->plan;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function okStatus(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'errornumber' => 0,
|
||||||
|
'messagestring' => 'Success',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function statusError(string $message, int $statusCode = 400)
|
||||||
|
{
|
||||||
|
return response()->json([
|
||||||
|
'Status' => [
|
||||||
|
'errornumber' => 1,
|
||||||
|
'messagestring' => $message,
|
||||||
|
],
|
||||||
|
'Data' => null,
|
||||||
|
], $statusCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function headerError(string $message, int $statusCode = 400)
|
||||||
|
{
|
||||||
|
return response()->json([
|
||||||
|
'header-token' => null,
|
||||||
|
'userid' => 0,
|
||||||
|
'usertoken' => null,
|
||||||
|
'kodeprovider' => null,
|
||||||
|
'namaprovider' => null,
|
||||||
|
'errornumber' => 1,
|
||||||
|
'messagestring' => $message,
|
||||||
|
], $statusCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function isoDate($date): ?string
|
||||||
|
{
|
||||||
|
if (empty($date)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Carbon::parse($date)->toISOString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function generateNextRequestLogCode(Organization $organization, Member $member): string
|
||||||
|
{
|
||||||
|
$data = [
|
||||||
|
'source' => 'H',
|
||||||
|
'provideCode' => $organization->code ?? '',
|
||||||
|
'date' => date('ymd'),
|
||||||
|
'policy' => optional($member->currentPolicy)->code ?? '-',
|
||||||
|
'member_code' => $member->member_id ?? '-',
|
||||||
|
];
|
||||||
|
|
||||||
|
$lastNumericCode = RequestLog::query()
|
||||||
|
->select(DB::raw('MAX(CAST(SUBSTRING_INDEX(code, ".", -1) AS SIGNED)) as max_numeric_code'))
|
||||||
|
->whereRaw('SUBSTRING_INDEX(code, ".", -1) REGEXP "^[0-9]+$"')
|
||||||
|
->value('max_numeric_code');
|
||||||
|
|
||||||
|
$nextNumber = $lastNumericCode ? ((int) $lastNumericCode + 1) : 1;
|
||||||
|
|
||||||
|
return $this->makeRequestLogCode($nextNumber, $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function makeRequestLogCode(int $nextNumber, array $data): string
|
||||||
|
{
|
||||||
|
$nextNumber = max(1, $nextNumber);
|
||||||
|
|
||||||
|
return implode('.', [
|
||||||
|
'LOG',
|
||||||
|
$data['source'],
|
||||||
|
$data['provideCode'],
|
||||||
|
$data['date'],
|
||||||
|
$data['policy'],
|
||||||
|
$data['member_code'],
|
||||||
|
str_pad((string) $nextNumber, 5, '0', STR_PAD_LEFT),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,6 +6,7 @@ use Modules\HospitalPortal\Http\Controllers\Api\ClaimRequestController;
|
|||||||
use Modules\HospitalPortal\Http\Controllers\Api\MemberController;
|
use Modules\HospitalPortal\Http\Controllers\Api\MemberController;
|
||||||
use Modules\HospitalPortal\Http\Controllers\ClaimController;
|
use Modules\HospitalPortal\Http\Controllers\ClaimController;
|
||||||
use Modules\HospitalPortal\Http\Controllers\Api\NotificationController;
|
use Modules\HospitalPortal\Http\Controllers\Api\NotificationController;
|
||||||
|
use Modules\HospitalPortal\Http\Controllers\Api\ProviderOnlineController;
|
||||||
use Modules\HospitalPortal\Http\Controllers\Api\RequestLogController;
|
use Modules\HospitalPortal\Http\Controllers\Api\RequestLogController;
|
||||||
use Modules\Internal\Http\Controllers\Api\RequestLogController as RequestLogControllerInternal;
|
use Modules\Internal\Http\Controllers\Api\RequestLogController as RequestLogControllerInternal;
|
||||||
use Modules\Internal\Http\Controllers\Api\RequestLogBenefitController;
|
use Modules\Internal\Http\Controllers\Api\RequestLogBenefitController;
|
||||||
@@ -135,4 +136,15 @@ Route::prefix('v1')->group(function() {
|
|||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Route::prefix('v1/bridging-service/ProviderOnline')->group(function () {
|
||||||
|
Route::post('HeaderKey', [ProviderOnlineController::class, 'getHeaderKey']);
|
||||||
|
Route::post('AddHeaderKey', [ProviderOnlineController::class, 'addHeaderKey']);
|
||||||
|
Route::post('EligibilitasPeserta', [ProviderOnlineController::class, 'checkEligibilitasPeserta']);
|
||||||
|
Route::post('Pendaftaran', [ProviderOnlineController::class, 'createPendaftaran']);
|
||||||
|
Route::post('Pengesahan', [ProviderOnlineController::class, 'createPengesahan']);
|
||||||
|
Route::post('RincianBiayaKlaim', [ProviderOnlineController::class, 'getRincianBiayaKlaim']);
|
||||||
|
Route::post('StrukPendaftaran', [ProviderOnlineController::class, 'downloadStrukPendaftaran']);
|
||||||
|
Route::post('StrukPengesahan', [ProviderOnlineController::class, 'downloadStrukPengesahan']);
|
||||||
|
});
|
||||||
|
|||||||
@@ -13,4 +13,5 @@
|
|||||||
|
|
||||||
Route::prefix('hospitalportal')->group(function() {
|
Route::prefix('hospitalportal')->group(function() {
|
||||||
Route::get('/', 'HospitalPortalController@index');
|
Route::get('/', 'HospitalPortalController@index');
|
||||||
|
Route::get('download-log/{id}', 'HospitalPortalController@downloadLog');
|
||||||
});
|
});
|
||||||
|
|||||||
32
app/Models/Provider.php
Normal file
32
app/Models/Provider.php
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
|
class Provider extends Model
|
||||||
|
{
|
||||||
|
use HasFactory;
|
||||||
|
|
||||||
|
protected $table = 'providers';
|
||||||
|
|
||||||
|
protected $fillable = [
|
||||||
|
'organization_id',
|
||||||
|
'username',
|
||||||
|
'password',
|
||||||
|
'code',
|
||||||
|
'status',
|
||||||
|
'header_token',
|
||||||
|
'token',
|
||||||
|
];
|
||||||
|
|
||||||
|
protected $hidden = [
|
||||||
|
'password',
|
||||||
|
];
|
||||||
|
|
||||||
|
public function organization()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Organization::class, 'organization_id', 'id');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -272,6 +272,11 @@ class RequestLog extends Model
|
|||||||
return $this->belongsTo(Service::class, 'service_code', 'code');
|
return $this->belongsTo(Service::class, 'service_code', 'code');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function plan()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Plan::class, 'plan_id');
|
||||||
|
}
|
||||||
|
|
||||||
public function requestLogBenefits()
|
public function requestLogBenefits()
|
||||||
{
|
{
|
||||||
return $this->hasMany(RequestLogBenefit::class, 'request_log_id');
|
return $this->hasMany(RequestLogBenefit::class, 'request_log_id');
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::create('providers', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
$table->foreignId('organization_id')->constrained('organizations')->cascadeOnDelete();
|
||||||
|
$table->string('username');
|
||||||
|
$table->string('password');
|
||||||
|
$table->string('code')->nullable();
|
||||||
|
$table->string('status')->default('active');
|
||||||
|
$table->string('header_token', 255)->nullable();
|
||||||
|
$table->string('token', 255)->nullable();
|
||||||
|
$table->timestamps();
|
||||||
|
|
||||||
|
$table->unique(['organization_id', 'username']);
|
||||||
|
$table->index(['organization_id', 'code', 'status']);
|
||||||
|
$table->index('token');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('providers');
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::table('request_logs', function (Blueprint $table) {
|
||||||
|
if (!Schema::hasColumn('request_logs', 'plan_id')) {
|
||||||
|
$table->unsignedBigInteger('plan_id')->nullable()->after('member_id');
|
||||||
|
$table->index('plan_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Schema::hasColumn('request_logs', 'admission_date')) {
|
||||||
|
$table->dateTime('admission_date')->nullable()->after('submission_date');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Schema::hasColumn('request_logs', 'nomor_sep')) {
|
||||||
|
$table->string('nomor_sep')->nullable()->after('keterangan');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Schema::hasColumn('request_logs', 'status_rujukan')) {
|
||||||
|
$table->string('status_rujukan')->nullable()->after('nomor_sep');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Schema::hasColumn('request_logs', 'nomor_rujukan')) {
|
||||||
|
$table->string('nomor_rujukan')->nullable()->after('status_rujukan');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Schema::hasColumn('request_logs', 'no_transaksi_provider')) {
|
||||||
|
$table->string('no_transaksi_provider')->nullable()->after('nomor_rujukan');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Schema::hasColumn('request_logs', 'inacbgs_code')) {
|
||||||
|
$table->string('inacbgs_code')->nullable()->after('no_transaksi_provider');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Schema::hasColumn('request_logs', 'inacbgs_amount')) {
|
||||||
|
$table->decimal('inacbgs_amount', 18, 2)->nullable()->after('inacbgs_code');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Schema::table('request_logs', function (Blueprint $table) {
|
||||||
|
if (Schema::hasColumn('request_logs', 'inacbgs_amount')) {
|
||||||
|
$table->dropColumn('inacbgs_amount');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Schema::hasColumn('request_logs', 'inacbgs_code')) {
|
||||||
|
$table->dropColumn('inacbgs_code');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Schema::hasColumn('request_logs', 'no_transaksi_provider')) {
|
||||||
|
$table->dropColumn('no_transaksi_provider');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Schema::hasColumn('request_logs', 'nomor_rujukan')) {
|
||||||
|
$table->dropColumn('nomor_rujukan');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Schema::hasColumn('request_logs', 'status_rujukan')) {
|
||||||
|
$table->dropColumn('status_rujukan');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Schema::hasColumn('request_logs', 'nomor_sep')) {
|
||||||
|
$table->dropColumn('nomor_sep');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Schema::hasColumn('request_logs', 'admission_date')) {
|
||||||
|
$table->dropColumn('admission_date');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Schema::hasColumn('request_logs', 'plan_id')) {
|
||||||
|
$table->dropIndex(['plan_id']);
|
||||||
|
$table->dropColumn('plan_id');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
242
docs/ai-agent-guide.id.md
Normal file
242
docs/ai-agent-guide.id.md
Normal file
@@ -0,0 +1,242 @@
|
|||||||
|
# Panduan Penggunaan AI Agent (ASO)
|
||||||
|
|
||||||
|
Panduan ini menjelaskan cara tim ASO menyiapkan dan memakai AI coding agent secara aman di repository ini.
|
||||||
|
|
||||||
|
Tujuan utamanya adalah memberi AI konteks project, command yang benar, dan batasan safety agar AI membantu lebih cepat tanpa merusak codebase.
|
||||||
|
|
||||||
|
## Prinsip Utama
|
||||||
|
|
||||||
|
1. Human tetap pemilik keputusan final.
|
||||||
|
2. AI wajib mengikuti konvensi project ASO.
|
||||||
|
3. AI tidak boleh commit/push kecuali diminta eksplisit.
|
||||||
|
4. AI harus membuat rencana singkat sebelum perubahan besar/berisiko.
|
||||||
|
5. AI harus menjalankan verifikasi relevan sebelum menyatakan selesai.
|
||||||
|
6. Human wajib review area kritikal: security, auth/permission, scoping corporate/member, migration, payments, data deletion, production config.
|
||||||
|
7. Repository ini wajib memiliki `AGENTS.md` di root.
|
||||||
|
|
||||||
|
## Konteks Project Ini
|
||||||
|
|
||||||
|
ASO adalah platform operasional health insurance/managed care untuk pengelolaan corporate, member, claim, monitoring, livechat, dan pelaporan.
|
||||||
|
|
||||||
|
Stack utama saat ini:
|
||||||
|
|
||||||
|
- Backend: Laravel 9 (`app/`) + modular backend `nwidart/laravel-modules` (`Modules/`)
|
||||||
|
- Frontend: `frontend/dashboard` dan `frontend/client-portal` (React + TS + Vite), plus assets root Laravel (`resources/`, Vite/Mix)
|
||||||
|
- Auth/Permission: Sanctum + Spatie Permission (`role`, `permission`) + beberapa flow JWT untuk integrasi tertentu
|
||||||
|
- Testing backend: PHPUnit (`tests/Feature`, `tests/Unit`)
|
||||||
|
|
||||||
|
## AI Agent Ready untuk Repo ASO
|
||||||
|
|
||||||
|
Repository ini dianggap AI Agent Ready jika AI bisa cepat menjawab:
|
||||||
|
|
||||||
|
- Struktur backend modular dan batas tiap module
|
||||||
|
- Command setup/run/test/lint/build yang benar untuk root + dua frontend app
|
||||||
|
- Aturan peletakan business logic
|
||||||
|
- Aturan auth/permission/scoping corporate-member pada endpoint terlindungi
|
||||||
|
- Aturan migration aman
|
||||||
|
- Aturan git (tidak commit/push tanpa instruksi)
|
||||||
|
|
||||||
|
## File Minimum yang Harus Ada
|
||||||
|
|
||||||
|
```text
|
||||||
|
.
|
||||||
|
├── AGENTS.md
|
||||||
|
├── README.md
|
||||||
|
├── .env-example (dan/atau .env.example)
|
||||||
|
└── docs/
|
||||||
|
└── ai-agent-guide.id.md
|
||||||
|
```
|
||||||
|
|
||||||
|
Disarankan ditambah:
|
||||||
|
|
||||||
|
```text
|
||||||
|
docs/architecture.md
|
||||||
|
docs/testing.md
|
||||||
|
docs/database.md
|
||||||
|
docs/api-contracts.md
|
||||||
|
```
|
||||||
|
|
||||||
|
## Command Operasional (Aktual Repo Ini)
|
||||||
|
|
||||||
|
Install dependencies:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
composer install
|
||||||
|
npm install
|
||||||
|
cd frontend/dashboard && yarn install
|
||||||
|
cd ../client-portal && yarn install
|
||||||
|
```
|
||||||
|
|
||||||
|
Menjalankan aplikasi lokal:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
php artisan serve
|
||||||
|
npm run dev
|
||||||
|
cd frontend/dashboard && yarn start
|
||||||
|
cd ../client-portal && yarn start
|
||||||
|
```
|
||||||
|
|
||||||
|
Testing backend:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
php artisan test
|
||||||
|
```
|
||||||
|
|
||||||
|
Lint frontend:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd frontend/dashboard && yarn lint
|
||||||
|
cd ../client-portal && yarn lint
|
||||||
|
```
|
||||||
|
|
||||||
|
Build frontend:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run production
|
||||||
|
cd frontend/dashboard && yarn build
|
||||||
|
cd ../client-portal && yarn build
|
||||||
|
```
|
||||||
|
|
||||||
|
Migration:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
php artisan migrate
|
||||||
|
```
|
||||||
|
|
||||||
|
Catatan:
|
||||||
|
|
||||||
|
- Script `typecheck` standar belum tersedia konsisten di root/subproject; jika perlu, jalankan `tsc` manual sesuai kebutuhan task.
|
||||||
|
- Repo ini memiliki lockfile campuran (`package-lock.json`, `pnpm-lock.yaml`, `yarn.lock`). Ikuti lockfile dan package manager yang sudah dipakai pada subproject yang disentuh.
|
||||||
|
|
||||||
|
## Struktur Repository Penting
|
||||||
|
|
||||||
|
```text
|
||||||
|
app/ # Core Laravel app
|
||||||
|
routes/ # Root routes (web/api)
|
||||||
|
Modules/ # Module domain (Client, Internal, Linksehat, Primaya, HospitalPortal)
|
||||||
|
Modules/*/Routes/api.php # API route per module
|
||||||
|
database/migrations/ # Root migrations
|
||||||
|
frontend/dashboard/ # Internal/admin frontend
|
||||||
|
frontend/client-portal/ # Client portal frontend
|
||||||
|
resources/ # View/assets Laravel utama
|
||||||
|
public/ # Public assets + output build frontend
|
||||||
|
tests/ # PHPUnit tests
|
||||||
|
```
|
||||||
|
|
||||||
|
## Aturan Arsitektur untuk Agent
|
||||||
|
|
||||||
|
- Gunakan pattern yang sudah ada sebelum menambah pattern baru.
|
||||||
|
- Jaga controller tetap tipis; letakkan logic di service/domain yang sudah ada (`app/Services`, `Modules/*/Services`).
|
||||||
|
- Untuk API module, tambahkan route dan controller dalam module yang sesuai; hindari menaruh logic module di tempat acak.
|
||||||
|
- Jangan cross-import antar frontend app (`dashboard` vs `client-portal`) kecuali memang sudah ada kontrak eksplisit.
|
||||||
|
- Jika mengubah perilaku endpoint protected, review middleware terkait (`auth:sanctum`, role/permission, middleware custom module).
|
||||||
|
|
||||||
|
## Workflow AI Harian
|
||||||
|
|
||||||
|
### 1. Mulai Task
|
||||||
|
|
||||||
|
- Baca `AGENTS.md`.
|
||||||
|
- Pahami pattern existing di area yang akan diubah.
|
||||||
|
- Buat plan singkat untuk task non-trivial.
|
||||||
|
- Klarifikasi requirement jika masih ambigu.
|
||||||
|
|
||||||
|
### 2. Feature Work
|
||||||
|
|
||||||
|
- Implement perubahan kecil dan fokus.
|
||||||
|
- Hindari refactor unrelated.
|
||||||
|
- Tambah/update test saat behavior berubah.
|
||||||
|
- Jalankan verifikasi relevan.
|
||||||
|
|
||||||
|
### 3. Bug Fixing
|
||||||
|
|
||||||
|
- Reproduce/inspeksi issue terlebih dahulu.
|
||||||
|
- Temukan root cause.
|
||||||
|
- Terapkan fix minimal dan aman.
|
||||||
|
- Tambah regression test jika memungkinkan.
|
||||||
|
|
||||||
|
### 4. Review Sebelum PR
|
||||||
|
|
||||||
|
Periksa:
|
||||||
|
|
||||||
|
- correctness
|
||||||
|
- security
|
||||||
|
- auth/permission/scoping
|
||||||
|
- migration safety
|
||||||
|
- missing tests
|
||||||
|
- lint/type/build impact
|
||||||
|
- perubahan unrelated
|
||||||
|
|
||||||
|
## Definition of Done untuk Task AI
|
||||||
|
|
||||||
|
Task AI belum selesai sampai:
|
||||||
|
|
||||||
|
- Perubahan mengikuti pattern existing.
|
||||||
|
- Tidak ada perubahan unrelated.
|
||||||
|
- Test/lint/build relevan sudah dijalankan atau alasannya dijelaskan.
|
||||||
|
- Risiko migration/auth/scoping dijelaskan jika relevan.
|
||||||
|
- File berubah dan command yang dijalankan dicatat.
|
||||||
|
|
||||||
|
## Safety Rules (Wajib)
|
||||||
|
|
||||||
|
AI agent tidak boleh:
|
||||||
|
|
||||||
|
- Commit/push/rewrite history tanpa instruksi eksplisit.
|
||||||
|
- Mengubah `.env` asli tanpa instruksi eksplisit.
|
||||||
|
- Print secrets ke output/chat.
|
||||||
|
- Menghapus data/folder besar tanpa konfirmasi.
|
||||||
|
- Mengubah schema DB tanpa migration baru dan catatan safety.
|
||||||
|
- Menambah dependency besar tanpa alasan jelas dan dampak.
|
||||||
|
|
||||||
|
## Prompt Siap Pakai untuk Repo Ini
|
||||||
|
|
||||||
|
### Setup / Sinkronisasi Dokumentasi
|
||||||
|
|
||||||
|
```text
|
||||||
|
Baca AGENTS.md dan docs/ai-agent-guide.id.md.
|
||||||
|
Sinkronkan dokumentasi AI agent dengan kondisi codebase saat ini.
|
||||||
|
Validasi command setup/test/lint/build berdasarkan file konfigurasi aktual.
|
||||||
|
Jangan commit perubahan.
|
||||||
|
```
|
||||||
|
|
||||||
|
### Feature Work
|
||||||
|
|
||||||
|
```text
|
||||||
|
Implement feature berikut: [jelaskan].
|
||||||
|
Rules:
|
||||||
|
- Baca AGENTS.md terlebih dahulu.
|
||||||
|
- Ikuti pattern existing repo ASO.
|
||||||
|
- Jangan commit.
|
||||||
|
- Jalankan verifikasi relevan.
|
||||||
|
Output akhir wajib: Summary, Files changed, Commands run, Risks/notes.
|
||||||
|
```
|
||||||
|
|
||||||
|
### Bug Fixing
|
||||||
|
|
||||||
|
```text
|
||||||
|
Fix bug berikut: [jelaskan].
|
||||||
|
Gunakan systematic debugging:
|
||||||
|
1. Reproduce/inspeksi issue
|
||||||
|
2. Temukan root cause
|
||||||
|
3. Terapkan fix minimal aman
|
||||||
|
4. Tambah regression test jika memungkinkan
|
||||||
|
5. Jalankan verifikasi
|
||||||
|
Jangan commit.
|
||||||
|
```
|
||||||
|
|
||||||
|
## Format Final Response Standar
|
||||||
|
|
||||||
|
Gunakan format ini:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Summary:
|
||||||
|
- ...
|
||||||
|
|
||||||
|
Files changed:
|
||||||
|
- ...
|
||||||
|
|
||||||
|
Commands run:
|
||||||
|
- ...
|
||||||
|
|
||||||
|
Risks / notes:
|
||||||
|
- ...
|
||||||
|
```
|
||||||
242
docs/ai-agent-guide.md
Normal file
242
docs/ai-agent-guide.md
Normal file
@@ -0,0 +1,242 @@
|
|||||||
|
# AI Agent Usage Guide (ASO)
|
||||||
|
|
||||||
|
This guide explains how the ASO team should prepare and use AI coding agents safely in this repository.
|
||||||
|
|
||||||
|
The main goal is to provide the AI with correct project context, commands, and safety boundaries so it can help faster without damaging the codebase.
|
||||||
|
|
||||||
|
## Core Principles
|
||||||
|
|
||||||
|
1. Humans make the final decisions.
|
||||||
|
2. AI must follow ASO project conventions.
|
||||||
|
3. AI must not commit/push unless explicitly asked.
|
||||||
|
4. AI should create a short plan before large/risky changes.
|
||||||
|
5. AI must run relevant verification checks before saying work is done.
|
||||||
|
6. Humans must review critical areas: security, auth/permission, corporate/member scoping, migrations, payments, data deletion, and production config.
|
||||||
|
7. This repository must have `AGENTS.md` at the root.
|
||||||
|
|
||||||
|
## Project Context
|
||||||
|
|
||||||
|
ASO is an operational health insurance/managed-care platform for managing corporates, members, claims, monitoring, livechat, and reporting.
|
||||||
|
|
||||||
|
Current main stack:
|
||||||
|
|
||||||
|
- Backend: Laravel 9 (`app/`) + modular backend with `nwidart/laravel-modules` (`Modules/`)
|
||||||
|
- Frontend: `frontend/dashboard` and `frontend/client-portal` (React + TypeScript + Vite), plus Laravel root assets (`resources/`, Vite/Mix)
|
||||||
|
- Auth/Permission: Sanctum + Spatie Permission (`role`, `permission`) + some JWT flows for specific integrations
|
||||||
|
- Backend testing: PHPUnit (`tests/Feature`, `tests/Unit`)
|
||||||
|
|
||||||
|
## AI Agent Ready in ASO Repo
|
||||||
|
|
||||||
|
This repository is considered AI Agent Ready when an AI can quickly answer:
|
||||||
|
|
||||||
|
- Modular backend structure and module boundaries
|
||||||
|
- Correct setup/run/test/lint/build commands for root + both frontend apps
|
||||||
|
- Where business logic should live
|
||||||
|
- Auth/permission/corporate-member scoping rules on protected endpoints
|
||||||
|
- Safe migration rules
|
||||||
|
- Git rules (no commit/push without instruction)
|
||||||
|
|
||||||
|
## Minimum Required Files
|
||||||
|
|
||||||
|
```text
|
||||||
|
.
|
||||||
|
├── AGENTS.md
|
||||||
|
├── README.md
|
||||||
|
├── .env-example (and/or .env.example)
|
||||||
|
└── docs/
|
||||||
|
└── ai-agent-guide.id.md
|
||||||
|
```
|
||||||
|
|
||||||
|
Recommended additions:
|
||||||
|
|
||||||
|
```text
|
||||||
|
docs/architecture.md
|
||||||
|
docs/testing.md
|
||||||
|
docs/database.md
|
||||||
|
docs/api-contracts.md
|
||||||
|
```
|
||||||
|
|
||||||
|
## Operational Commands (Current Repo)
|
||||||
|
|
||||||
|
Install dependencies:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
composer install
|
||||||
|
npm install
|
||||||
|
cd frontend/dashboard && yarn install
|
||||||
|
cd ../client-portal && yarn install
|
||||||
|
```
|
||||||
|
|
||||||
|
Run locally:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
php artisan serve
|
||||||
|
npm run dev
|
||||||
|
cd frontend/dashboard && yarn start
|
||||||
|
cd ../client-portal && yarn start
|
||||||
|
```
|
||||||
|
|
||||||
|
Backend tests:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
php artisan test
|
||||||
|
```
|
||||||
|
|
||||||
|
Frontend lint:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd frontend/dashboard && yarn lint
|
||||||
|
cd ../client-portal && yarn lint
|
||||||
|
```
|
||||||
|
|
||||||
|
Frontend build:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run production
|
||||||
|
cd frontend/dashboard && yarn build
|
||||||
|
cd ../client-portal && yarn build
|
||||||
|
```
|
||||||
|
|
||||||
|
Migrations:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
php artisan migrate
|
||||||
|
```
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
|
||||||
|
- A standard `typecheck` script is not consistently available across root/subprojects; run manual `tsc` only when needed for the task.
|
||||||
|
- This repo has mixed lockfiles (`package-lock.json`, `pnpm-lock.yaml`, `yarn.lock`). Follow the lockfile and package manager already used in the touched subproject.
|
||||||
|
|
||||||
|
## Key Repository Structure
|
||||||
|
|
||||||
|
```text
|
||||||
|
app/ # Core Laravel app
|
||||||
|
routes/ # Root routes (web/api)
|
||||||
|
Modules/ # Domain modules (Client, Internal, Linksehat, Primaya, HospitalPortal)
|
||||||
|
Modules/*/Routes/api.php # API route entry per module
|
||||||
|
database/migrations/ # Root migrations
|
||||||
|
frontend/dashboard/ # Internal/admin frontend
|
||||||
|
frontend/client-portal/ # Client portal frontend
|
||||||
|
resources/ # Main Laravel views/assets
|
||||||
|
public/ # Public assets + frontend build output
|
||||||
|
tests/ # PHPUnit tests
|
||||||
|
```
|
||||||
|
|
||||||
|
## Architecture Rules for Agents
|
||||||
|
|
||||||
|
- Follow existing patterns before introducing new ones.
|
||||||
|
- Keep controllers thin; place logic in existing service/domain layers (`app/Services`, `Modules/*/Services`).
|
||||||
|
- For module APIs, add routes/controllers within the correct module; avoid placing module logic in random locations.
|
||||||
|
- Do not cross-import between frontend apps (`dashboard` vs `client-portal`) unless there is an explicit existing contract.
|
||||||
|
- When changing protected endpoint behavior, review related middleware (`auth:sanctum`, role/permission, module-specific middleware).
|
||||||
|
|
||||||
|
## Daily AI Workflow
|
||||||
|
|
||||||
|
### 1. Start a Task
|
||||||
|
|
||||||
|
- Read `AGENTS.md`.
|
||||||
|
- Understand existing patterns in the area you will change.
|
||||||
|
- Create a short plan for non-trivial work.
|
||||||
|
- Clarify ambiguous requirements.
|
||||||
|
|
||||||
|
### 2. Feature Work
|
||||||
|
|
||||||
|
- Keep changes small and focused.
|
||||||
|
- Avoid unrelated refactors.
|
||||||
|
- Add/update tests when behavior changes.
|
||||||
|
- Run relevant verification commands.
|
||||||
|
|
||||||
|
### 3. Bug Fixing
|
||||||
|
|
||||||
|
- Reproduce/inspect the issue first.
|
||||||
|
- Find the root cause.
|
||||||
|
- Apply a minimal, safe fix.
|
||||||
|
- Add a regression test when possible.
|
||||||
|
|
||||||
|
### 4. Pre-PR Review
|
||||||
|
|
||||||
|
Check:
|
||||||
|
|
||||||
|
- correctness
|
||||||
|
- security
|
||||||
|
- auth/permission/scoping
|
||||||
|
- migration safety
|
||||||
|
- missing tests
|
||||||
|
- lint/type/build impact
|
||||||
|
- unrelated changes
|
||||||
|
|
||||||
|
## Definition of Done for AI Tasks
|
||||||
|
|
||||||
|
AI work is not done until:
|
||||||
|
|
||||||
|
- Changes follow existing patterns.
|
||||||
|
- No unrelated files are changed.
|
||||||
|
- Relevant test/lint/build checks are run or failures are explained.
|
||||||
|
- Migration/auth/scoping risks are explained when relevant.
|
||||||
|
- Changed files and commands run are documented.
|
||||||
|
|
||||||
|
## Safety Rules (Mandatory)
|
||||||
|
|
||||||
|
AI agents must not:
|
||||||
|
|
||||||
|
- Commit/push/rewrite history without explicit instruction.
|
||||||
|
- Modify real `.env` values without explicit instruction.
|
||||||
|
- Print secrets in chat/output.
|
||||||
|
- Delete large data/folders without confirmation.
|
||||||
|
- Change DB schema without new migrations and safety notes.
|
||||||
|
- Add major dependencies without clear justification and impact.
|
||||||
|
|
||||||
|
## Ready-to-Use Prompts for This Repo
|
||||||
|
|
||||||
|
### Setup / Documentation Sync
|
||||||
|
|
||||||
|
```text
|
||||||
|
Read AGENTS.md and docs/ai-agent-guide.id.md.
|
||||||
|
Sync AI-agent documentation with the current codebase state.
|
||||||
|
Validate setup/test/lint/build commands against actual config files.
|
||||||
|
Do not commit changes.
|
||||||
|
```
|
||||||
|
|
||||||
|
### Feature Work
|
||||||
|
|
||||||
|
```text
|
||||||
|
Implement this feature: [describe].
|
||||||
|
Rules:
|
||||||
|
- Read AGENTS.md first.
|
||||||
|
- Follow existing ASO repository patterns.
|
||||||
|
- Do not commit.
|
||||||
|
- Run relevant verification commands.
|
||||||
|
Final output must include: Summary, Files changed, Commands run, Risks/notes.
|
||||||
|
```
|
||||||
|
|
||||||
|
### Bug Fixing
|
||||||
|
|
||||||
|
```text
|
||||||
|
Fix this bug: [describe].
|
||||||
|
Use systematic debugging:
|
||||||
|
1. Reproduce/inspect issue
|
||||||
|
2. Find root cause
|
||||||
|
3. Apply minimal safe fix
|
||||||
|
4. Add regression test if possible
|
||||||
|
5. Run verification
|
||||||
|
Do not commit.
|
||||||
|
```
|
||||||
|
|
||||||
|
## Standard Final Response Format
|
||||||
|
|
||||||
|
Use this format:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Summary:
|
||||||
|
- ...
|
||||||
|
|
||||||
|
Files changed:
|
||||||
|
- ...
|
||||||
|
|
||||||
|
Commands run:
|
||||||
|
- ...
|
||||||
|
|
||||||
|
Risks / notes:
|
||||||
|
- ...
|
||||||
|
```
|
||||||
@@ -216,8 +216,9 @@
|
|||||||
<div class="triangle2"></div>
|
<div class="triangle2"></div>
|
||||||
<div class="triangle1"></div>
|
<div class="triangle1"></div>
|
||||||
@php
|
@php
|
||||||
if(!empty($logoPerusahaan->path)) {
|
$logoPath = !empty($logoPerusahaan->path) ? storage_path('app/public/' . $logoPerusahaan->path) : null;
|
||||||
$imgSrc = 'data:image/png;base64,' . base64_encode(file_get_contents(storage_path('app/public/' . $logoPerusahaan->path)));
|
if (!empty($logoPath) && is_file($logoPath)) {
|
||||||
|
$imgSrc = 'data:image/png;base64,' . base64_encode(file_get_contents($logoPath));
|
||||||
echo '<img class="logo_company-' . now()->timestamp . '" src="' . $imgSrc . '">';
|
echo '<img class="logo_company-' . now()->timestamp . '" src="' . $imgSrc . '">';
|
||||||
}
|
}
|
||||||
@endphp
|
@endphp
|
||||||
|
|||||||
@@ -216,8 +216,9 @@
|
|||||||
<div class="triangle2"></div>
|
<div class="triangle2"></div>
|
||||||
<div class="triangle1"></div>
|
<div class="triangle1"></div>
|
||||||
@php
|
@php
|
||||||
if(!empty($logoPerusahaan->path)) {
|
$logoPath = !empty($logoPerusahaan->path) ? storage_path('app/public/' . $logoPerusahaan->path) : null;
|
||||||
$imgSrc = 'data:image/png;base64,' . base64_encode(file_get_contents(storage_path('app/public/' . $logoPerusahaan->path)));
|
if (!empty($logoPath) && is_file($logoPath)) {
|
||||||
|
$imgSrc = 'data:image/png;base64,' . base64_encode(file_get_contents($logoPath));
|
||||||
echo '<img class="logo_company-' . now()->timestamp . '" src="' . $imgSrc . '">';
|
echo '<img class="logo_company-' . now()->timestamp . '" src="' . $imgSrc . '">';
|
||||||
}
|
}
|
||||||
@endphp
|
@endphp
|
||||||
|
|||||||
@@ -210,8 +210,9 @@
|
|||||||
<div class="triangle2"></div>
|
<div class="triangle2"></div>
|
||||||
<div class="triangle1"></div>
|
<div class="triangle1"></div>
|
||||||
@php
|
@php
|
||||||
if(!empty($logoPerusahaan->path)) {
|
$logoPath = !empty($logoPerusahaan->path) ? storage_path('app/public/' . $logoPerusahaan->path) : null;
|
||||||
$imgSrc = 'data:image/png;base64,' . base64_encode(file_get_contents(storage_path('app/public/' . $logoPerusahaan->path)));
|
if (!empty($logoPath) && is_file($logoPath)) {
|
||||||
|
$imgSrc = 'data:image/png;base64,' . base64_encode(file_get_contents($logoPath));
|
||||||
echo '<img class="logo_company-' . now()->timestamp . '" src="' . $imgSrc . '">';
|
echo '<img class="logo_company-' . now()->timestamp . '" src="' . $imgSrc . '">';
|
||||||
}
|
}
|
||||||
@endphp
|
@endphp
|
||||||
|
|||||||
@@ -210,8 +210,9 @@
|
|||||||
<div class="triangle2"></div>
|
<div class="triangle2"></div>
|
||||||
<div class="triangle1"></div>
|
<div class="triangle1"></div>
|
||||||
@php
|
@php
|
||||||
if(!empty($logoPerusahaan->path)) {
|
$logoPath = !empty($logoPerusahaan->path) ? storage_path('app/public/' . $logoPerusahaan->path) : null;
|
||||||
$imgSrc = 'data:image/png;base64,' . base64_encode(file_get_contents(storage_path('app/public/' . $logoPerusahaan->path)));
|
if (!empty($logoPath) && is_file($logoPath)) {
|
||||||
|
$imgSrc = 'data:image/png;base64,' . base64_encode(file_get_contents($logoPath));
|
||||||
echo '<img class="logo_company-' . now()->timestamp . '" src="' . $imgSrc . '">';
|
echo '<img class="logo_company-' . now()->timestamp . '" src="' . $imgSrc . '">';
|
||||||
}
|
}
|
||||||
@endphp
|
@endphp
|
||||||
|
|||||||
@@ -224,8 +224,9 @@
|
|||||||
<div class="triangle2"></div>
|
<div class="triangle2"></div>
|
||||||
<div class="triangle1"></div>
|
<div class="triangle1"></div>
|
||||||
@php
|
@php
|
||||||
if(!empty($logoPerusahaan->path)) {
|
$logoPath = !empty($logoPerusahaan->path) ? storage_path('app/public/' . $logoPerusahaan->path) : null;
|
||||||
$imgSrc = 'data:image/png;base64,' . base64_encode(file_get_contents(storage_path('app/public/' . $logoPerusahaan->path)));
|
if (!empty($logoPath) && is_file($logoPath)) {
|
||||||
|
$imgSrc = 'data:image/png;base64,' . base64_encode(file_get_contents($logoPath));
|
||||||
echo '<img class="logo_company-' . now()->timestamp . '" src="' . $imgSrc . '">';
|
echo '<img class="logo_company-' . now()->timestamp . '" src="' . $imgSrc . '">';
|
||||||
}
|
}
|
||||||
@endphp
|
@endphp
|
||||||
|
|||||||
@@ -222,8 +222,9 @@
|
|||||||
<div class="triangle2"></div>
|
<div class="triangle2"></div>
|
||||||
<div class="triangle1"></div>
|
<div class="triangle1"></div>
|
||||||
@php
|
@php
|
||||||
if(!empty($logoPerusahaan->path)) {
|
$logoPath = !empty($logoPerusahaan->path) ? storage_path('app/public/' . $logoPerusahaan->path) : null;
|
||||||
$imgSrc = 'data:image/png;base64,' . base64_encode(file_get_contents(storage_path('app/public/' . $logoPerusahaan->path)));
|
if (!empty($logoPath) && is_file($logoPath)) {
|
||||||
|
$imgSrc = 'data:image/png;base64,' . base64_encode(file_get_contents($logoPath));
|
||||||
echo '<img class="logo_company-' . now()->timestamp . '" src="' . $imgSrc . '">';
|
echo '<img class="logo_company-' . now()->timestamp . '" src="' . $imgSrc . '">';
|
||||||
}
|
}
|
||||||
@endphp
|
@endphp
|
||||||
|
|||||||
Reference in New Issue
Block a user