diff --git a/app/Filters/QueryFilters.php b/app/Filters/QueryFilters.php
index 83fb8a36fdc6..87bfb3288bee 100644
--- a/app/Filters/QueryFilters.php
+++ b/app/Filters/QueryFilters.php
@@ -169,7 +169,7 @@ abstract class QueryFilters
public function clientFilter()
{
if (auth()->guard('contact')->user()) {
- return $this->builder->whereClientId(auth()->guard('contact')->user()->client->id);
+ return $this->builder->where('client_id', auth()->guard('contact')->user()->client->id);
}
}
@@ -179,6 +179,15 @@ abstract class QueryFilters
$created_at = date('Y-m-d H:i:s', $value);
+ if(is_string($created_at)){
+
+ $created_at = strtotime(str_replace("/","-",$created_at));
+
+ if(!$created_at)
+ return $this->builder;
+
+ }
+
return $this->builder->where('created_at', '>=', $created_at);
}
diff --git a/app/Helpers/SwissQr/SwissQrGenerator.php b/app/Helpers/SwissQr/SwissQrGenerator.php
index 6d134adf90da..b3eb796bd409 100644
--- a/app/Helpers/SwissQr/SwissQrGenerator.php
+++ b/app/Helpers/SwissQr/SwissQrGenerator.php
@@ -112,7 +112,7 @@ class SwissQrGenerator
}
else
{
- $invoice_number = $this->invoice->number;
+ $invoice_number = iconv("UTF-8", "ASCII", $this->invoice->number);
}
if(strlen($this->company->present()->besr_id()) > 1)
@@ -141,7 +141,7 @@ class SwissQrGenerator
// Optionally, add some human-readable information about what the bill is for.
$qrBill->setAdditionalInformation(
QrBill\DataGroup\Element\AdditionalInformation::create(
- $this->invoice->public_notes ? substr($this->invoice->public_notes, 0, 139) : ctrans('texts.invoice_number_placeholder', ['invoice' => $invoice_number])
+ $this->invoice->public_notes ? substr($this->invoice->public_notes, 0, 139) : ctrans('texts.invoice_number_placeholder', ['invoice' => $this->invoice->number])
)
);
diff --git a/app/Http/Controllers/CreditController.php b/app/Http/Controllers/CreditController.php
index 55a1d03aad8e..30104c8c9fd3 100644
--- a/app/Http/Controllers/CreditController.php
+++ b/app/Http/Controllers/CreditController.php
@@ -33,6 +33,7 @@ use App\Models\Client;
use App\Models\Credit;
use App\Models\Invoice;
use App\Repositories\CreditRepository;
+use App\Services\PdfMaker\PdfMerge;
use App\Transformers\CreditTransformer;
use App\Utils\Ninja;
use App\Utils\TempFile;
@@ -534,6 +535,20 @@ class CreditController extends BaseController
return response()->json(['message' => ctrans('texts.sent_message')], 200);
}
+ if($action == 'merge' && auth()->user()->can('view', $credits->first())){
+
+ $paths = $credits->map(function ($credit){
+ return $credit->service()->getCreditPdf($credit->invitations->first());
+ });
+
+ $merge = (new PdfMerge($paths->toArray()))->run();
+
+ return response()->streamDownload(function () use ($merge) {
+ echo ($merge);
+ }, 'print.pdf', ['Content-Type' => 'application/pdf']);
+
+ }
+
$credits->each(function ($credit, $key) use ($action) {
if (auth()->user()->can('edit', $credit)) {
$this->performAction($credit, $action, true);
diff --git a/app/Http/Controllers/InvoiceController.php b/app/Http/Controllers/InvoiceController.php
index 8050c433af1b..8e61ae7fdbf9 100644
--- a/app/Http/Controllers/InvoiceController.php
+++ b/app/Http/Controllers/InvoiceController.php
@@ -40,6 +40,7 @@ use App\Models\Invoice;
use App\Models\Quote;
use App\Models\TransactionEvent;
use App\Repositories\InvoiceRepository;
+use App\Services\PdfMaker\PdfMerge;
use App\Transformers\InvoiceTransformer;
use App\Transformers\QuoteTransformer;
use App\Utils\Ninja;
@@ -588,6 +589,20 @@ class InvoiceController extends BaseController
}
+ if($action == 'merge' && auth()->user()->can('view', $invoices->first())){
+
+ $paths = $invoices->map(function ($invoice){
+ return $invoice->service()->getInvoicePdf();
+ });
+
+ $merge = (new PdfMerge($paths->toArray()))->run();
+
+ return response()->streamDownload(function () use ($merge) {
+ echo ($merge);
+ }, 'print.pdf', ['Content-Type' => 'application/pdf']);
+
+ }
+
/*
* Send the other actions to the switch
*/
diff --git a/app/Http/Controllers/PurchaseOrderController.php b/app/Http/Controllers/PurchaseOrderController.php
index 83d8c9cd55cb..e417027f1b31 100644
--- a/app/Http/Controllers/PurchaseOrderController.php
+++ b/app/Http/Controllers/PurchaseOrderController.php
@@ -32,6 +32,7 @@ use App\Models\Client;
use App\Models\Expense;
use App\Models\PurchaseOrder;
use App\Repositories\PurchaseOrderRepository;
+use App\Services\PdfMaker\PdfMerge;
use App\Transformers\ExpenseTransformer;
use App\Transformers\PurchaseOrderTransformer;
use App\Utils\Ninja;
@@ -515,6 +516,20 @@ class PurchaseOrderController extends BaseController
return response()->json(['message' => ctrans('texts.sent_message')], 200);
}
+ if($action == 'merge' && auth()->user()->can('view', $purchase_orders->first())){
+
+ $paths = $purchase_orders->map(function ($purchase_order){
+ return $purchase_order->service()->getPurchaseOrderPdf();
+ });
+
+ $merge = (new PdfMerge($paths->toArray()))->run();
+
+ return response()->streamDownload(function () use ($merge) {
+ echo ($merge);
+ }, 'print.pdf', ['Content-Type' => 'application/pdf']);
+
+ }
+
/*
* Send the other actions to the switch
*/
diff --git a/app/Http/Controllers/QuoteController.php b/app/Http/Controllers/QuoteController.php
index a42c9c09c8e3..fc8f6319329c 100644
--- a/app/Http/Controllers/QuoteController.php
+++ b/app/Http/Controllers/QuoteController.php
@@ -35,6 +35,7 @@ use App\Models\Invoice;
use App\Models\Project;
use App\Models\Quote;
use App\Repositories\QuoteRepository;
+use App\Services\PdfMaker\PdfMerge;
use App\Transformers\InvoiceTransformer;
use App\Transformers\ProjectTransformer;
use App\Transformers\QuoteTransformer;
@@ -561,6 +562,20 @@ class QuoteController extends BaseController
return $this->listResponse(Quote::withTrashed()->whereIn('id', $this->transformKeys($ids))->company());
}
+ if($action == 'merge' && auth()->user()->can('view', $quotes->first())){
+
+ $paths = $quotes->map(function ($quote){
+ return $quote->service()->getQuotePdf();
+ });
+
+ $merge = (new PdfMerge($paths->toArray()))->run();
+
+ return response()->streamDownload(function () use ($merge) {
+ echo ($merge);
+ }, 'print.pdf', ['Content-Type' => 'application/pdf']);
+
+ }
+
if($action == 'convert_to_project')
{
diff --git a/app/Jobs/Util/ReminderJob.php b/app/Jobs/Util/ReminderJob.php
index 403ef964a37e..b5ed69206c61 100644
--- a/app/Jobs/Util/ReminderJob.php
+++ b/app/Jobs/Util/ReminderJob.php
@@ -99,7 +99,7 @@ class ReminderJob implements ShouldQueue
(Ninja::isSelfHost() || $invoice->company->account->isPaidHostedClient())) {
$invoice->invitations->each(function ($invitation) use ($invoice, $reminder_template) {
- EmailEntity::dispatchSync($invitation, $invitation->company, $reminder_template);
+ EmailEntity::dispatch($invitation, $invitation->company, $reminder_template);
nlog("Firing reminder email for invoice {$invoice->number} - {$reminder_template}");
});
diff --git a/app/Mail/Engine/PaymentEmailEngine.php b/app/Mail/Engine/PaymentEmailEngine.php
index 8e383090ad6f..951fe587a3e1 100644
--- a/app/Mail/Engine/PaymentEmailEngine.php
+++ b/app/Mail/Engine/PaymentEmailEngine.php
@@ -251,17 +251,17 @@ class PaymentEmailEngine extends BaseEmailEngine
private function formatInvoiceField($field)
{
- $invoice = '';
+ $invoicex = '';
foreach ($this->payment->invoices as $invoice) {
$invoice_field = $invoice->{$field};
- $invoice .= ctrans('texts.invoice_number_short') . "{$invoice->number} {$invoice_field}";
+ $invoicex .= ctrans('texts.invoice_number_short') . "{$invoice->number} {$invoice_field}";
}
- return $invoice;
+ return $invoicex;
}
diff --git a/app/PaymentDrivers/PayPalExpressPaymentDriver.php b/app/PaymentDrivers/PayPalExpressPaymentDriver.php
index 86cad72f4aca..c69f680dec2b 100644
--- a/app/PaymentDrivers/PayPalExpressPaymentDriver.php
+++ b/app/PaymentDrivers/PayPalExpressPaymentDriver.php
@@ -95,7 +95,7 @@ class PayPalExpressPaymentDriver extends BaseDriver
return $response->redirect();
}
- $this->sendFailureMail($response->getMessage() ?: '');
+ // $this->sendFailureMail($response->getMessage() ?: '');
$message = [
'server_response' => $response->getMessage(),
diff --git a/app/PaymentDrivers/Stripe/Charge.php b/app/PaymentDrivers/Stripe/Charge.php
index 3c927e544baa..eb6bc14558e3 100644
--- a/app/PaymentDrivers/Stripe/Charge.php
+++ b/app/PaymentDrivers/Stripe/Charge.php
@@ -137,6 +137,9 @@ class Charge
return false;
}
+ if($response?->status != 'succeeded')
+ $this->stripe->processInternallyFailedPayment($this->stripe, new \Exception('Auto billing failed.',400));
+
if ($cgt->gateway_type_id == GatewayType::SEPA) {
$payment_method_type = PaymentType::SEPA;
$status = Payment::STATUS_PENDING;
diff --git a/app/Repositories/BaseRepository.php b/app/Repositories/BaseRepository.php
index 1595b422eda5..127387abbfff 100644
--- a/app/Repositories/BaseRepository.php
+++ b/app/Repositories/BaseRepository.php
@@ -304,8 +304,9 @@ class BaseRepository
if (! $model->design_id)
$model->design_id = $this->decodePrimaryKey($client->getSetting('invoice_design_id'));
- //links tasks and expenses back to the invoice.
- $model->service()->linkEntities()->save();
+ //links tasks and expenses back to the invoice, but only if we are not in the middle of a transaction.
+ if (\DB::transactionLevel() == 0)
+ $model->service()->linkEntities()->save();
if($this->new_model)
event('eloquent.created: App\Models\Invoice', $model);
diff --git a/app/Services/PdfMaker/PdfMerge.php b/app/Services/PdfMaker/PdfMerge.php
new file mode 100644
index 000000000000..e4b2f50137b5
--- /dev/null
+++ b/app/Services/PdfMaker/PdfMerge.php
@@ -0,0 +1,42 @@
+file_paths as $file) {
+ $pageCount = $pdf->setSourceFile(StreamReader::createByString(Storage::get($file)));
+ for ($i = 0; $i < $pageCount; $i++) {
+ $tpl = $pdf->importPage($i + 1, '/MediaBox');
+ $pdf->addPage();
+ $pdf->useTemplate($tpl);
+ }
+ }
+
+ return $pdf->Output('S');
+
+ }
+
+}
diff --git a/app/Utils/HtmlEngine.php b/app/Utils/HtmlEngine.php
index 4806a89d1d35..f38e8ca9de42 100644
--- a/app/Utils/HtmlEngine.php
+++ b/app/Utils/HtmlEngine.php
@@ -143,6 +143,7 @@ class HtmlEngine
$data['$credit.datetime'] = &$data['$entity.datetime'];
$data['$payment_button'] = ['value' => ''.ctrans('texts.pay_now').'', 'label' => ctrans('texts.pay_now')];
$data['$payment_link'] = ['value' => $this->invitation->getPaymentLink(), 'label' => ctrans('texts.pay_now')];
+ $data['$payment_qrcode'] = ['value' => $this->invitation->getPaymentQrCode(), 'label' => ctrans('texts.pay_now')];
if ($this->entity_string == 'invoice' || $this->entity_string == 'recurring_invoice') {
diff --git a/app/Utils/Traits/Inviteable.php b/app/Utils/Traits/Inviteable.php
index 7e2e6fae1455..8b6481bd1ff0 100644
--- a/app/Utils/Traits/Inviteable.php
+++ b/app/Utils/Traits/Inviteable.php
@@ -13,6 +13,11 @@ namespace App\Utils\Traits;
use App\Utils\Ninja;
use Illuminate\Support\Str;
+use BaconQrCode\Renderer\ImageRenderer;
+use BaconQrCode\Renderer\Image\SvgImageBackEnd;
+use BaconQrCode\Renderer\RendererStyle\RendererStyle;
+use BaconQrCode\Writer;
+
/**
* Class Inviteable.
@@ -54,6 +59,22 @@ trait Inviteable
return $domain.'/client/pay/'.$this->key;
}
+ public function getPaymentQrCode()
+ {
+
+ $renderer = new ImageRenderer(
+ new RendererStyle(200),
+ new SvgImageBackEnd()
+ );
+ $writer = new Writer($renderer);
+
+ $qr = $writer->writeString($this->getPaymentLink());
+
+ return "";
+
+ }
+
public function getUnsubscribeLink()
{
if (Ninja::isHosted()) {