Files
streamline-emr/docker/statistics/Modules/Stores/Services/StoresItemService.php
T
2025-03-31 03:46:05 +03:00

370 lines
17 KiB
PHP
Executable File

<?php
namespace Modules\Stores\Services;
use Illuminate\Database\Eloquent\Collection as EloquentCollection;
use Streamline\Models\ItemBatchWatcher;
use Streamline\Models\OpticalStockReconciliation;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Auth;
use Streamline\Models\BatchConsumptionTracking;
use Streamline\Models\IncomingWardChart;
use Streamline\Models\StockReconciliationReason;
use Streamline\Models\ReconilicationReason;
use Streamline\Models\WardTreatmentBatchDispensation;
use Streamline\Models\Dental;
use Streamline\Models\Drug;
use Streamline\Models\DrugForm;
use Streamline\Models\GeneralForm;
use Streamline\Models\Lab;
use Streamline\Models\LabForm;
use Streamline\Models\Radiology;
use Streamline\Models\Sundry;
class StoresItemService {
//function to get form name based on item type
public function get_item_form_name($item_id, $item_type){
switch($item_type)
{
case 1:
//get drug item form
$form_id = Drug::find($item_id)->form_id;
$form_name = DrugForm::find($form_id)->name??'';
return $form_name;
break;
case 2:
//get sundry item form
$form_id = Sundry::find($item_id)->form_id;
$form_name = GeneralForm::find($form_id)->name??'';
return $form_name;
break;
case 3:
//get dental item form
$form_name='';
return $form_name;
break;
case 4:
//get radiology item form
$form_id = Radiology::find($item_id)->form_id;
$form_name = GeneralForm::find($form_id)->name??'';
return $form_name;
break;
case 5:
//get labs item form
$form_id = Lab::find($item_id)->form_id??'';
$form_name = LabForm::find($form_id)->name??'';
return $form_name;
break;
case 6:
//get general_item item form
$form_name='';
return $form_name;
break;
case 7:
//get optical item form
$form_name='';
return $form_name;
break;
}
}
public function find_item_batch_watcher($batch_item_id, $selected_columns): array|object|null
{
return ItemBatchWatcher::select($selected_columns)->find($batch_item_id);
}
public function move_item_between_sections($item_batch_watcher, $quantity_to_move, $other_stock)
{
$item_batch_watcher->store_stock -= $quantity_to_move;
$item_batch_watcher->{$other_stock} =$item_batch_watcher->{$other_stock} + $quantity_to_move;
$item_batch_watcher->save();
}
public function move_next_available_item($item_type, $item_id,$column, $selected_columns, $quantity_to_move,$other_stock): array|object|null
{
if (batch_tracking_method() == "FIFO") {
$random_batch_next = ItemBatchWatcher::where(['item_id' => $item_id, 'item_type' => $item_type])->where("{$column}", '>', 0)->orderBy('id', 'asc')->select($selected_columns)->first();;
}
elseif (batch_tracking_method() == "FEFO") {
$random_batch_next = ItemBatchWatcher::where(['item_id' => $item_id, 'item_type' => $item_type])->where("{$column}", '>', 0)->orderBy(DB::raw('ABS(DATEDIFF(expiry_date, NOW()))'))->select($selected_columns)->first();;
}
if ($random_batch_next) {
$random_batch_next->store_stock -= $quantity_to_move;
$random_batch_next->{$other_stock} += $quantity_to_move;
$random_batch_next->save();
return $random_batch_next;
}
return false;
}
public function move_batch_item_from_store_to_other_sections($item_type, $item_id, $batch_item_id, $quantity_to_move, $requisition_id,$item_name_table,$move_to,$selected_columns,$other_stock)
{
$item_batch_watcher = $this->find_item_batch_watcher($batch_item_id,$selected_columns);
$batch_number = "";
if ($item_batch_watcher) {
$this->move_item_between_sections($item_batch_watcher, $quantity_to_move, $other_stock);
$batch_number = $item_batch_watcher->batch_number;
} else{
//reduce the next one in line that has enough store stock.
$batch_details= $this->move_next_available_item($item_type, $item_id,'store_stock', $selected_columns, $quantity_to_move,$other_stock);
if($batch_details){
$batch_number = $batch_details->batch_number;
}
}
record_store_stock_movement($item_type, $item_id, $batch_number, $quantity_to_move, "{$move_to}", $requisition_id);
}
//function to create optical reconciliation
public function createOpticalReconciliation($item_type,$batche,$variance, $item_id, $physical_new_stock, $difference, $system_stock,$completion,$affected_account_id,$reconciliation_date,$general_comment,$max_stock_count_identifier):void
{
$batch = $batche[$item_id];
$physical_stock = new OpticalStockReconciliation;
$physical_stock->item_id = $item_id;
$physical_stock->system_stock = $system_stock;
$physical_stock->physical_stock = $physical_new_stock;
$physical_stock->difference = $difference;
$physical_stock->variance = $variance;
$physical_stock->batches = json_encode($batche[$item_id]);
$physical_stock->reason_for_difference = null;
$physical_stock->affected_account_id = $affected_account_id;
$physical_stock->completion_status = $completion;
$physical_stock->reconciliation_date = $reconciliation_date;
$physical_stock->stock_count_identifier = $max_stock_count_identifier+1;//increase ID by 1 for new
$physical_stock->general_comment = $general_comment;
$physical_stock->type =$item_type; //1 - pharmarcy, 0 - stores
$physical_stock->created_by = Auth::id();
$physical_stock->save();
if (isset($batch['reason_id'])){
foreach($batch['reason_id'] as $reason_id){
//update reconciliation reason
StockReconciliationReason::where('id', $reason_id)->update(['reconciliation_id' => $physical_stock->id]);
}
}
}
//save reconciliation reason
public function saveReconciliationReason($item_type,$item_id,$reason,$source):int
{
$reason_stock = new StockReconciliationReason;
$reason_stock->item_id = $item_id;
$reason_stock->item_type = $item_type;
$reason_stock->reason_id = $reason;
$reason_stock->source = $source;
$reason_stock->created_by = Auth::id();
$reason_stock->save();
return $reason_stock->id;
}
public function reconcileBatches($batches,$reason,$source, $item_type, $item_id, $batch_number, $batch_quantity, $batch_expiry_date, $store_or_pharmacy, $batch_unit_cost, $batch_records_db_id, $ward_id): array
{
$batch_record = ItemBatchWatcher::find($batch_records_db_id);
if ($batch_record) {
$item_batch_watcher = $batch_record;
} else {
$item_batch_watcher = new ItemBatchWatcher;
$item_batch_watcher->item_id = $item_id;
$item_batch_watcher->item_type = $item_type;
}
// Determine the original quantity based on store_or_pharmacy
$original_quantity = 0;
switch ($store_or_pharmacy) {
case 'store':
$original_quantity = $item_batch_watcher->store_stock ?? 0;
break;
case 'pharmacy':
$original_quantity = $item_batch_watcher->pharmacy_stock ?? 0;
break;
case 'lab':
$original_quantity = $item_batch_watcher->lab_stock ?? 0;
break;
case 'ward':
// Special handling for ward stock (aggregate or specific ward)
$exploded_ward_stocks = $item_batch_watcher->ward_stock ? explode(",", $item_batch_watcher->ward_stock) : [];
$exploded_ward_ids = $item_batch_watcher->ward_id ? explode(",", $item_batch_watcher->ward_id) : [];
$ward_id_key = array_search($ward_id, $exploded_ward_ids);
$original_quantity = $ward_id_key !== false ? ($exploded_ward_stocks[$ward_id_key] ?? 0) : 0;
break;
}
$original_values = [
'original_unit_cost' => $item_batch_watcher->cost_price ?? 0,
'original_quantity' => $original_quantity,
'original_batch_number' => $item_batch_watcher->batch_number ?? null,
];
$adjusted_values = [
'batch_id' => $batch_record->id??null,
'unit_cost' => $batch_unit_cost,
'quantity' => $batch_quantity,
'expiry_date' => $batch_expiry_date,
'batch_number' => $batch_number,
];
if (isset($batches[$item_id])) {
$batch_exists = false;
foreach ($batches[$item_id] as &$data) {
// Check if batch_id exists
if(isset($data['batch_id'])){
if ($data['batch_id'] == $batch_record->id) {
$batch_exists = true;
// Update the existing batch
$data['original_unit_cost'] = $original_values['original_unit_cost'];
$data['original_quantity'] = $original_values['original_quantity'];
$data['original_batch_number'] = $original_values['original_batch_number'];
$data['unit_cost'] = $batch_unit_cost;
$data['quantity'] = $batch_quantity;
$data['expiry_date'] = $batch_expiry_date;
$data['batch_number'] = $batch_number;
break;
}
}
}
// If batch_id doesn't exist, add new batch
if (!$batch_exists) {
$batches[$item_id][] = array_merge($original_values, $adjusted_values);
}
} else {
$batches[$item_id][] = array_merge($original_values, $adjusted_values);
}
// Update ItemBatchWatcher
$item_batch_watcher->batch_number = $batch_number;
is_null($batch_unit_cost) ? "" : $item_batch_watcher->cost_price = $batch_unit_cost;
if ($store_or_pharmacy == "store") {
$item_batch_watcher->store_stock = $batch_quantity;
}
if ($store_or_pharmacy == "pharmacy") {
$item_batch_watcher->pharmacy_stock = $batch_quantity;
}
if ($store_or_pharmacy == "lab"){
$item_batch_watcher->lab_stock = $batch_quantity;
}
if ($store_or_pharmacy == "ward") {
$exploded_ward_stocks = $item_batch_watcher->ward_stock ? explode(",",$item_batch_watcher->ward_stock) : [];
$exploded_ward_ids = $item_batch_watcher->ward_id ? explode(",", $item_batch_watcher->ward_id) : [];
$ward_id_key = array_search($ward_id, $exploded_ward_ids);
if($ward_id_key !== false){
$exploded_ward_stocks[$ward_id_key] = $batch_quantity;
}
$item_batch_watcher->ward_stock = implode(",", $exploded_ward_stocks);
}
if($reason != ''){
$reason_id =$this->saveReconciliationReason($item_type,$item_id,$reason,$source);
$batches[$item_id]['reason_id'][] = $reason_id;
}
$item_batch_watcher->expiry_date = $batch_expiry_date;
$item_batch_watcher->save();
return $batches;
}
public function processReconcileItems($items_reconciled):EloquentCollection
{
foreach($items_reconciled as $item_reconciled){
$batches = json_decode($item_reconciled->batches,true) ;
//remove reason_id if exist
if(isset($batches['reason_id'])){
// Set reason_id property on the $item_reconciled object
$item_reconciled->reason_id = $batches['reason_id'];
// Remove reason_id from the batches array
unset($batches['reason_id']);
}
//modified batches array to JSON
//get reason
if(isset($item_reconciled->reason_id)){
foreach($item_reconciled->reason_id as $val){
$store_reason = StockReconciliationReason::find($val);
if (!is_null($store_reason)) {
$reason = ReconilicationReason::find($store_reason->reason_id);
if (!is_null($reason)){
$reasons[] = $reason->name;
}
}
}
}
foreach($batches as $batch_key => $batch){
$batch['reason'] = isset($reasons[$batch_key]) ? $reasons[$batch_key] : null;
$batches[$batch_key] = $batch;
}
$item_reconciled->batches = $batches;
}
return $items_reconciled;
}
//function to get all reconciliation reasons
public function getAllReconciliationReasonsArray():array
{
return ReconilicationReason::orderBy('name','asc')->pluck('name','id')->prepend('Select Reason ', '')->toArray();
}
//function to return stock to any col stores or pharmacy
public function returnStockToAnySection($item_type,$drug_id,$ward_treatments_dispensation,$patient_id,$episode_id, $quantity_to_return,$stock_col,$selected_batch_id):void
{
if ($selected_batch_id !=null){
$batch_id = $selected_batch_id;
}else{
$batch_id = WardTreatmentBatchDispensation::where(['patient_id'=>$patient_id,'episode_id'=>$episode_id,'item_id'=>$drug_id])->value('item_batch_id');
}
//check for batch id
if($batch_id != null){
//get batch number that was consumed
$containsComma = array_filter([$batch_id], function($string) {
return strpos($string, ',') !== false;
});
if (!empty($containsComma)) {
$batch_id = explode(', ', $batch_id);
$batch_id = $batch_id[0];
}
//get item batch watcher record using batch number, item_type, item_id, where stock is not 0
$item_batch_watcher = ItemBatchWatcher::find($batch_id);
if ($item_batch_watcher) {
$item_batch_watcher->{$stock_col} = $item_batch_watcher->{$stock_col} + $quantity_to_return;
$item_batch_watcher->update();
}else{
// check if that drug has any batch number
$item_batch_watcher = ItemBatchWatcher::where(['item_type'=>$item_type, 'item_id'=>$drug_id, ])->where("{$stock_col}", '>', '0')->orderBy(DB::raw('ABS(DATEDIFF(expiry_date, NOW()))'))->first();
if ($item_batch_watcher) {
$item_batch_watcher->{$stock_col} = $item_batch_watcher->{$stock_col} + $quantity_to_return;
$item_batch_watcher->update();
}else{
$item_batch_watcher = new ItemBatchWatcher;
$item_batch_watcher->item_type = $item_type;
$item_batch_watcher->item_id = $drug_id;
$item_batch_watcher->batch_number = 'RTS-00'.$drug_id;
$item_batch_watcher->{$stock_col} = $quantity_to_return;
$item_batch_watcher->cost_price = $ward_treatments_dispensation->cost_price;
// add one month to expiry date
$item_batch_watcher->expiry_date = date('Y-m-d', strtotime('+1 month'));
$item_batch_watcher->save();
}
}
}else{
// check if that drug has any batch number
$item_batch_watcher = ItemBatchWatcher::where(['item_type'=>$item_type, 'item_id'=>$drug_id, ])->where("{$stock_col}", '>', '0')->orderBy(DB::raw('ABS(DATEDIFF(expiry_date, NOW()))'))->first();
if ($item_batch_watcher) {
$item_batch_watcher->{$stock_col} = $item_batch_watcher->{$stock_col} + $quantity_to_return;
$item_batch_watcher->update();
}else{
$item_batch_watcher = new ItemBatchWatcher;
$item_batch_watcher->item_type = $item_type;
$item_batch_watcher->item_id = $drug_id;
$item_batch_watcher->batch_number = 'RTS-00'.$drug_id;
$item_batch_watcher->{$stock_col} = $quantity_to_return;
$item_batch_watcher->cost_price = $ward_treatments_dispensation->cost_price;
// add one month to expiry date
$item_batch_watcher->expiry_date = date('Y-m-d', strtotime('+1 month'));
$item_batch_watcher->save();
}
}
}
}