diff --git a/app/Modules/Inventory/Controllers/ProductController.php b/app/Modules/Inventory/Controllers/ProductController.php index 22a7319..a1d9a30 100644 --- a/app/Modules/Inventory/Controllers/ProductController.php +++ b/app/Modules/Inventory/Controllers/ProductController.php @@ -197,6 +197,10 @@ class ProductController extends Controller $validated['code'] = $this->generateRandomCode(); } + if (empty($validated['barcode'])) { + $validated['barcode'] = $this->generateRandomBarcode(); + } + $product = Product::create($validated); return redirect()->route('products.index')->with('success', '商品已建立'); @@ -260,6 +264,10 @@ class ProductController extends Controller $validated['code'] = $this->generateRandomCode(); } + if (empty($validated['barcode'])) { + $validated['barcode'] = $this->generateRandomBarcode(); + } + $product->update($validated); if ($request->input('from') === 'show') { @@ -328,4 +336,21 @@ class ProductController extends Controller return $code; } + + /** + * 生成隨機 13 碼條碼 (純數字) + */ + private function generateRandomBarcode(): string + { + $barcode = ''; + + do { + $barcode = ''; + for ($i = 0; $i < 13; $i++) { + $barcode .= rand(0, 9); + } + } while (Product::where('barcode', $barcode)->exists()); + + return $barcode; + } } diff --git a/app/Modules/Inventory/Exports/InstructionSheet.php b/app/Modules/Inventory/Exports/InstructionSheet.php new file mode 100644 index 0000000..d37ae65 --- /dev/null +++ b/app/Modules/Inventory/Exports/InstructionSheet.php @@ -0,0 +1,54 @@ + ['font' => ['bold' => true]], + // 欄位寬度自動 + ]; + } +} diff --git a/app/Modules/Inventory/Exports/ProductImportSheet.php b/app/Modules/Inventory/Exports/ProductImportSheet.php new file mode 100644 index 0000000..4b0570e --- /dev/null +++ b/app/Modules/Inventory/Exports/ProductImportSheet.php @@ -0,0 +1,43 @@ + NumberFormat::FORMAT_TEXT, // 商品代號 + 'B' => NumberFormat::FORMAT_TEXT, // 條碼 + ]; + } +} diff --git a/app/Modules/Inventory/Exports/ProductTemplateExport.php b/app/Modules/Inventory/Exports/ProductTemplateExport.php index 6d04e40..5321e74 100644 --- a/app/Modules/Inventory/Exports/ProductTemplateExport.php +++ b/app/Modules/Inventory/Exports/ProductTemplateExport.php @@ -2,39 +2,15 @@ namespace App\Modules\Inventory\Exports; -use Maatwebsite\Excel\Concerns\WithHeadings; +use Maatwebsite\Excel\Concerns\WithMultipleSheets; -use Maatwebsite\Excel\Concerns\WithColumnFormatting; -// use Maatwebsite\Excel\Concerns\WithHeadings; -use PhpOffice\PhpSpreadsheet\Style\NumberFormat; - -class ProductTemplateExport implements WithHeadings, WithColumnFormatting +class ProductTemplateExport implements WithMultipleSheets { - public function headings(): array + public function sheets(): array { return [ - '商品代號', - '條碼', - '商品名稱', - '類別名稱', - '品牌', - '規格', - '基本單位', - '大單位', - '換算率', - '成本價', - '售價', - '會員價', - '批發價', - - ]; - } - - public function columnFormats(): array - { - return [ - 'A' => NumberFormat::FORMAT_TEXT, // 商品代號 - 'B' => NumberFormat::FORMAT_TEXT, // 條碼 + new ProductImportSheet(), + new InstructionSheet(), ]; } } diff --git a/app/Modules/Inventory/Imports/ProductImport.php b/app/Modules/Inventory/Imports/ProductImport.php index 9836d32..4dafbaf 100644 --- a/app/Modules/Inventory/Imports/ProductImport.php +++ b/app/Modules/Inventory/Imports/ProductImport.php @@ -57,21 +57,25 @@ class ProductImport implements ToModel, WithHeadingRow, WithValidation, WithMapp $baseUnitId = $this->units[$row['基本單位']] ?? null; $largeUnitId = isset($row['大單位']) ? ($this->units[$row['大單位']] ?? null) : null; - // 若必要關聯找不到,理論上 Validation 會攔截,但此處做防禦性編程 if (!$categoryId || !$baseUnitId) { return null; } - // 處理商品代號:若為空則自動生成 $code = $row['商品代號'] ?? null; - if (empty($code)) { - $code = $this->generateRandomCode(); + $barcode = $row['條碼'] ?? null; + + // Upsert 邏輯:優先以條碼查找,次之以商品代號查找 + $product = null; + if (!empty($barcode)) { + $product = Product::where('barcode', $barcode)->first(); } - return new Product([ - 'code' => $code, - 'barcode' => $row['條碼'], + if (!$product && !empty($code)) { + $product = Product::where('code', $code)->first(); + } + + $data = [ 'name' => $row['商品名稱'], 'category_id' => $categoryId, 'brand' => $row['品牌'] ?? null, @@ -84,7 +88,26 @@ class ProductImport implements ToModel, WithHeadingRow, WithValidation, WithMapp 'price' => $row['售價'] ?? null, 'member_price' => $row['會員價'] ?? null, 'wholesale_price' => $row['批發價'] ?? null, - ]); + ]; + + if ($product) { + // 更新現有商品 + $product->update($data); + return null; // 返回 null 以避免 Maatwebsite/Excel 嘗試再次 insert + } + + // 建立新商品:處理代碼與條碼自動生成 + if (empty($code)) { + $code = $this->generateRandomCode(); + } + if (empty($barcode)) { + $barcode = $this->generateRandomBarcode(); + } + + $data['code'] = $code; + $data['barcode'] = $barcode; + + return new Product($data); } /** @@ -105,11 +128,28 @@ class ProductImport implements ToModel, WithHeadingRow, WithValidation, WithMapp return $code; } + /** + * 生成隨機 13 碼條碼 (純數字) + */ + private function generateRandomBarcode(): string + { + $barcode = ''; + + do { + $barcode = ''; + for ($i = 0; $i < 13; $i++) { + $barcode .= rand(0, 9); + } + } while (Product::where('barcode', $barcode)->exists()); + + return $barcode; + } + public function rules(): array { return [ - '商品代號' => ['nullable', 'string', 'min:2', 'max:8', 'unique:products,code'], - '條碼' => ['required', 'string', 'unique:products,barcode'], + '商品代號' => ['nullable', 'string', 'min:2', 'max:8'], + '條碼' => ['nullable', 'string'], '商品名稱' => ['required', 'string'], '類別名稱' => ['required', function($attribute, $value, $fail) { if (!isset($this->categories[$value])) { diff --git a/resources/js/Components/Product/ProductForm.tsx b/resources/js/Components/Product/ProductForm.tsx index 816198a..856dc2a 100644 --- a/resources/js/Components/Product/ProductForm.tsx +++ b/resources/js/Components/Product/ProductForm.tsx @@ -62,8 +62,11 @@ export default function ProductForm({ }; const generateRandomBarcode = () => { - const randomDigits = Math.floor(Math.random() * 9000000000) + 1000000000; - setData("barcode", randomDigits.toString()); + let result = ""; + for (let i = 0; i < 13; i++) { + result += Math.floor(Math.random() * 10).toString(); + } + setData("barcode", result); }; const generateRandomCode = () => { @@ -150,7 +153,7 @@ export default function ProductForm({