UI優化: 全系統狀態標籤 (StatusBadge) 統一化重構完成 (Phase 3 & 4)
All checks were successful
Koori-ERP-Deploy-System / deploy-demo (push) Has been skipped
Koori-ERP-Deploy-System / deploy-production (push) Successful in 1m8s

This commit is contained in:
2026-02-13 13:16:05 +08:00
56 changed files with 3343 additions and 429 deletions

View File

@@ -106,16 +106,23 @@ class InventoryTransferOrder extends Model
'doc_no',
'from_warehouse_id',
'to_warehouse_id',
'transit_warehouse_id',
'status',
'remarks',
'posted_at',
'created_by',
'updated_by',
'posted_by',
'dispatched_at',
'dispatched_by',
'received_at',
'received_by',
];
protected $casts = [
'posted_at' => 'datetime',
'dispatched_at' => 'datetime',
'received_at' => 'datetime',
];
protected static function boot()
@@ -163,8 +170,28 @@ class InventoryTransferOrder extends Model
return $this->belongsTo(User::class, 'created_by');
}
public function storeRequisition(): \Illuminate\Database\Eloquent\Relations\HasOne
{
return $this->hasOne(StoreRequisition::class, 'transfer_order_id');
}
public function postedBy(): BelongsTo
{
return $this->belongsTo(User::class, 'posted_by');
}
public function transitWarehouse(): BelongsTo
{
return $this->belongsTo(Warehouse::class, 'transit_warehouse_id');
}
public function dispatchedBy(): BelongsTo
{
return $this->belongsTo(User::class, 'dispatched_by');
}
public function receivedBy(): BelongsTo
{
return $this->belongsTo(User::class, 'received_by');
}
}

View File

@@ -0,0 +1,147 @@
<?php
namespace App\Modules\Inventory\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Spatie\Activitylog\Traits\LogsActivity;
use Spatie\Activitylog\LogOptions;
use App\Modules\Core\Models\User;
class StoreRequisition extends Model
{
use HasFactory, LogsActivity;
protected $fillable = [
'doc_no',
'store_warehouse_id',
'supply_warehouse_id',
'status',
'remark',
'reject_reason',
'created_by',
'approved_by',
'submitted_at',
'approved_at',
'transfer_order_id',
];
protected $casts = [
'submitted_at' => 'datetime',
'approved_at' => 'datetime',
];
public function getActivitylogOptions(): LogOptions
{
return LogOptions::defaults()
->logFillable()
->logOnlyDirty()
->dontSubmitEmptyLogs();
}
/**
* 自定義日誌屬性,解析 ID 為名稱
*/
public function tapActivity(\Spatie\Activitylog\Models\Activity $activity, string $eventName)
{
$properties = $activity->properties->toArray();
// 基本單據資訊快照
$properties['snapshot'] = [
'doc_no' => $this->doc_no,
'store_warehouse_name' => $this->storeWarehouse?->name,
'supply_warehouse_name' => $this->supplyWarehouse?->name,
'status' => $this->status,
];
// 移除雜訊欄位
if (isset($properties['attributes'])) {
unset($properties['attributes']['updated_at']);
}
if (isset($properties['old'])) {
unset($properties['old']['updated_at']);
}
$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');
}
}

View File

@@ -0,0 +1,41 @@
<?php
namespace App\Modules\Inventory\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class StoreRequisitionItem extends Model
{
use HasFactory;
protected $fillable = [
'store_requisition_id',
'product_id',
'requested_qty',
'approved_qty',
'remark',
];
protected $casts = [
'requested_qty' => 'decimal:2',
'approved_qty' => 'decimal:2',
];
/**
* 所屬叫貨單
*/
public function requisition(): BelongsTo
{
return $this->belongsTo(StoreRequisition::class, 'store_requisition_id');
}
/**
* 關聯商品(同模組)
*/
public function product(): BelongsTo
{
return $this->belongsTo(Product::class);
}
}

View File

@@ -20,6 +20,7 @@ class Warehouse extends Model
'description',
'license_plate',
'driver_name',
'default_transit_warehouse_id',
];
protected $casts = [
@@ -50,7 +51,13 @@ class Warehouse extends Model
return $this->hasMany(Inventory::class);
}
/**
* 預設在途倉庫
*/
public function defaultTransitWarehouse(): \Illuminate\Database\Eloquent\Relations\BelongsTo
{
return $this->belongsTo(self::class, 'default_transit_warehouse_id');
}
public function products(): \Illuminate\Database\Eloquent\Relations\BelongsToMany
{