Merge pull request #7914 from turbo124/v5-stable

v5.5.37
This commit is contained in:
David Bomba 2022-11-02 14:19:42 +11:00 committed by GitHub
commit 4b0935185f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
47 changed files with 5307 additions and 5107 deletions

View File

@ -1 +1 @@
5.5.36
5.5.37

View File

@ -198,7 +198,7 @@ class Handler extends ExceptionHandler
// nlog($exception->validator->getMessageBag());
return response()->json(['message' => 'The given data was invalid.', 'errors' => $exception->validator->getMessageBag()], 422);
} elseif ($exception instanceof RelationNotFoundException && $request->expectsJson()) {
return response()->json(['message' => $exception->getMessage()], 400);
return response()->json(['message' => "Relation `{$exception->relation}` is not a valid include."], 400);
} elseif ($exception instanceof GenericPaymentDriverFailure && $request->expectsJson()) {
return response()->json(['message' => $exception->getMessage()], 400);
} elseif ($exception instanceof GenericPaymentDriverFailure) {

View File

@ -169,7 +169,7 @@ abstract class QueryFilters
public function clientFilter()
{
if (auth()->guard('contact')->user()) {
return $this->builder->whereClientId(auth()->guard('contact')->user()->client->id);
return $this->builder->where('client_id', auth()->guard('contact')->user()->client->id);
}
}
@ -179,6 +179,15 @@ abstract class QueryFilters
$created_at = date('Y-m-d H:i:s', $value);
if(is_string($created_at)){
$created_at = strtotime(str_replace("/","-",$created_at));
if(!$created_at)
return $this->builder;
}
return $this->builder->where('created_at', '>=', $created_at);
}

View File

@ -40,4 +40,7 @@ function nlog($output, $context = []): void
} else {
\Illuminate\Support\Facades\Log::channel('invoiceninja')->info($output, $context);
}
$output = null;
$context = null;
}

View File

@ -105,14 +105,14 @@ class SwissQrGenerator
// Add payment reference
// This is what you will need to identify incoming payments.
if(stripos($this->invoice->number, "Live-") === 0)
if(stripos($this->invoice->number, "Live") === 0)
{
// we're currently in preview status. Let's give a dummy reference for now
$invoice_number = "123456789";
}
else
{
$invoice_number = $this->invoice->number;
$invoice_number = iconv("UTF-8", "ASCII", $this->invoice->number);
}
if(strlen($this->company->present()->besr_id()) > 1)
@ -141,7 +141,7 @@ class SwissQrGenerator
// Optionally, add some human-readable information about what the bill is for.
$qrBill->setAdditionalInformation(
QrBill\DataGroup\Element\AdditionalInformation::create(
$this->invoice->public_notes ? substr($this->invoice->public_notes, 0, 139) : ctrans('texts.invoice_number_placeholder', ['invoice' => $invoice_number])
$this->invoice->public_notes ? substr($this->invoice->public_notes, 0, 139) : ctrans('texts.invoice_number_placeholder', ['invoice' => $this->invoice->number])
)
);

View File

