partial update create mutations workflow
This commit is contained in:
@@ -6,6 +6,7 @@ use App\Enums\MutationStatus;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class Mutation extends Model
|
||||
{
|
||||
@@ -192,18 +193,20 @@ class Mutation extends Model
|
||||
throw new \Exception('Mutasi tidak dapat diselesaikan dalam status saat ini');
|
||||
}
|
||||
|
||||
\DB::beginTransaction();
|
||||
DB::beginTransaction();
|
||||
try {
|
||||
foreach ($this->mutationDetails as $detail) {
|
||||
if ($detail->quantity_approved > 0) {
|
||||
// Proses semua detail yang memiliki quantity_requested > 0
|
||||
// karena barang sudah dikirim dari dealer asal
|
||||
if ($detail->quantity_requested > 0) {
|
||||
$this->processStockMovement($detail);
|
||||
}
|
||||
}
|
||||
|
||||
$this->update(['status' => MutationStatus::COMPLETED]);
|
||||
\DB::commit();
|
||||
DB::commit();
|
||||
} catch (\Exception $e) {
|
||||
\DB::rollback();
|
||||
DB::rollback();
|
||||
throw $e;
|
||||
}
|
||||
|
||||
@@ -212,23 +215,23 @@ class Mutation extends Model
|
||||
|
||||
private function processStockMovement(MutationDetail $detail)
|
||||
{
|
||||
// Kurangi stock dari dealer asal
|
||||
// Kurangi stock dari dealer asal berdasarkan quantity_requested (barang yang dikirim)
|
||||
$fromStock = Stock::firstOrCreate([
|
||||
'product_id' => $detail->product_id,
|
||||
'dealer_id' => $this->from_dealer_id
|
||||
], ['quantity' => 0]);
|
||||
|
||||
if ($fromStock->quantity < $detail->quantity_approved) {
|
||||
if ($fromStock->quantity < $detail->quantity_requested) {
|
||||
throw new \Exception("Stock tidak mencukupi untuk produk {$detail->product->name} di {$this->fromDealer->name}");
|
||||
}
|
||||
|
||||
$fromStock->updateStock(
|
||||
$fromStock->quantity - $detail->quantity_approved,
|
||||
$fromStock->quantity - $detail->quantity_requested,
|
||||
$this,
|
||||
"Mutasi keluar ke {$this->toDealer->name} - {$this->mutation_number}"
|
||||
"Mutasi keluar ke {$this->toDealer->name} - {$this->mutation_number} (Dikirim: {$detail->quantity_requested}, Diterima: {$detail->quantity_approved})"
|
||||
);
|
||||
|
||||
// Tambah stock ke dealer tujuan
|
||||
// Tambah stock ke dealer tujuan berdasarkan quantity_approved (barang yang diterima)
|
||||
$toStock = Stock::firstOrCreate([
|
||||
'product_id' => $detail->product_id,
|
||||
'dealer_id' => $this->to_dealer_id
|
||||
@@ -237,8 +240,23 @@ class Mutation extends Model
|
||||
$toStock->updateStock(
|
||||
$toStock->quantity + $detail->quantity_approved,
|
||||
$this,
|
||||
"Mutasi masuk dari {$this->fromDealer->name} - {$this->mutation_number}"
|
||||
"Mutasi masuk dari {$this->fromDealer->name} - {$this->mutation_number} (Dikirim: {$detail->quantity_requested}, Diterima: {$detail->quantity_approved})"
|
||||
);
|
||||
|
||||
// Jika ada selisih (kehilangan), catat sebagai stock log terpisah untuk audit
|
||||
$lostQuantity = $detail->quantity_requested - $detail->quantity_approved;
|
||||
if ($lostQuantity > 0) {
|
||||
// Buat stock log untuk barang yang hilang/rusak
|
||||
StockLog::create([
|
||||
'stock_id' => $fromStock->id,
|
||||
'previous_quantity' => $fromStock->quantity + $detail->quantity_requested, // Stock sebelum pengurangan
|
||||
'new_quantity' => $fromStock->quantity, // Stock setelah pengurangan
|
||||
'source_type' => get_class($this),
|
||||
'source_id' => $this->id,
|
||||
'description' => "Kehilangan/kerusakan saat mutasi ke {$this->toDealer->name} - {$this->mutation_number} (Hilang: {$lostQuantity})",
|
||||
'user_id' => auth()->id()
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
private function generateMutationNumber()
|
||||
|
||||
@@ -40,21 +40,31 @@ class MutationDetail extends Model
|
||||
|
||||
public function isFullyApproved()
|
||||
{
|
||||
return $this->quantity_approved == $this->quantity_requested;
|
||||
return $this->quantity_approved !== null && $this->quantity_approved == $this->quantity_requested;
|
||||
}
|
||||
|
||||
public function isPartiallyApproved()
|
||||
{
|
||||
return $this->quantity_approved > 0 && $this->quantity_approved < $this->quantity_requested;
|
||||
return $this->quantity_approved !== null && $this->quantity_approved > 0 && $this->quantity_approved < $this->quantity_requested;
|
||||
}
|
||||
|
||||
public function isRejected()
|
||||
{
|
||||
return $this->quantity_approved == 0;
|
||||
// Hanya dianggap ditolak jika mutasi sudah di-approve/reject dan quantity_approved = 0
|
||||
$mutationStatus = $this->mutation->status->value ?? null;
|
||||
return in_array($mutationStatus, ['approved', 'completed', 'rejected']) && $this->quantity_approved == 0;
|
||||
}
|
||||
|
||||
public function getApprovalStatusAttribute()
|
||||
{
|
||||
$mutationStatus = $this->mutation->status->value ?? null;
|
||||
|
||||
// Jika mutasi belum di-approve, semua detail statusnya "Menunggu"
|
||||
if (!in_array($mutationStatus, ['approved', 'completed', 'rejected'])) {
|
||||
return 'Menunggu';
|
||||
}
|
||||
|
||||
// Jika mutasi sudah di-approve/complete, baru cek quantity_approved
|
||||
if ($this->isFullyApproved()) {
|
||||
return 'Disetujui Penuh';
|
||||
} elseif ($this->isPartiallyApproved()) {
|
||||
@@ -68,6 +78,14 @@ class MutationDetail extends Model
|
||||
|
||||
public function getApprovalStatusColorAttribute()
|
||||
{
|
||||
$mutationStatus = $this->mutation->status->value ?? null;
|
||||
|
||||
// Jika mutasi belum di-approve, semua detail statusnya "info" (menunggu)
|
||||
if (!in_array($mutationStatus, ['approved', 'completed', 'rejected'])) {
|
||||
return 'info';
|
||||
}
|
||||
|
||||
// Jika mutasi sudah di-approve/complete, baru cek quantity_approved
|
||||
if ($this->isFullyApproved()) {
|
||||
return 'success';
|
||||
} elseif ($this->isPartiallyApproved()) {
|
||||
|
||||
@@ -75,4 +75,61 @@ class User extends Authenticatable
|
||||
{
|
||||
return $this->hasOne(Dealer::class, 'id', 'dealer_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the role associated with the User
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
*/
|
||||
public function role()
|
||||
{
|
||||
return $this->belongsTo(Role::class, 'role_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if user has a specific role
|
||||
*
|
||||
* @param string $roleName
|
||||
* @return bool
|
||||
*/
|
||||
public function hasRole($roleName)
|
||||
{
|
||||
// If role_id is 0 or null, user has no role
|
||||
if (!$this->role_id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// For admin role, we can check if user has admin privileges
|
||||
if (strtolower($roleName) === 'admin') {
|
||||
return $this->isAdmin();
|
||||
}
|
||||
|
||||
// Load role if not already loaded
|
||||
if (!$this->relationLoaded('role')) {
|
||||
$this->load('role');
|
||||
}
|
||||
|
||||
return $this->role && strtolower($this->role->name) === strtolower($roleName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if user is admin by checking admin privileges
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isAdmin()
|
||||
{
|
||||
// Check if user has admin privileges by checking if they can access admin area
|
||||
try {
|
||||
$adminPrivilege = \App\Models\Privilege::join('menus', 'menus.id', '=', 'privileges.menu_id')
|
||||
->where('menus.link', 'adminarea')
|
||||
->where('privileges.role_id', $this->role_id)
|
||||
->where('privileges.view', 1)
|
||||
->first();
|
||||
|
||||
return $adminPrivilege !== null;
|
||||
} catch (\Exception $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user