Fixes for missing a rollback

This commit is contained in:
David Bomba 2024-10-01 12:51:44 +10:00
parent 2072de908a
commit 8f793ea861
11 changed files with 102 additions and 144 deletions

View File

@ -93,16 +93,13 @@ class EpcQrGenerator
isset($this->company?->custom_fields?->company1) ? $this->company->settings->custom_value1 : '', // IBAN isset($this->company?->custom_fields?->company1) ? $this->company->settings->custom_value1 : '', // IBAN
$this->formatMoney($this->amount), // Amount with EUR prefix $this->formatMoney($this->amount), // Amount with EUR prefix
'', // Reference '', // Reference
substr($this->invoice->number, 0, 34) // Unstructured remittance information substr(($this->invoice->number ?? ''), 0, 34) // Unstructured remittance information
]; ];
return implode("\n", $data); return implode("\n", $data);
} }
// substr("{$this->invoice->number} {$this->invoice->client->number}", 0,139),
private function validateFields() private function validateFields()
{ {
if (Ninja::isSelfHost() && isset($this->company?->custom_fields?->company2)) { if (Ninja::isSelfHost() && isset($this->company?->custom_fields?->company2)) {

View File

@ -65,7 +65,7 @@ class PostMarkController extends BaseController
public function webhook(Request $request) public function webhook(Request $request)
{ {
if ($request->header('X-API-SECURITY') && $request->header('X-API-SECURITY') == config('services.postmark.token')) { 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); return response()->json(['message' => 'Success'], 200);
} }

View File

@ -1,66 +0,0 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2024. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Jobs\Invitation;
use App\Utils\Traits\NumberFormatter;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
//todo - ensure we are MultiDB Aware in dispatched jobs
class MarkOpened implements ShouldQueue
{
use Dispatchable;
use InteractsWithQueue;
use Queueable;
use SerializesModels;
use NumberFormatter;
public $message_id;
public $entity;
/**
* Create a new job instance.
*
* @param string $message_id
* @param string $entity
*/
public function __construct(string $message_id, string $entity)
{
$this->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();
}
}

View File

@ -69,7 +69,8 @@ class ProcessMailgunWebhook implements ShouldQueue
->where('company_id', $this->invitation->company_id) ->where('company_id', $this->invitation->company_id)
->where('type_id', SystemLog::TYPE_WEBHOOK_RESPONSE) ->where('type_id', SystemLog::TYPE_WEBHOOK_RESPONSE)
->where('category_id', SystemLog::CATEGORY_MAIL) ->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') ->orderBy('id', 'desc')
->first(); ->first();

View File

@ -70,7 +70,8 @@ class ProcessPostmarkWebhook implements ShouldQueue
->where('type_id', SystemLog::TYPE_WEBHOOK_RESPONSE) ->where('type_id', SystemLog::TYPE_WEBHOOK_RESPONSE)
->where('category_id', SystemLog::CATEGORY_MAIL) ->where('category_id', SystemLog::CATEGORY_MAIL)
// ->where('client_id', $this->invitation->contact->client_id) // ->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') ->orderBy('id', 'desc')
->first(); ->first();
@ -444,4 +445,10 @@ class ProcessPostmarkWebhook implements ShouldQueue
} }
} }
public function middleware()
{
return [new \Illuminate\Queue\Middleware\WithoutOverlapping($this->request['Tag'])];
}
} }

View File

