Merge branch 'v5-develop' into yodlee

This commit is contained in:
David Bomba 2022-10-24 08:43:02 +11:00
commit ce47e30aa2
65 changed files with 305215 additions and 299764 deletions

View File

@ -1 +1 @@
5.5.27
5.5.32

View File

@ -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();
}

View File

@ -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,

View File

@ -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);

View File

@ -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.'%');

View File

@ -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

View File

@ -102,7 +102,7 @@ class SelfUpdateController extends BaseController
nlog('Extracting zip');
//clean up old snappdf installations
$this->cleanOldSnapChromeBinaries();
//$this->cleanOldSnapChromeBinaries();
$zipFile = new \PhpZip\ZipFile();

View File

@ -36,6 +36,7 @@ class SubdomainController extends BaseController
'lb',
'shopify',
'beta',
'prometh'
];
public function __construct()

View File

@ -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',
];
}

View File

@ -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;
}

View File

@ -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();

View File

@ -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());
}

View File

@ -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;
}

View File

@ -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) {

View File

@ -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);

View File

@ -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;
}

View File

@ -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]);
}
}

View File

@ -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()

View File

@ -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;
}
}

View File

@ -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',

View File

@ -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);

View File

@ -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) {

View File

@ -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);
}

View File

@ -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',

View 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);
}
}

View File

@ -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)) {

View File

@ -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) {

View File

@ -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,

View File

@ -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);

View File

@ -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());
}
}

View File

@ -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 = '';

View File

@ -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();
}
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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

View File

@ -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) {

View File

@ -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) {

View File

@ -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;
}
/**

View File

@ -150,7 +150,7 @@ class HtmlEngine
$data['$number'] = ['value' => $this->entity->number ?: '&nbsp;', 'label' => ctrans('texts.invoice_number')];
$data['$invoice'] = ['value' => $this->entity->number ?: '&nbsp;', 'label' => ctrans('texts.invoice_number')];
$data['$number_short'] = ['value' => $this->entity->number ?: '&nbsp;', '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) ?: '&nbsp;', '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) ?: '&nbsp;', '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) ?: '&nbsp;', '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' => ''];

View File

@ -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;

View File

@ -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;
}

View File

@ -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);

View File

@ -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'),

View File

@ -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.

View File

@ -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' => [

View File

@ -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', ''),

View File

@ -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()
{
}
};

View File

@ -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()
{
//
}
};

View File

@ -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
View 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();

View File

@ -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

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

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -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"}

View File

@ -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>

View File

@ -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') }}

View File

@ -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>

View File

@ -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);
}
}

View File

@ -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([

View File

@ -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([

View File

@ -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);
}
}

View File

@ -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);
}
}