mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-07-09 03:14:30 -04:00
commit
9eaf5373ba
@ -1 +1 @@
|
||||
5.3.64
|
||||
5.3.65
|
@ -77,17 +77,27 @@ class BackupUpdate extends Command
|
||||
{
|
||||
set_time_limit(0);
|
||||
|
||||
Backup::whereRaw('html_backup IS NOT NULL')->chunk(5000, function ($backups) {
|
||||
foreach ($backups as $backup) {
|
||||
Backup::whereHas('activity')->whereRaw('html_backup IS NOT NULL')->cursor()->each( function ($backup) {
|
||||
|
||||
if(strlen($backup->html_backup) > 1 && $backup->activity->client()->exists()){
|
||||
|
||||
$client = $backup->activity->client;
|
||||
if(strlen($backup->html_backup) > 1 && $backup->activity->invoice->exists()){
|
||||
|
||||
$client = $backup->activity->invoice->client;
|
||||
$backup->storeRemotely($backup->html_backup, $client);
|
||||
|
||||
}else if(strlen($backup->html_backup) > 1 && $backup->activity->quote->exists()){
|
||||
|
||||
$client = $backup->activity->quote->client;
|
||||
$backup->storeRemotely($backup->html_backup, $client);
|
||||
|
||||
}else if(strlen($backup->html_backup) > 1 && $backup->activity->credit->exists()){
|
||||
|
||||
$client = $backup->activity->credit->client;
|
||||
$backup->storeRemotely($backup->html_backup, $client);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
@ -12,6 +12,7 @@
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Jobs\Util\VersionCheck;
|
||||
use App\Utils\Ninja;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\Artisan;
|
||||
|
||||
@ -59,7 +60,12 @@ class PostUpdate extends Command
|
||||
info("finished running composer install ");
|
||||
|
||||
try {
|
||||
Artisan::call('optimize');
|
||||
|
||||
if(Ninja::isHosted())
|
||||
Artisan::call('optimize');
|
||||
else
|
||||
Artisan::call('config:clear');
|
||||
|
||||
} catch (\Exception $e) {
|
||||
info("I wasn't able to optimize.");
|
||||
}
|
||||
|
@ -149,7 +149,9 @@ class ContactLoginController extends Controller
|
||||
return redirect(session()->get('url.intended'));
|
||||
}
|
||||
|
||||
return redirect(route('client.dashboard'));
|
||||
$this->setRedirectPath();
|
||||
|
||||
return redirect($this->redirectTo);
|
||||
}
|
||||
|
||||
public function logout()
|
||||
|
@ -62,21 +62,46 @@ class RecurringInvoiceController extends Controller
|
||||
|
||||
public function requestCancellation(RequestCancellationRequest $request, RecurringInvoice $recurring_invoice)
|
||||
{
|
||||
if (is_null($recurring_invoice->subscription_id) || optional($recurring_invoice->subscription)->allow_cancellation) {
|
||||
nlog("outside cancellation");
|
||||
|
||||
if (optional($recurring_invoice->subscription)->allow_cancellation) {
|
||||
|
||||
nlog("inside the cancellation");
|
||||
|
||||
$nmo = new NinjaMailerObject;
|
||||
$nmo->mailable = (new NinjaMailer((new ClientContactRequestCancellationObject($recurring_invoice, auth()->user()))->build()));
|
||||
$nmo->company = $recurring_invoice->company;
|
||||
$nmo->settings = $recurring_invoice->company->settings;
|
||||
|
||||
$notifiable_users = $this->filterUsersByPermissions($recurring_invoice->company->company_users, $recurring_invoice, ['recurring_cancellation']);
|
||||
// $notifiable_users = $this->filterUsersByPermissions($recurring_invoice->company->company_users, $recurring_invoice, ['recurring_cancellation']);
|
||||
|
||||
$recurring_invoice->company->company_users->each(function ($company_user) use ($nmo){
|
||||
|
||||
|
||||
$methods = $this->findCompanyUserNotificationType($company_user, ['recurring_cancellation', 'all_notifications']);
|
||||
|
||||
|
||||
//if mail is a method type -fire mail!!
|
||||
if (($key = array_search('mail', $methods)) !== false) {
|
||||
unset($methods[$key]);
|
||||
|
||||
|
||||
$nmo->to_user = $company_user->user;
|
||||
NinjaMailerJob::dispatch($nmo);
|
||||
|
||||
}
|
||||
|
||||
$notifiable_users->each(function ($company_user) use($nmo){
|
||||
|
||||
$nmo->to_user = $company_user->user;
|
||||
NinjaMailerJob::dispatch($nmo);
|
||||
|
||||
});
|
||||
|
||||
// $notifiable_users->each(function ($company_user) use($nmo){
|
||||
|
||||
// $nmo->to_user = $company_user->user;
|
||||
// NinjaMailerJob::dispatch($nmo);
|
||||
|
||||
// });
|
||||
|
||||
//$recurring_invoice->user->notify(new ClientContactRequestCancellation($recurring_invoice, auth()->user()));
|
||||
|
||||
return $this->render('recurring_invoices.cancellation.index', [
|
||||
|
@ -24,8 +24,10 @@ use App\Http\Requests\Credit\ShowCreditRequest;
|
||||
use App\Http\Requests\Credit\StoreCreditRequest;
|
||||
use App\Http\Requests\Credit\UpdateCreditRequest;
|
||||
use App\Http\Requests\Credit\UploadCreditRequest;
|
||||
use App\Jobs\Credit\ZipCredits;
|
||||
use App\Jobs\Entity\EmailEntity;
|
||||
use App\Jobs\Invoice\EmailCredit;
|
||||
use App\Jobs\Invoice\ZipInvoices;
|
||||
use App\Models\Account;
|
||||
use App\Models\Client;
|
||||
use App\Models\Credit;
|
||||
@ -505,12 +507,30 @@ class CreditController extends BaseController
|
||||
|
||||
$ids = request()->input('ids');
|
||||
|
||||
$credits = Credit::withTrashed()->whereIn('id', $this->transformKeys($ids));
|
||||
$credits = Credit::withTrashed()->whereIn('id', $this->transformKeys($ids))->company()->get();
|
||||
|
||||
if (! $credits) {
|
||||
return response()->json(['message' => ctrans('texts.no_credits_found')]);
|
||||
}
|
||||
|
||||
/*
|
||||
* Download Invoice/s
|
||||
*/
|
||||
|
||||
if ($action == 'bulk_download' && $credits->count() > 1) {
|
||||
$credits->each(function ($credit) {
|
||||
if (auth()->user()->cannot('view', $credit)) {
|
||||
nlog("access denied");
|
||||
return response()->json(['message' => ctrans('text.access_denied')]);
|
||||
}
|
||||
});
|
||||
|
||||
ZipCredits::dispatch($credits, $credits->first()->company, auth()->user());
|
||||
|
||||
return response()->json(['message' => ctrans('texts.sent_message')], 200);
|
||||
}
|
||||
|
||||
|
||||
$credits->each(function ($credit, $key) use ($action) {
|
||||
if (auth()->user()->can('edit', $credit)) {
|
||||
$this->performAction($credit, $action, true);
|
||||
|
@ -27,6 +27,7 @@ use App\Http\Requests\Quote\StoreQuoteRequest;
|
||||
use App\Http\Requests\Quote\UpdateQuoteRequest;
|
||||
use App\Http\Requests\Quote\UploadQuoteRequest;
|
||||
use App\Jobs\Invoice\ZipInvoices;
|
||||
use App\Jobs\Quote\ZipQuotes;
|
||||
use App\Models\Account;
|
||||
use App\Models\Client;
|
||||
use App\Models\Invoice;
|
||||
@ -527,14 +528,14 @@ class QuoteController extends BaseController
|
||||
* Download Invoice/s
|
||||
*/
|
||||
|
||||
if ($action == 'download' && $quotes->count() >= 1) {
|
||||
if ($action == 'bulk_download' && $quotes->count() >= 1) {
|
||||
$quotes->each(function ($quote) {
|
||||
if (auth()->user()->cannot('view', $quote)) {
|
||||
return response()->json(['message'=> ctrans('texts.access_denied')]);
|
||||
}
|
||||
});
|
||||
|
||||
ZipInvoices::dispatch($quotes, $quotes->first()->company, auth()->user());
|
||||
ZipQuotes::dispatch($quotes, $quotes->first()->company, auth()->user());
|
||||
|
||||
return response()->json(['message' => ctrans('texts.sent_message')], 200);
|
||||
}
|
||||
|
@ -169,7 +169,8 @@ class SetupController extends Controller
|
||||
|
||||
/* Run migrations */
|
||||
if (!config('ninja.disable_auto_update')) {
|
||||
Artisan::call('optimize');
|
||||
Artisan::call('config:clear');
|
||||
// Artisan::call('optimize');
|
||||
}
|
||||
|
||||
Artisan::call('migrate', ['--force' => true]);
|
||||
|
@ -140,6 +140,10 @@ class Wave extends BaseImport implements ImportInterface
|
||||
$entity_type = 'vendor';
|
||||
|
||||
$data = $this->getCsvData($entity_type);
|
||||
|
||||
if(!is_array($data))
|
||||
return;
|
||||
|
||||
$data = $this->preTransform($data, $entity_type);
|
||||
|
||||
if (empty($data)) {
|
||||
|
@ -31,19 +31,30 @@ class InvoiceTransformer extends BaseTransformer {
|
||||
throw new ImportException( 'Invoice number already exists' );
|
||||
}
|
||||
|
||||
if(array_key_exists('Invoice Date', $invoice_data))
|
||||
$date_key = 'Invoice Date';
|
||||
else
|
||||
$date_key = 'Transaction Date';
|
||||
|
||||
if(array_key_exists('Customer Name', $invoice_data))
|
||||
$customer_key = 'Customer Name';
|
||||
else
|
||||
$customer_key = 'Customer';
|
||||
|
||||
$transformed = [
|
||||
'company_id' => $this->company->id,
|
||||
'client_id' => $this->getClient( $customer_name = $this->getString( $invoice_data, 'Customer' ), null ),
|
||||
'client_id' => $this->getClient( $customer_name = $this->getString( $invoice_data, $customer_key ), null ),
|
||||
'number' => $invoice_number = $this->getString( $invoice_data, 'Invoice Number' ),
|
||||
'date' => date( 'Y-m-d', strtotime( $invoice_data['Transaction Date'] ) ) ?: now()->format('Y-m-d'), //27-01-2022
|
||||
'date' => date( 'Y-m-d', strtotime( $invoice_data[$date_key] ) ) ?: now()->format('Y-m-d'), //27-01-2022
|
||||
'currency_id' => $this->getCurrencyByCode( $invoice_data, 'Currency' ),
|
||||
'status_id' => Invoice::STATUS_SENT,
|
||||
'due_date' => array_key_exists('Due Date', $invoice_data) ? date( 'Y-m-d', strtotime( $invoice_data['Due Date'] ) ) : null,
|
||||
];
|
||||
|
||||
$line_items = [];
|
||||
$payments = [];
|
||||
foreach ( $line_items_data as $record ) {
|
||||
if ( $record['Account Type'] === 'Income' ) {
|
||||
if (array_key_exists('Account Type', $record) && $record['Account Type'] === 'Income' ) {
|
||||
$description = $this->getString( $record, 'Transaction Line Description' );
|
||||
|
||||
// Remove duplicate data from description
|
||||
@ -63,13 +74,31 @@ class InvoiceTransformer extends BaseTransformer {
|
||||
|
||||
'quantity' => 1,
|
||||
];
|
||||
} elseif ( $record['Account Type'] === 'System Receivable Invoice' ) {
|
||||
} elseif (array_key_exists('Account Type', $record) && $record['Account Type'] === 'System Receivable Invoice' ) {
|
||||
// This is a payment
|
||||
$payments[] = [
|
||||
'date' => date( 'Y-m-d', strtotime( $invoice_data['Transaction Date'] ) ),
|
||||
'date' => date( 'Y-m-d', strtotime( $invoice_data[$date_key] ) ),
|
||||
'amount' => $this->getFloat( $record, 'Amount (One column)' ),
|
||||
];
|
||||
}
|
||||
else {
|
||||
//could be a generate invoices.csv file
|
||||
$line_items[] = [
|
||||
'notes' => 'Imported Invoice',
|
||||
'cost' => $this->getFloat( $record, 'Invoice Total' ),
|
||||
'tax_name1' => 'Tax',
|
||||
'tax_rate1' => round($this->getFloat( $record, 'Invoice Tax Total' ) / $this->getFloat( $record, 'Invoice Total' ) * 100,2),
|
||||
'quantity' => 1,
|
||||
];
|
||||
|
||||
if($record['Invoice Paid'] > 0){
|
||||
$payments[] = [
|
||||
'date' => date( 'Y-m-d', strtotime( $record['Last Payment Date'] ) ),
|
||||
'amount' => $this->getFloat( $record, 'Invoice Paid' ),
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
$transformed['line_items'] = $line_items;
|
||||
|
123
app/Jobs/Credit/ZipCredits.php
Normal file
123
app/Jobs/Credit/ZipCredits.php
Normal file
@ -0,0 +1,123 @@
|
||||
<?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\Jobs\Credit;
|
||||
|
||||
use App\Jobs\Entity\CreateEntityPdf;
|
||||
use App\Jobs\Mail\NinjaMailerJob;
|
||||
use App\Jobs\Mail\NinjaMailerObject;
|
||||
use App\Jobs\Util\UnlinkFile;
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Mail\DownloadCredits;
|
||||
use App\Models\Company;
|
||||
use App\Models\User;
|
||||
use App\Utils\TempFile;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use ZipArchive;
|
||||
|
||||
class ZipCredits implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
public $credits;
|
||||
|
||||
private $company;
|
||||
|
||||
private $user;
|
||||
|
||||
public $settings;
|
||||
|
||||
public $tries = 1;
|
||||
/**
|
||||
* @param $invoices
|
||||
* @param Company $company
|
||||
* @param $email
|
||||
* @deprecated confirm to be deleted
|
||||
* Create a new job instance.
|
||||
*
|
||||
*/
|
||||
public function __construct($credits, Company $company, User $user)
|
||||
{
|
||||
$this->credits = $credits;
|
||||
|
||||
$this->company = $company;
|
||||
|
||||
$this->user = $user;
|
||||
|
||||
$this->settings = $company->settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*
|
||||
* @return void
|
||||
* @throws \ZipStream\Exception\FileNotFoundException
|
||||
* @throws \ZipStream\Exception\FileNotReadableException
|
||||
* @throws \ZipStream\Exception\OverflowException
|
||||
*/
|
||||
|
||||
public function handle()
|
||||
{
|
||||
MultiDB::setDb($this->company->db);
|
||||
|
||||
# create new zip object
|
||||
$zipFile = new \PhpZip\ZipFile();
|
||||
$file_name = date('Y-m-d').'_'.str_replace(' ', '_', trans('texts.credits')).'.zip';
|
||||
$invitation = $this->credits->first()->invitations->first();
|
||||
$path = $this->credits->first()->client->quote_filepath($invitation);
|
||||
|
||||
|
||||
$this->credits->each(function ($credit){
|
||||
|
||||
CreateEntityPdf::dispatchNow($credit->invitations()->first());
|
||||
|
||||
});
|
||||
|
||||
try{
|
||||
|
||||
foreach ($this->credits as $credit) {
|
||||
|
||||
$download_file = file_get_contents($credit->pdf_file_path($invitation, 'url', true));
|
||||
$zipFile->addFromString(basename($credit->pdf_file_path($invitation)), $download_file);
|
||||
|
||||
}
|
||||
|
||||
Storage::put($path.$file_name, $zipFile->outputAsString());
|
||||
|
||||
$nmo = new NinjaMailerObject;
|
||||
$nmo->mailable = new DownloadCredits(Storage::url($path.$file_name), $this->company);
|
||||
$nmo->to_user = $this->user;
|
||||
$nmo->settings = $this->settings;
|
||||
$nmo->company = $this->company;
|
||||
|
||||
NinjaMailerJob::dispatch($nmo);
|
||||
|
||||
UnlinkFile::dispatch(config('filesystems.default'), $path.$file_name)->delay(now()->addHours(1));
|
||||
|
||||
|
||||
}
|
||||
catch(\PhpZip\Exception\ZipException $e){
|
||||
// handle exception
|
||||
}
|
||||
finally{
|
||||
$zipFile->close();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -11,9 +11,11 @@
|
||||
|
||||
namespace App\Jobs\Invoice;
|
||||
|
||||
use App\Jobs\Entity\CreateEntityPdf;
|
||||
use App\Jobs\Mail\NinjaMailerJob;
|
||||
use App\Jobs\Mail\NinjaMailerObject;
|
||||
use App\Jobs\Util\UnlinkFile;
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Mail\DownloadInvoices;
|
||||
use App\Models\Company;
|
||||
use App\Models\User;
|
||||
@ -39,6 +41,8 @@ class ZipInvoices implements ShouldQueue
|
||||
|
||||
public $settings;
|
||||
|
||||
public $tries = 1;
|
||||
|
||||
/**
|
||||
* @param $invoices
|
||||
* @param Company $company
|
||||
@ -69,41 +73,51 @@ class ZipInvoices implements ShouldQueue
|
||||
|
||||
public function handle()
|
||||
{
|
||||
# create new zip object
|
||||
$zip = new ZipArchive();
|
||||
MultiDB::setDb($this->company->db);
|
||||
|
||||
# create new zip object
|
||||
$zipFile = new \PhpZip\ZipFile();
|
||||
$file_name = date('Y-m-d').'_'.str_replace(' ', '_', trans('texts.invoices')).'.zip';
|
||||
$invitation = $this->invoices->first()->invitations->first();
|
||||
$path = $this->invoices->first()->client->invoice_filepath($invitation);
|
||||
$file_name = date('Y-m-d').'_'.str_replace(' ', '_', trans('texts.invoices')).'.zip';
|
||||
|
||||
$tmp_file = @tempnam('.', '');
|
||||
$zip->open($tmp_file , ZipArchive::OVERWRITE);
|
||||
$this->invoices->each(function ($invoice){
|
||||
|
||||
# loop through each file
|
||||
foreach ($this->invoices as $invoice) {
|
||||
CreateEntityPdf::dispatchNow($invoice->invitations()->first());
|
||||
|
||||
$inv = $invoice->invitations->first();
|
||||
});
|
||||
|
||||
# download file
|
||||
$download_file = file_get_contents($invoice->pdf_file_path($inv, 'url', true));
|
||||
try{
|
||||
|
||||
#add it to the zip
|
||||
$zip->addFromString(basename($invoice->pdf_file_path($inv)), $download_file);
|
||||
foreach ($this->invoices as $invoice) {
|
||||
|
||||
$download_file = file_get_contents($invoice->pdf_file_path($invitation, 'url', true));
|
||||
$zipFile->addFromString(basename($invoice->pdf_file_path($invitation)), $download_file);
|
||||
|
||||
}
|
||||
|
||||
Storage::put($path.$file_name, $zipFile->outputAsString());
|
||||
|
||||
$nmo = new NinjaMailerObject;
|
||||
$nmo->mailable = new DownloadInvoices(Storage::url($path.$file_name), $this->company);
|
||||
$nmo->to_user = $this->user;
|
||||
$nmo->settings = $this->settings;
|
||||
$nmo->company = $this->company;
|
||||
|
||||
NinjaMailerJob::dispatch($nmo);
|
||||
|
||||
UnlinkFile::dispatch(config('filesystems.default'), $path.$file_name)->delay(now()->addHours(1));
|
||||
|
||||
|
||||
}
|
||||
catch(\PhpZip\Exception\ZipException $e){
|
||||
nlog("could not make zip => ". $e->getMessage());
|
||||
}
|
||||
finally{
|
||||
$zipFile->close();
|
||||
}
|
||||
|
||||
# close zip
|
||||
$zip->close();
|
||||
|
||||
Storage::put($path.$file_name, file_get_contents($tmp_file));
|
||||
|
||||
$nmo = new NinjaMailerObject;
|
||||
$nmo->mailable = new DownloadInvoices(Storage::url($path.$file_name), $this->company);
|
||||
$nmo->to_user = $this->user;
|
||||
$nmo->settings = $this->settings;
|
||||
$nmo->company = $this->company;
|
||||
|
||||
NinjaMailerJob::dispatch($nmo);
|
||||
|
||||
UnlinkFile::dispatch(config('filesystems.default'), $path.$file_name)->delay(now()->addHours(1));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -41,12 +41,17 @@ class NinjaMailer extends Mailable
|
||||
$from_name = $this->mail_obj->from_name;
|
||||
}
|
||||
|
||||
return $this->from(config('mail.from.address'), $from_name)
|
||||
$ninja_mailable = $this->from(config('mail.from.address'), $from_name)
|
||||
->subject($this->mail_obj->subject)
|
||||
->view($this->mail_obj->markdown, $this->mail_obj->data)
|
||||
->withSwiftMessage(function ($message) {
|
||||
$message->getHeaders()->addTextHeader('Tag', $this->mail_obj->tag);
|
||||
});
|
||||
|
||||
if(property_exists($this->mail_obj, 'text_view')){
|
||||
$ninja_mailable->text($this->mail_obj->text_view, $this->mail_obj->data);
|
||||
}
|
||||
|
||||
return $ninja_mailable;
|
||||
}
|
||||
}
|
||||
|
124
app/Jobs/Quote/ZipQuotes.php
Normal file
124
app/Jobs/Quote/ZipQuotes.php
Normal file
@ -0,0 +1,124 @@
|
||||
<?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\Jobs\Quote;
|
||||
|
||||
use App\Jobs\Entity\CreateEntityPdf;
|
||||
use App\Jobs\Mail\NinjaMailerJob;
|
||||
use App\Jobs\Mail\NinjaMailerObject;
|
||||
use App\Jobs\Util\UnlinkFile;
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Mail\DownloadQuotes;
|
||||
use App\Models\Company;
|
||||
use App\Models\User;
|
||||
use App\Utils\TempFile;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use ZipArchive;
|
||||
|
||||
class ZipQuotes implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
public $quotes;
|
||||
|
||||
private $company;
|
||||
|
||||
private $user;
|
||||
|
||||
public $settings;
|
||||
|
||||
public $tries = 1;
|
||||
|
||||
/**
|
||||
* @param $invoices
|
||||
* @param Company $company
|
||||
* @param $email
|
||||
* @deprecated confirm to be deleted
|
||||
* Create a new job instance.
|
||||
*
|
||||
*/
|
||||
public function __construct($quotes, Company $company, User $user)
|
||||
{
|
||||
$this->quotes = $quotes;
|
||||
|
||||
$this->company = $company;
|
||||
|
||||
$this->user = $user;
|
||||
|
||||
$this->settings = $company->settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*
|
||||
* @return void
|
||||
* @throws \ZipStream\Exception\FileNotFoundException
|
||||
* @throws \ZipStream\Exception\FileNotReadableException
|
||||
* @throws \ZipStream\Exception\OverflowException
|
||||
*/
|
||||
|
||||
public function handle()
|
||||
{
|
||||
MultiDB::setDb($this->company->db);
|
||||
|
||||
# create new zip object
|
||||
$zipFile = new \PhpZip\ZipFile();
|
||||
$file_name = date('Y-m-d').'_'.str_replace(' ', '_', trans('texts.quotes')).'.zip';
|
||||
$invitation = $this->quotes->first()->invitations->first();
|
||||
$path = $this->quotes->first()->client->quote_filepath($invitation);
|
||||
|
||||
|
||||
$this->quotes->each(function ($quote){
|
||||
|
||||
CreateEntityPdf::dispatchNow($quote->invitations()->first());
|
||||
|
||||
});
|
||||
|
||||
try{
|
||||
|
||||
foreach ($this->quotes as $quote) {
|
||||
|
||||
$download_file = file_get_contents($quote->pdf_file_path($invitation, 'url', true));
|
||||
$zipFile->addFromString(basename($quote->pdf_file_path($invitation)), $download_file);
|
||||
|
||||
}
|
||||
|
||||
Storage::put($path.$file_name, $zipFile->outputAsString());
|
||||
|
||||
$nmo = new NinjaMailerObject;
|
||||
$nmo->mailable = new DownloadQuotes(Storage::url($path.$file_name), $this->company);
|
||||
$nmo->to_user = $this->user;
|
||||
$nmo->settings = $this->settings;
|
||||
$nmo->company = $this->company;
|
||||
|
||||
NinjaMailerJob::dispatch($nmo);
|
||||
|
||||
UnlinkFile::dispatch(config('filesystems.default'), $path.$file_name)->delay(now()->addHours(1));
|
||||
|
||||
|
||||
}
|
||||
catch(\PhpZip\Exception\ZipException $e){
|
||||
// handle exception
|
||||
}
|
||||
finally{
|
||||
$zipFile->close();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -77,9 +77,6 @@ class UserEmailChanged implements ShouldQueue
|
||||
|
||||
NinjaMailerJob::dispatch($nmo, true);
|
||||
|
||||
// $nmo->to_user = $this->new_user;
|
||||
// NinjaMailerJob::dispatch($nmo);
|
||||
|
||||
$this->new_user->service()->invite($this->company);
|
||||
|
||||
}
|
||||
|
@ -39,6 +39,10 @@ class UnlinkFile implements ShouldQueue
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
/* Do not delete files if we are on the sync queue*/
|
||||
if(config('queue.default') == 'sync')
|
||||
return;
|
||||
|
||||
Storage::disk($this->disk)->delete($this->file_path);
|
||||
}
|
||||
}
|
||||
|
@ -63,6 +63,7 @@ class VerifyUserObject
|
||||
$mail_obj->data = $data;
|
||||
$mail_obj->markdown = 'email.admin.generic';
|
||||
$mail_obj->tag = $this->company->company_key;
|
||||
$mail_obj->text_view = 'email.admin.verify_user_text';
|
||||
|
||||
return $mail_obj;
|
||||
}
|
||||
|
@ -58,6 +58,7 @@ class ContactPasswordlessLogin extends Mailable
|
||||
|
||||
return $this
|
||||
->subject(ctrans('texts.account_passwordless_login'))
|
||||
->text('email.billing.passwordless-login_text')
|
||||
->view('email.billing.passwordless-login', [
|
||||
'logo' => $this->company->present()->logo(),
|
||||
'settings' => $this->company->settings,
|
||||
|
@ -43,6 +43,9 @@ class DownloadBackup extends Mailable
|
||||
|
||||
return $this->from(config('mail.from.address'), config('mail.from.name'))
|
||||
->subject(ctrans('texts.download_backup_subject', ['company' => $company->present()->name()]))
|
||||
->text('email.admin.download_files_text',[
|
||||
'url' => $this->file_path,
|
||||
])
|
||||
->view('email.admin.download_files', [
|
||||
'url' => $this->file_path,
|
||||
'logo' => $company->present()->logo,
|
||||
|
56
app/Mail/DownloadCredits.php
Normal file
56
app/Mail/DownloadCredits.php
Normal file
@ -0,0 +1,56 @@
|
||||
<?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;
|
||||
|
||||
use App\Models\Company;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Mail\Mailable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\App;
|
||||
|
||||
class DownloadCredits extends Mailable
|
||||
{
|
||||
// use Queueable, SerializesModels;
|
||||
|
||||
public $file_path;
|
||||
|
||||
public $company;
|
||||
|
||||
public function __construct($file_path, Company $company)
|
||||
{
|
||||
$this->file_path = $file_path;
|
||||
|
||||
$this->company = $company;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the message.
|
||||
*/
|
||||
public function build()
|
||||
{
|
||||
|
||||
App::setLocale($this->company->getLocale());
|
||||
|
||||
return $this->from(config('mail.from.address'), config('mail.from.name'))
|
||||
->subject(ctrans('texts.download_files'))
|
||||
->text('email.admin.download_credits_text', [
|
||||
'url' => $this->file_path,
|
||||
])
|
||||
->view('email.admin.download_credits', [
|
||||
'url' => $this->file_path,
|
||||
'logo' => $this->company->present()->logo,
|
||||
'whitelabel' => $this->company->account->isPaid() ? true : false,
|
||||
'settings' => $this->company->settings,
|
||||
'greeting' => $this->company->present()->name(),
|
||||
]);
|
||||
}
|
||||
}
|
@ -1,4 +1,13 @@
|
||||
<?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;
|
||||
|
||||
@ -33,6 +42,9 @@ class DownloadInvoices extends Mailable
|
||||
|
||||
return $this->from(config('mail.from.address'), config('mail.from.name'))
|
||||
->subject(ctrans('texts.download_files'))
|
||||
->text('email.admin.download_invoices_text', [
|
||||
'url' => $this->file_path,
|
||||
])
|
||||
->view('email.admin.download_invoices', [
|
||||
'url' => $this->file_path,
|
||||
'logo' => $this->company->present()->logo,
|
||||
|
56
app/Mail/DownloadQuotes.php
Normal file
56
app/Mail/DownloadQuotes.php
Normal file
@ -0,0 +1,56 @@
|
||||
<?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;
|
||||
|
||||
use App\Models\Company;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Mail\Mailable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\App;
|
||||
|
||||
class DownloadQuotes extends Mailable
|
||||
{
|
||||
// use Queueable, SerializesModels;
|
||||
|
||||
public $file_path;
|
||||
|
||||
public $company;
|
||||
|
||||
public function __construct($file_path, Company $company)
|
||||
{
|
||||
$this->file_path = $file_path;
|
||||
|
||||
$this->company = $company;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the message.
|
||||
*/
|
||||
public function build()
|
||||
{
|
||||
|
||||
App::setLocale($this->company->getLocale());
|
||||
|
||||
return $this->from(config('mail.from.address'), config('mail.from.name'))
|
||||
->subject(ctrans('texts.download_files'))
|
||||
->text('email.admin.download_quotes_text', [
|
||||
'url' => $this->file_path,
|
||||
])
|
||||
->view('email.admin.download_quotes', [
|
||||
'url' => $this->file_path,
|
||||
'logo' => $this->company->present()->logo,
|
||||
'whitelabel' => $this->company->account->isPaid() ? true : false,
|
||||
'settings' => $this->company->settings,
|
||||
'greeting' => $this->company->present()->name(),
|
||||
]);
|
||||
}
|
||||
}
|
@ -33,6 +33,10 @@ class BaseEmailEngine implements EngineInterface
|
||||
|
||||
public $invitation;
|
||||
|
||||
public $text_body;
|
||||
|
||||
public $text_footer;
|
||||
|
||||
public function setFooter($footer)
|
||||
{
|
||||
$this->footer = $footer;
|
||||
@ -105,6 +109,13 @@ class BaseEmailEngine implements EngineInterface
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setTextBody($text)
|
||||
{
|
||||
$this->text_body = $text;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getSubject()
|
||||
{
|
||||
return $this->subject;
|
||||
@ -148,11 +159,37 @@ class BaseEmailEngine implements EngineInterface
|
||||
public function setInvitation($invitation)
|
||||
{
|
||||
$this->invitation = $invitation;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getInvitation()
|
||||
{
|
||||
return $this->invitation;
|
||||
}
|
||||
|
||||
public function getTextBody()
|
||||
{
|
||||
return $this->text_body;
|
||||
}
|
||||
|
||||
private function replaceEntities($content)
|
||||
{
|
||||
$find = [
|
||||
'<p>',
|
||||
'</p>',
|
||||
'<div class="center">',
|
||||
'<\div>',
|
||||
];
|
||||
|
||||
$replace = [
|
||||
'',
|
||||
'\n\n',
|
||||
'',
|
||||
'\n\n',
|
||||
];
|
||||
|
||||
return str_replace($find, $replace, $content);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -93,6 +93,18 @@ class CreditEmailEngine extends BaseEmailEngine
|
||||
);
|
||||
}
|
||||
|
||||
$text_body = trans(
|
||||
'texts.credit_message',
|
||||
[
|
||||
'credit' => $this->credit->number,
|
||||
'company' => $this->credit->company->present()->name(),
|
||||
'amount' => Number::formatMoney($this->credit->balance, $this->client),
|
||||
],
|
||||
null,
|
||||
$this->client->locale()
|
||||
|
||||
) . "\n\n" . $this->invitation->getLink();
|
||||
|
||||
$this->setTemplate($this->client->getSetting('email_style'))
|
||||
->setContact($this->contact)
|
||||
->setVariables((new HtmlEngine($this->invitation))->makeValues())//move make values into the htmlengine
|
||||
@ -101,7 +113,8 @@ class CreditEmailEngine extends BaseEmailEngine
|
||||
->setFooter("<a href='{$this->invitation->getLink()}'>".ctrans('texts.view_credit').'</a>')
|
||||
->setViewLink($this->invitation->getLink())
|
||||
->setViewText(ctrans('texts.view_credit'))
|
||||
->setInvitation($this->invitation);
|
||||
->setInvitation($this->invitation)
|
||||
->setTextBody($text_body);
|
||||
|
||||
if ($this->client->getSetting('pdf_email_attachment') !== false && $this->credit->company->account->hasFeature(Account::FEATURE_PDF_ATTACHMENT)) {
|
||||
|
||||
|
@ -46,4 +46,7 @@ interface EngineInterface
|
||||
public function getViewText();
|
||||
|
||||
public function build();
|
||||
|
||||
public function getTextBody();
|
||||
|
||||
}
|
||||
|
@ -79,6 +79,17 @@ class InvoiceEmailEngine extends BaseEmailEngine
|
||||
|
||||
}
|
||||
|
||||
$text_body = trans(
|
||||
'texts.invoice_message',
|
||||
[
|
||||
'invoice' => $this->invoice->number,
|
||||
'company' => $this->invoice->company->present()->name(),
|
||||
'amount' => Number::formatMoney($this->invoice->balance, $this->client),
|
||||
],
|
||||
null,
|
||||
$this->client->locale()
|
||||
) . "\n\n" . $this->invitation->getLink();
|
||||
|
||||
if (is_array($this->template_data) && array_key_exists('subject', $this->template_data) && strlen($this->template_data['subject']) > 0) {
|
||||
$subject_template = $this->template_data['subject'];
|
||||
nlog("subject = template data");
|
||||
@ -111,7 +122,8 @@ class InvoiceEmailEngine extends BaseEmailEngine
|
||||
->setFooter("<a href='{$this->invitation->getLink()}'>".ctrans('texts.view_invoice').'</a>')
|
||||
->setViewLink($this->invitation->getLink())
|
||||
->setViewText(ctrans('texts.view_invoice'))
|
||||
->setInvitation($this->invitation);
|
||||
->setInvitation($this->invitation)
|
||||
->setTextBody($text_body);
|
||||
|
||||
if ($this->client->getSetting('pdf_email_attachment') !== false && $this->invoice->company->account->hasFeature(Account::FEATURE_PDF_ATTACHMENT)) {
|
||||
|
||||
|
@ -94,6 +94,18 @@ class QuoteEmailEngine extends BaseEmailEngine
|
||||
);
|
||||
}
|
||||
|
||||
$text_body = trans(
|
||||
'texts.quote_message',
|
||||
[
|
||||
'quote' => $this->quote->number,
|
||||
'company' => $this->quote->company->present()->name(),
|
||||
'amount' => Number::formatMoney($this->quote->amount, $this->client),
|
||||
],
|
||||
null,
|
||||
$this->client->locale()
|
||||
|
||||
) . "\n\n" . $this->invitation->getLink();
|
||||
|
||||
$this->setTemplate($this->client->getSetting('email_style'))
|
||||
->setContact($this->contact)
|
||||
->setVariables((new HtmlEngine($this->invitation))->makeValues())//move make values into the htmlengine
|
||||
@ -102,7 +114,8 @@ class QuoteEmailEngine extends BaseEmailEngine
|
||||
->setFooter("<a href='{$this->invitation->getLink()}'>".ctrans('texts.view_quote').'</a>')
|
||||
->setViewLink($this->invitation->getLink())
|
||||
->setViewText(ctrans('texts.view_quote'))
|
||||
->setInvitation($this->invitation);
|
||||
->setInvitation($this->invitation)
|
||||
->setTextBody($text_body);
|
||||
|
||||
if ($this->client->getSetting('pdf_email_attachment') !== false && $this->quote->company->account->hasFeature(Account::FEATURE_PDF_ATTACHMENT)) {
|
||||
|
||||
|
@ -44,6 +44,7 @@ class ExistingMigration extends Mailable
|
||||
|
||||
return $this
|
||||
->from(config('mail.from.address'), config('mail.from.name'))
|
||||
->text('email.migration.existing_text')
|
||||
->view('email.migration.existing');
|
||||
}
|
||||
}
|
||||
|
@ -54,6 +54,7 @@ class ACHVerificationNotification extends Mailable
|
||||
|
||||
return $this
|
||||
->subject(ctrans('texts.ach_verification_notification_label'))
|
||||
->text('email.gateways.ach-verification-notification_text')
|
||||
->view('email.gateways.ach-verification-notification', [
|
||||
'logo' => $this->company->present()->logo(),
|
||||
'settings' => $this->company->settings,
|
||||
|
@ -59,10 +59,9 @@ class CompanyImportFailure extends Mailable
|
||||
$this->title = ctrans('texts.company_import_failure_subject', ['company' => $this->company->present()->name()]);
|
||||
$this->whitelabel = $this->company->account->isPaid();
|
||||
|
||||
nlog($this->user_message);
|
||||
|
||||
return $this->from(config('mail.from.address'), config('mail.from.name'))
|
||||
->subject(ctrans('texts.company_import_failure_subject', ['company' => $this->company->present()->name()]))
|
||||
->text('email.import.import_failure_text')
|
||||
->view('email.import.import_failure', ['user_message' => $this->user_message, 'title' => $this->title]);
|
||||
}
|
||||
}
|
||||
|
@ -48,6 +48,7 @@ class MigrationCompleted extends Mailable
|
||||
$data['logo'] = $this->company->present()->logo();
|
||||
|
||||
$result = $this->from(config('mail.from.address'), config('mail.from.name'))
|
||||
->text('email.import.completed_text', $data)
|
||||
->view('email.import.completed', $data);
|
||||
|
||||
return $result;
|
||||
|
@ -40,6 +40,7 @@ class MigrationFailed extends Mailable
|
||||
|
||||
return $this
|
||||
->from(config('mail.from.address'), config('mail.from.name'))
|
||||
->text('email.migration.failed_text')
|
||||
->view('email.migration.failed', [
|
||||
'logo' => $this->company->present()->logo(),
|
||||
'settings' => $this->company->settings,
|
||||
|
@ -58,6 +58,7 @@ class ClientContactRequestCancellationObject
|
||||
$mail_obj->data = $data;
|
||||
$mail_obj->markdown = 'email.admin.generic';
|
||||
$mail_obj->tag = $this->company->company_key;
|
||||
$mail_obj->text_view = 'email.admin.generic_text';
|
||||
|
||||
return $mail_obj;
|
||||
}
|
||||
|
@ -92,12 +92,10 @@ class TemplateEmail extends Mailable
|
||||
$this->bcc(explode(",",str_replace(" ", "", $settings->bcc_email)));//remove whitespace if any has been inserted.
|
||||
|
||||
$this->subject($this->build_email->getSubject())
|
||||
->text('email.template.plain', [
|
||||
'body' => $this->build_email->getBody(),
|
||||
'footer' => $this->build_email->getFooter(),
|
||||
->text('email.template.text', [
|
||||
'text_body' => $this->build_email->getTextBody(),
|
||||
'whitelabel' => $this->client->user->account->isPaid() ? true : false,
|
||||
'settings' => $settings,
|
||||
'unsubscribe_link' => $this->invitation ? $this->invitation->getUnsubscribeLink() : '',
|
||||
])
|
||||
->view($template_name, [
|
||||
'greeting' => ctrans('texts.email_salutation', ['name' => $this->contact->present()->name()]),
|
||||
@ -111,7 +109,6 @@ class TemplateEmail extends Mailable
|
||||
'company' => $company,
|
||||
'whitelabel' => $this->client->user->account->isPaid() ? true : false,
|
||||
'logo' => $this->company->present()->logo($settings),
|
||||
'unsubscribe_link' => $this->invitation ? $this->invitation->getUnsubscribeLink() : '',
|
||||
])
|
||||
->withSwiftMessage(function ($message) use($company){
|
||||
$message->getHeaders()->addTextHeader('Tag', $company->company_key);
|
||||
|
@ -50,6 +50,7 @@ class UserAdded extends Mailable
|
||||
|
||||
return $this->from(config('mail.from.address'), config('mail.from.name'))
|
||||
->subject(ctrans('texts.created_user'))
|
||||
->text('email.admin.user_added_text')
|
||||
->view('email.admin.user_added')
|
||||
->with([
|
||||
'settings' => $this->company->settings,
|
||||
|
@ -50,6 +50,10 @@ class UserLoggedIn extends Mailable
|
||||
|
||||
return $this->from(config('mail.from.address'), config('mail.from.name'))
|
||||
->subject(ctrans('texts.new_login_detected'))
|
||||
->text('email.admin.generic_text',[
|
||||
'title' => ctrans('texts.new_login_detected'),
|
||||
'body' => strip_tags(ctrans('texts.new_login_description', ['email' => $this->user->email, 'ip' => $this->ip, 'time' => now()]))
|
||||
])
|
||||
->view('email.admin.notification')
|
||||
->with([
|
||||
'settings' => $this->company->settings,
|
||||
|
@ -37,6 +37,10 @@ class UserNotificationMailer extends Mailable
|
||||
{
|
||||
return $this->from(config('mail.from.address'), config('mail.from.name'))
|
||||
->subject($this->mail_obj->subject)
|
||||
->text('email.admin.generic_text',[
|
||||
'title' => $this->mail_obj->data['title'],
|
||||
'body' => $this->mail_obj->data['message'],
|
||||
])
|
||||
->view($this->mail_obj->markdown, $this->mail_obj->data)
|
||||
->withSwiftMessage(function ($message) {
|
||||
$message->getHeaders()->addTextHeader('Tag', $this->mail_obj->tag);
|
||||
|
@ -177,6 +177,11 @@ class Activity extends StaticModel
|
||||
return $this->belongsTo(Invoice::class)->withTrashed();
|
||||
}
|
||||
|
||||
public function credit()
|
||||
{
|
||||
return $this->belongsTo(Credit::class)->withTrashed();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
|
@ -38,7 +38,7 @@ class PaymentHash extends Model
|
||||
|
||||
public function fee_invoice()
|
||||
{
|
||||
return $this->belongsTo(Invoice::class, 'fee_invoice_id', 'id');
|
||||
return $this->belongsTo(Invoice::class, 'fee_invoice_id', 'id')->withTrashed();
|
||||
}
|
||||
|
||||
public function withData(string $property, $value): self
|
||||
|
@ -60,7 +60,9 @@ class CreditCard
|
||||
{
|
||||
|
||||
|
||||
$description = $this->stripe->decodeUnicodeString(ctrans('texts.invoices') . ': ' . collect($data['invoices'])->pluck('invoice_number')) . " for client {$this->stripe->client->present()->name()}";
|
||||
// $description = $this->stripe->decodeUnicodeString(ctrans('texts.invoices') . ': ' . collect($data['invoices'])->pluck('invoice_number')) . " for client {$this->stripe->client->present()->name()}";
|
||||
$invoice_numbers = collect($data['invoices'])->pluck('invoice_number')->implode(',');
|
||||
$description = "Invoices: {$invoice_numbers} for {$data['total']['amount_with_fee']} for client {$this->stripe->client->present()->name()}";
|
||||
|
||||
|
||||
$payment_intent_data = [
|
||||
|
@ -75,7 +75,6 @@ class ACH
|
||||
|
||||
$message = [
|
||||
'server_response' => $e->getMessage(),
|
||||
'data' => $this->wepay_payment_driver->payment_hash->data,
|
||||
];
|
||||
|
||||
SystemLogger::dispatch(
|
||||
|
@ -148,6 +148,7 @@ class ApplyPayment
|
||||
|
||||
if ((int)$this->invoice->balance == 0) {
|
||||
$this->invoice->service()->deletePdf();
|
||||
$this->invoice = $this->invoice->fresh();
|
||||
event(new InvoiceWasPaid($this->invoice, $this->payment, $this->payment->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
|
||||
}
|
||||
}
|
||||
|
@ -47,7 +47,7 @@ class AutoBillInvoice extends AbstractService
|
||||
|
||||
MultiDB::setDb($this->db);
|
||||
|
||||
$this->client = $this->invoice->client;
|
||||
$this->client = $this->invoice->client->fresh();
|
||||
|
||||
$is_partial = false;
|
||||
|
||||
@ -178,14 +178,16 @@ class AutoBillInvoice extends AbstractService
|
||||
}
|
||||
|
||||
$payment->ledger()
|
||||
->updatePaymentBalance($amount * -1)
|
||||
->save();
|
||||
->updatePaymentBalance($amount * -1)
|
||||
->save();
|
||||
|
||||
$this->invoice->client->service()
|
||||
->updateBalance($amount * -1)
|
||||
->updatePaidToDate($amount)
|
||||
->adjustCreditBalance($amount * -1)
|
||||
->save();
|
||||
$client = $this->invoice->client->fresh();
|
||||
|
||||
$client->service()
|
||||
->updateBalance($amount * -1)
|
||||
->updatePaidToDate($amount)
|
||||
->adjustCreditBalance($amount * -1)
|
||||
->save();
|
||||
|
||||
$this->invoice->ledger()
|
||||
->updateInvoiceBalance($amount * -1, "Invoice {$this->invoice->number} payment using Credit {$current_credit->number}")
|
||||
|
@ -41,6 +41,8 @@ class UpdateInvoicePayment
|
||||
|
||||
collect($paid_invoices)->each(function ($paid_invoice) use ($invoices) {
|
||||
|
||||
$client = $this->payment->client->fresh();
|
||||
|
||||
$invoice = $invoices->first(function ($inv) use ($paid_invoice) {
|
||||
return $paid_invoice->invoice_id == $inv->hashed_id;
|
||||
});
|
||||
@ -70,8 +72,7 @@ class UpdateInvoicePayment
|
||||
->ledger()
|
||||
->updatePaymentBalance($paid_amount * -1);
|
||||
|
||||
$this->payment
|
||||
->client
|
||||
$client
|
||||
->service()
|
||||
->updateBalance($paid_amount * -1)
|
||||
->updatePaidToDate($paid_amount)
|
||||
@ -95,10 +96,11 @@ class UpdateInvoicePayment
|
||||
|
||||
$invoices->each(function ($invoice) {
|
||||
|
||||
$invoice = $invoice->fresh();
|
||||
event(new InvoiceWasUpdated($invoice, $invoice->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
|
||||
|
||||
});
|
||||
|
||||
return $this->payment;
|
||||
return $this->payment->fresh();
|
||||
}
|
||||
}
|
||||
|
@ -16,9 +16,13 @@ use App\Factory\CreditFactory;
|
||||
use App\Factory\InvoiceFactory;
|
||||
use App\Factory\InvoiceToRecurringInvoiceFactory;
|
||||
use App\Factory\RecurringInvoiceFactory;
|
||||
use App\Jobs\Mail\NinjaMailer;
|
||||
use App\Jobs\Mail\NinjaMailerJob;
|
||||
use App\Jobs\Mail\NinjaMailerObject;
|
||||
use App\Jobs\Util\SubscriptionWebhookHandler;
|
||||
use App\Jobs\Util\SystemLogger;
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Mail\RecurringInvoice\ClientContactRequestCancellationObject;
|
||||
use App\Models\Client;
|
||||
use App\Models\ClientContact;
|
||||
use App\Models\Credit;
|
||||
@ -36,6 +40,7 @@ use App\Services\Subscription\ZeroCostProduct;
|
||||
use App\Utils\Ninja;
|
||||
use App\Utils\Traits\CleanLineItems;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use App\Utils\Traits\Notifications\UserNotifies;
|
||||
use App\Utils\Traits\SubscriptionHooker;
|
||||
use Carbon\Carbon;
|
||||
use GuzzleHttp\RequestOptions;
|
||||
@ -46,6 +51,7 @@ class SubscriptionService
|
||||
use MakesHash;
|
||||
use CleanLineItems;
|
||||
use SubscriptionHooker;
|
||||
use UserNotifies;
|
||||
|
||||
/** @var subscription */
|
||||
private $subscription;
|
||||
@ -102,7 +108,7 @@ class SubscriptionService
|
||||
}
|
||||
else
|
||||
{
|
||||
$invoice = Invoice::find($payment_hash->fee_invoice_id);
|
||||
$invoice = Invoice::withTrashed()->find($payment_hash->fee_invoice_id);
|
||||
|
||||
$context = [
|
||||
'context' => 'single_purchase',
|
||||
@ -934,6 +940,29 @@ class SubscriptionService
|
||||
|
||||
$this->triggerWebhook($context);
|
||||
|
||||
$nmo = new NinjaMailerObject;
|
||||
$nmo->mailable = (new NinjaMailer((new ClientContactRequestCancellationObject($recurring_invoice, auth()->guard('contact')->user()))->build()));
|
||||
$nmo->company = $recurring_invoice->company;
|
||||
$nmo->settings = $recurring_invoice->company->settings;
|
||||
|
||||
$recurring_invoice->company->company_users->each(function ($company_user) use ($nmo){
|
||||
|
||||
$methods = $this->findCompanyUserNotificationType($company_user, ['recurring_cancellation', 'all_notifications']);
|
||||
|
||||
//if mail is a method type -fire mail!!
|
||||
if (($key = array_search('mail', $methods)) !== false) {
|
||||
unset($methods[$key]);
|
||||
|
||||
$nmo->to_user = $company_user->user;
|
||||
NinjaMailerJob::dispatch($nmo);
|
||||
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
return $this->handleRedirect('client/subscriptions');
|
||||
|
||||
}
|
||||
|
@ -26,12 +26,12 @@
|
||||
],
|
||||
"type": "project",
|
||||
"require": {
|
||||
"php": "^7.4|^8",
|
||||
"php": "^7.4|^8.0",
|
||||
"ext-dom": "*",
|
||||
"ext-json": "*",
|
||||
"ext-libxml": "*",
|
||||
"afosto/yaac": "^1.4",
|
||||
"asm/php-ansible": "dev-main",
|
||||
"asm/php-ansible": "^3",
|
||||
"authorizenet/authorizenet": "^2.0",
|
||||
"bacon/bacon-qr-code": "^2.0",
|
||||
"beganovich/snappdf": "^1.7",
|
||||
|
1168
composer.lock
generated
1168
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@ -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.64',
|
||||
'app_tag' => '5.3.64',
|
||||
'app_version' => '5.3.65',
|
||||
'app_tag' => '5.3.65',
|
||||
'minimum_client_version' => '5.0.16',
|
||||
'terms_version' => '1.0.1',
|
||||
'api_secret' => env('API_SECRET', ''),
|
||||
|
6
public/flutter_service_worker.js
vendored
6
public/flutter_service_worker.js
vendored
@ -28,11 +28,11 @@ const RESOURCES = {
|
||||
"assets/NOTICES": "9a4bf0423a5e265f38c4df37f7a0a913",
|
||||
"assets/fonts/MaterialIcons-Regular.otf": "7e7a6cccddf6d7b20012a548461d5d81",
|
||||
"favicon.ico": "51636d3a390451561744c42188ccd628",
|
||||
"/": "5b6588d4983a2540f5d0da10ef512a78",
|
||||
"version.json": "a00481850d5c63ba5df4e22636643438",
|
||||
"/": "7c3ca4e7f4b430421585744bdd926f67",
|
||||
"version.json": "443986d36b3df952ad780139ecccd516",
|
||||
"icons/Icon-512.png": "0f9aff01367f0a0c69773d25ca16ef35",
|
||||
"icons/Icon-192.png": "bb1cf5f6982006952211c7c8404ffbed",
|
||||
"main.dart.js": "8be39c52c4228fcf411da26c2517a432",
|
||||
"main.dart.js": "c0d886e3bce647afe312a4cc2853c08f",
|
||||
"favicon.png": "dca91c54388f52eded692718d5a98b8b",
|
||||
"manifest.json": "ef43d90e57aa7682d7e2cfba2f484a40",
|
||||
"canvaskit/profiling/canvaskit.js": "ae2949af4efc61d28a4a80fffa1db900",
|
||||
|
2
public/js/clients/payments/stripe-sepa.js
vendored
2
public/js/clients/payments/stripe-sepa.js
vendored
File diff suppressed because one or more lines are too long
97662
public/main.dart.js
vendored
97662
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
83234
public/main.foss.dart.js
vendored
83234
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
95150
public/main.html.dart.js
vendored
95150
public/main.html.dart.js
vendored
File diff suppressed because one or more lines are too long
81784
public/main.next.dart.js
vendored
81784
public/main.next.dart.js
vendored
File diff suppressed because one or more lines are too long
5687
public/main.profile.dart.js
vendored
5687
public/main.profile.dart.js
vendored
File diff suppressed because one or more lines are too long
@ -27,7 +27,7 @@
|
||||
"/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=4ed4c8a09803ddd0a9a7",
|
||||
"/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=da7b16ffaf5645535c7c",
|
||||
"/js/clients/payments/stripe-sepa.js": "/js/clients/payments/stripe-sepa.js?id=9134495bcbfdd3e25aef",
|
||||
"/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=2a973971ed2b890524ee",
|
||||
"/js/clients/payments/stripe-acss.js": "/js/clients/payments/stripe-acss.js?id=41367f4e80e52a0ab436",
|
||||
|
@ -1 +1 @@
|
||||
{"app_name":"invoiceninja_flutter","version":"5.0.76","build_number":"76","package_name":"invoiceninja_flutter"}
|
||||
{"app_name":"invoiceninja_flutter","version":"5.0.77","build_number":"77","package_name":"invoiceninja_flutter"}
|
6
resources/js/clients/payments/stripe-sepa.js
vendored
6
resources/js/clients/payments/stripe-sepa.js
vendored
@ -97,8 +97,6 @@ class ProcessSEPA {
|
||||
|
||||
document.getElementById('pay-now').addEventListener('click', (e) => {
|
||||
|
||||
console.log(document.querySelector('input[name=token]').value);
|
||||
|
||||
if (document.querySelector('input[name=token]').value.length !== 0) {
|
||||
|
||||
document.getElementById('pay-now').disabled = true;
|
||||
@ -199,6 +197,10 @@ class ProcessSEPA {
|
||||
tokenBillingCheckbox.value;
|
||||
}
|
||||
|
||||
if(document.querySelector('input[name=token]').value.length > 2){
|
||||
document.querySelector('input[name="store_card"]').value = false;
|
||||
}
|
||||
|
||||
document.getElementById('server-response').submit();
|
||||
}
|
||||
|
||||
|
@ -4566,6 +4566,9 @@ $LANG = array(
|
||||
'upgrade_to_add_company' => 'Upgrade your plan to add companies',
|
||||
'file_saved_in_downloads_folder' => 'The file has been saved in the downloads folder',
|
||||
'small' => 'Small',
|
||||
'quotes_backup_subject' => 'Your quotes are ready for download',
|
||||
'credits_backup_subject' => 'Your credits are ready for download',
|
||||
|
||||
);
|
||||
|
||||
return $LANG;
|
||||
|
10
resources/views/email/admin/download_credits.blade.php
Normal file
10
resources/views/email/admin/download_credits.blade.php
Normal file
@ -0,0 +1,10 @@
|
||||
@component('email.template.admin', ['logo' => $logo, 'settings' => $settings])
|
||||
<div class="center">
|
||||
<h1>{{ ctrans('texts.credits_backup_subject') }}</h1>
|
||||
<p>{{ ctrans('texts.download_timeframe') }}</p>
|
||||
|
||||
<a target="_blank" class="button" href="{{ $url }}">
|
||||
{{ ctrans('texts.download') }}
|
||||
</a>
|
||||
</div>
|
||||
@endcomponent
|
@ -0,0 +1,5 @@
|
||||
{!! ctrans('texts.credits_backup_subject') !!}
|
||||
|
||||
{!! ctrans('texts.download_timeframe') !!}
|
||||
|
||||
{!! $url !!}
|
@ -0,0 +1,5 @@
|
||||
{!! ctrans('texts.download_backup_subject') !!}
|
||||
|
||||
{!! ctrans('texts.download_timeframe') !!}
|
||||
|
||||
{!! $url !!}
|
@ -0,0 +1,5 @@
|
||||
{!! ctrans('texts.invoices_backup_subject') !!}
|
||||
|
||||
{!! ctrans('texts.download_timeframe') !!}
|
||||
|
||||
{!! $url !!}
|
10
resources/views/email/admin/download_quotes.blade.php
Normal file
10
resources/views/email/admin/download_quotes.blade.php
Normal file
@ -0,0 +1,10 @@
|
||||
@component('email.template.admin', ['logo' => $logo, 'settings' => $settings])
|
||||
<div class="center">
|
||||
<h1>{{ ctrans('texts.quotes_backup_subject') }}</h1>
|
||||
<p>{{ ctrans('texts.download_timeframe') }}</p>
|
||||
|
||||
<a target="_blank" class="button" href="{{ $url }}">
|
||||
{{ ctrans('texts.download') }}
|
||||
</a>
|
||||
</div>
|
||||
@endcomponent
|
@ -0,0 +1,5 @@
|
||||
{!! ctrans('texts.quotes_backup_subject') !!}
|
||||
|
||||
{!! ctrans('texts.download_timeframe') !!}
|
||||
|
||||
{!! $url !!}
|
9
resources/views/email/admin/generic_text.blade.php
Normal file
9
resources/views/email/admin/generic_text.blade.php
Normal file
@ -0,0 +1,9 @@
|
||||
{!! $title !!}
|
||||
|
||||
@isset($body)
|
||||
{!! $body !!}
|
||||
@endisset
|
||||
|
||||
@isset($content)
|
||||
{!! $content !!}
|
||||
@endisset
|
3
resources/views/email/admin/user_added_text.blade.php
Normal file
3
resources/views/email/admin/user_added_text.blade.php
Normal file
@ -0,0 +1,3 @@
|
||||
{!! $title !!}
|
||||
|
||||
{!! $body !!}
|
5
resources/views/email/admin/verify_user_text.blade.php
Normal file
5
resources/views/email/admin/verify_user_text.blade.php
Normal file
@ -0,0 +1,5 @@
|
||||
{!! $title !!}
|
||||
|
||||
{!! ctrans('texts.confirmation_message') !!}
|
||||
|
||||
{!! $url !!}
|
@ -0,0 +1,6 @@
|
||||
{!! ctrans('texts.login_link_requested_label') !!}
|
||||
|
||||
{!! ctrans('texts.login_link_requested') !!}
|
||||
|
||||
{!! $url !!}
|
||||
|
@ -0,0 +1,5 @@
|
||||
{!! ctrans('texts.ach_verification_notification_label') !!}
|
||||
|
||||
{!! ctrans('texts.ach_verification_notification') !!}
|
||||
|
||||
{!! $url !!}
|
73
resources/views/email/import/completed_text.blade.php
Normal file
73
resources/views/email/import/completed_text.blade.php
Normal file
@ -0,0 +1,73 @@
|
||||
Hello, here is the output of your recent import job.
|
||||
|
||||
If your logo imported correctly it will available display below. If it didn't import, you'll need to reupload your logo
|
||||
|
||||
{!! $company->present()->logo() !!}
|
||||
|
||||
@if(isset($company) && $company->clients->count() >=1)
|
||||
{!! ctrans('texts.clients') !!}: {!! $company->clients->count() !!}
|
||||
@endif
|
||||
|
||||
@if(isset($company) && count($company->products) >=1)
|
||||
{!! ctrans('texts.products') !!}: {!! count($company->products) !!}
|
||||
@endif
|
||||
|
||||
@if(isset($company) && count($company->invoices) >=1)
|
||||
{!! ctrans('texts.invoices') !!}: {!! count($company->invoices) !!}
|
||||
@endif
|
||||
|
||||
@if(isset($company) && count($company->payments) >=1)
|
||||
{!! ctrans('texts.payments') !!}: {!! count($company->payments) !!}
|
||||
@endif
|
||||
|
||||
@if(isset($company) && count($company->recurring_invoices) >=1)
|
||||
{!! ctrans('texts.recurring_invoices') !!}: {!! count($company->recurring_invoices) !!}
|
||||
@endif
|
||||
|
||||
@if(isset($company) && count($company->quotes) >=1)
|
||||
{!! ctrans('texts.quotes') !!}: {!! count($company->quotes) !!}
|
||||
@endif
|
||||
|
||||
@if(isset($company) && count($company->credits) >=1)
|
||||
{!! ctrans('texts.credits') !!}: {!! count($company->credits) !!}
|
||||
@endif
|
||||
|
||||
@if(isset($company) && count($company->projects) >=1)
|
||||
{!! ctrans('texts.projects') !!}: {!! count($company->projects) !!}
|
||||
@endif
|
||||
|
||||
@if(isset($company) && count($company->tasks) >=1)
|
||||
{!! ctrans('texts.tasks') !!}: {!! count($company->tasks) !!}
|
||||
@endif
|
||||
|
||||
@if(isset($company) && count($company->vendors) >=1)
|
||||
{!! ctrans('texts.vendors') !!}: {!! count($company->vendors) !!}
|
||||
@endif
|
||||
|
||||
@if(isset($company) && count($company->expenses) >=1)
|
||||
{!! ctrans('texts.expenses') !!}: {!! count($company->expenses) !!}
|
||||
@endif
|
||||
|
||||
@if(isset($company) && count($company->company_gateways) >=1)
|
||||
{!! ctrans('texts.gateways') !!}: {!! count($company->company_gateways) !!}
|
||||
@endif
|
||||
|
||||
@if(isset($company) && count($company->client_gateway_tokens) >=1)
|
||||
{!! ctrans('texts.tokens') !!}: {!! count($company->client_gateway_tokens) !!}
|
||||
@endif
|
||||
|
||||
@if(isset($company) && count($company->tax_rates) >=1)
|
||||
{!! ctrans('texts.tax_rates') !!}: {!! count($company->tax_rates) !!}
|
||||
@endif
|
||||
|
||||
@if(isset($company) && count($company->documents) >=1)
|
||||
{!! ctrans('texts.documents') !!}: {!! count($company->documents) !!}
|
||||
@endif
|
||||
|
||||
{!! url('/') !!}
|
||||
|
||||
{!! ctrans('texts.email_signature') !!}
|
||||
|
||||
{!! ctrans('texts.email_from') !!}
|
||||
|
||||
|
@ -0,0 +1,7 @@
|
||||
{!! $title !!}
|
||||
|
||||
{!! ctrans('texts.company_import_failure_body') !!}
|
||||
|
||||
@if(isset($whitelabel) && !$whitelabel)
|
||||
{{ ctrans('texts.ninja_email_footer', ['site' => 'https://invoiceninja.com']) }}
|
||||
@endif
|
3
resources/views/email/migration/existing_text.blade.php
Normal file
3
resources/views/email/migration/existing_text.blade.php
Normal file
@ -0,0 +1,3 @@
|
||||
{!! ctrans('texts.migration_already_completed') !!}
|
||||
|
||||
{!! ctrans('texts.migration_already_completed_desc', ['company_name' => $company_name]) !!}
|
11
resources/views/email/migration/failed_text.blade
Normal file
11
resources/views/email/migration/failed_text.blade
Normal file
@ -0,0 +1,11 @@
|
||||
{!! ctrans('texts.migration_failed_label') !!}
|
||||
|
||||
{!! ctrans('texts.migration_failed') }} {{ $company->present()->name() !!}
|
||||
|
||||
@if(\App\Utils\Ninja::isSelfHost() || $is_system)
|
||||
{!! $exception->getMessage() !!}
|
||||
{!! $content !!}
|
||||
@else
|
||||
Please contact us at contact@invoiceninja.com for more information on this error.
|
||||
@endif
|
||||
|
@ -177,9 +177,6 @@
|
||||
</p>
|
||||
@endif
|
||||
</div>
|
||||
@if(isset($unsubscribe_link))
|
||||
<p><a href="{{$unsubscribe_link}}">{{ ctrans('texts.unsubscribe') }}</a></p>
|
||||
@endif
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
@ -5,7 +5,7 @@
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<title>Invoice Ninja</title>
|
||||
<title></title>
|
||||
</head>
|
||||
|
||||
<div id="content-wrapper">
|
||||
|
7
resources/views/email/template/text.blade.php
Normal file
7
resources/views/email/template/text.blade.php
Normal file
@ -0,0 +1,7 @@
|
||||
{!! $text_body !!}
|
||||
@isset($whitelabel)
|
||||
@if(!$whitelabel)
|
||||
|
||||
{{ ctrans('texts.ninja_email_footer', ['site' => 'https://invoiceninja.com']) }}
|
||||
@endif
|
||||
@endisset
|
Loading…
x
Reference in New Issue
Block a user