<?php
declare(strict_types=1);
namespace App\Services\Pharmacy;
use App\Models\Medicine;
use App\Models\MedicineBatch;
use App\Models\PrescriptionItem;
use Illuminate\Support\Facades\DB;
use RuntimeException;
final class FefoInventoryDispensingService
{
public function dispenseItem(PrescriptionItem $item, int $quantity): void
{
DB::transaction(function () use ($item, $quantity): void {
$medicine = Medicine::where('id', $item->medicine_id)->lockForUpdate()->firstOrFail();
if ($medicine->total_stock < $quantity) {
throw new RuntimeException("Insufficient stock for {$medicine->name}.");
}
$batches = MedicineBatch::where('medicine_id', $medicine->id)
->where('quantity_remaining', '>', 0)
->where('expiry_date', '>', now())
->orderBy('expiry_date', 'asc')
->lockForUpdate()
->get();
$needed = $quantity;
foreach ($batches as $batch) {
if ($needed <= 0) break;
$deduct = min($batch->quantity_remaining, $needed);
$batch->decrement('quantity_remaining', $deduct);
$medicine->decrement('total_stock', $deduct);
$needed -= $deduct;
}
$item->update(['status' => 'dispensed']);
});
}
}
Enterprise multi-tenancy and high-throughput architectural patterns require dedicated database partitioning, connection pooling, and resilient worker strategies...
By subscribing to our developer tier, you unlock complete production source code, downloadable PowerPoint decks, and interactive labs.
Premium Developer Content
Join our membership for KES 500/month to unlock all comprehensive engineering deep-dives and slide decks.
Unlock Full Access (KES 500)Accelerate Your Engineering Career
Get monthly masterclasses, exclusive code snippets, and direct architectural reviews.
Become a Supporter • KES 500 / Month