changes to management
This commit is contained in:
156
app/Http/Controllers/BoxController.php
Normal file
156
app/Http/Controllers/BoxController.php
Normal file
@@ -0,0 +1,156 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Enums\RecordStatusEnum;
|
||||
use App\Models\BaseModel;
|
||||
use App\Models\Box;
|
||||
use App\Models\Rack;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Session;
|
||||
|
||||
class BoxController extends Controller
|
||||
{
|
||||
protected $model;
|
||||
protected $rackModel;
|
||||
protected $baseOrganizationId;
|
||||
protected $base;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->baseOrganizationId = Session::get('base_organization_id');
|
||||
$this->base = Session::get('base_organization');
|
||||
$this->model = new Box();
|
||||
$this->rackModel = new Rack();
|
||||
}
|
||||
|
||||
public function index(Request $request)
|
||||
{
|
||||
$recordStatusCondition = Auth::id() == 1 ? [RecordStatusEnum::DELETE, RecordStatusEnum::ACTIVE] : [RecordStatusEnum::ACTIVE];
|
||||
$racks = $this->rackModel::with(['equipment.room.building'])
|
||||
->where(BaseModel::RECORD_STATUS_FIELD, RecordStatusEnum::ACTIVE)
|
||||
->whereHas('equipment', function ($query) {
|
||||
$query->where('organization_id', $this->baseOrganizationId);
|
||||
})
|
||||
->get();
|
||||
|
||||
$boxes = $this->model::with(['rack.equipment.room.building'])
|
||||
->whereIn(BaseModel::RECORD_STATUS_FIELD, $recordStatusCondition)
|
||||
->whereIn('rack_id', $racks->pluck('id')->toArray())
|
||||
->when(!empty($request->kword), function ($query) use ($request) {
|
||||
$keyword = str_replace(' ', '', $request->kword);
|
||||
$query->where(function ($query) use ($keyword) {
|
||||
$query->whereRaw("replace(name, ' ','') like ?", ['%' . $keyword . '%'])
|
||||
->orWhereRaw("replace(code, ' ','') like ?", ['%' . $keyword . '%']);
|
||||
});
|
||||
})
|
||||
->when(!empty(trim($request->rack_id)), function ($query) use ($request) {
|
||||
$query->where('rack_id', $request->rack_id);
|
||||
})
|
||||
->orderBy(BaseModel::CREATED_AT_FIELD, 'desc')
|
||||
->paginate(config('nrml.pagination.perpage', 10));
|
||||
|
||||
return view('box', ['boxes' => $boxes, 'racks' => $racks]);
|
||||
}
|
||||
|
||||
public function save(Request $request)
|
||||
{
|
||||
try {
|
||||
$validator = \Validator::make($request->all(), [
|
||||
'rack_id' => 'required',
|
||||
'code' => 'required',
|
||||
'name' => 'required',
|
||||
'rows' => 'required|integer|min:1',
|
||||
'columns' => 'required|integer|min:1',
|
||||
]);
|
||||
|
||||
if ($validator->fails()) {
|
||||
return response()->json(['success' => false, 'message' => 'Unable to create box.', 'errors' => $validator->errors()->all()]);
|
||||
}
|
||||
|
||||
$this->model::query()->insert([
|
||||
'rack_id' => $request->rack_id,
|
||||
'code' => $request->code,
|
||||
'name' => $request->name,
|
||||
'rows' => $request->rows,
|
||||
'columns' => $request->columns,
|
||||
'capacity' => $request->capacity ?: ((int) $request->rows * (int) $request->columns),
|
||||
BaseModel::CREATED_AT_FIELD => date('Y-m-d H:i:s'),
|
||||
BaseModel::CREATED_BY_FIELD => Auth::id(),
|
||||
]);
|
||||
|
||||
return response()->json(['success' => true, 'message' => 'Box has been created.']);
|
||||
} catch (\Exception $e) {
|
||||
return response()->json(['success' => false, 'message' => 'Unable to create box.', 'errors' => $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
|
||||
public function update(Request $request)
|
||||
{
|
||||
try {
|
||||
$validator = \Validator::make($request->all(), [
|
||||
'box_id' => 'required',
|
||||
'rack_id' => 'required',
|
||||
'code' => 'required',
|
||||
'name' => 'required',
|
||||
'rows' => 'required|integer|min:1',
|
||||
'columns' => 'required|integer|min:1',
|
||||
]);
|
||||
|
||||
if ($validator->fails()) {
|
||||
return response()->json(['success' => false, 'message' => 'Unable to update box.', 'errors' => $validator->errors()->all()]);
|
||||
}
|
||||
|
||||
$this->model::query()->where('id', $request->box_id)->update([
|
||||
'rack_id' => $request->rack_id,
|
||||
'code' => $request->code,
|
||||
'name' => $request->name,
|
||||
'rows' => $request->rows,
|
||||
'columns' => $request->columns,
|
||||
'capacity' => $request->capacity ?: ((int) $request->rows * (int) $request->columns),
|
||||
BaseModel::UPDATED_AT_FIELD => date('Y-m-d H:i:s'),
|
||||
BaseModel::UPDATED_BY_FIELD => Auth::id(),
|
||||
]);
|
||||
|
||||
return response()->json(['success' => true, 'message' => 'Box has been updated.']);
|
||||
} catch (\Exception $e) {
|
||||
return response()->json(['success' => false, 'message' => 'Unable to update box.', 'errors' => $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
|
||||
public function get(Request $request)
|
||||
{
|
||||
try {
|
||||
$box = $this->model::query()->where('id', $request->box_id)->first();
|
||||
return response()->json(['success' => true, 'message' => 'Box has been retrieved.', 'data' => $box]);
|
||||
} catch (\Exception $e) {
|
||||
return response()->json(['success' => false, 'message' => 'Unable to retrieve box.', 'errors' => $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
|
||||
public function delete(Request $request)
|
||||
{
|
||||
try {
|
||||
$this->model::query()->where('id', $request->box_id)->update([
|
||||
BaseModel::DELETED_AT_FIELD => date('Y-m-d H:i:s'),
|
||||
BaseModel::DELETED_BY_FIELD => Auth::id(),
|
||||
BaseModel::RECORD_STATUS_FIELD => RecordStatusEnum::DELETE,
|
||||
]);
|
||||
|
||||
return response()->json(['success' => true, 'message' => 'Box has been deleted.']);
|
||||
} catch (\Exception $e) {
|
||||
return response()->json(['success' => false, 'message' => 'Unable to delete box.', 'errors' => $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
|
||||
public function restore(Request $request)
|
||||
{
|
||||
try {
|
||||
$this->model::query()->where('id', $request->box_id)->update([BaseModel::RECORD_STATUS_FIELD => RecordStatusEnum::ACTIVE]);
|
||||
return response()->json(['success' => true, 'message' => 'Box has been restored.']);
|
||||
} catch (\Exception $e) {
|
||||
return response()->json(['success' => false, 'message' => 'Unable to restore box.', 'errors' => $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
}
|
||||
146
app/Http/Controllers/RackController.php
Normal file
146
app/Http/Controllers/RackController.php
Normal file
@@ -0,0 +1,146 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Enums\RecordStatusEnum;
|
||||
use App\Models\BaseModel;
|
||||
use App\Models\Equipment;
|
||||
use App\Models\Rack;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Session;
|
||||
|
||||
class RackController extends Controller
|
||||
{
|
||||
protected $model;
|
||||
protected $equipmentModel;
|
||||
protected $baseOrganizationId;
|
||||
protected $base;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->baseOrganizationId = Session::get('base_organization_id');
|
||||
$this->base = Session::get('base_organization');
|
||||
$this->model = new Rack();
|
||||
$this->equipmentModel = new Equipment();
|
||||
}
|
||||
|
||||
public function index(Request $request)
|
||||
{
|
||||
$recordStatusCondition = Auth::id() == 1 ? [RecordStatusEnum::DELETE, RecordStatusEnum::ACTIVE] : [RecordStatusEnum::ACTIVE];
|
||||
$equipments = $this->equipmentModel::with(['room.building'])
|
||||
->where(BaseModel::RECORD_STATUS_FIELD, RecordStatusEnum::ACTIVE)
|
||||
->where('organization_id', $this->baseOrganizationId)
|
||||
->get();
|
||||
|
||||
$racks = $this->model::with(['equipment.room.building'])
|
||||
->whereIn(BaseModel::RECORD_STATUS_FIELD, $recordStatusCondition)
|
||||
->whereIn('equipment_id', $equipments->pluck('id')->toArray())
|
||||
->when(!empty($request->kword), function ($query) use ($request) {
|
||||
$keyword = str_replace(' ', '', $request->kword);
|
||||
$query->where(function ($query) use ($keyword) {
|
||||
$query->whereRaw("replace(name, ' ','') like ?", ['%' . $keyword . '%'])
|
||||
->orWhereRaw("replace(code, ' ','') like ?", ['%' . $keyword . '%']);
|
||||
});
|
||||
})
|
||||
->when(!empty(trim($request->equipment_id)), function ($query) use ($request) {
|
||||
$query->where('equipment_id', $request->equipment_id);
|
||||
})
|
||||
->orderBy(BaseModel::CREATED_AT_FIELD, 'desc')
|
||||
->paginate(config('nrml.pagination.perpage', 10));
|
||||
|
||||
return view('rack', ['racks' => $racks, 'equipments' => $equipments]);
|
||||
}
|
||||
|
||||
public function save(Request $request)
|
||||
{
|
||||
try {
|
||||
$validator = \Validator::make($request->all(), [
|
||||
'equipment_id' => 'required',
|
||||
'code' => 'required',
|
||||
'name' => 'required',
|
||||
]);
|
||||
|
||||
if ($validator->fails()) {
|
||||
return response()->json(['success' => false, 'message' => 'Unable to create rack.', 'errors' => $validator->errors()->all()]);
|
||||
}
|
||||
|
||||
$this->model::query()->insert([
|
||||
'equipment_id' => $request->equipment_id,
|
||||
'code' => $request->code,
|
||||
'name' => $request->name,
|
||||
'description' => $request->description,
|
||||
BaseModel::CREATED_AT_FIELD => date('Y-m-d H:i:s'),
|
||||
BaseModel::CREATED_BY_FIELD => Auth::id(),
|
||||
]);
|
||||
|
||||
return response()->json(['success' => true, 'message' => 'Rack has been created.']);
|
||||
} catch (\Exception $e) {
|
||||
return response()->json(['success' => false, 'message' => 'Unable to create rack.', 'errors' => $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
|
||||
public function update(Request $request)
|
||||
{
|
||||
try {
|
||||
$validator = \Validator::make($request->all(), [
|
||||
'rack_id' => 'required',
|
||||
'equipment_id' => 'required',
|
||||
'code' => 'required',
|
||||
'name' => 'required',
|
||||
]);
|
||||
|
||||
if ($validator->fails()) {
|
||||
return response()->json(['success' => false, 'message' => 'Unable to update rack.', 'errors' => $validator->errors()->all()]);
|
||||
}
|
||||
|
||||
$this->model::query()->where('id', $request->rack_id)->update([
|
||||
'equipment_id' => $request->equipment_id,
|
||||
'code' => $request->code,
|
||||
'name' => $request->name,
|
||||
'description' => $request->description,
|
||||
BaseModel::UPDATED_AT_FIELD => date('Y-m-d H:i:s'),
|
||||
BaseModel::UPDATED_BY_FIELD => Auth::id(),
|
||||
]);
|
||||
|
||||
return response()->json(['success' => true, 'message' => 'Rack has been updated.']);
|
||||
} catch (\Exception $e) {
|
||||
return response()->json(['success' => false, 'message' => 'Unable to update rack.', 'errors' => $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
|
||||
public function get(Request $request)
|
||||
{
|
||||
try {
|
||||
$rack = $this->model::query()->where('id', $request->rack_id)->first();
|
||||
return response()->json(['success' => true, 'message' => 'Rack has been retrieved.', 'data' => $rack]);
|
||||
} catch (\Exception $e) {
|
||||
return response()->json(['success' => false, 'message' => 'Unable to retrieve rack.', 'errors' => $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
|
||||
public function delete(Request $request)
|
||||
{
|
||||
try {
|
||||
$this->model::query()->where('id', $request->rack_id)->update([
|
||||
BaseModel::DELETED_AT_FIELD => date('Y-m-d H:i:s'),
|
||||
BaseModel::DELETED_BY_FIELD => Auth::id(),
|
||||
BaseModel::RECORD_STATUS_FIELD => RecordStatusEnum::DELETE,
|
||||
]);
|
||||
|
||||
return response()->json(['success' => true, 'message' => 'Rack has been deleted.']);
|
||||
} catch (\Exception $e) {
|
||||
return response()->json(['success' => false, 'message' => 'Unable to delete rack.', 'errors' => $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
|
||||
public function restore(Request $request)
|
||||
{
|
||||
try {
|
||||
$this->model::query()->where('id', $request->rack_id)->update([BaseModel::RECORD_STATUS_FIELD => RecordStatusEnum::ACTIVE]);
|
||||
return response()->json(['success' => true, 'message' => 'Rack has been restored.']);
|
||||
} catch (\Exception $e) {
|
||||
return response()->json(['success' => false, 'message' => 'Unable to restore rack.', 'errors' => $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
}
|
||||
472
app/Http/Controllers/StoragePositionController.php
Normal file
472
app/Http/Controllers/StoragePositionController.php
Normal file
@@ -0,0 +1,472 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Enums\RecordStatusEnum;
|
||||
use App\Models\AliqoutMovement;
|
||||
use App\Models\Aliquot;
|
||||
use App\Models\AliquotTransaction;
|
||||
use App\Models\BaseModel;
|
||||
use App\Models\Box;
|
||||
use App\Models\ContainerType;
|
||||
use App\Models\Pathogen;
|
||||
use App\Models\PathogenInventorySample;
|
||||
use App\Models\Position;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Session;
|
||||
|
||||
class StoragePositionController extends Controller
|
||||
{
|
||||
protected $baseOrganizationId;
|
||||
protected $base;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->baseOrganizationId = Session::get('base_organization_id');
|
||||
$this->base = Session::get('base_organization');
|
||||
}
|
||||
|
||||
public function index(Box $box)
|
||||
{
|
||||
$box->load(['rack.equipment.room.building']);
|
||||
|
||||
$positions = Position::with(['aliquot.sample.pathogen', 'aliquot.containerType.containerName'])
|
||||
->where('box_id', $box->id)
|
||||
->where(BaseModel::RECORD_STATUS_FIELD, RecordStatusEnum::ACTIVE)
|
||||
->orderBy('row_label')
|
||||
->orderByRaw('CAST(column_label AS UNSIGNED)')
|
||||
->get();
|
||||
|
||||
$samples = PathogenInventorySample::with(['pathogen'])
|
||||
->where(BaseModel::RECORD_STATUS_FIELD, RecordStatusEnum::ACTIVE)
|
||||
->where('organization_id', $this->baseOrganizationId)
|
||||
->orderBy(BaseModel::CREATED_AT_FIELD, 'desc')
|
||||
->limit(200)
|
||||
->get();
|
||||
|
||||
$pathogens = Pathogen::with(['riskGroup', 'bsl'])
|
||||
->where(BaseModel::RECORD_STATUS_FIELD, RecordStatusEnum::ACTIVE)
|
||||
->orderBy('scientific_name')
|
||||
->get();
|
||||
|
||||
$containerTypes = ContainerType::with(['containerName'])
|
||||
->where(BaseModel::RECORD_STATUS_FIELD, RecordStatusEnum::ACTIVE)
|
||||
->get();
|
||||
|
||||
$emptyPositions = Position::with(['box.rack.equipment.room.building'])
|
||||
->where(BaseModel::RECORD_STATUS_FIELD, RecordStatusEnum::ACTIVE)
|
||||
->where('occupied', 0)
|
||||
->orderByRaw('box_id = ? DESC', [$box->id])
|
||||
->orderBy('box_id')
|
||||
->orderBy('row_label')
|
||||
->orderByRaw('CAST(column_label AS UNSIGNED)')
|
||||
->get();
|
||||
|
||||
$recentMovements = AliqoutMovement::with([
|
||||
'aliquot.sample.pathogen',
|
||||
'fromPosition.box.rack.equipment.room.building',
|
||||
'toPosition.box.rack.equipment.room.building',
|
||||
])
|
||||
->where(BaseModel::RECORD_STATUS_FIELD, RecordStatusEnum::ACTIVE)
|
||||
->where(function ($query) use ($box) {
|
||||
$query->whereHas('fromPosition', function ($query) use ($box) {
|
||||
$query->where('box_id', $box->id);
|
||||
})->orWhereHas('toPosition', function ($query) use ($box) {
|
||||
$query->where('box_id', $box->id);
|
||||
});
|
||||
})
|
||||
->orderBy(BaseModel::CREATED_AT_FIELD, 'desc')
|
||||
->limit(10)
|
||||
->get();
|
||||
|
||||
return view('box_positions', [
|
||||
'box' => $box,
|
||||
'positions' => $positions,
|
||||
'samples' => $samples,
|
||||
'pathogens' => $pathogens,
|
||||
'containerTypes' => $containerTypes,
|
||||
'emptyPositions' => $emptyPositions,
|
||||
'recentMovements' => $recentMovements,
|
||||
]);
|
||||
}
|
||||
|
||||
public function generate(Request $request)
|
||||
{
|
||||
$request->validate(['box_id' => 'required']);
|
||||
|
||||
try {
|
||||
$box = Box::query()->findOrFail($request->box_id);
|
||||
|
||||
if ($box->rows < 1 || $box->columns < 1) {
|
||||
return response()->json(['success' => false, 'message' => 'Box rows and columns must be configured before generating positions.']);
|
||||
}
|
||||
|
||||
DB::beginTransaction();
|
||||
|
||||
for ($row = 1; $row <= $box->rows; $row++) {
|
||||
$rowLabel = $this->rowLabel($row);
|
||||
|
||||
for ($column = 1; $column <= $box->columns; $column++) {
|
||||
Position::query()->firstOrCreate(
|
||||
[
|
||||
'box_id' => $box->id,
|
||||
'row_label' => $rowLabel,
|
||||
'column_label' => (string) $column,
|
||||
],
|
||||
[
|
||||
'code' => $rowLabel . $column,
|
||||
'occupied' => 0,
|
||||
BaseModel::CREATED_AT_FIELD => date('Y-m-d H:i:s'),
|
||||
BaseModel::CREATED_BY_FIELD => Auth::id(),
|
||||
BaseModel::RECORD_STATUS_FIELD => RecordStatusEnum::ACTIVE,
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
DB::commit();
|
||||
|
||||
return response()->json(['success' => true, 'message' => 'Box positions generated successfully.']);
|
||||
} catch (\Exception $e) {
|
||||
DB::rollBack();
|
||||
return response()->json(['success' => false, 'message' => 'Unable to generate box positions.', 'errors' => $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
|
||||
public function storeAliquot(Request $request)
|
||||
{
|
||||
try {
|
||||
$validator = \Validator::make($request->all(), [
|
||||
'storage_position_id' => 'required',
|
||||
'aliquot_number' => 'required',
|
||||
'initial_volume' => 'required|numeric|min:0',
|
||||
'remaining_volume' => 'required|numeric|min:0',
|
||||
'volume_unit' => 'required',
|
||||
'status' => 'required',
|
||||
]);
|
||||
|
||||
if ($validator->fails()) {
|
||||
return response()->json(['success' => false, 'message' => 'Unable to save aliquot.', 'errors' => $validator->errors()->all()]);
|
||||
}
|
||||
|
||||
$position = Position::with(['aliquot'])->findOrFail($request->storage_position_id);
|
||||
if (!empty($position->aliquot)) {
|
||||
return response()->json(['success' => false, 'message' => 'This position is already occupied. Remove or transfer the current aliquot first.']);
|
||||
}
|
||||
|
||||
DB::beginTransaction();
|
||||
|
||||
$sampleId = $request->sample_id;
|
||||
if (empty($sampleId) && !empty($request->pathogen_id)) {
|
||||
$sampleId = PathogenInventorySample::query()->insertGetId([
|
||||
'organization_id' => $this->baseOrganizationId,
|
||||
'pathogen_id' => $request->pathogen_id,
|
||||
'sample_code' => $request->sample_code ?: $request->aliquot_number,
|
||||
'initial_volume' => $request->initial_volume,
|
||||
'remaining_volume' => $request->remaining_volume,
|
||||
'volume_unit' => $request->volume_unit,
|
||||
'status' => 'Stored',
|
||||
BaseModel::CREATED_AT_FIELD => date('Y-m-d H:i:s'),
|
||||
BaseModel::CREATED_BY_FIELD => Auth::id(),
|
||||
BaseModel::RECORD_STATUS_FIELD => RecordStatusEnum::ACTIVE,
|
||||
]);
|
||||
}
|
||||
|
||||
$aliquotId = Aliquot::query()->insertGetId([
|
||||
'sample_id' => $sampleId,
|
||||
'container_type_id' => $request->container_type_id,
|
||||
'aliquot_number' => $request->aliquot_number,
|
||||
'initial_volume' => $request->initial_volume,
|
||||
'remaining_volume' => $request->remaining_volume,
|
||||
'volume_unit' => $request->volume_unit,
|
||||
'concentration' => $request->concentration,
|
||||
'storage_position_id' => $request->storage_position_id,
|
||||
'expiry_date' => !empty($request->expiry_date) ? date('Y-m-d H:i:s', strtotime($request->expiry_date)) : null,
|
||||
'status' => $request->status,
|
||||
'last_accessed_at' => date('Y-m-d H:i:s'),
|
||||
'description' => $request->description,
|
||||
BaseModel::CREATED_AT_FIELD => date('Y-m-d H:i:s'),
|
||||
BaseModel::CREATED_BY_FIELD => Auth::id(),
|
||||
BaseModel::RECORD_STATUS_FIELD => RecordStatusEnum::ACTIVE,
|
||||
]);
|
||||
|
||||
$position->update([
|
||||
'occupied' => 1,
|
||||
BaseModel::UPDATED_AT_FIELD => date('Y-m-d H:i:s'),
|
||||
BaseModel::UPDATED_BY_FIELD => Auth::id(),
|
||||
]);
|
||||
|
||||
AliqoutMovement::query()->insert([
|
||||
'aliquot_id' => $aliquotId,
|
||||
'from_position_id' => null,
|
||||
'to_position_id' => $request->storage_position_id,
|
||||
'movement_reason' => 'Initial storage placement',
|
||||
'moved_by' => optional(Auth::user())->name ?? optional(Auth::user())->email ?? (string) Auth::id(),
|
||||
BaseModel::CREATED_AT_FIELD => date('Y-m-d H:i:s'),
|
||||
BaseModel::CREATED_BY_FIELD => Auth::id(),
|
||||
BaseModel::RECORD_STATUS_FIELD => RecordStatusEnum::ACTIVE,
|
||||
]);
|
||||
|
||||
DB::commit();
|
||||
|
||||
return response()->json(['success' => true, 'message' => 'Aliquot placed in storage position.']);
|
||||
} catch (\Exception $e) {
|
||||
DB::rollBack();
|
||||
return response()->json(['success' => false, 'message' => 'Unable to save aliquot.', 'errors' => $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
|
||||
public function removeAliquot(Request $request)
|
||||
{
|
||||
try {
|
||||
$validator = \Validator::make($request->all(), [
|
||||
'aliquot_id' => 'required',
|
||||
]);
|
||||
|
||||
if ($validator->fails()) {
|
||||
return response()->json(['success' => false, 'message' => 'Unable to remove aliquot.', 'errors' => $validator->errors()->all()]);
|
||||
}
|
||||
|
||||
$aliquot = Aliquot::query()->findOrFail($request->aliquot_id);
|
||||
|
||||
DB::beginTransaction();
|
||||
|
||||
$positionId = $aliquot->storage_position_id;
|
||||
$aliquot->update([
|
||||
'status' => 'Removed',
|
||||
BaseModel::DELETED_AT_FIELD => date('Y-m-d H:i:s'),
|
||||
BaseModel::DELETED_BY_FIELD => Auth::id(),
|
||||
BaseModel::RECORD_STATUS_FIELD => RecordStatusEnum::DELETE,
|
||||
]);
|
||||
|
||||
Position::query()->where('id', $positionId)->update([
|
||||
'occupied' => 0,
|
||||
BaseModel::UPDATED_AT_FIELD => date('Y-m-d H:i:s'),
|
||||
BaseModel::UPDATED_BY_FIELD => Auth::id(),
|
||||
]);
|
||||
|
||||
AliqoutMovement::query()->insert([
|
||||
'aliquot_id' => $aliquot->id,
|
||||
'from_position_id' => $positionId,
|
||||
'to_position_id' => null,
|
||||
'movement_reason' => $request->movement_reason ?: 'Removed from storage',
|
||||
'moved_by' => optional(Auth::user())->name ?? optional(Auth::user())->email ?? (string) Auth::id(),
|
||||
BaseModel::CREATED_AT_FIELD => date('Y-m-d H:i:s'),
|
||||
BaseModel::CREATED_BY_FIELD => Auth::id(),
|
||||
BaseModel::RECORD_STATUS_FIELD => RecordStatusEnum::ACTIVE,
|
||||
]);
|
||||
|
||||
DB::commit();
|
||||
|
||||
return response()->json(['success' => true, 'message' => 'Aliquot removed from this position.']);
|
||||
} catch (\Exception $e) {
|
||||
DB::rollBack();
|
||||
return response()->json(['success' => false, 'message' => 'Unable to remove aliquot.', 'errors' => $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
|
||||
public function moveAliquot(Request $request)
|
||||
{
|
||||
try {
|
||||
$validator = \Validator::make($request->all(), [
|
||||
'aliquot_id' => 'required',
|
||||
'to_position_id' => 'required',
|
||||
'movement_reason' => 'required',
|
||||
]);
|
||||
|
||||
if ($validator->fails()) {
|
||||
return response()->json(['success' => false, 'message' => 'Unable to move aliquot.', 'errors' => $validator->errors()->all()]);
|
||||
}
|
||||
|
||||
$aliquot = Aliquot::query()
|
||||
->where(BaseModel::RECORD_STATUS_FIELD, RecordStatusEnum::ACTIVE)
|
||||
->findOrFail($request->aliquot_id);
|
||||
|
||||
$fromPositionId = $aliquot->storage_position_id;
|
||||
$toPosition = Position::with(['aliquot'])->findOrFail($request->to_position_id);
|
||||
|
||||
if ((int) $fromPositionId === (int) $toPosition->id) {
|
||||
return response()->json(['success' => false, 'message' => 'The aliquot is already in that position.']);
|
||||
}
|
||||
|
||||
if (!empty($toPosition->aliquot) || (int) $toPosition->occupied === 1) {
|
||||
return response()->json(['success' => false, 'message' => 'Target position is occupied. Choose an empty position.']);
|
||||
}
|
||||
|
||||
DB::beginTransaction();
|
||||
|
||||
Position::query()->where('id', $fromPositionId)->update([
|
||||
'occupied' => 0,
|
||||
BaseModel::UPDATED_AT_FIELD => date('Y-m-d H:i:s'),
|
||||
BaseModel::UPDATED_BY_FIELD => Auth::id(),
|
||||
]);
|
||||
|
||||
Position::query()->where('id', $toPosition->id)->update([
|
||||
'occupied' => 1,
|
||||
BaseModel::UPDATED_AT_FIELD => date('Y-m-d H:i:s'),
|
||||
BaseModel::UPDATED_BY_FIELD => Auth::id(),
|
||||
]);
|
||||
|
||||
$aliquot->update([
|
||||
'storage_position_id' => $toPosition->id,
|
||||
'last_accessed_at' => date('Y-m-d H:i:s'),
|
||||
BaseModel::UPDATED_AT_FIELD => date('Y-m-d H:i:s'),
|
||||
BaseModel::UPDATED_BY_FIELD => Auth::id(),
|
||||
]);
|
||||
|
||||
AliqoutMovement::query()->insert([
|
||||
'aliquot_id' => $aliquot->id,
|
||||
'from_position_id' => $fromPositionId,
|
||||
'to_position_id' => $toPosition->id,
|
||||
'movement_reason' => $request->movement_reason,
|
||||
'moved_by' => optional(Auth::user())->name ?? optional(Auth::user())->email ?? (string) Auth::id(),
|
||||
BaseModel::CREATED_AT_FIELD => date('Y-m-d H:i:s'),
|
||||
BaseModel::CREATED_BY_FIELD => Auth::id(),
|
||||
BaseModel::RECORD_STATUS_FIELD => RecordStatusEnum::ACTIVE,
|
||||
]);
|
||||
|
||||
DB::commit();
|
||||
|
||||
return response()->json(['success' => true, 'message' => 'Aliquot moved successfully.']);
|
||||
} catch (\Exception $e) {
|
||||
DB::rollBack();
|
||||
return response()->json(['success' => false, 'message' => 'Unable to move aliquot.', 'errors' => $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
|
||||
public function movementHistory(Request $request)
|
||||
{
|
||||
try {
|
||||
$validator = \Validator::make($request->all(), [
|
||||
'aliquot_id' => 'required',
|
||||
]);
|
||||
|
||||
if ($validator->fails()) {
|
||||
return response()->json(['success' => false, 'message' => 'Unable to retrieve movement history.', 'errors' => $validator->errors()->all()]);
|
||||
}
|
||||
|
||||
$movements = AliqoutMovement::with([
|
||||
'fromPosition.box.rack.equipment.room.building',
|
||||
'toPosition.box.rack.equipment.room.building',
|
||||
])
|
||||
->where('aliquot_id', $request->aliquot_id)
|
||||
->where(BaseModel::RECORD_STATUS_FIELD, RecordStatusEnum::ACTIVE)
|
||||
->orderBy(BaseModel::CREATED_AT_FIELD, 'desc')
|
||||
->get()
|
||||
->map(function ($movement) {
|
||||
return [
|
||||
'from' => $this->positionLabel($movement->fromPosition),
|
||||
'to' => $this->positionLabel($movement->toPosition),
|
||||
'reason' => $movement->movement_reason,
|
||||
'moved_by' => $movement->moved_by,
|
||||
'moved_at' => $movement->created_at,
|
||||
];
|
||||
});
|
||||
|
||||
return response()->json(['success' => true, 'message' => 'Movement history retrieved.', 'data' => $movements]);
|
||||
} catch (\Exception $e) {
|
||||
return response()->json(['success' => false, 'message' => 'Unable to retrieve movement history.', 'errors' => $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
|
||||
public function useVolume(Request $request)
|
||||
{
|
||||
try {
|
||||
$validator = \Validator::make($request->all(), [
|
||||
'aliquot_id' => 'required',
|
||||
'used_volume' => 'required|numeric|min:0.01',
|
||||
'use_reason' => 'required',
|
||||
]);
|
||||
|
||||
if ($validator->fails()) {
|
||||
return response()->json(['success' => false, 'message' => 'Unable to record aliquot use.', 'errors' => $validator->errors()->all()]);
|
||||
}
|
||||
|
||||
$aliquot = Aliquot::query()
|
||||
->where(BaseModel::RECORD_STATUS_FIELD, RecordStatusEnum::ACTIVE)
|
||||
->findOrFail($request->aliquot_id);
|
||||
|
||||
$sourceVolume = (float) $aliquot->remaining_volume;
|
||||
$usedVolume = (float) $request->used_volume;
|
||||
|
||||
if ($usedVolume > $sourceVolume) {
|
||||
return response()->json(['success' => false, 'message' => 'Used volume cannot be greater than remaining volume.']);
|
||||
}
|
||||
|
||||
$remainingVolume = $sourceVolume - $usedVolume;
|
||||
|
||||
DB::beginTransaction();
|
||||
|
||||
$aliquot->update([
|
||||
'remaining_volume' => $remainingVolume,
|
||||
'status' => $remainingVolume <= 0 ? 'Consumed' : $aliquot->status,
|
||||
'last_accessed_at' => date('Y-m-d H:i:s'),
|
||||
BaseModel::UPDATED_AT_FIELD => date('Y-m-d H:i:s'),
|
||||
BaseModel::UPDATED_BY_FIELD => Auth::id(),
|
||||
]);
|
||||
|
||||
AliquotTransaction::query()->insert([
|
||||
'aliquot_id' => $aliquot->id,
|
||||
'transaction_at' => date('Y-m-d H:i:s'),
|
||||
'performed_by' => optional(Auth::user())->name ?? optional(Auth::user())->email ?? (string) Auth::id(),
|
||||
'source_volumn' => $sourceVolume,
|
||||
'affected_volume' => $usedVolume,
|
||||
'remaining_volume' => $remainingVolume,
|
||||
'volume_unit' => $aliquot->volume_unit,
|
||||
'storage_position_id' => $aliquot->storage_position_id,
|
||||
'reference_number' => $request->reference_number,
|
||||
'description' => $request->use_reason,
|
||||
'state' => 'approved',
|
||||
BaseModel::CREATED_AT_FIELD => date('Y-m-d H:i:s'),
|
||||
BaseModel::CREATED_BY_FIELD => Auth::id(),
|
||||
BaseModel::RECORD_STATUS_FIELD => RecordStatusEnum::ACTIVE,
|
||||
]);
|
||||
|
||||
DB::commit();
|
||||
|
||||
return response()->json(['success' => true, 'message' => 'Aliquot use recorded. Remaining volume: ' . $remainingVolume . ' ' . $aliquot->volume_unit]);
|
||||
} catch (\Exception $e) {
|
||||
DB::rollBack();
|
||||
return response()->json(['success' => false, 'message' => 'Unable to record aliquot use.', 'errors' => $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
|
||||
private function rowLabel(int $row): string
|
||||
{
|
||||
$label = '';
|
||||
|
||||
while ($row > 0) {
|
||||
$row--;
|
||||
$label = chr(65 + ($row % 26)) . $label;
|
||||
$row = intdiv($row, 26);
|
||||
}
|
||||
|
||||
return $label;
|
||||
}
|
||||
|
||||
private function positionLabel($position): string
|
||||
{
|
||||
if (empty($position)) {
|
||||
return 'Outside storage';
|
||||
}
|
||||
|
||||
$box = $position->box;
|
||||
$rack = optional($box)->rack;
|
||||
$equipment = optional($rack)->equipment;
|
||||
$room = optional($equipment)->room;
|
||||
$building = optional($room)->building;
|
||||
|
||||
return trim(
|
||||
collect([
|
||||
optional($building)->name,
|
||||
optional($room)->name,
|
||||
optional($equipment)->name,
|
||||
optional($rack)->name,
|
||||
optional($box)->name,
|
||||
$position->code,
|
||||
])->filter()->implode(' / ')
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user