mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2024-11-10 05:02:36 +01:00
Implement payment rules at application edge (FormRequest) (#3202)
* Ensure payments, invoice and credit amount balance in the validator prior to saving * additional payment validation rules and tests for processing payments * Factories for credits * Tests for payments * Working on updating a payment * Working on updating a payment * fixes for updating a payment * Working on Payment Tests * More tests for payments, formrequests * remove product_key as required from products
This commit is contained in:
parent
c1c016a5b2
commit
0878decf18
@ -75,4 +75,37 @@ class InvoiceItemFactory
|
|||||||
|
|
||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates an array of dummy data for invoice items
|
||||||
|
* @param int $items Number of line items to create
|
||||||
|
* @return array array of objects
|
||||||
|
*/
|
||||||
|
public static function generateCredit(int $items = 1) :array
|
||||||
|
{
|
||||||
|
$faker = \Faker\Factory::create();
|
||||||
|
|
||||||
|
$data = [];
|
||||||
|
|
||||||
|
for ($x=0; $x<$items; $x++) {
|
||||||
|
$item = self::create();
|
||||||
|
$item->quantity = $faker->numberBetween(-1, -10);
|
||||||
|
$item->cost = $faker->randomFloat(2, -1, -1000);
|
||||||
|
$item->line_total = $item->quantity * $item->cost;
|
||||||
|
$item->is_amount_discount = true;
|
||||||
|
$item->discount = $faker->numberBetween(1, 10);
|
||||||
|
$item->notes = $faker->realText(20);
|
||||||
|
$item->product_key = $faker->word();
|
||||||
|
$item->custom_value1 = $faker->realText(10);
|
||||||
|
$item->custom_value2 = $faker->realText(10);
|
||||||
|
$item->custom_value3 = $faker->realText(10);
|
||||||
|
$item->custom_value4 = $faker->realText(10);
|
||||||
|
$item->tax_name1 = 'GST';
|
||||||
|
$item->tax_rate1 = 10.00;
|
||||||
|
|
||||||
|
$data[] = $item;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -412,7 +412,7 @@ class PaymentController extends BaseController
|
|||||||
if($request->entityIsDeleted($payment))
|
if($request->entityIsDeleted($payment))
|
||||||
return $request->disallowUpdate();
|
return $request->disallowUpdate();
|
||||||
|
|
||||||
$payment = $this->payment_repo->save(request(), $payment);
|
$payment = $this->payment_repo->save($request, $payment);
|
||||||
|
|
||||||
return $this->itemResponse($payment);
|
return $this->itemResponse($payment);
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,8 @@ namespace App\Http\Requests\Payment;
|
|||||||
|
|
||||||
use App\Http\Requests\Request;
|
use App\Http\Requests\Request;
|
||||||
use App\Http\ValidationRules\ValidPayableInvoicesRule;
|
use App\Http\ValidationRules\ValidPayableInvoicesRule;
|
||||||
|
use App\Http\ValidationRules\PaymentAmountsBalanceRule;
|
||||||
|
use App\Http\ValidationRules\ValidCreditsPresentRule;
|
||||||
use App\Models\Payment;
|
use App\Models\Payment;
|
||||||
use App\Utils\Traits\MakesHash;
|
use App\Utils\Traits\MakesHash;
|
||||||
|
|
||||||
@ -46,11 +48,20 @@ class StorePaymentRequest extends Request
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (isset($input['invoices']) && is_array($input['invoices']) === false) {
|
if (isset($input['invoices']) && is_array($input['invoices']) === false) {
|
||||||
$input['invoices'] = null;
|
$input['invoices'] = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isset($input['credits']) && is_array($input['credits']) !== false) {
|
||||||
|
foreach ($input['credits'] as $key => $value) {
|
||||||
|
$input['credits'][$key]['id'] = $this->decodePrimaryKey($value['id']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($input['credits']) && is_array($input['credits']) === false) {
|
||||||
|
$input['credits'] = null;
|
||||||
|
}
|
||||||
|
|
||||||
$this->replace($input);
|
$this->replace($input);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -58,10 +69,11 @@ class StorePaymentRequest extends Request
|
|||||||
{
|
{
|
||||||
$rules = [
|
$rules = [
|
||||||
'amount' => 'numeric|required',
|
'amount' => 'numeric|required',
|
||||||
|
'amount' => [new PaymentAmountsBalanceRule(),new ValidCreditsPresentRule()],
|
||||||
'date' => 'required',
|
'date' => 'required',
|
||||||
'client_id' => 'required',
|
'client_id' => 'required',
|
||||||
'invoices' => new ValidPayableInvoicesRule(),
|
'invoices' => new ValidPayableInvoicesRule(),
|
||||||
'number' => 'nullable|unique',
|
'number' => 'nullable',
|
||||||
];
|
];
|
||||||
|
|
||||||
return $rules;
|
return $rules;
|
||||||
|
@ -12,13 +12,16 @@
|
|||||||
namespace App\Http\Requests\Payment;
|
namespace App\Http\Requests\Payment;
|
||||||
|
|
||||||
use App\Http\Requests\Request;
|
use App\Http\Requests\Request;
|
||||||
|
use App\Http\ValidationRules\PaymentAppliedValidAmount;
|
||||||
|
use App\Http\ValidationRules\ValidCreditsPresentRule;
|
||||||
use App\Utils\Traits\ChecksEntityStatus;
|
use App\Utils\Traits\ChecksEntityStatus;
|
||||||
|
use App\Utils\Traits\MakesHash;
|
||||||
use Illuminate\Validation\Rule;
|
use Illuminate\Validation\Rule;
|
||||||
|
|
||||||
class UpdatePaymentRequest extends Request
|
class UpdatePaymentRequest extends Request
|
||||||
{
|
{
|
||||||
use ChecksEntityStatus;
|
use ChecksEntityStatus;
|
||||||
|
use MakesHash;
|
||||||
/**
|
/**
|
||||||
* Determine if the user is authorized to make this request.
|
* Determine if the user is authorized to make this request.
|
||||||
*
|
*
|
||||||
@ -34,8 +37,38 @@ class UpdatePaymentRequest extends Request
|
|||||||
public function rules()
|
public function rules()
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
|
'invoices' => ['required','array','min:1',new PaymentAppliedValidAmount,new ValidCreditsPresentRule],
|
||||||
'documents' => 'mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx',
|
'documents' => 'mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx',
|
||||||
'client_id' => 'required',
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function prepareForValidation()
|
||||||
|
{
|
||||||
|
$input = $this->all();
|
||||||
|
|
||||||
|
if(isset($input['client_id']))
|
||||||
|
unset($input['client_id']);
|
||||||
|
|
||||||
|
if(isset($input['amount']))
|
||||||
|
unset($input['amount']);
|
||||||
|
|
||||||
|
if(isset($input['type_id']))
|
||||||
|
unset($input['type_id']);
|
||||||
|
|
||||||
|
if(isset($input['date']))
|
||||||
|
unset($input['date']);
|
||||||
|
|
||||||
|
if(isset($input['transaction_reference']))
|
||||||
|
unset($input['transaction_reference']);
|
||||||
|
|
||||||
|
if(isset($input['number']))
|
||||||
|
unset($input['number']);
|
||||||
|
|
||||||
|
if (isset($input['invoices']) && is_array($input['invoices']) !== false) {
|
||||||
|
foreach ($input['invoices'] as $key => $value) {
|
||||||
|
$input['invoices'][$key]['id'] = $this->decodePrimaryKey($value['id']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$this->replace($input);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,7 @@ class StoreProductRequest extends Request
|
|||||||
public function rules()
|
public function rules()
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'product_key' => 'required|unique:products,product_key,null,null,company_id,'.auth()->user()->companyId(),
|
//'product_key' => 'required|unique:products,product_key,null,null,company_id,'.auth()->user()->companyId(),
|
||||||
'cost' => 'numeric',
|
'cost' => 'numeric',
|
||||||
'price' => 'numeric',
|
'price' => 'numeric',
|
||||||
'quantity' => 'numeric',
|
'quantity' => 'numeric',
|
||||||
|
72
app/Http/ValidationRules/PaymentAmountsBalanceRule.php
Normal file
72
app/Http/ValidationRules/PaymentAmountsBalanceRule.php
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
<?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 App\Http\ValidationRules;
|
||||||
|
|
||||||
|
use App\Libraries\MultiDB;
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Contracts\Validation\Rule;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class NewUniqueUserRule
|
||||||
|
* @package App\Http\ValidationRules
|
||||||
|
*/
|
||||||
|
class PaymentAmountsBalanceRule implements Rule
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $attribute
|
||||||
|
* @param mixed $value
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function passes($attribute, $value)
|
||||||
|
{
|
||||||
|
return $this->calculateAmounts();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function message()
|
||||||
|
{
|
||||||
|
return 'Amounts do not balance correctly.';
|
||||||
|
}
|
||||||
|
|
||||||
|
private function calculateAmounts() :bool
|
||||||
|
{
|
||||||
|
$data = [];
|
||||||
|
$payment_amounts = 0;
|
||||||
|
$invoice_amounts = 0;
|
||||||
|
|
||||||
|
$payment_amounts += request()->input('amount');
|
||||||
|
|
||||||
|
if(request()->input('credits') && is_array(request()->input('credits')))
|
||||||
|
{
|
||||||
|
foreach(request()->input('credits') as $credit)
|
||||||
|
{
|
||||||
|
$payment_amounts += $credit['amount'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(request()->input('invoices') && is_array(request()->input('invoices')))
|
||||||
|
{
|
||||||
|
foreach(request()->input('invoices') as $invoice)
|
||||||
|
{
|
||||||
|
$invoice_amounts =+ $invoice['amount'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return true; // if no invoices are present, then this is an unapplied payment, let this pass validation!
|
||||||
|
|
||||||
|
return $payment_amounts >= $invoice_amounts;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
79
app/Http/ValidationRules/PaymentAppliedValidAmount.php
Normal file
79
app/Http/ValidationRules/PaymentAppliedValidAmount.php
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
<?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 App\Http\ValidationRules;
|
||||||
|
|
||||||
|
use App\Libraries\MultiDB;
|
||||||
|
use App\Models\Payment;
|
||||||
|
use App\Models\User;
|
||||||
|
use App\Utils\Traits\MakesHash;
|
||||||
|
use Illuminate\Contracts\Validation\Rule;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class PaymentAppliedValidAmount
|
||||||
|
* @package App\Http\ValidationRules
|
||||||
|
*/
|
||||||
|
class PaymentAppliedValidAmount implements Rule
|
||||||
|
{
|
||||||
|
use MakesHash;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $attribute
|
||||||
|
* @param mixed $value
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function passes($attribute, $value)
|
||||||
|
{
|
||||||
|
return $this->calculateAmounts();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function message()
|
||||||
|
{
|
||||||
|
return 'Insufficient applied amount remaining to cover payment.';
|
||||||
|
}
|
||||||
|
|
||||||
|
private function calculateAmounts() :bool
|
||||||
|
{
|
||||||
|
|
||||||
|
$payment = Payment::whereId($this->decodePrimaryKey(request()->segment(4)))->company()->first();
|
||||||
|
|
||||||
|
if(!$payment)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
$data = [];
|
||||||
|
$payment_amounts = 0;
|
||||||
|
$invoice_amounts = 0;
|
||||||
|
|
||||||
|
$payment_amounts = $payment->amount - $payment->applied;
|
||||||
|
|
||||||
|
if(request()->input('credits') && is_array(request()->input('credits')))
|
||||||
|
{
|
||||||
|
foreach(request()->input('credits') as $credit)
|
||||||
|
{
|
||||||
|
$payment_amounts += $credit['amount'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(request()->input('invoices') && is_array(request()->input('invoices')))
|
||||||
|
{
|
||||||
|
foreach(request()->input('invoices') as $invoice)
|
||||||
|
{
|
||||||
|
$invoice_amounts =+ $invoice['amount'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $payment_amounts >= $invoice_amounts;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
65
app/Http/ValidationRules/ValidCreditsPresentRule.php
Normal file
65
app/Http/ValidationRules/ValidCreditsPresentRule.php
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
<?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 App\Http\ValidationRules;
|
||||||
|
|
||||||
|
use App\Libraries\MultiDB;
|
||||||
|
use App\Models\Credit;
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Contracts\Validation\Rule;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class ValidCreditsPresentRule
|
||||||
|
* @package App\Http\ValidationRules
|
||||||
|
*/
|
||||||
|
class ValidCreditsPresentRule implements Rule
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $attribute
|
||||||
|
* @param mixed $value
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function passes($attribute, $value)
|
||||||
|
{
|
||||||
|
return $this->validCreditsPresent();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function message()
|
||||||
|
{
|
||||||
|
return 'Attempting to use one or more invalid credits';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private function validCreditsPresent() :bool
|
||||||
|
{
|
||||||
|
$data = [];
|
||||||
|
//todo need to ensure the clients credits are here not random ones!
|
||||||
|
|
||||||
|
if(request()->input('credits') && is_array(request()->input('credits')))
|
||||||
|
{
|
||||||
|
foreach(request()->input('credits') as $credit)
|
||||||
|
{
|
||||||
|
$cred = Credit::find($credit['id']);
|
||||||
|
|
||||||
|
if($cred->balance >= $credit['amount'])
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -1,27 +1,27 @@
|
|||||||
<?php
|
<?php
|
||||||
/**
|
/**
|
||||||
* Invoice Ninja (https://invoiceninja.com)
|
* Credit Ninja (https://invoiceninja.com)
|
||||||
*
|
*
|
||||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||||
*
|
*
|
||||||
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
|
* @copyright Copyright (c) 2020. Credit Ninja LLC (https://invoiceninja.com)
|
||||||
*
|
*
|
||||||
* @license https://opensource.org/licenses/AAL
|
* @license https://opensource.org/licenses/AAL
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace App\Jobs\Invoice;
|
namespace App\Jobs\Credit;
|
||||||
|
|
||||||
use App\Events\Payment\PaymentWasCreated;
|
use App\Events\Payment\PaymentWasCreated;
|
||||||
use App\Factory\PaymentFactory;
|
use App\Factory\PaymentFactory;
|
||||||
use App\Jobs\Client\UpdateClientBalance;
|
use App\Jobs\Client\UpdateClientBalance;
|
||||||
use App\Jobs\Client\UpdateClientPaidToDate;
|
use App\Jobs\Client\UpdateClientPaidToDate;
|
||||||
use App\Jobs\Company\UpdateCompanyLedgerWithPayment;
|
use App\Jobs\Company\UpdateCompanyLedgerWithPayment;
|
||||||
use App\Jobs\Invoice\ApplyPaymentToInvoice;
|
use App\Jobs\Credit\ApplyPaymentToCredit;
|
||||||
use App\Libraries\MultiDB;
|
use App\Libraries\MultiDB;
|
||||||
use App\Models\Company;
|
use App\Models\Company;
|
||||||
use App\Models\Invoice;
|
use App\Models\Credit;
|
||||||
use App\Models\Payment;
|
use App\Models\Payment;
|
||||||
use App\Repositories\InvoiceRepository;
|
use App\Repositories\CreditRepository;
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
use Illuminate\Foundation\Bus\Dispatchable;
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
@ -32,7 +32,7 @@ class ApplyCreditPayment implements ShouldQueue
|
|||||||
{
|
{
|
||||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
public $invoice;
|
public $credit;
|
||||||
|
|
||||||
public $payment;
|
public $payment;
|
||||||
|
|
||||||
@ -45,9 +45,9 @@ class ApplyCreditPayment implements ShouldQueue
|
|||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function __construct(Invoice $invoice, Payment $payment, float $amount, Company $company)
|
public function __construct(Credit $credit, Payment $payment, float $amount, Company $company)
|
||||||
{
|
{
|
||||||
$this->invoice = $invoice;
|
$this->credit = $credit;
|
||||||
$this->payment = $payment;
|
$this->payment = $payment;
|
||||||
$this->amount = $amount;
|
$this->amount = $amount;
|
||||||
$this->company = $company;
|
$this->company = $company;
|
||||||
@ -63,46 +63,25 @@ class ApplyCreditPayment implements ShouldQueue
|
|||||||
{
|
{
|
||||||
MultiDB::setDB($this->company->db);
|
MultiDB::setDB($this->company->db);
|
||||||
|
|
||||||
UpdateCompanyLedgerWithPayment::dispatchNow($this->payment, ($this->amount*-1), $this->company);
|
|
||||||
UpdateClientBalance::dispatchNow($this->payment->client, $this->amount*-1, $this->company);
|
|
||||||
UpdateClientPaidToDate::dispatchNow($this->payment->client, $this->amount, $this->company);
|
|
||||||
|
|
||||||
/* Update Pivot Record amount */
|
/* Update Pivot Record amount */
|
||||||
$this->payment->invoices->each(function ($inv) {
|
$this->payment->credits->each(function ($cred) {
|
||||||
if ($inv->id == $this->invoice->id) {
|
if ($cred->id == $this->credit->id) {
|
||||||
$inv->pivot->amount = $this->amount;
|
$cred->pivot->amount = $this->amount;
|
||||||
$inv->pivot->save();
|
$cred->pivot->save();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if ($this->invoice->hasPartial()) {
|
$credit_balance = $this->credit->balance;
|
||||||
//is partial and amount is exactly the partial amount
|
|
||||||
if ($this->invoice->partial == $this->amount) {
|
if ($this->amount == $credit_balance) { //total credit applied.
|
||||||
$this->invoice->clearPartial();
|
$this->credit->setStatus(Credit::STATUS_APPLIED);
|
||||||
$this->invoice->setDueDate();
|
$this->credit->updateBalance($this->amount*-1);
|
||||||
$this->invoice->setStatus(Invoice::STATUS_PARTIAL);
|
} elseif($this->amount < $credit_balance) { //compare number appropriately
|
||||||
$this->invoice->updateBalance($this->amount*-1);
|
$this->credit->setStatus(Credit::PARTIAL);
|
||||||
} elseif ($this->invoice->partial > 0 && $this->invoice->partial > $this->amount) { //partial amount exists, but the amount is less than the partial amount
|
$this->credit->updateBalance($this->amount*-1);
|
||||||
$this->invoice->partial -= $this->amount;
|
|
||||||
$this->invoice->updateBalance($this->amount*-1);
|
|
||||||
} elseif ($this->invoice->partial > 0 && $this->invoice->partial < $this->amount) { //partial exists and the amount paid is GREATER than the partial amount
|
|
||||||
$this->invoice->clearPartial();
|
|
||||||
$this->invoice->setDueDate();
|
|
||||||
$this->invoice->setStatus(Invoice::STATUS_PARTIAL);
|
|
||||||
$this->invoice->updateBalance($this->amount*-1);
|
|
||||||
}
|
|
||||||
} elseif ($this->amount == $this->invoice->balance) { //total invoice paid.
|
|
||||||
$this->invoice->clearPartial();
|
|
||||||
//$this->invoice->setDueDate();
|
|
||||||
$this->invoice->setStatus(Invoice::STATUS_PAID);
|
|
||||||
$this->invoice->updateBalance($this->amount*-1);
|
|
||||||
} elseif($this->amount < $this->invoice->balance) { //partial invoice payment made
|
|
||||||
$this->invoice->clearPartial();
|
|
||||||
$this->invoice->updateBalance($this->amount*-1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Update Payment Applied Amount*/
|
/* Update Payment Applied Amount*/
|
||||||
$this->payment->applied += $this->amount;
|
|
||||||
$this->payment->save();
|
$this->payment->save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,7 +65,7 @@ class ApplyInvoicePayment implements ShouldQueue
|
|||||||
|
|
||||||
UpdateCompanyLedgerWithPayment::dispatchNow($this->payment, ($this->amount*-1), $this->company);
|
UpdateCompanyLedgerWithPayment::dispatchNow($this->payment, ($this->amount*-1), $this->company);
|
||||||
UpdateClientBalance::dispatchNow($this->payment->client, $this->amount*-1, $this->company);
|
UpdateClientBalance::dispatchNow($this->payment->client, $this->amount*-1, $this->company);
|
||||||
UpdateClientPaidToDate::dispatchNow($this->payment->client, $this->amount, $this->company);
|
//UpdateClientPaidToDate::dispatchNow($this->payment->client, $this->amount, $this->company);
|
||||||
|
|
||||||
/* Update Pivot Record amount */
|
/* Update Pivot Record amount */
|
||||||
$this->payment->invoices->each(function ($inv) {
|
$this->payment->invoices->each(function ($inv) {
|
||||||
@ -102,8 +102,8 @@ class ApplyInvoicePayment implements ShouldQueue
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Update Payment Applied Amount*/
|
/* Update Payment Applied Amount*/
|
||||||
$this->payment->applied += $this->amount;
|
// $this->payment->applied += $this->amount;
|
||||||
$this->payment->save();
|
// $this->payment->save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -130,4 +130,38 @@ class Credit extends BaseModel
|
|||||||
|
|
||||||
return $credit_calc->build();
|
return $credit_calc->build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param float $balance_adjustment
|
||||||
|
*/
|
||||||
|
public function updateBalance($balance_adjustment)
|
||||||
|
{
|
||||||
|
if ($this->is_deleted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$balance_adjustment = floatval($balance_adjustment);
|
||||||
|
|
||||||
|
$this->balance = $this->balance + $balance_adjustment;
|
||||||
|
|
||||||
|
if ($this->balance == 0) {
|
||||||
|
$this->status_id = self::STATUS_APPLIED;
|
||||||
|
$this->save();
|
||||||
|
//event(new InvoiceWasPaid($this, $this->company));//todo
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setStatus($status)
|
||||||
|
{
|
||||||
|
$this->status_id = $status;
|
||||||
|
$this->save();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@ namespace App\Repositories;
|
|||||||
|
|
||||||
use App\Events\Payment\PaymentWasCreated;
|
use App\Events\Payment\PaymentWasCreated;
|
||||||
use App\Factory\CreditFactory;
|
use App\Factory\CreditFactory;
|
||||||
|
use App\Jobs\Client\UpdateClientPaidToDate;
|
||||||
use App\Jobs\Company\UpdateCompanyLedgerWithPayment;
|
use App\Jobs\Company\UpdateCompanyLedgerWithPayment;
|
||||||
use App\Jobs\Invoice\ApplyClientPayment;
|
use App\Jobs\Invoice\ApplyClientPayment;
|
||||||
use App\Jobs\Invoice\ApplyInvoicePayment;
|
use App\Jobs\Invoice\ApplyInvoicePayment;
|
||||||
@ -45,7 +46,7 @@ class PaymentRepository extends BaseRepository
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Saves a payment.
|
* Saves and updates a payment. //todo refactor to handle refunds and payments.
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* @param Request $request the request object
|
* @param Request $request the request object
|
||||||
@ -54,22 +55,32 @@ class PaymentRepository extends BaseRepository
|
|||||||
*/
|
*/
|
||||||
public function save(Request $request, Payment $payment) : ?Payment
|
public function save(Request $request, Payment $payment) : ?Payment
|
||||||
{
|
{
|
||||||
//todo this save() only works for new payments... will fail if a Payment is updated and saved through here.
|
|
||||||
$payment->fill($request->input());
|
|
||||||
|
|
||||||
|
//todo this save() only works for new payments... will fail if a Payment is updated and saved through here.
|
||||||
|
$payment->fill($request->all());
|
||||||
$payment->status_id = Payment::STATUS_COMPLETED;
|
$payment->status_id = Payment::STATUS_COMPLETED;
|
||||||
$payment->number = $payment->client->getNextPaymentNumber($payment->client);
|
|
||||||
|
|
||||||
$payment->save();
|
$payment->save();
|
||||||
|
|
||||||
|
if(!$payment->number)
|
||||||
|
$payment->number = $payment->client->getNextPaymentNumber($payment->client);
|
||||||
|
|
||||||
|
//we only ever update the ACTUAL amount of money transferred
|
||||||
|
UpdateClientPaidToDate::dispatchNow($payment->client, $payment->amount, $payment->company);
|
||||||
|
|
||||||
|
$invoice_totals = 0;
|
||||||
|
$credit_totals = 0;
|
||||||
|
|
||||||
if ($request->input('invoices') && is_array($request->input('invoices'))) {
|
if ($request->input('invoices') && is_array($request->input('invoices'))) {
|
||||||
|
|
||||||
|
$invoice_totals = array_sum(array_column($request->input('invoices'),'amount'));
|
||||||
|
|
||||||
$invoices = Invoice::whereIn('id', array_column($request->input('invoices'), 'id'))->company()->get();
|
$invoices = Invoice::whereIn('id', array_column($request->input('invoices'), 'id'))->company()->get();
|
||||||
|
|
||||||
$payment->invoices()->saveMany($invoices);
|
$payment->invoices()->saveMany($invoices);
|
||||||
|
|
||||||
foreach ($request->input('invoices') as $paid_invoice) {
|
foreach ($request->input('invoices') as $paid_invoice) {
|
||||||
$invoice = Invoice::whereId($this->decodePrimaryKey($paid_invoice['id'])->company()->first();
|
$invoice = Invoice::whereId($paid_invoice['id'])->company()->first();
|
||||||
|
|
||||||
if ($invoice) {
|
if ($invoice) {
|
||||||
ApplyInvoicePayment::dispatchNow($invoice, $payment, $paid_invoice['amount'], $invoice->company);
|
ApplyInvoicePayment::dispatchNow($invoice, $payment, $paid_invoice['amount'], $invoice->company);
|
||||||
@ -82,13 +93,16 @@ class PaymentRepository extends BaseRepository
|
|||||||
|
|
||||||
if($request->input('credits') && is_array($request->input('credits')))
|
if($request->input('credits') && is_array($request->input('credits')))
|
||||||
{
|
{
|
||||||
|
|
||||||
|
$credit_totals = array_sum(array_column($request->input('credits'),'amount'));
|
||||||
|
|
||||||
$credits = Credit::whereIn('id', array_column($request->input('credits'), 'id'))->company()->get();
|
$credits = Credit::whereIn('id', array_column($request->input('credits'), 'id'))->company()->get();
|
||||||
|
|
||||||
$payment->credits()->saveMany($credits);
|
$payment->credits()->saveMany($credits);
|
||||||
|
|
||||||
foreach ($request->input('credits') as $paid_credit)
|
foreach ($request->input('credits') as $paid_credit)
|
||||||
{
|
{
|
||||||
$credit = Credit::whereId($this->decodePrimaryKey($paid_credit['id'])->company()->first();
|
$credit = Credit::whereId($paid_credit['id'])->company()->first();
|
||||||
|
|
||||||
if($credit)
|
if($credit)
|
||||||
ApplyCreditPayment::dispatchNow($paid_credit, $payment, $paid_credit['amount'], $credit->company);
|
ApplyCreditPayment::dispatchNow($paid_credit, $payment, $paid_credit['amount'], $credit->company);
|
||||||
@ -98,7 +112,15 @@ class PaymentRepository extends BaseRepository
|
|||||||
|
|
||||||
event(new PaymentWasCreated($payment, $payment->company));
|
event(new PaymentWasCreated($payment, $payment->company));
|
||||||
|
|
||||||
|
$invoice_totals -= $credit_totals;
|
||||||
|
|
||||||
|
if($invoice_totals == $payment->amount)
|
||||||
|
$payment->applied = $payment->amount;
|
||||||
|
elseif($invoice_totals < $payment->amount)
|
||||||
|
$payment->applied = $invoice_totals;
|
||||||
|
|
||||||
//UpdateInvoicePayment::dispatchNow($payment);
|
//UpdateInvoicePayment::dispatchNow($payment);
|
||||||
|
$payment->save();
|
||||||
|
|
||||||
return $payment->fresh();
|
return $payment->fresh();
|
||||||
}
|
}
|
||||||
@ -124,8 +146,22 @@ class PaymentRepository extends BaseRepository
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private function applyPayment(Request $request, Payment $payment) :?Payment
|
private function applyPayment(Request $request, Payment $payment) :?Payment
|
||||||
{
|
{
|
||||||
|
$invoice_totals = array_sum(array_column($request->input('invoices'),'amount'));
|
||||||
|
|
||||||
|
$invoices = Invoice::whereIn('id', array_column($request->input('invoices'), 'id'))->company()->get();
|
||||||
|
|
||||||
|
$payment->invoices()->saveMany($invoices);
|
||||||
|
|
||||||
|
foreach ($request->input('invoices') as $paid_invoice) {
|
||||||
|
$invoice = Invoice::whereId($paid_invoice['id'])->company()->first();
|
||||||
|
|
||||||
|
if ($invoice) {
|
||||||
|
ApplyInvoicePayment::dispatchNow($invoice, $payment, $paid_invoice['amount'], $invoice->company);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -170,6 +206,8 @@ class PaymentRepository extends BaseRepository
|
|||||||
$client = $payment->client;
|
$client = $payment->client;
|
||||||
$client->paid_to_date += $invoice_total_adjustment;
|
$client->paid_to_date += $invoice_total_adjustment;
|
||||||
|
|
||||||
|
$payment->save();
|
||||||
|
$client->save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
32
database/factories/CreditFactory.php
Normal file
32
database/factories/CreditFactory.php
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use App\DataMapper\ClientSettings;
|
||||||
|
use App\DataMapper\CompanySettings;
|
||||||
|
use App\Factory\InvoiceItemFactory;
|
||||||
|
use Faker\Generator as Faker;
|
||||||
|
|
||||||
|
$factory->define(App\Models\Credit::class, function (Faker $faker) {
|
||||||
|
return [
|
||||||
|
'status_id' => App\Models\Credit::STATUS_DRAFT,
|
||||||
|
'number' => $faker->ean13(),
|
||||||
|
'discount' => $faker->numberBetween(1,10),
|
||||||
|
'is_amount_discount' => (bool)random_int(0,1),
|
||||||
|
'tax_name1' => 'GST',
|
||||||
|
'tax_rate1' => 10,
|
||||||
|
'tax_name2' => 'VAT',
|
||||||
|
'tax_rate2' => 17.5,
|
||||||
|
//'tax_name3' => 'THIRDTAX',
|
||||||
|
//'tax_rate3' => 5,
|
||||||
|
// 'custom_value1' => $faker->numberBetween(1,4),
|
||||||
|
// 'custom_value2' => $faker->numberBetween(1,4),
|
||||||
|
// 'custom_value3' => $faker->numberBetween(1,4),
|
||||||
|
// 'custom_value4' => $faker->numberBetween(1,4),
|
||||||
|
'is_deleted' => false,
|
||||||
|
'po_number' => $faker->text(10),
|
||||||
|
'date' => $faker->date(),
|
||||||
|
'due_date' => $faker->date(),
|
||||||
|
'line_items' => InvoiceItemFactory::generateCredit(5),
|
||||||
|
'backup' => '',
|
||||||
|
'terms' => $faker->text(500),
|
||||||
|
];
|
||||||
|
});
|
@ -100,7 +100,7 @@ class ClientApiTest extends TestCase
|
|||||||
|
|
||||||
$arr = $response->json();
|
$arr = $response->json();
|
||||||
|
|
||||||
$this->assertNull($arr['data']['deleted_at']);
|
$this->assertNull($arr['data']['archived_at']);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testClientArchived()
|
public function testClientArchived()
|
||||||
@ -117,7 +117,7 @@ class ClientApiTest extends TestCase
|
|||||||
|
|
||||||
$arr = $response->json();
|
$arr = $response->json();
|
||||||
\Log::error($arr);
|
\Log::error($arr);
|
||||||
$this->assertNotNull($arr['data'][0]['deleted_at']);
|
$this->assertNotNull($arr['data'][0]['archived_at']);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testClientRestored()
|
public function testClientRestored()
|
||||||
@ -134,7 +134,7 @@ class ClientApiTest extends TestCase
|
|||||||
|
|
||||||
$arr = $response->json();
|
$arr = $response->json();
|
||||||
|
|
||||||
$this->assertNull($arr['data'][0]['deleted_at']);
|
$this->assertNull($arr['data'][0]['archived_at']);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testClientDeleted()
|
public function testClientDeleted()
|
||||||
|
@ -18,7 +18,6 @@ use Illuminate\Foundation\Testing\DatabaseTransactions;
|
|||||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
use Illuminate\Foundation\Testing\WithFaker;
|
use Illuminate\Foundation\Testing\WithFaker;
|
||||||
use Illuminate\Support\Carbon;
|
use Illuminate\Support\Carbon;
|
||||||
use Illuminate\Support\Facades\Log;
|
|
||||||
use Illuminate\Support\Facades\Session;
|
use Illuminate\Support\Facades\Session;
|
||||||
use Illuminate\Validation\ValidationException;
|
use Illuminate\Validation\ValidationException;
|
||||||
use Tests\MockAccountData;
|
use Tests\MockAccountData;
|
||||||
@ -35,6 +34,7 @@ class PaymentTest extends TestCase
|
|||||||
use MakesHash;
|
use MakesHash;
|
||||||
use DatabaseTransactions;
|
use DatabaseTransactions;
|
||||||
use MockAccountData;
|
use MockAccountData;
|
||||||
|
|
||||||
public function setUp() :void
|
public function setUp() :void
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -74,8 +74,9 @@ class PaymentTest extends TestCase
|
|||||||
|
|
||||||
$client = Client::all()->first();
|
$client = Client::all()->first();
|
||||||
|
|
||||||
factory(\App\Models\Payment::class, 1)->create(['user_id' => $this->user->id, 'company_id' => $this->company->id, 'client_id' => $client->id]);
|
factory(\App\Models\Payment::class, 1)->create(
|
||||||
|
['user_id' => $this->user->id, 'company_id' => $this->company->id, 'client_id' => $client->id]
|
||||||
|
);
|
||||||
|
|
||||||
$response = $this->withHeaders([
|
$response = $this->withHeaders([
|
||||||
'X-API-SECRET' => config('ninja.api_secret'),
|
'X-API-SECRET' => config('ninja.api_secret'),
|
||||||
@ -319,7 +320,7 @@ class PaymentTest extends TestCase
|
|||||||
catch(ValidationException $e) {
|
catch(ValidationException $e) {
|
||||||
$message = json_decode($e->validator->getMessageBag(),1);
|
$message = json_decode($e->validator->getMessageBag(),1);
|
||||||
$this->assertNotNull($message);
|
$this->assertNotNull($message);
|
||||||
\Log::error($message);
|
//\Log::error($message);
|
||||||
}
|
}
|
||||||
|
|
||||||
if($response) {
|
if($response) {
|
||||||
@ -466,4 +467,472 @@ class PaymentTest extends TestCase
|
|||||||
$this->assertEquals($invoice->balance, 8);
|
$this->assertEquals($invoice->balance, 8);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testPaymentValidationAmount()
|
||||||
|
{
|
||||||
|
|
||||||
|
$this->invoice = null;
|
||||||
|
|
||||||
|
$client = ClientFactory::create($this->company->id, $this->user->id);
|
||||||
|
$client->save();
|
||||||
|
|
||||||
|
$this->invoice = InvoiceFactory::create($this->company->id,$this->user->id);//stub the company and user_id
|
||||||
|
$this->invoice->client_id = $client->id;
|
||||||
|
|
||||||
|
$this->invoice->partial = 5.0;
|
||||||
|
$this->invoice->line_items = $this->buildLineItems();
|
||||||
|
$this->invoice->uses_inclusive_taxes = false;
|
||||||
|
|
||||||
|
$this->invoice->save();
|
||||||
|
|
||||||
|
$this->invoice_calc = new InvoiceSum($this->invoice);
|
||||||
|
$this->invoice_calc->build();
|
||||||
|
|
||||||
|
$this->invoice = $this->invoice_calc->getInvoice();
|
||||||
|
$this->invoice->save();
|
||||||
|
$this->invoice->markSent();
|
||||||
|
$this->invoice->save();
|
||||||
|
|
||||||
|
|
||||||
|
$data = [
|
||||||
|
'amount' => 1.0,
|
||||||
|
'client_id' => $client->hashed_id,
|
||||||
|
'invoices' => [
|
||||||
|
[
|
||||||
|
'id' => $this->invoice->hashed_id,
|
||||||
|
'amount' => 2.0
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'date' => '2019/12/12',
|
||||||
|
];
|
||||||
|
|
||||||
|
try {
|
||||||
|
$response = $this->withHeaders([
|
||||||
|
'X-API-SECRET' => config('ninja.api_secret'),
|
||||||
|
'X-API-TOKEN' => $this->token,
|
||||||
|
])->post('/api/v1/payments?include=invoices', $data);
|
||||||
|
|
||||||
|
}
|
||||||
|
catch(ValidationException $e) {
|
||||||
|
|
||||||
|
$message = json_decode($e->validator->getMessageBag(),1);
|
||||||
|
|
||||||
|
$this->assertTrue(array_key_exists('amount', $message));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// if($response)
|
||||||
|
// $response->assertStatus(200);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function testPaymentChangesBalancesCorrectly()
|
||||||
|
{
|
||||||
|
|
||||||
|
$this->invoice = null;
|
||||||
|
|
||||||
|
$client = ClientFactory::create($this->company->id, $this->user->id);
|
||||||
|
$client->save();
|
||||||
|
|
||||||
|
$this->invoice = InvoiceFactory::create($this->company->id,$this->user->id);//stub the company and user_id
|
||||||
|
$this->invoice->client_id = $client->id;
|
||||||
|
|
||||||
|
$this->invoice->line_items = $this->buildLineItems();
|
||||||
|
$this->invoice->uses_inclusive_taxes = false;
|
||||||
|
|
||||||
|
$this->invoice->save();
|
||||||
|
|
||||||
|
$this->invoice_calc = new InvoiceSum($this->invoice);
|
||||||
|
$this->invoice_calc->build();
|
||||||
|
|
||||||
|
$this->invoice = $this->invoice_calc->getInvoice();
|
||||||
|
$this->invoice->save();
|
||||||
|
$this->invoice->markSent();
|
||||||
|
$this->invoice->save();
|
||||||
|
|
||||||
|
|
||||||
|
$data = [
|
||||||
|
'amount' => 2.0,
|
||||||
|
'client_id' => $client->hashed_id,
|
||||||
|
'invoices' => [
|
||||||
|
[
|
||||||
|
'id' => $this->invoice->hashed_id,
|
||||||
|
'amount' => 2.0
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'date' => '2019/12/12',
|
||||||
|
];
|
||||||
|
|
||||||
|
$response = false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
$response = $this->withHeaders([
|
||||||
|
'X-API-SECRET' => config('ninja.api_secret'),
|
||||||
|
'X-API-TOKEN' => $this->token,
|
||||||
|
])->post('/api/v1/payments?include=invoices', $data);
|
||||||
|
|
||||||
|
}
|
||||||
|
catch(ValidationException $e) {
|
||||||
|
|
||||||
|
$message = json_decode($e->validator->getMessageBag(),1);
|
||||||
|
|
||||||
|
$this->assertTrue(array_key_exists('amount', $message));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if($response){
|
||||||
|
$response->assertStatus(200);
|
||||||
|
|
||||||
|
$invoice = Invoice::find($this->decodePrimaryKey($this->invoice->hashed_id));
|
||||||
|
|
||||||
|
$this->assertEquals($invoice->balance, 8);
|
||||||
|
|
||||||
|
$payment = $invoice->payments()->first();
|
||||||
|
|
||||||
|
$this->assertEquals($payment->applied, 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testUpdatePaymentValidationWorks()
|
||||||
|
{
|
||||||
|
|
||||||
|
$this->invoice = null;
|
||||||
|
|
||||||
|
$client = ClientFactory::create($this->company->id, $this->user->id);
|
||||||
|
$client->save();
|
||||||
|
|
||||||
|
$this->invoice = InvoiceFactory::create($this->company->id,$this->user->id);//stub the company and user_id
|
||||||
|
$this->invoice->client_id = $client->id;
|
||||||
|
|
||||||
|
$this->invoice->line_items = $this->buildLineItems();
|
||||||
|
$this->invoice->uses_inclusive_taxes = false;
|
||||||
|
|
||||||
|
$this->invoice->save();
|
||||||
|
|
||||||
|
$this->invoice_calc = new InvoiceSum($this->invoice);
|
||||||
|
$this->invoice_calc->build();
|
||||||
|
|
||||||
|
$this->invoice = $this->invoice_calc->getInvoice();
|
||||||
|
$this->invoice->save();
|
||||||
|
$this->invoice->markSent();
|
||||||
|
$this->invoice->save();
|
||||||
|
|
||||||
|
$payment = PaymentFactory::create($this->company->id, $this->user->id);
|
||||||
|
$payment->amount = 10;
|
||||||
|
$payment->client_id = $client->id;
|
||||||
|
$payment->date = now();
|
||||||
|
$payment->save();
|
||||||
|
|
||||||
|
|
||||||
|
$data = [
|
||||||
|
'amount' => 2.0,
|
||||||
|
'client_id' => $client->hashed_id,
|
||||||
|
'invoices' => [],
|
||||||
|
'date' => '2019/12/12',
|
||||||
|
];
|
||||||
|
|
||||||
|
$response = false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
$response = $this->withHeaders([
|
||||||
|
'X-API-SECRET' => config('ninja.api_secret'),
|
||||||
|
'X-API-TOKEN' => $this->token,
|
||||||
|
])->put('/api/v1/payments/'.$this->encodePrimaryKey($payment->id), $data);
|
||||||
|
|
||||||
|
}
|
||||||
|
catch(ValidationException $e) {
|
||||||
|
|
||||||
|
$message = json_decode($e->validator->getMessageBag(),1);
|
||||||
|
|
||||||
|
$this->assertTrue(array_key_exists('invoices', $message));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if($response)
|
||||||
|
$response->assertStatus(302);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function testUpdatePaymentValidationPasses()
|
||||||
|
{
|
||||||
|
|
||||||
|
$this->invoice = null;
|
||||||
|
|
||||||
|
$client = ClientFactory::create($this->company->id, $this->user->id);
|
||||||
|
$client->save();
|
||||||
|
|
||||||
|
$this->invoice = InvoiceFactory::create($this->company->id,$this->user->id);//stub the company and user_id
|
||||||
|
$this->invoice->client_id = $client->id;
|
||||||
|
|
||||||
|
$this->invoice->line_items = $this->buildLineItems();
|
||||||
|
$this->invoice->uses_inclusive_taxes = false;
|
||||||
|
|
||||||
|
$this->invoice->save();
|
||||||
|
|
||||||
|
$this->invoice_calc = new InvoiceSum($this->invoice);
|
||||||
|
$this->invoice_calc->build();
|
||||||
|
|
||||||
|
$this->invoice = $this->invoice_calc->getInvoice();
|
||||||
|
$this->invoice->save();
|
||||||
|
$this->invoice->markSent();
|
||||||
|
$this->invoice->save();
|
||||||
|
|
||||||
|
$payment = PaymentFactory::create($this->company->id, $this->user->id);
|
||||||
|
$payment->amount = 10;
|
||||||
|
$payment->client_id = $client->id;
|
||||||
|
$payment->date = now();
|
||||||
|
$payment->number = $client->getNextPaymentNumber($client);
|
||||||
|
$payment->save();
|
||||||
|
|
||||||
|
|
||||||
|
$data = [
|
||||||
|
'amount' => 10.0,
|
||||||
|
'client_id' => $this->encodePrimaryKey($client->id),
|
||||||
|
'invoices' => [
|
||||||
|
[
|
||||||
|
'id' => $this->encodePrimaryKey($this->invoice->id),
|
||||||
|
'amount' => 10,
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'date' => '2019/12/12',
|
||||||
|
];
|
||||||
|
|
||||||
|
$response = false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
$response = $this->withHeaders([
|
||||||
|
'X-API-SECRET' => config('ninja.api_secret'),
|
||||||
|
'X-API-TOKEN' => $this->token,
|
||||||
|
])->put('/api/v1/payments/'.$this->encodePrimaryKey($payment->id), $data);
|
||||||
|
|
||||||
|
}
|
||||||
|
catch(ValidationException $e) {
|
||||||
|
|
||||||
|
$message = json_decode($e->validator->getMessageBag(),1);
|
||||||
|
\Log::error(print_r($e->validator->getMessageBag(),1));
|
||||||
|
|
||||||
|
$this->assertTrue(array_key_exists('invoices', $message));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if($response)
|
||||||
|
$response->assertStatus(200);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function testDoublePaymentTestWithInvalidAmounts()
|
||||||
|
{
|
||||||
|
|
||||||
|
$this->invoice = null;
|
||||||
|
|
||||||
|
$client = ClientFactory::create($this->company->id, $this->user->id);
|
||||||
|
$client->save();
|
||||||
|
|
||||||
|
$this->invoice = InvoiceFactory::create($this->company->id,$this->user->id);//stub the company and user_id
|
||||||
|
$this->invoice->client_id = $client->id;
|
||||||
|
|
||||||
|
$this->invoice->line_items = $this->buildLineItems();
|
||||||
|
$this->invoice->uses_inclusive_taxes = false;
|
||||||
|
|
||||||
|
$this->invoice->save();
|
||||||
|
|
||||||
|
$this->invoice_calc = new InvoiceSum($this->invoice);
|
||||||
|
$this->invoice_calc->build();
|
||||||
|
|
||||||
|
$this->invoice = $this->invoice_calc->getInvoice();
|
||||||
|
$this->invoice->save();
|
||||||
|
$this->invoice->markSent();
|
||||||
|
|
||||||
|
$data = [
|
||||||
|
'amount' => 15.0,
|
||||||
|
'client_id' => $this->encodePrimaryKey($client->id),
|
||||||
|
'invoices' => [
|
||||||
|
[
|
||||||
|
'id' => $this->encodePrimaryKey($this->invoice->id),
|
||||||
|
'amount' => 10,
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'date' => '2019/12/12',
|
||||||
|
];
|
||||||
|
|
||||||
|
$response = false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
$response = $this->withHeaders([
|
||||||
|
'X-API-SECRET' => config('ninja.api_secret'),
|
||||||
|
'X-API-TOKEN' => $this->token,
|
||||||
|
])->post('/api/v1/payments/', $data);
|
||||||
|
}
|
||||||
|
catch(ValidationException $e) {
|
||||||
|
|
||||||
|
$message = json_decode($e->validator->getMessageBag(),1);
|
||||||
|
\Log::error(print_r($e->validator->getMessageBag(),1));
|
||||||
|
}
|
||||||
|
|
||||||
|
$response->assertStatus(200);
|
||||||
|
|
||||||
|
$arr = $response->json();
|
||||||
|
|
||||||
|
$payment_id = $arr['data']['id'];
|
||||||
|
|
||||||
|
$payment = Payment::find($this->decodePrimaryKey($payment_id))->first();
|
||||||
|
|
||||||
|
$this->assertEquals($payment->amount, 15);
|
||||||
|
$this->assertEquals($payment->applied, 10);
|
||||||
|
|
||||||
|
$this->invoice = null;
|
||||||
|
$this->invoice = InvoiceFactory::create($this->company->id,$this->user->id);//stub the company and user_id
|
||||||
|
$this->invoice->client_id = $client->id;
|
||||||
|
|
||||||
|
$this->invoice->line_items = $this->buildLineItems();
|
||||||
|
$this->invoice->uses_inclusive_taxes = false;
|
||||||
|
|
||||||
|
$this->invoice->save();
|
||||||
|
|
||||||
|
$this->invoice_calc = new InvoiceSum($this->invoice);
|
||||||
|
$this->invoice_calc->build();
|
||||||
|
|
||||||
|
$this->invoice = $this->invoice_calc->getInvoice();
|
||||||
|
$this->invoice->save();
|
||||||
|
$this->invoice->markSent();
|
||||||
|
|
||||||
|
|
||||||
|
$data = [
|
||||||
|
'amount' => 15.0,
|
||||||
|
'client_id' => $this->encodePrimaryKey($client->id),
|
||||||
|
'invoices' => [
|
||||||
|
[
|
||||||
|
'id' => $this->encodePrimaryKey($this->invoice->id),
|
||||||
|
'amount' => 10,
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'date' => '2019/12/12',
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
$response = false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
$response = $this->withHeaders([
|
||||||
|
'X-API-SECRET' => config('ninja.api_secret'),
|
||||||
|
'X-API-TOKEN' => $this->token,
|
||||||
|
])->put('/api/v1/payments/'.$this->encodePrimaryKey($payment->id), $data);
|
||||||
|
|
||||||
|
}
|
||||||
|
catch(ValidationException $e) {
|
||||||
|
|
||||||
|
$message = json_decode($e->validator->getMessageBag(),1);
|
||||||
|
\Log::error(print_r($e->validator->getMessageBag(),1));
|
||||||
|
|
||||||
|
$this->assertTrue(array_key_exists('invoices', $message));
|
||||||
|
\Log::error('hit error');
|
||||||
|
}
|
||||||
|
|
||||||
|
//$response->assertStatus(302);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function testDoublePaymentTestWithValidAmounts()
|
||||||
|
{
|
||||||
|
|
||||||
|
$this->invoice = null;
|
||||||
|
|
||||||
|
$client = ClientFactory::create($this->company->id, $this->user->id);
|
||||||
|
$client->save();
|
||||||
|
|
||||||
|
$this->invoice = InvoiceFactory::create($this->company->id,$this->user->id);//stub the company and user_id
|
||||||
|
$this->invoice->client_id = $client->id;
|
||||||
|
|
||||||
|
$this->invoice->line_items = $this->buildLineItems();
|
||||||
|
$this->invoice->uses_inclusive_taxes = false;
|
||||||
|
|
||||||
|
$this->invoice->save();
|
||||||
|
|
||||||
|
$this->invoice_calc = new InvoiceSum($this->invoice);
|
||||||
|
$this->invoice_calc->build();
|
||||||
|
|
||||||
|
$this->invoice = $this->invoice_calc->getInvoice();
|
||||||
|
$this->invoice->save();
|
||||||
|
$this->invoice->markSent();
|
||||||
|
|
||||||
|
$data = [
|
||||||
|
'amount' => 20.0,
|
||||||
|
'client_id' => $this->encodePrimaryKey($client->id),
|
||||||
|
'invoices' => [
|
||||||
|
[
|
||||||
|
'id' => $this->encodePrimaryKey($this->invoice->id),
|
||||||
|
'amount' => 10,
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'date' => '2019/12/12',
|
||||||
|
];
|
||||||
|
|
||||||
|
$response = $this->withHeaders([
|
||||||
|
'X-API-SECRET' => config('ninja.api_secret'),
|
||||||
|
'X-API-TOKEN' => $this->token,
|
||||||
|
])->post('/api/v1/payments/', $data);
|
||||||
|
|
||||||
|
$response->assertStatus(200);
|
||||||
|
|
||||||
|
$arr = $response->json();
|
||||||
|
|
||||||
|
$payment_id = $arr['data']['id'];
|
||||||
|
|
||||||
|
$payment = Payment::find($this->decodePrimaryKey($payment_id))->first();
|
||||||
|
|
||||||
|
$this->assertEquals($payment->amount, 20);
|
||||||
|
$this->assertEquals($payment->applied, 10);
|
||||||
|
|
||||||
|
$this->invoice = null;
|
||||||
|
$this->invoice = InvoiceFactory::create($this->company->id,$this->user->id);//stub the company and user_id
|
||||||
|
$this->invoice->client_id = $client->id;
|
||||||
|
|
||||||
|
$this->invoice->line_items = $this->buildLineItems();
|
||||||
|
$this->invoice->uses_inclusive_taxes = false;
|
||||||
|
|
||||||
|
$this->invoice->save();
|
||||||
|
|
||||||
|
$this->invoice_calc = new InvoiceSum($this->invoice);
|
||||||
|
$this->invoice_calc->build();
|
||||||
|
|
||||||
|
$this->invoice = $this->invoice_calc->getInvoice();
|
||||||
|
$this->invoice->save();
|
||||||
|
$this->invoice->markSent();
|
||||||
|
|
||||||
|
|
||||||
|
$data = [
|
||||||
|
'amount' => 20.0,
|
||||||
|
'client_id' => $this->encodePrimaryKey($client->id),
|
||||||
|
'invoices' => [
|
||||||
|
[
|
||||||
|
'id' => $this->encodePrimaryKey($this->invoice->id),
|
||||||
|
'amount' => 10,
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'date' => '2019/12/12',
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
$response = false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
$response = $this->withHeaders([
|
||||||
|
'X-API-SECRET' => config('ninja.api_secret'),
|
||||||
|
'X-API-TOKEN' => $this->token,
|
||||||
|
])->put('/api/v1/payments/'.$this->encodePrimaryKey($payment->id), $data);
|
||||||
|
|
||||||
|
}
|
||||||
|
catch(ValidationException $e) {
|
||||||
|
|
||||||
|
$message = json_decode($e->validator->getMessageBag(),1);
|
||||||
|
\Log::error(print_r($e->validator->getMessageBag(),1));
|
||||||
|
|
||||||
|
$this->assertTrue(array_key_exists('invoices', $message));
|
||||||
|
\Log::error('hit error');
|
||||||
|
}
|
||||||
|
|
||||||
|
$response->assertStatus(200);
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -56,7 +56,7 @@ class UserTest extends TestCase
|
|||||||
$response = $this->withHeaders([
|
$response = $this->withHeaders([
|
||||||
'X-API-SECRET' => config('ninja.api_secret'),
|
'X-API-SECRET' => config('ninja.api_secret'),
|
||||||
'X-API-TOKEN' => $this->token,
|
'X-API-TOKEN' => $this->token,
|
||||||
'X-API-PASSWORD' => 'ALongAndBriliantPassword',
|
'X-API-PASSWORD' => 'ALongAndBriliantPassword',
|
||||||
])->get('/api/v1/users');
|
])->get('/api/v1/users');
|
||||||
|
|
||||||
$response->assertStatus(200);
|
$response->assertStatus(200);
|
||||||
|
@ -62,7 +62,7 @@ class MultiDBUserTest extends TestCase
|
|||||||
'last_name' => 'user_db_1-s',
|
'last_name' => 'user_db_1-s',
|
||||||
'phone' => '55555',
|
'phone' => '55555',
|
||||||
'email_verified_at' => now(),
|
'email_verified_at' => now(),
|
||||||
'password' => 'ALongAndBriliantPassword', // secret
|
'password' => Hash::make('ALongAndBriliantPassword'), // secret
|
||||||
'remember_token' => \Illuminate\Support\Str::random(10),
|
'remember_token' => \Illuminate\Support\Str::random(10),
|
||||||
'email' => 'db1@example.com',
|
'email' => 'db1@example.com',
|
||||||
'oauth_user_id' => '123',
|
'oauth_user_id' => '123',
|
||||||
|
@ -37,6 +37,7 @@ use App\Utils\Traits\GeneratesCounter;
|
|||||||
use App\Utils\Traits\MakesHash;
|
use App\Utils\Traits\MakesHash;
|
||||||
use Illuminate\Support\Carbon;
|
use Illuminate\Support\Carbon;
|
||||||
use Illuminate\Support\Facades\Cache;
|
use Illuminate\Support\Facades\Cache;
|
||||||
|
use Illuminate\Support\Facades\Hash;
|
||||||
use Illuminate\Support\Facades\Schema;
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -101,7 +102,7 @@ trait MockAccountData
|
|||||||
|
|
||||||
if(!$this->user){
|
if(!$this->user){
|
||||||
$this->user = factory(\App\Models\User::class)->create([
|
$this->user = factory(\App\Models\User::class)->create([
|
||||||
'password' => 'ALongAndBriliantPassword',
|
'password' => Hash::make('ALongAndBriliantPassword'),
|
||||||
'confirmation_code' => $this->createDbHash(config('database.default'))
|
'confirmation_code' => $this->createDbHash(config('database.default'))
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user