mirror of
https://github.com/InvoiceShelf/InvoiceShelf.git
synced 2026-09-02 21:31:01 +00:00
129 lines
4.4 KiB
PHP
129 lines
4.4 KiB
PHP
<?php
|
|
|
|
namespace App\Domains\Receivables\Http\Requests;
|
|
|
|
use App\Domains\Accounts\Models\CompanySetting;
|
|
use App\Domains\Contacts\Models\Customer;
|
|
use Illuminate\Foundation\Http\FormRequest;
|
|
use Illuminate\Validation\Rule;
|
|
use Illuminate\Validation\Rules\Unique;
|
|
use Illuminate\Validation\Validator;
|
|
|
|
/**
|
|
* Validates the payment write surface and assembles the attributes the service
|
|
* layer stores. Money arrives as integer minor units, and the invoices a
|
|
* payment settles are named in its allocation rows.
|
|
*/
|
|
class PaymentRequest extends FormRequest
|
|
{
|
|
/**
|
|
* The payment abilities are checked by the controller.
|
|
*/
|
|
public function authorize(): bool
|
|
{
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @return array<string, mixed>
|
|
*/
|
|
public function rules(): array
|
|
{
|
|
return [
|
|
'payment_date' => ['required'],
|
|
'customer_id' => [
|
|
'required',
|
|
Rule::exists('customers', 'id')->where('company_id', $this->header('company')),
|
|
],
|
|
// A rate is only demanded when the payer settles in a currency of
|
|
// their own; it is still checked when volunteered.
|
|
'exchange_rate' => $this->foreignCurrency()
|
|
? ['required', 'numeric', 'gt:0']
|
|
: ['nullable', 'numeric', 'gt:0'],
|
|
'amount' => ['required', 'integer', 'min:1'],
|
|
'payment_number' => ['required', $this->uniqueNumber()],
|
|
// Row-level shape only. What the rows are allowed to add up to, and
|
|
// which invoices they may name, is the allocation engine's call.
|
|
'allocations' => ['sometimes', 'array'],
|
|
'allocations.*.invoice_id' => ['required', 'integer', 'distinct'],
|
|
'allocations.*.amount' => ['required', 'integer', 'min:1'],
|
|
'payment_method_id' => ['nullable'],
|
|
'notes' => ['nullable'],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* The singular invoice_id link is retired: invoices are reached through
|
|
* allocations. It is refused from here instead of through a rule so the
|
|
* dead field never shows up in the generated API schema.
|
|
*/
|
|
public function withValidator(Validator $validator): void
|
|
{
|
|
$validator->after(function (Validator $validator): void {
|
|
if (! $this->exists('invoice_id')) {
|
|
return;
|
|
}
|
|
|
|
$validator->errors()->add(
|
|
'invoice_id',
|
|
__('validation.prohibited', ['attribute' => 'invoice id'])
|
|
);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* The stored attributes.
|
|
*
|
|
* A payment is always denominated in the customer's currency: at home the
|
|
* rate is 1, abroad it is the submitted one, and the base amount is the
|
|
* converted total rounded to whole minor units.
|
|
*/
|
|
public function getPaymentPayload()
|
|
{
|
|
$currencyId = Customer::find($this->customer_id)->currency_id;
|
|
$homeCurrency = CompanySetting::getSetting('currency', $this->header('company'));
|
|
$rate = (string) $homeCurrency !== (string) $currencyId ? (float) $this->exchange_rate : 1;
|
|
|
|
return collect($this->validated())
|
|
->except('allocations')
|
|
->merge([
|
|
'creator_id' => $this->user()->id,
|
|
'company_id' => $this->header('company'),
|
|
'exchange_rate' => $rate,
|
|
'base_amount' => (int) round($this->amount * $rate),
|
|
'currency_id' => $currencyId,
|
|
])
|
|
->toArray();
|
|
}
|
|
|
|
/**
|
|
* Numbers are unique inside a company; a replace exempts the payment being
|
|
* written from its own number.
|
|
*/
|
|
private function uniqueNumber(): Unique
|
|
{
|
|
$rule = Rule::unique('payments')->where('company_id', $this->header('company'));
|
|
|
|
return $this->isMethod('PUT')
|
|
? $rule->ignore($this->route('payment')->id)
|
|
: $rule;
|
|
}
|
|
|
|
/**
|
|
* True when the payer's currency is not the company's own, which is what
|
|
* makes a rate mandatory. An unknown customer or an unset company currency
|
|
* leaves the rate optional; the customer rule reports that instead.
|
|
*/
|
|
private function foreignCurrency(): bool
|
|
{
|
|
$homeCurrency = CompanySetting::getSetting('currency', $this->header('company'));
|
|
$payer = Customer::find($this->customer_id);
|
|
|
|
if (! $payer || ! $homeCurrency) {
|
|
return false;
|
|
}
|
|
|
|
return (string) $payer->currency_id !== $homeCurrency;
|
|
}
|
|
}
|