|
@@ -5,7 +5,6 @@ namespace App\Filament\Company\Resources\Sales\InvoiceResource\RelationManagers;
|
5
|
5
|
use App\Enums\Accounting\InvoiceStatus;
|
6
|
6
|
use App\Enums\Accounting\PaymentMethod;
|
7
|
7
|
use App\Enums\Accounting\TransactionType;
|
8
|
|
-use App\Filament\Company\Resources\Sales\InvoiceResource\Pages\ViewInvoice;
|
9
|
8
|
use App\Models\Accounting\Invoice;
|
10
|
9
|
use App\Models\Accounting\Transaction;
|
11
|
10
|
use App\Models\Banking\BankAccount;
|
|
@@ -41,7 +40,7 @@ class PaymentsRelationManager extends RelationManager
|
41
|
40
|
|
42
|
41
|
public static function canViewForRecord(Model $ownerRecord, string $pageClass): bool
|
43
|
42
|
{
|
44
|
|
- return $ownerRecord->status !== InvoiceStatus::Draft && $pageClass === ViewInvoice::class;
|
|
43
|
+ return $ownerRecord->status !== InvoiceStatus::Draft;
|
45
|
44
|
}
|
46
|
45
|
|
47
|
46
|
public function form(Form $form): Form
|
|
@@ -51,63 +50,143 @@ class PaymentsRelationManager extends RelationManager
|
51
|
50
|
->schema([
|
52
|
51
|
Forms\Components\DatePicker::make('posted_at')
|
53
|
52
|
->label('Date'),
|
54
|
|
- Forms\Components\TextInput::make('amount')
|
55
|
|
- ->label('Amount')
|
56
|
|
- ->required()
|
57
|
|
- ->money()
|
58
|
|
- ->live(onBlur: true)
|
59
|
|
- ->helperText(function (RelationManager $livewire, $state, ?Transaction $record) {
|
60
|
|
- if (! CurrencyConverter::isValidAmount($state)) {
|
|
53
|
+ Forms\Components\Grid::make()
|
|
54
|
+ ->schema([
|
|
55
|
+ Forms\Components\Select::make('bank_account_id')
|
|
56
|
+ ->label('Account')
|
|
57
|
+ ->required()
|
|
58
|
+ ->live()
|
|
59
|
+ ->options(function () {
|
|
60
|
+ return BankAccount::query()
|
|
61
|
+ ->join('accounts', 'bank_accounts.account_id', '=', 'accounts.id')
|
|
62
|
+ ->select(['bank_accounts.id', 'accounts.name', 'accounts.currency_code'])
|
|
63
|
+ ->get()
|
|
64
|
+ ->mapWithKeys(function ($account) {
|
|
65
|
+ $label = $account->name;
|
|
66
|
+ if ($account->currency_code) {
|
|
67
|
+ $label .= " ({$account->currency_code})";
|
|
68
|
+ }
|
|
69
|
+
|
|
70
|
+ return [$account->id => $label];
|
|
71
|
+ })
|
|
72
|
+ ->toArray();
|
|
73
|
+ })
|
|
74
|
+ ->searchable(),
|
|
75
|
+ Forms\Components\TextInput::make('amount')
|
|
76
|
+ ->label('Amount')
|
|
77
|
+ ->required()
|
|
78
|
+ ->money(function (RelationManager $livewire) {
|
|
79
|
+ /** @var Invoice $invoice */
|
|
80
|
+ $invoice = $livewire->getOwnerRecord();
|
|
81
|
+
|
|
82
|
+ return $invoice->currency_code;
|
|
83
|
+ })
|
|
84
|
+ ->live(onBlur: true)
|
|
85
|
+ ->helperText(function (RelationManager $livewire, $state, ?Transaction $record) {
|
|
86
|
+ /** @var Invoice $ownerRecord */
|
|
87
|
+ $ownerRecord = $livewire->getOwnerRecord();
|
|
88
|
+
|
|
89
|
+ $invoiceCurrency = $ownerRecord->currency_code;
|
|
90
|
+
|
|
91
|
+ if (! CurrencyConverter::isValidAmount($state, $invoiceCurrency)) {
|
|
92
|
+ return null;
|
|
93
|
+ }
|
|
94
|
+
|
|
95
|
+ $amountDue = $ownerRecord->getRawOriginal('amount_due');
|
|
96
|
+
|
|
97
|
+ $amount = CurrencyConverter::convertToCents($state, $invoiceCurrency);
|
|
98
|
+
|
|
99
|
+ if ($amount <= 0) {
|
|
100
|
+ return 'Please enter a valid positive amount';
|
|
101
|
+ }
|
|
102
|
+
|
|
103
|
+ $currentPaymentAmount = $record?->getRawOriginal('amount') ?? 0;
|
|
104
|
+
|
|
105
|
+ if ($ownerRecord->status === InvoiceStatus::Overpaid) {
|
|
106
|
+ $newAmountDue = $amountDue + $amount - $currentPaymentAmount;
|
|
107
|
+ } else {
|
|
108
|
+ $newAmountDue = $amountDue - $amount + $currentPaymentAmount;
|
|
109
|
+ }
|
|
110
|
+
|
|
111
|
+ return match (true) {
|
|
112
|
+ $newAmountDue > 0 => 'Amount due after payment will be ' . CurrencyConverter::formatCentsToMoney($newAmountDue, $invoiceCurrency),
|
|
113
|
+ $newAmountDue === 0 => 'Invoice will be fully paid',
|
|
114
|
+ default => 'Invoice will be overpaid by ' . CurrencyConverter::formatCentsToMoney(abs($newAmountDue), $invoiceCurrency),
|
|
115
|
+ };
|
|
116
|
+ })
|
|
117
|
+ ->rules([
|
|
118
|
+ static fn (RelationManager $livewire): Closure => static function (string $attribute, $value, Closure $fail) use ($livewire) {
|
|
119
|
+ /** @var Invoice $invoice */
|
|
120
|
+ $invoice = $livewire->getOwnerRecord();
|
|
121
|
+
|
|
122
|
+ if (! CurrencyConverter::isValidAmount($value, $invoice->currency_code)) {
|
|
123
|
+ $fail('Please enter a valid amount');
|
|
124
|
+ }
|
|
125
|
+ },
|
|
126
|
+ ]),
|
|
127
|
+ ])->columns(2),
|
|
128
|
+ Forms\Components\Placeholder::make('currency_conversion')
|
|
129
|
+ ->label('Currency Conversion')
|
|
130
|
+ ->content(function (Forms\Get $get, RelationManager $livewire) {
|
|
131
|
+ $amount = $get('amount');
|
|
132
|
+ $bankAccountId = $get('bank_account_id');
|
|
133
|
+
|
|
134
|
+ /** @var Invoice $invoice */
|
|
135
|
+ $invoice = $livewire->getOwnerRecord();
|
|
136
|
+ $invoiceCurrency = $invoice->currency_code;
|
|
137
|
+
|
|
138
|
+ if (empty($amount) || empty($bankAccountId) || ! CurrencyConverter::isValidAmount($amount, $invoiceCurrency)) {
|
|
139
|
+ return null;
|
|
140
|
+ }
|
|
141
|
+
|
|
142
|
+ $bankAccount = BankAccount::with('account')->find($bankAccountId);
|
|
143
|
+ if (! $bankAccount) {
|
61
|
144
|
return null;
|
62
|
145
|
}
|
63
|
146
|
|
64
|
|
- /** @var Invoice $ownerRecord */
|
65
|
|
- $ownerRecord = $livewire->getOwnerRecord();
|
|
147
|
+ $bankCurrency = $bankAccount->account->currency_code ?? CurrencyAccessor::getDefaultCurrency();
|
|
148
|
+
|
|
149
|
+ // If currencies are the same, no conversion needed
|
|
150
|
+ if ($invoiceCurrency === $bankCurrency) {
|
|
151
|
+ return null;
|
|
152
|
+ }
|
66
|
153
|
|
67
|
|
- $amountDue = $ownerRecord->getRawOriginal('amount_due');
|
|
154
|
+ // Convert amount from invoice currency to bank currency
|
|
155
|
+ $amountInInvoiceCurrencyCents = CurrencyConverter::convertToCents($amount, $invoiceCurrency);
|
|
156
|
+ $amountInBankCurrencyCents = CurrencyConverter::convertBalance(
|
|
157
|
+ $amountInInvoiceCurrencyCents,
|
|
158
|
+ $invoiceCurrency,
|
|
159
|
+ $bankCurrency
|
|
160
|
+ );
|
68
|
161
|
|
69
|
|
- $amount = CurrencyConverter::convertToCents($state);
|
|
162
|
+ $formattedBankAmount = CurrencyConverter::formatCentsToMoney($amountInBankCurrencyCents, $bankCurrency);
|
70
|
163
|
|
71
|
|
- if ($amount <= 0) {
|
72
|
|
- return 'Please enter a valid positive amount';
|
|
164
|
+ return "Payment will be recorded as {$formattedBankAmount} in the bank account's currency ({$bankCurrency}).";
|
|
165
|
+ })
|
|
166
|
+ ->hidden(function (Forms\Get $get, RelationManager $livewire) {
|
|
167
|
+ $bankAccountId = $get('bank_account_id');
|
|
168
|
+ if (empty($bankAccountId)) {
|
|
169
|
+ return true;
|
73
|
170
|
}
|
74
|
171
|
|
75
|
|
- $currentPaymentAmount = $record?->getRawOriginal('amount') ?? 0;
|
|
172
|
+ /** @var Invoice $invoice */
|
|
173
|
+ $invoice = $livewire->getOwnerRecord();
|
|
174
|
+ $invoiceCurrency = $invoice->currency_code;
|
76
|
175
|
|
77
|
|
- if ($ownerRecord->status === InvoiceStatus::Overpaid) {
|
78
|
|
- $newAmountDue = $amountDue + $amount - $currentPaymentAmount;
|
79
|
|
- } else {
|
80
|
|
- $newAmountDue = $amountDue - $amount + $currentPaymentAmount;
|
|
176
|
+ $bankAccount = BankAccount::with('account')->find($bankAccountId);
|
|
177
|
+ if (! $bankAccount) {
|
|
178
|
+ return true;
|
81
|
179
|
}
|
82
|
180
|
|
83
|
|
- return match (true) {
|
84
|
|
- $newAmountDue > 0 => 'Amount due after payment will be ' . CurrencyConverter::formatCentsToMoney($newAmountDue),
|
85
|
|
- $newAmountDue === 0 => 'Invoice will be fully paid',
|
86
|
|
- default => 'Invoice will be overpaid by ' . CurrencyConverter::formatCentsToMoney(abs($newAmountDue)),
|
87
|
|
- };
|
88
|
|
- })
|
89
|
|
- ->rules([
|
90
|
|
- static fn (): Closure => static function (string $attribute, $value, Closure $fail) {
|
91
|
|
- if (! CurrencyConverter::isValidAmount($value)) {
|
92
|
|
- $fail('Please enter a valid amount');
|
93
|
|
- }
|
94
|
|
- },
|
95
|
|
- ]),
|
|
181
|
+ $bankCurrency = $bankAccount->account->currency_code ?? CurrencyAccessor::getDefaultCurrency();
|
|
182
|
+
|
|
183
|
+ // Hide if currencies are the same
|
|
184
|
+ return $invoiceCurrency === $bankCurrency;
|
|
185
|
+ }),
|
96
|
186
|
Forms\Components\Select::make('payment_method')
|
97
|
187
|
->label('Payment method')
|
98
|
188
|
->required()
|
99
|
189
|
->options(PaymentMethod::class),
|
100
|
|
- Forms\Components\Select::make('bank_account_id')
|
101
|
|
- ->label('Account')
|
102
|
|
- ->required()
|
103
|
|
- ->options(function () {
|
104
|
|
- return BankAccount::query()
|
105
|
|
- ->join('accounts', 'bank_accounts.account_id', '=', 'accounts.id')
|
106
|
|
- ->select(['bank_accounts.id', 'accounts.name'])
|
107
|
|
- ->pluck('accounts.name', 'bank_accounts.id')
|
108
|
|
- ->toArray();
|
109
|
|
- })
|
110
|
|
- ->searchable(),
|
111
|
190
|
Forms\Components\Textarea::make('notes')
|
112
|
191
|
->label('Notes'),
|
113
|
192
|
]);
|