mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-05-31 02:04:33 -04:00
Add recurring options to ivnoice
This commit is contained in:
parent
a10f06796a
commit
f14de42684
@ -53,6 +53,7 @@ class RecurringInvoiceFactory
|
|||||||
$invoice->remaining_cycles = -1;
|
$invoice->remaining_cycles = -1;
|
||||||
$invoice->paid_to_date = 0;
|
$invoice->paid_to_date = 0;
|
||||||
$invoice->auto_bill_enabled = false;
|
$invoice->auto_bill_enabled = false;
|
||||||
|
$invoice->is_proforma = false;
|
||||||
$invoice->auto_bill = 'off';
|
$invoice->auto_bill = 'off';
|
||||||
|
|
||||||
return $invoice;
|
return $invoice;
|
||||||
|
@ -46,6 +46,7 @@ class RecurringInvoiceToInvoiceFactory
|
|||||||
$invoice->custom_value4 = $recurring_invoice->custom_value4;
|
$invoice->custom_value4 = $recurring_invoice->custom_value4;
|
||||||
$invoice->amount = $recurring_invoice->amount;
|
$invoice->amount = $recurring_invoice->amount;
|
||||||
$invoice->uses_inclusive_taxes = $recurring_invoice->uses_inclusive_taxes;
|
$invoice->uses_inclusive_taxes = $recurring_invoice->uses_inclusive_taxes;
|
||||||
|
$invoice->is_proforma = $recurring_invoice->is_proforma;
|
||||||
|
|
||||||
$invoice->custom_surcharge1 = $recurring_invoice->custom_surcharge1;
|
$invoice->custom_surcharge1 = $recurring_invoice->custom_surcharge1;
|
||||||
$invoice->custom_surcharge2 = $recurring_invoice->custom_surcharge2;
|
$invoice->custom_surcharge2 = $recurring_invoice->custom_surcharge2;
|
||||||
|
@ -80,6 +80,7 @@ class PrePaymentController extends Controller
|
|||||||
$invoice = $invoice_repo->save($data, $invoice)
|
$invoice = $invoice_repo->save($data, $invoice)
|
||||||
->service()
|
->service()
|
||||||
->markSent()
|
->markSent()
|
||||||
|
->applyNumber()
|
||||||
->fillDefaults()
|
->fillDefaults()
|
||||||
->save();
|
->save();
|
||||||
|
|
||||||
@ -107,6 +108,9 @@ class PrePaymentController extends Controller
|
|||||||
'hashed_ids' => $invoices->pluck('hashed_id'),
|
'hashed_ids' => $invoices->pluck('hashed_id'),
|
||||||
'total' => $total,
|
'total' => $total,
|
||||||
'pre_payment' => true,
|
'pre_payment' => true,
|
||||||
|
'frequency_id' => $request->frequency_id,
|
||||||
|
'remaining_cycles' => $request->remaining_cycles,
|
||||||
|
'is_recurring' => $request->is_recurring,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
||||||
|
@ -138,9 +138,9 @@ class PortalComposer
|
|||||||
$data[] = ['title' => ctrans('texts.subscriptions'), 'url' => 'client.subscriptions.index', 'icon' => 'calendar'];
|
$data[] = ['title' => ctrans('texts.subscriptions'), 'url' => 'client.subscriptions.index', 'icon' => 'calendar'];
|
||||||
}
|
}
|
||||||
|
|
||||||
if(property_exists($this->settings, 'client_initiated_payments') && $this->settings->client_initiated_payments) {
|
// if(property_exists($this->settings, 'client_initiated_payments') && $this->settings->client_initiated_payments) {
|
||||||
$data[] = ['title' => ctrans('texts.pre_payment'), 'url' => 'client.pre_payments.index', 'icon' => 'dollar-sign'];
|
$data[] = ['title' => ctrans('texts.pre_payment'), 'url' => 'client.pre_payments.index', 'icon' => 'dollar-sign'];
|
||||||
}
|
// }
|
||||||
|
|
||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
|
@ -34,6 +34,7 @@ class InstantPayment
|
|||||||
use MakesHash;
|
use MakesHash;
|
||||||
use MakesDates;
|
use MakesDates;
|
||||||
|
|
||||||
|
/** $request mixed */
|
||||||
public Request $request;
|
public Request $request;
|
||||||
|
|
||||||
public function __construct(Request $request)
|
public function __construct(Request $request)
|
||||||
@ -214,7 +215,16 @@ class InstantPayment
|
|||||||
$credit_totals = 0;
|
$credit_totals = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
$hash_data = ['invoices' => $payable_invoices->toArray(), 'credits' => $credit_totals, 'amount_with_fee' => max(0, (($invoice_totals + $fee_totals) - $credit_totals)), 'pre_payment' => $this->request->pre_payment];
|
/** $hash_data = mixed[] */
|
||||||
|
$hash_data = [
|
||||||
|
'invoices' => $payable_invoices->toArray(),
|
||||||
|
'credits' => $credit_totals,
|
||||||
|
'amount_with_fee' => max(0, (($invoice_totals + $fee_totals) - $credit_totals)),
|
||||||
|
'pre_payment' => $this->request->pre_payment,
|
||||||
|
'frequency_id' => $this->request->frequency_id,
|
||||||
|
'remaining_cycles' => $this->request->remaining_cycles,
|
||||||
|
'is_recurring' => $this->request->is_recurring,
|
||||||
|
];
|
||||||
|
|
||||||
if ($this->request->query('hash')) {
|
if ($this->request->query('hash')) {
|
||||||
$hash_data['billing_context'] = Cache::get($this->request->query('hash'));
|
$hash_data['billing_context'] = Cache::get($this->request->query('hash'));
|
||||||
|
@ -40,6 +40,13 @@ class ApplyNumber extends AbstractService
|
|||||||
return $this->invoice;
|
return $this->invoice;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Do no give pro forma invoices a proper invoice number */
|
||||||
|
if($this->invoice->is_proforma) {
|
||||||
|
$this->invoice->number = ctrans('texts.pre_payment') . " " . now()->format('Y-m-d : H:i:s');
|
||||||
|
$this->invoice->saveQuietly();
|
||||||
|
return $this->invoice;
|
||||||
|
}
|
||||||
|
|
||||||
switch ($this->client->getSetting('counter_number_applied')) {
|
switch ($this->client->getSetting('counter_number_applied')) {
|
||||||
case 'when_saved':
|
case 'when_saved':
|
||||||
$this->trySaving();
|
$this->trySaving();
|
||||||
|
@ -121,7 +121,17 @@ class AutoBillInvoice extends AbstractService
|
|||||||
|
|
||||||
$payment_hash = PaymentHash::create([
|
$payment_hash = PaymentHash::create([
|
||||||
'hash' => Str::random(64),
|
'hash' => Str::random(64),
|
||||||
'data' => ['amount_with_fee' => $amount + $fee, 'invoices' => [['invoice_id' => $this->invoice->hashed_id, 'amount' => $amount, 'invoice_number' => $this->invoice->number]]],
|
'data' => [
|
||||||
|
'amount_with_fee' => $amount + $fee,
|
||||||
|
'invoices' => [
|
||||||
|
[
|
||||||
|
'invoice_id' => $this->invoice->hashed_id,
|
||||||
|
'amount' => $amount,
|
||||||
|
'invoice_number' => $this->invoice->number,
|
||||||
|
'pre_payment' => $this->invoice->is_proforma,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
'fee_total' => $fee,
|
'fee_total' => $fee,
|
||||||
'fee_invoice_id' => $this->invoice->id,
|
'fee_invoice_id' => $this->invoice->id,
|
||||||
]);
|
]);
|
||||||
|
@ -11,12 +11,14 @@
|
|||||||
|
|
||||||
namespace App\Services\Payment;
|
namespace App\Services\Payment;
|
||||||
|
|
||||||
use App\Events\Invoice\InvoiceWasUpdated;
|
use App\Utils\Ninja;
|
||||||
use App\Models\Invoice;
|
use App\Models\Invoice;
|
||||||
use App\Models\Payment;
|
use App\Models\Payment;
|
||||||
use App\Models\PaymentHash;
|
use App\Models\PaymentHash;
|
||||||
use App\Utils\Ninja;
|
|
||||||
use App\Utils\Traits\MakesHash;
|
use App\Utils\Traits\MakesHash;
|
||||||
|
use App\Models\RecurringInvoice;
|
||||||
|
use App\Factory\RecurringInvoiceFactory;
|
||||||
|
use App\Events\Invoice\InvoiceWasUpdated;
|
||||||
|
|
||||||
class UpdateInvoicePayment
|
class UpdateInvoicePayment
|
||||||
{
|
{
|
||||||
@ -93,9 +95,31 @@ class UpdateInvoicePayment
|
|||||||
$invoice->is_deleted = true;
|
$invoice->is_deleted = true;
|
||||||
$invoice->deleted_at = now();
|
$invoice->deleted_at = now();
|
||||||
$invoice->saveQuietly();
|
$invoice->saveQuietly();
|
||||||
|
|
||||||
|
if (property_exists($this->payment_hash->data, 'is_recurring') && $this->payment_hash->data->is_recurring == "1") {
|
||||||
|
$recurring_invoice = RecurringInvoiceFactory::create($invoice->company_id, $invoice->user_id);
|
||||||
|
$recurring_invoice->client_id = $invoice->client_id;
|
||||||
|
$recurring_invoice->line_items = $invoice->line_items;
|
||||||
|
$recurring_invoice->frequency_id = $this->payment_hash->data->is_recurring ?: RecurringInvoice::FREQUENCY_MONTHLY;
|
||||||
|
$recurring_invoice->date = now();
|
||||||
|
$recurring_invoice->remaining_cycles = $this->payment_hash->data->remaining_cycles;
|
||||||
|
$recurring_invoice->auto_bill = 'always';
|
||||||
|
$recurring_invoice->auto_bill_enabled = true;
|
||||||
|
$recurring_invoice->due_date_days = 'on_receipt';
|
||||||
|
$recurring_invoice->next_send_date = now()->format('Y-m-d');
|
||||||
|
$recurring_invoice->next_send_date_client = now()->format('Y-m-d');
|
||||||
|
|
||||||
|
$recurring_invoice->saveQuietly();
|
||||||
|
$recurring_invoice->next_send_date = $recurring_invoice->nextSendDate();
|
||||||
|
$recurring_invoice->next_send_date_client = $recurring_invoice->nextSendDateClient();
|
||||||
|
$recurring_invoice->saveQuietly();
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (strlen($invoice->number) > 1 && str_starts_with($invoice->number, "####"))
|
if (strlen($invoice->number) > 1 && str_starts_with($invoice->number, "####"))
|
||||||
$invoice->number = '';
|
$invoice->number = '';
|
||||||
|
|
||||||
|
@ -1087,7 +1087,7 @@ class SubscriptionService
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private function setAutoBillFlag($auto_bill)
|
private function setAutoBillFlag($auto_bill): bool
|
||||||
{
|
{
|
||||||
if ($auto_bill == 'always' || $auto_bill == 'optout') {
|
if ($auto_bill == 'always' || $auto_bill == 'optout') {
|
||||||
return true;
|
return true;
|
||||||
|
@ -0,0 +1,31 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::table('recurring_invoices', function (Blueprint $table) {
|
||||||
|
$table->boolean('is_proforma')->default(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
};
|
@ -5021,7 +5021,9 @@ $LANG = array(
|
|||||||
'payment_type_Klarna' => 'Klarna',
|
'payment_type_Klarna' => 'Klarna',
|
||||||
'payment_type_Interac E Transfer' => 'Interac E Transfer',
|
'payment_type_Interac E Transfer' => 'Interac E Transfer',
|
||||||
'pre_payment' => 'Pre Payment',
|
'pre_payment' => 'Pre Payment',
|
||||||
'client_remaining_cycles_helper' => 'The number of times this invoice will be generated',
|
'number_of_payments' => 'Number of payments',
|
||||||
|
'number_of_payments_helper' => 'The number of times this payment will be made',
|
||||||
|
'pre_payment_indefinitely' => 'Continue until cancelled',
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
@ -14,6 +14,9 @@
|
|||||||
<input type="hidden" name="payment_method_id" id="payment_method_id">
|
<input type="hidden" name="payment_method_id" id="payment_method_id">
|
||||||
<input type="hidden" name="signature">
|
<input type="hidden" name="signature">
|
||||||
<input type="hidden" name="pre_payment" value="{{ isset($pre_payment) ? $pre_payment : false }}">
|
<input type="hidden" name="pre_payment" value="{{ isset($pre_payment) ? $pre_payment : false }}">
|
||||||
|
<input type="hidden" name="is_recurring" value="{{ isset($is_recurring) ? $is_recurring : false }}">
|
||||||
|
<input type="hidden" name="frequency_id" value="{{ isset($frequency_id) ? $frequency_id : false }}">
|
||||||
|
<input type="hidden" name="remaining_cycles" value="{{ isset($remaining_cycles) ? $remaining_cycles : false }}">
|
||||||
|
|
||||||
<div class="container mx-auto">
|
<div class="container mx-auto">
|
||||||
<div class="grid grid-cols-6 gap-4">
|
<div class="grid grid-cols-6 gap-4">
|
||||||
|
@ -56,15 +56,15 @@
|
|||||||
@endcomponent
|
@endcomponent
|
||||||
|
|
||||||
<div x-cloak x-show="show">
|
<div x-cloak x-show="show">
|
||||||
@component('portal.ninja2020.components.general.card-element', ['title' => ctrans('texts.cycles_remaining')])
|
@component('portal.ninja2020.components.general.card-element', ['title' => ctrans('texts.number_of_payments')])
|
||||||
<select name="remaining_cycles" class="form-select input w-full bg-white">
|
<select name="remaining_cycles" class="form-select input w-full bg-white">
|
||||||
<option value="-1" selected>{{ ctrans('texts.freq_indefinitely')}}</option>
|
<option value="-1" selected>{{ ctrans('texts.pre_payment_indefinitely')}}</option>
|
||||||
@for($i = 1; $i < 60; $i++)
|
@for($i = 1; $i < 60; $i++)
|
||||||
<option value={{$i}}>{{$i}}</option>
|
<option value={{$i}}>{{$i}}</option>
|
||||||
@endfor
|
@endfor
|
||||||
</select>
|
</select>
|
||||||
<span class="py-2">
|
<span class="py-2">
|
||||||
<label for="remaining_cycles" class="col-form-label text-center col-lg-3 text-gray-900">{{ ctrans ('texts.client_remaining_cycles_helper')}}</label>
|
<label for="remaining_cycles" class="col-form-label text-center col-lg-3 text-gray-900">{{ ctrans ('texts.number_of_payments_helper')}}</label>
|
||||||
</span>
|
</span>
|
||||||
@endcomponent
|
@endcomponent
|
||||||
@component('portal.ninja2020.components.general.card-element', ['title' => ctrans('texts.frequency')])
|
@component('portal.ninja2020.components.general.card-element', ['title' => ctrans('texts.frequency')])
|
||||||
@ -73,14 +73,14 @@
|
|||||||
<option value="2">{{ ctrans('texts.freq_weekly') }}</option>
|
<option value="2">{{ ctrans('texts.freq_weekly') }}</option>
|
||||||
<option value="3">{{ ctrans('texts.freq_two_weeks') }}</option>
|
<option value="3">{{ ctrans('texts.freq_two_weeks') }}</option>
|
||||||
<option value="4">{{ ctrans('texts.freq_four_weeks') }}</option>
|
<option value="4">{{ ctrans('texts.freq_four_weeks') }}</option>
|
||||||
<option value="5">{{ ctrans('texts.freq_monthly') }}</option>
|
<option value="5" selected>{{ ctrans('texts.freq_monthly') }}</option>
|
||||||
<option value="6">{{ ctrans('texts.freq_two_months') }}</option>
|
<option value="6">{{ ctrans('texts.freq_two_months') }}</option>
|
||||||
<option value="7">{{ ctrans('texts.freq_three_months') }}</option>
|
<option value="7">{{ ctrans('texts.freq_three_months') }}</option>
|
||||||
<option value="8">{{ ctrans('texts.') }}</option>
|
<option value="8">{{ ctrans('texts.freq_four_months') }}</option>
|
||||||
<option value="9">{{ ctrans('texts.') }}</option>
|
<option value="9">{{ ctrans('texts.freq_six_months') }}</option>
|
||||||
<option value="10">{{ ctrans('texts.') }}</option>
|
<option value="10">{{ ctrans('texts.freq_annually') }}</option>
|
||||||
<option value="11">{{ ctrans('texts.') }}</option>
|
<option value="11">{{ ctrans('texts.freq_two_years') }}</option>
|
||||||
<option value="12">{{ ctrans('texts.') }}</option>
|
<option value="12">{{ ctrans('texts.freq_three_years') }}</option>
|
||||||
</select>
|
</select>
|
||||||
@endcomponent
|
@endcomponent
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user