mirror of
				https://github.com/invoiceninja/invoiceninja.git
				synced 2025-10-31 12:17:32 -04:00 
			
		
		
		
	Merge branch 'v5-develop' into yodlee
This commit is contained in:
		
						commit
						ce47e30aa2
					
				| @ -1 +1 @@ | ||||
| 5.5.27 | ||||
| 5.5.32 | ||||
| @ -712,14 +712,23 @@ class CheckData extends Command | ||||
|                                             ->pluck('p') | ||||
|                                             ->first(); | ||||
| 
 | ||||
|                     $over_payment = $over_payment*-1; | ||||
| 
 | ||||
|                     if(floatval($over_payment) == floatval($client->balance)){ | ||||
| 
 | ||||
|                     } | ||||
|                     else { | ||||
| 
 | ||||
|                         $this->logMessage("# {$client->id} # {$client->name} {$client->balance} is invalid should be {$over_payment}"); | ||||
| 
 | ||||
|                     } | ||||
| 
 | ||||
|                     $this->logMessage("# {$client->id} # {$client->name} {$client->balance} is invalid should be {$over_payment}"); | ||||
| 
 | ||||
|                     if($this->option('client_balance') && (floatval($over_payment) != floatval($client->balance) )){ | ||||
|                          | ||||
|                         $this->logMessage("# {$client->id} " . $client->present()->name().' - '.$client->number." Fixing {$client->balance} to 0"); | ||||
| 
 | ||||
|                         $client->balance = $over_payment * -1; | ||||
|                         $client->balance = $over_payment; | ||||
|                         $client->save(); | ||||
| 
 | ||||
|                     } | ||||
|  | ||||
| @ -437,7 +437,7 @@ class CreateTestData extends Command | ||||
|             'company_id' => $client->company->id, | ||||
|         ]); | ||||
| 
 | ||||
