feat: 整合門市領料日誌、API 文件存取、修改庫存與併發編號問題、供應商商品內聯編輯及日誌 UI 優化
All checks were successful
ERP-Deploy-Demo / deploy-demo (push) Successful in 1m0s
All checks were successful
ERP-Deploy-Demo / deploy-demo (push) Successful in 1m0s
This commit is contained in:
@@ -26,8 +26,7 @@ class InventoryImport implements ToModel, WithHeadingRow, WithValidation, WithMa
|
||||
|
||||
// 修正時間精度:將選定的日期與「現在的時分秒」結合
|
||||
// 這樣既能保留使用者選的日期,又能提供精確的紀錄時點排順序
|
||||
$currentTime = date('H:i:s');
|
||||
$this->inboundDate = $inboundDate . ' ' . $currentTime;
|
||||
$this->inboundDate = \Illuminate\Support\Carbon::parse($inboundDate)->setTimeFrom(now())->toDateTimeString();
|
||||
|
||||
$this->notes = $notes;
|
||||
}
|
||||
|
||||
@@ -9,10 +9,43 @@ use Illuminate\Validation\Rule;
|
||||
use Maatwebsite\Excel\Concerns\ToModel;
|
||||
use Maatwebsite\Excel\Concerns\WithHeadingRow;
|
||||
use Maatwebsite\Excel\Concerns\WithMapping;
|
||||
use Maatwebsite\Excel\Concerns\WithMultipleSheets;
|
||||
use Maatwebsite\Excel\Concerns\WithValidation;
|
||||
use Maatwebsite\Excel\Concerns\SkipsEmptyRows;
|
||||
use Maatwebsite\Excel\Imports\HeadingRowFormatter;
|
||||
|
||||
class ProductImport implements ToModel, WithHeadingRow, WithValidation, WithMapping
|
||||
/**
|
||||
* 商品匯入主類別
|
||||
*
|
||||
* 實作 WithMultipleSheets 以限定只讀取第一個工作表(資料頁),
|
||||
* 跳過第二個工作表(填寫說明頁),避免說明頁的資料被誤匯入並觸發驗證錯誤。
|
||||
*/
|
||||
class ProductImport implements WithMultipleSheets
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
// 禁用標題格式化,保留中文標題
|
||||
HeadingRowFormatter::default('none');
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定只處理第一個工作表 (index 0)
|
||||
*/
|
||||
public function sheets(): array
|
||||
{
|
||||
return [
|
||||
0 => new ProductDataSheetImport(),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 商品匯入 - 資料工作表處理類別
|
||||
*
|
||||
* 負責實際的資料解析、驗證與儲存邏輯。
|
||||
* 只會被套用到 Excel 的第一個工作表(資料頁)。
|
||||
*/
|
||||
class ProductDataSheetImport implements ToModel, WithHeadingRow, WithValidation, WithMapping, SkipsEmptyRows
|
||||
{
|
||||
private $categories;
|
||||
private $units;
|
||||
@@ -20,9 +53,6 @@ class ProductImport implements ToModel, WithHeadingRow, WithValidation, WithMapp
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
// 禁用標題格式化,保留中文標題
|
||||
HeadingRowFormatter::default('none');
|
||||
|
||||
// 快取所有類別與單位,避免 N+1 查詢
|
||||
$this->categories = Category::pluck('id', 'name');
|
||||
$this->units = Unit::pluck('id', 'name');
|
||||
@@ -30,28 +60,37 @@ class ProductImport implements ToModel, WithHeadingRow, WithValidation, WithMapp
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $row
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
* 資料映射:將 Excel 原始標題(含「(選填)」)對應到乾淨的鍵名
|
||||
*
|
||||
* 注意:WithValidation 驗證的是 map() 之前的原始資料,
|
||||
* 因此 rules() 中的鍵名必須匹配 Excel 的原始標題。
|
||||
* map() 的返回值只影響 model() 接收到的資料。
|
||||
*/
|
||||
public function map($row): array
|
||||
{
|
||||
// 強制將代號與條碼轉為字串,避免純數字被當作整數處理導致 max:5 驗證錯誤
|
||||
if (isset($row['商品代號'])) {
|
||||
$row['商品代號'] = (string) $row['商品代號'];
|
||||
}
|
||||
if (isset($row['條碼'])) {
|
||||
$row['條碼'] = (string) $row['條碼'];
|
||||
}
|
||||
|
||||
return $row;
|
||||
$code = $row['商品代號(選填)'] ?? $row['商品代號'] ?? null;
|
||||
$barcode = $row['條碼(選填)'] ?? $row['條碼'] ?? null;
|
||||
|
||||
return [
|
||||
'商品代號' => $code !== null ? (string)$code : null,
|
||||
'條碼' => $barcode !== null ? (string)$barcode : null,
|
||||
'商品名稱' => $row['商品名稱'] ?? null,
|
||||
'類別名稱' => $row['類別名稱'] ?? null,
|
||||
'品牌' => $row['品牌'] ?? null,
|
||||
'規格' => $row['規格'] ?? null,
|
||||
'基本單位' => $row['基本單位'] ?? null,
|
||||
'大單位' => $row['大單位'] ?? null,
|
||||
'換算率' => isset($row['換算率']) ? (float)$row['換算率'] : null,
|
||||
'成本價' => isset($row['成本價']) ? (float)$row['成本價'] : null,
|
||||
'售價' => isset($row['售價']) ? (float)$row['售價'] : null,
|
||||
'會員價' => isset($row['會員價']) ? (float)$row['會員價'] : null,
|
||||
'批發價' => isset($row['批發價']) ? (float)$row['批發價'] : null,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $row
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Model|null
|
||||
*/
|
||||
* @param array $row (map() 回傳的乾淨鍵名陣列)
|
||||
*/
|
||||
public function model(array $row)
|
||||
{
|
||||
// 查找關聯 ID
|
||||
@@ -96,13 +135,17 @@ class ProductImport implements ToModel, WithHeadingRow, WithValidation, WithMapp
|
||||
return null; // 返回 null,因為 Service 已經處理完儲存
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 驗證規則
|
||||
*
|
||||
* 鍵名必須匹配 Excel 原始標題(含「(選填)」後綴),
|
||||
* 因為 WithValidation 驗證的是 map() 之前的原始資料。
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'商品代號' => ['nullable', 'string', 'min:2', 'max:8'],
|
||||
'條碼' => ['nullable', 'string'],
|
||||
'商品代號(選填)' => ['nullable', 'string', 'min:2', 'max:8'],
|
||||
'條碼(選填)' => ['nullable', 'string'],
|
||||
'商品名稱' => ['required', 'string'],
|
||||
'類別名稱' => ['required', function($attribute, $value, $fail) {
|
||||
if (!isset($this->categories[$value])) {
|
||||
@@ -127,4 +170,16 @@ class ProductImport implements ToModel, WithHeadingRow, WithValidation, WithMapp
|
||||
'批發價' => ['nullable', 'numeric', 'min:0'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 自訂驗證錯誤訊息的欄位名稱
|
||||
* 把含 "(選填)" 後綴的欄位顯示為友善名稱
|
||||
*/
|
||||
public function customValidationAttributes(): array
|
||||
{
|
||||
return [
|
||||
'商品代號(選填)' => '商品代號',
|
||||
'條碼(選填)' => '條碼',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user