287 lines
9.3 KiB
PHP
287 lines
9.3 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers\Api;
|
|
|
|
use App\Http\Controllers\Controller;
|
|
use App\Models\OriginCountry;
|
|
use App\Models\StockBatch;
|
|
use App\Models\StockEntry;
|
|
use App\Models\StockPosition;
|
|
use App\Models\PhysicalItem;
|
|
use App\Models\StockSection;
|
|
use App\Models\Supplier;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\Validator;
|
|
|
|
class StockBatchController extends Controller
|
|
{
|
|
/**
|
|
* Display a paginated listing of stock entries.
|
|
*
|
|
* @param Request $request
|
|
* @return \Illuminate\Http\JsonResponse
|
|
*/
|
|
public function index(Request $request)
|
|
{
|
|
$query = StockBatch::query()
|
|
->with(['user', 'supplier', 'stockEntries.statusHistory.status', 'stockEntries.sections','stockEntries.physicalItem', 'files']);
|
|
|
|
// Apply filters if provided
|
|
if ($request->has('search')) {
|
|
$search = $request->search;
|
|
$query->whereHas('physicalItem', function($q) use ($search) {
|
|
$q->where('name', 'like', "%{$search}%");
|
|
});
|
|
}
|
|
$query->where('default_batch', 0);
|
|
|
|
// Sort
|
|
$sortField = $request->input('sort_field', 'updated_at');
|
|
$sortDirection = $request->input('sort_direction', 'desc');
|
|
$query->orderBy($sortField, $sortDirection);
|
|
|
|
// Paginate
|
|
$perPage = $request->input('per_page', 50);
|
|
$page = $request->input('page', 1);
|
|
|
|
$entries = $query->paginate($perPage, ['*'], 'page', $page);
|
|
|
|
return response()->json([
|
|
'data' => $entries->items(),
|
|
'meta' => [
|
|
'total' => $entries->total(),
|
|
'per_page' => $entries->perPage(),
|
|
'current_page' => $entries->currentPage(),
|
|
'last_page' => $entries->lastPage(),
|
|
],
|
|
]);
|
|
}
|
|
|
|
public function getBatch(Request $request, $id)
|
|
{
|
|
$batch = StockBatch::with(['stockEntries.physicalItem', 'stockEntries.sections.position.shelf.rack.line.room', 'stockEntries.supplier', 'files', 'supplier', 'stockEntries.statusHistory.status', 'user'])
|
|
->findOrFail($id);
|
|
return response()->json([
|
|
'batch' => $batch,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Store a newly created stock batch, with multiple files.
|
|
*/
|
|
public function addData(Request $request)
|
|
{
|
|
try {
|
|
|
|
|
|
$validator = Validator::make($request->all(), [
|
|
'supplier_id' => 'nullable|integer',
|
|
'tracking_number' => 'nullable|string',
|
|
'arrival_date' => 'nullable|date',
|
|
'files.*' => 'file'
|
|
.'|mimes:jpeg,png,jpg,heic,heif,pdf,doc,docx,xls,xlsx,txt'
|
|
.'|max:20480', // max size in KB (20 MB)
|
|
'file_types' => 'array',
|
|
'file_types.*' => 'in:invoice,label,other',
|
|
]);
|
|
|
|
if ($validator->fails()) {
|
|
return response()->json(['errors' => $validator->errors()], 422);
|
|
}
|
|
|
|
// create the batch
|
|
$batch = StockBatch::create([
|
|
'user_id' => auth()->id() ?? 1,
|
|
'supplier_id' => $request->input('supplier_id'),
|
|
'tracking_number' => $request->input('tracking_number'),
|
|
'arrival_date' => $request->input('arrival_date'),
|
|
]);
|
|
|
|
// attach each uploaded file
|
|
if ($request->hasFile('files')) {
|
|
foreach ($request->file('files') as $i => $upload) {
|
|
$batch->files()->create([
|
|
'filename' => $upload->getClientOriginalName(),
|
|
'file_data' => file_get_contents($upload->getRealPath()),
|
|
'file_type' => $request->input("file_types.{$i}", 'other'),
|
|
'user_id' => auth()->id() ?? 1,
|
|
]);
|
|
}
|
|
}
|
|
|
|
return response()->json([
|
|
'message' => 'Stock batch created successfully',
|
|
'data' => $batch->load(['supplier', 'user', 'files', 'stockEntries']),
|
|
], 201);
|
|
}
|
|
catch (\Exception $e) {
|
|
return response()->json(['errors' => $e->getMessage()], 500);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Display the specified stock batch, with its files & entries.
|
|
*/
|
|
public function show($id)
|
|
{
|
|
$batch = StockBatch::with(['user','supplier','files','stockEntries'])
|
|
->findOrFail($id);
|
|
|
|
return response()->json(['data' => $batch]);
|
|
}
|
|
|
|
/**
|
|
* Update the specified stock batch and optionally add new files.
|
|
*/
|
|
public function updateData(Request $request, $id)
|
|
{
|
|
$validator = Validator::make($request->all(), [
|
|
'supplier_id' => 'nullable|integer|exists:supplier,id',
|
|
'tracking_number' => 'nullable|integer',
|
|
'arrival_date' => 'nullable|date',
|
|
'files.*' => 'file',
|
|
'file_types' => 'array',
|
|
'file_types.*' => 'in:invoice,label,other',
|
|
]);
|
|
|
|
if ($validator->fails()) {
|
|
return response()->json(['errors' => $validator->errors()], 422);
|
|
}
|
|
|
|
$batch = StockBatch::findOrFail($id);
|
|
$batch->update($request->only(['supplier_id','tracking_number','arrival_date']) + [
|
|
'updated_at' => now(),
|
|
]);
|
|
|
|
// if there are new files, attach them
|
|
if ($request->hasFile('files')) {
|
|
foreach ($request->file('files') as $i => $upload) {
|
|
$batch->files()->create([
|
|
'filename' => $upload->getClientOriginalName(),
|
|
'file_data' => file_get_contents($upload->getRealPath()),
|
|
'file_type' => $request->input("file_types.{$i}", 'other'),
|
|
'user_id' => auth()->id() ?? 1,
|
|
]);
|
|
}
|
|
}
|
|
|
|
return response()->json([
|
|
'message' => 'Stock batch updated successfully',
|
|
'data' => $batch->fresh(['supplier','user','files','stockEntries']),
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Remove the specified stock batch (and its files).
|
|
*/
|
|
public function destroy($id)
|
|
{
|
|
$batch = StockBatch::with('files')->findOrFail($id);
|
|
|
|
// delete related files first if you need to clean up storage
|
|
foreach ($batch->files as $file) {
|
|
$file->delete();
|
|
}
|
|
|
|
$batch->delete();
|
|
|
|
return response()->json([
|
|
'message' => 'Stock batch deleted successfully',
|
|
]);
|
|
}
|
|
/**
|
|
* Get options for dropdown lists.
|
|
*
|
|
* @return \Illuminate\Http\JsonResponse
|
|
*/
|
|
public function getOptions()
|
|
{
|
|
$stockPositions = StockSection::doesntHave('entries')
|
|
->with('position.shelf.rack.line.room')
|
|
->get()
|
|
->map(function (StockSection $section) {
|
|
$pos = $section->position;
|
|
$shelf = $pos->shelf;
|
|
$rack = $shelf->rack;
|
|
$line = $rack->line;
|
|
$room = $line->room;
|
|
|
|
return [
|
|
'id' => $section->section_id,
|
|
'name' => sprintf(
|
|
'%s-%s-%s-%s-%s-%s',
|
|
$room->room_symbol,
|
|
$line->line_symbol,
|
|
$rack->rack_symbol,
|
|
$shelf->shelf_symbol,
|
|
$pos->position_symbol,
|
|
$section->section_symbol
|
|
),
|
|
];
|
|
});
|
|
|
|
// Get suppliers from warehouse DB
|
|
$suppliers = Supplier::select('id', 'name')->get();
|
|
|
|
// Get physical items from warehouse DB
|
|
$countriesOrigin = OriginCountry::select('id', 'code as name')->get();
|
|
|
|
return response()->json([
|
|
'stockPositions' => $stockPositions,
|
|
'suppliers' => $suppliers,
|
|
'countriesOrigin' => $countriesOrigin,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Get options for dropdown lists.
|
|
*
|
|
* @return \Illuminate\Http\JsonResponse
|
|
*/
|
|
public function getItems(Request $request)
|
|
{
|
|
// Get physical items from warehouse DB
|
|
$physicalItems = PhysicalItem::select('id', 'name')
|
|
->where('name', 'like', '%' . $request->input('item_name', '') . '%')
|
|
->get();
|
|
|
|
return response()->json([
|
|
'physicalItems' => $physicalItems,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Get options for dropdown lists.
|
|
*
|
|
* @return \Illuminate\Http\JsonResponse
|
|
*/
|
|
public function getEntries(Request $request, $id)
|
|
{
|
|
// Get physical items from warehouse DB
|
|
$stockEntries = StockEntry::with(['physicalItem', 'supplier', 'sections', 'statusHistory'])->where('stock_batch_id', $id)->get();
|
|
|
|
return response()->json([
|
|
"data" => $stockEntries
|
|
]);
|
|
}
|
|
|
|
|
|
public function addEntries(Request $request, $id)
|
|
{
|
|
// Get physical items from warehouse DB
|
|
$stockEntries = StockEntry::whereIn('id', $request->get('ids'))->get();
|
|
|
|
foreach ($stockEntries as $entry) {
|
|
$entry->update([
|
|
'stock_batch_id' => $id,
|
|
'on_the_way' => false,
|
|
]);
|
|
}
|
|
|
|
return response()->json([
|
|
'message' => 'Batch entries updated successfully',
|
|
'data' => $entry->fresh(),
|
|
]);
|
|
}
|
|
}
|