|         Document::factory()->count(50)->create([ | ||||
|         Document::factory()->count(5)->create([ | ||||
|             'user_id' => $client->user->id, | ||||
|             'company_id' => $client->company_id, | ||||
|             'documentable_type' => Vendor::class, | ||||
|  | ||||
| @ -25,9 +25,9 @@ class RecurringInvoiceToInvoiceFactory | ||||
|         $invoice->discount = $recurring_invoice->discount; | ||||
|         $invoice->is_amount_discount = $recurring_invoice->is_amount_discount; | ||||
|         $invoice->po_number = $recurring_invoice->po_number; | ||||
|         $invoice->footer = self::tranformObject($recurring_invoice->footer, $client); | ||||
|         $invoice->terms = self::tranformObject($recurring_invoice->terms, $client); | ||||
|         $invoice->public_notes = self::tranformObject($recurring_invoice->public_notes, $client); | ||||
|         $invoice->footer = $recurring_invoice->footer ? self::tranformObject($recurring_invoice->footer, $client) : null; | ||||
|         $invoice->terms = $recurring_invoice->terms ? self::tranformObject($recurring_invoice->terms, $client) :  null; | ||||
|         $invoice->public_notes = $recurring_invoice->public_notes ? self::tranformObject($recurring_invoice->public_notes, $client) : null; | ||||
|         $invoice->private_notes = $recurring_invoice->private_notes; | ||||
|         $invoice->is_deleted = $recurring_invoice->is_deleted; | ||||
|         $invoice->line_items = self::transformItems($recurring_invoice, $client); | ||||
|  | ||||
| @ -28,7 +28,7 @@ class ClientFilters extends QueryFilters | ||||
|      * @param string $name | ||||
|      * @return Builder | ||||
|      */ | ||||
|     public function name(string $name): Builder | ||||
|     public function name(string $name = ''): Builder | ||||
|     { | ||||
|         if(strlen($name) >=1) | ||||
|             return $this->builder->where('name', 'like', '%'.$name.'%'); | ||||
|  | ||||
| @ -640,7 +640,14 @@ class ClientController extends BaseController | ||||
|     { | ||||
|         //delete all documents
 | ||||
|         $client->documents->each(function ($document) { | ||||
|             Storage::disk(config('filesystems.default'))->delete($document->url); | ||||
| 
 | ||||
|             try{ | ||||
|                 Storage::disk(config('filesystems.default'))->delete($document->url); | ||||
|             } | ||||
|             catch(\Exception $e){ | ||||
|                 nlog($e->getMessage()); | ||||
|             } | ||||
| 
 | ||||
|         }); | ||||
| 
 | ||||
|         //force delete the client
 | ||||
|  | ||||
| @ -102,7 +102,7 @@ class SelfUpdateController extends BaseController | ||||
|         nlog('Extracting zip'); | ||||
| 
 | ||||
|         //clean up old snappdf installations
 | ||||
|         $this->cleanOldSnapChromeBinaries(); | ||||
|         //$this->cleanOldSnapChromeBinaries();
 | ||||
| 
 | ||||
|         $zipFile = new \PhpZip\ZipFile(); | ||||
| 
 | ||||
|  | ||||
| @ -36,6 +36,7 @@ class SubdomainController extends BaseController | ||||
|         'lb', | ||||
|         'shopify', | ||||
|         'beta', | ||||
|         'prometh' | ||||
|     ]; | ||||
| 
 | ||||
|     public function __construct() | ||||
|  | ||||
| @ -36,6 +36,10 @@ class ExpenseMap | ||||
|             17 => 'expense.tax_rate3', | ||||
|             18 => 'expense.uses_inclusive_taxes', | ||||
|             19 => 'expense.payment_date', | ||||
|             20 => 'expense.custom_value1', | ||||
|             21 => 'expense.custom_value2', | ||||
|             22 => 'expense.custom_value3', | ||||
|             23 => 'expense.custom_value4', | ||||
| 
 | ||||
|         ]; | ||||
|     } | ||||
| @ -63,6 +67,10 @@ class ExpenseMap | ||||
|             17 => 'texts.tax_rate3', | ||||
|             18 => 'texts.uses_inclusive_taxes', | ||||
|             19 => 'texts.payment_date', | ||||
|             20 => 'texts.custom_value1', | ||||
|             21 => 'texts.custom_value2', | ||||
|             22 => 'texts.custom_value3', | ||||
|             23 => 'texts.custom_value4', | ||||
| 
 | ||||
|         ]; | ||||
|     } | ||||
|  | ||||
| @ -86,7 +86,7 @@ class CreateAccount | ||||
|             $sp794f3f->hosted_company_count = config('ninja.quotas.free.max_companies'); | ||||
|             $sp794f3f->account_sms_verified = true; | ||||
| 
 | ||||
|             if(in_array($this->getDomain($this->request['email']), ['gmail.com', 'hotmail.com', 'outlook.com', 'yahoo.com', 'aol.com', 'mail.ru'])){ | ||||
|             if(in_array($this->getDomain($this->request['email']), ['yopmail.com','gmail.com', 'hotmail.com', 'outlook.com', 'yahoo.com', 'aol.com', 'mail.ru'])){ | ||||
|                 $sp794f3f->account_sms_verified = false; | ||||
|             } | ||||
| 
 | ||||
|  | ||||
| @ -515,8 +515,8 @@ class CompanyExport implements ShouldQueue | ||||
| 
 | ||||
|         $path = 'backups'; | ||||
|          | ||||
|         if(!Storage::disk(config('filesystems.default'))->exists($path)) | ||||
|             Storage::disk(config('filesystems.default'))->makeDirectory($path, 0775); | ||||
|         // if(!Storage::disk(config('filesystems.default'))->exists($path))
 | ||||
|         //     Storage::disk(config('filesystems.default'))->makeDirectory($path, 0775);
 | ||||
| 
 | ||||
|         $zip_path = public_path('storage/backups/'.$file_name); | ||||
|         $zip = new \ZipArchive(); | ||||
|  | ||||
| @ -213,11 +213,7 @@ class CreateEntityPdf implements ShouldQueue | ||||
| 
 | ||||
|         if ($pdf) { | ||||
|             try { | ||||
|                 if (! Storage::disk($this->disk)->exists($path)) { | ||||
|                     Storage::disk($this->disk)->makeDirectory($path, 0775); | ||||
|                 } | ||||
| 
 | ||||
|                 Storage::disk($this->disk)->put($file_path, $pdf, 'public'); | ||||
|                 Storage::disk($this->disk)->put($file_path, $pdf); | ||||
|             } catch (\Exception $e) { | ||||
|                 throw new FilePermissionsFailure($e->getMessage()); | ||||
|             } | ||||
|  | ||||
| @ -51,7 +51,7 @@ class ClientLedgerBalanceUpdate implements ShouldQueue | ||||
| 
 | ||||
|         MultiDB::setDb($this->company->db); | ||||
| 
 | ||||
|         CompanyLedger::where('balance', 0)->where('client_id', $this->client->id)->cursor()->each(function ($company_ledger) { | ||||
|         CompanyLedger::where('balance', 0)->where('client_id', $this->client->id)->orderBy('updated_at', 'ASC')->cursor()->each(function ($company_ledger) { | ||||
|             if ($company_ledger->balance > 0) { | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
| @ -83,9 +83,23 @@ class UpdateOrCreateProduct implements ShouldQueue | ||||
| 
 | ||||
|             $product = Product::withTrashed()->firstOrNew(['product_key' => $item->product_key, 'company_id' => $this->invoice->company->id]); | ||||
| 
 | ||||
|                 /* If a user is using placeholders in their descriptions, do not update the products */ | ||||
|                 $string_hit = false; | ||||
| 
 | ||||
|                 foreach ( [':MONTH',':YEAR',':QUARTER',':WEEK'] as $string )  | ||||
|                 { | ||||
|                  | ||||
|                     if(stripos($product->notes, $string) !== FALSE) { | ||||
|                         $string_hit = true;  | ||||
|                     } | ||||
|                      | ||||
|                 } | ||||
| 
 | ||||
|                 if($string_hit) | ||||
|                     continue; | ||||
| 
 | ||||
|             $product->product_key = $item->product_key; | ||||
|             $product->notes = isset($item->notes) ? $item->notes : ''; | ||||
|             //$product->cost = isset($item->cost) ? $item->cost : 0; //this value shouldn't be updated.
 | ||||
|             $product->price = isset($item->cost) ? $item->cost : 0; | ||||
| 
 | ||||
|             if (! $product->id) { | ||||
|  | ||||
| @ -167,7 +167,7 @@ class UploadFile implements ShouldQueue | ||||
|                     $previewHeight = $height * Document::DOCUMENT_PREVIEW_SIZE / $width; | ||||
|                 } else { | ||||
|                     $previewHeight = Document::DOCUMENT_PREVIEW_SIZE; | ||||
|                     $previewWidth = $width * DOCUMENT_PREVIEW_SIZE / $height; | ||||
|                     $previewWidth = $width * Document::DOCUMENT_PREVIEW_SIZE / $height; | ||||
|                 } | ||||
| 
 | ||||
|                 $img->resize($previewWidth, $previewHeight); | ||||
|  | ||||
							
								
								
									
										10
									
								
								app/Jobs/Vendor/CreatePurchaseOrderPdf.php
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										10
									
								
								app/Jobs/Vendor/CreatePurchaseOrderPdf.php
									
									
									
									
										vendored
									
									
								
							| @ -99,18 +99,11 @@ class CreatePurchaseOrderPdf implements ShouldQueue | ||||
|         if ($pdf) { | ||||
| 
 | ||||
|             try{ | ||||
|                  | ||||
|                 if(!Storage::disk($this->disk)->exists($this->path))  | ||||
|                     Storage::disk($this->disk)->makeDirectory($this->path, 0775); | ||||
| 
 | ||||
|                 Storage::disk($this->disk)->put($this->file_path, $pdf, 'public'); | ||||
| 
 | ||||
|                 Storage::disk($this->disk)->put($this->file_path, $pdf); | ||||
|             } | ||||
|             catch(\Exception $e) | ||||
|             { | ||||
| 
 | ||||
|                 throw new FilePermissionsFailure($e->getMessage()); | ||||
| 
 | ||||
|             } | ||||
|         } | ||||
|          | ||||
| @ -209,7 +202,6 @@ class CreatePurchaseOrderPdf implements ShouldQueue | ||||
| 
 | ||||
|                 if($numbered_pdf) | ||||
|                     $pdf = $numbered_pdf; | ||||
|                  | ||||
| 
 | ||||
|             } | ||||
| 
 | ||||
|  | ||||
| @ -146,10 +146,17 @@ class TemplateEmail extends Mailable | ||||
| 
 | ||||
|         } | ||||
| 
 | ||||
|         //22-10-2022 - Performance - To improve the performance/reliability of sending emails, attaching as Data is much better, stubs in place
 | ||||
|         foreach ($this->build_email->getAttachments() as $file) { | ||||
|             if (is_string($file)) { | ||||
|                 // nlog($file);
 | ||||
|                 // $file_data = file_get_contents($file);
 | ||||
|                 // $this->attachData($file_data, basename($file));
 | ||||
|                 $this->attach($file); | ||||
|             } elseif (is_array($file)) { | ||||
|                 // nlog($file['path']);
 | ||||
|                 // $file_data = file_get_contents($file['path']);
 | ||||
|                 // $this->attachData($file_data, $file['name']);
 | ||||
|                 $this->attach($file['path'], ['as' => $file['name'], 'mime' => null]); | ||||
|             } | ||||
|         } | ||||
|  | ||||
| @ -36,15 +36,15 @@ class Backup extends BaseModel | ||||
|         $filename = now()->format('Y_m_d').'_'.md5(time()).'.html'; | ||||
|         $file_path = $path.$filename; | ||||
| 
 | ||||
|         Storage::disk(config('filesystems.default'))->makeDirectory($path, 0775); | ||||
|         // Storage::disk(config('filesystems.default'))->makeDirectory($path, 0775);
 | ||||
| 
 | ||||
|         Storage::disk(config('filesystems.default'))->put($file_path, $html); | ||||
| 
 | ||||
|         if (Storage::disk(config('filesystems.default'))->exists($file_path)) { | ||||
|         // if (Storage::disk(config('filesystems.default'))->exists($file_path)) {
 | ||||
|             $this->html_backup = ''; | ||||
|             $this->filename = $file_path; | ||||
|             $this->save(); | ||||
|         } | ||||
|         // }
 | ||||
|     } | ||||
| 
 | ||||
|     public function deleteFile() | ||||
|  | ||||
| @ -17,6 +17,7 @@ use App\Jobs\Mail\NinjaMailerObject; | ||||
| use App\Mail\ClientContact\ClientContactResetPasswordObject; | ||||
| use App\Models\Presenters\ClientContactPresenter; | ||||
| use App\Notifications\ClientContactResetPassword; | ||||
| use App\Utils\Ninja; | ||||
| use App\Utils\Traits\MakesHash; | ||||
| use Illuminate\Contracts\Translation\HasLocalePreference; | ||||
| use Illuminate\Database\Eloquent\Factories\HasFactory; | ||||
| @ -257,8 +258,33 @@ class ClientContact extends Authenticatable implements HasLocalePreference | ||||
|      */ | ||||
|     public function getLoginLink() | ||||
|     { | ||||
|         $domain = isset($this->company->portal_domain) ? $this->company->portal_domain : $this->company->domain(); | ||||
|         // $domain = isset($this->company->portal_domain) ? $this->company->portal_domain : $this->company->domain();
 | ||||
| 
 | ||||
|         // return $domain.'/client/key_login/'.$this->contact_key;
 | ||||
| 
 | ||||
|         if (Ninja::isHosted()) { | ||||
|             $domain = $this->company->domain(); | ||||
|         } else { | ||||
|             $domain = config('ninja.app_url'); | ||||
|         } | ||||
| 
 | ||||
|         switch ($this->company->portal_mode) { | ||||
|             case 'subdomain': | ||||
|                 return $domain.'/client/key_login/'.$this->contact_key; | ||||
|                 break; | ||||
|             case 'iframe': | ||||
|                 return $domain.'/client/key_login/'.$this->contact_key; | ||||
|                 //return $domain . $entity_type .'/'. $this->contact->client->client_hash .'/'. $this->key;
 | ||||
|                 break; | ||||
|             case 'domain': | ||||
|                 return $domain.'/client/key_login/'.$this->contact_key; | ||||
|                 break; | ||||
| 
 | ||||
|             default: | ||||
|                 return ''; | ||||
|                 break; | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         return $domain.'/client/key_login/'.$this->contact_key; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -42,6 +42,7 @@ class CompanyGateway extends BaseModel | ||||
|         'require_postal_code', | ||||
|         'require_client_phone', | ||||
|         'require_contact_name', | ||||
|         'require_contact_email', | ||||
|         'update_details', | ||||
|         'config', | ||||
|         'fees_and_limits', | ||||
|  | ||||
| @ -288,11 +288,21 @@ class Credit extends BaseModel | ||||
|             return Storage::disk(config('filesystems.default'))->{$type}($file_path); | ||||
|         } | ||||
| 
 | ||||
|         try { | ||||
|             $file_exists = Storage::disk(config('filesystems.default'))->exists($file_path); | ||||
|         } catch (\Exception $e) { | ||||
|             nlog($e->getMessage()); | ||||
|         } | ||||
| 
 | ||||
|         if ($file_exists) { | ||||
|             return Storage::disk(config('filesystems.default'))->{$type}($file_path); | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         if (Storage::disk('public')->exists($file_path)) { | ||||
|             return Storage::disk('public')->{$type}($file_path); | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         $file_path = (new CreateEntityPdf($invitation))->handle(); | ||||
| 
 | ||||
|         return Storage::disk('public')->{$type}($file_path); | ||||
|  | ||||
| @ -451,6 +451,17 @@ class Invoice extends BaseModel | ||||
|             return Storage::disk(config('filesystems.default'))->{$type}($file_path); | ||||
|         } | ||||
| 
 | ||||
|         try { | ||||
|             $file_exists = Storage::disk(config('filesystems.default'))->exists($file_path); | ||||
|         } catch (\Exception $e) { | ||||
|             nlog($e->getMessage()); | ||||
|         } | ||||
| 
 | ||||
|         if ($file_exists) { | ||||
|             return Storage::disk(config('filesystems.default'))->{$type}($file_path); | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         try { | ||||
|             $file_exists = Storage::disk('public')->exists($file_path); | ||||
|         } catch (\Exception $e) { | ||||
|  | ||||
| @ -247,6 +247,16 @@ class Quote extends BaseModel | ||||
|             return Storage::disk(config('filesystems.default'))->{$type}($file_path); | ||||
|         } | ||||
| 
 | ||||
|         try { | ||||
|             $file_exists = Storage::disk(config('filesystems.default'))->exists($file_path); | ||||
|         } catch (\Exception $e) { | ||||
|             nlog($e->getMessage()); | ||||
|         } | ||||
| 
 | ||||
|         if ($file_exists) { | ||||
|             return Storage::disk(config('filesystems.default'))->{$type}($file_path); | ||||
|         } | ||||
| 
 | ||||
|         if (Storage::disk('public')->exists($file_path)) { | ||||
|             return Storage::disk('public')->{$type}($file_path); | ||||
|         } | ||||
|  | ||||
| @ -41,6 +41,8 @@ class SystemLog extends Model | ||||
| 
 | ||||
|     const CATEGORY_SECURITY = 5; | ||||
| 
 | ||||
|     const CATEGORY_LOG = 6; | ||||
| 
 | ||||
|     /* Event IDs*/ | ||||
|     const EVENT_PAYMENT_RECONCILIATION_FAILURE = 10; | ||||
| 
 | ||||
| @ -127,6 +129,8 @@ class SystemLog extends Model | ||||
| 
 | ||||
|     const TYPE_LOGIN_FAILURE = 801; | ||||
| 
 | ||||
|     const TYPE_GENERIC = 900; | ||||
| 
 | ||||
|     protected $fillable = [ | ||||
|         'client_id', | ||||
|         'company_id', | ||||
|  | ||||
							
								
								
									
										86
									
								
								app/Notifications/Ninja/DomainRenewalFailureNotification.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								app/Notifications/Ninja/DomainRenewalFailureNotification.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,86 @@ | ||||
| <?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\Notifications\Ninja; | ||||
| 
 | ||||
| use App\Models\Account; | ||||
| use App\Models\Client; | ||||
| use Illuminate\Bus\Queueable; | ||||
| use Illuminate\Contracts\Queue\ShouldQueue; | ||||
| use Illuminate\Foundation\Bus\Dispatchable; | ||||
| use Illuminate\Notifications\Messages\MailMessage; | ||||
| use Illuminate\Notifications\Messages\SlackMessage; | ||||
| use Illuminate\Notifications\Notification; | ||||
| use Illuminate\Queue\InteractsWithQueue; | ||||
| use Illuminate\Queue\SerializesModels; | ||||
| 
 | ||||
| class DomainRenewalFailureNotification extends Notification  | ||||
| { | ||||
| 
 | ||||
|     /** | ||||
|      * Create a new notification instance. | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
| 
 | ||||
|     protected string $domain; | ||||
| 
 | ||||
|     public function __construct(string $domain) | ||||
|     { | ||||
|         $this->domain = $domain; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the notification's delivery channels. | ||||
|      * | ||||
|      * @param  mixed  $notifiable | ||||
|      * @return array | ||||
|      */ | ||||
|     public function via($notifiable) | ||||
|     { | ||||
|         return ['slack']; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the mail representation of the notification. | ||||
|      * | ||||
|      * @param  mixed  $notifiable | ||||
|      * @return MailMessage | ||||
|      */ | ||||
|     public function toMail($notifiable) | ||||
|     { | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the array representation of the notification. | ||||
|      * | ||||
|      * @param  mixed  $notifiable | ||||
|      * @return array | ||||
|      */ | ||||
|     public function toArray($notifiable) | ||||
|     { | ||||
|         return [ | ||||
|             //
 | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
|     public function toSlack($notifiable) | ||||
|     { | ||||
|         $content = "Domain Certificate _renewal_ failure:\n"; | ||||
|         $content .= "{$this->domain}\n";         | ||||
| 
 | ||||
|         return (new SlackMessage) | ||||
|                 ->success() | ||||
|                 ->from(ctrans('texts.notification_bot')) | ||||
|                 ->image('https://app.invoiceninja.com/favicon.png') | ||||
|                 ->content($content); | ||||
|     } | ||||
| } | ||||
| @ -602,11 +602,11 @@ class BaseDriver extends AbstractPaymentDriver | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         if ($this->company_gateway->require_contact_email) { | ||||
|             if ($this->checkRequiredResource($this->email)) { | ||||
|                 $this->required_fields[] = 'contact_email'; | ||||
|             } | ||||
|         } | ||||
|         // if ($this->company_gateway->require_contact_email) {
 | ||||
|         //     if ($this->checkRequiredResource($this->email)) {
 | ||||
|         //         $this->required_fields[] = 'contact_email';
 | ||||
|         //     }
 | ||||
|         // }
 | ||||
| 
 | ||||
|         // if ($this->company_gateway->require_contact_name) {
 | ||||
|         //     if ($this->checkRequiredResource($this->first_name)) {
 | ||||
|  | ||||
| @ -14,9 +14,11 @@ namespace App\PaymentDrivers\CheckoutCom; | ||||
| 
 | ||||
| use App\Exceptions\PaymentFailed; | ||||
| use App\Http\Requests\ClientPortal\Payments\PaymentResponseRequest; | ||||
| use App\Jobs\Util\SystemLogger; | ||||
| use App\Models\ClientGatewayToken; | ||||
| use App\Models\GatewayType; | ||||
| use App\Models\Payment; | ||||
| use App\Models\SystemLog; | ||||
| use App\PaymentDrivers\CheckoutComPaymentDriver; | ||||
| use App\PaymentDrivers\Common\MethodInterface; | ||||
| use App\Utils\Traits\MakesHash; | ||||
| @ -245,6 +247,16 @@ class CreditCard implements MethodInterface | ||||
|             if ($response['status'] == 'Declined') { | ||||
|                 $this->checkout->unWindGatewayFees($this->checkout->payment_hash); | ||||
| 
 | ||||
|                 //18-10-2022
 | ||||
|                 SystemLogger::dispatch( | ||||
|                     $response, | ||||
|                     SystemLog::CATEGORY_GATEWAY_RESPONSE, | ||||
|                     SystemLog::EVENT_GATEWAY_ERROR, | ||||
|                     SystemLog::TYPE_CHECKOUT, | ||||
|                     $this->checkout->client, | ||||
|                     $this->checkout->client->company, | ||||
|                 ); | ||||
| 
 | ||||
|                 return $this->processUnsuccessfulPayment($response); | ||||
|             } | ||||
|         } catch (CheckoutApiException $e) { | ||||
|  | ||||
| @ -78,6 +78,7 @@ class Charge | ||||
|                 'payment_method' => $cgt->token, | ||||
|                 'customer' => $cgt->gateway_customer_reference, | ||||
|                 'confirm' => true, | ||||
|                 // 'off_session' => true,
 | ||||
|                 'description' => $description, | ||||
|                 'metadata' => [ | ||||
|                     'payment_hash' => $payment_hash->hash, | ||||
|  | ||||
| @ -398,7 +398,7 @@ class StripePaymentDriver extends BaseDriver | ||||
|     { | ||||
|         $this->init(); | ||||
| 
 | ||||
|         $params = []; | ||||
|         $params = ['usage' => 'off_session']; | ||||
|         $meta = $this->stripe_connect_auth; | ||||
| 
 | ||||
|         return SetupIntent::create($params, $meta); | ||||
|  | ||||
| @ -49,8 +49,8 @@ class EntityPolicy | ||||
|         return ($user->isAdmin() && $entity->company_id == $user->companyId()) | ||||
|             || ($user->hasPermission('edit_'.strtolower(\Illuminate\Support\Str::snake(class_basename($entity)))) && $entity->company_id == $user->companyId()) | ||||
|             || ($user->hasPermission('edit_all') && $entity->company_id == $user->companyId()) | ||||
|             || $user->owns($entity) | ||||
|             || $user->assigned($entity); | ||||
|             || ($user->owns($entity) && $entity->company_id == $user->companyId()) | ||||
|             || ($user->assigned($entity) && $entity->company_id == $user->companyId()); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -66,7 +66,7 @@ class EntityPolicy | ||||
|         return ($user->isAdmin() && $entity->company_id == $user->companyId()) | ||||
|             || ($user->hasPermission('view_'.strtolower(\Illuminate\Support\Str::snake(class_basename($entity)))) && $entity->company_id == $user->companyId()) | ||||
|             || ($user->hasPermission('view_all') && $entity->company_id == $user->companyId()) | ||||
|             || $user->owns($entity) | ||||
|             || $user->assigned($entity); | ||||
|             || ($user->owns($entity) && $entity->company_id == $user->companyId()) | ||||
|             || ($user->assigned($entity) && $entity->company_id == $user->companyId()); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -59,7 +59,8 @@ class ActivityRepository extends BaseRepository | ||||
| 
 | ||||
|         $activity->save(); | ||||
| 
 | ||||
|         $this->createBackup($entity, $activity); | ||||
|         //rate limiter
 | ||||
|         // $this->createBackup($entity, $activity);
 | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -82,7 +83,8 @@ class ActivityRepository extends BaseRepository | ||||
|             $backup = new Backup(); | ||||
|             $entity->load('client'); | ||||
|             $contact = $entity->client->primary_contact()->first(); | ||||
|             $backup->html_backup = $this->generateHtml($entity); | ||||
|             $backup->html_backup = ''; | ||||
|             // $backup->html_backup = $this->generateHtml($entity);
 | ||||
|             $backup->amount = $entity->amount; | ||||
|             $backup->activity_id = $activity->id; | ||||
|             $backup->json_backup = ''; | ||||
|  | ||||
| @ -19,9 +19,11 @@ use App\Models\Credit; | ||||
| use App\Models\Invoice; | ||||
| use App\Models\Quote; | ||||
| use App\Models\RecurringInvoice; | ||||
| use App\Utils\Helpers; | ||||
| use App\Utils\Ninja; | ||||
| use App\Utils\Traits\MakesHash; | ||||
| use App\Utils\Traits\SavesDocuments; | ||||
| use Google\Service\Vision\Property; | ||||
| use ReflectionClass; | ||||
| 
 | ||||
| class BaseRepository | ||||
| @ -108,32 +110,6 @@ class BaseRepository | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @param $ids | ||||
|      * @param $action | ||||
|      * | ||||
|      * @return int | ||||
|      * @deprecated - this doesn't appear to be used anywhere? | ||||
|      */ | ||||
|     // public function bulk($ids, $action)
 | ||||
|     // {
 | ||||
|     //     if (! $ids) {
 | ||||
|     //         return 0;
 | ||||
|     //     }
 | ||||
| 
 | ||||
|     //     $ids = $this->transformKeys($ids);
 | ||||
| 
 | ||||
|     //     $entities = $this->findByPublicIdsWithTrashed($ids);
 | ||||
| 
 | ||||
|     //     foreach ($entities as $entity) {
 | ||||
|     //         if (auth()->user()->can('edit', $entity)) {
 | ||||
|     //             $this->$action($entity);
 | ||||
|     //         }
 | ||||
|     //     }
 | ||||
| 
 | ||||
|     //     return count($entities);
 | ||||
|     // }
 | ||||
| 
 | ||||
|     /* Returns an invoice if defined as a key in the $resource array*/ | ||||
|     public function getInvitation($invitation, $resource) | ||||
|     { | ||||
| @ -171,7 +147,7 @@ class BaseRepository | ||||
|      * @throws \ReflectionException | ||||
|      */ | ||||
|     protected function alternativeSave($data, $model) | ||||
|     { | ||||
|     {   //$start = microtime(true);
 | ||||
|         //forces the client_id if it doesn't exist
 | ||||
|         if(array_key_exists('client_id', $data))  | ||||
|             $model->client_id = $data['client_id']; | ||||
| @ -208,9 +184,21 @@ class BaseRepository | ||||
|         $model->custom_surcharge_tax3 = $client->company->custom_surcharge_taxes3; | ||||
|         $model->custom_surcharge_tax4 = $client->company->custom_surcharge_taxes4; | ||||
| 
 | ||||
|         if(!$model->id) | ||||
|         if(!$model->id){ | ||||
|             $this->new_model = true; | ||||
|          | ||||
| 
 | ||||
|             if(is_array($model->line_items)) | ||||
|             {                 | ||||
|                 $model->line_items = (collect($model->line_items))->map(function ($item) use($model,$client) { | ||||
| 
 | ||||
|                     $item->notes = Helpers::processReservedKeywords($item->notes, $client); | ||||
| 
 | ||||
|                     return $item; | ||||
| 
 | ||||
|                 }); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         $model->saveQuietly(); | ||||
| 
 | ||||
|         /* Model now persisted, now lets do some child tasks */ | ||||
| @ -378,6 +366,8 @@ class BaseRepository | ||||
| 
 | ||||
|         $model->save(); | ||||
| 
 | ||||
| //        nlog("save time = ". microtime(true) - $start);
 | ||||
| 
 | ||||
|         return $model->fresh(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -105,10 +105,7 @@ class GenerateDeliveryNote | ||||
|             info($maker->getCompiledHTML()); | ||||
|         } | ||||
| 
 | ||||
|         if (! Storage::disk($this->disk)->exists($this->invoice->client->invoice_filepath($invitation))) { | ||||
|             Storage::disk($this->disk)->makeDirectory($this->invoice->client->invoice_filepath($invitation), 0775); | ||||
|         } | ||||
|         Storage::disk($this->disk)->put($file_path, $pdf, 'public'); | ||||
|         Storage::disk($this->disk)->put($file_path, $pdf); | ||||
| 
 | ||||
|         return $file_path; | ||||
|     } | ||||
|  | ||||
| @ -218,6 +218,7 @@ class InvoiceService | ||||
|     public function markDeleted() | ||||
|     { | ||||
|         $this->removeUnpaidGatewayFees(); | ||||
|         $this->deletePdf(); | ||||
| 
 | ||||
|         $this->invoice = (new MarkInvoiceDeleted($this->invoice))->run(); | ||||
| 
 | ||||
| @ -268,7 +269,11 @@ class InvoiceService | ||||
|             return $this; | ||||
|         } | ||||
| 
 | ||||
|         $this->invoice->due_date = Carbon::parse($this->invoice->date)->addDays($this->invoice->client->getSetting('payment_terms')); | ||||
|         //12-10-2022
 | ||||
|         if($this->invoice->partial > 0 && !$this->invoice->partial_due_date) | ||||
|             $this->invoice->partial_due_date = Carbon::parse($this->invoice->date)->addDays($this->invoice->client->getSetting('payment_terms')); | ||||
|         else | ||||
|             $this->invoice->due_date = Carbon::parse($this->invoice->date)->addDays($this->invoice->client->getSetting('payment_terms')); | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| @ -294,6 +299,9 @@ class InvoiceService | ||||
|         } elseif ($this->invoice->balance > 0 && $this->invoice->balance < $this->invoice->amount) { | ||||
|             $this->setStatus(Invoice::STATUS_PARTIAL); | ||||
|         } | ||||
|         elseif($this->invoice->balance < 0) { | ||||
|             $this->setStatus(Invoice::STATUS_PARTIAL);    | ||||
|         } | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| @ -309,7 +317,7 @@ class InvoiceService | ||||
|         } elseif ($this->invoice->balance > 0 && $this->invoice->balance < $this->invoice->amount) { | ||||
|             $this->invoice->status_id = Invoice::STATUS_PARTIAL; | ||||
|         } | ||||
|         elseif ($this->invoice->balance < 0) { | ||||
|         elseif ($this->invoice->balance > 0) { | ||||
|             $this->invoice->status_id = Invoice::STATUS_SENT; | ||||
|         } | ||||
| 
 | ||||
|  | ||||
| @ -71,10 +71,6 @@ class MarkInvoiceDeleted extends AbstractService | ||||
| 
 | ||||
|     private function adjustPaidToDateAndBalance() | ||||
|     { | ||||
|         // $client = $this->invoice->client->fresh();
 | ||||
|         // $client->paid_to_date += $this->adjustment_amount * -1;
 | ||||
|         // $client->balance += $this->balance_adjustment * -1;
 | ||||
|         // $client->save();
 | ||||
| 
 | ||||
|         // 06-09-2022
 | ||||
|         $this->invoice | ||||
|  | ||||
| @ -28,6 +28,7 @@ class UpdateReminder extends AbstractService | ||||
|         $this->settings = $settings; | ||||
|     } | ||||
| 
 | ||||
|     /* We only support setting reminders based on the due date, not the partial due date */ | ||||
|     public function run() | ||||
|     { | ||||
|         if (! $this->settings) { | ||||
|  | ||||
| @ -16,10 +16,12 @@ use App\Factory\CreditFactory; | ||||
| use App\Factory\InvoiceItemFactory; | ||||
| use App\Jobs\Ninja\TransactionLog; | ||||
| use App\Jobs\Payment\EmailRefundPayment; | ||||
| use App\Jobs\Util\SystemLogger; | ||||
| use App\Models\Activity; | ||||
| use App\Models\Credit; | ||||
| use App\Models\Invoice; | ||||
| use App\Models\Payment; | ||||
| use App\Models\SystemLog; | ||||
| use App\Models\TransactionEvent; | ||||
| use App\Repositories\ActivityRepository; | ||||
| use App\Utils\Ninja; | ||||
| @ -77,6 +79,11 @@ class RefundPayment | ||||
| 
 | ||||
|         TransactionLog::dispatch(TransactionEvent::PAYMENT_REFUND, $transaction, $this->payment->company->db); | ||||
| 
 | ||||
|         $notes = ctrans('texts.refunded') . " : {$this->total_refund} - " . ctrans('texts.gateway_refund') . " : "; | ||||
|         $notes .= $this->refund_data['gateway_refund'] !== false ? ctrans('texts.yes') : ctrans('texts.no'); | ||||
| 
 | ||||
|         $this->createActivity($notes); | ||||
| 
 | ||||
|         return $this->payment; | ||||
|     } | ||||
| 
 | ||||
| @ -94,8 +101,6 @@ class RefundPayment | ||||
| 
 | ||||
|                 $this->payment->refunded += $this->total_refund; | ||||
| 
 | ||||
|                 $this->createActivity($this->payment); | ||||
| 
 | ||||
|                 if ($response['success'] == false) { | ||||
|                     $this->payment->save(); | ||||
| 
 | ||||
| @ -129,7 +134,7 @@ class RefundPayment | ||||
|         $fields->company_id = $this->payment->company_id; | ||||
|         $fields->activity_type_id = Activity::REFUNDED_PAYMENT; | ||||
|         // $fields->credit_id = $this->credit_note->id; // TODO
 | ||||
|         $fields->notes = json_encode($notes); | ||||
|         $fields->notes = $notes; | ||||
| 
 | ||||
|         if (isset($this->refund_data['invoices'])) { | ||||
|             foreach ($this->refund_data['invoices'] as $invoice) { | ||||
|  | ||||
| @ -128,7 +128,7 @@ class Helpers | ||||
| 
 | ||||
|         if(!$string_hit) | ||||
|             return $value; | ||||
|         // 04-10-2022 Return Early if no reserved keywords are present, this is a very expenseive process
 | ||||
|         // 04-10-2022 Return Early if no reserved keywords are present, this is a very expensive process
 | ||||
| 
 | ||||
|         Carbon::setLocale($entity->locale()); | ||||
| 
 | ||||
| @ -296,8 +296,7 @@ class Helpers | ||||
|         } | ||||
| 
 | ||||
|         return $value; | ||||
|         // $x = str_replace(["\n", "<br>"], ["\r", "<br>"], $value);
 | ||||
|         // return $x;
 | ||||
|          | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|  | ||||
| @ -150,7 +150,7 @@ class HtmlEngine | ||||
|             $data['$number'] = ['value' => $this->entity->number ?: ' ', 'label' => ctrans('texts.invoice_number')]; | ||||
|             $data['$invoice'] = ['value' => $this->entity->number ?: ' ', 'label' => ctrans('texts.invoice_number')]; | ||||
|             $data['$number_short'] = ['value' => $this->entity->number ?: ' ', 'label' => ctrans('texts.invoice_number_short')]; | ||||
|             $data['$entity.terms'] = ['value' => Helpers::processReservedKeywords(\nl2br($this->entity->terms), $this->client) ?: '', 'label' => ctrans('texts.invoice_terms')]; | ||||
|             $data['$entity.terms'] = ['value' => Helpers::processReservedKeywords(\nl2br($this->entity->terms ?: ''), $this->client) ?: '', 'label' => ctrans('texts.invoice_terms')]; | ||||
|             $data['$terms'] = &$data['$entity.terms']; | ||||
|             $data['$view_link'] = ['value' => '<a class="button" href="'.$this->invitation->getLink().'">'.ctrans('texts.view_invoice').'</a>', 'label' => ctrans('texts.view_invoice')]; | ||||
|             $data['$viewLink'] = &$data['$view_link']; | ||||
| @ -185,7 +185,7 @@ class HtmlEngine | ||||
|             $data['$entity'] = ['value' => '', 'label' => ctrans('texts.quote')]; | ||||
|             $data['$number'] = ['value' => $this->entity->number ?: '', 'label' => ctrans('texts.quote_number')]; | ||||
|             $data['$number_short'] = ['value' => $this->entity->number ?: '', 'label' => ctrans('texts.quote_number_short')]; | ||||
|             $data['$entity.terms'] = ['value' => Helpers::processReservedKeywords(\nl2br($this->entity->terms), $this->client) ?: '', 'label' => ctrans('texts.quote_terms')]; | ||||
|             $data['$entity.terms'] = ['value' => Helpers::processReservedKeywords(\nl2br($this->entity->terms ?: ''), $this->client) ?: '', 'label' => ctrans('texts.quote_terms')]; | ||||
|             $data['$terms'] = &$data['$entity.terms']; | ||||
|             $data['$view_link'] = ['value' => '<a class="button" href="'.$this->invitation->getLink().'">'.ctrans('texts.view_quote').'</a>', 'label' => ctrans('texts.view_quote')]; | ||||
|             $data['$viewLink'] = &$data['$view_link']; | ||||
| @ -210,7 +210,7 @@ class HtmlEngine | ||||
|             $data['$entity'] = ['value' => '', 'label' => ctrans('texts.credit')]; | ||||
|             $data['$number'] = ['value' => $this->entity->number ?: '', 'label' => ctrans('texts.credit_number')]; | ||||
|             $data['$number_short'] = ['value' => $this->entity->number ?: '', 'label' => ctrans('texts.credit_number_short')]; | ||||
|             $data['$entity.terms'] = ['value' => Helpers::processReservedKeywords(\nl2br($this->entity->terms), $this->client) ?: '', 'label' => ctrans('texts.credit_terms')]; | ||||
|             $data['$entity.terms'] = ['value' => Helpers::processReservedKeywords(\nl2br($this->entity->terms ?: ''), $this->client) ?: '', 'label' => ctrans('texts.credit_terms')]; | ||||
|             $data['$terms'] = &$data['$entity.terms']; | ||||
|             $data['$view_link'] = ['value' => '<a class="button" href="'.$this->invitation->getLink().'">'.ctrans('texts.view_credit').'</a>', 'label' => ctrans('texts.view_credit')]; | ||||
|             $data['$viewButton'] = &$data['$view_link']; | ||||
| @ -303,7 +303,7 @@ class HtmlEngine | ||||
|         $data['$invoice.custom2'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'invoice2', $this->entity->custom_value2, $this->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'invoice2')]; | ||||
|         $data['$invoice.custom3'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'invoice3', $this->entity->custom_value3, $this->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'invoice3')]; | ||||
|         $data['$invoice.custom4'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'invoice4', $this->entity->custom_value4, $this->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'invoice4')]; | ||||
|         $data['$invoice.public_notes'] = ['value' => Helpers::processReservedKeywords(\nl2br($this->entity->public_notes), $this->client) ?: '', 'label' => ctrans('texts.public_notes')]; | ||||
|         $data['$invoice.public_notes'] = ['value' => Helpers::processReservedKeywords(\nl2br($this->entity->public_notes ?: ''), $this->client) ?: '', 'label' => ctrans('texts.public_notes')]; | ||||
|         $data['$entity.public_notes'] = &$data['$invoice.public_notes']; | ||||
|         $data['$public_notes'] = &$data['$invoice.public_notes']; | ||||
|         $data['$notes'] = &$data['$public_notes']; | ||||
| @ -535,7 +535,7 @@ class HtmlEngine | ||||
|         $data['$description'] = ['value' => '', 'label' => ctrans('texts.description')]; | ||||
| 
 | ||||
|         //$data['$entity_footer'] = ['value' => $this->client->getSetting("{$this->entity_string}_footer"), 'label' => ''];
 | ||||
|         $data['$entity_footer'] = ['value' => Helpers::processReservedKeywords(\nl2br($this->entity->footer), $this->client), 'label' => '']; | ||||
|         $data['$entity_footer'] = ['value' => Helpers::processReservedKeywords(\nl2br($this->entity->footer ?: ''), $this->client), 'label' => '']; | ||||
|         $data['$footer'] = &$data['$entity_footer']; | ||||
|          | ||||
|         $data['$page_size'] = ['value' => $this->settings->page_size, 'label' => '']; | ||||
|  | ||||
| @ -54,17 +54,33 @@ class Number | ||||
|      * Formats a given value based on the clients currency. | ||||
|      * | ||||
|      * @param  float  $value    The number to be formatted | ||||
|      * @param  object $currency The client currency object | ||||
|      * | ||||
|      * @return string           The formatted value | ||||
|      */ | ||||
|     public static function formatValueNoTrailingZeroes($value, $currency) :string | ||||
|     public static function formatValueNoTrailingZeroes($value, $entity) :string | ||||
|     { | ||||
|         $value = floatval($value); | ||||
| 
 | ||||
|         $currency = $entity->currency(); | ||||
| 
 | ||||
|         $thousand = $currency->thousand_separator; | ||||
|         $decimal = $currency->decimal_separator; | ||||
|         $precision = $currency->precision; | ||||
|         // $precision = $currency->precision;
 | ||||
| 
 | ||||
|         if ($entity instanceof Company) { | ||||
|             $country = $entity->country(); | ||||
|         } else { | ||||
|             $country = $entity->country; | ||||
|         } | ||||
| 
 | ||||
|         /* Country settings override client settings */ | ||||
|         if (isset($country->thousand_separator) && strlen($country->thousand_separator) >= 1) { | ||||
|             $thousand = $country->thousand_separator; | ||||
|         } | ||||
| 
 | ||||
|         if (isset($country->decimal_separator) && strlen($country->decimal_separator) >= 1) { | ||||
|             $decimal = $country->decimal_separator; | ||||
|         } | ||||
| 
 | ||||
|         $precision = 10; | ||||
| 
 | ||||
|  | ||||
| @ -65,7 +65,17 @@ trait ClientGroupSettingsSaver | ||||
|         } | ||||
| 
 | ||||
|         $entity->settings = $entity_settings; | ||||
|         $entity->save(); | ||||
| 
 | ||||
|         try{ | ||||
|             $entity->save(); | ||||
|         } | ||||
|         catch(\Exception $e){ | ||||
| 
 | ||||
|             nlog("client settings failure"); | ||||
|             nlog($entity_settings); | ||||
|             nlog($e->getMessage()); | ||||
|              | ||||
|         } | ||||
| 
 | ||||
|         return $entity_settings; | ||||
|     } | ||||
|  | ||||
| @ -300,13 +300,13 @@ trait MakesInvoiceValues | ||||
|             $data[$key][$table_type.'.notes'] = Helpers::processReservedKeywords($item->notes, $entity); | ||||
|             $data[$key][$table_type.'.description'] = Helpers::processReservedKeywords($item->notes, $entity); | ||||
| 
 | ||||
|             $data[$key][$table_type.".{$_table_type}1"] = strlen($item->custom_value1) > 1 ? $helpers->formatCustomFieldValue($this->company->custom_fields, "{$_table_type}1", $item->custom_value1, $entity) : ''; | ||||
|             $data[$key][$table_type.".{$_table_type}2"] = strlen($item->custom_value2) > 2 ? $helpers->formatCustomFieldValue($this->company->custom_fields, "{$_table_type}2", $item->custom_value2, $entity) : ''; | ||||
|             $data[$key][$table_type.".{$_table_type}3"] = strlen($item->custom_value3) > 3 ? $helpers->formatCustomFieldValue($this->company->custom_fields, "{$_table_type}3", $item->custom_value3, $entity) : ''; | ||||
|             $data[$key][$table_type.".{$_table_type}4"] = strlen($item->custom_value4) > 4 ? $helpers->formatCustomFieldValue($this->company->custom_fields, "{$_table_type}4", $item->custom_value4, $entity) : ''; | ||||
|             $data[$key][$table_type.".{$_table_type}1"] = strlen($item->custom_value1) >= 1 ? $helpers->formatCustomFieldValue($this->company->custom_fields, "{$_table_type}1", $item->custom_value1, $entity) : ''; | ||||
|             $data[$key][$table_type.".{$_table_type}2"] = strlen($item->custom_value2) >= 1 ? $helpers->formatCustomFieldValue($this->company->custom_fields, "{$_table_type}2", $item->custom_value2, $entity) : ''; | ||||
|             $data[$key][$table_type.".{$_table_type}3"] = strlen($item->custom_value3) >= 1 ? $helpers->formatCustomFieldValue($this->company->custom_fields, "{$_table_type}3", $item->custom_value3, $entity) : ''; | ||||
|             $data[$key][$table_type.".{$_table_type}4"] = strlen($item->custom_value4) >= 1 ? $helpers->formatCustomFieldValue($this->company->custom_fields, "{$_table_type}4", $item->custom_value4, $entity) : ''; | ||||
| 
 | ||||
|             if ($item->quantity > 0 || $item->cost > 0) { | ||||
|                 $data[$key][$table_type.'.quantity'] = Number::formatValueNoTrailingZeroes($item->quantity, $entity_currency); | ||||
|                 $data[$key][$table_type.'.quantity'] = Number::formatValueNoTrailingZeroes($item->quantity, $entity); | ||||
| 
 | ||||
|                 $data[$key][$table_type.'.unit_cost'] = Number::formatMoneyNoRounding($item->cost, $entity); | ||||
| 
 | ||||
|  | ||||
| @ -79,6 +79,19 @@ return [ | ||||
|             'throw' => false, | ||||
|         ], | ||||
| 
 | ||||
|         'r2' => [ | ||||
|             'driver' => 's3', | ||||
|             'key' => env('R2_ACCESS_KEY_ID'), | ||||
|             'secret' => env('R2_SECRET_ACCESS_KEY'), | ||||
|             'region' => env('R2_DEFAULT_REGION'), | ||||
|             'bucket' => env('R2_BUCKET'), | ||||
|             'url' => env('R2_URL'), | ||||
|             'visibility' => 'private', | ||||
|             'endpoint' => env('R2_ENDPOINT'), | ||||
|             'use_path_style_endpoint' => env('R2_USE_PATH_STYLE_ENDPOINT', false), | ||||
|             'throw' => false, | ||||
|         ], | ||||
| 
 | ||||
|         'gcs' => [ | ||||
|             'driver' => 'gcs', | ||||
|             'project_id' => env('GOOGLE_CLOUD_PROJECT_ID', 'your-project-id'), | ||||
|  | ||||
| @ -40,7 +40,7 @@ return [ | ||||
|             /* | ||||
|              * Route for accessing parsed swagger annotations. | ||||
|             */ | ||||
|             'docs' => 'docs', | ||||
|             'docs' => 'swagger-docs-that-should-be-inaccessible', | ||||
| 
 | ||||
|             /* | ||||
|              * Route for Oauth2 authentication callback. | ||||
|  | ||||
| @ -43,6 +43,7 @@ return [ | ||||
|             'password' => env('MAIL_PASSWORD'), | ||||
|             'timeout' => null, | ||||
|             'local_domain' => env('MAIL_EHLO_DOMAIN'), | ||||
|             'verify_peer' => env('MAIL_VERIFY_PEER', true), | ||||
|         ], | ||||
| 
 | ||||
|         'ses' => [ | ||||
|  | ||||
| @ -14,8 +14,8 @@ return [ | ||||
|     'require_https' => env('REQUIRE_HTTPS', true), | ||||
|     'app_url' => rtrim(env('APP_URL', ''), '/'), | ||||
|     'app_domain' => env('APP_DOMAIN', 'invoicing.co'), | ||||
|     'app_version' => '5.5.27', | ||||
|     'app_tag' => '5.5.27', | ||||
|     'app_version' => '5.5.32', | ||||
|     'app_tag' => '5.5.32', | ||||
|     'minimum_client_version' => '5.0.16', | ||||
|     'terms_version' => '1.0.1', | ||||
|     'api_secret' => env('API_SECRET', ''), | ||||
|  | ||||
| @ -0,0 +1,30 @@ | ||||
| <?php | ||||
| 
 | ||||
| use Illuminate\Database\Migrations\Migration; | ||||
| use Illuminate\Database\Schema\Blueprint; | ||||
| use Illuminate\Support\Facades\Schema; | ||||
| 
 | ||||
| return new class extends Migration | ||||
| { | ||||
|     /** | ||||
|      * Run the migrations. | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     public function up() | ||||
|     { | ||||
|         Schema::table('company_tokens', function (Blueprint $table) { | ||||
|             $table->index('token'); | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Reverse the migrations. | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     public function down() | ||||
|     { | ||||
| 
 | ||||
|     } | ||||
| }; | ||||
| @ -0,0 +1,45 @@ | ||||
| <?php | ||||
| 
 | ||||
| use Illuminate\Database\Migrations\Migration; | ||||
| use Illuminate\Database\Schema\Blueprint; | ||||
| use Illuminate\Support\Facades\Schema; | ||||
| 
 | ||||
| return new class extends Migration | ||||
| { | ||||
|     /** | ||||
|      * Run the migrations. | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     public function up() | ||||
|     { | ||||
|        Schema::table('documents', function (Blueprint $table) { | ||||
|             $table->index(['documentable_id', 'documentable_type', 'deleted_at']); | ||||
|        }); | ||||
| 
 | ||||
|        Schema::table('expenses', function (Blueprint $table) { | ||||
|             $table->index(['invoice_id', 'deleted_at']); | ||||
|        }); | ||||
| 
 | ||||
|        Schema::table('company_tokens', function (Blueprint $table) { | ||||
|             $table->dropIndex('company_tokens_token_index'); | ||||
|             $table->index(['token','deleted_at']); | ||||
|        }); | ||||
| 
 | ||||
|        Schema::table('invoice_invitations', function (Blueprint $table) { | ||||
|             $table->dropIndex('invoice_invitations_key_index'); | ||||
|             $table->index(['key','deleted_at']); | ||||
|        }); | ||||
|         | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Reverse the migrations. | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     public function down() | ||||
|     { | ||||
|         //
 | ||||
|     } | ||||
| }; | ||||
| @ -3414,7 +3414,7 @@ $LANG = array( | ||||
|     'credit_number_counter' => 'Credit Number Counter', | ||||
|     'reset_counter_date' => 'Reset Counter Date', | ||||
|     'counter_padding' => 'Counter Padding', | ||||
|     'shared_invoice_quote_counter' => 'Shared Invoice Quote Counter', | ||||
|     'shared_invoice_quote_counter' => 'Share Invoice Quote Counter', | ||||
|     'default_tax_name_1' => 'Default Tax Name 1', | ||||
|     'default_tax_rate_1' => 'Default Tax Rate 1', | ||||
|     'default_tax_name_2' => 'Default Tax Name 2', | ||||
| @ -4199,7 +4199,7 @@ $LANG = array( | ||||
|      'client_id_number' => 'Client ID Number', | ||||
|      'count_minutes' => ':count Minutes', | ||||
|      'password_timeout' => 'Password Timeout', | ||||
|      'shared_invoice_credit_counter' => 'Shared Invoice/Credit Counter', | ||||
|      'shared_invoice_credit_counter' => 'Share Invoice/Credit Counter', | ||||
| 
 | ||||
|     'activity_80' => ':user created subscription :subscription', | ||||
|     'activity_81' => ':user updated subscription :subscription', | ||||
| @ -4776,7 +4776,8 @@ $LANG = array( | ||||
|     'client_email' => 'Client Email', | ||||
|     'invoice_task_project' => 'Invoice Task Project', | ||||
|     'invoice_task_project_help' => 'Add the project to the invoice line items', | ||||
| 
 | ||||
|     'bulk_action' => 'Bulk Action', | ||||
|      | ||||
| ); | ||||
| 
 | ||||
| return $LANG; | ||||
|  | ||||
							
								
								
									
										136
									
								
								preload.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										136
									
								
								preload.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,136 @@ | ||||
| <?php | ||||
| 
 | ||||
| require_once __DIR__ . '/vendor/autoload.php'; | ||||
| 
 | ||||
| class Preloader | ||||
| { | ||||
|     private array $ignores = []; | ||||
| 
 | ||||
|     private static int $count = 0; | ||||
| 
 | ||||
|     private array $paths; | ||||
| 
 | ||||
|     private array $fileMap; | ||||
| 
 | ||||
|     public function __construct(string ...$paths) | ||||
|     { | ||||
|         $this->paths = $paths; | ||||
| 
 | ||||
|         // We'll use composer's classmap
 | ||||
|         // to easily find which classes to autoload,
 | ||||
|         // based on their filename
 | ||||
|         $classMap = require __DIR__ . '/vendor/composer/autoload_classmap.php'; | ||||
| 
 | ||||
|         $this->fileMap = array_flip($classMap); | ||||
|     } | ||||
| 
 | ||||
|     public function paths(string ...$paths): Preloader | ||||
|     { | ||||
|         $this->paths = array_merge( | ||||
|             $this->paths, | ||||
|             $paths | ||||
|         ); | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|     public function ignore(string ...$names): Preloader | ||||
|     { | ||||
|         $this->ignores = array_merge( | ||||
|             $this->ignores, | ||||
|             $names | ||||
|         ); | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|     public function load(): void | ||||
|     { | ||||
|         // We'll loop over all registered paths
 | ||||
|         // and load them one by one
 | ||||
|         foreach ($this->paths as $path) { | ||||
|             $this->loadPath(rtrim($path, '/')); | ||||
|         } | ||||
| 
 | ||||
|         $count = self::$count; | ||||
| 
 | ||||
|         echo "[Preloader] Preloaded {$count} classes" . PHP_EOL; | ||||
|     } | ||||
| 
 | ||||
|     private function loadPath(string $path): void | ||||
|     { | ||||
|         // If the current path is a directory,
 | ||||
|         // we'll load all files in it
 | ||||
|         if (is_dir($path)) { | ||||
|             $this->loadDir($path); | ||||
| 
 | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         // Otherwise we'll just load this one file
 | ||||
|         $this->loadFile($path); | ||||
|     } | ||||
| 
 | ||||
|     private function loadDir(string $path): void | ||||
|     { | ||||
|         $handle = opendir($path); | ||||
| 
 | ||||
|         // We'll loop over all files and directories
 | ||||
|         // in the current path,
 | ||||
|         // and load them one by one
 | ||||
|         while ($file = readdir($handle)) { | ||||
|             if (in_array($file, ['.', '..'])) { | ||||
|                 continue; | ||||
|             } | ||||
| 
 | ||||
|             $this->loadPath("{$path}/{$file}"); | ||||
|         } | ||||
| 
 | ||||
|         closedir($handle); | ||||
|     } | ||||
| 
 | ||||
|     private function loadFile(string $path): void | ||||
|     { | ||||
|         // We resolve the classname from composer's autoload mapping
 | ||||
|         $class = $this->fileMap[$path] ?? null; | ||||
| 
 | ||||
|         // And use it to make sure the class shouldn't be ignored
 | ||||
|         if ($this->shouldIgnore($class)) { | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         // Finally we require the path,
 | ||||
|         // causing all its dependencies to be loaded as well
 | ||||
|         require_once($path); | ||||
| 
 | ||||
|         self::$count++; | ||||
| 
 | ||||
|         echo "[Preloader] Preloaded `{$class}`" . PHP_EOL; | ||||
|     } | ||||
| 
 | ||||
|     private function shouldIgnore(?string $name): bool | ||||
|     { | ||||
|         if ($name === null) { | ||||
|             return true; | ||||
|         } | ||||
| 
 | ||||
|         foreach ($this->ignores as $ignore) { | ||||
|             if (strpos($name, $ignore) === 0) { | ||||
|                 return true; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return false; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| (new Preloader()) | ||||
|     ->paths(__DIR__ . '/vendor/laravel') | ||||
|     ->ignore( | ||||
|         \Illuminate\Filesystem\Cache::class, | ||||
|         \Illuminate\Log\LogManager::class, | ||||
|         \Illuminate\Http\Testing\File::class, | ||||
|         \Illuminate\Http\UploadedFile::class, | ||||
|         \Illuminate\Support\Carbon::class, | ||||
|     ) | ||||
|     ->load(); | ||||
							
								
								
									
										6
									
								
								public/flutter_service_worker.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								public/flutter_service_worker.js
									
									
									
									
										vendored
									
									
								
							| @ -4,9 +4,9 @@ const TEMP = 'flutter-temp-cache'; | ||||
| const CACHE_NAME = 'flutter-app-cache'; | ||||
| const RESOURCES = { | ||||
|   "favicon.png": "dca91c54388f52eded692718d5a98b8b", | ||||
| "main.dart.js": "5b1a19e00c074ba73b725b51f90da896", | ||||
| "main.dart.js": "da1ca4eca714583ff82c65cd3792029b", | ||||
| "favicon.ico": "51636d3a390451561744c42188ccd628", | ||||
| "/": "eb40da80095b8fd3e80ef1eeb7fb0e22", | ||||
| "/": "384495013e69b7963a1f0883fc10f045", | ||||
| "flutter.js": "f85e6fb278b0fd20c349186fb46ae36d", | ||||
| "manifest.json": "ef43d90e57aa7682d7e2cfba2f484a40", | ||||
| "canvaskit/canvaskit.js": "2bc454a691c631b07a9307ac4ca47797", | ||||
| @ -299,7 +299,7 @@ const RESOURCES = { | ||||
| "assets/assets/google_fonts/Roboto-Regular.ttf": "8a36205bd9b83e03af0591a004bc97f4", | ||||
| "assets/fonts/MaterialIcons-Regular.otf": "95db9098c58fd6db106f1116bae85a0b", | ||||
| "assets/NOTICES": "1a34e70168d56fad075adfb4bdbb20eb", | ||||
| "version.json": "674c2b7791134c91ed5f7bc241b11d8d", | ||||
| "version.json": "ecddd9a09423be80c670ca3af19cf971", | ||||
| "icons/Icon-512.png": "0f9aff01367f0a0c69773d25ca16ef35", | ||||
| "icons/Icon-192.png": "bb1cf5f6982006952211c7c8404ffbed" | ||||
| }; | ||||
|  | ||||
							
								
								
									
										315425
									
								
								public/main.dart.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										315425
									
								
								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
											
										
									
								
							
							
								
								
									
										266517
									
								
								public/main.foss.dart.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										266517
									
								
								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
											
										
									
								
							
							
								
								
									
										22096
									
								
								public/main.profile.dart.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										22096
									
								
								public/main.profile.dart.js
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| @ -1 +1 @@ | ||||
| {"app_name":"invoiceninja_flutter","version":"5.0.95","build_number":"95","package_name":"invoiceninja_flutter"} | ||||
| {"app_name":"invoiceninja_flutter","version":"5.0.99","build_number":"99","package_name":"invoiceninja_flutter"} | ||||
| @ -77,11 +77,6 @@ | ||||
|                                 {{ ctrans('texts.name') }} | ||||
|                             </span> | ||||
|                         </th> | ||||
|                         <th class="px-6 py-3 border-b border-gray-200 bg-primary text-left text-xs leading-4 font-medium text-white uppercase tracking-wider"> | ||||
|                             <span role="button" wire:click="sortBy('type')" class="cursor-pointer"> | ||||
|                                 {{ ctrans('texts.type') }} | ||||
|                             </span> | ||||
|                         </th> | ||||
|                         <th class="px-6 py-3 border-b border-gray-200 bg-primary text-left text-xs leading-4 font-medium text-white uppercase tracking-wider"> | ||||
|                             <span role="button" wire:click="sortBy('size')" class="cursor-pointer"> | ||||
|                                 {{ ctrans('texts.size') }} | ||||
| @ -102,9 +97,7 @@ | ||||
|                             <td class="px-6 py-4 whitespace-nowrap text-sm leading-5 text-gray-500"> | ||||
|                                 {{ Illuminate\Support\Str::limit($document->name, 20) }} | ||||
|                             </td> | ||||
|                             <td class="px-6 py-4 whitespace-nowrap text-sm leading-5 text-gray-500"> | ||||
|                                 {{ App\Models\Document::$types[$document->type]['mime'] }} | ||||
|                             </td> | ||||
| 
 | ||||
|                             <td class="px-6 py-4 whitespace-nowrap text-sm leading-5 text-gray-500"> | ||||
|                                 {{ $document->size / 1000 }} kB | ||||
|                             </td> | ||||
|  | ||||
| @ -24,14 +24,6 @@ | ||||
|                                 {{ Illuminate\Support\Str::limit($document->name, 40) }} | ||||
|                             </dd> | ||||
|                         </div> | ||||
|                         <div class="bg-white px-4 py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6"> | ||||
|                             <dt class="text-sm leading-5 font-medium text-gray-500"> | ||||
|                                 {{ ctrans('texts.type') }} | ||||
|                             </dt> | ||||
|                             <dd class="mt-1 text-sm leading-5 text-gray-900 sm:mt-0 sm:col-span-2"> | ||||
|                                 {{ App\Models\Document::$types[$document->type]['mime'] }} | ||||
|                             </dd> | ||||
|                         </div> | ||||
|                         <div class="bg-gray-50 px-4 py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6"> | ||||
|                             <dt class="text-sm leading-5 font-medium text-gray-500"> | ||||
|                                 {{ ctrans('texts.hash') }} | ||||
|  | ||||
| @ -43,7 +43,7 @@ | ||||
| @endsection | ||||
| 
 | ||||
| @section('gateway_footer') | ||||
|     @if($gateway->forte->company_gateway->getConfigField('testMode')) | ||||
|     @if($gateway->company_gateway->getConfigField('testMode')) | ||||
|         <script type="text/javascript" src="https://sandbox.forte.net/api/js/v1"></script> | ||||
|     @else | ||||
|         <script type="text/javascript" src="https://api.forte.net/js/v1"></script> | ||||
|  | ||||
| @ -12,6 +12,7 @@ | ||||
| namespace Tests\Feature; | ||||
| 
 | ||||
| use App\Models\Country; | ||||
| use App\Utils\Number; | ||||
| use App\Utils\Traits\MakesHash; | ||||
| use Illuminate\Database\Eloquent\Model; | ||||
| use Illuminate\Foundation\Testing\DatabaseTransactions; | ||||
| @ -401,4 +402,89 @@ class ClientApiTest extends TestCase | ||||
| 
 | ||||
|         $response->assertStatus(302); | ||||
|     } | ||||
| 
 | ||||
|     public function testRoundingDecimalsTwo() | ||||
|     { | ||||
|         $currency = $this->company; | ||||
| 
 | ||||
|         $x = Number::formatValueNoTrailingZeroes(0.05, $currency); | ||||
| 
 | ||||
|         $this->assertEquals(0.05, $x); | ||||
|     } | ||||
| 
 | ||||
|     public function testRoundingDecimalsThree() | ||||
|     { | ||||
|         $currency = $this->company; | ||||
| 
 | ||||
|         $x = Number::formatValueNoTrailingZeroes(0.005, $currency); | ||||
| 
 | ||||
|         $this->assertEquals(0.005, $x); | ||||
|     } | ||||
| 
 | ||||
|     public function testRoundingDecimalsFour() | ||||
|     { | ||||
|         $currency = $this->company; | ||||
| 
 | ||||
|         $x = Number::formatValueNoTrailingZeroes(0.0005, $currency); | ||||
| 
 | ||||
|         $this->assertEquals(0.0005, $x); | ||||
|     } | ||||
| 
 | ||||
|     public function testRoundingDecimalsFive() | ||||
|     { | ||||
|         $currency = $this->company; | ||||
| 
 | ||||
|         $x = Number::formatValueNoTrailingZeroes(0.00005, $currency); | ||||
| 
 | ||||
|         $this->assertEquals(0.00005, $x); | ||||
|     } | ||||
| 
 | ||||
|     public function testRoundingDecimalsSix() | ||||
|     { | ||||
|         $currency = $this->company; | ||||
| 
 | ||||
|         $x = Number::formatValueNoTrailingZeroes(0.000005, $currency); | ||||
| 
 | ||||
|         $this->assertEquals(0.000005, $x); | ||||
|     } | ||||
| 
 | ||||
|     public function testRoundingDecimalsSeven() | ||||
|     { | ||||
|         $currency = $this->company; | ||||
| 
 | ||||
|         $x = Number::formatValueNoTrailingZeroes(0.0000005, $currency); | ||||
| 
 | ||||
|         $this->assertEquals(0.0000005, $x); | ||||
|     } | ||||
| 
 | ||||
|     public function testRoundingDecimalsEight() | ||||
|     { | ||||
|         $currency = $this->company; | ||||
| 
 | ||||
|         $x = Number::formatValueNoTrailingZeroes(0.00000005, $currency); | ||||
| 
 | ||||
|         $this->assertEquals(0.00000005, $x); | ||||
|     } | ||||
| 
 | ||||
|     public function testRoundingPositive() | ||||
|     { | ||||
|         $currency = $this->company; | ||||
| 
 | ||||
|         $x = Number::formatValueNoTrailingZeroes(1.5, $currency); | ||||
|         $this->assertEquals(1.5, $x); | ||||
| 
 | ||||
|         $x = Number::formatValueNoTrailingZeroes(1.50, $currency); | ||||
|         $this->assertEquals(1.5, $x); | ||||
| 
 | ||||
|         $x = Number::formatValueNoTrailingZeroes(1.500, $currency); | ||||
|         $this->assertEquals(1.5, $x); | ||||
| 
 | ||||
|         $x = Number::formatValueNoTrailingZeroes(1.50005, $currency); | ||||
|         $this->assertEquals(1.50005, $x); | ||||
| 
 | ||||
|         $x = Number::formatValueNoTrailingZeroes(1.50000005, $currency); | ||||
|         $this->assertEquals(1.50000005, $x); | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
| } | ||||
|  | ||||
| @ -166,11 +166,12 @@ class RecurringInvoiceTest extends TestCase | ||||
|                 'company_id' => $this->company->id, | ||||
|             ]); | ||||
|         }); | ||||
|         $client = Client::all()->first(); | ||||
| 
 | ||||
|         $client = Client::query()->orderBy('id', 'DESC')->first(); | ||||
| 
 | ||||
|         RecurringInvoice::factory()->create(['user_id' => $this->user->id, 'company_id' => $this->company->id, 'client_id' => $this->client->id]); | ||||
| 
 | ||||
|         $RecurringInvoice = RecurringInvoice::where('user_id', $this->user->id)->first(); | ||||
|         $RecurringInvoice = RecurringInvoice::query()->where('user_id', $this->user->id)->orderBy('id', 'DESC')->first(); | ||||
|         $RecurringInvoice->save(); | ||||
| 
 | ||||
|         $response = $this->withHeaders([ | ||||
|  | ||||
| @ -63,7 +63,7 @@ class RecurringQuoteTest extends TestCase | ||||
|     { | ||||
|         RecurringQuote::factory()->create(['user_id' => $this->user->id, 'company_id' => $this->company->id, 'client_id' => $this->client->id]); | ||||
| 
 | ||||
|         $RecurringQuote = RecurringQuote::where('user_id', $this->user->id)->first(); | ||||
|         $RecurringQuote = RecurringQuote::query()->where('user_id', $this->user->id)->orderBy('id','DESC')->first(); | ||||
|         $RecurringQuote->save(); | ||||
| 
 | ||||
|         $response = $this->withHeaders([ | ||||
|  | ||||
| @ -58,37 +58,5 @@ class DownloadHistoricalInvoiceTest extends TestCase | ||||
|         $this->assertNotNull($this->invoice->activities); | ||||
|     } | ||||
| 
 | ||||
|     public function testBackupExists() | ||||
|     { | ||||
|         $this->mockActivity(); | ||||
| 
 | ||||
|         $this->assertNotNull($this->invoice->activities->first()->backup->html_backup); | ||||
|     } | ||||
| 
 | ||||
|     public function testBackupDownload() | ||||
|     { | ||||
|         $this->mockActivity(); | ||||
| 
 | ||||
|         $response = $this->withHeaders([ | ||||
|             'X-API-SECRET' => config('ninja.api_secret'), | ||||
|             'X-API-TOKEN' => $this->token, | ||||
|         ])->get('/api/v1/activities/download_entity/'.$this->encodePrimaryKey($this->invoice->activities->first()->id)); | ||||
| 
 | ||||
|         $response->assertStatus(200); | ||||
|     } | ||||
| 
 | ||||
|     public function testBackupCheckPriorToDownloadWorks() | ||||
|     { | ||||
|         $this->mockActivity(); | ||||
| 
 | ||||
|         $backup = $this->invoice->activities->first()->backup; | ||||
|         $backup->forceDelete(); | ||||
| 
 | ||||
|         $response = $this->withHeaders([ | ||||
|             'X-API-SECRET' => config('ninja.api_secret'), | ||||
|             'X-API-TOKEN' => $this->token, | ||||
|         ])->get('/api/v1/activities/download_entity/'.$this->encodePrimaryKey($this->invoice->activities->first()->id)); | ||||
| 
 | ||||
|         $response->assertStatus(404); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -89,18 +89,6 @@ class NumberTest extends TestCase | ||||
|         $this->assertEquals(2.15, $rounded); | ||||
|     } | ||||
| 
 | ||||
|     //this method proved an error! removing this method from production
 | ||||
|     // public function testImportFloatConversion()
 | ||||
|     // {
 | ||||
| 
 | ||||
|     //     $amount = '€7,99';
 | ||||
| 
 | ||||
|     //     $converted_amount = Number::parseStringFloat($amount);
 | ||||
| 
 | ||||
|     //     $this->assertEquals(799, $converted_amount);
 | ||||
| 
 | ||||
|     // }
 | ||||
| 
 | ||||
|     public function testParsingStringCurrency() | ||||
|     { | ||||
|         $amount = '€7,99'; | ||||
| @ -110,86 +98,4 @@ class NumberTest extends TestCase | ||||
|         $this->assertEquals(7.99, $converted_amount); | ||||
|     } | ||||
| 
 | ||||
|     public function testRoundingDecimalsTwo() | ||||
|     { | ||||
|         $currency = Currency::find(1); | ||||
| 
 | ||||
|         $x = Number::formatValueNoTrailingZeroes(0.05, $currency); | ||||
| 
 | ||||
|         $this->assertEquals(0.05, $x); | ||||
|     } | ||||
| 
 | ||||
|     public function testRoundingDecimalsThree() | ||||
|     { | ||||
|         $currency = Currency::find(1); | ||||
| 
 | ||||
|         $x = Number::formatValueNoTrailingZeroes(0.005, $currency); | ||||
| 
 | ||||
|         $this->assertEquals(0.005, $x); | ||||
|     } | ||||
| 
 | ||||
|     public function testRoundingDecimalsFour() | ||||
|     { | ||||
|         $currency = Currency::find(1); | ||||
| 
 | ||||
|         $x = Number::formatValueNoTrailingZeroes(0.0005, $currency); | ||||
| 
 | ||||
|         $this->assertEquals(0.0005, $x); | ||||
|     } | ||||
| 
 | ||||
|     public function testRoundingDecimalsFive() | ||||
|     { | ||||
|         $currency = Currency::find(1); | ||||
| 
 | ||||
|         $x = Number::formatValueNoTrailingZeroes(0.00005, $currency); | ||||
| 
 | ||||
|         $this->assertEquals(0.00005, $x); | ||||
|     } | ||||
| 
 | ||||
|     public function testRoundingDecimalsSix() | ||||
|     { | ||||
|         $currency = Currency::find(1); | ||||
| 
 | ||||
|         $x = Number::formatValueNoTrailingZeroes(0.000005, $currency); | ||||
| 
 | ||||
|         $this->assertEquals(0.000005, $x); | ||||
|     } | ||||
| 
 | ||||
|     public function testRoundingDecimalsSeven() | ||||
|     { | ||||
|         $currency = Currency::find(1); | ||||
| 
 | ||||
|         $x = Number::formatValueNoTrailingZeroes(0.0000005, $currency); | ||||
| 
 | ||||
|         $this->assertEquals(0.0000005, $x); | ||||
|     } | ||||
| 
 | ||||
|     public function testRoundingDecimalsEight() | ||||
|     { | ||||
|         $currency = Currency::find(1); | ||||
| 
 | ||||
|         $x = Number::formatValueNoTrailingZeroes(0.00000005, $currency); | ||||
| 
 | ||||
|         $this->assertEquals(0.00000005, $x); | ||||
|     } | ||||
| 
 | ||||
|     public function testRoundingPositive() | ||||
|     { | ||||
|         $currency = Currency::find(1); | ||||
| 
 | ||||
|         $x = Number::formatValueNoTrailingZeroes(1.5, $currency); | ||||
|         $this->assertEquals(1.5, $x); | ||||
| 
 | ||||
|         $x = Number::formatValueNoTrailingZeroes(1.50, $currency); | ||||
|         $this->assertEquals(1.5, $x); | ||||
| 
 | ||||
|         $x = Number::formatValueNoTrailingZeroes(1.500, $currency); | ||||
|         $this->assertEquals(1.5, $x); | ||||
| 
 | ||||
|         $x = Number::formatValueNoTrailingZeroes(1.50005, $currency); | ||||
|         $this->assertEquals(1.50005, $x); | ||||
| 
 | ||||
|         $x = Number::formatValueNoTrailingZeroes(1.50000005, $currency); | ||||
|         $this->assertEquals(1.50000005, $x); | ||||
|     } | ||||
| } | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user