211 lines
7.5 KiB
PHP
211 lines
7.5 KiB
PHP
<?php
|
|
|
|
namespace App\Modules\Inventory\Controllers;
|
|
|
|
use App\Http\Controllers\Controller;
|
|
use App\Modules\Inventory\Models\InventoryAdjustDoc;
|
|
use App\Modules\Inventory\Models\InventoryCountDoc;
|
|
use App\Modules\Inventory\Models\Warehouse;
|
|
use App\Modules\Inventory\Services\AdjustService;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Inertia\Inertia;
|
|
|
|
class AdjustDocController extends Controller
|
|
{
|
|
protected $adjustService;
|
|
|
|
public function __construct(AdjustService $adjustService)
|
|
{
|
|
$this->adjustService = $adjustService;
|
|
}
|
|
|
|
public function index(Request $request)
|
|
{
|
|
$query = InventoryAdjustDoc::query()
|
|
->with(['createdBy', 'postedBy', 'warehouse']);
|
|
|
|
// 搜尋
|
|
if ($request->filled('search')) {
|
|
$search = $request->search;
|
|
$query->where(function($q) use ($search) {
|
|
$q->where('doc_no', 'like', "%{$search}%")
|
|
->orWhere('reason', 'like', "%{$search}%")
|
|
->orWhere('remarks', 'like', "%{$search}%");
|
|
});
|
|
}
|
|
|
|
if ($request->filled('warehouse_id')) {
|
|
$query->where('warehouse_id', $request->warehouse_id);
|
|
}
|
|
|
|
$perPage = $request->input('per_page', 15);
|
|
$docs = $query->orderByDesc('created_at')
|
|
->paginate($perPage)
|
|
->withQueryString()
|
|
->through(function ($doc) {
|
|
return [
|
|
'id' => (string) $doc->id,
|
|
'doc_no' => $doc->doc_no,
|
|
'status' => $doc->status,
|
|
'warehouse_name' => $doc->warehouse->name,
|
|
'reason' => $doc->reason,
|
|
'created_at' => $doc->created_at->format('Y-m-d H:i'),
|
|
'posted_at' => $doc->posted_at ? $doc->posted_at->format('Y-m-d H:i') : '-',
|
|
'created_by' => $doc->createdBy?->name,
|
|
'remarks' => $doc->remarks,
|
|
];
|
|
});
|
|
|
|
return Inertia::render('Inventory/Adjust/Index', [
|
|
'docs' => $docs,
|
|
'warehouses' => Warehouse::all()->map(fn($w) => ['id' => (string)$w->id, 'name' => $w->name]),
|
|
'filters' => $request->only(['warehouse_id', 'search', 'per_page']),
|
|
]);
|
|
}
|
|
|
|
public function store(Request $request)
|
|
{
|
|
// 模式 1: 從盤點單建立
|
|
if ($request->filled('count_doc_id')) {
|
|
$countDoc = InventoryCountDoc::findOrFail($request->count_doc_id);
|
|
|
|
// 檢查是否已存在對應的盤調單 (避免重複建立)
|
|
if (InventoryAdjustDoc::where('count_doc_id', $countDoc->id)->exists()) {
|
|
return redirect()->back()->with('error', '此盤點單已建立過盤調單');
|
|
}
|
|
|
|
$doc = $this->adjustService->createFromCountDoc($countDoc, auth()->id());
|
|
return redirect()->route('inventory.adjust.show', [$doc->id])
|
|
->with('success', '已從盤點單生成盤調單');
|
|
}
|
|
|
|
// 模式 2: 一般手動調整 (保留原始邏輯但更新訊息)
|
|
$validated = $request->validate([
|
|
'warehouse_id' => 'required',
|
|
'reason' => 'required|string',
|
|
'remarks' => 'nullable|string',
|
|
]);
|
|
|
|
$doc = $this->adjustService->createDoc(
|
|
$validated['warehouse_id'],
|
|
$validated['reason'],
|
|
$validated['remarks'],
|
|
auth()->id()
|
|
);
|
|
|
|
return redirect()->route('inventory.adjust.show', [$doc->id])
|
|
->with('success', '已建立盤調單');
|
|
}
|
|
|
|
/**
|
|
* API: 獲取可盤調的已完成盤點單 (支援掃描單號)
|
|
*/
|
|
public function getPendingCounts(Request $request)
|
|
{
|
|
$query = InventoryCountDoc::where('status', 'completed')
|
|
->whereNotExists(function ($query) {
|
|
$query->select(DB::raw(1))
|
|
->from('inventory_adjust_docs')
|
|
->whereColumn('inventory_adjust_docs.count_doc_id', 'inventory_count_docs.id');
|
|
});
|
|
|
|
if ($request->filled('search')) {
|
|
$search = $request->search;
|
|
$query->where('doc_no', 'like', "%{$search}%");
|
|
}
|
|
|
|
$counts = $query->limit(10)->get()->map(function($c) {
|
|
return [
|
|
'id' => (string)$c->id,
|
|
'doc_no' => $c->doc_no,
|
|
'warehouse_name' => $c->warehouse->name,
|
|
'completed_at' => $c->completed_at->format('Y-m-d H:i'),
|
|
];
|
|
});
|
|
|
|
return response()->json($counts);
|
|
}
|
|
|
|
public function show(InventoryAdjustDoc $doc)
|
|
{
|
|
$doc->load(['items.product.baseUnit', 'createdBy', 'postedBy', 'warehouse', 'countDoc']);
|
|
|
|
$docData = [
|
|
'id' => (string) $doc->id,
|
|
'doc_no' => $doc->doc_no,
|
|
'warehouse_id' => (string) $doc->warehouse_id,
|
|
'warehouse_name' => $doc->warehouse->name,
|
|
'status' => $doc->status,
|
|
'reason' => $doc->reason,
|
|
'remarks' => $doc->remarks,
|
|
'created_at' => $doc->created_at->format('Y-m-d H:i'),
|
|
'created_by' => $doc->createdBy?->name,
|
|
'count_doc_id' => $doc->count_doc_id ? (string)$doc->count_doc_id : null,
|
|
'count_doc_no' => $doc->countDoc?->doc_no,
|
|
'items' => $doc->items->map(function ($item) {
|
|
return [
|
|
'id' => (string) $item->id,
|
|
'product_id' => (string) $item->product_id,
|
|
'product_name' => $item->product->name,
|
|
'product_code' => $item->product->code,
|
|
'batch_number' => $item->batch_number,
|
|
'unit' => $item->product->baseUnit?->name,
|
|
'qty_before' => (float) $item->qty_before,
|
|
'adjust_qty' => (float) $item->adjust_qty,
|
|
'notes' => $item->notes,
|
|
];
|
|
}),
|
|
];
|
|
|
|
return Inertia::render('Inventory/Adjust/Show', [
|
|
'doc' => $docData,
|
|
]);
|
|
}
|
|
|
|
public function update(Request $request, InventoryAdjustDoc $doc)
|
|
{
|
|
if ($doc->status !== 'draft') {
|
|
return redirect()->back()->with('error', '只能修改草稿狀態的單據');
|
|
}
|
|
|
|
// 提交 (items 更新 或 過帳)
|
|
if ($request->input('action') === 'post') {
|
|
$this->adjustService->post($doc, auth()->id());
|
|
return redirect()->route('inventory.adjust.index')
|
|
->with('success', '盤調單已過帳生效');
|
|
}
|
|
|
|
// 僅儲存資料
|
|
$validated = $request->validate([
|
|
'items' => 'array',
|
|
'items.*.product_id' => 'required|exists:products,id',
|
|
'items.*.adjust_qty' => 'required|numeric', // 可以是負數
|
|
'items.*.batch_number' => 'nullable|string',
|
|
'items.*.notes' => 'nullable|string',
|
|
]);
|
|
|
|
if ($request->has('items')) {
|
|
$this->adjustService->updateItems($doc, $validated['items']);
|
|
}
|
|
|
|
// 更新表頭
|
|
$doc->update($request->only(['reason', 'remarks']));
|
|
|
|
return redirect()->back()->with('success', '儲存成功');
|
|
}
|
|
|
|
public function destroy(InventoryAdjustDoc $doc)
|
|
{
|
|
if ($doc->status !== 'draft') {
|
|
return redirect()->back()->with('error', '只能刪除草稿狀態的單據');
|
|
}
|
|
|
|
$doc->items()->delete();
|
|
$doc->delete();
|
|
|
|
return redirect()->route('inventory.adjust.index')
|
|
->with('success', '盤調單已刪除');
|
|
}
|
|
}
|