chore: initial commit of existing codebase

This commit is contained in:
ghassan committed 2026-05-19 12:40:08 +03:00
commit 11e94889b2
292 files changed
+38586

No files matched your search

@@ -0,0 +1,78 @@
<?php
namespace App\Http\Controllers\Production;
use App\Http\Controllers\Controller;
use App\Models\BillOfMaterial;
use App\Models\Item;
use Illuminate\Http\Request;
class BillOfMaterialController extends Controller
{
public function index()
{
$boms = BillOfMaterial::with(['product', 'rawMaterial'])
->get()
->groupBy('product_id');
return view('production.bom.index', compact('boms'));
}
public function create()
{
$finishedGoods = Item::where('category', 'finished_good')->get();
$rawMaterials = Item::where('category', 'raw_material')->get();
return view('production.bom.create', compact('finishedGoods', 'rawMaterials'));
}
public function store(Request $request)
{
$request->validate([
'product_id' => 'required|exists:items,id',
'raw_material_id' => 'required|exists:items,id',
'quantity_required' => 'required|numeric|min:0.01',
'unit_of_measure' => 'required|string|max:50',
]);
BillOfMaterial::create($request->all());
return redirect()->route('production.bom.index')->with('success', 'BOM entry created successfully.');
}
public function show(BillOfMaterial $billOfMaterial)
{
$billOfMaterial->load(['product', 'rawMaterial']);
return view('production.bom.show', compact('billOfMaterial'));
}
public function edit(BillOfMaterial $billOfMaterial)
{
$finishedGoods = Item::where('category', 'finished_good')->get();
$rawMaterials = Item::where('category', 'raw_material')->get();
return view('production.bom.edit', compact('billOfMaterial', 'finishedGoods', 'rawMaterials'));
}
public function update(Request $request, BillOfMaterial $billOfMaterial)
{
$request->validate([
'product_id' => 'required|exists:items,id',
'raw_material_id' => 'required|exists:items,id',
'quantity_required' => 'required|numeric|min:0.01',
'unit_of_measure' => 'required|string|max:50',
]);
$billOfMaterial->update($request->all());
return redirect()->route('production.bom.index')->with('success', 'BOM entry updated successfully.');
}
public function destroy(BillOfMaterial $billOfMaterial)
{
$billOfMaterial->delete();
return redirect()->route('production.bom.index')->with('success', 'BOM entry deleted successfully.');
}
}
@@ -0,0 +1,118 @@
<?php
namespace App\Http\Controllers\Production;
use App\Http\Controllers\Controller;
use App\Models\Item;
use App\Models\MaterialIssue;
use App\Models\ProductionOrder;
use App\Models\StockLevel;
use App\Models\StockMovement;
use App\Models\Warehouse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
class MaterialIssueController extends Controller
{
public function index()
{
$issues = MaterialIssue::with(['productionOrder', 'item', 'warehouse'])->paginate(15);
return view('production.material-issues.index', compact('issues'));
}
public function create()
{
$productionOrders = ProductionOrder::whereIn('status', ['planned', 'in_progress'])->get();
$items = Item::where('category', 'raw_material')->get();
$warehouses = Warehouse::all();
return view('production.material-issues.create', compact('productionOrders', 'items', 'warehouses'));
}
public function store(Request $request)
{
$request->validate([
'production_order_id' => 'required|exists:production_orders,id',
'item_id' => 'required|exists:items,id',
'warehouse_id' => 'required|exists:warehouses,id',
'quantity' => 'required|numeric|min:0.01',
'issue_date' => 'required|date',
]);
DB::transaction(function () use ($request) {
$issueNumber = 'MI-' . str_pad(MaterialIssue::max('id') + 1, 5, '0', STR_PAD_LEFT);
$stockLevel = StockLevel::firstOrCreate(
['item_id' => $request->item_id, 'warehouse_id' => $request->warehouse_id],
['quantity' => 0]
);
// Decrement stock, floor at zero
$decrement = min($request->quantity, $stockLevel->quantity);
$stockLevel->decrement('quantity', $decrement);
StockMovement::create([
'item_id' => $request->item_id,
'warehouse_id' => $request->warehouse_id,
'type' => 'out',
'quantity' => $request->quantity,
'reference_type' => 'MaterialIssue',
'reference_id' => null, // will be updated after create
'created_by' => auth()->id(),
]);
$issue = MaterialIssue::create([
'issue_number' => $issueNumber,
'production_order_id' => $request->production_order_id,
'item_id' => $request->item_id,
'warehouse_id' => $request->warehouse_id,
'quantity' => $request->quantity,
'issue_date' => $request->issue_date,
'issued_by' => auth()->id(),
]);
// Backfill reference_id on the movement just created
StockMovement::where('item_id', $request->item_id)
->where('warehouse_id', $request->warehouse_id)
->where('type', 'out')
->where('reference_type', 'MaterialIssue')
->whereNull('reference_id')
->latest('id')
->first()
?->update(['reference_id' => $issue->id]);
});
return redirect()->back()->with('success', 'Material issued successfully.');
}
public function show(MaterialIssue $materialIssue)
{
$materialIssue->load(['productionOrder', 'item', 'warehouse']);
return view('production.material-issues.show', compact('materialIssue'));
}
public function edit(MaterialIssue $materialIssue)
{
return view('production.material-issues.edit', compact('materialIssue'));
}
public function update(Request $request, MaterialIssue $materialIssue)
{
$request->validate([
'issue_date' => 'required|date',
]);
$materialIssue->update($request->only('issue_date', 'notes'));
return redirect()->route('production.material-issues.index')->with('success', 'Material issue updated successfully.');
}
public function destroy(MaterialIssue $materialIssue)
{
$materialIssue->delete();
return redirect()->route('production.material-issues.index')->with('success', 'Material issue deleted successfully.');
}
}
@@ -0,0 +1,94 @@
<?php
namespace App\Http\Controllers\Production;
use App\Http\Controllers\Controller;
use App\Models\Item;
use App\Models\ProductionCost;
use App\Models\ProductionOrder;
use Illuminate\Http\Request;
class ProductionOrderController extends Controller
{
public function index()
{
$orders = ProductionOrder::with('product')->paginate(15);
return view('production.orders.index', compact('orders'));
}
public function create()
{
// Only finished_good type items can be produced
$items = Item::where('category', 'finished_good')->get();
return view('production.orders.create', compact('items'));
}
public function store(Request $request)
{
$request->validate([
'product_id' => 'required|exists:items,id',
'quantity_to_produce' => 'required|numeric|min:1',
'production_date' => 'required|date',
]);
$data = $request->all();
$data['order_number'] = 'PRO-' . str_pad(ProductionOrder::max('id') + 1, 5, '0', STR_PAD_LEFT);
$data['created_by'] = auth()->id();
$data['status'] = 'planned';
$order = ProductionOrder::create($data);
return redirect()->route('production.orders.show', $order)->with('success', 'Production order created successfully.');
}
public function show(ProductionOrder $productionOrder)
{
$productionOrder->load(['product', 'materialIssues.item', 'outputs.item', 'cost']);
return view('production.orders.show', compact('productionOrder'));
}
public function edit(ProductionOrder $productionOrder)
{
$items = Item::where('category', 'finished_good')->get();
return view('production.orders.edit', compact('productionOrder', 'items'));
}
public function update(Request $request, ProductionOrder $productionOrder)
{
$request->validate([
'production_date' => 'required|date',
]);
$productionOrder->update($request->only('production_date', 'notes'));
return redirect()->route('production.orders.show', $productionOrder)->with('success', 'Production order updated successfully.');
}
public function destroy(ProductionOrder $productionOrder)
{
$productionOrder->delete();
return redirect()->route('production.orders.index')->with('success', 'Production order deleted successfully.');
}
public function start(ProductionOrder $productionOrder)
{
$productionOrder->update(['status' => 'in_progress']);
return redirect()->back()->with('success', 'Production order started.');
}
public function complete(ProductionOrder $productionOrder)
{
$productionOrder->update([
'status' => 'completed',
'completion_date' => now(),
]);
return redirect()->back()->with('success', 'Production order marked as completed.');
}
}
@@ -0,0 +1,105 @@
<?php
namespace App\Http\Controllers\Production;
use App\Http\Controllers\Controller;
use App\Models\Item;
use App\Models\ProductionOrder;
use App\Models\ProductionOutput;
use App\Models\StockLevel;
use App\Models\StockMovement;
use App\Models\Warehouse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
class ProductionOutputController extends Controller
{
public function index()
{
$outputs = ProductionOutput::with(['productionOrder', 'item', 'warehouse'])->paginate(15);
return view('production.outputs.index', compact('outputs'));
}
public function create()
{
$productionOrders = ProductionOrder::whereIn('status', ['in_progress'])->with('product')->get();
$warehouses = Warehouse::all();
return view('production.outputs.create', compact('productionOrders', 'warehouses'));
}
public function store(Request $request)
{
$request->validate([
'production_order_id' => 'required|exists:production_orders,id',
'item_id' => 'required|exists:items,id',
'warehouse_id' => 'required|exists:warehouses,id',
'quantity' => 'required|numeric|min:0.01',
'output_date' => 'required|date',
]);
DB::transaction(function () use ($request) {
$stockLevel = StockLevel::firstOrCreate(
['item_id' => $request->item_id, 'warehouse_id' => $request->warehouse_id],
['quantity' => 0]
);
$stockLevel->increment('quantity', $request->quantity);
$output = ProductionOutput::create([
'production_order_id' => $request->production_order_id,
'item_id' => $request->item_id,
'warehouse_id' => $request->warehouse_id,
'quantity' => $request->quantity,
'output_date' => $request->output_date,
'recorded_by' => auth()->id(),
]);
StockMovement::create([
'item_id' => $request->item_id,
'warehouse_id' => $request->warehouse_id,
'type' => 'in',
'quantity' => $request->quantity,
'reference_type' => 'ProductionOutput',
'reference_id' => $output->id,
'created_by' => auth()->id(),
]);
// Accumulate quantity_produced on the production order
ProductionOrder::where('id', $request->production_order_id)
->increment('quantity_produced', $request->quantity);
});
return redirect()->back()->with('success', 'Production output recorded and stock updated.');
}
public function show(ProductionOutput $productionOutput)
{
$productionOutput->load(['productionOrder', 'item', 'warehouse']);
return view('production.outputs.show', compact('productionOutput'));
}
public function edit(ProductionOutput $productionOutput)
{
return view('production.outputs.edit', compact('productionOutput'));
}
public function update(Request $request, ProductionOutput $productionOutput)
{
$request->validate([
'output_date' => 'required|date',
]);
$productionOutput->update($request->only('output_date', 'notes'));
return redirect()->route('production.outputs.index')->with('success', 'Production output updated successfully.');
}
public function destroy(ProductionOutput $productionOutput)
{
$productionOutput->delete();
return redirect()->route('production.outputs.index')->with('success', 'Production output deleted successfully.');
}
}