@ -97,6 +97,9 @@ class ReminderJob implements ShouldQueue
->whereHas('company', function ($query) { ->whereHas('company', function ($query) {
$query->where('is_disabled', 0); $query->where('is_disabled', 0);
}) })
->whereHas('company.account', function ($q){
$q->whereNotNull('plan')->where('plan_expire', '>', now()->subDays(2));
})
->with('invitations') ->with('invitations')
->cursor() ->cursor()
->each(function ($invoice) { ->each(function ($invoice) {

View File

@ -16,6 +16,7 @@ use Illuminate\Contracts\Queue\ShouldQueue;
class UpdateContactLastLogin implements ShouldQueue class UpdateContactLastLogin implements ShouldQueue
{ {
public $delay = 8;
/** /**
* Create the event listener. * Create the event listener.
* *
@ -38,8 +39,6 @@ class UpdateContactLastLogin implements ShouldQueue
$client_contact = $event->client_contact; $client_contact = $event->client_contact;
$client_contact->last_login = now(); $client_contact->last_login = now();
$client_contact->client->last_login = now(); $client_contact->save();
$client_contact->push();
} }
} }

View File

@ -215,6 +215,11 @@ class SystemLog extends Model
return $query; return $query;
} }
public function company()
{
return $this->hasMany(\App\Models\Company::class);
}
public function getCategoryName() public function getCategoryName()
{ {
switch ($this->category_id) { switch ($this->category_id) {

View File

@ -253,7 +253,7 @@ class ClientService
$pdf = $statement->run(); $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 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) { if (!empty($options['only_clients_with_invoices']) && $statement->getInvoices()->count() == 0) {
return false; return false;
@ -311,6 +311,8 @@ class ClientService
$invoice = $this->client->invoices()->whereHas('invitations')->first(); $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->attachments = [['file' => base64_encode($pdf), 'name' => ctrans('texts.statement') . ".pdf"]];
$email_object->client_id = $this->client->id; $email_object->client_id = $this->client->id;
$email_object->entity_class = Invoice::class; $email_object->entity_class = Invoice::class;

View File

@ -55,79 +55,94 @@ class Statement
public function run(): ?string public function run(): ?string
{ {
$this try {
->setupOptions() $this
->setupEntity(); ->setupOptions()
->setupEntity();
$html = new HtmlEngine($this->getInvitation()); $html = new HtmlEngine($this->getInvitation());
$variables = []; $variables = [];
$variables = $html->generateLabelsAndValues(); $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']['$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['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']['$start_date_label'] = ctrans('texts.start_date');
$variables['labels']['$end_date_label'] = ctrans('texts.end_date'); $variables['labels']['$end_date_label'] = ctrans('texts.end_date');
return $this->templateStatement($variables); return $this->templateStatement($variables);
} }
if ($this->getDesign()->is_custom) { if ($this->getDesign()->is_custom) {
$this->options['custom_partials'] = \json_decode(\json_encode($this->getDesign()->design), true); $this->options['custom_partials'] = \json_decode(\json_encode($this->getDesign()->design), true);
$template = new PdfMakerDesign(\App\Services\PdfMaker\Design::CUSTOM, $this->options); $template = new PdfMakerDesign(\App\Services\PdfMaker\Design::CUSTOM, $this->options);
} else { } else {
$template = new PdfMakerDesign(strtolower($this->getDesign()->name), $this->options); $template = new PdfMakerDesign(strtolower($this->getDesign()->name), $this->options);
} }
$variables = $html->generateLabelsAndValues(); $variables = $html->generateLabelsAndValues();
$variables['values']['$show_paid_stamp'] = 'none'; //do not show paid stamp on statement $variables['values']['$show_paid_stamp'] = 'none'; //do not show paid stamp on statement
$state = [ $state = [
'template' => $template->elements([ 'template' => $template->elements([
'client' => $this->client, 'client' => $this->client,
'entity' => $this->entity, 'entity' => $this->entity,
'pdf_variables' => (array) $this->entity->company->settings->pdf_variables, 'pdf_variables' => (array) $this->entity->company->settings->pdf_variables,
'$product' => $this->getDesign()->design->product, '$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, 'variables' => $variables,
'invoices' => $this->getInvoices()->cursor(), 'options' => [
'payments' => $this->getPayments()->cursor(), ],
'credits' => $this->getCredits()->cursor(), 'process_markdown' => $this->entity->client->company->markdown_enabled,
'aging' => $this->getAging(), ];
], \App\Services\PdfMaker\Design::STATEMENT),
'variables' => $variables,
'options' => [
],
'process_markdown' => $this->entity->client->company->markdown_enabled,
];
$maker = new PdfMaker($state); $maker = new PdfMaker($state);
$maker $maker
->design($template) ->design($template)
->build(); ->build();
$pdf = null; $pdf = null;
$html = $maker->getCompiledHTML(true); $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 public function setVariables($variables): self
@ -178,19 +193,14 @@ class Statement
{ {
$pdf = false; $pdf = false;
try { if (config('ninja.phantomjs_pdf_generation') || config('ninja.pdf_generator') == 'phantom') {
if (config('ninja.phantomjs_pdf_generation') || config('ninja.pdf_generator') == 'phantom') { $pdf = (new Phantom())->convertHtmlToPdf($html);
$pdf = (new Phantom())->convertHtmlToPdf($html); } elseif (config('ninja.invoiceninja_hosted_pdf_generation') || config('ninja.pdf_generator') == 'hosted_ninja') {
} elseif (config('ninja.invoiceninja_hosted_pdf_generation') || config('ninja.pdf_generator') == 'hosted_ninja') { $pdf = (new NinjaPdf())->build($html);
$pdf = (new NinjaPdf())->build($html); } else {
} else { $pdf = $this->makePdf(null, null, $html);
$pdf = $this->makePdf(null, null, $html);
}
} catch (\Exception $e) {
nlog(print_r($e->getMessage(), true));
} }
return $pdf; return $pdf;
} }
/** /**
@ -208,7 +218,7 @@ class Statement
DB::connection(config('database.default'))->beginTransaction(); DB::connection(config('database.default'))->beginTransaction();
$this->rollback = true; $this->rollback = true;
$invoice = InvoiceFactory::create($this->client->company->id, $this->client->user->id); $invoice = InvoiceFactory::create($this->client->company->id, $this->client->user->id);
$invoice->client_id = $this->client->id; $invoice->client_id = $this->client->id;
$invoice->line_items = $this->buildLineItems(); $invoice->line_items = $this->buildLineItems();

View File

@ -70,7 +70,7 @@ class EmailStatementService
'show_aging_table' => $this->scheduler->parameters['show_aging_table'] ?? true, 'show_aging_table' => $this->scheduler->parameters['show_aging_table'] ?? true,
'show_credits_table' => $this->scheduler->parameters['show_credits_table'] ?? true, 'show_credits_table' => $this->scheduler->parameters['show_credits_table'] ?? true,
'only_clients_with_invoices' => $this->scheduler->parameters['only_clients_with_invoices'] ?? false, 'only_clients_with_invoices' => $this->scheduler->parameters['only_clients_with_invoices'] ?? false,
'status' => $this->scheduler->parameters['status'] 'status' => $this->scheduler->parameters['status'] ?? 'all',
]; ];
} }