diff --git a/app/Services/ShorakaNewGadaiService.php b/app/Services/ShorakaNewGadaiService.php index b7602d4..7c7d4bf 100644 --- a/app/Services/ShorakaNewGadaiService.php +++ b/app/Services/ShorakaNewGadaiService.php @@ -65,14 +65,35 @@ class ShorakaNewGadaiService $date = Carbon::now()->toDateString(); $counter = 'shoraka:serial:' . $branch . ':' . $date; $latest = Gadai::on($cawangan->mysql)->where('t_gadai', $date)->max('sirig'); - $serial = max((int) $latest, (int) $this->cache()->get($counter, 0)); - do { - ++$serial; - $reference = strtoupper($branch) . Carbon::now()->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, $serial)) { + $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, [ @@ -90,9 +111,24 @@ class ShorakaNewGadaiService public function abandon(string $branch, string $reference): void { - $this->locked($branch . ':' . $reference, function () use ($branch, $reference) { - $this->mutable($branch, $reference); - $this->cache()->forget($this->key($branch, $reference)); + // 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.'); + } + }); }); }