feat(inventory): 強化調撥單功能,支援販賣機貨道欄位、開放商品重複加入及優化過帳庫存檢核
This commit is contained in:
131
app/Modules/Inventory/Imports/InventoryTransferItemImport.php
Normal file
131
app/Modules/Inventory/Imports/InventoryTransferItemImport.php
Normal file
@@ -0,0 +1,131 @@
|
||||
<?php
|
||||
|
||||
namespace App\Modules\Inventory\Imports;
|
||||
|
||||
use App\Modules\Inventory\Models\InventoryTransferItem;
|
||||
use App\Modules\Inventory\Models\InventoryTransferOrder;
|
||||
use App\Modules\Inventory\Models\Product;
|
||||
use Illuminate\Support\Collection;
|
||||
use Maatwebsite\Excel\Concerns\ToCollection;
|
||||
use Maatwebsite\Excel\Concerns\WithHeadingRow;
|
||||
use Maatwebsite\Excel\Concerns\WithMultipleSheets;
|
||||
use Exception;
|
||||
|
||||
class InventoryTransferItemImport implements ToCollection, WithMultipleSheets
|
||||
{
|
||||
protected $transferOrder;
|
||||
|
||||
public function __construct(InventoryTransferOrder $transferOrder)
|
||||
{
|
||||
$this->transferOrder = $transferOrder;
|
||||
}
|
||||
|
||||
public function collection(Collection $rows)
|
||||
{
|
||||
if ($rows->isEmpty()) {
|
||||
throw new Exception("檔案中沒有資料。");
|
||||
}
|
||||
|
||||
// 移除標題列並解析索引
|
||||
$headerRow = $rows->shift();
|
||||
$headers = $headerRow->toArray();
|
||||
|
||||
// 建立標題對應索引 (支援中文與英文)
|
||||
$colMap = [
|
||||
'product_code' => -1,
|
||||
'batch_number' => -1,
|
||||
'quantity' => -1,
|
||||
'position' => -1,
|
||||
'notes' => -1,
|
||||
];
|
||||
|
||||
foreach ($headers as $index => $label) {
|
||||
$label = trim((string)$label);
|
||||
if (in_array($label, ['商品代碼', 'product_code', 'shang_pin_dai_ma'])) $colMap['product_code'] = $index;
|
||||
if (in_array($label, ['批號', 'batch_number', 'pi_hao'])) $colMap['batch_number'] = $index;
|
||||
if (in_array($label, ['數量', 'quantity', 'shu_liang'])) $colMap['quantity'] = $index;
|
||||
if (in_array($label, ['貨道/儲位', '貨道', 'position', 'slot', 'huo_dao'])) $colMap['position'] = $index;
|
||||
if (in_array($label, ['備註', 'notes', 'bei_zhu'])) $colMap['notes'] = $index;
|
||||
}
|
||||
|
||||
// 檢查必要欄位是否有找到
|
||||
if ($colMap['product_code'] === -1 || $colMap['quantity'] === -1) {
|
||||
$foundHeaders = implode(', ', array_filter($headers));
|
||||
throw new Exception("找不到必要的欄位「商品代碼」或「數量」。讀取到的標題為:{$foundHeaders}。請確認使用的是正確的範本。");
|
||||
}
|
||||
|
||||
// 預先載入商品 (優化效能)
|
||||
$productCodes = $rows->map(fn($row) => trim((string)($row[$colMap['product_code']] ?? '')))->filter()->unique()->toArray();
|
||||
$products = Product::whereIn('code', $productCodes)->get()->keyBy('code');
|
||||
|
||||
$newItems = [];
|
||||
$errors = [];
|
||||
|
||||
foreach ($rows as $index => $row) {
|
||||
$productCode = trim((string)($row[$colMap['product_code']] ?? ''));
|
||||
$quantity = $row[$colMap['quantity']] ?? null;
|
||||
$batchNumber = $colMap['batch_number'] !== -1 ? trim((string)($row[$colMap['batch_number']] ?? '')) : '';
|
||||
$position = $colMap['position'] !== -1 ? trim((string)($row[$colMap['position']] ?? '')) : null;
|
||||
$notes = $colMap['notes'] !== -1 ? ($row[$colMap['notes']] ?? null) : null;
|
||||
|
||||
// 跳過全空行
|
||||
if (empty($productCode) && ($quantity === null || $quantity === '')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$lineNum = $index + 2; // 因為 shift 過,且 Excel 從 1 開始
|
||||
|
||||
if (empty($productCode)) {
|
||||
$errors[] = "第 {$lineNum} 行:商品代碼不能為空";
|
||||
continue;
|
||||
}
|
||||
|
||||
$product = $products->get($productCode);
|
||||
if (!$product) {
|
||||
$errors[] = "第 {$lineNum} 行:找不到商品代碼 '{$productCode}'";
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!is_numeric($quantity) || (float)$quantity <= 0) {
|
||||
$errors[] = "第 {$lineNum} 行:數量必須為大於 0 的數字 (目前值: " . ($quantity ?? '空') . ")";
|
||||
continue;
|
||||
}
|
||||
|
||||
if (empty($batchNumber)) {
|
||||
$batchNumber = 'NO-BATCH';
|
||||
}
|
||||
|
||||
$newItems[] = [
|
||||
'transfer_order_id' => $this->transferOrder->id,
|
||||
'product_id' => $product->id,
|
||||
'batch_number' => $batchNumber,
|
||||
'quantity' => (float)$quantity,
|
||||
'position' => $position,
|
||||
'notes' => $notes,
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
];
|
||||
}
|
||||
|
||||
if (count($errors) > 0) {
|
||||
throw new Exception(implode("\n", $errors));
|
||||
}
|
||||
|
||||
if (count($newItems) === 0) {
|
||||
throw new Exception("檔案中沒有可匯入的有效資料。");
|
||||
}
|
||||
|
||||
InventoryTransferItem::insert($newItems);
|
||||
$this->transferOrder->touch();
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定只匯入第一個分頁 (明細匯入)
|
||||
*/
|
||||
public function sheets(): array
|
||||
{
|
||||
return [
|
||||
0 => $this,
|
||||
];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user