1. 新增發票狀態機(pending/issued/failed/void)與冪等鍵 relate_number,並補上 last_checked_at、retry_count、voided_at、void_reason 欄位(migration 全欄位 nullable/有預設,對既有資料零影響並回填既有狀態)。 2. finalize 帶入發票輸入時改為先建 pending 發票,commit 後派發 IssueInvoiceJob 非同步向綠界開立,避免在交易內等待 ECPay 造成長交易。 3. 新增 EcpayInvoiceService 封裝綠界 B2C API(GetIssue 查詢、Issue 補開、Invalid 作廢),金鑰仍取自各機台 payment_configs。 4. 新增 invoices:reconcile 排程指令,每 5 分鐘對 pending 發票以 RelateNumber 向綠界 GetIssue 查證,補登 issued 或標記 failed 待補開。 5. SalesController 新增 reconcileInvoice/reissueInvoice/voidInvoice 三個後台操作端點與對應路由。 6. 發票列表改以 created_at 篩選(pending/failed 尚無 invoice_date 也能顯示),新增狀態過濾器與查詢/補開/作廢操作按鈕,狀態徽章改以狀態機顯示。 7. recordInvoice 改以 flow_id updateOrCreate 收斂,避免重送產生重複發票列;查詢機台/訂單時加上 withoutGlobalScopes。 8. 同步新增相關三語系字串並加入 ecpay_invoice base_url 設定。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
66 lines
1.8 KiB
PHP
66 lines
1.8 KiB
PHP
<?php
|
||
|
||
namespace App\Models\Transaction;
|
||
|
||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||
use Illuminate\Database\Eloquent\Model;
|
||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||
use App\Traits\TenantScoped;
|
||
|
||
class Invoice extends Model
|
||
{
|
||
// TenantScoped:自動以登入者 company_id 過濾(含 route-model binding 與列表查詢),
|
||
// 防止跨公司讀取/操作他人發票。console(佇列 Job / reconcile 命令)會自動跳過 scope,
|
||
// 系統身分仍可跨公司開立/對帳。
|
||
use HasFactory, SoftDeletes, TenantScoped;
|
||
|
||
protected $fillable = [
|
||
'company_id',
|
||
'order_id',
|
||
'machine_id',
|
||
'flow_id',
|
||
'invoice_no',
|
||
'status',
|
||
'relate_number',
|
||
'amount',
|
||
'carrier_id',
|
||
'invoice_date',
|
||
'machine_time',
|
||
'last_checked_at',
|
||
'retry_count',
|
||
'voided_at',
|
||
'void_reason',
|
||
'random_number',
|
||
'love_code',
|
||
'rtn_code',
|
||
'rtn_msg',
|
||
'metadata',
|
||
];
|
||
|
||
protected $casts = [
|
||
'amount' => 'decimal:2',
|
||
'invoice_date' => 'date',
|
||
'machine_time' => 'datetime',
|
||
'last_checked_at' => 'datetime',
|
||
'voided_at' => 'datetime',
|
||
'retry_count' => 'integer',
|
||
'metadata' => 'array',
|
||
];
|
||
|
||
// 發票狀態常數
|
||
public const STATUS_PENDING = 'pending'; // 已送出綠界、尚未確認
|
||
public const STATUS_ISSUED = 'issued'; // 已開立成功
|
||
public const STATUS_FAILED = 'failed'; // 綠界回失敗,待補開
|
||
public const STATUS_VOID = 'void'; // 已作廢
|
||
|
||
public function order()
|
||
{
|
||
return $this->belongsTo(Order::class);
|
||
}
|
||
|
||
public function machine()
|
||
{
|
||
return $this->belongsTo(\App\Models\Machine\Machine::class);
|
||
}
|
||
}
|