mirror of
				https://github.com/invoiceninja/invoiceninja.git
				synced 2025-10-31 14:07:32 -04:00 
			
		
		
		
	Merge remote-tracking branch 'upstream/v5-develop' into browser-pay
This commit is contained in:
		
						commit
						e1590af4cf
					
				| @ -99,7 +99,7 @@ class CheckData extends Command | |||||||
|             config(['database.default' => $database]); |             config(['database.default' => $database]); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         $this->checkInvoiceBalances(); |         // $this->checkInvoiceBalances();
 | ||||||
|         $this->checkInvoicePayments(); |         $this->checkInvoicePayments(); | ||||||
|         $this->checkPaidToDates(); |         $this->checkPaidToDates(); | ||||||
|         // $this->checkPaidToCompanyDates();
 |         // $this->checkPaidToCompanyDates();
 | ||||||
| @ -406,6 +406,79 @@ class CheckData extends Command | |||||||
|     //     });
 |     //     });
 | ||||||
| 
 | 
 | ||||||
|     // }
 |     // }
 | ||||||
|  |     private function clientPaidToDateQuery() | ||||||
|  |     { | ||||||
|  |         $results = \DB::select( \DB::raw(" | ||||||
|  |          SELECT  | ||||||
|  |          clients.id as client_id,  | ||||||
|  |          clients.paid_to_date as client_paid_to_date, | ||||||
|  |          SUM(coalesce(payments.amount - payments.refunded,0)) as payments_applied | ||||||
|  |          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 | ||||||
|  |          GROUP BY clients.id | ||||||
|  |          HAVING payments_applied != client_paid_to_date | ||||||
|  |          ORDER BY clients.id; | ||||||
|  |         ") );
 | ||||||
|  |      | ||||||
|  |         return $results; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private function clientCreditPaymentables($client) | ||||||
|  |     { | ||||||
|  |         $results = \DB::select( \DB::raw(" | ||||||
|  |         SELECT  | ||||||
|  |         SUM(paymentables.amount - paymentables.refunded) as credit_payment | ||||||
|  |         FROM payments | ||||||
|  |         LEFT JOIN paymentables | ||||||
|  |         ON | ||||||
|  |         payments.id = paymentables.payment_id | ||||||
|  |         WHERE paymentable_type = 'App\\Models\\Credit' | ||||||
|  |         AND paymentables.deleted_at is NULL | ||||||
|  |         AND payments.client_id = 85; | ||||||
|  |         ") );
 | ||||||
|  |      | ||||||
|  |         return $results; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private function checkPaidToDatesNew() | ||||||
|  |     { | ||||||
|  |         $clients_to_check = $this->clientPaidToDateQuery(); | ||||||
|  | 
 | ||||||
|  |         $this->wrong_paid_to_dates = 0; | ||||||
|  |      | ||||||
|  |         foreach($clients_to_check as $_client) | ||||||
|  |         { | ||||||
|  |             $client = Client::find($_client['client_id']); | ||||||
|  | 
 | ||||||
|  |             $credits_used_for_payments = $this->clientCreditPaymentables($client); | ||||||
|  | 
 | ||||||
|  |             $total_paid_to_date = $_client['payments_applied'] + $credits_used_for_payments['credit_payment']; | ||||||
|  | 
 | ||||||
|  |             if(round($total_paid_to_date,2) != round($_client['client_paid_to_date'],2)){ | ||||||
|  | 
 | ||||||
|  |                 $this->wrong_paid_to_dates++; | ||||||
|  | 
 | ||||||
|  |                 $this->logMessage($client->present()->name.' id = # '.$client->id." - Paid to date does not match Client Paid To Date = {$client->paid_to_date} - Invoice Payments = {$total_paid_to_date}"); | ||||||
|  | 
 | ||||||
|  |                 $this->isValid = false; | ||||||
|  | 
 | ||||||
|  |                 if($this->option('paid_to_date')){ | ||||||
|  |                     $this->logMessage("# {$client->id} " . $client->present()->name.' - '.$client->number." Fixing {$client->paid_to_date} to {$total_paid_to_date}"); | ||||||
|  |                     $client->paid_to_date = $total_paid_to_date; | ||||||
|  |                     $client->save(); | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
|     private function checkPaidToDates() |     private function checkPaidToDates() | ||||||
|     { |     { | ||||||
| @ -496,8 +569,6 @@ class CheckData extends Command | |||||||
|                                     ->pluck('p') |                                     ->pluck('p') | ||||||
|                                     ->first(); |                                     ->first(); | ||||||
| 
 | 
 | ||||||
|                 // $total_paid = $total_amount - $total_refund;
 |  | ||||||
| 
 |  | ||||||
|                 $total_credit = $invoice->credits()->get()->sum('amount'); |                 $total_credit = $invoice->credits()->get()->sum('amount'); | ||||||
| 
 | 
 | ||||||
|                 $calculated_paid_amount = $invoice->amount - $invoice->balance - $total_credit; |                 $calculated_paid_amount = $invoice->amount - $invoice->balance - $total_credit; | ||||||
| @ -543,7 +614,32 @@ class CheckData extends Command | |||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| 
 |     private function clientBalanceQuery() | ||||||
|  |     { | ||||||
|  |         $results = \DB::select( \DB::raw(" | ||||||
|  |          SELECT  | ||||||
|  |          SUM(invoices.balance) as invoice_balance,  | ||||||
|  |          SUM(credits.balance) as credit_balance,  | ||||||
|  |          clients.id as client_id,  | ||||||
|  |          clients.balance as client_balance | ||||||
|  |          FROM invoices  | ||||||
|  |          INNER JOIN | ||||||
|  |          clients ON  | ||||||
|  |          clients.id=invoices.client_id  | ||||||
|  |          INNER JOIN | ||||||
|  |          credits ON | ||||||
|  |          credits.client_id = clients.id | ||||||
|  |          WHERE invoices.is_deleted = false  | ||||||
|  |          AND invoices.status_id > 1  | ||||||
|  |          AND credits.is_deleted = false | ||||||
|  |          AND credits.status_id > 1 | ||||||
|  |          GROUP BY clients.id | ||||||
|  |          HAVING invoice_balance != clients.balance | ||||||
|  |          ORDER BY clients.id; | ||||||
|  |         ") );
 | ||||||
|  |      | ||||||
|  |         return $results; | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @ -553,26 +649,62 @@ class CheckData extends Command | |||||||
|         $this->wrong_balances = 0; |         $this->wrong_balances = 0; | ||||||
|         $this->wrong_paid_to_dates = 0; |         $this->wrong_paid_to_dates = 0; | ||||||
| 
 | 
 | ||||||
|         foreach (Client::cursor()->where('is_deleted', 0)->where('clients.updated_at', '>', now()->subDays(2)) as $client) { |         $clients = $this->clientBalanceQuery(); | ||||||
|             //$invoice_balance = $client->invoices->where('is_deleted', false)->where('status_id', '>', 1)->sum('balance');
 |  | ||||||
|             $invoice_balance = Invoice::where('client_id', $client->id)->where('is_deleted', false)->where('status_id', '>', 1)->withTrashed()->sum('balance'); |  | ||||||
|             $credit_balance = Credit::where('client_id', $client->id)->where('is_deleted', false)->withTrashed()->sum('balance'); |  | ||||||
| 
 | 
 | ||||||
|             /*Legacy - V4 will add credits to the balance - we may need to reverse engineer this and remove the credits from the client balance otherwise we need this hack here and in the invoice balance check.*/ |         foreach($clients as $client) | ||||||
|             if($client->balance != $invoice_balance) |         { | ||||||
|                 $invoice_balance -= $credit_balance; |             $client = (array)$client; | ||||||
|  |              | ||||||
|  |             $invoice_balance = $client['invoice_balance'] - $client['credit_balance']; | ||||||
| 
 | 
 | ||||||
|             $ledger = CompanyLedger::where('client_id', $client->id)->orderBy('id', 'DESC')->first(); |             $ledger = CompanyLedger::where('client_id', $client['client_id'])->orderBy('id', 'DESC')->first(); | ||||||
| 
 | 
 | ||||||
|             if ($ledger && (string) $invoice_balance != (string) $client->balance) { |             if ($ledger && (string) $invoice_balance != (string) $client['client_balance']) { | ||||||
|                 $this->wrong_paid_to_dates++; |                 $this->wrong_paid_to_dates++; | ||||||
|                 $this->logMessage($client->present()->name.' - '.$client->id." - calculated client balances do not match Invoice Balances = {$invoice_balance} - Client Balance = ".rtrim($client->balance, '0'). " Ledger balance = {$ledger->balance}"); |  | ||||||
| 
 | 
 | ||||||
|                 $this->isValid = false; |                 $client_object = Client::find($client['client_id']); | ||||||
| 
 | 
 | ||||||
|  |                 $this->logMessage($client_object->present()->name.' - '.$client_object->id." - calculated client balances do not match Invoice Balances = {$invoice_balance} - Client Balance = ".rtrim($client['client_balance'], '0'). " Ledger balance = {$ledger->balance}"); | ||||||
|  |   | ||||||
|  |       | ||||||
|  |                 if($this->option('client_balance')){ | ||||||
|  |                      | ||||||
|  |                     $this->logMessage("# {$client_object->id} " . $client_object->present()->name.' - '.$client_object->number." Fixing {$client_object->balance} to {$invoice_balance}"); | ||||||
|  |                     $client->balance = $invoice_balance; | ||||||
|  |                     $client->save(); | ||||||
|  | 
 | ||||||
|  |                     $ledger->adjustment = $invoice_balance; | ||||||
|  |                     $ledger->balance = $invoice_balance; | ||||||
|  |                     $ledger->notes = 'Ledger Adjustment'; | ||||||
|  |                     $ledger->save(); | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |             $this->isValid = false; | ||||||
|  |              | ||||||
|             } |             } | ||||||
|  | 
 | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         // foreach (Client::cursor()->where('is_deleted', 0)->where('clients.updated_at', '>', now()->subDays(2)) as $client) {
 | ||||||
|  | 
 | ||||||
|  |         //     $invoice_balance = Invoice::where('client_id', $client->id)->where('is_deleted', false)->where('status_id', '>', 1)->withTrashed()->sum('balance');
 | ||||||
|  |         //     $credit_balance = Credit::where('client_id', $client->id)->where('is_deleted', false)->withTrashed()->sum('balance');
 | ||||||
|  | 
 | ||||||
|  |         //     if($client->balance != $invoice_balance)
 | ||||||
|  |         //         $invoice_balance -= $credit_balance;
 | ||||||
|  | 
 | ||||||
|  |         //     $ledger = CompanyLedger::where('client_id', $client->id)->orderBy('id', 'DESC')->first();
 | ||||||
|  | 
 | ||||||
|  |         //     if ($ledger && (string) $invoice_balance != (string) $client->balance) {
 | ||||||
|  |         //         $this->wrong_paid_to_dates++;
 | ||||||
|  |         //         $this->logMessage($client->present()->name.' - '.$client->id." - calculated client balances do not match Invoice Balances = {$invoice_balance} - Client Balance = ".rtrim($client->balance, '0'). " Ledger balance = {$ledger->balance}");
 | ||||||
|  | 
 | ||||||
|  |         //         $this->isValid = false;
 | ||||||
|  | 
 | ||||||
|  |         //     }
 | ||||||
|  |         // }
 | ||||||
|  | 
 | ||||||
|         $this->logMessage("{$this->wrong_paid_to_dates} clients with incorrect client balances"); |         $this->logMessage("{$this->wrong_paid_to_dates} clients with incorrect client balances"); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -213,12 +213,18 @@ abstract class QueryFilters | |||||||
|     public function with_trashed($value) |     public function with_trashed($value) | ||||||
|     { |     { | ||||||
| 
 | 
 | ||||||
|         if($value == 'true'){ |         if($value == 'false'){ | ||||||
| 
 | 
 | ||||||
|             $this->builder->withTrashed(); |             return $this->builder->where('is_deleted', 0); | ||||||
| 
 | 
 | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         // if($value == 'true'){
 | ||||||
|  | 
 | ||||||
|  |         //     $this->builder->withTrashed();
 | ||||||
|  | 
 | ||||||
|  |         // }
 | ||||||
|  | 
 | ||||||
|         return $this->builder; |         return $this->builder; | ||||||
| 
 | 
 | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -50,6 +50,9 @@ class TokenController extends BaseController | |||||||
|         parent::__construct(); |         parent::__construct(); | ||||||
| 
 | 
 | ||||||
|         $this->token_repo = $token_repo; |         $this->token_repo = $token_repo; | ||||||
|  | 
 | ||||||
|  |         $this->middleware('password_protected')->only(['store','update']); | ||||||
|  | 
 | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|  | |||||||
| @ -13,6 +13,7 @@ namespace App\Http\Requests\Client; | |||||||
| 
 | 
 | ||||||
| use App\DataMapper\ClientSettings; | use App\DataMapper\ClientSettings; | ||||||
| use App\Http\Requests\Request; | use App\Http\Requests\Request; | ||||||
|  | use App\Http\ValidationRules\Client\CountryCodeExistsRule; | ||||||
| use App\Http\ValidationRules\Ninja\CanStoreClientsRule; | use App\Http\ValidationRules\Ninja\CanStoreClientsRule; | ||||||
| use App\Http\ValidationRules\ValidClientGroupSettingsRule; | use App\Http\ValidationRules\ValidClientGroupSettingsRule; | ||||||
| use App\Models\Client; | use App\Models\Client; | ||||||
| @ -51,6 +52,14 @@ class StoreClientRequest extends Request | |||||||
|             $rules['number'] = Rule::unique('clients')->where('company_id', auth()->user()->company()->id); |             $rules['number'] = Rule::unique('clients')->where('company_id', auth()->user()->company()->id); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         if(isset($this->currency_code)){ | ||||||
|  |             $rules['currency_code'] = 'sometimes|exists:currencies,code'; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if(isset($this->country_code)){ | ||||||
|  |             $rules['country_code'] = new CountryCodeExistsRule(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         /* Ensure we have a client name, and that all emails are unique*/ |         /* Ensure we have a client name, and that all emails are unique*/ | ||||||
|         //$rules['name'] = 'required|min:1';
 |         //$rules['name'] = 'required|min:1';
 | ||||||
|         $rules['settings'] = new ValidClientGroupSettingsRule(); |         $rules['settings'] = new ValidClientGroupSettingsRule(); | ||||||
| @ -133,6 +142,7 @@ class StoreClientRequest extends Request | |||||||
|             // 'unique' => ctrans('validation.unique', ['attribute' => ['email','number']),
 |             // 'unique' => ctrans('validation.unique', ['attribute' => ['email','number']),
 | ||||||
|             //'required' => trans('validation.required', ['attribute' => 'email']),
 |             //'required' => trans('validation.required', ['attribute' => 'email']),
 | ||||||
|             'contacts.*.email.required' => ctrans('validation.email', ['attribute' => 'email']), |             'contacts.*.email.required' => ctrans('validation.email', ['attribute' => 'email']), | ||||||
|  |             'currency_code' => 'Currency code does not exist', | ||||||
|         ]; |         ]; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -158,6 +168,9 @@ class StoreClientRequest extends Request | |||||||
|             return $item->code == $code; |             return $item->code == $code; | ||||||
|         })->first(); |         })->first(); | ||||||
| 
 | 
 | ||||||
|         return (string) $currency->id; |         if($currency) | ||||||
|  |             return (string) $currency->id; | ||||||
|  | 
 | ||||||
|  |         return ""; | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										60
									
								
								app/Http/ValidationRules/Client/CountryCodeExistsRule.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								app/Http/ValidationRules/Client/CountryCodeExistsRule.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,60 @@ | |||||||
|  | <?php | ||||||
|  | /** | ||||||
|  |  * Credit Ninja (https://creditninja.com). | ||||||
|  |  * | ||||||
|  |  * @link https://github.com/creditninja/creditninja source repository | ||||||
|  |  * | ||||||
|  |  * @copyright Copyright (c) 2021. Credit Ninja LLC (https://creditninja.com) | ||||||
|  |  * | ||||||
|  |  * @license https://www.elastic.co/licensing/elastic-license | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | namespace App\Http\ValidationRules\Client; | ||||||
|  | 
 | ||||||
|  | use App\Models\Country; | ||||||
|  | use Illuminate\Contracts\Validation\Rule; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Class UniqueCreditNumberRule. | ||||||
|  |  */ | ||||||
|  | class CountryCodeExistsRule implements Rule | ||||||
|  | { | ||||||
|  | 
 | ||||||
|  |     public function __construct() | ||||||
|  |     { | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * @param string $attribute | ||||||
|  |      * @param mixed $value | ||||||
|  |      * @return bool | ||||||
|  |      */ | ||||||
|  |     public function passes($attribute, $value) | ||||||
|  |     { | ||||||
|  |         return $this->checkIfCodeExists($value); //if it exists, return false!
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * @return string | ||||||
|  |      */ | ||||||
|  |     public function message() | ||||||
|  |     { | ||||||
|  |         return 'Country code does not exist'; | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * @return bool | ||||||
|  |      */ | ||||||
|  |     private function checkIfCodeExists($value) : bool | ||||||
|  |     { | ||||||
|  |         $country = Country::where('iso_3166_2', $value) | ||||||
|  |                         ->orWhere('iso_3166_3', $value) | ||||||
|  |                         ->exists(); | ||||||
|  | 
 | ||||||
|  |         if ($country)  | ||||||
|  |             return true; | ||||||
|  |          | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -1032,10 +1032,12 @@ class CompanyImport implements ShouldQueue | |||||||
|             unset($user_array['hashed_id']); |             unset($user_array['hashed_id']); | ||||||
|             unset($user_array['id']); |             unset($user_array['id']); | ||||||
| 
 | 
 | ||||||
|             $new_user = User::firstOrNew( |             /*Make sure we are searching for archived users also and restore if we find them.*/ | ||||||
|  | 
 | ||||||
|  |             $new_user = User::withTrashed()->firstOrNew( | ||||||
|                 ['email' => $user->email], |                 ['email' => $user->email], | ||||||
|                 $user_array, |                 $user_array, | ||||||
|             ); |             )->restore(); | ||||||
| 
 | 
 | ||||||
|             $new_user->account_id = $this->account->id; |             $new_user->account_id = $this->account->id; | ||||||
|             $new_user->save(['timestamps' => false]); |             $new_user->save(['timestamps' => false]); | ||||||
| @ -1062,10 +1064,10 @@ class CompanyImport implements ShouldQueue | |||||||
|             unset($cu_array['company_id']); |             unset($cu_array['company_id']); | ||||||
|             unset($cu_array['user_id']); |             unset($cu_array['user_id']); | ||||||
| 
 | 
 | ||||||
|             $new_cu = CompanyUser::firstOrNew( |             $new_cu = CompanyUser::withTrashed()->firstOrNew( | ||||||
|                         ['user_id' => $user_id, 'company_id' => $this->company->id], |                         ['user_id' => $user_id, 'company_id' => $this->company->id], | ||||||
|                         $cu_array, |                         $cu_array, | ||||||
|                     ); |                     )->restore(); | ||||||
| 
 | 
 | ||||||
|             $new_cu->account_id = $this->account->id; |             $new_cu->account_id = $this->account->id; | ||||||
|             $new_cu->save(['timestamps' => false]); |             $new_cu->save(['timestamps' => false]); | ||||||
|  | |||||||
| @ -212,6 +212,8 @@ class NinjaMailerJob implements ShouldQueue | |||||||
| 
 | 
 | ||||||
|             $google->getClient()->setAccessToken(json_encode($user->oauth_user_token)); |             $google->getClient()->setAccessToken(json_encode($user->oauth_user_token)); | ||||||
| 
 | 
 | ||||||
|  |             //need to slow down gmail requests otherwise we hit 429's
 | ||||||
|  |             sleep(1); | ||||||
|         } |         } | ||||||
|         catch(\Exception $e) { |         catch(\Exception $e) { | ||||||
|             $this->logMailError('Gmail Token Invalid', $this->company->clients()->first()); |             $this->logMailError('Gmail Token Invalid', $this->company->clients()->first()); | ||||||
| @ -225,9 +227,6 @@ class NinjaMailerJob implements ShouldQueue | |||||||
|          *  just for this request. |          *  just for this request. | ||||||
|         */ |         */ | ||||||
| 
 | 
 | ||||||
|         // config(['mail.driver' => 'gmail']);
 |  | ||||||
|         // (new MailServiceProvider(app()))->register();
 |  | ||||||
| 
 |  | ||||||
|         $token = $user->oauth_user_token->access_token; |         $token = $user->oauth_user_token->access_token; | ||||||
| 
 | 
 | ||||||
|         $this->nmo |         $this->nmo | ||||||
|  | |||||||
| @ -530,7 +530,7 @@ class Client extends BaseModel implements HasLocalePreference | |||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if ($this->country->iso_3166_3 == 'GBR' && in_array(GatewayType::DIRECT_DEBIT, array_column($pms, 'gateway_type_id'))) { |         if ($this->country && $this->country->iso_3166_3 == 'GBR' && in_array(GatewayType::DIRECT_DEBIT, array_column($pms, 'gateway_type_id'))) { | ||||||
|             foreach ($pms as $pm) { |             foreach ($pms as $pm) { | ||||||
|                 if ($pm['gateway_type_id'] == GatewayType::DIRECT_DEBIT) { |                 if ($pm['gateway_type_id'] == GatewayType::DIRECT_DEBIT) { | ||||||
|                     $cg = CompanyGateway::find($pm['company_gateway_id']); |                     $cg = CompanyGateway::find($pm['company_gateway_id']); | ||||||
|  | |||||||
| @ -30,7 +30,7 @@ class PaymentObserver | |||||||
|                             ->exists(); |                             ->exists(); | ||||||
| 
 | 
 | ||||||
|         if ($subscriptions) { |         if ($subscriptions) { | ||||||
|             WebhookHandler::dispatch(Webhook::EVENT_CREATE_PAYMENT, $payment, $payment->company, 'invoices'); |             WebhookHandler::dispatch(Webhook::EVENT_CREATE_PAYMENT, $payment, $payment->company, 'invoices,client'); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -57,7 +57,7 @@ class PaymentObserver | |||||||
|                         ->exists(); |                         ->exists(); | ||||||
| 
 | 
 | ||||||
|         if ($subscriptions) { |         if ($subscriptions) { | ||||||
|             WebhookHandler::dispatch(Webhook::EVENT_DELETE_PAYMENT, $payment, $payment->company, 'invoices'); |             WebhookHandler::dispatch(Webhook::EVENT_DELETE_PAYMENT, $payment, $payment->company, 'invoices,client'); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										2
									
								
								public/css/app.css
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								public/css/app.css
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										2
									
								
								public/css/card-js.min.css
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								public/css/card-js.min.css
									
									
									
									
										vendored
									
									
								
							| @ -1 +1 @@ | |||||||
| .card-js input.card-number{padding-right:48px}.card-js .card-number-wrapper .card-type-icon{height:23px;width:32px;position:absolute;display:block;right:8px;top:7px;background:url(https://cardjs.co.uk/img/cards.png) 0 23px no-repeat;pointer-events:none;opacity:0;-webkit-transition:opacity .15s linear;-moz-transition:opacity .15s linear;-ms-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.card-js .card-number-wrapper .show{opacity:1}.card-js .card-number-wrapper .card-type-icon.visa{background-position:0 0}.card-js .card-number-wrapper .card-type-icon.master-card{background-position:-32px 0}.card-js .card-number-wrapper .card-type-icon.american-express{background-position:-64px 0}.card-js .card-number-wrapper .card-type-icon.discover{background-position:-96px 0}.card-js .card-number-wrapper .card-type-icon.diners{background-position:-128px 0}.card-js .card-number-wrapper .card-type-icon.jcb{background-position:-160px 0}.card-js .cvc-container{width:50%;float:right}.card-js .cvc-wrapper{box-sizing:border-box;margin-left:5px}.card-js .cvc-wrapper .cvc{display:block;width:100%}.card-js .expiry-container{width:50%;float:left}.card-js .expiry-wrapper{box-sizing:border-box;margin-right:5px}.card-js .expiry-wrapper .expiry{display:block;width:100%}.card-js .expiry-wrapper .expiry-month{border-top-right-radius:0;border-bottom-right-radius:0;padding-left:30px}.card-js .expiry-wrapper .expiry-year{border-top-left-radius:0;border-bottom-left-radius:0;border-left:0}.card-js .expiry-wrapper .expiry-month,.card-js .expiry-wrapper .expiry-year{display:inline-block}.card-js .expiry-wrapper .expiry{padding-left:38px}.card-js .icon{position:absolute;display:block;width:24px;height:17px;left:8px;top:10px;pointer-events:none}.card-js .icon.right{right:8px;left:auto}.card-js .icon.popup{cursor:pointer;pointer-events:auto}.card-js .icon .svg{fill:#888}.card-js .icon.popup .svg{fill:#aaa!important}.card-js .card-number-wrapper,.card-js .name-wrapper{margin-bottom:15px;width:100%}.card-js .card-number-wrapper,.card-js .cvc-wrapper,.card-js .expiry-wrapper,.card-js .name-wrapper{-webkit-box-shadow:0 1px 0 rgba(255,255,255,.7),inset 0 1px 0 rgba(255,255,255,.7);-moz-box-shadow:0 1px 0 rgba(255,255,255,.7),inset 0 1px 0 rgba(255,255,255,.7);-ms-box-shadow:0 1px 0 rgba(255,255,255,.7),inset 0 1px 0 rgba(255,255,255,.7);-o-box-shadow:0 1px 0 rgba(255,255,255,.7),inset 0 1px 0 rgba(255,255,255,.7);box-shadow:0 1px 0 rgba(255,255,255,.7),inset 0 1px 0 rgba(255,255,255,.7);position:relative}.card-js .card-number-wrapper,.card-js .cvc-container,.card-js .expiry-container,.card-js .name-wrapper{display:inline-block}.card-js::after{content:' ';display:table;clear:both}.card-js input,.card-js select{color:#676767;font-size:15px;font-weight:300;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;height:36px;border:1px solid #d9d9d9;border-radius:4px;box-shadow:none;background-color:#fdfdfd;box-sizing:border-box;padding:0;-webkit-transition:border-color .15s linear,box-shadow .15s linear;-moz-transition:border-color .15s linear,box-shadow .15s linear;-ms-transition:border-color .15s linear,box-shadow .15s linear;-o-transition:border-color .15s linear,box-shadow .15s linear;transition:border-color .15s linear,box-shadow .15s linear}.card-js select{-moz-appearance:none;text-indent:.01px;text-overflow:''}.card-js input[disabled],.card-js select[disabled]{background-color:#eee;color:#555}.card-js select option[hidden]{color:#aba9a9}.card-js input:focus,.card-js select:focus{background-color:#fff;outline:0;border-color:#66afe9;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)}.card-js input[readonly=readonly]:not([disabled]),.card-js input[readonly]:not([disabled]){background-color:#fff;cursor:pointer}.card-js .has-error input,.card-js .has-error input:focus{border-color:#f64b2f;box-shadow:none}.card-js input.card-number,.card-js input.cvc,.card-js input.name{padding-left:38px;width:100%}.card-js.stripe .icon .svg{fill:#559A28} | .card-js input.card-number{padding-right:48px}.card-js .card-number-wrapper .card-type-icon{height:23px;width:32px;position:absolute;display:block;right:8px;top:7px;background:url(https://cardjs.co.uk/img/cards.png) 0 23px no-repeat;pointer-events:none;opacity:0;-webkit-transition:opacity .15s linear;-moz-transition:opacity .15s linear;-ms-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.card-js .card-number-wrapper .show{opacity:1}.card-js .card-number-wrapper .card-type-icon.visa{background-position:0 0}.card-js .card-number-wrapper .card-type-icon.master-card{background-position:-32px 0}.card-js .card-number-wrapper .card-type-icon.american-express{background-position:-64px 0}.card-js .card-number-wrapper .card-type-icon.discover{background-position:-96px 0}.card-js .card-number-wrapper .card-type-icon.diners{background-position:-128px 0}.card-js .card-number-wrapper .card-type-icon.jcb{background-position:-160px 0}.card-js .cvc-container{width:50%;float:right}.card-js .cvc-wrapper{box-sizing:border-box;margin-left:5px}.card-js .cvc-wrapper .cvc{display:block;width:100%}.card-js .expiry-container{width:50%;float:left}.card-js .expiry-wrapper{box-sizing:border-box;margin-right:5px}.card-js .expiry-wrapper .expiry{display:block;width:100%}.card-js .expiry-wrapper .expiry-month{border-top-right-radius:0;border-bottom-right-radius:0;padding-left:30px}.card-js .expiry-wrapper .expiry-year{border-top-left-radius:0;border-bottom-left-radius:0;border-left:0}.card-js .expiry-wrapper .expiry-month,.card-js .expiry-wrapper .expiry-year{display:inline-block}.card-js .expiry-wrapper .expiry{padding-left:38px}.card-js .icon{position:absolute;display:block;width:24px;height:17px;left:8px;top:10px;pointer-events:none}.card-js .icon.right{right:8px;left:auto}.card-js .icon.popup{cursor:pointer;pointer-events:auto}.card-js .icon .svg{fill:#888}.card-js .icon.popup .svg{fill:#aaa!important}.card-js .card-number-wrapper,.card-js .name-wrapper{margin-bottom:15px;width:100%}.card-js .card-number-wrapper,.card-js .cvc-wrapper,.card-js .expiry-wrapper,.card-js .name-wrapper{-webkit-box-shadow:0 1px 0 rgba(255,255,255,.7),inset 0 1px 0 rgba(255,255,255,.7);-moz-box-shadow:0 1px 0 rgba(255,255,255,.7),inset 0 1px 0 rgba(255,255,255,.7);-ms-box-shadow:0 1px 0 rgba(255,255,255,.7),inset 0 1px 0 rgba(255,255,255,.7);-o-box-shadow:0 1px 0 rgba(255,255,255,.7),inset 0 1px 0 rgba(255,255,255,.7);box-shadow:0 1px 0 rgba(255,255,255,.7),inset 0 1px 0 rgba(255,255,255,.7);position:relative}.card-js .card-number-wrapper,.card-js .cvc-container,.card-js .expiry-container,.card-js .name-wrapper{display:inline-block}.card-js::after{content:' ';display:table;clear:both}.card-js input,.card-js select{color:#676767;font-size:15px;font-weight:300;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;height:36px;border:1px solid #d9d9d9;border-radius:4px;box-shadow:none;background-color:#FDFDFD;box-sizing:border-box;padding:0;-webkit-transition:border-color .15s linear,box-shadow .15s linear;-moz-transition:border-color .15s linear,box-shadow .15s linear;-ms-transition:border-color .15s linear,box-shadow .15s linear;-o-transition:border-color .15s linear,box-shadow .15s linear;transition:border-color .15s linear,box-shadow .15s linear}.card-js select{-moz-appearance:none;text-indent:.01px;text-overflow:''}.card-js input[disabled],.card-js select[disabled]{background-color:#eee;color:#555}.card-js select option[hidden]{color:#ABA9A9}.card-js input:focus,.card-js select:focus{background-color:#fff;outline:0;border-color:#66afe9;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)}.card-js input[readonly=readonly]:not([disabled]),.card-js input[readonly]:not([disabled]){background-color:#fff;cursor:pointer}.card-js .has-error input,.card-js .has-error input:focus{border-color:#F64B2F;box-shadow:none}.card-js input.card-number,.card-js input.cvc,.card-js input.name{padding-left:38px;width:100%}.card-js.stripe .icon .svg{fill:#559A28} | ||||||
							
								
								
									
										6
									
								
								public/flutter_service_worker.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								public/flutter_service_worker.js
									
									
									
									
										vendored
									
									
								
							| @ -3,7 +3,7 @@ const MANIFEST = 'flutter-app-manifest'; | |||||||
| const TEMP = 'flutter-temp-cache'; | const TEMP = 'flutter-temp-cache'; | ||||||
| const CACHE_NAME = 'flutter-app-cache'; | const CACHE_NAME = 'flutter-app-cache'; | ||||||
| const RESOURCES = { | const RESOURCES = { | ||||||
|   "version.json": "27abc97e9c76cf112b697fa080c304b5", |   "version.json": "3930722e1581f582eefe59c75ab50c99", | ||||||
| "favicon.ico": "51636d3a390451561744c42188ccd628", | "favicon.ico": "51636d3a390451561744c42188ccd628", | ||||||
| "favicon.png": "dca91c54388f52eded692718d5a98b8b", | "favicon.png": "dca91c54388f52eded692718d5a98b8b", | ||||||
| "assets/fonts/MaterialIcons-Regular.otf": "4e6447691c9509f7acdbf8a931a85ca1", | "assets/fonts/MaterialIcons-Regular.otf": "4e6447691c9509f7acdbf8a931a85ca1", | ||||||
| @ -32,9 +32,9 @@ const RESOURCES = { | |||||||
| "assets/packages/material_design_icons_flutter/lib/fonts/materialdesignicons-webfont.ttf": "174c02fc4609e8fc4389f5d21f16a296", | "assets/packages/material_design_icons_flutter/lib/fonts/materialdesignicons-webfont.ttf": "174c02fc4609e8fc4389f5d21f16a296", | ||||||
| "icons/Icon-192.png": "bb1cf5f6982006952211c7c8404ffbed", | "icons/Icon-192.png": "bb1cf5f6982006952211c7c8404ffbed", | ||||||
| "icons/Icon-512.png": "0f9aff01367f0a0c69773d25ca16ef35", | "icons/Icon-512.png": "0f9aff01367f0a0c69773d25ca16ef35", | ||||||
| "main.dart.js": "9331fefe016a4110385aae52ece5bb7c", | "main.dart.js": "c2610220ba3a55e358c3c653d6242d0f", | ||||||
| "manifest.json": "ef43d90e57aa7682d7e2cfba2f484a40", | "manifest.json": "ef43d90e57aa7682d7e2cfba2f484a40", | ||||||
| "/": "3a8fb0769c1c1df7d1a95d627a2e2857" | "/": "b8da32f39d55fc73f8a721a7e141bbc1" | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| // The application shell files that are downloaded before a service worker can
 | // The application shell files that are downloaded before a service worker can
 | ||||||
|  | |||||||
							
								
								
									
										2
									
								
								public/js/clients/shared/pdf.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								public/js/clients/shared/pdf.js
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										36361
									
								
								public/main.dart.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										36361
									
								
								public/main.dart.js
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										36217
									
								
								public/main.foss.dart.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										36217
									
								
								public/main.foss.dart.js
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										34465
									
								
								public/main.html.dart.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										34465
									
								
								public/main.html.dart.js
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										41631
									
								
								public/main.next.dart.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										41631
									
								
								public/main.next.dart.js
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										213
									
								
								public/main.profile.dart.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										213
									
								
								public/main.profile.dart.js
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| @ -13,7 +13,7 @@ | |||||||
|     "/js/clients/payments/stripe-credit-card.js": "/js/clients/payments/stripe-credit-card.js?id=ea4250be693260798735", |     "/js/clients/payments/stripe-credit-card.js": "/js/clients/payments/stripe-credit-card.js?id=ea4250be693260798735", | ||||||
|     "/js/setup/setup.js": "/js/setup/setup.js?id=6b870beeb350d83668c5", |     "/js/setup/setup.js": "/js/setup/setup.js?id=6b870beeb350d83668c5", | ||||||
|     "/js/clients/payments/card-js.min.js": "/js/clients/payments/card-js.min.js?id=8ce33c3deae058ad314f", |     "/js/clients/payments/card-js.min.js": "/js/clients/payments/card-js.min.js?id=8ce33c3deae058ad314f", | ||||||
|     "/js/clients/shared/pdf.js": "/js/clients/shared/pdf.js?id=8dc6dd2bab2a56bc86bb", |     "/js/clients/shared/pdf.js": "/js/clients/shared/pdf.js?id=73a0d914ad3577f257f4", | ||||||
|     "/js/clients/shared/multiple-downloads.js": "/js/clients/shared/multiple-downloads.js?id=c2caa29f753ad1f3a12c", |     "/js/clients/shared/multiple-downloads.js": "/js/clients/shared/multiple-downloads.js?id=c2caa29f753ad1f3a12c", | ||||||
|     "/js/clients/linkify-urls.js": "/js/clients/linkify-urls.js?id=448d055fa1e8357130e6", |     "/js/clients/linkify-urls.js": "/js/clients/linkify-urls.js?id=448d055fa1e8357130e6", | ||||||
|     "/js/clients/payments/braintree-credit-card.js": "/js/clients/payments/braintree-credit-card.js?id=a334dd9257dd510a1feb", |     "/js/clients/payments/braintree-credit-card.js": "/js/clients/payments/braintree-credit-card.js?id=a334dd9257dd510a1feb", | ||||||
| @ -36,7 +36,6 @@ | |||||||
|     "/js/clients/payments/stripe-eps.js": "/js/clients/payments/stripe-eps.js?id=1ed972f879869de66c8a", |     "/js/clients/payments/stripe-eps.js": "/js/clients/payments/stripe-eps.js?id=1ed972f879869de66c8a", | ||||||
|     "/js/clients/payments/stripe-ideal.js": "/js/clients/payments/stripe-ideal.js?id=73ce56676f9252b0cecf", |     "/js/clients/payments/stripe-ideal.js": "/js/clients/payments/stripe-ideal.js?id=73ce56676f9252b0cecf", | ||||||
|     "/js/clients/payments/stripe-przelewy24.js": "/js/clients/payments/stripe-przelewy24.js?id=f3a14f78bec8209c30ba", |     "/js/clients/payments/stripe-przelewy24.js": "/js/clients/payments/stripe-przelewy24.js?id=f3a14f78bec8209c30ba", | ||||||
|     "/js/clients/payments/stripe-browserpay.js": "/js/clients/payments/stripe-browserpay.js?id=71e49866d66a6d85b88a", |     "/css/app.css": "/css/app.css?id=6d7f6103a3a7738d363b", | ||||||
|     "/css/app.css": "/css/app.css?id=317a907834a0db9f8d41", |  | ||||||
|     "/css/card-js.min.css": "/css/card-js.min.css?id=62afeb675235451543ad" |     "/css/card-js.min.css": "/css/card-js.min.css?id=62afeb675235451543ad" | ||||||
| } | } | ||||||
|  | |||||||
| @ -1 +1 @@ | |||||||
| {"app_name":"invoiceninja_flutter","version":"5.0.62","build_number":"62"} | {"app_name":"invoiceninja_flutter","version":"5.0.63","build_number":"63"} | ||||||
							
								
								
									
										4
									
								
								resources/js/clients/shared/pdf.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								resources/js/clients/shared/pdf.js
									
									
									
									
										vendored
									
									
								
							| @ -15,11 +15,11 @@ class PDF { | |||||||
|         this.context = canvas.getContext('2d'); |         this.context = canvas.getContext('2d'); | ||||||
|         this.currentPage = 1; |         this.currentPage = 1; | ||||||
|         this.maxPages = 1; |         this.maxPages = 1; | ||||||
|         this.currentScale = 0.75; |         this.currentScale = 1.25; | ||||||
|         this.currentScaleText = document.getElementById('zoom-level'); |         this.currentScaleText = document.getElementById('zoom-level'); | ||||||
| 
 | 
 | ||||||
|         if (matchMedia('only screen and (max-width: 480px)').matches) { |         if (matchMedia('only screen and (max-width: 480px)').matches) { | ||||||
|             this.currentScale = 0.5; |             this.currentScale = 1.25; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         this.currentScaleText.textContent = this.currentScale * 100 + '%'; |         this.currentScaleText.textContent = this.currentScale * 100 + '%'; | ||||||
|  | |||||||
| @ -82,8 +82,8 @@ | |||||||
| </div> | </div> | ||||||
| 
 | 
 | ||||||
| @if($mobile) | @if($mobile) | ||||||
|     <div class="flex justify-center"> |     <div class="w-full h-full overflow-auto mt-4"> | ||||||
|         <canvas id="pdf-placeholder" class="shadow rounded-lg bg-white mt-4 p-4"></canvas> |         <canvas id="pdf-placeholder" class="shadow rounded-lg bg-white"></canvas> | ||||||
|     </div> |     </div> | ||||||
| @else | @else | ||||||
|     <iframe id="pdf-iframe" src="{{ $url ?? $entity->pdf_file_path(null, 'url', true) }}" class="h-screen w-full border-0 mt-4"></iframe> |     <iframe id="pdf-iframe" src="{{ $url ?? $entity->pdf_file_path(null, 'url', true) }}" class="h-screen w-full border-0 mt-4"></iframe> | ||||||
|  | |||||||
| @ -10,10 +10,12 @@ | |||||||
|  */ |  */ | ||||||
| namespace Tests\Feature; | namespace Tests\Feature; | ||||||
| 
 | 
 | ||||||
|  | use App\Models\Country; | ||||||
| use App\Utils\Traits\MakesHash; | use App\Utils\Traits\MakesHash; | ||||||
| use Illuminate\Database\Eloquent\Model; | use Illuminate\Database\Eloquent\Model; | ||||||
| use Illuminate\Foundation\Testing\DatabaseTransactions; | use Illuminate\Foundation\Testing\DatabaseTransactions; | ||||||
| use Illuminate\Support\Facades\Session; | use Illuminate\Support\Facades\Session; | ||||||
|  | use Illuminate\Validation\ValidationException; | ||||||
| use Tests\MockAccountData; | use Tests\MockAccountData; | ||||||
| use Tests\TestCase; | use Tests\TestCase; | ||||||
| 
 | 
 | ||||||
| @ -40,6 +42,77 @@ class ClientApiTest extends TestCase | |||||||
|         Model::reguard(); |         Model::reguard(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     public function testClientCountryCodeValidationTrue() | ||||||
|  |     { | ||||||
|  | 
 | ||||||
|  |         $data = [ | ||||||
|  |             'name' => $this->faker->firstName, | ||||||
|  |             'id_number' => 'Coolio', | ||||||
|  |             'country_code' => 'AM' | ||||||
|  |         ]; | ||||||
|  | 
 | ||||||
|  |         $response = false; | ||||||
|  | 
 | ||||||
|  |         try{ | ||||||
|  |         $response = $this->withHeaders([ | ||||||
|  |             'X-API-SECRET' => config('ninja.api_secret'), | ||||||
|  |             'X-API-TOKEN' => $this->token, | ||||||
|  |         ])->post('/api/v1/clients/', $data); | ||||||
|  |         } catch (ValidationException $e) { | ||||||
|  |             $message = json_decode($e->validator->getMessageBag(), 1); | ||||||
|  |             nlog($message); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         $response->assertStatus(200); | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     public function testClientCountryCodeValidationTrueIso3() | ||||||
|  |     { | ||||||
|  | 
 | ||||||
|  |         $data = [ | ||||||
|  |             'name' => $this->faker->firstName, | ||||||
|  |             'id_number' => 'Coolio', | ||||||
|  |             'country_code' => 'ARM' | ||||||
|  |         ]; | ||||||
|  | 
 | ||||||
|  |         $response = false; | ||||||
|  | 
 | ||||||
|  |         try{ | ||||||
|  |         $response = $this->withHeaders([ | ||||||
|  |             'X-API-SECRET' => config('ninja.api_secret'), | ||||||
|  |             'X-API-TOKEN' => $this->token, | ||||||
|  |         ])->post('/api/v1/clients/', $data); | ||||||
|  |         } catch (ValidationException $e) { | ||||||
|  |             $message = json_decode($e->validator->getMessageBag(), 1); | ||||||
|  |             nlog($message); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         $response->assertStatus(200); | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     public function testClientCountryCodeValidationFalse() | ||||||
|  |     { | ||||||
|  | 
 | ||||||
|  |         $data = [ | ||||||
|  |             'name' => $this->faker->firstName, | ||||||
|  |             'id_number' => 'Coolio', | ||||||
|  |             'country_code' => 'AdfdfdfM' | ||||||
|  |         ]; | ||||||
|  | 
 | ||||||
|  |         $response = $this->withHeaders([ | ||||||
|  |             'X-API-SECRET' => config('ninja.api_secret'), | ||||||
|  |             'X-API-TOKEN' => $this->token, | ||||||
|  |         ])->post('/api/v1/clients/', $data); | ||||||
|  | 
 | ||||||
|  |         $response->assertStatus(302); | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     public function testClientPost() |     public function testClientPost() | ||||||
|     { |     { | ||||||
|         $data = [ |         $data = [ | ||||||
| @ -174,4 +247,45 @@ class ClientApiTest extends TestCase | |||||||
| 
 | 
 | ||||||
|         $this->assertTrue($arr['data'][0]['is_deleted']); |         $this->assertTrue($arr['data'][0]['is_deleted']); | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     public function testClientCurrencyCodeValidationTrue() | ||||||
|  |     { | ||||||
|  | 
 | ||||||
|  |         $data = [ | ||||||
|  |             'name' => $this->faker->firstName, | ||||||
|  |             'id_number' => 'Coolio', | ||||||
|  |             'currency_code' => 'USD' | ||||||
|  |         ]; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         $response = $this->withHeaders([ | ||||||
|  |             'X-API-SECRET' => config('ninja.api_secret'), | ||||||
|  |             'X-API-TOKEN' => $this->token, | ||||||
|  |         ])->post('/api/v1/clients/', $data); | ||||||
|  | 
 | ||||||
|  |         $response->assertStatus(200); | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public function testClientCurrencyCodeValidationFalse() | ||||||
|  |     { | ||||||
|  | 
 | ||||||
|  |         $data = [ | ||||||
|  |             'name' => $this->faker->firstName, | ||||||
|  |             'id_number' => 'Coolio', | ||||||
|  |             'currency_code' => 'R' | ||||||
|  |         ]; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         $response = $this->withHeaders([ | ||||||
|  |             'X-API-SECRET' => config('ninja.api_secret'), | ||||||
|  |             'X-API-TOKEN' => $this->token, | ||||||
|  |         ])->post('/api/v1/clients/', $data); | ||||||
|  | 
 | ||||||
|  |         $response->assertStatus(302); | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| } | } | ||||||
|  | |||||||
| @ -27,25 +27,25 @@ class RefundUnitTest extends TestCase | |||||||
|         parent::setUp(); |         parent::setUp(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public function testProRataRefundMonthly() |     // public function testProRataRefundMonthly()
 | ||||||
|     { |     // {
 | ||||||
|         $pro_rata = new ProRata(); |     //     $pro_rata = new ProRata();
 | ||||||
|         $refund = $pro_rata->refund(10, Carbon::parse('2021-01-01'), Carbon::parse('2021-01-31'), RecurringInvoice::FREQUENCY_MONTHLY); |     //     $refund = $pro_rata->refund(10, Carbon::parse('2021-01-01'), Carbon::parse('2021-01-31'), RecurringInvoice::FREQUENCY_MONTHLY);
 | ||||||
| 
 | 
 | ||||||
|         $this->assertEquals(9.68, $refund); |     //     $this->assertEquals(9.68, $refund);
 | ||||||
| 
 | 
 | ||||||
|         $this->assertEquals(30, Carbon::parse('2021-01-01')->diffInDays(Carbon::parse('2021-01-31'))); |     //     $this->assertEquals(30, Carbon::parse('2021-01-01')->diffInDays(Carbon::parse('2021-01-31')));
 | ||||||
| 
 | 
 | ||||||
|     } |     // }
 | ||||||
| 
 | 
 | ||||||
|     public function testProRataRefundYearly() |     // public function testProRataRefundYearly()
 | ||||||
|     { |     // {
 | ||||||
|         $pro_rata = new ProRata(); |     //     $pro_rata = new ProRata();
 | ||||||
| 
 | 
 | ||||||
|         $refund = $pro_rata->refund(10, Carbon::parse('2021-01-01'), Carbon::parse('2021-01-31'), RecurringInvoice::FREQUENCY_ANNUALLY); |     //     $refund = $pro_rata->refund(10, Carbon::parse('2021-01-01'), Carbon::parse('2021-01-31'), RecurringInvoice::FREQUENCY_ANNUALLY);
 | ||||||
| 
 | 
 | ||||||
|         $this->assertEquals(0.82, $refund); |     //     $this->assertEquals(0.82, $refund);
 | ||||||
|     } |     // }
 | ||||||
| 
 | 
 | ||||||
|     public function testDiffInDays() |     public function testDiffInDays() | ||||||
|     { |     { | ||||||
|  | |||||||
| @ -94,13 +94,13 @@ class SubscriptionsCalcTest extends TestCase | |||||||
| 
 | 
 | ||||||
|         $refund = $pro_rata->refund($invoice->amount, Carbon::parse('2021-01-01'), Carbon::parse('2021-01-06'), $subscription->frequency_id); |         $refund = $pro_rata->refund($invoice->amount, Carbon::parse('2021-01-01'), Carbon::parse('2021-01-06'), $subscription->frequency_id); | ||||||
| 
 | 
 | ||||||
|         $this->assertEquals(1.61, $refund); |         $this->assertEquals(1.67, $refund); | ||||||
| 
 | 
 | ||||||
|         $pro_rata = new ProRata; |         $pro_rata = new ProRata; | ||||||
| 
 | 
 | ||||||
|         $upgrade = $pro_rata->charge($target->price, Carbon::parse('2021-01-01'), Carbon::parse('2021-01-06'), $subscription->frequency_id); |         $upgrade = $pro_rata->charge($target->price, Carbon::parse('2021-01-01'), Carbon::parse('2021-01-06'), $subscription->frequency_id); | ||||||
| 
 | 
 | ||||||
|         $this->assertEquals(3.23, $upgrade); |         $this->assertEquals(3.33, $upgrade); | ||||||
| 
 | 
 | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user