mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2024-11-16 16:13:20 +01:00
commit
9ea175eb74
2
LICENSE
2
LICENSE
@ -45,3 +45,5 @@ AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
|
||||
IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
For more information regarding the interpretation of this license please see here: https://invoiceninja.github.io/docs/legal/license/
|
@ -1 +1 @@
|
||||
5.2.1
|
||||
5.2.2
|
61
app/Console/Commands/HostedUsers.php
Normal file
61
app/Console/Commands/HostedUsers.php
Normal file
@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\ClientContact;
|
||||
use App\Models\Company;
|
||||
use App\Models\User;
|
||||
use App\Utils\Ninja;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class HostedUsers extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'ninja:sync-users';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Syncs Invoice Ninja Users';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
|
||||
Company::on('db-ninja-01')->each(function ($company){
|
||||
|
||||
if(Ninja::isHosted())
|
||||
\Modules\Admin\Jobs\Account\NinjaUser::dispatchNow([], $company);
|
||||
|
||||
});
|
||||
|
||||
Company::on('db-ninja-02')->each(function ($company){
|
||||
|
||||
if(Ninja::isHosted())
|
||||
\Modules\Admin\Jobs\Account\NinjaUser::dispatchNow([], $company);
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
}
|
71
app/Console/Commands/S3Cleanup.php
Normal file
71
app/Console/Commands/S3Cleanup.php
Normal file
@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\Company;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class S3Cleanup extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'ninja:s3-cleanup';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Remove orphan folders';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
|
||||
$c1 = Company::on('db-ninja-01')->pluck('company_key');
|
||||
$c2 = Company::on('db-ninja-02')->pluck('company_key');
|
||||
|
||||
$merged = $c1->merge($c2)->toArray();
|
||||
|
||||
$directories = Storage::disk(config('filesystems.default'))->directories();
|
||||
|
||||
$this->LogMessage("Disk Cleanup");
|
||||
|
||||
foreach($directories as $dir)
|
||||
{
|
||||
if(!in_array($dir, $merged))
|
||||
{
|
||||
$this->logMessage("Deleting $dir");
|
||||
Storage::disk(config('filesystems.default'))->deleteDirectory($dir);
|
||||
}
|
||||
}
|
||||
|
||||
$this->logMessage("exiting");
|
||||
|
||||
}
|
||||
|
||||
private function logMessage($str)
|
||||
{
|
||||
$str = date('Y-m-d h:i:s').' '.$str;
|
||||
$this->info($str);
|
||||
$this->log .= $str."\n";
|
||||
}
|
||||
}
|
@ -74,14 +74,12 @@ class GmailTransport extends Transport
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
$this->gmail->send();
|
||||
|
||||
$this->sendPerformed($message);
|
||||
|
||||
|
||||
return $this->numberOfRecipients($message);
|
||||
}
|
||||
}
|
||||
|
@ -1,47 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
|
||||
namespace App\Helpers\Mail;
|
||||
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Mail\SupportMessageSent;
|
||||
use App\Models\User;
|
||||
use App\Providers\MailServiceProvider;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use Laravel\Socialite\Facades\Socialite;
|
||||
|
||||
/**
|
||||
* GmailTransportConfig.
|
||||
*/
|
||||
class GmailTransportConfig
|
||||
{
|
||||
public function test()
|
||||
{
|
||||
/********************* We may need to fetch a new token on behalf of the client ******************************/
|
||||
$query = [
|
||||
'email' => 'david@invoiceninja.com',
|
||||
];
|
||||
|
||||
$user = MultiDB::hasUser($query);
|
||||
// $oauth_user = Socialite::driver('google')->stateless()->userFromToken($user->oauth_user_token);
|
||||
|
||||
// $user->oauth_user_token = $oauth_user->refreshToken;
|
||||
// $user->save();
|
||||
|
||||
Config::set('mail.driver', 'gmail');
|
||||
Config::set('services.gmail.token', $user->oauth_user_token);
|
||||
(new MailServiceProvider(app()))->register();
|
||||
|
||||
Mail::to('david@romulus.com.au')
|
||||
->send(new SupportMessageSent('a cool message'));
|
||||
}
|
||||
}
|
@ -222,14 +222,9 @@ class LoginController extends BaseController
|
||||
|
||||
});
|
||||
|
||||
// $cu->first()->account->companies->each(function ($company) use($cu, $request){
|
||||
|
||||
// if($company->tokens()->where('is_system', true)->count() == 0)
|
||||
// {
|
||||
// CreateCompanyToken::dispatchNow($company, $cu->first()->user, $request->server('HTTP_USER_AGENT'));
|
||||
// }
|
||||
|
||||
// });
|
||||
/*On the hosted platform, only owners can login for free/pro accounts*/
|
||||
if(Ninja::isHosted() && !$cu->first()->is_owner && !$user->account->isEnterpriseClient())
|
||||
return response()->json(['message' => 'Pro / Free accounts only the owner can log in. Please upgrade'], 403);
|
||||
|
||||
return $this->timeConstrainedResponse($cu);
|
||||
|
||||
@ -318,6 +313,9 @@ class LoginController extends BaseController
|
||||
if($request->has('current_company') && $request->input('current_company') == 'true')
|
||||
$cu->where("company_id", $company_token->company_id);
|
||||
|
||||
if(Ninja::isHosted() && !$cu->first()->is_owner && !$cu->first()->user->account->isEnterpriseClient())
|
||||
return response()->json(['message' => 'Pro / Free accounts only the owner can log in. Please upgrade'], 403);
|
||||
|
||||
return $this->refreshResponse($cu);
|
||||
}
|
||||
|
||||
@ -379,6 +377,9 @@ class LoginController extends BaseController
|
||||
}
|
||||
});
|
||||
|
||||
if(Ninja::isHosted() && !$cu->first()->is_owner && !$existing_user->account->isEnterpriseClient())
|
||||
return response()->json(['message' => 'Pro / Free accounts only the owner can log in. Please upgrade'], 403);
|
||||
|
||||
return $this->timeConstrainedResponse($cu);
|
||||
|
||||
}
|
||||
@ -407,6 +408,9 @@ class LoginController extends BaseController
|
||||
}
|
||||
});
|
||||
|
||||
if(Ninja::isHosted() && !$cu->first()->is_owner && !$existing_login_user->account->isEnterpriseClient())
|
||||
return response()->json(['message' => 'Pro / Free accounts only the owner can log in. Please upgrade'], 403);
|
||||
|
||||
return $this->timeConstrainedResponse($cu);
|
||||
}
|
||||
|
||||
@ -439,6 +443,9 @@ class LoginController extends BaseController
|
||||
}
|
||||
});
|
||||
|
||||
if(Ninja::isHosted() && !$cu->first()->is_owner && !$existing_login_user->account->isEnterpriseClient())
|
||||
return response()->json(['message' => 'Pro / Free accounts only the owner can log in. Please upgrade'], 403);
|
||||
|
||||
return $this->timeConstrainedResponse($cu);
|
||||
}
|
||||
|
||||
@ -478,6 +485,9 @@ class LoginController extends BaseController
|
||||
}
|
||||
});
|
||||
|
||||
if(Ninja::isHosted() && !$cu->first()->is_owner && !auth()->user()->account->isEnterpriseClient())
|
||||
return response()->json(['message' => 'Pro / Free accounts only the owner can log in. Please upgrade'], 403);
|
||||
|
||||
return $this->timeConstrainedResponse($cu);
|
||||
}
|
||||
|
||||
|
@ -164,8 +164,9 @@ class InvoiceController extends Controller
|
||||
|
||||
//if only 1 pdf, output to buffer for download
|
||||
if ($invoices->count() == 1) {
|
||||
|
||||
$file = $invoices->first()->pdf_file_path();
|
||||
$invoice = $invoices->first();
|
||||
$invitation = $invoice->invitations->first();
|
||||
$file = $invoice->pdf_file_path($invitation);
|
||||
return response()->download($file, basename($file), ['Cache-Control:' => 'no-cache'])->deleteFileAfterSend(true);;
|
||||
|
||||
}
|
||||
|
@ -564,7 +564,7 @@ class CreditController extends BaseController
|
||||
// EmailCredit::dispatch($credit, $credit->company);
|
||||
|
||||
$credit->invitations->load('contact.client.country', 'credit.client.country', 'credit.company')->each(function ($invitation) use ($credit) {
|
||||
EmailEntity::dispatch($invitation, $credit->company, 'credit')->delay(now()->addSeconds(60));
|
||||
EmailEntity::dispatch($invitation, $credit->company, 'credit');
|
||||
});
|
||||
|
||||
|
||||
|
@ -132,7 +132,7 @@ class EmailController extends BaseController
|
||||
$entity_obj->service()->markSent()->save();
|
||||
|
||||
EmailEntity::dispatch($invitation->fresh(), $invitation->company, $template, $data)
|
||||
->delay(now()->addSeconds(60));
|
||||
->delay(now()->addSeconds(30));
|
||||
|
||||
}
|
||||
|
||||
|
@ -49,7 +49,7 @@ class StoreCompanyRequest extends Request
|
||||
} else {
|
||||
|
||||
if(Ninja::isHosted()){
|
||||
$rules['subdomain'] = ['nullable', 'alpha_num', new ValidSubdomain($this->all())];
|
||||
$rules['subdomain'] = ['nullable', 'regex:/^[a-zA-Z0-9][a-zA-Z0-9.-]+[a-zA-Z0-9]$/', new ValidSubdomain($this->all())];
|
||||
}
|
||||
else
|
||||
$rules['subdomain'] = 'nullable|alpha_num';
|
||||
|
@ -50,7 +50,7 @@ class UpdateCompanyRequest extends Request
|
||||
} else {
|
||||
|
||||
if(Ninja::isHosted()){
|
||||
$rules['subdomain'] = ['nullable', 'alpha_num', new ValidSubdomain($this->all())];
|
||||
$rules['subdomain'] = ['nullable', 'regex:/^[a-zA-Z0-9][a-zA-Z0-9.-]+[a-zA-Z0-9]$/', new ValidSubdomain($this->all())];
|
||||
}
|
||||
else
|
||||
$rules['subdomain'] = 'nullable|alpha_num';
|
||||
|
@ -104,7 +104,10 @@ class CreateAccount
|
||||
//todo implement SLACK notifications
|
||||
//$sp035a66->notification(new NewAccountCreated($spaa9f78, $sp035a66))->ninja();
|
||||
|
||||
VersionCheck::dispatchNow();
|
||||
if(Ninja::isHosted())
|
||||
\Modules\Admin\Jobs\Account\NinjaUser::dispatch([], $sp035a66);
|
||||
|
||||
VersionCheck::dispatch();
|
||||
|
||||
LightLogs::create(new AnalyticsAccountCreated())
|
||||
->increment()
|
||||
@ -118,10 +121,6 @@ class CreateAccount
|
||||
if(Ninja::isHosted() && Cache::get('currencies'))
|
||||
{
|
||||
|
||||
//&& $data = unserialize(@file_get_contents('http://www.geoplugin.net/php.gp?ip=' . $this->client_ip))
|
||||
// $currency_code = strtolower($data['geoplugin_currencyCode']);
|
||||
// $country_code = strtolower($data['geoplugin_countryCode']);
|
||||
|
||||
$currency = Cache::get('currencies')->filter(function ($item) use ($currency_code) {
|
||||
return strtolower($item->code) == $currency_code;
|
||||
})->first();
|
||||
@ -146,8 +145,6 @@ class CreateAccount
|
||||
$settings->language_id = (string)$language->id;
|
||||
}
|
||||
|
||||
//$timezone = Timezone::where('name', $data['geoplugin_timezone'])->first();
|
||||
|
||||
if($timezone) {
|
||||
$settings->timezone_id = (string)$timezone->id;
|
||||
}
|
||||
|
@ -480,7 +480,11 @@ class CompanyExport implements ShouldQueue
|
||||
|
||||
$file_name = date('Y-m-d').'_'.str_replace(' ', '_', $this->company->present()->name() . '_' . $this->company->company_key .'.zip');
|
||||
|
||||
Storage::makeDirectory(public_path('storage/backups/'), 0775);
|
||||
$path = public_path('storage/backups/');
|
||||
|
||||
if(!Storage::exists($path))
|
||||
Storage::makeDirectory($path, 0775);
|
||||
|
||||
$zip_path = public_path('storage/backups/'.$file_name);
|
||||
$zip = new \ZipArchive();
|
||||
|
||||
|
@ -219,7 +219,7 @@ class CompanyImport implements ShouldQueue
|
||||
if(count($backup_users) > 1){
|
||||
// $this->message = 'Only one user can be in the import for a Free Account';
|
||||
// $this->pre_flight_checks_pass = false;
|
||||
$this->force_user_coalesce = true;
|
||||
//$this->force_user_coalesce = true;
|
||||
}
|
||||
|
||||
nlog("backup users email = " . $backup_users[0]->email);
|
||||
@ -227,7 +227,7 @@ class CompanyImport implements ShouldQueue
|
||||
if(count($backup_users) == 1 && $this->company_owner->email != $backup_users[0]->email) {
|
||||
// $this->message = 'Account emails do not match. Account owner email must match backup user email';
|
||||
// $this->pre_flight_checks_pass = false;
|
||||
$this->force_user_coalesce = true;
|
||||
// $this->force_user_coalesce = true;
|
||||
}
|
||||
|
||||
$backup_users_emails = array_column($backup_users, 'email');
|
||||
@ -243,7 +243,7 @@ class CompanyImport implements ShouldQueue
|
||||
if($this->account->plan == 'pro'){
|
||||
// $this->message = 'Pro plan is limited to one user, you have multiple users in the backup file';
|
||||
// $this->pre_flight_checks_pass = false;
|
||||
$this->force_user_coalesce = true;
|
||||
// $this->force_user_coalesce = true;
|
||||
}
|
||||
|
||||
if($this->account->plan == 'enterprise'){
|
||||
|
@ -102,12 +102,11 @@ class CreateEntityPdf implements ShouldQueue
|
||||
/* Set the locale*/
|
||||
App::setLocale($this->contact->preferredLocale());
|
||||
|
||||
// nlog($this->entity->client->getMergedSettings());
|
||||
|
||||
/* Set customized translations _NOW_ */
|
||||
$t->replace(Ninja::transformTranslations($this->entity->client->getMergedSettings()));
|
||||
|
||||
$this->entity->service()->deletePdf();
|
||||
/*This line of code hurts... it deletes ALL $entity PDFs... this causes a race condition when trying to send an email*/
|
||||
// $this->entity->service()->deletePdf();
|
||||
|
||||
if (config('ninja.phantomjs_pdf_generation') || config('ninja.pdf_generator') == 'phantom') {
|
||||
return (new Phantom)->generate($this->invitation);
|
||||
@ -116,16 +115,16 @@ class CreateEntityPdf implements ShouldQueue
|
||||
$entity_design_id = '';
|
||||
|
||||
if ($this->entity instanceof Invoice) {
|
||||
$path = $this->entity->client->invoice_filepath();
|
||||
$path = $this->entity->client->invoice_filepath($this->invitation);
|
||||
$entity_design_id = 'invoice_design_id';
|
||||
} elseif ($this->entity instanceof Quote) {
|
||||
$path = $this->entity->client->quote_filepath();
|
||||
$path = $this->entity->client->quote_filepath($this->invitation);
|
||||
$entity_design_id = 'quote_design_id';
|
||||
} elseif ($this->entity instanceof Credit) {
|
||||
$path = $this->entity->client->credit_filepath();
|
||||
$path = $this->entity->client->credit_filepath($this->invitation);
|
||||
$entity_design_id = 'credit_design_id';
|
||||
} elseif ($this->entity instanceof RecurringInvoice) {
|
||||
$path = $this->entity->client->recurring_invoice_filepath();
|
||||
$path = $this->entity->client->recurring_invoice_filepath($this->invitation);
|
||||
$entity_design_id = 'invoice_design_id';
|
||||
}
|
||||
|
||||
@ -195,6 +194,11 @@ class CreateEntityPdf implements ShouldQueue
|
||||
|
||||
try{
|
||||
|
||||
if(!Storage::disk($this->disk)->exists($path))
|
||||
Storage::disk($this->disk)->makeDirectory($path, 0775);
|
||||
|
||||
nlog($file_path);
|
||||
|
||||
Storage::disk($this->disk)->put($file_path, $pdf);
|
||||
|
||||
}
|
||||
|
@ -78,13 +78,16 @@ class ZipInvoices implements ShouldQueue
|
||||
// create a new zipstream object
|
||||
$file_name = date('Y-m-d').'_'.str_replace(' ', '_', trans('texts.invoices')).'.zip';
|
||||
|
||||
$path = $this->invoices->first()->client->invoice_filepath();
|
||||
$invoice = $this->invoices->first();
|
||||
$invitation = $invoice->invitations->first();
|
||||
|
||||
$path = $invoice->client->invoice_filepath($invitation);
|
||||
|
||||
$zip = new ZipStream($file_name, $options);
|
||||
|
||||
foreach ($this->invoices as $invoice) {
|
||||
//$zip->addFileFromPath(basename($invoice->pdf_file_path()), TempFile::path($invoice->pdf_file_path()));
|
||||
$zip->addFileFromPath(basename($invoice->pdf_file_path()), $invoice->pdf_file_path());
|
||||
$zip->addFileFromPath(basename($invoice->pdf_file_path($invitation)), $invoice->pdf_file_path());
|
||||
}
|
||||
|
||||
$zip->finish();
|
||||
|
@ -213,7 +213,7 @@ class SendReminders implements ShouldQueue
|
||||
if ($this->checkSendSetting($invoice, $template) && $invoice->company->account->hasFeature(Account::FEATURE_EMAIL_TEMPLATES_REMINDERS)) {
|
||||
nlog("firing email");
|
||||
|
||||
EmailEntity::dispatchNow($invitation, $invitation->company, $template)->delay(now()->addSeconds(60));
|
||||
EmailEntity::dispatchNow($invitation, $invitation->company, $template);
|
||||
|
||||
}
|
||||
});
|
||||
|
@ -96,7 +96,7 @@ class SendRecurring implements ShouldQueue
|
||||
if ($invitation->contact && strlen($invitation->contact->email) >=1) {
|
||||
|
||||
try{
|
||||
EmailEntity::dispatch($invitation, $invoice->company)->delay(now()->addSeconds(60));
|
||||
EmailEntity::dispatch($invitation, $invoice->company);
|
||||
}
|
||||
catch(\Exception $e) {
|
||||
nlog($e->getMessage());
|
||||
|
@ -53,14 +53,18 @@ class ReminderJob implements ShouldQueue
|
||||
|
||||
private function processReminders()
|
||||
{
|
||||
Invoice::whereDate('next_send_date', '<=', now())->with('invitations')->cursor()->each(function ($invoice) {
|
||||
Invoice::whereDate('next_send_date', '<=', now())
|
||||
->where('is_deleted', 0)
|
||||
->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL])
|
||||
->where('balance', '>', 0)
|
||||
->with('invitations')->cursor()->each(function ($invoice) {
|
||||
|
||||
if ($invoice->isPayable()) {
|
||||
$reminder_template = $invoice->calculateTemplate('invoice');
|
||||
$invoice->service()->touchReminder($reminder_template)->save();
|
||||
|
||||
$invoice->invitations->each(function ($invitation) use ($invoice, $reminder_template) {
|
||||
EmailEntity::dispatch($invitation, $invitation->company, $reminder_template)->delay(now()->addSeconds(60));
|
||||
EmailEntity::dispatch($invitation, $invitation->company, $reminder_template);
|
||||
nlog("Firing reminder email for invoice {$invoice->number}");
|
||||
});
|
||||
|
||||
|
@ -64,7 +64,7 @@ class SendFailedEmails implements ShouldQueue
|
||||
|
||||
if ($invitation->invoice) {
|
||||
if ($invitation->contact->send_email && $invitation->contact->email) {
|
||||
EmailEntity::dispatch($invitation, $invitation->company, $job_meta_array['reminder_template'])->delay(now()->addSeconds(60));
|
||||
EmailEntity::dispatch($invitation, $invitation->company, $job_meta_array['reminder_template']);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -101,9 +101,9 @@ class CreditEmailEngine extends BaseEmailEngine
|
||||
if ($this->client->getSetting('pdf_email_attachment') !== false && $this->credit->company->account->hasFeature(Account::FEATURE_PDF_ATTACHMENT)) {
|
||||
|
||||
if(Ninja::isHosted())
|
||||
$this->setAttachments([$this->credit->pdf_file_path(null, 'url', true)]);
|
||||
$this->setAttachments([$this->credit->pdf_file_path($this->invitation, 'url', true)]);
|
||||
else
|
||||
$this->setAttachments([$this->credit->pdf_file_path()]);
|
||||
$this->setAttachments([$this->credit->pdf_file_path($this->invitation)]);
|
||||
|
||||
}
|
||||
|
||||
|
@ -112,9 +112,9 @@ class InvoiceEmailEngine extends BaseEmailEngine
|
||||
if ($this->client->getSetting('pdf_email_attachment') !== false && $this->invoice->company->account->hasFeature(Account::FEATURE_PDF_ATTACHMENT)) {
|
||||
|
||||
if(Ninja::isHosted())
|
||||
$this->setAttachments([$this->invoice->pdf_file_path(null, 'url', true)]);
|
||||
$this->setAttachments([$this->invoice->pdf_file_path($this->invitation, 'url', true)]);
|
||||
else
|
||||
$this->setAttachments([$this->invoice->pdf_file_path()]);
|
||||
$this->setAttachments([$this->invoice->pdf_file_path($this->invitation)]);
|
||||
|
||||
// $this->setAttachments(['path' => $this->invoice->pdf_file_path(), 'name' => basename($this->invoice->pdf_file_path())]);
|
||||
|
||||
|
@ -77,7 +77,7 @@ class PaymentEmailEngine extends BaseEmailEngine
|
||||
|
||||
$this->payment->invoices->each(function ($invoice){
|
||||
|
||||
$this->setAttachments([$invoice->pdf_file_path()]);
|
||||
$this->setAttachments([$invoice->pdf_file_path($invoice->invitations->first())]);
|
||||
|
||||
});
|
||||
|
||||
|
@ -103,9 +103,9 @@ class QuoteEmailEngine extends BaseEmailEngine
|
||||
if ($this->client->getSetting('pdf_email_attachment') !== false && $this->quote->company->account->hasFeature(Account::FEATURE_PDF_ATTACHMENT)) {
|
||||
|
||||
if(Ninja::isHosted())
|
||||
$this->setAttachments([$this->quote->pdf_file_path(null, 'url', true)]);
|
||||
$this->setAttachments([$this->quote->pdf_file_path($this->invitation, 'url', true)]);
|
||||
else
|
||||
$this->setAttachments([$this->quote->pdf_file_path()]);
|
||||
$this->setAttachments([$this->quote->pdf_file_path($this->invitation)]);
|
||||
|
||||
}
|
||||
|
||||
|
@ -41,9 +41,6 @@ class MigrationCompleted extends Mailable
|
||||
$result = $this->from(config('mail.from.address'), config('mail.from.name'))
|
||||
->view('email.import.completed', $data);
|
||||
|
||||
// if($this->company->invoices->count() >=1)
|
||||
// $result->attach($this->company->invoices->first()->pdf_file_path());
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
@ -638,24 +638,28 @@ class Client extends BaseModel implements HasLocalePreference
|
||||
})->first()->locale;
|
||||
}
|
||||
|
||||
public function invoice_filepath()
|
||||
public function invoice_filepath($invitation)
|
||||
{
|
||||
return $this->company->company_key.'/'.$this->client_hash.'/invoices/';
|
||||
$contact_key = $invitation->contact->contact_key;
|
||||
return $this->company->company_key.'/'.$this->client_hash.'/'.$contact_key.'/invoices/';
|
||||
}
|
||||
|
||||
public function quote_filepath()
|
||||
public function quote_filepath($invitation)
|
||||
{
|
||||
return $this->company->company_key.'/'.$this->client_hash.'/quotes/';
|
||||
$contact_key = $invitation->contact->contact_key;
|
||||
return $this->company->company_key.'/'.$this->client_hash.'/'.$contact_key.'/quotes/';
|
||||
}
|
||||
|
||||
public function credit_filepath()
|
||||
public function credit_filepath($invitation)
|
||||
{
|
||||
return $this->company->company_key.'/'.$this->client_hash.'/credits/';
|
||||
$contact_key = $invitation->contact->contact_key;
|
||||
return $this->company->company_key.'/'.$this->client_hash.'/'.$contact_key.'/credits/';
|
||||
}
|
||||
|
||||
public function recurring_invoice_filepath()
|
||||
public function recurring_invoice_filepath($invitation)
|
||||
{
|
||||
return $this->company->company_key.'/'.$this->client_hash.'/recurring_invoices/';
|
||||
$contact_key = $invitation->contact->contact_key;
|
||||
return $this->company->company_key.'/'.$this->client_hash.'/'.$contact_key.'/recurring_invoices/';
|
||||
}
|
||||
|
||||
public function company_filepath()
|
||||
|
@ -267,7 +267,7 @@ class Credit extends BaseModel
|
||||
if(!$invitation)
|
||||
throw new \Exception('Hard fail, could not create an invitation - is there a valid contact?');
|
||||
|
||||
$file_path = $this->client->credit_filepath().$this->numberFormatter().'.pdf';
|
||||
$file_path = $this->client->credit_filepath($invitation).$this->numberFormatter().'.pdf';
|
||||
|
||||
if(Ninja::isHosted() && $portal && Storage::disk(config('filesystems.default'))->exists($file_path)){
|
||||
return Storage::disk(config('filesystems.default'))->{$type}($file_path);
|
||||
|
@ -126,9 +126,9 @@ class CreditInvitation extends BaseModel
|
||||
|
||||
public function pdf_file_path()
|
||||
{
|
||||
$storage_path = Storage::url($this->credit->client->quote_filepath().$this->credit->numberFormatter().'.pdf');
|
||||
$storage_path = Storage::url($this->credit->client->quote_filepath($this).$this->credit->numberFormatter().'.pdf');
|
||||
|
||||
if (! Storage::exists($this->credit->client->credit_filepath().$this->credit->numberFormatter().'.pdf')) {
|
||||
if (! Storage::exists($this->credit->client->credit_filepath($this).$this->credit->numberFormatter().'.pdf')) {
|
||||
event(new CreditWasUpdated($this, $this->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
|
||||
CreateEntityPdf::dispatchNow($this);
|
||||
}
|
||||
|
@ -409,7 +409,7 @@ class Invoice extends BaseModel
|
||||
if(!$invitation)
|
||||
throw new \Exception('Hard fail, could not create an invitation - is there a valid contact?');
|
||||
|
||||
$file_path = $this->client->invoice_filepath().$this->numberFormatter().'.pdf';
|
||||
$file_path = $this->client->invoice_filepath($invitation).$this->numberFormatter().'.pdf';
|
||||
|
||||
if(Ninja::isHosted() && $portal && Storage::disk(config('filesystems.default'))->exists($file_path)){
|
||||
return Storage::disk(config('filesystems.default'))->{$type}($file_path);
|
||||
|
@ -142,7 +142,7 @@ class InvoiceInvitation extends BaseModel
|
||||
{
|
||||
$storage_path = Storage::url($this->invoice->client->invoice_filepath().$this->invoice->numberFormatter().'.pdf');
|
||||
|
||||
if (! Storage::exists($this->invoice->client->invoice_filepath().$this->invoice->numberFormatter().'.pdf')) {
|
||||
if (! Storage::exists($this->invoice->client->invoice_filepath($this).$this->invoice->numberFormatter().'.pdf')) {
|
||||
event(new InvoiceWasUpdated($this->invoice, $this->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
|
||||
CreateEntityPdf::dispatchNow($this);
|
||||
}
|
||||
|
@ -219,7 +219,7 @@ class Quote extends BaseModel
|
||||
if(!$invitation)
|
||||
throw new \Exception('Hard fail, could not create an invitation - is there a valid contact?');
|
||||
|
||||
$file_path = $this->client->quote_filepath().$this->numberFormatter().'.pdf';
|
||||
$file_path = $this->client->quote_filepath($invitation).$this->numberFormatter().'.pdf';
|
||||
|
||||
if(Ninja::isHosted() && $portal && Storage::disk(config('filesystems.default'))->exists($file_path)){
|
||||
return Storage::disk(config('filesystems.default'))->{$type}($file_path);
|
||||
|
@ -130,9 +130,9 @@ class QuoteInvitation extends BaseModel
|
||||
|
||||
public function pdf_file_path()
|
||||
{
|
||||
$storage_path = Storage::url($this->quote->client->quote_filepath().$this->quote->numberFormatter().'.pdf');
|
||||
$storage_path = Storage::url($this->quote->client->quote_filepath($this).$this->quote->numberFormatter().'.pdf');
|
||||
|
||||
if (! Storage::exists($this->quote->client->quote_filepath().$this->quote->numberFormatter().'.pdf')) {
|
||||
if (! Storage::exists($this->quote->client->quote_filepath($this).$this->quote->numberFormatter().'.pdf')) {
|
||||
event(new QuoteWasUpdated($this->quote, $this->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
|
||||
CreateEntityPdf::dispatchNow($this);
|
||||
}
|
||||
|
@ -52,11 +52,6 @@ class InvoiceObserver
|
||||
WebhookHandler::dispatch(Webhook::EVENT_UPDATE_INVOICE, $invoice, $invoice->company);
|
||||
}
|
||||
|
||||
// if($invoice->isDirty('date') || $invoice->isDirty('due_date'))
|
||||
// $invoice->service()->setReminder()->save();
|
||||
|
||||
// UnlinkFile::dispatchNow(config('filesystems.default'), $invoice->client->invoice_filepath() . $invoice->numberFormatter().'.pdf');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -58,7 +58,7 @@ class UpdatePaymentMethods
|
||||
|
||||
// }
|
||||
|
||||
private function updateMethods(Customer $customer, Client $client)
|
||||
public function updateMethods(Customer $customer, Client $client)
|
||||
{
|
||||
$card_methods = PaymentMethod::all([
|
||||
'customer' => $customer->id,
|
||||
@ -145,7 +145,7 @@ class UpdatePaymentMethods
|
||||
|
||||
}
|
||||
|
||||
private function buildPaymentMethodMeta(PaymentMethod $method, GatewayType $type_id)
|
||||
private function buildPaymentMethodMeta(PaymentMethod $method, $type_id)
|
||||
{
|
||||
|
||||
switch ($type_id) {
|
||||
|
@ -140,7 +140,11 @@ class CreditService
|
||||
|
||||
public function deletePdf()
|
||||
{
|
||||
UnlinkFile::dispatchNow(config('filesystems.default'), $this->credit->client->credit_filepath() . $this->credit->numberFormatter().'.pdf');
|
||||
$this->credit->invitations->each(function ($invitation){
|
||||
|
||||
UnlinkFile::dispatchNow(config('filesystems.default'), $this->credit->client->credit_filepath($invitation) . $this->credit->numberFormatter().'.pdf');
|
||||
|
||||
});
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
@ -37,7 +37,7 @@ class GetCreditPdf extends AbstractService
|
||||
$this->contact = $this->credit->client->primary_contact()->first();
|
||||
}
|
||||
|
||||
$path = $this->credit->client->credit_filepath();
|
||||
$path = $this->credit->client->credit_filepath($this->invitation);
|
||||
|
||||
$file_path = $path.$this->credit->numberFormatter().'.pdf';
|
||||
|
||||
|
@ -60,14 +60,15 @@ class GenerateDeliveryNote
|
||||
? $this->invoice->design_id
|
||||
: $this->decodePrimaryKey($this->invoice->client->getSetting('invoice_design_id'));
|
||||
|
||||
$file_path = sprintf('%s%s_delivery_note.pdf', $this->invoice->client->invoice_filepath(), $this->invoice->number);
|
||||
$invitation = $this->invoice->invitations->first();
|
||||
$file_path = sprintf('%s%s_delivery_note.pdf', $this->invoice->client->invoice_filepath($invitation), $this->invoice->number);
|
||||
|
||||
if (config('ninja.phantomjs_pdf_generation') || config('ninja.pdf_generator') == 'phantom') {
|
||||
return (new Phantom)->generate($this->invoice->invitations->first());
|
||||
}
|
||||
|
||||
$design = Design::find($design_id);
|
||||
$html = new HtmlEngine($this->invoice->invitations->first());
|
||||
$html = new HtmlEngine($invitation);
|
||||
|
||||
if ($design->is_custom) {
|
||||
$options = ['custom_partials' => json_decode(json_encode($design->design), true)];
|
||||
@ -105,6 +106,9 @@ class GenerateDeliveryNote
|
||||
info($maker->getCompiledHTML());
|
||||
}
|
||||
|
||||
if(!Storage::disk($this->disk)->exists($this->invoice->client->invoice_filepath($invitation)))
|
||||
Storage::disk($this->disk)->makeDirectory($this->invoice->client->invoice_filepath($invitation), 0775);
|
||||
|
||||
Storage::disk($this->disk)->put($file_path, $pdf);
|
||||
|
||||
return Storage::disk($this->disk)->path($file_path);
|
||||
|
@ -35,7 +35,7 @@ class GetInvoicePdf extends AbstractService
|
||||
|
||||
$invitation = $this->invoice->invitations->where('client_contact_id', $this->contact->id)->first();
|
||||
|
||||
$path = $this->invoice->client->invoice_filepath();
|
||||
$path = $this->invoice->client->invoice_filepath($invitation);
|
||||
|
||||
$file_path = $path.$this->invoice->numberFormatter().'.pdf';
|
||||
|
||||
|
@ -307,13 +307,16 @@ class InvoiceService
|
||||
|
||||
public function deletePdf()
|
||||
{
|
||||
//UnlinkFile::dispatchNow(config('filesystems.default'), $this->invoice->client->invoice_filepath() . $this->invoice->numberFormatter().'.pdf');
|
||||
Storage::disk(config('filesystems.default'))->delete($this->invoice->client->invoice_filepath() . $this->invoice->numberFormatter().'.pdf');
|
||||
$this->invoice->invitations->each(function ($invitation){
|
||||
|
||||
Storage::disk(config('filesystems.default'))->delete($this->invoice->client->invoice_filepath($invitation) . $this->invoice->numberFormatter().'.pdf');
|
||||
|
||||
if(Ninja::isHosted()) {
|
||||
Storage::disk('public')->delete($this->invoice->client->invoice_filepath() . $this->invoice->numberFormatter().'.pdf');
|
||||
Storage::disk('public')->delete($this->invoice->client->invoice_filepath($invitation) . $this->invoice->numberFormatter().'.pdf');
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@ -351,8 +354,17 @@ class InvoiceService
|
||||
* PDF when it is updated etc.
|
||||
* @return InvoiceService
|
||||
*/
|
||||
public function touchPdf()
|
||||
public function touchPdf($force = false)
|
||||
{
|
||||
if($force){
|
||||
|
||||
$this->invoice->invitations->each(function ($invitation) {
|
||||
CreateEntityPdf::dispatchNow($invitation);
|
||||
});
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
$this->invoice->invitations->each(function ($invitation) {
|
||||
CreateEntityPdf::dispatch($invitation);
|
||||
});
|
||||
@ -380,7 +392,8 @@ class InvoiceService
|
||||
$this->invoice->reminder_last_sent = now()->format('Y-m-d');
|
||||
break;
|
||||
default:
|
||||
// code...
|
||||
$this->invoice->reminder1_sent = now()->format('Y-m-d');
|
||||
$this->invoice->reminder_last_sent = now()->format('Y-m-d');
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -62,7 +62,7 @@ class TriggeredActions extends AbstractService
|
||||
$reminder_template = 'payment';
|
||||
|
||||
$this->invoice->invitations->load('contact.client.country', 'invoice.client.country', 'invoice.company')->each(function ($invitation) use ($reminder_template) {
|
||||
EmailEntity::dispatch($invitation, $this->invoice->company, $reminder_template)->delay(now()->addSeconds(60));
|
||||
EmailEntity::dispatch($invitation, $this->invoice->company, $reminder_template);
|
||||
});
|
||||
|
||||
if ($this->invoice->invitations->count() > 0) {
|
||||
|
@ -126,7 +126,10 @@ class UpdateReminder extends AbstractService
|
||||
$date_collection->push($reminder_date);
|
||||
}
|
||||
|
||||
if($date_collection->count() >=1)
|
||||
$this->invoice->next_send_date = $date_collection->sort()->first();
|
||||
else
|
||||
$this->invoice->next_send_date = null;
|
||||
|
||||
return $this->invoice;
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ class GetQuotePdf extends AbstractService
|
||||
|
||||
$invitation = $this->quote->invitations->where('client_contact_id', $this->contact->id)->first();
|
||||
|
||||
$path = $this->quote->client->quote_filepath();
|
||||
$path = $this->quote->client->quote_filepath($invitation);
|
||||
|
||||
$file_path = $path.$this->quote->numberFormatter().'.pdf';
|
||||
|
||||
|
@ -178,7 +178,11 @@ class QuoteService
|
||||
|
||||
public function deletePdf()
|
||||
{
|
||||
UnlinkFile::dispatchNow(config('filesystems.default'), $this->quote->client->quote_filepath() . $this->quote->numberFormatter().'.pdf');
|
||||
$this->quote->invitations->each(function ($invitation){
|
||||
|
||||
UnlinkFile::dispatchNow(config('filesystems.default'), $this->quote->client->quote_filepath($invitation) . $this->quote->numberFormatter().'.pdf');
|
||||
|
||||
});
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
@ -37,7 +37,7 @@ class GetInvoicePdf extends AbstractService
|
||||
|
||||
$invitation = $this->entity->invitations->where('client_contact_id', $this->contact->id)->first();
|
||||
|
||||
$path = $this->entity->client->recurring_invoice_filepath();
|
||||
$path = $this->entity->client->recurring_invoice_filepath($invitation);
|
||||
|
||||
$file_path = $path.$this->entity->hashed_id.'.pdf';
|
||||
|
||||
|
@ -87,7 +87,13 @@ class RecurringService
|
||||
|
||||
public function deletePdf()
|
||||
{
|
||||
UnlinkFile::dispatchNow(config('filesystems.default'), $this->recurring_entity->client->recurring_invoice_filepath() . $this->recurring_entity->numberFormatter().'.pdf');
|
||||
|
||||
$this->recurring_entity->invitations->each(function ($invitation){
|
||||
|
||||
UnlinkFile::dispatchNow(config('filesystems.default'), $this->recurring_entity->client->recurring_invoice_filepath($invitation) . $this->recurring_entity->numberFormatter().'.pdf');
|
||||
|
||||
});
|
||||
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
@ -181,6 +181,7 @@ class HtmlEngine
|
||||
$data['$amount_due'] = ['value' => &$data['$total']['value'], 'label' => ctrans('texts.amount_due')];
|
||||
$data['$quote.total'] = &$data['$total'];
|
||||
$data['$invoice.total'] = ['value' => Number::formatMoney($this->entity_calc->getTotal(), $this->client) ?: ' ', 'label' => ctrans('texts.invoice_total')];
|
||||
$data['$invoice_total_raw'] = ['value' => $this->entity_calc->getTotal(), 'label' => ctrans('texts.invoice_total')];
|
||||
$data['$invoice.amount'] = &$data['$total'];
|
||||
$data['$quote.amount'] = ['value' => Number::formatMoney($this->entity_calc->getTotal(), $this->client) ?: ' ', 'label' => ctrans('texts.quote_total')];
|
||||
$data['$credit.total'] = ['value' => Number::formatMoney($this->entity_calc->getTotal(), $this->client) ?: ' ', 'label' => ctrans('texts.credit_total')];
|
||||
|
@ -62,19 +62,19 @@ class Phantom
|
||||
$entity_obj = $invitation->{$entity};
|
||||
|
||||
if ($entity == 'invoice') {
|
||||
$path = $entity_obj->client->invoice_filepath();
|
||||
$path = $entity_obj->client->invoice_filepath($invitation);
|
||||
}
|
||||
|
||||
if ($entity == 'quote') {
|
||||
$path = $entity_obj->client->quote_filepath();
|
||||
$path = $entity_obj->client->quote_filepath($invitation);
|
||||
}
|
||||
|
||||
if ($entity == 'credit') {
|
||||
$path = $entity_obj->client->credit_filepath();
|
||||
$path = $entity_obj->client->credit_filepath($invitation);
|
||||
}
|
||||
|
||||
if ($entity == 'recurring_invoice') {
|
||||
$path = $entity_obj->client->recurring_invoice_filepath();
|
||||
$path = $entity_obj->client->recurring_invoice_filepath($invitation);
|
||||
}
|
||||
|
||||
$file_path = $path.$entity_obj->numberFormatter().'.pdf';
|
||||
@ -90,6 +90,9 @@ class Phantom
|
||||
|
||||
$this->checkMime($pdf, $invitation, $entity);
|
||||
|
||||
if(!Storage::disk(config('filesystems.default'))->exists($path))
|
||||
Storage::disk(config('filesystems.default'))->makeDirectory($path, 0775);
|
||||
|
||||
$instance = Storage::disk(config('filesystems.default'))->put($file_path, $pdf);
|
||||
|
||||
return $file_path;
|
||||
@ -118,8 +121,6 @@ class Phantom
|
||||
|
||||
$finfo = new \finfo(FILEINFO_MIME);
|
||||
|
||||
nlog($pdf);
|
||||
|
||||
if($finfo->buffer($pdf) != 'application/pdf; charset=binary')
|
||||
{
|
||||
SystemLogger::dispatch(
|
||||
|
@ -14,8 +14,8 @@ return [
|
||||
'require_https' => env('REQUIRE_HTTPS', true),
|
||||
'app_url' => rtrim(env('APP_URL', ''), '/'),
|
||||
'app_domain' => env('APP_DOMAIN', 'invoicing.co'),
|
||||
'app_version' => '5.2.1',
|
||||
'app_tag' => '5.2.1-release',
|
||||
'app_version' => '5.2.2',
|
||||
'app_tag' => '5.2.2-release',
|
||||
'minimum_client_version' => '5.0.16',
|
||||
'terms_version' => '1.0.1',
|
||||
'api_secret' => env('API_SECRET', ''),
|
||||
|
@ -194,6 +194,8 @@ class MultiDBUserTest extends TestCase
|
||||
],
|
||||
];
|
||||
|
||||
$response = false;
|
||||
|
||||
try {
|
||||
$response = $this->withHeaders([
|
||||
'X-API-SECRET' => config('ninja.api_secret'),
|
||||
@ -203,7 +205,7 @@ class MultiDBUserTest extends TestCase
|
||||
} catch (ValidationException $e) {
|
||||
$message = json_decode($e->validator->getMessageBag(), 1);
|
||||
$this->assertNotNull($message);
|
||||
|
||||
nlog($message);
|
||||
}
|
||||
|
||||
if ($response) {
|
||||
|
39
tests/Unit/S3CleanupTest.php
Normal file
39
tests/Unit/S3CleanupTest.php
Normal file
@ -0,0 +1,39 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
namespace Tests\Unit;
|
||||
|
||||
use App\DataMapper\ClientSettings;
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
class S3CleanupTest extends TestCase
|
||||
{
|
||||
public function setUp() :void
|
||||
{
|
||||
parent::setUp();
|
||||
}
|
||||
|
||||
public function testMergeCollections()
|
||||
{
|
||||
$c1 = collect(["1","2","3","4"]);
|
||||
$c2 = collect(["5","6","7","8"]);
|
||||
|
||||
$c3 = collect(["1","2","10"]);
|
||||
|
||||
$merged = $c1->merge($c2)->toArray();
|
||||
|
||||
$this->assertTrue(in_array("1", $merged));
|
||||
$this->assertFalse(in_array("10", $merged));
|
||||
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user