Add phone login support and member import functionality
Features: - Support login via phone number or email (LoginRequest) - Add members:import-roster command for Excel roster import - Merge survey emails with roster data Code Quality (Phase 1-4): - Add database locking for balance calculation - Add self-approval checks for finance workflow - Create service layer (FinanceDocumentApprovalService, PaymentVerificationService) - Add HasAccountingEntries and HasApprovalWorkflow traits - Create FormRequest classes for validation - Add status-badge component - Define authorization gates in AuthServiceProvider - Add accounting config file Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Traits\HasAccountingEntries;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
@@ -10,7 +11,7 @@ use Illuminate\Support\Facades\DB;
|
||||
|
||||
class Income extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
use HasAccountingEntries, HasFactory;
|
||||
|
||||
// 收入類型常數
|
||||
const TYPE_MEMBERSHIP_FEE = 'membership_fee'; // 會費收入
|
||||
@@ -144,11 +145,27 @@ class Income extends Model
|
||||
}
|
||||
|
||||
/**
|
||||
* 會計分錄
|
||||
* Override trait's foreign key for accounting entries
|
||||
*/
|
||||
public function accountingEntries(): HasMany
|
||||
protected function getAccountingForeignKey(): string
|
||||
{
|
||||
return $this->hasMany(AccountingEntry::class);
|
||||
return 'income_id';
|
||||
}
|
||||
|
||||
/**
|
||||
* Override trait's accounting date
|
||||
*/
|
||||
protected function getAccountingDate()
|
||||
{
|
||||
return $this->income_date ?? $this->created_at ?? now();
|
||||
}
|
||||
|
||||
/**
|
||||
* Override trait's accounting description
|
||||
*/
|
||||
protected function getAccountingDescription(): string
|
||||
{
|
||||
return "收入:{$this->title} ({$this->income_number})";
|
||||
}
|
||||
|
||||
// ========== 狀態查詢 ==========
|
||||
@@ -216,7 +233,7 @@ class Income extends Model
|
||||
$ledgerEntry = $this->createCashierLedgerEntry();
|
||||
|
||||
// 3. 產生會計分錄
|
||||
$this->generateAccountingEntries();
|
||||
$this->createIncomeAccountingEntries();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -263,42 +280,46 @@ class Income extends Model
|
||||
}
|
||||
|
||||
/**
|
||||
* 產生會計分錄
|
||||
* 產生會計分錄 (使用 trait 的方法)
|
||||
*/
|
||||
protected function generateAccountingEntries(): void
|
||||
protected function createIncomeAccountingEntries(): void
|
||||
{
|
||||
// 借方:資產帳戶(現金或銀行存款)
|
||||
$assetAccountId = $this->getAssetAccountId();
|
||||
$assetAccountId = $this->getAssetAccountIdForPaymentMethod();
|
||||
$description = $this->getAccountingDescription();
|
||||
$entryDate = $this->getAccountingDate();
|
||||
|
||||
AccountingEntry::create([
|
||||
'income_id' => $this->id,
|
||||
'chart_of_account_id' => $assetAccountId,
|
||||
'entry_type' => AccountingEntry::ENTRY_TYPE_DEBIT,
|
||||
'amount' => $this->amount,
|
||||
'entry_date' => $this->income_date,
|
||||
'description' => "收入:{$this->title} ({$this->income_number})",
|
||||
]);
|
||||
$entries = [
|
||||
// 借方:資產帳戶(現金或銀行存款)
|
||||
[
|
||||
'chart_of_account_id' => $assetAccountId,
|
||||
'entry_type' => AccountingEntry::ENTRY_TYPE_DEBIT,
|
||||
'amount' => $this->amount,
|
||||
'entry_date' => $entryDate,
|
||||
'description' => $description,
|
||||
],
|
||||
// 貸方:收入科目
|
||||
[
|
||||
'chart_of_account_id' => $this->chart_of_account_id,
|
||||
'entry_type' => AccountingEntry::ENTRY_TYPE_CREDIT,
|
||||
'amount' => $this->amount,
|
||||
'entry_date' => $entryDate,
|
||||
'description' => $description,
|
||||
],
|
||||
];
|
||||
|
||||
// 貸方:收入科目
|
||||
AccountingEntry::create([
|
||||
'income_id' => $this->id,
|
||||
'chart_of_account_id' => $this->chart_of_account_id,
|
||||
'entry_type' => AccountingEntry::ENTRY_TYPE_CREDIT,
|
||||
'amount' => $this->amount,
|
||||
'entry_date' => $this->income_date,
|
||||
'description' => "收入:{$this->title} ({$this->income_number})",
|
||||
]);
|
||||
// Use trait's method
|
||||
$this->generateAccountingEntries($entries);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根據付款方式取得資產帳戶 ID
|
||||
* 根據付款方式取得資產帳戶 ID (使用 config)
|
||||
*/
|
||||
protected function getAssetAccountId(): int
|
||||
protected function getAssetAccountIdForPaymentMethod(): int
|
||||
{
|
||||
$accountCode = match ($this->payment_method) {
|
||||
self::PAYMENT_METHOD_BANK_TRANSFER => '1201', // 銀行存款
|
||||
self::PAYMENT_METHOD_CHECK => '1201', // 銀行存款
|
||||
default => '1101', // 現金
|
||||
self::PAYMENT_METHOD_BANK_TRANSFER,
|
||||
self::PAYMENT_METHOD_CHECK => config('accounting.account_codes.bank', '1201'),
|
||||
default => config('accounting.account_codes.cash', '1101'),
|
||||
};
|
||||
|
||||
return ChartOfAccount::where('account_code', $accountCode)->value('id') ?? 1;
|
||||
|
||||
Reference in New Issue
Block a user