1
0
mirror of https://github.com/invoiceninja/invoiceninja.git synced 2024-11-10 13:12:50 +01:00

Merge pull request #9392 from turbo124/v5-develop

Fixes for payment validation
This commit is contained in:
David Bomba 2024-03-20 22:12:37 +11:00 committed by GitHub
commit 40f441cb13
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 556 additions and 309 deletions

View File

@ -200,14 +200,16 @@ class InvoiceFilters extends QueryFilters
*/
public function payable(string $client_id = ''): Builder
{
if (strlen($client_id) == 0) {
return $this->builder;
}
return $this->builder->whereIn('status_id', [Invoice::STATUS_DRAFT, Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL])
->where('balance', '>', 0)
->where('is_deleted', 0)
->where('client_id', $this->decodePrimaryKey($client_id));
return $this->builder
->where('client_id', $this->decodePrimaryKey($client_id))
->whereIn('status_id', [Invoice::STATUS_DRAFT, Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL])
->where('is_deleted', 0)
->where('balance', '>', 0);
}

View File

@ -43,7 +43,7 @@ class ContactLoginController extends Controller
if ($request->session()->has('company_key')) {
MultiDB::findAndSetDbByCompanyKey($request->session()->get('company_key'));
$company = Company::where('company_key', $request->input('company_key'))->first();
$company = Company::where('company_key', $request->session()->get('company_key'))->first();
} elseif ($request->has('company_key')) {
MultiDB::findAndSetDbByCompanyKey($request->input('company_key'));
$company = Company::where('company_key', $request->input('company_key'))->first();

View File

@ -57,8 +57,6 @@ class RefundPaymentRequest extends Request
if (isset($input['credits'])) {
unset($input['credits']);
// foreach($input['credits'] as $key => $credit)
// $input['credits'][$key]['credit_id'] = $this->decodePrimaryKey($credit['credit_id']);
}
$this->replace($input);

View File

@ -16,7 +16,6 @@ use App\Http\ValidationRules\Credit\CreditsSumRule;
use App\Http\ValidationRules\Credit\ValidCreditsRules;
use App\Http\ValidationRules\Payment\ValidInvoicesRules;
use App\Http\ValidationRules\PaymentAmountsBalanceRule;
use App\Http\ValidationRules\ValidCreditsPresentRule;
use App\Http\ValidationRules\ValidPayableInvoicesRule;
use App\Models\Payment;
use App\Utils\Traits\MakesHash;
@ -39,6 +38,41 @@ class StorePaymentRequest extends Request
return $user->can('create', Payment::class);
}
public function rules()
{
/** @var \App\Models\User $user */
$user = auth()->user();
$rules = [
'client_id' => ['bail','required',Rule::exists('clients','id')->where('company_id',$user->company()->id)->where('is_deleted', 0)],
'amount' => ['bail', 'numeric', new PaymentAmountsBalanceRule()],
'invoices.*.amount' => ['bail','required'],
'invoices.*.invoice_id' => ['bail','required','distinct',new ValidInvoicesRules($this->all()),Rule::exists('invoices','id')->where('company_id', $user->company()->id)->where('client_id', request()->input('client_id'))],
'credits.*.credit_id' => ['bail','required','distinct',new ValidCreditsRules($this->all()),Rule::exists('credits','id')->where('company_id', $user->company()->id)->where('client_id', request()->input('client_id'))],
'credits.*.amount' => ['bail','required', new CreditsSumRule($this->all())],
'invoices' => ['bail','sometimes','array', new ValidPayableInvoicesRule()],
'number' => ['bail', 'nullable', Rule::unique('payments')->where('company_id', $user->company()->id)],
'idempotency_key' => ['nullable', 'bail', 'string','max:64', Rule::unique('payments')->where('company_id', $user->company()->id)],
];
if ($this->file('documents') && is_array($this->file('documents'))) {
$rules['documents.*'] = $this->fileValidation();
} elseif ($this->file('documents')) {
$rules['documents'] = $this->fileValidation();
}else {
$rules['documents'] = 'bail|sometimes|array';
}
if ($this->file('file') && is_array($this->file('file'))) {
$rules['file.*'] = $this->fileValidation();
} elseif ($this->file('file')) {
$rules['file'] = $this->fileValidation();
}
return $rules;
}
public function prepareForValidation()
{
@ -78,7 +112,6 @@ class StorePaymentRequest extends Request
foreach ($input['credits'] as $key => $value) {
if (array_key_exists('credit_id', $input['credits'][$key])) {
$input['credits'][$key]['credit_id'] = $this->decodePrimaryKey($value['credit_id']);
$credits_total += $value['amount'];
}
}
@ -103,39 +136,5 @@ class StorePaymentRequest extends Request
$this->replace($input);
}
public function rules()
{
/** @var \App\Models\User $user */
$user = auth()->user();
$rules = [
'amount' => ['numeric', 'bail', new PaymentAmountsBalanceRule(), new ValidCreditsPresentRule($this->all())],
'client_id' => 'bail|required|exists:clients,id,company_id,'.$user->company()->id.',is_deleted,0',
'invoices.*.invoice_id' => 'bail|required|distinct|exists:invoices,id',
'invoices.*.amount' => 'bail|required',
'invoices.*.invoice_id' => new ValidInvoicesRules($this->all()),
'credits.*.credit_id' => 'bail|required|exists:credits,id',
'credits.*.credit_id' => new ValidCreditsRules($this->all()),
'credits.*.amount' => ['bail','required', new CreditsSumRule($this->all())],
'invoices' => new ValidPayableInvoicesRule(),
'number' => ['nullable', 'bail', Rule::unique('payments')->where('company_id', $user->company()->id)],
'idempotency_key' => ['nullable', 'bail', 'string','max:64', Rule::unique('payments')->where('company_id', $user->company()->id)],
];
if ($this->file('documents') && is_array($this->file('documents'))) {
$rules['documents.*'] = $this->fileValidation();
} elseif ($this->file('documents')) {
$rules['documents'] = $this->fileValidation();
}else {
$rules['documents'] = 'bail|sometimes|array';
}
if ($this->file('file') && is_array($this->file('file'))) {
$rules['file.*'] = $this->fileValidation();
} elseif ($this->file('file')) {
$rules['file'] = $this->fileValidation();
}
return $rules;
}
}

