diff --git a/app/Helpers/Epc/EpcQrGenerator.php b/app/Helpers/Epc/EpcQrGenerator.php index 256e2d734181..700703419516 100644 --- a/app/Helpers/Epc/EpcQrGenerator.php +++ b/app/Helpers/Epc/EpcQrGenerator.php @@ -93,16 +93,13 @@ class EpcQrGenerator isset($this->company?->custom_fields?->company1) ? $this->company->settings->custom_value1 : '', // IBAN $this->formatMoney($this->amount), // Amount with EUR prefix '', // Reference - substr($this->invoice->number, 0, 34) // Unstructured remittance information + substr(($this->invoice->number ?? ''), 0, 34) // Unstructured remittance information ]; return implode("\n", $data); } - - // substr("{$this->invoice->number} {$this->invoice->client->number}", 0,139), - private function validateFields() { if (Ninja::isSelfHost() && isset($this->company?->custom_fields?->company2)) { diff --git a/app/Http/Controllers/PostMarkController.php b/app/Http/Controllers/PostMarkController.php index 5e972cdfcb7a..15660ac2e37c 100644 --- a/app/Http/Controllers/PostMarkController.php +++ b/app/Http/Controllers/PostMarkController.php @@ -65,7 +65,7 @@ class PostMarkController extends BaseController public function webhook(Request $request) { if ($request->header('X-API-SECURITY') && $request->header('X-API-SECURITY') == config('services.postmark.token')) { - ProcessPostmarkWebhook::dispatch($request->all())->delay(rand(2, 10)); + ProcessPostmarkWebhook::dispatch($request->all())->delay(rand(6, 14)); return response()->json(['message' => 'Success'], 200); } diff --git a/app/Jobs/Invitation/MarkOpened.php b/app/Jobs/Invitation/MarkOpened.php deleted file mode 100644 index 4194ab2b2edf..000000000000 --- a/app/Jobs/Invitation/MarkOpened.php +++ /dev/null @@ -1,66 +0,0 @@ -message_id = $message_id; - - $this->entity = $entity; - } - - /** - * Execute the job. - * - */ - public function handle() - { - $invitation = $this->entity::with('user', 'contact') - ->whereMessageId($this->message_id) - ->first(); - - if (! $invitation) { - return false; - } - - $invitation->opened_date = now(); - //$invitation->email_error = $error; - $invitation->save(); - } -} diff --git a/app/Jobs/Mailgun/ProcessMailgunWebhook.php b/app/Jobs/Mailgun/ProcessMailgunWebhook.php index f82314717de2..37bc7f8fb88a 100644 --- a/app/Jobs/Mailgun/ProcessMailgunWebhook.php +++ b/app/Jobs/Mailgun/ProcessMailgunWebhook.php @@ -69,7 +69,8 @@ class ProcessMailgunWebhook implements ShouldQueue ->where('company_id', $this->invitation->company_id) ->where('type_id', SystemLog::TYPE_WEBHOOK_RESPONSE) ->where('category_id', SystemLog::CATEGORY_MAIL) - ->whereJsonContains('log', ['MessageID' => $this->message_id]) + ->where('log->MessageID', $message_id) + // ->whereJsonContains('log', ['MessageID' => $this->message_id]) ->orderBy('id', 'desc') ->first(); diff --git a/app/Jobs/PostMark/ProcessPostmarkWebhook.php b/app/Jobs/PostMark/ProcessPostmarkWebhook.php index 528eb7218a75..323ca7c425fa 100644 --- a/app/Jobs/PostMark/ProcessPostmarkWebhook.php +++ b/app/Jobs/PostMark/ProcessPostmarkWebhook.php @@ -70,7 +70,8 @@ class ProcessPostmarkWebhook implements ShouldQueue ->where('type_id', SystemLog::TYPE_WEBHOOK_RESPONSE) ->where('category_id', SystemLog::CATEGORY_MAIL) // ->where('client_id', $this->invitation->contact->client_id) - ->whereJsonContains('log', ['MessageID' => $message_id]) + // ->whereJsonContains('log', ['MessageID' => $message_id]) + ->where('log->MessageID', $message_id) ->orderBy('id', 'desc') ->first(); @@ -444,4 +445,10 @@ class ProcessPostmarkWebhook implements ShouldQueue } } + + public function middleware() + { + return [new \Illuminate\Queue\Middleware\WithoutOverlapping($this->request['Tag'])]; + } + } diff --git a/app/Jobs/Util/ReminderJob.php b/app/Jobs/Util/ReminderJob.php index 241050885ac7..3686cfe5a4ff 100644 --- a/app/Jobs/Util/ReminderJob.php +++ b/app/Jobs/Util/ReminderJob.php @@ -97,6 +97,9 @@ class ReminderJob implements ShouldQueue ->whereHas('company', function ($query) { $query->where('is_disabled', 0); }) + ->whereHas('company.account', function ($q){ + $q->whereNotNull('plan')->where('plan_expire', '>', now()->subDays(2)); + }) ->with('invitations') ->cursor() ->each(function ($invoice) { diff --git a/app/Listeners/Contact/UpdateContactLastLogin.php b/app/Listeners/Contact/UpdateContactLastLogin.php index 03fc011e0b6b..3cdd98e59fe4 100644 --- a/app/Listeners/Contact/UpdateContactLastLogin.php +++ b/app/Listeners/Contact/UpdateContactLastLogin.php @@ -16,6 +16,7 @@ use Illuminate\Contracts\Queue\ShouldQueue; class UpdateContactLastLogin implements ShouldQueue { + public $delay = 8; /** * Create the event listener. * @@ -38,8 +39,6 @@ class UpdateContactLastLogin implements ShouldQueue $client_contact = $event->client_contact; $client_contact->last_login = now(); - $client_contact->client->last_login = now(); - - $client_contact->push(); + $client_contact->save(); } } diff --git a/app/Models/SystemLog.php b/app/Models/SystemLog.php index 0f833fa3f32e..7be88683eafc 100644 --- a/app/Models/SystemLog.php +++ b/app/Models/SystemLog.php @@ -215,6 +215,11 @@ class SystemLog extends Model return $query; } + public function company() + { + return $this->hasMany(\App\Models\Company::class); + } + public function getCategoryName() { switch ($this->category_id) { diff --git a/app/Services/Client/ClientService.php b/app/Services/Client/ClientService.php index b7ba322a2b3c..e966f6f7a4f7 100644 --- a/app/Services/Client/ClientService.php +++ b/app/Services/Client/ClientService.php @@ -253,7 +253,7 @@ class ClientService $pdf = $statement->run(); - if ($send_email) { + if ($send_email && $pdf) { // If selected, ignore clients that don't have any invoices to put on the statement. if (!empty($options['only_clients_with_invoices']) && $statement->getInvoices()->count() == 0) { return false; @@ -311,6 +311,8 @@ class ClientService $invoice = $this->client->invoices()->whereHas('invitations')->first(); + $invoice = \App\Models\Invoice::where('client_id', $this->client->id)->whereHas('invitations')->first(); + $email_object->attachments = [['file' => base64_encode($pdf), 'name' => ctrans('texts.statement') . ".pdf"]]; $email_object->client_id = $this->client->id; $email_object->entity_class = Invoice::class; diff --git a/app/Services/Client/Statement.php b/app/Services/Client/Statement.php index d001103885e7..36978329b24c 100644 --- a/app/Services/Client/Statement.php +++ b/app/Services/Client/Statement.php @@ -55,79 +55,94 @@ class Statement public function run(): ?string { - $this - ->setupOptions() - ->setupEntity(); + try { + $this + ->setupOptions() + ->setupEntity(); - $html = new HtmlEngine($this->getInvitation()); + $html = new HtmlEngine($this->getInvitation()); - $variables = []; - $variables = $html->generateLabelsAndValues(); + $variables = []; + $variables = $html->generateLabelsAndValues(); - $option_template = &$this->options['template']; + $option_template = &$this->options['template']; - $custom_statement_template = \App\Models\Design::where('id', $this->decodePrimaryKey($this->client->getSetting('statement_design_id')))->where('is_template', true)->first(); + $custom_statement_template = \App\Models\Design::where('id', $this->decodePrimaryKey($this->client->getSetting('statement_design_id')))->where('is_template', true)->first(); - if($custom_statement_template || $option_template && $option_template != '') { + if($custom_statement_template || $option_template && $option_template != '') { - $variables['values']['$start_date'] = $this->translateDate($this->options['start_date'], $this->client->date_format(), $this->client->locale()); - $variables['values']['$end_date'] = $this->translateDate($this->options['end_date'], $this->client->date_format(), $this->client->locale()); - $variables['labels']['$start_date_label'] = ctrans('texts.start_date'); - $variables['labels']['$end_date_label'] = ctrans('texts.end_date'); + $variables['values']['$start_date'] = $this->translateDate($this->options['start_date'], $this->client->date_format(), $this->client->locale()); + $variables['values']['$end_date'] = $this->translateDate($this->options['end_date'], $this->client->date_format(), $this->client->locale()); + $variables['labels']['$start_date_label'] = ctrans('texts.start_date'); + $variables['labels']['$end_date_label'] = ctrans('texts.end_date'); - return $this->templateStatement($variables); - } + return $this->templateStatement($variables); + } - if ($this->getDesign()->is_custom) { - $this->options['custom_partials'] = \json_decode(\json_encode($this->getDesign()->design), true); + if ($this->getDesign()->is_custom) { + $this->options['custom_partials'] = \json_decode(\json_encode($this->getDesign()->design), true); - $template = new PdfMakerDesign(\App\Services\PdfMaker\Design::CUSTOM, $this->options); - } else { - $template = new PdfMakerDesign(strtolower($this->getDesign()->name), $this->options); - } + $template = new PdfMakerDesign(\App\Services\PdfMaker\Design::CUSTOM, $this->options); + } else { + $template = new PdfMakerDesign(strtolower($this->getDesign()->name), $this->options); + } - $variables = $html->generateLabelsAndValues(); - $variables['values']['$show_paid_stamp'] = 'none'; //do not show paid stamp on statement + $variables = $html->generateLabelsAndValues(); + $variables['values']['$show_paid_stamp'] = 'none'; //do not show paid stamp on statement - $state = [ - 'template' => $template->elements([ - 'client' => $this->client, - 'entity' => $this->entity, - 'pdf_variables' => (array) $this->entity->company->settings->pdf_variables, - '$product' => $this->getDesign()->design->product, + $state = [ + 'template' => $template->elements([ + 'client' => $this->client, + 'entity' => $this->entity, + 'pdf_variables' => (array) $this->entity->company->settings->pdf_variables, + '$product' => $this->getDesign()->design->product, + 'variables' => $variables, + 'invoices' => $this->getInvoices()->cursor(), + 'payments' => $this->getPayments()->cursor(), + 'credits' => $this->getCredits()->cursor(), + 'aging' => $this->getAging(), + ], \App\Services\PdfMaker\Design::STATEMENT), 'variables' => $variables, - 'invoices' => $this->getInvoices()->cursor(), - 'payments' => $this->getPayments()->cursor(), - 'credits' => $this->getCredits()->cursor(), - 'aging' => $this->getAging(), - ], \App\Services\PdfMaker\Design::STATEMENT), - 'variables' => $variables, - 'options' => [ - ], - 'process_markdown' => $this->entity->client->company->markdown_enabled, - ]; + 'options' => [ + ], + 'process_markdown' => $this->entity->client->company->markdown_enabled, + ]; - $maker = new PdfMaker($state); + $maker = new PdfMaker($state); - $maker - ->design($template) - ->build(); + $maker + ->design($template) + ->build(); - $pdf = null; - $html = $maker->getCompiledHTML(true); + $pdf = null; + $html = $maker->getCompiledHTML(true); + + if ($this->rollback) { + \DB::connection(config('database.default'))->rollBack(); + $this->rollback = false; + } + + $pdf = $this->convertToPdf($html); + + $this->setVariables($variables); + + $maker = null; + $state = null; + + return $pdf; + } + catch(\Throwable $th){ + + nlog("STATEMENT:: Throwable::" . $th->getMessage()); + + if ($this->rollback) { + \DB::connection(config('database.default'))->rollBack(); + } - if ($this->rollback) { - \DB::connection(config('database.default'))->rollBack(); } - $pdf = $this->convertToPdf($html); + return null; - $this->setVariables($variables); - - $maker = null; - $state = null; - - return $pdf; } public function setVariables($variables): self @@ -178,19 +193,14 @@ class Statement { $pdf = false; - try { - if (config('ninja.phantomjs_pdf_generation') || config('ninja.pdf_generator') == 'phantom') { - $pdf = (new Phantom())->convertHtmlToPdf($html); - } elseif (config('ninja.invoiceninja_hosted_pdf_generation') || config('ninja.pdf_generator') == 'hosted_ninja') { - $pdf = (new NinjaPdf())->build($html); - } else { - $pdf = $this->makePdf(null, null, $html); - } - } catch (\Exception $e) { - nlog(print_r($e->getMessage(), true)); + if (config('ninja.phantomjs_pdf_generation') || config('ninja.pdf_generator') == 'phantom') { + $pdf = (new Phantom())->convertHtmlToPdf($html); + } elseif (config('ninja.invoiceninja_hosted_pdf_generation') || config('ninja.pdf_generator') == 'hosted_ninja') { + $pdf = (new NinjaPdf())->build($html); + } else { + $pdf = $this->makePdf(null, null, $html); } - return $pdf; } /** @@ -208,7 +218,7 @@ class Statement DB::connection(config('database.default'))->beginTransaction(); $this->rollback = true; - + $invoice = InvoiceFactory::create($this->client->company->id, $this->client->user->id); $invoice->client_id = $this->client->id; $invoice->line_items = $this->buildLineItems(); diff --git a/app/Services/Scheduler/EmailStatementService.php b/app/Services/Scheduler/EmailStatementService.php index 64da20836793..6a77671886a1 100644 --- a/app/Services/Scheduler/EmailStatementService.php +++ b/app/Services/Scheduler/EmailStatementService.php @@ -70,7 +70,7 @@ class EmailStatementService 'show_aging_table' => $this->scheduler->parameters['show_aging_table'] ?? true, 'show_credits_table' => $this->scheduler->parameters['show_credits_table'] ?? true, 'only_clients_with_invoices' => $this->scheduler->parameters['only_clients_with_invoices'] ?? false, - 'status' => $this->scheduler->parameters['status'] + 'status' => $this->scheduler->parameters['status'] ?? 'all', ]; }