chore: update document approval workflow and bug fixes
This commit is contained in:
127
Modules/MaterialLogistics/app/Services/InventoryService.php
Normal file
127
Modules/MaterialLogistics/app/Services/InventoryService.php
Normal file
@@ -0,0 +1,127 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\MaterialLogistics\Services;
|
||||
|
||||
use Modules\MaterialLogistics\Enums\DeploymentStatus;
|
||||
use Modules\MaterialLogistics\Events\MaterialDelivered;
|
||||
use Modules\MaterialLogistics\Events\MaterialRequested;
|
||||
use Modules\MasterData\Models\Material;
|
||||
use Modules\MaterialLogistics\Models\MaterialDeployment;
|
||||
use Modules\MaterialLogistics\Models\ProjectInventory;
|
||||
|
||||
class InventoryService
|
||||
{
|
||||
/**
|
||||
* Allocate stock to a project (add to on_hand).
|
||||
*/
|
||||
public function allocate(int $projectId, int $materialId, float $quantity, ?float $unitCost = null): ProjectInventory
|
||||
{
|
||||
$material = Material::findOrFail($materialId);
|
||||
|
||||
return ProjectInventory::updateOrCreate(
|
||||
['project_id' => $projectId, 'material_id' => $materialId],
|
||||
[
|
||||
'on_hand_qty' => \DB::raw("on_hand_qty + {$quantity}"),
|
||||
'unit_cost' => $unitCost ?? $material->unit_cost,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Request a deployment (creates deployment record).
|
||||
*/
|
||||
public function requestDeployment(
|
||||
int $projectId,
|
||||
int $materialId,
|
||||
float $quantity,
|
||||
?int $requestedBy = null,
|
||||
?string $notes = null,
|
||||
): MaterialDeployment {
|
||||
$deployment = MaterialDeployment::create([
|
||||
'project_id' => $projectId,
|
||||
'material_id' => $materialId,
|
||||
'requested_by' => $requestedBy,
|
||||
'quantity' => $quantity,
|
||||
'status' => DeploymentStatus::Requested,
|
||||
'notes' => $notes,
|
||||
]);
|
||||
|
||||
MaterialRequested::dispatch($deployment);
|
||||
|
||||
return $deployment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch deployment (guard: check inventory).
|
||||
*/
|
||||
public function dispatch(MaterialDeployment $deployment): MaterialDeployment
|
||||
{
|
||||
$inventory = ProjectInventory::where('project_id', $deployment->project_id)
|
||||
->where('material_id', $deployment->material_id)
|
||||
->first();
|
||||
|
||||
if (!$inventory || $inventory->available_qty < $deployment->quantity) {
|
||||
throw new \InvalidArgumentException(
|
||||
"Insufficient stock. Available: " . ($inventory?->available_qty ?? 0) . ", Requested: {$deployment->quantity}"
|
||||
);
|
||||
}
|
||||
|
||||
// Allocate from inventory
|
||||
$inventory->increment('allocated_qty', $deployment->quantity);
|
||||
|
||||
$deployment->transitionTo(DeploymentStatus::PendingDispatch);
|
||||
$deployment->transitionTo(DeploymentStatus::InTransit);
|
||||
|
||||
return $deployment->fresh();
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark as delivered.
|
||||
*/
|
||||
public function deliver(MaterialDeployment $deployment): MaterialDeployment
|
||||
{
|
||||
$deployment->transitionTo(DeploymentStatus::Delivered);
|
||||
MaterialDelivered::dispatch($deployment);
|
||||
return $deployment->fresh();
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark as consumed (deduct from inventory).
|
||||
*/
|
||||
public function consume(MaterialDeployment $deployment): MaterialDeployment
|
||||
{
|
||||
$deployment->transitionTo(DeploymentStatus::Consumed);
|
||||
|
||||
$inventory = ProjectInventory::where('project_id', $deployment->project_id)
|
||||
->where('material_id', $deployment->material_id)
|
||||
->first();
|
||||
|
||||
if ($inventory) {
|
||||
$inventory->decrement('on_hand_qty', $deployment->quantity);
|
||||
$inventory->decrement('allocated_qty', $deployment->quantity);
|
||||
$inventory->increment('consumed_qty', $deployment->quantity);
|
||||
}
|
||||
|
||||
return $deployment->fresh();
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel a deployment.
|
||||
*/
|
||||
public function cancel(MaterialDeployment $deployment): MaterialDeployment
|
||||
{
|
||||
// Release allocated stock if it was dispatched
|
||||
if (in_array($deployment->status, [DeploymentStatus::PendingDispatch, DeploymentStatus::InTransit])) {
|
||||
$inventory = ProjectInventory::where('project_id', $deployment->project_id)
|
||||
->where('material_id', $deployment->material_id)
|
||||
->first();
|
||||
|
||||
if ($inventory) {
|
||||
$inventory->decrement('allocated_qty', $deployment->quantity);
|
||||
}
|
||||
}
|
||||
|
||||
$deployment->transitionTo(DeploymentStatus::Cancelled);
|
||||
return $deployment->fresh();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user