@ -33,6 +33,7 @@ use App\Models\Client;
use App\Models\Credit;
use App\Models\Invoice;
use App\Repositories\CreditRepository;
use App\Services\PdfMaker\PdfMerge;
use App\Transformers\CreditTransformer;
use App\Utils\Ninja;
use App\Utils\TempFile;
@ -534,6 +535,20 @@ class CreditController extends BaseController
return response()->json(['message' => ctrans('texts.sent_message')], 200);
}
if($action == 'bulk_print' && auth()->user()->can('view', $credits->first())){
$paths = $credits->map(function ($credit){
return $credit->service()->getCreditPdf($credit->invitations->first());
});
$merge = (new PdfMerge($paths->toArray()))->run();
return response()->streamDownload(function () use ($merge) {
echo ($merge);
}, 'print.pdf', ['Content-Type' => 'application/pdf']);
}
$credits->each(function ($credit, $key) use ($action) {
if (auth()->user()->can('edit', $credit)) {
$this->performAction($credit, $action, true);

View File

@ -40,6 +40,7 @@ use App\Models\Invoice;
use App\Models\Quote;
use App\Models\TransactionEvent;
use App\Repositories\InvoiceRepository;
use App\Services\PdfMaker\PdfMerge;
use App\Transformers\InvoiceTransformer;
use App\Transformers\QuoteTransformer;
use App\Utils\Ninja;
@ -588,6 +589,20 @@ class InvoiceController extends BaseController
}
if($action == 'bulk_print' && auth()->user()->can('view', $invoices->first())){
$paths = $invoices->map(function ($invoice){
return $invoice->service()->getInvoicePdf();
});
$merge = (new PdfMerge($paths->toArray()))->run();
return response()->streamDownload(function () use ($merge) {
echo ($merge);
}, 'print.pdf', ['Content-Type' => 'application/pdf']);
}
/*
* Send the other actions to the switch
*/

View File

@ -32,6 +32,7 @@ use App\Models\Client;
use App\Models\Expense;
use App\Models\PurchaseOrder;
use App\Repositories\PurchaseOrderRepository;
use App\Services\PdfMaker\PdfMerge;
use App\Transformers\ExpenseTransformer;
use App\Transformers\PurchaseOrderTransformer;
use App\Utils\Ninja;
@ -515,6 +516,20 @@ class PurchaseOrderController extends BaseController
return response()->json(['message' => ctrans('texts.sent_message')], 200);
}
if($action == 'bulk_print' && auth()->user()->can('view', $purchase_orders->first())){
$paths = $purchase_orders->map(function ($purchase_order){
return $purchase_order->service()->getPurchaseOrderPdf();
});
$merge = (new PdfMerge($paths->toArray()))->run();
return response()->streamDownload(function () use ($merge) {
echo ($merge);
}, 'print.pdf', ['Content-Type' => 'application/pdf']);
}
/*
* Send the other actions to the switch
*/

View File

@ -35,6 +35,7 @@ use App\Models\Invoice;
use App\Models\Project;
use App\Models\Quote;
use App\Repositories\QuoteRepository;
use App\Services\PdfMaker\PdfMerge;
use App\Transformers\InvoiceTransformer;
use App\Transformers\ProjectTransformer;
use App\Transformers\QuoteTransformer;
@ -561,6 +562,20 @@ class QuoteController extends BaseController
return $this->listResponse(Quote::withTrashed()->whereIn('id', $this->transformKeys($ids))->company());
}
if($action == 'bulk_print' && auth()->user()->can('view', $quotes->first())){
$paths = $quotes->map(function ($quote){
return $quote->service()->getQuotePdf();
});
$merge = (new PdfMerge($paths->toArray()))->run();
return response()->streamDownload(function () use ($merge) {
echo ($merge);
}, 'print.pdf', ['Content-Type' => 'application/pdf']);
}
if($action == 'convert_to_project')
{

View File

@ -112,6 +112,8 @@ class SelfUpdateController extends BaseController
$zipFile->close();
$zipFile = null;
nlog('Finished extracting files');
unlink($file);

View File

@ -55,7 +55,7 @@ class ValidRefundableRequest implements Rule
}
$request_invoices = request()->has('invoices') ? $this->input['invoices'] : [];
$request_credits = request()->has('credits') ? $this->input['credits'] : [];
// $request_credits = request()->has('credits') ? $this->input['credits'] : [];
if ($payment->invoices()->exists()) {
foreach ($payment->invoices as $paymentable_invoice) {

View File

@ -44,6 +44,30 @@ class BaseTransformer
$this->company = $company;
}
public function parseDate($date)
{
try{
$parsed_date = Carbon::parse($date);
return $parsed_date->format('Y-m-d');
}
catch(\Exception $e){
$parsed_date = date('Y-m-d', strtotime($date));
if($parsed_date == '1970-01-01')
return now()->format('Y-m-d');
return $parsed_date;
}
}
public function getString($data, $field)
{
return isset($data[$field]) && $data[$field] ? trim($data[$field]) : '';

View File

@ -31,7 +31,7 @@ class ExpenseTransformer extends BaseTransformer
return [
'company_id' => $this->company->id,
'amount' => $this->getFloat($data, 'expense.amount'),
'amount' => abs($this->getFloat($data, 'expense.amount')),
'currency_id' => $this->getCurrencyByCode(
$data,
'expense.currency_id'

View File

@ -57,10 +57,10 @@ class InvoiceTransformer extends BaseTransformer
'discount' => $this->getFloat($invoice_data, 'invoice.discount'),
'po_number' => $this->getString($invoice_data, 'invoice.po_number'),
'date' => isset($invoice_data['invoice.date'])
? date('Y-m-d', strtotime(str_replace("/","-",$invoice_data['invoice.date'])))
? $this->parseDate($invoice_data['invoice.date'])
: now()->format('Y-m-d'),
'due_date' => isset($invoice_data['invoice.due_date'])
? date('Y-m-d', strtotime(str_replace("/","-",$invoice_data['invoice.due_date'])))
? $this->parseDate($invoice_data['invoice.due_date'])
: null,
'terms' => $this->getString($invoice_data, 'invoice.terms'),
'public_notes' => $this->getString(
@ -140,10 +140,7 @@ class InvoiceTransformer extends BaseTransformer
$transformed['payments'] = [
[
'date' => isset($invoice_data['payment.date'])
? date(
'Y-m-d',
strtotime($invoice_data['payment.date'])
)
? $this->parseDate($invoice_data['payment.date'])
: date('y-m-d'),
'transaction_reference' => $this->getString(
$invoice_data,
@ -159,10 +156,7 @@ class InvoiceTransformer extends BaseTransformer
$transformed['payments'] = [
[
'date' => isset($invoice_data['payment.date'])
? date(
'Y-m-d',
strtotime($invoice_data['payment.date'])
)
? $this->parseDate($invoice_data['payment.date'])
: date('y-m-d'),
'transaction_reference' => $this->getString(
$invoice_data,
@ -182,10 +176,7 @@ class InvoiceTransformer extends BaseTransformer
$transformed['payments'] = [
[
'date' => isset($invoice_data['payment.date'])
? date(
'Y-m-d',
strtotime($invoice_data['payment.date'])
)
? $this->parseDate($invoice_data['payment.date'])
: date('y-m-d'),
'transaction_reference' => $this->getString(
$invoice_data,

View File

@ -218,7 +218,15 @@ class CreateEntityPdf implements ShouldQueue
throw new FilePermissionsFailure($e->getMessage());
}
}
$this->invitation = null;
$this->entity = null;
$this->company = null;
$this->client = null;
$this->contact = null;
$maker = null;
$state = null;
return $file_path;
}

View File

@ -200,6 +200,8 @@ class CreateRawPdf implements ShouldQueue
}
if ($pdf) {
$maker =null;
$state = null;
return $pdf;
}

View File

@ -128,6 +128,17 @@ class EmailEntity implements ShouldQueue
$nmo->entity = $this->entity;
(new NinjaMailerJob($nmo))->handle();
$nmo = null;
$this->invitation = null;
$this->company = null;
$this->entity_string = null;
$this->entity = null;
$this->settings = null;
$this->reminder_template = null;
$this->html_engine = null;
$this->template_data = null;
$this->email_entity_builder = null;
}
private function resolveEntityString() :string

View File

@ -23,6 +23,7 @@ use App\Libraries\MultiDB;
use App\Models\Client;
use App\Models\Company;
use App\Models\Vendor;
use App\Utils\Ninja;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
@ -81,6 +82,10 @@ class CSVIngest implements ShouldQueue
$engine->finalizeImport();
$this->checkContacts();
if(Ninja::isHosted())
app('queue.worker')->shouldQuit = 1;
}
private function checkContacts()

View File

@ -115,6 +115,7 @@ class NinjaMailerJob implements ShouldQueue
//send email
try {
nlog("trying to send to {$this->nmo->to_user->email} ". now()->toDateTimeString());
nlog("Using mailer => ". $this->mailer);
@ -128,7 +129,15 @@ class NinjaMailerJob implements ShouldQueue
LightLogs::create(new EmailSuccess($this->nmo->company->company_key))
->send();
// nlog('Using ' . ((int) (memory_get_usage(true) / (1024 * 1024))) . 'MB ');
$this->nmo = null;
$this->company = null;
$mem_usage = memory_get_usage();
nlog('The script is now using: ' . round($mem_usage / 1024) . 'KBof memory.');
} catch (\Exception | \RuntimeException | \Google\Service\Exception $e) {
nlog("error failed with {$e->getMessage()}");
@ -166,7 +175,16 @@ class NinjaMailerJob implements ShouldQueue
/* Don't send postmark failures to Sentry */
if(Ninja::isHosted() && (!$e instanceof ClientException))
app('sentry')->captureException($e);
$message = null;
$this->nmo = null;
$this->company = null;
app('queue.worker')->shouldQuit = 1;
}
}
/* Switch statement to handle failure notifications */
@ -188,6 +206,7 @@ class NinjaMailerJob implements ShouldQueue
if ($this->nmo->to_user instanceof ClientContact)
$this->logMailError($message, $this->nmo->to_user->client);
}
private function setMailDriver()
@ -381,7 +400,7 @@ class NinjaMailerJob implements ShouldQueue
private function logMailError($errors, $recipient_object)
{
SystemLogger::dispatch(
SystemLogger::dispatchSync(
$errors,
SystemLog::CATEGORY_MAIL,
SystemLog::EVENT_MAIL_SEND,
@ -396,6 +415,9 @@ class NinjaMailerJob implements ShouldQueue
LightLogs::create($job_failure)
->send();
$job_failure = null;
}
public function failed($exception = null)

View File

@ -99,7 +99,7 @@ class ReminderJob implements ShouldQueue
(Ninja::isSelfHost() || $invoice->company->account->isPaidHostedClient())) {
$invoice->invitations->each(function ($invitation) use ($invoice, $reminder_template) {
EmailEntity::dispatchSync($invitation, $invitation->company, $reminder_template);
EmailEntity::dispatch($invitation, $invitation->company, $reminder_template);
nlog("Firing reminder email for invoice {$invoice->number} - {$reminder_template}");
});

View File

@ -79,11 +79,22 @@ class SystemLogger implements ShouldQueue
if (! $this->log) {
nlog('SystemLogger:: no log to store');
$this->category_id = null;
$this->event_id = null;
$this->type_id = null;
$this->client = null;
$this->company = null;
return;
}
SystemLog::create($sl);
$this->log = null;
$this->category_id = null;
$this->event_id = null;
$this->type_id = null;
$this->client = null;
$this->company = null;
}
public function failed($e)

View File

@ -213,6 +213,9 @@ class CreatePurchaseOrderPdf implements ShouldQueue
info($maker->getCompiledHTML());
}
$maker = null;
$state = null;
return $pdf;
}

View File

@ -515,17 +515,10 @@ class MultiDB
{
/* This will set the database connection for the request */
config(['database.default' => $database]);
// for some reason this breaks everything _hard_
// DB::purge($database);
// DB::reconnect($database);
}
public static function setDefaultDatabase()
{
config(['database.default' => config('ninja.db.default')]);
// DB::purge(config('ninja.db.default'));
// DB::reconnect(config('ninja.db.default'));
}
}

View File

@ -251,17 +251,17 @@ class PaymentEmailEngine extends BaseEmailEngine
private function formatInvoiceField($field)
{
$invoice = '';
$invoicex = '';
foreach ($this->payment->invoices as $invoice) {
$invoice_field = $invoice->{$field};
$invoice .= ctrans('texts.invoice_number_short') . "{$invoice->number} {$invoice_field}";
$invoicex .= ctrans('texts.invoice_number_short') . "{$invoice->number} {$invoice_field}";
}
return $invoice;
return $invoicex;
}

View File

@ -95,7 +95,7 @@ class PayPalExpressPaymentDriver extends BaseDriver
return $response->redirect();
}
$this->sendFailureMail($response->getMessage() ?: '');
// $this->sendFailureMail($response->getMessage() ?: '');
$message = [
'server_response' => $response->getMessage(),

View File

@ -137,6 +137,9 @@ class Charge
return false;
}
if($response?->status != 'succeeded')
$this->stripe->processInternallyFailedPayment($this->stripe, new \Exception('Auto billing failed.',400));
if ($cgt->gateway_type_id == GatewayType::SEPA) {
$payment_method_type = PaymentType::SEPA;
$status = Payment::STATUS_PENDING;

View File

@ -41,7 +41,7 @@ class ACH
public function authorizeView($data)
{
$data['gateway'] = $this->wepay_payment_driver;
$data['country_code'] = $this->wepay_payment_driver->client ? $this->wepay_payment_driver->client->country->iso_3166_2 : $this->wepay_payment_driver->company_gateway->company()->iso_3166_2;
$data['country_code'] = $this->wepay_payment_driver?->client?->country ? $this->wepay_payment_driver->client->country->iso_3166_2 : $this->wepay_payment_driver->company_gateway->company()->iso_3166_2;
return render('gateways.wepay.authorize.bank_transfer', $data);
}

View File

@ -37,7 +37,7 @@ class CreditCard
public function authorizeView($data)
{
$data['gateway'] = $this->wepay_payment_driver;
$data['country_code'] = $this->wepay_payment_driver->client ? $this->wepay_payment_driver->client->country->iso_3166_2 : $this->wepay_payment_driver->company_gateway->company()->iso_3166_2;
$data['country_code'] = $this->wepay_payment_driver?->client?->country ? $this->wepay_payment_driver->client->country->iso_3166_2 : $this->wepay_payment_driver->company_gateway->company()->iso_3166_2;
return render('gateways.wepay.authorize.authorize', $data);
}
@ -102,7 +102,7 @@ class CreditCard
{
$data['gateway'] = $this->wepay_payment_driver;
$data['description'] = ctrans('texts.invoices').': '.collect($data['invoices'])->pluck('invoice_number');
$data['country_code'] = $this->wepay_payment_driver->client ? $this->wepay_payment_driver->client->country->iso_3166_2 : $this->wepay_payment_driver->company_gateway->company()->iso_3166_2;
$data['country_code'] = $this->wepay_payment_driver?->client?->country ? $this->wepay_payment_driver->client->country->iso_3166_2 : $this->wepay_payment_driver->company_gateway->company()->iso_3166_2;
return render('gateways.wepay.credit_card.pay', $data);
}

View File

@ -60,7 +60,7 @@ class ActivityRepository extends BaseRepository
$activity->save();
//rate limiter
// $this->createBackup($entity, $activity);
$this->createBackup($entity, $activity);
}
/**
@ -167,8 +167,13 @@ class ActivityRepository extends BaseRepository
$maker = new PdfMakerService($state);
return $maker->design($template)
$html = $maker->design($template)
->build()
->getCompiledHTML(true);
$maker = null;
$state = null;
return $html;
}
}

View File

@ -304,8 +304,9 @@ class BaseRepository
if (! $model->design_id)
$model->design_id = $this->decodePrimaryKey($client->getSetting('invoice_design_id'));
//links tasks and expenses back to the invoice.
$model->service()->linkEntities()->save();
//links tasks and expenses back to the invoice, but only if we are not in the middle of a transaction.
if (\DB::transactionLevel() == 0)
$model->service()->linkEntities()->save();
if($this->new_model)
event('eloquent.created: App\Models\Invoice', $model);

View File

@ -108,6 +108,9 @@ class Statement
\DB::connection(config('database.default'))->rollBack();
}
$maker = null;
$state = null;
return $pdf;
}

View File

@ -107,6 +107,9 @@ class GenerateDeliveryNote
Storage::disk($this->disk)->put($file_path, $pdf);
$maker = null;
$state = null;
return $file_path;
}
}

View File

@ -712,6 +712,8 @@ class Design extends BaseDesign
$elements[] = $element;
}
$document = null;
return $elements;
}

View File

@ -234,44 +234,11 @@ trait DesignHelpers
});
";
// Unminified version, just for the reference.
// By default all table headers are hidden with HTML `hidden` property.
// This will check for table data values & if they're not empty it will remove hidden from the column itself.
/*
document.addEventListener('DOMContentLoaded', function() {
document.querySelectorAll("#product-table > tbody > tr > td, #task-table > tbody > tr > td, #delivery-note-table > tbody > tr > td").forEach(e => {
if ("" !== e.innerText) {
let t = e.getAttribute("data-ref").slice(0, -3);
document.querySelector(`th[data-ref="${t}-th"]`).removeAttribute("hidden");
}
});
document.querySelectorAll("#product-table > tbody > tr > td, #task-table > tbody > tr > td, #delivery-note-table > tbody > tr > td").forEach(e => {
let t = e.getAttribute("data-ref").slice(0, -3);
t = document.querySelector(`th[data-ref="${t}-th"]`);
if (!t.hasAttribute('hidden')) {
return;
}
if ("" == e.innerText) {
e.setAttribute('hidden', 'true');
}
});
}, false);
*/
$javascript = 'document.addEventListener("DOMContentLoaded",function(){document.querySelectorAll("#product-table > tbody > tr > td, #task-table > tbody > tr > td, #delivery-note-table > tbody > tr > td").forEach(t=>{if(""!==t.innerText){let e=t.getAttribute("data-ref").slice(0,-3);document.querySelector(`th[data-ref="${e}-th"]`).removeAttribute("hidden")}}),document.querySelectorAll("#product-table > tbody > tr > td, #task-table > tbody > tr > td, #delivery-note-table > tbody > tr > td").forEach(t=>{let e=t.getAttribute("data-ref").slice(0,-3);(e=document.querySelector(`th[data-ref="${e}-th"]`)).hasAttribute("hidden")&&""==t.innerText&&t.setAttribute("hidden","true")})},!1);';
// Previously we've been decoding the HTML on the backend and XML parsing isn't good options because it requires,
// strict & valid HTML to even output/decode. Decoding is now done on the frontend with this piece of Javascript.
/**
document.addEventListener('DOMContentLoaded', function() {
document.querySelectorAll(`[data-state="encoded-html"]`).forEach((element) => element.innerHTML = element.innerText)
}, false);
*/
$html_decode = 'document.addEventListener("DOMContentLoaded",function(){document.querySelectorAll(`[data-state="encoded-html"]`).forEach(e=>e.innerHTML=e.innerText)},!1);';
return ['element' => 'div', 'elements' => [
@ -391,17 +358,6 @@ document.addEventListener('DOMContentLoaded', function() {
return $converter->convert($markdown);
}
// public function processMarkdownOnLineItems(array &$items): void
// {
// foreach ($items as $key => $item) {
// foreach ($item as $variable => $value) {
// $item[$variable] = DesignHelpers::parseMarkdownToHtml($value ?? '');
// }
// $items[$key] = $item;
// }
// }
public function processNewLines(array &$items): void
{
foreach ($items as $key => $item) {

View File

@ -0,0 +1,42 @@
<?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\Services\PdfMaker;
use Illuminate\Support\Facades\Storage;
use \setasign\Fpdi\Fpdi;
use setasign\Fpdi\PdfParser\StreamReader;
class PdfMerge
{
public function __construct(private array $file_paths) {}
public function run()
{
$pdf = new FPDI();
foreach ($this->file_paths as $file) {
$pageCount = $pdf->setSourceFile(StreamReader::createByString(Storage::get($file)));
for ($i = 0; $i < $pageCount; $i++) {
$tpl = $pdf->importPage($i + 1, '/MediaBox');
$pdf->addPage();
$pdf->useTemplate($tpl);
}
}
return $pdf->Output('S');
}
}

View File

@ -143,6 +143,7 @@ class HtmlEngine
$data['$credit.datetime'] = &$data['$entity.datetime'];
$data['$payment_button'] = ['value' => '<a class="button" href="'.$this->invitation->getPaymentLink().'">'.ctrans('texts.pay_now').'</a>', 'label' => ctrans('texts.pay_now')];
$data['$payment_link'] = ['value' => $this->invitation->getPaymentLink(), 'label' => ctrans('texts.pay_now')];
$data['$payment_qrcode'] = ['value' => $this->invitation->getPaymentQrCode(), 'label' => ctrans('texts.pay_now')];
if ($this->entity_string == 'invoice' || $this->entity_string == 'recurring_invoice') {
@ -894,6 +895,10 @@ html {
$dom->appendChild($container);
return $dom->saveHTML();
$html = $dom->saveHTML();
$dom = null;
return $html;
}
}

View File

@ -137,6 +137,8 @@ class Number
public static function formatMoney($value, $entity) :string
{
$value = floatval($value);
$currency = $entity->currency();
$thousand = $currency->thousand_separator;

View File

@ -90,6 +90,9 @@ class SystemHealth
private static function checkCurrencySanity()
{
if(!self::simpleDbCheck())
return true;
if(strlen(config('ninja.currency_converter_api_key')) == 0){
$cs = DB::table('clients')

View File

@ -13,6 +13,11 @@ namespace App\Utils\Traits;
use App\Utils\Ninja;
use Illuminate\Support\Str;
use BaconQrCode\Renderer\ImageRenderer;
use BaconQrCode\Renderer\Image\SvgImageBackEnd;
use BaconQrCode\Renderer\RendererStyle\RendererStyle;
use BaconQrCode\Writer;
/**
* Class Inviteable.
@ -54,6 +59,22 @@ trait Inviteable
return $domain.'/client/pay/'.$this->key;
}
public function getPaymentQrCode()
{
$renderer = new ImageRenderer(
new RendererStyle(200),
new SvgImageBackEnd()
);
$writer = new Writer($renderer);
$qr = $writer->writeString($this->getPaymentLink());
return "<svg viewBox='0 0 200 200' width='200' height='200' x='0' y='0' xmlns='http://www.w3.org/2000/svg'>
<rect x='0' y='0' width='100%'' height='100%' />{$qr}</svg>";
}
public function getUnsubscribeLink()
{
if (Ninja::isHosted()) {

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.36',
'app_tag' => '5.5.36',
'app_version' => '5.5.37',
'app_tag' => '5.5.37',
'minimum_client_version' => '5.0.16',
'terms_version' => '1.0.1',
'api_secret' => env('API_SECRET', ''),

View File

@ -11,9 +11,9 @@ const RESOURCES = {
"favicon.png": "dca91c54388f52eded692718d5a98b8b",
"favicon.ico": "51636d3a390451561744c42188ccd628",
"flutter.js": "f85e6fb278b0fd20c349186fb46ae36d",
"/": "cb6f2c73612e3d57c56452ef7c10795a",
"/": "b929c2274033670c857cf631017d7f84",
"manifest.json": "ef43d90e57aa7682d7e2cfba2f484a40",
"main.dart.js": "69afcf35930517b4949ed2f4d551307a",
"main.dart.js": "b165f2dcfe57c6bae02dc84a3d527590",
"assets/AssetManifest.json": "759f9ef9973f7e26c2a51450b55bb9fa",
"assets/FontManifest.json": "087fb858dc3cbfbf6baf6a30004922f1",
"assets/NOTICES": "1a34e70168d56fad075adfb4bdbb20eb",

4978
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

4970
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

@ -41,6 +41,10 @@
color: #000;
}
#qr-bill td {
max-width: none;
}
.header-container {
display: grid;
grid-template-columns: 1fr 1fr 1fr;