Files
wasl/Backend/app/Models/TransactionEntry.php
2026-06-20 21:55:06 +03:00

84 lines
2.1 KiB
PHP

<?php
namespace App\Models;
use App\Enums\EntryType;
use Illuminate\Database\Eloquent\Factories\HasFactory;
/**
* transaction_entries — DOUBLE-ENTRY LEDGER
*
* Every transaction MUST have exactly 2 entries (debit + credit).
* balance_after_minor is the wallet balance snapshot after this entry.
* This table is the source of truth for all balance calculations.
*
* @property int $transaction_id
* @property int $wallet_id
* @property string $entry_type debit/credit
* @property int $amount_minor BIGINT
* @property int $balance_after_minor BIGINT — snapshot after this entry
*/
class TransactionEntry extends BaseModel
{
use HasFactory;
protected $fillable = [
'transaction_id',
'wallet_id',
'entry_type',
'amount_minor',
'balance_after_minor',
];
protected $casts = [
'entry_type' => EntryType::class,
'amount_minor' => 'integer',
'balance_after_minor' => 'integer',
];
public $timestamps = false; // created_at only, set via useCurrent()
// ── Relationships ──
public function transaction()
{
return $this->belongsTo(Transaction::class);
}
public function wallet()
{
return $this->belongsTo(Wallet::class);
}
// ── Scopes ──
public function scopeDebits($query)
{
return $query->where('entry_type', EntryType::DEBIT);
}
public function scopeCredits($query)
{
return $query->where('entry_type', EntryType::CREDIT);
}
public function scopeForWallet($query, int $walletId)
{
return $query->where('wallet_id', $walletId);
}
// ── Reconciliation helper ──
/**
* Verify double-entry integrity: SUM(debits) must equal SUM(credits)
* for the given transaction_id.
*/
public static function verifyIntegrity(int $transactionId): bool
{
$debits = self::where('transaction_id', $transactionId)->debits()->sum('amount_minor');
$credits = self::where('transaction_id', $transactionId)->credits()->sum('amount_minor');
return $debits === $credits && $debits > 0;
}
}