mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-06-23 20:00:33 -04:00
commit
54e2ce974e
@ -1 +1 @@
|
||||
5.3.31
|
||||
5.3.32
|
106
app/Console/Commands/TranslationsExport.php
Normal file
106
app/Console/Commands/TranslationsExport.php
Normal file
@ -0,0 +1,106 @@
|
||||
<?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\Console\Commands;
|
||||
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\Backup;
|
||||
use App\Models\Design;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\Lang;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use stdClass;
|
||||
|
||||
class TranslationsExport extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'ninja:translations';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Transform translations to json';
|
||||
|
||||
private array $langs = [
|
||||
'ar',
|
||||
'ca',
|
||||
'cs',
|
||||
'da',
|
||||
'de',
|
||||
'el',
|
||||
'en',
|
||||
'en_GB',
|
||||
'es',
|
||||
'es_ES',
|
||||
'fa',
|
||||
'fi',
|
||||
'fr',
|
||||
'fr_CA',
|
||||
'hr',
|
||||
'it',
|
||||
'ja',
|
||||
'lt',
|
||||
'lv_LV',
|
||||
'mk_MK',
|
||||
'nb_NO',
|
||||
'nl',
|
||||
'pl',
|
||||
'pt_BR',
|
||||
'pt_PT',
|
||||
'ro',
|
||||
'ru_RU',
|
||||
'sl',
|
||||
'sq',
|
||||
'sv',
|
||||
'th',
|
||||
'tr_TR',
|
||||
'zh_TW'
|
||||
];
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
|
||||
Storage::makeDirectory(storage_path('lang'));
|
||||
|
||||
foreach($this->langs as $lang)
|
||||
{
|
||||
Storage::makeDirectory(storage_path("lang/{$lang}"));
|
||||
|
||||
$translations = Lang::getLoader()->load($lang,'texts');
|
||||
|
||||
Storage::put(storage_path("lang/{$lang}/{$lang}.json"), json_encode(Arr::dot($translations), JSON_UNESCAPED_UNICODE));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -28,7 +28,7 @@ class ClientFactory
|
||||
$client->public_notes = '';
|
||||
$client->balance = 0;
|
||||
$client->paid_to_date = 0;
|
||||
$client->country_id = 4;
|
||||
$client->country_id = 840;
|
||||
$client->is_deleted = 0;
|
||||
$client->client_hash = Str::random(40);
|
||||
$client->settings = ClientSettings::defaults();
|
||||
|
@ -58,7 +58,7 @@ class ContactForgotPasswordController extends Controller
|
||||
*/
|
||||
public function showLinkRequestForm(Request $request)
|
||||
{
|
||||
$account_id = $request->get('account_id');
|
||||
$account_id = $request->has('account_id') ? $request->get('account_id') : 1;
|
||||
$account = Account::find($account_id);
|
||||
$company = $account->companies->first();
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
if(!$account){
|
||||
$account_id = $request->get('account_id');
|
||||
$account = Account::find($account_id);
|
||||
}
|
||||
|
||||
return $this->render('auth.login', ['account' => $account, 'company' => $company]);
|
||||
|
||||
|
@ -68,7 +68,7 @@ class ContactResetPasswordController extends Controller
|
||||
*/
|
||||
public function showResetForm(Request $request, $token = null)
|
||||
{
|
||||
$account_id = $request->get('account_id');
|
||||
$account_id = $request->has('account_id') ? $request->get('account_id') : 1;
|
||||
$account = Account::find($account_id);
|
||||
$db = $account->companies->first()->db;
|
||||
$company = $account->companies->first();
|
||||
|
@ -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) {
|
||||
|
@ -29,7 +29,9 @@ class SubscriptionController extends Controller
|
||||
->where('client_id', auth('contact')->user()->client->id)
|
||||
->where('company_id', auth('contact')->user()->client->company_id)
|
||||
->where('status_id', RecurringInvoice::STATUS_ACTIVE)
|
||||
->where('is_deleted', 0)
|
||||
->whereNotNull('subscription_id')
|
||||
->withTrashed()
|
||||
->count();
|
||||
|
||||
if($count == 0)
|
||||
|
@ -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)));
|
||||
|
||||
|
29
app/Http/Controllers/Gateways/GoCardlessController.php
Normal file
29
app/Http/Controllers/Gateways/GoCardlessController.php
Normal file
@ -0,0 +1,29 @@
|
||||
<?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\Http\Controllers\Gateways;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Gateways\GoCardless\IbpRequest;
|
||||
use App\Models\GatewayType;
|
||||
|
||||
class GoCardlessController extends Controller
|
||||
{
|
||||
public function ibpRedirect(IbpRequest $request)
|
||||
{
|
||||
return $request
|
||||
->getCompanyGateway()
|
||||
->driver($request->getClient())
|
||||
->setPaymentMethod(GatewayType::INSTANT_BANK_PAY)
|
||||
->processPaymentResponse($request);
|
||||
}
|
||||
}
|
@ -174,12 +174,7 @@ class PreviewController extends BaseController
|
||||
|
||||
MultiDB::setDb($company->db);
|
||||
|
||||
if($request->input('entity') == 'invoice'){
|
||||
$repo = new InvoiceRepository();
|
||||
$entity_obj = InvoiceFactory::create($company->id, auth()->user()->id);
|
||||
$class = Invoice::class;
|
||||
}
|
||||
elseif($request->input('entity') == 'quote'){
|
||||
if($request->input('entity') == 'quote'){
|
||||
$repo = new QuoteRepository();
|
||||
$entity_obj = QuoteFactory::create($company->id, auth()->user()->id);
|
||||
$class = Quote::class;
|
||||
@ -195,7 +190,11 @@ class PreviewController extends BaseController
|
||||
$entity_obj = RecurringInvoiceFactory::create($company->id, auth()->user()->id);
|
||||
$class = RecurringInvoice::class;
|
||||
}
|
||||
|
||||
else { //assume it is either an invoice or a null object
|
||||
$repo = new InvoiceRepository();
|
||||
$entity_obj = InvoiceFactory::create($company->id, auth()->user()->id);
|
||||
$class = Invoice::class;
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
|
@ -189,6 +189,9 @@ class BillingPortalPurchase extends Component
|
||||
$this->coupon = request()->query('coupon');
|
||||
$this->handleCoupon();
|
||||
}
|
||||
elseif(strlen($this->subscription->promo_code) == 0 && $this->subscription->promo_discount > 0){
|
||||
$this->price = $this->subscription->promo_price;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -141,7 +141,7 @@ class RequiredClientInfo extends Component
|
||||
$_field = $this->mappings[$field['name']];
|
||||
|
||||
if (Str::startsWith($field['name'], 'client_')) {
|
||||
if (empty($this->contact->client->{$_field}) || is_null($this->contact->client->{$_field})) {
|
||||
if (empty($this->contact->client->{$_field}) || is_null($this->contact->client->{$_field}) || $this->contact->client->{$_field} = 840) {
|
||||
$this->show_form = true;
|
||||
} else {
|
||||
$this->fields[$index]['filled'] = true;
|
||||
@ -149,7 +149,7 @@ class RequiredClientInfo extends Component
|
||||
}
|
||||
|
||||
if (Str::startsWith($field['name'], 'contact_')) {
|
||||
if ((empty($this->contact->{$_field}) || is_null($this->contact->{$_field}))) {
|
||||
if ((empty($this->contact->{$_field}) || is_null($this->contact->{$_field})) || $this->contact->client->{$_field} = 840) {
|
||||
$this->show_form = true;
|
||||
} else {
|
||||
$this->fields[$index]['filled'] = 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') {
|
||||
|
@ -84,6 +84,8 @@ class WepaySignup extends Component
|
||||
|
||||
public function submit()
|
||||
{
|
||||
MultiDB::setDb($this->company->db);
|
||||
|
||||
$data = $this->validate($this->rules);
|
||||
|
||||
//need to create or get a new WePay CompanyGateway
|
||||
|
@ -34,12 +34,12 @@ class CreateAccountRequest extends Request
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
//'email' => 'required|string|email|max:100',
|
||||
'first_name' => 'string|max:100',
|
||||
'last_name' => 'string:max:100',
|
||||
'password' => 'required|string|min:6',
|
||||
'email' => 'bail|required|email:rfc,dns',
|
||||
'email' => new NewUniqueUserRule(),
|
||||
// 'email' => 'bail|required|email:rfc,dns',
|
||||
// 'email' => new NewUniqueUserRule(),
|
||||
'email' => ['required', 'email:rfc,dns', new NewUniqueUserRule],
|
||||
'privacy_policy' => 'required|boolean',
|
||||
'terms_of_service' => 'required|boolean',
|
||||
];
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
67
app/Http/Requests/Gateways/GoCardless/IbpRequest.php
Normal file
67
app/Http/Requests/Gateways/GoCardless/IbpRequest.php
Normal file
@ -0,0 +1,67 @@
|
||||
<?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\Http\Requests\Gateways\GoCardless;
|
||||
|
||||
use App\Models\Client;
|
||||
use App\Models\Company;
|
||||
use App\Models\CompanyGateway;
|
||||
use App\Models\PaymentHash;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class IbpRequest extends FormRequest
|
||||
{
|
||||
use MakesHash;
|
||||
|
||||
/**
|
||||
* 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 [
|
||||
//
|
||||
];
|
||||
}
|
||||
|
||||
public function getCompany(): ?Company
|
||||
{
|
||||
return Company::where('company_key', $this->company_key)->first();
|
||||
}
|
||||
|
||||
public function getCompanyGateway(): ?CompanyGateway
|
||||
{
|
||||
return CompanyGateway::find($this->decodePrimaryKey($this->company_gateway_id));
|
||||
}
|
||||
|
||||
public function getPaymentHash(): ?PaymentHash
|
||||
{
|
||||
return PaymentHash::where('hash', $this->hash)->firstOrFail();
|
||||
}
|
||||
|
||||
public function getClient(): ?Client
|
||||
{
|
||||
return Client::find($this->getPaymentHash()->data->client_id);
|
||||
}
|
||||
}
|
@ -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)
|
||||
|
86
app/Listeners/Quote/QuoteApprovedNotification.php
Normal file
86
app/Listeners/Quote/QuoteApprovedNotification.php
Normal file
@ -0,0 +1,86 @@
|
||||
<?php
|
||||
/**
|
||||
* Quote Ninja (https://quoteninja.com).
|
||||
*
|
||||
* @link https://github.com/quoteninja/quoteninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Quote Ninja LLC (https://quoteninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Listeners\Quote;
|
||||
|
||||
use App\Jobs\Mail\NinjaMailer;
|
||||
use App\Jobs\Mail\NinjaMailerJob;
|
||||
use App\Jobs\Mail\NinjaMailerObject;
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Mail\Admin\EntityCreatedObject;
|
||||
use App\Mail\Admin\QuoteApprovedObject;
|
||||
use App\Notifications\Admin\EntitySentNotification;
|
||||
use App\Utils\Traits\Notifications\UserNotifies;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
|
||||
class QuoteApprovedNotification implements ShouldQueue
|
||||
{
|
||||
use UserNotifies;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the event.
|
||||
*
|
||||
* @param object $event
|
||||
* @return void
|
||||
*/
|
||||
public function handle($event)
|
||||
{
|
||||
MultiDB::setDb($event->company->db);
|
||||
|
||||
$first_notification_sent = true;
|
||||
|
||||
$quote = $event->quote;
|
||||
|
||||
$nmo = new NinjaMailerObject;
|
||||
$nmo->mailable = new NinjaMailer( (new QuoteApprovedObject($quote, $event->company))->build() );
|
||||
$nmo->company = $quote->company;
|
||||
$nmo->settings = $quote->company->settings;
|
||||
|
||||
/* We loop through each user and determine whether they need to be notified */
|
||||
foreach ($event->company->company_users as $company_user) {
|
||||
|
||||
/* 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');
|
||||
|
||||
/* Returns an array of notification methods */
|
||||
$methods = $this->findUserNotificationTypes($quote->invitations()->first(), $company_user, 'quote', ['all_notifications', 'quote_approved', 'quote_approved_all']);
|
||||
|
||||
/* If one of the methods is email then we fire the EntitySentMailer */
|
||||
if (($key = array_search('mail', $methods)) !== false) {
|
||||
unset($methods[$key]);
|
||||
|
||||
|
||||
$nmo->to_user = $user;
|
||||
|
||||
NinjaMailerJob::dispatch($nmo);
|
||||
|
||||
/* This prevents more than one notification being sent */
|
||||
$first_notification_sent = false;
|
||||
}
|
||||
|
||||
/* Override the methods in the Notification Class */
|
||||
// $notification->method = $methods;
|
||||
|
||||
// Notify on the alternate channels
|
||||
// $user->notify($notification);
|
||||
}
|
||||
}
|
||||
}
|
@ -69,7 +69,7 @@ class ClientPaymentFailureObject
|
||||
/* Set customized translations _NOW_ */
|
||||
$t->replace(Ninja::transformTranslations($this->company->settings));
|
||||
|
||||
$this->invoices = Invoice::whereIn('id', $this->transformKeys(array_column($this->payment_hash->invoices(), 'invoice_id')))->get();
|
||||
$this->invoices = Invoice::withTrashed()->whereIn('id', $this->transformKeys(array_column($this->payment_hash->invoices(), 'invoice_id')))->get();
|
||||
|
||||
$mail_obj = new stdClass;
|
||||
$mail_obj->amount = $this->getAmount();
|
||||
@ -101,8 +101,13 @@ class ClientPaymentFailureObject
|
||||
|
||||
private function getData()
|
||||
{
|
||||
$invitation = $this->invoices->first()->invitations->first();
|
||||
|
||||
if(!$invitation)
|
||||
throw new \Exception('Unable to find invitation for reference');
|
||||
|
||||
$signature = $this->client->getSetting('email_signature');
|
||||
$html_variables = (new HtmlEngine($this->invoices->first()->invitations->first()))->makeValues();
|
||||
$html_variables = (new HtmlEngine($invitation))->makeValues();
|
||||
$signature = str_replace(array_keys($html_variables), array_values($html_variables), $signature);
|
||||
|
||||
$data = [
|
||||
|
103
app/Mail/Admin/QuoteApprovedObject.php
Normal file
103
app/Mail/Admin/QuoteApprovedObject.php
Normal file
@ -0,0 +1,103 @@
|
||||
<?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\Mail\Admin;
|
||||
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\Company;
|
||||
use App\Models\Quote;
|
||||
use App\Utils\Ninja;
|
||||
use App\Utils\Number;
|
||||
use Illuminate\Support\Facades\App;
|
||||
use stdClass;
|
||||
|
||||
class QuoteApprovedObject
|
||||
{
|
||||
|
||||
public $quote;
|
||||
|
||||
public $company;
|
||||
|
||||
public $settings;
|
||||
|
||||
public function __construct(Quote $quote, Company $company)
|
||||
{
|
||||
$this->quote = $quote;
|
||||
$this->company = $company;
|
||||
}
|
||||
|
||||
public function build()
|
||||
{
|
||||
MultiDB::setDb($this->company->db);
|
||||
|
||||
if(!$this->quote)
|
||||
return;
|
||||
|
||||
App::forgetInstance('translator');
|
||||
/* Init a new copy of the translator*/
|
||||
$t = app('translator');
|
||||
/* Set the locale*/
|
||||
App::setLocale($this->company->getLocale());
|
||||
/* Set customized translations _NOW_ */
|
||||
$t->replace(Ninja::transformTranslations($this->company->settings));
|
||||
|
||||
$mail_obj = new stdClass;
|
||||
$mail_obj->amount = $this->getAmount();
|
||||
$mail_obj->subject = $this->getSubject();
|
||||
$mail_obj->data = $this->getData();
|
||||
$mail_obj->markdown = 'email.admin.generic';
|
||||
$mail_obj->tag = $this->company->company_key;
|
||||
|
||||
return $mail_obj;
|
||||
}
|
||||
|
||||
private function getAmount()
|
||||
{
|
||||
return Number::formatMoney($this->quote->amount, $this->quote->client);
|
||||
}
|
||||
|
||||
private function getSubject()
|
||||
{
|
||||
return
|
||||
ctrans(
|
||||
"texts.notification_quote_approved_subject",
|
||||
[
|
||||
'client' => $this->quote->client->present()->name(),
|
||||
'invoice' => $this->quote->number,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
private function getData()
|
||||
{
|
||||
$settings = $this->quote->client->getMergedSettings();
|
||||
|
||||
$data = [
|
||||
'title' => $this->getSubject(),
|
||||
'message' => ctrans(
|
||||
"texts.notification_quote_approved",
|
||||
[
|
||||
'amount' => $this->getAmount(),
|
||||
'client' => $this->quote->client->present()->name(),
|
||||
'invoice' => $this->quote->number,
|
||||
]
|
||||
),
|
||||
'url' => $this->quote->invitations->first()->getAdminLink(),
|
||||
'button' => ctrans("texts.view_quote"),
|
||||
'signature' => $settings->email_signature,
|
||||
'logo' => $this->company->present()->logo(),
|
||||
'settings' => $settings,
|
||||
'whitelabel' => $this->company->account->isPaid() ? true : false,
|
||||
];
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -159,7 +159,8 @@ class Gateway extends StaticModel
|
||||
return [
|
||||
GatewayType::BANK_TRANSFER => ['refund' => true, 'token_billing' => true, 'webhooks' => [' ']], // GoCardless
|
||||
GatewayType::DIRECT_DEBIT => ['refund' => false, 'token_billing' => true, 'webhooks' => [' ']],
|
||||
GatewayType::SEPA => ['refund' => false, 'token_billing' => true, 'webhooks' => [' ']]
|
||||
GatewayType::SEPA => ['refund' => false, 'token_billing' => true, 'webhooks' => [' ']],
|
||||
GatewayType::INSTANT_BANK_PAY => ['refund' => false, 'token_billing' => true, 'webhooks' => [' ']],
|
||||
];
|
||||
break;
|
||||
case 58:
|
||||
|
@ -37,6 +37,7 @@ class GatewayType extends StaticModel
|
||||
const DIRECT_DEBIT = 18;
|
||||
const ACSS = 19;
|
||||
const BECS = 20;
|
||||
const INSTANT_BANK_PAY = 21;
|
||||
|
||||
public function gateway()
|
||||
{
|
||||
@ -89,6 +90,8 @@ class GatewayType extends StaticModel
|
||||
return ctrans('texts.acss');
|
||||
case self::DIRECT_DEBIT:
|
||||
return ctrans('texts.payment_type_direct_debit');
|
||||
case self::INSTANT_BANK_PAY:
|
||||
return ctrans('texts.payment_type_instant_bank_pay');
|
||||
default:
|
||||
return 'Undefined.';
|
||||
break;
|
||||
|
@ -53,6 +53,7 @@ class PaymentType extends StaticModel
|
||||
const DIRECT_DEBIT = 42;
|
||||
const BECS = 43;
|
||||
const ACSS = 44;
|
||||
const INSTANT_BANK_PAY = 45;
|
||||
|
||||
public static function parseCardType($cardName)
|
||||
{
|
||||
|
@ -222,6 +222,9 @@ class RecurringInvoice extends BaseModel
|
||||
return null;
|
||||
}
|
||||
|
||||
nlog("frequency = $this->frequency_id");
|
||||
nlog("frequency = $this->next_send_date");
|
||||
|
||||
$offset = $this->client->timezone_offset();
|
||||
|
||||
/*
|
||||
|
@ -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)) {
|
||||
|
182
app/PaymentDrivers/GoCardless/InstantBankPay.php
Normal file
182
app/PaymentDrivers/GoCardless/InstantBankPay.php
Normal file
@ -0,0 +1,182 @@
|
||||
<?php
|
||||
|
||||
namespace App\PaymentDrivers\GoCardless;
|
||||
|
||||
use App\Exceptions\PaymentFailed;
|
||||
use App\Jobs\Mail\PaymentFailureMailer;
|
||||
use App\Jobs\Util\SystemLogger;
|
||||
use App\Models\GatewayType;
|
||||
use App\Models\Payment;
|
||||
use App\Models\PaymentType;
|
||||
use App\Models\SystemLog;
|
||||
use App\PaymentDrivers\Common\MethodInterface;
|
||||
use App\PaymentDrivers\GoCardlessPaymentDriver;
|
||||
use Illuminate\Contracts\Container\BindingResolutionException;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class InstantBankPay implements MethodInterface
|
||||
{
|
||||
protected GoCardlessPaymentDriver $go_cardless;
|
||||
|
||||
public function __construct(GoCardlessPaymentDriver $go_cardless)
|
||||
{
|
||||
$this->go_cardless = $go_cardless;
|
||||
|
||||
$this->go_cardless->init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Authorization page for Instant Bank Pay.
|
||||
*
|
||||
* @param array $data
|
||||
* @return RedirectResponse
|
||||
* @throws BindingResolutionException
|
||||
*/
|
||||
public function authorizeView(array $data): RedirectResponse
|
||||
{
|
||||
return redirect()->back();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle authorization for Instant Bank Pay.
|
||||
*
|
||||
* @param array $data
|
||||
* @return RedirectResponse
|
||||
* @throws BindingResolutionException
|
||||
*/
|
||||
public function authorizeResponse(Request $request): RedirectResponse
|
||||
{
|
||||
return redirect()->back();
|
||||
}
|
||||
|
||||
public function paymentView(array $data)
|
||||
{
|
||||
try {
|
||||
$billing_request = $this->go_cardless->gateway->billingRequests()->create([
|
||||
'params' => [
|
||||
'payment_request' => [
|
||||
'description' => ctrans('texts.invoices') . ': ' . collect($data['invoices'])->pluck('invoice_number'),
|
||||
'amount' => (string) $data['amount_with_fee'] * 100,
|
||||
'currency' => $this->go_cardless->client->getCurrencyCode(),
|
||||
],
|
||||
]
|
||||
]);
|
||||
|
||||
$billing_request_flow = $this->go_cardless->gateway->billingRequestFlows()->create([
|
||||
'params' => [
|
||||
'redirect_uri' => route('gocardless.ibp_redirect', [
|
||||
'company_key' => $this->go_cardless->company_gateway->company->company_key,
|
||||
'company_gateway_id' => $this->go_cardless->company_gateway->hashed_id,
|
||||
'hash' => $this->go_cardless->payment_hash->hash,
|
||||
]),
|
||||
'links' => [
|
||||
'billing_request' => $billing_request->id,
|
||||
]
|
||||
],
|
||||
]);
|
||||
|
||||
$this->go_cardless->payment_hash
|
||||
->withData('client_id', $this->go_cardless->client->id)
|
||||
->withData('billing_request', $billing_request->id)
|
||||
->withData('billing_request_flow', $billing_request_flow->id);
|
||||
|
||||
return redirect(
|
||||
$billing_request_flow->authorisation_url
|
||||
);
|
||||
} catch (\Exception $exception) {
|
||||
throw $exception;
|
||||
}
|
||||
}
|
||||
|
||||
public function paymentResponse($request)
|
||||
{
|
||||
$this->go_cardless->setPaymentHash(
|
||||
$request->getPaymentHash()
|
||||
);
|
||||
|
||||
try {
|
||||
$billing_request = $this->go_cardless->gateway->billingRequests()->get(
|
||||
$this->go_cardless->payment_hash->data->billing_request
|
||||
);
|
||||
|
||||
$payment = $this->go_cardless->gateway->payments()->get(
|
||||
$billing_request->payment_request->links->payment
|
||||
);
|
||||
|
||||
if ($billing_request->status === 'fulfilled') {
|
||||
return $this->processSuccessfulPayment($payment);
|
||||
}
|
||||
|
||||
return $this->processUnsuccessfulPayment($payment);
|
||||
} catch (\Exception $exception) {
|
||||
throw new PaymentFailed(
|
||||
$exception->getMessage(),
|
||||
$exception->getCode()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle pending payments for Instant Bank Transfer.
|
||||
*
|
||||
* @param ResourcesPayment $payment
|
||||
* @param array $data
|
||||
* @return RedirectResponse
|
||||
*/
|
||||
public function processSuccessfulPayment(\GoCardlessPro\Resources\Payment $payment, array $data = [])
|
||||
{
|
||||
$data = [
|
||||
'payment_method' => $payment->links->mandate,
|
||||
'payment_type' => PaymentType::INSTANT_BANK_PAY,
|
||||
'amount' => $this->go_cardless->payment_hash->data->amount_with_fee,
|
||||
'transaction_reference' => $payment->id,
|
||||
'gateway_type_id' => GatewayType::INSTANT_BANK_PAY,
|
||||
];
|
||||
|
||||
$payment = $this->go_cardless->createPayment($data, Payment::STATUS_COMPLETED);
|
||||
|
||||
SystemLogger::dispatch(
|
||||
['response' => $payment, 'data' => $data],
|
||||
SystemLog::CATEGORY_GATEWAY_RESPONSE,
|
||||
SystemLog::EVENT_GATEWAY_SUCCESS,
|
||||
SystemLog::TYPE_GOCARDLESS,
|
||||
$this->go_cardless->client,
|
||||
$this->go_cardless->client->company,
|
||||
);
|
||||
|
||||
return redirect()->route('client.payments.show', ['payment' => $this->go_cardless->encodePrimaryKey($payment->id)]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process unsuccessful payments for Direct Debit.
|
||||
*
|
||||
* @param ResourcesPayment $payment
|
||||
* @return never
|
||||
*/
|
||||
public function processUnsuccessfulPayment(\GoCardlessPro\Resources\Payment $payment)
|
||||
{
|
||||
PaymentFailureMailer::dispatch($this->go_cardless->client, $payment->status, $this->go_cardless->client->company, $this->go_cardless->payment_hash->data->amount_with_fee);
|
||||
|
||||
PaymentFailureMailer::dispatch(
|
||||
$this->go_cardless->client,
|
||||
$payment,
|
||||
$this->go_cardless->client->company,
|
||||
$payment->amount
|
||||
);
|
||||
|
||||
$message = [
|
||||
'server_response' => $payment,
|
||||
'data' => $this->go_cardless->payment_hash->data,
|
||||
];
|
||||
|
||||
SystemLogger::dispatch(
|
||||
$message,
|
||||
SystemLog::CATEGORY_GATEWAY_RESPONSE,
|
||||
SystemLog::EVENT_GATEWAY_FAILURE,
|
||||
SystemLog::TYPE_GOCARDLESS,
|
||||
$this->go_cardless->client,
|
||||
$this->go_cardless->client->company,
|
||||
);
|
||||
}
|
||||
}
|
@ -40,6 +40,7 @@ class GoCardlessPaymentDriver extends BaseDriver
|
||||
GatewayType::BANK_TRANSFER => \App\PaymentDrivers\GoCardless\ACH::class,
|
||||
GatewayType::DIRECT_DEBIT => \App\PaymentDrivers\GoCardless\DirectDebit::class,
|
||||
GatewayType::SEPA => \App\PaymentDrivers\GoCardless\SEPA::class,
|
||||
GatewayType::INSTANT_BANK_PAY => \App\PaymentDrivers\GoCardless\InstantBankPay::class,
|
||||
];
|
||||
|
||||
const SYSTEM_LOG_TYPE = SystemLog::TYPE_GOCARDLESS;
|
||||
@ -77,6 +78,10 @@ class GoCardlessPaymentDriver extends BaseDriver
|
||||
$types[] = GatewayType::SEPA;
|
||||
}
|
||||
|
||||
if ($this->client->currency()->code === 'GBP') {
|
||||
$types[] = GatewayType::INSTANT_BANK_PAY;
|
||||
}
|
||||
|
||||
return $types;
|
||||
}
|
||||
|
||||
|
@ -86,6 +86,7 @@ class Bancontact implements MethodInterface
|
||||
'webhookUrl' => $this->mollie->company_gateway->webhookUrl(),
|
||||
'metadata' => [
|
||||
'client_id' => $this->mollie->client->hashed_id,
|
||||
'hash' => $this->mollie->payment_hash->hash
|
||||
],
|
||||
]);
|
||||
|
||||
|
@ -89,6 +89,7 @@ class BankTransfer implements MethodInterface
|
||||
'webhookUrl' => $this->mollie->company_gateway->webhookUrl(),
|
||||
'metadata' => [
|
||||
'client_id' => $this->mollie->client->hashed_id,
|
||||
'hash' => $this->mollie->payment_hash->hash
|
||||
],
|
||||
]);
|
||||
|
||||
|
@ -86,6 +86,7 @@ class IDEAL implements MethodInterface
|
||||
'webhookUrl' => $this->mollie->company_gateway->webhookUrl(),
|
||||
'metadata' => [
|
||||
'client_id' => $this->mollie->client->hashed_id,
|
||||
'hash' => $this->mollie->payment_hash->hash
|
||||
],
|
||||
]);
|
||||
|
||||
|
@ -86,6 +86,7 @@ class KBC implements MethodInterface
|
||||
'webhookUrl' => $this->mollie->company_gateway->webhookUrl(),
|
||||
'metadata' => [
|
||||
'client_id' => $this->mollie->client->hashed_id,
|
||||
'hash' => $this->mollie->payment_hash->hash
|
||||
],
|
||||
]);
|
||||
|
||||
|
@ -93,7 +93,7 @@ class PayPalExpressPaymentDriver extends BaseDriver
|
||||
return $response->redirect();
|
||||
}
|
||||
|
||||
$this->sendFailureMail($response->getMessage());
|
||||
$this->sendFailureMail($response->getMessage() ?: '');
|
||||
|
||||
$message = [
|
||||
'server_response' => $response->getMessage(),
|
||||
@ -151,7 +151,7 @@ class PayPalExpressPaymentDriver extends BaseDriver
|
||||
|
||||
$data = $response->getData();
|
||||
|
||||
$this->sendFailureMail($response->getMessage());
|
||||
$this->sendFailureMail($response->getMessage() ?: '');
|
||||
|
||||
$message = [
|
||||
'server_response' => $data['L_LONGMESSAGE0'],
|
||||
|
@ -99,4 +99,9 @@ class RazorpayPaymentDriver extends BaseDriver
|
||||
{
|
||||
return \number_format((float) $amount * 100, 0, '.', '');
|
||||
}
|
||||
|
||||
public function processWebhookRequest(): void
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
|
@ -524,14 +524,25 @@ class StripePaymentDriver extends BaseDriver
|
||||
if ($request->type === 'charge.succeeded' || $request->type === 'payment_intent.succeeded') {
|
||||
|
||||
foreach ($request->data as $transaction) {
|
||||
|
||||
if(array_key_exists('payment_intent', $transaction))
|
||||
{
|
||||
$payment = Payment::query()
|
||||
->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();
|
||||
}
|
||||
else
|
||||
{
|
||||
$payment = Payment::query()
|
||||
->where('company_id', $request->getCompany()->id)
|
||||
->where('transaction_reference', $transaction['id'])
|
||||
->first();
|
||||
}
|
||||
|
||||
if ($payment) {
|
||||
$payment->status_id = Payment::STATUS_COMPLETED;
|
||||
$payment->save();
|
||||
|
@ -208,8 +208,8 @@ class WePayPaymentDriver extends BaseDriver
|
||||
|
||||
return 'Processed successfully';
|
||||
} elseif ($objectType == 'account') {
|
||||
if ($accountId != $objectId) {
|
||||
throw new \Exception('Unknown account');
|
||||
if ($accountId !== $objectId) {
|
||||
throw new \Exception('Unknown account ' . $accountId . ' does not equal '.$objectId);
|
||||
}
|
||||
|
||||
$wepayAccount = $this->wepay->request('account', array(
|
||||
|
@ -171,6 +171,7 @@ use App\Listeners\Payment\PaymentEmailedActivity;
|
||||
use App\Listeners\Payment\PaymentNotification;
|
||||
use App\Listeners\Payment\PaymentRestoredActivity;
|
||||
use App\Listeners\Quote\QuoteApprovedActivity;
|
||||
use App\Listeners\Quote\QuoteApprovedNotification;
|
||||
use App\Listeners\Quote\QuoteApprovedWebhook;
|
||||
use App\Listeners\Quote\QuoteArchivedActivity;
|
||||
use App\Listeners\Quote\QuoteCreatedNotification;
|
||||
@ -437,6 +438,7 @@ class EventServiceProvider extends ServiceProvider
|
||||
ReachWorkflowSettings::class,
|
||||
QuoteApprovedActivity::class,
|
||||
QuoteApprovedWebhook::class,
|
||||
QuoteApprovedNotification::class,
|
||||
],
|
||||
QuoteWasCreated::class => [
|
||||
CreatedQuoteActivity::class,
|
||||
|
@ -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()
|
||||
|
@ -14,6 +14,7 @@ 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
|
||||
@ -150,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();
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -136,7 +136,17 @@ class InvoiceService
|
||||
*/
|
||||
public function updateBalance($balance_adjustment, bool $is_draft = false)
|
||||
{
|
||||
$this->invoice = (new UpdateBalance($this->invoice, $balance_adjustment, $is_draft))->run();
|
||||
// $this->invoice = (new UpdateBalance($this->invoice, $balance_adjustment, $is_draft))->run();
|
||||
|
||||
if ($this->invoice->is_deleted) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
$this->invoice->balance += $balance_adjustment;
|
||||
|
||||
if ($this->invoice->balance == 0 && !$is_draft) {
|
||||
$this->invoice->status_id = Invoice::STATUS_PAID;
|
||||
}
|
||||
|
||||
if ((int)$this->invoice->balance == 0) {
|
||||
$this->invoice->next_send_date = null;
|
||||
|
@ -82,6 +82,11 @@ class UpdateInvoicePayment
|
||||
->updateBalance($paid_amount * -1)
|
||||
->updatePaidToDate($paid_amount)
|
||||
->updateStatus()
|
||||
->save();
|
||||
|
||||
$invoice->refresh();
|
||||
|
||||
$invoice->service()
|
||||
->deletePdf()
|
||||
->workFlow()
|
||||
->save();
|
||||
|
@ -75,9 +75,9 @@ class SubscriptionService
|
||||
$recurring_invoice = $this->convertInvoiceToRecurring($payment_hash->payment->client_id);
|
||||
$recurring_invoice_repo = new RecurringInvoiceRepository();
|
||||
|
||||
$recurring_invoice->next_send_date = now();
|
||||
$recurring_invoice = $recurring_invoice_repo->save([], $recurring_invoice);
|
||||
$recurring_invoice->next_send_date = $recurring_invoice->nextSendDate();
|
||||
// $recurring_invoice->next_send_date = now()->format('Y-m-d');
|
||||
// $recurring_invoice->next_send_date = $recurring_invoice->nextSendDate();
|
||||
$recurring_invoice->auto_bill = $this->subscription->auto_bill;
|
||||
|
||||
/* Start the recurring service */
|
||||
@ -161,6 +161,11 @@ class SubscriptionService
|
||||
$recurring_invoice->discount = $this->subscription->promo_discount;
|
||||
$recurring_invoice->is_amount_discount = $this->subscription->is_amount_discount;
|
||||
}
|
||||
elseif(strlen($this->subscription->promo_code) == 0 && $this->subscription->promo_discount > 0) {
|
||||
$recurring_invoice->discount = $this->subscription->promo_discount;
|
||||
$recurring_invoice->is_amount_discount = $this->subscription->is_amount_discount;
|
||||
}
|
||||
|
||||
|
||||
$recurring_invoice = $recurring_invoice_repo->save($data, $recurring_invoice);
|
||||
|
||||
@ -594,7 +599,7 @@ class SubscriptionService
|
||||
|
||||
$recurring_invoice = $this->convertInvoiceToRecurring($old_recurring_invoice->client_id);
|
||||
$recurring_invoice = $recurring_invoice_repo->save([], $recurring_invoice);
|
||||
$recurring_invoice->next_send_date = now();
|
||||
$recurring_invoice->next_send_date = now()->format('Y-m-d');
|
||||
$recurring_invoice->next_send_date = $recurring_invoice->nextSendDate();
|
||||
|
||||
/* Start the recurring service */
|
||||
@ -693,6 +698,11 @@ class SubscriptionService
|
||||
$invoice->discount = $this->subscription->promo_discount;
|
||||
$invoice->is_amount_discount = $this->subscription->is_amount_discount;
|
||||
}
|
||||
elseif(strlen($this->subscription->promo_code) == 0 && $this->subscription->promo_discount > 0) {
|
||||
$invoice->discount = $this->subscription->promo_discount;
|
||||
$invoice->is_amount_discount = $this->subscription->is_amount_discount;
|
||||
}
|
||||
|
||||
|
||||
return $invoice_repo->save($data, $invoice);
|
||||
|
||||
@ -722,6 +732,8 @@ class SubscriptionService
|
||||
$recurring_invoice->auto_bill = $client->getSetting('auto_bill');
|
||||
$recurring_invoice->auto_bill_enabled = $this->setAutoBillFlag($recurring_invoice->auto_bill);
|
||||
$recurring_invoice->due_date_days = 'terms';
|
||||
$recurring_invoice->next_send_date = now()->format('Y-m-d');
|
||||
$recurring_invoice->next_send_date = $recurring_invoice->nextSendDate();
|
||||
|
||||
return $recurring_invoice;
|
||||
}
|
||||
@ -1027,7 +1039,7 @@ class SubscriptionService
|
||||
'subscription' => $this->subscription->hashed_id,
|
||||
'recurring_invoice' => $recurring_invoice_hashed_id,
|
||||
'client' => $invoice->client->hashed_id,
|
||||
'contact' => $invoice->client->primary_contact()->first() ? $invoice->client->primary_contact()->first(): $invoice->client->contacts->first(),
|
||||
'contact' => $invoice->client->primary_contact()->first() ? $invoice->client->primary_contact()->first()->hashed_id: $invoice->client->contacts->first()->hashed_id,
|
||||
'invoice' => $invoice->hashed_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,
|
||||
|
@ -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.31',
|
||||
'app_tag' => '5.3.31',
|
||||
'app_version' => '5.3.32',
|
||||
'app_tag' => '5.3.32',
|
||||
'minimum_client_version' => '5.0.16',
|
||||
'terms_version' => '1.0.1',
|
||||
'api_secret' => env('API_SECRET', ''),
|
||||
|
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
use App\Models\GatewayType;
|
||||
use App\Models\PaymentType;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class AddInstantBankTransfer extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
$type = new PaymentType();
|
||||
|
||||
$type->id = PaymentType::INSTANT_BANK_PAY;
|
||||
$type->name = 'Instant Bank Pay';
|
||||
$type->gateway_type_id = GatewayType::INSTANT_BANK_PAY;
|
||||
|
||||
$type->save();
|
||||
}
|
||||
}
|
4
public/flutter_service_worker.js
vendored
4
public/flutter_service_worker.js
vendored
@ -6,7 +6,7 @@ const RESOURCES = {
|
||||
"icons/Icon-512.png": "0f9aff01367f0a0c69773d25ca16ef35",
|
||||
"icons/Icon-192.png": "bb1cf5f6982006952211c7c8404ffbed",
|
||||
"favicon.png": "dca91c54388f52eded692718d5a98b8b",
|
||||
"/": "1c5f475f85b7fcd619029ee7f07d9d02",
|
||||
"/": "6310be19342ca9ed6920572bcc23151f",
|
||||
"manifest.json": "ef43d90e57aa7682d7e2cfba2f484a40",
|
||||
"favicon.ico": "51636d3a390451561744c42188ccd628",
|
||||
"version.json": "9c7b0edc83733da56c726678aacd9fd3",
|
||||
@ -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": "97d45d9acc730c1517f80cecf1a90511"
|
||||
"main.dart.js": "d57b5bf95106fcc642bac2ab5bc52fc3"
|
||||
};
|
||||
|
||||
// The application shell files that are downloaded before a service worker can
|
||||
|
2
public/js/app.js
vendored
2
public/js/app.js
vendored
File diff suppressed because one or more lines are too long
2
public/js/clients/linkify-urls.js
vendored
2
public/js/clients/linkify-urls.js
vendored
@ -1 +1 @@
|
||||
(()=>{var e,t={2623:(e,t,r)=>{"use strict";e.exports=r(4666)},1886:(e,t)=>{"use strict";const r=e=>e.replace(/&/g,"&").replace(/"/g,""").replace(/'/g,"'").replace(/</g,"<").replace(/>/g,">"),n=e=>e.replace(/>/g,">").replace(/</g,"<").replace(/�?39;/g,"'").replace(/"/g,'"').replace(/&/g,"&");t.T=(e,...t)=>{if("string"==typeof e)return r(e);let n=e[0];for(const[o,a]of t.entries())n=n+r(String(a))+e[o+1];return n}},7636:(e,t,r)=>{"use strict";r.r(t),r.d(t,{default:()=>s});var n=r(1886);var o=r(2623);const a=e=>e.replace(/&/g,"&").replace(/"/g,""").replace(/'/g,"'").replace(/</g,"<").replace(/>/g,">");const i=new Set(o);function c({name:e="div",attributes:t={},html:r="",text:o}={}){if(r&&o)throw new Error("The `html` and `text` options are mutually exclusive");const c=o?function(e,...t){if("string"==typeof e)return a(e);let r=e[0];for(const[n,o]of t.entries())r=r+a(String(o))+e[n+1];return r}(o):r;let l=`<${e}${function(e){const t=[];for(let[r,o]of Object.entries(e)){if(!1===o)continue;Array.isArray(o)&&(o=o.join(" "));let e=(0,n.T)(r);!0!==o&&(e+=`="${(0,n.T)(String(o))}"`),t.push(e)}return t.length>0?" "+t.join(" "):""}(t)}>`;return i.has(e)||(l+=`${c}</${e}>`),l}const l=(e,t)=>c({name:"a",attributes:{href:"",...t.attributes,href:e},text:void 0===t.value?e:void 0,html:void 0===t.value?void 0:"function"==typeof t.value?t.value(e):t.value});function s(e,t){if("string"===(t={attributes:{},type:"string",...t}).type)return((e,t)=>e.replace(/((?<!\+)https?:\/\/(?:www\.)?(?:[-\w.]+?[.@][a-zA-Z\d]{2,}|localhost)(?:[-\w.:%+~#*$!?&/=@]*?(?:,(?!\s))*?)*)/g,(e=>l(e,t))))(e,t);if("dom"===t.type)return((e,t)=>{const r=document.createDocumentFragment();for(const[o,a]of Object.entries(e.split(/((?<!\+)https?:\/\/(?:www\.)?(?:[-\w.]+?[.@][a-zA-Z\d]{2,}|localhost)(?:[-\w.:%+~#*$!?&/=@]*?(?:,(?!\s))*?)*)/g)))o%2?r.append((n=l(a,t),document.createRange().createContextualFragment(n))):a.length>0&&r.append(a);var n;return r})(e,t);throw new TypeError("The type option must be either `dom` or `string`")}},4666:e=>{"use strict";e.exports=JSON.parse('["area","base","br","col","embed","hr","img","input","link","menuitem","meta","param","source","track","wbr"]')}},r={};function n(e){var o=r[e];if(void 0!==o)return o.exports;var a=r[e]={exports:{}};return t[e](a,a.exports,n),a.exports}n.d=(e,t)=>{for(var r in t)n.o(t,r)&&!n.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})},n.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),n.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},e=n(7636),document.querySelectorAll("[data-ref=entity-terms]").forEach((function(t){t.innerHTML=e(t.innerText,{attributes:{target:"_blank",class:"text-primary"}})}))})();
|
||||
(()=>{var e,t={8945:(e,t,r)=>{"use strict";const a=r(920),n=r(3523),s=r(2263),o=new Set(n);e.exports=e=>{if((e=Object.assign({name:"div",attributes:{},html:""},e)).html&&e.text)throw new Error("The `html` and `text` options are mutually exclusive");const t=e.text?s.escape(e.text):e.html;let r=`<${e.name}${a(e.attributes)}>`;return o.has(e.name)||(r+=`${t}</${e.name}>`),r}},3523:(e,t,r)=>{"use strict";e.exports=r(8346)},2263:(e,t)=>{"use strict";t.escape=e=>e.replace(/&/g,"&").replace(/"/g,""").replace(/'/g,"'").replace(/</g,"<").replace(/>/g,">"),t.unescape=e=>e.replace(/>/g,">").replace(/</g,"<").replace(/'/g,"'").replace(/"/g,'"').replace(/&/g,"&"),t.escapeTag=function(e){let r=e[0];for(let a=1;a<arguments.length;a++)r=r+t.escape(arguments[a])+e[a];return r},t.unescapeTag=function(e){let r=e[0];for(let a=1;a<arguments.length;a++)r=r+t.unescape(arguments[a])+e[a];return r}},1881:(e,t,r)=>{"use strict";const a=r(8945),n=(e,t)=>a({name:"a",attributes:{href:"",...t.attributes,href:e},text:void 0===t.value?e:void 0,html:void 0===t.value?void 0:"function"==typeof t.value?t.value(e):t.value});e.exports=(e,t)=>{if("string"===(t={attributes:{},type:"string",...t}).type)return((e,t)=>e.replace(/((?<!\+)(?:https?(?::\/\/))(?:www\.)?(?:[a-zA-Z\d-_.]+(?:(?:\.|@)[a-zA-Z\d]{2,})|localhost)(?:(?:[-a-zA-Z\d:%_+.~#*$!?&//=@]*)(?:[,](?![\s]))*)*)/g,(e=>n(e,t))))(e,t);if("dom"===t.type)return((e,t)=>{const r=document.createDocumentFragment();for(const[s,o]of Object.entries(e.split(/((?<!\+)(?:https?(?::\/\/))(?:www\.)?(?:[a-zA-Z\d-_.]+(?:(?:\.|@)[a-zA-Z\d]{2,})|localhost)(?:(?:[-a-zA-Z\d:%_+.~#*$!?&//=@]*)(?:[,](?![\s]))*)*)/g)))s%2?r.append((a=n(o,t),document.createRange().createContextualFragment(a))):o.length>0&&r.append(o);var a;return r})(e,t);throw new Error("The type option must be either `dom` or `string`")}},920:(e,t,r)=>{"use strict";const a=r(2263);e.exports=e=>{const t=[];for(const r of Object.keys(e)){let n=e[r];if(!1===n)continue;Array.isArray(n)&&(n=n.join(" "));let s=a.escape(r);!0!==n&&(s+=`="${a.escape(String(n))}"`),t.push(s)}return t.length>0?" "+t.join(" "):""}},8346:e=>{"use strict";e.exports=JSON.parse('["area","base","br","col","embed","hr","img","input","link","menuitem","meta","param","source","track","wbr"]')}},r={};function a(e){var n=r[e];if(void 0!==n)return n.exports;var s=r[e]={exports:{}};return t[e](s,s.exports,a),s.exports}e=a(1881),document.querySelectorAll("[data-ref=entity-terms]").forEach((function(t){t.innerHTML=e(t.innerText,{attributes:{target:"_blank",class:"text-primary"}})}))})();
|
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()}})();
|
2
public/js/setup/setup.js
vendored
2
public/js/setup/setup.js
vendored
File diff suppressed because one or more lines are too long
70865
public/main.dart.js
vendored
70865
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
70459
public/main.foss.dart.js
vendored
70459
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
68185
public/main.html.dart.js
vendored
68185
public/main.html.dart.js
vendored
File diff suppressed because one or more lines are too long
133
public/main.profile.dart.js
vendored
133
public/main.profile.dart.js
vendored
@ -360012,10 +360012,12 @@
|
||||
t5 = "";
|
||||
C.JSArray_methods.addAll$1(t4, H.setRuntimeTypeInfo([T.Expanded$(new D.AppButton(_null, C.IconData_57648_MaterialIcons_null_false, t5, new V.DocumentGrid_build_closure(this), _null, _null), 1), new T.SizedBox(14, _null, _null, _null)], t2));
|
||||
}
|
||||
if (!D.isMacOS() && !D.isLinux()) {
|
||||
t1 = J.$index$asx($.LocalizationsProvider__localizedValues.$index(0, t1.localeCode), "upload_file");
|
||||
if (t1 == null)
|
||||
t1 = "";
|
||||
t4.push(T.Expanded$(new D.AppButton(_null, C.IconData_58178_MaterialIcons_null_false, t1, new V.DocumentGrid_build_closure0(this), _null, _null), 1));
|
||||
}
|
||||
t3.push(new T.Padding(C.EdgeInsets_16_0_16_16, T.Row$(t4, C.CrossAxisAlignment_2, C.MainAxisAlignment_0, C.MainAxisSize_1, _null), _null));
|
||||
} else
|
||||
t3.push(new T.Padding(new V.EdgeInsets(0, 30, 0, 30), T.Center$(L.Text$(t1.get$requiresAnEnterprisePlan(), _null, _null, _null, _null, _null, A.TextStyle$(_null, _null, C.MaterialColor_Map_HFpTk_4288585374, _null, _null, _null, _null, _null, _null, _null, _null, 18, _null, _null, _null, _null, true, _null, _null, _null, _null, _null, _null, _null), _null, _null, _null), _null, _null), _null));
|
||||
@ -369199,7 +369201,7 @@
|
||||
};
|
||||
Y._LoginState_build_closure16.prototype = {
|
||||
call$0: function() {
|
||||
T.launch("https://status.invoiceninja.com", null, false);
|
||||
T.launch("https://status.invoiceninja.com/", null, false);
|
||||
},
|
||||
$signature: 1
|
||||
};
|
||||
@ -384888,7 +384890,7 @@
|
||||
$.WidgetsBinding__instance.SchedulerBinding__postFrameCallbacks.push(new E._InvoiceEditItemsDesktopState__onFocusChange_closure());
|
||||
},
|
||||
build$1: function(_, context) {
|
||||
var t4, company, invoice, t5, t6, client, t7, precision, lineItems, t8, includedLineItems, productState, productIds, hasTax1, hasTax2, hasTax3, customField1, customField2, customField3, customField4, lastIndex, tableFontColor, tableHeaderColor, t9, t10, t11, index, t12, t13, t14, t15, _this = this, _null = null,
|
||||
var t4, company, invoice, t5, t6, client, t7, precision, lineItems, t8, includedLineItems, productState, productIds, hasTax1, hasTax2, hasTax3, customField1, customField2, customField3, customField4, tableFontColor, tableHeaderColor, lastIndex, t9, t10, t11, index, t12, t13, t14, t15, _this = this, _null = null,
|
||||
t1 = L.Localizations_of(context, C.Type_AppLocalization_KyD, type$.legacy_AppLocalization),
|
||||
viewModel = _this._widget.viewModel,
|
||||
state = viewModel.state,
|
||||
@ -384923,9 +384925,48 @@
|
||||
customField2 = t2 ? "task2" : "product2";
|
||||
customField3 = t2 ? "task3" : "product3";
|
||||
customField4 = t2 ? "task4" : "product4";
|
||||
t2 = state.prefState.customColors._map$_map;
|
||||
t3 = J.getInterceptor$asx(t2);
|
||||
tableFontColor = t3.$index(t2, "invoice_header_font_color");
|
||||
if (tableFontColor == null)
|
||||
tableFontColor = "";
|
||||
tableHeaderColor = t3.$index(t2, "invoice_header_background_color");
|
||||
if (tableHeaderColor == null)
|
||||
tableHeaderColor = "";
|
||||
if (_this._isReordering) {
|
||||
t1 = type$.JSArray_legacy_Widget;
|
||||
return Y.FormCard$(_null, H.setRuntimeTypeInfo([T.Row$(H.setRuntimeTypeInfo([B.IconButton$(C.Alignment_0_0, _null, _null, true, L.Icon$(C.IconData_57706_MaterialIcons_null_false, _null, _null), 24, new E._InvoiceEditItemsDesktopState_build_closure3(_this), C.EdgeInsets_8_8_8_8, _null, _null)], t1), C.CrossAxisAlignment_2, C.MainAxisAlignment_1, C.MainAxisSize_1, _null), new Z.ReorderableListView(new E._InvoiceEditItemsDesktopState_build_closure4(_this, lineItems, company, customField1, customField2, customField3, customField4, hasTax1, hasTax2, hasTax3, invoice, precision), J.get$length$asx(t7), new E._InvoiceEditItemsDesktopState_build_closure5(lineItems, viewModel), false, _null, false, true, _null)], t1), _null, 4, false, _null, false, C.EdgeInsets_12_0_12_0);
|
||||
t2 = tableHeaderColor.length !== 0 ? new S.BoxDecoration(E.convertHexStringToColor(tableHeaderColor), _null, _null, _null, _null, _null, C.BoxShape_0) : new S.BoxDecoration(_null, _null, _null, _null, _null, _null, C.BoxShape_0);
|
||||
t3 = type$.JSArray_legacy_Widget;
|
||||
t4 = H.setRuntimeTypeInfo([T.Expanded$(new E.TableHeader(t1.get$item(t1), false, true, _null), 1), T.Expanded$(new E.TableHeader(t1.get$description(t1), false, false, _null), 2)], t3);
|
||||
if (company.getCustomFieldLabel$1(customField1).length !== 0)
|
||||
t4.push(T.Expanded$(new E.TableHeader(company.getCustomFieldLabel$1(customField1), false, false, _null), 1));
|
||||
if (company.getCustomFieldLabel$1(customField2).length !== 0)
|
||||
t4.push(T.Expanded$(new E.TableHeader(company.getCustomFieldLabel$1(customField2), false, false, _null), 1));
|
||||
if (company.getCustomFieldLabel$1(customField3).length !== 0)
|
||||
t4.push(T.Expanded$(new E.TableHeader(company.getCustomFieldLabel$1(customField3), false, false, _null), 1));
|
||||
if (company.getCustomFieldLabel$1(customField4).length !== 0)
|
||||
t4.push(T.Expanded$(new E.TableHeader(company.getCustomFieldLabel$1(customField4), false, false, _null), 1));
|
||||
if (hasTax1) {
|
||||
t5 = t1.get$tax();
|
||||
t4.push(T.Expanded$(new E.TableHeader(t5 + (company.settings.enableInclusiveTaxes ? " - " + t1.get$inclusive() : ""), false, false, _null), 1));
|
||||
}
|
||||
if (hasTax2) {
|
||||
t5 = t1.get$tax();
|
||||
t4.push(T.Expanded$(new E.TableHeader(t5 + (company.settings.enableInclusiveTaxes ? " - " + t1.get$inclusive() : ""), false, false, _null), 1));
|
||||
}
|
||||
if (hasTax3) {
|
||||
t5 = t1.get$tax();
|
||||
t4.push(T.Expanded$(new E.TableHeader(t5 + (company.settings.enableInclusiveTaxes ? " - " + t1.get$inclusive() : ""), false, false, _null), 1));
|
||||
}
|
||||
t4.push(T.Expanded$(new E.TableHeader(_this._widget.isTasks ? t1.get$rate(t1) : t1.get$unitCost(), true, false, _null), 1));
|
||||
if (company.enableProductQuantity || _this._widget.isTasks)
|
||||
t4.push(T.Expanded$(new E.TableHeader(_this._widget.isTasks ? t1.get$hours() : t1.get$quantity(), true, false, _null), 1));
|
||||
if (company.enableProductDiscount)
|
||||
t4.push(T.Expanded$(new E.TableHeader(t1.get$discount(), true, false, _null), 1));
|
||||
t4.push(T.Expanded$(new E.TableHeader(t1.get$lineTotal(), true, false, _null), 1));
|
||||
t4.push(new T.SizedBox(16, _null, _null, _null));
|
||||
t1 = tableFontColor.length !== 0 ? E.convertHexStringToColor(tableFontColor) : _null;
|
||||
t4.push(B.IconButton$(C.Alignment_0_0, t1, _null, true, L.Icon$(C.IconData_57706_MaterialIcons_null_false, _null, _null), 24, new E._InvoiceEditItemsDesktopState_build_closure3(_this), C.EdgeInsets_8_8_8_8, _null, _null));
|
||||
return Y.FormCard$(_null, H.setRuntimeTypeInfo([M.DecoratedBox$(T.Row$(t4, C.CrossAxisAlignment_2, C.MainAxisAlignment_0, C.MainAxisSize_1, _null), t2, C.DecorationPosition_0), new Z.ReorderableListView(new E._InvoiceEditItemsDesktopState_build_closure4(_this, lineItems, company, customField1, customField2, customField3, customField4, hasTax1, hasTax2, hasTax3, invoice, precision), J.get$length$asx(t7), new E._InvoiceEditItemsDesktopState_build_closure5(lineItems, viewModel), false, _null, false, true, _null)], t3), _null, 4, false, _null, false, C.EdgeInsets_12_0_12_0);
|
||||
}
|
||||
t2 = t5.where$1(t7, new E._InvoiceEditItemsDesktopState_build_closure6());
|
||||
if (!t2.get$iterator(t2).moveNext$0()) {
|
||||
@ -384952,61 +384993,41 @@
|
||||
++lastIndex;
|
||||
if (company.getCustomFieldLabel$1(customField4).length !== 0)
|
||||
++lastIndex;
|
||||
t4 = state.prefState.customColors._map$_map;
|
||||
t5 = J.getInterceptor$asx(t4);
|
||||
tableFontColor = t5.$index(t4, "invoice_header_font_color");
|
||||
if (tableFontColor == null)
|
||||
tableFontColor = "";
|
||||
tableHeaderColor = t5.$index(t4, "invoice_header_background_color");
|
||||
if (tableHeaderColor == null)
|
||||
tableHeaderColor = "";
|
||||
t4 = t1.localeCode;
|
||||
t5 = J.$index$asx($.LocalizationsProvider__localizedValues.$index(0, t4), "item");
|
||||
if (t5 == null)
|
||||
t5 = "";
|
||||
t5 = H.setRuntimeTypeInfo([new E.TableHeader(t5, false, true, _null), new E.TableHeader(t1.get$description(t1), false, false, _null)], type$.JSArray_legacy_StatelessWidget);
|
||||
t4 = H.setRuntimeTypeInfo([new E.TableHeader(t1.get$item(t1), false, true, _null), new E.TableHeader(t1.get$description(t1), false, false, _null)], type$.JSArray_legacy_StatelessWidget);
|
||||
if (company.getCustomFieldLabel$1(customField1).length !== 0)
|
||||
t5.push(new E.TableHeader(company.getCustomFieldLabel$1(customField1), false, false, _null));
|
||||
t4.push(new E.TableHeader(company.getCustomFieldLabel$1(customField1), false, false, _null));
|
||||
if (company.getCustomFieldLabel$1(customField2).length !== 0)
|
||||
t5.push(new E.TableHeader(company.getCustomFieldLabel$1(customField2), false, false, _null));
|
||||
t4.push(new E.TableHeader(company.getCustomFieldLabel$1(customField2), false, false, _null));
|
||||
if (company.getCustomFieldLabel$1(customField3).length !== 0)
|
||||
t5.push(new E.TableHeader(company.getCustomFieldLabel$1(customField3), false, false, _null));
|
||||
t4.push(new E.TableHeader(company.getCustomFieldLabel$1(customField3), false, false, _null));
|
||||
if (company.getCustomFieldLabel$1(customField4).length !== 0)
|
||||
t5.push(new E.TableHeader(company.getCustomFieldLabel$1(customField4), false, false, _null));
|
||||
t4.push(new E.TableHeader(company.getCustomFieldLabel$1(customField4), false, false, _null));
|
||||
if (hasTax1) {
|
||||
t7 = t1.get$tax();
|
||||
t5.push(new E.TableHeader(t7 + (company.settings.enableInclusiveTaxes ? " - " + t1.get$inclusive() : ""), false, false, _null));
|
||||
t5 = t1.get$tax();
|
||||
t4.push(new E.TableHeader(t5 + (company.settings.enableInclusiveTaxes ? " - " + t1.get$inclusive() : ""), false, false, _null));
|
||||
}
|
||||
if (hasTax2) {
|
||||
t7 = t1.get$tax();
|
||||
t5.push(new E.TableHeader(t7 + (company.settings.enableInclusiveTaxes ? " - " + t1.get$inclusive() : ""), false, false, _null));
|
||||
t5 = t1.get$tax();
|
||||
t4.push(new E.TableHeader(t5 + (company.settings.enableInclusiveTaxes ? " - " + t1.get$inclusive() : ""), false, false, _null));
|
||||
}
|
||||
if (hasTax3) {
|
||||
t7 = t1.get$tax();
|
||||
t5.push(new E.TableHeader(t7 + (company.settings.enableInclusiveTaxes ? " - " + t1.get$inclusive() : ""), false, false, _null));
|
||||
}
|
||||
t5.push(new E.TableHeader(_this._widget.isTasks ? t1.get$rate(t1) : t1.get$unitCost(), true, false, _null));
|
||||
if (!t2 || _this._widget.isTasks) {
|
||||
if (_this._widget.isTasks) {
|
||||
t7 = J.$index$asx($.LocalizationsProvider__localizedValues.$index(0, t4), "hours");
|
||||
if (t7 == null)
|
||||
t7 = "";
|
||||
} else
|
||||
t7 = t1.get$quantity();
|
||||
t5.push(new E.TableHeader(t7, true, false, _null));
|
||||
t5 = t1.get$tax();
|
||||
t4.push(new E.TableHeader(t5 + (company.settings.enableInclusiveTaxes ? " - " + t1.get$inclusive() : ""), false, false, _null));
|
||||
}
|
||||
t4.push(new E.TableHeader(_this._widget.isTasks ? t1.get$rate(t1) : t1.get$unitCost(), true, false, _null));
|
||||
if (!t2 || _this._widget.isTasks)
|
||||
t4.push(new E.TableHeader(_this._widget.isTasks ? t1.get$hours() : t1.get$quantity(), true, false, _null));
|
||||
if (t3)
|
||||
t5.push(new E.TableHeader(t1.get$discount(), true, false, _null));
|
||||
t4 = J.$index$asx($.LocalizationsProvider__localizedValues.$index(0, t4), "line_total");
|
||||
t5.push(new E.TableHeader(t4 == null ? "" : t4, true, false, _null));
|
||||
t4 = L.Icon$(_this._isReordering ? C.IconData_57706_MaterialIcons_null_false : C.IconData_58919_MaterialIcons_null_false, _null, _null);
|
||||
t4.push(new E.TableHeader(t1.get$discount(), true, false, _null));
|
||||
t4.push(new E.TableHeader(t1.get$lineTotal(), true, false, _null));
|
||||
t5 = L.Icon$(C.IconData_58919_MaterialIcons_null_false, _null, _null);
|
||||
t7 = tableFontColor.length !== 0 ? E.convertHexStringToColor(tableFontColor) : _null;
|
||||
t8 = new H.WhereIterable(includedLineItems, new E._InvoiceEditItemsDesktopState_build_closure7(), H._arrayInstanceType(includedLineItems)._eval$1("WhereIterable<1>"));
|
||||
t5.push(B.IconButton$(C.Alignment_0_0, t7, _null, true, t4, 24, t8.get$length(t8) < 2 ? _null : new E._InvoiceEditItemsDesktopState_build_closure8(_this), C.EdgeInsets_8_8_8_8, _null, _null));
|
||||
t4 = P.LinkedHashMap_LinkedHashMap$_literal([0, new S.FlexColumnWidth(1.3), 1, new S.FlexColumnWidth(2.2), lastIndex, new S.FixedColumnWidth(40)], type$.legacy_int, type$.legacy_TableColumnWidth);
|
||||
t4.push(B.IconButton$(C.Alignment_0_0, t7, _null, true, t5, 24, t8.get$length(t8) < 2 ? _null : new E._InvoiceEditItemsDesktopState_build_closure8(_this), C.EdgeInsets_8_8_8_8, _null, _null));
|
||||
t5 = P.LinkedHashMap_LinkedHashMap$_literal([0, new S.FlexColumnWidth(1.3), 1, new S.FlexColumnWidth(2.2), lastIndex, new S.FixedColumnWidth(40)], type$.legacy_int, type$.legacy_TableColumnWidth);
|
||||
t7 = "__datatable_" + H.S(_this._invoice_edit_items_desktop$_updatedAt) + "__";
|
||||
t9 = type$.ValueKey_legacy_String;
|
||||
t5 = H.setRuntimeTypeInfo([new S.TableRow(_null, tableHeaderColor.length !== 0 ? new S.BoxDecoration(E.convertHexStringToColor(tableHeaderColor), _null, _null, _null, _null, _null, C.BoxShape_0) : _null, t5)], type$.JSArray_legacy_TableRow);
|
||||
t4 = H.setRuntimeTypeInfo([new S.TableRow(_null, tableHeaderColor.length !== 0 ? new S.BoxDecoration(E.convertHexStringToColor(tableHeaderColor), _null, _null, _null, _null, _null, C.BoxShape_0) : new S.BoxDecoration(_null, _null, _null, _null, _null, _null, C.BoxShape_0), t4)], type$.JSArray_legacy_TableRow);
|
||||
for (t8 = type$.PopupMenuButton_legacy_String, t10 = type$.legacy_String, t11 = type$.JSArray_legacy_Widget, index = 0; index < J.get$length$asx(lineItems._copy_on_write_list$_list); ++index) {
|
||||
if (!(J.$index$asx(lineItems._copy_on_write_list$_list, index).typeId === "2" && _this._widget.isTasks))
|
||||
t12 = J.$index$asx(lineItems._copy_on_write_list$_list, index).typeId !== "2" && !_this._widget.isTasks || J.get$isEmpty$asx(J.$index$asx(lineItems._copy_on_write_list$_list, index));
|
||||
@ -385054,10 +385075,10 @@
|
||||
t13 = "__total_" + index + "_" + H.S(J.total$2$x(J.$index$asx(lineItems._copy_on_write_list$_list, index), invoice, precision)) + "_" + t6 + "__";
|
||||
t14.push(new T.Padding(C.EdgeInsets_0_0_16_0, E.TextFormField$(true, _null, false, _null, _null, C.InputDecoration_so3, false, false, _null, Y.formatNumber(J.total$2$x(J.$index$asx(lineItems._copy_on_write_list$_list, index), invoice, precision), context, t6, _null, C.FormatNumberType_0, true, _null, false), _null, new D.ValueKey(t13, t9), _null, 1, _null, false, _null, _null, _null, _null, true, _null, C.TextAlign_1, _null, _null), _null));
|
||||
t14.push(new Z.PopupMenuButton(new E._InvoiceEditItemsDesktopState_build_closure37(includedLineItems, lineItems, index, t1), _null, new E._InvoiceEditItemsDesktopState_build_closure38(_this, t1, viewModel, index, lineItems), _null, C.EdgeInsets_8_8_8_8, _null, new L.Icon(C.IconData_58372_MaterialIcons_null_false, _null, _null, _null), !J.get$isEmpty$asx(J.$index$asx(lineItems._copy_on_write_list$_list, index)), _null, _null, t8));
|
||||
t5.push(new S.TableRow(new D.ValueKey(t12, t9), _null, t14));
|
||||
t4.push(new S.TableRow(new D.ValueKey(t12, t9), _null, t14));
|
||||
}
|
||||
}
|
||||
return Y.FormCard$(S.Table$(t5, t4, C.FlexColumnWidth_1, C.TableCellVerticalAlignment_2, new D.ValueKey(t7, t9)), _null, _null, 4, false, _null, false, C.EdgeInsets_12_0_12_0);
|
||||
return Y.FormCard$(S.Table$(t4, t5, C.FlexColumnWidth_1, C.TableCellVerticalAlignment_2, new D.ValueKey(t7, t9)), _null, _null, 4, false, _null, false, C.EdgeInsets_12_0_12_0);
|
||||
}
|
||||
};
|
||||
E._InvoiceEditItemsDesktopState__updateTable_closure.prototype = {
|
||||
@ -396923,8 +396944,12 @@
|
||||
t13.push(new T.SizedBox(28, 28, U.CircularProgressIndicator$(_null, _null, _null, _null, _null, 4, _null, _null), _null));
|
||||
t13 = T.Row$(t13, C.CrossAxisAlignment_2, C.MainAxisAlignment_0, C.MainAxisSize_0, _null);
|
||||
t15 = H.setRuntimeTypeInfo([], t12);
|
||||
if (D.getLayout(context) === C.AppLayout_desktop)
|
||||
C.JSArray_methods.addAll$1(t15, H.setRuntimeTypeInfo([new T.Builder(new A.ReportsScreen_build_closure13(_this, t1, reportResult), _null), new X.AppTextButton(t1.get$$export(), new A.ReportsScreen_build_closure14(_this, context), true, _null, _null)], t12));
|
||||
if (D.getLayout(context) === C.AppLayout_desktop) {
|
||||
t16 = H.setRuntimeTypeInfo([new T.Builder(new A.ReportsScreen_build_closure13(_this, t1, reportResult), _null)], t12);
|
||||
if (!(D.isMacOS() || D.isWindows() || D.isLinux()))
|
||||
t16.push(new X.AppTextButton(t1.get$$export(), new A.ReportsScreen_build_closure14(_this, context), true, _null, _null));
|
||||
C.JSArray_methods.addAll$1(t15, t16);
|
||||
}
|
||||
if (D.getLayout(context) === C.AppLayout_mobile || !state.prefState.isHistoryVisible)
|
||||
t15.push(new T.Builder(new A.ReportsScreen_build_closure15(t1, state, store), _null));
|
||||
t13 = E.AppBar$(t15, _null, false, _null, _null, _null, 1, _null, false, _null, false, _null, _null, _null, leading, _null, true, _null, _null, _null, _null, t13, _null, _null, _null, 1, _null);
|
||||
@ -419921,6 +419946,14 @@
|
||||
var t1 = J.$index$asx($.LocalizationsProvider__localizedValues.$index(0, this.localeCode), "please_enter_a_value");
|
||||
return t1 == null ? "" : t1;
|
||||
},
|
||||
get$item: function(_) {
|
||||
var t1 = J.$index$asx($.LocalizationsProvider__localizedValues.$index(0, this.localeCode), "item");
|
||||
return t1 == null ? "" : t1;
|
||||
},
|
||||
get$lineTotal: function() {
|
||||
var t1 = J.$index$asx($.LocalizationsProvider__localizedValues.$index(0, this.localeCode), "line_total");
|
||||
return t1 == null ? "" : t1;
|
||||
},
|
||||
get$contactUs: function() {
|
||||
var t1 = J.$index$asx($.LocalizationsProvider__localizedValues.$index(0, this.localeCode), "contact_us");
|
||||
return t1 == null ? "" : t1;
|
||||
@ -420177,6 +420210,10 @@
|
||||
var t1 = J.$index$asx($.LocalizationsProvider__localizedValues.$index(0, this.localeCode), "apply_payment");
|
||||
return t1 == null ? "" : t1;
|
||||
},
|
||||
get$hours: function() {
|
||||
var t1 = J.$index$asx($.LocalizationsProvider__localizedValues.$index(0, this.localeCode), "hours");
|
||||
return t1 == null ? "" : t1;
|
||||
},
|
||||
get$gateway: function() {
|
||||
var t1 = J.$index$asx($.LocalizationsProvider__localizedValues.$index(0, this.localeCode), "gateway");
|
||||
return t1 == null ? "" : t1;
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"/js/app.js": "/js/app.js?id=0e3959ab851d3350364d",
|
||||
"/js/app.js": "/js/app.js?id=0d1e02ebdcc97462d422",
|
||||
"/js/clients/payment_methods/authorize-authorize-card.js": "/js/clients/payment_methods/authorize-authorize-card.js?id=de4468c682d6861847de",
|
||||
"/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",
|
||||
@ -11,11 +11,11 @@
|
||||
"/js/clients/quotes/action-selectors.js": "/js/clients/quotes/action-selectors.js?id=63f0688329be80ee8693",
|
||||
"/js/clients/quotes/approve.js": "/js/clients/quotes/approve.js?id=795d2f44cf3d117a554e",
|
||||
"/js/clients/payments/stripe-credit-card.js": "/js/clients/payments/stripe-credit-card.js?id=ea4250be693260798735",
|
||||
"/js/setup/setup.js": "/js/setup/setup.js?id=7e19431f4cb9ad45e177",
|
||||
"/js/setup/setup.js": "/js/setup/setup.js?id=6b870beeb350d83668c5",
|
||||
"/js/clients/payments/card-js.min.js": "/js/clients/payments/card-js.min.js?id=8ce33c3deae058ad314f",
|
||||
"/js/clients/shared/pdf.js": "/js/clients/shared/pdf.js?id=73a0d914ad3577f257f4",
|
||||
"/js/clients/shared/multiple-downloads.js": "/js/clients/shared/multiple-downloads.js?id=c2caa29f753ad1f3a12c",
|
||||
"/js/clients/linkify-urls.js": "/js/clients/linkify-urls.js?id=44c51b4838d1f135bbe3",
|
||||
"/js/clients/linkify-urls.js": "/js/clients/linkify-urls.js?id=448d055fa1e8357130e6",
|
||||
"/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",
|
||||
@ -26,7 +26,7 @@
|
||||
"/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",
|
||||
|
13
resources/js/clients/payments/razorpay-aio.js
vendored
13
resources/js/clients/payments/razorpay-aio.js
vendored
@ -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();
|
||||
};
|
||||
|
@ -4339,6 +4339,7 @@ $LANG = array(
|
||||
'browser_pay' => 'Google Pay, Apple Pay, Microsoft Pay',
|
||||
'no_available_methods' => 'We can\'t find any credit cards on your device. <a href="https://invoiceninja.github.io/docs/payments#apple-pay-google-pay-microsoft-pay" target="_blank" class="underline">Read more about this.</a>',
|
||||
'gocardless_mandate_not_ready' => 'Payment mandate is not ready. Please try again later.',
|
||||
'payment_type_instant_bank_pay' => 'Instant Bank Pay',
|
||||
);
|
||||
|
||||
return $LANG;
|
||||
|
@ -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>
|
||||
|
@ -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']);
|
||||
|
@ -43,3 +43,4 @@ Route::get('stripe/completed', 'StripeConnectController@completed')->name('strip
|
||||
|
||||
Route::get('checkout/3ds_redirect/{company_key}/{company_gateway_id}/{hash}', 'Gateways\Checkout3dsController@index')->name('checkout.3ds_redirect');
|
||||
Route::get('mollie/3ds_redirect/{company_key}/{company_gateway_id}/{hash}', 'Gateways\Mollie3dsController@index')->name('mollie.3ds_redirect');
|
||||
Route::get('gocardless/ibp_redirect/{company_key}/{company_gateway_id}/{hash}', 'Gateways\GoCardlessController@ibpRedirect')->name('gocardless.ibp_redirect');
|
||||
|
@ -10,6 +10,7 @@
|
||||
*/
|
||||
namespace Tests\Unit;
|
||||
|
||||
use App\Factory\ClientContactFactory;
|
||||
use App\Factory\InvoiceItemFactory;
|
||||
use App\Utils\Traits\UserSessionAttributes;
|
||||
use Illuminate\Support\Facades\Session;
|
||||
@ -77,4 +78,41 @@ class CollectionMergingTest extends TestCase
|
||||
$this->assertTrue(collect($items)->contains('type_id', 3));
|
||||
|
||||
}
|
||||
|
||||
public function testClientContactSendEmailExists()
|
||||
{
|
||||
$new_collection = collect();
|
||||
|
||||
$cc = ClientContactFactory::create(1,1);
|
||||
$cc->send_email = true;
|
||||
|
||||
$new_collection->push($cc);
|
||||
|
||||
$cc_false = ClientContactFactory::create(1,1);
|
||||
$cc_false->send_email = false;
|
||||
|
||||
$new_collection->push($cc_false);
|
||||
|
||||
$this->assertTrue($new_collection->contains('send_email', true));
|
||||
|
||||
}
|
||||
|
||||
public function testClientContactSendEmailDoesNotExists()
|
||||
{
|
||||
$new_collection = collect();
|
||||
|
||||
$cc = ClientContactFactory::create(1,1);
|
||||
$cc->send_email = false;
|
||||
|
||||
$new_collection->push($cc);
|
||||
|
||||
$cc_false = ClientContactFactory::create(1,1);
|
||||
$cc_false->send_email = false;
|
||||
|
||||
$new_collection->push($cc_false);
|
||||
|
||||
$this->assertFalse($new_collection->contains('send_email', true));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user