mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-06-23 20:00:33 -04:00
Merge branch 'v5-develop' of https://github.com/turbo124/invoiceninja into v5-develop
This commit is contained in:
commit
645c77ab04
@ -1 +1 @@
|
||||
5.3.29
|
||||
5.3.31
|
@ -178,7 +178,8 @@ class EmailTemplateDefaults
|
||||
|
||||
public static function emailReminder1Template()
|
||||
{
|
||||
return '';
|
||||
return self::emailInvoiceTemplate();
|
||||
//return '';
|
||||
}
|
||||
|
||||
public static function emailReminder2Subject()
|
||||
@ -188,7 +189,8 @@ class EmailTemplateDefaults
|
||||
|
||||
public static function emailReminder2Template()
|
||||
{
|
||||
return '';
|
||||
return self::emailInvoiceTemplate();
|
||||
//return '';
|
||||
}
|
||||
|
||||
public static function emailReminder3Subject()
|
||||
@ -198,7 +200,8 @@ class EmailTemplateDefaults
|
||||
|
||||
public static function emailReminder3Template()
|
||||
{
|
||||
return '';
|
||||
return self::emailInvoiceTemplate();
|
||||
//return '';
|
||||
}
|
||||
|
||||
public static function emailReminderEndlessSubject()
|
||||
@ -208,6 +211,7 @@ class EmailTemplateDefaults
|
||||
|
||||
public static function emailReminderEndlessTemplate()
|
||||
{
|
||||
return self::emailInvoiceTemplate();
|
||||
return '';
|
||||
}
|
||||
|
||||
|
30
app/Helpers/Document/WithTypeHelpers.php
Normal file
30
app/Helpers/Document/WithTypeHelpers.php
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Helpers\Document;
|
||||
|
||||
trait WithTypeHelpers
|
||||
{
|
||||
/**
|
||||
* Returns boolean based on checks for image.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isImage(): bool
|
||||
{
|
||||
if (in_array($this->type, ['png', 'svg', 'jpeg', 'jpg', 'tiff', 'gif'])) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
@ -70,7 +70,7 @@ class GmailTransport extends Transport
|
||||
if($child->getContentType() != 'text/plain')
|
||||
{
|
||||
|
||||
$this->gmail->attach(TempFile::filePath($child->getBody(), $child->getHeaders()->get('Content-Type')->getParameter('name') ));
|
||||
$this->gmail->attach(TempFile::filePath($child->getBody(), $child->getHeaders()->get('Content-Type')->getParameter('name') ));
|
||||
|
||||
}
|
||||
|
||||
|
@ -36,12 +36,9 @@ class ContactLoginController extends Controller
|
||||
|
||||
public function showLoginForm(Request $request)
|
||||
{
|
||||
//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;
|
||||
// }else
|
||||
|
||||
$company = false;
|
||||
$account = false;
|
||||
|
||||
if($request->has('company_key')){
|
||||
MultiDB::findAndSetDbByCompanyKey($request->input('company_key'));
|
||||
@ -65,13 +62,16 @@ class ContactLoginController extends Controller
|
||||
|
||||
}
|
||||
elseif (Ninja::isSelfHost()) {
|
||||
$company = Account::first()->default_company;
|
||||
$account = Account::first();
|
||||
$company = $account->default_company;
|
||||
} else {
|
||||
$company = null;
|
||||
}
|
||||
|
||||
$account_id = $request->get('account_id');
|
||||
$account = Account::find($account_id);
|
||||
if(!$account){
|
||||
$account_id = $request->get('account_id');
|
||||
$account = Account::find($account_id);
|
||||
}
|
||||
|
||||
return $this->render('auth.login', ['account' => $account, 'company' => $company]);
|
||||
|
||||
|
@ -334,7 +334,7 @@ class BaseController extends Controller
|
||||
|
||||
},
|
||||
'company.expense_categories'=> function ($query) use ($updated_at, $user) {
|
||||
$query->where('updated_at', '>=', $updated_at);
|
||||
$query->whereNotNull('updated_at');
|
||||
},
|
||||
'company.task_statuses'=> function ($query) use ($updated_at, $user) {
|
||||
$query->whereNotNull('updated_at');
|
||||
@ -568,7 +568,7 @@ class BaseController extends Controller
|
||||
|
||||
},
|
||||
'company.expense_categories'=> function ($query) use ($created_at, $user) {
|
||||
$query->where('created_at', '>=', $created_at);
|
||||
$query->whereNotNull('created_at');
|
||||
|
||||
},
|
||||
'company.task_statuses'=> function ($query) use ($created_at, $user) {
|
||||
|
@ -59,7 +59,6 @@ class InvoiceController extends Controller
|
||||
|
||||
$invoice->service()->removeUnpaidGatewayFees()->save();
|
||||
|
||||
|
||||
$invitation = $invoice->invitations()->where('client_contact_id', auth()->user()->id)->first();
|
||||
|
||||
if ($invitation && auth()->guard('contact') && ! request()->has('silent') && ! $invitation->viewed_date) {
|
||||
|
@ -200,6 +200,7 @@ class CreditController extends BaseController
|
||||
|
||||
$credit = $credit->service()
|
||||
->fillDefaults()
|
||||
->triggeredActions($request)
|
||||
->save();
|
||||
|
||||
event(new CreditWasCreated($credit, $credit->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
|
||||
@ -377,7 +378,9 @@ class CreditController extends BaseController
|
||||
|
||||
$credit = $this->credit_repository->save($request->all(), $credit);
|
||||
|
||||
$credit->service()->deletePdf();
|
||||
$credit->service()
|
||||
->triggeredActions($request)
|
||||
->deletePdf();
|
||||
|
||||
event(new CreditWasUpdated($credit, $credit->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
|
||||
|
||||
|
@ -83,7 +83,7 @@ class ImportController extends Controller {
|
||||
$contents = file_get_contents( $file->getPathname() );
|
||||
|
||||
// Store the csv in cache with an expiry of 10 minutes
|
||||
Cache::put( $hash . '-' . $entityType, base64_encode( $contents ), 3600 );
|
||||
Cache::put( $hash . '-' . $entityType, base64_encode( $contents ), 600 );
|
||||
|
||||
// Parse CSV
|
||||
$csv_array = $this->getCsvData( $contents );
|
||||
@ -111,7 +111,7 @@ class ImportController extends Controller {
|
||||
$contents = file_get_contents( $file->getPathname() );
|
||||
|
||||
// Store the csv in cache with an expiry of 10 minutes
|
||||
Cache::put( $hash . '-' . $entityType, base64_encode( $contents ), 3600 );
|
||||
Cache::put( $hash . '-' . $entityType, base64_encode( $contents ), 600 );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -532,9 +532,10 @@ class InvoiceController extends BaseController
|
||||
* Download Invoice/s
|
||||
*/
|
||||
|
||||
if ($action == 'download' && $invoices->count() > 1) {
|
||||
if ($action == 'bulk_download' && $invoices->count() > 1) {
|
||||
$invoices->each(function ($invoice) {
|
||||
if (auth()->user()->cannot('view', $invoice)) {
|
||||
nlog("access denied");
|
||||
return response()->json(['message' => ctrans('text.access_denied')]);
|
||||
}
|
||||
});
|
||||
|
@ -26,6 +26,8 @@ use Illuminate\Support\Facades\App;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Str;
|
||||
use App\DataMapper\ClientSettings;
|
||||
use Livewire\Component;
|
||||
|
||||
class BillingPortalPurchase extends Component
|
||||
@ -241,7 +243,8 @@ class BillingPortalPurchase extends Component
|
||||
'contacts' => [
|
||||
['email' => $this->email],
|
||||
],
|
||||
'settings' => [],
|
||||
'client_hash' => Str::random(40),
|
||||
'settings' => ClientSettings::defaults(),
|
||||
];
|
||||
|
||||
foreach ($this->request_data as $field => $value) {
|
||||
@ -290,7 +293,7 @@ class BillingPortalPurchase extends Component
|
||||
return $this;
|
||||
}
|
||||
|
||||
if ((int)$this->subscription->price == 0)
|
||||
if ((int)$this->price == 0)
|
||||
$this->steps['payment_required'] = false;
|
||||
else
|
||||
$this->steps['fetched_payment_methods'] = true;
|
||||
|
@ -36,6 +36,7 @@ class TasksTable extends Component
|
||||
{
|
||||
$query = Task::query()
|
||||
->where('company_id', $this->company->id)
|
||||
->where('is_deleted', false)
|
||||
->where('client_id', auth('contact')->user()->client->id);
|
||||
|
||||
if ($this->company->getSetting('show_all_tasks_client_portal') === 'invoiced') {
|
||||
|
@ -42,6 +42,16 @@ class TokenAuth
|
||||
return response()->json($error, 403);
|
||||
}
|
||||
|
||||
if(Ninja::isHosted() && $company_token->is_system == 0 && !$user->account->isPaid()){
|
||||
|
||||
$error = [
|
||||
'message' => 'Feature not available with free / unpaid account.',
|
||||
'errors' => new stdClass,
|
||||
];
|
||||
|
||||
return response()->json($error, 403);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
||||
| Necessary evil here: As we are authenticating on CompanyToken,
|
||||
|
@ -28,7 +28,8 @@ class ShowDocumentRequest extends FormRequest
|
||||
public function authorize()
|
||||
{
|
||||
return auth()->user('contact')->client->id == $this->document->documentable_id
|
||||
|| $this->document->documentable->client_id == auth()->user('contact')->client->id;
|
||||
|| $this->document->documentable->client_id == auth()->user('contact')->client->id
|
||||
|| $this->document->company_id == auth()->user('contact')->company->id;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -33,11 +33,13 @@ class CanAddUserRule implements Rule
|
||||
public function passes($attribute, $value)
|
||||
{
|
||||
|
||||
$count = CompanyUser::query()
|
||||
->where('account_id', auth()->user()->account_id)
|
||||
->whereNull('deleted_at')
|
||||
->distinct()
|
||||
->count('user_id');
|
||||
$count = CompanyUser::query()
|
||||
->where('company_user.account_id', auth()->user()->account_id)
|
||||
->join('users', 'users.id', '=', 'company_user.user_id')
|
||||
->whereNull('users.deleted_at')
|
||||
->whereNull('company_user.deleted_at')
|
||||
->distinct()
|
||||
->count('company_user.user_id');
|
||||
|
||||
return $count < auth()->user()->company()->account->num_users;
|
||||
|
||||
|
@ -33,12 +33,13 @@ class CanRestoreUserRule implements Rule
|
||||
public function passes($attribute, $value)
|
||||
{
|
||||
|
||||
$count = CompanyUser::query()
|
||||
->where('account_id', auth()->user()->account_id)
|
||||
->whereNull('deleted_at')
|
||||
->distinct()
|
||||
->count('user_id');
|
||||
|
||||
$count = CompanyUser::query()
|
||||
->where('company_user.account_id', auth()->user()->account_id)
|
||||
->join('users', 'users.id', '=', 'company_user.user_id')
|
||||
->whereNull('users.deleted_at')
|
||||
->whereNull('company_user.deleted_at')
|
||||
->distinct()
|
||||
->count('company_user.user_id');
|
||||
|
||||
return $count < auth()->user()->company()->account->num_users;
|
||||
|
||||
|
@ -24,6 +24,8 @@ class ValidProjectForClient implements Rule
|
||||
|
||||
public $input;
|
||||
|
||||
public $message;
|
||||
|
||||
public function __construct($input)
|
||||
{
|
||||
$this->input = $input;
|
||||
@ -35,15 +37,20 @@ class ValidProjectForClient implements Rule
|
||||
*/
|
||||
public function passes($attribute, $value)
|
||||
{
|
||||
$this->message = ctrans('texts.project_client_do_not_match');
|
||||
|
||||
if (empty($this->input['project_id'])) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (is_string($this->input['project_id'])) {
|
||||
$this->input['project_id'] = $this->decodePrimaryKey($this->input['project_id']);
|
||||
}
|
||||
// if (is_string($this->input['project_id'])) {
|
||||
// $this->input['project_id'] = $this->decodePrimaryKey($this->input['project_id']);
|
||||
// }
|
||||
|
||||
$project = Project::findOrFail($this->input['project_id']);
|
||||
$project = Project::find($this->input['project_id']);
|
||||
|
||||
if(!$project)
|
||||
$this->message = "Project not found";
|
||||
|
||||
return $project->client_id == $this->input['client_id'];
|
||||
}
|
||||
@ -53,6 +60,6 @@ class ValidProjectForClient implements Rule
|
||||
*/
|
||||
public function message()
|
||||
{
|
||||
return ctrans('texts.project_client_do_not_match');
|
||||
return $this->message;
|
||||
}
|
||||
}
|
||||
|
@ -221,6 +221,9 @@ class BaseTransformer
|
||||
{
|
||||
$name = strtolower(trim($name));
|
||||
|
||||
if(strlen($name) == 2)
|
||||
return $this->getCountryIdBy2($name);
|
||||
|
||||
return isset($this->maps['countries'][$name]) ? $this->maps['countries'][$name] : null;
|
||||
}
|
||||
|
||||
|
@ -69,6 +69,7 @@ class ApplyCreditPayment implements ShouldQueue
|
||||
|
||||
$this->credit
|
||||
->service()
|
||||
->markSent()
|
||||
->setStatus(Credit::STATUS_APPLIED)
|
||||
->adjustBalance($this->amount * -1)
|
||||
->updatePaidToDate($this->amount)
|
||||
@ -78,6 +79,7 @@ class ApplyCreditPayment implements ShouldQueue
|
||||
|
||||
$this->credit
|
||||
->service()
|
||||
->markSent()
|
||||
->setStatus(Credit::STATUS_PARTIAL)
|
||||
->adjustBalance($this->amount * -1)
|
||||
->updatePaidToDate($this->amount)
|
||||
|
@ -134,10 +134,6 @@ class CSVImport implements ShouldQueue {
|
||||
'company' => $this->company,
|
||||
];
|
||||
|
||||
App::forgetInstance('translator');
|
||||
$t = app('translator');
|
||||
$t->replace(Ninja::transformTranslations($this->company->settings));
|
||||
|
||||
$nmo = new NinjaMailerObject;
|
||||
$nmo->mailable = new ImportCompleted($this->company, $data);
|
||||
$nmo->company = $this->company;
|
||||
@ -591,7 +587,7 @@ class CSVImport implements ShouldQueue {
|
||||
}
|
||||
|
||||
private function getCsvData( $entityType ) {
|
||||
$base64_encoded_csv = Cache::get( $this->hash . '-' . $entityType );
|
||||
$base64_encoded_csv = Cache::pull( $this->hash . '-' . $entityType );
|
||||
if ( empty( $base64_encoded_csv ) ) {
|
||||
return null;
|
||||
}
|
||||
|
@ -120,6 +120,7 @@ class SendRecurring implements ShouldQueue
|
||||
|
||||
*/
|
||||
|
||||
event('eloquent.created: App\Models\Invoice', $invoice);
|
||||
|
||||
//Admin notification for recurring invoice sent.
|
||||
if ($invoice->invitations->count() >= 1 ) {
|
||||
|
@ -232,6 +232,7 @@ class Import implements ShouldQueue
|
||||
|
||||
$account = $this->company->account;
|
||||
$account->default_company_id = $this->company->id;
|
||||
$account->is_migrated = true;
|
||||
$account->save();
|
||||
|
||||
//company size check
|
||||
|
@ -49,15 +49,19 @@ class SystemLogger implements ShouldQueue
|
||||
|
||||
public function handle() :void
|
||||
{
|
||||
if(!$this->company)
|
||||
if(!$this->company){
|
||||
nlog("SystemLogger:: No company");
|
||||
return;
|
||||
}
|
||||
|
||||
MultiDB::setDb($this->company->db);
|
||||
|
||||
$client_id = $this->client ? $this->client->id : null;
|
||||
|
||||
if(!$this->client && !$this->company->owner())
|
||||
if(!$this->client && !$this->company->owner()){
|
||||
nlog("SystemLogger:: could not find client and/or company owner");
|
||||
return;
|
||||
}
|
||||
|
||||
$user_id = $this->client ? $this->client->user_id : $this->company->owner()->id;
|
||||
|
||||
@ -71,9 +75,16 @@ class SystemLogger implements ShouldQueue
|
||||
'type_id' => $this->type_id,
|
||||
];
|
||||
|
||||
if(!$this->log)
|
||||
if(!$this->log){
|
||||
nlog("SystemLogger:: no log to store");
|
||||
return;
|
||||
}
|
||||
|
||||
SystemLog::create($sl);
|
||||
}
|
||||
|
||||
public function failed($e)
|
||||
{
|
||||
nlog($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
@ -52,6 +52,10 @@ class InvoiceCreatedNotification implements ShouldQueue
|
||||
|
||||
/* The User */
|
||||
$user = $company_user->user;
|
||||
|
||||
if(!$user)
|
||||
continue;
|
||||
|
||||
/* This is only here to handle the alternate message channels - ie Slack */
|
||||
// $notification = new EntitySentNotification($event->invitation, 'invoice');
|
||||
|
||||
@ -71,11 +75,6 @@ class InvoiceCreatedNotification implements ShouldQueue
|
||||
|
||||
}
|
||||
|
||||
/* Override the methods in the Notification Class */
|
||||
// $notification->method = $methods;
|
||||
|
||||
// Notify on the alternate channels
|
||||
// $user->notify($notification);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -53,6 +53,9 @@ class QuoteCreatedNotification implements ShouldQueue
|
||||
/* The User */
|
||||
$user = $company_user->user;
|
||||
|
||||
if(!$user)
|
||||
continue;
|
||||
|
||||
/* This is only here to handle the alternate message channels - ie Slack */
|
||||
// $notification = new EntitySentNotification($event->invitation, 'quote');
|
||||
|
||||
|
@ -75,7 +75,7 @@ class ClientPaymentFailureObject
|
||||
$mail_obj->amount = $this->getAmount();
|
||||
$mail_obj->subject = $this->getSubject();
|
||||
$mail_obj->data = $this->getData();
|
||||
$mail_obj->markdown = 'email.admin.generic';
|
||||
$mail_obj->markdown = 'email.client.generic';
|
||||
$mail_obj->tag = $this->company->company_key;
|
||||
|
||||
return $mail_obj;
|
||||
@ -113,14 +113,15 @@ class ClientPaymentFailureObject
|
||||
]
|
||||
),
|
||||
'greeting' => ctrans('texts.email_salutation', ['name' => $this->client->present()->name]),
|
||||
'message' => ctrans('texts.client_payment_failure_body', ['invoice' => implode(",", $this->invoices->pluck('number')->toArray()), 'amount' => $this->getAmount()]),
|
||||
'content' => ctrans('texts.client_payment_failure_body', ['invoice' => implode(",", $this->invoices->pluck('number')->toArray()), 'amount' => $this->getAmount()]),
|
||||
'signature' => $signature,
|
||||
'logo' => $this->company->present()->logo(),
|
||||
'settings' => $this->client->getMergedSettings(),
|
||||
'whitelabel' => $this->company->account->isPaid() ? true : false,
|
||||
'url' => route('client.login'),
|
||||
'button' => ctrans('texts.login'),
|
||||
'additional_info' => false
|
||||
'url' => $this->invoices->first()->invitations->first()->getPaymentLink(),
|
||||
'button' => 'texts.pay_now',
|
||||
'additional_info' => false,
|
||||
'company' => $this->company,
|
||||
];
|
||||
|
||||
return $data;
|
||||
|
@ -136,4 +136,4 @@ class InvoiceEmailEngine extends BaseEmailEngine
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
}
|
@ -13,9 +13,11 @@
|
||||
namespace App\Mail\Import;
|
||||
|
||||
use App\Models\Company;
|
||||
use App\Utils\Ninja;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Mail\Mailable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\App;
|
||||
|
||||
class ImportCompleted extends Mailable
|
||||
{
|
||||
@ -45,6 +47,11 @@ class ImportCompleted extends Mailable
|
||||
*/
|
||||
public function build()
|
||||
{
|
||||
|
||||
App::forgetInstance('translator');
|
||||
$t = app('translator');
|
||||
$t->replace(Ninja::transformTranslations($this->company->settings));
|
||||
|
||||
$data = array_merge($this->data, [
|
||||
'logo' => $this->company->present()->logo(),
|
||||
'settings' => $this->company->settings,
|
||||
|
@ -3,9 +3,11 @@
|
||||
namespace App\Mail;
|
||||
|
||||
use App\Models\Company;
|
||||
use App\Utils\Ninja;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Mail\Mailable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\App;
|
||||
|
||||
class MigrationCompleted extends Mailable
|
||||
{
|
||||
@ -33,6 +35,11 @@ class MigrationCompleted extends Mailable
|
||||
*/
|
||||
public function build()
|
||||
{
|
||||
|
||||
App::forgetInstance('translator');
|
||||
$t = app('translator');
|
||||
$t->replace(Ninja::transformTranslations($this->company->settings));
|
||||
|
||||
$data['settings'] = $this->company->settings;
|
||||
$data['company'] = $this->company->fresh();
|
||||
$data['whitelabel'] = $this->company->account->isPaid() ? true : false;
|
||||
|
@ -18,6 +18,7 @@ use App\Models\ClientContact;
|
||||
use App\Models\User;
|
||||
use App\Services\PdfMaker\Designs\Utilities\DesignHelpers;
|
||||
use App\Utils\HtmlEngine;
|
||||
use App\Utils\Ninja;
|
||||
use App\Utils\TemplateEngine;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Mail\Mailable;
|
||||
@ -114,6 +115,10 @@ class TemplateEmail extends Mailable
|
||||
$message->invitation = $this->invitation;
|
||||
});
|
||||
|
||||
/*In the hosted platform we need to slow things down a little for Storage to catch up.*/
|
||||
if(Ninja::isHosted())
|
||||
sleep(1);
|
||||
|
||||
foreach ($this->build_email->getAttachments() as $file) {
|
||||
|
||||
if(is_string($file))
|
||||
|
@ -195,6 +195,8 @@ class BaseModel extends Model
|
||||
// Remove any runs of periods (thanks falstro!)
|
||||
$formatted_number = mb_ereg_replace("([\.]{2,})", '', $formatted_number);
|
||||
|
||||
$formatted_number = str_replace(" ", "", $formatted_number);
|
||||
|
||||
return $formatted_number;
|
||||
}
|
||||
|
||||
|
@ -217,6 +217,9 @@ class ClientContact extends Authenticatable implements HasLocalePreference
|
||||
{
|
||||
$languages = Cache::get('languages');
|
||||
|
||||
if(!$languages)
|
||||
$this->buildCache(true);
|
||||
|
||||
return $languages->filter(function ($item) {
|
||||
return $item->id == $this->client->getSetting('language_id');
|
||||
})->first()->locale;
|
||||
|
@ -11,6 +11,7 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Helpers\Document\WithTypeHelpers;
|
||||
use App\Models\Filterable;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
@ -19,6 +20,7 @@ class Document extends BaseModel
|
||||
{
|
||||
use SoftDeletes;
|
||||
use Filterable;
|
||||
use WithTypeHelpers;
|
||||
|
||||
const DOCUMENT_PREVIEW_SIZE = 300; // pixels
|
||||
|
||||
|
@ -404,7 +404,7 @@ class BaseDriver extends AbstractPaymentDriver
|
||||
throw new PaymentFailed($error, $e->getCode());
|
||||
}
|
||||
|
||||
public function sendFailureMail(string $error)
|
||||
public function sendFailureMail($error = '')
|
||||
{
|
||||
|
||||
if (!is_null($this->payment_hash)) {
|
||||
|
@ -63,8 +63,8 @@ class CreditCard
|
||||
$transaction = [
|
||||
'Reference' => $this->eway_driver->client->number,
|
||||
'Title' => '',
|
||||
'FirstName' => $this->eway_driver->client->contacts()->first()->present()->last_name(),
|
||||
'LastName' => $this->eway_driver->client->contacts()->first()->present()->first_name(),
|
||||
'FirstName' => $this->eway_driver->client->contacts()->first()->present()->first_name(),
|
||||
'LastName' => $this->eway_driver->client->contacts()->first()->present()->last_name(),
|
||||
'CompanyName' => $this->eway_driver->client->name,
|
||||
'Street1' => $this->eway_driver->client->address1,
|
||||
'Street2' => $this->eway_driver->client->address2,
|
||||
|
@ -99,4 +99,9 @@ class RazorpayPaymentDriver extends BaseDriver
|
||||
{
|
||||
return \number_format((float) $amount * 100, 0, '.', '');
|
||||
}
|
||||
|
||||
public function processWebhookRequest(): void
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
|
@ -524,10 +524,24 @@ class StripePaymentDriver extends BaseDriver
|
||||
if ($request->type === 'charge.succeeded' || $request->type === 'payment_intent.succeeded') {
|
||||
|
||||
foreach ($request->data as $transaction) {
|
||||
$payment = Payment::query()
|
||||
->where('transaction_reference', $transaction['id'])
|
||||
|
||||
if(array_key_exists('payment_intent', $transaction))
|
||||
{
|
||||
$payment = Payment::query()
|
||||
->where('company_id', $request->getCompany()->id)
|
||||
->where(function ($query) use ($transaction) {
|
||||
$query->where('transaction_reference', $transaction['payment_intent'])
|
||||
->orWhere('transaction_reference', $transaction['id']);
|
||||
})
|
||||
->first();
|
||||
}
|
||||
else
|
||||
{
|
||||
$payment = Payment::query()
|
||||
->where('company_id', $request->getCompany()->id)
|
||||
->where('transaction_reference', $transaction['id'])
|
||||
->first();
|
||||
}
|
||||
|
||||
if ($payment) {
|
||||
$payment->status_id = Payment::STATUS_COMPLETED;
|
||||
@ -546,10 +560,13 @@ class StripePaymentDriver extends BaseDriver
|
||||
|
||||
if ($charge->captured) {
|
||||
$payment = Payment::query()
|
||||
->where('transaction_reference', $transaction['id'])
|
||||
->where('transaction_reference', $transaction['payment_intent'])
|
||||
->where('company_id', $request->getCompany()->id)
|
||||
->where(function ($query) use ($transaction) {
|
||||
$query->where('transaction_reference', $transaction['payment_intent'])
|
||||
->orWhere('transaction_reference', $transaction['id']);
|
||||
})
|
||||
->first();
|
||||
|
||||
if ($payment) {
|
||||
$payment->status_id = Payment::STATUS_COMPLETED;
|
||||
$payment->save();
|
||||
|
@ -22,7 +22,9 @@ use Illuminate\Support\Str;
|
||||
*/
|
||||
class ClientContactRepository extends BaseRepository
|
||||
{
|
||||
public $is_primary;
|
||||
private bool $is_primary = true;
|
||||
|
||||
private bool $set_send_email_on_contact = false;
|
||||
|
||||
public function save(array $data, Client $client) : void
|
||||
{
|
||||
@ -36,13 +38,20 @@ class ClientContactRepository extends BaseRepository
|
||||
ClientContact::destroy($contact);
|
||||
});
|
||||
|
||||
$this->is_primary = true;
|
||||
/* Ensure send_email always exists in at least one contact */
|
||||
if(!$contacts->contains('send_email', true))
|
||||
$this->set_send_email_on_contact = true;
|
||||
|
||||
/* Set first record to primary - always */
|
||||
$contacts = $contacts->sortByDesc('is_primary')->map(function ($contact) {
|
||||
$contact['is_primary'] = $this->is_primary;
|
||||
$this->is_primary = false;
|
||||
|
||||
if($this->set_send_email_on_contact){
|
||||
$contact['send_email'] = true;
|
||||
$this->set_send_email_on_contact = false;
|
||||
}
|
||||
|
||||
return $contact;
|
||||
});
|
||||
|
||||
|
@ -129,7 +129,7 @@ class PaymentRepository extends BaseRepository {
|
||||
|
||||
//todo optimize this into a single query
|
||||
foreach ($data['invoices'] as $paid_invoice) {
|
||||
$invoice = Invoice::whereId($paid_invoice['invoice_id'])->first();
|
||||
$invoice = Invoice::withTrashed()->whereId($paid_invoice['invoice_id'])->first();
|
||||
|
||||
if ($invoice) {
|
||||
$invoice = $invoice->service()
|
||||
|
@ -23,7 +23,7 @@ class CreateInvitations extends AbstractService
|
||||
{
|
||||
use MakesHash;
|
||||
|
||||
private $credit;
|
||||
public $credit;
|
||||
|
||||
public function __construct(Credit $credit)
|
||||
{
|
||||
@ -45,6 +45,7 @@ class CreateInvitations extends AbstractService
|
||||
$invitation = CreditInvitation::whereCompanyId($this->credit->company_id)
|
||||
->whereClientContactId($contact->id)
|
||||
->whereCreditId($this->credit->id)
|
||||
->withTrashed()
|
||||
->first();
|
||||
|
||||
if (! $invitation) {
|
||||
@ -58,6 +59,34 @@ class CreateInvitations extends AbstractService
|
||||
}
|
||||
});
|
||||
|
||||
if($this->credit->invitations()->count() == 0) {
|
||||
|
||||
if($contacts->count() == 0){
|
||||
$contact = $this->createBlankContact();
|
||||
}
|
||||
else{
|
||||
$contact = $contacts->first();
|
||||
|
||||
$invitation = CreditInvitation::where('company_id', $this->credit->company_id)
|
||||
->where('client_contact_id', $contact->id)
|
||||
->where('credit_id', $this->credit->id)
|
||||
->withTrashed()
|
||||
->first();
|
||||
|
||||
if($invitation){
|
||||
$invitation->restore();
|
||||
return $this->credit;
|
||||
}
|
||||
}
|
||||
|
||||
$ii = CreditInvitationFactory::create($this->credit->company_id, $this->credit->user_id);
|
||||
$ii->key = $this->createDbHash($this->credit->company->db);
|
||||
$ii->credit_id = $this->credit->id;
|
||||
$ii->client_contact_id = $contact->id;
|
||||
$ii->save();
|
||||
}
|
||||
|
||||
|
||||
return $this->credit;
|
||||
}
|
||||
|
||||
|
@ -13,13 +13,15 @@ namespace App\Services\Credit;
|
||||
|
||||
use App\Jobs\Util\UnlinkFile;
|
||||
use App\Models\Credit;
|
||||
use App\Services\Credit\CreateInvitations;
|
||||
use App\Services\Credit\TriggeredActions;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
|
||||
class CreditService
|
||||
{
|
||||
use MakesHash;
|
||||
|
||||
protected $credit;
|
||||
public $credit;
|
||||
|
||||
public function __construct($credit)
|
||||
{
|
||||
@ -149,6 +151,13 @@ class CreditService
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function triggeredActions($request)
|
||||
{
|
||||
$this->invoice = (new TriggeredActions($this->credit, $request))->run();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the credit.
|
||||
* @return Credit object
|
||||
|
76
app/Services/Credit/TriggeredActions.php
Normal file
76
app/Services/Credit/TriggeredActions.php
Normal file
@ -0,0 +1,76 @@
|
||||
<?php
|
||||
/**
|
||||
* Credit Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Credit Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Services\Credit;
|
||||
|
||||
use App\Events\Credit\CreditWasEmailed;
|
||||
use App\Jobs\Entity\EmailEntity;
|
||||
use App\Models\Credit;
|
||||
use App\Services\AbstractService;
|
||||
use App\Utils\Ninja;
|
||||
use App\Utils\Traits\GeneratesCounter;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class TriggeredActions extends AbstractService
|
||||
{
|
||||
use GeneratesCounter;
|
||||
|
||||
private $request;
|
||||
|
||||
private $credit;
|
||||
|
||||
public function __construct(Credit $credit, Request $request)
|
||||
{
|
||||
$this->request = $request;
|
||||
|
||||
$this->credit = $credit;
|
||||
}
|
||||
|
||||
public function run()
|
||||
{
|
||||
// if ($this->request->has('auto_bill') && $this->request->input('auto_bill') == 'true') {
|
||||
// $this->credit = $this->credit->service()->autoBill()->save();
|
||||
// }
|
||||
|
||||
// if ($this->request->has('paid') && $this->request->input('paid') == 'true') {
|
||||
// $this->credit = $this->credit->service()->markPaid()->save();
|
||||
// }
|
||||
|
||||
// if ($this->request->has('amount_paid') && is_numeric($this->request->input('amount_paid')) ) {
|
||||
// $this->credit = $this->credit->service()->applyPaymentAmount($this->request->input('amount_paid'))->save();
|
||||
// }
|
||||
|
||||
if ($this->request->has('send_email') && $this->request->input('send_email') == 'true') {
|
||||
$this->sendEmail();
|
||||
}
|
||||
|
||||
if ($this->request->has('mark_sent') && $this->request->input('mark_sent') == 'true') {
|
||||
$this->credit = $this->credit->service()->markSent()->save();
|
||||
}
|
||||
|
||||
|
||||
return $this->credit;
|
||||
}
|
||||
|
||||
private function sendEmail()
|
||||
{
|
||||
|
||||
$reminder_template = $this->credit->calculateTemplate('credit');
|
||||
|
||||
$this->credit->invitations->load('contact.client.country', 'credit.client.country', 'credit.company')->each(function ($invitation) use ($reminder_template) {
|
||||
EmailEntity::dispatch($invitation, $this->credit->company, $reminder_template);
|
||||
});
|
||||
|
||||
if ($this->credit->invitations->count() > 0) {
|
||||
event(new CreditWasEmailed($this->credit->invitations->first(), $this->credit->company, Ninja::eventVars(), 'credit'));
|
||||
}
|
||||
}
|
||||
}
|
@ -47,7 +47,7 @@ class ApplyPayment extends AbstractService
|
||||
|
||||
$amount_paid = $this->payment_amount * -1;
|
||||
|
||||
$this->invoice->service()->clearPartial()->setDueDate()->setStatus(Invoice::STATUS_PARTIAL)->updateBalance($amount_paid);
|
||||
$this->invoice->service()->clearPartial()->setDueDate()->setStatus(Invoice::STATUS_PARTIAL)->updateBalance($amount_paid)->save();
|
||||
|
||||
}
|
||||
elseif ($this->invoice->partial > 0 && $this->invoice->partial > $this->payment_amount)
|
||||
@ -56,7 +56,7 @@ class ApplyPayment extends AbstractService
|
||||
|
||||
$amount_paid = $this->payment_amount * -1;
|
||||
|
||||
$this->invoice->service()->updatePartial($amount_paid)->updateBalance($amount_paid);
|
||||
$this->invoice->service()->updatePartial($amount_paid)->updateBalance($amount_paid)->save();
|
||||
|
||||
}
|
||||
elseif ($this->invoice->partial > 0 && $this->invoice->partial < $this->payment_amount)
|
||||
@ -65,7 +65,7 @@ class ApplyPayment extends AbstractService
|
||||
|
||||
$amount_paid = $this->payment_amount * -1;
|
||||
|
||||
$this->invoice->service()->clearPartial()->setDueDate()->setStatus(Invoice::STATUS_PARTIAL)->updateBalance($amount_paid);
|
||||
$this->invoice->service()->clearPartial()->setDueDate()->setStatus(Invoice::STATUS_PARTIAL)->updateBalance($amount_paid)->save();
|
||||
|
||||
}
|
||||
|
||||
@ -76,7 +76,7 @@ class ApplyPayment extends AbstractService
|
||||
{
|
||||
$amount_paid = $this->payment_amount * -1;
|
||||
|
||||
$this->invoice->service()->clearPartial()->setStatus(Invoice::STATUS_PAID)->updateBalance($amount_paid);
|
||||
$this->invoice->service()->clearPartial()->setStatus(Invoice::STATUS_PAID)->updateBalance($amount_paid)->save();
|
||||
|
||||
}
|
||||
elseif ($this->payment_amount < $this->invoice->balance)
|
||||
@ -85,7 +85,7 @@ class ApplyPayment extends AbstractService
|
||||
|
||||
$amount_paid = $this->payment_amount * -1;
|
||||
|
||||
$this->invoice->service()->clearPartial()->setStatus(Invoice::STATUS_PARTIAL)->updateBalance($amount_paid);
|
||||
$this->invoice->service()->clearPartial()->setStatus(Invoice::STATUS_PARTIAL)->updateBalance($amount_paid)->save();
|
||||
|
||||
|
||||
}
|
||||
@ -95,7 +95,7 @@ class ApplyPayment extends AbstractService
|
||||
|
||||
$amount_paid = $this->invoice->balance * -1;
|
||||
|
||||
$this->invoice->service()->clearPartial()->setStatus(Invoice::STATUS_PAID)->updateBalance($amount_paid);
|
||||
$this->invoice->service()->clearPartial()->setStatus(Invoice::STATUS_PAID)->updateBalance($amount_paid)->save();
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -62,7 +62,23 @@ class CreateInvitations extends AbstractService
|
||||
|
||||
if($this->invoice->invitations()->count() == 0) {
|
||||
|
||||
$contact = $this->createBlankContact();
|
||||
if($contacts->count() == 0){
|
||||
$contact = $this->createBlankContact();
|
||||
}
|
||||
else{
|
||||
$contact = $contacts->first();
|
||||
|
||||
$invitation = InvoiceInvitation::where('company_id', $this->invoice->company_id)
|
||||
->where('client_contact_id', $contact->id)
|
||||
->where('invoice_id', $this->invoice->id)
|
||||
->withTrashed()
|
||||
->first();
|
||||
|
||||
if($invitation){
|
||||
$invitation->restore();
|
||||
return $this->invoice;
|
||||
}
|
||||
}
|
||||
|
||||
$ii = InvoiceInvitationFactory::create($this->invoice->company_id, $this->invoice->user_id);
|
||||
$ii->key = $this->createDbHash($this->invoice->company->db);
|
||||
|
@ -57,7 +57,7 @@ class HandleReversal extends AbstractService
|
||||
$paymentables->each(function ($paymentable) use ($total_paid) {
|
||||
|
||||
//new concept - when reversing, we unwind the payments
|
||||
$payment = Payment::find($paymentable->payment_id);
|
||||
$payment = Payment::withTrashed()->find($paymentable->payment_id);
|
||||
|
||||
$reversable_amount = $paymentable->amount - $paymentable->refunded;
|
||||
$total_paid -= $reversable_amount;
|
||||
|
@ -33,7 +33,7 @@ class InvoiceService
|
||||
{
|
||||
use MakesHash;
|
||||
|
||||
private $invoice;
|
||||
public $invoice;
|
||||
|
||||
public function __construct($invoice)
|
||||
{
|
||||
|
@ -37,34 +37,37 @@ class MarkSent extends AbstractService
|
||||
return $this->invoice;
|
||||
}
|
||||
|
||||
$adjustment = $this->invoice->amount;
|
||||
|
||||
/*Set status*/
|
||||
$this->invoice
|
||||
->service()
|
||||
->setStatus(Invoice::STATUS_SENT)
|
||||
->updateBalance($adjustment, true)
|
||||
->save();
|
||||
|
||||
$this->invoice
|
||||
/*Adjust client balance*/
|
||||
$this->client
|
||||
->service()
|
||||
->updateBalance($adjustment)
|
||||
->save();
|
||||
|
||||
/*Update ledger*/
|
||||
$this->invoice
|
||||
->ledger()
|
||||
->updateInvoiceBalance($adjustment, "Invoice {$this->invoice->number} marked as sent.");
|
||||
|
||||
/* Perform additional actions on invoice */
|
||||
$this->invoice
|
||||
->service()
|
||||
->applyNumber()
|
||||
->setDueDate()
|
||||
->updateBalance($this->invoice->amount, true)
|
||||
->deletePdf()
|
||||
->setReminder()
|
||||
->save();
|
||||
|
||||
$this->invoice->markInvitationsSent();
|
||||
|
||||
/*Adjust client balance*/
|
||||
$this->client
|
||||
->service()
|
||||
->updateBalance($this->invoice->balance)
|
||||
->save();
|
||||
|
||||
/*Update ledger*/
|
||||
$this->invoice
|
||||
->ledger()
|
||||
->updateInvoiceBalance($this->invoice->balance, "Invoice {$this->invoice->number} marked as sent.");
|
||||
|
||||
event(new InvoiceWasUpdated($this->invoice, $this->invoice->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
|
||||
|
||||
return $this->invoice->fresh();
|
||||
|
@ -34,6 +34,9 @@ class UpdateBalance extends AbstractService
|
||||
if ($this->invoice->is_deleted) {
|
||||
return $this->invoice;
|
||||
}
|
||||
nlog("invoice id = {$this->invoice->id}");
|
||||
nlog("invoice balance = {$this->invoice->balance}");
|
||||
nlog("invoice adjustment = {$this->balance_adjustment}");
|
||||
|
||||
$this->invoice->balance += floatval($this->balance_adjustment);
|
||||
|
||||
@ -41,6 +44,8 @@ class UpdateBalance extends AbstractService
|
||||
$this->invoice->status_id = Invoice::STATUS_PAID;
|
||||
}
|
||||
|
||||
nlog("final balance = {$this->invoice->balance}");
|
||||
|
||||
return $this->invoice;
|
||||
}
|
||||
}
|
||||
|
@ -59,6 +59,33 @@ class CreateInvitations
|
||||
}
|
||||
});
|
||||
|
||||
if($this->quote->invitations()->count() == 0) {
|
||||
|
||||
if($contacts->count() == 0){
|
||||
$contact = $this->createBlankContact();
|
||||
}
|
||||
else{
|
||||
$contact = $contacts->first();
|
||||
|
||||
$invitation = QuoteInvitation::where('company_id', $this->quote->company_id)
|
||||
->where('client_contact_id', $contact->id)
|
||||
->where('quote_id', $this->quote->id)
|
||||
->withTrashed()
|
||||
->first();
|
||||
|
||||
if($invitation){
|
||||
$invitation->restore();
|
||||
return $this->quote;
|
||||
}
|
||||
}
|
||||
|
||||
$ii = QuoteInvitationFactory::create($this->quote->company_id, $this->quote->user_id);
|
||||
$ii->key = $this->createDbHash($this->quote->company->db);
|
||||
$ii->quote_id = $this->quote->id;
|
||||
$ii->client_contact_id = $contact->id;
|
||||
$ii->save();
|
||||
}
|
||||
|
||||
return $this->quote->fresh();
|
||||
}
|
||||
|
||||
|
@ -82,6 +82,7 @@ class AccountTransformer extends EntityTransformer
|
||||
'disable_auto_update' => (bool) config('ninja.disable_auto_update'),
|
||||
'emails_sent' => (int) $account->emailsSent(),
|
||||
'email_quota' => (int) $account->getDailyEmailLimit(),
|
||||
'is_migrated' => (bool) $account->is_migrated,
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -151,6 +151,7 @@ class HtmlEngine
|
||||
|
||||
if($this->entity->project) {
|
||||
$data['$project.name'] = ['value' => $this->entity->project->name, 'label' => ctrans('texts.project_name')];
|
||||
$data['$invoice.project'] = &$data['$project.name'];
|
||||
}
|
||||
}
|
||||
|
||||
@ -480,6 +481,8 @@ class HtmlEngine
|
||||
$data['$statement_amount'] = ['value' => '', 'label' => ctrans('texts.amount')];
|
||||
$data['$statement'] = ['value' => '', 'label' => ctrans('texts.statement')];
|
||||
|
||||
$data['$entity_images'] = ['value' => $this->generateEntityImagesMarkup(), 'label' => ''];
|
||||
|
||||
$arrKeysLength = array_map('strlen', array_keys($data));
|
||||
array_multisort($arrKeysLength, SORT_DESC, $data);
|
||||
|
||||
@ -736,4 +739,38 @@ html {
|
||||
|
||||
return $css;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate markup for HTML images on entity.
|
||||
*
|
||||
* @return string|void
|
||||
*/
|
||||
protected function generateEntityImagesMarkup()
|
||||
{
|
||||
if ($this->client->getSetting('embed_documents') === false) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$dom = new \DOMDocument('1.0', 'UTF-8');
|
||||
|
||||
$container = $dom->createElement('div');
|
||||
$container->setAttribute('style', 'display:grid; grid-auto-flow: row; grid-template-columns: repeat(4, 1fr); grid-template-rows: repeat(2, 1fr);');
|
||||
|
||||
foreach ($this->entity->documents as $document) {
|
||||
if (!$document->isImage()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$image = $dom->createElement('img');
|
||||
|
||||
$image->setAttribute('src', $document->generateUrl());
|
||||
$image->setAttribute('style', 'max-height: 100px; margin-top: 20px;');
|
||||
|
||||
$container->appendChild($image);
|
||||
}
|
||||
|
||||
$dom->appendChild($container);
|
||||
|
||||
return $dom->saveHTML();
|
||||
}
|
||||
}
|
||||
|
@ -113,8 +113,14 @@ class Ninja
|
||||
|
||||
public static function eventVars($user_id = null)
|
||||
{
|
||||
|
||||
if(request()->hasHeader('Cf-Connecting-Ip'))
|
||||
$ip = request()->header('Cf-Connecting-Ip');
|
||||
else
|
||||
$ip = request()->getClientIp();
|
||||
|
||||
return [
|
||||
'ip' => request()->getClientIp(),
|
||||
'ip' => $ip,
|
||||
'token' => request()->header('X-API-TOKEN'),
|
||||
'is_system' => app()->runningInConsole(),
|
||||
'user_id' => $user_id,
|
||||
|
@ -257,7 +257,7 @@ class TemplateEngine
|
||||
]);
|
||||
|
||||
|
||||
if($this->entity == 'invoice')
|
||||
if(!$this->entity || $this->entity == 'invoice')
|
||||
{
|
||||
$this->entity_obj = Invoice::factory()->create([
|
||||
'user_id' => auth()->user()->id,
|
||||
|
@ -207,7 +207,7 @@ return [
|
||||
['options' => [
|
||||
'replication' => 'sentinel',
|
||||
'service' => env('REDIS_SENTINEL_SERVICE', 'mymaster'),
|
||||
'sentinel_timeout' => 1.0,
|
||||
'sentinel_timeout' => 2.0,
|
||||
'parameters' => [
|
||||
'password' => env('REDIS_PASSWORD', null),
|
||||
'database' => env('REDIS_DB', 0),
|
||||
@ -226,7 +226,7 @@ return [
|
||||
['options' => [
|
||||
'replication' => 'sentinel',
|
||||
'service' => env('REDIS_SENTINEL_SERVICE', 'mymaster'),
|
||||
'sentinel_timeout' => 1.0,
|
||||
'sentinel_timeout' => 2.0,
|
||||
'parameters' => [
|
||||
'password' => env('REDIS_PASSWORD', null),
|
||||
'database' => env('REDIS_CACHE_DB', 1),
|
||||
|
@ -14,8 +14,8 @@ return [
|
||||
'require_https' => env('REQUIRE_HTTPS', true),
|
||||
'app_url' => rtrim(env('APP_URL', ''), '/'),
|
||||
'app_domain' => env('APP_DOMAIN', 'invoicing.co'),
|
||||
'app_version' => '5.3.29',
|
||||
'app_tag' => '5.3.29',
|
||||
'app_version' => '5.3.31',
|
||||
'app_tag' => '5.3.31',
|
||||
'minimum_client_version' => '5.0.16',
|
||||
'terms_version' => '1.0.1',
|
||||
'api_secret' => env('API_SECRET', ''),
|
||||
|
@ -1,21 +0,0 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class Onboarding extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('accounts', function (Blueprint $table) {
|
||||
$table->boolean('is_onboarding')->default(false);
|
||||
$table->mediumText('onboarding')->nullable();
|
||||
});
|
||||
}
|
||||
}
|
37
database/migrations/2021_11_08_131308_onboarding.php
Normal file
37
database/migrations/2021_11_08_131308_onboarding.php
Normal file
@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class Onboarding extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
|
||||
if (!Schema::hasColumn('accounts', 'is_onboarding'))
|
||||
{
|
||||
|
||||
Schema::table('accounts', function (Blueprint $table) {
|
||||
$table->boolean('is_onboarding')->default(false);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
if (!Schema::hasColumn('accounts', 'onboarding'))
|
||||
{
|
||||
|
||||
Schema::table('accounts', function (Blueprint $table) {
|
||||
$table->mediumText('onboarding')->nullable();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class AddIsMigrateColumnToAccountsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('accounts', function (Blueprint $table) {
|
||||
$table->boolean('is_migrated')->default(false);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('accounts', function (Blueprint $table) {
|
||||
//
|
||||
});
|
||||
}
|
||||
}
|
663
package-lock.json
generated
663
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -7207,31 +7207,6 @@ DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
flutter_share
|
||||
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018 Lucas Britto
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
flutter_slidable
|
||||
|
||||
|
8
public/flutter_service_worker.js
vendored
8
public/flutter_service_worker.js
vendored
@ -6,12 +6,12 @@ const RESOURCES = {
|
||||
"icons/Icon-512.png": "0f9aff01367f0a0c69773d25ca16ef35",
|
||||
"icons/Icon-192.png": "bb1cf5f6982006952211c7c8404ffbed",
|
||||
"favicon.png": "dca91c54388f52eded692718d5a98b8b",
|
||||
"/": "75a79956958abb5cc1e72568f6f5825f",
|
||||
"/": "6310be19342ca9ed6920572bcc23151f",
|
||||
"manifest.json": "ef43d90e57aa7682d7e2cfba2f484a40",
|
||||
"favicon.ico": "51636d3a390451561744c42188ccd628",
|
||||
"version.json": "3930722e1581f582eefe59c75ab50c99",
|
||||
"version.json": "9c7b0edc83733da56c726678aacd9fd3",
|
||||
"assets/fonts/MaterialIcons-Regular.otf": "4e6447691c9509f7acdbf8a931a85ca1",
|
||||
"assets/NOTICES": "9eb7e2eb2888ea5bae5f536720db37cd",
|
||||
"assets/NOTICES": "7610cf8f301427a1104669ea3f4074ac",
|
||||
"assets/packages/material_design_icons_flutter/lib/fonts/materialdesignicons-webfont.ttf": "174c02fc4609e8fc4389f5d21f16a296",
|
||||
"assets/FontManifest.json": "cf3c681641169319e61b61bd0277378f",
|
||||
"assets/assets/images/payment_types/paypal.png": "8e06c094c1871376dfea1da8088c29d1",
|
||||
@ -34,7 +34,7 @@ const RESOURCES = {
|
||||
"assets/assets/images/google_logo.png": "0f118259ce403274f407f5e982e681c3",
|
||||
"assets/assets/images/logo_light.png": "e5f46d5a78e226e7a9553d4ca6f69219",
|
||||
"assets/AssetManifest.json": "38d9aea341601f3a5c6fa7b5a1216ea5",
|
||||
"main.dart.js": "6e207d13aff376878b13c6469e07163d"
|
||||
"main.dart.js": "d57b5bf95106fcc642bac2ab5bc52fc3"
|
||||
};
|
||||
|
||||
// The application shell files that are downloaded before a service worker can
|
||||
|
2
public/js/clients/invoices/payment.js
vendored
2
public/js/clients/invoices/payment.js
vendored
@ -1,2 +1,2 @@
|
||||
/*! For license information please see payment.js.LICENSE.txt */
|
||||
(()=>{function e(e,t){for(var n=0;n<t.length;n++){var a=t[n];a.enumerable=a.enumerable||!1,a.configurable=!0,"value"in a&&(a.writable=!0),Object.defineProperty(e,a.key,a)}}var t=function(){function t(e,n){!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,t),this.shouldDisplayTerms=e,this.shouldDisplaySignature=n,this.termsAccepted=!1}var n,a,i;return n=t,(a=[{key:"handleMethodSelect",value:function(e){var t=this;document.getElementById("company_gateway_id").value=e.dataset.companyGatewayId,document.getElementById("payment_method_id").value=e.dataset.gatewayTypeId,this.shouldDisplaySignature&&!this.shouldDisplayTerms&&(this.displayTerms(),document.getElementById("accept-terms-button").addEventListener("click",(function(){t.termsAccepted=!0,t.submitForm()}))),!this.shouldDisplaySignature&&this.shouldDisplayTerms&&(this.displaySignature(),document.getElementById("signature-next-step").addEventListener("click",(function(){document.querySelector('input[name="signature"').value=t.signaturePad.toDataURL(),t.submitForm()}))),this.shouldDisplaySignature&&this.shouldDisplayTerms&&(this.displaySignature(),document.getElementById("signature-next-step").addEventListener("click",(function(){t.displayTerms(),document.getElementById("accept-terms-button").addEventListener("click",(function(){document.querySelector('input[name="signature"').value=t.signaturePad.toDataURL(),t.termsAccepted=!0,t.submitForm()}))}))),this.shouldDisplaySignature||this.shouldDisplayTerms||this.submitForm()}},{key:"submitForm",value:function(){document.getElementById("payment-form").submit()}},{key:"displayTerms",value:function(){document.getElementById("displayTermsModal").removeAttribute("style")}},{key:"displaySignature",value:function(){document.getElementById("displaySignatureModal").removeAttribute("style");var e=new SignaturePad(document.getElementById("signature-pad"),{penColor:"rgb(0, 0, 0)"});this.signaturePad=e}},{key:"handle",value:function(){var e=this;document.querySelectorAll(".dropdown-gateway-button").forEach((function(t){t.addEventListener("click",(function(){return e.handleMethodSelect(t)}))}))}}])&&e(n.prototype,a),i&&e(n,i),t}(),n=document.querySelector('meta[name="require-invoice-signature"]').content,a=document.querySelector('meta[name="show-invoice-terms"]').content;new t(Boolean(+n),Boolean(+a)).handle()})();
|
||||
(()=>{function e(e,t){for(var n=0;n<t.length;n++){var a=t[n];a.enumerable=a.enumerable||!1,a.configurable=!0,"value"in a&&(a.writable=!0),Object.defineProperty(e,a.key,a)}}var t=function(){function t(e,n){!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,t),this.shouldDisplayTerms=e,this.shouldDisplaySignature=n,this.termsAccepted=!1,this.submitting=!1}var n,a,i;return n=t,(a=[{key:"handleMethodSelect",value:function(e){var t=this;document.getElementById("company_gateway_id").value=e.dataset.companyGatewayId,document.getElementById("payment_method_id").value=e.dataset.gatewayTypeId,this.shouldDisplaySignature&&!this.shouldDisplayTerms&&(this.displayTerms(),document.getElementById("accept-terms-button").addEventListener("click",(function(){t.termsAccepted=!0,t.submitForm()}))),!this.shouldDisplaySignature&&this.shouldDisplayTerms&&(this.displaySignature(),document.getElementById("signature-next-step").addEventListener("click",(function(){document.querySelector('input[name="signature"').value=t.signaturePad.toDataURL(),t.submitForm()}))),this.shouldDisplaySignature&&this.shouldDisplayTerms&&(this.displaySignature(),document.getElementById("signature-next-step").addEventListener("click",(function(){t.displayTerms(),document.getElementById("accept-terms-button").addEventListener("click",(function(){document.querySelector('input[name="signature"').value=t.signaturePad.toDataURL(),t.termsAccepted=!0,t.submitForm()}))}))),this.shouldDisplaySignature||this.shouldDisplayTerms||this.submitForm()}},{key:"submitForm",value:function(){document.getElementById("payment-form").submit()}},{key:"displayTerms",value:function(){document.getElementById("displayTermsModal").removeAttribute("style")}},{key:"displaySignature",value:function(){document.getElementById("displaySignatureModal").removeAttribute("style");var e=new SignaturePad(document.getElementById("signature-pad"),{penColor:"rgb(0, 0, 0)"});this.signaturePad=e}},{key:"handle",value:function(){var e=this;document.querySelectorAll(".dropdown-gateway-button").forEach((function(t){t.addEventListener("click",(function(){e.submitting||(e.handleMethodSelect(t),e.submitting=!0)}))}))}}])&&e(n.prototype,a),i&&e(n,i),t}(),n=document.querySelector('meta[name="require-invoice-signature"]').content,a=document.querySelector('meta[name="show-invoice-terms"]').content;new t(Boolean(+n),Boolean(+a)).handle()})();
|
@ -1,2 +1,2 @@
|
||||
/*! For license information please see wepay-bank-account.js.LICENSE.txt */
|
||||
(()=>{function e(e,n){for(var t=0;t<n.length;t++){var o=n[t];o.enumerable=o.enumerable||!1,o.configurable=!0,"value"in o&&(o.writable=!0),Object.defineProperty(e,o.key,o)}}var n=function(){function n(){!function(e,n){if(!(e instanceof n))throw new TypeError("Cannot call a class as a function")}(this,n)}var t,o,r;return t=n,(o=[{key:"initializeWePay",value:function(){var e,n=null===(e=document.querySelector('meta[name="wepay-environment"]'))||void 0===e?void 0:e.content;return WePay.set_endpoint("staging"===n?"stage":"production"),this}},{key:"showBankPopup",value:function(){var e,n;WePay.bank_account.create({client_id:null===(e=document.querySelector("meta[name=wepay-client-id]"))||void 0===e?void 0:e.content,email:null===(n=document.querySelector("meta[name=contact-email]"))||void 0===n?void 0:n.content},(function(e){e.error?(errors.textContent="",errors.textContent=e.error_description,errors.hidden=!1):(document.querySelector('input[name="bank_account_id"]').value=e.bank_account_id,document.getElementById("server_response").submit())}),(function(e){e.error&&(errors.textContent="",errors.textContent=e.error_description,errors.hidden=!1)}))}},{key:"handle",value:function(){this.initializeWePay().showBankPopup()}}])&&e(t.prototype,o),r&&e(t,r),n}();document.addEventListener("DOMContentLoaded",(function(){(new n).handle()}))})();
|
||||
(()=>{function e(e,n){for(var t=0;t<n.length;t++){var o=n[t];o.enumerable=o.enumerable||!1,o.configurable=!0,"value"in o&&(o.writable=!0),Object.defineProperty(e,o.key,o)}}var n=function(){function n(){!function(e,n){if(!(e instanceof n))throw new TypeError("Cannot call a class as a function")}(this,n)}var t,o,r;return t=n,(o=[{key:"initializeWePay",value:function(){var e,n=null===(e=document.querySelector('meta[name="wepay-environment"]'))||void 0===e?void 0:e.content;return WePay.set_endpoint("staging"===n?"stage":"production"),this}},{key:"showBankPopup",value:function(){var e,n;WePay.bank_account.create({client_id:null===(e=document.querySelector("meta[name=wepay-client-id]"))||void 0===e?void 0:e.content,email:null===(n=document.querySelector("meta[name=contact-email]"))||void 0===n?void 0:n.content,options:{avoidMicrodeposits:!0}},(function(e){e.error?(errors.textContent="",errors.textContent=e.error_description,errors.hidden=!1):(document.querySelector('input[name="bank_account_id"]').value=e.bank_account_id,document.getElementById("server_response").submit())}),(function(e){e.error&&(errors.textContent="",errors.textContent=e.error_description,errors.hidden=!1)}))}},{key:"handle",value:function(){this.initializeWePay().showBankPopup()}}])&&e(t.prototype,o),r&&e(t,r),n}();document.addEventListener("DOMContentLoaded",(function(){(new n).handle()}))})();
|
2
public/js/clients/payments/razorpay-aio.js
vendored
2
public/js/clients/payments/razorpay-aio.js
vendored
@ -1,2 +1,2 @@
|
||||
/*! For license information please see razorpay-aio.js.LICENSE.txt */
|
||||
(()=>{var e,n=JSON.parse(null===(e=document.querySelector("meta[name=razorpay-options]"))||void 0===e?void 0:e.content);n.handler=function(e){document.getElementById("razorpay_payment_id").value=e.razorpay_payment_id,document.getElementById("razorpay_signature").value=e.razorpay_signature,document.getElementById("server-response").submit()};var t=new Razorpay(n);document.getElementById("pay-now").onclick=function(e){e.target.parentElement.disabled=!0,t.open()}})();
|
||||
(()=>{var e,n=JSON.parse(null===(e=document.querySelector("meta[name=razorpay-options]"))||void 0===e?void 0:e.content);n.handler=function(e){document.getElementById("razorpay_payment_id").value=e.razorpay_payment_id,document.getElementById("razorpay_signature").value=e.razorpay_signature,document.getElementById("server-response").submit()},n.modal={ondismiss:function(){o.disabled=!1}};var a=new Razorpay(n),o=document.getElementById("pay-now");o.onclick=function(e){o.disabled=!0,a.open()}})();
|
245394
public/main.dart.js
vendored
245394
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
250386
public/main.foss.dart.js
vendored
250386
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
243568
public/main.html.dart.js
vendored
243568
public/main.html.dart.js
vendored
File diff suppressed because one or more lines are too long
229747
public/main.next.dart.js
vendored
229747
public/main.next.dart.js
vendored
File diff suppressed because one or more lines are too long
13709
public/main.profile.dart.js
vendored
13709
public/main.profile.dart.js
vendored
File diff suppressed because one or more lines are too long
@ -4,7 +4,7 @@
|
||||
"/js/clients/payments/authorize-credit-card-payment.js": "/js/clients/payments/authorize-credit-card-payment.js?id=cfe5de1cf87a0b01568d",
|
||||
"/js/clients/payments/stripe-ach.js": "/js/clients/payments/stripe-ach.js?id=5e74bc0d346beeb57ee9",
|
||||
"/js/clients/invoices/action-selectors.js": "/js/clients/invoices/action-selectors.js?id=6b79265cbb8c963eef19",
|
||||
"/js/clients/invoices/payment.js": "/js/clients/invoices/payment.js?id=5b79f72432f92a85fefa",
|
||||
"/js/clients/invoices/payment.js": "/js/clients/invoices/payment.js?id=d9132fae12153a6943a6",
|
||||
"/js/clients/payments/stripe-sofort.js": "/js/clients/payments/stripe-sofort.js?id=926c7b9d1ee48bbf786b",
|
||||
"/js/clients/payments/stripe-alipay.js": "/js/clients/payments/stripe-alipay.js?id=1e159400d6a5ca4662c1",
|
||||
"/js/clients/payments/checkout-credit-card.js": "/js/clients/payments/checkout-credit-card.js?id=0b47ce36fe20191adb33",
|
||||
@ -19,14 +19,14 @@
|
||||
"/js/clients/payments/braintree-credit-card.js": "/js/clients/payments/braintree-credit-card.js?id=a334dd9257dd510a1feb",
|
||||
"/js/clients/payments/braintree-paypal.js": "/js/clients/payments/braintree-paypal.js?id=37950e8a39281d2f596a",
|
||||
"/js/clients/payments/wepay-credit-card.js": "/js/clients/payments/wepay-credit-card.js?id=ba4d5b7175117ababdb2",
|
||||
"/js/clients/payment_methods/wepay-bank-account.js": "/js/clients/payment_methods/wepay-bank-account.js?id=b1704cb9bd7975605310",
|
||||
"/js/clients/payment_methods/wepay-bank-account.js": "/js/clients/payment_methods/wepay-bank-account.js?id=8328c6c32a65cd3e8a3d",
|
||||
"/js/clients/payments/paytrace-credit-card.js": "/js/clients/payments/paytrace-credit-card.js?id=59d9913b746fe5a540ff",
|
||||
"/js/clients/payments/mollie-credit-card.js": "/js/clients/payments/mollie-credit-card.js?id=c2cf632fb3cc91b4ff7c",
|
||||
"/js/clients/payments/eway-credit-card.js": "/js/clients/payments/eway-credit-card.js?id=ff17e039dd15d505448f",
|
||||
"/js/clients/payment_methods/braintree-ach.js": "/js/clients/payment_methods/braintree-ach.js?id=656ad159838b726969f5",
|
||||
"/js/clients/payments/square-credit-card.js": "/js/clients/payments/square-credit-card.js?id=8f05ce6bd2d6cae7e5f2",
|
||||
"/js/clients/statements/view.js": "/js/clients/statements/view.js?id=ea3db0c04b4372f76735",
|
||||
"/js/clients/payments/razorpay-aio.js": "/js/clients/payments/razorpay-aio.js?id=1dae47a0b783814ce895",
|
||||
"/js/clients/payments/razorpay-aio.js": "/js/clients/payments/razorpay-aio.js?id=c36ab5621413ef1de7c8",
|
||||
"/js/clients/payments/stripe-sepa.js": "/js/clients/payments/stripe-sepa.js?id=cbd7bb4c483ca75333f4",
|
||||
"/js/clients/payment_methods/authorize-checkout-card.js": "/js/clients/payment_methods/authorize-checkout-card.js?id=61becda97682c7909f29",
|
||||
"/js/clients/payments/stripe-giropay.js": "/js/clients/payments/stripe-giropay.js?id=cdf300d72a1564d19b72",
|
||||
|
@ -1 +1 @@
|
||||
{"app_name":"invoiceninja_flutter","version":"5.0.63","build_number":"63"}
|
||||
{"app_name":"invoiceninja_flutter","version":"5.0.67","build_number":"67"}
|
11
resources/js/clients/invoices/payment.js
vendored
11
resources/js/clients/invoices/payment.js
vendored
@ -13,6 +13,7 @@ class Payment {
|
||||
this.shouldDisplayTerms = displayTerms;
|
||||
this.shouldDisplaySignature = displaySignature;
|
||||
this.termsAccepted = false;
|
||||
this.submitting = false;
|
||||
}
|
||||
|
||||
handleMethodSelect(element) {
|
||||
@ -95,9 +96,13 @@ class Payment {
|
||||
document
|
||||
.querySelectorAll(".dropdown-gateway-button")
|
||||
.forEach(element => {
|
||||
element.addEventListener("click", () =>
|
||||
this.handleMethodSelect(element)
|
||||
);
|
||||
element.addEventListener("click", () => {
|
||||
if (!this.submitting) {
|
||||
this.handleMethodSelect(element)
|
||||
|
||||
this.submitting = true;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -20,7 +20,10 @@ class WePayBank {
|
||||
showBankPopup() {
|
||||
WePay.bank_account.create({
|
||||
client_id: document.querySelector('meta[name=wepay-client-id]')?.content,
|
||||
email: document.querySelector('meta[name=contact-email]')?.content
|
||||
email: document.querySelector('meta[name=contact-email]')?.content,
|
||||
options: {
|
||||
avoidMicrodeposits:true
|
||||
}
|
||||
}, function (data) {
|
||||
if (data.error) {
|
||||
errors.textContent = '';
|
||||
|
15
resources/js/clients/payments/razorpay-aio.js
vendored
15
resources/js/clients/payments/razorpay-aio.js
vendored
@ -12,7 +12,7 @@ let options = JSON.parse(
|
||||
document.querySelector('meta[name=razorpay-options]')?.content
|
||||
);
|
||||
|
||||
options.handler = function(response) {
|
||||
options.handler = function (response) {
|
||||
document.getElementById('razorpay_payment_id').value =
|
||||
response.razorpay_payment_id;
|
||||
document.getElementById('razorpay_signature').value =
|
||||
@ -20,10 +20,17 @@ options.handler = function(response) {
|
||||
document.getElementById('server-response').submit();
|
||||
};
|
||||
|
||||
let razorpay = new Razorpay(options);
|
||||
options.modal = {
|
||||
ondismiss: function () {
|
||||
payNowButton.disabled = false;
|
||||
},
|
||||
};
|
||||
|
||||
document.getElementById('pay-now').onclick = function(event) {
|
||||
event.target.parentElement.disabled = true;
|
||||
let razorpay = new Razorpay(options);
|
||||
let payNowButton = document.getElementById('pay-now');
|
||||
|
||||
payNowButton.onclick = function (event) {
|
||||
payNowButton.disabled = true;
|
||||
|
||||
razorpay.open();
|
||||
};
|
||||
|
@ -367,6 +367,9 @@
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
$entity_images
|
||||
|
||||
<div id="footer">
|
||||
<div>
|
||||
<p data-ref="total_table-footer">$entity_footer</p>
|
||||
|
@ -294,7 +294,7 @@
|
||||
/** To find out selectors on your own: https://invoiceninja.github.io/docs/custom-fields/#snippets **/
|
||||
</style>
|
||||
|
||||
<table>
|
||||
<table style="min-width: 100%">
|
||||
<thead>
|
||||
<tr>
|
||||
<td>
|
||||
@ -347,6 +347,8 @@
|
||||
|
||||
<div class="repeating-header" id="header"></div>
|
||||
|
||||
$entity_images
|
||||
|
||||
<div class="repeating-footer" id="footer">
|
||||
<p data-ref="total_table-footer">$entity_footer</p>
|
||||
</div>
|
||||
|
@ -258,7 +258,7 @@
|
||||
/** To find out selectors on your own: https://invoiceninja.github.io/docs/custom-fields/#snippets **/
|
||||
</style>
|
||||
|
||||
<table>
|
||||
<table style="min-width: 100%">
|
||||
<thead>
|
||||
<tr>
|
||||
<td>
|
||||
@ -309,6 +309,8 @@
|
||||
|
||||
<div class="repeating-header" id="header"></div>
|
||||
|
||||
$entity_images
|
||||
|
||||
<div class="repeating-footer" id="footer">
|
||||
<p data-ref="total_table-footer">$entity_footer</p>
|
||||
</div>
|
||||
|
@ -247,7 +247,7 @@
|
||||
/** To find out selectors on your own: https://invoiceninja.github.io/docs/custom-fields/#snippets **/
|
||||
</style>
|
||||
|
||||
<table>
|
||||
<table style="min-width: 100%">
|
||||
<thead>
|
||||
<tr>
|
||||
<td>
|
||||
@ -307,6 +307,8 @@
|
||||
<p data-ref="total_table-footer">$entity_footer</p>
|
||||
</div>
|
||||
|
||||
$entity_images
|
||||
|
||||
<script>
|
||||
// Clear up space a bit, if [product-table, tasks-table, delivery-note-table] isn't present.
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
|
@ -317,6 +317,8 @@
|
||||
<p data-ref="total_table-footer">$entity_footer</p>
|
||||
</div>
|
||||
|
||||
$entity_images
|
||||
|
||||
<script>
|
||||
// Clear up space a bit, if [product-table, tasks-table, delivery-note-table] isn't present.
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
|
@ -272,7 +272,7 @@
|
||||
/** To find out selectors on your own: https://invoiceninja.github.io/docs/custom-fields/#snippets **/
|
||||
</style>
|
||||
|
||||
<table>
|
||||
<table style="min-width: 100%">
|
||||
<thead>
|
||||
<tr>
|
||||
<td>
|
||||
@ -358,6 +358,8 @@
|
||||
<p data-ref="total_table-footer">$entity_footer</p>
|
||||
</div>
|
||||
|
||||
$entity_images
|
||||
|
||||
<script>
|
||||
// Clear up space a bit, if [product-table, tasks-table, delivery-note-table] isn't present.
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
|
@ -344,6 +344,8 @@
|
||||
</table>
|
||||
</div>
|
||||
|
||||
$entity_images
|
||||
|
||||
<div id="footer">
|
||||
<div class="footer-content">
|
||||
<div>
|
||||
|
@ -238,7 +238,7 @@
|
||||
/** To find out selectors on your own: https://invoiceninja.github.io/docs/custom-fields/#snippets **/
|
||||
</style>
|
||||
|
||||
<table>
|
||||
<table style="min-width: 100%">
|
||||
<thead>
|
||||
<tr>
|
||||
<td>
|
||||
@ -292,6 +292,8 @@
|
||||
<p data-ref="total_table-footer">$entity_footer</p>
|
||||
</div>
|
||||
|
||||
$entity_images
|
||||
|
||||
<script>
|
||||
// Clear up space a bit, if [product-table, tasks-table, delivery-note-table] isn't present.
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
|
@ -375,7 +375,9 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="repeating-footer" id="footer">
|
||||
$entity_images
|
||||
|
||||
<div class="repeating-footer" id="footer">
|
||||
<p data-ref="total_table-footer">$entity_footer</p>
|
||||
|
||||
<div id="footer-colors">
|
||||
|
@ -357,6 +357,8 @@
|
||||
<p data-ref="total_table-footer">$entity_footer</p>
|
||||
</div>
|
||||
|
||||
$entity_images
|
||||
|
||||
<script>
|
||||
// Clear up space a bit, if [product-table, tasks-table, delivery-note-table] isn't present.
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
|
@ -1,23 +1,42 @@
|
||||
@if($entity->documents->count() > 0)
|
||||
@if ($entity->documents->count() > 0 || $entity->company->documents->count() > 0)
|
||||
<div class="bg-white shadow sm:rounded-lg my-4">
|
||||
<div class="px-4 py-5 sm:p-6">
|
||||
<div class="sm:flex sm:items-start sm:justify-between">
|
||||
<div>
|
||||
<p class="text-lg leading-6 font-medium text-gray-900">{{ ctrans('texts.attachments') }}:</p>
|
||||
@foreach($entity->documents as $document)
|
||||
@foreach ($entity->documents as $document)
|
||||
<div class="inline-flex items-center space-x-1">
|
||||
<a href="{{ route('client.documents.show', $document->hashed_id) }}" target="_blank"
|
||||
class="block text-sm button-link text-primary">{{ Illuminate\Support\Str::limit($document->name, 40) }}</a>
|
||||
class="block text-sm button-link text-primary">{{ Illuminate\Support\Str::limit($document->name, 40) }}</a>
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"
|
||||
fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
|
||||
stroke-linejoin="round" class="text-primary h-6 w-4">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"
|
||||
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
|
||||
class="text-primary h-6 w-4">
|
||||
<path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"></path>
|
||||
<polyline points="15 3 21 3 21 9"></polyline>
|
||||
<line x1="10" y1="14" x2="21" y2="3"></line>
|
||||
</svg>
|
||||
|
||||
@if(!$loop->last)
|
||||
@if (!$loop->last)
|
||||
<span>—</span>
|
||||
@endif
|
||||
</div>
|
||||
@endforeach
|
||||
|
||||
@foreach ($entity->company->documents as $document)
|
||||
<div class="inline-flex items-center space-x-1">
|
||||
<a href="{{ route('client.documents.show', $document->hashed_id) }}" target="_blank"
|
||||
class="block text-sm button-link text-primary">{{ Illuminate\Support\Str::limit($document->name, 40) }}</a>
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"
|
||||
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
|
||||
class="text-primary h-6 w-4">
|
||||
<path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"></path>
|
||||
<polyline points="15 3 21 3 21 9"></polyline>
|
||||
<line x1="10" y1="14" x2="21" y2="3"></line>
|
||||
</svg>
|
||||
|
||||
@if (!$loop->last)
|
||||
<span>—</span>
|
||||
@endif
|
||||
</div>
|
||||
|
@ -25,8 +25,8 @@
|
||||
@foreach($multiple_contacts as $contact)
|
||||
<a data-turbolinks="false"
|
||||
href="{{ route('client.switch_company', $contact->hashed_id) }}"
|
||||
class="block px-4 py-2 text-sm leading-5 text-gray-700 hover:bg-gray-100 hover:text-gray-900 focus:outline-none focus:bg-gray-100 focus:text-gray-900">{{ $contact->company->present()->name() }}
|
||||
- {{ $contact->client->present()->name()}}</a>
|
||||
class="block px-4 py-2 text-sm leading-5 text-gray-700 hover:bg-gray-100 hover:text-gray-900 focus:outline-none focus:bg-gray-100 focus:text-gray-900">{{ $contact->client->present()->name()}} - {{ $contact->company->present()->name() }}
|
||||
</a>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
|
@ -64,7 +64,7 @@
|
||||
<option>15</option>
|
||||
<option>20</option>
|
||||
</select>
|
||||
<button x-on:click="document.getElementById('multiple-downloads').submit()" class="button button-primary bg-primary py-2 ml-2">
|
||||
<button onclick="document.getElementById('multiple-downloads').submit(); setTimeout(() => this.disabled = true, 0); setTimeout(() => this.disabled = false, 5000);" class="button button-primary bg-primary py-2 ml-2">
|
||||
<span class="hidden md:block">
|
||||
{{ ctrans('texts.download_selected') }}
|
||||
</span>
|
||||
|
@ -99,7 +99,7 @@
|
||||
@csrf
|
||||
<input type="hidden" name="invoices[]" value="{{ $invoice->hashed_id }}">
|
||||
<input type="hidden" name="action" value="payment">
|
||||
<button class="px-2 py-1 mr-3 text-xs uppercase button button-primary bg-primary" dusk="pay-now">
|
||||
<button onclick="setTimeout(() => this.disabled = true, 0); return true;" class="px-2 py-1 mr-3 text-xs uppercase button button-primary bg-primary" dusk="pay-now">
|
||||
{{ ctrans('texts.pay_now') }}
|
||||
</button>
|
||||
</form>
|
||||
|
@ -15,10 +15,10 @@
|
||||
<div class="flex items-center">
|
||||
<form action="{{ route('client.invoices.bulk') }}" method="post" id="bulkActions">
|
||||
@csrf
|
||||
<button type="submit" class="button button-primary bg-primary" name="action" value="download">{{ ctrans('texts.download') }}</button>
|
||||
<button type="submit" onclick="setTimeout(() => this.disabled = true, 0); setTimeout(() => this.disabled = false, 5000); return true;" class="button button-primary bg-primary" name="action" value="download">{{ ctrans('texts.download') }}</button>
|
||||
|
||||
@if(!empty(auth()->user()->client->service()->getPaymentMethods(0)))
|
||||
<button type="submit" class="button button-primary bg-primary" name="action" value="payment">{{ ctrans('texts.pay_now') }}</button>
|
||||
<button onclick="setTimeout(() => this.disabled = true, 0); return true;" type="submit" class="button button-primary bg-primary" name="action" value="payment">{{ ctrans('texts.pay_now') }}</button>
|
||||
@endif
|
||||
</form>
|
||||
</div>
|
||||
|
@ -36,7 +36,7 @@
|
||||
<form action="{{ route('client.payment_methods.destroy', [$payment_method->hashed_id, 'method' => $payment_method->gateway_type->id]) }}" method="post">
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
<button type="submit" class="button button-danger button-block" dusk="confirm-payment-removal">
|
||||
<button type="submit" onclick="setTimeout(() => this.disabled = true, 0); return true;" class="button button-danger button-block" dusk="confirm-payment-removal">
|
||||
{{ ctrans('texts.remove') }}
|
||||
</button>
|
||||
</form>
|
||||
|
@ -26,7 +26,7 @@
|
||||
<div class="relative inline-block text-left">
|
||||
<div>
|
||||
<div class="rounded-md shadow-sm">
|
||||
<button type="button" id="approve-button"
|
||||
<button type="button" id="approve-button" onclick="setTimeout(() => this.disabled = true, 0); return true;"
|
||||
class="inline-flex justify-center w-full rounded-md border border-gray-300 px-4 py-2 bg-white text-sm leading-5 font-medium text-gray-700 hover:text-gray-500 focus:outline-none focus:border-blue-300 focus:ring-blue active:bg-gray-50 active:text-gray-800 transition ease-in-out duration-150">
|
||||
{{ ctrans('texts.approve') }}
|
||||
</button>
|
||||
|
@ -1,27 +1,30 @@
|
||||
<form action="{{ route('client.quotes.bulk') }}" method="post" id="approve-form" />
|
||||
@csrf
|
||||
<input type="hidden" name="action" value="approve">
|
||||
<input type="hidden" name="process" value="true">
|
||||
<input type="hidden" name="quotes[]" value="{{ $quote->hashed_id }}">
|
||||
<input type="hidden" name="signature">
|
||||
@csrf
|
||||
|
||||
<div class="bg-white shadow sm:rounded-lg">
|
||||
<div class="px-4 py-5 sm:p-6">
|
||||
<div class="sm:flex sm:items-start sm:justify-between">
|
||||
<h3 class="text-lg leading-6 font-medium text-gray-900">
|
||||
{{ ctrans('texts.approve') }}
|
||||
</h3>
|
||||
<input type="hidden" name="action" value="approve">
|
||||
<input type="hidden" name="process" value="true">
|
||||
<input type="hidden" name="quotes[]" value="{{ $quote->hashed_id }}">
|
||||
<input type="hidden" name="signature">
|
||||
|
||||
<div class="mt-5 sm:mt-0 sm:ml-6 sm:flex-shrink-0 sm:flex sm:items-center">
|
||||
@yield('quote-not-approved-right-side')
|
||||
<div class="bg-white shadow sm:rounded-lg">
|
||||
<div class="px-4 py-5 sm:p-6">
|
||||
<div class="sm:flex sm:items-start sm:justify-between">
|
||||
<h3 class="text-lg leading-6 font-medium text-gray-900">
|
||||
{{ ctrans('texts.approve') }}
|
||||
</h3>
|
||||
|
||||
<div class="inline-flex rounded-md shadow-sm">
|
||||
<input type="hidden" name="action" value="payment">
|
||||
<button type="button" class="button button-primary bg-primary" id="approve-button">{{ ctrans('texts.approve') }}</button>
|
||||
</div>
|
||||
<div class="mt-5 sm:mt-0 sm:ml-6 sm:flex-shrink-0 sm:flex sm:items-center">
|
||||
@yield('quote-not-approved-right-side')
|
||||
|
||||
<div class="inline-flex rounded-md shadow-sm">
|
||||
<input type="hidden" name="action" value="payment">
|
||||
<button onclick="setTimeout(() => this.disabled = true, 0); return true;" type="button"
|
||||
class="button button-primary bg-primary"
|
||||
id="approve-button">{{ ctrans('texts.approve') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
@ -2,9 +2,9 @@
|
||||
@section('meta_title', ctrans('texts.quotes'))
|
||||
|
||||
@section('header')
|
||||
@if($errors->any())
|
||||
@if ($errors->any())
|
||||
<div class="alert alert-failure mb-4">
|
||||
@foreach($errors->all() as $error)
|
||||
@foreach ($errors->all() as $error)
|
||||
<p>{{ $error }}</p>
|
||||
@endforeach
|
||||
</div>
|
||||
@ -15,13 +15,17 @@
|
||||
<div class="flex justify-between items-center">
|
||||
<form action="{{ route('client.quotes.bulk') }}" method="post" id="bulkActions">
|
||||
@csrf
|
||||
<button type="submit" class="button button-primary bg-primary" name="action"
|
||||
value="download">{{ ctrans('texts.download') }}</button>
|
||||
<button type="submit" class="button button-primary bg-primary" name="action"
|
||||
value="approve">{{ ctrans('texts.approve') }}</button>
|
||||
<button type="submit"
|
||||
onclick="setTimeout(() => this.disabled = true, 0); setTimeout(() => this.disabled = false, 5000); return true;"
|
||||
class="button button-primary bg-primary" name="action"
|
||||
value="download">{{ ctrans('texts.download') }}</button>
|
||||
<button type="submit" onclick="setTimeout(() => this.disabled = true, 0); return true;"
|
||||
class="button button-primary bg-primary" name="action"
|
||||
value="approve">{{ ctrans('texts.approve') }}</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col mt-4">
|
||||
@livewire('quotes-table', ['company' => $company])
|
||||
</div>
|
||||
@endsection
|
||||
@endsection
|
||||
|
@ -31,7 +31,7 @@
|
||||
<span class="ml-2">{{ ctrans('texts.show_aging') }}</span>
|
||||
</label> <!-- End show aging checkbox -->
|
||||
</div>
|
||||
<button id="pdf-download" class="button button-primary bg-primary mt-4 md:mt-0">{{ ctrans('texts.download') }}</button>
|
||||
<button onclick="setTimeout(() => this.disabled = true, 0); setTimeout(() => this.disabled = false, 5000); return true;" id="pdf-download" class="button button-primary bg-primary mt-4 md:mt-0">{{ ctrans('texts.download') }}</button>
|
||||
</div>
|
||||
|
||||
@include('portal.ninja2020.components.pdf-viewer', ['url' => route('client.statement.raw')])
|
||||
|
@ -23,7 +23,7 @@ Route::get('tmp_pdf/{hash}', 'ClientPortal\TempRouteController@index')->name('tm
|
||||
|
||||
Route::get('client/key_login/{contact_key}', 'ClientPortal\ContactHashLoginController@login')->name('client.contact_login')->middleware(['domain_db','contact_key_login']);
|
||||
Route::get('client/magic_link/{magic_link}', 'ClientPortal\ContactHashLoginController@magicLink')->name('client.contact_magic_link')->middleware(['domain_db','contact_key_login']);
|
||||
Route::get('documents/{document_hash}', 'ClientPortal\DocumentController@publicDownload')->name('documents.public_download')->middleware(['document_db']);
|
||||
Route::get('documents/{document_hash}', 'ClientPortal\DocumentController@publicDownload')->name('documents.public_download')->middleware(['domain_db']);
|
||||
Route::get('error', 'ClientPortal\ContactHashLoginController@errorPage')->name('client.error');
|
||||
Route::get('client/payment/{contact_key}/{payment_id}', 'ClientPortal\InvitationController@paymentRouter')->middleware(['domain_db','contact_key_login']);
|
||||
Route::get('client/ninja/{contact_key}/{company_key}', 'ClientPortal\NinjaPlanController@index')->name('client.ninja_contact_login')->middleware(['domain_db']);
|
||||
|
@ -297,6 +297,8 @@ class ClientTest extends TestCase
|
||||
$company_token->account_id = $account->id;
|
||||
$company_token->name = $user->first_name.' '.$user->last_name;
|
||||
$company_token->token = Str::random(64);
|
||||
$company_token->is_system = true;
|
||||
|
||||
$company_token->save();
|
||||
|
||||
$this->token = $company_token->token;
|
||||
@ -353,6 +355,7 @@ class ClientTest extends TestCase
|
||||
$company_token->account_id = $account->id;
|
||||
$company_token->name = $user->first_name.' '.$user->last_name;
|
||||
$company_token->token = Str::random(64);
|
||||
$company_token->is_system = true;
|
||||
$company_token->save();
|
||||
|
||||
$this->token = $company_token->token;
|
||||
|
@ -160,6 +160,7 @@ class LoginTest extends TestCase
|
||||
$company_token->account_id = $account->id;
|
||||
$company_token->name = $user->first_name.' '.$user->last_name;
|
||||
$company_token->token = \Illuminate\Support\Str::random(64);
|
||||
$company_token->is_system = true;
|
||||
$company_token->save();
|
||||
|
||||
$user->companies()->attach($company->id, [
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user