big update all menus

This commit is contained in:
Wian Drs
2026-08-07 09:16:13 +07:00
parent 75342dc7b7
commit e7575ba1c0
101 changed files with 3451 additions and 89 deletions
@@ -10,6 +10,8 @@ use App\Http\Requests\Customer\UpdateCustomerRequest;
use App\Http\Resources\Customer\CustomerResource;
use App\Services\Customer\CustomerService;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Http;
use Illuminate\Validation\ValidationException;
class CustomerController extends ApiController
{
@@ -32,6 +34,60 @@ class CustomerController extends ApiController
);
}
public function odpPorts(Request $request, int $node)
{
$data=$request->validate(['customer_id'=>['nullable','integer','exists:customers,id']]);
return $this->success($this->service->availableOdpPorts($node,$request->user(),$this->tenantId($request),$data['customer_id'] ?? null),'Port ODP tersedia berhasil diambil');
}
public function ocrKtp(Request $request)
{
$data = $request->validate([
'image' => ['required', 'image', 'mimes:jpeg,jpg,png,webp', 'max:5120'],
'tenant_id' => ['nullable', 'integer', 'exists:tenants,id'],
]);
$tenantId = $request->user()->isMasterAdmin()
? ($data['tenant_id'] ?? $request->attributes->get('tenant_id'))
: $request->attributes->get('tenant_id');
abort_unless($tenantId, 422, 'Tenant wajib dipilih sebelum menjalankan OCR.');
if (! $request->user()->isMasterAdmin()) {
abort_unless($request->user()->userTenants()->where('tenant_id', $tenantId)->exists(), 403);
}
$setting = \App\Models\TenantInstallationSetting::where('tenant_id', $tenantId)->first();
abort_unless($setting?->ktp_ocr_enabled, 422, 'OCR KTP belum diaktifkan untuk tenant ini.');
abort_unless($setting->ocr_provider && $setting->ocr_api_key, 422, 'Provider atau API key OCR belum dikonfigurasi.');
$file = $data['image'];
$mime = $file->getMimeType();
$base64 = base64_encode(file_get_contents($file->getRealPath()));
$prompt = 'Baca KTP Indonesia ini. Kembalikan hanya JSON valid tanpa markdown dengan field: nik, nama, alamat, rt_rw, kelurahan_desa, kecamatan, kabupaten_kota, provinsi. Gunakan null bila tidak terbaca.';
if ($setting->ocr_provider === 'openai') {
$response = Http::withToken($setting->ocr_api_key)->timeout(60)->post('https://api.openai.com/v1/chat/completions', [
'model' => 'gpt-4.1-mini',
'response_format' => ['type' => 'json_object'],
'messages' => [['role' => 'user', 'content' => [
['type' => 'text', 'text' => $prompt],
['type' => 'image_url', 'image_url' => ['url' => "data:{$mime};base64,{$base64}"]],
]]],
]);
$content = $response->successful() ? $response->json('choices.0.message.content') : null;
} else {
$response = Http::timeout(60)->post('https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key='.urlencode($setting->ocr_api_key), [
'contents' => [['parts' => [['text' => $prompt], ['inline_data' => ['mime_type' => $mime, 'data' => $base64]]]]],
'generationConfig' => ['responseMimeType' => 'application/json'],
]);
$content = $response->successful() ? $response->json('candidates.0.content.parts.0.text') : null;
}
if (! $response->successful() || ! $content) {
throw ValidationException::withMessages(['image' => 'Provider AI gagal membaca KTP. Periksa API key, kuota, dan kualitas gambar.']);
}
$result = json_decode(trim(str_replace(['```json', '```'], '', $content)), true);
if (! is_array($result)) throw ValidationException::withMessages(['image' => 'Respons OCR tidak dapat diproses.']);
return $this->success($result, 'KTP berhasil dibaca');
}
public function store(StoreCustomerRequest $request)
{
return $this->created(
@@ -59,12 +115,41 @@ class CustomerController extends ApiController
);
}
public function prepareActivation(UpdateCustomerRequest $request, int $id)
{
$customer = $this->service->find($id);
$this->authorizeTenant($request, $customer->tenant_id);
$mode = \App\Models\TenantInstallationSetting::firstOrCreate(
['tenant_id' => $customer->tenant_id],
['activation_mode' => 'list_order']
)->activation_mode;
abort_unless($mode === 'list_order', 422, 'Tenant mengatur persiapan aktivasi melalui Ticket.');
$customer = $this->service->update($customer, $request->validated(), $request->user());
$customer = $this->service->ensureActivationCode($customer);
return $this->updated(
new CustomerResource($customer),
'Persiapan aktivasi berhasil disimpan'
);
}
public function updateRegistration(UpdateCustomerRequest $request, int $id)
{
$customer = $this->service->find($id);
$this->authorizeTenant($request, $customer->tenant_id);
return $this->updated(
new CustomerResource($this->service->update($customer, $request->validated(), $request->user())),
'Data registrasi berhasil disimpan'
);
}
public function activate(ActivateCustomerRequest $request, int $id)
{
$customer = $this->service->find($id);
$this->authorizeTenant($request, $customer->tenant_id);
return $this->updated(new CustomerResource($this->service->activate($customer, $request->validated())), 'Customer berhasil diaktifkan');
return $this->updated(new CustomerResource($this->service->activate($customer, $request->validated(), $request->user())), 'Customer berhasil diaktifkan');
}
public function deactivate(Request $request, int $id)
@@ -84,6 +169,16 @@ class CustomerController extends ApiController
return $this->deleted('Customer dipindahkan ke Sampah');
}
public function cancelOrder(Request $request, int $id)
{
$customer = $this->service->find($id);
$this->authorizeTenant($request, $customer->tenant_id);
abort_unless($customer->status === 'order', 422, 'Hanya customer dalam List Order yang dapat dihapus melalui aksi ini.');
$this->service->trash($customer);
return $this->deleted('List Order dipindahkan ke Sampah');
}
public function restore(Request $request, int $id)
{
$customer = $this->service->find($id, true);