partial update transaction work with stock product

This commit is contained in:
2025-06-24 19:42:19 +07:00
parent 33502e905d
commit c3233ea6b2
20 changed files with 3432 additions and 239 deletions

View File

@@ -0,0 +1,247 @@
<?php
namespace App\Http\Controllers;
use App\Models\Work;
use App\Models\Product;
use App\Models\WorkProduct;
use App\Models\Menu;
use App\Services\StockService;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Facades\Log;
use Yajra\DataTables\DataTables;
class WorkProductController extends Controller
{
protected $stockService;
public function __construct(StockService $stockService)
{
$this->stockService = $stockService;
}
/**
* Display work products for a specific work
*/
public function index(Request $request, $workId)
{
$menu = Menu::where('link', 'work.index')->first();
abort_if(Gate::denies('view', $menu), 403, 'Unauthorized User');
$work = Work::with('category')->findOrFail($workId);
if ($request->ajax()) {
Log::info('Work products index AJAX request for work ID: ' . $workId);
$workProducts = WorkProduct::with(['product', 'product.category'])
->where('work_id', $workId)
->get();
Log::info('Found ' . $workProducts->count() . ' work products');
return DataTables::of($workProducts)
->addIndexColumn()
->addColumn('product_name', function($row) {
return $row->product->name;
})
->addColumn('product_code', function($row) {
return $row->product->code;
})
->addColumn('product_category', function($row) {
return $row->product->category ? $row->product->category->name : '-';
})
->addColumn('unit', function($row) {
return $row->product->unit;
})
->addColumn('quantity_required', function($row) {
return number_format($row->quantity_required, 2);
})
->addColumn('action', function($row) use ($menu) {
$btn = '<div class="d-flex flex-row gap-1">';
if(Gate::allows('update', $menu)) {
$btn .= '<button class="btn btn-warning btn-sm btn-edit-work-product" data-id="'.$row->id.'">
Edit
</button>';
}
if(Gate::allows('delete', $menu)) {
$btn .= '<button class="btn btn-danger btn-sm btn-delete-work-product" data-id="'.$row->id.'">
Hapus
</button>';
}
$btn .= '</div>';
return $btn;
})
->rawColumns(['action'])
->make(true);
}
$products = Product::where('active', true)->with('category')->get();
return view('back.master.work-products', compact('work', 'products'));
}
/**
* Store work product relationship
*/
public function store(Request $request)
{
$menu = Menu::where('link', 'work.index')->first();
abort_if(Gate::denies('create', $menu), 403, 'Unauthorized User');
$request->validate([
'work_id' => 'required|exists:works,id',
'product_id' => 'required|exists:products,id',
'quantity_required' => 'required|numeric|min:0.01',
'notes' => 'nullable|string'
]);
// Check if combination already exists
$exists = WorkProduct::where('work_id', $request->work_id)
->where('product_id', $request->product_id)
->exists();
if ($exists) {
return response()->json([
'status' => 422,
'message' => 'Produk sudah ditambahkan ke pekerjaan ini'
], 422);
}
WorkProduct::create($request->all());
return response()->json([
'status' => 200,
'message' => 'Produk berhasil ditambahkan ke pekerjaan'
]);
}
/**
* Show work product for editing
*/
public function show($workId, $workProductId)
{
$menu = Menu::where('link', 'work.index')->first();
abort_if(Gate::denies('view', $menu), 403, 'Unauthorized User');
try {
$workProduct = WorkProduct::with(['work', 'product', 'product.category'])
->where('work_id', $workId)
->where('id', $workProductId)
->firstOrFail();
return response()->json([
'status' => 200,
'data' => $workProduct
]);
} catch (\Exception $e) {
Log::error('Error fetching work product: ' . $e->getMessage());
return response()->json([
'status' => 404,
'message' => 'Work product tidak ditemukan'
], 404);
}
}
/**
* Update work product relationship
*/
public function update(Request $request, $workId, $workProductId)
{
$menu = Menu::where('link', 'work.index')->first();
abort_if(Gate::denies('update', $menu), 403, 'Unauthorized User');
$request->validate([
'quantity_required' => 'required|numeric|min:0.01',
'notes' => 'nullable|string'
]);
try {
$workProduct = WorkProduct::where('work_id', $workId)
->where('id', $workProductId)
->firstOrFail();
$workProduct->update($request->only(['quantity_required', 'notes']));
return response()->json([
'status' => 200,
'message' => 'Data produk pekerjaan berhasil diupdate'
]);
} catch (\Exception $e) {
Log::error('Error updating work product: ' . $e->getMessage());
return response()->json([
'status' => 404,
'message' => 'Work product tidak ditemukan'
], 404);
}
}
/**
* Remove work product relationship
*/
public function destroy($workId, $workProductId)
{
$menu = Menu::where('link', 'work.index')->first();
abort_if(Gate::denies('delete', $menu), 403, 'Unauthorized User');
try {
$workProduct = WorkProduct::where('work_id', $workId)
->where('id', $workProductId)
->firstOrFail();
$workProduct->delete();
return response()->json([
'status' => 200,
'message' => 'Produk berhasil dihapus dari pekerjaan'
]);
} catch (\Exception $e) {
Log::error('Error deleting work product: ' . $e->getMessage());
return response()->json([
'status' => 404,
'message' => 'Work product tidak ditemukan'
], 404);
}
}
/**
* Get stock prediction for work
*/
public function stockPrediction(Request $request, $workId)
{
$quantity = $request->get('quantity', 1);
$prediction = $this->stockService->getStockUsagePrediction($workId, $quantity);
return response()->json([
'status' => 200,
'data' => $prediction
]);
}
/**
* Check stock availability for work at specific dealer
*/
public function checkStock(Request $request)
{
$request->validate([
'work_id' => 'required|exists:works,id',
'dealer_id' => 'required|exists:dealers,id',
'quantity' => 'required|integer|min:1'
]);
$availability = $this->stockService->checkStockAvailability(
$request->work_id,
$request->dealer_id,
$request->quantity
);
return response()->json([
'status' => 200,
'data' => $availability
]);
}
}