From 81385e4857a1388f7c1e21a3410559f28294ea93 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 19 Mar 2024 12:17:25 +1100 Subject: [PATCH 01/11] Clean up for password resets --- app/Jobs/Cron/UpdateCalculatedFields.php | 13 +++++--- .../PayPalPPCPPaymentDriver.php | 27 +++++++++++++++- .../PayPalRestPaymentDriver.php | 32 +++++++++++++++++-- .../ninja2020/gateways/paypal/pay.blade.php | 4 +-- 4 files changed, 66 insertions(+), 10 deletions(-) diff --git a/app/Jobs/Cron/UpdateCalculatedFields.php b/app/Jobs/Cron/UpdateCalculatedFields.php index 9145ab8f9842..8abf9a2bd48e 100644 --- a/app/Jobs/Cron/UpdateCalculatedFields.php +++ b/app/Jobs/Cron/UpdateCalculatedFields.php @@ -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(); } } diff --git a/app/PaymentDrivers/PayPalPPCPPaymentDriver.php b/app/PaymentDrivers/PayPalPPCPPaymentDriver.php index 5c86fae72acc..35709850a55d 100644 --- a/app/PaymentDrivers/PayPalPPCPPaymentDriver.php +++ b/app/PaymentDrivers/PayPalPPCPPaymentDriver.php @@ -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; diff --git a/app/PaymentDrivers/PayPalRestPaymentDriver.php b/app/PaymentDrivers/PayPalRestPaymentDriver.php index 1d2bbd90bac3..323392205afe 100644 --- a/app/PaymentDrivers/PayPalRestPaymentDriver.php +++ b/app/PaymentDrivers/PayPalRestPaymentDriver.php @@ -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; diff --git a/resources/views/portal/ninja2020/gateways/paypal/pay.blade.php b/resources/views/portal/ninja2020/gateways/paypal/pay.blade.php index db7b9d563d64..05a9f6cf84f0 100644 --- a/resources/views/portal/ninja2020/gateways/paypal/pay.blade.php +++ b/resources/views/portal/ninja2020/gateways/paypal/pay.blade.php @@ -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(); From 0cada4e6476d9f37f251272e3913291db363f6f5 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 19 Mar 2024 13:06:42 +1100 Subject: [PATCH 02/11] Fies for company-key path --- app/Http/Controllers/Auth/ContactLoginController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Http/Controllers/Auth/ContactLoginController.php b/app/Http/Controllers/Auth/ContactLoginController.php index 42add3851db3..6060dabae268 100644 --- a/app/Http/Controllers/Auth/ContactLoginController.php +++ b/app/Http/Controllers/Auth/ContactLoginController.php @@ -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(); From 4f8ceebf8062fcb94064db2a59cdce9998684075 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 19 Mar 2024 13:16:20 +1100 Subject: [PATCH 03/11] Minor fixes --- app/PaymentDrivers/PayPalRestPaymentDriver.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/PaymentDrivers/PayPalRestPaymentDriver.php b/app/PaymentDrivers/PayPalRestPaymentDriver.php index 323392205afe..464177737f15 100644 --- a/app/PaymentDrivers/PayPalRestPaymentDriver.php +++ b/app/PaymentDrivers/PayPalRestPaymentDriver.php @@ -213,7 +213,7 @@ class PayPalRestPaymentDriver extends BaseDriver $request['gateway_response'] = str_replace("Error: ", "", $request['gateway_response']); $response = json_decode($request['gateway_response'], true); - nlog($response); + // nlog($response); //capture $orderID = $response['orderID']; From 00b5dac278b10a267df92e0a9ac92a707800408e Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 19 Mar 2024 19:48:26 +1100 Subject: [PATCH 04/11] Add upload extensions to account transformer --- app/Transformers/AccountTransformer.php | 3 +- composer.lock | 46 ++++++------ config/open-telemetry.php | 98 ------------------------- 3 files changed, 25 insertions(+), 122 deletions(-) delete mode 100644 config/open-telemetry.php diff --git a/app/Transformers/AccountTransformer.php b/app/Transformers/AccountTransformer.php index 4e38ee26a385..30023ce9623b 100644 --- a/app/Transformers/AccountTransformer.php +++ b/app/Transformers/AccountTransformer.php @@ -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'), ]; } diff --git a/composer.lock b/composer.lock index f5797d572c29..9dec41427fb6 100644 --- a/composer.lock +++ b/composer.lock @@ -1343,16 +1343,16 @@ }, { "name": "aws/aws-sdk-php", - "version": "3.301.1", + "version": "3.301.2", "source": { "type": "git", "url": "https://github.com/aws/aws-sdk-php.git", - "reference": "0a910d2b35e7087337cdf3569dc9b6ce232aafba" + "reference": "7f8180275e624cb566d8af77d2f1c958bf5be35b" }, "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/7f8180275e624cb566d8af77d2f1c958bf5be35b", + "reference": "7f8180275e624cb566d8af77d2f1c958bf5be35b", "shasum": "" }, "require": { @@ -1432,9 +1432,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.2" }, - "time": "2024-03-15T18:14:42+00:00" + "time": "2024-03-18T18:06:18+00:00" }, { "name": "bacon/bacon-qr-code", @@ -3442,16 +3442,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": { @@ -3480,9 +3480,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", @@ -16748,16 +16748,16 @@ }, { "name": "friendsofphp/php-cs-fixer", - "version": "v3.51.0", + "version": "v3.52.0", "source": { "type": "git", "url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git", - "reference": "127fa74f010da99053e3f5b62672615b72dd6efd" + "reference": "a3564bd66f4bce9bc871ef18b690e2dc67a7f969" }, "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/a3564bd66f4bce9bc871ef18b690e2dc67a7f969", + "reference": "a3564bd66f4bce9bc871ef18b690e2dc67a7f969", "shasum": "" }, "require": { @@ -16828,7 +16828,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.0" }, "funding": [ { @@ -16836,7 +16836,7 @@ "type": "github" } ], - "time": "2024-02-28T19:50:06+00:00" + "time": "2024-03-18T18:40:11+00:00" }, { "name": "hamcrest/hamcrest-php", @@ -17562,16 +17562,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": { @@ -17620,7 +17620,7 @@ "type": "tidelift" } ], - "time": "2024-03-13T12:27:20+00:00" + "time": "2024-03-18T16:53:53+00:00" }, { "name": "phpunit/php-code-coverage", diff --git a/config/open-telemetry.php b/config/open-telemetry.php deleted file mode 100644 index 355c0118e819..000000000000 --- a/config/open-telemetry.php +++ /dev/null @@ -1,98 +0,0 @@ - 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, -]; From 00061cfa25675d66d70bf9330e68aa451a519034 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 20 Mar 2024 08:43:49 +1100 Subject: [PATCH 05/11] Catch for payment edge cases --- app/PaymentDrivers/Stripe/ACH.php | 34 ++++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/app/PaymentDrivers/Stripe/ACH.php b/app/PaymentDrivers/Stripe/ACH.php index 4015fde43569..d7594ea1c05d 100644 --- a/app/PaymentDrivers/Stripe/ACH.php +++ b/app/PaymentDrivers/Stripe/ACH.php @@ -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: From 73c73d1f5c100019b73dc29840d953655029533d Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 20 Mar 2024 16:52:14 +1100 Subject: [PATCH 06/11] Fixes for applied and unapplied amounts after a refund --- .../Payment/ValidRefundableRequest.php | 3 - .../PaymentAppliedValidAmount.php | 5 +- tests/Feature/RefundTest.php | 128 ++++++++++++++++++ 3 files changed, 132 insertions(+), 4 deletions(-) diff --git a/app/Http/ValidationRules/Payment/ValidRefundableRequest.php b/app/Http/ValidationRules/Payment/ValidRefundableRequest.php index 49530bca72a1..093aba14c839 100644 --- a/app/Http/ValidationRules/Payment/ValidRefundableRequest.php +++ b/app/Http/ValidationRules/Payment/ValidRefundableRequest.php @@ -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) { diff --git a/app/Http/ValidationRules/PaymentAppliedValidAmount.php b/app/Http/ValidationRules/PaymentAppliedValidAmount.php index c6257fde8d1f..06f83315b288 100644 --- a/app/Http/ValidationRules/PaymentAppliedValidAmount.php +++ b/app/Http/ValidationRules/PaymentAppliedValidAmount.php @@ -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')) diff --git a/tests/Feature/RefundTest.php b/tests/Feature/RefundTest.php index 3b5b246da49c..002a3eb43256 100644 --- a/tests/Feature/RefundTest.php +++ b/tests/Feature/RefundTest.php @@ -11,6 +11,7 @@ namespace Tests\Feature; +use App\DataMapper\InvoiceItem; use App\Factory\ClientFactory; use App\Factory\CreditFactory; use App\Factory\InvoiceFactory; @@ -59,6 +60,133 @@ 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. From a871e319eae47574bd749841356aa0b93b802c90 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 20 Mar 2024 17:17:23 +1100 Subject: [PATCH 07/11] Fixes for attempting to pass a negative value for a applied amount --- .../Requests/Payment/StorePaymentRequest.php | 1 - .../Requests/Payment/UpdatePaymentRequest.php | 5 +- .../PaymentAppliedValidAmount.php | 4 - .../ValidCreditsPresentRule.php | 5 +- tests/Feature/PaymentTest.php | 79 +++++++- tests/Feature/Payments/CreditPaymentTest.php | 17 +- tests/Feature/RefundTest.php | 187 +++++++++--------- 7 files changed, 176 insertions(+), 122 deletions(-) diff --git a/app/Http/Requests/Payment/StorePaymentRequest.php b/app/Http/Requests/Payment/StorePaymentRequest.php index 1bb5808b82e8..000a07d6ac01 100644 --- a/app/Http/Requests/Payment/StorePaymentRequest.php +++ b/app/Http/Requests/Payment/StorePaymentRequest.php @@ -78,7 +78,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']; } } diff --git a/app/Http/Requests/Payment/UpdatePaymentRequest.php b/app/Http/Requests/Payment/UpdatePaymentRequest.php index f4b90758adcf..d24ed6b32d89 100644 --- a/app/Http/Requests/Payment/UpdatePaymentRequest.php +++ b/app/Http/Requests/Payment/UpdatePaymentRequest.php @@ -44,7 +44,8 @@ class UpdatePaymentRequest extends Request $rules = [ 'invoices' => ['array', new PaymentAppliedValidAmount($this->all()), new ValidCreditsPresentRule($this->all())], - 'invoices.*.invoice_id' => 'distinct', + 'invoices.*.invoice_id' => 'sometimes|distinct', + 'invoices.*.amount' => 'sometimes|numeric|min:0', ]; if ($this->number) { @@ -85,7 +86,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 +93,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']); } diff --git a/app/Http/ValidationRules/PaymentAppliedValidAmount.php b/app/Http/ValidationRules/PaymentAppliedValidAmount.php index 06f83315b288..863b56d68986 100644 --- a/app/Http/ValidationRules/PaymentAppliedValidAmount.php +++ b/app/Http/ValidationRules/PaymentAppliedValidAmount.php @@ -87,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']) { diff --git a/app/Http/ValidationRules/ValidCreditsPresentRule.php b/app/Http/ValidationRules/ValidCreditsPresentRule.php index 7dfb07df5f99..877a0c1d3e1d 100644 --- a/app/Http/ValidationRules/ValidCreditsPresentRule.php +++ b/app/Http/ValidationRules/ValidCreditsPresentRule.php @@ -49,11 +49,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(); - + $credit_collection = Credit::query()->where('client_id', request()->input('client_id'))->whereIn('id', array_column($this->input['credits'], 'credit_id'))->count(); return $credit_collection == count($this->input['credits']); } diff --git a/tests/Feature/PaymentTest.php b/tests/Feature/PaymentTest.php index cba90ac705d5..dcadebe4f301 100644 --- a/tests/Feature/PaymentTest.php +++ b/tests/Feature/PaymentTest.php @@ -62,6 +62,74 @@ class PaymentTest extends TestCase ); } + 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 +1463,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 +1479,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, diff --git a/tests/Feature/Payments/CreditPaymentTest.php b/tests/Feature/Payments/CreditPaymentTest.php index 465a3d7f0cc6..1ecde31a3018 100644 --- a/tests/Feature/Payments/CreditPaymentTest.php +++ b/tests/Feature/Payments/CreditPaymentTest.php @@ -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(); diff --git a/tests/Feature/RefundTest.php b/tests/Feature/RefundTest.php index 002a3eb43256..e27c89c80dfb 100644 --- a/tests/Feature/RefundTest.php +++ b/tests/Feature/RefundTest.php @@ -63,127 +63,126 @@ class RefundTest extends TestCase public function testRefundAndAppliedAmounts() { + $data = [ + 'amount' => 500, + 'client_id' => $this->client->hashed_id, + 'date' => '2020/12/12', -$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 = $this->withHeaders([ - 'X-API-SECRET' => config('ninja.api_secret'), - 'X-API-TOKEN' => $this->token, -])->postJson('/api/v1/payments', $data); + $response->assertStatus(200); -$response->assertStatus(200); + $arr = $response->json(); -$arr = $response->json(); + $payment_id = $arr['data']['id']; -$payment_id = $arr['data']['id']; + $item = new InvoiceItem; + $item->cost = 300; + $item->quantity = 1; -$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 = 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(); -$i->calc()->getInvoice(); -$i->service()->markSent()->save(); + $this->assertEquals(300, $i->balance); -$this->assertEquals(300, $i->balance); + $data = [ + 'client_id' => $this->client->hashed_id, + 'invoices' => [ + [ + 'invoice_id' => $i->hashed_id, + 'amount' => 300 + ], + ] + ]; -$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 = $this->withHeaders([ - 'X-API-SECRET' => config('ninja.api_secret'), - 'X-API-TOKEN' => $this->token, -])->putJson('/api/v1/payments/'.$payment_id, $data); + $response->assertStatus(200); -$response->assertStatus(200); + $i = $i->fresh(); -$i = $i->fresh(); + $this->assertEquals(0, $i->balance); -$this->assertEquals(0, $i->balance); + $payment = Payment::find($this->decodePrimaryKey($payment_id)); -$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); -$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', + ]; -$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 = $this->withHeaders([ - 'X-API-SECRET' => config('ninja.api_secret'), - 'X-API-TOKEN' => $this->token, -])->postJson('/api/v1/payments/refund', $data); + $response->assertStatus(200); -$response->assertStatus(200); + $payment = $payment->fresh(); + $i = $i->fresh(); -$payment = $payment->fresh(); -$i = $i->fresh(); - -$this->assertEquals(300, $payment->refunded); -$this->assertEquals(300, $i->balance); -$this->assertEquals(2, $i->status_id); + $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 - ], - ] -]; + $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 = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->putJson('/api/v1/payments/'.$payment_id, $data); -$response->assertStatus(200); + $response->assertStatus(200); -$payment = $payment->fresh(); -$i = $i->fresh(); + $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); + $this->assertEquals(300, $payment->refunded); + $this->assertEquals(100, $i->balance); + $this->assertEquals(3, $i->status_id); + $this->assertEquals(500, $payment->applied); } From 584c83e8a68e0071fd2828c6c28483bd9f7e4f01 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 20 Mar 2024 17:51:07 +1100 Subject: [PATCH 08/11] Fixes fo r tests --- .../ValidCreditsPresentRule.php | 6 +- tests/Feature/RefundTest.php | 86 +++++++++++++------ 2 files changed, 63 insertions(+), 29 deletions(-) diff --git a/app/Http/ValidationRules/ValidCreditsPresentRule.php b/app/Http/ValidationRules/ValidCreditsPresentRule.php index 877a0c1d3e1d..bdec15177c8f 100644 --- a/app/Http/ValidationRules/ValidCreditsPresentRule.php +++ b/app/Http/ValidationRules/ValidCreditsPresentRule.php @@ -50,7 +50,11 @@ class ValidCreditsPresentRule implements Rule private function validCreditsPresent(): bool { if (array_key_exists('credits', $this->input) && is_array($this->input['credits']) && count($this->input['credits']) > 0) { - $credit_collection = Credit::query()->where('client_id', request()->input('client_id'))->whereIn('id', array_column($this->input['credits'], 'credit_id'))->count(); + $client_id = is_numeric(request()->input('client_id')) ?: $this->decodePrimaryKey(request()->input('client_id')); + // $credit_collection = Credit::query()->where('client_id', $client_id)->whereIn('id', array_column($this->input['credits'], 'credit_id'))->count(); + + $credit_collection = Credit::query()->whereIn('id', array_column($this->input['credits'], 'credit_id'))->count(); + return $credit_collection == count($this->input['credits']); } diff --git a/tests/Feature/RefundTest.php b/tests/Feature/RefundTest.php index e27c89c80dfb..690bbc68cbdf 100644 --- a/tests/Feature/RefundTest.php +++ b/tests/Feature/RefundTest.php @@ -11,23 +11,24 @@ namespace Tests\Feature; -use App\DataMapper\InvoiceItem; -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 @@ -679,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, @@ -783,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, @@ -833,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' => [ From 05593f0824fef0fc296c147ab43b54ca87f9a6b2 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 20 Mar 2024 20:05:22 +1100 Subject: [PATCH 09/11] Minor fixes --- app/Filters/InvoiceFilters.php | 10 ++++++---- app/Http/Requests/Payment/RefundPaymentRequest.php | 2 -- app/Models/Task.php | 2 ++ 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/app/Filters/InvoiceFilters.php b/app/Filters/InvoiceFilters.php index bd03ca01d42a..0d6d2ff2470a 100644 --- a/app/Filters/InvoiceFilters.php +++ b/app/Filters/InvoiceFilters.php @@ -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); } diff --git a/app/Http/Requests/Payment/RefundPaymentRequest.php b/app/Http/Requests/Payment/RefundPaymentRequest.php index bf43b65f70d6..abcb694fbb17 100644 --- a/app/Http/Requests/Payment/RefundPaymentRequest.php +++ b/app/Http/Requests/Payment/RefundPaymentRequest.php @@ -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); diff --git a/app/Models/Task.php b/app/Models/Task.php index f1716b15a870..a5d63760c19d 100644 --- a/app/Models/Task.php +++ b/app/Models/Task.php @@ -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); } From 8274ba0a3d0d9e8df16937a4288b4e72af6f8955 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 20 Mar 2024 22:10:06 +1100 Subject: [PATCH 10/11] Fixes for validation --- .../Requests/Payment/StorePaymentRequest.php | 70 +++++++++---------- .../Requests/Payment/UpdatePaymentRequest.php | 21 +++--- .../ValidCreditsPresentRule.php | 5 +- tests/Feature/PaymentTest.php | 53 ++++++++++++++ 4 files changed, 97 insertions(+), 52 deletions(-) diff --git a/app/Http/Requests/Payment/StorePaymentRequest.php b/app/Http/Requests/Payment/StorePaymentRequest.php index 000a07d6ac01..fed4c2f9c808 100644 --- a/app/Http/Requests/Payment/StorePaymentRequest.php +++ b/app/Http/Requests/Payment/StorePaymentRequest.php @@ -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() { @@ -102,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; - } } diff --git a/app/Http/Requests/Payment/UpdatePaymentRequest.php b/app/Http/Requests/Payment/UpdatePaymentRequest.php index d24ed6b32d89..67198525aaee 100644 --- a/app/Http/Requests/Payment/UpdatePaymentRequest.php +++ b/app/Http/Requests/Payment/UpdatePaymentRequest.php @@ -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,17 +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' => 'sometimes|distinct', - 'invoices.*.amount' => 'sometimes|numeric|min:0', + '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')) { @@ -75,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']); } diff --git a/app/Http/ValidationRules/ValidCreditsPresentRule.php b/app/Http/ValidationRules/ValidCreditsPresentRule.php index bdec15177c8f..50d3b32af5d0 100644 --- a/app/Http/ValidationRules/ValidCreditsPresentRule.php +++ b/app/Http/ValidationRules/ValidCreditsPresentRule.php @@ -17,6 +17,7 @@ use Illuminate\Contracts\Validation\Rule; /** * Class ValidCreditsPresentRule. + * @deprecated 20-03-2024 */ class ValidCreditsPresentRule implements Rule { @@ -50,11 +51,7 @@ class ValidCreditsPresentRule implements Rule private function validCreditsPresent(): bool { if (array_key_exists('credits', $this->input) && is_array($this->input['credits']) && count($this->input['credits']) > 0) { - $client_id = is_numeric(request()->input('client_id')) ?: $this->decodePrimaryKey(request()->input('client_id')); - // $credit_collection = Credit::query()->where('client_id', $client_id)->whereIn('id', array_column($this->input['credits'], 'credit_id'))->count(); - $credit_collection = Credit::query()->whereIn('id', array_column($this->input['credits'], 'credit_id'))->count(); - return $credit_collection == count($this->input['credits']); } diff --git a/tests/Feature/PaymentTest.php b/tests/Feature/PaymentTest.php index dcadebe4f301..2c662a67fcfa 100644 --- a/tests/Feature/PaymentTest.php +++ b/tests/Feature/PaymentTest.php @@ -62,6 +62,59 @@ 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([ From 44542b343c1512b8a537a90ad7db3ca86dc5e14b Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 20 Mar 2024 22:12:21 +1100 Subject: [PATCH 11/11] Updated lock --- composer.lock | 147 +++++++++++++++++++++++++------------------------- 1 file changed, 74 insertions(+), 73 deletions(-) diff --git a/composer.lock b/composer.lock index 23fc78064892..4648e229f4ea 100644 --- a/composer.lock +++ b/composer.lock @@ -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.2", + "version": "3.301.3", "source": { "type": "git", "url": "https://github.com/aws/aws-sdk-php.git", - "reference": "7f8180275e624cb566d8af77d2f1c958bf5be35b" + "reference": "6b21e34d24a73ea66492869be90443069034fdb3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/7f8180275e624cb566d8af77d2f1c958bf5be35b", - "reference": "7f8180275e624cb566d8af77d2f1c958bf5be35b", + "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.2" + "source": "https://github.com/aws/aws-sdk-php/tree/3.301.3" }, - "time": "2024-03-18T18:06:18+00:00" + "time": "2024-03-19T18:05:04+00:00" }, { "name": "bacon/bacon-qr-code", @@ -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.52.0", + "version": "v3.52.1", "source": { "type": "git", "url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git", - "reference": "a3564bd66f4bce9bc871ef18b690e2dc67a7f969" + "reference": "6e77207f0d851862ceeb6da63e6e22c01b1587bc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/a3564bd66f4bce9bc871ef18b690e2dc67a7f969", - "reference": "a3564bd66f4bce9bc871ef18b690e2dc67a7f969", + "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.52.0" + "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.52.1" }, "funding": [ { @@ -17006,7 +17007,7 @@ "type": "github" } ], - "time": "2024-03-18T18:40:11+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", @@ -19704,5 +19705,5 @@ "platform-dev": { "php": "^8.1|^8.2" }, - "plugin-api-version": "2.6.0" + "plugin-api-version": "2.3.0" }