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

@@ -9,17 +9,30 @@ use App\Models\Stock;
use App\Models\Transaction;
use App\Models\User;
use App\Models\Work;
use App\Services\StockService;
use Illuminate\Http\Request;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Validator;
use Exception;
class TransactionController extends Controller
{
protected $stockService;
public function __construct(StockService $stockService)
{
$this->stockService = $stockService;
}
public function index()
{
$work_works = Work::leftJoin('categories as c', 'c.id', '=', 'works.category_id')->select('c.name as category_name', 'works.*')->where('c.name', 'LIKE', '%kerja%')->get();
$work_works = Work::leftJoin('categories as c', 'c.id', '=', 'works.category_id')
->select('c.name as category_name', 'works.*')
->where('c.name', 'LIKE', '%kerja%')
->orderBy('works.name', 'asc')
->get();
$wash_work = Work::leftJoin('categories as c', 'c.id', '=', 'works.category_id')->select('c.name as category_name', 'works.*')->where('c.name', 'LIKE', '%cuci%')->first();
$user_sas = User::where('role_id', 4)->where('dealer_id', Auth::user()->dealer_id)->get();
$count_transaction_users = Transaction::where("user_id", Auth::user()->id)->count();
@@ -41,7 +54,9 @@ class TransactionController extends Controller
public function workcategory($category_id)
{
$works = Work::where('category_id', $category_id)->get();
$works = Work::where('category_id', $category_id)
->orderBy('name', 'asc')
->get();
$response = [
"message" => "get work category successfully",
"data" => $works,
@@ -629,14 +644,28 @@ class TransactionController extends Controller
public function destroy($id)
{
Transaction::find($id)->delete();
$response = [
'message' => 'Data deleted successfully',
'status' => 200
];
return redirect()->back();
DB::beginTransaction();
try {
$transaction = Transaction::find($id);
if (!$transaction) {
return redirect()->back()->withErrors(['error' => 'Transaksi tidak ditemukan']);
}
// Restore stock before deleting transaction
$this->stockService->restoreStockForTransaction($transaction);
// Delete the transaction
$transaction->delete();
DB::commit();
return redirect()->back()->with('success', 'Transaksi berhasil dihapus dan stock telah dikembalikan');
} catch (Exception $e) {
DB::rollback();
return redirect()->back()->withErrors(['error' => 'Gagal menghapus transaksi: ' . $e->getMessage()]);
}
}
public function store(Request $request)
@@ -645,9 +674,19 @@ class TransactionController extends Controller
$request->validate([
'work_id.*' => ['required', 'integer'],
'quantity.*' => ['required', 'integer'],
'spk_no' => ['required', function($attribute, $value, $fail) use($request) {
$date = explode('/', $request->date);
$date = $date[2].'-'.$date[0].'-'.$date[1];
'spk_no' => ['required', 'string', 'min:1', function($attribute, $value, $fail) use($request) {
// Handle date format conversion safely for validation
if (strpos($request->date, '/') !== false) {
$dateParts = explode('/', $request->date);
if (count($dateParts) === 3) {
$date = $dateParts[2].'-'.$dateParts[0].'-'.$dateParts[1];
} else {
$fail('Format tanggal tidak valid');
return;
}
} else {
$date = $request->date;
}
if(!$request->work_id) {
$fail('Pekerjaan harus diisi');
@@ -665,9 +704,19 @@ class TransactionController extends Controller
}
}
}],
'police_number' => ['required', function($attribute, $value, $fail) use($request) {
$date = explode('/', $request->date);
$date = $date[2].'-'.$date[0].'-'.$date[1];
'police_number' => ['required', 'string', 'min:1', function($attribute, $value, $fail) use($request) {
// Handle date format conversion safely for validation
if (strpos($request->date, '/') !== false) {
$dateParts = explode('/', $request->date);
if (count($dateParts) === 3) {
$date = $dateParts[2].'-'.$dateParts[0].'-'.$dateParts[1];
} else {
$fail('Format tanggal tidak valid');
return;
}
} else {
$date = $request->date;
}
if(!$request->work_id) {
$fail('Pekerjaan harus diisi');
@@ -686,9 +735,19 @@ class TransactionController extends Controller
}
}],
'warranty' => ['required'],
'date' => ['required', function($attribute, $value, $fail) use($request) {
$date = explode('/', $value);
$date = $date[2].'-'.$date[0].'-'.$date[1];
'date' => ['required', 'string', 'min:1', function($attribute, $value, $fail) use($request) {
// Handle date format conversion safely for validation
if (strpos($value, '/') !== false) {
$dateParts = explode('/', $value);
if (count($dateParts) === 3) {
$date = $dateParts[2].'-'.$dateParts[0].'-'.$dateParts[1];
} else {
$fail('Format tanggal tidak valid. Gunakan format MM/DD/YYYY atau YYYY-MM-DD');
return;
}
} else {
$date = $value;
}
if(!$request->work_id) {
$fail('Pekerjaan harus diisi');
@@ -707,31 +766,117 @@ class TransactionController extends Controller
}
}],
'category' => ['required'],
'user_sa_id' => ['required', 'integer'],
'user_sa_id' => ['required', 'integer', 'exists:users,id'],
], [
'spk_no.required' => 'No. SPK harus diisi',
'spk_no.min' => 'No. SPK tidak boleh kosong',
'police_number.required' => 'No. Polisi harus diisi',
'police_number.min' => 'No. Polisi tidak boleh kosong',
'date.required' => 'Tanggal Pekerjaan harus diisi',
'date.min' => 'Tanggal Pekerjaan tidak boleh kosong',
'user_sa_id.required' => 'Service Advisor harus dipilih',
'user_sa_id.exists' => 'Service Advisor yang dipilih tidak valid',
'work_id.*.required' => 'Pekerjaan harus dipilih',
'quantity.*.required' => 'Quantity harus diisi',
]);
$request['date'] = explode('/', $request->date);
$request['date'] = $request['date'][2].'-'.$request['date'][0].'-'.$request['date'][1];
$data = [];
for($i = 0; $i < count($request->work_id); $i++) {
$data[] = [
"user_id" => $request->mechanic_id,
"dealer_id" => $request->dealer_id,
"form" => $request->form,
"work_id" => $request->work_id[$i],
"qty" => $request->quantity[$i],
"spk" => $request->spk_no,
"police_number" => $request->police_number,
"warranty" => $request->warranty,
"user_sa_id" => $request->user_sa_id,
"date" => $request->date,
"created_at" => date('Y-m-d H:i:s')
];
// Handle date format conversion safely
$dateValue = $request->date;
if (strpos($dateValue, '/') !== false) {
// If date is in MM/DD/YYYY format, convert to Y-m-d
$dateParts = explode('/', $dateValue);
if (count($dateParts) === 3) {
$request['date'] = $dateParts[2].'-'.$dateParts[0].'-'.$dateParts[1];
} else {
// Invalid date format, use as is
$request['date'] = $dateValue;
}
} else {
// Date is already in Y-m-d format or other format, use as is
$request['date'] = $dateValue;
}
Transaction::insert($data);
return redirect()->back()->with('success', 'Berhasil input pekerjaan');
// Check stock availability for all works before creating transactions
$stockErrors = [];
for($i = 0; $i < count($request->work_id); $i++) {
$stockCheck = $this->stockService->checkStockAvailability(
$request->work_id[$i],
$request->dealer_id,
$request->quantity[$i]
);
if (!$stockCheck['available']) {
$work = Work::find($request->work_id[$i]);
$stockErrors[] = "Pekerjaan '{$work->name}': {$stockCheck['message']}";
// Add detailed stock information
if (!empty($stockCheck['details'])) {
foreach ($stockCheck['details'] as $detail) {
if (!$detail['is_available']) {
$stockErrors[] = "- {$detail['product_name']}: Dibutuhkan {$detail['required_quantity']}, Tersedia {$detail['available_stock']}";
}
}
}
}
}
// If there are stock errors, return with error messages
if (!empty($stockErrors)) {
return redirect()->back()
->withErrors(['stock' => implode('<br>', $stockErrors)])
->withInput();
}
DB::beginTransaction();
try {
$transactions = [];
$data = [];
// Create transaction records
for($i = 0; $i < count($request->work_id); $i++) {
$transactionData = [
"user_id" => $request->mechanic_id,
"dealer_id" => $request->dealer_id,
"form" => $request->form,
"work_id" => $request->work_id[$i],
"qty" => $request->quantity[$i],
"spk" => $request->spk_no,
"police_number" => $request->police_number,
"warranty" => $request->warranty,
"user_sa_id" => $request->user_sa_id,
"date" => $request->date,
"status" => 'completed', // Mark as completed to trigger stock reduction
"created_at" => date('Y-m-d H:i:s'),
"updated_at" => date('Y-m-d H:i:s')
];
$data[] = $transactionData;
}
// Insert all transactions
Transaction::insert($data);
// Get the created transactions for stock reduction
$createdTransactions = Transaction::where('spk', $request->spk_no)
->where('police_number', $request->police_number)
->where('date', $request->date)
->where('dealer_id', $request->dealer_id)
->get();
// Reduce stock for each transaction
foreach ($createdTransactions as $transaction) {
$this->stockService->reduceStockForTransaction($transaction);
}
DB::commit();
return redirect()->back()->with('success', 'Berhasil input pekerjaan dan stock telah dikurangi otomatis');
} catch (Exception $e) {
DB::rollback();
return redirect()->back()
->withErrors(['error' => 'Gagal menyimpan transaksi: ' . $e->getMessage()])
->withInput();
}
}
public function edit($id)
@@ -764,4 +909,62 @@ class TransactionController extends Controller
return response()->json($response);
}
/**
* Check stock availability for work at dealer
*/
public function checkStockAvailability(Request $request)
{
$request->validate([
'work_id' => 'required|exists:works,id',
'dealer_id' => 'required|exists:dealers,id',
'quantity' => 'required|integer|min:1'
]);
try {
$availability = $this->stockService->checkStockAvailability(
$request->work_id,
$request->dealer_id,
$request->quantity
);
return response()->json([
'status' => 200,
'data' => $availability
]);
} catch (Exception $e) {
return response()->json([
'status' => 500,
'message' => 'Error checking stock: ' . $e->getMessage()
], 500);
}
}
/**
* Get stock prediction for work
*/
public function getStockPrediction(Request $request)
{
$request->validate([
'work_id' => 'required|exists:works,id',
'quantity' => 'required|integer|min:1'
]);
try {
$prediction = $this->stockService->getStockUsagePrediction(
$request->work_id,
$request->quantity
);
return response()->json([
'status' => 200,
'data' => $prediction
]);
} catch (Exception $e) {
return response()->json([
'status' => 500,
'message' => 'Error getting prediction: ' . $e->getMessage()
], 500);
}
}
}