'datetime', 'approved_at' => 'datetime', ]; public function getActivitylogOptions(): LogOptions { return LogOptions::defaults() ->logFillable() ->logOnlyDirty() ->dontSubmitEmptyLogs(); } /** * @var array 暫存的活動紀錄屬性 (不會存入資料庫) */ public $activityProperties = []; /** * 自定義日誌屬性,解析 ID 為名稱 */ public function tapActivity(\Spatie\Activitylog\Models\Activity $activity, string $eventName) { $properties = $activity->properties->toArray(); // 處置日誌事件與狀態中文化 $statusMap = [ 'draft' => '草稿', 'pending' => '待審核', 'approved' => '已核准', 'rejected' => '已駁回', 'completed' => '已完成', ]; // 處理 ID 轉名稱 $idToNameFields = [ 'store_warehouse_id' => 'storeWarehouse', 'supply_warehouse_id' => 'supplyWarehouse', 'created_by' => 'createdBy', 'approved_by' => 'approvedBy', 'transfer_order_id' => 'transferOrder', ]; foreach (['attributes', 'old'] as $part) { if (isset($properties[$part])) { // 1. 解析狀態中文並替換原始 status 欄位 if (isset($properties[$part]['status'])) { $statusValue = $properties[$part]['status']; $properties[$part]['status'] = $statusMap[$statusValue] ?? $statusValue; } // 2. 解析關連名稱 foreach ($idToNameFields as $idField => $relation) { if (isset($properties[$part][$idField])) { $id = $properties[$part][$idField]; if (!$id) continue; $nameField = str_replace('_id', '_name', $idField); if (str_contains($idField, '_by')) { $nameField = str_replace('_by', '_user_name', $idField); } $name = null; try { if ($this->relationLoaded($relation) && $this->$relation && $this->$relation->id == $id) { // 特別處理調撥單號 $name = ($relation === 'transferOrder') ? $this->$relation->doc_no : $this->$relation->name; } else { $relatedModel = $this->$relation()->getRelated(); $model = $relatedModel->find($id); if ($model) { $name = ($relation === 'transferOrder') ? ($model->doc_no ?? "ID: $id") : ($model->name ?? "ID: $id"); } else { $name = "ID: $id"; } } } catch (\Exception $e) { $name = "ID: $id"; } $properties[$part][$nameField] = $name; // 移除原生的技術 ID 欄位,讓詳情更乾淨 unset($properties[$part][$idField]); } } } } // 基本單據資訊快照 $properties['snapshot'] = [ 'doc_no' => $this->doc_no, 'store_warehouse_name' => $this->storeWarehouse?->name, 'supply_warehouse_name' => $this->supplyWarehouse?->name, 'status' => $statusMap[$this->status] ?? $this->status, ]; // 移除雜訊與重複欄位 if (isset($properties['attributes'])) { unset($properties['attributes']['updated_at']); unset($properties['attributes']['activityProperties']); } if (isset($properties['old'])) { unset($properties['old']['updated_at']); } // 合併暫存屬性 (例如 items_diff) if (!empty($this->activityProperties)) { $properties = array_merge($properties, $this->activityProperties); } $activity->properties = collect($properties); } /** * 自動產生單號 SR-YYYYMMDD-XX */ protected static function boot() { parent::boot(); static::creating(function ($model) { if (empty($model->doc_no)) { $today = date('Ymd'); $prefix = 'SR-' . $today . '-'; $lastDoc = static::where('doc_no', 'like', $prefix . '%') ->orderBy('doc_no', 'desc') ->first(); if ($lastDoc) { $lastNumber = substr($lastDoc->doc_no, -2); $nextNumber = str_pad((int)$lastNumber + 1, 2, '0', STR_PAD_LEFT); } else { $nextNumber = '01'; } $model->doc_no = $prefix . $nextNumber; } }); } // ===== 關聯 ===== /** * 申請倉庫 */ public function storeWarehouse(): BelongsTo { return $this->belongsTo(Warehouse::class, 'store_warehouse_id'); } /** * 供貨倉庫(審核時填入) */ public function supplyWarehouse(): BelongsTo { return $this->belongsTo(Warehouse::class, 'supply_warehouse_id'); } /** * 叫貨明細 */ public function items(): HasMany { return $this->hasMany(StoreRequisitionItem::class); } /** * 申請人 */ public function createdBy(): BelongsTo { return $this->belongsTo(User::class, 'created_by'); } /** * 審核人 */ public function approvedBy(): BelongsTo { return $this->belongsTo(User::class, 'approved_by'); } /** * 關聯調撥單 */ public function transferOrder(): BelongsTo { return $this->belongsTo(InventoryTransferOrder::class, 'transfer_order_id'); } }