mirror of
				https://github.com/invoiceninja/invoiceninja.git
				synced 2025-11-01 20:17:35 -04:00 
			
		
		
		
	Throttle payment methods to prevent spam:
This commit is contained in:
		
							parent
							
								
									ffbfc11407
								
							
						
					
					
						commit
						f604e463c2
					
				| @ -33,6 +33,7 @@ class PaymentFactory | ||||
|         $payment->transaction_reference = null; | ||||
|         $payment->payer_id = null; | ||||
|         $payment->status_id = Payment::STATUS_PENDING; | ||||
|         $payment->exchange_rate = 1; | ||||
| 
 | ||||
|         return $payment; | ||||
|     } | ||||
|  | ||||
| @ -30,6 +30,11 @@ class PaymentMethodController extends Controller | ||||
| { | ||||
|     use MakesDates; | ||||
| 
 | ||||
|     public function __construct() | ||||
|     { | ||||
|         $this->middleware('throttle:10,1')->only('store'); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Display a listing of the resource. | ||||
|      * | ||||
| @ -92,7 +97,6 @@ class PaymentMethodController extends Controller | ||||
| 
 | ||||
|     public function verify(ClientGatewayToken $payment_method) | ||||
|     { | ||||
| //        $gateway = $this->getClientGateway();
 | ||||
| 
 | ||||
|         return $payment_method->gateway | ||||
|             ->driver(auth()->user()->client) | ||||
|  | ||||
| @ -22,6 +22,7 @@ class BlackListRule implements Rule | ||||
|     private array $blacklist = [ | ||||
|         'candassociates.com', | ||||
|         'vusra.com', | ||||
|         'fourthgenet.com', | ||||
|     ]; | ||||
| 
 | ||||
|     /** | ||||
|  | ||||
| @ -1450,6 +1450,20 @@ class CompanyImport implements ShouldQueue | ||||
|                 $new_obj->save(['timestamps' => false]); | ||||
|                 $new_obj->number = $this->getNextRecurringExpenseNumber($new_obj);    | ||||
|             } | ||||
|             elseif($class == 'App\Models\Project' && is_null($obj->{$match_key})){ | ||||
|                 $new_obj = new Project(); | ||||
|                 $new_obj->company_id = $this->company->id; | ||||
|                 $new_obj->fill($obj_array); | ||||
|                 $new_obj->save(['timestamps' => false]); | ||||
|                 $new_obj->number = $this->getNextProjectNumber($new_obj);    | ||||
|             } | ||||
|             elseif($class == 'App\Models\Task' && is_null($obj->{$match_key})){ | ||||
|                 $new_obj = new Task(); | ||||
|                 $new_obj->company_id = $this->company->id; | ||||
|                 $new_obj->fill($obj_array); | ||||
|                 $new_obj->save(['timestamps' => false]); | ||||
|                 $new_obj->number = $this->getNextTaskNumber($new_obj);    | ||||
|             } | ||||
|             elseif($class == 'App\Models\CompanyLedger'){ | ||||
|                 $new_obj = $class::firstOrNew( | ||||
|                         [$match_key => $obj->{$match_key}, 'company_id' => $this->company->id], | ||||
|  | ||||
							
								
								
									
										89
									
								
								app/Jobs/Report/ProfitAndLoss.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								app/Jobs/Report/ProfitAndLoss.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,89 @@ | ||||
| <?php | ||||
| /** | ||||
|  * Invoice Ninja (https://invoiceninja.com). | ||||
|  * | ||||
|  * @link https://github.com/invoiceninja/invoiceninja source repository | ||||
|  * | ||||
|  * @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com) | ||||
|  * | ||||
|  * @license https://www.elastic.co/licensing/elastic-license | ||||
|  */ | ||||
| 
 | ||||
| namespace App\Jobs\Report; | ||||
| 
 | ||||
| use App\Libraries\MultiDB; | ||||
| use App\Models\Company; | ||||
| use App\Services\Report\ProfitLoss; | ||||
| use Illuminate\Bus\Queueable; | ||||
| use Illuminate\Contracts\Queue\ShouldQueue; | ||||
| use Illuminate\Foundation\Bus\Dispatchable; | ||||
| use Illuminate\Queue\InteractsWithQueue; | ||||
| use Illuminate\Queue\SerializesModels; | ||||
| 
 | ||||
| class ProfitAndLoss implements ShouldQueue | ||||
| { | ||||
|     use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; | ||||
| 
 | ||||
|     protected Company $company; | ||||
| 
 | ||||
|     protected array $payload; | ||||
| 
 | ||||
|     /** | ||||
|      * Create a new job instance. | ||||
|      * | ||||
|      * @param RecurringInvoice $recurring_invoice | ||||
|      * @param string $db | ||||
|      */ | ||||
|     public function __construct(Company $company, array $payload) | ||||
|     { | ||||
|         $this->company = $company; | ||||
| 
 | ||||
|         $this->payload = $payload; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Execute the job. | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     public function handle() : void | ||||
|     { | ||||
| 
 | ||||
|         MultiDB::setDb($this->company->db); | ||||
| 
 | ||||
|         /* | ||||
|             payload variables. | ||||
| 
 | ||||
|             start_date - Y-m-d | ||||
|             end_date - Y-m-d | ||||
|             date_range -  | ||||
|                 all | ||||
|                 last7 | ||||
|                 last30 | ||||
|                 this_month | ||||
|                 last_month | ||||
|                 this_quarter | ||||
|                 last_quarter | ||||
|                 this_year | ||||
|                 custom | ||||
|             income_billed - true = Invoiced || false = Payments | ||||
|             expense_billed - true = Expensed || false = Expenses marked as paid | ||||
|             include_tax - true tax_included || false - tax_excluded | ||||
| 
 | ||||
|         */ | ||||
| 
 | ||||
|         $pl = new ProfitLoss($this->company, $this->payload); | ||||
| 
 | ||||
|         $pl->build(); | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|     public function failed($exception = null) | ||||
|     { | ||||
| 
 | ||||
|     } | ||||
| } | ||||
| @ -66,7 +66,11 @@ class PaymentRepository extends BaseRepository { | ||||
| 
 | ||||
|         //check currencies here and fill the exchange rate data if necessary
 | ||||
|         if (! $payment->id) { | ||||
|             $this->processExchangeRates($data, $payment); | ||||
|             $payment = $this->processExchangeRates($data, $payment); | ||||
| 
 | ||||
|             /* This is needed here otherwise the ->fill() overwrites anything that exists*/ | ||||
|             if($payment->exchange_rate != 1) | ||||
|                 unset($data['exchange_rate']); | ||||
| 
 | ||||
|             $is_existing_payment = false; | ||||
|             $client = Client::where('id', $data['client_id'])->withTrashed()->first(); | ||||
| @ -100,7 +104,12 @@ class PaymentRepository extends BaseRepository { | ||||
|         $payment->status_id = Payment::STATUS_COMPLETED; | ||||
| 
 | ||||
|         if (! $payment->currency_id && $client) { | ||||
|             $payment->currency_id = $client->company->settings->currency_id; | ||||
| 
 | ||||
|             if(property_exists($client->settings, 'currency_id')) | ||||
|                 $payment->currency_id = $client->settings->currency_id; | ||||
|             else     | ||||
|                 $payment->currency_id = $client->company->settings->currency_id; | ||||
|              | ||||
|         } | ||||
| 
 | ||||
|         $payment->save(); | ||||
| @ -199,8 +208,9 @@ class PaymentRepository extends BaseRepository { | ||||
|     public function processExchangeRates($data, $payment) | ||||
|     { | ||||
| 
 | ||||
|         if(array_key_exists('exchange_rate', $data) && isset($data['exchange_rate'])) | ||||
|         if(array_key_exists('exchange_rate', $data) && isset($data['exchange_rate']) && $data['exchange_rate'] != 1){ | ||||
|             return $payment; | ||||
|         } | ||||
| 
 | ||||
|         $client = Client::withTrashed()->find($data['client_id']); | ||||
| 
 | ||||
| @ -212,7 +222,6 @@ class PaymentRepository extends BaseRepository { | ||||
|             $exchange_rate = new CurrencyApi(); | ||||
| 
 | ||||
|             $payment->exchange_rate = $exchange_rate->exchangeRate($client_currency, $company_currency, Carbon::parse($payment->date)); | ||||
|             // $payment->exchange_currency_id = $client_currency;
 | ||||
|             $payment->exchange_currency_id = $company_currency; | ||||
|             $payment->currency_id = $client_currency; | ||||
| 
 | ||||
| @ -221,7 +230,6 @@ class PaymentRepository extends BaseRepository { | ||||
|          | ||||
|         $payment->currency_id = $company_currency; | ||||
| 
 | ||||
| 
 | ||||
|         return $payment; | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -61,7 +61,7 @@ class MarkPaid extends AbstractService | ||||
|         $payment->transaction_reference = ctrans('texts.manual_entry'); | ||||
|         $payment->currency_id = $this->invoice->client->getSetting('currency_id'); | ||||
|         $payment->is_manual = true; | ||||
|          | ||||
| 
 | ||||
|         if($this->invoice->company->timezone()) | ||||
|             $payment->date = now()->addSeconds($this->invoice->company->timezone()->utc_offset)->format('Y-m-d'); | ||||
| 
 | ||||
| @ -149,7 +149,7 @@ class MarkPaid extends AbstractService | ||||
|             //$payment->exchange_currency_id = $client_currency; // 23/06/2021
 | ||||
|             $payment->exchange_currency_id = $company_currency; | ||||
|          | ||||
|             $payment->save(); | ||||
|             $payment->saveQuietly(); | ||||
| 
 | ||||
|         } | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										246
									
								
								app/Services/Report/ProfitLoss.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										246
									
								
								app/Services/Report/ProfitLoss.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,246 @@ | ||||
| <?php | ||||
| /** | ||||
|  * Invoice Ninja (https://invoiceninja.com). | ||||
|  * | ||||
|  * @link https://github.com/invoiceninja/invoiceninja source repository | ||||
|  * | ||||
|  * @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com) | ||||
|  * | ||||
|  * @license https://www.elastic.co/licensing/elastic-license | ||||
|  */ | ||||
| 
 | ||||
| namespace App\Services\Report; | ||||
| 
 | ||||
| use App\Models\Company; | ||||
| use Illuminate\Support\Carbon; | ||||
| 
 | ||||
| class ProfitLoss | ||||
| { | ||||
|     private bool $is_income_billed = true; | ||||
| 
 | ||||
|     private bool $is_expense_billed = true; | ||||
| 
 | ||||
|     private bool $is_tax_included = true; | ||||
| 
 | ||||
|     private $start_date; | ||||
| 
 | ||||
|     private $end_date; | ||||
| 
 | ||||
|    /* | ||||
|         payload variables. | ||||
| 
 | ||||
|         start_date - Y-m-d | ||||
|         end_date - Y-m-d | ||||
|         date_range -  | ||||
|             all | ||||
|             last7 | ||||
|             last30 | ||||
|             this_month | ||||
|             last_month | ||||
|             this_quarter | ||||
|             last_quarter | ||||
|             this_year | ||||
|             custom | ||||
|         income_billed - true = Invoiced || false = Payments | ||||
|         expense_billed - true = Expensed || false = Expenses marked as paid | ||||
|         include_tax - true tax_included || false - tax_excluded | ||||
| 
 | ||||
|     */ | ||||
| 
 | ||||
|     protected array $payload; | ||||
| 
 | ||||
|     protected Company $company; | ||||
| 
 | ||||
|     public function __construct(Company $company, array $payload) | ||||
|     { | ||||
| 
 | ||||
|         $this->company = $company; | ||||
| 
 | ||||
|         $this->payload = $payload; | ||||
| 
 | ||||
|         $this->setBillingReportType(); | ||||
|     } | ||||
|      | ||||
|     public function build() | ||||
|     { | ||||
|         //get income
 | ||||
| 
 | ||||
|             //sift foreign currencies - calculate both converted foreign amounts to native currency and also also group amounts by currency.
 | ||||
| 
 | ||||
|         //get expenses
 | ||||
| 
 | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     /* | ||||
|         //returns an array of objects
 | ||||
|         => [ | ||||
|          {#2047
 | ||||
|            +"amount": "706.480000", | ||||
|            +"total_taxes": "35.950000", | ||||
|            +"currency_id": ""1"", | ||||
|            +"net_converted_amount": "670.5300000000", | ||||
|          }, | ||||
|          {#2444
 | ||||
|            +"amount": "200.000000", | ||||
|            +"total_taxes": "0.000000", | ||||
|            +"currency_id": ""23"", | ||||
|            +"net_converted_amount": "1.7129479802", | ||||
|          }, | ||||
|          {#2654
 | ||||
|            +"amount": "140.000000", | ||||
|            +"total_taxes": "40.000000", | ||||
|            +"currency_id": ""12"", | ||||
|            +"net_converted_amount": "69.3275024282", | ||||
|          }, | ||||
|        ] | ||||
|    */ | ||||
|     private function invoiceIncome() | ||||
|     { | ||||
|         return \DB::select( \DB::raw(" | ||||
|             SELECT | ||||
|             sum(invoices.amount) as amount, | ||||
|             sum(invoices.total_taxes) as total_taxes, | ||||
|             sum(invoices.amount - invoices.total_taxes) as net_amount, | ||||
|             IFNULL(JSON_EXTRACT( settings, '$.currency_id' ), :company_currency) AS currency_id, | ||||
|             (sum(invoices.amount - invoices.total_taxes) / IFNULL(invoices.exchange_rate, 1)) AS net_converted_amount | ||||
|             FROM clients | ||||
|             JOIN invoices | ||||
|             on invoices.client_id = clients.id | ||||
|             WHERE invoices.status_id IN (2,3,4) | ||||
|             AND invoices.company_id = :company_id | ||||
|             AND invoices.amount > 0 | ||||
|             AND clients.is_deleted = 0 | ||||
|             AND invoices.is_deleted = 0 | ||||
|             AND (invoices.date BETWEEN :start_date AND :end_date) | ||||
|             GROUP BY currency_id | ||||
|         "), ['company_currency' => $this->company->settings->currency_id, 'company_id' => $this->company->id, 'start_date' => $this->start_date, 'end_date' => $this->end_date]  );
 | ||||
| 
 | ||||
| 
 | ||||
|         //
 | ||||
|         // $total = array_reduce( commissionsArray, function ($sum, $entry) {
 | ||||
|         //   $sum += $entry->commission;
 | ||||
|         //   return $sum;
 | ||||
|         // }, 0);
 | ||||
|     } | ||||
| 
 | ||||
|     private function paymentIncome() | ||||
|     { | ||||
|         return \DB::select( \DB::raw(" | ||||
|              SELECT  | ||||
|              SUM(coalesce(payments.amount - payments.refunded,0)) as payments, | ||||
|              SUM(coalesce(payments.amount - payments.refunded,0)) * IFNULL(payments.exchange_rate ,1) as payments_converted | ||||
|              FROM clients  | ||||
|              INNER JOIN | ||||
|              payments ON  | ||||
|              clients.id=payments.client_id  | ||||
|              WHERE payments.status_id IN (1,4,5,6) | ||||
|              AND clients.is_deleted = false | ||||
|              AND payments.is_deleted = false | ||||
|              AND payments.company_id = :company_id | ||||
|              AND (payments.date BETWEEN :start_date AND :end_date) | ||||
|              GROUP BY payments.currency_id | ||||
|              ORDER BY payments.currency_id; | ||||
|         "), ['company_id' => $this->company->id, 'start_date' => $this->start_date, 'end_date' => $this->end_date]);
 | ||||
|      | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     private function expenseCalc() | ||||
|     { | ||||
| 
 | ||||
|       return \DB::select( \DB::raw(" | ||||
|             SELECT sum(expenses.amount) as amount, | ||||
|             IFNULL(expenses.currency_id, :company_currency) as currency_id | ||||
|             FROM expenses | ||||
|             WHERE expenses.is_deleted = 0 | ||||
|             AND expenses.company_id = :company_id | ||||
|             AND (expenses.date BETWEEN :start_date AND :end_date) | ||||
|             GROUP BY currency_id | ||||
|         "), ['company_currency' => $this->company->settings->currency_id, 'company_id' => $this->company->id, 'start_date' => $this->start_date, 'end_date' => $this->end_date]  );
 | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     private function setBillingReportType() | ||||
|     { | ||||
| 
 | ||||
|         if(array_key_exists('income_billed', $this->payload)) | ||||
|             $this->is_income_billed = boolval($this->payload['income_billed']); | ||||
| 
 | ||||
|         if(array_key_exists('expense_billed', $this->payload)) | ||||
|             $this->is_expense_billed = boolval($this->payload['expense_billed']); | ||||
| 
 | ||||
|         if(array_key_exists('include_tax', $this->payload)) | ||||
|             $this->is_tax_included = boolval($this->payload['is_tax_included']); | ||||
| 
 | ||||
|         return $this; | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     private function addDateRange($query) | ||||
|     { | ||||
| 
 | ||||
|         $date_range = $this->payload['date_range']; | ||||
|          | ||||
|         try{ | ||||
| 
 | ||||
|             $custom_start_date = Carbon::parse($this->payload['start_date']); | ||||
|             $custom_end_date = Carbon::parse($this->payload['end_date']);     | ||||
| 
 | ||||
|         } | ||||
|         catch(\Exception $e){ | ||||
| 
 | ||||
|             $custom_start_date = now()->startOfYear(); | ||||
|             $custom_end_date = now(); | ||||
| 
 | ||||
|         } | ||||
|          | ||||
|         switch ($date_range) { | ||||
| 
 | ||||
|             case 'all': | ||||
|                 $this->start_date = now()->subYears(50); | ||||
|                 $this->end_date = now(); | ||||
|                 // return $query;
 | ||||
|             case 'last7': | ||||
|                 $this->start_date = now()->subDays(7); | ||||
|                 $this->end_date = now(); | ||||
|                 // return $query->whereBetween($this->date_key, [now()->subDays(7), now()])->orderBy($this->date_key, 'ASC');
 | ||||
|             case 'last30': | ||||
|                 $this->start_date = now()->subDays(30); | ||||
|                 $this->end_date = now(); | ||||
|                 // return $query->whereBetween($this->date_key, [now()->subDays(30), now()])->orderBy($this->date_key, 'ASC');
 | ||||
|             case 'this_month': | ||||
|                 $this->start_date = now()->startOfMonth(); | ||||
|                 $this->end_date = now(); | ||||
|                 //return $query->whereBetween($this->date_key, [now()->startOfMonth(), now()])->orderBy($this->date_key, 'ASC');
 | ||||
|             case 'last_month': | ||||
|                 $this->start_date = now()->startOfMonth()->subMonth(); | ||||
|                 $this->end_date = now()->startOfMonth()->subMonth()->endOfMonth(); | ||||
|                 //return $query->whereBetween($this->date_key, [now()->startOfMonth()->subMonth(), now()->startOfMonth()->subMonth()->endOfMonth()])->orderBy($this->date_key, 'ASC');
 | ||||
|             case 'this_quarter': | ||||
|                 $this->start_date = (new \Carbon\Carbon('-3 months'))->firstOfQuarter(); | ||||
|                 $this->end_date = (new \Carbon\Carbon('-3 months'))->lastOfQuarter(); | ||||
|                 //return $query->whereBetween($this->date_key, [(new \Carbon\Carbon('-3 months'))->firstOfQuarter(), (new \Carbon\Carbon('-3 months'))->lastOfQuarter()])->orderBy($this->date_key, 'ASC');
 | ||||
|             case 'last_quarter': | ||||
|                 $this->start_date = (new \Carbon\Carbon('-6 months'))->firstOfQuarter(); | ||||
|                 $this->end_date = (new \Carbon\Carbon('-6 months'))->lastOfQuarter(); | ||||
|                 //return $query->whereBetween($this->date_key, [(new \Carbon\Carbon('-6 months'))->firstOfQuarter(), (new \Carbon\Carbon('-6 months'))->lastOfQuarter()])->orderBy($this->date_key, 'ASC');
 | ||||
|             case 'this_year': | ||||
|                 $this->start_date = now()->startOfYear(); | ||||
|                 $this->end_date = now(); | ||||
|                 //return $query->whereBetween($this->date_key, [now()->startOfYear(), now()])->orderBy($this->date_key, 'ASC');
 | ||||
|             case 'custom': | ||||
|                 $this->start_date = $custom_start_date; | ||||
|                 $this->end_date = $custom_end_date; | ||||
|                 //return $query->whereBetween($this->date_key, [$custom_start_date, $custom_end_date])->orderBy($this->date_key, 'ASC');
 | ||||
|             default: | ||||
|                 $this->start_date = now()->startOfYear(); | ||||
|                 $this->end_date = now(); | ||||
|                 // return $query->whereBetween($this->date_key, [now()->startOfYear(), now()])->orderBy($this->date_key, 'ASC');
 | ||||
| 
 | ||||
|         } | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -8,7 +8,7 @@ Route::get('client/login', 'Auth\ContactLoginController@showLoginForm')->name('c | ||||
| Route::post('client/login', 'Auth\ContactLoginController@login')->name('client.login.submit'); | ||||
| 
 | ||||
| Route::get('client/register/{company_key?}', 'Auth\ContactRegisterController@showRegisterForm')->name('client.register')->middleware(['domain_db', 'contact_account', 'contact_register','locale']); | ||||
| Route::post('client/register/{company_key?}', 'Auth\ContactRegisterController@register')->middleware(['domain_db', 'contact_account', 'contact_register', 'locale','throttle:10,1']); | ||||
| Route::post('client/register/{company_key?}', 'Auth\ContactRegisterController@register')->middleware(['domain_db', 'contact_account', 'contact_register', 'locale', 'throttle:10,1']); | ||||
| 
 | ||||
| Route::get('client/password/reset', 'Auth\ContactForgotPasswordController@showLinkRequestForm')->name('client.password.request')->middleware(['domain_db', 'contact_account','locale']); | ||||
| Route::post('client/password/email', 'Auth\ContactForgotPasswordController@sendResetLinkEmail')->name('client.password.email')->middleware('locale'); | ||||
| @ -62,7 +62,7 @@ Route::group(['middleware' => ['auth:contact', 'locale', 'domain_db','check_clie | ||||
|     Route::put('profile/{client_contact}/localization', 'ClientPortal\ProfileController@updateClientLocalization')->name('profile.edit_localization'); | ||||
| 
 | ||||
|     Route::get('payment_methods/{payment_method}/verification', 'ClientPortal\PaymentMethodController@verify')->name('payment_methods.verification'); | ||||
|     Route::post('payment_methods/{payment_method}/verification', 'ClientPortal\PaymentMethodController@processVerification'); | ||||
|     Route::post('payment_methods/{payment_method}/verification', 'ClientPortal\PaymentMethodController@processVerification')->middleware(['throttle:10,1']); | ||||
| 
 | ||||
|     Route::get('payment_methods/confirm', 'ClientPortal\PaymentMethodController@store')->name('payment_methods.confirm'); | ||||
| 
 | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user