Merge pull request #7374 from turbo124/v5-develop

v5.3.80
This commit is contained in:
David Bomba 2022-04-19 16:06:08 +09:30 committed by GitHub
commit abeac50aba
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
35 changed files with 659 additions and 79 deletions

View File

@ -35,7 +35,6 @@ jobs:
php artisan key:generate
php artisan optimize
php artisan storage:link
php artisan livewire:publish
sudo php artisan cache:clear
sudo find ./vendor/bin/ -type f -exec chmod +x {} \;
sudo find ./ -type d -exec chmod 755 {} \;

View File

@ -1 +1 @@
5.3.79
5.3.80

View File

@ -37,6 +37,15 @@ class ClientSettings extends BaseSettings
'size_id' => 'string',
];
public static $property_casts = [
'language_id' => 'string',
'currency_id' => 'string',
'payment_terms' => 'string',
'valid_until' => 'string',
'default_task_rate' => 'float',
'send_reminders' => 'bool',
];
/**
* Cast object values and return entire class
* prevents missing properties from not being returned

View File

@ -503,6 +503,7 @@ class CompanySettings extends BaseSettings
'language_id' => 'string',
'show_currency_code' => 'bool',
'website' => 'string',
'default_task_rate' => 'float',
];
/**

View File

@ -28,7 +28,11 @@ class CloneQuoteToInvoiceFactory
unset($quote_array['invoice_id']);
unset($quote_array['id']);
unset($quote_array['invitations']);
unset($quote_array['terms']);
//preserve terms if they exist on Quotes
if(array_key_exists('terms', $quote_array) && strlen($quote_array['terms']) < 2)
unset($quote_array['terms']);
// unset($quote_array['public_notes']);
unset($quote_array['footer']);
unset($quote_array['design_id']);

View File

@ -24,7 +24,6 @@ class CompanyGatewayFactory
$company_gateway->require_billing_address = false;
$company_gateway->require_shipping_address = false;
$company_gateway->config = encrypt(json_encode(new \stdClass));
// $company_gateway->fees_and_limits = new FeesAndLimits;
return $company_gateway;
}

View File

@ -48,6 +48,16 @@ class RecurringInvoiceToInvoiceFactory
$invoice->custom_value4 = $recurring_invoice->custom_value4;
$invoice->amount = $recurring_invoice->amount;
$invoice->uses_inclusive_taxes = $recurring_invoice->uses_inclusive_taxes;
$invoice->custom_surcharge1 = $recurring_invoice->custom_surcharge1;
$invoice->custom_surcharge2 = $recurring_invoice->custom_surcharge2;
$invoice->custom_surcharge3 = $recurring_invoice->custom_surcharge3;
$invoice->custom_surcharge4 = $recurring_invoice->custom_surcharge4;
$invoice->custom_surcharge_tax1 = $recurring_invoice->custom_surcharge_tax1;
$invoice->custom_surcharge_tax2 = $recurring_invoice->custom_surcharge_tax2;
$invoice->custom_surcharge_tax3 = $recurring_invoice->custom_surcharge_tax3;
$invoice->custom_surcharge_tax4 = $recurring_invoice->custom_surcharge_tax4;
// $invoice->balance = $recurring_invoice->balance;
$invoice->user_id = $recurring_invoice->user_id;
$invoice->assigned_user_id = $recurring_invoice->assigned_user_id;

View File

@ -168,7 +168,7 @@ abstract class QueryFilters
public function created_at($value)
{
$created_at = $value ? $value : 0;
$created_at = $value ? (int)$value : 0;
$created_at = date('Y-m-d H:i:s', $value);

View File

@ -218,7 +218,7 @@ class BaseController extends Controller
$query->with(
[
'company' => function ($query) use ($updated_at, $user) {
$query->whereNotNull('updated_at')->with('documents')->with('users');
$query->whereNotNull('updated_at')->with('documents','users');
},
'company.clients' => function ($query) use ($updated_at, $user) {
$query->where('clients.updated_at', '>=', $updated_at)->with('contacts.company', 'gateway_tokens', 'documents');
@ -392,7 +392,7 @@ class BaseController extends Controller
$query->with(
[
'company' => function ($query) use ($created_at, $user) {
$query->whereNotNull('created_at')->with('documents');
$query->whereNotNull('created_at')->with('documents','users');
},
'company.designs'=> function ($query) use ($created_at, $user) {
$query->where('created_at', '>=', $created_at)->with('company');
@ -466,7 +466,7 @@ class BaseController extends Controller
$query->with(
[
'company' => function ($query) use ($created_at, $user) {
$query->whereNotNull('created_at')->with('documents');
$query->whereNotNull('created_at')->with('documents','users');
},
'company.clients' => function ($query) use ($created_at, $user) {
$query->where('clients.created_at', '>=', $created_at)->with('contacts.company', 'gateway_tokens', 'documents');
@ -500,9 +500,6 @@ class BaseController extends Controller
},
'company.groups' => function ($query) use ($created_at, $user) {
$query->where('created_at', '>=', $created_at)->with('documents');
// if(!$user->isAdmin())
// $query->where('group_settings.user_id', $user->id);
},
'company.invoices'=> function ($query) use ($created_at, $user) {
$query->where('created_at', '>=', $created_at)->with('invitations', 'documents');
@ -583,13 +580,30 @@ class BaseController extends Controller
$query->where('activities.user_id', $user->id);
},
'company.webhooks'=> function ($query) use($user) {
if(!$user->isAdmin())
$query->where('webhooks.user_id', $user->id);
},
'company.tokens'=> function ($query) use($user) {
$query->where('company_tokens.user_id', $user->id);
},
'company.system_logs',
'company.subscriptions'=> function ($query) use($created_at, $user) {
$query->where('created_at', '>=', $created_at);
if(!$user->isAdmin())
$query->where('subscriptions.user_id', $user->id);
}
},
'company.recurring_expenses'=> function ($query) use ($created_at, $user) {
$query->where('created_at', '>=', $created_at)->with('documents');
if(!$user->hasPermission('view_recurring_expense'))
$query->where('recurring_expenses.user_id', $user->id)->orWhere('recurring_expenses.assigned_user_id', $user->id);
},
]
);

View File

@ -21,6 +21,15 @@ class SelfUpdateController extends BaseController
{
use DispatchesJobs;
private array $purge_file_list = [
'bootstrap/cache/compiled.php',
'bootstrap/cache/config.php',
'bootstrap/cache/packages.php',
'bootstrap/cache/services.php',
'bootstrap/cache/routes-v7.php',
'bootstrap/cache/livewire-components.php',
];
public function __construct()
{
}
@ -117,10 +126,12 @@ class SelfUpdateController extends BaseController
unlink($file);
$cacheCompiled = base_path('bootstrap/cache/compiled.php');
if (file_exists($cacheCompiled)) { unlink ($cacheCompiled); }
$cacheServices = base_path('bootstrap/cache/services.php');
if (file_exists($cacheServices)) { unlink ($cacheServices); }
foreach($this->purge_file_list as $purge_file_path)
{
$purge_file = base_path($purge_file_path);
if (file_exists($purge_file)) { unlink ($purge_file); }
}
Artisan::call('clear-compiled');
Artisan::call('route:clear');

View File

@ -36,7 +36,7 @@ class StoreClientRequest extends Request
}
public function rules()
{
{nlog($this->input);
if ($this->input('documents') && is_array($this->input('documents'))) {
$documents = count($this->input('documents'));
@ -95,6 +95,10 @@ class StoreClientRequest extends Request
if (array_key_exists('settings', $input) && ! empty($input['settings'])) {
foreach ($input['settings'] as $key => $value) {
if($key == 'default_task_rate')
$value = floatval($value);
$settings->{$key} = $value;
}
}

View File

@ -157,6 +157,10 @@ class UpdateClientRequest extends Request
if (! array_key_exists($key, $saveable_casts)) {
unset($settings->{$key});
}
if($key == 'default_task_rate'){
$settings->default_task_rate = floatval($value);
}
}
return $settings;

View File

@ -140,7 +140,7 @@ class CompanyImport implements ShouldQueue
'expenses',
'tasks',
'payments',
'activities',
// 'activities',
// 'backups',
'company_ledger',
'designs',

View File

@ -84,7 +84,7 @@ class ReminderJob implements ShouldQueue
//check if this reminder needs to be emailed
//15-01-2022 - insert addition if block if send_reminders is definitely set
if(in_array($reminder_template, ['reminder1','reminder2','reminder3','reminder_endless']) && $invoice->client->getSetting("enable_".$reminder_template) && $invoice->client->getSetting("send_reminders"))
if(in_array($reminder_template, ['reminder1','reminder2','reminder3','reminder_endless']) && $invoice->client->getSetting("enable_".$reminder_template) && $invoice->client->getSetting("send_reminders") && $invoice->company->account->isPaidHostedClient())
{
$invoice->invitations->each(function ($invitation) use ($invoice, $reminder_template) {
EmailEntity::dispatch($invitation, $invitation->company, $reminder_template);

View File

@ -190,7 +190,7 @@ class BaseModel extends Model
public function numberFormatter()
{
$number = strlen($this->number) >= 1 ? $this->number : class_basename($this) . "_" . Str::random(5); ;
$number = strlen($this->number) >= 1 ? $this->number : class_basename($this) . "_" . Str::random(5);
$formatted_number = mb_ereg_replace("([^\w\s\d\-_~,;\[\]\(\).])", '', $number);
// Remove any runs of periods (thanks falstro!)

View File

@ -52,6 +52,8 @@ class CompanyUser extends Pivot
protected $touches = ['user'];
protected $with = ['user','account'];
public function getEntityType()
{
return self::class;

View File

@ -42,7 +42,10 @@ class Webhook extends BaseModel
const EVENT_LATE_INVOICE = 22;
const EVENT_EXPIRED_QUOTE = 23;
const EVENT_REMIND_INVOICE = 24;
const EVENT_PROJECT_CREATE = 25;
const EVENT_PROJECT_UPDATE = 26;
public static $valid_events = [
self::EVENT_CREATE_CLIENT,
self::EVENT_CREATE_INVOICE,
@ -68,6 +71,8 @@ class Webhook extends BaseModel
self::EVENT_LATE_INVOICE,
self::EVENT_EXPIRED_QUOTE,
self::EVENT_REMIND_INVOICE,
self::EVENT_PROJECT_CREATE,
self::EVENT_PROJECT_UPDATE
];
protected $fillable = [

View File

@ -0,0 +1,89 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Observers;
use App\Jobs\Util\WebhookHandler;
use App\Models\Project;
use App\Models\Webhook;
class ProjectObserver
{
public $afterCommit = true;
/**
* Handle the product "created" event.
*
* @param Project $project
* @return void
*/
public function created(Project $project)
{
$subscriptions = Webhook::where('company_id', $project->company_id)
->where('event_id', Webhook::EVENT_PROJECT_CREATE)
->exists();
if ($subscriptions) {
WebhookHandler::dispatch(Webhook::EVENT_PROJECT_CREATE, $project, $project->company, 'client');
}
}
/**
* Handle the product "updated" event.
*
* @param Project $project
* @return void
*/
public function updated(Project $project)
{
$subscriptions = Webhook::where('company_id', $project->company_id)
->where('event_id', Webhook::EVENT_PROJECT_UPDATE)
->exists();
if ($subscriptions) {
WebhookHandler::dispatch(Webhook::EVENT_PROJECT_UPDATE, $project, $project->company, 'client');
}
}
/**
* Handle the product "deleted" event.
*
* @param Project $project
* @return void
*/
public function deleted(Project $project)
{
//
}
/**
* Handle the product "restored" event.
*
* @param Project $project
* @return void
*/
public function restored(Project $project)
{
//
}
/**
* Handle the product "force deleted" event.
*
* @param Project $project
* @return void
*/
public function forceDeleted(Project $project)
{
//
}
}

