Files
star-erp/app/Modules/Inventory/Services/CountService.php
sky121113 e5edad4fd0
All checks were successful
Koori-ERP-Deploy-System / deploy-demo (push) Successful in 1m4s
Koori-ERP-Deploy-System / deploy-production (push) Has been skipped
style: 修正盤點與盤調畫面 Table Padding 並統一 UI 規範
2026-01-28 18:04:45 +08:00

157 lines
5.7 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
namespace App\Modules\Inventory\Services;
use App\Modules\Inventory\Models\Inventory;
use App\Modules\Inventory\Models\InventoryCountDoc;
use App\Modules\Inventory\Models\InventoryCountItem;
use App\Modules\Inventory\Models\Warehouse;
use App\Modules\Inventory\Models\Product;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
class CountService
{
/**
* 建立新的盤點單並執行快照
*/
public function createDoc(string $warehouseId, string $remarks = null, int $userId): InventoryCountDoc
{
return DB::transaction(function () use ($warehouseId, $remarks, $userId) {
$doc = InventoryCountDoc::create([
'warehouse_id' => $warehouseId,
'status' => 'draft',
'remarks' => $remarks,
'created_by' => $userId,
]);
return $doc;
});
}
/**
* 執行快照:鎖定當前庫存量
*/
public function snapshot(InventoryCountDoc $doc): void
{
DB::transaction(function () use ($doc) {
// 清除舊的 items (如果有)
$doc->items()->delete();
// 取得該倉庫所有庫存 (包含 quantity = 0 但未軟刪除的)
// 這裡可以根據需求決定是否要過濾掉 0 庫存,通常盤點單會希望能看到所有 "帳上有紀錄" 的東西
$inventories = Inventory::where('warehouse_id', $doc->warehouse_id)
->whereNull('deleted_at')
->get();
$items = [];
foreach ($inventories as $inv) {
$items[] = [
'count_doc_id' => $doc->id,
'product_id' => $inv->product_id,
'batch_number' => $inv->batch_number,
'system_qty' => $inv->quantity,
'counted_qty' => null, // 預設未盤點
'diff_qty' => 0,
'created_at' => now(),
'updated_at' => now(),
];
}
if (!empty($items)) {
InventoryCountItem::insert($items);
}
$doc->update([
'status' => 'counting',
'snapshot_date' => now(),
]);
});
}
/**
* 完成盤點:過帳差異
*/
public function complete(InventoryCountDoc $doc, int $userId): void
{
DB::transaction(function () use ($doc, $userId) {
foreach ($doc->items as $item) {
// 如果沒有輸入實盤數量,預設跳過或是視為 0?
// 安全起見:如果 counted_qty 是 null表示沒盤到跳過不處理 (或者依業務邏輯視為0)
// 這裡假設前端會確保有送出資料,若 null 則不做異動
if (is_null($item->counted_qty)) {
continue;
}
$diff = $item->counted_qty - $item->system_qty;
// 如果無差異,更新 item 狀態即可 (diff_qty 已經是 computed field 或在儲存時計算)
// 這裡 update 一下 diff_qty 以防萬一
$item->update(['diff_qty' => $diff]);
if (abs($diff) > 0.0001) {
// 找回原本的 Inventory
$inventory = Inventory::where('warehouse_id', $doc->warehouse_id)
->where('product_id', $item->product_id)
->where('batch_number', $item->batch_number)
->first();
if (!$inventory) {
// 如果原本沒庫存紀錄 (例如是新增的盤點項目),需要新建 Inventory
// 但目前 snapshot 邏輯只抓現有。若允許 "盤盈" (發現不在帳上的),需要額外邏輯
// 暫時略過 "新增 Inventory" 的複雜邏輯,假設只能針對 existing batch 調整
continue;
}
$oldQty = $inventory->quantity;
$newQty = $oldQty + $diff;
$inventory->quantity = $newQty;
$inventory->total_value = $inventory->unit_cost * $newQty;
$inventory->save();
// 寫入 Transaction
$inventory->transactions()->create([
'type' => '盤點調整',
'quantity' => $diff,
'unit_cost' => $inventory->unit_cost,
'balance_before' => $oldQty,
'balance_after' => $newQty,
'reason' => "盤點單 {$doc->doc_no} 過帳",
'actual_time' => now(),
'user_id' => $userId,
]);
}
}
$doc->update([
'status' => 'completed',
'completed_at' => now(),
'completed_by' => $userId,
]);
});
}
/**
* 更新盤點數量
*/
public function updateCount(InventoryCountDoc $doc, array $itemsData): void
{
DB::transaction(function () use ($doc, $itemsData) {
foreach ($itemsData as $data) {
$item = $doc->items()->find($data['id']);
if ($item) {
$countedQty = $data['counted_qty'];
$diff = is_numeric($countedQty) ? ($countedQty - $item->system_qty) : 0;
$item->update([
'counted_qty' => $countedQty,
'diff_qty' => $diff,
'notes' => $data['notes'] ?? $item->notes,
]);
}
}
});
}
}