mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2024-11-05 18:52:44 +01:00
commit
7f31d076b6
@ -203,10 +203,8 @@ class BaseRepository
|
||||
$resource = explode('\\', $class->name)[2]; /** This will extract 'Invoice' from App\Models\Invoice */
|
||||
$lcfirst_resource_id = lcfirst($resource).'_id';
|
||||
|
||||
if ($class->name == Invoice::class || $class->name == Quote::class) {
|
||||
$state['starting_amount'] = $model->amount;
|
||||
}
|
||||
|
||||
$state['starting_amount'] = $model->amount;
|
||||
|
||||
if (! $model->id) {
|
||||
$company_defaults = $client->setCompanyDefaults($data, lcfirst($resource));
|
||||
$model->uses_inclusive_taxes = $client->getSetting('inclusive_taxes');
|
||||
@ -320,7 +318,10 @@ class BaseRepository
|
||||
}
|
||||
|
||||
if ($class->name == Credit::class) {
|
||||
|
||||
$model = $model->calc()->getCredit();
|
||||
|
||||
$model->ledger()->updateCreditBalance(($state['finished_amount'] - $state['starting_amount']));
|
||||
|
||||
if (! $model->design_id) {
|
||||
$model->design_id = $this->decodePrimaryKey($client->getSetting('credit_design_id'));
|
||||
|
@ -45,6 +45,7 @@ class MarkSent
|
||||
->applyNumber()
|
||||
->save();
|
||||
|
||||
|
||||
return $this->credit;
|
||||
}
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ use App\Models\PaymentHash;
|
||||
use App\Services\AbstractService;
|
||||
use App\Services\Client\ClientService;
|
||||
use App\Services\Payment\PaymentService;
|
||||
use App\Utils\Ninja;
|
||||
use App\Utils\Traits\GeneratesCounter;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
@ -30,7 +31,7 @@ class AutoBillInvoice extends AbstractService
|
||||
|
||||
private $client;
|
||||
|
||||
private $payment;
|
||||
private $used_credit = [];
|
||||
|
||||
public function __construct(Invoice $invoice)
|
||||
{
|
||||
@ -52,15 +53,15 @@ class AutoBillInvoice extends AbstractService
|
||||
if ((int)$this->invoice->balance == 0)
|
||||
return $this->invoice->service()->markPaid()->save();
|
||||
|
||||
$this->applyCreditPayment();
|
||||
$this->applyCreditPayment(); //if the credits cover the payments, we stop here, build the payment with credits and exit early
|
||||
|
||||
/* Determine $amount */
|
||||
if ($this->invoice->partial > 0)
|
||||
$amount = $this->invoice->partial;
|
||||
elseif($this->invoice->balance >0)
|
||||
elseif($this->invoice->balance > 0)
|
||||
$amount = $this->invoice->balance;
|
||||
else
|
||||
return $this->invoice;
|
||||
return $this->finalizePaymentUsingCredits();
|
||||
|
||||
$gateway_token = $this->getGateway($amount);
|
||||
|
||||
@ -86,6 +87,56 @@ class AutoBillInvoice extends AbstractService
|
||||
return $this->invoice;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the credits on file cover the invoice amount
|
||||
* the we create a matching payment using credits only
|
||||
*
|
||||
* @return Invoice $invoice
|
||||
*/
|
||||
private function finalizePaymentUsingCredits()
|
||||
{
|
||||
info("finalizing");
|
||||
info(print_r($this->used_credit,1));
|
||||
$amount = array_sum(array_column($this->used_credit, 'amount'));
|
||||
info("amount {$amount}");
|
||||
|
||||
$payment = PaymentFactory::create($this->invoice->company_id, $this->invoice->user_id);
|
||||
$payment->amount = $amount;
|
||||
$payment->client_id = $this->invoice->client_id;
|
||||
$payment->currency_id = $this->invoice->client->getSetting('currency_id');
|
||||
$payment->date = now();
|
||||
$payment->status_id = Payment::STATUS_COMPLETED;
|
||||
$payment->service()->applyNumber()->save();
|
||||
|
||||
$payment->invoices()->attach($this->invoice->id, ['amount' => $amount]);
|
||||
|
||||
$this->invoice->service()->setStatus(Invoice::STATUS_PAID)->save();
|
||||
|
||||
foreach($this->used_credit as $credit)
|
||||
{
|
||||
$payment->credits()->attach($credit['credit_id'], ['amount' => $credit['amount']]);
|
||||
}
|
||||
|
||||
$payment->ledger()
|
||||
->updatePaymentBalance($amount * -1)
|
||||
->save();
|
||||
|
||||
$this->invoice->client->service()
|
||||
->updateBalance($amount * -1)
|
||||
->updatePaidToDate($amount)
|
||||
->adjustCreditBalance($amount * -1)
|
||||
->save();
|
||||
|
||||
$this->invoice->ledger()
|
||||
->updateInvoiceBalance($amount * -1, 'Invoice payment using Credit')
|
||||
->updateCreditBalance($amount * -1, 'Credits used to pay down Invoice ' . $this->invoice->number)
|
||||
->save();
|
||||
|
||||
event(new PaymentWasCreated($payment, $payment->company, Ninja::eventVars()));
|
||||
|
||||
return $this->invoice->service()->setStatus(Invoice::STATUS_PAID)->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies credits to a payment prior to push
|
||||
* to the payment gateway
|
||||
@ -112,48 +163,50 @@ class AutoBillInvoice extends AbstractService
|
||||
$is_partial_amount = true;
|
||||
}
|
||||
|
||||
$this->payment = PaymentFactory::create($this->client->company_id, $this->client->user_id);
|
||||
$this->payment->save();
|
||||
$this->used_credit = [];
|
||||
|
||||
$available_credits->each(function($credit) use($is_partial_amount){
|
||||
foreach($available_credits as $key => $credit) {
|
||||
|
||||
//todo need to iterate until the partial or balance is completely consumed
|
||||
//by the credit, any remaining balance is then dealt with by
|
||||
//the gateway
|
||||
//each time a credit is applied SAVE the invoice
|
||||
if($is_partial_amount) {
|
||||
|
||||
//more credit than needed
|
||||
if($credit->balance >= $this->invoice->partial) {
|
||||
$this->used_credit[$key]['credit_id'] = $credit->id;
|
||||
$this->used_credit[$key]['amount'] = $this->invoice->partial;
|
||||
$this->invoice->balance -= $this->invoice->partial;
|
||||
$this->invoice->partial = 0;
|
||||
break;
|
||||
}
|
||||
else {
|
||||
$this->used_credit[$key]['credit_id'] = $credit->id;
|
||||
$this->used_credit[$key]['amount'] = $credit->balance;
|
||||
$this->invoice->partial -= $credit->balance;
|
||||
$this->invoice->balance -= $credit->balance;
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
//more credit than needed
|
||||
if($credit->balance >= $this->invoice->balance) {
|
||||
$this->used_credit[$key]['credit_id'] = $credit->id;
|
||||
$this->used_credit[$key]['amount'] = $this->invoice->balance;
|
||||
$this->invoice->balance = 0;
|
||||
break;
|
||||
}
|
||||
else {
|
||||
$this->used_credit[$key]['credit_id'] = $credit->id;
|
||||
$this->used_credit[$key]['amount'] = $credit->balance;
|
||||
$this->invoice->balance -= $credit->balance;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// if($credit->balance >= $amount){
|
||||
// //current credit covers the total amount
|
||||
|
||||
// }
|
||||
//return false to exit each loop
|
||||
});
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
private function buildPayment($credit, $is_partial_amount)
|
||||
{
|
||||
if($is_partial_amount) {
|
||||
|
||||
if($this->invoice->partial >= $credit->balance) {
|
||||
|
||||
$amount = $this->invoice->partial - $credit->balance;
|
||||
$this->invoice->partial -= $amount;
|
||||
|
||||
$this->payment->credits()->attach([
|
||||
$credit->id => ['amount' => $amount]
|
||||
]);
|
||||
|
||||
$this->payment->invoice()->attach([
|
||||
$this->invoice->id => ['amount' => $amount]
|
||||
]);
|
||||
|
||||
$this->applyPaymentToCredit($credit, $amount);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
private function applyPaymentToCredit($credit, $amount)
|
||||
@ -173,7 +226,6 @@ class AutoBillInvoice extends AbstractService
|
||||
|
||||
$credit = $credit->calc()->getCredit();
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -66,6 +66,8 @@ class LedgerService
|
||||
$company_ledger->save();
|
||||
|
||||
$this->entity->company_ledger()->save($company_ledger);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function updateCreditBalance($adjustment, $notes = '')
|
||||
|
@ -227,12 +227,6 @@ trait MockAccountData
|
||||
$this->invoice = InvoiceFactory::create($this->company->id, $this->user->id); //stub the company and user_id
|
||||
$this->invoice->client_id = $this->client->id;
|
||||
|
||||
// $this->invoice = Invoice::factory()->create([
|
||||
// 'user_id' => $this->user->id,
|
||||
// 'client_id' => $this->client->id,
|
||||
// 'company_id' => $this->company->id,
|
||||
// ]);
|
||||
|
||||
$this->invoice->line_items = $this->buildLineItems();
|
||||
$this->invoice->uses_inclusive_taxes = false;
|
||||
|
||||
@ -316,7 +310,6 @@ trait MockAccountData
|
||||
$this->credit->uses_inclusive_taxes = false;
|
||||
|
||||
$this->credit->save();
|
||||
|
||||
$this->credit->service()->createInvitations()->markSent();
|
||||
|
||||
$this->credit_calc = new InvoiceSum($this->credit);
|
||||
@ -325,6 +318,9 @@ trait MockAccountData
|
||||
$this->credit = $this->credit_calc->getCredit();
|
||||
$this->credit->service()->markSent();
|
||||
|
||||
$this->client->service()->adjustCreditBalance($this->credit->balance)->save();
|
||||
$this->credit->ledger()->updateCreditBalance($this->credit->balance)->save();
|
||||
|
||||
$contacts = $this->invoice->client->contacts;
|
||||
|
||||
$contacts->each(function ($contact) {
|
||||
|
57
tests/Unit/AutoBillInvoiceTest.php
Normal file
57
tests/Unit/AutoBillInvoiceTest.php
Normal file
@ -0,0 +1,57 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
namespace Tests\Unit;
|
||||
|
||||
use App\Models\Invoice;
|
||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||
use Tests\MockAccountData;
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @covers App\Services\Invoice\AutoBillInvoice
|
||||
*/
|
||||
class AutoBillInvoiceTest extends TestCase
|
||||
{
|
||||
use MockAccountData;
|
||||
use DatabaseTransactions;
|
||||
|
||||
public function setUp() :void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->makeTestData();
|
||||
}
|
||||
|
||||
public function testAutoBillFunctionality()
|
||||
{
|
||||
|
||||
// info("client balance = {$this->client->balance}");
|
||||
// info("invoice balance = {$this->invoice->balance}");
|
||||
|
||||
|
||||
$this->assertEquals($this->client->balance, 10);
|
||||
$this->assertEquals($this->client->paid_to_date, 0);
|
||||
$this->assertEquals($this->client->credit_balance, 10);
|
||||
|
||||
$this->invoice->service()->markSent()->autoBill()->save();
|
||||
|
||||
// info(print_r($this->invoice->payments()->first()->toArray(),1));
|
||||
$this->assertNotNull($this->invoice->payments());
|
||||
$this->assertEquals(10, $this->invoice->payments()->sum('payments.amount'));
|
||||
|
||||
//info(print_r($this->invoice->payments()->get(),1));
|
||||
$this->assertEquals($this->client->balance, 0);
|
||||
$this->assertEquals($this->client->paid_to_date, 10);
|
||||
$this->assertEquals($this->client->credit_balance, 0);
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user