大更新
This commit is contained in:
@@ -10,6 +10,7 @@ class InventoryController extends Controller
|
||||
{
|
||||
$warehouse->load([
|
||||
'inventories.product.category',
|
||||
'inventories.product.baseUnit',
|
||||
'inventories.lastIncomingTransaction',
|
||||
'inventories.lastOutgoingTransaction'
|
||||
]);
|
||||
@@ -20,7 +21,7 @@ class InventoryController extends Controller
|
||||
return [
|
||||
'id' => (string) $product->id, // Frontend expects string
|
||||
'name' => $product->name,
|
||||
'type' => $product->category ? $product->category->name : '其他', // 暫時用 Category Name 當 Type
|
||||
'type' => $product->category?->name ?? '其他', // 暫時用 Category Name 當 Type
|
||||
];
|
||||
});
|
||||
|
||||
@@ -32,9 +33,9 @@ class InventoryController extends Controller
|
||||
'id' => (string) $inv->id,
|
||||
'warehouseId' => (string) $inv->warehouse_id,
|
||||
'productId' => (string) $inv->product_id,
|
||||
'productName' => $inv->product->name,
|
||||
'productCode' => $inv->product->code ?? 'N/A',
|
||||
'unit' => $inv->product->base_unit ?? '個',
|
||||
'productName' => $inv->product?->name ?? '未知商品',
|
||||
'productCode' => $inv->product?->code ?? 'N/A',
|
||||
'unit' => $inv->product?->baseUnit?->name ?? '個',
|
||||
'quantity' => (float) $inv->quantity,
|
||||
'safetyStock' => $inv->safety_stock !== null ? (float) $inv->safety_stock : null,
|
||||
'status' => '正常', // 前端會根據 quantity 與 safetyStock 重算,但後端亦可提供基礎狀態
|
||||
@@ -53,8 +54,8 @@ class InventoryController extends Controller
|
||||
'id' => 'ss-' . $inv->id,
|
||||
'warehouseId' => (string) $inv->warehouse_id,
|
||||
'productId' => (string) $inv->product_id,
|
||||
'productName' => $inv->product->name,
|
||||
'productType' => $inv->product->category ? $inv->product->category->name : '其他',
|
||||
'productName' => $inv->product?->name ?? '未知商品',
|
||||
'productType' => $inv->product?->category?->name ?? '其他',
|
||||
'safetyStock' => (float) $inv->safety_stock,
|
||||
'createdAt' => $inv->created_at->toIso8601String(),
|
||||
'updatedAt' => $inv->updated_at->toIso8601String(),
|
||||
@@ -72,11 +73,13 @@ class InventoryController extends Controller
|
||||
public function create(\App\Models\Warehouse $warehouse)
|
||||
{
|
||||
// 取得所有商品供前端選單使用
|
||||
$products = \App\Models\Product::select('id', 'name', 'base_unit')->get()->map(function ($product) {
|
||||
$products = \App\Models\Product::with(['baseUnit', 'largeUnit'])->select('id', 'name', 'base_unit_id', 'large_unit_id', 'conversion_rate')->get()->map(function ($product) {
|
||||
return [
|
||||
'id' => (string) $product->id,
|
||||
'name' => $product->name,
|
||||
'unit' => $product->base_unit,
|
||||
'baseUnit' => $product->baseUnit?->name ?? '個',
|
||||
'largeUnit' => $product->largeUnit?->name, // 可能為 null
|
||||
'conversionRate' => (float) $product->conversion_rate,
|
||||
];
|
||||
});
|
||||
|
||||
@@ -145,7 +148,7 @@ class InventoryController extends Controller
|
||||
'id' => (string) $inventory->id,
|
||||
'warehouseId' => (string) $inventory->warehouse_id,
|
||||
'productId' => (string) $inventory->product_id,
|
||||
'productName' => $inventory->product->name,
|
||||
'productName' => $inventory->product?->name ?? '未知商品',
|
||||
'quantity' => (float) $inventory->quantity,
|
||||
'batchNumber' => 'BATCH-' . $inventory->id, // Mock
|
||||
'expiryDate' => '2099-12-31', // Mock
|
||||
@@ -315,8 +318,8 @@ class InventoryController extends Controller
|
||||
'warehouse' => $warehouse,
|
||||
'inventory' => [
|
||||
'id' => (string) $inventory->id,
|
||||
'productName' => $inventory->product->name,
|
||||
'productCode' => $inventory->product->code,
|
||||
'productName' => $inventory->product?->name ?? '未知商品',
|
||||
'productCode' => $inventory->product?->code ?? 'N/A',
|
||||
'quantity' => (float) $inventory->quantity,
|
||||
],
|
||||
'transactions' => $transactions
|
||||
|
||||
@@ -54,7 +54,7 @@ class PurchaseOrderController extends Controller
|
||||
|
||||
public function create()
|
||||
{
|
||||
$vendors = Vendor::with('products')->get()->map(function ($vendor) {
|
||||
$vendors = Vendor::with(['products.baseUnit', 'products.largeUnit', 'products.purchaseUnit'])->get()->map(function ($vendor) {
|
||||
return [
|
||||
'id' => (string) $vendor->id,
|
||||
'name' => $vendor->name,
|
||||
@@ -62,9 +62,11 @@ class PurchaseOrderController extends Controller
|
||||
return [
|
||||
'productId' => (string) $product->id,
|
||||
'productName' => $product->name,
|
||||
'unit' => $product->purchase_unit ?: ($product->large_unit ?: $product->base_unit), // 優先使用採購單位 > 大單位 > 基本單位
|
||||
'base_unit' => $product->base_unit,
|
||||
'purchase_unit' => $product->purchase_unit ?: $product->large_unit, // 若無採購單位,預設為大單位
|
||||
'base_unit_id' => $product->base_unit_id,
|
||||
'base_unit_name' => $product->baseUnit?->name,
|
||||
'large_unit_id' => $product->large_unit_id,
|
||||
'large_unit_name' => $product->largeUnit?->name,
|
||||
'purchase_unit_id' => $product->purchase_unit_id,
|
||||
'conversion_rate' => (float) $product->conversion_rate,
|
||||
'lastPrice' => (float) ($product->pivot->last_price ?? 0),
|
||||
];
|
||||
@@ -96,6 +98,7 @@ class PurchaseOrderController extends Controller
|
||||
'items.*.productId' => 'required|exists:products,id',
|
||||
'items.*.quantity' => 'required|numeric|min:0.01',
|
||||
'items.*.unitPrice' => 'required|numeric|min:0',
|
||||
'items.*.unitId' => 'nullable|exists:units,id', // 驗證單位ID
|
||||
]);
|
||||
|
||||
try {
|
||||
@@ -157,6 +160,7 @@ class PurchaseOrderController extends Controller
|
||||
$order->items()->create([
|
||||
'product_id' => $item['productId'],
|
||||
'quantity' => $item['quantity'],
|
||||
'unit_id' => $item['unitId'] ?? null, // 儲存單位ID
|
||||
'unit_price' => $item['unitPrice'],
|
||||
'subtotal' => $item['quantity'] * $item['unitPrice'],
|
||||
]);
|
||||
@@ -174,20 +178,39 @@ class PurchaseOrderController extends Controller
|
||||
|
||||
public function show($id)
|
||||
{
|
||||
$order = PurchaseOrder::with(['vendor', 'warehouse', 'user', 'items.product'])->findOrFail($id);
|
||||
$order = PurchaseOrder::with(['vendor', 'warehouse', 'user', 'items.product.baseUnit', 'items.product.largeUnit'])->findOrFail($id);
|
||||
|
||||
// Transform items to include product details needed for frontend calculation
|
||||
$order->items->transform(function ($item) {
|
||||
$order->items->transform(function ($item) use ($order) {
|
||||
$product = $item->product;
|
||||
if ($product) {
|
||||
// 手動附加 productName 和 unit (因為已從 $appends 移除)
|
||||
// 手動附加所有必要的屬性
|
||||
$item->productId = (string) $product->id;
|
||||
$item->productName = $product->name;
|
||||
$item->productId = $product->id;
|
||||
$item->base_unit = $product->base_unit;
|
||||
$item->purchase_unit = $product->purchase_unit ?: $product->large_unit; // Fallback logic same as Create
|
||||
$item->base_unit_id = $product->base_unit_id;
|
||||
$item->base_unit_name = $product->baseUnit?->name;
|
||||
$item->large_unit_id = $product->large_unit_id;
|
||||
$item->large_unit_name = $product->largeUnit?->name;
|
||||
$item->purchase_unit_id = $product->purchase_unit_id;
|
||||
|
||||
$item->conversion_rate = (float) $product->conversion_rate;
|
||||
// 優先使用採購單位 > 大單位 > 基本單位
|
||||
$item->unit = $product->purchase_unit ?: ($product->large_unit ?: $product->base_unit);
|
||||
|
||||
// Fetch last price
|
||||
$lastPrice = DB::table('product_vendor')
|
||||
->where('vendor_id', $order->vendor_id)
|
||||
->where('product_id', $product->id)
|
||||
->value('last_price');
|
||||
$item->previousPrice = (float) ($lastPrice ?? 0);
|
||||
|
||||
// 設定當前選中的單位 ID (from saved item)
|
||||
$item->unitId = $item->unit_id;
|
||||
|
||||
// 決定 selectedUnit (用於 UI 顯示)
|
||||
if ($item->unitId && $item->large_unit_id && $item->unitId == $item->large_unit_id) {
|
||||
$item->selectedUnit = 'large';
|
||||
} else {
|
||||
$item->selectedUnit = 'base';
|
||||
}
|
||||
|
||||
$item->unitPrice = (float) $item->unit_price;
|
||||
}
|
||||
return $item;
|
||||
@@ -202,7 +225,7 @@ class PurchaseOrderController extends Controller
|
||||
{
|
||||
$order = PurchaseOrder::with(['items.product'])->findOrFail($id);
|
||||
|
||||
$vendors = Vendor::with('products')->get()->map(function ($vendor) {
|
||||
$vendors = Vendor::with(['products.baseUnit', 'products.largeUnit', 'products.purchaseUnit'])->get()->map(function ($vendor) {
|
||||
return [
|
||||
'id' => (string) $vendor->id,
|
||||
'name' => $vendor->name,
|
||||
@@ -210,9 +233,11 @@ class PurchaseOrderController extends Controller
|
||||
return [
|
||||
'productId' => (string) $product->id,
|
||||
'productName' => $product->name,
|
||||
'unit' => $product->purchase_unit ?: ($product->large_unit ?: $product->base_unit),
|
||||
'base_unit' => $product->base_unit,
|
||||
'purchase_unit' => $product->purchase_unit ?: $product->large_unit,
|
||||
'base_unit_id' => $product->base_unit_id,
|
||||
'base_unit_name' => $product->baseUnit?->name,
|
||||
'large_unit_id' => $product->large_unit_id,
|
||||
'large_unit_name' => $product->largeUnit?->name,
|
||||
'purchase_unit_id' => $product->purchase_unit_id,
|
||||
'conversion_rate' => (float) $product->conversion_rate,
|
||||
'lastPrice' => (float) ($product->pivot->last_price ?? 0),
|
||||
];
|
||||
@@ -228,17 +253,38 @@ class PurchaseOrderController extends Controller
|
||||
});
|
||||
|
||||
// Transform items for frontend form
|
||||
$order->items->transform(function ($item) {
|
||||
// Transform items for frontend form
|
||||
$vendorId = $order->vendor_id;
|
||||
$order->items->transform(function ($item) use ($vendorId) {
|
||||
$product = $item->product;
|
||||
if ($product) {
|
||||
// 手動附加所有必要的屬性 (因為已從 $appends 移除)
|
||||
$item->productId = (string) $product->id; // Ensure consistent ID type
|
||||
// 手動附加所有必要的屬性
|
||||
$item->productId = (string) $product->id;
|
||||
$item->productName = $product->name;
|
||||
$item->base_unit = $product->base_unit;
|
||||
$item->purchase_unit = $product->purchase_unit ?: $product->large_unit;
|
||||
$item->base_unit_id = $product->base_unit_id;
|
||||
$item->base_unit_name = $product->baseUnit?->name;
|
||||
$item->large_unit_id = $product->large_unit_id;
|
||||
$item->large_unit_name = $product->largeUnit?->name;
|
||||
|
||||
$item->conversion_rate = (float) $product->conversion_rate;
|
||||
// 優先使用採購單位 > 大單位 > 基本單位
|
||||
$item->unit = $product->purchase_unit ?: ($product->large_unit ?: $product->base_unit);
|
||||
|
||||
// Fetch last price
|
||||
$lastPrice = DB::table('product_vendor')
|
||||
->where('vendor_id', $vendorId)
|
||||
->where('product_id', $product->id)
|
||||
->value('last_price');
|
||||
$item->previousPrice = (float) ($lastPrice ?? 0);
|
||||
|
||||
// 設定當前選中的單位 ID
|
||||
$item->unitId = $item->unit_id; // 資料庫中的 unit_id
|
||||
|
||||
// 決定 selectedUnit (用於 UI 狀態)
|
||||
if ($item->unitId && $item->large_unit_id && $item->unitId == $item->large_unit_id) {
|
||||
$item->selectedUnit = 'large';
|
||||
} else {
|
||||
$item->selectedUnit = 'base';
|
||||
}
|
||||
|
||||
$item->unitPrice = (float) $item->unit_price;
|
||||
}
|
||||
return $item;
|
||||
@@ -265,6 +311,7 @@ class PurchaseOrderController extends Controller
|
||||
'items.*.productId' => 'required|exists:products,id',
|
||||
'items.*.quantity' => 'required|numeric|min:0.01',
|
||||
'items.*.unitPrice' => 'required|numeric|min:0',
|
||||
'items.*.unitId' => 'nullable|exists:units,id', // 驗證單位ID
|
||||
]);
|
||||
|
||||
try {
|
||||
@@ -296,6 +343,7 @@ class PurchaseOrderController extends Controller
|
||||
$order->items()->create([
|
||||
'product_id' => $item['productId'],
|
||||
'quantity' => $item['quantity'],
|
||||
'unit_id' => $item['unitId'] ?? null, // 儲存單位ID
|
||||
'unit_price' => $item['unitPrice'],
|
||||
'subtotal' => $item['quantity'] * $item['unitPrice'],
|
||||
]);
|
||||
|
||||
@@ -18,7 +18,7 @@ class SafetyStockController extends Controller
|
||||
{
|
||||
$warehouse->load(['inventories.product.category']);
|
||||
|
||||
$allProducts = Product::with('category')->get();
|
||||
$allProducts = Product::with(['category', 'baseUnit'])->get();
|
||||
|
||||
// 準備可選商品列表
|
||||
$availableProducts = $allProducts->map(function ($product) {
|
||||
@@ -26,7 +26,7 @@ class SafetyStockController extends Controller
|
||||
'id' => (string) $product->id,
|
||||
'name' => $product->name,
|
||||
'type' => $product->category ? $product->category->name : '其他',
|
||||
'unit' => $product->base_unit,
|
||||
'unit' => $product->baseUnit?->name ?? '個',
|
||||
];
|
||||
});
|
||||
|
||||
@@ -51,7 +51,7 @@ class SafetyStockController extends Controller
|
||||
'productName' => $inv->product->name,
|
||||
'productType' => $inv->product->category ? $inv->product->category->name : '其他',
|
||||
'safetyStock' => (float) $inv->safety_stock,
|
||||
'unit' => $inv->product->base_unit,
|
||||
'unit' => $inv->product->baseUnit?->name ?? '個',
|
||||
'updatedAt' => $inv->updated_at->toIso8601String(),
|
||||
];
|
||||
})->values();
|
||||
|
||||
@@ -97,7 +97,7 @@ class TransferOrderController extends Controller
|
||||
public function getWarehouseInventories(Warehouse $warehouse)
|
||||
{
|
||||
$inventories = $warehouse->inventories()
|
||||
->with(['product:id,name,base_unit,category_id', 'product.category'])
|
||||
->with(['product.baseUnit', 'product.category'])
|
||||
->where('quantity', '>', 0) // 只回傳有庫存的
|
||||
->get()
|
||||
->map(function ($inv) {
|
||||
@@ -106,7 +106,7 @@ class TransferOrderController extends Controller
|
||||
'productName' => $inv->product->name,
|
||||
'batchNumber' => 'BATCH-' . $inv->id, // 模擬批號
|
||||
'availableQty' => (float) $inv->quantity,
|
||||
'unit' => $inv->product->base_unit,
|
||||
'unit' => $inv->product->baseUnit?->name ?? '個',
|
||||
];
|
||||
});
|
||||
|
||||
|
||||
@@ -51,10 +51,10 @@ class VendorController extends Controller
|
||||
*/
|
||||
public function show(Vendor $vendor): \Inertia\Response
|
||||
{
|
||||
$vendor->load('products');
|
||||
$vendor->load(['products.baseUnit', 'products.largeUnit']);
|
||||
return \Inertia\Inertia::render('Vendor/Show', [
|
||||
'vendor' => $vendor,
|
||||
'products' => \App\Models\Product::all(),
|
||||
'products' => \App\Models\Product::with('baseUnit')->get(),
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
@@ -72,9 +72,25 @@ class WarehouseController extends Controller
|
||||
|
||||
public function destroy(Warehouse $warehouse)
|
||||
{
|
||||
// 真實刪除
|
||||
$warehouse->delete();
|
||||
// 檢查是否有相關聯的採購單
|
||||
if ($warehouse->purchaseOrders()->exists()) {
|
||||
return redirect()->back()->with('error', '無法刪除:該倉庫有相關聯的採購單,請先處理採購單。');
|
||||
}
|
||||
|
||||
return redirect()->back()->with('success', '倉庫已刪除');
|
||||
\Illuminate\Support\Facades\DB::transaction(function () use ($warehouse) {
|
||||
// 刪除庫存異動紀錄 (透過庫存關聯)
|
||||
foreach ($warehouse->inventories as $inventory) {
|
||||
// 刪除該庫存的所有異動紀錄
|
||||
$inventory->transactions()->delete();
|
||||
}
|
||||
|
||||
// 刪除庫存項目
|
||||
$warehouse->inventories()->delete();
|
||||
|
||||
// 刪除倉庫
|
||||
$warehouse->delete();
|
||||
});
|
||||
|
||||
return redirect()->back()->with('success', '倉庫及其庫存與紀錄已刪除');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ class PurchaseOrderItem extends Model
|
||||
'purchase_order_id',
|
||||
'product_id',
|
||||
'quantity',
|
||||
'unit_id', // 新增單位ID欄位
|
||||
'unit_price',
|
||||
'subtotal',
|
||||
'received_quantity',
|
||||
@@ -26,25 +27,33 @@ class PurchaseOrderItem extends Model
|
||||
'received_quantity' => 'decimal:2',
|
||||
];
|
||||
|
||||
// 移除 $appends 以避免自動附加導致的錯誤
|
||||
// 這些屬性將在 Controller 中需要時手動附加
|
||||
// protected $appends = ['productName', 'unit'];
|
||||
|
||||
public function getProductNameAttribute(): string
|
||||
{
|
||||
return $this->product?->name ?? '';
|
||||
}
|
||||
|
||||
public function getUnitAttribute(): string
|
||||
|
||||
// 關聯單位
|
||||
public function unit(): \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
{
|
||||
// 優先使用採購單位 > 大單位 > 基本單位
|
||||
// 與 PurchaseOrderController 的邏輯保持一致
|
||||
return $this->belongsTo(Unit::class);
|
||||
}
|
||||
|
||||
public function getUnitNameAttribute(): string
|
||||
{
|
||||
// 優先使用關聯的 unit
|
||||
if ($this->unit) {
|
||||
return $this->unit->name;
|
||||
}
|
||||
|
||||
if (!$this->product) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return $this->product->purchase_unit
|
||||
?: ($this->product->large_unit ?: $this->product->base_unit);
|
||||
// Fallback: 嘗試從 Product 的關聯單位獲取
|
||||
return $this->product->purchaseUnit?->name
|
||||
?? $this->product->largeUnit?->name
|
||||
?? $this->product->baseUnit?->name
|
||||
?? '';
|
||||
}
|
||||
|
||||
public function purchaseOrder(): BelongsTo
|
||||
|
||||
Reference in New Issue
Block a user