1
0
mirror of https://github.com/invoiceninja/invoiceninja.git synced 2024-11-16 16:13:20 +01:00

Merge pull request #6023 from turbo124/v5-stable

v5.2.2
This commit is contained in:
David Bomba 2021-06-15 08:21:51 +10:00 committed by GitHub
commit 9ea175eb74
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
52 changed files with 336 additions and 155 deletions

View File

@ -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/

View File

@ -1 +1 @@
5.2.1
5.2.2

View 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);
});
}
}

View 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";
}
}

View File

@ -74,14 +74,12 @@ class GmailTransport extends Transport
}
}
$this->gmail->send();
$this->sendPerformed($message);
return $this->numberOfRecipients($message);
}
}

View File

@ -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'));
}
}

View File

@ -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);
}

View File

@ -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);;
}

View File

@ -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');
});

View File

@ -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));
}

View File

@ -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';

View File

@ -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';

View File

@ -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;
}

View File

@ -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();

View File

@ -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'){

View File

@ -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);
}

View File

@ -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();

View File

@ -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);
}
});

View File

@ -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());

View File

@ -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}");
});

View File

@ -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']);
}
}
});

View File

@ -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)]);
}

View File

@ -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())]);

View File

@ -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())]);
});

View File

@ -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)]);
}

View File

@ -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;
}
}

View File

@ -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()

View File

@ -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);

View File

@ -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);
}

View File

@ -409,13 +409,13 @@ 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);
}
elseif(Ninja::isHosted() && $portal){
$file_path = CreateEntityPdf::dispatchNow($invitation,config('filesystems.default'));
$file_path = CreateEntityPdf::dispatchNow($invitation, config('filesystems.default'));
return Storage::disk(config('filesystems.default'))->{$type}($file_path);
}

View File

@ -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);
}

View File

@ -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);

View File

@ -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);
}

View File

@ -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');
}
/**

View File

@ -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) {

View File

@ -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;
}

View File

@ -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';

View File

@ -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);

View File

@ -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';

View File

@ -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;
}

View File

@ -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) {

View File

@ -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;
}

View File

@ -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';

View File

@ -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;
}

View File

@ -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';

View File

@ -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;
}

View File

@ -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) ?: '&nbsp;', '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) ?: '&nbsp;', 'label' => ctrans('texts.quote_total')];
$data['$credit.total'] = ['value' => Number::formatMoney($this->entity_calc->getTotal(), $this->client) ?: '&nbsp;', 'label' => ctrans('texts.credit_total')];

View File

@ -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(

View File

@ -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', ''),

View File

@ -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) {

View 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));
}
}