View File

@ -164,13 +164,13 @@ class AuthorizePaymentMethod
if ($contact) {
// Create the Bill To info for new payment type
$billto = new CustomerAddressType();
$billto->setFirstName($contact->present()->first_name());
$billto->setLastName($contact->present()->last_name());
$billto->setCompany($this->authorize->client->present()->name());
$billto->setAddress($this->authorize->client->address1);
$billto->setCity($this->authorize->client->city);
$billto->setState($this->authorize->client->state);
$billto->setZip($this->authorize->client->postal_code);
$billto->setFirstName(substr(0,50,$contact->present()->first_name()));
$billto->setLastName(substr(0,50,$contact->present()->last_name()));
$billto->setCompany(substr(0,50,$this->authorize->client->present()->name()));
$billto->setAddress(substr(0,60,$this->authorize->client->address1));
$billto->setCity(substr(0,40,$this->authorize->client->city));
$billto->setState(substr(0,40,$this->authorize->client->state));
$billto->setZip(substr(0,20,$this->authorize->client->postal_code));
if ($this->authorize->client->country_id) {
$billto->setCountry($this->authorize->client->country->name);

View File

@ -170,16 +170,52 @@ class CreditCard
$this->logResponse($response);
$response_status = ErrorCode::getStatus($response->ResponseMessage);
// if(!$response || !property_exists($response, 'ResponseMessage'))
// throw new PaymentFailed('The gateway did not return a valid response. Please check your gateway credentials.', 400);
if(!$response_status['success']){
// $response_status = ErrorCode::getStatus($response->ResponseMessage);
$this->eway_driver->sendFailureMail($response_status['message']);
// if(!$response_status['success']){
throw new PaymentFailed($response_status['message'], 400);
}
// if($response->getErrors())
// {
// $message = false;
// foreach ($response->getErrors() as $error) {
// $message = \Eway\Rapid::getMessage($error);
// }
// $return_message = $message ?: $response_status['message'];
// }
// $this->eway_driver->sendFailureMail($response_status['message']);
// throw new PaymentFailed($response_status['message'], 400);
// }
if($response->TransactionStatus)
$payment = $this->storePayment($response);
else {
$message = 'Error processing payment.';
if(isset($response->ResponseMessage))
$message .= " Gateway Error Code = {$response->ResponseMessage}";
if($response->getErrors())
{
foreach ($response->getErrors() as $error) {
$message = \Eway\Rapid::getMessage($error);
}
}
$this->eway_driver->sendFailureMail($message);
throw new PaymentFailed($message, 400);
}
$payment = $this->storePayment($response);
return redirect()->route('client.payments.show', ['payment' => $this->encodePrimaryKey($payment->id)]);
@ -257,20 +293,32 @@ class CreditCard
$response = $this->eway_driver->init()->eway->createTransaction(\Eway\Rapid\Enum\ApiMethod::DIRECT, $transaction);
$response_status = ErrorCode::getStatus($response->ResponseMessage);
if($response->TransactionStatus){
$this->logResponse($response, true);
$payment = $this->storePayment($response);
}
else {
if(!$response_status['success']){
$message = 'Error processing payment.';
$this->logResponse($response, false);
if(isset($response->ResponseMessage))
$message .= " Gateway Error Code = {$response->ResponseMessage}";
$this->eway_driver->sendFailureMail($response_status['message']);
if($response->getErrors())
{
foreach ($response->getErrors() as $error) {
$message = \Eway\Rapid::getMessage($error);
}
throw new PaymentFailed($response_status['message'], 400);
}
}
$this->logResponse($response, true);
$this->logResponse($response, false);
$payment = $this->storePayment($response);
$this->eway_driver->sendFailureMail($message);
throw new PaymentFailed($message, 400);
}
return $payment;
}

View File

@ -32,6 +32,8 @@ class PayPalExpressPaymentDriver extends BaseDriver
private $omnipay_gateway;
private float $fee = 0;
const SYSTEM_LOG_TYPE = SystemLog::TYPE_PAYPAL;
public function gatewayTypes()
@ -173,11 +175,17 @@ class PayPalExpressPaymentDriver extends BaseDriver
public function generatePaymentDetails(array $data)
{
$_invoice = collect($this->payment_hash->data->invoices)->first();
$invoice = Invoice::withTrashed()->find($this->decodePrimaryKey($_invoice->invoice_id));
$this->fee = $this->feeCalc($invoice, $data['total']['amount_with_fee']);
return [
'currency' => $this->client->getCurrencyCode(),
'transactionType' => 'Purchase',
'clientIp' => request()->getClientIp(),
'amount' => $data['total']['amount_with_fee'],
'amount' => $data['total']['amount_with_fee'] + $this->fee,
'returnUrl' => route('client.payments.response', [
'company_gateway_id' => $this->company_gateway->id,
'payment_hash' => $this->payment_hash->hash,
@ -200,8 +208,6 @@ class PayPalExpressPaymentDriver extends BaseDriver
$_invoice = collect($this->payment_hash->data->invoices)->first();
$invoice = Invoice::withTrashed()->find($this->decodePrimaryKey($_invoice->invoice_id));
$line_item = collect($invoice->line_items)->first();
$items = [];
$items[] = new Item([
@ -211,8 +217,44 @@ class PayPalExpressPaymentDriver extends BaseDriver
'quantity' => 1,
]);
if($this->fee > 0.1){
$items[] = new Item([
'name' => " ",
'description' => ctrans('texts.gateway_fee_description'),
'price' => $this->fee,
'quantity' => 1,
]);
}
return $items;
}
private function feeCalc($invoice, $invoice_total)
{
$invoice->service()->removeUnpaidGatewayFees();
$invoice = $invoice->fresh();
$balance = floatval($invoice->balance);
$_updated_invoice = $invoice->service()->addGatewayFee($this->company_gateway, GatewayType::PAYPAL, $invoice_total)->save();
if(floatval($_updated_invoice->balance) > $balance){
$fee = floatval($_updated_invoice->balance) - $balance;
$this->payment_hash->fee_total = $fee;
$this->payment_hash->save();
return $fee;
}
return 0;
}
}

View File

@ -25,6 +25,7 @@ use Illuminate\Support\Facades\Queue;
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\ServiceProvider;
use Illuminate\Database\Eloquent\Model;
use Livewire\Livewire;
class AppServiceProvider extends ServiceProvider
@ -69,6 +70,11 @@ class AppServiceProvider extends ServiceProvider
app()->instance(TruthSource::class, new TruthSource());
// Model::preventLazyLoading(
// !$this->app->isProduction()
// );
}
/**

View File

@ -213,6 +213,7 @@ use App\Models\Expense;
use App\Models\Invoice;
use App\Models\Payment;
use App\Models\Product;
use App\Models\Project;
use App\Models\Proposal;
use App\Models\Quote;
use App\Models\Subscription;
@ -228,6 +229,7 @@ use App\Observers\ExpenseObserver;
use App\Observers\InvoiceObserver;
use App\Observers\PaymentObserver;
use App\Observers\ProductObserver;
use App\Observers\ProjectObserver;
use App\Observers\ProposalObserver;
use App\Observers\QuoteObserver;
use App\Observers\SubscriptionObserver;
@ -586,6 +588,7 @@ class EventServiceProvider extends ServiceProvider
Invoice::observe(InvoiceObserver::class);
Payment::observe(PaymentObserver::class);
Product::observe(ProductObserver::class);
Project::observe(ProjectObserver::class);
Proposal::observe(ProposalObserver::class);
Quote::observe(QuoteObserver::class);
Task::observe(TaskObserver::class);

View File

@ -19,20 +19,14 @@ use App\Models\Client;
use App\Models\Design;
use App\Models\Invoice;
use App\Models\Payment;
use App\Models\Product;
use App\Services\PdfMaker\Design as PdfMakerDesign;
use App\Services\PdfMaker\PdfMaker;
use App\Utils\HostedPDF\NinjaPdf;
use App\Utils\HtmlEngine;
use App\Utils\Ninja;
use App\Utils\Number;
use App\Utils\PhantomJS\Phantom;
use App\Utils\Traits\Pdf\PdfMaker as PdfMakerTrait;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\LazyCollection;
class Statement
{
@ -231,7 +225,7 @@ class Statement
->where('client_id', $this->client->id)
->whereIn('status_id', $this->invoiceStatuses())
->whereBetween('date', [Carbon::parse($this->options['start_date']), Carbon::parse($this->options['end_date'])])
->orderBy('date', 'ASC')
->orderBy('due_date', 'ASC')
->cursor();
}

View File

@ -79,7 +79,7 @@ class CompanyUserTransformer extends EntityTransformer
public function includeToken(CompanyUser $company_user)
{
$token = $company_user->tokens->where('company_id', $company_user->company_id)->where('user_id', $company_user->user_id)->first();
$token = $company_user->tokens()->where('company_id', $company_user->company_id)->where('user_id', $company_user->user_id)->first();
$transformer = new CompanyTokenTransformer($this->serializer);

View File

@ -30,7 +30,7 @@ class NinjaPdf
$response = $client->post($this->url,[
RequestOptions::JSON => ['html' => $html]
]);
return $response->getBody();
}

View File

@ -122,6 +122,9 @@ class HtmlEngine
$data['$invoice.date'] = &$data['$date'];
$data['$invoiceDate'] = &$data['$date'];
$data['$due_date'] = ['value' => $this->translateDate($this->entity->due_date, $this->client->date_format(), $this->client->locale()) ?: '&nbsp;', 'label' => ctrans('texts.'.$this->entity_string.'_due_date')];
$data['$partial_due_date'] = ['value' => $this->translateDate($this->entity->partial_due_date, $this->client->date_format(), $this->client->locale()) ?: '&nbsp;', 'label' => ctrans('texts.'.$this->entity_string.'_due_date')];
$data['$dueDate'] = &$data['$due_date'];
$data['$payment_due'] = ['value' => $this->translateDate($this->entity->due_date, $this->client->date_format(), $this->client->locale()) ?: '&nbsp;', 'label' => ctrans('texts.payment_due')];

View File

@ -11,6 +11,7 @@
namespace App\Utils\Traits;
use App\DataMapper\ClientSettings;
use App\DataMapper\CompanySettings;
use stdClass;
@ -63,15 +64,6 @@ trait ClientGroupSettingsSaver
$entity_settings->{$key} = $value;
}
//this pass will handle any null values that are in the translations
// foreach ($settings->translations as $key => $value) {
// if (is_null($settings->translations[$key])) {
// $settings->translations[$key] = '';
// }
// }
// $entity_settings->translations = $settings->translations;
$entity->settings = $entity_settings;
$entity->save();
@ -121,8 +113,12 @@ trait ClientGroupSettingsSaver
continue;
}
/*Separate loop if it is a _id field which is an integer cast as a string*/
elseif (substr($key, -3) == '_id' || substr($key, -14) == 'number_counter') {
$value = 'integer';
elseif (substr($key, -3) == '_id' ||
substr($key, -14) == 'number_counter' ||
($key == 'payment_terms' && property_exists($settings, 'payment_terms') && strlen($settings->{$key}) >= 1) ||
($key == 'valid_until' && property_exists($settings, 'valid_until') && strlen($settings->{$key}) >= 1)) {
$value = 'integer';
if (! property_exists($settings, $key)) {
continue;
@ -170,7 +166,11 @@ trait ClientGroupSettingsSaver
}
/*Separate loop if it is a _id field which is an integer cast as a string*/
if (substr($key, -3) == '_id' || substr($key, -14) == 'number_counter') {
if (substr($key, -3) == '_id' ||
substr($key, -14) == 'number_counter' ||
($key == 'payment_terms' && property_exists($settings, 'payment_terms') && strlen($settings->{$key}) >= 1) ||
($key == 'valid_until' && property_exists($settings, 'valid_until') && strlen($settings->{$key}) >= 1)) {
$value = 'integer';
if (! property_exists($settings, $key)) {
@ -219,8 +219,7 @@ trait ClientGroupSettingsSaver
switch ($key) {
case 'int':
case 'integer':
// return ctype_digit(strval(abs($value)));
return ctype_digit(strval($value));
return is_numeric($value) && ctype_digit(strval(abs($value)));
case 'real':
case 'float':
case 'double':

View File

@ -32,7 +32,7 @@ trait UserNotifies
$notifiable_methods = [];
$notifications = $company_user->notifications;
if ($company_user->company->is_disabled && is_array($notifications->email) || $company_user->trashed() || $company_user->user->trashed()) {
if ($invitation->company->is_disabled && is_array($notifications->email) || $company_user->trashed() || $company_user->user->trashed()) {
return [];
}
@ -56,7 +56,7 @@ trait UserNotifies
$notifiable_methods = [];
$notifications = $company_user->notifications;
if ($company_user->company->is_disabled || ! $notifications || $company_user->trashed() || $company_user->user->trashed()) {
if ($entity->company->is_disabled || ! $notifications || $company_user->trashed() || $company_user->user->trashed()) {
return [];
}

View File

@ -52,7 +52,7 @@ trait SettingsSaver
continue;
}
/*Separate loop if it is a _id field which is an integer cast as a string*/
elseif (substr($key, -3) == '_id' || substr($key, -14) == 'number_counter') {
elseif (substr($key, -3) == '_id' || substr($key, -14) == 'number_counter' || ($key == 'payment_terms' && strlen($settings->{$key}) >= 1) || ($key == 'valid_until' && strlen($settings->{$key}) >= 1)) {
$value = 'integer';
if($key == 'gmail_sending_user_id')
@ -94,12 +94,11 @@ trait SettingsSaver
switch ($key) {
case 'int':
case 'integer':
return ctype_digit(strval(abs($value)));
return is_numeric($value) && ctype_digit(strval(abs($value)));
case 'real':
case 'float':
case 'double':
return !is_string($value) && (is_float($value) || is_numeric(strval($value)));
// return is_float($value) || is_numeric(strval($value));
case 'string':
return !is_int($value) || ( is_string( $value ) && method_exists($value, '__toString') ) || is_null($value) || is_string($value);
case 'bool':

View File

@ -54,7 +54,7 @@ return [
|
*/
'asset_url' => null,
'asset_url' => env('ASSET_URL', null),
/*
|--------------------------------------------------------------------------

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.3.79',
'app_tag' => '5.3.79',
'app_version' => '5.3.80',
'app_tag' => '5.3.80',
'minimum_client_version' => '5.0.16',
'terms_version' => '1.0.1',
'api_secret' => env('API_SECRET', ''),

View File

@ -50,8 +50,6 @@ class CompanySettingsTest extends TestCase
$this->company->saveSettings($settings, $this->company);
//$this->withoutExceptionHandling();
$response = false;
try {

View File

@ -36,7 +36,7 @@ class UpdateCompanyUserTest extends TestCase
public function testUpdatingCompanyUserAsAdmin()
{
User::unguard();
// User::unguard();
$settings = new \stdClass;
$settings->invoice = 'ninja';
@ -59,7 +59,7 @@ class UpdateCompanyUserTest extends TestCase
$message = json_decode($e->validator->getMessageBag(), 1);
$this->assertNotNull($message);
}
$response->assertStatus(200);
$arr = $response->json();

View File

@ -0,0 +1,337 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://opensource.org/licenses/AAL
*/
namespace Tests\Unit;
use App\DataMapper\ClientSettings;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Validation\ValidationException;
use Tests\MockAccountData;
use Tests\TestCase;
/**
* @test
*/
class ClientSettingsTest extends TestCase
{
use MockAccountData;
use DatabaseTransactions;
public function setUp() :void
{
parent::setUp();
$this->makeTestData();
$this->faker = \Faker\Factory::create();
}
public function testClientBaseline()
{
$data = [
'name' => $this->faker->firstName,
'id_number' => 'Coolio',
];
$response = false;
try{
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->post('/api/v1/clients/', $data);
} catch (ValidationException $e) {
$message = json_decode($e->validator->getMessageBag(), 1);
nlog($message);
}
$response->assertStatus(200);
$arr = $response->json();
$this->assertEquals("1", $arr['data']['settings']['currency_id']);
}
public function testClientValidSettings()
{
$data = [
'name' => $this->faker->firstName,
'id_number' => 'Coolio',
'settings' => [
'currency_id' => '1',
'language_id' => '1',
'payment_terms' => '1',
'valid_until' => '1',
'default_task_rate' => 10,
'send_reminders' => true
]
];
$response = false;
try{
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->post('/api/v1/clients/', $data);
} catch (ValidationException $e) {
$message = json_decode($e->validator->getMessageBag(), 1);
nlog($message);
}
$response->assertStatus(200);
$arr = $response->json();
$this->assertEquals("1", $arr['data']['settings']['currency_id']);
$this->assertEquals("1", $arr['data']['settings']['language_id']);
$this->assertEquals("1", $arr['data']['settings']['payment_terms']);
$this->assertEquals(10, $arr['data']['settings']['default_task_rate']);
$this->assertEquals(true, $arr['data']['settings']['send_reminders']);
$this->assertEquals("1", $arr['data']['settings']['valid_until']);
}
public function testClientIllegalCurrency()
{
$data = [
'name' => $this->faker->firstName,
'id_number' => 'Coolio',
'settings' => [
'currency_id' => 'a',
'language_id' => '1',
'payment_terms' => '1',
'valid_until' => '1',
'default_task_rate' => 10,
'send_reminders' => true
]
];
$response = false;
try{
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->post('/api/v1/clients/', $data);
} catch (ValidationException $e) {
$message = json_decode($e->validator->getMessageBag(), 1);
nlog($message);
}
$response->assertStatus(302);
}
public function testClientIllegalLanguage()
{
$data = [
'name' => $this->faker->firstName,
'id_number' => 'Coolio',
'settings' => [
'currency_id' => '1',
'language_id' => 'a',
'payment_terms' => '1',
'valid_until' => '1',
'default_task_rate' => 10,
'send_reminders' => true
]
];
$response = false;
try{
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->post('/api/v1/clients/', $data);
} catch (ValidationException $e) {
$message = json_decode($e->validator->getMessageBag(), 1);
nlog($message);
}
$response->assertStatus(302);
}
public function testClientIllegalPaymenTerms()
{
$data = [
'name' => $this->faker->firstName,
'id_number' => 'Coolio',
'settings' => [
'currency_id' => '1',
'language_id' => '1',
'payment_terms' => 'a',
'valid_until' => '1',
'default_task_rate' => 10,
'send_reminders' => true
]
];
$response = false;
try{
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->post('/api/v1/clients/', $data);
} catch (ValidationException $e) {
$message = json_decode($e->validator->getMessageBag(), 1);
nlog($message);
}
$response->assertStatus(302);
}
public function testClientIllegalValidUntil()
{
$data = [
'name' => $this->faker->firstName,
'id_number' => 'Coolio',
'settings' => [
'currency_id' => '1',
'language_id' => '1',
'payment_terms' => '1',
'valid_until' => 'a',
'default_task_rate' => 10,
'send_reminders' => true
]
];
$response = false;
try{
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->post('/api/v1/clients/', $data);
} catch (ValidationException $e) {
$message = json_decode($e->validator->getMessageBag(), 1);
nlog($message);
}
$response->assertStatus(302);
}
public function testClientIllegalDefaultTaskRate()
{
$data = [
'name' => $this->faker->firstName,
'id_number' => 'Coolio',
'settings' => [
'currency_id' => '1',
'language_id' => '1',
'payment_terms' => '1',
'valid_until' => '1',
'default_task_rate' => "a",
'send_reminders' => true
]
];
$response = false;
try{
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->post('/api/v1/clients/', $data);
} catch (ValidationException $e) {
$message = json_decode($e->validator->getMessageBag(), 1);
nlog($message);
}
$response->assertStatus(200);
$arr = $response->json();
$this->assertFalse(array_key_exists('default_task_rate', $arr));
}
public function testClientIllegalSendReminderBool()
{
$data = [
'name' => $this->faker->firstName,
'id_number' => 'Coolio',
'settings' => [
'currency_id' => '1',
'language_id' => '1',
'payment_terms' => '1',
'valid_until' => '1',
'default_task_rate' => "a",
'send_reminders' => "faaalse"
]
];
$response = false;
try{
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->post('/api/v1/clients/', $data);
} catch (ValidationException $e) {
$message = json_decode($e->validator->getMessageBag(), 1);
nlog($message);
}
$response->assertStatus(302);
}
public function testClientSettingBools()
{
$data = [
'name' => $this->faker->firstName,
'id_number' => 'Coolio',
'settings' => [
'currency_id' => '1',
'language_id' => '1',
'payment_terms' => '1',
'valid_until' => '1',
'default_task_rate' => "a",
'send_reminders' => "true"
]
];
$response = false;
try{
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->post('/api/v1/clients/', $data);
} catch (ValidationException $e) {
$message = json_decode($e->validator->getMessageBag(), 1);
nlog($message);
}
$response->assertStatus(200);
}
}