View File

@ -13,7 +13,6 @@ namespace App\Http\Requests\Payment;
use App\Http\Requests\Request;
use App\Http\ValidationRules\PaymentAppliedValidAmount;
use App\Http\ValidationRules\ValidCreditsPresentRule;
use App\Utils\Traits\ChecksEntityStatus;
use App\Utils\Traits\MakesHash;
use Illuminate\Validation\Rule;
@ -41,16 +40,17 @@ class UpdatePaymentRequest extends Request
/** @var \App\Models\User $user */
$user = auth()->user();
$rules = [
'invoices' => ['array', new PaymentAppliedValidAmount($this->all()), new ValidCreditsPresentRule($this->all())],
'invoices.*.invoice_id' => 'distinct',
'client_id' => ['sometimes', 'bail', Rule::in([$this->payment->client_id])],
'number' => ['sometimes', 'bail', Rule::unique('payments')->where('company_id', $user->company()->id)->ignore($this->payment->id)],
'invoices' => ['sometimes', 'bail', 'array', new PaymentAppliedValidAmount($this->all())],
'invoices.*.invoice_id' => ['sometimes','distinct',Rule::exists('invoices','id')->where('company_id', $user->company()->id)->where('client_id', request()->input('client_id'))],
'invoices.*.amount' => ['sometimes','numeric','min:0'],
'credits.*.credit_id' => ['sometimes','bail','distinct',Rule::exists('credits','id')->where('company_id', $user->company()->id)->where('client_id', request()->input('client_id'))],
'credits.*.amount' => ['required', 'bail'],
];
if ($this->number) {
$rules['number'] = Rule::unique('payments')->where('company_id', $user->company()->id)->ignore($this->payment->id);
}
if ($this->file('documents') && is_array($this->file('documents'))) {
$rules['documents.*'] = $this->fileValidation();
} elseif ($this->file('documents')) {
@ -74,10 +74,6 @@ class UpdatePaymentRequest extends Request
$input = $this->decodePrimaryKeys($input);
if (isset($input['client_id'])) {
unset($input['client_id']);
}
if (isset($input['amount'])) {
unset($input['amount']);
}
@ -85,7 +81,6 @@ class UpdatePaymentRequest extends Request
if (isset($input['invoices']) && is_array($input['invoices']) !== false) {
foreach ($input['invoices'] as $key => $value) {
if(isset($input['invoices'][$key]['invoice_id'])) {
// if (array_key_exists('invoice_id', $input['invoices'][$key])) {
$input['invoices'][$key]['invoice_id'] = $this->decodePrimaryKey($value['invoice_id']);
}
}
@ -93,7 +88,6 @@ class UpdatePaymentRequest extends Request
if (isset($input['credits']) && is_array($input['credits']) !== false) {
foreach ($input['credits'] as $key => $value) {
// if (array_key_exists('credits', $input['credits'][$key])) {
if (isset($input['credits'][$key]['credit_id'])) {
$input['credits'][$key]['credit_id'] = $this->decodePrimaryKey($value['credit_id']);
}

View File

@ -57,9 +57,6 @@ class ValidRefundableRequest implements Rule
if ($payment->invoices()->exists()) {
$this->checkInvoice($payment->invoices, $request_invoices);
// foreach ($payment->invoices as $paymentable_invoice) {
// $this->checkInvoice($paymentable_invoice, $request_invoices);
// }
}
foreach ($request_invoices as $request_invoice) {

View File

@ -61,7 +61,10 @@ class PaymentAppliedValidAmount implements Rule
$payment_amounts = 0;
$invoice_amounts = 0;
$payment_amounts = $payment->amount - $payment->refunded - $payment->applied;
// $payment_amounts = $payment->amount - $payment->refunded - $payment->applied;
//20-03-2024 - applied amounts are never tainted by refunded amount.
$payment_amounts = $payment->amount - $payment->applied;
if (request()->has('credits')
&& is_array(request()->input('credits'))
@ -84,10 +87,6 @@ class PaymentAppliedValidAmount implements Rule
$inv = $inv_collection->firstWhere('id', $invoice['invoice_id']);
nlog($inv->status_id);
nlog($inv->amount);
nlog($invoice['amount']);
if($inv->status_id == Invoice::STATUS_DRAFT && $inv->amount >= $invoice['amount']) {
} elseif ($inv->balance < $invoice['amount']) {

View File

@ -17,6 +17,7 @@ use Illuminate\Contracts\Validation\Rule;
/**
* Class ValidCreditsPresentRule.
* @deprecated 20-03-2024
*/
class ValidCreditsPresentRule implements Rule
{
@ -49,11 +50,8 @@ class ValidCreditsPresentRule implements Rule
private function validCreditsPresent(): bool
{
//todo need to ensure the clients credits are here not random ones!
if (array_key_exists('credits', $this->input) && is_array($this->input['credits']) && count($this->input['credits']) > 0) {
$credit_collection = Credit::query()->whereIn('id', array_column($this->input['credits'], 'credit_id'))->count();
return $credit_collection == count($this->input['credits']);
}

View File

@ -63,11 +63,14 @@ class UpdateCalculatedFields
Project::query()->with('tasks')->whereHas('tasks', function ($query) {
$query->where('updated_at', '>', now()->subHours(2));
})
->cursor()
->each(function ($project) {
$project->current_hours = $this->calculateDuration($project);
$project->save();
});
->cursor()
->each(function ($project) {
$project->current_hours = $this->calculateDuration($project);
$project->save();
});
//Clean password resets table
\DB::connection($db)->table('password_resets')->where('created_at', '<', now()->subHour())->delete();
}
}

View File

@ -12,6 +12,7 @@
namespace App\Models;
use App\Utils\Traits\MakesHash;
use Carbon\CarbonInterval;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Carbon;
@ -248,6 +249,7 @@ class Task extends BaseModel
$duration += max($end_time - $start_time, 0);
}
// return CarbonInterval::seconds(round($duration))->locale($this->company->locale())->cascade()->forHumans();
return round($duration);
}

View File

@ -292,7 +292,32 @@ class PayPalPPCPPaymentDriver extends BaseDriver
}
$r = $this->gatewayRequest("/v2/checkout/orders/{$orderID}/capture", 'post', ['body' => '']);
try {
$r = $this->gatewayRequest("/v2/checkout/orders/{$orderID}/capture", 'post', ['body' => '']);
} catch(\Exception $e) {
//Rescue for duplicate invoice_id
if(stripos($e->getMessage(), 'DUPLICATE_INVOICE_ID') !== false) {
$_invoice = collect($this->payment_hash->data->invoices)->first();
$invoice = Invoice::withTrashed()->find($this->decodePrimaryKey($_invoice->invoice_id));
$new_invoice_number = $invoice->number."_".Str::random(5);
$update_data =
[[
"op" => "replace",
"path" => "/purchase_units/@reference_id=='default'/invoice_id",
"value" => $new_invoice_number,
]];
$r = $this->gatewayRequest("/v2/checkout/orders/{$orderID}", 'patch', $update_data);
$r = $this->gatewayRequest("/v2/checkout/orders/{$orderID}/capture", 'post', ['body' => '']);
}
}
$response = $r;

View File

@ -18,6 +18,7 @@ use App\Models\Invoice;
use App\Models\SystemLog;
use App\Models\GatewayType;
use App\Models\PaymentType;
use Illuminate\Support\Str;
use App\Jobs\Util\SystemLogger;
use App\Utils\Traits\MakesHash;
use App\Exceptions\PaymentFailed;
@ -211,7 +212,8 @@ class PayPalRestPaymentDriver extends BaseDriver
$request['gateway_response'] = str_replace("Error: ", "", $request['gateway_response']);
$response = json_decode($request['gateway_response'], true);
// nlog($response);
//capture
$orderID = $response['orderID'];
@ -235,7 +237,33 @@ class PayPalRestPaymentDriver extends BaseDriver
}
$r = $this->gatewayRequest("/v2/checkout/orders/{$orderID}/capture", 'post', ['body' => '']);
try{
$r = $this->gatewayRequest("/v2/checkout/orders/{$orderID}/capture", 'post', ['body' => '']);
}
catch(\Exception $e) {
//Rescue for duplicate invoice_id
if(stripos($e->getMessage(), 'DUPLICATE_INVOICE_ID') !== false){
$_invoice = collect($this->payment_hash->data->invoices)->first();
$invoice = Invoice::withTrashed()->find($this->decodePrimaryKey($_invoice->invoice_id));
$new_invoice_number = $invoice->number."_".Str::random(5);
$update_data =
[[
"op" => "replace",
"path" => "/purchase_units/@reference_id=='default'/invoice_id",
"value" => $new_invoice_number,
]];
$r = $this->gatewayRequest("/v2/checkout/orders/{$orderID}", 'patch', $update_data);
$r = $this->gatewayRequest("/v2/checkout/orders/{$orderID}/capture", 'post', ['body' => '']);
}
}
$response = $r;

View File

@ -109,15 +109,36 @@ class ACH
public function verificationView(ClientGatewayToken $token)
{
if (isset($token->meta->state) && $token->meta->state === 'authorized') {
return redirect()
->route('client.payment_methods.show', $token->hashed_id)
->with('message', __('texts.payment_method_verified'));
}
//double check here if we need to show the verification view.
$this->stripe->init();
if(substr($token->token,0,2) == 'pm'){
$pm = $this->stripe->getStripePaymentMethod($token->token);
if(!$pm->customer){
$meta = $token->meta;
$meta->state = 'unauthorized';
$token->meta = $meta;
$token->save();
return redirect()
->route('client.payment_methods.show', $token->hashed_id);
}
if (isset($token->meta->state) && $token->meta->state === 'authorized') {
return redirect()
->route('client.payment_methods.show', $token->hashed_id)
->with('message', __('texts.payment_method_verified'));
}
if($token->meta->next_action)
return redirect($token->meta->next_action);
}
$bank_account = Customer::retrieveSource($token->gateway_customer_reference, $token->token, [], $this->stripe->stripe_connect_auth);
/* Catch externally validated bank accounts and mark them as verified */
@ -319,6 +340,9 @@ class ACH
$data['message'] = 'Too many requests made to the API too quickly';
break;
case $e instanceof InvalidRequestException:
return redirect()->route('client.payment_methods.verification', ['payment_method' => $cgt->hashed_id, 'method' => GatewayType::BANK_TRANSFER]);
$data['message'] = 'Invalid parameters were supplied to Stripe\'s API';
break;
case $e instanceof AuthenticationException:

View File

@ -92,7 +92,8 @@ class AccountTransformer extends EntityTransformer
'account_sms_verified' => (bool) $account->account_sms_verified,
'has_iap_plan' => (bool)$account->inapp_transaction_id,
'tax_api_enabled' => (bool) config('services.tax.zip_tax.key') ? true : false,
'nordigen_enabled' => (bool) (config('ninja.nordigen.secret_id') && config('ninja.nordigen.secret_key')) ? true : false
'nordigen_enabled' => (bool) (config('ninja.nordigen.secret_id') && config('ninja.nordigen.secret_key')) ? true : false,
'upload_extensions' => (string) config('ninja.upload_extensions'),
];
}

169
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "ef338cb66991ec0e28b96643ac5a5c6f",
"content-hash": "b5347cd9ca42d75b5c80691e6b64dae5",
"packages": [
{
"name": "adrienrn/php-mimetyper",
@ -409,21 +409,22 @@
},
{
"name": "amphp/parallel",
"version": "v2.2.7",
"version": "v2.2.8",
"source": {
"type": "git",
"url": "https://github.com/amphp/parallel.git",
"reference": "ffda869c33c30627b6eb5c25f096882d885681dc"
"reference": "efd71b342b64c2e46d904e4eb057ed5ab20f8e2d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/amphp/parallel/zipball/ffda869c33c30627b6eb5c25f096882d885681dc",
"reference": "ffda869c33c30627b6eb5c25f096882d885681dc",
"url": "https://api.github.com/repos/amphp/parallel/zipball/efd71b342b64c2e46d904e4eb057ed5ab20f8e2d",
"reference": "efd71b342b64c2e46d904e4eb057ed5ab20f8e2d",
"shasum": ""
},
"require": {
"amphp/amp": "^3",
"amphp/byte-stream": "^2",
"amphp/cache": "^2",
"amphp/parser": "^1",
"amphp/pipeline": "^1",
"amphp/process": "^2",
@ -480,7 +481,7 @@
],
"support": {
"issues": "https://github.com/amphp/parallel/issues",
"source": "https://github.com/amphp/parallel/tree/v2.2.7"
"source": "https://github.com/amphp/parallel/tree/v2.2.8"
},
"funding": [
{
@ -488,7 +489,7 @@
"type": "github"
}
],
"time": "2024-03-16T16:15:46+00:00"
"time": "2024-03-19T16:09:34+00:00"
},
{
"name": "amphp/parser",
@ -747,16 +748,16 @@
},
{
"name": "amphp/socket",
"version": "v2.2.4",
"version": "v2.3.0",
"source": {
"type": "git",
"url": "https://github.com/amphp/socket.git",
"reference": "4223324c627cc26d44800630411e64856d3344bc"
"reference": "acc0a2f65ab498025ba5641f7cce499c4b1ed4b5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/amphp/socket/zipball/4223324c627cc26d44800630411e64856d3344bc",
"reference": "4223324c627cc26d44800630411e64856d3344bc",
"url": "https://api.github.com/repos/amphp/socket/zipball/acc0a2f65ab498025ba5641f7cce499c4b1ed4b5",
"reference": "acc0a2f65ab498025ba5641f7cce499c4b1ed4b5",
"shasum": ""
},
"require": {
@ -819,7 +820,7 @@
],
"support": {
"issues": "https://github.com/amphp/socket/issues",
"source": "https://github.com/amphp/socket/tree/v2.2.4"
"source": "https://github.com/amphp/socket/tree/v2.3.0"
},
"funding": [
{
@ -827,7 +828,7 @@
"type": "github"
}
],
"time": "2024-02-28T15:56:06+00:00"
"time": "2024-03-19T20:01:53+00:00"
},
{
"name": "amphp/sync",
@ -1383,16 +1384,16 @@
},
{
"name": "aws/aws-sdk-php",
"version": "3.301.1",
"version": "3.301.3",
"source": {
"type": "git",
"url": "https://github.com/aws/aws-sdk-php.git",
"reference": "0a910d2b35e7087337cdf3569dc9b6ce232aafba"
"reference": "6b21e34d24a73ea66492869be90443069034fdb3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/0a910d2b35e7087337cdf3569dc9b6ce232aafba",
"reference": "0a910d2b35e7087337cdf3569dc9b6ce232aafba",
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/6b21e34d24a73ea66492869be90443069034fdb3",
"reference": "6b21e34d24a73ea66492869be90443069034fdb3",
"shasum": ""
},
"require": {
@ -1472,9 +1473,9 @@
"support": {
"forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80",
"issues": "https://github.com/aws/aws-sdk-php/issues",
"source": "https://github.com/aws/aws-sdk-php/tree/3.301.1"
"source": "https://github.com/aws/aws-sdk-php/tree/3.301.3"
},
"time": "2024-03-15T18:14:42+00:00
"time": "2024-03-19T18:05:04+00:00"
},
{
"name": "bacon/bacon-qr-code",
@ -3541,16 +3542,16 @@
},
{
"name": "google/apiclient-services",
"version": "v0.339.0",
"version": "v0.340.0",
"source": {
"type": "git",
"url": "https://github.com/googleapis/google-api-php-client-services.git",
"reference": "5662d2ab3da41ac0e0e99db221a8c22c511c8f9c"
"reference": "c89999ea477da2b0803b2b4f14c9e7fc23b6344a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/googleapis/google-api-php-client-services/zipball/5662d2ab3da41ac0e0e99db221a8c22c511c8f9c",
"reference": "5662d2ab3da41ac0e0e99db221a8c22c511c8f9c",
"url": "https://api.github.com/repos/googleapis/google-api-php-client-services/zipball/c89999ea477da2b0803b2b4f14c9e7fc23b6344a",
"reference": "c89999ea477da2b0803b2b4f14c9e7fc23b6344a",
"shasum": ""
},
"require": {
@ -3579,9 +3580,9 @@
],
"support": {
"issues": "https://github.com/googleapis/google-api-php-client-services/issues",
"source": "https://github.com/googleapis/google-api-php-client-services/tree/v0.339.0"
"source": "https://github.com/googleapis/google-api-php-client-services/tree/v0.340.0"
},
"time": "2024-03-10T01:06:17+00:00"
"time": "2024-03-17T00:56:17+00:00"
},
{
"name": "google/auth",
@ -4403,16 +4404,16 @@
},
{
"name": "horstoeko/orderx",
"version": "v1.0.18",
"version": "v1.0.19",
"source": {
"type": "git",
"url": "https://github.com/horstoeko/orderx.git",
"reference": "0a8535c1cda5574d31e8002e7d03f8bbaafd30ed"
"reference": "7b4ed00ca98df5a88c916733d31728a16a3845b4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/horstoeko/orderx/zipball/0a8535c1cda5574d31e8002e7d03f8bbaafd30ed",
"reference": "0a8535c1cda5574d31e8002e7d03f8bbaafd30ed",
"url": "https://api.github.com/repos/horstoeko/orderx/zipball/7b4ed00ca98df5a88c916733d31728a16a3845b4",
"reference": "7b4ed00ca98df5a88c916733d31728a16a3845b4",
"shasum": ""
},
"require": {
@ -4465,9 +4466,9 @@
],
"support": {
"issues": "https://github.com/horstoeko/orderx/issues",
"source": "https://github.com/horstoeko/orderx/tree/v1.0.18"
"source": "https://github.com/horstoeko/orderx/tree/v1.0.19"
},
"time": "2024-01-27T09:26:23+00:00"
"time": "2024-03-20T04:07:11+00:00"
},
{
"name": "horstoeko/stringmanagement",
@ -4753,23 +4754,23 @@
},
{
"name": "imdhemy/google-play-billing",
"version": "1.5.1",
"version": "1.5.2",
"source": {
"type": "git",
"url": "https://github.com/imdhemy/google-play-billing.git",
"reference": "bb94f3b6ddb021605815e528f31b8c930c41677c"
"reference": "7f2b032354568fa50858e0f6dd25592d975b3979"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/imdhemy/google-play-billing/zipball/bb94f3b6ddb021605815e528f31b8c930c41677c",
"reference": "bb94f3b6ddb021605815e528f31b8c930c41677c",
"url": "https://api.github.com/repos/imdhemy/google-play-billing/zipball/7f2b032354568fa50858e0f6dd25592d975b3979",
"reference": "7f2b032354568fa50858e0f6dd25592d975b3979",
"shasum": ""
},
"require": {
"ext-json": "*",
"google/auth": "^1.26",
"guzzlehttp/guzzle": "^7.5.1",
"nesbot/carbon": "^2.66",
"nesbot/carbon": "^2.66|^3.0",
"php": ">=8.0"
},
"require-dev": {
@ -4798,9 +4799,9 @@
"description": "Google Play Billing",
"support": {
"issues": "https://github.com/imdhemy/google-play-billing/issues",
"source": "https://github.com/imdhemy/google-play-billing/tree/1.5.1"
"source": "https://github.com/imdhemy/google-play-billing/tree/1.5.2"
},
"time": "2023-12-15T10:25:05+00:00"
"time": "2024-03-19T17:56:34+00:00"
},
{
"name": "imdhemy/laravel-purchases",
@ -7491,16 +7492,16 @@
},
{
"name": "mollie/mollie-api-php",
"version": "v2.65.0",
"version": "v2.66.0",
"source": {
"type": "git",
"url": "https://github.com/mollie/mollie-api-php.git",
"reference": "3920816c311ec785f47f160204296d1b7f918da5"
"reference": "d7d09ac62a565e818bf49d04acb2f0432da758a9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/mollie/mollie-api-php/zipball/3920816c311ec785f47f160204296d1b7f918da5",
"reference": "3920816c311ec785f47f160204296d1b7f918da5",
"url": "https://api.github.com/repos/mollie/mollie-api-php/zipball/d7d09ac62a565e818bf49d04acb2f0432da758a9",
"reference": "d7d09ac62a565e818bf49d04acb2f0432da758a9",
"shasum": ""
},
"require": {
@ -7577,9 +7578,9 @@
],
"support": {
"issues": "https://github.com/mollie/mollie-api-php/issues",
"source": "https://github.com/mollie/mollie-api-php/tree/v2.65.0"
"source": "https://github.com/mollie/mollie-api-php/tree/v2.66.0"
},
"time": "2024-01-23T12:39:48+00:00"
"time": "2024-03-19T13:33:42+00:00"
},
{
"name": "moneyphp/money",
@ -11946,16 +11947,16 @@
},
{
"name": "spatie/laravel-package-tools",
"version": "1.16.3",
"version": "1.16.4",
"source": {
"type": "git",
"url": "https://github.com/spatie/laravel-package-tools.git",
"reference": "59db18c2e20d49a0b6d447bb1c654f6c123beb9e"
"reference": "ddf678e78d7f8b17e5cdd99c0c3413a4a6592e53"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/spatie/laravel-package-tools/zipball/59db18c2e20d49a0b6d447bb1c654f6c123beb9e",
"reference": "59db18c2e20d49a0b6d447bb1c654f6c123beb9e",
"url": "https://api.github.com/repos/spatie/laravel-package-tools/zipball/ddf678e78d7f8b17e5cdd99c0c3413a4a6592e53",
"reference": "ddf678e78d7f8b17e5cdd99c0c3413a4a6592e53",
"shasum": ""
},
"require": {
@ -11994,7 +11995,7 @@
],
"support": {
"issues": "https://github.com/spatie/laravel-package-tools/issues",
"source": "https://github.com/spatie/laravel-package-tools/tree/1.16.3"
"source": "https://github.com/spatie/laravel-package-tools/tree/1.16.4"
},
"funding": [
{
@ -12002,7 +12003,7 @@
"type": "github"
}
],
"time": "2024-03-07T07:35:57+00:00"
"time": "2024-03-20T07:29:11+00:00"
},
{
"name": "spatie/php-structure-discoverer",
@ -16568,16 +16569,16 @@
},
{
"name": "composer/pcre",
"version": "3.1.2",
"version": "3.1.3",
"source": {
"type": "git",
"url": "https://github.com/composer/pcre.git",
"reference": "4775f35b2d70865807c89d32c8e7385b86eb0ace"
"reference": "5b16e25a5355f1f3afdfc2f954a0a80aec4826a8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/composer/pcre/zipball/4775f35b2d70865807c89d32c8e7385b86eb0ace",
"reference": "4775f35b2d70865807c89d32c8e7385b86eb0ace",
"url": "https://api.github.com/repos/composer/pcre/zipball/5b16e25a5355f1f3afdfc2f954a0a80aec4826a8",
"reference": "5b16e25a5355f1f3afdfc2f954a0a80aec4826a8",
"shasum": ""
},
"require": {
@ -16619,7 +16620,7 @@
],
"support": {
"issues": "https://github.com/composer/pcre/issues",
"source": "https://github.com/composer/pcre/tree/3.1.2"
"source": "https://github.com/composer/pcre/tree/3.1.3"
},
"funding": [
{
@ -16635,7 +16636,7 @@
"type": "tidelift"
}
],
"time": "2024-03-07T15:38:35+00:00"
"time": "2024-03-19T10:26:25+00:00"
},
{
"name": "composer/semver",
@ -16918,16 +16919,16 @@
},
{
"name": "friendsofphp/php-cs-fixer",
"version": "v3.51.0",
"version": "v3.52.1",
"source": {
"type": "git",
"url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git",
"reference": "127fa74f010da99053e3f5b62672615b72dd6efd"
"reference": "6e77207f0d851862ceeb6da63e6e22c01b1587bc"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/127fa74f010da99053e3f5b62672615b72dd6efd",
"reference": "127fa74f010da99053e3f5b62672615b72dd6efd",
"url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/6e77207f0d851862ceeb6da63e6e22c01b1587bc",
"reference": "6e77207f0d851862ceeb6da63e6e22c01b1587bc",
"shasum": ""
},
"require": {
@ -16998,7 +16999,7 @@
],
"support": {
"issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues",
"source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.51.0"
"source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.52.1"
},
"funding": [
{
@ -17006,7 +17007,7 @@
"type": "github"
}
],
"time": "2024-02-28T19:50:06+00:00"
"time": "2024-03-19T21:02:43+00:00"
},
{
"name": "hamcrest/hamcrest-php",
@ -17061,25 +17062,25 @@
},
{
"name": "laracasts/cypress",
"version": "3.0.1",
"version": "3.0.2",
"source": {
"type": "git",
"url": "https://github.com/laracasts/cypress.git",
"reference": "dd4e61188d4edaf65ffa18851a5df38d0fa0619a"
"reference": "449f9d69da75091c77327093e5727a5c739a4cf8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laracasts/cypress/zipball/dd4e61188d4edaf65ffa18851a5df38d0fa0619a",
"reference": "dd4e61188d4edaf65ffa18851a5df38d0fa0619a",
"url": "https://api.github.com/repos/laracasts/cypress/zipball/449f9d69da75091c77327093e5727a5c739a4cf8",
"reference": "449f9d69da75091c77327093e5727a5c739a4cf8",
"shasum": ""
},
"require": {
"illuminate/support": "^6.0|^7.0|^8.0|^9.0|^10.0",
"illuminate/support": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0",
"php": "^8.0"
},
"require-dev": {
"orchestra/testbench": "^6.0|^7.0|^8.0",
"phpunit/phpunit": "^8.0|^9.5.10",
"orchestra/testbench": "^6.0|^7.0|^8.0|^9.0",
"phpunit/phpunit": "^8.0|^9.5.10|^10.5",
"spatie/laravel-ray": "^1.29"
},
"type": "library",
@ -17114,9 +17115,9 @@
],
"support": {
"issues": "https://github.com/laracasts/cypress/issues",
"source": "https://github.com/laracasts/cypress/tree/3.0.1"
"source": "https://github.com/laracasts/cypress/tree/3.0.2"
},
"time": "2023-02-16T20:00:16+00:00"
"time": "2024-03-19T14:07:37+00:00"
},
{
"name": "larastan/larastan",
@ -17288,16 +17289,16 @@
},
{
"name": "mockery/mockery",
"version": "1.6.9",
"version": "1.6.10",
"source": {
"type": "git",
"url": "https://github.com/mockery/mockery.git",
"reference": "0cc058854b3195ba21dc6b1f7b1f60f4ef3a9c06"
"reference": "47065d1be1fa05def58dc14c03cf831d3884ef0b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/mockery/mockery/zipball/0cc058854b3195ba21dc6b1f7b1f60f4ef3a9c06",
"reference": "0cc058854b3195ba21dc6b1f7b1f60f4ef3a9c06",
"url": "https://api.github.com/repos/mockery/mockery/zipball/47065d1be1fa05def58dc14c03cf831d3884ef0b",
"reference": "47065d1be1fa05def58dc14c03cf831d3884ef0b",
"shasum": ""
},
"require": {
@ -17309,8 +17310,8 @@
"phpunit/phpunit": "<8.0"
},
"require-dev": {
"phpunit/phpunit": "^8.5 || ^9.6.10",
"symplify/easy-coding-standard": "^12.0.8"
"phpunit/phpunit": "^8.5 || ^9.6.17",
"symplify/easy-coding-standard": "^12.1.14"
},
"type": "library",
"autoload": {
@ -17367,7 +17368,7 @@
"security": "https://github.com/mockery/mockery/security/advisories",
"source": "https://github.com/mockery/mockery"
},
"time": "2023-12-10T02:24:34+00:00"
"time": "2024-03-19T16:15:45+00:00"
},
{
"name": "myclabs/deep-copy",
@ -17732,16 +17733,16 @@
},
{
"name": "phpstan/phpstan",
"version": "1.10.62",
"version": "1.10.63",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan.git",
"reference": "cd5c8a1660ed3540b211407c77abf4af193a6af9"
"reference": "ad12836d9ca227301f5fb9960979574ed8628339"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/cd5c8a1660ed3540b211407c77abf4af193a6af9",
"reference": "cd5c8a1660ed3540b211407c77abf4af193a6af9",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/ad12836d9ca227301f5fb9960979574ed8628339",
"reference": "ad12836d9ca227301f5fb9960979574ed8628339",
"shasum": ""
},
"require": {
@ -17790,7 +17791,7 @@
"type": "tidelift"
}
],
"time": "2024-03-13T12:27:20+00:00"
"time": "2024-03-18T16:53:53+00:00"
},
{
"name": "phpunit/php-code-coverage",
@ -19704,5 +19705,5 @@
"platform-dev": {
"php": "^8.1|^8.2"
},
"plugin-api-version": "2.6.0"
"plugin-api-version": "2.3.0"
}

View File

@ -1,98 +0,0 @@
<?php
return [
/*
* This value will be sent along with your trace.
*
* When set to `null`, the app name will be used
*/
'default_trace_name' => null,
/*
* A driver is responsible for transmitting any measurements.
*/
'drivers' => [
Spatie\OpenTelemetry\Drivers\HttpDriver::class => [
'url' => 'http://localhost:9411/api/v2/spans',
// 'url' => 'http://localhost:4318/v1/traces'
],
],
/*
* This class determines if your measurements should actually be sent
* to the reporting drivers.
*/
'sampler' => Spatie\OpenTelemetry\Support\Samplers\AlwaysSampler::class,
/*
* Tags can be added to any measurement. These classes will determine the
* values of the tags when a new trace starts.
*/
'trace_tag_providers' => [
\Spatie\OpenTelemetry\Support\TagProviders\DefaultTagsProvider::class,
],
/*
* Tags can be added to any measurement. These classes will determine the
* values of the tags when a new span starts.
*/
'span_tag_providers' => [
],
'queue' => [
/*
* When enabled, any measurements (spans) you make in a queued job that implements
* `TraceAware` will automatically belong to the same trace that was
* started in the process that dispatched the job.
*/
'make_queue_trace_aware' => true,
/*
* When this is set to `false`, only jobs the implement
* `TraceAware` will be trace aware.
*/
'all_jobs_are_trace_aware_by_default' => true,
/*
* When set to `true` all jobs will
* automatically start a span.
*/
'all_jobs_auto_start_a_span' => true,
/*
* These jobs will be trace aware even if they don't
* implement the `TraceAware` interface.
*/
'trace_aware_jobs' => [
],
/*
* These jobs will never trace aware, regardless of `all_jobs_are_trace_aware_by_default`.
*/
'not_trace_aware_jobs' => [
],
],
/*
* These actions can be overridden to have fine-grained control over how
* the package performs certain tasks.
*
* In most cases, you should use the default values.
*/
'actions' => [
'make_queue_trace_aware' => Spatie\OpenTelemetry\Actions\MakeQueueTraceAwareAction::class,
],
/*
* This class determines how the package measures time.
*/
'stopwatch' => Spatie\OpenTelemetry\Support\Stopwatch::class,
/*
* This class generates IDs for traces and spans.
*/
'id_generator' => Spatie\OpenTelemetry\Support\IdGenerator::class,
];

View File

@ -46,8 +46,8 @@
var errorDetail = Array.isArray(data.details) && data.details[0];
if (errorDetail && ['INSTRUMENT_DECLINED', 'PAYER_ACTION_REQUIRED'].includes(errorDetail.issue)) {
return actions.restart();
}
return actions.restart();
}
document.getElementById("gateway_response").value =JSON.stringify( data );
document.getElementById("server_response").submit();

View File

@ -62,6 +62,127 @@ class PaymentTest extends TestCase
);
}
public function testClientIdValidation()
{
$p = Payment::factory()->create([
'company_id' => $this->company->id,
'user_id' => $this->user->id,
'client_id' => $this->client->id,
'status_id' => Payment::STATUS_COMPLETED,
'amount' => 100
]);
$data = [
'date' => now()->addDay()->format('Y-m-d')
];
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->putJson('/api/v1/payments/'.$p->hashed_id, $data);
$response->assertStatus(200);
$data = [
'date' => now()->addDay()->format('Y-m-d'),
'client_id' => $this->client->hashed_id,
];
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->putJson('/api/v1/payments/'.$p->hashed_id, $data);
$response->assertStatus(200);
$c = Client::factory()->create([
'user_id' => $this->user->id,
'company_id' => $this->company->id,
]);
$data = [
'date' => now()->addDay()->format('Y-m-d'),
'client_id' => $c->hashed_id,
];
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->putJson('/api/v1/payments/'.$p->hashed_id, $data);
$response->assertStatus(422);
}
public function testNegativeAppliedAmounts()
{
$p = Payment::factory()->create([
'company_id' => $this->company->id,
'user_id' => $this->user->id,
'client_id' => $this->client->id,
'status_id' => Payment::STATUS_COMPLETED,
'amount' => 100
]);
$i = Invoice::factory()->create([
'company_id' => $this->company->id,
'user_id' => $this->user->id,
'client_id' => $this->client->id,
'status_id' => Invoice::STATUS_SENT,
]);
$i->calc()->getInvoice()->service()->markSent()->save();
$this->assertGreaterThan(0, $i->balance);
$data = [
'amount' => 5,
'client_id' => $this->client->hashed_id,
'invoices' => [
[
'invoice_id' => $this->invoice->hashed_id,
'amount' => 5,
],
],
'date' => '2020/12/11',
'idempotency_key' => \Illuminate\Support\Str::uuid()->toString()
];
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->postJson('/api/v1/payments/', $data);
$response->assertStatus(200);
$payment_id = $response->json()['data']['id'];
$payment = Payment::find($this->decodePrimaryKey($payment_id));
$this->assertNotNull($payment);
$data = [
'client_id' => $this->client->hashed_id,
'invoices' => [
[
'invoice_id' => $this->invoice->hashed_id,
'amount' => -5,
],
],
'date' => '2020/12/11',
'idempotency_key' => \Illuminate\Support\Str::uuid()->toString()
];
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->putJson('/api/v1/payments/'.$payment_id, $data);
$response->assertStatus(422);
}
public function testCompletedPaymentLogic()
{
@ -1395,8 +1516,9 @@ class PaymentTest extends TestCase
$invoice_calc = new InvoiceSum($invoice);
$invoice_calc->build();
$invoice = $invoice_calc->getInvoice();
$invoice->save();
$invoice = $invoice_calc->getInvoice()->service()->markSent()->save();
$this->assertEquals(10, $invoice->amount);
$this->assertEquals(10, $invoice->balance);
$credit = CreditFactory::create($this->company->id, $this->user->id);
$credit->client_id = $client->id;
@ -1410,8 +1532,10 @@ class PaymentTest extends TestCase
$credit_calc = new InvoiceSum($credit);
$credit_calc->build();
$credit = $credit_calc->getCredit();
$credit->save(); //$10 credit
$credit = $credit_calc->getCredit()->service()->markSent()->save(); //$10 credit
$this->assertEquals(10, $credit->amount);
$this->assertEquals(10, $credit->balance);
$data = [
'amount' => $invoice->amount,

View File

@ -163,18 +163,11 @@ class CreditPaymentTest extends TestCase
'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);
nlog($e->validator->getMessageBag());
}
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->postJson('/api/v1/payments/', $data);
$response->assertStatus(200);
$arr = $response->json();

View File

@ -11,22 +11,24 @@
namespace Tests\Feature;
use App\Factory\ClientFactory;
use App\Factory\CreditFactory;
use App\Factory\InvoiceFactory;
use App\Helpers\Invoice\InvoiceSum;
use App\Models\ClientContact;
use Tests\TestCase;
use App\Models\Client;
use App\Models\Credit;
use App\Models\Invoice;
use App\Models\Payment;
use Tests\MockAccountData;
use App\Models\ClientContact;
use App\Factory\ClientFactory;
use App\Factory\CreditFactory;
use App\DataMapper\InvoiceItem;
use App\Factory\InvoiceFactory;
use App\Utils\Traits\MakesHash;
use App\Helpers\Invoice\InvoiceSum;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Routing\Middleware\ThrottleRequests;
use Illuminate\Support\Facades\Session;
use Illuminate\Validation\ValidationException;
use Tests\MockAccountData;
use Tests\TestCase;
use Illuminate\Routing\Middleware\ThrottleRequests;
use Illuminate\Foundation\Testing\DatabaseTransactions;
/**
* @test
@ -59,6 +61,132 @@ class RefundTest extends TestCase
// $this->withoutExceptionHandling();
}
public function testRefundAndAppliedAmounts()
{
$data = [
'amount' => 500,
'client_id' => $this->client->hashed_id,
'date' => '2020/12/12',
];
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->postJson('/api/v1/payments', $data);
$response->assertStatus(200);
$arr = $response->json();
$payment_id = $arr['data']['id'];
$item = new InvoiceItem;
$item->cost = 300;
$item->quantity = 1;
$i = Invoice::factory()
->create([
'user_id' => $this->user->id,
'company_id' => $this->company->id,
'client_id' => $this->client->id,
'line_items' => [$item],
'discount' => 0,
'tax_name1' => '',
'tax_name2' => '',
'tax_name3' => '',
'tax_rate1' => 0,
'tax_rate2' => 0,
'tax_rate3' => 0,
]);
$i->calc()->getInvoice();
$i->service()->markSent()->save();
$this->assertEquals(300, $i->balance);
$data = [
'client_id' => $this->client->hashed_id,
'invoices' => [
[
'invoice_id' => $i->hashed_id,
'amount' => 300
],
]
];
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->putJson('/api/v1/payments/'.$payment_id, $data);
$response->assertStatus(200);
$i = $i->fresh();
$this->assertEquals(0, $i->balance);
$payment = Payment::find($this->decodePrimaryKey($payment_id));
$this->assertNotNull($payment);
$this->assertEquals(500, $payment->amount);
$this->assertEquals(300, $payment->applied);
$this->assertEquals(0, $payment->refunded);
$data = [
'id' => $this->encodePrimaryKey($payment->id),
'invoices' => [
[
'invoice_id' => $i->hashed_id,
'amount' => $i->amount,
],
],
'date' => '2020/12/12',
];
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->postJson('/api/v1/payments/refund', $data);
$response->assertStatus(200);
$payment = $payment->fresh();
$i = $i->fresh();
$this->assertEquals(300, $payment->refunded);
$this->assertEquals(300, $i->balance);
$this->assertEquals(2, $i->status_id);
$data = [
'client_id' => $this->client->hashed_id,
'invoices' => [
[
'invoice_id' => $i->hashed_id,
'amount' => 200
],
]
];
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->putJson('/api/v1/payments/'.$payment_id, $data);
$response->assertStatus(200);
$payment = $payment->fresh();
$i = $i->fresh();
$this->assertEquals(300, $payment->refunded);
$this->assertEquals(100, $i->balance);
$this->assertEquals(3, $i->status_id);
$this->assertEquals(500, $payment->applied);
}
/**
* Test that a simple payment of $50
* is able to be refunded.
@ -552,29 +680,22 @@ class RefundTest extends TestCase
$this->invoice = InvoiceFactory::create($this->company->id, $this->user->id); //stub the company and user_id
$this->invoice->client_id = $client->id;
$this->invoice->status_id = Invoice::STATUS_SENT;
$this->invoice->line_items = $this->buildLineItems();
$this->invoice->uses_inclusive_taxes = false;
$this->invoice->client_id = $client->id;
$this->invoice->save();
$invoice_calc = new InvoiceSum($this->invoice);
$invoice_calc->build();
$this->invoice = $invoice_calc->getInvoice();
$this->invoice->save();
$this->invoice->calc()->getInvoice()->service()->markSent()->save();
$this->credit = CreditFactory::create($this->company->id, $this->user->id);
$this->credit->client_id = $client->id;
$this->credit->status_id = 2;
$this->credit->line_items = $this->buildLineItems();
$this->credit->amount = 10;
$this->credit->balance = 10;
$this->credit->uses_inclusive_taxes = false;
$this->credit->save();
$this->credit->date = now()->format('Y-m-d');
$this->credit->due_date = now()->addMonth()->format('Y-m-d');
$this->credit->calc()->getCredit()->service()->markSent()->save();
$this->assertEquals(10, $this->credit->amount);
$this->assertEquals(10, $this->credit->balance);
$data = [
'amount' => 50,
@ -656,26 +777,62 @@ class RefundTest extends TestCase
public function testRefundsWhenCreditsArePresent()
{
$cl = Client::factory()->create([
'company_id' => $this->company->id,
'user_id' => $this->user->id,
]);
nlog($cl->id);
$i = Invoice::factory()->create([
'company_id' => $this->company->id,
'user_id' => $this->user->id,
'client_id' => $this->client->id,
'client_id' => $cl->id,
'status_id' => Invoice::STATUS_SENT,
'amount' => 1000,
'balance' => 1000,
]);
$item = new InvoiceItem;
$item->cost = 1000;
$item->quantity = 1;
$i->line_items = [$item];
$i->service()->markSent()->save();
$this->assertEquals(1000, $i->balance);
$c = Credit::factory()->create([
'company_id' => $this->company->id,
'user_id' => $this->user->id,
'client_id' => $this->client->id,
'client_id' => $cl->id,
'status_id' => Invoice::STATUS_SENT,
'amount' => 100,
'balance' => 100,
'date' => now()->format('Y-m-d'),
'due_date' => now()->addMonth()->format('Y-m-d'),
]);
$item = new InvoiceItem();
$item->cost = 100;
$item->quantity = 1;
$c->line_items = [$item];
$c->service()->markSent()->save();
$this->assertEquals(100, $c->balance);
$this->assertNotNull($c);
$this->assertEquals(2, $c->status_id);
$this->assertEquals($cl->id, $c->client_id);
$this->assertEquals($cl->id, $i->client_id);
$data = [
'client_id' => $this->client->hashed_id,
'amount' => 900,
'client_id' => $cl->hashed_id,
'invoices' => [
[
'invoice_id' => $i->hashed_id,
@ -706,7 +863,7 @@ class RefundTest extends TestCase
$refund = [
'id' => $payment_id,
'client_id' => $this->client->hashed_id,
'client_id' => $cl->hashed_id,
'amount' => 10,
'date' => now()->format('Y-m-d'),
'invoices' => [