diff --git a/app/Events/Statement/StatementWasEmailed.php b/app/Events/Statement/StatementWasEmailed.php new file mode 100644 index 0000000000..ea79a211a2 --- /dev/null +++ b/app/Events/Statement/StatementWasEmailed.php @@ -0,0 +1,42 @@ +company()->id.',is_deleted,0'; - + $rules['amount'] = ['sometimes', 'bail', 'numeric', 'nullable', 'max:99999999999999']; return $rules; } @@ -55,6 +55,7 @@ class StoreBankTransactionRequest extends Request $input['bank_integration_id'] = $this->decodePrimaryKey($input['bank_integration_id']); } + $this->replace($input); } } diff --git a/app/Http/Requests/BankTransaction/UpdateBankTransactionRequest.php b/app/Http/Requests/BankTransaction/UpdateBankTransactionRequest.php index 9cfab70d59..03544f2d07 100644 --- a/app/Http/Requests/BankTransaction/UpdateBankTransactionRequest.php +++ b/app/Http/Requests/BankTransaction/UpdateBankTransactionRequest.php @@ -44,9 +44,7 @@ class UpdateBankTransactionRequest extends Request $rules['vendor_id'] = 'bail|required|exists:vendors,id,company_id,'.auth()->user()->company()->id.',is_deleted,0'; } - // if (isset($this->expense_id)) { - // $rules['expense_id'] = 'bail|required|exists:expenses,id,company_id,'.auth()->user()->company()->id.',is_deleted,0'; - // } + $rules['amount'] = ['sometimes', 'bail', 'nullable', 'numeric', 'max:99999999999999']; $rules['bank_integration_id'] = 'bail|required|exists:bank_integrations,id,company_id,'.auth()->user()->company()->id.',is_deleted,0'; diff --git a/app/Http/Requests/Expense/StoreExpenseRequest.php b/app/Http/Requests/Expense/StoreExpenseRequest.php index 5667e4b1ef..b3aec1fce9 100644 --- a/app/Http/Requests/Expense/StoreExpenseRequest.php +++ b/app/Http/Requests/Expense/StoreExpenseRequest.php @@ -53,6 +53,7 @@ class StoreExpenseRequest extends Request $rules['payment_date'] = 'bail|nullable|sometimes|date:Y-m-d'; $rules['date'] = 'bail|sometimes|date:Y-m-d'; $rules['documents'] = 'bail|sometimes|array'; + $rules['amount'] = ['sometimes', 'bail', 'nullable', 'numeric', 'max:99999999999999']; return $this->globalRules($rules); } diff --git a/app/Http/Requests/Expense/UpdateExpenseRequest.php b/app/Http/Requests/Expense/UpdateExpenseRequest.php index 25ca66022a..ced5c9bd1a 100644 --- a/app/Http/Requests/Expense/UpdateExpenseRequest.php +++ b/app/Http/Requests/Expense/UpdateExpenseRequest.php @@ -55,7 +55,7 @@ class UpdateExpenseRequest extends Request $rules['transaction_id'] = 'bail|sometimes|nullable|exists:bank_transactions,id,company_id,'.$user->company()->id; $rules['invoice_id'] = 'bail|sometimes|nullable|exists:invoices,id,company_id,'.$user->company()->id; $rules['documents'] = 'bail|sometimes|array'; - + $rules['amount'] = ['sometimes', 'bail', 'nullable', 'numeric', 'max:99999999999999']; return $this->globalRules($rules); } diff --git a/app/Http/Requests/Payment/RefundPaymentRequest.php b/app/Http/Requests/Payment/RefundPaymentRequest.php index 71fdf39e29..2cee811ea8 100644 --- a/app/Http/Requests/Payment/RefundPaymentRequest.php +++ b/app/Http/Requests/Payment/RefundPaymentRequest.php @@ -67,9 +67,8 @@ class RefundPaymentRequest extends Request $input = $this->all(); $rules = [ - 'id' => 'bail|required', //@phpstan-ignore-line - 'id' => new ValidRefundableRequest($input), - 'amount' => 'numeric', + 'id' => ['bail','required', new ValidRefundableRequest($input)], + 'amount' => ['numeric', 'max:99999999999999'], 'date' => 'required', 'invoices.*.invoice_id' => 'required', 'invoices.*.amount' => 'required', diff --git a/app/Http/Requests/Payment/StorePaymentRequest.php b/app/Http/Requests/Payment/StorePaymentRequest.php index 152a8aecff..10fe585229 100644 --- a/app/Http/Requests/Payment/StorePaymentRequest.php +++ b/app/Http/Requests/Payment/StorePaymentRequest.php @@ -50,7 +50,7 @@ class StorePaymentRequest extends Request 'invoices.*.invoice_id' => ['bail','required','distinct', new ValidInvoicesRules($this->all()),Rule::exists('invoices','id')->where('company_id', $user->company()->id)->where('client_id', $this->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', $this->client_id)], 'credits.*.amount' => ['bail','required', new CreditsSumRule($this->all())], - 'amount' => ['bail', 'numeric', new PaymentAmountsBalanceRule()], + 'amount' => ['bail', 'numeric', new PaymentAmountsBalanceRule(), 'max:99999999999999'], '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)], ]; diff --git a/app/Listeners/Statement/StatementEmailedActivity.php b/app/Listeners/Statement/StatementEmailedActivity.php new file mode 100644 index 0000000000..0fae3bde51 --- /dev/null +++ b/app/Listeners/Statement/StatementEmailedActivity.php @@ -0,0 +1,56 @@ +activityRepo = $activityRepo; + } + + /** + * Handle the event. + * + * @param object $event + * @return void + */ + public function handle($event) + { + MultiDB::setDb($event->company->db); + + $fields = new stdClass(); + + $user_id = isset($event->event_vars['user_id']) ? $event->event_vars['user_id'] : $event->client->id; + + $fields->user_id = $user_id; + $fields->client_id = $event->client->id; + $fields->notes = $event->end_date; + $fields->company_id = $event->company->id; + $fields->activity_type_id = Activity::EMAIL_STATEMENT; + + $this->activityRepo->save($fields, $event->client, $event->event_vars); + } +} diff --git a/tests/Feature/MaxAmountTest.php b/tests/Feature/MaxAmountTest.php new file mode 100644 index 0000000000..e69610b0f0 --- /dev/null +++ b/tests/Feature/MaxAmountTest.php @@ -0,0 +1,199 @@ +makeTestData(); + + $this->withoutMiddleware( + ThrottleRequests::class + ); + + } + + public function testInvoiceMaxAmount() + { + $item = new InvoiceItem(); + $item->cost = 10000000000000000; + $item->quantity = 100; + + $data = [ + 'user_id' => $this->user->id, + 'client_id' => $this->client->id, + 'company_id' => $this->company->id, + 'line_items' => [$item] + ]; + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->postJson('/api/v1/invoices', $data); + + $response->assertStatus(422); + + $i = Invoice::factory()->create($data); + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->putJson("/api/v1/invoices/{$i->hashed_id}", $data); + + $response->assertStatus(422); + } + + + public function testCreditMaxAmount() + { + $item = new InvoiceItem(); + $item->cost = 10000000000000000; + $item->quantity = 100; + + $data = [ + 'user_id' => $this->user->id, + 'client_id' => $this->client->id, + 'company_id' => $this->company->id, + 'line_items' => [$item] + ]; + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->postJson('/api/v1/credits', $data); + + $response->assertStatus(422); + + $i = Credit::factory()->create($data); + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->putJson("/api/v1/credits/{$i->hashed_id}", $data); + + $response->assertStatus(422); + } + + + public function testQuoteMaxAmount() + { + $item = new InvoiceItem(); + $item->cost = 10000000000000000; + $item->quantity = 100; + + $data = [ + 'user_id' => $this->user->id, + 'client_id' => $this->client->id, + 'company_id' => $this->company->id, + 'line_items' => [$item] + ]; + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->postJson('/api/v1/quotes', $data); + + $response->assertStatus(422); + + $i = Quote::factory()->create($data); + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->putJson("/api/v1/quotes/{$i->hashed_id}", $data); + + $response->assertStatus(422); + } + +public function testPurchaseOrderMaxAmount() + { + $item = new InvoiceItem(); + $item->cost = 10000000000000000; + $item->quantity = 100; + + $data = [ + 'user_id' => $this->user->id, + 'vendor_id' => $this->vendor->id, + 'company_id' => $this->company->id, + 'line_items' => [$item] + ]; + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->postJson('/api/v1/purchase_orders', $data); + + $response->assertStatus(422); + + $i = PurchaseOrder::factory()->create($data); + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->putJson("/api/v1/purchase_orders/{$i->hashed_id}", $data); + + $response->assertStatus(422); + } + + public function testRecurringInvoiceMaxAmount() + { + $item = new InvoiceItem(); + $item->cost = 10000000000000000; + $item->quantity = 100; + + $data = [ + 'user_id' => $this->user->id, + 'client_id' => $this->client->id, + 'company_id' => $this->company->id, + 'line_items' => [$item], + 'frequency_id' => 5 + ]; + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->postJson('/api/v1/recurring_invoices', $data); + + $response->assertStatus(422); + + $i = RecurringInvoice::factory()->create($data); + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->putJson("/api/v1/recurring_invoices/{$i->hashed_id}", $data); + + $response->assertStatus(422); + } +}