diff --git a/VERSION.txt b/VERSION.txt
index ffd041c1c1..fc0b8577dd 100644
--- a/VERSION.txt
+++ b/VERSION.txt
@@ -1 +1 @@
-5.11.23
\ No newline at end of file
+5.11.24
\ No newline at end of file
diff --git a/app/Helpers/SwissQr/SwissQrGenerator.php b/app/Helpers/SwissQr/SwissQrGenerator.php
index 242a6250df..47f9ae4f3c 100644
--- a/app/Helpers/SwissQr/SwissQrGenerator.php
+++ b/app/Helpers/SwissQr/SwissQrGenerator.php
@@ -172,6 +172,7 @@ class SwissQrGenerator
->setPrintable(false)
->getPaymentPart();
+ // return $html;
return htmlspecialchars($html);
} catch (\Exception $e) {
diff --git a/app/Http/Controllers/ClientPortal/InvoiceController.php b/app/Http/Controllers/ClientPortal/InvoiceController.php
index 60a58a4e40..a7a3105a98 100644
--- a/app/Http/Controllers/ClientPortal/InvoiceController.php
+++ b/app/Http/Controllers/ClientPortal/InvoiceController.php
@@ -106,14 +106,14 @@ class InvoiceController extends Controller
break;
}
- usleep(200000);
+ usleep(300000);
}
$invitation = false;
match($data['entity_type'] ?? 'invoice') {
- 'invoice' => $invitation = InvoiceInvitation::withTrashed()->find($data['invitation_id']),
+ 'invoice' => $invitation = InvoiceInvitation::withTrashed()->find($data['invitation_id']), //@todo - sometimes this is false!!
'quote' => $invitation = QuoteInvitation::withTrashed()->find($data['invitation_id']),
'credit' => $invitation = CreditInvitation::withTrashed()->find($data['invitation_id']),
'recurring_invoice' => $invitation = RecurringInvoiceInvitation::withTrashed()->find($data['invitation_id']),
diff --git a/app/Http/Requests/Company/UpdateCompanyRequest.php b/app/Http/Requests/Company/UpdateCompanyRequest.php
index 8aa24ffacb..63fb67815b 100644
--- a/app/Http/Requests/Company/UpdateCompanyRequest.php
+++ b/app/Http/Requests/Company/UpdateCompanyRequest.php
@@ -195,7 +195,10 @@ class UpdateCompanyRequest extends Request
if (Ninja::isHosted()) {
foreach ($this->protected_input as $protected_var) {
- $settings[$protected_var] = str_replace("script", "", $settings[$protected_var]);
+
+ if(isset($settings[$protected_var])) {
+ $settings[$protected_var] = str_replace("script", "", $settings[$protected_var]);
+ }
}
}
diff --git a/app/Http/Requests/RecurringInvoice/StoreRecurringInvoiceRequest.php b/app/Http/Requests/RecurringInvoice/StoreRecurringInvoiceRequest.php
index d02046b40b..ab60e55d2e 100644
--- a/app/Http/Requests/RecurringInvoice/StoreRecurringInvoiceRequest.php
+++ b/app/Http/Requests/RecurringInvoice/StoreRecurringInvoiceRequest.php
@@ -13,7 +13,6 @@ namespace App\Http\Requests\RecurringInvoice;
use App\Http\Requests\Request;
use App\Http\ValidationRules\Project\ValidProjectForClient;
-use App\Http\ValidationRules\Recurring\UniqueRecurringInvoiceNumberRule;
use App\Models\Client;
use App\Models\RecurringInvoice;
use App\Utils\Traits\CleanLineItems;
@@ -68,7 +67,7 @@ class StoreRecurringInvoiceRequest extends Request
$rules['project_id'] = ['bail', 'sometimes', new ValidProjectForClient($this->all())];
- $rules['number'] = new UniqueRecurringInvoiceNumberRule($this->all());
+ $rules['number'] = ['bail', 'nullable', \Illuminate\Validation\Rule::unique('recurring_invoices')->where('company_id', $user->company()->id)];
$rules['tax_rate1'] = 'bail|sometimes|numeric';
$rules['tax_rate2'] = 'bail|sometimes|numeric';
diff --git a/app/Http/Requests/RecurringQuote/StoreRecurringQuoteRequest.php b/app/Http/Requests/RecurringQuote/StoreRecurringQuoteRequest.php
index 657b96d0bb..ef9c13c301 100644
--- a/app/Http/Requests/RecurringQuote/StoreRecurringQuoteRequest.php
+++ b/app/Http/Requests/RecurringQuote/StoreRecurringQuoteRequest.php
@@ -12,7 +12,6 @@
namespace App\Http\Requests\RecurringQuote;
use App\Http\Requests\Request;
-use App\Http\ValidationRules\Recurring\UniqueRecurringQuoteNumberRule;
use App\Models\Client;
use App\Models\RecurringQuote;
use App\Utils\Traits\CleanLineItems;
@@ -63,7 +62,7 @@ class StoreRecurringQuoteRequest extends Request
$rules['frequency_id'] = 'required|integer|digits_between:1,12';
- $rules['number'] = new UniqueRecurringQuoteNumberRule($this->all());
+ $rules['number'] = ['bail', 'nullable', \Illuminate\Validation\Rule::unique('recurring_quotes')->where('company_id', $user->company()->id)];
return $rules;
}
diff --git a/app/Http/ValidationRules/Recurring/UniqueRecurringInvoiceNumberRule.php b/app/Http/ValidationRules/Recurring/UniqueRecurringInvoiceNumberRule.php
deleted file mode 100644
index b7865a0b51..0000000000
--- a/app/Http/ValidationRules/Recurring/UniqueRecurringInvoiceNumberRule.php
+++ /dev/null
@@ -1,67 +0,0 @@
-input = $input;
- }
-
- /**
- * @param string $attribute
- * @param mixed $value
- * @return bool
- */
- public function passes($attribute, $value)
- {
- return $this->checkIfInvoiceNumberUnique(); //if it exists, return false!
- }
-
- /**
- * @return string
- */
- public function message()
- {
- return ctrans('texts.recurring_invoice_number_taken', ['number' => $this->input['number']]);
- }
-
- /**
- * @return bool
- */
- private function checkIfInvoiceNumberUnique(): bool
- {
- if (empty($this->input['number'])) {
- return true;
- }
-
- $invoice = RecurringInvoice::query()->where('client_id', $this->input['client_id'])
- ->where('number', $this->input['number'])
- ->withTrashed()
- ->exists();
-
- if ($invoice) {
- return false;
- }
-
- return true;
- }
-}
diff --git a/app/Http/ValidationRules/Recurring/UniqueRecurringQuoteNumberRule.php b/app/Http/ValidationRules/Recurring/UniqueRecurringQuoteNumberRule.php
deleted file mode 100644
index 29693a067d..0000000000
--- a/app/Http/ValidationRules/Recurring/UniqueRecurringQuoteNumberRule.php
+++ /dev/null
@@ -1,67 +0,0 @@
-input = $input;
- }
-
- /**
- * @param string $attribute
- * @param mixed $value
- * @return bool
- */
- public function passes($attribute, $value)
- {
- return $this->checkIfQuoteNumberUnique(); //if it exists, return false!
- }
-
- /**
- * @return string
- */
- public function message()
- {
- return ctrans('texts.recurring_quote_number_taken', ['number' => $this->input['number']]);
- }
-
- /**
- * @return bool
- */
- private function checkIfQuoteNumberUnique(): bool
- {
- if (empty($this->input['number'])) {
- return true;
- }
-
- $invoice = RecurringQuote::query()->where('client_id', $this->input['client_id'])
- ->where('number', $this->input['number'])
- ->withTrashed()
- ->exists();
-
- if ($invoice) {
- return false;
- }
-
- return true;
- }
-}
diff --git a/app/Jobs/Mail/NinjaMailerJob.php b/app/Jobs/Mail/NinjaMailerJob.php
index 8414df5df3..a78861f221 100644
--- a/app/Jobs/Mail/NinjaMailerJob.php
+++ b/app/Jobs/Mail/NinjaMailerJob.php
@@ -515,7 +515,7 @@ class NinjaMailerJob implements ShouldQueue
private function checkValidSendingUser($user)
{
/* Always ensure the user is set on the correct account */
- if ($user->account_id != $this->company->account_id) {
+ if (!$user ||($user->account_id != $this->company->account_id)) {
$this->nmo->settings->email_sending_method = 'default';
return $this->setMailDriver();
}
diff --git a/app/Livewire/Flow2/InvoicePay.php b/app/Livewire/Flow2/InvoicePay.php
index 79c303a119..a434dbf7c4 100644
--- a/app/Livewire/Flow2/InvoicePay.php
+++ b/app/Livewire/Flow2/InvoicePay.php
@@ -141,6 +141,9 @@ class InvoicePay extends Component
$company_gateway = CompanyGateway::query()->find($company_gateway_id);
+ if(!$company_gateway)
+ return $this->required_fields = false;
+
$this->checkRequiredFields($company_gateway);
}
diff --git a/app/PaymentDrivers/Authorize/AuthorizeTransaction.php b/app/PaymentDrivers/Authorize/AuthorizeTransaction.php
index abd81d1f21..a49e65985c 100644
--- a/app/PaymentDrivers/Authorize/AuthorizeTransaction.php
+++ b/app/PaymentDrivers/Authorize/AuthorizeTransaction.php
@@ -119,6 +119,10 @@ class AuthorizeTransaction
$transactionRequestType->setOrder($order);
$transactionRequestType->addToTransactionSettings($duplicateWindowSetting);
+ $solution = new \net\authorize\api\contract\v1\SolutionType();
+ $solution->setId($this->authorize->company_gateway->getConfigField('testMode') ? 'AAA100303' : 'AAA172036');
+ $transactionRequestType->setSolution($solution);
+
$transactionRequestType->setPayment($paymentOne);
$transactionRequestType->setCurrencyCode($this->authorize->client->currency()->code);
diff --git a/app/PaymentDrivers/Authorize/ChargePaymentProfile.php b/app/PaymentDrivers/Authorize/ChargePaymentProfile.php
index 144350a8fa..28af2875c5 100644
--- a/app/PaymentDrivers/Authorize/ChargePaymentProfile.php
+++ b/app/PaymentDrivers/Authorize/ChargePaymentProfile.php
@@ -92,6 +92,10 @@ class ChargePaymentProfile
$transactionRequestType->setProfile($profileToCharge);
$transactionRequestType->setCurrencyCode($this->authorize->client->currency()->code);
+ $solution = new \net\authorize\api\contract\v1\SolutionType();
+ $solution->setId($this->authorize->company_gateway->getConfigField('testMode') ? 'AAA100303' : 'AAA172036');
+ $transactionRequestType->setSolution($solution);
+
$request = new CreateTransactionRequest();
$request->setMerchantAuthentication($this->authorize->merchant_authentication);
$request->setRefId($refId);
diff --git a/app/PaymentDrivers/Authorize/RefundTransaction.php b/app/PaymentDrivers/Authorize/RefundTransaction.php
index a543d867e5..41bc2fd3ad 100644
--- a/app/PaymentDrivers/Authorize/RefundTransaction.php
+++ b/app/PaymentDrivers/Authorize/RefundTransaction.php
@@ -83,6 +83,10 @@ class RefundTransaction
$transactionRequest->setPayment($paymentOne);
$transactionRequest->setRefTransId($payment->transaction_reference);
+ $solution = new \net\authorize\api\contract\v1\SolutionType();
+ $solution->setId($this->authorize->company_gateway->getConfigField('testMode') ? 'AAA100303' : 'AAA172036');
+ $transactionRequest->setSolution($solution);
+
$request = new CreateTransactionRequest();
$request->setMerchantAuthentication($this->authorize->merchant_authentication);
$request->setRefId($refId);
diff --git a/app/Services/Email/Email.php b/app/Services/Email/Email.php
index 9a9f618647..8fb90f0c7a 100644
--- a/app/Services/Email/Email.php
+++ b/app/Services/Email/Email.php
@@ -691,7 +691,7 @@ class Email implements ShouldQueue
private function checkValidSendingUser($user)
{
/* Always ensure the user is set on the correct account */
- if ($user->account_id != $this->company->account_id) {
+ if (!$user || ($user->account_id != $this->company->account_id)) {
$this->email_object->settings->email_sending_method = 'default';
return $this->setMailDriver();
diff --git a/app/Services/Pdf/PdfBuilder.php b/app/Services/Pdf/PdfBuilder.php
index c46251ce08..d5d29f5f11 100644
--- a/app/Services/Pdf/PdfBuilder.php
+++ b/app/Services/Pdf/PdfBuilder.php
@@ -352,7 +352,7 @@ class PdfBuilder
$outstanding = $this->service->options['invoices']->sum('balance');
return [
- ['element' => 'p', 'content' => '$outstanding_label: ' . $this->service->config->formatMoney($outstanding)],
+ ['element' => 'div', 'content' => '$outstanding_label: ' . $this->service->config->formatMoney($outstanding)],
];
}
@@ -402,7 +402,7 @@ class PdfBuilder
$outstanding = $this->service->options['credits']->sum('balance');
return [
- ['element' => 'p', 'content' => '$credit.balance_label: ' . $this->service->config->formatMoney($outstanding)],
+ ['element' => 'div', 'content' => '$credit.balance_label: ' . $this->service->config->formatMoney($outstanding)],
];
}
@@ -474,7 +474,7 @@ class PdfBuilder
}
}
}
-
+
return [
['element' => 'thead', 'elements' => $this->buildTableHeader('statement_payment')],
['element' => 'tbody', 'elements' => $tbody],
@@ -500,9 +500,9 @@ class PdfBuilder
$payment = $this->service->options['payments']->first();
return [
- ['element' => 'p', 'content' => \sprintf('%s: %s', ctrans('texts.amount_paid'), $this->service->config->formatMoney($this->payment_amount_total))],
- ['element' => 'p', 'content' => \sprintf('%s: %s', ctrans('texts.payment_method'), $payment->translatedType())],
- ['element' => 'p', 'content' => \sprintf('%s: %s', ctrans('texts.payment_date'), $this->translateDate($payment->date, $this->service->config->date_format, $this->service->config->locale) ?: ' ')],
+ ['element' => 'div', 'content' => \sprintf('%s: %s', ctrans('texts.amount_paid'), $this->service->config->formatMoney($this->payment_amount_total))],
+ ['element' => 'div', 'content' => \sprintf('%s: %s', ctrans('texts.payment_method'), $payment->translatedType())],
+ ['element' => 'div', 'content' => \sprintf('%s: %s', ctrans('texts.payment_date'), $this->translateDate($payment->date, $this->service->config->date_format, $this->service->config->locale) ?: ' ')],
];
}
@@ -520,9 +520,9 @@ class PdfBuilder
$payment = $this->service->options['unapplied']->first();
return [
- ['element' => 'p', 'content' => \sprintf('%s: %s', ctrans('texts.payment_balance'), $this->service->config->formatMoney($this->unapplied_total))],
- ['element' => 'p', 'content' => \sprintf('%s: %s', ctrans('texts.payment_method'), $payment->translatedType())],
- ['element' => 'p', 'content' => \sprintf('%s: %s', ctrans('texts.payment_date'), $this->translateDate($payment->date, $this->service->config->date_format, $this->service->config->locale) ?: ' ')],
+ ['element' => 'div', 'content' => \sprintf('%s: %s', ctrans('texts.payment_balance'), $this->service->config->formatMoney($this->unapplied_total))],
+ ['element' => 'div', 'content' => \sprintf('%s: %s', ctrans('texts.payment_method'), $payment->translatedType())],
+ ['element' => 'div', 'content' => \sprintf('%s: %s', ctrans('texts.payment_date'), $this->translateDate($payment->date, $this->service->config->date_format, $this->service->config->locale) ?: ' ')],
];
}
@@ -866,7 +866,6 @@ class PdfBuilder
$element['elements'][$last_visible]['properties']['class'] .= ' right-radius';
}
}
-
// Then, filter the elements array
$element['elements'] = array_map(function ($el) {
@@ -1405,7 +1404,7 @@ class PdfBuilder
['element' => 'span', 'content' => strtr(str_replace(["labels", "values"], ["",""], $_variables['values']['$entity.public_notes']), $_variables)]
]],
['element' => 'div', 'content' => '', 'properties' => ['style' => 'text-align: left; display: flex; flex-direction: column; page-break-inside: auto;'], 'elements' => [
- ['element' => 'span', 'content' => '$entity.terms_label: ', 'properties' => ['data-ref' => 'total_table-terms-label', 'style' => "text-align: left; margin-top: 1rem; {$show_terms_label}"]],
+ ['element' => 'span', 'content' => '$entity.terms_label: ', 'properties' => ['data-ref' => 'total_table-terms-label', 'style' => "font-weight:bold; text-align: left; margin-top: 1rem; {$show_terms_label}"]],
['element' => 'span', 'content' => strtr(str_replace("labels", "", $_variables['values']['$entity.terms']), $_variables['labels']), 'properties' => ['data-ref' => 'total_table-terms', 'style' => 'text-align: left;']],
]],
['element' => 'img', 'properties' => ['style' => 'max-width: 50%; height: auto;', 'src' => '$contact.signature', 'id' => 'contact-signature']],
@@ -1761,20 +1760,20 @@ class PdfBuilder
}
$elements = [
- ['element' => 'p', 'content' => ctrans('texts.delivery_note'), 'properties' => ['data-ref' => 'delivery_note-label', 'style' => 'font-weight: bold; text-transform: uppercase']],
- ['element' => 'p', 'content' => $this->service->config->client->name, 'show_empty' => false, 'properties' => ['data-ref' => 'delivery_note-client.name']],
- ['element' => 'p', 'content' => $this->service->config->client->shipping_address1, 'show_empty' => false, 'properties' => ['data-ref' => 'delivery_note-client.shipping_address1']],
- ['element' => 'p', 'content' => $this->service->config->client->shipping_address2, 'show_empty' => false, 'properties' => ['data-ref' => 'delivery_note-client.shipping_address2']],
- ['element' => 'p', 'show_empty' => false, 'elements' => [
+ ['element' => 'div', 'content' => ctrans('texts.delivery_note'), 'properties' => ['data-ref' => 'delivery_note-label', 'style' => 'font-weight: bold; text-transform: uppercase']],
+ ['element' => 'div', 'content' => $this->service->config->client->name, 'show_empty' => false, 'properties' => ['data-ref' => 'delivery_note-client.name']],
+ ['element' => 'div', 'content' => $this->service->config->client->shipping_address1, 'show_empty' => false, 'properties' => ['data-ref' => 'delivery_note-client.shipping_address1']],
+ ['element' => 'div', 'content' => $this->service->config->client->shipping_address2, 'show_empty' => false, 'properties' => ['data-ref' => 'delivery_note-client.shipping_address2']],
+ ['element' => 'div', 'show_empty' => false, 'elements' => [
['element' => 'span', 'content' => "{$this->service->config->client->shipping_city} ", 'properties' => ['ref' => 'delivery_note-client.shipping_city']],
['element' => 'span', 'content' => "{$this->service->config->client->shipping_state} ", 'properties' => ['ref' => 'delivery_note-client.shipping_state']],
['element' => 'span', 'content' => "{$this->service->config->client->shipping_postal_code} ", 'properties' => ['ref' => 'delivery_note-client.shipping_postal_code']],
]],
- ['element' => 'p', 'content' => optional($this->service->config->client->shipping_country)->name, 'show_empty' => false],
+ ['element' => 'div', 'content' => optional($this->service->config->client->shipping_country)->name, 'show_empty' => false],
];
if (!is_null($this->service->config->contact)) {
- $elements[] = ['element' => 'p', 'content' => $this->service->config->contact->email, 'show_empty' => false, 'properties' => ['data-ref' => 'delivery_note-contact.email']];
+ $elements[] = ['element' => 'div', 'content' => $this->service->config->contact->email, 'show_empty' => false, 'properties' => ['data-ref' => 'delivery_note-contact.email']];
}
return $elements;
@@ -1796,7 +1795,7 @@ class PdfBuilder
$variables = $this->service->config->pdf_variables['client_details'];
foreach ($variables as $variable) {
- $elements[] = ['element' => 'p', 'content' => $variable, 'show_empty' => false, 'properties' => ['data-ref' => 'client_details-' . substr($variable, 1)]];
+ $elements[] = ['element' => 'div', 'content' => $variable, 'show_empty' => false, 'properties' => ['data-ref' => 'client_details-' . substr($variable, 1)]];
}
return $elements;
@@ -1811,16 +1810,16 @@ class PdfBuilder
}
$elements = [
- ['element' => 'p', 'content' => ctrans('texts.shipping_address'), 'properties' => ['data-ref' => 'shipping_address-label', 'style' => 'font-weight: bold; text-transform: uppercase']],
- // ['element' => 'p', 'content' => $this->service->config->client->name, 'show_empty' => false, 'properties' => ['data-ref' => 'shipping_address-client.name']],
- ['element' => 'p', 'content' => $this->service->config->client->shipping_address1, 'show_empty' => false, 'properties' => ['data-ref' => 'shipping_address-client.shipping_address1']],
- ['element' => 'p', 'content' => $this->service->config->client->shipping_address2, 'show_empty' => false, 'properties' => ['data-ref' => 'shipping_address-client.shipping_address2']],
- ['element' => 'p', 'show_empty' => false, 'elements' => [
+ ['element' => 'div', 'content' => ctrans('texts.shipping_address'), 'properties' => ['data-ref' => 'shipping_address-label', 'style' => 'font-weight: bold; text-transform: uppercase']],
+ // ['element' => 'div', 'content' => $this->service->config->client->name, 'show_empty' => false, 'properties' => ['data-ref' => 'shipping_address-client.name']],
+ ['element' => 'div', 'content' => $this->service->config->client->shipping_address1, 'show_empty' => false, 'properties' => ['data-ref' => 'shipping_address-client.shipping_address1']],
+ ['element' => 'div', 'content' => $this->service->config->client->shipping_address2, 'show_empty' => false, 'properties' => ['data-ref' => 'shipping_address-client.shipping_address2']],
+ ['element' => 'div', 'show_empty' => false, 'elements' => [
['element' => 'span', 'content' => "{$this->service->config->client->shipping_city} ", 'properties' => ['ref' => 'shipping_address-client.shipping_city']],
['element' => 'span', 'content' => "{$this->service->config->client->shipping_state} ", 'properties' => ['ref' => 'shipping_address-client.shipping_state']],
['element' => 'span', 'content' => "{$this->service->config->client->shipping_postal_code} ", 'properties' => ['ref' => 'shipping_address-client.shipping_postal_code']],
]],
- ['element' => 'p', 'content' => optional($this->service->config->client->shipping_country)->name, 'show_empty' => false],
+ ['element' => 'div', 'content' => optional($this->service->config->client->shipping_country)->name, 'show_empty' => false],
];
return $elements;
@@ -1899,7 +1898,7 @@ class PdfBuilder
$elements = [];
foreach ($variables as $variable) {
- $elements[] = ['element' => 'p', 'content' => $variable, 'show_empty' => false, 'properties' => ['data-ref' => 'company_details-' . substr($variable, 1)]];
+ $elements[] = ['element' => 'div', 'content' => $variable, 'show_empty' => false, 'properties' => ['data-ref' => 'company_details-' . substr($variable, 1)]];
}
return $elements;
@@ -1919,7 +1918,7 @@ class PdfBuilder
$elements = [];
foreach ($variables as $variable) {
- $elements[] = ['element' => 'p', 'content' => $variable, 'show_empty' => false, 'properties' => ['data-ref' => 'company_address-' . substr($variable, 1)]];
+ $elements[] = ['element' => 'div', 'content' => $variable, 'show_empty' => false, 'properties' => ['data-ref' => 'company_address-' . substr($variable, 1)]];
}
return $elements;
@@ -1939,7 +1938,7 @@ class PdfBuilder
$variables = $this->service->config->pdf_variables['vendor_details'];
foreach ($variables as $variable) {
- $elements[] = ['element' => 'p', 'content' => $variable, 'show_empty' => false, 'properties' => ['data-ref' => 'vendor_details-' . substr($variable, 1)]];
+ $elements[] = ['element' => 'div', 'content' => $variable, 'show_empty' => false, 'properties' => ['data-ref' => 'vendor_details-' . substr($variable, 1)]];
}
return $elements;
@@ -1997,23 +1996,49 @@ class PdfBuilder
public function createElementContent($element, $children): self
{
foreach ($children as $child) {
+
$contains_html = false;
+ $contains_markdown = false;
+ $child['content'] = $child['content'] ?? '';
- if ($child['element'] !== 'script') {
- if ($this->service->company->markdown_enabled && array_key_exists('content', $child)) {
- $child['content'] = str_replace('
', "\r", ($child['content'] ?? ''));
- $child['content'] = $this->commonmark->convert($child['content'] ?? ''); //@phpstan-ignore-line
- }
- }
+ $lines = explode("\n", $child['content']);
+ $contains_markdown = false;
- if (isset($child['content'])) {
- if (isset($child['is_empty']) && $child['is_empty'] === true) {
+ foreach ($lines as $line) {
+ $trimmed = ltrim($line);
+ if (empty($trimmed)) {
continue;
}
- $contains_html = preg_match('#(?<=<)\w+(?=[^<]*?>)#', $child['content'], $m) != 0;
+ $first_char = substr($trimmed, 0, 1);
+
+ if (
+ $first_char === '#' || // Headers
+ $first_char === '>' || // Blockquotes
+ $first_char === '-' || // Lists
+ $first_char === '*' || // Lists/Bold
+ $first_char === '_' || // Italic
+ $first_char === '`' || // Code
+ $first_char === '[' || // Links
+ str_contains($trimmed, '**') // Bold (special case)
+ ) {
+ $contains_markdown = true;
+ break;
+ }
}
+ if ($this->service->company->markdown_enabled && $contains_markdown && $child['element'] !== 'script') {
+ $child['content'] = str_ireplace('
', "\r", $child['content']);
+ $child['content'] = $this->commonmark->convert($child['content']); //@phpstan-ignore-line
+ }
+
+ if (isset($child['is_empty']) && $child['is_empty'] === true) {
+ continue;
+ }
+
+ $contains_html = str_contains($child['content'], '<') && str_contains($child['content'], '>');
+
+
if ($contains_html) {
// If the element contains the HTML, we gonna display it as is. Backend is going to
@@ -2030,7 +2055,7 @@ class PdfBuilder
// .. in case string doesn't contain any HTML, we'll just return
// raw $content
- $_child = $this->document->createElement($child['element'], isset($child['content']) ? htmlspecialchars($child['content']) : '');
+ $_child = $this->document->createElement($child['element'], htmlspecialchars($child['content']));
}
$element->appendChild($_child);
diff --git a/app/Services/PdfMaker/Design.php b/app/Services/PdfMaker/Design.php
index d5234e0908..3e05054ed5 100644
--- a/app/Services/PdfMaker/Design.php
+++ b/app/Services/PdfMaker/Design.php
@@ -241,7 +241,7 @@ class Design extends BaseDesign
$elements = [];
foreach ($variables as $variable) {
- $elements[] = ['element' => 'p', 'content' => $variable, 'show_empty' => false, 'properties' => ['data-ref' => 'company_details-' . substr($variable, 1)]];
+ $elements[] = ['element' => 'div', 'content' => $variable, 'show_empty' => false, 'properties' => ['data-ref' => 'company_details-' . substr($variable, 1)]];
}
return $elements;
@@ -254,7 +254,7 @@ class Design extends BaseDesign
$elements = [];
foreach ($variables as $variable) {
- $elements[] = ['element' => 'p', 'content' => $variable, 'show_empty' => false, 'properties' => ['data-ref' => 'company_address-' . substr($variable, 1)]];
+ $elements[] = ['element' => 'div', 'content' => $variable, 'show_empty' => false, 'properties' => ['data-ref' => 'company_address-' . substr($variable, 1)]];
}
return $elements;
@@ -271,7 +271,7 @@ class Design extends BaseDesign
$variables = $this->context['pdf_variables']['vendor_details'];
foreach ($variables as $variable) {
- $elements[] = ['element' => 'p', 'content' => $variable, 'show_empty' => false, 'properties' => ['data-ref' => 'vendor_details-' . substr($variable, 1)]];
+ $elements[] = ['element' => 'div', 'content' => $variable, 'show_empty' => false, 'properties' => ['data-ref' => 'vendor_details-' . substr($variable, 1)]];
}
return $elements;
@@ -301,12 +301,12 @@ class Design extends BaseDesign
})->map(function ($variable) {
$variable = str_replace('$client.', '$client.shipping_', $variable);
- return ['element' => 'p', 'content' => $variable, 'show_empty' => false, 'properties' => ['data-ref' => "client_details-shipping-" . substr($variable, 1)]];
+ return ['element' => 'div', 'content' => $variable, 'show_empty' => false, 'properties' => ['data-ref' => "client_details-shipping-" . substr($variable, 1)]];
})->toArray();
$header = [];
- $header[] = ['element' => 'p', 'content' => ctrans('texts.shipping_address'), 'properties' => ['data-ref' => 'shipping_address-label', 'style' => 'font-weight: bold; text-transform: uppercase']];
+ $header[] = ['element' => 'div', 'content' => ctrans('texts.shipping_address'), 'properties' => ['data-ref' => 'shipping_address-label', 'style' => 'font-weight: bold; text-transform: uppercase']];
return array_merge($header, $elements);
@@ -322,20 +322,20 @@ class Design extends BaseDesign
if ($this->type == self::DELIVERY_NOTE) {
$elements = [
- ['element' => 'p', 'content' => ctrans('texts.delivery_note'), 'properties' => ['data-ref' => 'delivery_note-label', 'style' => 'font-weight: bold; text-transform: uppercase']],
- ['element' => 'p', 'content' => $this->client->name, 'show_empty' => false, 'properties' => ['data-ref' => 'delivery_note-client.name']],
- ['element' => 'p', 'content' => $this->client->shipping_address1, 'show_empty' => false, 'properties' => ['data-ref' => 'delivery_note-client.shipping_address1']],
- ['element' => 'p', 'content' => $this->client->shipping_address2, 'show_empty' => false, 'properties' => ['data-ref' => 'delivery_note-client.shipping_address2']],
- ['element' => 'p', 'show_empty' => false, 'elements' => [
+ ['element' => 'div', 'content' => ctrans('texts.delivery_note'), 'properties' => ['data-ref' => 'delivery_note-label', 'style' => 'font-weight: bold; text-transform: uppercase']],
+ ['element' => 'div', 'content' => $this->client->name, 'show_empty' => false, 'properties' => ['data-ref' => 'delivery_note-client.name']],
+ ['element' => 'div', 'content' => $this->client->shipping_address1, 'show_empty' => false, 'properties' => ['data-ref' => 'delivery_note-client.shipping_address1']],
+ ['element' => 'div', 'content' => $this->client->shipping_address2, 'show_empty' => false, 'properties' => ['data-ref' => 'delivery_note-client.shipping_address2']],
+ ['element' => 'div', 'show_empty' => false, 'elements' => [
['element' => 'span', 'content' => "{$this->client->shipping_city} ", 'properties' => ['ref' => 'delivery_note-client.shipping_city']],
['element' => 'span', 'content' => "{$this->client->shipping_state} ", 'properties' => ['ref' => 'delivery_note-client.shipping_state']],
['element' => 'span', 'content' => "{$this->client->shipping_postal_code} ", 'properties' => ['ref' => 'delivery_note-client.shipping_postal_code']],
]],
- ['element' => 'p', 'content' => optional($this->client->shipping_country)->name, 'show_empty' => false],
+ ['element' => 'div', 'content' => optional($this->client->shipping_country)->name, 'show_empty' => false],
];
if (!is_null($this->context['contact'])) {
- $elements[] = ['element' => 'p', 'content' => $this->context['contact']->email, 'show_empty' => false, 'properties' => ['data-ref' => 'delivery_note-contact.email']];
+ $elements[] = ['element' => 'div', 'content' => $this->context['contact']->email, 'show_empty' => false, 'properties' => ['data-ref' => 'delivery_note-contact.email']];
}
return $elements;
@@ -344,7 +344,7 @@ class Design extends BaseDesign
$variables = $this->context['pdf_variables']['client_details'];
foreach ($variables as $variable) {
- $elements[] = ['element' => 'p', 'content' => $variable, 'show_empty' => false, 'properties' => ['data-ref' => 'client_details-' . substr($variable, 1)]];
+ $elements[] = ['element' => 'div', 'content' => $variable, 'show_empty' => false, 'properties' => ['data-ref' => 'client_details-' . substr($variable, 1)]];
}
return $elements;
@@ -553,7 +553,7 @@ class Design extends BaseDesign
$outstanding = $this->invoices->sum('balance');
return [
- ['element' => 'p', 'content' => '$outstanding_label: ' . Number::formatMoney($outstanding, $this->client)],
+ ['element' => 'div', 'content' => '$outstanding_label: ' . Number::formatMoney($outstanding, $this->client)],
];
}
@@ -682,7 +682,7 @@ class Design extends BaseDesign
$outstanding = $this->credits->sum('balance');
return [
- ['element' => 'p', 'content' => '$credit.balance_label: ' . Number::formatMoney($outstanding, $this->client)],
+ ['element' => 'div', 'content' => '$credit.balance_label: ' . Number::formatMoney($outstanding, $this->client)],
];
}
@@ -699,8 +699,8 @@ class Design extends BaseDesign
$payment = $this->payments->first();
return [
- // ['element' => 'p', 'content' => \sprintf('%s: %s', ctrans('texts.amount_paid'), Number::formatMoney($this->payments->sum('amount'), $this->client))],
- ['element' => 'p', 'content' => \sprintf('%s: %s', ctrans('texts.amount_paid'), Number::formatMoney($this->payment_amount_total, $this->client))],
+ // ['element' => 'div', 'content' => \sprintf('%s: %s', ctrans('texts.amount_paid'), Number::formatMoney($this->payments->sum('amount'), $this->client))],
+ ['element' => 'div', 'content' => \sprintf('%s: %s', ctrans('texts.amount_paid'), Number::formatMoney($this->payment_amount_total, $this->client))],
];
}
@@ -716,7 +716,7 @@ class Design extends BaseDesign
}
return [
- ['element' => 'p', 'content' => \sprintf('%s: %s', ctrans('texts.payment_balance_on_file'), Number::formatMoney($this->unapplied_total, $this->client))],
+ ['element' => 'div', 'content' => \sprintf('%s: %s', ctrans('texts.payment_balance_on_file'), Number::formatMoney($this->unapplied_total, $this->client))],
];
}
@@ -1127,10 +1127,10 @@ class Design extends BaseDesign
$elements = [
['element' => 'div', 'properties' => ['style' => 'display: flex; flex-direction: column;'], 'elements' => [
- ['element' => 'p', 'properties' => ['data-ref' => 'total_table-public_notes', 'style' => 'text-align: left;'], 'elements' => [
+ ['element' => 'div', 'properties' => ['data-ref' => 'total_table-public_notes', 'style' => 'text-align: left;'], 'elements' => [
['element' => 'span', 'content' => strtr(str_replace(["labels", "values"], ["",""], $_variables['values']['$entity.public_notes']), $_variables)]
]],
- ['element' => 'p', 'content' => '', 'properties' => ['style' => 'text-align: left; display: flex; flex-direction: column; page-break-inside: auto;'], 'elements' => [
+ ['element' => 'div', 'content' => '', 'properties' => ['style' => 'text-align: left; display: flex; flex-direction: column; page-break-inside: auto;'], 'elements' => [
['element' => 'span', 'content' => '$entity.terms_label: ', 'properties' => ['data-ref' => 'total_table-terms-label', 'style' => "font-weight: bold; text-align: left; margin-top: 1rem; {$show_terms_label}"]],
['element' => 'span', 'content' => strtr(str_replace("labels", "", $_variables['values']['$entity.terms']), $_variables['labels']), 'properties' => ['data-ref' => 'total_table-terms', 'style' => 'text-align: left;']],
]],
diff --git a/app/Services/PdfMaker/PdfMakerUtilities.php b/app/Services/PdfMaker/PdfMakerUtilities.php
index cf7b98c557..1ff942749a 100644
--- a/app/Services/PdfMaker/PdfMakerUtilities.php
+++ b/app/Services/PdfMaker/PdfMakerUtilities.php
@@ -91,22 +91,46 @@ trait PdfMakerUtilities
{
foreach ($children as $child) {
$contains_html = false;
+ $contains_markdown = false;
+ $child['content'] = $child['content'] ?? '';
- if ($child['element'] !== 'script') {
- if (array_key_exists('process_markdown', $this->data) && array_key_exists('content', $child) && $this->data['process_markdown']) {
- $child['content'] = str_replace('
', "\r", ($child['content'] ?? ''));
- $child['content'] = $this->commonmark->convert($child['content'] ?? ''); //@phpstan-ignore-line
- }
- }
+ $lines = explode("\n", $child['content']);
+ $contains_markdown = false;
- if (isset($child['content'])) {
- if (isset($child['is_empty']) && $child['is_empty'] === true) {
+ foreach ($lines as $line) {
+ $trimmed = ltrim($line);
+ if (empty($trimmed)) {
continue;
}
- $contains_html = preg_match('#(?<=<)\w+(?=[^<]*?>)#', $child['content'], $m) != 0;
+ $first_char = substr($trimmed, 0, 1);
+
+ if (
+ $first_char === '#' || // Headers
+ $first_char === '>' || // Blockquotes
+ $first_char === '-' || // Lists
+ $first_char === '*' || // Lists/Bold
+ $first_char === '_' || // Italic
+ $first_char === '`' || // Code
+ $first_char === '[' || // Links
+ str_contains($trimmed, '**') // Bold (special case)
+ ) {
+ $contains_markdown = true;
+ break;
+ }
}
+ if (isset($this->data['process_markdown']) && $this->data['process_markdown'] && $contains_markdown &&$child['element'] !== 'script') {
+ $child['content'] = str_replace('
', "\r", $child['content']);
+ $child['content'] = $this->commonmark->convert($child['content']); //@phpstan-ignore-line
+ }
+
+ if (isset($child['is_empty']) && $child['is_empty'] === true) {
+ continue;
+ }
+
+ $contains_html = str_contains($child['content'], '<') && str_contains($child['content'], '>');
+
if ($contains_html) {
// If the element contains the HTML, we gonna display it as is. Backend is going to
// encode it for us, preventing any errors on the processing stage.
@@ -118,9 +142,7 @@ trait PdfMakerUtilities
$_child->nodeValue = htmlspecialchars($child['content']);
} else {
// .. in case string doesn't contain any HTML, we'll just return
- // raw $content.
-
- $_child = $this->document->createElement($child['element'], isset($child['content']) ? htmlspecialchars($child['content']) : '');
+ $_child = $this->document->createElement($child['element'], htmlspecialchars($child['content']));
}
$element->appendChild($_child);
diff --git a/app/Services/Template/TemplateService.php b/app/Services/Template/TemplateService.php
index 83efda5e05..51f88e3cb5 100644
--- a/app/Services/Template/TemplateService.php
+++ b/app/Services/Template/TemplateService.php
@@ -68,7 +68,7 @@ class TemplateService
private ?Vendor $vendor = null;
- private Invoice | Quote | Credit | PurchaseOrder | RecurringInvoice | Task | Project $entity;
+ private Invoice | Quote | Credit | PurchaseOrder | RecurringInvoice | Task | Project | Payment $entity;
private Payment $payment;
@@ -1575,23 +1575,14 @@ class TemplateService
foreach ($children as $child) {
$contains_html = false;
+ $child['content'] = $child['content'] ?? '';
- //06-11-2023 for some reason this parses content as HTML
- // if ($child['element'] !== 'script') {
- // if ($this->company->markdown_enabled && array_key_exists('content', $child)) {
- // $child['content'] = str_replace('
', "\r", $child['content']);
- // $child['content'] = $this->commonmark->convert($child['content'] ?? '');
- // }
- // }
-
- if (isset($child['content'])) {
- if (isset($child['is_empty']) && $child['is_empty'] === true) {
- continue;
- }
-
- $contains_html = preg_match('#(?<=<)\w+(?=[^<]*?>)#', $child['content'], $m) != 0;
+ if (isset($child['is_empty']) && $child['is_empty'] === true) {
+ continue;
}
+ $contains_html = str_contains($child['content'], '<') && str_contains($child['content'], '>');
+
if ($contains_html) {
// If the element contains the HTML, we gonna display it as is. Backend is going to
// encode it for us, preventing any errors on the processing stage.
@@ -1605,7 +1596,7 @@ class TemplateService
} else {
// .. in case string doesn't contain any HTML, we'll just return
// raw $content.
- $_child = $this->document->createElement($child['element'], isset($child['content']) ? $child['content'] : '');
+ $_child = $this->document->createElement($child['element'], $child['content']);
}
$element->appendChild($_child);
diff --git a/app/Utils/Traits/ClientGroupSettingsSaver.php b/app/Utils/Traits/ClientGroupSettingsSaver.php
index e049886bab..ae7937b762 100644
--- a/app/Utils/Traits/ClientGroupSettingsSaver.php
+++ b/app/Utils/Traits/ClientGroupSettingsSaver.php
@@ -46,12 +46,14 @@ trait ClientGroupSettingsSaver
unset($settings[$field]);
}
+ $company_settings_stub = new CompanySettings();
+
/*
* for clients and group settings, if a field is not set or is set to a blank value,
* we unset it from the settings object
*/
foreach ($settings as $key => $value) {
- if (! isset($settings->{$key}) || empty($settings->{$key}) || (! is_object($settings->{$key}) && strlen($settings->{$key}) == 0)) {
+ if (! isset($settings->{$key}) || empty($settings->{$key}) || !property_exists($company_settings_stub, $key) || (! is_object($settings->{$key}) && strlen($settings->{$key}) == 0)) {
unset($settings->{$key});
}
}
diff --git a/app/Utils/Traits/Pdf/PdfMaker.php b/app/Utils/Traits/Pdf/PdfMaker.php
index 926692467a..ccaf0a10aa 100644
--- a/app/Utils/Traits/Pdf/PdfMaker.php
+++ b/app/Utils/Traits/Pdf/PdfMaker.php
@@ -94,7 +94,8 @@ trait PdfMaker
}
$html = str_ireplace(['file:/', 'iframe', '