diff --git a/app/Modules/Inventory/Models/InventoryAdjustDoc.php b/app/Modules/Inventory/Models/InventoryAdjustDoc.php
index 4b307fc..87ebc5c 100644
--- a/app/Modules/Inventory/Models/InventoryAdjustDoc.php
+++ b/app/Modules/Inventory/Models/InventoryAdjustDoc.php
@@ -36,7 +36,7 @@ class InventoryAdjustDoc extends Model
static::creating(function ($model) {
if (empty($model->doc_no)) {
$today = date('Ymd');
- $prefix = 'ADJ' . $today;
+ $prefix = 'ADJ-' . $today . '-';
$lastDoc = static::where('doc_no', 'like', $prefix . '%')
->orderBy('doc_no', 'desc')
diff --git a/app/Modules/Inventory/Models/InventoryCountDoc.php b/app/Modules/Inventory/Models/InventoryCountDoc.php
index 3ee6dec..c41973f 100644
--- a/app/Modules/Inventory/Models/InventoryCountDoc.php
+++ b/app/Modules/Inventory/Models/InventoryCountDoc.php
@@ -36,7 +36,7 @@ class InventoryCountDoc extends Model
static::creating(function ($model) {
if (empty($model->doc_no)) {
$today = date('Ymd');
- $prefix = 'CNT' . $today;
+ $prefix = 'CNT-' . $today . '-';
// 查詢當天編號最大的單據
$lastDoc = static::where('doc_no', 'like', $prefix . '%')
diff --git a/app/Modules/Inventory/Models/InventoryTransferOrder.php b/app/Modules/Inventory/Models/InventoryTransferOrder.php
index d9cc54a..f6cb0c7 100644
--- a/app/Modules/Inventory/Models/InventoryTransferOrder.php
+++ b/app/Modules/Inventory/Models/InventoryTransferOrder.php
@@ -35,7 +35,7 @@ class InventoryTransferOrder extends Model
static::creating(function ($model) {
if (empty($model->doc_no)) {
$today = date('Ymd');
- $prefix = 'TRF' . $today;
+ $prefix = 'TRF-' . $today . '-';
$lastDoc = static::where('doc_no', 'like', $prefix . '%')
->orderBy('doc_no', 'desc')
diff --git a/app/Modules/Inventory/Services/GoodsReceiptService.php b/app/Modules/Inventory/Services/GoodsReceiptService.php
index 139527d..fb2dd9c 100644
--- a/app/Modules/Inventory/Services/GoodsReceiptService.php
+++ b/app/Modules/Inventory/Services/GoodsReceiptService.php
@@ -90,8 +90,8 @@ class GoodsReceiptService
private function generateCode(string $date)
{
- // Format: GR + YYYYMMDD + NNN
- $prefix = 'GR' . date('Ymd', strtotime($date));
+ // Format: GR-YYYYMMDD-NN
+ $prefix = 'GR-' . date('Ymd', strtotime($date)) . '-';
$last = GoodsReceipt::where('code', 'like', $prefix . '%')
->orderBy('id', 'desc')
@@ -99,11 +99,11 @@ class GoodsReceiptService
->first();
if ($last) {
- $seq = intval(substr($last->code, -3)) + 1;
+ $seq = intval(substr($last->code, -2)) + 1;
} else {
$seq = 1;
}
- return $prefix . str_pad($seq, 3, '0', STR_PAD_LEFT);
+ return $prefix . str_pad($seq, 2, '0', STR_PAD_LEFT);
}
}
diff --git a/app/Modules/Procurement/Controllers/PurchaseOrderController.php b/app/Modules/Procurement/Controllers/PurchaseOrderController.php
index 68909f6..e82d538 100644
--- a/app/Modules/Procurement/Controllers/PurchaseOrderController.php
+++ b/app/Modules/Procurement/Controllers/PurchaseOrderController.php
@@ -187,20 +187,20 @@ class PurchaseOrderController extends Controller
try {
DB::beginTransaction();
- // 生成單號:POYYYYMMDD001
+ // 生成單號:PO-YYYYMMDD-01
$today = now()->format('Ymd');
- $prefix = 'PO' . $today;
+ $prefix = 'PO-' . $today . '-';
$lastOrder = PurchaseOrder::where('code', 'like', $prefix . '%')
->lockForUpdate() // 鎖定以避免並發衝突
->orderBy('code', 'desc')
->first();
if ($lastOrder) {
- // 取得最後 3 碼序號並加 1
- $lastSequence = intval(substr($lastOrder->code, -3));
- $sequence = str_pad($lastSequence + 1, 3, '0', STR_PAD_LEFT);
+ // 取得最後 2 碼序號並加 1
+ $lastSequence = intval(substr($lastOrder->code, -2));
+ $sequence = str_pad($lastSequence + 1, 2, '0', STR_PAD_LEFT);
} else {
- $sequence = '001';
+ $sequence = '01';
}
$code = $prefix . $sequence;
diff --git a/app/Modules/Production/Controllers/ProductionOrderController.php b/app/Modules/Production/Controllers/ProductionOrderController.php
index 0ea7135..41af550 100644
--- a/app/Modules/Production/Controllers/ProductionOrderController.php
+++ b/app/Modules/Production/Controllers/ProductionOrderController.php
@@ -269,6 +269,33 @@ class ProductionOrderController extends Controller
return response()->json($data);
}
+ /**
+ * 取得商品在各倉庫的庫存分佈
+ */
+ public function getProductWarehouses($productId)
+ {
+ $inventories = \App\Modules\Inventory\Models\Inventory::with(['warehouse', 'product.baseUnit'])
+ ->where('product_id', $productId)
+ ->where('quantity', '>', 0)
+ ->get();
+
+ $data = $inventories->map(function ($inv) {
+ return [
+ 'id' => $inv->id, // Inventory ID
+ 'warehouse_id' => $inv->warehouse_id,
+ 'warehouse_name' => $inv->warehouse->name ?? '未知倉庫',
+ 'batch_number' => $inv->batch_number,
+ 'quantity' => $inv->quantity,
+ 'expiry_date' => $inv->expiry_date ? $inv->expiry_date->format('Y-m-d') : null,
+ 'unit_name' => $inv->product->baseUnit->name ?? '',
+ 'base_unit_id' => $inv->product->base_unit_id ?? null,
+ 'conversion_rate' => $inv->product->conversion_rate ?? 1,
+ ];
+ });
+
+ return response()->json($data);
+ }
+
/**
* 編輯生產單
*/
diff --git a/app/Modules/Production/Routes/web.php b/app/Modules/Production/Routes/web.php
index d7c47d5..b0ceb15 100644
--- a/app/Modules/Production/Routes/web.php
+++ b/app/Modules/Production/Routes/web.php
@@ -30,6 +30,10 @@ Route::middleware('auth')->group(function () {
->middleware('permission:production_orders.create')
->name('api.production.warehouses.inventories');
+ Route::get('/api/production/products/{product}/inventories', [ProductionOrderController::class, 'getProductWarehouses'])
+ ->middleware('permission:production_orders.create')
+ ->name('api.production.products.inventories');
+
Route::get('/api/production/recipes/latest-by-product/{productId}', [RecipeController::class, 'getLatestByProduct'])
->name('api.production.recipes.latest-by-product');
diff --git a/database/migrations/tenant/2026_02_04_111029_remove_purchase_orders_publish_permission.php b/database/migrations/tenant/2026_02_04_111029_remove_purchase_orders_publish_permission.php
new file mode 100644
index 0000000..b405b56
--- /dev/null
+++ b/database/migrations/tenant/2026_02_04_111029_remove_purchase_orders_publish_permission.php
@@ -0,0 +1,37 @@
+delete();
+
+ // 重置權限快取
+ app()[\Spatie\Permission\PermissionRegistrar::class]->forgetCachedPermissions();
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ // 恢復權限(如果需要回滾)
+ \Spatie\Permission\Models\Permission::firstOrCreate(['name' => 'purchase_orders.publish']);
+
+ // 重新分配給 admin (簡單恢復,可能無法完全還原所有角色配置)
+ $admin = \Spatie\Permission\Models\Role::where('name', 'admin')->first();
+ if ($admin) {
+ $admin->givePermissionTo('purchase_orders.publish');
+ }
+
+ app()[\Spatie\Permission\PermissionRegistrar::class]->forgetCachedPermissions();
+ }
+};
diff --git a/database/seeders/PermissionSeeder.php b/database/seeders/PermissionSeeder.php
index 732a078..a05f462 100644
--- a/database/seeders/PermissionSeeder.php
+++ b/database/seeders/PermissionSeeder.php
@@ -30,7 +30,7 @@ class PermissionSeeder extends Seeder
'purchase_orders.create',
'purchase_orders.edit',
'purchase_orders.delete',
- 'purchase_orders.publish',
+
// 庫存管理
'inventory.view',
@@ -132,7 +132,7 @@ class PermissionSeeder extends Seeder
$admin->givePermissionTo([
'products.view', 'products.create', 'products.edit', 'products.delete',
'purchase_orders.view', 'purchase_orders.create', 'purchase_orders.edit',
- 'purchase_orders.delete', 'purchase_orders.publish',
+ 'purchase_orders.delete',
'inventory.view', 'inventory.view_cost', 'inventory.delete',
'inventory_count.view', 'inventory_count.create', 'inventory_count.edit', 'inventory_count.delete',
'inventory_adjust.view', 'inventory_adjust.create', 'inventory_adjust.edit', 'inventory_adjust.delete',
diff --git a/resources/js/Pages/Admin/Role/PermissionSelector.tsx b/resources/js/Pages/Admin/Role/PermissionSelector.tsx
index c765b3a..b1e4b5e 100644
--- a/resources/js/Pages/Admin/Role/PermissionSelector.tsx
+++ b/resources/js/Pages/Admin/Role/PermissionSelector.tsx
@@ -36,7 +36,7 @@ export default function PermissionSelector({ groupedPermissions, selectedPermiss
'create': '新增',
'edit': '編輯',
'delete': '刪除',
- 'publish': '發布',
+
'adjust': '調整',
'transfer': '調撥',
'count': '盤點',
diff --git a/resources/js/Pages/Inventory/Adjust/Show.tsx b/resources/js/Pages/Inventory/Adjust/Show.tsx
index 6c26ce2..cabab2d 100644
--- a/resources/js/Pages/Inventory/Adjust/Show.tsx
+++ b/resources/js/Pages/Inventory/Adjust/Show.tsx
@@ -542,6 +542,7 @@ export default function Show({ doc }: { auth: any, doc: AdjDoc }) {
updateItem(index, 'adjust_qty', e.target.value)}
diff --git a/resources/js/Pages/Inventory/Count/Show.tsx b/resources/js/Pages/Inventory/Count/Show.tsx
index f2c4204..e7c3aaf 100644
--- a/resources/js/Pages/Inventory/Count/Show.tsx
+++ b/resources/js/Pages/Inventory/Count/Show.tsx
@@ -265,14 +265,14 @@ export default function Show({ doc }: any) {
{item.batch_number || '-'}
- {item.system_qty.toFixed(0)}
+ {Number(item.system_qty)}
{isReadOnly ? (
{item.counted_qty}
) : (
updateItem(index, 'counted_qty', e.target.value)}
onWheel={(e: any) => e.target.blur()}
@@ -290,7 +290,7 @@ export default function Show({ doc }: any) {
: 'text-red-600'
}`}>
{formItem.counted_qty !== '' && formItem.counted_qty !== null
- ? diff.toFixed(0)
+ ? Number(diff.toFixed(2))
: '-'}
diff --git a/resources/js/Pages/Inventory/GoodsReceipt/Create.tsx b/resources/js/Pages/Inventory/GoodsReceipt/Create.tsx
index cf49f88..a9f8782 100644
--- a/resources/js/Pages/Inventory/GoodsReceipt/Create.tsx
+++ b/resources/js/Pages/Inventory/GoodsReceipt/Create.tsx
@@ -314,7 +314,7 @@ export default function GoodsReceiptCreate({ warehouses, pendingPurchaseOrders,
{/* Header */}
-