mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-07-09 03:14:30 -04:00
commit
b8f75e4ca0
@ -1 +1 @@
|
|||||||
5.8.30
|
5.8.31
|
@ -947,7 +947,35 @@ class CheckData extends Command
|
|||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Company::whereDoesntHave('company_users', function ($query){
|
||||||
|
$query->where('is_owner', 1);
|
||||||
|
})
|
||||||
|
->cursor()
|
||||||
|
->when(Ninja::isHosted())
|
||||||
|
->each(function ($c){
|
||||||
|
|
||||||
|
$this->logMessage("Orphan Account # {$c->account_id}");
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
CompanyUser::whereDoesntHave('tokens')
|
||||||
|
->cursor()
|
||||||
|
->when(Ninja::isHosted())
|
||||||
|
->each(function ($cu){
|
||||||
|
|
||||||
|
$this->logMessage("Missing tokens for Company User # {$cu->id}");
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
CompanyUser::whereDoesntHave('user')
|
||||||
|
->cursor()
|
||||||
|
->when(Ninja::isHosted())
|
||||||
|
->each(function ($cu) {
|
||||||
|
|
||||||
|
$this->logMessage("Missing user for Company User # {$cu->id}");
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -264,7 +264,7 @@ class BaseRule implements RuleInterface
|
|||||||
return USStates::getState(strlen($this->client->postal_code) > 1 ? $this->client->postal_code : $this->client->shipping_postal_code);
|
return USStates::getState(strlen($this->client->postal_code) > 1 ? $this->client->postal_code : $this->client->shipping_postal_code);
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
return $this->client->company->country()->iso_3166_2 == 'US' ? $this->client->company->tax_data->seller_subregion : 'CA';
|
return 'CA';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,11 +29,13 @@ class DocumentFilters extends QueryFilters
|
|||||||
*/
|
*/
|
||||||
public function filter(string $filter = ''): Builder
|
public function filter(string $filter = ''): Builder
|
||||||
{
|
{
|
||||||
|
|
||||||
if (strlen($filter) == 0) {
|
if (strlen($filter) == 0) {
|
||||||
return $this->builder;
|
return $this->builder;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->builder;
|
return $this->builder->where('name', 'like', '%'.$filter.'%');
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -47,9 +49,42 @@ class DocumentFilters extends QueryFilters
|
|||||||
*/
|
*/
|
||||||
public function client_id(string $client_id = ''): Builder
|
public function client_id(string $client_id = ''): Builder
|
||||||
{
|
{
|
||||||
return $this->builder;
|
|
||||||
|
return $this->builder->where(function ($query) use ($client_id) {
|
||||||
|
$query->whereHasMorph('documentable', [
|
||||||
|
\App\Models\Invoice::class,
|
||||||
|
\App\Models\Quote::class,
|
||||||
|
\App\Models\Credit::class,
|
||||||
|
\App\Models\Expense::class,
|
||||||
|
\App\Models\Payment::class,
|
||||||
|
\App\Models\Task::class], function ($q2) use ($client_id) {
|
||||||
|
$q2->where('client_id', $this->decodePrimaryKey($client_id));
|
||||||
|
})->orWhereHasMorph('documentable', [\App\Models\Client::class], function ($q3) use ($client_id) {
|
||||||
|
$q3->where('id', $this->decodePrimaryKey($client_id));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function type(string $types = '')
|
||||||
|
{
|
||||||
|
$types = explode(',', $types);
|
||||||
|
|
||||||
|
foreach ($types as $type)
|
||||||
|
{
|
||||||
|
match($type) {
|
||||||
|
'private' => $this->builder->where('is_public', 0),
|
||||||
|
'public' => $this->builder->where('is_public', 1),
|
||||||
|
'pdf' => $this->builder->where('type', 'pdf'),
|
||||||
|
'image' => $this->builder->whereIn('type', ['png','jpeg','jpg','gif','svg']),
|
||||||
|
'other' => $this->builder->whereNotIn('type', ['pdf','png','jpeg','jpg','gif','svg']),
|
||||||
|
default => $this->builder,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->builder;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sorts the list based on $sort.
|
* Sorts the list based on $sort.
|
||||||
*
|
*
|
||||||
|
@ -277,7 +277,7 @@ class InvoiceItemSum
|
|||||||
|
|
||||||
$item_tax += $item_tax_rate1_total;
|
$item_tax += $item_tax_rate1_total;
|
||||||
|
|
||||||
if (strlen($this->item->tax_name1) > 2) {
|
if (strlen($this->item->tax_name1) > 1) {
|
||||||
$this->groupTax($this->item->tax_name1, $this->item->tax_rate1, $item_tax_rate1_total);
|
$this->groupTax($this->item->tax_name1, $this->item->tax_rate1, $item_tax_rate1_total);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -285,7 +285,7 @@ class InvoiceItemSum
|
|||||||
|
|
||||||
$item_tax += $item_tax_rate2_total;
|
$item_tax += $item_tax_rate2_total;
|
||||||
|
|
||||||
if (strlen($this->item->tax_name2) > 2) {
|
if (strlen($this->item->tax_name2) > 1) {
|
||||||
$this->groupTax($this->item->tax_name2, $this->item->tax_rate2, $item_tax_rate2_total);
|
$this->groupTax($this->item->tax_name2, $this->item->tax_rate2, $item_tax_rate2_total);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -293,7 +293,7 @@ class InvoiceItemSum
|
|||||||
|
|
||||||
$item_tax += $item_tax_rate3_total;
|
$item_tax += $item_tax_rate3_total;
|
||||||
|
|
||||||
if (strlen($this->item->tax_name3) > 2) {
|
if (strlen($this->item->tax_name3) > 1) {
|
||||||
$this->groupTax($this->item->tax_name3, $this->item->tax_rate3, $item_tax_rate3_total);
|
$this->groupTax($this->item->tax_name3, $this->item->tax_rate3, $item_tax_rate3_total);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -231,7 +231,7 @@ class InvoiceItemSumInclusive
|
|||||||
/** @var float $item_tax */
|
/** @var float $item_tax */
|
||||||
$item_tax += $this->formatValue($item_tax_rate1_total, $this->currency->precision);
|
$item_tax += $this->formatValue($item_tax_rate1_total, $this->currency->precision);
|
||||||
|
|
||||||
if (strlen($this->item->tax_name1) > 2) {
|
if (strlen($this->item->tax_name1) > 1) {
|
||||||
$this->groupTax($this->item->tax_name1, $this->item->tax_rate1, $item_tax_rate1_total);
|
$this->groupTax($this->item->tax_name1, $this->item->tax_rate1, $item_tax_rate1_total);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -239,7 +239,7 @@ class InvoiceItemSumInclusive
|
|||||||
|
|
||||||
$item_tax += $this->formatValue($item_tax_rate2_total, $this->currency->precision);
|
$item_tax += $this->formatValue($item_tax_rate2_total, $this->currency->precision);
|
||||||
|
|
||||||
if (strlen($this->item->tax_name2) > 2) {
|
if (strlen($this->item->tax_name2) > 1) {
|
||||||
$this->groupTax($this->item->tax_name2, $this->item->tax_rate2, $item_tax_rate2_total);
|
$this->groupTax($this->item->tax_name2, $this->item->tax_rate2, $item_tax_rate2_total);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -247,7 +247,7 @@ class InvoiceItemSumInclusive
|
|||||||
|
|
||||||
$item_tax += $this->formatValue($item_tax_rate3_total, $this->currency->precision);
|
$item_tax += $this->formatValue($item_tax_rate3_total, $this->currency->precision);
|
||||||
|
|
||||||
if (strlen($this->item->tax_name3) > 2) {
|
if (strlen($this->item->tax_name3) > 1) {
|
||||||
$this->groupTax($this->item->tax_name3, $this->item->tax_rate3, $item_tax_rate3_total);
|
$this->groupTax($this->item->tax_name3, $this->item->tax_rate3, $item_tax_rate3_total);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -340,8 +340,7 @@ class InvoiceSumInclusive
|
|||||||
|
|
||||||
$this->total_taxes += $total_line_tax;
|
$this->total_taxes += $total_line_tax;
|
||||||
}
|
}
|
||||||
nlog($this->tax_map);
|
|
||||||
nlog($this->total_taxes);
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,16 +30,24 @@ class SmtpController extends BaseController
|
|||||||
$user = auth()->user();
|
$user = auth()->user();
|
||||||
$company = $user->company();
|
$company = $user->company();
|
||||||
|
|
||||||
|
$smtp_host = $request->input('smtp_host', $company->smtp_host);
|
||||||
|
$smtp_port = $request->input('smtp_port', $company->smtp_port);
|
||||||
|
$smtp_username = $request->input('smtp_username', $company->smtp_username);
|
||||||
|
$smtp_password = $request->input('smtp_password', $company->smtp_password);
|
||||||
|
$smtp_encryption = $request->input('smtp_encryption', $company->smtp_encryption ?? 'tls');
|
||||||
|
$smtp_local_domain = $request->input('smtp_local_domain', strlen($company->smtp_local_domain) > 2 ? $company->smtp_local_domain : null);
|
||||||
|
$smtp_verify_peer = $request->input('verify_peer', $company->smtp_verify_peer ?? true);
|
||||||
|
|
||||||
config([
|
config([
|
||||||
'mail.mailers.smtp' => [
|
'mail.mailers.smtp' => [
|
||||||
'transport' => 'smtp',
|
'transport' => 'smtp',
|
||||||
'host' => $request->input('smtp_host', $company->smtp_host),
|
'host' => $smtp_host,
|
||||||
'port' => $request->input('smtp_port', $company->smtp_port),
|
'port' => $smtp_port,
|
||||||
'username' => $request->input('smtp_username', $company->smtp_username),
|
'username' => $smtp_username,
|
||||||
'password' => $request->input('smtp_password', $company->smtp_password),
|
'password' => $smtp_password,
|
||||||
'encryption' => $request->input('smtp_encryption', $company->smtp_encryption ?? 'tls'),
|
'encryption' => $smtp_encryption,
|
||||||
'local_domain' => $request->input('smtp_local_domain', strlen($company->smtp_local_domain) > 2 ? $company->smtp_local_domain : null),
|
'local_domain' => $smtp_local_domain,
|
||||||
'verify_peer' => $request->input('verify_peer', $company->smtp_verify_peer ?? true),
|
'verify_peer' => $smtp_verify_peer,
|
||||||
'timeout' => 5,
|
'timeout' => 5,
|
||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
|
@ -54,6 +54,8 @@ class StripeConnectController extends BaseController
|
|||||||
$redirect_uri = config('ninja.app_url').'/stripe/completed';
|
$redirect_uri = config('ninja.app_url').'/stripe/completed';
|
||||||
$endpoint = "https://connect.stripe.com/oauth/authorize?response_type=code&client_id={$stripe_client_id}&redirect_uri={$redirect_uri}&scope=read_write&state={$token}";
|
$endpoint = "https://connect.stripe.com/oauth/authorize?response_type=code&client_id={$stripe_client_id}&redirect_uri={$redirect_uri}&scope=read_write&state={$token}";
|
||||||
|
|
||||||
|
\Illuminate\Support\Facades\Cache::pull($token);
|
||||||
|
|
||||||
return redirect($endpoint);
|
return redirect($endpoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,6 +66,8 @@ class StripeConnectController extends BaseController
|
|||||||
if ($request->has('error') && $request->error == 'access_denied') {
|
if ($request->has('error') && $request->error == 'access_denied') {
|
||||||
return view('auth.connect.access_denied');
|
return view('auth.connect.access_denied');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$response = false;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
/** @class \stdClass $response
|
/** @class \stdClass $response
|
||||||
@ -88,6 +92,11 @@ class StripeConnectController extends BaseController
|
|||||||
nlog($response);
|
nlog($response);
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!$response) {
|
||||||
return view('auth.connect.access_denied');
|
return view('auth.connect.access_denied');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -144,11 +153,12 @@ class StripeConnectController extends BaseController
|
|||||||
if(isset($request->getTokenContent()['is_react']) && $request->getTokenContent()['is_react']) {
|
if(isset($request->getTokenContent()['is_react']) && $request->getTokenContent()['is_react']) {
|
||||||
$redirect_uri = config('ninja.react_url').'/#/settings/online_payments';
|
$redirect_uri = config('ninja.react_url').'/#/settings/online_payments';
|
||||||
} else {
|
} else {
|
||||||
$redirect_uri = config('ninja.app_url').'/stripe/completed';
|
$redirect_uri = config('ninja.app_url');
|
||||||
}
|
}
|
||||||
|
|
||||||
//response here
|
//response here
|
||||||
return view('auth.connect.completed', ['url' => $redirect_uri]);
|
return view('auth.connect.completed', ['url' => $redirect_uri]);
|
||||||
|
// return redirect($redirect_uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -36,18 +36,46 @@ class CheckSmtpRequest extends Request
|
|||||||
public function rules()
|
public function rules()
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
|
'smtp_host' => 'sometimes|nullable|string|min:3',
|
||||||
|
'smtp_port' => 'sometimes|nullable|integer',
|
||||||
|
'smtp_username' => 'sometimes|nullable|string|min:3',
|
||||||
|
'smtp_password' => 'sometimes|nullable|string|min:3',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function prepareForValidation()
|
public function prepareForValidation()
|
||||||
{
|
{
|
||||||
|
|
||||||
|
/** @var \App\Models\User $user */
|
||||||
|
$user = auth()->user();
|
||||||
|
$company = $user->company();
|
||||||
|
|
||||||
$input = $this->input();
|
$input = $this->input();
|
||||||
|
|
||||||
if(isset($input['smtp_username']) && $input['smtp_username'] == '********')
|
if(isset($input['smtp_username']) && $input['smtp_username'] == '********'){
|
||||||
unset($input['smtp_username']);
|
// unset($input['smtp_username']);
|
||||||
|
$input['smtp_username'] = $company->smtp_username;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(isset($input['smtp_password'])&& $input['smtp_password'] == '********'){
|
||||||
|
// unset($input['smtp_password']);
|
||||||
|
$input['smtp_password'] = $company->smtp_password;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(isset($input['smtp_host']) && strlen($input['smtp_host']) >=3){
|
||||||
|
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$input['smtp_host'] = $company->smtp_host;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if(isset($input['smtp_port']) && strlen($input['smtp_port']) >= 3) {
|
||||||
|
|
||||||
|
} else {
|
||||||
|
$input['smtp_port'] = $company->smtp_port;
|
||||||
|
}
|
||||||
|
|
||||||
if(isset($input['smtp_password'])&& $input['smtp_password'] == '********')
|
|
||||||
unset($input['smtp_password']);
|
|
||||||
|
|
||||||
$this->replace($input);
|
$this->replace($input);
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,6 @@ use App\Utils\Traits\MakesReminders;
|
|||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
use Illuminate\Queue\InteractsWithQueue;
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
use Spatie\OpenTelemetry\Jobs\TraceAware;
|
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
use Illuminate\Foundation\Bus\Dispatchable;
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
|
|
||||||
|
@ -208,6 +208,27 @@ class Document extends BaseModel
|
|||||||
return ctrans('texts.document');
|
return ctrans('texts.document');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function link()
|
||||||
|
{
|
||||||
|
$entity_id = $this->encodePrimaryKey($this->documentable_id);
|
||||||
|
$link = '';
|
||||||
|
|
||||||
|
match($this->documentable_type) {
|
||||||
|
'App\Models\Vendor' => $link = "vendors/{$entity_id}",
|
||||||
|
'App\Models\Project' => $link = "projects/{$entity_id}",
|
||||||
|
'invoices' => $link = "invoices/{$entity_id}/edit",
|
||||||
|
'App\Models\Quote' => $link = "quotes/{$entity_id}/edit",
|
||||||
|
'App\Models\Credit' => $link = "credits/{$entity_id}/edit",
|
||||||
|
'App\Models\Expense' => $link = "expenses/{$entity_id}/edit",
|
||||||
|
'App\Models\Payment' => $link = "payments/{$entity_id}/edit",
|
||||||
|
'App\Models\Task' => $link = "tasks/{$entity_id}/edit",
|
||||||
|
'App\Models\Client' => $link = "clients/{$entity_id}",
|
||||||
|
default => $link = '',
|
||||||
|
};
|
||||||
|
|
||||||
|
return $link;
|
||||||
|
}
|
||||||
|
|
||||||
public function compress(): mixed
|
public function compress(): mixed
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -52,6 +52,7 @@ class DocumentTransformer extends EntityTransformer
|
|||||||
'created_at' => (int) $document->created_at,
|
'created_at' => (int) $document->created_at,
|
||||||
'is_deleted' => (bool) false,
|
'is_deleted' => (bool) false,
|
||||||
'is_public' => (bool) $document->is_public,
|
'is_public' => (bool) $document->is_public,
|
||||||
|
'link' => (string) $document->link(),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -397,7 +397,8 @@ class HtmlEngine
|
|||||||
$data['$credit.date'] = ['value' => $this->translateDate($this->entity->date, $this->client->date_format(), $this->client->locale()), 'label' => ctrans('texts.credit_date')];
|
$data['$credit.date'] = ['value' => $this->translateDate($this->entity->date, $this->client->date_format(), $this->client->locale()), 'label' => ctrans('texts.credit_date')];
|
||||||
$data['$balance'] = ['value' => Number::formatMoney($this->getBalance(), $this->client) ?: ' ', 'label' => ctrans('texts.balance')];
|
$data['$balance'] = ['value' => Number::formatMoney($this->getBalance(), $this->client) ?: ' ', 'label' => ctrans('texts.balance')];
|
||||||
$data['$credit.balance'] = ['value' => Number::formatMoney($this->entity_calc->getBalance(), $this->client) ?: ' ', 'label' => ctrans('texts.credit_balance')];
|
$data['$credit.balance'] = ['value' => Number::formatMoney($this->entity_calc->getBalance(), $this->client) ?: ' ', 'label' => ctrans('texts.credit_balance')];
|
||||||
|
$data['$client.credit_balance'] = &$data['$credit.balance'];
|
||||||
|
|
||||||
$data['$invoice.balance'] = &$data['$balance'];
|
$data['$invoice.balance'] = &$data['$balance'];
|
||||||
$data['$taxes'] = ['value' => Number::formatMoney($this->entity_calc->getItemTotalTaxes(), $this->client) ?: ' ', 'label' => ctrans('texts.taxes')];
|
$data['$taxes'] = ['value' => Number::formatMoney($this->entity_calc->getItemTotalTaxes(), $this->client) ?: ' ', 'label' => ctrans('texts.taxes')];
|
||||||
$data['$invoice.taxes'] = &$data['$taxes'];
|
$data['$invoice.taxes'] = &$data['$taxes'];
|
||||||
|
@ -93,7 +93,7 @@ class Number
|
|||||||
* @param string $value The formatted number to be converted back to float
|
* @param string $value The formatted number to be converted back to float
|
||||||
* @return float The formatted value
|
* @return float The formatted value
|
||||||
*/
|
*/
|
||||||
public static function parseFloat($value)
|
public static function parseFloat2($value)
|
||||||
{
|
{
|
||||||
if(!$value)
|
if(!$value)
|
||||||
return 0;
|
return 0;
|
||||||
@ -104,7 +104,7 @@ class Number
|
|||||||
$decimal = strpos($value, '.');
|
$decimal = strpos($value, '.');
|
||||||
$comma = strpos($value, ',');
|
$comma = strpos($value, ',');
|
||||||
|
|
||||||
if(!$comma) //no comma must be a decimal number already
|
if($comma === false) //no comma must be a decimal number already
|
||||||
return (float) $value;
|
return (float) $value;
|
||||||
|
|
||||||
if($decimal < $comma){ //decimal before a comma = euro
|
if($decimal < $comma){ //decimal before a comma = euro
|
||||||
@ -143,6 +143,52 @@ class Number
|
|||||||
// return (float) $s;
|
// return (float) $s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//next iteration of float parsing
|
||||||
|
public static function parseFloat($value)
|
||||||
|
{
|
||||||
|
|
||||||
|
if(!$value) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//remove everything except for numbers, decimals, commas and hyphens
|
||||||
|
$value = preg_replace('/[^0-9.,-]+/', '', $value);
|
||||||
|
|
||||||
|
$decimal = strpos($value, '.');
|
||||||
|
$comma = strpos($value, ',');
|
||||||
|
|
||||||
|
//check the 3rd last character
|
||||||
|
if(!in_array(substr($value, -3, 1), [".", ","])) {
|
||||||
|
|
||||||
|
if($comma && (substr($value, -3, 1) != ".")) {
|
||||||
|
$value .= ".00";
|
||||||
|
} elseif($decimal && (substr($value, -3, 1) != ",")) {
|
||||||
|
$value .= ",00";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
$decimal = strpos($value, '.');
|
||||||
|
$comma = strpos($value, ',');
|
||||||
|
|
||||||
|
if($comma === false) { //no comma must be a decimal number already
|
||||||
|
return (float) $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if($decimal < $comma) { //decimal before a comma = euro
|
||||||
|
$value = str_replace(['.',','], ['','.'], $value);
|
||||||
|
return (float) $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
//comma first = traditional thousan separator
|
||||||
|
$value = str_replace(',', '', $value);
|
||||||
|
|
||||||
|
return (float)$value;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public static function parseStringFloat($value)
|
public static function parseStringFloat($value)
|
||||||
{
|
{
|
||||||
$value = preg_replace('/[^0-9-.]+/', '', $value);
|
$value = preg_replace('/[^0-9-.]+/', '', $value);
|
||||||
|
@ -84,6 +84,7 @@ class SystemHealth
|
|||||||
'trailing_slash' => (bool) self::checkUrlState(),
|
'trailing_slash' => (bool) self::checkUrlState(),
|
||||||
'file_permissions' => (string) self::checkFileSystem(),
|
'file_permissions' => (string) self::checkFileSystem(),
|
||||||
'exchange_rate_api_not_configured' => (bool)self::checkCurrencySanity(),
|
'exchange_rate_api_not_configured' => (bool)self::checkCurrencySanity(),
|
||||||
|
'api_version' => (string) config('ninja.app_version'),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,8 +17,8 @@ return [
|
|||||||
'require_https' => env('REQUIRE_HTTPS', true),
|
'require_https' => env('REQUIRE_HTTPS', true),
|
||||||
'app_url' => rtrim(env('APP_URL', ''), '/'),
|
'app_url' => rtrim(env('APP_URL', ''), '/'),
|
||||||
'app_domain' => env('APP_DOMAIN', 'invoicing.co'),
|
'app_domain' => env('APP_DOMAIN', 'invoicing.co'),
|
||||||
'app_version' => env('APP_VERSION', '5.8.30'),
|
'app_version' => env('APP_VERSION', '5.8.31'),
|
||||||
'app_tag' => env('APP_TAG', '5.8.30'),
|
'app_tag' => env('APP_TAG', '5.8.31'),
|
||||||
'minimum_client_version' => '5.0.16',
|
'minimum_client_version' => '5.0.16',
|
||||||
'terms_version' => '1.0.1',
|
'terms_version' => '1.0.1',
|
||||||
'api_secret' => env('API_SECRET', false),
|
'api_secret' => env('API_SECRET', false),
|
||||||
|
@ -5240,6 +5240,7 @@ $lang = array(
|
|||||||
'use_available_payments' => 'Use Available Payments',
|
'use_available_payments' => 'Use Available Payments',
|
||||||
'test_email_sent' => 'Successfully sent email',
|
'test_email_sent' => 'Successfully sent email',
|
||||||
'gateway_type' => 'Gateway Type',
|
'gateway_type' => 'Gateway Type',
|
||||||
|
'save_template_body' => 'Would you like to save this import mapping as a template for future use?',
|
||||||
);
|
);
|
||||||
|
|
||||||
return $lang;
|
return $lang;
|
||||||
|
@ -11,13 +11,14 @@
|
|||||||
|
|
||||||
namespace Tests\Feature;
|
namespace Tests\Feature;
|
||||||
|
|
||||||
|
use Tests\TestCase;
|
||||||
|
use App\Models\Task;
|
||||||
use App\Models\Document;
|
use App\Models\Document;
|
||||||
|
use Tests\MockAccountData;
|
||||||
use App\Utils\Traits\MakesHash;
|
use App\Utils\Traits\MakesHash;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
|
||||||
use Illuminate\Support\Facades\Session;
|
use Illuminate\Support\Facades\Session;
|
||||||
use Tests\MockAccountData;
|
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||||
use Tests\TestCase;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @test
|
* @test
|
||||||
@ -44,6 +45,135 @@ class DocumentsApiTest extends TestCase
|
|||||||
Model::reguard();
|
Model::reguard();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testDocumentFilters()
|
||||||
|
{
|
||||||
|
Document::query()->withTrashed()->cursor()->each(function ($d){
|
||||||
|
$d->forceDelete();
|
||||||
|
});
|
||||||
|
|
||||||
|
$d = Document::factory()->create([
|
||||||
|
'company_id' => $this->company->id,
|
||||||
|
'user_id' => $this->user->id,
|
||||||
|
'name' => 'searchable.jpg',
|
||||||
|
'type' => 'jpg',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->client->documents()->save($d);
|
||||||
|
|
||||||
|
$response = $this->withHeaders([
|
||||||
|
'X-API-SECRET' => config('ninja.api_secret'),
|
||||||
|
'X-API-TOKEN' => $this->token,
|
||||||
|
])->get("/api/v1/documents/{$d->hashed_id}?client_id={$this->client->hashed_id}");
|
||||||
|
|
||||||
|
$response->assertStatus(200);
|
||||||
|
|
||||||
|
$this->assertCount(1, $response->json());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function testDocumentFilters2()
|
||||||
|
{
|
||||||
|
Document::query()->withTrashed()->cursor()->each(function ($d){
|
||||||
|
$d->forceDelete();
|
||||||
|
});
|
||||||
|
|
||||||
|
$d = Document::factory()->create([
|
||||||
|
'company_id' => $this->company->id,
|
||||||
|
'user_id' => $this->user->id,
|
||||||
|
'name' => 'searchable.jpg',
|
||||||
|
'type' => 'jpg',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->task->documents()->save($d);
|
||||||
|
|
||||||
|
$response = $this->withHeaders([
|
||||||
|
'X-API-SECRET' => config('ninja.api_secret'),
|
||||||
|
'X-API-TOKEN' => $this->token,
|
||||||
|
])->get("/api/v1/documents/{$d->hashed_id}?client_id={$this->client->hashed_id}");
|
||||||
|
|
||||||
|
$response->assertStatus(200);
|
||||||
|
|
||||||
|
$this->assertCount(1, $response->json());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDocumentFilters3()
|
||||||
|
{
|
||||||
|
Document::query()->withTrashed()->cursor()->each(function ($d){
|
||||||
|
$d->forceDelete();
|
||||||
|
});
|
||||||
|
|
||||||
|
$d = Document::factory()->create([
|
||||||
|
'company_id' => $this->company->id,
|
||||||
|
'user_id' => $this->user->id,
|
||||||
|
'name' => 'searchable.jpg',
|
||||||
|
'type' => 'jpg',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$t = Task::factory()->create([
|
||||||
|
'company_id' => $this->company->id,
|
||||||
|
'user_id' => $this->user->id,
|
||||||
|
'client_id' => $this->client->id,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$t->documents()->save($d);
|
||||||
|
|
||||||
|
$dd = Document::factory()->create([
|
||||||
|
'company_id' => $this->company->id,
|
||||||
|
'user_id' => $this->user->id,
|
||||||
|
'name' => 'searchable2.jpg',
|
||||||
|
'type' => 'jpg',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->client->documents()->save($dd);
|
||||||
|
|
||||||
|
$response = $this->withHeaders([
|
||||||
|
'X-API-SECRET' => config('ninja.api_secret'),
|
||||||
|
'X-API-TOKEN' => $this->token,
|
||||||
|
])->get("/api/v1/documents?client_id={$this->client->hashed_id}");
|
||||||
|
|
||||||
|
$response->assertStatus(200);
|
||||||
|
|
||||||
|
$this->assertCount(2, $response->json()['data']);
|
||||||
|
|
||||||
|
$response = $this->withHeaders([
|
||||||
|
'X-API-SECRET' => config('ninja.api_secret'),
|
||||||
|
'X-API-TOKEN' => $this->token,
|
||||||
|
])->get("/api/v1/documents?client_id={$this->client->hashed_id}&filter=craycray");
|
||||||
|
|
||||||
|
$response->assertStatus(200);
|
||||||
|
|
||||||
|
$this->assertCount(0, $response->json()['data']);
|
||||||
|
|
||||||
|
$response = $this->withHeaders([
|
||||||
|
'X-API-SECRET' => config('ninja.api_secret'),
|
||||||
|
'X-API-TOKEN' => $this->token,
|
||||||
|
])->get("/api/v1/documents?client_id={$this->client->hashed_id}&filter=s");
|
||||||
|
|
||||||
|
$response->assertStatus(200);
|
||||||
|
|
||||||
|
$this->assertCount(2, $response->json()['data']);
|
||||||
|
|
||||||
|
$response = $this->withHeaders([
|
||||||
|
'X-API-SECRET' => config('ninja.api_secret'),
|
||||||
|
'X-API-TOKEN' => $this->token,
|
||||||
|
])->get("/api/v1/documents?client_id={$this->client->hashed_id}&filter=searchable");
|
||||||
|
|
||||||
|
$response->assertStatus(200);
|
||||||
|
|
||||||
|
$this->assertCount(2, $response->json()['data']);
|
||||||
|
|
||||||
|
$response = $this->withHeaders([
|
||||||
|
'X-API-SECRET' => config('ninja.api_secret'),
|
||||||
|
'X-API-TOKEN' => $this->token,
|
||||||
|
])->get("/api/v1/documents?client_id={$this->client->hashed_id}&filter=searchable2");
|
||||||
|
|
||||||
|
$response->assertStatus(200);
|
||||||
|
|
||||||
|
$this->assertCount(1, $response->json()['data']);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public function testIsPublicTypesForDocumentRequest()
|
public function testIsPublicTypesForDocumentRequest()
|
||||||
{
|
{
|
||||||
$d = Document::factory()->create([
|
$d = Document::factory()->create([
|
||||||
|
@ -36,6 +36,42 @@ class InvoiceItemTest extends TestCase
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function testEdgeCasewithDiscountsPercentageAndTaxCalculations()
|
||||||
|
{
|
||||||
|
$invoice = InvoiceFactory::create($this->company->id, $this->user->id);
|
||||||
|
$invoice->client_id = $this->client->id;
|
||||||
|
$invoice->uses_inclusive_taxes = false;
|
||||||
|
$invoice->is_amount_discount =false;
|
||||||
|
$invoice->discount = 0;
|
||||||
|
$invoice->tax_rate1 = 0;
|
||||||
|
$invoice->tax_rate2 = 0;
|
||||||
|
$invoice->tax_rate3 = 0;
|
||||||
|
$invoice->tax_name1 = '';
|
||||||
|
$invoice->tax_name2 = '';
|
||||||
|
$invoice->tax_name3 = '';
|
||||||
|
|
||||||
|
$line_items = [];
|
||||||
|
|
||||||
|
$line_item = new InvoiceItem;
|
||||||
|
$line_item->quantity = 1;
|
||||||
|
$line_item->cost = 100;
|
||||||
|
$line_item->tax_rate1 = 22;
|
||||||
|
$line_item->tax_name1 = 'Km';
|
||||||
|
$line_item->product_key = 'Test';
|
||||||
|
$line_item->notes = 'Test';
|
||||||
|
$line_item->is_amount_discount = false;
|
||||||
|
$line_items[] = $line_item;
|
||||||
|
|
||||||
|
$invoice->line_items = $line_items;
|
||||||
|
$invoice->save();
|
||||||
|
|
||||||
|
$invoice = $invoice->calc()->getInvoice();
|
||||||
|
|
||||||
|
$this->assertEquals(122, $invoice->amount);
|
||||||
|
$this->assertEquals(22, $invoice->total_taxes);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public function testDiscountsWithInclusiveTaxes()
|
public function testDiscountsWithInclusiveTaxes()
|
||||||
{
|
{
|
||||||
$invoice = InvoiceFactory::create($this->company->id, $this->user->id);
|
$invoice = InvoiceFactory::create($this->company->id, $this->user->id);
|
||||||
|
@ -20,6 +20,53 @@ use Tests\TestCase;
|
|||||||
*/
|
*/
|
||||||
class NumberTest extends TestCase
|
class NumberTest extends TestCase
|
||||||
{
|
{
|
||||||
|
|
||||||
|
public function testRangeOfNumberFormats()
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
$floatvals = [
|
||||||
|
"22000.76" =>"22 000,76",
|
||||||
|
"22000.76" =>"22.000,76",
|
||||||
|
"22000.76" =>"22,000.76",
|
||||||
|
"22000" =>"22 000",
|
||||||
|
"22000" =>"22,000",
|
||||||
|
"22000" =>"22.000",
|
||||||
|
"22000.76" =>"22000.76",
|
||||||
|
"22000.76" =>"22000,76",
|
||||||
|
"1022000.76" =>"1.022.000,76",
|
||||||
|
"1022000.76" =>"1,022,000.76",
|
||||||
|
"1000000" =>"1,000,000",
|
||||||
|
"1000000" =>"1.000.000",
|
||||||
|
"1022000.76" =>"1022000.76",
|
||||||
|
"1022000.76" =>"1022000,76",
|
||||||
|
"1022000" =>"1022000",
|
||||||
|
"0.76" =>"0.76",
|
||||||
|
"0.76" =>"0,76",
|
||||||
|
"0" =>"0.00",
|
||||||
|
"0" =>"0,00",
|
||||||
|
"1" =>"1.00",
|
||||||
|
"1" =>"1,00",
|
||||||
|
"423545" =>"423545 €",
|
||||||
|
"423545" =>"423,545 €",
|
||||||
|
"423545" =>"423.545 €",
|
||||||
|
"1" =>"1,00 €",
|
||||||
|
"1.02" =>"€ 1.02",
|
||||||
|
"1000.02" =>"1'000,02 EUR",
|
||||||
|
"1000.02" =>"1 000.02$",
|
||||||
|
"1000.02" =>"1,000.02$",
|
||||||
|
"1000.02" =>"1.000,02 EURO"
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
foreach($floatvals as $key => $value) {
|
||||||
|
|
||||||
|
$this->assertEquals($key, Number::parseFloat($value));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public function testNegativeFloatParse()
|
public function testNegativeFloatParse()
|
||||||
{
|
{
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user