DONE: remove cache, and remove dto financing
This commit is contained in:
@@ -55,10 +55,6 @@ class GadaiController extends Controller
|
||||
$cawangan = Cawangan::on('mysql7')->where('kodcaw','=',$cawangan_code)->first();
|
||||
|
||||
$gadaiTransaction = Gadai::on($cawangan['mysql'])->where('t_gadai','=',$date)->orderBy('sirig', 'desc')->first();
|
||||
$serial = app(\App\Services\ShorakaNewGadaiService::class)->latestSerial($cawangan_code, $date);
|
||||
if ($serial > (int) ($gadaiTransaction->sirig ?? 0)) {
|
||||
$gadaiTransaction = ['sirig' => $serial];
|
||||
}
|
||||
}catch(\Exception $e){
|
||||
Log::debug($e);
|
||||
Log::debug($cawangan_code);
|
||||
@@ -115,7 +111,7 @@ class GadaiController extends Controller
|
||||
$gadai_all = [];
|
||||
|
||||
foreach($cawangan_all as $index=>$cawangan){
|
||||
$gadai = collect(array_filter([app(\App\Services\ShorakaNewGadaiService::class)->find($cawangan->kodcaw, $norujukan)]));
|
||||
$gadai = Gadai::on($cawangan['mysql'])->where('norujukan','=',$norujukan)->get();
|
||||
if(sizeof($gadai) != 0){
|
||||
array_push($gadai_all,$gadai);
|
||||
}
|
||||
@@ -134,7 +130,7 @@ class GadaiController extends Controller
|
||||
$gadai_all = [];
|
||||
|
||||
foreach($cawangan_all as $index=>$cawangan){
|
||||
$gadai = collect(array_filter([app(\App\Services\ShorakaNewGadaiService::class)->find($cawangan->kodcaw, $norujukan)]));
|
||||
$gadai = Gadai::on($cawangan['mysql'])->where('norujukan','=',$norujukan)->get();
|
||||
if(sizeof($gadai) != 0){
|
||||
array_push($gadai_all,$gadai);
|
||||
}
|
||||
@@ -156,22 +152,12 @@ class GadaiController extends Controller
|
||||
|
||||
public function showOneGadaibyRujukanAndCawangan($kodcaw,$norujukan){
|
||||
$cawangan = Cawangan::on('mysql7')->where('kodcaw','=',$kodcaw)->first();
|
||||
$gadai = collect(array_filter([app(\App\Services\ShorakaNewGadaiService::class)->find($cawangan->kodcaw, $norujukan)]));
|
||||
$gadai = Gadai::on($cawangan['mysql'])->where('norujukan','=',$norujukan)->get();
|
||||
|
||||
return response()->json($gadai);
|
||||
}
|
||||
|
||||
public function create($kodcaw,Request $request){
|
||||
if ($request->boolean('shoraka_new')) {
|
||||
$this->validate($request, ['nokp' => 'required|string', 'nopelanggan' => 'required']);
|
||||
return response()->json(app(\App\Services\ShorakaNewGadaiService::class)->reserve(
|
||||
$kodcaw, $request->only(['nokp', 'nopelanggan'])
|
||||
), 201);
|
||||
}
|
||||
|
||||
if (app(\App\Services\ShorakaNewGadaiService::class)->pending($kodcaw, (string) $request->norujukan)) {
|
||||
abort(409, 'Nombor rujukan sedang digunakan. Sila dapatkan nombor baharu.');
|
||||
}
|
||||
$master = new MasterServices($kodcaw);
|
||||
$response = $master->createGadai($request);
|
||||
|
||||
@@ -246,10 +232,6 @@ class GadaiController extends Controller
|
||||
if (\App\ShorakaTransaction::where('norujukan', $norujukan)->exists()) {
|
||||
abort(409, 'Kontrak telah dihantar ke Shoraka dan tidak boleh diubah.');
|
||||
}
|
||||
$temporary = app(\App\Services\ShorakaNewGadaiService::class)->pending($kodcaw, $norujukan);
|
||||
if ($temporary && (string) $temporary['gadai']['nokp'] !== (string) $request->nokp) {
|
||||
abort(409, 'Pelanggan tidak sepadan dengan borang gadaian sementara.');
|
||||
}
|
||||
$request->merge(['kodcaw' => $kodcaw, 'norujukan' => $norujukan]);
|
||||
$master = new MasterServices($kodcaw);
|
||||
$response = $master->createMarhunGadai($norujukan, $request);
|
||||
@@ -266,13 +248,7 @@ class GadaiController extends Controller
|
||||
|
||||
public function checkNorujukan(Request $request){
|
||||
$cawangan = Cawangan::on('mysql7')->where('kodcaw','=',$request->kodcaw)->first();
|
||||
$gadai = app(\App\Services\ShorakaNewGadaiService::class)->find($request->kodcaw, $request->norujukan);
|
||||
if (!$gadai && \App\ShorakaTransaction::where('norujukan', $request->norujukan)->exists()) {
|
||||
abort(409, 'Transaksi Shoraka wujud tetapi maklumat sementara tiada. Hubungi operasi; jangan daftar semula.');
|
||||
}
|
||||
if ($gadai && $gadai->nokp !== $request->nokp) {
|
||||
$gadai = null;
|
||||
}
|
||||
$gadai = Gadai::on($cawangan['mysql'])->where([['norujukan','=',$request->norujukan],['nokp','=',$request->nokp]])->first();
|
||||
|
||||
return response()->json($gadai);
|
||||
}
|
||||
@@ -395,16 +371,9 @@ class GadaiController extends Controller
|
||||
}
|
||||
|
||||
public function batalGadai($kodcaw, $norujukan,Request $request){
|
||||
$temporary = app(\App\Services\ShorakaNewGadaiService::class);
|
||||
$order = \App\ShorakaTransaction::where('norujukan', $norujukan)->first();
|
||||
if ($order && !empty($order->request_payload['_new_gadai'])) {
|
||||
if (\App\ShorakaTransaction::where('norujukan', $norujukan)->exists()) {
|
||||
abort(409, 'Transaksi Shoraka telah dihantar. Semakan operasi diperlukan sebelum pembatalan.');
|
||||
}
|
||||
if ($temporary->pending($kodcaw, $norujukan)) {
|
||||
$temporary->abandon($kodcaw, $norujukan);
|
||||
return response('Batal Berjaya', 200);
|
||||
}
|
||||
|
||||
|
||||
$current = Carbon::now();
|
||||
$current = new Carbon();
|
||||
@@ -459,8 +428,7 @@ class GadaiController extends Controller
|
||||
}
|
||||
|
||||
public function cetakSAG($kodcaw,$norujukan,Request $request){
|
||||
if (\App\ShorakaTransaction::where('norujukan', $norujukan)->exists()
|
||||
|| app(\App\Services\ShorakaNewGadaiService::class)->pending($kodcaw, $norujukan)) {
|
||||
if (\App\ShorakaTransaction::where('norujukan', $norujukan)->exists()) {
|
||||
app(\App\Services\ShorakaGadaiService::class)->assertCompleted($kodcaw, $norujukan);
|
||||
}
|
||||
|
||||
@@ -488,8 +456,7 @@ class GadaiController extends Controller
|
||||
}
|
||||
|
||||
public function serahMarhun($kodcaw,$norujukan){
|
||||
if (\App\ShorakaTransaction::where('norujukan', $norujukan)->exists()
|
||||
|| app(\App\Services\ShorakaNewGadaiService::class)->pending($kodcaw, $norujukan)) {
|
||||
if (\App\ShorakaTransaction::where('norujukan', $norujukan)->exists()) {
|
||||
app(\App\Services\ShorakaGadaiService::class)->assertCompleted($kodcaw, $norujukan);
|
||||
}
|
||||
|
||||
|
||||
@@ -26,9 +26,6 @@ class MarhunController extends Controller
|
||||
$marhun_all = [];
|
||||
foreach($cawangan_all as $index=>$cawangan){
|
||||
$marhun = Marhun::on($cawangan['mysql'])->where('norujukan','=',$norujukan)->get();
|
||||
if ($marhun->isEmpty()) {
|
||||
$marhun = app(\App\Services\ShorakaNewGadaiService::class)->items($cawangan->kodcaw, $norujukan);
|
||||
}
|
||||
if(sizeof($marhun) != 0){
|
||||
array_push($marhun_all,$marhun);
|
||||
}
|
||||
@@ -43,9 +40,6 @@ class MarhunController extends Controller
|
||||
|
||||
$marhun = Marhun::on($cawangan['mysql'])->where('norujukan',$request->norujukan)->distinct()->pluck('karat')->toArray();
|
||||
|
||||
if (app(\App\Services\ShorakaNewGadaiService::class)->pending($request->kodcaw, $request->norujukan)) {
|
||||
$marhun = app(\App\Services\ShorakaNewGadaiService::class)->items($request->kodcaw, $request->norujukan)->pluck('karat')->unique()->all();
|
||||
}
|
||||
$mutu = HargaEmas::on('mysql7')->whereIn('karat',$marhun)->pluck('keterangan')->toArray();
|
||||
|
||||
return response()->json($mutu);
|
||||
@@ -66,7 +60,7 @@ class MarhunController extends Controller
|
||||
public function create($kodcaw,Request $request)
|
||||
{
|
||||
$order = \App\ShorakaTransaction::where('norujukan', $request->norujukan)->first();
|
||||
if ($order && !empty($order->request_payload['_new_gadai'])) {
|
||||
if ($order) {
|
||||
abort(409, 'Kontrak Shoraka telah dihantar dan tidak boleh diubah.');
|
||||
}
|
||||
|
||||
@@ -107,10 +101,6 @@ class MarhunController extends Controller
|
||||
};
|
||||
|
||||
$cawangan = Cawangan::on('mysql7')->where('kodcaw','=',$kodcaw)->first();
|
||||
$temporary = app(\App\Services\ShorakaNewGadaiService::class);
|
||||
if ($temporary->pending($kodcaw, $request->norujukan)) {
|
||||
return response()->json($temporary->changeItem($kodcaw, $request->norujukan, $marhunToCreate), 201);
|
||||
}
|
||||
$marhun = Marhun::on($cawangan['mysql'])->create($marhunToCreate);
|
||||
|
||||
$audit = new Audit();
|
||||
@@ -143,17 +133,10 @@ class MarhunController extends Controller
|
||||
public function deletemarhun($kodcaw,$norujukan,Request $request)
|
||||
{
|
||||
$order = \App\ShorakaTransaction::where('norujukan', $norujukan)->first();
|
||||
if ($order && !empty($order->request_payload['_new_gadai'])) {
|
||||
if ($order) {
|
||||
abort(409, 'Kontrak Shoraka telah dihantar dan tidak boleh diubah.');
|
||||
}
|
||||
|
||||
$temporary = app(\App\Services\ShorakaNewGadaiService::class);
|
||||
if ($temporary->pending($kodcaw, $norujukan)) {
|
||||
$temporary->changeItem($kodcaw, $norujukan, [], $request->recno, true);
|
||||
return response('Deleted Successfully', 200);
|
||||
}
|
||||
|
||||
|
||||
$current = Carbon::now();
|
||||
$current = new Carbon();
|
||||
|
||||
@@ -179,7 +162,7 @@ class MarhunController extends Controller
|
||||
public function editmarhun($kodcaw,$norujukan,Request $request)
|
||||
{
|
||||
$order = \App\ShorakaTransaction::where('norujukan', $norujukan)->first();
|
||||
if ($order && !empty($order->request_payload['_new_gadai'])) {
|
||||
if ($order) {
|
||||
abort(409, 'Kontrak Shoraka telah dihantar dan tidak boleh diubah.');
|
||||
}
|
||||
|
||||
@@ -190,17 +173,6 @@ class MarhunController extends Controller
|
||||
$harga = HargaEmas::on('mysql7')->where('karat',$request->karat)->first();
|
||||
$nilai_marhun = $request->berat * $harga['harga'];
|
||||
|
||||
$temporary = app(\App\Services\ShorakaNewGadaiService::class);
|
||||
if ($temporary->pending($kodcaw, $norujukan)) {
|
||||
$temporary->changeItem($kodcaw, $norujukan, [
|
||||
'karat' => $request->karat, 'marhun' => $request->marhun, 'nota' => $request->nota,
|
||||
'tag_permata' => $request->tag_permata,
|
||||
'berat_batu_permata' => $request->berat_batu_permata ?: null,
|
||||
'berat' => $request->berat, 'harga' => $harga['harga'], 'n_marhun' => $nilai_marhun,
|
||||
'bil' => $request->kuantiti,
|
||||
], $request->recno);
|
||||
return response()->json(true);
|
||||
}
|
||||
$cawangan = Cawangan::on('mysql7')->where('kodcaw','=',$kodcaw)->first();
|
||||
|
||||
// Lock the parent first so concurrent edits to this SAG are serialized.
|
||||
@@ -299,9 +271,6 @@ class MarhunController extends Controller
|
||||
|
||||
$marhuns = Marhun::on($cawangan['mysql'])->where('norujukan',$request->norujukan)->get();
|
||||
|
||||
if ($marhuns->isEmpty()) {
|
||||
$marhuns = app(\App\Services\ShorakaNewGadaiService::class)->items($request->kodcaw, $request->norujukan);
|
||||
}
|
||||
$totalnilaimarhun = 0;
|
||||
|
||||
foreach($marhuns as $index=>$marhun){
|
||||
@@ -315,11 +284,6 @@ class MarhunController extends Controller
|
||||
|
||||
public function getMarhunCaw($kodcaw,$norujukan)
|
||||
{
|
||||
$temporary = app(\App\Services\ShorakaNewGadaiService::class);
|
||||
if ($temporary->pending($kodcaw, $norujukan)) {
|
||||
return response()->json($temporary->items($kodcaw, $norujukan)->sum('n_marhun'));
|
||||
}
|
||||
|
||||
$cawangan = Cawangan::on('mysql7')->where('kodcaw','=',$kodcaw)->first();
|
||||
$data = Marhun::on($cawangan['mysql'])
|
||||
->where('norujukan','=', $norujukan)
|
||||
|
||||
@@ -58,15 +58,10 @@ class ShorakaController extends Controller
|
||||
'ownership_doc_no' => $payload['ownership_doc_no'],
|
||||
'order_amount' => $payload['order_amount'],
|
||||
'murabaha_amount' => $payload['murabaha_amount'],
|
||||
'request_payload' => $request->attributes->get('shoraka_new')
|
||||
? array_merge($payload, ['_new_gadai' => true]) : $payload,
|
||||
'request_payload' => $payload,
|
||||
'attempt_count' => 1,
|
||||
];
|
||||
$create = function () use ($attributes) { return ShorakaTransaction::create($attributes); };
|
||||
$transaction = $request->attributes->get('shoraka_new')
|
||||
? app(\App\Services\ShorakaNewGadaiService::class)->claim(
|
||||
$request->attributes->get('shoraka_kodcaw'), $validated['norujukan'], $payload, $create
|
||||
) : $create();
|
||||
$transaction = ShorakaTransaction::create($attributes);
|
||||
} catch (QueryException $exception) {
|
||||
$existing = ShorakaTransaction::where('norujukan', $validated['norujukan'])->first();
|
||||
if ($existing) {
|
||||
|
||||
@@ -0,0 +1,168 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\ShorakaFinancing;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Database\QueryException;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class ShorakaFinancingController extends Controller
|
||||
{
|
||||
public function index(Request $request)
|
||||
{
|
||||
$this->validate($request, [
|
||||
'kodcaw' => 'nullable|string|max:30',
|
||||
'norujukan' => 'nullable|string|max:100',
|
||||
'state' => 'nullable|in:WAITING,PROCESSING,REVIEW,DONE',
|
||||
'date_from' => 'nullable|date_format:Y-m-d',
|
||||
'date_to' => 'nullable|date_format:Y-m-d',
|
||||
'per_page' => 'nullable|integer|min:1|max:100',
|
||||
'page' => 'nullable|integer|min:1',
|
||||
'branch_scope' => 'nullable|string|max:30',
|
||||
]);
|
||||
|
||||
try {
|
||||
$query = ShorakaFinancing::query();
|
||||
foreach (['kodcaw', 'norujukan', 'state'] as $field) {
|
||||
if ($request->filled($field)) {
|
||||
$query->where($field, $request->input($field));
|
||||
}
|
||||
}
|
||||
if ($request->filled('date_from')) {
|
||||
$query->where('created_at', '>=', $request->input('date_from') . ' 00:00:00');
|
||||
}
|
||||
if ($request->filled('date_to')) {
|
||||
$query->where('created_at', '<', Carbon::parse($request->input('date_to'))->addDay()->format('Y-m-d') . ' 00:00:00');
|
||||
}
|
||||
$records = $query->orderByDesc('created_at')->orderByDesc('id')
|
||||
->paginate(min(100, max(1, (int) $request->input('per_page', 25))));
|
||||
$payload = [
|
||||
'data' => $records->getCollection()->map(function (ShorakaFinancing $record) {
|
||||
return $this->present($record, false);
|
||||
})->values(),
|
||||
'total' => $records->total(),
|
||||
'current_page' => $records->currentPage(),
|
||||
'per_page' => $records->perPage(),
|
||||
'last_page' => $records->lastPage(),
|
||||
];
|
||||
if ($request->input('with_branches')) {
|
||||
$branches = ShorakaFinancing::query();
|
||||
if ($request->filled('branch_scope')) {
|
||||
$branches->where('kodcaw', $request->input('branch_scope'));
|
||||
}
|
||||
$payload['branches'] = $branches->distinct()->orderBy('kodcaw')->pluck('kodcaw')->values();
|
||||
}
|
||||
return response()->json($payload);
|
||||
} catch (QueryException $exception) {
|
||||
return $this->unavailable();
|
||||
}
|
||||
}
|
||||
|
||||
public function show(string $kodcaw, string $norujukan)
|
||||
{
|
||||
try {
|
||||
$record = $this->find($kodcaw, $norujukan);
|
||||
} catch (QueryException $exception) {
|
||||
return $this->unavailable();
|
||||
}
|
||||
if (!$record) {
|
||||
return response()->json(['message' => 'Rekod pembiayaan tidak dijumpai.'], 404);
|
||||
}
|
||||
return response()->json(['data' => $this->present($record)]);
|
||||
}
|
||||
|
||||
public function store(string $kodcaw, string $norujukan, Request $request)
|
||||
{
|
||||
$this->validate($request, [
|
||||
'teller' => 'required|string|max:255',
|
||||
'action' => 'required|string|max:30',
|
||||
'context' => 'present|array',
|
||||
]);
|
||||
try {
|
||||
$existing = $this->find($kodcaw, $norujukan);
|
||||
if ($existing) {
|
||||
return response()->json(['created' => false, 'data' => $this->present($existing)]);
|
||||
}
|
||||
$record = ShorakaFinancing::create([
|
||||
'kodcaw' => $kodcaw,
|
||||
'norujukan' => $norujukan,
|
||||
'teller' => $request->input('teller'),
|
||||
'action' => $request->input('action'),
|
||||
'state' => 'WAITING',
|
||||
'context' => $request->input('context'),
|
||||
]);
|
||||
return response()->json(['created' => true, 'data' => $this->present($record)], 201);
|
||||
} catch (QueryException $exception) {
|
||||
try {
|
||||
$existing = $this->find($kodcaw, $norujukan);
|
||||
} catch (QueryException $again) {
|
||||
return $this->unavailable();
|
||||
}
|
||||
if ($existing) {
|
||||
return response()->json(['created' => false, 'data' => $this->present($existing)]);
|
||||
}
|
||||
return $this->unavailable();
|
||||
}
|
||||
}
|
||||
|
||||
public function update(string $kodcaw, string $norujukan, Request $request)
|
||||
{
|
||||
$this->validate($request, [
|
||||
'state' => 'required|in:WAITING,PROCESSING,REVIEW,DONE',
|
||||
'expected_state' => 'nullable|in:WAITING,PROCESSING,REVIEW,DONE',
|
||||
'context' => 'nullable|array',
|
||||
]);
|
||||
try {
|
||||
$record = $this->find($kodcaw, $norujukan);
|
||||
if (!$record) {
|
||||
return response()->json(['message' => 'Rekod pembiayaan tidak dijumpai.'], 404);
|
||||
}
|
||||
if ($request->filled('expected_state')) {
|
||||
$claimed = ShorakaFinancing::where('id', $record->id)
|
||||
->where('state', $request->input('expected_state'))
|
||||
->update(['state' => $request->input('state'), 'updated_at' => Carbon::now()]);
|
||||
return response()->json([
|
||||
'claimed' => $claimed === 1,
|
||||
'data' => $this->present($record->fresh()),
|
||||
]);
|
||||
}
|
||||
$fields = ['state' => $request->input('state')];
|
||||
if ($request->exists('context')) {
|
||||
$fields['context'] = $request->input('context') ?? [];
|
||||
}
|
||||
$record->update($fields);
|
||||
return response()->json(['claimed' => true, 'data' => $this->present($record->fresh())]);
|
||||
} catch (QueryException $exception) {
|
||||
return $this->unavailable();
|
||||
}
|
||||
}
|
||||
|
||||
private function find(string $kodcaw, string $norujukan): ?ShorakaFinancing
|
||||
{
|
||||
return ShorakaFinancing::where('kodcaw', $kodcaw)->where('norujukan', $norujukan)->first();
|
||||
}
|
||||
|
||||
private function present(ShorakaFinancing $record, bool $withContext = true): array
|
||||
{
|
||||
$data = [
|
||||
'id' => $record->id,
|
||||
'kodcaw' => $record->kodcaw,
|
||||
'norujukan' => $record->norujukan,
|
||||
'teller' => $record->teller,
|
||||
'action' => $record->action,
|
||||
'state' => $record->state,
|
||||
'created_at' => optional($record->created_at)->toDateTimeString(),
|
||||
'updated_at' => optional($record->updated_at)->toDateTimeString(),
|
||||
];
|
||||
if ($withContext) {
|
||||
$data['context'] = $record->context ?? [];
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
private function unavailable()
|
||||
{
|
||||
return response()->json(['message' => 'Rekod pembiayaan Shoraka tidak tersedia.'], 503);
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,13 @@ class ShorakaGadaiController extends ShorakaController
|
||||
{
|
||||
public function readiness(ShorakaService $shoraka)
|
||||
{
|
||||
return response()->json(['provider' => 'shoraka', 'ready' => $shoraka->configured()]);
|
||||
try {
|
||||
\App\ShorakaFinancing::query()->limit(1)->exists();
|
||||
$storeReady = true;
|
||||
} catch (\Throwable $exception) {
|
||||
$storeReady = false;
|
||||
}
|
||||
return response()->json(['provider' => 'shoraka', 'ready' => $storeReady && $shoraka->configured()]);
|
||||
}
|
||||
|
||||
public function showGadai(string $kodcaw, string $norujukan, ShorakaGadaiService $gadaiService)
|
||||
@@ -45,16 +51,8 @@ class ShorakaGadaiController extends ShorakaController
|
||||
|| $gadai->cetak || $gadai->sttebus) {
|
||||
abort(409, 'Gadaian lama atau telah selesai tidak boleh dihantar sebagai order baharu.');
|
||||
}
|
||||
$temporary = app(\App\Services\ShorakaNewGadaiService::class)->pending($kodcaw, $norujukan);
|
||||
if ($temporary && $gadai->exists) {
|
||||
abort(409, 'Rujukan sementara bertindih dengan gadaian sedia ada. Semakan operasi diperlukan.');
|
||||
}
|
||||
if ($temporary && !$temporary['prepared']) {
|
||||
abort(409, 'Sila sahkan semula amaun gadaian selepas perubahan marhun.');
|
||||
}
|
||||
$request = new Request($gadaiService->payload($gadai));
|
||||
$request->attributes->set('shoraka_kodcaw', $kodcaw);
|
||||
$request->attributes->set('shoraka_new', !$gadai->exists);
|
||||
return $this->submit($request, $shoraka);
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ class ShorakaGadaiService
|
||||
public function gadai(string $kodcaw, string $norujukan): Gadai
|
||||
{
|
||||
$branch = Cawangan::on('mysql7')->where('kodcaw', $kodcaw)->firstOrFail();
|
||||
return app(ShorakaNewGadaiService::class)->find($kodcaw, $norujukan)
|
||||
return Gadai::on($branch->mysql)->where('kodcaw', $kodcaw)->where('norujukan', $norujukan)->first()
|
||||
?? abort(404, 'Maklumat gadaian tidak tersedia. Semakan diperlukan.');
|
||||
}
|
||||
|
||||
@@ -53,7 +53,6 @@ class ShorakaGadaiService
|
||||
if (!$order->kodcaw) {
|
||||
return;
|
||||
}
|
||||
app(ShorakaNewGadaiService::class)->promote($order);
|
||||
$gadai = $this->gadai($order->kodcaw, $order->norujukan);
|
||||
if (!$gadai->exists) {
|
||||
return;
|
||||
|
||||
@@ -1,259 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Cawangan;
|
||||
use App\Gadai;
|
||||
use App\ShorakaTransaction;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
|
||||
/** Temporary new financing only. Existing/refinancing contracts retain their database path. */
|
||||
class ShorakaNewGadaiService
|
||||
{
|
||||
const TTL = 604800; // Seven days; loss/expiry requires reconciliation, never resubmission.
|
||||
|
||||
private function cache()
|
||||
{
|
||||
return Cache::store(config('services.shoraka.pending_cache_store', 'file'));
|
||||
}
|
||||
|
||||
private function key(string $branch, string $reference): string
|
||||
{
|
||||
return 'shoraka:new:' . hash('sha256', $branch . ':' . $reference);
|
||||
}
|
||||
|
||||
public function pending(string $branch, string $reference): ?array
|
||||
{
|
||||
return $this->cache()->get($this->key($branch, $reference));
|
||||
}
|
||||
|
||||
private function put(string $branch, string $reference, array $data): void
|
||||
{
|
||||
if (!$this->cache()->put($this->key($branch, $reference), $data, self::TTL)) {
|
||||
abort(503, 'Maklumat sementara tidak dapat disimpan.');
|
||||
}
|
||||
}
|
||||
|
||||
// File cache and these locks must be shared by every API worker serving a branch.
|
||||
public function locked(string $key, callable $callback)
|
||||
{
|
||||
$directory = storage_path('framework/cache/shoraka-locks');
|
||||
if (!is_dir($directory) && !@mkdir($directory, 0700, true) && !is_dir($directory)) {
|
||||
abort(503, 'Kunci transaksi tidak tersedia.');
|
||||
}
|
||||
$handle = fopen($directory . '/' . hash('sha256', $key) . '.lock', 'c');
|
||||
if (!$handle) {
|
||||
abort(503, 'Kunci transaksi tidak tersedia.');
|
||||
}
|
||||
if (!flock($handle, LOCK_EX | LOCK_NB)) {
|
||||
fclose($handle);
|
||||
abort(409, 'Transaksi sedang diproses. Sila tunggu.');
|
||||
}
|
||||
try {
|
||||
return $callback();
|
||||
} finally {
|
||||
flock($handle, LOCK_UN);
|
||||
fclose($handle);
|
||||
}
|
||||
}
|
||||
|
||||
public function reserve(string $branch, array $customer): Gadai
|
||||
{
|
||||
return $this->locked('reserve:' . $branch, function () use ($branch, $customer) {
|
||||
$cawangan = Cawangan::on('mysql7')->where('kodcaw', $branch)->firstOrFail();
|
||||
$date = Carbon::now()->toDateString();
|
||||
$counter = 'shoraka:serial:' . $branch . ':' . $date;
|
||||
$latest = Gadai::on($cawangan->mysql)->where('t_gadai', $date)->max('sirig');
|
||||
$highest = max((int) $latest, (int) $this->cache()->get($counter, 0));
|
||||
$releasedKey = 'shoraka:released:' . $branch . ':' . $date;
|
||||
$released = $this->cache()->get($releasedKey, []);
|
||||
sort($released, SORT_NUMERIC);
|
||||
$serial = null;
|
||||
while ($released) {
|
||||
$candidate = (int) array_shift($released);
|
||||
$candidateReference = strtoupper($branch) . Carbon::parse($date)->format('ymd') . '-' . sprintf('%04d', $candidate);
|
||||
if (!ShorakaTransaction::where('norujukan', $candidateReference)->exists()
|
||||
&& !Gadai::on($cawangan->mysql)->where('norujukan', $candidateReference)->exists()
|
||||
&& !$this->pending($branch, $candidateReference)) {
|
||||
$serial = $candidate;
|
||||
$reference = $candidateReference;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!$this->cache()->forever($releasedKey, $released)) {
|
||||
abort(503, 'Nombor rujukan dibatalkan tidak dapat dikemas kini.');
|
||||
}
|
||||
if ($serial === null) {
|
||||
$serial = $highest;
|
||||
do {
|
||||
++$serial;
|
||||
$reference = strtoupper($branch) . Carbon::parse($date)->format('ymd') . '-' . sprintf('%04d', $serial);
|
||||
} while (ShorakaTransaction::where('norujukan', $reference)->exists()
|
||||
|| Gadai::on($cawangan->mysql)->where('norujukan', $reference)->exists()
|
||||
|| $this->pending($branch, $reference));
|
||||
}
|
||||
if (!$this->cache()->forever($counter, max($highest, $serial))) {
|
||||
abort(503, 'Nombor rujukan tidak dapat ditempah.');
|
||||
}
|
||||
$data = array_merge($customer, [
|
||||
'kodcaw' => $branch, 'norujukan' => $reference, 'sirig' => $serial,
|
||||
't_gadai' => $date, 'masa' => Carbon::now()->toTimeString(), 'sttebus' => '',
|
||||
'cara_terima' => 'TUNAI', 'cetak' => 0, 'tagserah' => 0, 'tagkomuditi' => 0,
|
||||
]);
|
||||
if ($cawangan->version === 'v2') {
|
||||
$data['tagbaru'] = 1;
|
||||
}
|
||||
$this->put($branch, $reference, ['gadai' => $data, 'items' => [], 'next_item' => 1, 'revision' => 0, 'prepared' => false]);
|
||||
return $this->find($branch, $reference);
|
||||
});
|
||||
}
|
||||
|
||||
public function abandon(string $branch, string $reference): void
|
||||
{
|
||||
// Use the reservation lock as well so release and allocation cannot race.
|
||||
$this->locked('reserve:' . $branch, function () use ($branch, $reference) {
|
||||
$this->locked($branch . ':' . $reference, function () use ($branch, $reference) {
|
||||
$pending = $this->mutable($branch, $reference);
|
||||
$cawangan = Cawangan::on('mysql7')->where('kodcaw', $branch)->firstOrFail();
|
||||
if (Gadai::on($cawangan->mysql)->where('norujukan', $reference)->exists()) {
|
||||
abort(409, 'Gadaian telah disimpan dan nombor rujukan tidak boleh digunakan semula.');
|
||||
}
|
||||
$releasedKey = 'shoraka:released:' . $branch . ':' . $pending['gadai']['t_gadai'];
|
||||
$released = $this->cache()->get($releasedKey, []);
|
||||
$released[] = (int) $pending['gadai']['sirig'];
|
||||
if (!$this->cache()->forever($releasedKey, array_values(array_unique($released)))) {
|
||||
abort(503, 'Nombor rujukan tidak dapat dilepaskan.');
|
||||
}
|
||||
if (!$this->cache()->forget($this->key($branch, $reference))) {
|
||||
abort(503, 'Maklumat sementara tidak dapat dibatalkan.');
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public function latestSerial(string $branch, string $date): int
|
||||
{
|
||||
return (int) $this->cache()->get('shoraka:serial:' . $branch . ':' . $date, 0);
|
||||
}
|
||||
|
||||
public function claim(string $branch, string $reference, array $payload, callable $create)
|
||||
{
|
||||
return $this->locked($branch . ':' . $reference, function () use ($branch, $reference, $payload, $create) {
|
||||
$pending = $this->mutable($branch, $reference);
|
||||
if (!$pending['prepared']
|
||||
|| number_format((float) ($pending['gadai']['pinjaman'] ?? 0), 2, '.', '') !== $payload['order_amount']
|
||||
|| number_format((float) ($pending['gadai']['hargajualan'] ?? 0), 2, '.', '') !== $payload['murabaha_amount']) {
|
||||
abort(409, 'Maklumat gadaian berubah. Sila sahkan semula sebelum penghantaran.');
|
||||
}
|
||||
return $create(); // The unique transaction reference freezes further edits.
|
||||
});
|
||||
}
|
||||
|
||||
public function find(string $branch, string $reference)
|
||||
{
|
||||
$cawangan = Cawangan::on('mysql7')->where('kodcaw', $branch)->firstOrFail();
|
||||
$saved = Gadai::on($cawangan->mysql)->where('kodcaw', $branch)->where('norujukan', $reference)->first();
|
||||
if ($saved) {
|
||||
return $saved;
|
||||
}
|
||||
$pending = $this->pending($branch, $reference);
|
||||
if (!$pending) {
|
||||
return null;
|
||||
}
|
||||
$model = (new Gadai())->setConnection($cawangan->mysql);
|
||||
$model->setRawAttributes($pending['gadai']);
|
||||
return $model;
|
||||
}
|
||||
|
||||
private function mutable(string $branch, string $reference): array
|
||||
{
|
||||
if (ShorakaTransaction::where('norujukan', $reference)->exists()) {
|
||||
abort(409, 'Kontrak telah dihantar ke Shoraka dan tidak boleh diubah.');
|
||||
}
|
||||
$pending = $this->pending($branch, $reference);
|
||||
if (!$pending) {
|
||||
abort(409, 'Maklumat sementara tamat. Semakan diperlukan.');
|
||||
}
|
||||
return $pending;
|
||||
}
|
||||
|
||||
public function prepare(string $branch, string $reference, array $fields, int $revision): void
|
||||
{
|
||||
$this->locked($branch . ':' . $reference, function () use ($branch, $reference, $fields, $revision) {
|
||||
$pending = $this->mutable($branch, $reference);
|
||||
if (($pending['revision'] ?? 0) !== $revision) {
|
||||
abort(409, 'Maklumat marhun berubah semasa pengiraan. Sila sahkan semula.');
|
||||
}
|
||||
$pending['gadai'] = array_merge($pending['gadai'], $fields);
|
||||
$pending['prepared'] = true;
|
||||
$this->put($branch, $reference, $pending);
|
||||
});
|
||||
}
|
||||
|
||||
public function items(string $branch, string $reference)
|
||||
{
|
||||
return collect(array_values($this->pending($branch, $reference)['items'] ?? []));
|
||||
}
|
||||
|
||||
public function changeItem(string $branch, string $reference, array $fields, $id = null, bool $delete = false)
|
||||
{
|
||||
return $this->locked($branch . ':' . $reference, function () use ($branch, $reference, $fields, $id, $delete) {
|
||||
$pending = $this->mutable($branch, $reference);
|
||||
if ($id !== null && !isset($pending['items'][$id])) {
|
||||
abort(404, 'Marhun tidak dijumpai.');
|
||||
}
|
||||
if ($id === null) {
|
||||
$id = $pending['next_item']++;
|
||||
}
|
||||
$item = array_merge($pending['items'][$id] ?? [], $fields, ['recno' => (int) $id]);
|
||||
if ($delete) {
|
||||
unset($pending['items'][$id]);
|
||||
} else {
|
||||
$pending['items'][$id] = $item;
|
||||
}
|
||||
$pending['revision'] = ($pending['revision'] ?? 0) + 1;
|
||||
$pending['prepared'] = false; // Reprice the contract after any collateral edit.
|
||||
$this->put($branch, $reference, $pending);
|
||||
return $item;
|
||||
});
|
||||
}
|
||||
|
||||
public function promote(ShorakaTransaction $order): void
|
||||
{
|
||||
if ($order->status !== 'Completed' || !$this->pending($order->kodcaw, $order->norujukan)) {
|
||||
return;
|
||||
}
|
||||
$this->locked($order->kodcaw . ':' . $order->norujukan, function () use ($order) {
|
||||
$pending = $this->pending($order->kodcaw, $order->norujukan);
|
||||
$data = $pending['gadai'];
|
||||
if (!$pending['prepared']
|
||||
|| number_format((float) ($data['pinjaman'] ?? 0), 2, '.', '') !== $order->order_amount
|
||||
|| number_format((float) ($data['hargajualan'] ?? 0), 2, '.', '') !== $order->murabaha_amount) {
|
||||
abort(409, 'Amaun gadaian tidak sepadan dengan Shoraka.');
|
||||
}
|
||||
$branch = Cawangan::on('mysql7')->where('kodcaw', $order->kodcaw)->firstOrFail();
|
||||
$connection = (new Gadai())->setConnection($branch->mysql)->getConnection();
|
||||
$connection->transaction(function () use ($connection, $pending, $data, $order) {
|
||||
$existing = $connection->table('gadai')->where('norujukan', $order->norujukan)->lockForUpdate()->first();
|
||||
if ($existing) {
|
||||
if ($existing->kodcaw !== $order->kodcaw
|
||||
|| number_format((float) $existing->pinjaman, 2, '.', '') !== $order->order_amount
|
||||
|| number_format((float) $existing->hargajualan, 2, '.', '') !== $order->murabaha_amount) {
|
||||
abort(409, 'Rujukan gadaian bertindih. Semakan diperlukan.');
|
||||
}
|
||||
return;
|
||||
}
|
||||
$connection->table('gadai')->insert(array_merge($data, ['tagkomuditi' => 1]));
|
||||
foreach ($pending['items'] as $item) {
|
||||
unset($item['recno']);
|
||||
$connection->table('marhun')->insert($item);
|
||||
}
|
||||
});
|
||||
\App\Audit::firstOrCreate([
|
||||
'kodcaw' => $order->kodcaw, 'norujukan' => $order->norujukan, 'actions' => 'Daftar Gadai',
|
||||
], ['users' => $data['teller'] ?? '', 'new_data' => json_encode($data)]);
|
||||
// Retain the cache briefly for idempotent reconciliation after a lost response.
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace App;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class ShorakaFinancing extends Model
|
||||
{
|
||||
protected $connection = 'mysql7';
|
||||
|
||||
protected $table = 'shoraka_financings';
|
||||
|
||||
protected $guarded = [];
|
||||
|
||||
protected $casts = ['context' => 'array'];
|
||||
}
|
||||
@@ -73,13 +73,10 @@ class GadaiV1
|
||||
$old_data = Gadai::on($cawangan['mysql'])->where('norujukan','=',$norujukan)->first();
|
||||
}
|
||||
|
||||
$temporary = app(\App\Services\ShorakaNewGadaiService::class);
|
||||
$temporaryData = $temporary->pending($request->kodcaw, $norujukan);
|
||||
$isTemporary = $temporaryData !== null;
|
||||
$bil_marhun = ($isTemporary ? $temporary->items($request->kodcaw, $norujukan)->count() : Marhun::on($cawangan['mysql'])->where('norujukan','=',$norujukan)->count());
|
||||
$bil_marhun = Marhun::on($cawangan['mysql'])->where('norujukan','=',$norujukan)->count();
|
||||
|
||||
$total_nilai_marhun = ($isTemporary ? $temporary->items($request->kodcaw, $norujukan)->sum('n_marhun') : Marhun::on($cawangan['mysql'])->where('norujukan','=',$norujukan)->sum('n_marhun'));
|
||||
$total_berat = ($isTemporary ? $temporary->items($request->kodcaw, $norujukan)->sum('berat') : Marhun::on($cawangan['mysql'])->where('norujukan','=',$norujukan)->sum('berat'));
|
||||
$total_nilai_marhun = Marhun::on($cawangan['mysql'])->where('norujukan','=',$norujukan)->sum('n_marhun');
|
||||
$total_berat = Marhun::on($cawangan['mysql'])->where('norujukan','=',$norujukan)->sum('berat');
|
||||
|
||||
$kadar_raw = KadarUpah::where('min','<=',$total_nilai_marhun)->where('max','>=',$total_nilai_marhun)->first();
|
||||
$tawarruqservices = new Calculation();
|
||||
@@ -117,10 +114,6 @@ class GadaiV1
|
||||
'tempohbayar' => 0.00,
|
||||
'lelong_tawarruq' => 1
|
||||
);
|
||||
if ($isTemporary) {
|
||||
$temporary->prepare($request->kodcaw, $norujukan, $gadaiFields, $temporaryData['revision'] ?? 0);
|
||||
return response()->json(true);
|
||||
}
|
||||
$gadai = Gadai::on($cawangan['mysql'])->where('norujukan', $norujukan)->update($gadaiFields);
|
||||
|
||||
$new_data = Gadai::on($cawangan['mysql'])->where('norujukan','=',$norujukan)->first();
|
||||
|
||||
@@ -946,8 +946,7 @@ class Penebusan
|
||||
$gadaiTransaction = Gadai::on($cawangan['mysql'])->where('t_gadai','=',$current->toDateString())->orderBy('sirig', 'desc')->first();
|
||||
$gadaiTransaction = optional($gadaiTransaction)->toArray();
|
||||
|
||||
$new_sirig = max((int) ($gadaiTransaction['sirig'] ?? 0),
|
||||
app(\App\Services\ShorakaNewGadaiService::class)->latestSerial($kodcaw, $current->toDateString())) + 1;
|
||||
$new_sirig = (int) ($gadaiTransaction['sirig'] ?? 0) + 1;
|
||||
$curent_year = substr($current->year,2,2);
|
||||
$curent_month = sprintf("%02d", $current->month);
|
||||
$current_day = sprintf("%02d", $current->day);
|
||||
|
||||
@@ -107,13 +107,10 @@ class GadaiV2
|
||||
$old_data = Gadai::on($cawangan['mysql'])->where('norujukan','=',$norujukan)->first();
|
||||
}
|
||||
|
||||
$temporary = app(\App\Services\ShorakaNewGadaiService::class);
|
||||
$temporaryData = $temporary->pending($request->kodcaw, $norujukan);
|
||||
$isTemporary = $temporaryData !== null;
|
||||
$bil_marhun = ($isTemporary ? $temporary->items($request->kodcaw, $norujukan)->sum('bil') : Marhun::on($cawangan['mysql'])->where('norujukan','=',$norujukan)->sum('bil'));
|
||||
$bil_marhun = Marhun::on($cawangan['mysql'])->where('norujukan','=',$norujukan)->sum('bil');
|
||||
|
||||
$total_nilai_marhun = ($isTemporary ? $temporary->items($request->kodcaw, $norujukan)->sum('n_marhun') : Marhun::on($cawangan['mysql'])->where('norujukan','=',$norujukan)->sum('n_marhun'));
|
||||
$total_berat = ($isTemporary ? $temporary->items($request->kodcaw, $norujukan)->sum('berat') : Marhun::on($cawangan['mysql'])->where('norujukan','=',$norujukan)->sum('berat'));
|
||||
$total_nilai_marhun = Marhun::on($cawangan['mysql'])->where('norujukan','=',$norujukan)->sum('n_marhun');
|
||||
$total_berat = Marhun::on($cawangan['mysql'])->where('norujukan','=',$norujukan)->sum('berat');
|
||||
|
||||
$kadar_raw = $this->getKadarUpahAnggota($request->nokp, $total_nilai_marhun, $cawangan['group_id']);
|
||||
|
||||
@@ -193,10 +190,6 @@ if (is_null($kadar_raw)) {
|
||||
}
|
||||
}
|
||||
|
||||
if ($isTemporary) {
|
||||
$temporary->prepare($request->kodcaw, $norujukan, $gadaiFields, $temporaryData['revision'] ?? 0);
|
||||
return 'true';
|
||||
}
|
||||
$gadai = Gadai::on($cawangan['mysql'])
|
||||
->where('norujukan','=',$norujukan)
|
||||
->update($gadaiFields);
|
||||
|
||||
@@ -1224,8 +1224,7 @@ class Penebusan
|
||||
$gadaiTransaction = Gadai::on($cawangan['mysql'])->where('t_gadai','=',$current->toDateString())->orderBy('sirig', 'desc')->first();
|
||||
$gadaiTransaction = optional($gadaiTransaction)->toArray();
|
||||
|
||||
$new_sirig = max((int) ($gadaiTransaction['sirig'] ?? 0),
|
||||
app(\App\Services\ShorakaNewGadaiService::class)->latestSerial($kodcaw, $current->toDateString())) + 1;
|
||||
$new_sirig = (int) ($gadaiTransaction['sirig'] ?? 0) + 1;
|
||||
$curent_year = substr($current->year,2,2);
|
||||
$curent_month = sprintf("%02d", $current->month);
|
||||
$current_day = sprintf("%02d", $current->day);
|
||||
|
||||
@@ -120,6 +120,10 @@ $router->group(['prefix'=>'api'], function () use ($router){
|
||||
$router->get('gadai/online/search/detail',['uses'=>'GadaiController@searchGadaiOnline']);
|
||||
|
||||
$router->get('shoraka/readiness', ['uses' => 'ShorakaGadaiController@readiness']);
|
||||
$router->get('shoraka/financings', ['uses' => 'ShorakaFinancingController@index']);
|
||||
$router->get('shoraka/financings/{kodcaw}/{norujukan}', ['uses' => 'ShorakaFinancingController@show']);
|
||||
$router->post('shoraka/financings/{kodcaw}/{norujukan}', ['uses' => 'ShorakaFinancingController@store']);
|
||||
$router->put('shoraka/financings/{kodcaw}/{norujukan}', ['uses' => 'ShorakaFinancingController@update']);
|
||||
$router->get('shoraka/gadai/{kodcaw}/{norujukan}', ['uses' => 'ShorakaGadaiController@showGadai']);
|
||||
$router->post('shoraka/gadai/{kodcaw}/{norujukan}/submit', ['uses' => 'ShorakaGadaiController@submitGadai']);
|
||||
$router->post('shoraka/gadai/{kodcaw}/{norujukan}/refresh', ['uses' => 'ShorakaGadaiController@refreshGadai']);
|
||||
|
||||
Reference in New Issue
Block a user