mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2024-11-15 23:52:33 +01:00
commit
9701d19149
29
README.md
29
README.md
@ -4,14 +4,19 @@
|
||||
|
||||
![v5-develop phpunit](https://github.com/invoiceninja/invoiceninja/workflows/phpunit/badge.svg?branch=v5-develop)
|
||||
![v5-stable phpunit](https://github.com/invoiceninja/invoiceninja/workflows/phpunit/badge.svg?branch=v5-stable)
|
||||
|
||||
[![Codacy Badge](https://api.codacy.com/project/badge/Grade/d39acb4bf0f74a0698dc77f382769ba5)](https://www.codacy.com/app/turbo124/invoiceninja?utm_source=github.com&utm_medium=referral&utm_content=invoiceninja/invoiceninja&utm_campaign=Badge_Grade)
|
||||
[![Codacy Badge](https://app.codacy.com/project/badge/Grade/d16c78aad8574466bf83232b513ef4fb)](https://www.codacy.com/gh/turbo124/invoiceninja/dashboard?utm_source=github.com&utm_medium=referral&utm_content=turbo124/invoiceninja&utm_campaign=Badge_Grade)
|
||||
|
||||
# Invoice Ninja version 5!
|
||||
|
||||
## Quick Start
|
||||
## Preamble
|
||||
|
||||
Currently the client portal and API are of alpha quality, to get started:
|
||||
Version 5 of Invoice Ninja is here! We've taken the best parts of version 4 and bolted on all of the most requested features to produce a invoicing application like no other.
|
||||
|
||||
The new interface has a lot more functionality so it isn't a carbon copy of v4, but once you get used to the new layout and functionality we are sure you will love it!
|
||||
|
||||
If you have any questions, please join us on our [forum](https://forum.invoiceninja.com) or on [slack](https://invoiceninja.slack.com)
|
||||
|
||||
## Quick Start
|
||||
|
||||
```bash
|
||||
git clone https://github.com/invoiceninja/invoiceninja.git
|
||||
@ -69,6 +74,8 @@ To improve chances of PRs being merged please include tests to ensure your code
|
||||
|
||||
API documentation is hosted using Swagger and can be found [HERE](https://app.swaggerhub.com/apis/invoiceninja/invoiceninja)
|
||||
|
||||
Installation, Configuration and Troubleshooting documentation can be found [HERE] (https://invoiceninja.github.io)
|
||||
|
||||
## Credits
|
||||
* [Hillel Coren](https://hillelcoren.com/)
|
||||
* [David Bomba](https://github.com/turbo124)
|
||||
@ -85,10 +92,18 @@ API documentation is hosted using Swagger and can be found [HERE](https://app.sw
|
||||
|
||||
## Current work in progress
|
||||
|
||||
Invoice Ninja is currently being written in a combination of Laravel for the API and Client Portal and Flutter for the front end management console. This will allow an immersive and consistent experience across any device: mobile, tablet or desktop.
|
||||
Invoice Ninja is written in a combination of technologies:
|
||||
|
||||
To manage our workflow we will be creating separate branches for the client (Flutter) and server (Laravel API / Client Portal) and merge these into a release branch for deployments.
|
||||
API - Laravel
|
||||
Client Portal - Laravel + Tailwind
|
||||
Admin Portal - Flutter
|
||||
|
||||
This allows an immersive and consistent experience across any device: mobile, tablet or desktop.
|
||||
|
||||
## Security
|
||||
|
||||
If you find a security issue with this application please send an email to contact@invoiceninja.com Please follow responsible disclosure procedures if you detect an issue. For further information on responsible disclosure please read [here](https://cheatsheetseries.owasp.org/cheatsheets/Vulnerability_Disclosure_Cheat_Sheet.html)
|
||||
|
||||
## License
|
||||
Invoice Ninja is released under the Attribution Assurance License.
|
||||
Invoice Ninja is released under the Elastic License.
|
||||
See [LICENSE](LICENSE) for details.
|
||||
|
@ -1 +1 @@
|
||||
5.2.10
|
||||
5.2.11
|
@ -365,7 +365,7 @@ class CheckData extends Command
|
||||
/* Due to accounting differences we need to perform a second loop here to ensure there actually is an issue */
|
||||
$clients->each(function ($client_record) use ($credit_total_applied) {
|
||||
|
||||
$client = Client::find($client_record->id);
|
||||
$client = Client::withTrashed()->find($client_record->id);
|
||||
|
||||
$total_invoice_payments = 0;
|
||||
|
||||
@ -594,6 +594,7 @@ class CheckData extends Command
|
||||
'client',
|
||||
'client_contact',
|
||||
'payment',
|
||||
'recurring_invoice',
|
||||
],
|
||||
'invoices' => [
|
||||
'client',
|
||||
|
@ -64,7 +64,7 @@ class Kernel extends ConsoleKernel
|
||||
|
||||
$schedule->job(new AutoBillCron)->dailyAt('00:30')->withoutOverlapping();
|
||||
|
||||
$schedule->job(new SchedulerCheck)->everyFiveMinutes();
|
||||
$schedule->job(new SchedulerCheck)->daily()->withoutOverlapping();
|
||||
|
||||
/* Run hosted specific jobs */
|
||||
if (Ninja::isHosted()) {
|
||||
|
@ -243,7 +243,7 @@ class CompanySettings extends BaseSettings
|
||||
public $font_size = 7; //@implemented
|
||||
public $primary_font = 'Roboto';
|
||||
public $secondary_font = 'Roboto';
|
||||
public $primary_color = '#142cb5';
|
||||
public $primary_color = '#298AAB';
|
||||
public $secondary_color = '#7081e0';
|
||||
|
||||
public $hide_paid_to_date = false; //@TODO where?
|
||||
|
@ -19,6 +19,8 @@ class InvoiceItem
|
||||
|
||||
public $product_key = '';
|
||||
|
||||
public $product_cost = 0;
|
||||
|
||||
public $notes = '';
|
||||
|
||||
public $discount = 0;
|
||||
@ -57,6 +59,7 @@ class InvoiceItem
|
||||
'type_id' => 'string',
|
||||
'quantity' => 'float',
|
||||
'cost' => 'float',
|
||||
'product_cost' => 'float',
|
||||
'product_key' => 'string',
|
||||
'notes' => 'string',
|
||||
'discount' => 'float',
|
||||
|
@ -81,17 +81,23 @@ class Handler extends ExceptionHandler
|
||||
|
||||
app('sentry')->configureScope(function (Scope $scope): void {
|
||||
|
||||
if(auth()->guard('contact') && auth()->guard('contact')->user())
|
||||
$name = 'hosted@invoiceninja.com';
|
||||
|
||||
if(auth()->guard('contact') && auth()->guard('contact')->user()){
|
||||
$name = "Contact = ".auth()->guard('contact')->user()->email;
|
||||
$key = auth()->guard('contact')->user()->company->account->key;
|
||||
elseif (auth()->guard('user') && auth()->guard('user')->user())
|
||||
}
|
||||
elseif (auth()->guard('user') && auth()->guard('user')->user()){
|
||||
$name = "Admin = ".auth()->guard('user')->user()->email;
|
||||
$key = auth()->user()->account->key;
|
||||
}
|
||||
else
|
||||
$key = 'Anonymous';
|
||||
|
||||
$scope->setUser([
|
||||
'id' => 'Hosted_User',
|
||||
'id' => $key,
|
||||
'email' => 'hosted@invoiceninja.com',
|
||||
'name' => $key,
|
||||
'name' => $name,
|
||||
]);
|
||||
});
|
||||
|
||||
@ -120,8 +126,7 @@ class Handler extends ExceptionHandler
|
||||
}
|
||||
}
|
||||
|
||||
// if(config('ninja.expanded_logging'))
|
||||
parent::report($exception);
|
||||
parent::report($exception);
|
||||
|
||||
}
|
||||
|
||||
@ -191,7 +196,7 @@ class Handler extends ExceptionHandler
|
||||
} elseif ($exception instanceof GenericPaymentDriverFailure && $request->expectsJson()) {
|
||||
return response()->json(['message' => $exception->getMessage()], 400);
|
||||
} elseif ($exception instanceof GenericPaymentDriverFailure) {
|
||||
$data['message'] = $exception->getMessage();
|
||||
return response()->json(['message' => $exception->getMessage()], 400);
|
||||
}
|
||||
|
||||
return parent::render($request, $exception);
|
||||
|
@ -12,6 +12,7 @@
|
||||
namespace App\Http\Controllers\Auth;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\ClientPortal\Contact\ContactPasswordResetRequest;
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\Account;
|
||||
use Illuminate\Contracts\View\Factory;
|
||||
@ -73,9 +74,8 @@ class ContactForgotPasswordController extends Controller
|
||||
return Password::broker('contacts');
|
||||
}
|
||||
|
||||
public function sendResetLinkEmail(Request $request)
|
||||
public function sendResetLinkEmail(ContactPasswordResetRequest $request)
|
||||
{
|
||||
//MultiDB::userFindAndSetDb($request->input('email'));
|
||||
|
||||
$user = MultiDB::hasContact($request->input('email'));
|
||||
|
||||
|
@ -35,10 +35,16 @@ class ContactLoginController extends Controller
|
||||
|
||||
public function showLoginForm(Request $request)
|
||||
{
|
||||
if (strpos($request->getHost(), 'invoicing.co') !== false) {
|
||||
//if we are on the root domain invoicing.co do not show any company logos
|
||||
if(Ninja::isHosted() && count(explode('.', request()->getHost())) == 2){
|
||||
$company = null;
|
||||
}elseif (strpos($request->getHost(), 'invoicing.co') !== false) {
|
||||
$subdomain = explode('.', $request->getHost())[0];
|
||||
$company = Company::where('subdomain', $subdomain)->first();
|
||||
} elseif (Ninja::isSelfHost()) {
|
||||
} elseif(Ninja::isHosted() && $company = Company::where('portal_domain', $request->getSchemeAndHttpHost())->first()){
|
||||
|
||||
}
|
||||
elseif (Ninja::isSelfHost()) {
|
||||
$company = Account::first()->default_company;
|
||||
} else {
|
||||
$company = null;
|
||||
|
@ -488,6 +488,8 @@ class LoginController extends BaseController
|
||||
auth()->user()->email_verified_at = now();
|
||||
auth()->user()->save();
|
||||
|
||||
auth()->user()->setCompany(auth()->user()->account->default_company);
|
||||
|
||||
$this->setLoginCache(auth()->user());
|
||||
|
||||
$cu = CompanyUser::whereUserId(auth()->user()->id);
|
||||
|
@ -24,6 +24,7 @@ use Illuminate\Contracts\View\Factory;
|
||||
use Illuminate\View\View;
|
||||
use ZipStream\Option\Archive;
|
||||
use ZipStream\ZipStream;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class InvoiceController extends Controller
|
||||
{
|
||||
@ -170,8 +171,10 @@ class InvoiceController extends Controller
|
||||
$invitation = $invoice->invitations->first();
|
||||
//$file = $invoice->pdf_file_path($invitation);
|
||||
$file = $invoice->service()->getInvoicePdf(auth()->user());
|
||||
return response()->download($file, basename($file), ['Cache-Control:' => 'no-cache'])->deleteFileAfterSend(true);;
|
||||
|
||||
// return response()->download($file, basename($file), ['Cache-Control:' => 'no-cache'])->deleteFileAfterSend(true);;
|
||||
return response()->streamDownload(function () use($file) {
|
||||
echo Storage::get($file);
|
||||
}, basename($file));
|
||||
}
|
||||
|
||||
// enable output of HTTP headers
|
||||
|
@ -24,8 +24,10 @@ use App\Utils\TempFile;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Illuminate\Contracts\View\Factory;
|
||||
use Illuminate\View\View;
|
||||
use Symfony\Component\HttpFoundation\BinaryFileResponse;
|
||||
use ZipStream\Option\Archive;
|
||||
use ZipStream\ZipStream;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class QuoteController extends Controller
|
||||
{
|
||||
@ -46,7 +48,7 @@ class QuoteController extends Controller
|
||||
*
|
||||
* @param ShowQuoteRequest $request
|
||||
* @param Quote $quote
|
||||
* @return Factory|View|\Symfony\Component\HttpFoundation\BinaryFileResponse
|
||||
* @return Factory|View|BinaryFileResponse
|
||||
*/
|
||||
public function show(ShowQuoteRequest $request, Quote $quote)
|
||||
{
|
||||
@ -88,8 +90,11 @@ class QuoteController extends Controller
|
||||
|
||||
if ($quotes->count() == 1) {
|
||||
|
||||
$file = $quotes->first()->pdf_file_path();
|
||||
return response()->download($file, basename($file), ['Cache-Control:' => 'no-cache'])->deleteFileAfterSend(true);
|
||||
$file = $quotes->first()->service()->getQuotePdf();
|
||||
// return response()->download($file, basename($file), ['Cache-Control:' => 'no-cache'])->deleteFileAfterSend(true);
|
||||
return response()->streamDownload(function () use($file) {
|
||||
echo Storage::get($file);
|
||||
}, basename($file));
|
||||
}
|
||||
|
||||
// enable output of HTTP headers
|
||||
@ -110,16 +115,18 @@ class QuoteController extends Controller
|
||||
protected function approve(array $ids, $process = false)
|
||||
{
|
||||
$quotes = Quote::whereIn('id', $ids)
|
||||
->whereClientId(auth()->user()->client->id)
|
||||
->where('client_id', auth('contact')->user()->client->id)
|
||||
->where('company_id', auth('contact')->user()->client->company_id)
|
||||
->where('status_id', Quote::STATUS_SENT)
|
||||
->get();
|
||||
|
||||
if (! $quotes || $quotes->count() == 0) {
|
||||
if (!$quotes || $quotes->count() == 0) {
|
||||
return redirect()->route('client.quotes.index');
|
||||
}
|
||||
|
||||
if ($process) {
|
||||
foreach ($quotes as $quote) {
|
||||
$quote->service()->approve(auth('contact')->user())->save();
|
||||
$quote->service()->approve(auth()->user())->save();
|
||||
event(new QuoteWasApproved(auth('contact')->user(), $quote, $quote->company, Ninja::eventVars()));
|
||||
|
||||
if (request()->has('signature') && !is_null(request()->signature) && !empty(request()->signature)) {
|
||||
|
@ -37,6 +37,7 @@ use App\Utils\TempFile;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use App\Utils\Traits\SavesDocuments;
|
||||
use Illuminate\Http\Response;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
/**
|
||||
* Class CreditController.
|
||||
@ -536,8 +537,14 @@ class CreditController extends BaseController
|
||||
}
|
||||
break;
|
||||
case 'download':
|
||||
$file = $credit->pdf_file_path();
|
||||
return response()->download($file, basename($file), ['Cache-Control:' => 'no-cache'])->deleteFileAfterSend(true);
|
||||
// $file = $credit->pdf_file_path();
|
||||
$file = $credit->service()->getCreditPdf($credit->invitations->first());
|
||||
|
||||
// return response()->download($file, basename($file), ['Cache-Control:' => 'no-cache'])->deleteFileAfterSend(true);
|
||||
|
||||
return response()->streamDownload(function () use($file) {
|
||||
echo Storage::get($file);
|
||||
}, basename($file));
|
||||
break;
|
||||
case 'archive':
|
||||
$this->credit_repository->archive($credit);
|
||||
@ -585,9 +592,12 @@ class CreditController extends BaseController
|
||||
// $contact = $invitation->contact;
|
||||
$credit = $invitation->credit;
|
||||
|
||||
$file_path = $credit->service()->getCreditPdf($invitation);
|
||||
|
||||
return response()->download($file_path, basename($file_path), ['Cache-Control:' => 'no-cache'])->deleteFileAfterSend(true);
|
||||
$file = $credit->service()->getCreditPdf($invitation);
|
||||
|
||||
return response()->streamDownload(function () use($file) {
|
||||
echo Storage::get($file);
|
||||
}, basename($file));
|
||||
// return response()->download($file_path, basename($file_path), ['Cache-Control:' => 'no-cache'])->deleteFileAfterSend(true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -672,8 +672,17 @@ class InvoiceController extends BaseController
|
||||
break;
|
||||
case 'download':
|
||||
|
||||
$file = $invoice->pdf_file_path();
|
||||
return response()->download($file, basename($file), ['Cache-Control:' => 'no-cache'])->deleteFileAfterSend(true);
|
||||
// $file = $invoice->pdf_file_path();
|
||||
// return response()->download($file, basename($file), ['Cache-Control:' => 'no-cache'])->deleteFileAfterSend(true);
|
||||
|
||||
$file = $invoice->service()->getInvoicePdf();
|
||||
|
||||
// return response()->download(Storage::get($file), basename($file), ['Cache-Control:' => 'no-cache'])->deleteFileAfterSend(true);
|
||||
|
||||
return response()->streamDownload(function () use($file) {
|
||||
echo Storage::get($file);
|
||||
}, basename($file));
|
||||
|
||||
|
||||
break;
|
||||
case 'restore':
|
||||
@ -722,10 +731,11 @@ class InvoiceController extends BaseController
|
||||
}
|
||||
|
||||
//touch reminder1,2,3_sent + last_sent here if the email is a reminder.
|
||||
$invoice->service()->touchReminder($this->reminder_template)->deletePdf()->save();
|
||||
//$invoice->service()->touchReminder($this->reminder_template)->deletePdf()->save();
|
||||
$invoice->service()->touchReminder($this->reminder_template)->markSent()->save();
|
||||
|
||||
$invoice->invitations->load('contact.client.country', 'invoice.client.country', 'invoice.company')->each(function ($invitation) use ($invoice) {
|
||||
EmailEntity::dispatch($invitation, $invoice->company, $this->reminder_template);
|
||||
EmailEntity::dispatch($invitation, $invoice->company, $this->reminder_template)->delay(now()->addSeconds(30));
|
||||
});
|
||||
|
||||
if ($invoice->invitations->count() >= 1) {
|
||||
@ -795,8 +805,11 @@ class InvoiceController extends BaseController
|
||||
|
||||
$file = $invoice->service()->getInvoicePdf($contact);
|
||||
|
||||
return response()->download($file, basename($file), ['Cache-Control:' => 'no-cache'])->deleteFileAfterSend(true);
|
||||
// return response()->download(Storage::get($file), basename($file), ['Cache-Control:' => 'no-cache'])->deleteFileAfterSend(true);
|
||||
|
||||
return response()->streamDownload(function () use($file) {
|
||||
echo Storage::get($file);
|
||||
}, basename($file));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -848,7 +861,10 @@ class InvoiceController extends BaseController
|
||||
|
||||
$file = $invoice->service()->getInvoiceDeliveryNote($invoice, $invoice->invitations->first()->contact);
|
||||
|
||||
return response()->download($file, basename($file), ['Cache-Control:' => 'no-cache'])->deleteFileAfterSend(true);
|
||||
// return response()->download($file, basename($file), ['Cache-Control:' => 'no-cache'])->deleteFileAfterSend(true);
|
||||
return response()->streamDownload(function () use($file) {
|
||||
echo Storage::get($file);
|
||||
}, basename($file));
|
||||
|
||||
}
|
||||
|
||||
|
@ -39,6 +39,7 @@ use App\Utils\Traits\MakesHash;
|
||||
use App\Utils\Traits\SavesDocuments;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Response;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
/**
|
||||
* Class QuoteController.
|
||||
@ -676,8 +677,14 @@ class QuoteController extends BaseController
|
||||
break;
|
||||
case 'download':
|
||||
|
||||
$file = $quote->pdf_file_path();
|
||||
return response()->download($file, basename($file), ['Cache-Control:' => 'no-cache'])->deleteFileAfterSend(true);
|
||||
//$file = $quote->pdf_file_path();
|
||||
$file = $quote->service()->getQuotePdf();
|
||||
|
||||
return response()->streamDownload(function () use($file) {
|
||||
echo Storage::get($file);
|
||||
}, basename($file));
|
||||
|
||||
//return response()->download($file, basename($file), ['Cache-Control:' => 'no-cache'])->deleteFileAfterSend(true);
|
||||
|
||||
break;
|
||||
case 'restore':
|
||||
@ -728,9 +735,14 @@ class QuoteController extends BaseController
|
||||
$contact = $invitation->contact;
|
||||
$quote = $invitation->quote;
|
||||
|
||||
$file_path = $quote->service()->getQuotePdf($contact);
|
||||
$file = $quote->service()->getQuotePdf($contact);
|
||||
nlog($file);
|
||||
|
||||
return response()->download($file_path, basename($file_path), ['Cache-Control:' => 'no-cache'])->deleteFileAfterSend(true);
|
||||
return response()->streamDownload(function () use($file) {
|
||||
echo Storage::get($file);
|
||||
}, basename($file));
|
||||
|
||||
// return response()->download($file_path, basename($file_path), ['Cache-Control:' => 'no-cache'])->deleteFileAfterSend(true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -33,6 +33,7 @@ use App\Utils\Traits\SavesDocuments;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Response;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
/**
|
||||
* Class RecurringInvoiceController.
|
||||
@ -500,9 +501,12 @@ class RecurringInvoiceController extends BaseController
|
||||
$contact = $invitation->contact;
|
||||
$recurring_invoice = $invitation->recurring_invoice;
|
||||
|
||||
$file_path = $recurring_invoice->service()->getInvoicePdf($contact);
|
||||
$file = $recurring_invoice->service()->getInvoicePdf($contact);
|
||||
|
||||
return response()->streamDownload(function () use($file) {
|
||||
echo Storage::get($file);
|
||||
}, basename($file));
|
||||
|
||||
return response()->download($file_path, basename($file_path), ['Cache-Control:' => 'no-cache'])->deleteFileAfterSend(true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -241,6 +241,11 @@ class SetupController extends Controller
|
||||
$pdf->setChromiumPath(config('ninja.snappdf_chromium_path'));
|
||||
}
|
||||
|
||||
if (config('ninja.snappdf_chromium_arguments')) {
|
||||
$pdf->clearChromiumArguments();
|
||||
$pdf->addChromiumArguments(config('ninja.snappdf_chromium_arguments'));
|
||||
}
|
||||
|
||||
$pdf = $pdf
|
||||
->setHtml('GENERATING PDFs WORKS! Thank you for using Invoice Ninja!')
|
||||
->generate();
|
||||
|
@ -60,12 +60,6 @@ class StripeConnectController extends BaseController
|
||||
$redirect_uri = 'https://invoicing.co/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}";
|
||||
|
||||
// if($email = $request->getContact()->email)
|
||||
// $endpoint .= "&stripe_user[email]={$email}";
|
||||
|
||||
// $company_name = str_replace(" ", "_", $company->present()->name());
|
||||
// $endpoint .= "&stripe_user[business_name]={$company_name}";
|
||||
|
||||
return redirect($endpoint);
|
||||
}
|
||||
|
||||
@ -87,18 +81,24 @@ class StripeConnectController extends BaseController
|
||||
|
||||
}
|
||||
|
||||
// nlog($response);
|
||||
|
||||
$company = Company::where('company_key', $request->getTokenContent()['company_key'])->first();
|
||||
|
||||
$company_gateway = CompanyGatewayFactory::create($company->id, $company->owner()->id);
|
||||
$fees_and_limits = new \stdClass;
|
||||
$fees_and_limits->{GatewayType::CREDIT_CARD} = new FeesAndLimits;
|
||||
$company_gateway->gateway_key = 'd14dd26a47cecc30fdd65700bfb67b34';
|
||||
$company_gateway->fees_and_limits = $fees_and_limits;
|
||||
$company_gateway->setConfig([]);
|
||||
$company_gateway->token_billing = 'always';
|
||||
// $company_gateway->save();
|
||||
$company_gateway = CompanyGateway::query()
|
||||
->where('gateway_key', 'd14dd26a47cecc30fdd65700bfb67b34')
|
||||
->where('company_id', $company->id)
|
||||
->first();
|
||||
|
||||
if(!$company_gateway)
|
||||
{
|
||||
$company_gateway = CompanyGatewayFactory::create($company->id, $company->owner()->id);
|
||||
$fees_and_limits = new \stdClass;
|
||||
$fees_and_limits->{GatewayType::CREDIT_CARD} = new FeesAndLimits;
|
||||
$company_gateway->gateway_key = 'd14dd26a47cecc30fdd65700bfb67b34';
|
||||
$company_gateway->fees_and_limits = $fees_and_limits;
|
||||
$company_gateway->setConfig([]);
|
||||
$company_gateway->token_billing = 'always';
|
||||
// $company_gateway->save();
|
||||
}
|
||||
|
||||
$payload = [
|
||||
'account_id' => $response->stripe_user_id,
|
||||
@ -111,18 +111,6 @@ class StripeConnectController extends BaseController
|
||||
"access_token" => $response->access_token
|
||||
];
|
||||
|
||||
/* Link account if existing account exists */
|
||||
// if($account_id = $this->checkAccountAlreadyLinkToEmail($company_gateway, $request->getContact()->email)) {
|
||||
|
||||
// $payload['account_id'] = $account_id;
|
||||
// $payload['stripe_user_id'] = $account_id;
|
||||
// $company_gateway->setConfig($payload);
|
||||
// $company_gateway->save();
|
||||
|
||||
// return view('auth.connect.existing');
|
||||
|
||||
// }
|
||||
|
||||
$company_gateway->setConfig($payload);
|
||||
$company_gateway->save();
|
||||
|
||||
|
@ -35,6 +35,9 @@ class StripeController extends BaseController
|
||||
public function import()
|
||||
{
|
||||
|
||||
return response()->json(['message' => 'Processing'], 200);
|
||||
|
||||
|
||||
if(auth()->user()->isAdmin())
|
||||
{
|
||||
|
||||
|
@ -51,10 +51,10 @@ class QueryLogging
|
||||
$count = count($queries);
|
||||
$timeEnd = microtime(true);
|
||||
$time = $timeEnd - $timeStart;
|
||||
|
||||
//nlog($request->method().' - '.urldecode($request->url()).": $count queries - ".$time);
|
||||
// if($count > 50)
|
||||
//nlog($queries);
|
||||
|
||||
if($count > 150)
|
||||
nlog($queries);
|
||||
|
||||
$ip = '';
|
||||
|
||||
if(request()->header('Cf-Connecting-Ip'))
|
||||
|
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests\ClientPortal\Contact;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class ContactPasswordResetRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'email' => 'required',
|
||||
];
|
||||
}
|
||||
|
||||
}
|
@ -67,8 +67,8 @@ class UpdateCompanyRequest extends Request
|
||||
{
|
||||
$input = $this->all();
|
||||
|
||||
// if(array_key_exists('portal_domain', $input) && strlen($input['portal_domain']) > 1)
|
||||
// $input['portal_domain'] = str_replace("http:", "https:", $input['portal_domain']);
|
||||
if(Ninja::isHosted() && array_key_exists('portal_domain', $input) && strlen($input['portal_domain']) > 1)
|
||||
$input['portal_domain'] = $this->addScheme($input['portal_domain']);
|
||||
|
||||
if (array_key_exists('settings', $input)) {
|
||||
$input['settings'] = $this->filterSaveableSettings($input['settings']);
|
||||
@ -105,4 +105,15 @@ class UpdateCompanyRequest extends Request
|
||||
|
||||
return $settings;
|
||||
}
|
||||
|
||||
private function addScheme($url, $scheme = 'https://')
|
||||
{
|
||||
|
||||
$url = str_replace("http://", "", $url);
|
||||
|
||||
$url = parse_url($url, PHP_URL_SCHEME) === null ? $scheme . $url : $url;
|
||||
|
||||
return rtrim($url, '/');
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -28,12 +28,7 @@ class ImportJsonRequest extends Request
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
// 'import_type' => 'required',
|
||||
// 'files' => 'required_without:hash|array|min:1|max:6',
|
||||
// 'hash' => 'nullable|string',
|
||||
// 'column_map' => 'required_with:hash|array',
|
||||
// 'skip_header' => 'required_with:hash|boolean',
|
||||
// 'files.*' => 'file|mimes:csv,txt',
|
||||
'files' => 'file|mimes:zip',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ use App\Models\Account;
|
||||
use App\Models\Timezone;
|
||||
use App\Notifications\Ninja\NewAccountCreated;
|
||||
use App\Utils\Ninja;
|
||||
use App\Utils\Traits\User\LoginCache;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
@ -39,6 +40,7 @@ use Turbo124\Beacon\Facades\LightLogs;
|
||||
class CreateAccount
|
||||
{
|
||||
use Dispatchable;
|
||||
use LoginCache;
|
||||
|
||||
protected $request;
|
||||
|
||||
@ -77,9 +79,6 @@ class CreateAccount
|
||||
|
||||
$sp794f3f->save();
|
||||
|
||||
if(Ninja::isHosted())
|
||||
$sp794f3f->startTrial('pro');
|
||||
|
||||
$sp035a66 = CreateCompany::dispatchNow($this->request, $sp794f3f);
|
||||
$sp035a66->load('account');
|
||||
$sp794f3f->default_company_id = $sp035a66->id;
|
||||
@ -95,6 +94,8 @@ class CreateAccount
|
||||
}
|
||||
|
||||
$spaa9f78->setCompany($sp035a66);
|
||||
$this->setLoginCache($spaa9f78);
|
||||
|
||||
$spafe62e = isset($this->request['token_name']) ? $this->request['token_name'] : request()->server('HTTP_USER_AGENT');
|
||||
$sp2d97e8 = CreateCompanyToken::dispatchNow($sp035a66, $spaa9f78, $spafe62e);
|
||||
|
||||
|
@ -221,8 +221,8 @@ class CompanyImport implements ShouldQueue
|
||||
private function unzipFile()
|
||||
{
|
||||
|
||||
if(mime_content_type(Storage::path($this->file_location)) == 'text/plain')
|
||||
return Storage::path($this->file_location);
|
||||
// if(mime_content_type(Storage::path($this->file_location)) == 'text/plain')
|
||||
// return Storage::path($this->file_location);
|
||||
|
||||
$path = TempFile::filePath(Storage::get($this->file_location), basename($this->file_location));
|
||||
|
||||
|
@ -86,9 +86,8 @@ class CreateEntityPdf implements ShouldQueue
|
||||
|
||||
$this->contact = $invitation->contact;
|
||||
|
||||
$this->disk = $disk;
|
||||
|
||||
// $this->disk = $disk ?? config('filesystems.default');
|
||||
$this->disk = Ninja::isHosted() ? config('filesystems.default') : $disk;
|
||||
|
||||
}
|
||||
|
||||
public function handle()
|
||||
@ -201,11 +200,9 @@ class CreateEntityPdf implements ShouldQueue
|
||||
|
||||
if(!Storage::disk($this->disk)->exists($path))
|
||||
Storage::disk($this->disk)->makeDirectory($path, 0775);
|
||||
|
||||
Storage::disk($this->disk)->put($file_path, $pdf);
|
||||
|
||||
nlog($file_path);
|
||||
|
||||
Storage::disk($this->disk)->put($file_path, $pdf);
|
||||
|
||||
}
|
||||
catch(\Exception $e)
|
||||
{
|
||||
|
@ -190,6 +190,9 @@ class Import implements ShouldQueue
|
||||
{
|
||||
set_time_limit(0);
|
||||
|
||||
nlog("Starting Migration");
|
||||
nlog($this->user->email);
|
||||
|
||||
auth()->login($this->user, false);
|
||||
auth()->user()->setCompany($this->company);
|
||||
|
||||
@ -333,7 +336,7 @@ class Import implements ShouldQueue
|
||||
|
||||
$data = $this->transformCompanyData($data);
|
||||
|
||||
if(Ninja::isHosted() && strlen($data['subdomain']) > 1) {
|
||||
if(Ninja::isHosted()) {
|
||||
|
||||
if(!MultiDB::checkDomainAvailable($data['subdomain']))
|
||||
$data['subdomain'] = MultiDB::randomSubdomainGenerator();
|
||||
@ -364,6 +367,10 @@ class Import implements ShouldQueue
|
||||
unset($data['referral_code']);
|
||||
}
|
||||
|
||||
if (isset($data['custom_fields']) && is_array($data['custom_fields'])) {
|
||||
$data['custom_fields'] = $this->parseCustomFields($data['custom_fields']);
|
||||
}
|
||||
|
||||
$company_repository = new CompanyRepository();
|
||||
$company_repository->save($data, $this->company);
|
||||
|
||||
@ -385,6 +392,34 @@ class Import implements ShouldQueue
|
||||
$company_repository = null;
|
||||
}
|
||||
|
||||
private function parseCustomFields($fields) :array
|
||||
{
|
||||
|
||||
if(array_key_exists('account1', $fields))
|
||||
$fields['company1'] = $fields['account1'];
|
||||
|
||||
if(array_key_exists('account2', $fields))
|
||||
$fields['company2'] = $fields['account2'];
|
||||
|
||||
if(array_key_exists('invoice1', $fields))
|
||||
$fields['surcharge1'] = $fields['invoice1'];
|
||||
|
||||
if(array_key_exists('invoice2', $fields))
|
||||
$fields['surcharge2'] = $fields['invoice2'];
|
||||
|
||||
if(array_key_exists('invoice_text1', $fields))
|
||||
$fields['invoice1'] = $fields['invoice_text1'];
|
||||
|
||||
if(array_key_exists('invoice_text2', $fields))
|
||||
$fields['invoice2'] = $fields['invoice_text2'];
|
||||
|
||||
foreach ($fields as &$value) {
|
||||
$value = (string) $value;
|
||||
}
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
private function transformCompanyData(array $data): array
|
||||
{
|
||||
$company_settings = CompanySettings::defaults();
|
||||
@ -1321,7 +1356,7 @@ class Import implements ShouldQueue
|
||||
$modified['fees_and_limits'] = $this->cleanFeesAndLimits($modified['fees_and_limits']);
|
||||
}
|
||||
|
||||
else if(Ninja::isHosted() && $modified['gateway_key'] == 'd14dd26a37cecc30fdd65700bfb55b23'){
|
||||
if(Ninja::isHosted() && $modified['gateway_key'] == 'd14dd26a37cecc30fdd65700bfb55b23'){
|
||||
$modified['gateway_key'] = 'd14dd26a47cecc30fdd65700bfb67b34';
|
||||
$modified['fees_and_limits'] = [];
|
||||
}
|
||||
|
@ -50,7 +50,7 @@ $user_id = array_key_exists('user_id', $event->event_vars) ? $event->event_vars[
|
||||
$fields->subscription_id = $subscription->id;
|
||||
$fields->user_id = $user_id;
|
||||
$fields->company_id = $subscription->company_id;
|
||||
$fields->activity_type_id = Activity::ARCHIVE_SUBSCRIPTIOn;
|
||||
$fields->activity_type_id = Activity::ARCHIVE_SUBSCRIPTION;
|
||||
|
||||
$this->activity_repo->save($fields, $subscription, $event->event_vars);
|
||||
}
|
||||
|
@ -11,6 +11,7 @@
|
||||
|
||||
namespace App\Mail\Engine;
|
||||
|
||||
use App\Models\Account;
|
||||
use App\Utils\HtmlEngine;
|
||||
use App\Utils\Ninja;
|
||||
use App\Utils\Number;
|
||||
|
@ -12,6 +12,7 @@
|
||||
namespace App\Mail\Engine;
|
||||
|
||||
use App\DataMapper\EmailTemplateDefaults;
|
||||
use App\Jobs\Entity\CreateEntityPdf;
|
||||
use App\Models\Account;
|
||||
use App\Utils\HtmlEngine;
|
||||
use App\Utils\Ninja;
|
||||
@ -116,8 +117,6 @@ class InvoiceEmailEngine extends BaseEmailEngine
|
||||
else
|
||||
$this->setAttachments([$this->invoice->pdf_file_path($this->invitation)]);
|
||||
|
||||
// $this->setAttachments(['path' => $this->invoice->pdf_file_path(), 'name' => basename($this->invoice->pdf_file_path())]);
|
||||
|
||||
}
|
||||
|
||||
//attach third party documents
|
||||
|
@ -52,7 +52,7 @@ class SupportMessageSent extends Mailable
|
||||
|
||||
$account = auth()->user()->account;
|
||||
|
||||
$plan = $account->plan ?: 'Free Self Hosted';
|
||||
$plan = $account->plan ?: 'Forever Free';
|
||||
|
||||
$company = auth()->user()->company();
|
||||
$user = auth()->user();
|
||||
|
@ -13,6 +13,8 @@ namespace App\Models;
|
||||
|
||||
use App\DataMapper\ClientSettings;
|
||||
use App\DataMapper\CompanySettings;
|
||||
use App\DataMapper\FeesAndLimits;
|
||||
use App\Models\CompanyGateway;
|
||||
use App\Models\Presenters\ClientPresenter;
|
||||
use App\Services\Client\ClientService;
|
||||
use App\Utils\Traits\AppSetup;
|
||||
@ -396,61 +398,131 @@ class Client extends BaseModel implements HasLocalePreference
|
||||
*/
|
||||
public function getCreditCardGateway() :?CompanyGateway
|
||||
{
|
||||
$company_gateways = $this->getSetting('company_gateway_ids');
|
||||
// $company_gateways = $this->getSetting('company_gateway_ids');
|
||||
|
||||
/* It is very important to respect the order of the company_gateway_ids as they are ordered by priority*/
|
||||
if (strlen($company_gateways) >= 1) {
|
||||
$transformed_ids = $this->transformKeys(explode(',', $company_gateways));
|
||||
$gateways = $this->company
|
||||
->company_gateways
|
||||
->whereIn('id', $transformed_ids)
|
||||
->sortby(function ($model) use ($transformed_ids) {
|
||||
return array_search($model->id, $transformed_ids);
|
||||
});
|
||||
} else {
|
||||
$gateways = $this->company->company_gateways;
|
||||
}
|
||||
// /* It is very important to respect the order of the company_gateway_ids as they are ordered by priority*/
|
||||
// if (strlen($company_gateways) >= 1) {
|
||||
// $transformed_ids = $this->transformKeys(explode(',', $company_gateways));
|
||||
// $gateways = $this->company
|
||||
// ->company_gateways
|
||||
// ->whereIn('id', $transformed_ids)
|
||||
// ->sortby(function ($model) use ($transformed_ids) {
|
||||
// return array_search($model->id, $transformed_ids);
|
||||
// });
|
||||
// } else {
|
||||
// $gateways = $this->company->company_gateways;
|
||||
// }
|
||||
|
||||
foreach ($gateways as $gateway) {
|
||||
if (in_array(GatewayType::CREDIT_CARD, $gateway->driver($this)->gatewayTypeEnabled(GatewayType::CREDIT_CARD))) {
|
||||
return $gateway;
|
||||
// foreach ($gateways as $gateway) {
|
||||
// if (in_array(GatewayType::CREDIT_CARD, $gateway->driver($this)->gatewayTypeEnabled($gateway, GatewayType::CREDIT_CARD))) {
|
||||
// return $gateway;
|
||||
// }
|
||||
// }
|
||||
|
||||
// return null;
|
||||
//
|
||||
|
||||
$pms = $this->service()->getPaymentMethods(0);
|
||||
|
||||
foreach($pms as $pm)
|
||||
{
|
||||
|
||||
if($pm['gateway_type_id'] == GatewayType::CREDIT_CARD)
|
||||
{
|
||||
$cg = CompanyGateway::find($pm['company_gateway_id']);
|
||||
|
||||
if($cg && !property_exists($cg->fees_and_limits, GatewayType::CREDIT_CARD)){
|
||||
$fees_and_limits = $cg->fees_and_limits;
|
||||
$fees_and_limits->{GatewayType::CREDIT_CARD} = new FeesAndLimits;
|
||||
$cg->fees_and_limits = $fees_and_limits;
|
||||
$cg->save();
|
||||
}
|
||||
|
||||
if($cg && $cg->fees_and_limits->{GatewayType::CREDIT_CARD}->is_enabled)
|
||||
return $cg;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
return null;
|
||||
|
||||
|
||||
}
|
||||
|
||||
//todo refactor this - it is only searching for existing tokens
|
||||
public function getBankTransferGateway() :?CompanyGateway
|
||||
{
|
||||
$company_gateways = $this->getSetting('company_gateway_ids');
|
||||
$pms = $this->service()->getPaymentMethods(0);
|
||||
|
||||
if($this->currency()->code == 'USD' && in_array(GatewayType::BANK_TRANSFER, array_column($pms, 'gateway_type_id'))){
|
||||
|
||||
foreach($pms as $pm){
|
||||
|
||||
if($pm['gateway_type_id'] == GatewayType::BANK_TRANSFER)
|
||||
{
|
||||
$cg = CompanyGateway::find($pm['company_gateway_id']);
|
||||
|
||||
if($cg && !property_exists($cg->fees_and_limits, GatewayType::BANK_TRANSFER)){
|
||||
$fees_and_limits = $cg->fees_and_limits;
|
||||
$fees_and_limits->{GatewayType::BANK_TRANSFER} = new FeesAndLimits;
|
||||
$cg->fees_and_limits = $fees_and_limits;
|
||||
$cg->save();
|
||||
}
|
||||
|
||||
if($cg && $cg->fees_and_limits->{GatewayType::BANK_TRANSFER}->is_enabled)
|
||||
return $cg;
|
||||
}
|
||||
}
|
||||
|
||||
if (strlen($company_gateways) >= 1) {
|
||||
$transformed_ids = $this->transformKeys(explode(',', $company_gateways));
|
||||
$gateways = $this->company
|
||||
->company_gateways
|
||||
->whereIn('id', $transformed_ids)
|
||||
->sortby(function ($model) use ($transformed_ids) {
|
||||
return array_search($model->id, $transformed_ids);
|
||||
});
|
||||
} else {
|
||||
$gateways = $this->company->company_gateways;
|
||||
}
|
||||
|
||||
foreach ($gateways as $gateway) {
|
||||
if ($this->currency()->code == 'USD' && in_array(GatewayType::BANK_TRANSFER, $gateway->driver($this)->gatewayTypeEnabled(GatewayType::BANK_TRANSFER))) {
|
||||
return $gateway;
|
||||
if($this->currency()->code == 'EUR' && in_array(GatewayType::BANK_TRANSFER, array_column($pms, 'gateway_type_id'))){
|
||||
|
||||
foreach($pms as $pm){
|
||||
|
||||
if($pm['gateway_type_id'] == GatewayType::SEPA)
|
||||
{
|
||||
$cg = CompanyGateway::find($pm['company_gateway_id']);
|
||||
|
||||
if($cg && $cg->fees_and_limits->{GatewayType::SEPA}->is_enabled)
|
||||
return $cg;
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->currency()->code == 'EUR' && in_array(GatewayType::SEPA, $gateway->driver($this)->gatewayTypeEnabled(GatewayType::SEPA))) {
|
||||
return $gateway;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
// $company_gateways = $this->getSetting('company_gateway_ids');
|
||||
|
||||
// if (strlen($company_gateways) >= 1) {
|
||||
// $transformed_ids = $this->transformKeys(explode(',', $company_gateways));
|
||||
// $gateways = $this->company
|
||||
// ->company_gateways
|
||||
// ->whereIn('id', $transformed_ids)
|
||||
// ->sortby(function ($model) use ($transformed_ids) {
|
||||
// return array_search($model->id, $transformed_ids);
|
||||
// });
|
||||
// } else {
|
||||
// $gateways = $this->company->company_gateways;
|
||||
// }
|
||||
|
||||
// foreach ($gateways as $gateway) {
|
||||
// if ($this->currency()->code == 'USD' && in_array(GatewayType::BANK_TRANSFER, $gateway->driver($this)->gatewayTypeEnabled(GatewayType::BANK_TRANSFER))) {
|
||||
// return $gateway;
|
||||
// }
|
||||
|
||||
// if ($this->currency()->code == 'EUR' && in_array(GatewayType::SEPA, $gateway->driver($this)->gatewayTypeEnabled(GatewayType::SEPA))) {
|
||||
// return $gateway;
|
||||
// }
|
||||
// }
|
||||
|
||||
// return null;
|
||||
}
|
||||
|
||||
public function getBankTransferMethodType()
|
||||
{
|
||||
|
||||
if ($this->currency()->code == 'USD') {
|
||||
return GatewayType::BANK_TRANSFER;
|
||||
}
|
||||
|
@ -59,6 +59,17 @@ class CompanyGateway extends BaseModel
|
||||
16 => ['card' => 'images/credit_cards/Test-Discover-Icon.png', 'text' => 'Discover'],
|
||||
];
|
||||
|
||||
|
||||
// const TYPE_PAYPAL = 300;
|
||||
// const TYPE_STRIPE = 301;
|
||||
// const TYPE_LEDGER = 302;
|
||||
// const TYPE_FAILURE = 303;
|
||||
// const TYPE_CHECKOUT = 304;
|
||||
// const TYPE_AUTHORIZE = 305;
|
||||
// const TYPE_CUSTOM = 306;
|
||||
// const TYPE_BRAINTREE = 307;
|
||||
// const TYPE_WEPAY = 309;
|
||||
|
||||
public $gateway_consts = [
|
||||
'38f2c48af60c7dd69e04248cbb24c36e' => 300,
|
||||
'd14dd26a37cecc30fdd65700bfb55b23' => 301,
|
||||
@ -66,6 +77,8 @@ class CompanyGateway extends BaseModel
|
||||
'3b6621f970ab18887c4f6dca78d3f8bb' => 305,
|
||||
'54faab2ab6e3223dbe848b1686490baa' => 306,
|
||||
'd14dd26a47cecc30fdd65700bfb67b34' => 301,
|
||||
'8fdeed552015b3c7b44ed6c8ebd9e992' => 309,
|
||||
'f7ec488676d310683fb51802d076d713' => 307,
|
||||
];
|
||||
|
||||
protected $touches = [];
|
||||
|
@ -161,6 +161,8 @@ class User extends Authenticatable implements MustVerifyEmail
|
||||
public function setCompany($company)
|
||||
{
|
||||
$this->company = $company;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -202,6 +202,7 @@ class AuthorizeCreditCard
|
||||
private function processFailedResponse($data, $request)
|
||||
{
|
||||
$response = $data['response'];
|
||||
$amount = array_key_exists('amount_with_fee', $data) ? $data['amount_with_fee'] : 0;
|
||||
|
||||
PaymentFailureMailer::dispatch($this->authorize->client, $response->getTransactionResponse()->getTransId(), $this->authorize->client->company, $data['amount_with_fee']);
|
||||
|
||||
|
@ -114,7 +114,7 @@ class StripePaymentDriver extends BaseDriver
|
||||
public function gatewayTypes(): array
|
||||
{
|
||||
$types = [
|
||||
GatewayType::CRYPTO,
|
||||
// GatewayType::CRYPTO,
|
||||
GatewayType::CREDIT_CARD
|
||||
];
|
||||
|
||||
|
@ -49,28 +49,11 @@ class CompanyRepository extends BaseRepository
|
||||
private function parseCustomFields($fields) :array
|
||||
{
|
||||
|
||||
if(array_key_exists('account1', $fields))
|
||||
$fields['company1'] = $fields['account1'];
|
||||
|
||||
if(array_key_exists('account2', $fields))
|
||||
$fields['company2'] = $fields['account2'];
|
||||
|
||||
if(array_key_exists('invoice1', $fields))
|
||||
$fields['surcharge1'] = $fields['invoice1'];
|
||||
|
||||
if(array_key_exists('invoice2', $fields))
|
||||
$fields['surcharge2'] = $fields['invoice2'];
|
||||
|
||||
if(array_key_exists('invoice_text1', $fields))
|
||||
$fields['invoice1'] = $fields['invoice_text1'];
|
||||
|
||||
if(array_key_exists('invoice_text2', $fields))
|
||||
$fields['invoice2'] = $fields['invoice_text2'];
|
||||
|
||||
foreach ($fields as &$value) {
|
||||
$value = (string) $value;
|
||||
}
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -60,14 +60,16 @@ class ClientService
|
||||
return Number::roundValue($credits->sum('balance'), $this->client->currency()->precision);
|
||||
}
|
||||
|
||||
public function getCredits() :Collection
|
||||
public function getCredits()
|
||||
{
|
||||
return $this->client->credits()
|
||||
->where('is_deleted', false)
|
||||
->where('balance', '>', 0)
|
||||
->whereDate('due_date', '<=', now()->format('Y-m-d'))
|
||||
->orWhere('due_date', NULL)
|
||||
->orderBy('created_at','ASC');
|
||||
->where(function ($query){
|
||||
$query->whereDate('due_date', '<=', now()->format('Y-m-d'))
|
||||
->orWhereNull('due_date');
|
||||
})
|
||||
->orderBy('created_at','ASC')->get();
|
||||
}
|
||||
|
||||
public function getPaymentMethods(float $amount)
|
||||
|
@ -41,10 +41,13 @@ class GetCreditPdf extends AbstractService
|
||||
|
||||
$file_path = $path.$this->credit->numberFormatter().'.pdf';
|
||||
|
||||
$disk = 'public';
|
||||
// $disk = 'public';
|
||||
$disk = config('filesystems.default');
|
||||
|
||||
$file_path = CreateEntityPdf::dispatchNow($this->invitation);
|
||||
|
||||
return Storage::disk($disk)->path($file_path);
|
||||
nlog($file_path);
|
||||
return $file_path;
|
||||
// return Storage::disk($disk)->path($file_path);
|
||||
}
|
||||
}
|
||||
|
@ -49,9 +49,9 @@ class GenerateDeliveryNote
|
||||
|
||||
$this->contact = $contact;
|
||||
|
||||
$this->disk = 'public';
|
||||
// $this->disk = 'public';
|
||||
|
||||
// $this->disk = $disk ?? config('filesystems.default');
|
||||
$this->disk = $disk ?? config('filesystems.default');
|
||||
}
|
||||
|
||||
public function run()
|
||||
@ -111,7 +111,8 @@ class GenerateDeliveryNote
|
||||
|
||||
Storage::disk($this->disk)->put($file_path, $pdf);
|
||||
|
||||
return Storage::disk($this->disk)->path($file_path);
|
||||
//return Storage::disk($this->disk)->path($file_path);
|
||||
|
||||
return $file_path;
|
||||
}
|
||||
}
|
||||
|
@ -39,7 +39,8 @@ class GetInvoicePdf extends AbstractService
|
||||
|
||||
$file_path = $path.$this->invoice->numberFormatter().'.pdf';
|
||||
|
||||
$disk = 'public';
|
||||
// $disk = 'public';
|
||||
$disk = config('filesystems.default');
|
||||
|
||||
$file = Storage::disk($disk)->exists($file_path);
|
||||
|
||||
@ -47,6 +48,8 @@ class GetInvoicePdf extends AbstractService
|
||||
$file_path = CreateEntityPdf::dispatchNow($invitation);
|
||||
}
|
||||
|
||||
return Storage::disk($disk)->path($file_path);
|
||||
// return Storage::disk($disk)->path($file_path);
|
||||
//
|
||||
return $file_path;
|
||||
}
|
||||
}
|
||||
|
@ -119,6 +119,7 @@ class PaymentService
|
||||
->service()
|
||||
->getCredits();
|
||||
|
||||
|
||||
foreach ($credits as $credit) {
|
||||
//starting invoice balance
|
||||
$invoice_balance = $invoice->balance;
|
||||
|
@ -39,10 +39,12 @@ class GetQuotePdf extends AbstractService
|
||||
|
||||
$file_path = $path.$this->quote->numberFormatter().'.pdf';
|
||||
|
||||
$disk = 'public';
|
||||
|
||||
// $disk = 'public';
|
||||
$disk = config('filesystems.default');
|
||||
|
||||
$file_path = CreateEntityPdf::dispatchNow($invitation);
|
||||
|
||||
return Storage::disk($disk)->path($file_path);
|
||||
return $file_path;
|
||||
//return Storage::disk($disk)->path($file_path);
|
||||
}
|
||||
}
|
||||
|
@ -51,6 +51,11 @@ class QuoteService
|
||||
|
||||
$this->quote->fresh();
|
||||
|
||||
if ($this->quote->client->getSetting('auto_archive_quote')) {
|
||||
$quote_repo = new QuoteRepository();
|
||||
$quote_repo->archive($this->quote);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@ -129,10 +134,6 @@ class QuoteService
|
||||
public function convertToInvoice()
|
||||
{
|
||||
|
||||
//to prevent circular references we need to explicit call this here.
|
||||
// $mark_approved = new MarkApproved($this->quote->client);
|
||||
// $this->quote = $mark_approved->run($this->quote);
|
||||
|
||||
$this->convert();
|
||||
|
||||
$this->invoice->service()->createInvitations();
|
||||
|
@ -41,14 +41,14 @@ class GetInvoicePdf extends AbstractService
|
||||
|
||||
$file_path = $path.$this->entity->hashed_id.'.pdf';
|
||||
|
||||
$disk = 'public';
|
||||
$disk = config('filesystems.default');
|
||||
|
||||
$file = Storage::disk($disk)->exists($file_path);
|
||||
|
||||
if (! $file) {
|
||||
$file_path = CreateEntityPdf::dispatchNow($invitation);
|
||||
}
|
||||
|
||||
return Storage::disk($disk)->path($file_path);
|
||||
|
||||
return $file_path;
|
||||
}
|
||||
}
|
||||
|
@ -34,6 +34,11 @@ trait PdfMaker
|
||||
$pdf->setChromiumPath(config('ninja.snappdf_chromium_path'));
|
||||
}
|
||||
|
||||
if (config('ninja.snappdf_chromium_arguments')) {
|
||||
$pdf->clearChromiumArguments();
|
||||
$pdf->addChromiumArguments(config('ninja.snappdf_chromium_arguments'));
|
||||
}
|
||||
|
||||
$generated = $pdf
|
||||
->setHtml($html)
|
||||
->generate();
|
||||
|
@ -37,9 +37,13 @@ trait SubscriptionHooker
|
||||
|
||||
nlog("method name must be a string");
|
||||
nlog($subscription->webhook_configuration['post_purchase_rest_method']);
|
||||
nlog($subscription->webhook_configuration['post_purchase_url']);
|
||||
|
||||
$post_purchase_rest_method = (string)$subscription->webhook_configuration['post_purchase_rest_method'];
|
||||
$post_purchase_url = (string)$subscription->webhook_configuration['post_purchase_url'];
|
||||
|
||||
try {
|
||||
$response = $client->{$subscription->webhook_configuration['post_purchase_rest_method']}($subscription->webhook_configuration['post_purchase_url'],[
|
||||
$response = $client->{$post_purchase_rest_method}($post_purchase_url,[
|
||||
RequestOptions::JSON => ['body' => $body], RequestOptions::ALLOW_REDIRECTS => false
|
||||
]);
|
||||
|
||||
|
@ -33,7 +33,7 @@
|
||||
"asm/php-ansible": "dev-main",
|
||||
"authorizenet/authorizenet": "^2.0",
|
||||
"bacon/bacon-qr-code": "^2.0",
|
||||
"beganovich/snappdf": "^1.0",
|
||||
"beganovich/snappdf": "^1.7",
|
||||
"braintree/braintree_php": "^6.0",
|
||||
"checkout/checkout-sdk-php": "^1.0",
|
||||
"cleverit/ubl_invoice": "^1.3",
|
||||
@ -47,6 +47,7 @@
|
||||
"google/apiclient": "^2.7",
|
||||
"guzzlehttp/guzzle": "^7.0.1",
|
||||
"hashids/hashids": "^4.0",
|
||||
"hedii/laravel-gelf-logger": "^6.0",
|
||||
"intervention/image": "^2.5",
|
||||
"laracasts/presenter": "^0.2.1",
|
||||
"laravel/framework": "^8.0",
|
||||
|
705
composer.lock
generated
705
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@ -102,8 +102,72 @@ return [
|
||||
|
||||
'invoiceninja' => [
|
||||
'driver' => 'single',
|
||||
'level' => 'debug',
|
||||
'path' => storage_path('logs/invoiceninja.log'),
|
||||
],
|
||||
|
||||
'gelf' => [
|
||||
'driver' => 'custom',
|
||||
|
||||
|
||||
'via' => \Hedii\LaravelGelfLogger\GelfLoggerFactory::class,
|
||||
|
||||
// This optional option determines the processors that should be
|
||||
// pushed to the handler. This option is useful to modify a field
|
||||
// in the log context (see NullStringProcessor), or to add extra
|
||||
// data. Each processor must be a callable or an object with an
|
||||
// __invoke method: see monolog documentation about processors.
|
||||
// Default is an empty array.
|
||||
'processors' => [
|
||||
\Hedii\LaravelGelfLogger\Processors\NullStringProcessor::class,
|
||||
// another processor...
|
||||
],
|
||||
|
||||
// This optional option determines the minimum "level" a message
|
||||
// must be in order to be logged by the channel. Default is 'debug'
|
||||
'level' => 'debug',
|
||||
|
||||
// This optional option determines the channel name sent with the
|
||||
// message in the 'facility' field. Default is equal to app.env
|
||||
// configuration value
|
||||
'name' => 'my-custom-name',
|
||||
|
||||
// This optional option determines the system name sent with the
|
||||
// message in the 'source' field. When forgotten or set to null,
|
||||
// the current hostname is used.
|
||||
'system_name' => null,
|
||||
|
||||
// This optional option determines if you want the UDP, TCP or HTTP
|
||||
// transport for the gelf log messages. Default is UDP
|
||||
'transport' => 'udp',
|
||||
|
||||
// This optional option determines the host that will receive the
|
||||
// gelf log messages. Default is 127.0.0.1
|
||||
'host' => env('GRAYLOG_SERVER', '127.0.0.1'),
|
||||
|
||||
// This optional option determines the port on which the gelf
|
||||
// receiver host is listening. Default is 12201
|
||||
'port' => 12201,
|
||||
|
||||
// This optional option determines the path used for the HTTP
|
||||
// transport. When forgotten or set to null, default path '/gelf'
|
||||
// is used.
|
||||
'path' => null,
|
||||
|
||||
// This optional option determines the maximum length per message
|
||||
// field. When forgotten or set to null, the default value of
|
||||
// \Monolog\Formatter\GelfMessageFormatter::DEFAULT_MAX_LENGTH is
|
||||
// used (currently this value is 32766)
|
||||
'max_length' => null,
|
||||
|
||||
// This optional option determines the prefix for 'context' fields
|
||||
// from the Monolog record. Default is null (no context prefix)
|
||||
'context_prefix' => null,
|
||||
|
||||
// This optional option determines the prefix for 'extra' fields
|
||||
// from the Monolog record. Default is null (no extra prefix)
|
||||
'extra_prefix' => null,
|
||||
],
|
||||
],
|
||||
|
||||
];
|
||||
|
@ -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.2.10',
|
||||
'app_tag' => '5.2.10',
|
||||
'app_version' => '5.2.11',
|
||||
'app_tag' => '5.2.11',
|
||||
'minimum_client_version' => '5.0.16',
|
||||
'terms_version' => '1.0.1',
|
||||
'api_secret' => env('API_SECRET', ''),
|
||||
@ -142,6 +142,7 @@ return [
|
||||
'log_pdf_html' => env('LOG_PDF_HTML', false),
|
||||
'expanded_logging' => env('EXPANDED_LOGGING', false),
|
||||
'snappdf_chromium_path' => env('SNAPPDF_CHROMIUM_PATH', false),
|
||||
'snappdf_chromium_arguments' => env('SNAPPDF_CHROMIUM_ARGUMENTS', false),
|
||||
'v4_migration_version' => '4.5.35',
|
||||
'flutter_renderer' => env('FLUTTER_RENDERER', 'selfhosted-html'),
|
||||
'webcron_secret' => env('WEBCRON_SECRET', false),
|
||||
|
@ -1,8 +1,6 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class UpdateDesigns extends Migration
|
||||
{
|
File diff suppressed because it is too large
Load Diff
2
public/css/app.css
vendored
2
public/css/app.css
vendored
File diff suppressed because one or more lines are too long
4
public/flutter_service_worker.js
vendored
4
public/flutter_service_worker.js
vendored
@ -4,7 +4,7 @@ const TEMP = 'flutter-temp-cache';
|
||||
const CACHE_NAME = 'flutter-app-cache';
|
||||
const RESOURCES = {
|
||||
"favicon.png": "dca91c54388f52eded692718d5a98b8b",
|
||||
"main.dart.js": "ec0d23274ffa0ea4b6743fe88c16ccaa",
|
||||
"main.dart.js": "bb665dece53f7ac166766cfaaecff64e",
|
||||
"/": "23224b5e03519aaa87594403d54412cf",
|
||||
"manifest.json": "ce1b79950eb917ea619a0a30da27c6a3",
|
||||
"assets/packages/material_design_icons_flutter/lib/fonts/materialdesignicons-webfont.ttf": "174c02fc4609e8fc4389f5d21f16a296",
|
||||
@ -29,7 +29,7 @@ const RESOURCES = {
|
||||
"assets/assets/images/payment_types/solo.png": "2030c3ccaccf5d5e87916a62f5b084d6",
|
||||
"assets/assets/images/payment_types/visa.png": "3ddc4a4d25c946e8ad7e6998f30fd4e3",
|
||||
"assets/AssetManifest.json": "7e49562f32e24a9e2557fe4178a84b79",
|
||||
"version.json": "4d10e2258012cbb88b24009334a24f24",
|
||||
"version.json": "037c648413a92d1fa58410a4da834fbf",
|
||||
"icons/Icon-192.png": "bb1cf5f6982006952211c7c8404ffbed",
|
||||
"icons/Icon-512.png": "0f9aff01367f0a0c69773d25ca16ef35",
|
||||
"favicon.ico": "51636d3a390451561744c42188ccd628"
|
||||
|
124639
public/main.dart.js
vendored
124639
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
120183
public/main.foss.dart.js
vendored
120183
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
@ -1,6 +1,6 @@
|
||||
{
|
||||
"/js/app.js": "/js/app.js?id=696e8203d5e8e7cf5ff5",
|
||||
"/css/app.css": "/css/app.css?id=9d6698418e6cdd571d49",
|
||||
"/css/app.css": "/css/app.css?id=b1d2337c232fcb67a548",
|
||||
"/js/clients/invoices/action-selectors.js": "/js/clients/invoices/action-selectors.js?id=a09bb529b8e1826f13b4",
|
||||
"/js/clients/invoices/payment.js": "/js/clients/invoices/payment.js?id=8ce8955ba775ea5f47d1",
|
||||
"/js/clients/linkify-urls.js": "/js/clients/linkify-urls.js?id=0dc8c34010d09195d2f7",
|
||||
@ -21,7 +21,5 @@
|
||||
"/js/clients/shared/multiple-downloads.js": "/js/clients/shared/multiple-downloads.js?id=5c35d28cf0a3286e7c45",
|
||||
"/js/clients/shared/pdf.js": "/js/clients/shared/pdf.js?id=fc3055d6a099f523ea98",
|
||||
"/js/setup/setup.js": "/js/setup/setup.js?id=8d454e7090f119552a6c",
|
||||
"/css/card-js.min.css": "/css/card-js.min.css?id=62afeb675235451543ad",
|
||||
"/js/admin.js": "/js/admin.js?id=003930085af69b13a86a",
|
||||
"/css/admin.css": "/css/admin.css?id=0ca9742f0da64aa54214"
|
||||
"/css/card-js.min.css": "/css/card-js.min.css?id=62afeb675235451543ad"
|
||||
}
|
||||
|
@ -1 +1 @@
|
||||
{"app_name":"invoiceninja_flutter","version":"5.0.52","build_number":"52"}
|
||||
{"app_name":"invoiceninja_flutter","version":"5.0.53","build_number":"53"}
|
@ -4273,6 +4273,11 @@ $LANG = array(
|
||||
'already_default_payment_method' => 'This is your preferred way of paying.',
|
||||
'auto_bill_disabled' => 'Auto Bill Disabled',
|
||||
'select_payment_method' => 'Select a payment method:',
|
||||
'login_without_password' => 'Log in without password',
|
||||
'email_sent' => 'E-mail sent, please check your inbox.',
|
||||
'one_time_purchases' => 'One time purchases',
|
||||
'recurring_purchases' => 'Recurring purchases',
|
||||
'you_might_be_interested_in_following' => 'You might be interested in following',
|
||||
);
|
||||
|
||||
return $LANG;
|
||||
|
@ -34,7 +34,7 @@
|
||||
}
|
||||
|
||||
.company-logo {
|
||||
zoom: 50%; /** Adapt the zoom size, if you think it's necessary. **/
|
||||
max-width: 65%;
|
||||
}
|
||||
|
||||
#company-details,
|
||||
|
@ -29,7 +29,7 @@
|
||||
}
|
||||
|
||||
.company-logo {
|
||||
zoom: 50%; /** Adapt the zoom size, if you think it's necessary. **/
|
||||
max-width: 65%;
|
||||
}
|
||||
|
||||
.header-container > span {
|
||||
|
@ -29,7 +29,7 @@
|
||||
}
|
||||
|
||||
.company-logo {
|
||||
zoom: 50%; /** Adapt the zoom size, if you think it's necessary. **/
|
||||
max-width: 65%;
|
||||
}
|
||||
|
||||
#company-details {
|
||||
|
@ -29,7 +29,7 @@
|
||||
}
|
||||
|
||||
.company-logo {
|
||||
zoom: 50%; /** Adapt the zoom size, if you think it's necessary. **/
|
||||
max-width: 65%;
|
||||
}
|
||||
|
||||
.header-wrapper #client-details,
|
||||
|
@ -23,7 +23,7 @@
|
||||
}
|
||||
|
||||
.company-logo {
|
||||
zoom: 50%; /** Adapt the zoom size, if you think it's necessary. **/
|
||||
max-width: 65%;
|
||||
}
|
||||
|
||||
.company-logo-wrapper {
|
||||
|
@ -71,7 +71,7 @@
|
||||
}
|
||||
|
||||
.company-logo {
|
||||
zoom: 50%; /** Adapt the zoom size, if you think it's necessary. **/
|
||||
max-width: 65%;
|
||||
}
|
||||
|
||||
.entity-label {
|
||||
|
@ -61,7 +61,7 @@
|
||||
}
|
||||
|
||||
.company-logo {
|
||||
zoom: 50%; /** Adapt the zoom size, if you think it's necessary. **/
|
||||
max-width: 65%;
|
||||
}
|
||||
|
||||
#client-details {
|
||||
|
@ -28,7 +28,7 @@
|
||||
}
|
||||
|
||||
.company-logo {
|
||||
zoom: 50%; /** Adapt the zoom size, if you think it's necessary. **/
|
||||
max-width: 65%;
|
||||
}
|
||||
|
||||
.header-wrapper #company-address {
|
||||
|
@ -47,7 +47,7 @@
|
||||
}
|
||||
|
||||
.company-logo {
|
||||
zoom: 50%; /** Adapt the zoom size, if you think it's necessary. **/
|
||||
max-width: 65%;
|
||||
}
|
||||
|
||||
.contacts-wrapper {
|
||||
|
@ -54,7 +54,7 @@
|
||||
}
|
||||
|
||||
.company-logo {
|
||||
zoom: 50%;
|
||||
max-width: 65%;
|
||||
}
|
||||
|
||||
.header-invoice-number {
|
||||
|
@ -32,7 +32,8 @@
|
||||
<input type="email" name="email" id="email"
|
||||
class="input"
|
||||
value="{{ request()->query('email') ?? old('email') }}"
|
||||
autofocus>
|
||||
autofocus
|
||||
required>
|
||||
@error('email')
|
||||
<div class="validation validation-fail">
|
||||
{{ $message }}
|
||||
|
@ -1,5 +1,5 @@
|
||||
<div class="grid grid-cols-12">
|
||||
<div class="col-span-12 lg:col-span-6 bg-gray-50 flex flex-col items-center">
|
||||
<div class="col-span-12 xl:col-span-8 bg-gray-50 flex flex-col items-center">
|
||||
<div class="w-full p-10 lg:mt-24 md:max-w-3xl">
|
||||
<img class="h-8" src="{{ $subscription->company->present()->logo }}"
|
||||
alt="{{ $subscription->company->present()->name }}">
|
||||
@ -14,7 +14,7 @@
|
||||
<div class="flex flex-col mt-8">
|
||||
<p
|
||||
class="mb-4 uppercase leading-4 tracking-wide inline-flex items-center rounded-full text-xs font-medium">
|
||||
One-time purchases:
|
||||
{{ ctrans('texts.one_time_purchases') }}
|
||||
</p>
|
||||
|
||||
@foreach($subscription->service()->products() as $product)
|
||||
@ -25,7 +25,6 @@
|
||||
<div data-ref="price-and-quantity-container">
|
||||
<span
|
||||
data-ref="price">{{ \App\Utils\Number::formatMoney($product->price, $subscription->company) }}</span>
|
||||
{{-- <span data-ref="quantity" class="text-sm">(1x)</span>--}}
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
@ -36,7 +35,7 @@
|
||||
<div class="flex flex-col mt-8">
|
||||
<p
|
||||
class="mb-4 uppercase leading-4 tracking-wide inline-flex items-center rounded-full text-xs font-medium">
|
||||
Recurring purchases:
|
||||
{{ ctrans('texts.recurring_purchases') }}
|
||||
</p>
|
||||
|
||||
@foreach($subscription->service()->recurring_products() as $product)
|
||||
@ -82,12 +81,26 @@
|
||||
<span>{{ ctrans('texts.client_portal') }}</span>
|
||||
</a>
|
||||
@endif
|
||||
|
||||
@if($subscription->service()->getPlans()->count() - 1 > 1)
|
||||
<div class="flex flex-col mt-10">
|
||||
<p class="mb-4 uppercase leading-4 tracking-wide inline-flex items-center rounded-full text-xs font-medium">
|
||||
{{ ctrans('texts.you_might_be_interested_in_following') }}:
|
||||
</p>
|
||||
|
||||
<div class="mt-4 space-x-2">
|
||||
@foreach($subscription->service()->getPlans() as $_subscription)
|
||||
<a class="border mt-4 bg-white rounded py-2 px-4 hover:bg-gray-100 text-sm" target="_blank" href="{{ route('client.subscription.purchase', $_subscription->hashed_id) }}">{{ $_subscription->name }}</a>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-span-12 lg:col-span-6 bg-white lg:h-screen">
|
||||
<div class="grid grid-cols-12 flex flex-col p-10 lg:mt-48 lg:ml-16">
|
||||
<div class="col-span-12 w-full lg:col-span-6">
|
||||
<div class="col-span-12 xl:col-span-4 bg-white flex flex-col items-center lg:h-screen">
|
||||
<div class="w-full p-10 md:p-24 xl:mt-32 md:max-w-3xl">
|
||||
<div class="col-span-12 w-full xl:col-span-9">
|
||||
<h2 class="text-2xl font-bold tracking-wide">{{ $heading_text ?? ctrans('texts.login') }}</h2>
|
||||
@if (session()->has('message'))
|
||||
@component('portal.ninja2020.components.message')
|
||||
@ -179,12 +192,12 @@
|
||||
|
||||
<button wire:loading.attr="disabled" type="button" wire:click="passwordlessLogin"
|
||||
class="mt-4 text-sm active:outline-none focus:outline-none">
|
||||
Log in without password
|
||||
{{ ctrans('texts.login_without_password') }}
|
||||
</button>
|
||||
|
||||
@if($steps['passwordless_login_sent'])
|
||||
<span
|
||||
class="block mt-2 text-sm text-green-600">E-mail sent. Please check your inbox!</span>
|
||||
class="block mt-2 text-sm text-green-600">{{ ctrans('texts.email_sent') }}</span>
|
||||
@endif
|
||||
@endif
|
||||
|
||||
@ -200,7 +213,7 @@
|
||||
</div>
|
||||
|
||||
<div class="relative flex justify-center text-sm leading-5">
|
||||
<span class="px-2 text-gray-700 bg-white">Have a coupon code?</span>
|
||||
<span class="px-2 text-gray-700 bg-white">{{ ctrans('texts.promo_code') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -211,7 +224,7 @@
|
||||
<input type="text" wire:model.lazy="coupon" class="input w-full m-0"/>
|
||||
</label>
|
||||
|
||||
<button class="button button-primary bg-primary">Apply</button>
|
||||
<button class="button button-primary bg-primary">{{ ctrans('texts.apply') }}</button>
|
||||
</form>
|
||||
@endif
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
@extends('portal.ninja2020.layout.clean')
|
||||
@extends('themes.ninja2020.clean')
|
||||
@section('meta_title', ctrans('texts.set_password'))
|
||||
|
||||
@section('body')
|
||||
|
@ -1,4 +1,4 @@
|
||||
@extends('portal.ninja2020.layout.clean')
|
||||
@extends('themes.ninja2020.clean')
|
||||
@section('meta_title', ctrans('texts.confirmation'))
|
||||
|
||||
@section('body')
|
||||
|
113
resources/views/themes/ninja2020/clean.blade.php
Normal file
113
resources/views/themes/ninja2020/clean.blade.php
Normal file
@ -0,0 +1,113 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
|
||||
|
||||
<head>
|
||||
<!-- Error: {{ session('error') }} -->
|
||||
|
||||
@if (config('services.analytics.tracking_id'))
|
||||
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-122229484-1"></script>
|
||||
<script>
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
|
||||
function gtag() {
|
||||
dataLayer.push(arguments);
|
||||
}
|
||||
|
||||
gtag('js', new Date());
|
||||
gtag('config', '{{ config('services.analytics.tracking_id') }}', {'anonymize_ip': true});
|
||||
|
||||
function trackEvent(category, action) {
|
||||
ga('send', 'event', category, action, this.src);
|
||||
}
|
||||
</script>
|
||||
<script>
|
||||
Vue.config.devtools = true;
|
||||
</script>
|
||||
@else
|
||||
<script>
|
||||
function gtag() {
|
||||
}
|
||||
</script>
|
||||
@endif
|
||||
|
||||
<!-- Title -->
|
||||
@auth()
|
||||
<title>@yield('meta_title', '') — {{ auth('contact')->user()->user->account->isPaid() ? auth('contact')->user()->company->present()->name() : 'Invoice Ninja' }}</title>
|
||||
@endauth
|
||||
|
||||
@guest
|
||||
<title>@yield('meta_title', '') — {{ config('app.name') }}</title>
|
||||
@endguest
|
||||
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="description" content="@yield('meta_description')"/>
|
||||
|
||||
<!-- CSRF Token -->
|
||||
<meta name="csrf-token" content="{{ csrf_token() }}">
|
||||
|
||||
<!-- Scripts -->
|
||||
<script src="{{ mix('js/app.js') }}" defer></script>
|
||||
<script src="https://cdn.jsdelivr.net/gh/alpinejs/alpine@v2.7.x/dist/alpine.min.js" defer></script>
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="dns-prefetch" href="https://fonts.gstatic.com">
|
||||
<link href="https://fonts.googleapis.com/css?family=Open+Sans&display=swap" rel="stylesheet" type="text/css">
|
||||
|
||||
<!-- Styles -->
|
||||
<link href="{{ mix('css/app.css') }}" rel="stylesheet">
|
||||
{{-- <link href="{{ mix('favicon.png') }}" rel="shortcut icon" type="image/png"> --}}
|
||||
|
||||
<link rel="canonical" href="{{ config('ninja.app_url') }}/{{ request()->path() }}"/>
|
||||
|
||||
{{-- Feel free to push anything to header using @push('header') --}}
|
||||
@stack('head')
|
||||
|
||||
@livewireStyles
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/cookieconsent@3/build/cookieconsent.min.css" />
|
||||
</head>
|
||||
|
||||
<body class="antialiased">
|
||||
@if(session()->has('message'))
|
||||
<div class="py-1 text-sm text-center text-white bg-primary disposable-alert">
|
||||
{{ session('message') }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@yield('body')
|
||||
|
||||
@livewireScripts
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/cookieconsent@3/build/cookieconsent.min.js" data-cfasync="false"></script>
|
||||
<script>
|
||||
window.addEventListener("load", function(){
|
||||
if (! window.cookieconsent) {
|
||||
return;
|
||||
}
|
||||
window.cookieconsent.initialise({
|
||||
"palette": {
|
||||
"popup": {
|
||||
"background": "#000"
|
||||
},
|
||||
"button": {
|
||||
"background": "#f1d600"
|
||||
},
|
||||
},
|
||||
"content": {
|
||||
"href": "https://www.invoiceninja.com/privacy-policy/",
|
||||
"message": "This website uses cookies to ensure you get the best experience on our website.",
|
||||
"dismiss": "Got it!",
|
||||
"link": "Learn more",
|
||||
}
|
||||
})}
|
||||
);
|
||||
</script>
|
||||
</body>
|
||||
|
||||
<footer>
|
||||
@yield('footer')
|
||||
@stack('footer')
|
||||
</footer>
|
||||
|
||||
</html>
|
@ -125,9 +125,9 @@ class TaskStatusApiTest extends TestCase
|
||||
|
||||
public function testTaskStatusDeletedFromDELETEROute()
|
||||
{
|
||||
$data = [
|
||||
'ids' => [$this->encodePrimaryKey($this->task_status->id)],
|
||||
];
|
||||
// $data = [
|
||||
// 'ids' => [$this->encodePrimaryKey($this->task_status->id)],
|
||||
// ];
|
||||
|
||||
$response = $this->withHeaders([
|
||||
'X-API-SECRET' => config('ninja.api_secret'),
|
||||
@ -135,7 +135,8 @@ class TaskStatusApiTest extends TestCase
|
||||
])->delete('/api/v1/task_statuses/'.$this->encodePrimaryKey($this->task_status->id));
|
||||
|
||||
$arr = $response->json();
|
||||
nlog($arr);
|
||||
// nlog($arr);
|
||||
|
||||
$this->assertTrue($arr['data']['is_deleted']);
|
||||
}
|
||||
|
||||
|
@ -34,6 +34,11 @@ class PdfGenerationTest extends TestCase
|
||||
$pdf->setChromiumPath(config('ninja.snappdf_chromium_path'));
|
||||
}
|
||||
|
||||
if (config('ninja.snappdf_chromium_arguments')) {
|
||||
$pdf->clearChromiumArguments();
|
||||
$pdf->addChromiumArguments(config('ninja.snappdf_chromium_arguments'));
|
||||
}
|
||||
|
||||
$pdf = $pdf
|
||||
->setHtml('<h1>Invoice Ninja</h1>')
|
||||
->generate();
|
||||
|
59
tests/Unit/UrlTest.php
Normal file
59
tests/Unit/UrlTest.php
Normal file
@ -0,0 +1,59 @@
|
||||
<?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 Tests\TestCase;
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
class UrlTest extends TestCase
|
||||
{
|
||||
|
||||
public function setUp() :void
|
||||
{
|
||||
parent::setUp();
|
||||
}
|
||||
|
||||
public function testNoScheme()
|
||||
{
|
||||
$url = 'google.com';
|
||||
|
||||
$this->assertEquals("https://google.com", $this->addScheme($url));
|
||||
}
|
||||
|
||||
public function testNoSchemeAndTrailingSlash()
|
||||
{
|
||||
$url = 'google.com/';
|
||||
|
||||
$this->assertEquals("https://google.com", $this->addScheme($url));
|
||||
}
|
||||
|
||||
|
||||
public function testNoSchemeAndTrailingSlashAndHttp()
|
||||
{
|
||||
$url = 'http://google.com/';
|
||||
|
||||
$this->assertEquals("https://google.com", $this->addScheme($url));
|
||||
}
|
||||
|
||||
private function addScheme($url, $scheme = 'https://')
|
||||
{
|
||||
|
||||
$url = str_replace("http://", "", $url);
|
||||
|
||||
$url = parse_url($url, PHP_URL_SCHEME) === null ? $scheme . $url : $url;
|
||||
|
||||
return rtrim($url, '/');
|
||||
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user