partial update create mutations workflow
This commit is contained in:
@@ -19,7 +19,7 @@ class MutationsController extends Controller
|
|||||||
$menu = Menu::where('link','mutations.index')->first();
|
$menu = Menu::where('link','mutations.index')->first();
|
||||||
|
|
||||||
if ($request->ajax()) {
|
if ($request->ajax()) {
|
||||||
$data = Mutation::with(['fromDealer', 'toDealer', 'requestedBy', 'approvedBy', 'receivedBy'])
|
$data = Mutation::with(['fromDealer', 'toDealer', 'requestedBy.role', 'approvedBy.role', 'receivedBy.role'])
|
||||||
->select('mutations.*');
|
->select('mutations.*');
|
||||||
|
|
||||||
// Filter berdasarkan dealer jika user bukan admin
|
// Filter berdasarkan dealer jika user bukan admin
|
||||||
@@ -47,7 +47,19 @@ class MutationsController extends Controller
|
|||||||
->addColumn('status', function($row) {
|
->addColumn('status', function($row) {
|
||||||
$statusColor = $row->status_color;
|
$statusColor = $row->status_color;
|
||||||
$statusLabel = $row->status_label;
|
$statusLabel = $row->status_label;
|
||||||
return "<span class=\"kt-badge kt-badge--{$statusColor} kt-badge--dot\"></span> <span class=\"kt-font-bold kt-font-{$statusColor}\">{$statusLabel}</span>";
|
|
||||||
|
$textColorClass = match($statusColor) {
|
||||||
|
'success' => 'text-success',
|
||||||
|
'warning' => 'text-warning',
|
||||||
|
'danger' => 'text-danger',
|
||||||
|
'info' => 'text-info',
|
||||||
|
'primary' => 'text-primary',
|
||||||
|
'brand' => 'text-primary',
|
||||||
|
'secondary' => 'text-muted',
|
||||||
|
default => 'text-dark'
|
||||||
|
};
|
||||||
|
|
||||||
|
return "<span class=\"font-weight-bold {$textColorClass}\">{$statusLabel}</span>";
|
||||||
})
|
})
|
||||||
->addColumn('total_items', function($row) {
|
->addColumn('total_items', function($row) {
|
||||||
return number_format($row->total_items, 0);
|
return number_format($row->total_items, 0);
|
||||||
@@ -79,7 +91,6 @@ class MutationsController extends Controller
|
|||||||
$request->validate([
|
$request->validate([
|
||||||
'from_dealer_id' => 'required|exists:dealers,id',
|
'from_dealer_id' => 'required|exists:dealers,id',
|
||||||
'to_dealer_id' => 'required|exists:dealers,id|different:from_dealer_id',
|
'to_dealer_id' => 'required|exists:dealers,id|different:from_dealer_id',
|
||||||
'notes' => 'nullable|string',
|
|
||||||
'products' => 'required|array|min:1',
|
'products' => 'required|array|min:1',
|
||||||
'products.*.product_id' => 'required|exists:products,id',
|
'products.*.product_id' => 'required|exists:products,id',
|
||||||
'products.*.quantity_requested' => 'required|numeric|min:0.01'
|
'products.*.quantity_requested' => 'required|numeric|min:0.01'
|
||||||
@@ -92,8 +103,7 @@ class MutationsController extends Controller
|
|||||||
'from_dealer_id' => $request->from_dealer_id,
|
'from_dealer_id' => $request->from_dealer_id,
|
||||||
'to_dealer_id' => $request->to_dealer_id,
|
'to_dealer_id' => $request->to_dealer_id,
|
||||||
'status' => 'sent',
|
'status' => 'sent',
|
||||||
'requested_by' => auth()->id(),
|
'requested_by' => auth()->id()
|
||||||
'notes' => $request->notes
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Buat mutation details
|
// Buat mutation details
|
||||||
@@ -101,8 +111,7 @@ class MutationsController extends Controller
|
|||||||
MutationDetail::create([
|
MutationDetail::create([
|
||||||
'mutation_id' => $mutation->id,
|
'mutation_id' => $mutation->id,
|
||||||
'product_id' => $productData['product_id'],
|
'product_id' => $productData['product_id'],
|
||||||
'quantity_requested' => $productData['quantity_requested'],
|
'quantity_requested' => $productData['quantity_requested']
|
||||||
'notes' => $productData['notes'] ?? null
|
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,24 +127,63 @@ class MutationsController extends Controller
|
|||||||
|
|
||||||
public function show(Mutation $mutation)
|
public function show(Mutation $mutation)
|
||||||
{
|
{
|
||||||
$mutation->load(['fromDealer', 'toDealer', 'requestedBy', 'approvedBy', 'receivedBy', 'mutationDetails.product']);
|
$mutation->load(['fromDealer', 'toDealer', 'requestedBy.role', 'approvedBy.role', 'receivedBy.role', 'mutationDetails.product']);
|
||||||
|
|
||||||
return view('warehouse_management.mutations.show', compact('mutation'));
|
return view('warehouse_management.mutations.show', compact('mutation'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function receive(Mutation $mutation)
|
public function receive(Request $request, Mutation $mutation)
|
||||||
{
|
{
|
||||||
|
$request->validate([
|
||||||
|
'notes' => 'nullable|string',
|
||||||
|
'products' => 'required|array',
|
||||||
|
'products.*.quantity_approved' => 'required|numeric|min:0',
|
||||||
|
'products.*.notes' => 'nullable|string'
|
||||||
|
]);
|
||||||
|
|
||||||
if (!$mutation->canBeReceived()) {
|
if (!$mutation->canBeReceived()) {
|
||||||
return back()->withErrors(['error' => 'Mutasi tidak dapat diterima dalam status saat ini']);
|
return back()->withErrors(['error' => 'Mutasi tidak dapat diterima dalam status saat ini']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DB::beginTransaction();
|
||||||
try {
|
try {
|
||||||
|
// Update mutation notes jika ada
|
||||||
|
if ($request->notes) {
|
||||||
|
$mutation->update(['notes' => $request->notes]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update product details dengan quantity_approved dan notes
|
||||||
|
if ($request->products) {
|
||||||
|
foreach ($request->products as $detailId => $productData) {
|
||||||
|
$updateData = [];
|
||||||
|
|
||||||
|
// Set quantity_approved
|
||||||
|
if (isset($productData['quantity_approved'])) {
|
||||||
|
$updateData['quantity_approved'] = $productData['quantity_approved'];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set notes jika ada
|
||||||
|
if (isset($productData['notes']) && !empty($productData['notes'])) {
|
||||||
|
$updateData['notes'] = $productData['notes'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($updateData)) {
|
||||||
|
MutationDetail::where('id', $detailId)
|
||||||
|
->where('mutation_id', $mutation->id)
|
||||||
|
->update($updateData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Receive mutation
|
||||||
$mutation->receive(auth()->id());
|
$mutation->receive(auth()->id());
|
||||||
|
|
||||||
|
DB::commit();
|
||||||
return redirect()->route('mutations.index')
|
return redirect()->route('mutations.index')
|
||||||
->with('success', 'Mutasi berhasil diterima dan menunggu persetujuan pengirim');
|
->with('success', 'Mutasi berhasil diterima dan menunggu persetujuan pengirim');
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
|
DB::rollback();
|
||||||
return back()->withErrors(['error' => 'Gagal menerima mutasi: ' . $e->getMessage()]);
|
return back()->withErrors(['error' => 'Gagal menerima mutasi: ' . $e->getMessage()]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -143,34 +191,21 @@ class MutationsController extends Controller
|
|||||||
public function approve(Request $request, Mutation $mutation)
|
public function approve(Request $request, Mutation $mutation)
|
||||||
{
|
{
|
||||||
$request->validate([
|
$request->validate([
|
||||||
'notes' => 'nullable|string',
|
'notes' => 'nullable|string'
|
||||||
'details' => 'required|array',
|
|
||||||
'details.*.quantity_approved' => 'required|numeric|min:0'
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if (!$mutation->canBeApproved()) {
|
if (!$mutation->canBeApproved()) {
|
||||||
return back()->withErrors(['error' => 'Mutasi tidak dapat disetujui dalam status saat ini']);
|
return back()->withErrors(['error' => 'Mutasi tidak dapat disetujui dalam status saat ini']);
|
||||||
}
|
}
|
||||||
|
|
||||||
DB::beginTransaction();
|
|
||||||
try {
|
try {
|
||||||
// Update mutation details dengan quantity approved
|
// Approve mutation (quantity_approved sudah diisi saat receive)
|
||||||
foreach ($request->details as $detailId => $detailData) {
|
|
||||||
$mutationDetail = MutationDetail::findOrFail($detailId);
|
|
||||||
$mutationDetail->update([
|
|
||||||
'quantity_approved' => $detailData['quantity_approved']
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Approve mutation
|
|
||||||
$mutation->approve(auth()->id(), $request->notes);
|
$mutation->approve(auth()->id(), $request->notes);
|
||||||
|
|
||||||
DB::commit();
|
|
||||||
return redirect()->route('mutations.index')
|
return redirect()->route('mutations.index')
|
||||||
->with('success', 'Mutasi berhasil disetujui');
|
->with('success', 'Mutasi berhasil disetujui');
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
DB::rollback();
|
|
||||||
return back()->withErrors(['error' => 'Gagal menyetujui mutasi: ' . $e->getMessage()]);
|
return back()->withErrors(['error' => 'Gagal menyetujui mutasi: ' . $e->getMessage()]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -230,36 +265,7 @@ class MutationsController extends Controller
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// API untuk mendapatkan detail mutasi untuk approval
|
|
||||||
public function getDetails(Mutation $mutation)
|
|
||||||
{
|
|
||||||
$mutation->load(['mutationDetails.product', 'fromDealer']);
|
|
||||||
|
|
||||||
$details = $mutation->mutationDetails->map(function($detail) use ($mutation) {
|
|
||||||
$availableStock = $detail->product->getStockByDealer($mutation->from_dealer_id);
|
|
||||||
|
|
||||||
return [
|
|
||||||
'id' => $detail->id,
|
|
||||||
'product' => [
|
|
||||||
'id' => $detail->product->id,
|
|
||||||
'name' => $detail->product->name
|
|
||||||
],
|
|
||||||
'quantity_requested' => $detail->quantity_requested,
|
|
||||||
'quantity_approved' => $detail->quantity_approved,
|
|
||||||
'available_stock' => $availableStock
|
|
||||||
];
|
|
||||||
});
|
|
||||||
|
|
||||||
return response()->json([
|
|
||||||
'mutation' => [
|
|
||||||
'id' => $mutation->id,
|
|
||||||
'mutation_number' => $mutation->mutation_number,
|
|
||||||
'from_dealer' => $mutation->fromDealer->name,
|
|
||||||
'to_dealer' => $mutation->toDealer->name
|
|
||||||
],
|
|
||||||
'details' => $details
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// API untuk mendapatkan stock produk di dealer tertentu
|
// API untuk mendapatkan stock produk di dealer tertentu
|
||||||
public function getProductStock(Request $request)
|
public function getProductStock(Request $request)
|
||||||
|
|||||||
@@ -39,14 +39,25 @@ class OpnamesController extends Controller
|
|||||||
return Carbon::parse($row->created_at)->format('d M Y H:i');
|
return Carbon::parse($row->created_at)->format('d M Y H:i');
|
||||||
})
|
})
|
||||||
->editColumn('status', function ($row) {
|
->editColumn('status', function ($row) {
|
||||||
$statusClass = [
|
$statusColor = [
|
||||||
'draft' => 'warning',
|
'draft' => 'warning',
|
||||||
'pending' => 'info',
|
'pending' => 'info',
|
||||||
'approved' => 'success',
|
'approved' => 'success',
|
||||||
'rejected' => 'danger'
|
'rejected' => 'danger'
|
||||||
][$row->status] ?? 'secondary';
|
][$row->status] ?? 'secondary';
|
||||||
|
|
||||||
return '<span class="badge badge-' . $statusClass . '">' . ucfirst($row->status) . '</span>';
|
$textColorClass = match($statusColor) {
|
||||||
|
'success' => 'text-success',
|
||||||
|
'warning' => 'text-warning',
|
||||||
|
'danger' => 'text-danger',
|
||||||
|
'info' => 'text-info',
|
||||||
|
'primary' => 'text-primary',
|
||||||
|
'brand' => 'text-primary',
|
||||||
|
'secondary' => 'text-muted',
|
||||||
|
default => 'text-dark'
|
||||||
|
};
|
||||||
|
|
||||||
|
return "<span class=\"font-weight-bold {$textColorClass}\">" . ucfirst($row->status) . "</span>";
|
||||||
})
|
})
|
||||||
->addColumn('action', function ($row) use ($menu) {
|
->addColumn('action', function ($row) use ($menu) {
|
||||||
$btn = '<div class="d-flex">';
|
$btn = '<div class="d-flex">';
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ use App\Enums\MutationStatus;
|
|||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
|
||||||
class Mutation extends Model
|
class Mutation extends Model
|
||||||
{
|
{
|
||||||
@@ -192,18 +193,20 @@ class Mutation extends Model
|
|||||||
throw new \Exception('Mutasi tidak dapat diselesaikan dalam status saat ini');
|
throw new \Exception('Mutasi tidak dapat diselesaikan dalam status saat ini');
|
||||||
}
|
}
|
||||||
|
|
||||||
\DB::beginTransaction();
|
DB::beginTransaction();
|
||||||
try {
|
try {
|
||||||
foreach ($this->mutationDetails as $detail) {
|
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->processStockMovement($detail);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->update(['status' => MutationStatus::COMPLETED]);
|
$this->update(['status' => MutationStatus::COMPLETED]);
|
||||||
\DB::commit();
|
DB::commit();
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
\DB::rollback();
|
DB::rollback();
|
||||||
throw $e;
|
throw $e;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -212,23 +215,23 @@ class Mutation extends Model
|
|||||||
|
|
||||||
private function processStockMovement(MutationDetail $detail)
|
private function processStockMovement(MutationDetail $detail)
|
||||||
{
|
{
|
||||||
// Kurangi stock dari dealer asal
|
// Kurangi stock dari dealer asal berdasarkan quantity_requested (barang yang dikirim)
|
||||||
$fromStock = Stock::firstOrCreate([
|
$fromStock = Stock::firstOrCreate([
|
||||||
'product_id' => $detail->product_id,
|
'product_id' => $detail->product_id,
|
||||||
'dealer_id' => $this->from_dealer_id
|
'dealer_id' => $this->from_dealer_id
|
||||||
], ['quantity' => 0]);
|
], ['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}");
|
throw new \Exception("Stock tidak mencukupi untuk produk {$detail->product->name} di {$this->fromDealer->name}");
|
||||||
}
|
}
|
||||||
|
|
||||||
$fromStock->updateStock(
|
$fromStock->updateStock(
|
||||||
$fromStock->quantity - $detail->quantity_approved,
|
$fromStock->quantity - $detail->quantity_requested,
|
||||||
$this,
|
$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([
|
$toStock = Stock::firstOrCreate([
|
||||||
'product_id' => $detail->product_id,
|
'product_id' => $detail->product_id,
|
||||||
'dealer_id' => $this->to_dealer_id
|
'dealer_id' => $this->to_dealer_id
|
||||||
@@ -237,8 +240,23 @@ class Mutation extends Model
|
|||||||
$toStock->updateStock(
|
$toStock->updateStock(
|
||||||
$toStock->quantity + $detail->quantity_approved,
|
$toStock->quantity + $detail->quantity_approved,
|
||||||
$this,
|
$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()
|
private function generateMutationNumber()
|
||||||
|
|||||||
@@ -40,21 +40,31 @@ class MutationDetail extends Model
|
|||||||
|
|
||||||
public function isFullyApproved()
|
public function isFullyApproved()
|
||||||
{
|
{
|
||||||
return $this->quantity_approved == $this->quantity_requested;
|
return $this->quantity_approved !== null && $this->quantity_approved == $this->quantity_requested;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isPartiallyApproved()
|
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()
|
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()
|
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()) {
|
if ($this->isFullyApproved()) {
|
||||||
return 'Disetujui Penuh';
|
return 'Disetujui Penuh';
|
||||||
} elseif ($this->isPartiallyApproved()) {
|
} elseif ($this->isPartiallyApproved()) {
|
||||||
@@ -68,6 +78,14 @@ class MutationDetail extends Model
|
|||||||
|
|
||||||
public function getApprovalStatusColorAttribute()
|
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()) {
|
if ($this->isFullyApproved()) {
|
||||||
return 'success';
|
return 'success';
|
||||||
} elseif ($this->isPartiallyApproved()) {
|
} elseif ($this->isPartiallyApproved()) {
|
||||||
|
|||||||
@@ -75,4 +75,61 @@ class User extends Authenticatable
|
|||||||
{
|
{
|
||||||
return $this->hasOne(Dealer::class, 'id', 'dealer_id');
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
// Update existing records yang quantity_approved = 0 menjadi null untuk mutasi yang belum approved
|
||||||
|
DB::table('mutation_details')
|
||||||
|
->join('mutations', 'mutations.id', '=', 'mutation_details.mutation_id')
|
||||||
|
->where('mutation_details.quantity_approved', 0)
|
||||||
|
->whereNotIn('mutations.status', ['approved', 'completed', 'rejected'])
|
||||||
|
->update(['mutation_details.quantity_approved' => null]);
|
||||||
|
|
||||||
|
Schema::table('mutation_details', function (Blueprint $table) {
|
||||||
|
$table->decimal('quantity_approved', 15, 2)->nullable()->default(null)->change();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('mutation_details', function (Blueprint $table) {
|
||||||
|
$table->decimal('quantity_approved', 15, 2)->default(0)->change();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
11167
public/css/app.css
11167
public/css/app.css
File diff suppressed because one or more lines are too long
1
public/css/app.css.map
Normal file
1
public/css/app.css.map
Normal file
File diff suppressed because one or more lines are too long
1211
public/js/app.js
1211
public/js/app.js
File diff suppressed because one or more lines are too long
@@ -1,3 +1,9 @@
|
|||||||
|
/*!
|
||||||
|
* Bootstrap v5.1.3 (https://getbootstrap.com/)
|
||||||
|
* Copyright 2011-2021 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
|
||||||
|
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @license
|
* @license
|
||||||
* Lodash <https://lodash.com/>
|
* Lodash <https://lodash.com/>
|
||||||
|
|||||||
1
public/js/app.js.map
Normal file
1
public/js/app.js.map
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
public/js/warehouse_management/mutations/create.js.map
Normal file
1
public/js/warehouse_management/mutations/create.js.map
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
public/js/warehouse_management/mutations/index.js.map
Normal file
1
public/js/warehouse_management/mutations/index.js.map
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
public/js/warehouse_management/opnames/create.js.map
Normal file
1
public/js/warehouse_management/opnames/create.js.map
Normal file
File diff suppressed because one or more lines are too long
@@ -1,32 +1,2 @@
|
|||||||
/*
|
(()=>{$.ajaxSetup({headers:{"X-CSRF-TOKEN":$('meta[name="csrf-token"]').attr("content")}});var a=$("#opname-detail-table").data("url");$("#opname-detail-table").DataTable({processing:!0,serverSide:!0,ajax:a,columns:[{data:"opname_date",name:"opname_date"},{data:"user_name",name:"user.name"},{data:"product_name",name:"product.name"},{data:"system_stock",name:"system_stock"},{data:"physical_stock",name:"physical_stock"},{data:"difference",name:"difference"}]})})();
|
||||||
* ATTENTION: An "eval-source-map" devtool has been used.
|
//# sourceMappingURL=detail.js.map
|
||||||
* This devtool is neither made for production nor for readable output files.
|
|
||||||
* It uses "eval()" calls to create a separate source file with attached SourceMaps in the browser devtools.
|
|
||||||
* If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
|
|
||||||
* or disable the default devtool with "devtool: false".
|
|
||||||
* If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
|
|
||||||
*/
|
|
||||||
/******/ (() => { // webpackBootstrap
|
|
||||||
/******/ var __webpack_modules__ = ({
|
|
||||||
|
|
||||||
/***/ "./resources/js/warehouse_management/opnames/detail.js":
|
|
||||||
/*!*************************************************************!*\
|
|
||||||
!*** ./resources/js/warehouse_management/opnames/detail.js ***!
|
|
||||||
\*************************************************************/
|
|
||||||
/***/ (() => {
|
|
||||||
|
|
||||||
eval("$.ajaxSetup({\n headers: {\n \"X-CSRF-TOKEN\": $('meta[name=\"csrf-token\"]').attr(\"content\")\n }\n});\nvar tableContainer = $(\"#opname-detail-table\");\nvar url = tableContainer.data(\"url\");\nvar table = $(\"#opname-detail-table\").DataTable({\n processing: true,\n serverSide: true,\n ajax: url,\n columns: [{\n data: \"opname_date\",\n name: \"opname_date\"\n }, {\n data: \"user_name\",\n name: \"user.name\"\n }, {\n data: \"product_name\",\n name: \"product.name\"\n }, {\n data: \"system_stock\",\n name: \"system_stock\"\n }, {\n data: \"physical_stock\",\n name: \"physical_stock\"\n }, {\n data: \"difference\",\n name: \"difference\"\n }]\n});//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyIkIiwiYWpheFNldHVwIiwiaGVhZGVycyIsImF0dHIiLCJ0YWJsZUNvbnRhaW5lciIsInVybCIsImRhdGEiLCJ0YWJsZSIsIkRhdGFUYWJsZSIsInByb2Nlc3NpbmciLCJzZXJ2ZXJTaWRlIiwiYWpheCIsImNvbHVtbnMiLCJuYW1lIl0sInNvdXJjZXMiOlsid2VicGFjazovLy8uL3Jlc291cmNlcy9qcy93YXJlaG91c2VfbWFuYWdlbWVudC9vcG5hbWVzL2RldGFpbC5qcz9mMzY3Il0sInNvdXJjZXNDb250ZW50IjpbIiQuYWpheFNldHVwKHtcbiAgICBoZWFkZXJzOiB7XG4gICAgICAgIFwiWC1DU1JGLVRPS0VOXCI6ICQoJ21ldGFbbmFtZT1cImNzcmYtdG9rZW5cIl0nKS5hdHRyKFwiY29udGVudFwiKSxcbiAgICB9LFxufSk7XG5sZXQgdGFibGVDb250YWluZXIgPSAkKFwiI29wbmFtZS1kZXRhaWwtdGFibGVcIik7XG5sZXQgdXJsID0gdGFibGVDb250YWluZXIuZGF0YShcInVybFwiKTtcbmxldCB0YWJsZSA9ICQoXCIjb3BuYW1lLWRldGFpbC10YWJsZVwiKS5EYXRhVGFibGUoe1xuICAgIHByb2Nlc3Npbmc6IHRydWUsXG4gICAgc2VydmVyU2lkZTogdHJ1ZSxcbiAgICBhamF4OiB1cmwsXG4gICAgY29sdW1uczogW1xuICAgICAgICB7IGRhdGE6IFwib3BuYW1lX2RhdGVcIiwgbmFtZTogXCJvcG5hbWVfZGF0ZVwiIH0sXG4gICAgICAgIHsgZGF0YTogXCJ1c2VyX25hbWVcIiwgbmFtZTogXCJ1c2VyLm5hbWVcIiB9LFxuICAgICAgICB7IGRhdGE6IFwicHJvZHVjdF9uYW1lXCIsIG5hbWU6IFwicHJvZHVjdC5uYW1lXCIgfSxcbiAgICAgICAgeyBkYXRhOiBcInN5c3RlbV9zdG9ja1wiLCBuYW1lOiBcInN5c3RlbV9zdG9ja1wiIH0sXG4gICAgICAgIHsgZGF0YTogXCJwaHlzaWNhbF9zdG9ja1wiLCBuYW1lOiBcInBoeXNpY2FsX3N0b2NrXCIgfSxcbiAgICAgICAgeyBkYXRhOiBcImRpZmZlcmVuY2VcIiwgbmFtZTogXCJkaWZmZXJlbmNlXCIgfSxcbiAgICBdLFxufSk7XG4iXSwibWFwcGluZ3MiOiJBQUFBQSxDQUFDLENBQUNDLFNBQUYsQ0FBWTtFQUNSQyxPQUFPLEVBQUU7SUFDTCxnQkFBZ0JGLENBQUMsQ0FBQyx5QkFBRCxDQUFELENBQTZCRyxJQUE3QixDQUFrQyxTQUFsQztFQURYO0FBREQsQ0FBWjtBQUtBLElBQUlDLGNBQWMsR0FBR0osQ0FBQyxDQUFDLHNCQUFELENBQXRCO0FBQ0EsSUFBSUssR0FBRyxHQUFHRCxjQUFjLENBQUNFLElBQWYsQ0FBb0IsS0FBcEIsQ0FBVjtBQUNBLElBQUlDLEtBQUssR0FBR1AsQ0FBQyxDQUFDLHNCQUFELENBQUQsQ0FBMEJRLFNBQTFCLENBQW9DO0VBQzVDQyxVQUFVLEVBQUUsSUFEZ0M7RUFFNUNDLFVBQVUsRUFBRSxJQUZnQztFQUc1Q0MsSUFBSSxFQUFFTixHQUhzQztFQUk1Q08sT0FBTyxFQUFFLENBQ0w7SUFBRU4sSUFBSSxFQUFFLGFBQVI7SUFBdUJPLElBQUksRUFBRTtFQUE3QixDQURLLEVBRUw7SUFBRVAsSUFBSSxFQUFFLFdBQVI7SUFBcUJPLElBQUksRUFBRTtFQUEzQixDQUZLLEVBR0w7SUFBRVAsSUFBSSxFQUFFLGNBQVI7SUFBd0JPLElBQUksRUFBRTtFQUE5QixDQUhLLEVBSUw7SUFBRVAsSUFBSSxFQUFFLGNBQVI7SUFBd0JPLElBQUksRUFBRTtFQUE5QixDQUpLLEVBS0w7SUFBRVAsSUFBSSxFQUFFLGdCQUFSO0lBQTBCTyxJQUFJLEVBQUU7RUFBaEMsQ0FMSyxFQU1MO0lBQUVQLElBQUksRUFBRSxZQUFSO0lBQXNCTyxJQUFJLEVBQUU7RUFBNUIsQ0FOSztBQUptQyxDQUFwQyxDQUFaIiwiZmlsZSI6Ii4vcmVzb3VyY2VzL2pzL3dhcmVob3VzZV9tYW5hZ2VtZW50L29wbmFtZXMvZGV0YWlsLmpzIiwic291cmNlUm9vdCI6IiJ9\n//# sourceURL=webpack-internal:///./resources/js/warehouse_management/opnames/detail.js\n");
|
|
||||||
|
|
||||||
/***/ })
|
|
||||||
|
|
||||||
/******/ });
|
|
||||||
/************************************************************************/
|
|
||||||
/******/
|
|
||||||
/******/ // startup
|
|
||||||
/******/ // Load entry module and return exports
|
|
||||||
/******/ // This entry module can't be inlined because the eval-source-map devtool is used.
|
|
||||||
/******/ var __webpack_exports__ = {};
|
|
||||||
/******/ __webpack_modules__["./resources/js/warehouse_management/opnames/detail.js"]();
|
|
||||||
/******/
|
|
||||||
/******/ })()
|
|
||||||
;
|
|
||||||
1
public/js/warehouse_management/opnames/detail.js.map
Normal file
1
public/js/warehouse_management/opnames/detail.js.map
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"/js/warehouse_management/opnames/detail.js","mappings":"MAAAA,EAAEC,UAAU,CACRC,QAAS,CACL,eAAgBF,EAAE,2BAA2BG,KAAK,cAG1D,IACIC,EADiBJ,EAAE,wBACEK,KAAK,OAClBL,EAAE,wBAAwBM,UAAU,CAC5CC,YAAY,EACZC,YAAY,EACZC,KAAML,EACNM,QAAS,CACL,CAAEL,KAAM,cAAeM,KAAM,eAC7B,CAAEN,KAAM,YAAaM,KAAM,aAC3B,CAAEN,KAAM,eAAgBM,KAAM,gBAC9B,CAAEN,KAAM,eAAgBM,KAAM,gBAC9B,CAAEN,KAAM,iBAAkBM,KAAM,kBAChC,CAAEN,KAAM,aAAcM,KAAM,gB","sources":["webpack:///./resources/js/warehouse_management/opnames/detail.js"],"sourcesContent":["$.ajaxSetup({\n headers: {\n \"X-CSRF-TOKEN\": $('meta[name=\"csrf-token\"]').attr(\"content\"),\n },\n});\nlet tableContainer = $(\"#opname-detail-table\");\nlet url = tableContainer.data(\"url\");\nlet table = $(\"#opname-detail-table\").DataTable({\n processing: true,\n serverSide: true,\n ajax: url,\n columns: [\n { data: \"opname_date\", name: \"opname_date\" },\n { data: \"user_name\", name: \"user.name\" },\n { data: \"product_name\", name: \"product.name\" },\n { data: \"system_stock\", name: \"system_stock\" },\n { data: \"physical_stock\", name: \"physical_stock\" },\n { data: \"difference\", name: \"difference\" },\n ],\n});\n"],"names":["$","ajaxSetup","headers","attr","url","data","DataTable","processing","serverSide","ajax","columns","name"],"sourceRoot":""}
|
||||||
@@ -1,32 +1,2 @@
|
|||||||
/*
|
(()=>{$.ajaxSetup({headers:{"X-CSRF-TOKEN":$('meta[name="csrf-token"]').attr("content")}});var a=$("#opnames-table").data("url");$("#opnames-table").DataTable({processing:!0,serverSide:!0,ajax:a,order:[[0,"desc"]],columns:[{data:"created_at",name:"created_at",visible:!1},{data:"opname_date",name:"opname_date"},{data:"dealer_name",name:"dealer.name"},{data:"user_name",name:"user.name"},{data:"status",name:"status"},{data:"action",name:"action",orderable:!1,searchable:!1}]})})();
|
||||||
* ATTENTION: An "eval-source-map" devtool has been used.
|
//# sourceMappingURL=index.js.map
|
||||||
* This devtool is neither made for production nor for readable output files.
|
|
||||||
* It uses "eval()" calls to create a separate source file with attached SourceMaps in the browser devtools.
|
|
||||||
* If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
|
|
||||||
* or disable the default devtool with "devtool: false".
|
|
||||||
* If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
|
|
||||||
*/
|
|
||||||
/******/ (() => { // webpackBootstrap
|
|
||||||
/******/ var __webpack_modules__ = ({
|
|
||||||
|
|
||||||
/***/ "./resources/js/warehouse_management/opnames/index.js":
|
|
||||||
/*!************************************************************!*\
|
|
||||||
!*** ./resources/js/warehouse_management/opnames/index.js ***!
|
|
||||||
\************************************************************/
|
|
||||||
/***/ (() => {
|
|
||||||
|
|
||||||
eval("$.ajaxSetup({\n headers: {\n \"X-CSRF-TOKEN\": $('meta[name=\"csrf-token\"]').attr(\"content\")\n }\n});\nvar tableContainer = $(\"#opnames-table\");\nvar url = tableContainer.data(\"url\");\nvar table = $(\"#opnames-table\").DataTable({\n processing: true,\n serverSide: true,\n ajax: url,\n order: [[0, \"desc\"]],\n columns: [{\n data: \"created_at\",\n name: \"created_at\",\n visible: false\n }, {\n data: \"opname_date\",\n name: \"opname_date\"\n }, {\n data: \"dealer_name\",\n name: \"dealer.name\"\n }, {\n data: \"user_name\",\n name: \"user.name\"\n }, {\n data: \"status\",\n name: \"status\"\n }, {\n data: \"action\",\n name: \"action\",\n orderable: false,\n searchable: false\n }]\n});//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyIkIiwiYWpheFNldHVwIiwiaGVhZGVycyIsImF0dHIiLCJ0YWJsZUNvbnRhaW5lciIsInVybCIsImRhdGEiLCJ0YWJsZSIsIkRhdGFUYWJsZSIsInByb2Nlc3NpbmciLCJzZXJ2ZXJTaWRlIiwiYWpheCIsIm9yZGVyIiwiY29sdW1ucyIsIm5hbWUiLCJ2aXNpYmxlIiwib3JkZXJhYmxlIiwic2VhcmNoYWJsZSJdLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vLi9yZXNvdXJjZXMvanMvd2FyZWhvdXNlX21hbmFnZW1lbnQvb3BuYW1lcy9pbmRleC5qcz9hNGM4Il0sInNvdXJjZXNDb250ZW50IjpbIiQuYWpheFNldHVwKHtcbiAgICBoZWFkZXJzOiB7XG4gICAgICAgIFwiWC1DU1JGLVRPS0VOXCI6ICQoJ21ldGFbbmFtZT1cImNzcmYtdG9rZW5cIl0nKS5hdHRyKFwiY29udGVudFwiKSxcbiAgICB9LFxufSk7XG5cbmxldCB0YWJsZUNvbnRhaW5lciA9ICQoXCIjb3BuYW1lcy10YWJsZVwiKTtcbmxldCB1cmwgPSB0YWJsZUNvbnRhaW5lci5kYXRhKFwidXJsXCIpO1xubGV0IHRhYmxlID0gJChcIiNvcG5hbWVzLXRhYmxlXCIpLkRhdGFUYWJsZSh7XG4gICAgcHJvY2Vzc2luZzogdHJ1ZSxcbiAgICBzZXJ2ZXJTaWRlOiB0cnVlLFxuICAgIGFqYXg6IHVybCxcbiAgICBvcmRlcjogW1swLCBcImRlc2NcIl1dLFxuICAgIGNvbHVtbnM6IFtcbiAgICAgICAgeyBkYXRhOiBcImNyZWF0ZWRfYXRcIiwgbmFtZTogXCJjcmVhdGVkX2F0XCIsIHZpc2libGU6IGZhbHNlIH0sXG4gICAgICAgIHsgZGF0YTogXCJvcG5hbWVfZGF0ZVwiLCBuYW1lOiBcIm9wbmFtZV9kYXRlXCIgfSxcbiAgICAgICAgeyBkYXRhOiBcImRlYWxlcl9uYW1lXCIsIG5hbWU6IFwiZGVhbGVyLm5hbWVcIiB9LFxuICAgICAgICB7IGRhdGE6IFwidXNlcl9uYW1lXCIsIG5hbWU6IFwidXNlci5uYW1lXCIgfSxcbiAgICAgICAgeyBkYXRhOiBcInN0YXR1c1wiLCBuYW1lOiBcInN0YXR1c1wiIH0sXG4gICAgICAgIHsgZGF0YTogXCJhY3Rpb25cIiwgbmFtZTogXCJhY3Rpb25cIiwgb3JkZXJhYmxlOiBmYWxzZSwgc2VhcmNoYWJsZTogZmFsc2UgfSxcbiAgICBdLFxufSk7XG4iXSwibWFwcGluZ3MiOiJBQUFBQSxDQUFDLENBQUNDLFNBQUYsQ0FBWTtFQUNSQyxPQUFPLEVBQUU7SUFDTCxnQkFBZ0JGLENBQUMsQ0FBQyx5QkFBRCxDQUFELENBQTZCRyxJQUE3QixDQUFrQyxTQUFsQztFQURYO0FBREQsQ0FBWjtBQU1BLElBQUlDLGNBQWMsR0FBR0osQ0FBQyxDQUFDLGdCQUFELENBQXRCO0FBQ0EsSUFBSUssR0FBRyxHQUFHRCxjQUFjLENBQUNFLElBQWYsQ0FBb0IsS0FBcEIsQ0FBVjtBQUNBLElBQUlDLEtBQUssR0FBR1AsQ0FBQyxDQUFDLGdCQUFELENBQUQsQ0FBb0JRLFNBQXBCLENBQThCO0VBQ3RDQyxVQUFVLEVBQUUsSUFEMEI7RUFFdENDLFVBQVUsRUFBRSxJQUYwQjtFQUd0Q0MsSUFBSSxFQUFFTixHQUhnQztFQUl0Q08sS0FBSyxFQUFFLENBQUMsQ0FBQyxDQUFELEVBQUksTUFBSixDQUFELENBSitCO0VBS3RDQyxPQUFPLEVBQUUsQ0FDTDtJQUFFUCxJQUFJLEVBQUUsWUFBUjtJQUFzQlEsSUFBSSxFQUFFLFlBQTVCO0lBQTBDQyxPQUFPLEVBQUU7RUFBbkQsQ0FESyxFQUVMO0lBQUVULElBQUksRUFBRSxhQUFSO0lBQXVCUSxJQUFJLEVBQUU7RUFBN0IsQ0FGSyxFQUdMO0lBQUVSLElBQUksRUFBRSxhQUFSO0lBQXVCUSxJQUFJLEVBQUU7RUFBN0IsQ0FISyxFQUlMO0lBQUVSLElBQUksRUFBRSxXQUFSO0lBQXFCUSxJQUFJLEVBQUU7RUFBM0IsQ0FKSyxFQUtMO0lBQUVSLElBQUksRUFBRSxRQUFSO0lBQWtCUSxJQUFJLEVBQUU7RUFBeEIsQ0FMSyxFQU1MO0lBQUVSLElBQUksRUFBRSxRQUFSO0lBQWtCUSxJQUFJLEVBQUUsUUFBeEI7SUFBa0NFLFNBQVMsRUFBRSxLQUE3QztJQUFvREMsVUFBVSxFQUFFO0VBQWhFLENBTks7QUFMNkIsQ0FBOUIsQ0FBWiIsImZpbGUiOiIuL3Jlc291cmNlcy9qcy93YXJlaG91c2VfbWFuYWdlbWVudC9vcG5hbWVzL2luZGV4LmpzIiwic291cmNlUm9vdCI6IiJ9\n//# sourceURL=webpack-internal:///./resources/js/warehouse_management/opnames/index.js\n");
|
|
||||||
|
|
||||||
/***/ })
|
|
||||||
|
|
||||||
/******/ });
|
|
||||||
/************************************************************************/
|
|
||||||
/******/
|
|
||||||
/******/ // startup
|
|
||||||
/******/ // Load entry module and return exports
|
|
||||||
/******/ // This entry module can't be inlined because the eval-source-map devtool is used.
|
|
||||||
/******/ var __webpack_exports__ = {};
|
|
||||||
/******/ __webpack_modules__["./resources/js/warehouse_management/opnames/index.js"]();
|
|
||||||
/******/
|
|
||||||
/******/ })()
|
|
||||||
;
|
|
||||||
1
public/js/warehouse_management/opnames/index.js.map
Normal file
1
public/js/warehouse_management/opnames/index.js.map
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"/js/warehouse_management/opnames/index.js","mappings":"MAAAA,EAAEC,UAAU,CACRC,QAAS,CACL,eAAgBF,EAAE,2BAA2BG,KAAK,cAI1D,IACIC,EADiBJ,EAAE,kBACEK,KAAK,OAClBL,EAAE,kBAAkBM,UAAU,CACtCC,YAAY,EACZC,YAAY,EACZC,KAAML,EACNM,MAAO,CAAC,CAAC,EAAG,SACZC,QAAS,CACL,CAAEN,KAAM,aAAcO,KAAM,aAAcC,SAAS,GACnD,CAAER,KAAM,cAAeO,KAAM,eAC7B,CAAEP,KAAM,cAAeO,KAAM,eAC7B,CAAEP,KAAM,YAAaO,KAAM,aAC3B,CAAEP,KAAM,SAAUO,KAAM,UACxB,CAAEP,KAAM,SAAUO,KAAM,SAAUE,WAAW,EAAOC,YAAY,K","sources":["webpack:///./resources/js/warehouse_management/opnames/index.js"],"sourcesContent":["$.ajaxSetup({\n headers: {\n \"X-CSRF-TOKEN\": $('meta[name=\"csrf-token\"]').attr(\"content\"),\n },\n});\n\nlet tableContainer = $(\"#opnames-table\");\nlet url = tableContainer.data(\"url\");\nlet table = $(\"#opnames-table\").DataTable({\n processing: true,\n serverSide: true,\n ajax: url,\n order: [[0, \"desc\"]],\n columns: [\n { data: \"created_at\", name: \"created_at\", visible: false },\n { data: \"opname_date\", name: \"opname_date\" },\n { data: \"dealer_name\", name: \"dealer.name\" },\n { data: \"user_name\", name: \"user.name\" },\n { data: \"status\", name: \"status\" },\n { data: \"action\", name: \"action\", orderable: false, searchable: false },\n ],\n});\n"],"names":["$","ajaxSetup","headers","attr","url","data","DataTable","processing","serverSide","ajax","order","columns","name","visible","orderable","searchable"],"sourceRoot":""}
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
public/js/warehouse_management/products/index.js.map
Normal file
1
public/js/warehouse_management/products/index.js.map
Normal file
File diff suppressed because one or more lines are too long
@@ -1,5 +1,6 @@
|
|||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
let productIndex = 1;
|
let productIndex = 1;
|
||||||
|
let originalProductOptions = ""; // Store original product options
|
||||||
|
|
||||||
// Initialize Select2
|
// Initialize Select2
|
||||||
$(".select2").select2({
|
$(".select2").select2({
|
||||||
@@ -7,6 +8,12 @@ $(document).ready(function () {
|
|||||||
allowClear: true,
|
allowClear: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Store original product options on page load
|
||||||
|
const firstSelect = $(".product-select").first();
|
||||||
|
if (firstSelect.length > 0) {
|
||||||
|
originalProductOptions = firstSelect.html();
|
||||||
|
}
|
||||||
|
|
||||||
// Prevent same dealer selection
|
// Prevent same dealer selection
|
||||||
$("#from_dealer_id, #to_dealer_id").on("change", function () {
|
$("#from_dealer_id, #to_dealer_id").on("change", function () {
|
||||||
const fromDealerId = $("#from_dealer_id").val();
|
const fromDealerId = $("#from_dealer_id").val();
|
||||||
@@ -26,12 +33,12 @@ $(document).ready(function () {
|
|||||||
const newRow = createProductRow(productIndex);
|
const newRow = createProductRow(productIndex);
|
||||||
$("#products-tbody").append(newRow);
|
$("#products-tbody").append(newRow);
|
||||||
|
|
||||||
// Initialize Select2 for new row
|
// Initialize Select2 for new row after it's added to DOM
|
||||||
const newSelect = $(
|
const newSelect = $(
|
||||||
`#products-tbody tr[data-index="${productIndex}"] .product-select`
|
`#products-tbody tr[data-index="${productIndex}"] .product-select`
|
||||||
);
|
);
|
||||||
newSelect.select2({
|
newSelect.select2({
|
||||||
placeholder: "Pilih Produk...",
|
placeholder: "Pilih...",
|
||||||
allowClear: true,
|
allowClear: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -100,18 +107,14 @@ $(document).ready(function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
function createProductRow(index) {
|
function createProductRow(index) {
|
||||||
// Get product options from the existing select
|
|
||||||
const existingSelect = $(".product-select").first();
|
|
||||||
const productOptions = existingSelect.html();
|
|
||||||
|
|
||||||
return `
|
return `
|
||||||
<tr class="product-row" data-index="${index}">
|
<tr class="product-row" data-index="${index}">
|
||||||
<td>
|
<td>
|
||||||
<select name="products[${index}][product_id]" class="form-control select2 product-select" required>
|
<select name="products[${index}][product_id]" class="form-control product-select" required>
|
||||||
${productOptions}
|
${originalProductOptions}
|
||||||
</select>
|
</select>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td class="text-center">
|
||||||
<span class="available-stock text-muted">-</span>
|
<span class="available-stock text-muted">-</span>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
@@ -123,12 +126,7 @@ $(document).ready(function () {
|
|||||||
placeholder="0"
|
placeholder="0"
|
||||||
required>
|
required>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
|
||||||
<input type="text"
|
|
||||||
name="products[${index}][notes]"
|
|
||||||
class="form-control"
|
|
||||||
placeholder="Catatan produk (opsional)">
|
|
||||||
</td>
|
|
||||||
<td>
|
<td>
|
||||||
<button type="button" class="btn btn-danger btn-sm remove-product">
|
<button type="button" class="btn btn-danger btn-sm remove-product">
|
||||||
<i class="la la-trash"></i>
|
<i class="la la-trash"></i>
|
||||||
@@ -152,9 +150,6 @@ $(document).ready(function () {
|
|||||||
$(this)
|
$(this)
|
||||||
.find('input[name*="quantity_requested"]')
|
.find('input[name*="quantity_requested"]')
|
||||||
.attr("name", `products[${index}][quantity_requested]`);
|
.attr("name", `products[${index}][quantity_requested]`);
|
||||||
$(this)
|
|
||||||
.find('input[name*="notes"]')
|
|
||||||
.attr("name", `products[${index}][notes]`);
|
|
||||||
});
|
});
|
||||||
productIndex = $(".product-row").length;
|
productIndex = $(".product-row").length;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,12 +28,12 @@ $(document).ready(function () {
|
|||||||
{
|
{
|
||||||
data: "from_dealer",
|
data: "from_dealer",
|
||||||
name: "fromDealer.name",
|
name: "fromDealer.name",
|
||||||
width: "15%",
|
width: "13%",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
data: "to_dealer",
|
data: "to_dealer",
|
||||||
name: "toDealer.name",
|
name: "toDealer.name",
|
||||||
width: "15%",
|
width: "13%",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
data: "requested_by",
|
data: "requested_by",
|
||||||
@@ -57,117 +57,17 @@ $(document).ready(function () {
|
|||||||
name: "action",
|
name: "action",
|
||||||
orderable: false,
|
orderable: false,
|
||||||
searchable: false,
|
searchable: false,
|
||||||
width: "15%",
|
width: "20%",
|
||||||
className: "text-center",
|
className: "text-center",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
order: [[2, "desc"]], // Order by created_at desc
|
order: [[2, "desc"]], // Order by created_at desc
|
||||||
pageLength: 10,
|
pageLength: 10,
|
||||||
responsive: true,
|
responsive: true,
|
||||||
language: {
|
|
||||||
processing: "Memuat data...",
|
|
||||||
lengthMenu: "Tampilkan _MENU_ data per halaman",
|
|
||||||
zeroRecords: "Data tidak ditemukan",
|
|
||||||
info: "Menampilkan _START_ sampai _END_ dari _TOTAL_ data",
|
|
||||||
infoEmpty: "Menampilkan 0 sampai 0 dari 0 data",
|
|
||||||
infoFiltered: "(difilter dari _MAX_ total data)",
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Handle Receive Button Click
|
// Modal event handlers are now handled by Bootstrap 5 data attributes
|
||||||
$(document).on("click", ".btn-receive", function () {
|
// No need for manual modal show/hide handlers
|
||||||
var mutationId = $(this).data("id");
|
|
||||||
$("#receiveModal" + mutationId).modal("show");
|
|
||||||
});
|
|
||||||
|
|
||||||
// Handle Approve Button Click
|
|
||||||
$(document).on("click", ".btn-approve", function () {
|
|
||||||
var mutationId = $(this).data("id");
|
|
||||||
|
|
||||||
// Load mutation details via AJAX
|
|
||||||
$.ajax({
|
|
||||||
url: "/warehouse/mutations/" + mutationId + "/details",
|
|
||||||
type: "GET",
|
|
||||||
beforeSend: function () {
|
|
||||||
$("#mutation-details" + mutationId).html(
|
|
||||||
'<div class="text-center">' +
|
|
||||||
'<div class="spinner-border" role="status">' +
|
|
||||||
'<span class="sr-only">Loading...</span>' +
|
|
||||||
"</div>" +
|
|
||||||
"<p>Memuat detail produk...</p>" +
|
|
||||||
"</div>"
|
|
||||||
);
|
|
||||||
},
|
|
||||||
success: function (response) {
|
|
||||||
var detailsHtml = "<h6>Detail Produk:</h6>";
|
|
||||||
detailsHtml += '<div class="table-responsive">';
|
|
||||||
detailsHtml += '<table class="table table-sm">';
|
|
||||||
detailsHtml += "<thead>";
|
|
||||||
detailsHtml += "<tr>";
|
|
||||||
detailsHtml += "<th>Produk</th>";
|
|
||||||
detailsHtml += "<th>Diminta</th>";
|
|
||||||
detailsHtml += "<th>Disetujui</th>";
|
|
||||||
detailsHtml += "<th>Stock Tersedia</th>";
|
|
||||||
detailsHtml += "</tr>";
|
|
||||||
detailsHtml += "</thead>";
|
|
||||||
detailsHtml += "<tbody>";
|
|
||||||
|
|
||||||
response.details.forEach(function (detail, index) {
|
|
||||||
detailsHtml += "<tr>";
|
|
||||||
detailsHtml += "<td>" + detail.product.name + "</td>";
|
|
||||||
detailsHtml +=
|
|
||||||
"<td>" +
|
|
||||||
parseFloat(detail.quantity_requested).toLocaleString() +
|
|
||||||
"</td>";
|
|
||||||
detailsHtml += "<td>";
|
|
||||||
detailsHtml +=
|
|
||||||
'<input type="number" name="details[' +
|
|
||||||
detail.id +
|
|
||||||
'][quantity_approved]" ';
|
|
||||||
detailsHtml += 'class="form-control form-control-sm" ';
|
|
||||||
detailsHtml += 'value="' + detail.quantity_requested + '" ';
|
|
||||||
detailsHtml +=
|
|
||||||
'min="0" max="' +
|
|
||||||
Math.min(
|
|
||||||
detail.quantity_requested,
|
|
||||||
detail.available_stock
|
|
||||||
) +
|
|
||||||
'" ';
|
|
||||||
detailsHtml += 'step="0.01" required>';
|
|
||||||
detailsHtml += "</td>";
|
|
||||||
detailsHtml +=
|
|
||||||
"<td>" +
|
|
||||||
parseFloat(detail.available_stock).toLocaleString() +
|
|
||||||
"</td>";
|
|
||||||
detailsHtml += "</tr>";
|
|
||||||
});
|
|
||||||
|
|
||||||
detailsHtml += "</tbody>";
|
|
||||||
detailsHtml += "</table>";
|
|
||||||
detailsHtml += "</div>";
|
|
||||||
|
|
||||||
$("#mutation-details" + mutationId).html(detailsHtml);
|
|
||||||
},
|
|
||||||
error: function () {
|
|
||||||
$("#mutation-details" + mutationId).html(
|
|
||||||
'<div class="alert alert-danger">Gagal memuat detail produk</div>'
|
|
||||||
);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
$("#approveModal" + mutationId).modal("show");
|
|
||||||
});
|
|
||||||
|
|
||||||
// Handle other button clicks
|
|
||||||
$(document).on("click", ".btn-reject", function () {
|
|
||||||
var mutationId = $(this).data("id");
|
|
||||||
$("#rejectModal" + mutationId).modal("show");
|
|
||||||
});
|
|
||||||
|
|
||||||
$(document).on("click", ".btn-complete", function () {
|
|
||||||
var mutationId = $(this).data("id");
|
|
||||||
$("#completeModal" + mutationId).modal("show");
|
|
||||||
});
|
|
||||||
|
|
||||||
// Handle Cancel Button Click with SweetAlert
|
// Handle Cancel Button Click with SweetAlert
|
||||||
$(document).on("click", ".btn-cancel", function () {
|
$(document).on("click", ".btn-cancel", function () {
|
||||||
@@ -240,7 +140,7 @@ $(document).ready(function () {
|
|||||||
.html("Memproses...");
|
.html("Memproses...");
|
||||||
});
|
});
|
||||||
|
|
||||||
// Auto-calculate approved quantity based on available stock
|
// Validate quantity approved in receive modal
|
||||||
$(document).on("input", 'input[name*="quantity_approved"]', function () {
|
$(document).on("input", 'input[name*="quantity_approved"]', function () {
|
||||||
var maxValue = parseFloat($(this).attr("max"));
|
var maxValue = parseFloat($(this).attr("max"));
|
||||||
var currentValue = parseFloat($(this).val());
|
var currentValue = parseFloat($(this).val());
|
||||||
@@ -250,7 +150,7 @@ $(document).ready(function () {
|
|||||||
$(this).addClass("is-invalid");
|
$(this).addClass("is-invalid");
|
||||||
if (!$(this).siblings(".invalid-feedback").length) {
|
if (!$(this).siblings(".invalid-feedback").length) {
|
||||||
$(this).after(
|
$(this).after(
|
||||||
'<div class="invalid-feedback">Jumlah melebihi stock yang tersedia</div>'
|
'<div class="invalid-feedback">Quantity tidak boleh melebihi yang diminta</div>'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -1,4 +1,9 @@
|
|||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
|
$(".select2").select2({
|
||||||
|
placeholder: "Pilih...",
|
||||||
|
allowClear: true,
|
||||||
|
});
|
||||||
|
|
||||||
// Fungsi untuk mengambil data stok
|
// Fungsi untuk mengambil data stok
|
||||||
function fetchStockData() {
|
function fetchStockData() {
|
||||||
const dealerId = $("#dealer").val();
|
const dealerId = $("#dealer").val();
|
||||||
|
|||||||
@@ -1,29 +1,28 @@
|
|||||||
<div class="btn-group btn-group-sm" role="group">
|
<div class="btn-group btn-group-sm" role="group">
|
||||||
<!-- View Button -->
|
<!-- View Button -->
|
||||||
<a href="{{ route('mutations.show', $row->id) }}"
|
<a href="{{ route('mutations.show', $row->id) }}"
|
||||||
class="btn btn-sm btn-clean btn-icon btn-icon-md"
|
class="btn btn-sm btn-outline-info me-1">
|
||||||
title="Lihat Detail">
|
Detail
|
||||||
<i class="la la-eye"></i>
|
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
@if($row->status->value === 'sent')
|
@if($row->status->value === 'sent')
|
||||||
<!-- Receive Button (untuk dealer tujuan) -->
|
<!-- Receive Button (untuk dealer tujuan) -->
|
||||||
@if(auth()->user()->dealer_id == $row->to_dealer_id)
|
@if(auth()->user()->dealer_id == $row->to_dealer_id)
|
||||||
<button type="button"
|
<button type="button"
|
||||||
class="btn btn-sm btn-clean btn-icon btn-icon-md btn-receive"
|
class="btn btn-sm btn-outline-primary btn-receive me-1"
|
||||||
data-id="{{ $row->id }}"
|
data-id="{{ $row->id }}"
|
||||||
title="Terima Mutasi">
|
data-bs-toggle="modal"
|
||||||
<i class="la la-download text-primary"></i>
|
data-bs-target="#receiveModal{{ $row->id }}">
|
||||||
|
Terima
|
||||||
</button>
|
</button>
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
<!-- Cancel Button (untuk pengirim) -->
|
<!-- Cancel Button (untuk pengirim) -->
|
||||||
@if(auth()->user()->dealer_id == $row->from_dealer_id || auth()->user()->hasRole('admin'))
|
@if(auth()->user()->dealer_id == $row->from_dealer_id || auth()->user()->hasRole('admin'))
|
||||||
<button type="button"
|
<button type="button"
|
||||||
class="btn btn-sm btn-clean btn-icon btn-icon-md btn-cancel"
|
class="btn btn-sm btn-outline-warning btn-cancel me-1"
|
||||||
data-id="{{ $row->id }}"
|
data-id="{{ $row->id }}">
|
||||||
title="Batalkan Mutasi">
|
Batal
|
||||||
<i class="la la-ban text-warning"></i>
|
|
||||||
</button>
|
</button>
|
||||||
@endif
|
@endif
|
||||||
@endif
|
@endif
|
||||||
@@ -32,20 +31,22 @@
|
|||||||
<!-- Approve Button (untuk pengirim atau admin) -->
|
<!-- Approve Button (untuk pengirim atau admin) -->
|
||||||
@if(auth()->user()->dealer_id == $row->from_dealer_id || auth()->user()->hasRole('admin'))
|
@if(auth()->user()->dealer_id == $row->from_dealer_id || auth()->user()->hasRole('admin'))
|
||||||
<button type="button"
|
<button type="button"
|
||||||
class="btn btn-sm btn-clean btn-icon btn-icon-md btn-approve"
|
class="btn btn-sm btn-outline-success btn-approve me-1"
|
||||||
data-id="{{ $row->id }}"
|
data-id="{{ $row->id }}"
|
||||||
title="Setujui Mutasi">
|
data-bs-toggle="modal"
|
||||||
<i class="la la-check text-success"></i>
|
data-bs-target="#approveModal{{ $row->id }}">
|
||||||
|
Setujui
|
||||||
</button>
|
</button>
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
<!-- Reject Button (untuk pengirim atau admin) -->
|
<!-- Reject Button (untuk pengirim atau admin) -->
|
||||||
@if(auth()->user()->dealer_id == $row->from_dealer_id || auth()->user()->hasRole('admin'))
|
@if(auth()->user()->dealer_id == $row->from_dealer_id || auth()->user()->hasRole('admin'))
|
||||||
<button type="button"
|
<button type="button"
|
||||||
class="btn btn-sm btn-clean btn-icon btn-icon-md btn-reject"
|
class="btn btn-sm btn-outline-danger btn-reject me-1"
|
||||||
data-id="{{ $row->id }}"
|
data-id="{{ $row->id }}"
|
||||||
title="Tolak Mutasi">
|
data-bs-toggle="modal"
|
||||||
<i class="la la-times text-danger"></i>
|
data-bs-target="#rejectModal{{ $row->id }}">
|
||||||
|
Tolak
|
||||||
</button>
|
</button>
|
||||||
@endif
|
@endif
|
||||||
@endif
|
@endif
|
||||||
@@ -54,20 +55,20 @@
|
|||||||
<!-- Complete/Receive Button -->
|
<!-- Complete/Receive Button -->
|
||||||
@can('complete-mutation')
|
@can('complete-mutation')
|
||||||
<button type="button"
|
<button type="button"
|
||||||
class="btn btn-sm btn-clean btn-icon btn-icon-md btn-complete"
|
class="btn btn-sm btn-outline-primary btn-complete me-1"
|
||||||
data-id="{{ $row->id }}"
|
data-id="{{ $row->id }}"
|
||||||
title="Terima & Selesaikan Mutasi">
|
data-bs-toggle="modal"
|
||||||
<i class="la la-check-circle text-primary"></i>
|
data-bs-target="#completeModal{{ $row->id }}">
|
||||||
|
Selesaikan
|
||||||
</button>
|
</button>
|
||||||
@endcan
|
@endcan
|
||||||
|
|
||||||
<!-- Cancel Button -->
|
<!-- Cancel Button -->
|
||||||
@can('edit-mutation')
|
@can('edit-mutation')
|
||||||
<button type="button"
|
<button type="button"
|
||||||
class="btn btn-sm btn-clean btn-icon btn-icon-md btn-cancel"
|
class="btn btn-sm btn-outline-warning btn-cancel me-1"
|
||||||
data-id="{{ $row->id }}"
|
data-id="{{ $row->id }}">
|
||||||
title="Batalkan Mutasi">
|
Batal
|
||||||
<i class="la la-ban text-warning"></i>
|
|
||||||
</button>
|
</button>
|
||||||
@endcan
|
@endcan
|
||||||
@endif
|
@endif
|
||||||
@@ -75,53 +76,71 @@
|
|||||||
@if(in_array($row->status->value, ['pending', 'approved']) && auth()->user()->id === $row->requested_by)
|
@if(in_array($row->status->value, ['pending', 'approved']) && auth()->user()->id === $row->requested_by)
|
||||||
<!-- Edit Button (only for creator and if still pending/approved) -->
|
<!-- Edit Button (only for creator and if still pending/approved) -->
|
||||||
<a href="{{ route('mutations.edit', $row->id) }}"
|
<a href="{{ route('mutations.edit', $row->id) }}"
|
||||||
class="btn btn-sm btn-clean btn-icon btn-icon-md"
|
class="btn btn-sm btn-outline-secondary me-1">
|
||||||
title="Edit Mutasi">
|
Edit
|
||||||
<i class="la la-edit text-info"></i>
|
|
||||||
</a>
|
|
||||||
@endif
|
|
||||||
|
|
||||||
@if($row->status->value === 'completed')
|
|
||||||
<!-- Print Button -->
|
|
||||||
<a href="{{ route('mutations.print', $row->id) }}"
|
|
||||||
class="btn btn-sm btn-clean btn-icon btn-icon-md"
|
|
||||||
target="_blank"
|
|
||||||
title="Cetak Laporan">
|
|
||||||
<i class="la la-print text-info"></i>
|
|
||||||
</a>
|
</a>
|
||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Modal untuk Approve -->
|
<!-- Modal untuk Approve -->
|
||||||
<div class="modal fade" id="approveModal{{ $row->id }}" tabindex="-1" role="dialog">
|
<div class="modal fade" id="approveModal{{ $row->id }}" tabindex="-1" aria-labelledby="approveModalLabel{{ $row->id }}" aria-hidden="true">
|
||||||
<div class="modal-dialog" role="document">
|
<div class="modal-dialog">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<h5 class="modal-title">Setujui Mutasi</h5>
|
<h5 class="modal-title" id="approveModalLabel{{ $row->id }}">Setujui Mutasi</h5>
|
||||||
<button type="button" class="close" data-dismiss="modal">
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
<span>×</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
<form action="{{ route('mutations.approve', $row->id) }}" method="POST" class="approve-form">
|
<form action="{{ route('mutations.approve', $row->id) }}" method="POST" class="approve-form">
|
||||||
@csrf
|
@csrf
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
|
<div class="alert alert-info">
|
||||||
|
<strong>Konfirmasi!</strong> Anda akan menyetujui mutasi yang telah diterima oleh <strong>{{ $row->toDealer->name }}</strong>.
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>Catatan Persetujuan</label>
|
<label>Catatan Persetujuan</label>
|
||||||
<textarea name="notes" class="form-control" rows="3" placeholder="Opsional: tambahkan catatan..."></textarea>
|
<textarea name="notes" class="form-control" rows="3" placeholder="Opsional: tambahkan catatan..."></textarea>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Detail produk akan dimuat via AJAX -->
|
<h6>Detail Produk yang Diterima:</h6>
|
||||||
<div id="mutation-details{{ $row->id }}">
|
<div class="table-responsive">
|
||||||
<div class="text-center">
|
<table class="table table-sm table-bordered">
|
||||||
<div class="spinner-border" role="status">
|
<thead>
|
||||||
<span class="sr-only">Loading...</span>
|
<tr>
|
||||||
</div>
|
<th>Produk</th>
|
||||||
<p>Memuat detail produk...</p>
|
<th width="20%" class="text-center">Qty Diminta</th>
|
||||||
</div>
|
<th width="20%" class="text-center">Qty Diterima</th>
|
||||||
|
<th width="30%">Catatan</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
@foreach($row->mutationDetails as $detail)
|
||||||
|
<tr>
|
||||||
|
<td>{{ $detail->product->name }}</td>
|
||||||
|
<td class="text-center">{{ number_format($detail->quantity_requested, 2) }}</td>
|
||||||
|
<td class="text-center">
|
||||||
|
<span class="font-weight-bold {{ $detail->quantity_approved < $detail->quantity_requested ? 'text-warning' : 'text-success' }}">
|
||||||
|
{{ number_format($detail->quantity_approved, 2) }}
|
||||||
|
</span>
|
||||||
|
@if($detail->quantity_approved < $detail->quantity_requested)
|
||||||
|
<small class="text-muted d-block">
|
||||||
|
(Kurang {{ number_format($detail->quantity_requested - $detail->quantity_approved, 2) }})
|
||||||
|
</small>
|
||||||
|
@endif
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<small class="text-muted">{{ $detail->notes ?: '-' }}</small>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
@endforeach
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<p class="text-muted">Setelah disetujui, stock akan siap untuk dipindahkan.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">Batal</button>
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Batal</button>
|
||||||
<button type="submit" class="btn btn-success">Setujui Mutasi</button>
|
<button type="submit" class="btn btn-success">Setujui Mutasi</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
@@ -130,14 +149,12 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Modal untuk Reject -->
|
<!-- Modal untuk Reject -->
|
||||||
<div class="modal fade" id="rejectModal{{ $row->id }}" tabindex="-1" role="dialog">
|
<div class="modal fade" id="rejectModal{{ $row->id }}" tabindex="-1" aria-labelledby="rejectModalLabel{{ $row->id }}" aria-hidden="true">
|
||||||
<div class="modal-dialog" role="document">
|
<div class="modal-dialog">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<h5 class="modal-title">Tolak Mutasi</h5>
|
<h5 class="modal-title" id="rejectModalLabel{{ $row->id }}">Tolak Mutasi</h5>
|
||||||
<button type="button" class="close" data-dismiss="modal">
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
<span>×</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
<form action="{{ route('mutations.reject', $row->id) }}" method="POST">
|
<form action="{{ route('mutations.reject', $row->id) }}" method="POST">
|
||||||
@csrf
|
@csrf
|
||||||
@@ -151,7 +168,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">Batal</button>
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Batal</button>
|
||||||
<button type="submit" class="btn btn-danger">Tolak Mutasi</button>
|
<button type="submit" class="btn btn-danger">Tolak Mutasi</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
@@ -160,14 +177,12 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Modal untuk Receive -->
|
<!-- Modal untuk Receive -->
|
||||||
<div class="modal fade" id="receiveModal{{ $row->id }}" tabindex="-1" role="dialog">
|
<div class="modal fade" id="receiveModal{{ $row->id }}" tabindex="-1" aria-labelledby="receiveModalLabel{{ $row->id }}" aria-hidden="true">
|
||||||
<div class="modal-dialog" role="document">
|
<div class="modal-dialog">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<h5 class="modal-title">Terima Mutasi</h5>
|
<h5 class="modal-title" id="receiveModalLabel{{ $row->id }}">Terima Mutasi</h5>
|
||||||
<button type="button" class="close" data-dismiss="modal">
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
<span>×</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
<form action="{{ route('mutations.receive', $row->id) }}" method="POST">
|
<form action="{{ route('mutations.receive', $row->id) }}" method="POST">
|
||||||
@csrf
|
@csrf
|
||||||
@@ -175,11 +190,55 @@
|
|||||||
<div class="alert alert-info">
|
<div class="alert alert-info">
|
||||||
<strong>Konfirmasi!</strong> Anda akan menerima mutasi dari <strong>{{ $row->fromDealer->name }}</strong>.
|
<strong>Konfirmasi!</strong> Anda akan menerima mutasi dari <strong>{{ $row->fromDealer->name }}</strong>.
|
||||||
</div>
|
</div>
|
||||||
<p>Setelah menerima, mutasi akan menunggu persetujuan dari pengirim sebelum stock dipindahkan.</p>
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Catatan Penerimaan</label>
|
||||||
|
<textarea name="notes" class="form-control" rows="3" placeholder="Catatan kondisi barang saat diterima (opsional)"></textarea>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h6>Detail Produk yang Diterima:</h6>
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-sm table-bordered">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Produk</th>
|
||||||
|
<th width="15%" class="text-center">Qty Diminta</th>
|
||||||
|
<th width="15%" class="text-center">Qty Diterima</th>
|
||||||
|
<th width="35%">Catatan Produk</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
@foreach($row->mutationDetails as $index => $detail)
|
||||||
|
<tr>
|
||||||
|
<td>{{ $detail->product->name }}</td>
|
||||||
|
<td class="text-center">{{ number_format($detail->quantity_requested, 2) }}</td>
|
||||||
|
<td class="text-center">
|
||||||
|
<input type="number"
|
||||||
|
name="products[{{ $detail->id }}][quantity_approved]"
|
||||||
|
class="form-control form-control-sm text-center"
|
||||||
|
value="{{ $detail->quantity_requested }}"
|
||||||
|
min="0"
|
||||||
|
max="{{ $detail->quantity_requested }}"
|
||||||
|
step="0.01"
|
||||||
|
required>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<input type="text"
|
||||||
|
name="products[{{ $detail->id }}][notes]"
|
||||||
|
class="form-control form-control-sm"
|
||||||
|
placeholder="Catatan kondisi produk saat diterima">
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
@endforeach
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p class="text-muted">Setelah menerima, mutasi akan menunggu persetujuan dari pengirim sebelum stock dipindahkan.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">Batal</button>
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Batal</button>
|
||||||
<button type="submit" class="btn btn-primary">Ya, Terima</button>
|
<button type="submit" class="btn btn-primary">Ya, Terima Mutasi</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
@@ -187,14 +246,12 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Modal untuk Complete -->
|
<!-- Modal untuk Complete -->
|
||||||
<div class="modal fade" id="completeModal{{ $row->id }}" tabindex="-1" role="dialog">
|
<div class="modal fade" id="completeModal{{ $row->id }}" tabindex="-1" aria-labelledby="completeModalLabel{{ $row->id }}" aria-hidden="true">
|
||||||
<div class="modal-dialog" role="document">
|
<div class="modal-dialog">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<h5 class="modal-title">Selesaikan Mutasi</h5>
|
<h5 class="modal-title" id="completeModalLabel{{ $row->id }}">Selesaikan Mutasi</h5>
|
||||||
<button type="button" class="close" data-dismiss="modal">
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
<span>×</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
<form action="{{ route('mutations.complete', $row->id) }}" method="POST">
|
<form action="{{ route('mutations.complete', $row->id) }}" method="POST">
|
||||||
@csrf
|
@csrf
|
||||||
@@ -205,7 +262,7 @@
|
|||||||
<p>Apakah Anda yakin ingin menyelesaikan mutasi ini? Tindakan ini tidak dapat dibatalkan.</p>
|
<p>Apakah Anda yakin ingin menyelesaikan mutasi ini? Tindakan ini tidak dapat dibatalkan.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">Batal</button>
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Batal</button>
|
||||||
<button type="submit" class="btn btn-primary">Ya, Selesaikan</button>
|
<button type="submit" class="btn btn-primary">Ya, Selesaikan</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -65,11 +65,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="notes">Catatan</label>
|
|
||||||
<textarea name="notes" id="notes" class="form-control" rows="3" placeholder="Catatan untuk mutasi ini (opsional)">{{ old('notes') }}</textarea>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="kt-separator kt-separator--border-dashed kt-separator--space-lg"></div>
|
<div class="kt-separator kt-separator--border-dashed kt-separator--space-lg"></div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
@@ -84,24 +79,23 @@
|
|||||||
<table class="table table-bordered" id="products-table">
|
<table class="table table-bordered" id="products-table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th width="35%">Produk</th>
|
<th width="40%">Produk</th>
|
||||||
<th width="15%">Stock Tersedia</th>
|
<th width="20%">Stock Tersedia</th>
|
||||||
<th width="15%">Quantity</th>
|
<th width="25%">Quantity</th>
|
||||||
<th width="25%">Catatan</th>
|
<th width="15%">Aksi</th>
|
||||||
<th width="10%">Aksi</th>
|
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody id="products-tbody">
|
<tbody id="products-tbody">
|
||||||
<tr class="product-row" data-index="0">
|
<tr class="product-row" data-index="0">
|
||||||
<td>
|
<td>
|
||||||
<select name="products[0][product_id]" class="form-control select2 product-select" required>
|
<select name="products[0][product_id]" class="form-control product-select select2" required>
|
||||||
<option value="">Pilih Produk</option>
|
<option value="">Pilih Produk</option>
|
||||||
@foreach($products as $product)
|
@foreach($products as $product)
|
||||||
<option value="{{ $product->id }}">{{ $product->name }}</option>
|
<option value="{{ $product->id }}">{{ $product->name }}</option>
|
||||||
@endforeach
|
@endforeach
|
||||||
</select>
|
</select>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td class="text-center">
|
||||||
<span class="available-stock text-muted">-</span>
|
<span class="available-stock text-muted">-</span>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
@@ -113,12 +107,7 @@
|
|||||||
placeholder="0"
|
placeholder="0"
|
||||||
required>
|
required>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
|
||||||
<input type="text"
|
|
||||||
name="products[0][notes]"
|
|
||||||
class="form-control"
|
|
||||||
placeholder="Catatan produk (opsional)">
|
|
||||||
</td>
|
|
||||||
<td>
|
<td>
|
||||||
<button type="button" class="btn btn-danger btn-sm remove-product" disabled>
|
<button type="button" class="btn btn-danger btn-sm remove-product" disabled>
|
||||||
<i class="la la-trash"></i>
|
<i class="la la-trash"></i>
|
||||||
@@ -133,19 +122,18 @@
|
|||||||
<div class="alert alert-info">
|
<div class="alert alert-info">
|
||||||
<i class="la la-info-circle"></i>
|
<i class="la la-info-circle"></i>
|
||||||
<strong>Informasi:</strong>
|
<strong>Informasi:</strong>
|
||||||
Mutasi akan dibuat dengan status "Menunggu Persetujuan" dan memerlukan approval sebelum stock dipindahkan.
|
Mutasi akan dikirim ke dealer tujuan. Catatan dapat ditambahkan saat dealer tujuan menerima mutasi.
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="kt-portlet__foot">
|
<div class="kt-portlet__foot">
|
||||||
<div class="kt-form__actions kt-form__actions--right">
|
<div class="kt-form__actions kt-form__actions--right">
|
||||||
|
<button type="submit" class="btn btn-primary" id="submit-btn">
|
||||||
|
Simpan Mutasi
|
||||||
|
</button>
|
||||||
<button type="button" class="btn btn-secondary" onclick="window.history.back()">
|
<button type="button" class="btn btn-secondary" onclick="window.history.back()">
|
||||||
Batal
|
Batal
|
||||||
</button>
|
</button>
|
||||||
<button type="submit" class="btn btn-primary" id="submit-btn">
|
|
||||||
<i class="la la-save"></i>
|
|
||||||
Simpan Mutasi
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -15,8 +15,7 @@
|
|||||||
<div class="kt-portlet__head-wrapper">
|
<div class="kt-portlet__head-wrapper">
|
||||||
<div class="kt-portlet__head-actions">
|
<div class="kt-portlet__head-actions">
|
||||||
<a href="{{ route('mutations.create') }}" class="btn btn-bold btn-label-brand btn--sm">
|
<a href="{{ route('mutations.create') }}" class="btn btn-bold btn-label-brand btn--sm">
|
||||||
<i class="la la-plus"></i>
|
Tambah
|
||||||
Tambah Mutasi
|
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -102,38 +102,43 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="kt-portlet__body">
|
<div class="kt-portlet__body">
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table class="table table-bordered table-hover">
|
<table class="table table-bordered table-hover mutation-detail-table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>No.</th>
|
<th width="5%">No.</th>
|
||||||
<th>Nama Produk</th>
|
<th width="25%">Nama Produk</th>
|
||||||
<th>Jumlah Diminta</th>
|
<th width="15%" class="text-center">Jumlah Diminta</th>
|
||||||
<th>Jumlah Disetujui</th>
|
<th width="15%" class="text-center">Jumlah Disetujui</th>
|
||||||
<th>Status Approval</th>
|
<th width="15%" class="text-center">Status Approval</th>
|
||||||
<th>Catatan</th>
|
<th width="25%">Catatan</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@foreach($mutation->mutationDetails as $index => $detail)
|
@foreach($mutation->mutationDetails as $index => $detail)
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ $index + 1 }}</td>
|
<td class="text-center">{{ $index + 1 }}</td>
|
||||||
<td>{{ $detail->product->name }}</td>
|
<td>{{ $detail->product->name }}</td>
|
||||||
<td>{{ number_format($detail->quantity_requested, 2) }}</td>
|
<td class="text-center">{{ number_format($detail->quantity_requested, 2) }}</td>
|
||||||
<td>
|
<td class="text-center">
|
||||||
@if($mutation->status->value === 'received' || $mutation->status->value === 'approved' || $mutation->status->value === 'completed')
|
@if($mutation->status->value === 'received' || $mutation->status->value === 'approved' || $mutation->status->value === 'completed')
|
||||||
{{ number_format($detail->quantity_approved ?? 0, 2) }}
|
{{ number_format($detail->quantity_approved ?? 0, 2) }}
|
||||||
@else
|
@else
|
||||||
<span class="text-muted">Belum ditentukan</span>
|
<span class="text-muted">Belum ditentukan</span>
|
||||||
@endif
|
@endif
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td class="text-center">
|
||||||
@if($mutation->status->value === 'received' || $mutation->status->value === 'approved' || $mutation->status->value === 'completed')
|
@php
|
||||||
<span class="kt-badge kt-badge--{{ $detail->approval_status_color }} kt-badge--pill">
|
$textColorClass = match($detail->approval_status_color) {
|
||||||
{{ $detail->approval_status }}
|
'success' => 'text-success',
|
||||||
</span>
|
'warning' => 'text-warning',
|
||||||
@else
|
'danger' => 'text-danger',
|
||||||
<span class="kt-badge kt-badge--secondary kt-badge--pill">Menunggu</span>
|
'info' => 'text-info',
|
||||||
@endif
|
default => 'text-muted'
|
||||||
|
};
|
||||||
|
@endphp
|
||||||
|
<span class="font-weight-bold {{ $textColorClass }}">
|
||||||
|
{{ $detail->approval_status }}
|
||||||
|
</span>
|
||||||
</td>
|
</td>
|
||||||
<td>{{ $detail->notes ?? '-' }}</td>
|
<td>{{ $detail->notes ?? '-' }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -141,9 +146,9 @@
|
|||||||
</tbody>
|
</tbody>
|
||||||
<tfoot>
|
<tfoot>
|
||||||
<tr class="kt-font-bold">
|
<tr class="kt-font-bold">
|
||||||
<td colspan="2">Total</td>
|
<td colspan="2" class="text-right">Total</td>
|
||||||
<td>{{ number_format($mutation->mutationDetails->sum('quantity_requested'), 2) }}</td>
|
<td class="text-center">{{ number_format($mutation->mutationDetails->sum('quantity_requested'), 2) }}</td>
|
||||||
<td>
|
<td class="text-center">
|
||||||
@if($mutation->status->value === 'received' || $mutation->status->value === 'approved' || $mutation->status->value === 'completed')
|
@if($mutation->status->value === 'received' || $mutation->status->value === 'approved' || $mutation->status->value === 'completed')
|
||||||
{{ number_format($mutation->mutationDetails->sum('quantity_approved'), 2) }}
|
{{ number_format($mutation->mutationDetails->sum('quantity_approved'), 2) }}
|
||||||
@else
|
@else
|
||||||
@@ -165,8 +170,7 @@
|
|||||||
<div class="col-lg-12">
|
<div class="col-lg-12">
|
||||||
@if($mutation->status->value === 'sent' && auth()->user()->dealer_id == $mutation->to_dealer_id)
|
@if($mutation->status->value === 'sent' && auth()->user()->dealer_id == $mutation->to_dealer_id)
|
||||||
<!-- Receive Button for destination dealer -->
|
<!-- Receive Button for destination dealer -->
|
||||||
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#receiveModal">
|
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#receiveModal{{ $mutation->id }}">
|
||||||
<i class="la la-download"></i>
|
|
||||||
Terima Mutasi
|
Terima Mutasi
|
||||||
</button>
|
</button>
|
||||||
@endif
|
@endif
|
||||||
@@ -174,12 +178,10 @@
|
|||||||
@if($mutation->status->value === 'received' && (auth()->user()->dealer_id == $mutation->from_dealer_id || auth()->user()->hasRole('admin')))
|
@if($mutation->status->value === 'received' && (auth()->user()->dealer_id == $mutation->from_dealer_id || auth()->user()->hasRole('admin')))
|
||||||
<!-- Approve Button for sender or admin -->
|
<!-- Approve Button for sender or admin -->
|
||||||
<button type="button" class="btn btn-success btn-approve" data-id="{{ $mutation->id }}">
|
<button type="button" class="btn btn-success btn-approve" data-id="{{ $mutation->id }}">
|
||||||
<i class="la la-check"></i>
|
|
||||||
Setujui Mutasi
|
Setujui Mutasi
|
||||||
</button>
|
</button>
|
||||||
<!-- Reject Button for sender or admin -->
|
<!-- Reject Button for sender or admin -->
|
||||||
<button type="button" class="btn btn-danger" data-toggle="modal" data-target="#rejectModal">
|
<button type="button" class="btn btn-danger" data-toggle="modal" data-target="#rejectModal">
|
||||||
<i class="la la-times"></i>
|
|
||||||
Tolak Mutasi
|
Tolak Mutasi
|
||||||
</button>
|
</button>
|
||||||
@endif
|
@endif
|
||||||
@@ -187,7 +189,6 @@
|
|||||||
@if($mutation->status->value === 'approved')
|
@if($mutation->status->value === 'approved')
|
||||||
<!-- Complete Button -->
|
<!-- Complete Button -->
|
||||||
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#completeModal">
|
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#completeModal">
|
||||||
<i class="la la-check-circle"></i>
|
|
||||||
Selesaikan Mutasi
|
Selesaikan Mutasi
|
||||||
</button>
|
</button>
|
||||||
@endif
|
@endif
|
||||||
@@ -195,18 +196,11 @@
|
|||||||
@if($mutation->canBeCancelled() && (auth()->user()->dealer_id == $mutation->from_dealer_id || auth()->user()->hasRole('admin')))
|
@if($mutation->canBeCancelled() && (auth()->user()->dealer_id == $mutation->from_dealer_id || auth()->user()->hasRole('admin')))
|
||||||
<!-- Cancel Button -->
|
<!-- Cancel Button -->
|
||||||
<button type="button" class="btn btn-warning" data-toggle="modal" data-target="#cancelModal">
|
<button type="button" class="btn btn-warning" data-toggle="modal" data-target="#cancelModal">
|
||||||
<i class="la la-ban"></i>
|
|
||||||
Batalkan Mutasi
|
Batalkan Mutasi
|
||||||
</button>
|
</button>
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
@if($mutation->status->value === 'completed')
|
|
||||||
<!-- Print Button -->
|
|
||||||
<a href="{{ route('mutations.print', $mutation->id) }}" class="btn btn-info" target="_blank">
|
|
||||||
<i class="la la-print"></i>
|
|
||||||
Cetak Laporan
|
|
||||||
</a>
|
|
||||||
@endif
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -217,8 +211,8 @@
|
|||||||
<!-- Modals -->
|
<!-- Modals -->
|
||||||
@if($mutation->status->value === 'sent' && auth()->user()->dealer_id == $mutation->to_dealer_id)
|
@if($mutation->status->value === 'sent' && auth()->user()->dealer_id == $mutation->to_dealer_id)
|
||||||
<!-- Receive Modal -->
|
<!-- Receive Modal -->
|
||||||
<div class="modal fade" id="receiveModal" tabindex="-1" role="dialog">
|
<div class="modal fade" id="receiveModal{{ $mutation->id }}" tabindex="-1" role="dialog">
|
||||||
<div class="modal-dialog" role="document">
|
<div class="modal-dialog modal-lg" role="document">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<h5 class="modal-title">Terima Mutasi</h5>
|
<h5 class="modal-title">Terima Mutasi</h5>
|
||||||
@@ -232,11 +226,55 @@
|
|||||||
<div class="alert alert-info">
|
<div class="alert alert-info">
|
||||||
<strong>Konfirmasi!</strong> Anda akan menerima mutasi dari <strong>{{ $mutation->fromDealer->name }}</strong>.
|
<strong>Konfirmasi!</strong> Anda akan menerima mutasi dari <strong>{{ $mutation->fromDealer->name }}</strong>.
|
||||||
</div>
|
</div>
|
||||||
<p>Setelah menerima, mutasi akan menunggu persetujuan dari pengirim sebelum stock dipindahkan.</p>
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Catatan Penerimaan</label>
|
||||||
|
<textarea name="notes" class="form-control" rows="3" placeholder="Catatan kondisi barang saat diterima (opsional)"></textarea>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h6>Detail Produk yang Diterima:</h6>
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-sm table-bordered">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Produk</th>
|
||||||
|
<th width="15%" class="text-center">Qty Diminta</th>
|
||||||
|
<th width="15%" class="text-center">Qty Diterima</th>
|
||||||
|
<th width="35%">Catatan Produk</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
@foreach($mutation->mutationDetails as $index => $detail)
|
||||||
|
<tr>
|
||||||
|
<td>{{ $detail->product->name }}</td>
|
||||||
|
<td class="text-center">{{ number_format($detail->quantity_requested, 2) }}</td>
|
||||||
|
<td class="text-center">
|
||||||
|
<input type="number"
|
||||||
|
name="products[{{ $detail->id }}][quantity_approved]"
|
||||||
|
class="form-control form-control-sm text-center"
|
||||||
|
value="{{ $detail->quantity_requested }}"
|
||||||
|
min="0"
|
||||||
|
max="{{ $detail->quantity_requested }}"
|
||||||
|
step="0.01"
|
||||||
|
required>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<input type="text"
|
||||||
|
name="products[{{ $detail->id }}][notes]"
|
||||||
|
class="form-control form-control-sm"
|
||||||
|
placeholder="Catatan kondisi produk saat diterima">
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
@endforeach
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p class="text-muted">Setelah menerima, mutasi akan menunggu persetujuan dari pengirim sebelum stock dipindahkan.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">Batal</button>
|
<button type="button" class="btn btn-secondary" data-dismiss="modal">Batal</button>
|
||||||
<button type="submit" class="btn btn-primary">Ya, Terima</button>
|
<button type="submit" class="btn btn-primary">Ya, Terima Mutasi</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
@@ -258,20 +296,51 @@
|
|||||||
<form action="{{ route('mutations.approve', $mutation->id) }}" method="POST" class="approve-form">
|
<form action="{{ route('mutations.approve', $mutation->id) }}" method="POST" class="approve-form">
|
||||||
@csrf
|
@csrf
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
|
<div class="alert alert-info">
|
||||||
|
<strong>Konfirmasi!</strong> Anda akan menyetujui mutasi yang telah diterima oleh <strong>{{ $mutation->toDealer->name }}</strong>.
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>Catatan Persetujuan</label>
|
<label>Catatan Persetujuan</label>
|
||||||
<textarea name="notes" class="form-control" rows="3" placeholder="Opsional: tambahkan catatan..."></textarea>
|
<textarea name="notes" class="form-control" rows="3" placeholder="Opsional: tambahkan catatan..."></textarea>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Detail produk akan dimuat via AJAX -->
|
<h6>Detail Produk yang Diterima:</h6>
|
||||||
<div id="mutation-details{{ $mutation->id }}">
|
<div class="table-responsive">
|
||||||
<div class="text-center">
|
<table class="table table-sm table-bordered">
|
||||||
<div class="spinner-border" role="status">
|
<thead>
|
||||||
<span class="sr-only">Loading...</span>
|
<tr>
|
||||||
</div>
|
<th>Produk</th>
|
||||||
<p>Memuat detail produk...</p>
|
<th width="20%" class="text-center">Qty Diminta</th>
|
||||||
</div>
|
<th width="20%" class="text-center">Qty Diterima</th>
|
||||||
|
<th width="30%">Catatan</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
@foreach($mutation->mutationDetails as $detail)
|
||||||
|
<tr>
|
||||||
|
<td>{{ $detail->product->name }}</td>
|
||||||
|
<td class="text-center">{{ number_format($detail->quantity_requested, 2) }}</td>
|
||||||
|
<td class="text-center">
|
||||||
|
<span class="font-weight-bold {{ $detail->quantity_approved < $detail->quantity_requested ? 'text-warning' : 'text-success' }}">
|
||||||
|
{{ number_format($detail->quantity_approved, 2) }}
|
||||||
|
</span>
|
||||||
|
@if($detail->quantity_approved < $detail->quantity_requested)
|
||||||
|
<small class="text-muted d-block">
|
||||||
|
(Kurang {{ number_format($detail->quantity_requested - $detail->quantity_approved, 2) }})
|
||||||
|
</small>
|
||||||
|
@endif
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<small class="text-muted">{{ $detail->notes ?: '-' }}</small>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
@endforeach
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<p class="text-muted">Setelah disetujui, stock akan siap untuk dipindahkan.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">Batal</button>
|
<button type="button" class="btn btn-secondary" data-dismiss="modal">Batal</button>
|
||||||
@@ -372,71 +441,41 @@
|
|||||||
@endif
|
@endif
|
||||||
@endsection
|
@endsection
|
||||||
|
|
||||||
|
@section('styles')
|
||||||
|
<style>
|
||||||
|
/* Custom CSS for mutation detail table alignment */
|
||||||
|
.mutation-detail-table {
|
||||||
|
table-layout: fixed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mutation-detail-table th,
|
||||||
|
.mutation-detail-table td {
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mutation-detail-table .text-center {
|
||||||
|
text-align: center !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Ensure proper alignment in approval modal */
|
||||||
|
.approve-form .form-control.text-center {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
@endsection
|
||||||
|
|
||||||
@section('javascripts')
|
@section('javascripts')
|
||||||
<script>
|
<script>
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
// Handle Approve Button Click
|
// Handle Approve Button Click
|
||||||
$(document).on('click', '.btn-approve', function() {
|
$(document).on('click', '.btn-approve', function() {
|
||||||
var mutationId = $(this).data('id');
|
var mutationId = $(this).data('id');
|
||||||
|
|
||||||
// Load mutation details via AJAX
|
|
||||||
$.ajax({
|
|
||||||
url: '/warehouse/mutations/' + mutationId + '/details',
|
|
||||||
type: 'GET',
|
|
||||||
beforeSend: function() {
|
|
||||||
$('#mutation-details' + mutationId).html(
|
|
||||||
'<div class="text-center">' +
|
|
||||||
'<div class="spinner-border" role="status">' +
|
|
||||||
'<span class="sr-only">Loading...</span>' +
|
|
||||||
'</div>' +
|
|
||||||
'<p>Memuat detail produk...</p>' +
|
|
||||||
'</div>'
|
|
||||||
);
|
|
||||||
},
|
|
||||||
success: function(response) {
|
|
||||||
var detailsHtml = '<h6>Detail Produk:</h6>';
|
|
||||||
detailsHtml += '<div class="table-responsive">';
|
|
||||||
detailsHtml += '<table class="table table-sm">';
|
|
||||||
detailsHtml += '<thead>';
|
|
||||||
detailsHtml += '<tr>';
|
|
||||||
detailsHtml += '<th>Produk</th>';
|
|
||||||
detailsHtml += '<th>Diminta</th>';
|
|
||||||
detailsHtml += '<th>Disetujui</th>';
|
|
||||||
detailsHtml += '<th>Stock Tersedia</th>';
|
|
||||||
detailsHtml += '</tr>';
|
|
||||||
detailsHtml += '</thead>';
|
|
||||||
detailsHtml += '<tbody>';
|
|
||||||
|
|
||||||
response.details.forEach(function(detail, index) {
|
|
||||||
detailsHtml += '<tr>';
|
|
||||||
detailsHtml += '<td>' + detail.product.name + '</td>';
|
|
||||||
detailsHtml += '<td>' + parseFloat(detail.quantity_requested).toLocaleString() + '</td>';
|
|
||||||
detailsHtml += '<td>';
|
|
||||||
detailsHtml += '<input type="number" name="details[' + detail.id + '][quantity_approved]" ';
|
|
||||||
detailsHtml += 'class="form-control form-control-sm" ';
|
|
||||||
detailsHtml += 'value="' + detail.quantity_requested + '" ';
|
|
||||||
detailsHtml += 'min="0" max="' + Math.min(detail.quantity_requested, detail.available_stock) + '" ';
|
|
||||||
detailsHtml += 'step="0.01" required>';
|
|
||||||
detailsHtml += '</td>';
|
|
||||||
detailsHtml += '<td>' + parseFloat(detail.available_stock).toLocaleString() + '</td>';
|
|
||||||
detailsHtml += '</tr>';
|
|
||||||
});
|
|
||||||
|
|
||||||
detailsHtml += '</tbody>';
|
|
||||||
detailsHtml += '</table>';
|
|
||||||
detailsHtml += '</div>';
|
|
||||||
|
|
||||||
$('#mutation-details' + mutationId).html(detailsHtml);
|
|
||||||
},
|
|
||||||
error: function() {
|
|
||||||
$('#mutation-details' + mutationId).html('<div class="alert alert-danger">Gagal memuat detail produk</div>');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$('#approveModal' + mutationId).modal('show');
|
$('#approveModal' + mutationId).modal('show');
|
||||||
});
|
});
|
||||||
|
|
||||||
// Auto-calculate approved quantity based on available stock
|
// Validate quantity approved in receive modal
|
||||||
$(document).on('input', 'input[name*="quantity_approved"]', function() {
|
$(document).on('input', 'input[name*="quantity_approved"]', function() {
|
||||||
var maxValue = parseFloat($(this).attr('max'));
|
var maxValue = parseFloat($(this).attr('max'));
|
||||||
var currentValue = parseFloat($(this).val());
|
var currentValue = parseFloat($(this).val());
|
||||||
@@ -445,7 +484,7 @@ $(document).ready(function() {
|
|||||||
$(this).val(maxValue);
|
$(this).val(maxValue);
|
||||||
$(this).addClass('is-invalid');
|
$(this).addClass('is-invalid');
|
||||||
if (!$(this).siblings('.invalid-feedback').length) {
|
if (!$(this).siblings('.invalid-feedback').length) {
|
||||||
$(this).after('<div class="invalid-feedback">Jumlah melebihi stock yang tersedia</div>');
|
$(this).after('<div class="invalid-feedback">Quantity tidak boleh melebihi yang diminta</div>');
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$(this).removeClass('is-invalid');
|
$(this).removeClass('is-invalid');
|
||||||
|
|||||||
@@ -32,7 +32,7 @@
|
|||||||
$oldDealer = old('dealer');
|
$oldDealer = old('dealer');
|
||||||
$dealerValue = is_array($oldDealer) ? '' : $oldDealer;
|
$dealerValue = is_array($oldDealer) ? '' : $oldDealer;
|
||||||
@endphp
|
@endphp
|
||||||
<select name="dealer" id="dealer" class="form-control @error('dealer') is-invalid @enderror" required>
|
<select name="dealer" id="dealer" class="form-control select2 @error('dealer') is-invalid @enderror" required>
|
||||||
<option value="">Pilih Dealer</option>
|
<option value="">Pilih Dealer</option>
|
||||||
@foreach($dealers as $dealer)
|
@foreach($dealers as $dealer)
|
||||||
<option value="{{ $dealer->id }}" {{ $dealerValue == $dealer->id ? 'selected' : '' }}>
|
<option value="{{ $dealer->id }}" {{ $dealerValue == $dealer->id ? 'selected' : '' }}>
|
||||||
@@ -202,5 +202,5 @@
|
|||||||
@endsection
|
@endsection
|
||||||
|
|
||||||
@section('javascripts')
|
@section('javascripts')
|
||||||
<script src="{{ asset('js/warehouse_management/opnames/create.js') }}"></script>
|
<script src="{{ mix('js/warehouse_management/opnames/create.js') }}"></script>
|
||||||
@endsection
|
@endsection
|
||||||
|
|||||||
Reference in New Issue
Block a user