159 lines
5.9 KiB
PHP
159 lines
5.9 KiB
PHP
<?php
|
|
|
|
namespace App\Modules\Sales\Controllers;
|
|
|
|
use App\Http\Controllers\Controller;
|
|
use App\Modules\Sales\Models\SalesImportBatch;
|
|
use App\Modules\Sales\Imports\SalesImport;
|
|
use App\Modules\Inventory\Services\InventoryService; // Assuming this exists or we need to use ProductService
|
|
use Illuminate\Http\Request;
|
|
use Inertia\Inertia;
|
|
use Maatwebsite\Excel\Facades\Excel;
|
|
use Illuminate\Support\Facades\DB;
|
|
|
|
class SalesImportController extends Controller
|
|
{
|
|
public function index(Request $request)
|
|
{
|
|
$perPage = $request->input('per_page', 10);
|
|
$search = $request->input('search');
|
|
|
|
$batches = SalesImportBatch::with('importer')
|
|
->when($search, function ($query, $search) {
|
|
$query->where(function ($q) use ($search) {
|
|
$q->where('id', 'like', "%{$search}%")
|
|
->orWhereHas('importer', function ($u) use ($search) {
|
|
$u->where('name', 'like', "%{$search}%");
|
|
});
|
|
});
|
|
})
|
|
->orderByDesc('created_at')
|
|
->paginate($perPage)
|
|
->withQueryString();
|
|
|
|
return Inertia::render('Sales/Import/Index', [
|
|
'batches' => $batches,
|
|
'filters' => [
|
|
'per_page' => (string) $perPage,
|
|
'search' => $search,
|
|
],
|
|
]);
|
|
}
|
|
|
|
|
|
public function store(Request $request)
|
|
{
|
|
$request->validate([
|
|
'file' => 'required|file|mimes:xlsx,xls,csv,zip',
|
|
]);
|
|
|
|
DB::transaction(function () use ($request) {
|
|
$batch = SalesImportBatch::create([
|
|
'import_date' => now(),
|
|
'imported_by' => auth()->id(),
|
|
'status' => 'pending',
|
|
'tenant_id' => tenant('id'), // If tenant context requires it, but usually automatic
|
|
]);
|
|
|
|
Excel::import(new SalesImport($batch), $request->file('file'));
|
|
});
|
|
|
|
return redirect()->route('sales-imports.index')->with('success', '匯入成功,請確認內容。');
|
|
}
|
|
|
|
public function show(Request $request, SalesImportBatch $import)
|
|
{
|
|
$import->load(['items', 'importer']);
|
|
|
|
$perPage = $request->input('per_page', 10);
|
|
|
|
return Inertia::render('Sales/Import/Show', [
|
|
'import' => $import,
|
|
'items' => $import->items()->with(['product', 'warehouse'])->paginate($perPage)->withQueryString(),
|
|
'filters' => [
|
|
'per_page' => $perPage,
|
|
],
|
|
]);
|
|
}
|
|
|
|
public function confirm(SalesImportBatch $import, InventoryService $inventoryService)
|
|
{
|
|
if ($import->status !== 'pending') {
|
|
return back()->with('error', '此批次無法確認。');
|
|
}
|
|
|
|
DB::transaction(function () use ($import, $inventoryService) {
|
|
// 1. Prepare Aggregation
|
|
$aggregatedDeductions = []; // Key: "warehouse_id:product_id:slot"
|
|
|
|
// Pre-load necessary warehouses for matching
|
|
$machineIds = $import->items->pluck('machine_id')->filter()->unique();
|
|
$warehouses = \App\Modules\Inventory\Models\Warehouse::whereIn('code', $machineIds)->get()->keyBy('code');
|
|
|
|
foreach ($import->items as $item) {
|
|
// Only process shipped items with a valid product
|
|
if ($item->product_id && $item->original_status === '已出貨') {
|
|
// Resolve Warehouse from Machine ID
|
|
$warehouse = $warehouses->get($item->machine_id);
|
|
|
|
// Skip if machine_id is empty or warehouse not found
|
|
if (!$warehouse) {
|
|
continue;
|
|
}
|
|
|
|
// Aggregation Key includes Slot (貨道)
|
|
$slot = $item->slot ?: '';
|
|
$key = "{$warehouse->id}:{$item->product_id}:{$slot}";
|
|
|
|
if (!isset($aggregatedDeductions[$key])) {
|
|
$aggregatedDeductions[$key] = [
|
|
'warehouse_id' => $warehouse->id,
|
|
'product_id' => $item->product_id,
|
|
'slot' => $slot,
|
|
'quantity' => 0,
|
|
'details' => []
|
|
];
|
|
}
|
|
|
|
$aggregatedDeductions[$key]['quantity'] += $item->quantity;
|
|
$aggregatedDeductions[$key]['details'][] = $item->transaction_serial;
|
|
}
|
|
}
|
|
|
|
// 2. Execute Aggregated Deductions
|
|
foreach ($aggregatedDeductions as $deduction) {
|
|
// Construct a descriptive reason
|
|
$serialCount = count($deduction['details']);
|
|
$reason = "銷售出貨彙總 (批號: {$import->id}, 貨道: {$deduction['slot']}, 共 {$serialCount} 筆交易)";
|
|
|
|
$inventoryService->decreaseStock(
|
|
$deduction['product_id'],
|
|
$deduction['warehouse_id'],
|
|
$deduction['quantity'],
|
|
$reason,
|
|
true, // Force deduction
|
|
$deduction['slot'] // Location/Slot
|
|
);
|
|
}
|
|
|
|
// 3. Update Batch Status
|
|
$import->update([
|
|
'status' => 'confirmed',
|
|
'confirmed_at' => now(),
|
|
]);
|
|
});
|
|
|
|
return redirect()->route('sales-imports.index')->with('success', '已彙總(含貨道)並扣除庫存。');
|
|
}
|
|
|
|
public function destroy(SalesImportBatch $import)
|
|
{
|
|
if ($import->status !== 'pending') {
|
|
return back()->with('error', '只能刪除待確認的批次。');
|
|
}
|
|
|
|
$import->delete();
|
|
return redirect()->route('sales-imports.index')->with('success', '已刪除匯入批次。');
|
|
}
|
|
}
|