Tests for payment schedules
This commit is contained in:
parent
855639f48e
commit
413e4a0001
|
|
@ -29,8 +29,8 @@ class PaymentSchedule
|
||||||
{
|
{
|
||||||
$invoice = Invoice::find($this->decodePrimaryKey($this->scheduler->parameters['invoice_id']));
|
$invoice = Invoice::find($this->decodePrimaryKey($this->scheduler->parameters['invoice_id']));
|
||||||
|
|
||||||
// Needs to be draft, partial or paid AND not deleted
|
// Needs to be draft, partial or sent AND not deleted
|
||||||
if(!$invoice ||!in_array($invoice->status_id, [Invoice::STATUS_DRAFT, Invoice::STATUS_PARTIAL, Invoice::STATUS_PAID]) || $invoice->is_deleted){
|
if(!$invoice || !in_array($invoice->status_id, [Invoice::STATUS_DRAFT, Invoice::STATUS_PARTIAL, Invoice::STATUS_SENT]) || $invoice->is_deleted){
|
||||||
$this->scheduler->forceDelete();
|
$this->scheduler->forceDelete();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -43,12 +43,10 @@ class PaymentSchedule
|
||||||
$next_schedule = false;
|
$next_schedule = false;
|
||||||
|
|
||||||
foreach($schedule as $key =>$item){
|
foreach($schedule as $key =>$item){
|
||||||
|
|
||||||
if(now()->startOfDay()->eq(Carbon::parse($item['date'])->subSeconds($offset)->startOfDay())){
|
if(now()->startOfDay()->eq(Carbon::parse($item['date'])->subSeconds($offset)->startOfDay())){
|
||||||
$next_schedule = $item;
|
$next_schedule = $item;
|
||||||
$schedule_index = $key;
|
$schedule_index = $key;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!$next_schedule){
|
if(!$next_schedule){
|
||||||
|
|
@ -56,22 +54,41 @@ class PaymentSchedule
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$amount = max($next_schedule['amount'], ($next_schedule['percentage'] * $invoice->amount));
|
nlog($next_schedule);
|
||||||
$amount += $invoice->partial;
|
|
||||||
|
if($next_schedule['is_amount']){
|
||||||
|
nlog("is an amount");
|
||||||
|
$amount = $next_schedule['amount'];
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
$amount = round(($next_schedule['amount']/100)*$invoice->amount, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
$amount = min($amount, $invoice->amount);
|
||||||
|
|
||||||
if($amount > $invoice->balance){
|
if($amount > $invoice->balance){
|
||||||
$amount = $invoice->balance;
|
$amount = $invoice->balance;
|
||||||
}
|
}
|
||||||
|
|
||||||
$invoice->partial = $amount;
|
nlog("amount to add: {$amount}");
|
||||||
$invoice->partial_due_date = $item['date'];
|
nlog("invoice partial before: {$invoice->partial}");
|
||||||
$invoice->due_date = Carbon::parse($item['date'])->addDay()->format('Y-m-d');
|
$invoice->partial += $amount;
|
||||||
|
$invoice->partial_due_date = $next_schedule['date'];
|
||||||
|
$invoice->due_date = Carbon::parse($next_schedule['date'])->addDay()->format('Y-m-d');
|
||||||
|
|
||||||
$invoice->save();
|
$invoice->save();
|
||||||
|
|
||||||
|
|
||||||
|
nlog("invoice partial after: {$invoice->partial}");
|
||||||
|
|
||||||
if($this->scheduler->parameters['auto_bill']){
|
if($this->scheduler->parameters['auto_bill']){
|
||||||
$invoice->service()->autoBill();
|
|
||||||
|
try{
|
||||||
|
$invoice->service()->autoBill();
|
||||||
|
}
|
||||||
|
catch(\Throwable $e){
|
||||||
|
nlog("Error auto-billing invoice {$invoice->id}: {$e->getMessage()}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
$invoice->service()->sendEmail();
|
$invoice->service()->sendEmail();
|
||||||
|
|
@ -79,7 +96,7 @@ class PaymentSchedule
|
||||||
|
|
||||||
$total_schedules = count($schedule);
|
$total_schedules = count($schedule);
|
||||||
|
|
||||||
if($total_schedules >= $schedule_index + 1){
|
if(isset($schedule[$schedule_index + 1])){
|
||||||
$next_run = $schedule[$schedule_index + 1]['date'];
|
$next_run = $schedule[$schedule_index + 1]['date'];
|
||||||
$this->scheduler->next_run_client = $next_run;
|
$this->scheduler->next_run_client = $next_run;
|
||||||
$this->scheduler->next_run = Carbon::parse($next_run)->addSeconds($offset);
|
$this->scheduler->next_run = Carbon::parse($next_run)->addSeconds($offset);
|
||||||
|
|
|
||||||
|
|
@ -13,8 +13,10 @@
|
||||||
namespace App\Services\Scheduler;
|
namespace App\Services\Scheduler;
|
||||||
|
|
||||||
use App\Models\Scheduler;
|
use App\Models\Scheduler;
|
||||||
use App\Utils\Traits\MakesDates;
|
|
||||||
use App\Utils\Traits\MakesHash;
|
use App\Utils\Traits\MakesHash;
|
||||||
|
use App\Utils\Traits\MakesDates;
|
||||||
|
use App\Services\Scheduler\PaymentSchedule;
|
||||||
|
|
||||||
class SchedulerService
|
class SchedulerService
|
||||||
{
|
{
|
||||||
use MakesHash;
|
use MakesHash;
|
||||||
|
|
@ -56,6 +58,10 @@ class SchedulerService
|
||||||
(new InvoiceOutstandingTasksService($this->scheduler))->run();
|
(new InvoiceOutstandingTasksService($this->scheduler))->run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function payment_schedule()
|
||||||
|
{
|
||||||
|
(new PaymentSchedule($this->scheduler))->run();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the next run date of the scheduled task
|
* Sets the next run date of the scheduled task
|
||||||
|
|
|
||||||
|
|
@ -60,9 +60,233 @@ class SchedulerTest extends TestCase
|
||||||
ThrottleRequests::class
|
ThrottleRequests::class
|
||||||
);
|
);
|
||||||
|
|
||||||
// $this->withoutExceptionHandling();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testPaymentScheduleCalculationsIsAmountWithAutoBill()
|
||||||
|
{
|
||||||
|
$settings = $this->company->settings;
|
||||||
|
$settings->use_credits_payment = 'off';
|
||||||
|
$settings->use_unapplied_payment = 'off';
|
||||||
|
$this->company->settings = $settings;
|
||||||
|
$this->company->save();
|
||||||
|
|
||||||
|
\App\Models\Credit::where('client_id', $this->client->id)->delete();
|
||||||
|
|
||||||
|
$invoice = Invoice::factory()->create([
|
||||||
|
'company_id' => $this->company->id,
|
||||||
|
'user_id' => $this->user->id,
|
||||||
|
'client_id' => $this->client->id,
|
||||||
|
'date' => now()->format('Y-m-d'),
|
||||||
|
'due_date' => now()->addDays(30)->format('Y-m-d'),
|
||||||
|
'partial' => 0,
|
||||||
|
'partial_due_date' => null,
|
||||||
|
'amount' => 300.00,
|
||||||
|
'balance' => 300.00,
|
||||||
|
'status_id' => Invoice::STATUS_SENT,
|
||||||
|
]);
|
||||||
|
|
||||||
|
|
||||||
|
$data = [
|
||||||
|
'name' => 'A test payment schedule scheduler',
|
||||||
|
'frequency_id' => 0,
|
||||||
|
'next_run' => now()->format('Y-m-d'),
|
||||||
|
'template' => 'payment_schedule',
|
||||||
|
'parameters' => [
|
||||||
|
'invoice_id' => $invoice->hashed_id,
|
||||||
|
'auto_bill' => true,
|
||||||
|
'schedule' => [
|
||||||
|
[
|
||||||
|
'id' => 1,
|
||||||
|
'date' => now()->format('Y-m-d'),
|
||||||
|
'amount' => 40,
|
||||||
|
'is_amount' => true,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'id' => 2,
|
||||||
|
'date' => now()->addDays(30)->format('Y-m-d'),
|
||||||
|
'amount' => 60.00,
|
||||||
|
'is_amount' => true,
|
||||||
|
]
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
$response = $this->withHeaders([
|
||||||
|
'X-API-SECRET' => config('ninja.api_secret'),
|
||||||
|
'X-API-TOKEN' => $this->token,
|
||||||
|
])->postJson('/api/v1/task_schedulers', $data);
|
||||||
|
|
||||||
|
$response->assertStatus(200);
|
||||||
|
|
||||||
|
$arr = $response->json();
|
||||||
|
|
||||||
|
$scheduler = Scheduler::find($this->decodePrimaryKey($arr['data']['id']));
|
||||||
|
|
||||||
|
$this->assertNotNull($scheduler);
|
||||||
|
|
||||||
|
$scheduler->service()->runTask();
|
||||||
|
|
||||||
|
$invoice = $invoice->fresh();
|
||||||
|
|
||||||
|
$this->assertEquals(40, $invoice->partial);
|
||||||
|
$this->assertEquals(now()->format('Y-m-d'), $invoice->partial_due_date->format('Y-m-d'));
|
||||||
|
|
||||||
|
$scheduler = $scheduler->fresh();
|
||||||
|
|
||||||
|
$this->assertEquals(now()->addDays(30)->format('Y-m-d'), $scheduler->next_run->format('Y-m-d'));
|
||||||
|
|
||||||
|
$this->travelTo(now()->addDays(30));
|
||||||
|
|
||||||
|
$scheduler->service()->runTask();
|
||||||
|
|
||||||
|
$invoice = $invoice->fresh();
|
||||||
|
|
||||||
|
$this->assertEquals(100, $invoice->partial);
|
||||||
|
$this->assertEquals(now()->format('Y-m-d'), $invoice->partial_due_date->format('Y-m-d'));
|
||||||
|
|
||||||
|
$this->travelBack();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function testPaymentScheduleCalculationsIsAmount()
|
||||||
|
{
|
||||||
|
$settings = $this->company->settings;
|
||||||
|
$settings->use_credits_payment = 'off';
|
||||||
|
$settings->use_unapplied_payment = 'off';
|
||||||
|
$this->company->settings = $settings;
|
||||||
|
$this->company->save();
|
||||||
|
|
||||||
|
$invoice = Invoice::factory()->create([
|
||||||
|
'company_id' => $this->company->id,
|
||||||
|
'user_id' => $this->user->id,
|
||||||
|
'client_id' => $this->client->id,
|
||||||
|
'date' => now()->format('Y-m-d'),
|
||||||
|
'due_date' => now()->addDays(30)->format('Y-m-d'),
|
||||||
|
'partial' => 0,
|
||||||
|
'partial_due_date' => null,
|
||||||
|
'amount' => 300.00,
|
||||||
|
'balance' => 300.00,
|
||||||
|
'status_id' => Invoice::STATUS_SENT,
|
||||||
|
]);
|
||||||
|
|
||||||
|
|
||||||
|
$data = [
|
||||||
|
'name' => 'A test payment schedule scheduler',
|
||||||
|
'frequency_id' => 0,
|
||||||
|
'next_run' => now()->format('Y-m-d'),
|
||||||
|
'template' => 'payment_schedule',
|
||||||
|
'parameters' => [
|
||||||
|
'invoice_id' => $invoice->hashed_id,
|
||||||
|
'auto_bill' => false,
|
||||||
|
'schedule' => [
|
||||||
|
[
|
||||||
|
'id' => 1,
|
||||||
|
'date' => now()->format('Y-m-d'),
|
||||||
|
'amount' => 40,
|
||||||
|
'is_amount' => true,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'id' => 2,
|
||||||
|
'date' => now()->addDays(30)->format('Y-m-d'),
|
||||||
|
'amount' => 60.00,
|
||||||
|
'is_amount' => true,
|
||||||
|
]
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
$response = $this->withHeaders([
|
||||||
|
'X-API-SECRET' => config('ninja.api_secret'),
|
||||||
|
'X-API-TOKEN' => $this->token,
|
||||||
|
])->postJson('/api/v1/task_schedulers', $data);
|
||||||
|
|
||||||
|
$response->assertStatus(200);
|
||||||
|
|
||||||
|
$arr = $response->json();
|
||||||
|
|
||||||
|
$scheduler = Scheduler::find($this->decodePrimaryKey($arr['data']['id']));
|
||||||
|
|
||||||
|
$this->assertNotNull($scheduler);
|
||||||
|
|
||||||
|
$scheduler->service()->runTask();
|
||||||
|
|
||||||
|
$invoice = $invoice->fresh();
|
||||||
|
|
||||||
|
$this->assertEquals(40, $invoice->partial);
|
||||||
|
$this->assertEquals(now()->format('Y-m-d'), $invoice->partial_due_date->format('Y-m-d'));
|
||||||
|
|
||||||
|
$scheduler = $scheduler->fresh();
|
||||||
|
|
||||||
|
$this->assertEquals(now()->addDays(30)->format('Y-m-d'), $scheduler->next_run->format('Y-m-d'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testPaymentScheduleCalculationsIsPercentage()
|
||||||
|
{
|
||||||
|
$settings = $this->company->settings;
|
||||||
|
$settings->use_credits_payment = 'off';
|
||||||
|
$settings->use_unapplied_payment = 'off';
|
||||||
|
$this->company->settings = $settings;
|
||||||
|
$this->company->save();
|
||||||
|
|
||||||
|
$invoice = Invoice::factory()->create([
|
||||||
|
'company_id' => $this->company->id,
|
||||||
|
'user_id' => $this->user->id,
|
||||||
|
'client_id' => $this->client->id,
|
||||||
|
'date' => now()->format('Y-m-d'),
|
||||||
|
'due_date' => now()->addDays(30)->format('Y-m-d'),
|
||||||
|
'partial' => 0,
|
||||||
|
'partial_due_date' => null,
|
||||||
|
'amount' => 300.00,
|
||||||
|
'balance' => 300.00,
|
||||||
|
'status_id' => Invoice::STATUS_SENT,
|
||||||
|
]);
|
||||||
|
|
||||||
|
|
||||||
|
$data = [
|
||||||
|
'name' => 'A test payment schedule scheduler',
|
||||||
|
'frequency_id' => 0,
|
||||||
|
'next_run' => now()->format('Y-m-d'),
|
||||||
|
'template' => 'payment_schedule',
|
||||||
|
'parameters' => [
|
||||||
|
'invoice_id' => $invoice->hashed_id,
|
||||||
|
'auto_bill' => false,
|
||||||
|
'schedule' => [
|
||||||
|
[
|
||||||
|
'id' => 1,
|
||||||
|
'date' => now()->format('Y-m-d'),
|
||||||
|
'amount' => 40,
|
||||||
|
'is_amount' => false,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'id' => 2,
|
||||||
|
'date' => now()->addDays(30)->format('Y-m-d'),
|
||||||
|
'amount' => 60.00,
|
||||||
|
'is_amount' => false,
|
||||||
|
]
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
$response = $this->withHeaders([
|
||||||
|
'X-API-SECRET' => config('ninja.api_secret'),
|
||||||
|
'X-API-TOKEN' => $this->token,
|
||||||
|
])->postJson('/api/v1/task_schedulers', $data);
|
||||||
|
|
||||||
|
$response->assertStatus(200);
|
||||||
|
|
||||||
|
$arr = $response->json();
|
||||||
|
|
||||||
|
$scheduler = Scheduler::find($this->decodePrimaryKey($arr['data']['id']));
|
||||||
|
|
||||||
|
$this->assertNotNull($scheduler);
|
||||||
|
|
||||||
|
$scheduler->service()->runTask();
|
||||||
|
|
||||||
|
$invoice = $invoice->fresh();
|
||||||
|
|
||||||
|
$this->assertEquals(120, $invoice->partial);
|
||||||
|
$this->assertEquals(now()->format('Y-m-d'), $invoice->partial_due_date->format('Y-m-d'));
|
||||||
|
}
|
||||||
|
|
||||||
public function testDuplicateInvoicePaymentSchedule()
|
public function testDuplicateInvoicePaymentSchedule()
|
||||||
{
|
{
|
||||||
|
|
@ -103,23 +327,7 @@ class SchedulerTest extends TestCase
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
// $data = [
|
|
||||||
// 'schedule' => [
|
|
||||||
// [
|
|
||||||
// 'id' => 1,
|
|
||||||
// 'date' => now()->format('Y-m-d'),
|
|
||||||
// 'amount' => 40,
|
|
||||||
// 'is_amount' => false,
|
|
||||||
// ],
|
|
||||||
// [
|
|
||||||
// 'id' => 2,
|
|
||||||
// 'date' => now()->addDays(30)->format('Y-m-d'),
|
|
||||||
// 'amount' => 60.00,
|
|
||||||
// 'is_amount' => false,
|
|
||||||
// ]
|
|
||||||
// ],
|
|
||||||
// 'auto_bill' => true,
|
|
||||||
// ];
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue