Files
star-erp/app/Modules/Inventory/Imports/InventoryImport.php

138 lines
4.9 KiB
PHP
Raw Normal View History

<?php
namespace App\Modules\Inventory\Imports;
use App\Modules\Inventory\Models\Product;
use App\Modules\Inventory\Models\Inventory;
use App\Modules\Inventory\Models\Warehouse;
use Maatwebsite\Excel\Concerns\ToModel;
use Maatwebsite\Excel\Concerns\WithHeadingRow;
use Maatwebsite\Excel\Concerns\WithValidation;
use Maatwebsite\Excel\Concerns\WithMapping;
use Maatwebsite\Excel\Concerns\SkipsEmptyRows;
use Maatwebsite\Excel\Imports\HeadingRowFormatter;
use Illuminate\Support\Facades\DB;
class InventoryImport implements ToModel, WithHeadingRow, WithValidation, WithMapping, SkipsEmptyRows
{
private $warehouse;
private $inboundDate;
private $notes;
public function __construct(Warehouse $warehouse, string $inboundDate, ?string $notes = null)
{
HeadingRowFormatter::default('none');
$this->warehouse = $warehouse;
$this->inboundDate = $inboundDate;
$this->notes = $notes;
}
public function map($row): array
{
// 處理條碼或代號為字串
if (isset($row['商品條碼'])) {
$row['商品條碼'] = (string) $row['商品條碼'];
}
if (isset($row['商品代號'])) {
$row['商品代號'] = (string) $row['商品代號'];
}
if (isset($row['儲位/貨道'])) {
$row['儲位/貨道'] = (string) $row['儲位/貨道'];
}
return $row;
}
public function model(array $row)
{
// 查找商品
$product = null;
if (!empty($row['商品條碼'])) {
$product = Product::where('barcode', $row['商品條碼'])->first();
}
if (!$product && !empty($row['商品代號'])) {
$product = Product::where('code', $row['商品代號'])->first();
}
if (!$product) {
return null; // 透過 Validation 攔截
}
$quantity = (float) $row['數量'];
$unitCost = isset($row['入庫單價']) ? (float) $row['入庫單價'] : ($product->cost_price ?? 0);
$location = $row['儲位/貨道'] ?? null;
// 批號邏輯:若 Excel 留空則使用 NO-BATCH
$batchNumber = !empty($row['批號']) ? $row['批號'] : 'NO-BATCH';
$originCountry = $row['產地'] ?? 'TW';
$expiryDate = !empty($row['效期']) ? $row['效期'] : null;
return DB::transaction(function () use ($product, $quantity, $unitCost, $location, $batchNumber, $originCountry, $expiryDate) {
// 使用與 InventoryController 相同的 firstOrNew 邏輯
$inventory = $this->warehouse->inventories()->withTrashed()->firstOrNew(
[
'product_id' => $product->id,
'batch_number' => $batchNumber,
'location' => $location, // 加入儲位/貨道作為區分關鍵字
],
[
'quantity' => 0,
'unit_cost' => $unitCost,
'total_value' => 0,
'arrival_date' => $this->inboundDate,
'expiry_date' => $expiryDate,
'origin_country' => $originCountry,
]
);
if ($inventory->trashed()) {
$inventory->restore();
}
// 更新數量
$oldQty = $inventory->quantity;
$inventory->quantity += $quantity;
// 更新單價與總價值
$inventory->unit_cost = $unitCost;
$inventory->total_value = $inventory->quantity * $unitCost;
$inventory->save();
// 記錄交易歷史
$inventory->transactions()->create([
'warehouse_id' => $this->warehouse->id,
'product_id' => $product->id,
'batch_number' => $inventory->batch_number,
'quantity' => $quantity,
'unit_cost' => $unitCost,
'type' => '手動入庫',
'reason' => 'Excel 匯入入庫',
'balance_before' => $oldQty,
'balance_after' => $inventory->quantity,
'actual_time' => $this->inboundDate,
'notes' => $this->notes,
'expiry_date' => $inventory->expiry_date,
]);
return $inventory;
});
}
public function rules(): array
{
return [
'商品條碼' => ['nullable', 'string'],
'商品代號' => ['nullable', 'string'],
'數量' => [
'required_with:商品條碼,商品代號', // 只有在有商品資訊時,數量才是必填
'numeric',
'min:0' // 允許數量為 0
],
'入庫單價' => ['nullable', 'numeric', 'min:0'],
'儲位/貨道' => ['nullable', 'string', 'max:50'],
'批號' => ['nullable', 'string'],
'效期' => ['nullable', 'date'],
'產地' => ['nullable', 'string', 'max:2'],
];
}
}